| ... | @@ -1,629 +1,648 @@ | ... | @@ -1,629 +1,648 @@ |
| 1 | # Plan Mode Deep Dive | 1 | # Plan Mode Implementation Plan |
| 2 | | 2 | |
| 3 | ## Goal | 3 | ## Goal |
| 4 | | 4 | |
| 5 | Understand how the three providers expose: | 5 | Implement plan mode in a provider-agnostic way across: |
| 6 | | | |
| 7 | - planning / read-only execution modes | | |
| 8 | - structured plan updates | | |
| 9 | - structured user-input / question prompts | | |
| 10 | - response plumbing needed for a provider-agnostic adapter layer | | |
| 11 | | | |
| 12 | Providers covered: | | |
| 13 | | 6 | |
| 14 | - Codex App Server | 7 | - Codex App Server |
| 15 | - Claude Agent SDK / Claude Code | 8 | - Claude Agent SDK / Claude Code |
| 16 | - Cursor ACP | 9 | - Cursor ACP |
| 17 | | 10 | |
| 18 | --- | 11 | The implementation must support: |
| 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: | | |
| 32 | | 12 | |
| 33 | - `turn.plan.updated` | 13 | - provider-native or adapter-derived plan mode state |
| 34 | - `content.delta` with `streamKind: "plan_text"` | 14 | - structured user-input question flows where available |
| 35 | - `user-input.requested` | 15 | - graceful fallback when a provider lacks structured prompts or structured plan updates |
| 36 | - `user-input.resolved` | 16 | - a front-end UX that renders from capabilities and canonical events rather than provider-specific conditionals |
| 37 | - `request.opened` | | |
| 38 | - `request.resolved` | | |
| 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. | 20 | - the architecture plan for shared orchestration and UI wiring |
| 43 | - Claude: native plan mode exists, but plan updates are mostly implicit via tool/message stream; user questioning appears tool-based rather than a dedicated runtime event. | 21 | - the provider adapter implementation plan for each supported agent runtime |
| 44 | - Cursor ACP: native plan mode exists, but current ACP surface appears to expose neither structured plan updates nor structured ask-user prompts in the same way as Codex. | | |
| 45 | | 22 | |
| 46 | --- | 23 | --- |
| 47 | | 24 | |
| 48 | ## Provider 1: Codex App Server | 25 | ## Source of truth |
| 49 | | | |
| 50 | ### What exists natively | | |
| 51 | | | |
| 52 | Codex has the richest first-class support for this feature set. | | |
| 53 | | | |
| 54 | ### Real local session evidence | | |
| 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"` | 29 | Codex protocol decisions should be based on the open-source app-server protocol, not on local rollout session files. |
| 59 | - final plan handoff is emitted as a normal assistant message containing a `<proposed_plan>` block | | |
| 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 | - `turn/plan/updated` | 33 | - `turn/plan/updated` |
| 73 | - `item/plan/delta` | 34 | - `item/plan/delta` |
| 74 | - `item/tool/requestUserInput` | 35 | - `item/tool/requestUserInput` |
| 75 | - `serverRequest/resolved` cleanup / completion notification for user input | 36 | - `serverRequest/resolved` |
| 76 | - `request_user_input` tool in core/runtime | 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` | 41 | - local Codex rollout JSONL files are a lossy or higher-level projection |
| 81 | - upstream `codex-rs/app-server-protocol` schemas | 42 | - they may preserve plan mode state and final `<proposed_plan>` output |
| 82 | - upstream `codex-rs/core/src/tools/handlers/request_user_input.rs` | 43 | - they should not be treated as proof that low-level plan events do or do not exist |
| 83 | - upstream `codex-rs/core/templates/collaboration_mode/plan.md` | | |
| 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** | 54 | - `AskUserQuestion` is a native `tool_use` |
| 94 | - `turn/plan/updated` | 55 | - `ExitPlanMode` is a native `tool_use` |
| 95 | - payload contains: | 56 | - `AskUserQuestion.input.questions[]` is structured and adapter-parseable |
| 96 | - optional `explanation` | | |
| 97 | - `plan: Array<{ step, status }>` | | |
| 98 | - statuses are `pending | inProgress | completed` | | |
| 99 | | 57 | |
| 100 | 2. **Plan text stream** | 58 | ### Cursor |
| 101 | - `item/plan/delta` | | |
| 102 | - useful for rendering streaming plan prose / bullet lists before or alongside structured state | | |
| 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` | 62 | - ACP docs |
| 107 | - `item/plan/delta` -> `content.delta(plan_text)` | 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 | 76 | ### 1. Operating mode |
| 120 | - each question has: | | |
| 121 | - prompt text | | |
| 122 | - stable id | | |
| 123 | - required options | | |
| 124 | - response carries answers per question | | |
| 125 | - freeform notes are supported in UX and answer model | | |
| 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 | 80 | Canonical operating modes: |
| 130 | - Codex TUI also supports an optional freeform note / text alongside selected options | | |
| 131 | - pending question requests are turn-scoped and cleaned up on turn completion/interruption | | |
| 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 | 90 | ### 2. Structured plan state |
| 138 | - explicit multi-question prompt flow | | |
| 139 | - explicit answer submission | | |
| 140 | - explicit pending/resolved bookkeeping | | |
| 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` | 96 | ```ts |
| 147 | - keep `content.delta(plan_text)` | 97 | interface CanonicalPlanState { |
| 148 | - map `item/tool/requestUserInput` to `user-input.requested` and `request.opened` | 98 | explanation?: string | null; |
| 149 | - map answer completion / cleanup to `user-input.resolved` and `request.resolved` | 99 | steps: Array<{ |
| | 100 | id?: string; |
| | 101 | text: string; |
| | 102 | status: 'pending' | 'inProgress' | 'completed'; |
| | 103 | source: 'native' | 'synthesized'; |
| | 104 | }>; |
| | 105 | } |
| | 106 | ``` |
| 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 | 112 | Canonical model: |
| 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 | ``` | | |
| 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. | 148 | This should remain distinct from approvals. |
| 192 | - Claude final plan handoff is tool-based too, not necessarily a dedicated transport event like Codex `turn/plan/updated`. | | |
| 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` | 154 | ## Summary |
| 199 | - user answer submission / tool result -> `user-input.resolved` | | |
| 200 | - `tool_use.name === "ExitPlanMode"` with `input.plan` -> final plan handoff event or synthesized `turn.plan.updated` snapshot | | |
| 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 | 158 | ### Shared goals |
| 205 | - each question has `header` and `question` | | |
| 206 | - each question has `options[]` with `label` + `description` | | |
| 207 | - `multiSelect` is explicit | | |
| 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` | 172 | Files: |
| 217 | - `/Users/julius/.bun/install/cache/@anthropic-ai/claude-agent-sdk@0.2.62@@@1/sdk-tools.d.ts` | | |
| 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` | 178 | Add or formalize the following runtime concepts: |
| 222 | - `AskUserQuestion` | | |
| 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` | 200 | Canonical runtime behavior: |
| 231 | - `system:status` | | |
| 232 | - `stream_event` | | |
| 233 | - `assistant` | | |
| 234 | - `result` | | |
| 235 | - hook/task/tool summary events | | |
| 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 | 207 | ### 2. Add canonical mode/capability publication from providers |
| 240 | - tool-use starts/stops via content blocks | | |
| 241 | - permission-mode metadata | | |
| 242 | - final result and usage | | |
| 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` | 211 | - `apps/server/src/provider/Layers/*Adapter.ts` |
| 247 | - no direct equivalent to Codex `item/plan/delta` | 212 | - `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts` |
| 248 | - no dedicated top-level `user-input.requested` transport event in the SDK stream | | |
| 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 | 216 | Preferred shape: |
| 253 | - `ExitPlanMode` tool | | |
| 254 | - regular content/tool stream events | | |
| 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'` | 225 | - `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts` |
| 263 | - tool execution is prevented / constrained | 226 | - `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts` |
| 264 | - assistant can explore, reason, and propose a plan | 227 | - any orchestration projection models feeding the web socket domain events |
| 265 | - eventually it may call `ExitPlanMode` to transition toward implementation | | |
| 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** | 236 | ### 4. Add frontend state model for plan mode |
| 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 | | |
| 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` | 246 | - current provider capabilities |
| 285 | - parse its input payload into canonical question schema | 247 | - current operating mode |
| 286 | - expose `user-input.requested` | 248 | - pending structured prompt |
| 287 | - accept a user answer and feed it back as the tool result / synthetic user response, depending SDK control path | 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` | 265 | Requirements: |
| 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` | | |
| 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 | 277 | - pending structured prompts should survive reconnect/resubscribe if still active |
| 310 | - whether freeform responses are supported natively or need synthetic augmentation | 278 | - resolved prompts should be rendered in history as completed interactions |
| 311 | - whether `ExitPlanMode` should be exposed to UI explicitly or handled internally by the adapter/session controller | 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` | 292 | ### Native protocol mapping |
| 324 | - `plan` | | |
| 325 | - `ask` | | |
| 326 | | | |
| 327 | The probe also confirmed current primary ACP update surfaces: | | |
| 328 | | 293 | |
| 329 | - `available_commands_update` | 294 | Source of truth: upstream open-source app-server protocol. |
| 330 | - `agent_thought_chunk` | | |
| 331 | - `agent_message_chunk` | | |
| 332 | - `tool_call` | | |
| 333 | - `tool_call_update` | | |
| 334 | - `session/request_permission` | | |
| 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 | 306 | ### Adapter implementation |
| 341 | - a dedicated “ask user question” request | | |
| 342 | - a dedicated plan delta stream | | |
| 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 | 318 | 2. Keep native plan text streaming as-is: |
| 353 | - regular thought/message chunk streaming | 319 | - `item/plan/delta` -> `content.delta` with `streamKind: "plan_text"` |
| 354 | - no dedicated structured plan payload | | |
| 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 | 349 | ## Provider 2: Claude Agent SDK / Claude Code |
| 369 | - possibly mode-switch semantics | | |
| 370 | - possibly skills/commands that we have not yet triggered | | |
| 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` | 357 | From the local Claude session transcript: |
| 379 | - no proven native support for structured plan steps | | |
| 380 | - no proven native support for structured user-input questions | | |
| 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 | 370 | Claude also has native: |
| 385 | - optionally synthesize `turn.plan.updated` from assistant text if we can extract steps safely | | |
| 386 | - for interactive questions, likely fall back to regular conversational turns unless a richer ACP mechanism is discovered | | |
| 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 | 378 | Files: |
| 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 | | |
| 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 | | 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. |
| 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 | | | |
| 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. | 457 | - available modes include `agent`, `plan`, and `ask` |
| 418 | - **Claude**: plan mode is a first-class permission mode, but structured plan/question UI likely needs adapter interpretation. | 458 | - observed updates include: |
| 419 | - **Cursor**: plan mode is a first-class session mode, but richer plan/question structure is not currently exposed. | 459 | - `available_commands_update` |
| | 460 | - `agent_thought_chunk` |
| | 461 | - `agent_message_chunk` |
| | 462 | - `tool_call` |
| | 463 | - `tool_call_update` |
| | 464 | - observed request type: |
| | 465 | - `session/request_permission` |
| 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: | 477 | - `apps/server/src/provider/Layers/CursorAdapter.ts` |
| 430 | | 478 | - `apps/server/src/provider/Layers/CursorAdapter.test.ts` |
| 431 | - `default` | 479 | - probe scripts under `scripts/` |
| 432 | - `plan` | | |
| 433 | - `ask` | | |
| 434 | - `execute` | | |
| 435 | - `unknown` | | |
| 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 | 493 | 3. Use graceful fallback behavior: |
| 444 | interface CanonicalPlanState { | 494 | - render plan mode as mode state + assistant text stream |
| 445 | explanation?: string | null; | 495 | - render ask mode as conversational mode + normal user composer |
| 446 | steps: Array<{ | 496 | - do not render structured question cards for Cursor initially |
| 447 | id?: string; | | |
| 448 | text: string; | | |
| 449 | status: 'pending' | 'inProgress' | 'completed'; | | |
| 450 | source: 'native' | 'synthesized'; | | |
| 451 | }>; | | |
| 452 | } | | |
| 453 | ``` | | |
| 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. | 502 | 5. Publish capabilities: |
| 458 | - Claude/Cursor may populate it only when extraction is confident. | 503 | - `supportsPlanMode = true` |
| 459 | - UI should tolerate absence of structured plan state. | 504 | - `supportsAskMode = true` |
| | 505 | - `supportsStructuredPlanUpdates = false` |
| | 506 | - `supportsPlanTextStreaming = false` initially |
| | 507 | - `supportsStructuredUserInput = false` |
| | 508 | - `supportsFreeformUserInput = false` via structured prompt path |
| | 509 | - `supportsExitPlanMode = false` |
| 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 | 518 | ### Cursor-specific follow-up probe work |
| 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 | ``` | | |
| 487 | | 519 | |
| 488 | And answers: | 520 | Add dedicated probes for: |
| 489 | | 521 | |
| 490 | ```ts | 522 | - mode-switching during an active session |
| 491 | interface CanonicalUserInputAnswer { | 523 | - ask-mode prompt behavior |
| 492 | promptId: string; | 524 | - any request types besides `session/request_permission` |
| 493 | answers: Array<{ | 525 | - whether specific built-in skills or prompt styles trigger structured question surfaces |
| 494 | questionId: string; | | |
| 495 | selectedOptionId?: string; | | |
| 496 | text?: string; | | |
| 497 | }>; | | |
| 498 | } | | |
| 499 | ``` | | |
| 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?” | 531 | ### Summary |
| 506 | - user-input prompts = “which direction do you want?” | | |
| 507 | | 532 | |
| 508 | Codex already blurs these at transport level because `item/tool/requestUserInput` is a request. | 533 | Render plan mode through a single UI model driven by canonical events and provider capabilities. |
| 509 | But product and UI should keep them separate. | | |
| 510 | | 534 | |
| 511 | Recommended rule: | 535 | ### UI states to support |
| 512 | | 536 | |
| 513 | - keep `request.opened/request.resolved` for transport-level correlation | 537 | - standard conversation |
| 514 | - keep `user-input.requested/user-input.resolved` for product-level UX | 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` | 561 | ## Data flow end to end |
| 527 | - preserve `content.delta(plan_text)` | | |
| 528 | - preserve `user-input.requested` | | |
| 529 | - preserve `user-input.resolved` | | |
| 530 | - expose raw/native metadata for future UI improvements | | |
| 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 | 578 | ### Contracts |
| 539 | - detect `AskUserQuestion` tool uses and convert to canonical user-input prompt | | |
| 540 | - convert question answers back into Claude-native tool response path | | |
| 541 | - detect `ExitPlanMode` and emit a canonical mode-change / handoff event | | |
| 542 | - optionally synthesize plan steps from assistant output in plan mode | | |
| 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` | 587 | - provider capabilities schema |
| 551 | - do not claim structured plan updates unless proven | 588 | - structured user-input prompt schema |
| 552 | - do not claim structured user-input prompts unless proven | 589 | - structured user-input answer schema |
| 553 | - optionally add a provider capability flag so UI can fall back to conversational follow-up | 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 | 601 | Add coverage for: |
| 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 | ``` | | |
| 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` | 611 | ### Web/UI tests |
| 576 | - Claude: `supportsPlanMode=true`, `supportsStructuredUserInput=likely true via adapter`, `supportsStructuredPlanUpdates=partial` | | |
| 577 | - Cursor: `supportsPlanMode=true`, `supportsAskMode=true`, most structured flags `false` | | |
| 578 | | 612 | |
| 579 | This lets the UI: | 613 | Add coverage for: |
| 580 | | 614 | |
| 581 | - render rich plan cards only when supported | 615 | - question card rendering from canonical prompt |
| 582 | - fall back to plain assistant text + reply box otherwise | 616 | - question answer submission |
| 583 | - avoid coupling the design to Codex-specific assumptions | 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 | 624 | - preserve the Claude transcript-derived `AskUserQuestion` and `ExitPlanMode` payloads as fixtures |
| 588 | | 625 | - preserve Codex protocol fixtures for `turn/plan/updated`, `item/plan/delta`, and `item/tool/requestUserInput` |
| 589 | 1. **Formalize provider capability flags** | 626 | - preserve ACP probe summaries as fixtures for unsupported-capability assertions |
| 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 | | |
| 601 | | 627 | |
| 602 | --- | 628 | --- |
| 603 | | 629 | |
| 604 | ## Open questions | 630 | ## Assumptions and defaults |
| 605 | | 631 | |
| 606 | 1. Claude runtime payloads: | 632 | - Codex is the reference provider for full structured plan/question UX. |
| 607 | - what exact `AskUserQuestion` input/output shapes arrive over the stream? | 633 | - Claude supports structured prompts via tool adaptation, but incremental structured plan updates are not assumed initially. |
| 608 | 2. Claude answer submission path: | 634 | - Cursor supports plan and ask modes, but not structured prompt cards or structured plan step updates initially. |
| 609 | - should answers be injected as tool results, control responses, or synthetic user turns? | 635 | - Product/UI should degrade gracefully rather than invent unsupported provider behavior. |
| 610 | 3. Cursor ACP: | 636 | - Raw provider payloads should always be retained where feasible to support future adapter refinement. |
| 611 | - is there an undocumented or less common interactive prompt primitive beyond `session/request_permission`? | | |
| 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? | | |
| 616 | | 637 | |
| 617 | --- | 638 | --- |
| 618 | | 639 | |
| 619 | ## Immediate recommendation | 640 | ## Recommended execution order |
| 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. | | |
| 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 |