Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 3 additions & 9 deletions packages/opencode/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ src/
│ └── entities.ts # Entity extraction and management
├── tools/
│ ├── index.ts # Tool exports
│ ├── delegate.ts # agentuity_coder_delegate tool
│ └── background.ts # Background task tools
├── background/
│ ├── index.ts # Background task exports
Expand Down Expand Up @@ -130,15 +129,10 @@ Sessions are stored with branch awareness to prevent stale memories from deleted

## Delegation Pattern

Lead delegates to specialized agents via `agentuity_coder_delegate` tool:
Lead delegates to specialized agents via two mechanisms:

```typescript
{
agent: 'scout' | 'builder' | 'architect' | 'reviewer' | 'memory' | 'reasoner' | 'expert' | 'runner' | 'product' | 'monitor',
task: 'Description of what to do',
context?: 'Additional context'
}
```
- **Task tool** (blocking) — Spawns a subagent, waits for result. Use for sequential work.
- **`agentuity_background_task`** (parallel) — Launches a task in a separate session. Use for independent concurrent work.

## Testing

Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/agents/architect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ export const architectAgent: AgentDefinition = {
displayName: 'Agentuity Coder Architect',
description:
'Senior implementer for complex autonomous tasks - Cadence mode, deep reasoning, extended execution',
defaultModel: 'openai/gpt-5.2-codex',
defaultModel: 'openai/gpt-5.3-codex',
systemPrompt: ARCHITECT_SYSTEM_PROMPT,
reasoningEffort: 'xhigh', // Maximum reasoning for complex tasks
temperature: 0.1, // Deterministic - precise code generation
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/agents/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ export const builderAgent: AgentDefinition = {
id: 'ag-builder',
displayName: 'Agentuity Coder Builder',
description: 'Agentuity Coder implementer - writes code, makes edits, runs tests and builds',
defaultModel: 'anthropic/claude-opus-4-5-20251101',
defaultModel: 'anthropic/claude-opus-4-6',
systemPrompt: BUILDER_SYSTEM_PROMPT,
variant: 'high', // Careful thinking for implementation
temperature: 0.1, // Deterministic - precise code generation
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/agents/lead.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1334,7 +1334,7 @@ export const leadAgent: AgentDefinition = {
displayName: 'Agentuity Coder Lead',
description:
'Agentuity Coder team orchestrator - delegates to Scout, Builder, Reviewer, Memory, Expert',
defaultModel: 'anthropic/claude-opus-4-5-20251101',
defaultModel: 'anthropic/claude-opus-4-6',
systemPrompt: LEAD_SYSTEM_PROMPT,
mode: 'all',
tools: {
Expand Down
8 changes: 4 additions & 4 deletions packages/opencode/src/agents/memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ Agents can have different views of each other. Store and retrieve perspectives t
"perspectiveId": "lead:view:builder",
"observer": "entity:agent:lead",
"observed": "entity:agent:builder",
"observerModel": "claude-opus-4-5-20251101",
"observedModel": "claude-opus-4-5-20251101",
"observerModel": "claude-opus-4-6",
"observedModel": "claude-opus-4-6",
"conclusions": [
{
"type": "inductive",
Expand Down Expand Up @@ -176,8 +176,8 @@ agentuity cloud kv set agentuity-opencode-memory "perspective:lead:builder" '{
"perspectiveId": "lead:view:builder",
"observer": "entity:agent:lead",
"observed": "entity:agent:builder",
"observerModel": "claude-opus-4-5-20251101",
"observedModel": "claude-opus-4-5-20251101",
"observerModel": "claude-opus-4-6-20260205",
"observedModel": "claude-opus-4-6-20260205",
"conclusions": [...],
"recommendations": [...],
"createdAt": "...",
Expand Down
1 change: 0 additions & 1 deletion packages/opencode/src/agents/monitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ export const monitorAgent: AgentDefinition = {
'glob',
'grep',
'task',
'agentuity_coder_delegate',
'agentuity_background_task',
'agentuity_background_cancel',
'agentuity_memory_share',
Expand Down
12 changes: 5 additions & 7 deletions packages/opencode/src/agents/reasoner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,12 @@ You can (and should) query the Memory agent to retrieve relevant context while r

### How to Query

Use \`agentuity_coder_delegate\` to ask Memory:
Use the Task tool to ask Memory:

\`\`\`
agentuity_coder_delegate({
agent: "memory",
task: "What auth patterns and corrections do we have?",
context: "Reasoning about auth implementation in session data"
})
@Agentuity Coder Memory

What auth patterns and corrections do we have? Context: Reasoning about auth implementation in session data.
\`\`\`

### The Feedback Loop
Expand Down Expand Up @@ -260,7 +258,7 @@ export const reasonerAgent: AgentDefinition = {
systemPrompt: REASONER_SYSTEM_PROMPT,
mode: 'subagent',
tools: {
exclude: ['write', 'edit', 'apply_patch', 'task'],
exclude: ['write', 'edit', 'apply_patch'],
},
reasoningEffort: 'high',
temperature: 0.3,
Expand Down
23 changes: 23 additions & 0 deletions packages/opencode/src/background/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,29 @@ export class BackgroundManager {
private async notifyParent(task: BackgroundTask): Promise<void> {
if (!task.parentSessionId) return;

// Prevent duplicate notifications for the same task+status combination
// This guards against OpenCode firing multiple events for the same status transition
const notifiedStatuses = task.notifiedStatuses ?? new Set();

// Self-healing for tasks created before deduplication was added:
// If a task is already in a terminal state but has no notification history,
// assume it was already notified and skip to prevent duplicate notifications.
if (
notifiedStatuses.size === 0 &&
(task.status === 'completed' || task.status === 'error' || task.status === 'cancelled')
) {
notifiedStatuses.add(task.status);
task.notifiedStatuses = notifiedStatuses;
return;
}

if (notifiedStatuses.has(task.status)) {
return; // Already notified for this status, skip duplicate
}
// Mark as notified BEFORE sending to prevent race conditions
notifiedStatuses.add(task.status);
task.notifiedStatuses = notifiedStatuses;

const statusLine = task.status === 'completed' ? 'completed' : task.status;
const message = `[BACKGROUND TASK ${statusLine.toUpperCase()}]

Expand Down
1 change: 1 addition & 0 deletions packages/opencode/src/background/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface BackgroundTask {
progress?: TaskProgress;
concurrencyKey?: string; // Active concurrency slot key
concurrencyGroup?: string; // Persistent key for re-acquiring on resume
notifiedStatuses?: Set<BackgroundTaskStatus>; // Tracks statuses already notified to prevent duplicates
}

export interface LaunchInput {
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/config/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export async function getConfigPath(): Promise<string> {
* {
* "agent": {
* "Agentuity Coder Architect": {
* "model": "openai/gpt-5.2-codex",
* "model": "openai/gpt-5.3-codex",
* "reasoningEffort": "xhigh"
* }
* }
Expand Down
62 changes: 0 additions & 62 deletions packages/opencode/src/plugin/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,26 +63,6 @@ You are running inside an Agentuity Sandbox (ID: ${SANDBOX_ID}).
// Agents that should receive sandbox context in their prompts
const SANDBOX_AWARE_AGENTS: AgentRole[] = ['lead', 'builder', 'architect'];

// Agent display names for @mentions
// Note: Monitor and Expert sub-agents have hidden: true so they won't appear in @ autocomplete,
// but they're still included here for programmatic invocation via Task tool
const AGENT_MENTIONS: Record<AgentRole, string> = {
lead: '@Agentuity Coder Lead',
scout: '@Agentuity Coder Scout',
builder: '@Agentuity Coder Builder',
architect: '@Agentuity Coder Architect',
reviewer: '@Agentuity Coder Reviewer',
memory: '@Agentuity Coder Memory',
expert: '@Agentuity Coder Expert',
'expert-backend': '@Agentuity Coder Expert Backend',
'expert-frontend': '@Agentuity Coder Expert Frontend',
'expert-ops': '@Agentuity Coder Expert Ops',
runner: '@Agentuity Coder Runner',
reasoner: '@Agentuity Coder Reasoner',
product: '@Agentuity Coder Product',
monitor: '@Agentuity Coder Monitor',
};

export async function createCoderPlugin(ctx: PluginInput): Promise<Hooks> {
ctx.client.app.log({
body: {
Expand Down Expand Up @@ -611,47 +591,6 @@ function createTools(backgroundManager: BackgroundManager): Hooks['tool'] {
// Use the schema from @opencode-ai/plugin's tool helper to avoid Zod version mismatches
const s = tool.schema;

const coderDelegate = tool({
description: `Delegate a task to a specialized Agentuity Coder agent.

Use this to:
- Scout: Explore codebase, find patterns, research documentation
- Builder: Implement features, write code, run tests (interactive work)
- Architect: Complex autonomous tasks, Cadence mode, deep reasoning (GPT Codex)
- Reviewer: Review changes, catch issues, apply fixes
- Memory: Store context, remember decisions across sessions
- Reasoner: Extract structured conclusions, resolve conflicts, surface corrections
- Expert: Get help with Agentuity CLI and cloud services
- Runner: Execute lint/build/test/typecheck/format commands, returns structured results
- Monitor: Watch background tasks and report when they complete`,
args: {
agent: s
.enum([
'scout',
'builder',
'architect',
'reviewer',
'memory',
'reasoner',
'expert',
'runner',
'product',
'monitor',
])
.describe('Which agent to delegate to'),
task: s.string().describe('Clear description of the task'),
context: s.string().optional().describe('Additional context from previous tasks'),
},
async execute(args) {
const mention = AGENT_MENTIONS[args.agent as AgentRole];
let prompt = `${mention}\n\n## Task\n${args.task}`;
if (args.context) {
prompt = `${mention}\n\n## Context\n${args.context}\n\n## Task\n${args.task}`;
}
return `To delegate this task, use the Task tool with this prompt:\n\n${prompt}\n\nThe ${args.agent} agent will handle this task.`;
},
});

const backgroundTask = tool({
description: `Launch a task to run in the background. Use this for parallel execution of multiple independent tasks.

Expand Down Expand Up @@ -926,7 +865,6 @@ Returns the public URL that can be copied and used anywhere.`,
});

return {
agentuity_coder_delegate: coderDelegate,
agentuity_background_task: backgroundTask,
agentuity_background_output: backgroundOutput,
agentuity_background_cancel: backgroundCancel,
Expand Down
90 changes: 0 additions & 90 deletions packages/opencode/src/tools/delegate.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/opencode/src/tools/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export { delegateTool, DelegateArgsSchema, type DelegateArgs } from './delegate';
export {
createBackgroundTools,
BackgroundTaskArgsSchema,
Expand Down
4 changes: 2 additions & 2 deletions packages/opencode/test/agents.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ describe('Agents', () => {

it('Architect agent has GPT Codex with xhigh reasoning', () => {
const architect = agents.architect;
expect(architect.defaultModel).toBe('openai/gpt-5.2-codex');
expect(architect.defaultModel).toBe('openai/gpt-5.3-codex');
expect(architect.reasoningEffort).toBe('xhigh');
expect(architect.temperature).toBe(0.1);
expect(architect.systemPrompt).toContain('Cadence');
Expand Down Expand Up @@ -149,7 +149,7 @@ describe('Agents', () => {
expect(agents.reasoner.mode).toBe('subagent');
expect(agents.reasoner.tools?.exclude).toContain('write');
expect(agents.reasoner.tools?.exclude).toContain('edit');
expect(agents.reasoner.tools?.exclude).toContain('task');
expect(agents.reasoner.tools?.exclude).not.toContain('task');
expect(agents.reasoner.defaultModel).toBe('openai/gpt-5.2');
expect(agents.reasoner.temperature).toBe(0.3);
});
Expand Down
6 changes: 3 additions & 3 deletions packages/opencode/test/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ describe('Config', () => {
describe('Model helpers', () => {
it('isOpenAIModel correctly identifies OpenAI models', () => {
expect(isOpenAIModel('openai/gpt-5.2')).toBe(true);
expect(isOpenAIModel('openai/gpt-5.2-codex')).toBe(true);
expect(isOpenAIModel('anthropic/claude-opus-4-5')).toBe(false);
expect(isOpenAIModel('openai/gpt-5.3-codex')).toBe(true);
expect(isOpenAIModel('anthropic/claude-opus-4-6')).toBe(false);
});

it('isAnthropicModel correctly identifies Anthropic models', () => {
expect(isAnthropicModel('anthropic/claude-opus-4-5')).toBe(true);
expect(isAnthropicModel('anthropic/claude-opus-4-6')).toBe(true);
expect(isAnthropicModel('anthropic/claude-haiku-4-5')).toBe(true);
expect(isAnthropicModel('openai/gpt-5.2')).toBe(false);
});
Expand Down