| ... | ... | @@ -1,629 +1,648 @@ |
| 1 | | # Plan Mode Deep Dive |
| 1 | # Plan Mode Implementation Plan |
| 2 | 2 | |
| 3 | 3 | ## Goal |
| 4 | 4 | |
| 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: |
| 13 | 6 | |
| 14 | 7 | - Codex App Server |
| 15 | 8 | - Claude Agent SDK / Claude Code |
| 16 | 9 | - Cursor ACP |
| 17 | 10 | |
| 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: |
| 32 | 12 | |
| 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 |
| 39 | 17 | |
| 40 | | This is a good base, but provider coverage is uneven: |
| 18 | This document now serves as both: |
| 41 | 19 | |
| 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 |
| 45 | 22 | |
| 46 | 23 | --- |
| 47 | 24 | |
| 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 |
| 55 | 26 | |
| 56 | | The local Codex rollout file confirms at least one important distinction: |
| 27 | ### Codex |
| 57 | 28 | |
| 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. |
| 60 | 30 | |
| 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: |
| 62 | 32 | |
| 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 |
| 72 | 33 | - `turn/plan/updated` |
| 73 | 34 | - `item/plan/delta` |
| 74 | 35 | - `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` |
| 77 | 38 | |
| 78 | | Evidence inspected: |
| 39 | Important implication: |
| 79 | 40 | |
| 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 |
| 84 | 44 | |
| 85 | | ### Semantics |
| 45 | ### Claude |
| 86 | 46 | |
| 87 | | #### Plan mode |
| 47 | Claude protocol decisions should be based on: |
| 88 | 48 | |
| 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 |
| 90 | 51 | |
| 91 | | Two distinct plan surfaces exist: |
| 52 | Confirmed from local Claude transcript: |
| 92 | 53 | |
| 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 |
| 99 | 57 | |
| 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 |
| 103 | 59 | |
| 104 | | This maps very cleanly to our canonical events: |
| 60 | Cursor protocol decisions should be based on: |
| 105 | 61 | |
| 106 | | - `turn/plan.updated` -> `turn.plan.updated` |
| 107 | | - `item/plan/delta` -> `content.delta(plan_text)` |
| 62 | - ACP docs |
| 63 | - current local ACP probe output |
| 108 | 64 | |
| 109 | | #### Structured user questions |
| 65 | Confirmed from probe: |
| 110 | 66 | |
| 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 |
| 112 | 69 | |
| 113 | | Server request: |
| 70 | --- |
| 114 | 71 | |
| 115 | | - `item/tool/requestUserInput` |
| 72 | ## High-level product model |
| 116 | 73 | |
| 117 | | Shape characteristics from upstream protocol/docs: |
| 74 | The implementation should separate three concepts that are currently easy to conflate. |
| 118 | 75 | |
| 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 |
| 126 | 77 | |
| 127 | | Important nuance: |
| 78 | This is the agent runtime mode. |
| 128 | 79 | |
| 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: |
| 132 | 81 | |
| 133 | | ### Why Codex is the reference model |
| 82 | - `default` |
| 83 | - `plan` |
| 84 | - `ask` |
| 85 | - `execute` |
| 86 | - `unknown` |
| 134 | 87 | |
| 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. |
| 136 | 89 | |
| 137 | | - explicit plan lifecycle |
| 138 | | - explicit multi-question prompt flow |
| 139 | | - explicit answer submission |
| 140 | | - explicit pending/resolved bookkeeping |
| 90 | ### 2. Structured plan state |
| 141 | 91 | |
| 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. |
| 143 | 93 | |
| 144 | | For Codex, the provider adapter layer should stay close to native protocol: |
| 94 | Canonical model: |
| 145 | 95 | |
| 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 | ``` |
| 150 | 107 | |
| 151 | | --- |
| 108 | ### 3. Structured user-input prompt |
| 152 | 109 | |
| 153 | | ## Provider 2: Claude Agent SDK / Claude Code |
| 110 | This is the product-level question card model. |
| 154 | 111 | |
| 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: |
| 186 | 113 | |
| 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 | } |
| 188 | 137 | |
| 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 | ``` |
| 190 | 147 | |
| 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. |
| 193 | 149 | |
| 194 | | ### Concrete adapter mapping from transcript evidence |
| 150 | --- |
| 195 | 151 | |
| 196 | | Recommended Claude mapping based on real session data: |
| 152 | ## Shared orchestration plan |
| 197 | 153 | |
| 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 |
| 201 | 155 | |
| 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. |
| 203 | 157 | |
| 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 |
| 208 | 159 | |
| 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 |
| 210 | 165 | |
| 166 | --- |
| 211 | 167 | |
| 212 | | - `PermissionMode = 'default' | 'acceptEdits' | 'bypassPermissions' | 'plan' | 'dontAsk'` |
| 168 | ## Orchestration work items |
| 213 | 169 | |
| 214 | | Confirmed from installed SDK typings: |
| 170 | ### 1. Extend canonical provider runtime contracts |
| 215 | 171 | |
| 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: |
| 218 | 173 | |
| 219 | | Claude also exposes plan-related tools/types: |
| 174 | - `packages/contracts/src/providerRuntime.ts` |
| 175 | - `packages/contracts/src/provider.ts` |
| 176 | - `EVENTS.md` |
| 220 | 177 | |
| 221 | | - `ExitPlanMode` |
| 222 | | - `AskUserQuestion` |
| 178 | Add or formalize the following runtime concepts: |
| 223 | 179 | |
| 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 |
| 225 | 185 | |
| 226 | | ### What the wire stream exposes |
| 186 | Required additions: |
| 227 | 187 | |
| 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 | ``` |
| 229 | 199 | |
| 230 | | - `system:init` |
| 231 | | - `system:status` |
| 232 | | - `stream_event` |
| 233 | | - `assistant` |
| 234 | | - `result` |
| 235 | | - hook/task/tool summary events |
| 200 | Canonical runtime behavior: |
| 236 | 201 | |
| 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 |
| 238 | 206 | |
| 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 |
| 243 | 208 | |
| 244 | | What it does **not** appear to expose as a first-class event: |
| 209 | Files: |
| 245 | 210 | |
| 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` |
| 249 | 213 | |
| 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. |
| 251 | 215 | |
| 252 | | - `AskUserQuestion` tool |
| 253 | | - `ExitPlanMode` tool |
| 254 | | - regular content/tool stream events |
| 216 | Preferred shape: |
| 255 | 217 | |
| 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 |
| 257 | 220 | |
| 258 | | #### Plan mode |
| 221 | ### 3. Preserve and project plan/user-input state in orchestration |
| 259 | 222 | |
| 260 | | Claude’s plan mode is primarily a **permission / behavior mode**: |
| 223 | Files: |
| 261 | 224 | |
| 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 |
| 266 | 228 | |
| 267 | | That means Claude plan mode is native, but **structured plan state is not guaranteed**. |
| 229 | Implementation requirements: |
| 268 | 230 | |
| 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 |
| 270 | 235 | |
| 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 |
| 277 | 237 | |
| 278 | | #### Asking user questions |
| 238 | Files likely involved: |
| 279 | 239 | |
| 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 |
| 281 | 243 | |
| 282 | | That means the adapter likely needs to: |
| 244 | Add frontend state for: |
| 283 | 245 | |
| 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 |
| 288 | 252 | |
| 289 | | This is adapter work, not just schema plumbing. |
| 253 | ### 5. Implement capability-driven UI rendering |
| 290 | 254 | |
| 291 | | ### Recommended stance for Claude |
| 255 | UI rules: |
| 292 | 256 | |
| 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 |
| 294 | 262 | |
| 295 | | Recommended mapping: |
| 263 | ### 6. Implement front-to-back response submission path |
| 296 | 264 | |
| 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: |
| 302 | 266 | |
| 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 |
| 304 | 272 | |
| 305 | | Claude currently appears less deterministic than Codex for structured plans. |
| 273 | ### 7. History and reconnect behavior |
| 306 | 274 | |
| 307 | | Main unknowns to resolve during implementation: |
| 275 | Requirements: |
| 308 | 276 | |
| 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 |
| 312 | 281 | |
| 313 | 282 | --- |
| 314 | 283 | |
| 315 | | ## Provider 3: Cursor ACP |
| 284 | ## Provider adapter plans |
| 316 | 285 | |
| 317 | | ### What exists natively |
| 286 | ## Provider 1: Codex App Server |
| 318 | 287 | |
| 319 | | Cursor ACP does have native session modes. |
| 288 | ### Summary |
| 320 | 289 | |
| 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. |
| 322 | 291 | |
| 323 | | - `agent` |
| 324 | | - `plan` |
| 325 | | - `ask` |
| 326 | | |
| 327 | | The probe also confirmed current primary ACP update surfaces: |
| 292 | ### Native protocol mapping |
| 328 | 293 | |
| 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. |
| 335 | 295 | |
| 336 | | ### What the probe did not find |
| 296 | Native incoming surfaces: |
| 337 | 297 | |
| 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` |
| 339 | 305 | |
| 340 | | - structured plan update events |
| 341 | | - a dedicated “ask user question” request |
| 342 | | - a dedicated plan delta stream |
| 306 | ### Adapter implementation |
| 343 | 307 | |
| 344 | | The current ACP surface therefore looks much thinner than Codex. |
| 308 | Files: |
| 345 | 309 | |
| 346 | | ### Semantics |
| 310 | - `apps/server/src/provider/Layers/CodexAdapter.ts` |
| 311 | - related tests in `apps/server/src/provider/Layers/*Codex*.test.ts` |
| 347 | 312 | |
| 348 | | #### Plan mode |
| 313 | Implementation requirements: |
| 349 | 314 | |
| 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` |
| 351 | 317 | |
| 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"` |
| 355 | 320 | |
| 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 |
| 357 | 324 | |
| 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` |
| 359 | 329 | |
| 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 |
| 361 | 338 | |
| 362 | | - `session/request_permission` |
| 339 | ### Codex-specific tests |
| 363 | 340 | |
| 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 |
| 365 | 346 | |
| 366 | | The existence of `ask` mode suggests Cursor may handle asking by: |
| 347 | --- |
| 367 | 348 | |
| 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 |
| 371 | 350 | |
| 372 | | But based on current probe evidence, we should **not assume** a Codex-style structured question API exists in ACP. |
| 351 | ### Summary |
| 373 | 352 | |
| 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. |
| 375 | 354 | |
| 376 | | Treat Cursor as: |
| 355 | ### Real native evidence |
| 377 | 356 | |
| 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: |
| 381 | 358 | |
| 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 |
| 383 | 369 | |
| 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: |
| 387 | 371 | |
| 388 | | ### Important product implication |
| 372 | - `permissionMode: 'plan'` |
| 373 | - `system:init.permissionMode` |
| 374 | - `system:status.permissionMode` |
| 389 | 375 | |
| 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 |
| 391 | 377 | |
| 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: |
| 395 | 379 | |
| 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 |
| 397 | 444 | |
| 398 | 445 | --- |
| 399 | 446 | |
| 400 | | ## Cross-provider comparison |
| 447 | ## Provider 3: Cursor ACP |
| 401 | 448 | |
| 402 | | ### Capability matrix |
| 449 | ### Summary |
| 403 | 450 | |
| 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. |
| 412 | 452 | |
| 413 | | ### Core insight |
| 453 | ### Real native evidence |
| 414 | 454 | |
| 415 | | “Plan mode” means **different things** across providers: |
| 455 | From the ACP probe: |
| 416 | 456 | |
| 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` |
| 420 | 466 | |
| 421 | | --- |
| 467 | Not observed: |
| 422 | 468 | |
| 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 |
| 424 | 472 | |
| 425 | | We should separate three concerns that are currently easy to conflate. |
| 473 | ### Adapter implementation |
| 426 | 474 | |
| 427 | | ### 1. Session / turn mode |
| 475 | Files: |
| 428 | 476 | |
| 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/` |
| 436 | 480 | |
| 437 | | This is about **agent operating mode**, not UI widgets. |
| 481 | Implementation requirements: |
| 438 | 482 | |
| 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 |
| 440 | 487 | |
| 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 |
| 442 | 492 | |
| 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 |
| 454 | 497 | |
| 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 |
| 456 | 501 | |
| 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` |
| 460 | 510 | |
| 461 | | ### 3. Structured user-input prompt |
| 511 | ### Cursor-specific tests |
| 462 | 512 | |
| 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 |
| 464 | 517 | |
| 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 |
| 487 | 519 | |
| 488 | | And answers: |
| 520 | Add dedicated probes for: |
| 489 | 521 | |
| 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 |
| 500 | 526 | |
| 501 | | ### Why separate `request.opened` from `user-input.requested` |
| 527 | --- |
| 502 | 528 | |
| 503 | | Because they are not the same abstraction: |
| 529 | ## Frontend implementation plan |
| 504 | 530 | |
| 505 | | - approvals = “may I run/edit/do X?” |
| 506 | | - user-input prompts = “which direction do you want?” |
| 531 | ### Summary |
| 507 | 532 | |
| 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. |
| 510 | 534 | |
| 511 | | Recommended rule: |
| 535 | ### UI states to support |
| 512 | 536 | |
| 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 |
| 515 | 543 | |
| 516 | | --- |
| 544 | ### Rendering rules |
| 517 | 545 | |
| 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` |
| 519 | 552 | |
| 520 | | ### Codex adapter |
| 553 | ### Submission behavior |
| 521 | 554 | |
| 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 |
| 523 | 558 | |
| 524 | | Implementation target: |
| 559 | --- |
| 525 | 560 | |
| 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 |
| 531 | 562 | |
| 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 |
| 533 | 573 | |
| 534 | | Implement tool-aware adaptation. |
| 574 | --- |
| 535 | 575 | |
| 536 | | Implementation target: |
| 576 | ## Test plan |
| 537 | 577 | |
| 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 |
| 543 | 579 | |
| 544 | | ### Cursor adapter |
| 580 | Files: |
| 545 | 581 | |
| 546 | | Implement conservative degraded support first. |
| 582 | - `packages/contracts/src/providerRuntime.test.ts` |
| 583 | - related schema tests |
| 547 | 584 | |
| 548 | | Implementation target: |
| 585 | Add coverage for: |
| 549 | 586 | |
| 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 |
| 554 | 591 | |
| 555 | | --- |
| 592 | ### Server/provider tests |
| 556 | 593 | |
| 557 | | ## Capability flags we should add |
| 594 | Files: |
| 558 | 595 | |
| 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` |
| 560 | 600 | |
| 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: |
| 572 | 602 | |
| 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 |
| 574 | 610 | |
| 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 |
| 578 | 612 | |
| 579 | | This lets the UI: |
| 613 | Add coverage for: |
| 580 | 614 | |
| 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 |
| 584 | 621 | |
| 585 | | --- |
| 622 | ### Probe / fixture tests |
| 586 | 623 | |
| 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 |
| 601 | 627 | |
| 602 | 628 | --- |
| 603 | 629 | |
| 604 | | ## Open questions |
| 630 | ## Assumptions and defaults |
| 605 | 631 | |
| 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. |
| 616 | 637 | |
| 617 | 638 | --- |
| 618 | 639 | |
| 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 |
| 628 | 641 | |
| 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 |