From bce926b8c1cada6a34bbc683422a0eda03dadf93 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Jan 2026 14:42:37 +0000 Subject: [PATCH] docs: add agent modes and configuration documentation - Document agent mode types (primary, subagent, all) - Explain default agent configuration and resolution - Show how to disable built-in agents (build, plan) - Include custom agent creation examples - Add source code references throughout --- .../analysis/agent-modes-configuration.md | 375 ++++++++++++++++++ 1 file changed, 375 insertions(+) create mode 100644 packages/opencode/docs/analysis/agent-modes-configuration.md diff --git a/packages/opencode/docs/analysis/agent-modes-configuration.md b/packages/opencode/docs/analysis/agent-modes-configuration.md new file mode 100644 index 00000000000..3a41d94d235 --- /dev/null +++ b/packages/opencode/docs/analysis/agent-modes-configuration.md @@ -0,0 +1,375 @@ +# Agent Modes and Configuration + +This document describes the agent mode system in opencode, including how to configure default agents, create custom agents, and disable built-in agents. + +--- + +## Overview + +Agents in opencode are specialized AI personas with distinct capabilities, tool access, and permissions. The agent system supports three mode types that determine visibility and usage context. + +--- + +## Agent Modes + +Defined in [`src/agent/agent.ts:23`](../src/agent/agent.ts): + +```typescript +mode: z.enum(["subagent", "primary", "all"]) +``` + +### Mode Types + +| Mode | Description | Tab Cycling | Task Tool | +|------|-------------|-------------|-----------| +| `primary` | Main user-facing agents | Yes | No | +| `subagent` | Called via Task tool only | No | Yes | +| `all` | Functions in both roles | Yes | Yes | + +### Visibility Rules + +Agents appear in tab cycling if ([`src/agent/agent.ts:269`](../src/agent/agent.ts)): + +```typescript +const hasPrimaryAgents = Object.values(result).filter( + (a) => a.mode !== "subagent" && !a.hidden +).length > 0 +``` + +--- + +## Built-in Agents + +Defined in [`src/agent/agent.ts:117-198`](../src/agent/agent.ts): + +| Agent | Mode | Hidden | Description | +|-------|------|--------|-------------| +| `build` | primary | No | Default agent with full tool access | +| `plan` | primary | No | Read-only planning agent | +| `general` | subagent | Yes | General-purpose Task tool agent | +| `explore` | subagent | No | Fast codebase exploration | +| `compaction` | primary | Yes | Context compression (internal) | +| `title` | primary | Yes | Title generation (internal) | +| `summary` | primary | Yes | Summary generation (internal) | + +### Build Agent + +Full tool access with default permissions ([`src/agent/agent.ts:118-125`](../src/agent/agent.ts)): + +```typescript +build: { + name: "build", + tools: { ...defaultTools }, + options: {}, + permission: agentPermission, + mode: "primary", + native: true, +} +``` + +### Plan Agent + +Read-only mode with restricted bash commands ([`src/agent/agent.ts:126-135`](../src/agent/agent.ts)): + +```typescript +plan: { + name: "plan", + options: {}, + permission: planPermission, // Restricted permissions + tools: { ...defaultTools }, + mode: "primary", + native: true, +} +``` + +Plan mode permissions deny edits and only allow read-only bash commands ([`src/agent/agent.ts:71-115`](../src/agent/agent.ts)): + +```typescript +const planPermission = mergeAgentPermissions({ + edit: "deny", + bash: { + "git diff*": "allow", + "git log*": "allow", + "grep*": "allow", + "ls*": "allow", + // ... other read-only commands + "*": "ask", // Ask for anything else + }, + webfetch: "allow", +}, cfg.permission ?? {}) +``` + +--- + +## Default Agent + +### Configuration + +Set the default agent in `opencode.json` ([`src/agent/agent.ts:258-267`](../src/agent/agent.ts)): + +```json +{ + "default_agent": "build" +} +``` + +### Resolution Logic + +```typescript +const defaultName = cfg.default_agent ?? "build" +const defaultCandidate = result[defaultName] +if (defaultCandidate && defaultCandidate.mode !== "subagent") { + defaultCandidate.default = true +} else { + // Fall back to "build" if configured default is invalid + if (result["build"]) { + result["build"].default = true + } +} +``` + +### Retrieval + +Get the default agent name ([`src/agent/agent.ts:288-292`](../src/agent/agent.ts)): + +```typescript +export async function defaultAgent(): Promise { + const agents = await state() + const defaultCandidate = Object.values(agents).find((a) => a.default) + return defaultCandidate?.name ?? "build" +} +``` + +--- + +## Disabling Built-in Agents + +### Configuration + +Disable agents via `opencode.json` using the `disable` property ([`src/config/config.ts:399`](../src/config/config.ts)): + +```json +{ + "agent": { + "build": { + "disable": true + } + } +} +``` + +### Implementation + +Disabled agents are removed from the agent registry ([`src/agent/agent.ts:199-203`](../src/agent/agent.ts)): + +```typescript +for (const [key, value] of Object.entries(cfg.agent ?? {})) { + if (value.disable) { + delete result[key] + continue + } + // ... rest of agent configuration +} +``` + +### Validation + +At least one primary agent must exist ([`src/agent/agent.ts:269-275`](../src/agent/agent.ts)): + +```typescript +const hasPrimaryAgents = Object.values(result).filter( + (a) => a.mode !== "subagent" && !a.hidden +).length > 0 + +if (!hasPrimaryAgents) { + throw new Config.InvalidError({ + path: "config", + message: "No primary agents are available. Please configure at least one agent with mode 'primary' or 'all'.", + }) +} +``` + +--- + +## Creating Custom Agents + +### Via Configuration File + +Create `.opencode/agent/myagent.md`: + +```markdown +--- +description: "When to use this agent" +mode: "primary" +tools: + edit: true + bash: true +--- +Your custom system prompt here... +``` + +### Via Config JSON + +In `opencode.json`: + +```json +{ + "agent": { + "myagent": { + "description": "My custom coding assistant", + "mode": "primary", + "prompt": "You are a helpful assistant...", + "tools": { + "edit": true, + "bash": true + } + } + } +} +``` + +### Via CLI + +```bash +opencode agent create \ + --path .opencode/agent \ + --description "My custom agent" \ + --mode primary +``` + +### Default Mode for Custom Agents + +Custom agents default to `mode: "all"` if not specified ([`src/agent/agent.ts:205-213`](../src/agent/agent.ts)): + +```typescript +if (!item) + item = result[key] = { + name: key, + mode: "all", // Default mode + permission: agentPermission, + options: {}, + tools: {}, + native: false, + } +``` + +--- + +## Agent Switching + +### Plan to Build Transition + +When switching from Plan to Build, a reminder is injected ([`src/session/prompt.ts:1018-1028`](../src/session/prompt.ts)): + +```typescript +const wasPlan = input.messages.some( + (msg) => msg.info.role === "assistant" && msg.info.agent === "plan" +) +if (wasPlan && input.agent.name === "build") { + userMessage.parts.push({ + // ... injects build-switch.txt content + text: BUILD_SWITCH, + synthetic: true, + }) +} +``` + +The switch prompt ([`src/session/prompt/build-switch.txt`](../src/session/prompt/build-switch.txt)): + +``` +Your operational mode has changed from plan to build. +You are no longer in read-only mode. +You are permitted to make file changes, run shell commands, and utilize your arsenal of tools as needed. +``` + +### Plan Mode Reminder + +Plan mode injects a read-only constraint ([`src/session/prompt.ts:1007-1017`](../src/session/prompt.ts)): + +```typescript +if (input.agent.name === "plan") { + userMessage.parts.push({ + // ... injects plan.txt content + text: PROMPT_PLAN, + synthetic: true, + }) +} +``` + +--- + +## Agent Configuration Schema + +Full schema from [`src/config/config.ts:393-410`](../src/config/config.ts): + +```typescript +z.object({ + model: z.string().optional(), // "provider/model" format + temperature: z.number().optional(), + top_p: z.number().optional(), + prompt: z.string().optional(), // System prompt + tools: z.record(z.string(), z.boolean()).optional(), + disable: z.boolean().optional(), // Remove agent entirely + description: z.string().optional(), // When to use + mode: z.enum(["subagent", "primary", "all"]).optional(), + color: z.string().optional(), // Hex color for UI + maxSteps: z.number().int().positive().optional(), + permission: z.object({ + edit: Permission, + bash: z.record(z.string(), Permission), + skill: z.record(z.string(), Permission), + webfetch: Permission.optional(), + doom_loop: Permission.optional(), + external_directory: Permission.optional(), + }).optional(), +}) +``` + +--- + +## Example: Replace Build with Custom Agent + +```json +{ + "default_agent": "coder", + "agent": { + "build": { + "disable": true + }, + "plan": { + "disable": true + }, + "coder": { + "description": "Expert coding assistant", + "mode": "primary", + "prompt": "You are an expert software engineer...", + "tools": { + "edit": true, + "bash": true, + "read": true, + "glob": true, + "grep": true + } + } + } +} +``` + +This configuration: +1. Disables built-in `build` and `plan` agents +2. Creates a custom `coder` agent as primary +3. Sets `coder` as the default agent +4. Tab cycling will only show `coder` and other custom primary agents + +--- + +## Related Files + +| File | Purpose | +|------|---------| +| [`src/agent/agent.ts`](../src/agent/agent.ts) | Agent definitions and registry | +| [`src/config/config.ts`](../src/config/config.ts) | Configuration schema | +| [`src/session/prompt.ts`](../src/session/prompt.ts) | Prompt assembly and mode switching | +| [`src/session/prompt/plan.txt`](../src/session/prompt/plan.txt) | Plan mode constraint | +| [`src/session/prompt/build-switch.txt`](../src/session/prompt/build-switch.txt) | Plan-to-build transition | +| [`src/cli/cmd/tui/context/local.tsx`](../src/cli/cmd/tui/context/local.tsx) | TUI agent cycling |