소스 검색

Merge latest #239 into #447

# Conflicts:
#	.agents/notes/implemented/feature/2026-07-07-plan-mode.md
#	docs/config-catalog.md
#	docs/cordis-catalog/services.md
#	packages/mode/README.md
#	packages/mode/mode/README.md
#	packages/mode/mode/src/index.ts
#	packages/mode/mode/tests/mode.spec.ts
Tianyi Cui 2 달 전
부모
커밋
06e4932984
78개의 변경된 파일과 261개의 추가작업 그리고 277개의 파일을 삭제
  1. 11 7
      .agents/notes/implemented/feature/2026-07-07-plan-mode.md
  2. 1 1
      docs/config-catalog.md
  3. 1 1
      docs/cordis-catalog/services.md
  4. 1 1
      docs/event-producer-consumer.md
  5. 2 1
      docs/module-graph.md
  6. 1 1
      docs/persistence-catalog.md
  7. 1 1
      examples/acp-agent/tests/goal-snapshots/goal-session/stdout.expected.jsonl
  8. 1 1
      examples/acp-agent/tests/snapshots/advanced-toolchain/stdout.expected.jsonl
  9. 1 1
      examples/acp-agent/tests/snapshots/bash-spill/session.jsonl
  10. 1 1
      examples/acp-agent/tests/snapshots/bash-spill/stdout.expected.jsonl
  11. 1 1
      examples/acp-agent/tests/snapshots/both-mode-turn/stdout.expected.jsonl
  12. 1 1
      examples/acp-agent/tests/snapshots/cancel-tool-calls/stdout.expected.jsonl
  13. 1 1
      examples/acp-agent/tests/snapshots/cancel/stdout.expected.jsonl
  14. 1 1
      examples/acp-agent/tests/snapshots/code-mode-turn/stdout.expected.jsonl
  15. 1 1
      examples/acp-agent/tests/snapshots/code-mode-workspace-context/stdout.expected.jsonl
  16. 1 1
      examples/acp-agent/tests/snapshots/config-options/stdout.expected.jsonl
  17. 1 1
      examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/stdout.expected.jsonl
  18. 1 1
      examples/acp-agent/tests/snapshots/error-finish/stdout.expected.jsonl
  19. 2 2
      examples/acp-agent/tests/snapshots/escalation-approved/session.jsonl
  20. 1 1
      examples/acp-agent/tests/snapshots/escalation-approved/stdout.expected.jsonl
  21. 2 2
      examples/acp-agent/tests/snapshots/escalation-rejected/session.jsonl
  22. 1 1
      examples/acp-agent/tests/snapshots/escalation-rejected/stdout.expected.jsonl
  23. 1 1
      examples/acp-agent/tests/snapshots/fs-edit/stdout.expected.jsonl
  24. 2 2
      examples/acp-agent/tests/snapshots/fs-escalation-approved/session.jsonl
  25. 1 1
      examples/acp-agent/tests/snapshots/fs-escalation-approved/stdout.expected.jsonl
  26. 1 1
      examples/acp-agent/tests/snapshots/fs-policy-reject/stdout.expected.jsonl
  27. 1 1
      examples/acp-agent/tests/snapshots/fs-read-window/stdout.expected.jsonl
  28. 1 1
      examples/acp-agent/tests/snapshots/fs-read/stdout.expected.jsonl
  29. 1 1
      examples/acp-agent/tests/snapshots/fs-terminal-card/stdout.expected.jsonl
  30. 1 1
      examples/acp-agent/tests/snapshots/fs-write-overwrite/stdout.expected.jsonl
  31. 1 1
      examples/acp-agent/tests/snapshots/fs-write/stdout.expected.jsonl
  32. 1 1
      examples/acp-agent/tests/snapshots/goal-command-status/stdout.expected.jsonl
  33. 1 1
      examples/acp-agent/tests/snapshots/handshake/stdout.expected.jsonl
  34. 1 1
      examples/acp-agent/tests/snapshots/hook-cc-posttool-block/stdout.expected.jsonl
  35. 1 1
      examples/acp-agent/tests/snapshots/hook-cc-posttool-context/stdout.expected.jsonl
  36. 2 2
      examples/acp-agent/tests/snapshots/hook-cc-pretool-ask/session.jsonl
  37. 1 1
      examples/acp-agent/tests/snapshots/hook-cc-pretool-ask/stdout.expected.jsonl
  38. 1 1
      examples/acp-agent/tests/snapshots/hook-cc-pretool-deny/stdout.expected.jsonl
  39. 1 1
      examples/acp-agent/tests/snapshots/hook-cc-promptsubmit-block/stdout.expected.jsonl
  40. 1 1
      examples/acp-agent/tests/snapshots/hook-cc-promptsubmit-context/stdout.expected.jsonl
  41. 1 1
      examples/acp-agent/tests/snapshots/hook-cc-stop-continue/stdout.expected.jsonl
  42. 1 1
      examples/acp-agent/tests/snapshots/hook-codex-posttool-block/stdout.expected.jsonl
  43. 1 1
      examples/acp-agent/tests/snapshots/hook-codex-posttool-context/stdout.expected.jsonl
  44. 1 1
      examples/acp-agent/tests/snapshots/hook-codex-pretool-block/stdout.expected.jsonl
  45. 1 1
      examples/acp-agent/tests/snapshots/hook-codex-promptsubmit-block/stdout.expected.jsonl
  46. 1 1
      examples/acp-agent/tests/snapshots/hook-codex-promptsubmit-context/stdout.expected.jsonl
  47. 1 1
      examples/acp-agent/tests/snapshots/hook-codex-stop-continue/stdout.expected.jsonl
  48. 1 1
      examples/acp-agent/tests/snapshots/model-switching/stdout.expected.jsonl
  49. 1 1
      examples/acp-agent/tests/snapshots/modes-advertise/stdout.expected.jsonl
  50. 1 1
      examples/acp-agent/tests/snapshots/multi-turn/stdout.expected.jsonl
  51. 1 1
      examples/acp-agent/tests/snapshots/parallel-tool-calls/stdout.expected.jsonl
  52. 1 1
      examples/acp-agent/tests/snapshots/permission-switching/stdout.expected.jsonl
  53. 1 1
      examples/acp-agent/tests/snapshots/plan-mode-reject/stdout.expected.jsonl
  54. 1 1
      examples/acp-agent/tests/snapshots/plan-mode/stdout.expected.jsonl
  55. 1 1
      examples/acp-agent/tests/snapshots/repeat-tool-guard/stdout.expected.jsonl
  56. 1 1
      examples/acp-agent/tests/snapshots/skill-load/stdout.expected.jsonl
  57. 1 1
      examples/acp-agent/tests/snapshots/subagent-depth-two-rejection/stdout.expected.jsonl
  58. 1 1
      examples/acp-agent/tests/snapshots/subagent-fork/stdout.expected.jsonl
  59. 1 1
      examples/acp-agent/tests/snapshots/subagent-mixed/stdout.expected.jsonl
  60. 1 1
      examples/acp-agent/tests/snapshots/subagent-multi/stdout.expected.jsonl
  61. 1 1
      examples/acp-agent/tests/snapshots/subagent-spawn/stdout.expected.jsonl
  62. 1 1
      examples/acp-agent/tests/snapshots/text-turn/stdout.expected.jsonl
  63. 1 1
      examples/acp-agent/tests/snapshots/todo-plan/stdout.expected.jsonl
  64. 1 1
      examples/acp-agent/tests/snapshots/tool-call-turn/stdout.expected.jsonl
  65. 1 1
      examples/acp-agent/tests/snapshots/workflow-run/stdout.expected.jsonl
  66. 1 1
      examples/acp-agent/tests/snapshots/workspace-context/stdout.expected.jsonl
  67. 1 1
      examples/acp-agent/tests/snapshots/workspace-edit/stdout.expected.jsonl
  68. 1 1
      examples/tui-agent/README.md
  69. 3 0
      examples/tui-agent/composition.md
  70. 6 0
      examples/tui-agent/cordis.yml
  71. 1 1
      packages/mode/README.md
  72. 8 4
      packages/mode/mode/README.md
  73. 7 0
      packages/mode/mode/package.json
  74. 41 66
      packages/mode/mode/src/index.ts
  75. 5 2
      packages/mode/mode/tests/integration.spec.ts
  76. 101 126
      packages/mode/mode/tests/mode.spec.ts
  77. 3 0
      packages/mode/mode/tsconfig.json
  78. 3 0
      pnpm-lock.yaml

+ 11 - 7
.agents/notes/implemented/feature/2026-07-07-plan-mode.md

@@ -22,7 +22,7 @@ The model leaves plan mode through the **`exit_plan_mode`** tool: its single arg
 
 ### A plan-mode session end to end
 
-The user switches the session to plan mode through the ACP mode picker, and from the next turn every request ships the configured plan guidance section. The `exit_plan_mode` schema was already present in default and remains byte-identical.
+The user switches the session to plan mode through the ACP mode picker or `/mode plan` in a terminal front door, and from the next turn every request ships the configured plan guidance section. The `exit_plan_mode` schema was already present in default and remains byte-identical.
 
 The model explores and designs; the section's guidance is what defers changes into the plan. The sandbox and approval knobs keep whatever the user set them to — a deployment (or user) that wants kernel-enforced read-only during planning pairs plan mode with the independent sandbox-mode option.
 
@@ -47,13 +47,17 @@ Mode definitions are validated plugin Config — per repo convention, changeable
 
 A definition is exactly `{ section }` — there is deliberately no per-mode tool list and no enforcement field ([FAQ](#faq)). `default` is reserved (the absence of policy) and rejected as a key; any unknown definition key — a `tools` list or an `access` cap included — fails validation at load; an unknown mode name fails loudly at `set()` time.
 
+### In the terminal
+
+Terminal front doors get `/mode` through the plugin-owned command registry (`@deepseek-ai/dsh-commands`): `dsh-mode` registers the command when a commands service is composed — bare `/mode` prints the current and available modes, `/mode <name>` records the switch (a command line, never sent to the model). The exit review prompts right in the terminal with no new machinery: it is an ordinary user-interaction question, so it rides the composed user-interaction provider's prompt queue that `ask_user_question` already uses.
+
 ### Over ACP
 
 The mode PICKER is this package's surface: `session/new`/`session/load` advertise `availableModes`/`currentModeId` from `ctx.modes` (consumed opportunistically via `ctx.get`, the `tool-bash` pattern), `session/set_mode` calls `set()` and notifies `current_mode_update` optimistically (the pending mode IS the user's selection; the logged `mode/set` follows at the boundary), and a `session/event` listener re-notifies on each logged flip that differs from the last sent. The exit tool reuses the user-interaction ACP provider's elicitation flow; its ACP mapping carries the review `detail` because Code Mode nested dispatches have no native plan card, while native calls may additionally stream the plan card. Individual environment knobs — sandbox mode, approval policy, the model — are NOT modes and belong to `session/set_config_option` ([FAQ](#faq)).
 
 ### For agent creators
 
-`ctx.modes` is the whole programmatic surface: `list()` returns the configured definitions plus the synthetic `default` entry (for pickers), `get(agent)` returns the folded mode plus any pending intent, and `set(agent, mode)` validates the name against `list()`'s vocabulary and records the boundary-applied intent — `default` is always a valid target, so exiting a mode is the same call as entering one. A creator seeds a child's initial mode through `AgentOptions.mode` (`AgentOptions` is merge-extensible; `dsh-mode` declares the optional field). There is no live `agent/*` mirror to subscribe: UIs read `mode/set` off `session/event`, per [event-domain semantics](../../implemented/architecture/2026-06-30-event-domain-semantics.md).
+`ctx.modes` is the whole programmatic surface: `list()` returns the configured definitions plus the synthetic `default` entry (for pickers), `get(agent)` returns the folded mode plus any pending intent, and `set(agent, mode)` validates the name against `list()`'s vocabulary and records the boundary-applied intent — `default` is always a valid target, so exiting a mode is the same call as entering one. There is no creation-time mode option — a caller selects through `set()` before the first turn, which flushes identically. There is no live `agent/*` mirror to subscribe: UIs read `mode/set` off `session/event`, per [event-domain semantics](../../implemented/architecture/2026-06-30-event-domain-semantics.md).
 
 ## Detailed design
 
@@ -82,7 +86,7 @@ The one-field shape is deliberate minimalism, not the final vocabulary: a per-to
 
 `foldMode(events)` is pure (exported for reconstructors and tests) and folds the append-only session log directly; `mode/set` is not a surface node, so compaction cannot shadow it. `set(agent, mode)` validates the name against `list()`'s vocabulary — the configured definitions plus the reserved `default`, which is rejected as a config KEY but always accepted as a `set()` TARGET — drops a no-op (target equals pending, else current), and otherwise records `{ mode, narrate }` in a `WeakMap` pending-intent slot. It cannot append immediately because [every session event is turn-enclosed](../../implemented/architecture/2026-06-15-turn-enclosure-invariant.md) and an idle agent has no open turn.
 
-Contained listeners on the loop's interception seams ([defensive patterns](../../../../docs/defensive-patterns.md): a policy plugin must not block a prompt or a turn) flush the pending intent as a `mode/set` append — `agent/prompt-submit` fires inside the just-opened turn before its first assembly, and `agent/turn-continuation` fires after an ordinary step closes before its successor. Automatic request recovery bypasses continuation, so a prepended `agent/request-error` wrapper delegates through the composed policy and asynchronous backoff, then flushes only a `retry` decision before the waterfall returns to the loop; an effect-scoped lifetime guard suppresses a captured wrapper that resumes after plugin disposal. All three paths sit outside tool execution and log publication (post-commit `session/event` observers are observe-only), so every step runs under the mode its assembly folded. When the flushed mode differs from the fold at the last `request/header`, the flush appends one coalesced `context/message` notice in the same frame ("The user switched this session to plan mode."); the user-visible narration cases are enumerated in the [FAQ](#faq). Seeding rides `agent/created`: `AgentOptions.mode` becomes a pending intent, so explicit options beat the logged baseline on create AND resume — the same precedence the call-config seed follows.
+Contained listeners on the loop's interception seams ([defensive patterns](../../../../docs/defensive-patterns.md): a policy plugin must not block a prompt or a turn) flush the pending intent as a `mode/set` append — `agent/prompt-submit` fires inside the just-opened turn before its first assembly, and `agent/turn-continuation` fires after an ordinary step closes before its successor. Automatic request recovery bypasses continuation, so a prepended `agent/request-error` wrapper delegates through the composed policy and asynchronous backoff, then flushes only a `retry` decision before the waterfall returns to the loop; an effect-scoped lifetime guard suppresses a captured wrapper that resumes after plugin disposal. All three paths sit outside tool execution and log publication (post-commit `session/event` observers are observe-only), so every step runs under the mode its assembly folded. When the flushed mode differs from the fold at the last `request/header`, the flush appends one coalesced `context/message` notice in the same frame ("The user switched this session to plan mode."); the user-visible narration cases are enumerated in the [FAQ](#faq).
 
 ### The soft layer: a computed section and a stable exit schema
 
@@ -98,7 +102,7 @@ Its [render intent](../../implemented/architecture/2026-07-02-tool-render-intent
 
 ### Dependencies and surfaces
 
-`dsh-mode` is one product package, not a capability-seam trio ([Alternatives considered](#alternatives-considered)): it peers on `cordis`, `dsh-session`, `dsh-agent`, `dsh-tools`, and `dsh-system-prompt`, injects `['tools', 'systemPrompt']`, reads `ctx.userInteraction` opportunistically at execute time (a type-only peer edge on `dsh-user-interaction`), and depends on no UI package. Beyond the `ctx.modes` call surface everything participates through listeners, so dropping the package gracefully removes modes rather than breaking a consumer. The ACP wire mapping is pinned in [High-level API](#over-acp); package-wise the bridge takes a type-only peer edge on `dsh-mode` and reads the service opportunistically, so a bridge without the plugin behaves exactly as today.
+`dsh-mode` is one product package, not a capability-seam trio ([Alternatives considered](#alternatives-considered)): it peers on `cordis`, `dsh-session`, `dsh-agent`, `dsh-tools`, and `dsh-system-prompt`, injects `['tools', 'systemPrompt']`, and reads `ctx.userInteraction` opportunistically at execute time (a type-only peer edge on `dsh-user-interaction`); its only UI-facing edges are optional type-only peers (`dsh-commands` for `/mode`). Beyond the `ctx.modes` call surface everything participates through listeners, so dropping the package gracefully removes modes rather than breaking a consumer. Terminal front doors need no mode-specific code: `dsh-mode` itself registers `/mode` on the command registry when one is composed (an optional type-only peer edge on `dsh-commands`), and the exit review rides the composed user-interaction provider's prompt queue. The ACP wire mapping is pinned in [High-level API](#over-acp); package-wise the bridge takes a type-only peer edge on `dsh-mode` and reads the service opportunistically, so a bridge without the plugin behaves exactly as today.
 
 ### The recorded scenario and the harness op
 
@@ -110,7 +114,7 @@ No new cordis event is declared (`mode/set` rides `session/event`; the listeners
 
 ## Deferred
 
-Each behind its own decision: subagent mode inheritance via a forwarded `AgentOptions.mode` (the option field itself ships), preset modes beyond `plan` (read-only, accept-edits), the idle-record primitive if pending-intent loss proves real, and — the big one — **effects self-declaration on tool definitions**: a per-tool read-only/mutating classification (the MCP `ToolAnnotations` vocabulary — `readOnlyHint`/`destructiveHint` — is the natural template, with its untrusted-hint caveat implying trust tiers). That item is what a general per-mode tool policy waits on: this Agent Note first shipped an interim per-mode name allowlist and removed it before release — a hand-maintained list mislabels the effects question, must track every tool a deployment composes, and rots silently as tools arrive — so mode-scoped tool availability (and per-tool `ask` policies) returns as a CONSUMER of declared effects, which is its restart trigger.
+Each behind its own decision: subagent mode inheritance via a forwarded creation-time mode option (removed as unconsumed; it returns with its first consumer), preset modes beyond `plan` (read-only, accept-edits), the idle-record primitive if pending-intent loss proves real, and — the big one — **effects self-declaration on tool definitions**: a per-tool read-only/mutating classification (the MCP `ToolAnnotations` vocabulary — `readOnlyHint`/`destructiveHint` — is the natural template, with its untrusted-hint caveat implying trust tiers). That item is what a general per-mode tool policy waits on: this Agent Note first shipped an interim per-mode name allowlist and removed it before release — a hand-maintained list mislabels the effects question, must track every tool a deployment composes, and rots silently as tools arrive — so mode-scoped tool availability (and per-tool `ask` policies) returns as a CONSUMER of declared effects, which is its restart trigger.
 
 The canonical [`examples/acp-agent`](../../../../examples/acp-agent/) composition mounts the mode and question-tool plugins on the full ACP coding server; plan mode is an additive session feature, not a second server profile. Its snapshot suite pins the plan-shaped initial header, a real read, scripted approval, stable tool schemas across the pure-removal header delta, a subsequent edit, rejection feedback, and the keyless mode wire. A self-skipping real-API smoke boots that same leaf, verifies the file before approving the review, and verifies the approved implementation afterward.
 
@@ -122,13 +126,13 @@ Behavioral clarifications of the chosen design; rejected designs live in [Altern
 
 **When is a mode change narrated to the model?** Only when the model-visible state actually changed: the flush compares the flushed mode against the fold at the last `request/header` and narrates once, coalesced. A net-zero flip sequence (plan then back, all before the boundary) narrates nothing; a tool-driven exit narrates through its own tool result instead; a mode set before the first turn narrates nothing — the section is the state statement. The principle is the in-flight env-state proposal's boundary narration: a silently flipped prompt surface leaves the transcript arguing from a state the header no longer has.
 
-**What happens on resume when the config no longer defines the folded mode?** One read-path rule closes the gap: a folded mode name the current config no longer defines behaves as the default mode plus one boundary notice naming the dropped definition — never a silent substitute restriction, never a bricked session. The notice itself is found in the durable log on resume, so later turns and process restarts do not duplicate it. `set()`'s loud validation covers only the write path; a resumed log answers to the config it finds.
+**What happens on resume when the config no longer defines the folded mode?** A folded mode name the current config no longer defines behaves as the default mode without a notice, so the session neither gains a substitute restriction nor becomes unusable. `set()`'s loud validation covers only the write path; a resumed log answers to the config it finds.
 
 **What if a deployment composes no user-interaction provider?** Plan mode stays safe but manual: `ctx.userInteraction.ask()` throws `NO_PROVIDER` (and an absent seam never resolves at all), the tool returns the corrective `isError`, and the exit degrades to the user toggling modes — never to an unreviewed exit. The mode section tells the model to present its plan through `exit_plan_mode` — and to ask the user in prose if that fails — so it keeps presenting instead of stalling.
 
 **Why is there no per-mode tool allowlist?** Because "which tools are safe in a planning mode" is a property of each TOOL (its effects), not of the mode — a per-mode name list re-declares that fact in the wrong home, must enumerate every tool the deployment composes (MCP servers included), and rots silently as tools arrive. Until tool definitions declare their effects ([Deferred](#deferred), where the removed interim allowlist is archived with its restart trigger), a mode restrains by its section and the exit review; the exposure is an accepted cost ([Consequences](#consequences)).
 
-**Do subagents inherit the parent's mode?** A fork child inherits for free — the parent's `mode/set` is inside the seeded prefix. A spawn child starts in the default mode unless its creator seeds `AgentOptions.mode`; automatic forwarding by subagent providers is deferred ([Deferred](#deferred)).
+**Do subagents inherit the parent's mode?** A fork child inherits for free — the parent's `mode/set` is inside the seeded prefix. A spawn child starts in the default mode; a creation-time mode option and automatic forwarding by subagent providers are deferred together ([Deferred](#deferred)).
 
 **How does plan mode relate to the sandbox's read-only mode?** They are separate axes that never touch: the mode is the collaboration stance (a `mode/set` fold), the sandbox mode is an enforcement knob (a `bash/sandbox-mode` fold, [the sandbox Agent Note](2026-07-06-sandbox.md)) — plan mode neither reads nor caps it, exactly as Codex keeps its Plan/Default presets separate from its sandbox and approval settings. A user who wants kernel-enforced read-only while planning sets both: flip the mode picker AND the sandbox-mode option, in either order; each switch changes only its own fold, so there is no interference and no restore step to crash out of. The log attributes each axis to its own event — the stance to `mode/set`, the confinement to `bash/sandbox-mode`.
 

+ 1 - 1
docs/config-catalog.md

@@ -696,7 +696,7 @@ export interface ModeDefinition {
 }
 ```
 
-Source: [`packages/mode/mode/src/index.ts:103`](../packages/mode/mode/src/index.ts)
+Source: [`packages/mode/mode/src/index.ts:95`](../packages/mode/mode/src/index.ts)
 
 ## `@deepseek-ai/dsh-permission`
 

+ 1 - 1
docs/cordis-catalog/services.md

@@ -698,7 +698,7 @@ set(agent: Agent, mode: string): void
 
 Types: [Agent](../core-data-structures/core.md)
 
-Source: [`packages/mode/mode/src/index.ts:217`](../../packages/mode/mode/src/index.ts)
+Source: [`packages/mode/mode/src/index.ts:204`](../../packages/mode/mode/src/index.ts)
 
 ## `ctx.permission` — `PermissionService`
 

+ 1 - 1
docs/event-producer-consumer.md

@@ -9,7 +9,7 @@ This matrix shows which packages dispatch each harness-owned event and which pac
 | --- | --- | --- | --- | --- |
 | `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:353`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | [`tui`](../packages/ui/tui) |
 | `agent/cancel-requested` | `emit` | [`packages/core/agent/src/types.ts:191`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-session`](../packages/goal/goal-session) |
-| `agent/created` | `emit` | [`packages/core/agent/src/types.ts:153`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`goal-session`](../packages/goal/goal-session), [`mode`](../packages/mode/mode), [`tui`](../packages/ui/tui) |
+| `agent/created` | `emit` | [`packages/core/agent/src/types.ts:153`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`goal-session`](../packages/goal/goal-session), [`tui`](../packages/ui/tui) |
 | `agent/disposed` | `emit` | [`packages/core/agent/src/types.ts:162`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), [`goal-session`](../packages/goal/goal-session), [`tui`](../packages/ui/tui) |
 | `agent/error` | `emit` | [`packages/core/agent/src/types.ts:328`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-session`](../packages/goal/goal-session), [`tui`](../packages/ui/tui) |
 | `agent/post-step` | `serial` | [`packages/core/agent/src/types.ts:280`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`compact-basic`](../packages/compact/compact-basic) |

+ 2 - 1
docs/module-graph.md

@@ -357,6 +357,7 @@ flowchart TD
   pkg_tool_todo --> pkg_session
   pkg_tool_todo --> pkg_tools
   pkg_mode --> pkg_agent
+  pkg_mode --> pkg_commands
   pkg_mode --> pkg_session
   pkg_mode --> pkg_system_prompt
   pkg_mode --> pkg_tools
@@ -595,7 +596,7 @@ flowchart TD
 | [`spill-policy`](../packages/spill/spill-policy) | `spill` | [`llm`](../packages/llm/llm), [`retention`](../packages/util/retention), [`session`](../packages/core/session), [`spill`](../packages/spill/spill), [`tools`](../packages/core/tools) |
 | [`timeout-policy`](../packages/timeout/timeout-policy) | `timeout` | [`llm`](../packages/llm/llm), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
 | [`tool-todo`](../packages/todo/tool-todo) | `todo` | [`agent`](../packages/core/agent), [`session`](../packages/core/session), [`tools`](../packages/core/tools) |
-| [`mode`](../packages/mode/mode) | `mode` | [`agent`](../packages/core/agent), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-interaction`](../packages/ui/user-interaction) |
+| [`mode`](../packages/mode/mode) | `mode` | [`agent`](../packages/core/agent), [`commands`](../packages/ui/commands), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-interaction`](../packages/ui/user-interaction) |
 | [`tool-cordis`](../packages/cordis/tool-cordis) | `cordis` | [`scope`](../packages/core/scope), [`tools`](../packages/core/tools) |
 | [`hooks-codex`](../packages/hooks/hooks-codex) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`tools`](../packages/core/tools) |
 | [`agent-loop-testkit`](../packages/support/agent-loop-testkit) | `support` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |

+ 1 - 1
docs/persistence-catalog.md

@@ -325,7 +325,7 @@ Source: [`packages/llm/llm-retry/src/index.ts:18`](../packages/llm/llm-retry/src
 'mode/set': { mode: string }
 ```
 
-Source: [`packages/mode/mode/src/index.ts:49`](../packages/mode/mode/src/index.ts)
+Source: [`packages/mode/mode/src/index.ts:52`](../packages/mode/mode/src/index.ts)
 
 ### `permission/*`
 

+ 1 - 1
examples/acp-agent/tests/goal-snapshots/goal-session/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call","toolCallId":"call_goal_create","title":"Create goal","kind":"other","status":"in_progress","rawInput":"Finish the ACP goal-session snapshot proof"}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"call_goal_create","status":"completed","content":[{"type":"content","content":{"type":"text","text":"{\"goal\":{\"id\":\"goal-{{sessionId}}\",\"revision\":1,\"objective\":\"Finish the ACP goal-session snapshot proof\",\"phase\":\"active\",\"roundsStarted\":0,\"maxGoalRounds\":2},\"activation\":\"armed\"}"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call","toolCallId":"call_goal_get","title":"Read current goal","kind":"read","status":"in_progress"}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/advanced-toolchain/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call","toolCallId":"advanced-mount","title":"Mount plugin into live cordis runtime","kind":"execute","status":"in_progress","rawInput":{"code":"return { name: 'snapshot-marker', apply() {} }"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"advanced-mount","status":"completed","content":[{"type":"content","content":{"type":"text","text":"mounted dyn-1 (plugin \"snapshot-marker\", state: active)"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call","toolCallId":"advanced-code","title":"return await tools.cordis_inspect({ what: 'dynamic' })","kind":"execute","status":"in_progress","rawInput":"return await tools.cordis_inspect({ what: 'dynamic' })"}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/bash-spill/session.jsonl

@@ -10,7 +10,7 @@
 {"type":"assistant/chunk","seq":8,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
 {"type":"assistant/message","seq":9,"time":0,"data":{"turn":1,"step":1,"content":[{"type":"tool-call","id":"call_spill","name":"bash","arguments":"{\"command\":\"node -e \\\"process.stdout.write('SPILL_START-' + 'x'.repeat(2000) + '-SPILL_END')\\\"\",\"description\":\"Print large deterministic output\"}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[4,5,6,7,8],"surfaceOp":"append"}
 {"type":"tool/call","seq":10,"time":0,"data":{"turn":1,"step":1,"callId":"call_spill","name":"bash","arguments":"{\"command\":\"node -e \\\"process.stdout.write('SPILL_START-' + 'x'.repeat(2000) + '-SPILL_END')\\\"\",\"description\":\"Print large deterministic output\"}"}}
-{"type":"tool/result","seq":11,"time":0,"data":{"turn":1,"step":1,"callId":"call_spill","content":[{"type":"text","text":"SPILL_START-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-SPILL_END\n\n(Omitted 1417 bytes. Full formatted result stored at: /tmp/dsh-acp-snap-ee77dff02/session-d279d010ae1d/aa6b473ddc0e-bash.txt. Use read with offset/limit, or grep this path to search within it.)"}],"isError":false},"sourceEventSeqs":[10],"surfaceOp":"append"}
+{"type":"tool/result","seq":11,"time":0,"data":{"turn":1,"step":1,"callId":"call_spill","content":[{"type":"text","text":"SPILL_START-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-SPILL_END\n\n(Omitted 1417 bytes. Full formatted result stored at: /tmp/dsh-acp-snap-ee77dff02/session-9fdaa5e0f66a/b8ac51f6d46e-bash.txt. Use read with offset/limit, or grep this path to search within it.)"}],"isError":false},"sourceEventSeqs":[10],"surfaceOp":"append"}
 {"type":"step/end","seq":12,"time":0,"data":{"turn":1,"step":1}}
 {"type":"step/start","seq":13,"time":0,"data":{"turn":1,"step":2}}
 {"type":"assistant/chunk","seq":14,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/bash-spill/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call","toolCallId":"call_spill","title":"node -e \"process.stdout.write('SPILL_START-' + 'x'.repeat(2000) + '-SPILL_END')\"","kind":"execute","status":"in_progress","rawInput":"node -e \"process.stdout.write('SPILL_START-' + 'x'.repeat(2000) + '-SPILL_END')\"","content":[{"type":"content","content":{"type":"text","text":"Print large deterministic output"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"call_spill","status":"completed","content":[{"type":"content","content":{"type":"text","text":"```console\nSPILL_START-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-SPILL_END\n\n(Omitted 1417 bytes. Full formatted result stored at: {{spillLocator:bash.txt}}. Use read with offset/limit, or grep this path to search within it.)\n```"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"DONE"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/both-mode-turn/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/cancel-tool-calls/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call","toolCallId":"call_wait","title":"node -e \"setInterval(() => {}, 1000)\"","kind":"execute","status":"in_progress","rawInput":"node -e \"setInterval(() => {}, 1000)\"","content":[{"type":"content","content":{"type":"text","text":"Wait until cancellation"}}]}}}
 {"jsonrpc":"2.0","id":3,"result":{"stopReason":"cancelled"}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"call_wait","status":"failed","content":[{"type":"content","content":{"type":"text","text":"```console\nError: command aborted\n```"}}]}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/cancel/stdout.expected.jsonl

@@ -1,5 +1,5 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"partial"}}}}
 {"jsonrpc":"2.0","id":3,"result":{"stopReason":"cancelled"}}

+ 1 - 1
examples/acp-agent/tests/snapshots/code-mode-turn/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/code-mode-workspace-context/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/config-options/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","id":3,"result":{"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"workspace-write","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
 {"jsonrpc":"2.0","id":4,"result":{"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
 {"jsonrpc":"2.0","id":5,"error":{"code":-32602,"message":"Invalid params: unknown permission value \"plan\""}}

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1 - 1
examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/stdout.expected.jsonl


+ 1 - 1
examples/acp-agent/tests/snapshots/error-finish/stdout.expected.jsonl

@@ -1,5 +1,5 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"\n\n[Model attempt failed; any partial output above is discarded: simulated provider error (HTTP 401)]\n\n"}}}}
 {"jsonrpc":"2.0","id":3,"error":{"code":-32603,"message":"Internal error: turn failed: simulated provider error (HTTP 401)"}}

+ 2 - 2
examples/acp-agent/tests/snapshots/escalation-approved/session.jsonl

@@ -131,8 +131,8 @@
 {"type":"assistant/chunk","seq":129,"time":1783962245385,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
 {"type":"assistant/message","seq":130,"time":1783962245385,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to run a command with sandbox_permissions set to danger-full-access, no prior run needed, justified as instructed."},{"type":"tool-call","id":"call_00_d0sAHpJ9mYOJi0z7KNy30441","name":"bash","arguments":"{\"command\": \"printf 'escalated\\\\n' > /tmp/dsh-escalated.txt && cat /tmp/dsh-escalated.txt && rm /tmp/dsh-escalated.txt\", \"description\": \"Write file outside workspace and verify\", \"sandbox_permissions\": \"danger-full-access\", \"justification\": \"the user asked to write a file outside the workspace\"}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":1501,"outputTokens":174,"cacheReadTokens":0,"reasoningTokens":28}},"sourceEventSeqs":[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129],"surfaceOp":"append"}
 {"type":"tool/call","seq":131,"time":1783962245385,"data":{"turn":1,"step":1,"callId":"call_00_d0sAHpJ9mYOJi0z7KNy30441","name":"bash","arguments":"{\"command\": \"printf 'escalated\\\\n' > /tmp/dsh-escalated.txt && cat /tmp/dsh-escalated.txt && rm /tmp/dsh-escalated.txt\", \"description\": \"Write file outside workspace and verify\", \"sandbox_permissions\": \"danger-full-access\", \"justification\": \"the user asked to write a file outside the workspace\"}"}}
-{"type":"approval/asked","seq":132,"time":1783962245386,"data":{"id":"660a6fa6-fb5a-422a-890d-7f9266fbe6f3","toolName":"bash","callId":"call_00_d0sAHpJ9mYOJi0z7KNy30441","reason":"escalate sandbox to danger-full-access: the user asked to write a file outside the workspace"}}
-{"type":"approval/decided","seq":133,"time":1783962245387,"data":{"id":"660a6fa6-fb5a-422a-890d-7f9266fbe6f3","outcome":"allowed-once"}}
+{"type":"approval/asked","seq":132,"time":1783962245386,"data":{"id":"8d80d7cf-d8d7-4358-82a3-85c9d8418ae3","toolName":"bash","callId":"call_00_d0sAHpJ9mYOJi0z7KNy30441","reason":"escalate sandbox to danger-full-access: the user asked to write a file outside the workspace"}}
+{"type":"approval/decided","seq":133,"time":1783962245387,"data":{"id":"8d80d7cf-d8d7-4358-82a3-85c9d8418ae3","outcome":"allowed-once"}}
 {"type":"tool/result","seq":134,"time":1783962245399,"data":{"turn":1,"step":1,"callId":"call_00_d0sAHpJ9mYOJi0z7KNy30441","content":[{"type":"text","text":"escalated\n"}],"isError":false},"sourceEventSeqs":[131],"surfaceOp":"append"}
 {"type":"step/end","seq":135,"time":1783962245400,"data":{"turn":1,"step":1}}
 {"type":"step/start","seq":136,"time":1783962245400,"data":{"turn":1,"step":2}}

+ 1 - 1
examples/acp-agent/tests/snapshots/escalation-approved/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","id":3,"result":{"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"workspace-write","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}

+ 2 - 2
examples/acp-agent/tests/snapshots/escalation-rejected/session.jsonl

@@ -155,8 +155,8 @@
 {"type":"assistant/chunk","seq":153,"time":1783962246274,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
 {"type":"assistant/message","seq":154,"time":1783962246274,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to run a specific command with `sandbox_permissions` set to `danger-full-access` and a specific justification. They explicitly said NOT to run it without sandbox_permissions first. Let me do exactly that."},{"type":"tool-call","id":"call_00_WB1vnPomi8yr6MlcFKTj7912","name":"bash","arguments":"{\"command\": \"printf 'escalated\\\\n' > /tmp/dsh-escalated.txt && cat /tmp/dsh-escalated.txt && rm /tmp/dsh-escalated.txt\", \"description\": \"Write to /tmp and verify, then clean up\", \"sandbox_permissions\": \"danger-full-access\", \"justification\": \"the user asked to write a file outside the workspace\"}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":1509,"outputTokens":198,"cacheReadTokens":0,"reasoningTokens":48}},"sourceEventSeqs":[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153],"surfaceOp":"append"}
 {"type":"tool/call","seq":155,"time":1783962246274,"data":{"turn":1,"step":1,"callId":"call_00_WB1vnPomi8yr6MlcFKTj7912","name":"bash","arguments":"{\"command\": \"printf 'escalated\\\\n' > /tmp/dsh-escalated.txt && cat /tmp/dsh-escalated.txt && rm /tmp/dsh-escalated.txt\", \"description\": \"Write to /tmp and verify, then clean up\", \"sandbox_permissions\": \"danger-full-access\", \"justification\": \"the user asked to write a file outside the workspace\"}"}}
-{"type":"approval/asked","seq":156,"time":1783962246275,"data":{"id":"64c0293f-bc4a-4ff9-bfd3-10256aae3127","toolName":"bash","callId":"call_00_WB1vnPomi8yr6MlcFKTj7912","reason":"escalate sandbox to danger-full-access: the user asked to write a file outside the workspace"}}
-{"type":"approval/decided","seq":157,"time":1783962246275,"data":{"id":"64c0293f-bc4a-4ff9-bfd3-10256aae3127","outcome":"rejected"}}
+{"type":"approval/asked","seq":156,"time":1783962246275,"data":{"id":"79ce231e-95f3-4d9e-a018-a92a03580513","toolName":"bash","callId":"call_00_WB1vnPomi8yr6MlcFKTj7912","reason":"escalate sandbox to danger-full-access: the user asked to write a file outside the workspace"}}
+{"type":"approval/decided","seq":157,"time":1783962246275,"data":{"id":"79ce231e-95f3-4d9e-a018-a92a03580513","outcome":"rejected"}}
 {"type":"tool/result","seq":158,"time":1783962246275,"data":{"turn":1,"step":1,"callId":"call_00_WB1vnPomi8yr6MlcFKTj7912","content":[{"type":"text","text":"Error: the user rejected escalating this command to \"danger-full-access\""}],"isError":true},"sourceEventSeqs":[155],"surfaceOp":"append"}
 {"type":"step/end","seq":159,"time":1783962246276,"data":{"turn":1,"step":1}}
 {"type":"step/start","seq":160,"time":1783962246276,"data":{"turn":1,"step":2}}

+ 1 - 1
examples/acp-agent/tests/snapshots/escalation-rejected/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","id":3,"result":{"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"workspace-write","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/fs-edit/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 2 - 2
examples/acp-agent/tests/snapshots/fs-escalation-approved/session.jsonl

@@ -89,8 +89,8 @@
 {"type":"assistant/chunk","seq":87,"time":1784045703776,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
 {"type":"assistant/message","seq":88,"time":1784045703780,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to create a file using the write tool with sandbox_permissions. Let me do that."},{"type":"tool-call","id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","arguments":"{\"file_path\": \"escalated.md\", \"content\": \"escalated\", \"sandbox_permissions\": \"danger-full-access\", \"justification\": \"the user asked to escalate this write\"}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":3871,"outputTokens":132,"cacheReadTokens":0,"reasoningTokens":23}},"sourceEventSeqs":[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87],"surfaceOp":"append"}
 {"type":"tool/call","seq":89,"time":1784045703780,"data":{"turn":1,"step":1,"callId":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","arguments":"{\"file_path\": \"escalated.md\", \"content\": \"escalated\", \"sandbox_permissions\": \"danger-full-access\", \"justification\": \"the user asked to escalate this write\"}"}}
-{"type":"approval/asked","seq":90,"time":1784045703782,"data":{"id":"a7cee570-890b-424a-b997-60b87cd2fdab","toolName":"write","callId":"call_00_Fnymmavpr4klMDy4Fdej3227","reason":"escalate sandbox to danger-full-access: the user asked to escalate this write"}}
-{"type":"approval/decided","seq":91,"time":1784045703786,"data":{"id":"a7cee570-890b-424a-b997-60b87cd2fdab","outcome":"allowed-once"}}
+{"type":"approval/asked","seq":90,"time":1784045703782,"data":{"id":"b6f67833-a7b4-49c9-964a-26f139334ce6","toolName":"write","callId":"call_00_Fnymmavpr4klMDy4Fdej3227","reason":"escalate sandbox to danger-full-access: the user asked to escalate this write"}}
+{"type":"approval/decided","seq":91,"time":1784045703786,"data":{"id":"b6f67833-a7b4-49c9-964a-26f139334ce6","outcome":"allowed-once"}}
 {"type":"tool/result","seq":92,"time":1784045703798,"data":{"turn":1,"step":1,"callId":"call_00_Fnymmavpr4klMDy4Fdej3227","content":[{"type":"text","text":"<path>/var/folders/2g/b32ct0qn1d728l_v6tdkjytr0000gn/T/acp-snap-cwd-vmEGzd/escalated.md</path>\n<type>file</type>\n<content>\nCreated file\n</content>"}],"isError":false},"sourceEventSeqs":[89],"surfaceOp":"append"}
 {"type":"step/end","seq":93,"time":1784045703798,"data":{"turn":1,"step":1}}
 {"type":"step/start","seq":94,"time":1784045703799,"data":{"turn":1,"step":2}}

+ 1 - 1
examples/acp-agent/tests/snapshots/fs-escalation-approved/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","id":3,"result":{"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"workspace-write","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/fs-policy-reject/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/fs-read-window/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/fs-read/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/fs-terminal-card/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/fs-write-overwrite/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/fs-write/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/goal-command-status/stdout.expected.jsonl

@@ -1,5 +1,5 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"No goal is currently set.\nUsage: /goal [<objective>|clear|edit <objective>|pause|resume]"}}}}
 {"jsonrpc":"2.0","id":3,"result":{"stopReason":"end_turn"}}

+ 1 - 1
examples/acp-agent/tests/snapshots/handshake/stdout.expected.jsonl

@@ -1,3 +1,3 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/hook-cc-posttool-block/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/hook-cc-posttool-context/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 2 - 2
examples/acp-agent/tests/snapshots/hook-cc-pretool-ask/session.jsonl

@@ -55,8 +55,8 @@
 {"type":"tool/call","seq":53,"time":1783352172557,"data":{"turn":1,"step":1,"callId":"call_00_6k0oGSliVHxGSgqBmMEO4311","name":"bash","arguments":"{\"command\": \"echo HELLO\", \"description\": \"Echo HELLO\"}"}}
 {"type":"hook/invoked","seq":54,"time":1783352172558,"data":{"turn":1,"point":"PreToolUse","dialect":"claude","handlerId":"claude:PreToolUse:1","matcher":"bash"}}
 {"type":"hook/result","seq":55,"time":1783352172573,"data":{"turn":1,"point":"PreToolUse","handlerId":"claude:PreToolUse:1","decision":"ask","exitCode":0,"durationMs":14.113374999999905}}
-{"type":"approval/asked","seq":56,"time":1783962235813,"data":{"id":"759f9b9e-4137-41ea-ac4b-2d4ab50737c8","toolName":"bash","callId":"call_00_6k0oGSliVHxGSgqBmMEO4311","reason":"bash requires manual approval in this session"}}
-{"type":"approval/decided","seq":57,"time":1783962235813,"data":{"id":"759f9b9e-4137-41ea-ac4b-2d4ab50737c8","outcome":"rejected"}}
+{"type":"approval/asked","seq":56,"time":1783962235813,"data":{"id":"33cdadc2-5899-4bed-8763-39fcdf6dff83","toolName":"bash","callId":"call_00_6k0oGSliVHxGSgqBmMEO4311","reason":"bash requires manual approval in this session"}}
+{"type":"approval/decided","seq":57,"time":1783962235813,"data":{"id":"33cdadc2-5899-4bed-8763-39fcdf6dff83","outcome":"rejected"}}
 {"type":"tool/result","seq":58,"time":1783962235814,"data":{"turn":1,"step":1,"callId":"call_00_6k0oGSliVHxGSgqBmMEO4311","content":[{"type":"text","text":"Error: the user rejected tool \"bash\""}],"isError":true},"sourceEventSeqs":[53],"surfaceOp":"append"}
 {"type":"step/end","seq":59,"time":1783962235814,"data":{"turn":1,"step":1}}
 {"type":"step/start","seq":60,"time":1783962235814,"data":{"turn":1,"step":2}}

+ 1 - 1
examples/acp-agent/tests/snapshots/hook-cc-pretool-ask/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/hook-cc-pretool-deny/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/hook-cc-promptsubmit-block/stdout.expected.jsonl

@@ -1,4 +1,4 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","id":3,"result":{"stopReason":"cancelled"}}

+ 1 - 1
examples/acp-agent/tests/snapshots/hook-cc-promptsubmit-context/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"'s"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/hook-cc-stop-continue/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/hook-codex-posttool-block/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/hook-codex-posttool-context/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/hook-codex-pretool-block/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/hook-codex-promptsubmit-block/stdout.expected.jsonl

@@ -1,4 +1,4 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","id":3,"result":{"stopReason":"cancelled"}}

+ 1 - 1
examples/acp-agent/tests/snapshots/hook-codex-promptsubmit-context/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" asked"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/hook-codex-stop-continue/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/model-switching/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/modes-advertise/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"current_mode_update","currentModeId":"plan"}}}
 {"jsonrpc":"2.0","id":3,"result":{}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"current_mode_update","currentModeId":"default"}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/multi-turn/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/parallel-tool-calls/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call","toolCallId":"call_read_a","title":"Read a.txt","kind":"read","status":"in_progress","locations":[{"path":"a.txt","line":1}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call","toolCallId":"call_read_b","title":"Read b.txt","kind":"read","status":"in_progress","locations":[{"path":"b.txt","line":1}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"call_read_a","status":"completed","content":[{"type":"content","content":{"type":"text","text":"<path>{{cwd}}/a.txt</path>\n<type>file</type>\n<content>\n1: alpha\n\n(End of file - total 1 lines)\n</content>"}}]}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/permission-switching/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","id":3,"result":{"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"workspace-write","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/plan-mode-reject/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"current_mode_update","currentModeId":"plan"}}}
 {"jsonrpc":"2.0","id":3,"result":{}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/plan-mode/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"current_mode_update","currentModeId":"plan"}}}
 {"jsonrpc":"2.0","id":3,"result":{}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/repeat-tool-guard/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call","toolCallId":"call_1","title":"Update todo list","kind":"other","status":"in_progress","rawInput":[{"content":"watch the kettle boil","status":"in_progress"}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"plan","entries":[{"content":"watch the kettle boil","priority":"medium","status":"in_progress"}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"call_1","status":"completed","content":[{"type":"content","content":{"type":"text","text":"Updated todo list: 0 pending, 1 in progress, 0 completed."}}]}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/skill-load/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"Load the requested skill."}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call","toolCallId":"call_skill_load","title":"Load skill snapshot-skill","kind":"read","status":"in_progress","rawInput":"snapshot-skill"}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"call_skill_load","status":"completed","content":[{"type":"content","content":{"type":"text","text":"<skill_content name=\"snapshot-skill\">\n<skill_resources>\nBase directory for this skill: {{cwd}}/.dsh/skills/snapshot-skill\nResolve relative paths mentioned by this skill against the base directory before using them. Load referenced resources only as needed.\n</skill_resources>\n\n<skill_instructions>\nFollow these snapshot-only instructions.\nResolve referenced resources relative to this skill directory.\n</skill_instructions>\n</skill_content>"}}]}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/subagent-depth-two-rejection/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call","toolCallId":"call_root_child","title":"subagent","kind":"other","status":"in_progress","rawInput":{"description":"Start depth one","prompt":"Call subagent once. Ask that child to attempt one further subagent call, then report the result."}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"call_root_child","status":"completed","content":[{"type":"content","content":{"type":"text","text":"DEPTH_ONE_DONE"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"ROOT_DONE"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/subagent-fork/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/subagent-mixed/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/subagent-multi/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/subagent-spawn/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/text-turn/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/todo-plan/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/tool-call-turn/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/workflow-run/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/workspace-context/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call","toolCallId":"call_workspace_read","title":"Read nested/task.txt","kind":"read","status":"in_progress","locations":[{"path":"nested/task.txt","line":1}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"call_workspace_read","status":"completed","content":[{"type":"content","content":{"type":"text","text":"<path>{{cwd}}/nested/task.txt</path>\n<type>file</type>\n<content>\n1: snapshot task\n\n(End of file - total 1 lines)\n</content>"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"DONE"}}}}

+ 1 - 1
examples/acp-agent/tests/snapshots/workspace-edit/stdout.expected.jsonl

@@ -1,6 +1,6 @@
 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
 {"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","modes":{"availableModes":[{"id":"default","name":"default"},{"id":"plan","name":"plan"}],"currentModeId":"default"},"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
-{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}}]}}}
+{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"available_commands_update","availableCommands":[{"name":"goal","description":"set or view the goal for a long-running task","input":{"hint":"[<objective>|clear|edit <objective>|pause|resume]"}},{"name":"mode","description":"Show or switch the session mode","input":{"hint":"[name]"}}]}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
 {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}

+ 1 - 1
examples/tui-agent/README.md

@@ -1,6 +1,6 @@
 # tui-agent
 
-The full-screen interactive coding agent: DeepSeek V4, local bash and filesystem tools, compaction, subagents, workflows and fresh-agent Ralph iteration, `todo_write`, timeout/spill policy, and [`@deepseek-ai/dsh-tui-demo`](../../packages/examples/tui-demo).
+The full-screen interactive coding agent: DeepSeek V4, local bash and filesystem tools, compaction, subagents, workflows and fresh-agent Ralph iteration, `todo_write`, session modes (`/mode`, plan mode with the reviewed exit), timeout/spill policy, and [`@deepseek-ai/dsh-tui-demo`](../../packages/examples/tui-demo).
 
 ## Run it
 

+ 3 - 0
examples/tui-agent/composition.md

@@ -47,6 +47,8 @@ flowchart LR
   cfg --> plugin_tui_tool_ralph
   plugin_tui_tool_todo["tool-todo<br/>@deepseek-ai/dsh-tool-todo"]
   cfg --> plugin_tui_tool_todo
+  plugin_tui_mode["mode<br/>@deepseek-ai/dsh-mode"]
+  cfg --> plugin_tui_mode
   plugin_tui_fs_local["fs-local<br/>@deepseek-ai/dsh-fs-local"]
   cfg --> plugin_tui_fs_local
   plugin_tui_fs_policy["fs-policy<br/>@deepseek-ai/dsh-fs-policy"]
@@ -81,6 +83,7 @@ flowchart LR
 | `tool-workflow` | `@deepseek-ai/dsh-tool-workflow` |
 | `tool-ralph` | `@deepseek-ai/dsh-tool-ralph` |
 | `tool-todo` | `@deepseek-ai/dsh-tool-todo` |
+| `mode` | `@deepseek-ai/dsh-mode` |
 | `fs-local` | `@deepseek-ai/dsh-fs-local` |
 | `fs-policy` | `@deepseek-ai/dsh-fs-policy` |
 | `tool-fs` | `@deepseek-ai/dsh-tool-fs` |

+ 6 - 0
examples/tui-agent/cordis.yml

@@ -88,6 +88,12 @@
 - id: tool-todo
   name: '@deepseek-ai/dsh-tool-todo'
 
+# Session modes (the shipped `plan` definition): the TUI's command registry
+# gains the plugin-registered /mode command, and the exit review rides the
+# TUI's user-interaction provider.
+- id: mode
+  name: '@deepseek-ai/dsh-mode'
+
 - id: fs-local
   name: '@deepseek-ai/dsh-fs-local'
   config:

+ 1 - 1
packages/mode/README.md

@@ -6,4 +6,4 @@ Session modes: named, logged, per-agent collaboration states, with **plan mode**
 |---|---|---|
 | `mode/` | `mode/set` vocabulary + fold, the `ctx.modes` service (list/get/set with the turn-boundary flush), the `mode:policy` guidance section, and the model-facing `exit_plan_mode` review tool | `ctx.modes` |
 
-The mode in force is a pure function of the session log (`SessionEventMap['mode/set']`, last one wins), so resume and fork restore it with no extra machinery. The deployment supplies the plan instructions through Cordis config, while the model-facing `exit_plan_mode` schema remains registered in every mode to keep the request tool catalog stable. UIs read flips off `session/event`; the [ACP bridge](../ui/acp) maps the vocabulary to the session-mode picker. Design: [plan-mode Agent Note](../../.agents/notes/implemented/feature/2026-07-07-plan-mode.md).
+The mode in force is a pure function of the session log (`SessionEventMap['mode/set']`, last one wins), so resume and fork restore it with no extra machinery. The deployment supplies plan instructions through Cordis config, while `exit_plan_mode` remains registered in every mode to keep the request tool catalog stable. UIs read flips off `session/event`; the [ACP bridge](../ui/acp) maps the vocabulary to the session-mode picker, and a composed [command registry](../ui/commands) gains the plugin-registered `/mode` command. Design: [plan-mode Agent Note](../../.agents/notes/implemented/feature/2026-07-07-plan-mode.md).

+ 8 - 4
packages/mode/mode/README.md

@@ -18,7 +18,11 @@ A mode does not gate execution, filter tools, or change sandbox or approval sett
 
 `list()` returns `default` followed by the configured definitions. `get(agent)` returns the folded mode, treating a removed definition as `default`, plus any pending intent. `set(agent, mode)` validates against that vocabulary and records a pending intent. The service flushes the intent on `agent/prompt-submit` before the first assembly, `agent/turn-continuation` before a normal successor step, or after a composed `agent/request-error` decision authorizes a retry. Each append is turn-enclosed and precedes the affected prompt assembly, including an automatic recovery step after asynchronous backoff. A changed user selection adds one coalesced `context/message` notice when the last logged request header described a different mode; a net-zero selection sequence adds nothing.
 
-`AgentOptions.mode` seeds an initial mode through the same pending-intent path. Forked sessions inherit mode state through their logged prefix.
+There is no creation-time mode option: a UI (or a plugin) selects through `set()` before the first turn, and a fork child needs no mechanism at all — the parent's `mode/set` is inside the seeded prefix.
+
+## The `/mode` command
+
+When a command registry (`@deepseek-ai/dsh-commands`) is composed, the plugin registers `/mode` for interactive front doors: bare `/mode` prints the current mode (plus any pending switch) and the available vocabulary; `/mode <name>` records the switch through `set()` and echoes that it applies from the next turn. Without a commands service the child never mounts and nothing else changes.
 
 ## `exit_plan_mode`
 
@@ -62,7 +66,7 @@ Within one mode, the section is stable. Entering or leaving a non-default mode c
 
 #### What the model sees
 
-A user-driven change whose previous request header described another mode appends either `The user switched this session to <mode> mode.` or `The user switched this session back to the default mode.` A logged mode removed from config reads as default and appends `Mode "<mode>" is no longer defined in this deployment's configuration; the session continues in the default mode.` once per removed name. Initial selection before the first header, net-zero selections, and the tool-driven exit add no notice.
+A user-driven change whose previous request header described another mode appends either `The user switched this session to <mode> mode.` or `The user switched this session back to the default mode.` A logged mode removed from config reads as default without a notice. Initial selection before the first header, net-zero selections, and the tool-driven exit add no notice.
 
 #### Token effect
 
@@ -70,7 +74,7 @@ Each qualifying transition adds one short conversation message once. The dynamic
 
 #### KV Cache effect
 
-The notice itself is append-only conversation growth. A real mode transition also changes the earlier order-50 section, so that section remains the limiting cache boundary; a dropped-definition notice with no section change preserves the prior request prefix and only extends it.
+The notice itself is append-only conversation growth. A real mode transition also changes the earlier order-50 section, so that section remains the limiting cache boundary.
 
 ### Exit tool schema and review exchange
 
@@ -96,6 +100,6 @@ The tool schema and generated SDK binding are byte-identical in `default`, `plan
 
 - A mode restrains by guidance only; pair it with independent enforcement knobs when a hard boundary is required. The [plan-mode Agent Note](../../../.agents/notes/implemented/feature/2026-07-07-plan-mode.md) records the rejected enforcement shapes and the effects-metadata restart trigger.
 - A pending flip selected while idle is lost if the process exits before the next boundary; the UI must reapply it.
-- Forked children inherit the logged mode, while spawned children start in `default` unless their creator seeds `AgentOptions.mode`.
+- Forked children inherit the logged mode, while spawned children start in `default`; there is no creation-time mode option.
 
 Design: [plan-mode Agent Note](../../../.agents/notes/implemented/feature/2026-07-07-plan-mode.md).

+ 7 - 0
packages/mode/mode/package.json

@@ -23,16 +23,23 @@
   "license": "BSD-3-Clause",
   "peerDependencies": {
     "@deepseek-ai/dsh-agent": "^0.0.1",
+    "@deepseek-ai/dsh-commands": "^0.0.1",
     "@deepseek-ai/dsh-session": "^0.0.1",
     "@deepseek-ai/dsh-system-prompt": "^0.0.1",
     "@deepseek-ai/dsh-tools": "^0.0.1",
     "@deepseek-ai/dsh-user-interaction": "^0.0.1",
     "cordis": "^4.0.0-rc.7"
   },
+  "peerDependenciesMeta": {
+    "@deepseek-ai/dsh-commands": {
+      "optional": true
+    }
+  },
   "devDependencies": {
     "@deepseek-ai/dsh-agent": "workspace:^",
     "@deepseek-ai/dsh-agent-loop": "workspace:^",
     "@deepseek-ai/dsh-code-runtime": "workspace:^",
+    "@deepseek-ai/dsh-commands": "workspace:^",
     "@deepseek-ai/dsh-llm": "workspace:^",
     "@deepseek-ai/dsh-session": "workspace:^",
     "@deepseek-ai/dsh-system-prompt": "workspace:^",

+ 41 - 66
packages/mode/mode/src/index.ts

@@ -38,6 +38,9 @@ import type { Session, SessionEvent } from '@deepseek-ai/dsh-session'
 import { defineTool } from '@deepseek-ai/dsh-tools'
 import type {} from '@deepseek-ai/dsh-system-prompt'
 import type {} from '@deepseek-ai/dsh-user-interaction'
+// Type-only edge: resolves `ctx.commands` for the `/mode` command child below;
+// the child mounts only when a commands service is composed.
+import type {} from '@deepseek-ai/dsh-commands'
 
 declare module '@deepseek-ai/dsh-session' {
   interface SessionEventMap {
@@ -50,17 +53,6 @@ declare module '@deepseek-ai/dsh-session' {
   }
 }
 
-declare module '@deepseek-ai/dsh-agent' {
-  interface AgentOptions {
-    /**
-     * Initial session mode for this agent. Applied as a pending intent flushed
-     * at the first turn boundary; an explicit option beats the logged baseline
-     * on create AND resume. An unknown name throws at agent creation.
-     */
-    mode?: string
-  }
-}
-
 declare module 'cordis' {
   interface Context {
     modes: ModesService
@@ -123,11 +115,6 @@ const EXIT_DESCRIPTION
   + 'The user may approve (carry out the plan from your next step) or keep '
   + 'planning — their feedback comes back in the tool result; revise and present again.'
 
-/** Durable notice text for a folded mode the current deployment no longer defines. */
-function droppedDefinitionNotice(name: string): string {
-  return `Mode "${name}" is no longer defined in this deployment's configuration; the session continues in the default mode.`
-}
-
 /** The plan's first markdown heading (any level), or `undefined` when it has none. */
 function firstHeading(plan: string): string | undefined {
   for (const line of plan.split('\n')) {
@@ -228,9 +215,6 @@ export class ModesService extends Service {
    */
   private readonly pendingIntents = new WeakMap<Session, { mode: string; narrate: boolean }>()
 
-  /** Mode-plugin notice texts already present in each live session; hydrated once from the durable log. */
-  private readonly noticeTexts = new WeakMap<Session, Set<string>>()
-
   constructor(ctx: Context, config: ModeConfig = { modes: {} }) {
     super(ctx, 'modes')
     this.resolved = resolveConfig(config)
@@ -249,7 +233,7 @@ export class ModesService extends Service {
     // only when session.append rejects during teardown.
     ctx.on('agent/prompt-submit', (agent, _content, _source, next) => {
       try {
-        this.onBoundary(agent.session, true)
+        this.onBoundary(agent)
       } catch (error) {
         ctx.logger.warn('dsh-mode: boundary flush failed: %o', error)
       }
@@ -257,7 +241,7 @@ export class ModesService extends Service {
     })
     ctx.on('agent/turn-continuation', (agent, _turn, _decision, next) => {
       try {
-        this.onBoundary(agent.session, false)
+        this.onBoundary(agent)
       } catch (error) {
         ctx.logger.warn('dsh-mode: boundary flush failed: %o', error)
       }
@@ -279,7 +263,7 @@ export class ModesService extends Service {
       // owning plugin fiber has been disposed.
       if (disposed || decision.action !== 'retry') return decision
       try {
-        this.onBoundary(agent.session, false)
+        this.onBoundary(agent)
       } catch (error) {
         ctx.logger.warn('dsh-mode: boundary flush failed: %o', error)
       }
@@ -287,18 +271,39 @@ export class ModesService extends Service {
     }, { prepend: true })
     ctx.effect(() => () => { disposed = true }, 'dsh-mode: close boundary lifetime')
 
-    ctx.on('agent/created', (agent) => {
-      const seed = agent.options.mode
-      if (seed === undefined) return
-      this.set(agent, seed)
-    })
-
     ctx.systemPrompt.section({
       name: 'mode:policy',
       order: 50,
       text: context => (context.agent === undefined ? '' : this.activeDefinition(context.agent.session)?.definition.section ?? ''),
     })
 
+    // The `/mode` command (show or switch the session mode) for interactive
+    // front doors, mounted only when a commands service is composed — the
+    // child plugin below activates on `ctx.commands` availability, so a
+    // commands-less deployment composes dsh-mode unchanged.
+    ctx.inject(['commands'], (commandCtx) => {
+      commandCtx.commands.register({
+        name: 'mode',
+        description: 'Show or switch the session mode',
+        input: { hint: '[name]' },
+        handler: ({ agent, rawInput }) => {
+          const target = rawInput.trim()
+          if (target === '') {
+            const { current, pending } = this.get(agent)
+            const pendingNote = pending === undefined ? '' : ` (pending: ${pending})`
+            return { kind: 'success', text: `mode: ${current}${pendingNote} — available: ${this.list().join(', ')}` }
+          }
+          try {
+            this.set(agent, target)
+            return { kind: 'success', text: `mode → ${target} (applies from the next turn)` }
+          } catch (error) {
+            // ModesService.set throws only Error (its unknown-name validation).
+            return { kind: 'error', text: (error as Error).message }
+          }
+        },
+      })
+    })
+
     ctx.tools.register(defineTool({
       name: EXIT_PLAN_MODE,
       description: EXIT_DESCRIPTION,
@@ -419,17 +424,13 @@ export class ModesService extends Service {
   }
 
   /**
-   * One boundary pass (`turnStart` = prompt-submit, else a normal-successor or
-   * recovery-retry boundary): narrate a folded mode the config dropped (once
-   * per name, turn starts only), then flush the pending intent — append the
-   * `mode/set` (skipped when the fold already matches: a net-zero flip
-   * sequence) and the one coalesced notice when the flushed mode differs from
-   * what the last logged request header told the model. Idempotent per
-   * boundary, so the per-message prompt-submit dispatches of one batch flush
-   * once.
+   * One boundary pass for prompt submission, a normal successor, or a recovery
+   * retry: append a changed pending `mode/set` and one coalesced notice when the
+   * flushed mode differs from what the last logged request header told the
+   * model. Idempotent per boundary, so repeated dispatches flush once.
    */
-  private onBoundary(session: Session, turnStart: boolean): void {
-    if (turnStart) this.noticeDroppedDefinition(session)
+  private onBoundary(agent: Agent): void {
+    const session = agent.session
     const pending = this.pendingIntents.get(session)
     if (pending === undefined) return
     const target = pending.mode
@@ -438,10 +439,8 @@ export class ModesService extends Service {
       return
     }
     session.append('mode/set', { mode: target })
-    // Clear the intent only AFTER the append landed: if a backend rejects the
-    // write, the intent stays parked and the next boundary retries — the UI's
-    // optimistic picker state and the log re-converge instead of diverging
-    // forever on a swallowed one-shot.
+    // Clear the intent only after the append lands; a failed write remains
+    // pending so a later boundary can retry it.
     this.pendingIntents.delete(session)
     if (!pending.narrate) return
     const told = modeAtLastHeader(session.events)
@@ -454,30 +453,6 @@ export class ModesService extends Service {
       source: { kind: 'plugin', plugin: 'mode' },
     }, { surfaceOp: 'append' })
   }
-
-  /** Narrate a folded mode name the current config no longer defines — the session reads as default plus this one notice. */
-  private noticeDroppedDefinition(session: Session): void {
-    const name = foldMode(session.events)
-    if (name === DEFAULT_MODE || this.resolved.definitions.has(name)) return
-    const text = droppedDefinitionNotice(name)
-    let noticed = this.noticeTexts.get(session)
-    if (noticed === undefined) {
-      noticed = new Set(session.events.flatMap(event => event.type === 'context/message'
-        && event.data.source.kind === 'plugin'
-        && event.data.source.plugin === 'mode'
-        && event.data.content.length === 1
-        && event.data.content[0]?.type === 'text'
-        ? [event.data.content[0].text]
-        : []))
-      this.noticeTexts.set(session, noticed)
-    }
-    if (noticed.has(text)) return
-    session.append('context/message', {
-      content: [{ type: 'text', text }],
-      source: { kind: 'plugin', plugin: 'mode' },
-    }, { surfaceOp: 'append' })
-    noticed.add(text)
-  }
 }
 
 export default ModesService

+ 5 - 2
packages/mode/mode/tests/integration.spec.ts

@@ -64,13 +64,16 @@ function findEvent<T extends SessionEvent['type']>(
 }
 
 describe('plan mode through the agent loop', () => {
-  it('seeds plan mode from AgentOptions: the FIRST header is already plan-shaped, and a non-shell call is guidance-constrained only', async () => {
+  it('a pre-turn set() makes the FIRST header plan-shaped, and a non-shell call is guidance-constrained only', async () => {
     const adapter = new MockAdapter([
       toolCallResponse('call-1', 'write', {}, 'Writing during plan.'),
       textResponse('Noted in the plan.'),
     ])
     const ctx = await harness(adapter)
-    const agent = ctx.agentLoop.create(SessionId('it-plan-seed'), { provider: 'mock', model: 'mock', mode: PLAN_MODE })
+    const agent = ctx.agentLoop.create(SessionId('it-plan-seed'), { provider: 'mock', model: 'mock' })
+    // Selected while idle (the ACP picker shape): the pending intent flushes at
+    // the first prompt-submit, BEFORE the first assembly.
+    ctx.modes.set(agent, PLAN_MODE)
 
     agent.send([{ type: 'text', text: 'explore the repo' }])
     await waitForIdle(ctx, agent)

+ 101 - 126
packages/mode/mode/tests/mode.spec.ts

@@ -5,7 +5,9 @@ import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
 import ToolRegistry, { RUN_CODE_NAME, defineTool } from '@deepseek-ai/dsh-tools'
 import { Session, SessionId } from '@deepseek-ai/dsh-session'
 import { agentEvents, type Agent, type RequestErrorDecision } from '@deepseek-ai/dsh-agent'
+import { createScope } from '@deepseek-ai/dsh-scope'
 import UserInteractionService, { type AskUserQuestionRequest } from '@deepseek-ai/dsh-user-interaction'
+import CommandService from '@deepseek-ai/dsh-commands'
 import { CodeRuntime, type CodeRunRequest, type CodeRunResult } from '@deepseek-ai/dsh-code-runtime'
 import ModesService, { DEFAULT_MODE, EXIT_PLAN_MODE, PLAN_MODE, foldMode, resolveConfig } from '../src/index.ts'
 import type { ModeConfig } from '../src/index.ts'
@@ -15,16 +17,31 @@ const PLAN_CONFIG = { modes: { plan: { section: TEST_PLAN_SECTION } } } satisfie
 
 /**
  * Drives the REAL plugin: mounts `dsh-mode` beside real `SystemPrompt` and
- * `ToolRegistry` services, with fake Agents carrying real `Session`s (the
- * tool-todo test shape). Turn boundaries are simulated by appending the real
- * boundary events and dispatching the ordinary interception seams the loop
- * fires there (`agent/prompt-submit` / `agent/turn-continuation`). Recovery
- * retry coverage lives in the full-loop integration suite.
+ * `ToolRegistry` services, with fake Agents carrying real `Session`s and a
+ * real scoped `agent.ctx` minted through `createScope`.
+ * Turn boundaries are simulated by appending the real boundary events and
+ * dispatching the interception seams the loop fires there. Recovery retries
+ * exercise the separate `agent/request-error` wrapper.
  */
 
-function agentWithSession(id = 'agent-1', options: { mode?: string } = {}): Agent & { session: Session } {
+async function agentWithSession(ctx: Context, id = 'agent-1', { mode }: { mode?: string } = {}): Promise<Agent & { session: Session }> {
   const session = new Session(SessionId(id))
-  return { id: SessionId(id), session, options } as unknown as Agent & { session: Session }
+  const agent = { id: SessionId(id), session, options: {} } as unknown as Agent & { session: Session }
+  let scoped!: Context
+  await ctx.plugin(Object.assign((inner: Context) => { scoped = createScope(inner, agent).ctx }, {
+    inject: ['tools'],
+  }))
+  ;(agent as { ctx?: Context }).ctx = scoped
+  // A seeded mode lands before the creation announcement, matching resume.
+  if (mode !== undefined) session.append('mode/set', { mode })
+  // The loop announces creation after publication.
+  ctx.emit('agent/created', agent)
+  return agent
+}
+
+/** Assemble exactly as the loop does: the agent is both subject and scope. */
+function assembleFor(ctx: Context, agent: Agent) {
+  return ctx.systemPrompt.assemble({ agent, scope: agent })
 }
 
 async function setup(config: ModeConfig = PLAN_CONFIG): Promise<Context> {
@@ -167,7 +184,7 @@ describe('ctx.modes: list/get/set', () => {
 
   it('reads the folded mode, mapping a dropped definition to default', async () => {
     const ctx = await setup()
-    const agent = agentWithSession()
+    const agent = await agentWithSession(ctx)
     expect(ctx.modes.get(agent)).toEqual({ current: DEFAULT_MODE })
     agent.session.append('mode/set', { mode: PLAN_MODE })
     expect(ctx.modes.get(agent)).toEqual({ current: PLAN_MODE })
@@ -177,13 +194,13 @@ describe('ctx.modes: list/get/set', () => {
 
   it('rejects an unknown mode name loudly, naming the vocabulary', async () => {
     const ctx = await setup()
-    const agent = agentWithSession()
+    const agent = await agentWithSession(ctx)
     expect(() => { ctx.modes.set(agent, 'nope') }).toThrow('unknown mode "nope" — available modes: default, plan')
   })
 
   it('accepts default as a target (exit-to-default is a valid write)', async () => {
     const ctx = await setup()
-    const agent = agentWithSession()
+    const agent = await agentWithSession(ctx)
     agent.session.append('mode/set', { mode: PLAN_MODE })
     ctx.modes.set(agent, DEFAULT_MODE)
     expect(ctx.modes.get(agent)).toEqual({ current: PLAN_MODE, pending: DEFAULT_MODE })
@@ -191,7 +208,7 @@ describe('ctx.modes: list/get/set', () => {
 
   it('drops a no-op set (target equals pending, else the current fold)', async () => {
     const ctx = await setup()
-    const agent = agentWithSession()
+    const agent = await agentWithSession(ctx)
     ctx.modes.set(agent, DEFAULT_MODE)
     expect(ctx.modes.get(agent)).toEqual({ current: DEFAULT_MODE })
     ctx.modes.set(agent, PLAN_MODE)
@@ -199,21 +216,12 @@ describe('ctx.modes: list/get/set', () => {
     expect(ctx.modes.get(agent)).toEqual({ current: DEFAULT_MODE, pending: PLAN_MODE })
   })
 
-  it('seeds the initial mode from AgentOptions.mode on agent/created', async () => {
-    const ctx = await setup()
-    const agent = agentWithSession('seeded', { mode: PLAN_MODE })
-    ctx.emit('agent/created', agent)
-    expect(ctx.modes.get(agent)).toEqual({ current: DEFAULT_MODE, pending: PLAN_MODE })
-    const bare = agentWithSession('unseeded')
-    ctx.emit('agent/created', bare)
-    expect(ctx.modes.get(bare)).toEqual({ current: DEFAULT_MODE })
-  })
 })
 
 describe('the boundary flush', () => {
   it('flushes the pending intent as a mode/set at turn/start', async () => {
     const ctx = await setup()
-    const agent = agentWithSession()
+    const agent = await agentWithSession(ctx)
     ctx.modes.set(agent, PLAN_MODE)
     await boundary(ctx, agent, 'turn/start')
     expect(foldMode(agent.session.events)).toBe(PLAN_MODE)
@@ -222,7 +230,7 @@ describe('the boundary flush', () => {
 
   it('flushes at step/end too (a mid-turn flip lands on the following step)', async () => {
     const ctx = await setup()
-    const agent = agentWithSession()
+    const agent = await agentWithSession(ctx)
     ctx.modes.set(agent, PLAN_MODE)
     await boundary(ctx, agent, 'step/end')
     expect(foldMode(agent.session.events)).toBe(PLAN_MODE)
@@ -230,7 +238,7 @@ describe('the boundary flush', () => {
 
   it('keeps the pending intent parked when recovery does not retry', async () => {
     const ctx = await setup()
-    const agent = agentWithSession()
+    const agent = await agentWithSession(ctx)
     ctx.modes.set(agent, PLAN_MODE)
     expect(await recoveryBoundary(ctx, agent, { action: 'fail' })).toEqual({ action: 'fail' })
     expect(ctx.modes.get(agent)).toEqual({ current: DEFAULT_MODE, pending: PLAN_MODE })
@@ -240,7 +248,7 @@ describe('the boundary flush', () => {
     const ctx = await setup()
     const warn = vi.fn()
     ctx.logger.warn = warn as never
-    const agent = agentWithSession()
+    const agent = await agentWithSession(ctx)
     ctx.modes.set(agent, PLAN_MODE)
     const original = agent.session.append.bind(agent.session)
     agent.session.append = (((type: string, ...rest: unknown[]) => {
@@ -255,7 +263,7 @@ describe('the boundary flush', () => {
 
   it('nets out a flip sequence that returns to the folded mode (no append, no notice)', async () => {
     const ctx = await setup()
-    const agent = agentWithSession()
+    const agent = await agentWithSession(ctx)
     ctx.modes.set(agent, PLAN_MODE)
     ctx.modes.set(agent, DEFAULT_MODE)
     await boundary(ctx, agent, 'turn/start')
@@ -265,7 +273,7 @@ describe('the boundary flush', () => {
 
   it('narrates nothing before the first request header (the section is the state statement)', async () => {
     const ctx = await setup()
-    const agent = agentWithSession()
+    const agent = await agentWithSession(ctx)
     ctx.modes.set(agent, PLAN_MODE)
     await boundary(ctx, agent, 'turn/start')
     expect(noticeTexts(agent.session)).toEqual([])
@@ -273,7 +281,7 @@ describe('the boundary flush', () => {
 
   it('narrates once when the flushed mode differs from what the last header told the model', async () => {
     const ctx = await setup()
-    const agent = agentWithSession()
+    const agent = await agentWithSession(ctx)
     header(agent.session)
     ctx.modes.set(agent, PLAN_MODE)
     await boundary(ctx, agent, 'turn/start')
@@ -284,7 +292,7 @@ describe('the boundary flush', () => {
 
   it('narrates a switch back to the default mode with the default wording', async () => {
     const ctx = await setup()
-    const agent = agentWithSession()
+    const agent = await agentWithSession(ctx)
     agent.session.append('mode/set', { mode: PLAN_MODE })
     header(agent.session)
     ctx.modes.set(agent, DEFAULT_MODE)
@@ -294,7 +302,7 @@ describe('the boundary flush', () => {
 
   it('stays silent when the header already reflects the flushed mode', async () => {
     const ctx = await setup()
-    const agent = agentWithSession()
+    const agent = await agentWithSession(ctx)
     agent.session.append('mode/set', { mode: PLAN_MODE })
     header(agent.session)
     agent.session.append('mode/set', { mode: DEFAULT_MODE })
@@ -304,64 +312,12 @@ describe('the boundary flush', () => {
     expect(noticeTexts(agent.session)).toEqual([])
   })
 
-  it('narrates a folded mode the config no longer defines, once, at turn starts', async () => {
-    const ctx = await setup()
-    const agent = agentWithSession()
-    agent.session.append('mode/set', { mode: 'retired' })
-    await boundary(ctx, agent, 'turn/start')
-    await boundary(ctx, agent, 'turn/start')
-    expect(noticeTexts(agent.session)).toEqual([
-      'Mode "retired" is no longer defined in this deployment\'s configuration; the session continues in the default mode.',
-    ])
-    await boundary(ctx, agent, 'step/end')
-    expect(noticeTexts(agent.session)).toHaveLength(1)
-  })
-
-  it('does not repeat a dropped-definition notice after the mode service restarts', async () => {
-    const first = await setup()
-    const original = agentWithSession('dropped-resume')
-    original.session.append('mode/set', { mode: 'retired' })
-    await boundary(first, original, 'turn/start')
-
-    const resumed = agentWithSession('dropped-resume')
-    resumed.session = new Session(SessionId('dropped-resume'), original.session.events)
-    const second = await setup()
-    await boundary(second, resumed, 'turn/start')
-
-    expect(noticeTexts(resumed.session)).toEqual([
-      'Mode "retired" is no longer defined in this deployment\'s configuration; the session continues in the default mode.',
-    ])
-  })
-
-  it('retries a dropped-definition notice when its append fails', async () => {
-    const ctx = await setup()
-    const warn = vi.fn()
-    ctx.logger.warn = warn as never
-    const agent = agentWithSession()
-    agent.session.append('mode/set', { mode: 'retired' })
-    const original = agent.session.append.bind(agent.session)
-    agent.session.append = (((type: string, ...rest: unknown[]) => {
-      if (type === 'context/message') throw new Error('backend gone')
-      return (original as (...args: unknown[]) => unknown)(type, ...rest)
-    }) as unknown) as typeof agent.session.append
-
-    await boundary(ctx, agent, 'turn/start')
-    expect(warn).toHaveBeenCalledOnce()
-    expect(noticeTexts(agent.session)).toEqual([])
-
-    agent.session.append = original
-    await boundary(ctx, agent, 'turn/start')
-    await boundary(ctx, agent, 'turn/start')
-    expect(noticeTexts(agent.session)).toEqual([
-      'Mode "retired" is no longer defined in this deployment\'s configuration; the session continues in the default mode.',
-    ])
-  })
 
   it('contains an append failure instead of blocking the prompt or the turn', async () => {
     const ctx = await setup()
     const warn = vi.fn()
     ctx.logger.warn = warn as never
-    const agent = agentWithSession()
+    const agent = await agentWithSession(ctx)
     ctx.modes.set(agent, PLAN_MODE)
     const original = agent.session.append.bind(agent.session)
     // Only the flush's own mode/set append fails; the boundary event itself
@@ -386,7 +342,7 @@ describe('the boundary flush', () => {
     const ctx = await setup()
     const warn = vi.fn()
     ctx.logger.warn = warn as never
-    const agent = agentWithSession()
+    const agent = await agentWithSession(ctx)
     ctx.modes.set(agent, PLAN_MODE)
     const original = agent.session.append.bind(agent.session)
     agent.session.append = (((type: string, ...rest: unknown[]) => {
@@ -403,13 +359,13 @@ describe('the soft layer', () => {
   it('keeps the tool schemas identical across default and plan mode', async () => {
     const ctx = await setup()
     registerNamedTools(ctx, ['read', 'write'])
-    const agent = agentWithSession()
-    const defaultAssembly = await ctx.systemPrompt.assemble({ agent })
+    const agent = await agentWithSession(ctx)
+    const defaultAssembly = await assembleFor(ctx, agent)
     expect(defaultAssembly.tools.map(tool => tool.name)).toEqual([EXIT_PLAN_MODE, 'read', 'write'])
     expect(defaultAssembly.sections.find(section => section.name === 'mode:policy')?.text).toBe('')
 
     agent.session.append('mode/set', { mode: PLAN_MODE })
-    const planAssembly = await ctx.systemPrompt.assemble({ agent })
+    const planAssembly = await assembleFor(ctx, agent)
     expect(planAssembly.tools).toEqual(defaultAssembly.tools)
     expect(planAssembly.sections.find(section => section.name === 'mode:policy')?.text).toBe(TEST_PLAN_SECTION)
   })
@@ -425,9 +381,8 @@ describe('the soft layer', () => {
   it('keeps the full toolset in plan mode and renders the configured mode section', async () => {
     const ctx = await setup()
     registerNamedTools(ctx, ['read', 'write', 'todo_write'])
-    const agent = agentWithSession()
-    agent.session.append('mode/set', { mode: PLAN_MODE })
-    const assembly = await ctx.systemPrompt.assemble({ agent })
+    const agent = await agentWithSession(ctx, 'agent-1', { mode: PLAN_MODE })
+    const assembly = await assembleFor(ctx, agent)
     expect(assembly.tools.map(tool => tool.name).sort()).toEqual([EXIT_PLAN_MODE, 'read', 'todo_write', 'write'])
     expect(assembly.sections.find(section => section.name === 'mode:policy')?.text).toBe(TEST_PLAN_SECTION)
   })
@@ -435,16 +390,14 @@ describe('the soft layer', () => {
   it('keeps exit_plan_mode visible in custom modes while rendering their guidance', async () => {
     const ctx = await setup({ modes: { ...PLAN_CONFIG.modes, review: { section: 'reviewing' } } })
     registerNamedTools(ctx, ['read', 'write'])
-    const agent = agentWithSession()
-    agent.session.append('mode/set', { mode: 'review' })
-    const assembly = await ctx.systemPrompt.assemble({ agent })
+    const agent = await agentWithSession(ctx, 'agent-1', { mode: 'review' })
+    const assembly = await assembleFor(ctx, agent)
     expect(assembly.tools.map(tool => tool.name)).toEqual([EXIT_PLAN_MODE, 'read', 'write'])
     expect(assembly.sections.find(section => section.name === 'mode:policy')?.text).toBe('reviewing')
   })
 
-  it('leaves foreign post-next() additions alone in plan mode (no general tool filtering)', async () => {
-    // A foreign listener that post-processes await next(): the addition
-    // survives, because modes do not filter the deployment's tool registry.
+  it('leaves foreign assemble additions alone in any mode (no assemble-layer filtering at all)', async () => {
+    // Modes do not filter the deployment's registry or later assembly additions.
     const ctx = new Context()
     await ctx.plugin(SystemPrompt)
     await ctx.plugin(ToolRegistry)
@@ -455,10 +408,12 @@ describe('the soft layer', () => {
     })
     await ctx.plugin(ModesService, PLAN_CONFIG)
     registerNamedTools(ctx, ['read'])
-    const agent = agentWithSession()
-    agent.session.append('mode/set', { mode: PLAN_MODE })
-    const assembly = await ctx.systemPrompt.assemble({ agent })
-    expect(assembly.tools.map(tool => tool.name)).toEqual(['exit_plan_mode', 'read', 'added-later'])
+    const planning = await agentWithSession(ctx, 'planning', { mode: PLAN_MODE })
+    expect((await assembleFor(ctx, planning)).tools.map(tool => tool.name))
+      .toEqual(['exit_plan_mode', 'read', 'added-later'])
+    const defaulted = await agentWithSession(ctx, 'defaulted')
+    expect((await assembleFor(ctx, defaulted)).tools.map(tool => tool.name))
+      .toEqual(['exit_plan_mode', 'read', 'added-later'])
   })
 
   it('keeps run_code the only wire tool in plan mode under the registry Code Mode; the SDK gains the exit binding', async () => {
@@ -475,9 +430,8 @@ describe('the soft layer', () => {
     await ctx.plugin(FakeRuntime)
     await ctx.plugin(ModesService, PLAN_CONFIG)
     registerNamedTools(ctx, ['read', 'write'])
-    const agent = agentWithSession()
-    agent.session.append('mode/set', { mode: PLAN_MODE })
-    const assembly = await ctx.systemPrompt.assemble({ agent })
+    const agent = await agentWithSession(ctx, 'agent-1', { mode: PLAN_MODE })
+    const assembly = await assembleFor(ctx, agent)
     expect(assembly.tools.map(tool => tool.name)).toEqual(['run_code'])
     // The SDK documents the full binding set plus the exit — a mode never
     // prunes capabilities; it restrains by the section's guidance alone.
@@ -499,9 +453,8 @@ describe('the soft layer', () => {
     await ctx.plugin(FakeRuntime)
     await ctx.plugin(ModesService, PLAN_CONFIG)
     registerNamedTools(ctx, ['read', 'write'])
-    const agent = agentWithSession()
-    agent.session.append('mode/set', { mode: PLAN_MODE })
-    const assembly = await ctx.systemPrompt.assemble({ agent })
+    const agent = await agentWithSession(ctx, 'agent-1', { mode: PLAN_MODE })
+    const assembly = await assembleFor(ctx, agent)
     // The stable registry contribution reaches both surfaces: the exit tool
     // is present on the wire AND in the SDK alongside the untouched toolset.
     expect(assembly.tools.map(tool => tool.name).sort()).toEqual(['exit_plan_mode', 'read', 'run_code', 'write'])
@@ -523,13 +476,13 @@ describe('the soft layer', () => {
     await withModes.plugin(FakeRuntime)
     await withModes.plugin(ModesService, PLAN_CONFIG)
     registerNamedTools(withModes, ['read', 'write'])
-    const agent = agentWithSession()
-    const defaultSdk = (await withModes.systemPrompt.assemble({ agent })).sections.find(section => section.name === 'tools:sdk')?.text ?? ''
+    const agent = await agentWithSession(withModes)
+    const defaultSdk = (await assembleFor(withModes, agent)).sections.find(section => section.name === 'tools:sdk')?.text ?? ''
     expect(defaultSdk).toContain('read(args:')
     expect(defaultSdk).toContain('write(args:')
     expect(defaultSdk).toContain('exit_plan_mode(args:')
     agent.session.append('mode/set', { mode: PLAN_MODE })
-    const planSdk = (await withModes.systemPrompt.assemble({ agent })).sections.find(section => section.name === 'tools:sdk')?.text ?? ''
+    const planSdk = (await assembleFor(withModes, agent)).sections.find(section => section.name === 'tools:sdk')?.text ?? ''
     expect(planSdk).toBe(defaultSdk)
 
     // Loading the mode plugin deliberately adds one stable binding compared
@@ -547,20 +500,19 @@ describe('the soft layer', () => {
   it('treats a dropped folded definition as the default mode', async () => {
     const ctx = await setup()
     registerNamedTools(ctx, ['read', 'write'])
-    const agent = agentWithSession()
-    agent.session.append('mode/set', { mode: 'retired' })
-    const assembly = await ctx.systemPrompt.assemble({ agent })
+    const agent = await agentWithSession(ctx, 'agent-1', { mode: 'retired' })
+    const assembly = await assembleFor(ctx, agent)
     expect(assembly.tools.map(tool => tool.name)).toEqual([EXIT_PLAN_MODE, 'read', 'write'])
   })
 })
 
-describe('no execution gating', () => {
+describe('no execution gating beyond the exit tool', () => {
   it('passes agent-less and default-mode executions through', async () => {
     const ctx = await setup()
     registerNamedTools(ctx, ['write'])
     const agentless = await execute(ctx, 'write')
     expect(agentless.isError).toBe(false)
-    const agent = agentWithSession()
+    const agent = await agentWithSession(ctx)
     const defaulted = await execute(ctx, 'write', agent)
     expect(defaulted.isError).toBe(false)
   })
@@ -568,8 +520,7 @@ describe('no execution gating', () => {
   it('runs every call in plan mode untouched — modes restrain by guidance, enforcement knobs are separate axes', async () => {
     const ctx = await setup()
     registerNamedTools(ctx, ['read', 'write', 'bash'])
-    const agent = agentWithSession()
-    agent.session.append('mode/set', { mode: PLAN_MODE })
+    const agent = await agentWithSession(ctx, 'agent-1', { mode: PLAN_MODE })
     for (const name of ['read', 'write', 'bash']) {
       const result = await execute(ctx, name, agent)
       expect(result.isError).toBe(false)
@@ -579,11 +530,38 @@ describe('no execution gating', () => {
   it('treats a dropped folded definition as the default mode', async () => {
     const ctx = await setup()
     registerNamedTools(ctx, ['write'])
-    const agent = agentWithSession()
-    agent.session.append('mode/set', { mode: 'retired' })
+    const agent = await agentWithSession(ctx, 'agent-1', { mode: 'retired' })
     const result = await execute(ctx, 'write', agent)
     expect(result.isError).toBe(false)
   })
+
+})
+
+describe('the /mode command', () => {
+  it('registers only when a commands service is composed, and shows or switches the mode', async () => {
+    const bare = await setup()
+    expect(bare.get('commands')).toBeUndefined()
+
+    const ctx = await setup()
+    await ctx.plugin(CommandService)
+    // The `ctx.inject` child mounts asynchronously once `commands` resolves.
+    await new Promise(resolve => setImmediate(resolve))
+    const agent = await agentWithSession(ctx)
+    expect(ctx.commands.list(agent).map(command => command.name)).toEqual(['mode'])
+
+    const signal = new AbortController().signal
+    const show = await ctx.commands.execute(agent, '/mode', signal)
+    expect(show).toEqual({ kind: 'success', text: 'mode: default — available: default, plan' })
+
+    const flip = await ctx.commands.execute(agent, '/mode plan', signal)
+    expect(flip).toEqual({ kind: 'success', text: 'mode → plan (applies from the next turn)' })
+    expect(ctx.modes.get(agent)).toEqual({ current: DEFAULT_MODE, pending: PLAN_MODE })
+    const pendingShow = await ctx.commands.execute(agent, '/mode', signal)
+    expect(pendingShow).toEqual({ kind: 'success', text: 'mode: default (pending: plan) — available: default, plan' })
+
+    const unknown = await ctx.commands.execute(agent, '/mode nope', signal)
+    expect(unknown).toEqual({ kind: 'error', text: 'unknown mode "nope" — available modes: default, plan' })
+  })
 })
 
 describe('exit_plan_mode', () => {
@@ -599,8 +577,7 @@ describe('exit_plan_mode', () => {
         },
       })
     }
-    const agent = agentWithSession()
-    agent.session.append('mode/set', { mode: PLAN_MODE })
+    const agent = await agentWithSession(ctx, 'agent-1', { mode: PLAN_MODE })
     return { ctx, agent, asked }
   }
 
@@ -631,7 +608,7 @@ describe('exit_plan_mode', () => {
 
   it('rejects a call outside plan mode while remaining advertised', async () => {
     const ctx = await setup()
-    const agent = agentWithSession()
+    const agent = await agentWithSession(ctx)
     expect(ctx.tools.schemas().map(tool => tool.name)).toContain(EXIT_PLAN_MODE)
     const result = await callExit(ctx, agent)
     expect(result.isError).toBe(true)
@@ -651,8 +628,7 @@ describe('exit_plan_mode', () => {
 
   it('degrades to the manual exit when no user-interaction seam is composed', async () => {
     const ctx = await setup()
-    const agent = agentWithSession()
-    agent.session.append('mode/set', { mode: PLAN_MODE })
+    const agent = await agentWithSession(ctx, 'agent-1', { mode: PLAN_MODE })
     const result = await callExit(ctx, agent)
     expect(result.isError).toBe(true)
     expect(result.content).toEqual([{ type: 'text', text: 'Error: no user-interaction channel is available to review the plan; ask the user to switch the session mode instead' }])
@@ -708,8 +684,7 @@ describe('exit_plan_mode', () => {
         return Promise.resolve({ answers: [{ id: 'plan-review', selected: ['Approve'] }] })
       },
     })
-    const agent = agentWithSession('code-mode-exit')
-    agent.session.append('mode/set', { mode: PLAN_MODE })
+    const agent = await agentWithSession(ctx, 'code-mode-exit', { mode: PLAN_MODE })
 
     const result = await ctx.tools.execute({
       callId: CallId(`call-exit-${++callCounter}`),
@@ -879,7 +854,7 @@ describe('HMR disposal', () => {
     await ctx.plugin(SystemPrompt)
     await ctx.plugin(ToolRegistry)
     const fiber = await ctx.plugin(ModesService, PLAN_CONFIG)
-    const agent = agentWithSession('disposed-in-flight-recovery')
+    const agent = await agentWithSession(ctx, 'disposed-in-flight-recovery')
     const recoveryEntered = Promise.withResolvers<true>()
     const releaseRecovery = Promise.withResolvers<true>()
     ctx.on('agent/request-error', async (_agent, _turn, _step, _error, _failure, _history, _signal, _next) => {
@@ -903,7 +878,7 @@ describe('HMR disposal', () => {
     await ctx.plugin(SystemPrompt)
     await ctx.plugin(ToolRegistry)
     const fiber = await ctx.plugin(ModesService, PLAN_CONFIG)
-    const agent = agentWithSession('disposed-recovery')
+    const agent = await agentWithSession(ctx, 'disposed-recovery')
     ctx.modes.set(agent, PLAN_MODE)
     expect(ctx.get('modes')).toBeInstanceOf(ModesService)
     expect(ctx.tools.get(EXIT_PLAN_MODE)).toBeDefined()

+ 3 - 0
packages/mode/mode/tsconfig.json

@@ -28,6 +28,9 @@
     },
     {
       "path": "../../ui/user-interaction"
+    },
+    {
+      "path": "../../ui/commands"
     }
   ]
 }

+ 3 - 0
pnpm-lock.yaml

@@ -1484,6 +1484,9 @@ importers:
       '@deepseek-ai/dsh-code-runtime':
         specifier: workspace:^
         version: link:../../code-runtime/code-runtime
+      '@deepseek-ai/dsh-commands':
+        specifier: workspace:^
+        version: link:../../ui/commands
       '@deepseek-ai/dsh-llm':
         specifier: workspace:^
         version: link:../../llm/llm

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.