authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-03-04 00:04:37-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-03-05 23:00:41-08:00
logdf6bb188d477f0e6affb089fc5496ff5ebc425f4
tree0db3107e966e38a062996d8b18cc1111e5ab9533
parent03053d9ee0db927881542c7075aa2fa239c3853d

Rewrite plan-mode deep dive as implementation blueprint

- Reframe doc into a provider-agnostic implementation plan for Codex, Claude, and Cursor - Define canonical mode, plan, and structured user-input models plus capability-driven UI behavior - Add concrete orchestration, adapter, frontend, and test work items for rollout

1 files changed, 460 insertions(+), 441 deletions(-)

docs/plan-mode-deep-dive.md+460-441
......@@ -1,629 +1,648 @@
1# Plan Mode Deep Dive
1# Plan Mode Implementation Plan
22
33## Goal
44
5Understand how the three providers expose:
6
7- planning / read-only execution modes
8- structured plan updates
9- structured user-input / question prompts
10- response plumbing needed for a provider-agnostic adapter layer
11
12Providers covered:
5Implement plan mode in a provider-agnostic way across:
136
147- Codex App Server
158- Claude Agent SDK / Claude Code
169- Cursor ACP
1710
18---
19
20## Existing T3 Code groundwork
21
22Current repo groundwork already exists in these places:
23
24- `EVENTS.md`
25- `packages/contracts/src/providerRuntime.ts`
26- `apps/server/src/provider/Layers/CodexAdapter.ts`
27- `apps/server/src/provider/Layers/ClaudeCodeAdapter.ts`
28- `apps/server/src/provider/Layers/CursorAdapter.ts`
29- `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts`
30
31Canonical runtime events already modeled:
11The implementation must support:
3212
33- `turn.plan.updated`
34- `content.delta` with `streamKind: "plan_text"`
35- `user-input.requested`
36- `user-input.resolved`
37- `request.opened`
38- `request.resolved`
13- provider-native or adapter-derived plan mode state
14- structured user-input question flows where available
15- graceful fallback when a provider lacks structured prompts or structured plan updates
16- a front-end UX that renders from capabilities and canonical events rather than provider-specific conditionals
3917
40This is a good base, but provider coverage is uneven:
18This document now serves as both:
4119
42- Codex: strong native support for both structured plans and structured user questions.
43- Claude: native plan mode exists, but plan updates are mostly implicit via tool/message stream; user questioning appears tool-based rather than a dedicated runtime event.
44- Cursor ACP: native plan mode exists, but current ACP surface appears to expose neither structured plan updates nor structured ask-user prompts in the same way as Codex.
20- the architecture plan for shared orchestration and UI wiring
21- the provider adapter implementation plan for each supported agent runtime
4522
4623---
4724
48## Provider 1: Codex App Server
49
50### What exists natively
51
52Codex has the richest first-class support for this feature set.
53
54### Real local session evidence
25## Source of truth
5526
56The local Codex rollout file confirms at least one important distinction:
27### Codex
5728
58- session metadata explicitly records collaboration mode as plan via `turn_context.payload.collaboration_mode.mode = "plan"`
59- final plan handoff is emitted as a normal assistant message containing a `<proposed_plan>` block
29Codex protocol decisions should be based on the open-source app-server protocol, not on local rollout session files.
6030
61From the sampled rollout file, I did **not** find raw `event_msg.plan_update` / `event_msg.plan_delta` records, which suggests either:
31Upstream Codex app-server explicitly supports:
6232
63- this particular session never emitted incremental structured plan updates, or
64- the desktop rollout file persists a higher-level message projection rather than every low-level app-server event
65
66That means the app-server protocol still supports native structured plan events, but local rollout files should not be assumed to contain every transport-level plan artifact.
67
68
69Native surfaces confirmed from upstream docs/source:
70
71- collaboration mode with plan behavior
7233- `turn/plan/updated`
7334- `item/plan/delta`
7435- `item/tool/requestUserInput`
75- `serverRequest/resolved` cleanup / completion notification for user input
76- `request_user_input` tool in core/runtime
36- `serverRequest/resolved`
37- lower-level `EventMsg` variants `plan_update`, `plan_delta`, and `request_user_input`
7738
78Evidence inspected:
39Important implication:
7940
80- upstream `codex-rs/app-server/README.md`
81- upstream `codex-rs/app-server-protocol` schemas
82- upstream `codex-rs/core/src/tools/handlers/request_user_input.rs`
83- upstream `codex-rs/core/templates/collaboration_mode/plan.md`
41- local Codex rollout JSONL files are a lossy or higher-level projection
42- they may preserve plan mode state and final `<proposed_plan>` output
43- they should not be treated as proof that low-level plan events do or do not exist
8444
85### Semantics
45### Claude
8646
87#### Plan mode
47Claude protocol decisions should be based on:
8848
89Codex plan mode is not just “tool execution denied”. It has explicit behavioral guidance and explicit planning outputs.
49- installed SDK typings
50- real local session JSONL evidence
9051
91Two distinct plan surfaces exist:
52Confirmed from local Claude transcript:
9253
931. **Structured plan state**
94 - `turn/plan/updated`
95 - payload contains:
96 - optional `explanation`
97 - `plan: Array<{ step, status }>`
98 - statuses are `pending | inProgress | completed`
54- `AskUserQuestion` is a native `tool_use`
55- `ExitPlanMode` is a native `tool_use`
56- `AskUserQuestion.input.questions[]` is structured and adapter-parseable
9957
1002. **Plan text stream**
101 - `item/plan/delta`
102 - useful for rendering streaming plan prose / bullet lists before or alongside structured state
58### Cursor
10359
104This maps very cleanly to our canonical events:
60Cursor protocol decisions should be based on:
10561
106- `turn/plan.updated` -> `turn.plan.updated`
107- `item/plan/delta` -> `content.delta(plan_text)`
62- ACP docs
63- current local ACP probe output
10864
109#### Structured user questions
65Confirmed from probe:
11066
111Codex has a dedicated `request_user_input` capability.
67- ACP session modes include `agent`, `plan`, and `ask`
68- currently observed ACP updates do not include native structured plan updates or native structured question prompts comparable to Codex or Claude
11269
113Server request:
70---
11471
115- `item/tool/requestUserInput`
72## High-level product model
11673
117Shape characteristics from upstream protocol/docs:
74The implementation should separate three concepts that are currently easy to conflate.
11875
119- 1–3 questions
120- each question has:
121 - prompt text
122 - stable id
123 - required options
124- response carries answers per question
125- freeform notes are supported in UX and answer model
76### 1. Operating mode
12677
127Important nuance:
78This is the agent runtime mode.
12879
129- upstream handler currently requires non-empty options for every question
130- Codex TUI also supports an optional freeform note / text alongside selected options
131- pending question requests are turn-scoped and cleaned up on turn completion/interruption
80Canonical operating modes:
13281
133### Why Codex is the reference model
82- `default`
83- `plan`
84- `ask`
85- `execute`
86- `unknown`
13487
135Codex should be treated as the reference shape for our provider-agnostic abstraction because it already matches the desired UI:
88This should be represented at runtime independently of plan content or prompt cards.
13689
137- explicit plan lifecycle
138- explicit multi-question prompt flow
139- explicit answer submission
140- explicit pending/resolved bookkeeping
90### 2. Structured plan state
14191
142### Adapter implications
92This is the current structured plan snapshot, if the provider can supply one natively or if the adapter can synthesize one confidently.
14393
144For Codex, the provider adapter layer should stay close to native protocol:
94Canonical model:
14595
146- keep `turn.plan.updated`
147- keep `content.delta(plan_text)`
148- map `item/tool/requestUserInput` to `user-input.requested` and `request.opened`
149- map answer completion / cleanup to `user-input.resolved` and `request.resolved`
96```ts
97interface CanonicalPlanState {
98 explanation?: string | null;
99 steps: Array<{
100 id?: string;
101 text: string;
102 status: 'pending' | 'inProgress' | 'completed';
103 source: 'native' | 'synthesized';
104 }>;
105}
106```
150107
151---
108### 3. Structured user-input prompt
152109
153## Provider 2: Claude Agent SDK / Claude Code
110This is the product-level question card model.
154111
155### What exists natively
156
157Claude has an explicit plan permission mode:
158
159### Real local session evidence
160
161The sampled Claude session file provides concrete payloads for both key plan-mode tools:
162
1631. `AskUserQuestion` appears as a normal assistant `tool_use` block with input like:
164
165```json
166{
167 "type": "tool_use",
168 "name": "AskUserQuestion",
169 "input": {
170 "questions": [
171 {
172 "question": "What does 'adding profiles to canvas' mean to you? What should profiles enable in canvas?",
173 "header": "Scope",
174 "options": [
175 { "label": "Organize by profile", "description": "..." },
176 { "label": "Profile icon bar in canvas", "description": "..." },
177 { "label": "Per-profile canvas settings", "description": "..." },
178 { "label": "All of the above", "description": "..." }
179 ],
180 "multiSelect": false
181 }
182 ]
183 }
184}
185```
112Canonical model:
186113
1872. `ExitPlanMode` also appears as a `tool_use` block, with a long-form `plan` string payload containing the finalized implementation spec.
114```ts
115interface CanonicalUserInputPrompt {
116 promptId: string;
117 title?: string;
118 description?: string;
119 questions: Array<{
120 id: string;
121 header?: string;
122 label: string;
123 description?: string;
124 options: Array<{
125 id: string;
126 label: string;
127 description?: string;
128 recommended?: boolean;
129 }>;
130 multiSelect?: boolean;
131 allowFreeform?: boolean;
132 freeformPlaceholder?: string;
133 required?: boolean;
134 }>;
135 source: 'native' | 'tool-derived' | 'synthesized';
136}
188137
189That gives us two strong adapter conclusions:
138interface CanonicalUserInputAnswer {
139 promptId: string;
140 answers: Array<{
141 questionId: string;
142 selectedOptionIds?: string[];
143 text?: string;
144 }>;
145}
146```
190147
191- Claude user-input prompting is definitely tool-based and structurally parseable.
192- Claude final plan handoff is tool-based too, not necessarily a dedicated transport event like Codex `turn/plan/updated`.
148This should remain distinct from approvals.
193149
194### Concrete adapter mapping from transcript evidence
150---
195151
196Recommended Claude mapping based on real session data:
152## Shared orchestration plan
197153
198- `tool_use.name === "AskUserQuestion"` -> `user-input.requested`
199- user answer submission / tool result -> `user-input.resolved`
200- `tool_use.name === "ExitPlanMode"` with `input.plan` -> final plan handoff event or synthesized `turn.plan.updated` snapshot
154## Summary
201155
202The `AskUserQuestion` payload shape is already close to the UI we want:
156Implement a canonical plan-mode interaction pipeline that starts in provider adapters, flows through provider runtime ingestion and orchestration projection, and terminates in capability-driven frontend rendering and response submission.
203157
204- `questions[]` array
205- each question has `header` and `question`
206- each question has `options[]` with `label` + `description`
207- `multiSelect` is explicit
158### Shared goals
208159
209The main remaining unknown is the exact runtime shape of the **answer** payload that Claude expects back for this tool.
160- normalize plan mode state from each provider
161- normalize structured prompts where possible
162- preserve native payloads for later refinement
163- avoid UI coupling to provider names
164- support degraded conversational fallback where native structured prompts are unavailable
210165
166---
211167
212- `PermissionMode = 'default' | 'acceptEdits' | 'bypassPermissions' | 'plan' | 'dontAsk'`
168## Orchestration work items
213169
214Confirmed from installed SDK typings:
170### 1. Extend canonical provider runtime contracts
215171
216- `/Users/julius/.bun/install/cache/@anthropic-ai/claude-agent-sdk@0.2.62@@@1/sdk.d.ts`
217- `/Users/julius/.bun/install/cache/@anthropic-ai/claude-agent-sdk@0.2.62@@@1/sdk-tools.d.ts`
172Files:
218173
219Claude also exposes plan-related tools/types:
174- `packages/contracts/src/providerRuntime.ts`
175- `packages/contracts/src/provider.ts`
176- `EVENTS.md`
220177
221- `ExitPlanMode`
222- `AskUserQuestion`
178Add or formalize the following runtime concepts:
223179
224These are strong signals that Claude’s plan UX is built around tool usage within a `permissionMode: 'plan'` session rather than a dedicated standalone `turn.plan.updated` event.
180- provider interactive capability payload
181- plan-mode state event or mode metadata surface
182- canonical structured user-input payload schema
183- canonical structured user-input answer payload schema
184- final-plan handoff payload for providers that emit a completed plan as text or tool output
225185
226### What the wire stream exposes
186Required additions:
227187
228Claude adapter input today is built from SDK messages like:
188```ts
189interface ProviderInteractiveCapabilities {
190 supportsPlanMode: boolean;
191 supportsAskMode: boolean;
192 supportsStructuredPlanUpdates: boolean;
193 supportsPlanTextStreaming: boolean;
194 supportsStructuredUserInput: boolean;
195 supportsFreeformUserInput: boolean;
196 supportsExitPlanMode: boolean;
197}
198```
229199
230- `system:init`
231- `system:status`
232- `stream_event`
233- `assistant`
234- `result`
235- hook/task/tool summary events
200Canonical runtime behavior:
236201
237The SDK stream exposes:
202- `request.opened/request.resolved` remain transport-oriented
203- `user-input.requested/user-input.resolved` remain product-oriented
204- `turn.plan.updated` remains the canonical structured plan update event
205- `content.delta(streamKind=plan_text)` remains the canonical streaming plan text surface
238206
239- message deltas
240- tool-use starts/stops via content blocks
241- permission-mode metadata
242- final result and usage
207### 2. Add canonical mode/capability publication from providers
243208
244What it does **not** appear to expose as a first-class event:
209Files:
245210
246- no direct equivalent to Codex `turn/plan/updated`
247- no direct equivalent to Codex `item/plan/delta`
248- no dedicated top-level `user-input.requested` transport event in the SDK stream
211- `apps/server/src/provider/Layers/*Adapter.ts`
212- `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts`
249213
250Instead, plan-mode-related behavior appears to surface through tool uses:
214Each adapter should publish provider capabilities and current operating mode early in session startup and when mode changes.
251215
252- `AskUserQuestion` tool
253- `ExitPlanMode` tool
254- regular content/tool stream events
216Preferred shape:
255217
256### Semantics
218- capabilities included in `session.configured`
219- current mode included in `session.configured`, `session.state.changed`, or a dedicated mode field on turn/session runtime metadata
257220
258#### Plan mode
221### 3. Preserve and project plan/user-input state in orchestration
259222
260Claude’s plan mode is primarily a **permission / behavior mode**:
223Files:
261224
262- session or turn is created with `permissionMode: 'plan'`
263- tool execution is prevented / constrained
264- assistant can explore, reason, and propose a plan
265- eventually it may call `ExitPlanMode` to transition toward implementation
225- `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts`
226- `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts`
227- any orchestration projection models feeding the web socket domain events
266228
267That means Claude plan mode is native, but **structured plan state is not guaranteed**.
229Implementation requirements:
268230
269In practice, there are two likely UI strategies:
231- persist latest structured plan snapshot per active turn
232- persist pending structured user-input prompts per turn
233- persist resolved answers for history rendering
234- preserve raw/native payloads for debugging and future adapter improvements
270235
2711. **Best-effort structured extraction**
272 - detect plan-like assistant/tool outputs
273 - synthesize `turn.plan.updated` from them
2742. **Text-first plan rendering**
275 - show reasoning / assistant output as plan narrative
276 - only emit `turn.plan.updated` when we can confidently extract steps
236### 4. Add frontend state model for plan mode
277237
278#### Asking user questions
238Files likely involved:
279239
280Claude’s `AskUserQuestion` appears to be a tool, not a dedicated transport-level question request primitive.
240- web app session/thread stores
241- conversation/event rendering layer
242- input composer / pending interaction surfaces
281243
282That means the adapter likely needs to:
244Add frontend state for:
283245
284- identify `tool_use` for `AskUserQuestion`
285- parse its input payload into canonical question schema
286- expose `user-input.requested`
287- accept a user answer and feed it back as the tool result / synthetic user response, depending SDK control path
246- current provider capabilities
247- current operating mode
248- pending structured prompt
249- structured plan snapshot
250- streaming plan text buffer
251- final plan handoff state when present
288252
289This is adapter work, not just schema plumbing.
253### 5. Implement capability-driven UI rendering
290254
291### Recommended stance for Claude
255UI rules:
292256
293Claude should support the same canonical user-facing contract, but the adapter will need to **manufacture structure from native tool activity**.
257- if `supportsStructuredUserInput`, render the multi-question card UI
258- if not, fall back to standard conversational reply flow
259- if `supportsStructuredPlanUpdates`, render status-tracked plan steps
260- if only `supportsPlanTextStreaming`, render streaming plan prose
261- if neither exists, render standard assistant content in plan mode with clear mode indicator
294262
295Recommended mapping:
263### 6. Implement front-to-back response submission path
296264
297- `permissionMode: 'plan'` => canonical session/turn metadata `mode = plan`
298- `AskUserQuestion` tool use => canonical `user-input.requested`
299- submitted answer => canonical `user-input.resolved`
300- `ExitPlanMode` tool use => canonical state transition or request to continue with implementation
301- assistant text / tool summaries in plan mode => optional synthesized `turn.plan.updated`
265Requirements:
302266
303### Risk / uncertainty
267- question card submit sends canonical answer payload to server
268- server routes answer to the correct provider adapter pending request/tool context
269- resolved provider response emits both:
270 - `user-input.resolved`
271 - `request.resolved` when applicable
304272
305Claude currently appears less deterministic than Codex for structured plans.
273### 7. History and reconnect behavior
306274
307Main unknowns to resolve during implementation:
275Requirements:
308276
309- exact `AskUserQuestion` tool input/output payload shapes at runtime
310- whether freeform responses are supported natively or need synthetic augmentation
311- whether `ExitPlanMode` should be exposed to UI explicitly or handled internally by the adapter/session controller
277- pending structured prompts should survive reconnect/resubscribe if still active
278- resolved prompts should be rendered in history as completed interactions
279- plan snapshots should be replayable from persisted orchestration activity
280- current streaming plan text should resume cleanly on reconnect if provider continues emitting deltas
312281
313282---
314283
315## Provider 3: Cursor ACP
284## Provider adapter plans
316285
317### What exists natively
286## Provider 1: Codex App Server
318287
319Cursor ACP does have native session modes.
288### Summary
320289
321Confirmed by local probe output in `.tmp/acp-plan-probe/summary.json`:
290Codex is the reference implementation and should be wired as the strongest native provider.
322291
323- `agent`
324- `plan`
325- `ask`
326
327The probe also confirmed current primary ACP update surfaces:
292### Native protocol mapping
328293
329- `available_commands_update`
330- `agent_thought_chunk`
331- `agent_message_chunk`
332- `tool_call`
333- `tool_call_update`
334- `session/request_permission`
294Source of truth: upstream open-source app-server protocol.
335295
336### What the probe did not find
296Native incoming surfaces:
337297
338The existing probe did **not** surface:
298- `turn/plan/updated`
299- `item/plan/delta`
300- `item/tool/requestUserInput`
301- `serverRequest/resolved`
302- lower-level `EventMsg.plan_update`
303- lower-level `EventMsg.plan_delta`
304- lower-level `EventMsg.request_user_input`
339305
340- structured plan update events
341- a dedicated “ask user question” request
342- a dedicated plan delta stream
306### Adapter implementation
343307
344The current ACP surface therefore looks much thinner than Codex.
308Files:
345309
346### Semantics
310- `apps/server/src/provider/Layers/CodexAdapter.ts`
311- related tests in `apps/server/src/provider/Layers/*Codex*.test.ts`
347312
348#### Plan mode
313Implementation requirements:
349314
350Cursor `plan` mode clearly exists as a session mode, but as currently observed it seems to mean:
3151. Keep native structured plan updates as-is:
316- `turn/plan/updated` -> `turn.plan.updated`
351317
352- read-only / design-first behavior
353- regular thought/message chunk streaming
354- no dedicated structured plan payload
3182. Keep native plan text streaming as-is:
319- `item/plan/delta` -> `content.delta` with `streamKind: "plan_text"`
355320
356So the plan is native as a mode, but not obviously as a structured protocol surface.
3213. Keep native structured user-input as-is:
322- `item/tool/requestUserInput` -> `user-input.requested`
323- also emit `request.opened` with a transport request type
357324
358#### Asking the user questions
3254. On client answer submission:
326- route answer to the corresponding Codex pending request id
327- emit `user-input.resolved`
328- observe/forward `serverRequest/resolved` -> `request.resolved`
359329
360Cursor ACP currently gives us a nearby primitive only for **permission requests**:
3305. Publish capabilities:
331- `supportsPlanMode = true`
332- `supportsAskMode = false` unless Codex exposes a distinct ask mode separately
333- `supportsStructuredPlanUpdates = true`
334- `supportsPlanTextStreaming = true`
335- `supportsStructuredUserInput = true`
336- `supportsFreeformUserInput = true` if answer payload supports note/text in practice
337- `supportsExitPlanMode = false` unless a separate explicit tool/event is introduced
361338
362- `session/request_permission`
339### Codex-specific tests
363340
364That is not the same as product-level question asking.
341- `turn/plan/updated` maps to canonical structured plan state
342- `item/plan/delta` maps to `plan_text`
343- `item/tool/requestUserInput` maps to canonical structured prompt shape
344- answer response resolves pending request and emits both canonical resolved events
345- reconnect/history replay preserves plan snapshot and pending prompt
365346
366The existence of `ask` mode suggests Cursor may handle asking by:
347---
367348
368- normal assistant prose in ask mode
369- possibly mode-switch semantics
370- possibly skills/commands that we have not yet triggered
349## Provider 2: Claude Agent SDK / Claude Code
371350
372But based on current probe evidence, we should **not assume** a Codex-style structured question API exists in ACP.
351### Summary
373352
374### Recommended stance for Cursor
353Claude requires tool-aware adaptation. Native structured interaction exists, but it is surfaced through `tool_use` blocks rather than dedicated transport events equivalent to Codex.
375354
376Treat Cursor as:
355### Real native evidence
377356
378- native support for `mode = plan`
379- no proven native support for structured plan steps
380- no proven native support for structured user-input questions
357From the local Claude session transcript:
381358
382Therefore Cursor likely needs the most adapter synthesis:
359- `AskUserQuestion` arrives as:
360 - assistant message
361 - content block type `tool_use`
362 - `name: "AskUserQuestion"`
363 - `input.questions[]`
364- `ExitPlanMode` arrives as:
365 - assistant message
366 - content block type `tool_use`
367 - `name: "ExitPlanMode"`
368 - `input.plan` containing finalized plan text/spec
383369
384- derive plan-mode state from ACP session mode
385- optionally synthesize `turn.plan.updated` from assistant text if we can extract steps safely
386- for interactive questions, likely fall back to regular conversational turns unless a richer ACP mechanism is discovered
370Claude also has native:
387371
388### Important product implication
372- `permissionMode: 'plan'`
373- `system:init.permissionMode`
374- `system:status.permissionMode`
389375
390If we want the same rich “multiple-choice question card” UX across all providers, Cursor may need one of these paths:
376### Adapter implementation
391377
3921. **Provider-native path** if later ACP exploration finds structured user question primitives
3932. **Adapter-mediated path** where the provider is instructed to emit a machine-readable question block in text and adapter parses it
3943. **Capability downgrade** where Cursor only supports conversational follow-up, not full structured plan-question cards
378Files:
395379
396Today, evidence supports option 3 as the safe baseline.
380- `apps/server/src/provider/Layers/ClaudeCodeAdapter.ts`
381- `apps/server/src/provider/Layers/ClaudeCodeAdapter.test.ts`
382
383Implementation requirements:
384
3851. Publish native operating mode:
386- `permissionMode: 'plan'` -> canonical mode `plan`
387- if future values map cleanly, publish `ask` or `default` accordingly
388
3892. Detect `AskUserQuestion` tool uses:
390- inspect `tool_use` blocks in assistant messages / stream events
391- when `name === 'AskUserQuestion'`, convert `input.questions[]` to canonical `user-input.requested`
392- also emit `request.opened` using a Claude-specific tool-user-input request type
393
3943. Canonical question mapping for Claude:
395- `header` -> canonical `header`
396- `question` -> canonical `label`
397- `options[].label` -> canonical option label
398- `options[].description` -> canonical option description
399- `multiSelect` -> canonical `multiSelect`
400- source = `tool-derived`
401
4024. Implement answer submission path:
403- on UI answer submit, convert canonical answers back into whatever Claude expects for the `AskUserQuestion` tool result path
404- emit `user-input.resolved`
405- emit `request.resolved`
406
4075. Detect `ExitPlanMode` tool uses:
408- when `name === 'ExitPlanMode'`, parse `input.plan`
409- emit a final plan handoff event and/or synthesize a `turn.plan.updated` snapshot if safe
410- treat this as the boundary between planning and implementation readiness
411
4126. Structured plan behavior:
413- Claude does not currently appear to expose a native `turn/plan/updated` equivalent
414- initial implementation should not over-synthesize incremental steps
415- use two-tier strategy:
416 - tier 1: preserve plan-related assistant text in `plan_text` only if clearly attributable
417 - tier 2: optionally synthesize `turn.plan.updated` from explicit structured plan strings only when parsing is robust
418
4197. Publish capabilities:
420- `supportsPlanMode = true`
421- `supportsAskMode = false` unless explicit Claude mode is observed
422- `supportsStructuredPlanUpdates = partial/false initially`
423- `supportsPlanTextStreaming = partial`
424- `supportsStructuredUserInput = true`
425- `supportsFreeformUserInput = unknown/false until answer path confirmed`
426- `supportsExitPlanMode = true`
427
428### Claude-specific tests
429
430- `system:init` and `system:status` publish plan mode metadata
431- `AskUserQuestion` tool maps into canonical prompt schema
432- multi-select and option descriptions round-trip correctly
433- answer submission resolves the pending prompt
434- `ExitPlanMode` tool emits final plan handoff state
435- ordinary tool uses do not get misclassified as prompts or plan exits
436
437### Claude-specific open question to resolve during implementation
438
439- exact runtime shape expected for the answer payload returned to `AskUserQuestion`
440
441Implementation default:
442
443- build adapter abstraction so only the final answer-serialization function is provider-specific and easily swappable once runtime shape is confirmed
397444
398445---
399446
400## Cross-provider comparison
447## Provider 3: Cursor ACP
401448
402### Capability matrix
449### Summary
403450
404| Capability | Codex App Server | Claude Agent SDK | Cursor ACP |
405| --- | --- | --- | --- |
406| Native plan mode | Yes | Yes | Yes |
407| Structured plan update event | Yes | Not obvious / likely no | Not observed |
408| Plan text delta stream | Yes | Not first-class | Not observed |
409| Native structured user question API | Yes | Tool-based, likely yes-ish | Not observed |
410| Native answer resolution lifecycle | Yes | Likely adapter-mediated | Not observed |
411| Best provider for canonical contract | Strong | Medium | Weak |
451Cursor should initially ship with degraded plan-mode support: native mode awareness, but no claim of structured prompt cards or structured plan steps until protocol evidence exists.
412452
413### Core insight
453### Real native evidence
414454
415“Plan mode” means **different things** across providers:
455From the ACP probe:
416456
417- **Codex**: plan mode is a first-class runtime concept with structured plan and question surfaces.
418- **Claude**: plan mode is a first-class permission mode, but structured plan/question UI likely needs adapter interpretation.
419- **Cursor**: plan mode is a first-class session mode, but richer plan/question structure is not currently exposed.
457- available modes include `agent`, `plan`, and `ask`
458- observed updates include:
459 - `available_commands_update`
460 - `agent_thought_chunk`
461 - `agent_message_chunk`
462 - `tool_call`
463 - `tool_call_update`
464- observed request type:
465 - `session/request_permission`
420466
421---
467Not observed:
422468
423## Recommended provider-agnostic contract
469- native structured plan update events
470- native plan text delta event distinct from normal message chunks
471- native structured ask-user-question event
424472
425We should separate three concerns that are currently easy to conflate.
473### Adapter implementation
426474
427### 1. Session / turn mode
475Files:
428476
429A canonical mode flag:
430
431- `default`
432- `plan`
433- `ask`
434- `execute`
435- `unknown`
477- `apps/server/src/provider/Layers/CursorAdapter.ts`
478- `apps/server/src/provider/Layers/CursorAdapter.test.ts`
479- probe scripts under `scripts/`
436480
437This is about **agent operating mode**, not UI widgets.
481Implementation requirements:
438482
439### 2. Structured plan state
4831. Publish native operating mode:
484- map ACP `plan` mode -> canonical `plan`
485- map ACP `ask` mode -> canonical `ask`
486- map ACP `agent` mode -> canonical `default` or `execute` depending product semantics
440487
441A canonical plan model:
4882. Do not claim unsupported structured capabilities:
489- no native `turn.plan.updated`
490- no native `user-input.requested`
491- no native `ExitPlanMode` equivalent confirmed
442492
443```ts
444interface CanonicalPlanState {
445 explanation?: string | null;
446 steps: Array<{
447 id?: string;
448 text: string;
449 status: 'pending' | 'inProgress' | 'completed';
450 source: 'native' | 'synthesized';
451 }>;
452}
453```
4933. Use graceful fallback behavior:
494- render plan mode as mode state + assistant text stream
495- render ask mode as conversational mode + normal user composer
496- do not render structured question cards for Cursor initially
454497
455Rules:
4984. Preserve room for future enrichment:
499- keep raw ACP notifications available in native event logs
500- extend the ACP probe to search for hidden or uncommon prompt/mode-change surfaces
456501
457- Codex can populate this natively.
458- Claude/Cursor may populate it only when extraction is confident.
459- UI should tolerate absence of structured plan state.
5025. Publish capabilities:
503- `supportsPlanMode = true`
504- `supportsAskMode = true`
505- `supportsStructuredPlanUpdates = false`
506- `supportsPlanTextStreaming = false` initially
507- `supportsStructuredUserInput = false`
508- `supportsFreeformUserInput = false` via structured prompt path
509- `supportsExitPlanMode = false`
460510
461### 3. Structured user-input prompt
511### Cursor-specific tests
462512
463Canonical prompt should be modeled independently of approvals:
513- ACP mode metadata maps correctly into canonical mode
514- `agent_thought_chunk` and `agent_message_chunk` still render normally in plan mode
515- UI does not try to open structured prompt cards for Cursor
516- fallback conversational flow remains functional
464517
465```ts
466interface CanonicalUserInputPrompt {
467 promptId: string;
468 title?: string;
469 description?: string;
470 questions: Array<{
471 id: string;
472 label: string;
473 description?: string;
474 options: Array<{
475 id: string;
476 label: string;
477 description?: string;
478 recommended?: boolean;
479 }>;
480 allowFreeform?: boolean;
481 freeformPlaceholder?: string;
482 required?: boolean;
483 }>;
484 source: 'native' | 'tool-derived' | 'synthesized';
485}
486```
518### Cursor-specific follow-up probe work
487519
488And answers:
520Add dedicated probes for:
489521
490```ts
491interface CanonicalUserInputAnswer {
492 promptId: string;
493 answers: Array<{
494 questionId: string;
495 selectedOptionId?: string;
496 text?: string;
497 }>;
498}
499```
522- mode-switching during an active session
523- ask-mode prompt behavior
524- any request types besides `session/request_permission`
525- whether specific built-in skills or prompt styles trigger structured question surfaces
500526
501### Why separate `request.opened` from `user-input.requested`
527---
502528
503Because they are not the same abstraction:
529## Frontend implementation plan
504530
505- approvals = “may I run/edit/do X?”
506- user-input prompts = “which direction do you want?”
531### Summary
507532
508Codex already blurs these at transport level because `item/tool/requestUserInput` is a request.
509But product and UI should keep them separate.
533Render plan mode through a single UI model driven by canonical events and provider capabilities.
510534
511Recommended rule:
535### UI states to support
512536
513- keep `request.opened/request.resolved` for transport-level correlation
514- keep `user-input.requested/user-input.resolved` for product-level UX
537- standard conversation
538- plan mode with structured steps
539- plan mode with text-only plan stream
540- pending structured question card
541- resolved question card in history
542- final plan handoff / completed plan artifact
515543
516---
544### Rendering rules
517545
518## Adapter strategy by provider
5461. Show operating mode indicator whenever current mode is `plan` or `ask`
5472. Show structured question card only when a pending `user-input.requested` exists
5483. Show structured plan step list when a current `turn.plan.updated` snapshot exists
5494. Append `plan_text` streaming content beneath or alongside structured steps when both exist
5505. Fall back to assistant text rendering when provider capabilities do not support structure
5516. Show completed selected answers in history after `user-input.resolved`
519552
520### Codex adapter
553### Submission behavior
521554
522Use native primitives directly.
555- option click / freeform answer submits canonical answer payload
556- disable duplicate submits while request is pending resolution
557- preserve pending-card state across reconnects if request remains open
523558
524Implementation target:
559---
525560
526- preserve `turn.plan.updated`
527- preserve `content.delta(plan_text)`
528- preserve `user-input.requested`
529- preserve `user-input.resolved`
530- expose raw/native metadata for future UI improvements
561## Data flow end to end
531562
532### Claude adapter
5631. Provider starts session and publishes capabilities + mode
5642. User starts plan-mode turn or provider enters plan mode
5653. Adapter emits structured plan and/or plan text events when available
5664. Adapter emits `user-input.requested` when provider asks a structured question
5675. Frontend renders question card or fallback conversational prompt based on capabilities
5686. User submits answer
5697. Server routes answer back to provider adapter pending request/tool context
5708. Adapter emits `user-input.resolved` and `request.resolved`
5719. Provider may continue planning, emit more plan updates, or emit final plan handoff
57210. Frontend renders final completed plan state and history
533573
534Implement tool-aware adaptation.
574---
535575
536Implementation target:
576## Test plan
537577
538- treat `permissionMode: 'plan'` as native plan-mode state
539- detect `AskUserQuestion` tool uses and convert to canonical user-input prompt
540- convert question answers back into Claude-native tool response path
541- detect `ExitPlanMode` and emit a canonical mode-change / handoff event
542- optionally synthesize plan steps from assistant output in plan mode
578### Contracts
543579
544### Cursor adapter
580Files:
545581
546Implement conservative degraded support first.
582- `packages/contracts/src/providerRuntime.test.ts`
583- related schema tests
547584
548Implementation target:
585Add coverage for:
549586
550- expose native session mode including `plan` and `ask`
551- do not claim structured plan updates unless proven
552- do not claim structured user-input prompts unless proven
553- optionally add a provider capability flag so UI can fall back to conversational follow-up
587- provider capabilities schema
588- structured user-input prompt schema
589- structured user-input answer schema
590- mode metadata schema
554591
555---
592### Server/provider tests
556593
557## Capability flags we should add
594Files:
558595
559To avoid overpromising in the UI, add provider runtime capabilities such as:
596- `apps/server/src/provider/Layers/CodexAdapter.test.ts`
597- `apps/server/src/provider/Layers/ClaudeCodeAdapter.test.ts`
598- `apps/server/src/provider/Layers/CursorAdapter.test.ts`
599- `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts`
560600
561```ts
562interface ProviderInteractiveCapabilities {
563 supportsPlanMode: boolean;
564 supportsAskMode: boolean;
565 supportsStructuredPlanUpdates: boolean;
566 supportsPlanTextStreaming: boolean;
567 supportsStructuredUserInput: boolean;
568 supportsFreeformUserInput: boolean;
569 supportsExitPlanMode: boolean;
570}
571```
601Add coverage for:
572602
573Expected initial values:
603- capabilities emission
604- mode propagation
605- plan update propagation
606- plan text propagation
607- structured prompt lifecycle
608- answer resolution lifecycle
609- reconnect/history replay behavior
574610
575- Codex: all or nearly all `true`
576- Claude: `supportsPlanMode=true`, `supportsStructuredUserInput=likely true via adapter`, `supportsStructuredPlanUpdates=partial`
577- Cursor: `supportsPlanMode=true`, `supportsAskMode=true`, most structured flags `false`
611### Web/UI tests
578612
579This lets the UI:
613Add coverage for:
580614
581- render rich plan cards only when supported
582- fall back to plain assistant text + reply box otherwise
583- avoid coupling the design to Codex-specific assumptions
615- question card rendering from canonical prompt
616- question answer submission
617- resolved question history rendering
618- structured plan step rendering
619- text-only plan rendering
620- capability-based fallback rendering for Cursor
584621
585---
622### Probe / fixture tests
586623
587## Suggested implementation order
588
5891. **Formalize provider capability flags**
590 - make support explicit before UI work
5912. **Finish canonical user-input contracts**
592 - question schema, answer schema, source markers
5933. **Codex end-to-end implementation first**
594 - closest match to desired UX
5954. **Claude adapter translation second**
596 - parse `AskUserQuestion` and `ExitPlanMode`
5975. **Cursor degraded plan-mode support third**
598 - mode awareness first, richer structure later
5996. **UI renders by capability, not provider name**
600 - prevents provider-specific branching from leaking upward
624- preserve the Claude transcript-derived `AskUserQuestion` and `ExitPlanMode` payloads as fixtures
625- preserve Codex protocol fixtures for `turn/plan/updated`, `item/plan/delta`, and `item/tool/requestUserInput`
626- preserve ACP probe summaries as fixtures for unsupported-capability assertions
601627
602628---
603629
604## Open questions
630## Assumptions and defaults
605631
6061. Claude runtime payloads:
607 - what exact `AskUserQuestion` input/output shapes arrive over the stream?
6082. Claude answer submission path:
609 - should answers be injected as tool results, control responses, or synthetic user turns?
6103. Cursor ACP:
611 - is there an undocumented or less common interactive prompt primitive beyond `session/request_permission`?
6124. Cursor mode switching:
613 - can ACP session mode change mid-session, and is that surfaced as `session/update` or request/response only?
6145. Plan synthesis:
615 - do we want best-effort extraction of numbered/bulleted plans into `turn.plan.updated` for Claude/Cursor, or should we keep structured plans native-only at first?
632- Codex is the reference provider for full structured plan/question UX.
633- Claude supports structured prompts via tool adaptation, but incremental structured plan updates are not assumed initially.
634- Cursor supports plan and ask modes, but not structured prompt cards or structured plan step updates initially.
635- Product/UI should degrade gracefully rather than invent unsupported provider behavior.
636- Raw provider payloads should always be retained where feasible to support future adapter refinement.
616637
617638---
618639
619## Immediate recommendation
620
621Build the product contract around **capabilities**, not around the assumption that every provider has Codex-style plan/question primitives.
622
623Concretely:
624
625- Codex should drive the first full UX implementation.
626- Claude should be supported with adapter-derived question handling and partial/synthesized plan state.
627- Cursor should initially support mode-aware UX with graceful fallback to conversational replies rather than full structured question cards.
640## Recommended execution order
628641
629That gives us a provider-agnostic architecture without pretending the underlying protocols are already equivalent.
6421. Formalize canonical capabilities + mode + structured prompt contracts
6432. Wire capabilities/mode through orchestration and web socket projections
6443. Finish Codex end-to-end plan mode implementation first
6454. Implement Claude `AskUserQuestion` and `ExitPlanMode` adapter mapping
6465. Add Cursor degraded plan/ask mode support
6476. Build capability-driven frontend rendering and answer submission
6487. Add reconnect/history coverage and transcript/protocol fixtures