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,629 +1,648 @@
1# Plan Mode Deep Dive1# Plan Mode Implementation Plan
22
3## Goal3## Goal
44
5Understand how the three providers expose:5Implement plan mode in a provider-agnostic way across:
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:
136
14- Codex App Server7- Codex App Server
15- Claude Agent SDK / Claude Code8- Claude Agent SDK / Claude Code
16- Cursor ACP9- Cursor ACP
1710
18---11The implementation must support:
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:
3212
33- `turn.plan.updated`13- provider-native or adapter-derived plan mode state
34- `content.delta` with `streamKind: "plan_text"`14- structured user-input question flows where available
35- `user-input.requested`15- graceful fallback when a provider lacks structured prompts or structured plan updates
36- `user-input.resolved`16- a front-end UX that renders from capabilities and canonical events rather than provider-specific conditionals
37- `request.opened`
38- `request.resolved`
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.20- the architecture plan for shared orchestration and UI wiring
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.21- the provider adapter implementation plan for each supported agent runtime
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.
4522
46---23---
4724
48## Provider 1: Codex App Server25## Source of truth
49
50### What exists natively
51
52Codex has the richest first-class support for this feature set.
53
54### Real local session evidence
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"`29Codex protocol decisions should be based on the open-source app-server protocol, not on local rollout session files.
59- final plan handoff is emitted as a normal assistant message containing a `<proposed_plan>` block
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
72- `turn/plan/updated`33- `turn/plan/updated`
73- `item/plan/delta`34- `item/plan/delta`
74- `item/tool/requestUserInput`35- `item/tool/requestUserInput`
75- `serverRequest/resolved` cleanup / completion notification for user input36- `serverRequest/resolved`
76- `request_user_input` tool in core/runtime37- 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`41- local Codex rollout JSONL files are a lossy or higher-level projection
81- upstream `codex-rs/app-server-protocol` schemas42- they may preserve plan mode state and final `<proposed_plan>` output
82- upstream `codex-rs/core/src/tools/handlers/request_user_input.rs`43- they should not be treated as proof that low-level plan events do or do not exist
83- upstream `codex-rs/core/templates/collaboration_mode/plan.md`
8444
85### Semantics45### Claude
8646
87#### Plan mode47Claude 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**54- `AskUserQuestion` is a native `tool_use`
94 - `turn/plan/updated`55- `ExitPlanMode` is a native `tool_use`
95 - payload contains:56- `AskUserQuestion.input.questions[]` is structured and adapter-parseable
96 - optional `explanation`
97 - `plan: Array<{ step, status }>`
98 - statuses are `pending | inProgress | completed`
9957
1002. **Plan text stream**58### Cursor
101 - `item/plan/delta`
102 - useful for rendering streaming plan prose / bullet lists before or alongside structured state
10359
104This maps very cleanly to our canonical events:60Cursor protocol decisions should be based on:
10561
106- `turn/plan.updated` -> `turn.plan.updated`62- ACP docs
107- `item/plan/delta` -> `content.delta(plan_text)`63- current local ACP probe output
10864
109#### Structured user questions65Confirmed 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 questions76### 1. Operating mode
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
12677
127Important nuance:78This is the agent runtime mode.
12879
129- upstream handler currently requires non-empty options for every question80Canonical operating modes:
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
13281
133### Why Codex is the reference model82- `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 lifecycle90### 2. Structured plan state
138- explicit multi-question prompt flow
139- explicit answer submission
140- explicit pending/resolved bookkeeping
14191
142### Adapter implications92This 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`96```ts
147- keep `content.delta(plan_text)`97interface CanonicalPlanState {
148- map `item/tool/requestUserInput` to `user-input.requested` and `request.opened`98 explanation?: string | null;
149- map answer completion / cleanup to `user-input.resolved` and `request.resolved`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 Code110This is the product-level question card model.
154111
155### What exists natively112Canonical model:
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```
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.148This should remain distinct from approvals.
192- Claude final plan handoff is tool-based too, not necessarily a dedicated transport event like Codex `turn/plan/updated`.
193149
194### Concrete adapter mapping from transcript evidence150---
195151
196Recommended Claude mapping based on real session data:152## Shared orchestration plan
197153
198- `tool_use.name === "AskUserQuestion"` -> `user-input.requested`154## Summary
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
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[]` array158### Shared goals
205- each question has `header` and `question`
206- each question has `options[]` with `label` + `description`
207- `multiSelect` is explicit
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`172Files:
217- `/Users/julius/.bun/install/cache/@anthropic-ai/claude-agent-sdk@0.2.62@@@1/sdk-tools.d.ts`
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`178Add or formalize the following runtime concepts:
222- `AskUserQuestion`
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 exposes186Required 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`200Canonical runtime behavior:
231- `system:status`
232- `stream_event`
233- `assistant`
234- `result`
235- hook/task/tool summary events
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 deltas207### 2. Add canonical mode/capability publication from providers
240- tool-use starts/stops via content blocks
241- permission-mode metadata
242- final result and usage
243208
244What it does **not** appear to expose as a first-class event:209Files:
245210
246- no direct equivalent to Codex `turn/plan/updated`211- `apps/server/src/provider/Layers/*Adapter.ts`
247- no direct equivalent to Codex `item/plan/delta`212- `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts`
248- no dedicated top-level `user-input.requested` transport event in the SDK stream
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` tool216Preferred shape:
253- `ExitPlanMode` tool
254- regular content/tool stream events
255217
256### Semantics218- 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 mode221### 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'`225- `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts`
263- tool execution is prevented / constrained226- `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts`
264- assistant can explore, reason, and propose a plan227- any orchestration projection models feeding the web socket domain events
265- eventually it may call `ExitPlanMode` to transition toward implementation
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**236### 4. Add frontend state model for plan mode
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
277237
278#### Asking user questions238Files 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`246- current provider capabilities
285- parse its input payload into canonical question schema247- current operating mode
286- expose `user-input.requested`248- pending structured prompt
287- accept a user answer and feed it back as the tool result / synthetic user response, depending SDK control path249- 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 Claude255UI 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`265Requirements:
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`
302266
303### Risk / uncertainty267- 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 runtime277- pending structured prompts should survive reconnect/resubscribe if still active
310- whether freeform responses are supported natively or need synthetic augmentation278- resolved prompts should be rendered in history as completed interactions
311- whether `ExitPlanMode` should be exposed to UI explicitly or handled internally by the adapter/session controller279- 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
313---282---
314283
315## Provider 3: Cursor ACP284## Provider adapter plans
316285
317### What exists natively286## 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`292### Native protocol mapping
324- `plan`
325- `ask`
326
327The probe also confirmed current primary ACP update surfaces:
328293
329- `available_commands_update`294Source of truth: upstream open-source app-server protocol.
330- `agent_thought_chunk`
331- `agent_message_chunk`
332- `tool_call`
333- `tool_call_update`
334- `session/request_permission`
335295
336### What the probe did not find296Native 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 events306### Adapter implementation
341- a dedicated “ask user question” request
342- a dedicated plan delta stream
343307
344The current ACP surface therefore looks much thinner than Codex.308Files:
345309
346### Semantics310- `apps/server/src/provider/Layers/CodexAdapter.ts`
311- related tests in `apps/server/src/provider/Layers/*Codex*.test.ts`
347312
348#### Plan mode313Implementation 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 behavior3182. Keep native plan text streaming as-is:
353- regular thought/message chunk streaming319- `item/plan/delta` -> `content.delta` with `streamKind: "plan_text"`
354- no dedicated structured plan payload
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 questions3254. 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 mode349## Provider 2: Claude Agent SDK / Claude Code
369- possibly mode-switch semantics
370- possibly skills/commands that we have not yet triggered
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 Cursor353Claude 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`357From the local Claude session transcript:
379- no proven native support for structured plan steps
380- no proven native support for structured user-input questions
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 mode370Claude also has native:
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
387371
388### Important product implication372- `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 primitives378Files:
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
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
398---445---
399446
400## Cross-provider comparison447## Provider 3: Cursor ACP
401448
402### Capability matrix449### Summary
403450
404| Capability | Codex App Server | Claude Agent SDK | Cursor ACP |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.
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 |
412452
413### Core insight453### 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.457- available modes include `agent`, `plan`, and `ask`
418- **Claude**: plan mode is a first-class permission mode, but structured plan/question UI likely needs adapter interpretation.458- observed updates include:
419- **Cursor**: plan mode is a first-class session mode, but richer plan/question structure is not currently exposed.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 contract469- 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 mode475Files:
428476
429A canonical mode flag:477- `apps/server/src/provider/Layers/CursorAdapter.ts`
430478- `apps/server/src/provider/Layers/CursorAdapter.test.ts`
431- `default`479- probe scripts under `scripts/`
432- `plan`
433- `ask`
434- `execute`
435- `unknown`
436480
437This is about **agent operating mode**, not UI widgets.481Implementation requirements:
438482
439### 2. Structured plan state4831. 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```ts4933. Use graceful fallback behavior:
444interface CanonicalPlanState {494- render plan mode as mode state + assistant text stream
445 explanation?: string | null;495- render ask mode as conversational mode + normal user composer
446 steps: Array<{496- do not render structured question cards for Cursor initially
447 id?: string;
448 text: string;
449 status: 'pending' | 'inProgress' | 'completed';
450 source: 'native' | 'synthesized';
451 }>;
452}
453```
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.5025. Publish capabilities:
458- Claude/Cursor may populate it only when extraction is confident.503- `supportsPlanMode = true`
459- UI should tolerate absence of structured plan state.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 prompt511### 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```ts518### Cursor-specific follow-up probe work
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```
487519
488And answers:520Add dedicated probes for:
489521
490```ts522- mode-switching during an active session
491interface CanonicalUserInputAnswer {523- ask-mode prompt behavior
492 promptId: string;524- any request types besides `session/request_permission`
493 answers: Array<{525- whether specific built-in skills or prompt styles trigger structured question surfaces
494 questionId: string;
495 selectedOptionId?: string;
496 text?: string;
497 }>;
498}
499```
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?”531### Summary
506- user-input prompts = “which direction do you want?”
507532
508Codex already blurs these at transport level because `item/tool/requestUserInput` is a request.533Render plan mode through a single UI model driven by canonical events and provider capabilities.
509But product and UI should keep them separate.
510534
511Recommended rule:535### UI states to support
512536
513- keep `request.opened/request.resolved` for transport-level correlation537- standard conversation
514- keep `user-input.requested/user-input.resolved` for product-level UX538- 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 provider5461. 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 adapter553### 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`561## Data flow end to end
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
531562
532### Claude adapter5631. 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 state578### Contracts
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
543579
544### Cursor adapter580Files:
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`587- provider capabilities schema
551- do not claim structured plan updates unless proven588- structured user-input prompt schema
552- do not claim structured user-input prompts unless proven589- structured user-input answer schema
553- optionally add a provider capability flag so UI can fall back to conversational follow-up590- mode metadata schema
554591
555---592### Server/provider tests
556593
557## Capability flags we should add594Files:
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```ts601Add coverage for:
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```
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`611### Web/UI tests
576- Claude: `supportsPlanMode=true`, `supportsStructuredUserInput=likely true via adapter`, `supportsStructuredPlanUpdates=partial`
577- Cursor: `supportsPlanMode=true`, `supportsAskMode=true`, most structured flags `false`
578612
579This lets the UI:613Add coverage for:
580614
581- render rich plan cards only when supported615- question card rendering from canonical prompt
582- fall back to plain assistant text + reply box otherwise616- question answer submission
583- avoid coupling the design to Codex-specific assumptions617- 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 order624- preserve the Claude transcript-derived `AskUserQuestion` and `ExitPlanMode` payloads as fixtures
588625- preserve Codex protocol fixtures for `turn/plan/updated`, `item/plan/delta`, and `item/tool/requestUserInput`
5891. **Formalize provider capability flags**626- preserve ACP probe summaries as fixtures for unsupported-capability assertions
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
601627
602---628---
603629
604## Open questions630## Assumptions and defaults
605631
6061. Claude runtime payloads:632- Codex is the reference provider for full structured plan/question UX.
607 - what exact `AskUserQuestion` input/output shapes arrive over the stream?633- Claude supports structured prompts via tool adaptation, but incremental structured plan updates are not assumed initially.
6082. Claude answer submission path:634- Cursor supports plan and ask modes, but not structured prompt cards or structured plan step updates initially.
609 - should answers be injected as tool results, control responses, or synthetic user turns?635- Product/UI should degrade gracefully rather than invent unsupported provider behavior.
6103. Cursor ACP:636- Raw provider payloads should always be retained where feasible to support future adapter refinement.
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?
616637
617---638---
618639
619## Immediate recommendation640## Recommended execution order
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.
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