Role
Plan Mode lets the agent “figure out and write down intent” before executing. While active, the agent is in a read-only phase:- Only read-only tools plus 4 whitelisted tools work:
plan_enter/plan_write/plan_exit/todo_write(the shell can be opted in — see below). - Any other tool call is rejected immediately (the agent sees a “plan-mode denied” note).
- Exiting Plan Mode requires HITL confirmation (reusing the permission system’s ASK), so the model can’t unilaterally jump into execution.
todo_write and subagents, it noticeably reduces “improvise-then-break-things” outcomes on long tasks.
Opt-in
You can also call
enableTaskList() so that todos created during the plan phase show up as a small reminder before each reasoning step.
The three tools
plan_write is a dedicated write entry for Plan Mode — avoids the security risk of whitelisting the generic write_file (which would let the model write anywhere during plan).
Workflow
Any non-whitelisted tool call (e.g.write_file, or execute unless you opt in) during the plan phase is rejected immediately with something like:
Reading the outcome
Plan-mode entry is autonomous, so a run can end in four states.isPlanModeActive() == false alone is ambiguous — don’t treat it as success without checking whether planning actually happened:
To tell these apart programmatically, track whether
plan_enter / plan_write were called (e.g. from ToolCallStartEvent) alongside the final isPlanModeActive() and the plan file’s existence.
Allowing the shell during the plan phase (opt-in)
By default the shell tool (execute) is denied during the plan phase. The shell is dual-use: a single tool call can read (cat / ls / grep / git log) or mutate (rm / > / git commit / npm install), and Plan Mode decides what to permit purely by tool name — so it cannot tell a read invocation from a write one. Denying the shell keeps the read-only guarantee intact.
But shell access is often the most flexible way to investigate a codebase and produce a realistic plan. When you accept that trade-off, opt in:
executeis added to the plan-phase allow-list, so the model can investigate via the shell.- The plan banner gains an extra instruction telling the model to keep shell usage read-only (
cat/ls/grep/git log/diff/show/status) and not to run mutating commands until the plan is approved. - The dedicated file-editing tools (
write_file/edit_file) remain denied — they are the primary mutation path, so the read-only intent is still enforced for file writes.
Runtime permission switching (the “bypass” escape hatch)
Plan Mode is one specific phase switch. Underneath it, every session carries aPermissionMode that the permission engine evaluates against. You can flip that mode at runtime — for example to grant a deliberate, user-initiated “skip all permission prompts” toggle (similar to a YOLO / dangerous-skip switch in other coding tools):
setPermissionMode(...) preserves the session’s configured allow/deny/ask rules and working directories — only the mode changes — and rebuilds that session’s cached permission engine so the switch takes effect on the next call. An in-flight call keeps the engine it started with.
⚠ BYPASS disables all rule evaluation, so treat it as an explicit, per-session, opt-in action and prefer pairing it with a sandbox. To run unattended without prompts but with enforcement, use PermissionMode.DONT_ASK instead (ASK decisions become DENY rather than being auto-allowed).
Plan state is persisted
Plan Mode is runtime state and is auto-persisted along withAgentState — process restarts, node failovers, and cross-replica restores all bring back the plan phase. The plan file itself is written to plans/ in the workspace and goes through whichever filesystem mode you’ve configured (local / sandbox / remote KV), so it’s distributed-safe.
Programmatic enter/exit
When app code drives Plan Mode (e.g. an admin console button):agentscope-admin-spring-boot-starter, the admin HTTP API also exposes Plan Mode controls (POST /v1/admin/sessions/{id}:enter-plan-mode / :exit-plan-mode / GET /v1/admin/sessions/{id}/plan).
Interaction with subagents
⚠ Current known gap: subagents spawned viaagent_spawn during Plan Mode do not automatically inherit the read-only restriction. To restrict the child:
- Narrow
toolsin the child’s declaration to a read-only set, or - Also
enablePlanMode()on the child’s own builder and enter it explicitly
Interaction with todo_write
Plan Mode and todo_write (provided by core) are independent but commonly used together:
- Plan Mode — phase switch + plan file + HITL exit
todo_write— maintain a structured “what to do now” list during execution (whole-list replace; exactly onein_progress)
PLAN.md during the plan phase → plan_exit → in execution use todo_write to slice the PLAN into 5–8 todos → progress one at a time. Each reasoning step shows the agent a todos reminder to stay focused.
⚠ Don’t confuse with subagent background tasks (task_output / task_cancel / task_list) — that’s a different concept; see Subagent.
Viewing the task list
The task list lives inAgentState.tasksContext and is persisted automatically with every call(). To read it from application code:
agentscope-admin-spring-boot-starter, the admin REST API provides a ready-made endpoint:
blocks / blockedBy).
To observe task changes in real time through the event stream, listen for todo_write tool calls in streamEvents():
Related Pages
- Workspace —
plans/directory location - Subagent —
todo_write≠ subagent task; don’t confuse them - Architecture — where Plan Mode sits in the call() timeline