ACP and headless mode does not respect default agent definition #6208

Closed
opened 2026-02-16 18:01:44 -05:00 by yindo · 0 comments
Owner

Originally created by @assagman on GitHub (Jan 15, 2026).

Originally assigned to: @rekram1-node on GitHub.

Description

The defaultAgent() function does not properly filter agents by their mode and visibility settings. This causes subagents (like explore) and hidden agents (like compaction) to be incorrectly selected as the default agent in ACP and headless (run) modes.

Root causes:

  1. Agent.defaultAgent() returns the first agent from state() without checking if it's a subagent or hidden
  2. ACP session init does not persist the resolved default mode, causing prompt() to use stale/wrong mode

Plugins

N/A

OpenCode version

0.0.0-dev-202601151329

Steps to reproduce

fresh config -> disable build agent -> set a custom default agent -> execute opencode run "echo back" -> it uses plan agent and tries to generate a plan

example jsonc config:

{
  "$schema": "https://opencode.ai/config.json",
  "default_agent": "yoda",
  "agent": {
    "build": {
      "disable": true,
    },
    "yoda": {
      "prompt": "say YODA! before every response.",
    },
  },
}

opencode run

current version output:

$ opencode run "echo back"
|  Write    .opencode/plans/1768489599174-witty-cabin.md

fixed version output:

$ bun run dev run "echo back"

YODA!
echo back

opencode acp

// acp-run.ts
import { spawn } from "bun"

const prompt = process.argv[3] || "echo back"

const testVersion = process.argv.find((arg) => arg.startsWith("--test-version="))?.split("=")[1] || "fixed"
console.log(`Using ACP version: ${testVersion}`)
const acpCommand =
  testVersion === "current"
    ? ["opencode", "acp"]
    : ["bun", "run", "--cwd", "./packages/opencode/", "--conditions=browser", "src/index.ts", "acp"]

const acp = spawn(acpCommand, {
  stdin: "pipe",
  stdout: "pipe",
  stderr: "ignore",
})

const send = (msg: object) => acp.stdin.write(JSON.stringify(msg) + "\n")

let id = 0
const rpc = (method: string, params: object) => send({ jsonrpc: "2.0", id: ++id, method, params })

// Initialize
rpc("initialize", { protocolVersion: 1, clientCapabilities: {} })

// Create session
rpc("session/new", { cwd: process.cwd(), mcpServers: [] })

// Read responses
const reader = acp.stdout.getReader()
const decoder = new TextDecoder()
let buffer = ""
let sessionId = ""

while (true) {
  const { done, value } = await reader.read()
  if (done) break

  buffer += decoder.decode(value)
  const lines = buffer.split("\n")
  buffer = lines.pop() || ""

  for (const line of lines) {
    if (!line) continue
    const msg = JSON.parse(line)

    // Get session ID from session/new response
    if (msg.result?.sessionId) {
      sessionId = msg.result.sessionId
      rpc("session/prompt", { sessionId, prompt: [{ type: "text", text: prompt }] })
    }

    // Print agent message chunks
    if (msg.params?.update?.sessionUpdate === "agent_message_chunk") {
      process.stdout.write(msg.params.update.content.text || "")
    }

    // Exit on completion
    if (msg.result?.stopReason === "end_turn") {
      console.log()
      process.exit(0)
    }
  }
}

In the project root, checked out to the fix branch in fix(agent): default agent selection in acp and headless modes #8678, execute this script in two different modes:

current opencode version, as-is:

[/path/to/opencode/fix-default-agent-selection]
$ bun acp-run.ts --test-version=current
Using ACP version: current

I'll help you understand the codebase and create a plan. Let me start by exploring the repository structure to understand what we're working with.

I'll help you understand the overall structure of this codebase. Let me start by exploring the repository structure and key files.

Now let me explore the main packages directory to understand the architecture:

...

fixed version:

[/path/to/opencode/fix-default-agent-selection]
$ bun acp-run.ts --test-version=fixed
Using ACP version: fixed

YODA! echo back%

Screenshot and/or share link

N/A

Operating System

macOS Tahoe 26.3 arm64

Terminal

ghostty 1.2.3
tmux next-3.5

Originally created by @assagman on GitHub (Jan 15, 2026). Originally assigned to: @rekram1-node on GitHub. ### Description The `defaultAgent()` function does not properly filter agents by their mode and visibility settings. This causes subagents (like `explore`) and hidden agents (like `compaction`) to be incorrectly selected as the default agent in ACP and headless (`run`) modes. **Root causes:** 1. `Agent.defaultAgent()` returns the first agent from `state()` without checking if it's a subagent or hidden 2. ACP session init does not persist the resolved default mode, causing `prompt()` to use stale/wrong mode ### Plugins N/A ### OpenCode version 0.0.0-dev-202601151329 ### Steps to reproduce fresh config -> disable build agent -> set a custom default agent -> execute opencode run "echo back" -> it uses plan agent and tries to generate a plan example jsonc config: ```jsonc { "$schema": "https://opencode.ai/config.json", "default_agent": "yoda", "agent": { "build": { "disable": true, }, "yoda": { "prompt": "say YODA! before every response.", }, }, } ``` #### opencode run current version output: ```sh $ opencode run "echo back" | Write .opencode/plans/1768489599174-witty-cabin.md ``` fixed version output: ```sh $ bun run dev run "echo back" YODA! echo back ``` #### opencode acp ```typescript // acp-run.ts import { spawn } from "bun" const prompt = process.argv[3] || "echo back" const testVersion = process.argv.find((arg) => arg.startsWith("--test-version="))?.split("=")[1] || "fixed" console.log(`Using ACP version: ${testVersion}`) const acpCommand = testVersion === "current" ? ["opencode", "acp"] : ["bun", "run", "--cwd", "./packages/opencode/", "--conditions=browser", "src/index.ts", "acp"] const acp = spawn(acpCommand, { stdin: "pipe", stdout: "pipe", stderr: "ignore", }) const send = (msg: object) => acp.stdin.write(JSON.stringify(msg) + "\n") let id = 0 const rpc = (method: string, params: object) => send({ jsonrpc: "2.0", id: ++id, method, params }) // Initialize rpc("initialize", { protocolVersion: 1, clientCapabilities: {} }) // Create session rpc("session/new", { cwd: process.cwd(), mcpServers: [] }) // Read responses const reader = acp.stdout.getReader() const decoder = new TextDecoder() let buffer = "" let sessionId = "" while (true) { const { done, value } = await reader.read() if (done) break buffer += decoder.decode(value) const lines = buffer.split("\n") buffer = lines.pop() || "" for (const line of lines) { if (!line) continue const msg = JSON.parse(line) // Get session ID from session/new response if (msg.result?.sessionId) { sessionId = msg.result.sessionId rpc("session/prompt", { sessionId, prompt: [{ type: "text", text: prompt }] }) } // Print agent message chunks if (msg.params?.update?.sessionUpdate === "agent_message_chunk") { process.stdout.write(msg.params.update.content.text || "") } // Exit on completion if (msg.result?.stopReason === "end_turn") { console.log() process.exit(0) } } } ``` In the project root, checked out to the fix branch in [fix(agent): default agent selection in acp and headless modes #8678](https://github.com/anomalyco/opencode/pull/8678), execute this script in two different modes: current opencode version, as-is: ```sh [/path/to/opencode/fix-default-agent-selection] $ bun acp-run.ts --test-version=current Using ACP version: current I'll help you understand the codebase and create a plan. Let me start by exploring the repository structure to understand what we're working with. I'll help you understand the overall structure of this codebase. Let me start by exploring the repository structure and key files. Now let me explore the main packages directory to understand the architecture: ... ``` fixed version: ```sh [/path/to/opencode/fix-default-agent-selection] $ bun acp-run.ts --test-version=fixed Using ACP version: fixed YODA! echo back% ``` ### Screenshot and/or share link N/A ### Operating System macOS Tahoe 26.3 arm64 ### Terminal ghostty 1.2.3 tmux next-3.5
yindo closed this issue 2026-02-16 18:01:45 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#6208