feat: add session-scoped plan/build mode#3140
Conversation
Plan mode lets the runtime filter tools to read-only ones and inject a
per-turn system reminder, so the agent drafts a plan instead of taking
actions. Build mode is the default and preserves today's behaviour.
The mode is a per-session property, persisted alongside the rest of the
session, and exposed via:
- POST /api/sessions { ..., mode }
- GET /api/sessions/:id -> { ..., mode }
- PATCH /api/sessions/:id/mode { mode }
Tool filtering is driven by the MCP-spec ReadOnlyHint annotation, so it
extends to user-added MCP tools without any per-tool config.
|
Question, why can't "plan mode" be implemented with a second agent that the user can select? |
I updated the PR description |
This is not true, you can always ctrl+number to switch to an agent |
|
@rumpl It could, but it scales poorly: every agent (built-in, community, user-authored) would need a hand-maintained "planner" twin kept in lockstep with its prompt, toolsets, hooks and sub-agents. Plan mode is effectively "every agent gets a free read-only twin", derived from each tool's MCP It also keeps you on the same agent under a different constraint, vs a sibling-agent switch (which has handoff cost: transfer event, possibly synthesised user prompt, separate conversation thread). |
|
Okay but... Not any agent can become a planner agent magically just by removing write tools. Its system prompt needs to be changed I would guess, also you are talking about ReadOnlyHint, but our builtin tools don't have it, do they? |
|
❌ PR Review Failed — The review agent encountered an error and could not complete the review. View logs. |
|
Two points: System prompt. It is changed per turn — ReadOnlyHint on built-ins. They do have it — 33 occurrences across every read-side tool:
Write-side tools ( On the sibling-agent alternative more broadly — beyond the points already in the description, it's painful operationally: every agent (built-in, community, user-authored) would need a hand-maintained planner twin kept in lockstep with its prompt, toolsets, hooks, and sub-agents. Two YAML surfaces per agent to keep in sync, every parent-agent change silently rots its planner sibling, and every third-party agent author has to do the same work. Plan mode collapses that to one knob across the fleet — including agents we don't author. |
|
❌ PR Review Failed — The review agent encountered an error and could not complete the review. View logs. |
|
It should cover most of #2788 |
Review —
|
Why
A planner sub-agent (e.g. the bundled
coderagent'splanner,pkg/config/builtin-agents/coder.yaml) puts planning under the agent's control. Plan mode puts it under the user's control: a session attribute the host (CLI, TUI, embedder) flips like a switch, and the model can't talk its way out of it.That distinction matters because:
coder's planner today still ships fullfilesystem(incl.write_file/edit_file); its read-only-ness is prompt-only. Plan mode strips every tool withoutAnnotations.ReadOnlyHintfrom what the model sees.ReadOnlyHintannotation, with no YAML to update.Additive: planner sub-agents stay the right answer when planning belongs in the agent's design; plan mode is the user-driven counterpart.
What
New per-session
Mode(builddefault,plan). In plan mode the runtime:Annotations.ReadOnlyHintfrom each turn's toolset (hard guarantee).<system-reminder>telling the model to plan, not act (so it doesn't waste turns trying tools that vanished).API:
PATCH /modeupdates the live in-memory session if a runtime is attached and persists to the store, so the next turn picks it up directly. Persistence is a newmode TEXTcolumn (migration 22); pre-existing rows normalize tobuildon read.Filter piggybacks on the existing
ExcludedToolsfilter site inruntime/loop.go; reminder rides the existing turn-start system-message splice.Scope
In: runtime filter + reminder (incl. harness), API + DTOs, persistence + migration, unit + HTTP tests.
Out (left to hosts): TUI/leantui UX, CLI flags, "exit plan mode" tool — hosts can PATCH the endpoint directly.