- llm-retry: validate config before registering the projection unit; document the branded-retry-id zod cast; start stateVersion at 1. - agent-loop: register turnBoundary only after every config validation, so a rejected constructor leaves no unit behind; the defensive-cap test no longer needs fiber cleanup. - agent-instructions: keep newest-first per-scope change history so the latest visible change survives a surface replacement shadowing the newest one (restores the previous scan-visible semantics); add a regression test for the delete-after-shadow sequence. - tool-skill: keep catalog-message history so a shadowed newest catalog message still falls back to the latest visible digest. - session-query-sqlite: drop the unused required sessionProjections injection. - plan-mode: restore the command/done error-drop regression test and the cold-replay command/done fold; drop the inaccurate state-reference comment. - tool-todo: remove a stray blank line; document the turnBoundary reader contract on the projection type.
@deepseek-ai/dsh-plan-mode
English | 中文
Logged, per-agent plan collaboration state with deployment-owned guidance, direct /plan [message] entry and /plan off exit commands, and the reviewed exit_plan_mode exit. Plan mode is soft guidance; sandbox mode and approval policy enforce restrictions independently and do not read or write plan state.
Durable state
plan/mode ({ active: boolean }) is a log-only, whole-value-replace SessionEventMap member. The required plan projection unit folds committed mode, command settlement, and the mode at the latest request header, so resume, fork, and compaction recover the state directly from the session log.
ctx.planMode.set(agent, active) appends the standalone plan/mode event immediately when the agent is idle, because no in-turn pre-step runs before the next prompt. While the agent is running, it holds a pending selection for the next accepted in-turn pre-step. It returns which happened (committed/queued), a cancelled reversal, or a noop. get(agent) returns { active, pending? }, separating the logged state used to assemble the current step from a user's mid-turn selection. Initial and continuation pre-steps both apply pending selections; a same-step request-recovery retry reuses its frozen assembly and leaves the selection pending for the next pre-step. A changed user selection contributes one plugin-sourced user/message notice when the last logged request header described the other state (both commit paths).
Model and human interactions
While active, plan:policy renders the configured section. The plugin always registers exit_plan_mode, keeping tool schemas stable across the transition; its execute path accepts only active plan mode and leaves it only after an exact user approval through ctx.userQuestions.
The review question declares the plan-review presentation intent, naming Approve as the label that approves it, so a capable UI presents the plan as a decision instead of a generic question; the answer the tool reads is the same either way. A dismissed review — the user closing the request to speak instead — is reported to the model as such, telling it to stay in plan mode and wait for the message; every other review failure keeps the seam's own message.
When ctx.commands is composed, the package registers /plan [message] and reserves the exact argument off for direct exit. Bare /plan selects plan mode; any other non-empty argument selects it first and is then submitted through agent.steer(), so it becomes the next step's ordinary logged user message under plan guidance. /plan off selects inactive without sending model input; it also cancels a pending entry before plan mode reaches a request. The command declares input.images: composer image attachments ride the steered message ahead of its text block. Bare /plan with images steers an image-only user message, while /plan off with images returns a direct error before any mode change so the composer keeps them.
The Web client consumes the plugin-owned /plan command; other entry points may drive the same service directly without defining a second mode vocabulary.
Session projection
This package requires ctx.sessionProjections (@deepseek-ai/dsh-session-projection) and registers the plan projection unit. A command/run record named plan with recorded args starts a candidate target (off → inactive, anything else → active); its paired command/done retains a successful selection and drops an error; plan/mode commits the logged state and clears the retained selection; request/header records which mode assembled the latest request. Every other event returns the same state reference. Host logic reads the full state through stateOf(). The client view remains { active, pending }, where pending is true only while an unsettled or successful selection differs from the logged state. The key merges into SessionProjectionStateMap for host state and SessionProjectionMap for the client view; carriers serve the latter on the history tail page and the session/projection push frame.
Configuration
- id: plan-mode
name: '@deepseek-ai/dsh-plan-mode'
config:
section: |
You are in plan mode. Explore and design before presenting the complete
plan through exit_plan_mode.
section is required and non-empty. Unknown keys fail at load. The package does not accept arbitrary named modes, tool filters, sandbox settings, or approval policy.
Design: plan-specific collaboration state.
Model Experience
Plan policy system prompt
What the model sees
While plan mode is active, the model sees the deployment's exact section text at prompt order 50; inactive mode contributes no text.
Configuration example
You are in plan mode. Explore and design before presenting the complete plan through exit_plan_mode.
Token effect
Inactive mode adds no tokens; active mode adds the configured section to every request.
KV Cache effect
The section is stable within plan mode, but entering or leaving changes the system prompt from order 50 onward.
Human command
What the model sees
/plan, /plan off, and their terminal results stay outside model history. A non-empty suffix other than the exact off argument becomes one user message through agent.steer() after plan mode is selected: any admitted image attachments as leading image blocks, then the trimmed text block. Bare /plan with admitted images steers one user message containing only those image blocks. An active /plan off selection contributes the standard logged user-switch notice only when the last request header described plan mode; cancelling a pending entry contributes none because no request observed it.
Token effect
The optional message costs the same history tokens as submitting that content separately. Bare /plan without images and /plan off add none; bare /plan with images has the normal image-prompt cost. A narrated active exit adds the small retained switch notice.
KV Cache effect
The user block is append-only conversation growth. Entering or leaving plan mode changes the earlier policy section; a narrated exit notice is appended after the reusable request prefix.
Exit tool schema and review exchange
What the model sees
The exit_plan_mode schema remains available in both states; execution outside plan mode fails, while an approved in-mode review returns the canonical { approved: true } value and renders the existing confirmation text. Rejection remains a failed call carrying review feedback, and a dismissed review a failed call naming the user's takeover.
Token effect
The stable schema is paid according to ToolRuntime mode, and each plan argument and review result remains in conversation history.
KV Cache effect
Mode transitions do not change the tool catalog; plan arguments and review results extend the conversation normally.
Known Limitations and Deferred Work
- Plan mode guides rather than enforces; deployments that need enforced restrictions must configure sandbox and approval controls independently.
- A selection made after the turn's final accepted pre-step is lost if the process exits before another accepted in-turn pre-step, so the UI must reapply it.
- Forked agents inherit logged plan state, while newly spawned agents begin inactive; there is no creation-time plan option.
- A live child owned by another agent cannot open the
exit_plan_modereview. The failed call tells the child to include the unresolved decision in its final result; durable fork lineage alone does not prevent a session resumed as a runtime root from opening the review. - Only the Web UI has a specialized
plan-reviewrenderer; another interaction provider may present the same request through its generic option flow.