You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Switching agent/mode in the V2 ACP path (e.g. brainstormer → build) never updates the durable session agent, so the tool loop keeps materializing the previous agent's toolset. Switching from a read-only agent like brainstormer to build (our code mode) leaves write/edit tools unavailable — the model can't write code even though the UI shows build selected.
This is the tools/permissions counterpart to the (closed) #18620, which described the same root cause for the model. It lives in the V2 ACP adapter tracked by #35457.
Root cause
The mode switch updates only ACP-local, in-memory state and relies on a prompt field that the protocol drops. The durable session.agent — which drives tool materialization — is never changed:
ACP setSessionConfigOption / setSessionMode (packages/opencode/src/acp/service.ts:438-463) only call session.setMode(...), which writes to the in-memory ACP session store (packages/opencode/src/acp/session.ts:149). This is ACP-local, not durable.
On prompt, ACP puts agent: modeId into the SDK session.prompt payload (service.ts:516).
The session.prompt protocol endpoint payload has only id, prompt, delivery, resume — no agent field (packages/protocol/src/groups/session.ts:207-212). The agent value is silently dropped at the HTTP boundary.
The runner resolves agents.select(session.agent) (packages/core/src/session/runner/llm.ts:182) then tools.materialize(agent.info?.permissions) (llm.ts:203) from the durable session row, which was never updated. So the old agent's tools/permissions (and system prompt) persist.
The durable agent is only updated by the separate session.switchAgent endpoint (packages/server/src/handlers/session.ts:108; exercised in packages/sdk-next/test/embedded.test.ts:177), which ACP never calls.
Impact
Any agent/mode switch in the TUI/ACP is a no-op for tools, permissions, and system prompt. Most visible when switching away from a restricted agent (brainstormer) to build: write/edit tools stay unavailable and the model cannot modify files.
Steps to reproduce
Start a session in brainstormer mode.
Switch mode to build (code mode).
Ask it to write or edit a file.
Observe it cannot — the toolset is still brainstormer's read-only set.
Proposed fix
Fix A (targeted): In ACP's mode-switch handlers (setSessionConfigOption for configId === "mode" and setSessionMode), also call input.sdk.session.switchAgent({ sessionID, agent: modeId }) so the durable agent updates. Minimal, reuses the existing endpoint. (Mirrors the model fix approach discussed in ACP: session/set_mode does not activate the agent's configured model #18620.)
Fix B (broader): Thread an optional agent through the session.prompt protocol endpoint and store so a per-prompt agent emits session.next.agent.switched before admission. More invasive (protocol regen).
Description
Switching agent/mode in the V2 ACP path (e.g.
brainstormer→build) never updates the durable session agent, so the tool loop keeps materializing the previous agent's toolset. Switching from a read-only agent likebrainstormertobuild(our code mode) leaves write/edit tools unavailable — the model can't write code even though the UI showsbuildselected.This is the tools/permissions counterpart to the (closed) #18620, which described the same root cause for the model. It lives in the V2 ACP adapter tracked by #35457.
Root cause
The mode switch updates only ACP-local, in-memory state and relies on a prompt field that the protocol drops. The durable
session.agent— which drives tool materialization — is never changed:setSessionConfigOption/setSessionMode(packages/opencode/src/acp/service.ts:438-463) only callsession.setMode(...), which writes to the in-memory ACP session store (packages/opencode/src/acp/session.ts:149). This is ACP-local, not durable.agent: modeIdinto the SDKsession.promptpayload (service.ts:516).session.promptprotocol endpoint payload has onlyid,prompt,delivery,resume— noagentfield (packages/protocol/src/groups/session.ts:207-212). Theagentvalue is silently dropped at the HTTP boundary.agents.select(session.agent)(packages/core/src/session/runner/llm.ts:182) thentools.materialize(agent.info?.permissions)(llm.ts:203) from the durable session row, which was never updated. So the old agent's tools/permissions (and system prompt) persist.The durable agent is only updated by the separate
session.switchAgentendpoint (packages/server/src/handlers/session.ts:108; exercised inpackages/sdk-next/test/embedded.test.ts:177), which ACP never calls.Impact
Any agent/mode switch in the TUI/ACP is a no-op for tools, permissions, and system prompt. Most visible when switching away from a restricted agent (
brainstormer) tobuild: write/edit tools stay unavailable and the model cannot modify files.Steps to reproduce
brainstormermode.build(code mode).brainstormer's read-only set.Proposed fix
setSessionConfigOptionforconfigId === "mode"andsetSessionMode), also callinput.sdk.session.switchAgent({ sessionID, agent: modeId })so the durable agent updates. Minimal, reuses the existing endpoint. (Mirrors the model fix approach discussed in ACP: session/set_mode does not activate the agent's configured model #18620.)agentthrough thesession.promptprotocol endpoint and store so a per-prompt agent emitssession.next.agent.switchedbefore admission. More invasive (protocol regen).Related
acp/agent.ts).