- Reframe doc into a provider-agnostic implementation plan for Codex, Claude, and Cursor - Define canonical mode, plan, and structured user-input models plus capability-driven UI behavior - Add concrete orchestration, adapter, frontend, and test work items for rollout
Unified · Split
| @@ -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 | ||