Skip to content
Merged
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
375 changes: 375 additions & 0 deletions packages/opencode/docs/analysis/agent-modes-configuration.md
Original file line number Diff line number Diff line change
@@ -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<string> {
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 |
Loading