History

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

Julius Marminge committed 8 months ago

Commit df6bb181 parent 03053d91 file · +460 −441Browse files

Unified · Split

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
5−Understand 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−
12−Providers covered:
5+Implement 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−
22−Current 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−
31−Canonical runtime events already modeled:
11+The 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
40−This is a good base, but provider coverage is uneven:
18+This 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−
52−Codex has the richest first-class support for this feature set.
53−
54−### Real local session evidence
25+## Source of truth
5526
56−The 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
29+Codex protocol decisions should be based on the open-source app-server protocol, not on local rollout session files.
6030
61−From the sampled rollout file, I did **not** find raw `event_msg.plan_update` / `event_msg.plan_delta` records, which suggests either:
31+Upstream 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−
66−That 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−
69−Native 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
78−Evidence inspected:
39+Important 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
47+Claude protocol decisions should be based on:
8848
89−Codex 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
91−Two distinct plan surfaces exist:
52+Confirmed from local Claude transcript:
9253
93−1. **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
100−2. **Plan text stream**
101− - `item/plan/delta`
102− - useful for rendering streaming plan prose / bullet lists before or alongside structured state
58+### Cursor
10359
104−This maps very cleanly to our canonical events:
60+Cursor 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
65+Confirmed from probe:
11066
111−Codex 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
113−Server request:
70+---
11471
115−- `item/tool/requestUserInput`
72+## High-level product model
11673
117−Shape characteristics from upstream protocol/docs:
74+The 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
127−Important nuance:
78+This 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
80+Canonical operating modes:
13281
133−### Why Codex is the reference model
82+- `default`
83+- `plan`
84+- `ask`
85+- `execute`
86+- `unknown`
13487
135−Codex should be treated as the reference shape for our provider-agnostic abstraction because it already matches the desired UI:
88+This 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
92+This is the current structured plan snapshot, if the provider can supply one natively or if the adapter can synthesize one confidently.
14393
144−For Codex, the provider adapter layer should stay close to native protocol:
94+Canonical 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
97+interface 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
110+This is the product-level question card model.
154111
155−### What exists natively
156−
157−Claude has an explicit plan permission mode:
158−
159−### Real local session evidence
160−
161−The sampled Claude session file provides concrete payloads for both key plan-mode tools:
162−
163−1. `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−```
112+Canonical model:
186113
187−2. `ExitPlanMode` also appears as a `tool_use` block, with a long-form `plan` string payload containing the finalized implementation spec.
114+```ts
115+interface 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
189−That gives us two strong adapter conclusions:
138+interface 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`.
148+This should remain distinct from approvals.
193149
194−### Concrete adapter mapping from transcript evidence
150+---
195151
196−Recommended 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
202−The `AskUserQuestion` payload shape is already close to the UI we want:
156+Implement 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
209−The 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
214−Confirmed 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`
172+Files:
218173
219−Claude 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`
178+Add or formalize the following runtime concepts:
223179
224−These 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
186+Required additions:
227187
228−Claude adapter input today is built from SDK messages like:
188+```ts
189+interface 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
200+Canonical runtime behavior:
236201
237−The 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
244−What it does **not** appear to expose as a first-class event:
209+Files:
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
250−Instead, plan-mode-related behavior appears to surface through tool uses:
214+Each 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
216+Preferred 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
260−Claude’s plan mode is primarily a **permission / behavior mode**:
223+Files:
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
267−That means Claude plan mode is native, but **structured plan state is not guaranteed**.
229+Implementation requirements:
268230
269−In 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
271−1. **Best-effort structured extraction**
272− - detect plan-like assistant/tool outputs
273− - synthesize `turn.plan.updated` from them
274−2. **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
238+Files likely involved:
279239
280−Claude’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
282−That means the adapter likely needs to:
244+Add 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
289−This is adapter work, not just schema plumbing.
253+### 5. Implement capability-driven UI rendering
290254
291−### Recommended stance for Claude
255+UI rules:
292256
293−Claude 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
295−Recommended 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`
265+Requirements:
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
305−Claude currently appears less deterministic than Codex for structured plans.
273+### 7. History and reconnect behavior
306274
307−Main unknowns to resolve during implementation:
275+Requirements:
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
319−Cursor ACP does have native session modes.
288+### Summary
320289
321−Confirmed by local probe output in `.tmp/acp-plan-probe/summary.json`:
290+Codex is the reference implementation and should be wired as the strongest native provider.
322291
323−- `agent`
324−- `plan`
325−- `ask`
326−
327−The 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`
294+Source of truth: upstream open-source app-server protocol.
335295
336−### What the probe did not find
296+Native incoming surfaces:
337297
338−The 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
344−The current ACP surface therefore looks much thinner than Codex.
308+Files:
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
313+Implementation requirements:
349314
350−Cursor `plan` mode clearly exists as a session mode, but as currently observed it seems to mean:
315+1. 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
318+2. Keep native plan text streaming as-is:
319+- `item/plan/delta` -> `content.delta` with `streamKind: "plan_text"`
355320
356−So the plan is native as a mode, but not obviously as a structured protocol surface.
321+3. 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
325+4. 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
360−Cursor ACP currently gives us a nearby primitive only for **permission requests**:
330+5. 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
364−That 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
366−The 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
372−But 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
353+Claude 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
376−Treat 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
357+From the local Claude session transcript:
381358
382−Therefore 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
370+Claude also has native:
387371
388−### Important product implication
372+- `permissionMode: 'plan'`
373+- `system:init.permissionMode`
374+- `system:status.permissionMode`
389375
390−If we want the same rich “multiple-choice question card” UX across all providers, Cursor may need one of these paths:
376+### Adapter implementation
391377
392−1. **Provider-native path** if later ACP exploration finds structured user question primitives
393−2. **Adapter-mediated path** where the provider is instructed to emit a machine-readable question block in text and adapter parses it
394−3. **Capability downgrade** where Cursor only supports conversational follow-up, not full structured plan-question cards
378+Files:
395379
396−Today, 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+
383+Implementation requirements:
384+
385+1. Publish native operating mode:
386+- `permissionMode: 'plan'` -> canonical mode `plan`
387+- if future values map cleanly, publish `ask` or `default` accordingly
388+
389+2. 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+
394+3. 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+
402+4. 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+
407+5. 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+
412+6. 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+
419+7. 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+
441+Implementation 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 |
451+Cursor 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:
455+From 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−---
467+Not 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
425−We should separate three concerns that are currently easy to conflate.
473+### Adapter implementation
426474
427−### 1. Session / turn mode
475+Files:
428476
429−A 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
437−This is about **agent operating mode**, not UI widgets.
481+Implementation requirements:
438482
439−### 2. Structured plan state
483+1. 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
441−A canonical plan model:
488+2. 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
444−interface 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−```
493+3. 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
455−Rules:
498+4. 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.
502+5. 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
463−Canonical 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
466−interface 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
488−And answers:
520+Add dedicated probes for:
489521
490−```ts
491−interface 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
503−Because 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
508−Codex already blurs these at transport level because `item/tool/requestUserInput` is a request.
509−But product and UI should keep them separate.
533+Render plan mode through a single UI model driven by canonical events and provider capabilities.
510534
511−Recommended 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
546+1. Show operating mode indicator whenever current mode is `plan` or `ask`
547+2. Show structured question card only when a pending `user-input.requested` exists
548+3. Show structured plan step list when a current `turn.plan.updated` snapshot exists
549+4. Append `plan_text` streaming content beneath or alongside structured steps when both exist
550+5. Fall back to assistant text rendering when provider capabilities do not support structure
551+6. Show completed selected answers in history after `user-input.resolved`
519552
520−### Codex adapter
553+### Submission behavior
521554
522−Use 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
524−Implementation 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
563+1. Provider starts session and publishes capabilities + mode
564+2. User starts plan-mode turn or provider enters plan mode
565+3. Adapter emits structured plan and/or plan text events when available
566+4. Adapter emits `user-input.requested` when provider asks a structured question
567+5. Frontend renders question card or fallback conversational prompt based on capabilities
568+6. User submits answer
569+7. Server routes answer back to provider adapter pending request/tool context
570+8. Adapter emits `user-input.resolved` and `request.resolved`
571+9. Provider may continue planning, emit more plan updates, or emit final plan handoff
572+10. Frontend renders final completed plan state and history
533573
534−Implement tool-aware adaptation.
574+---
535575
536−Implementation 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
580+Files:
545581
546−Implement conservative degraded support first.
582+- `packages/contracts/src/providerRuntime.test.ts`
583+- related schema tests
547584
548−Implementation target:
585+Add 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
594+Files:
558595
559−To 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
562−interface ProviderInteractiveCapabilities {
563− supportsPlanMode: boolean;
564− supportsAskMode: boolean;
565− supportsStructuredPlanUpdates: boolean;
566− supportsPlanTextStreaming: boolean;
567− supportsStructuredUserInput: boolean;
568− supportsFreeformUserInput: boolean;
569− supportsExitPlanMode: boolean;
570−}
571−```
601+Add coverage for:
572602
573−Expected 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
579−This lets the UI:
613+Add 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−
589−1. **Formalize provider capability flags**
590− - make support explicit before UI work
591−2. **Finish canonical user-input contracts**
592− - question schema, answer schema, source markers
593−3. **Codex end-to-end implementation first**
594− - closest match to desired UX
595−4. **Claude adapter translation second**
596− - parse `AskUserQuestion` and `ExitPlanMode`
597−5. **Cursor degraded plan-mode support third**
598− - mode awareness first, richer structure later
599−6. **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
606−1. Claude runtime payloads:
607− - what exact `AskUserQuestion` input/output shapes arrive over the stream?
608−2. Claude answer submission path:
609− - should answers be injected as tool results, control responses, or synthetic user turns?
610−3. Cursor ACP:
611− - is there an undocumented or less common interactive prompt primitive beyond `session/request_permission`?
612−4. Cursor mode switching:
613− - can ACP session mode change mid-session, and is that surfaced as `session/update` or request/response only?
614−5. 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−
621−Build the product contract around **capabilities**, not around the assumption that every provider has Codex-style plan/question primitives.
622−
623−Concretely:
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
629−That gives us a provider-agnostic architecture without pretending the underlying protocols are already equivalent.
642+1. Formalize canonical capabilities + mode + structured prompt contracts
643+2. Wire capabilities/mode through orchestration and web socket projections
644+3. Finish Codex end-to-end plan mode implementation first
645+4. Implement Claude `AskUserQuestion` and `ExitPlanMode` adapter mapping
646+5. Add Cursor degraded plan/ask mode support
647+6. Build capability-driven frontend rendering and answer submission
648+7. Add reconnect/history coverage and transcript/protocol fixtures