- 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 | 3 | ## Goal |
| 4 | 4 | |
| 5 | −Understand how the three providers expose: | |
| 6 | − | |
| 7 | −- planning / read-only execution modes | |
| 8 | −- structured plan updates | |
| 9 | −- structured user-input / question prompts | |
| 10 | −- response plumbing needed for a provider-agnostic adapter layer | |
| 11 | − | |
| 12 | −Providers covered: | |
| 5 | +Implement plan mode in a provider-agnostic way across: | |
| 13 | 6 | |
| 14 | 7 | - Codex App Server |
| 15 | 8 | - Claude Agent SDK / Claude Code |
| 16 | 9 | - Cursor ACP |
| 17 | 10 | |
| 18 | −--- | |
| 19 | − | |
| 20 | −## Existing T3 Code groundwork | |
| 21 | − | |
| 22 | −Current repo groundwork already exists in these places: | |
| 23 | − | |
| 24 | −- `EVENTS.md` | |
| 25 | −- `packages/contracts/src/providerRuntime.ts` | |
| 26 | −- `apps/server/src/provider/Layers/CodexAdapter.ts` | |
| 27 | −- `apps/server/src/provider/Layers/ClaudeCodeAdapter.ts` | |
| 28 | −- `apps/server/src/provider/Layers/CursorAdapter.ts` | |
| 29 | −- `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts` | |
| 30 | − | |
| 31 | −Canonical runtime events already modeled: | |
| 11 | +The implementation must support: | |
| 32 | 12 | |
| 33 | −- `turn.plan.updated` | |
| 34 | −- `content.delta` with `streamKind: "plan_text"` | |
| 35 | −- `user-input.requested` | |
| 36 | −- `user-input.resolved` | |
| 37 | −- `request.opened` | |
| 38 | −- `request.resolved` | |
| 13 | +- provider-native or adapter-derived plan mode state | |
| 14 | +- structured user-input question flows where available | |
| 15 | +- graceful fallback when a provider lacks structured prompts or structured plan updates | |
| 16 | +- a front-end UX that renders from capabilities and canonical events rather than provider-specific conditionals | |
| 39 | 17 | |
| 40 | −This is a good base, but provider coverage is uneven: | |
| 18 | +This document now serves as both: | |
| 41 | 19 | |
| 42 | −- Codex: strong native support for both structured plans and structured user questions. | |
| 43 | −- Claude: native plan mode exists, but plan updates are mostly implicit via tool/message stream; user questioning appears tool-based rather than a dedicated runtime event. | |
| 44 | −- Cursor ACP: native plan mode exists, but current ACP surface appears to expose neither structured plan updates nor structured ask-user prompts in the same way as Codex. | |
| 20 | +- the architecture plan for shared orchestration and UI wiring | |
| 21 | +- the provider adapter implementation plan for each supported agent runtime | |
| 45 | 22 | |
| 46 | 23 | --- |
| 47 | 24 | |
| 48 | −## Provider 1: Codex App Server | |
| 49 | − | |
| 50 | −### What exists natively | |
| 51 | − | |
| 52 | −Codex has the richest first-class support for this feature set. | |
| 53 | − | |
| 54 | −### Real local session evidence | |
| 25 | +## Source of truth | |
| 55 | 26 | |
| 56 | −The local Codex rollout file confirms at least one important distinction: | |
| 27 | +### Codex | |
| 57 | 28 | |
| 58 | −- session metadata explicitly records collaboration mode as plan via `turn_context.payload.collaboration_mode.mode = "plan"` | |
| 59 | −- final plan handoff is emitted as a normal assistant message containing a `<proposed_plan>` block | |
| 29 | +Codex protocol decisions should be based on the open-source app-server protocol, not on local rollout session files. | |
| 60 | 30 | |
| 61 | −From the sampled rollout file, I did **not** find raw `event_msg.plan_update` / `event_msg.plan_delta` records, which suggests either: | |
| 31 | +Upstream Codex app-server explicitly supports: | |
| 62 | 32 | |
| 63 | −- this particular session never emitted incremental structured plan updates, or | |
| 64 | −- the desktop rollout file persists a higher-level message projection rather than every low-level app-server event | |
| 65 | − | |
| 66 | −That means the app-server protocol still supports native structured plan events, but local rollout files should not be assumed to contain every transport-level plan artifact. | |
| 67 | − | |
| 68 | − | |
| 69 | −Native surfaces confirmed from upstream docs/source: | |
| 70 | − | |
| 71 | −- collaboration mode with plan behavior | |
| 72 | 33 | - `turn/plan/updated` |
| 73 | 34 | - `item/plan/delta` |
| 74 | 35 | - `item/tool/requestUserInput` |
| 75 | −- `serverRequest/resolved` cleanup / completion notification for user input | |
| 76 | −- `request_user_input` tool in core/runtime | |
| 36 | +- `serverRequest/resolved` | |
| 37 | +- lower-level `EventMsg` variants `plan_update`, `plan_delta`, and `request_user_input` | |
| 77 | 38 | |
| 78 | −Evidence inspected: | |
| 39 | +Important implication: | |
| 79 | 40 | |
| 80 | −- upstream `codex-rs/app-server/README.md` | |
| 81 | −- upstream `codex-rs/app-server-protocol` schemas | |
| 82 | −- upstream `codex-rs/core/src/tools/handlers/request_user_input.rs` | |
| 83 | −- upstream `codex-rs/core/templates/collaboration_mode/plan.md` | |
| 41 | +- local Codex rollout JSONL files are a lossy or higher-level projection | |
| 42 | +- they may preserve plan mode state and final `<proposed_plan>` output | |
| 43 | +- they should not be treated as proof that low-level plan events do or do not exist | |
| 84 | 44 | |
| 85 | −### Semantics | |
| 45 | +### Claude | |
| 86 | 46 | |
| 87 | −#### Plan mode | |
| 47 | +Claude protocol decisions should be based on: | |
| 88 | 48 | |
| 89 | −Codex plan mode is not just “tool execution denied”. It has explicit behavioral guidance and explicit planning outputs. | |
| 49 | +- installed SDK typings | |
| 50 | +- real local session JSONL evidence | |
| 90 | 51 | |
| 91 | −Two distinct plan surfaces exist: | |
| 52 | +Confirmed from local Claude transcript: | |
| 92 | 53 | |
| 93 | −1. **Structured plan state** | |
| 94 | − - `turn/plan/updated` | |
| 95 | − - payload contains: | |
| 96 | − - optional `explanation` | |
| 97 | − - `plan: Array<{ step, status }>` | |
| 98 | − - statuses are `pending | inProgress | completed` | |
| 54 | +- `AskUserQuestion` is a native `tool_use` | |
| 55 | +- `ExitPlanMode` is a native `tool_use` | |
| 56 | +- `AskUserQuestion.input.questions[]` is structured and adapter-parseable | |
| 99 | 57 | |
| 100 | −2. **Plan text stream** | |
| 101 | − - `item/plan/delta` | |
| 102 | − - useful for rendering streaming plan prose / bullet lists before or alongside structured state | |
| 58 | +### Cursor | |
| 103 | 59 | |
| 104 | −This maps very cleanly to our canonical events: | |
| 60 | +Cursor protocol decisions should be based on: | |
| 105 | 61 | |
| 106 | −- `turn/plan.updated` -> `turn.plan.updated` | |
| 107 | −- `item/plan/delta` -> `content.delta(plan_text)` | |
| 62 | +- ACP docs | |
| 63 | +- current local ACP probe output | |
| 108 | 64 | |
| 109 | −#### Structured user questions | |
| 65 | +Confirmed from probe: | |
| 110 | 66 | |
| 111 | −Codex has a dedicated `request_user_input` capability. | |
| 67 | +- ACP session modes include `agent`, `plan`, and `ask` | |
| 68 | +- currently observed ACP updates do not include native structured plan updates or native structured question prompts comparable to Codex or Claude | |
| 112 | 69 | |
| 113 | −Server request: | |
| 70 | +--- | |
| 114 | 71 | |
| 115 | −- `item/tool/requestUserInput` | |
| 72 | +## High-level product model | |
| 116 | 73 | |
| 117 | −Shape characteristics from upstream protocol/docs: | |
| 74 | +The implementation should separate three concepts that are currently easy to conflate. | |
| 118 | 75 | |
| 119 | −- 1–3 questions | |
| 120 | −- each question has: | |
| 121 | − - prompt text | |
| 122 | − - stable id | |
| 123 | − - required options | |
| 124 | −- response carries answers per question | |
| 125 | −- freeform notes are supported in UX and answer model | |
| 76 | +### 1. Operating mode | |
| 126 | 77 | |
| 127 | −Important nuance: | |
| 78 | +This is the agent runtime mode. | |
| 128 | 79 | |
| 129 | −- upstream handler currently requires non-empty options for every question | |
| 130 | −- Codex TUI also supports an optional freeform note / text alongside selected options | |
| 131 | −- pending question requests are turn-scoped and cleaned up on turn completion/interruption | |
| 80 | +Canonical operating modes: | |
| 132 | 81 | |
| 133 | −### Why Codex is the reference model | |
| 82 | +- `default` | |
| 83 | +- `plan` | |
| 84 | +- `ask` | |
| 85 | +- `execute` | |
| 86 | +- `unknown` | |
| 134 | 87 | |
| 135 | −Codex should be treated as the reference shape for our provider-agnostic abstraction because it already matches the desired UI: | |
| 88 | +This should be represented at runtime independently of plan content or prompt cards. | |
| 136 | 89 | |
| 137 | −- explicit plan lifecycle | |
| 138 | −- explicit multi-question prompt flow | |
| 139 | −- explicit answer submission | |
| 140 | −- explicit pending/resolved bookkeeping | |
| 90 | +### 2. Structured plan state | |
| 141 | 91 | |
| 142 | −### Adapter implications | |
| 92 | +This is the current structured plan snapshot, if the provider can supply one natively or if the adapter can synthesize one confidently. | |
| 143 | 93 | |
| 144 | −For Codex, the provider adapter layer should stay close to native protocol: | |
| 94 | +Canonical model: | |
| 145 | 95 | |
| 146 | −- keep `turn.plan.updated` | |
| 147 | −- keep `content.delta(plan_text)` | |
| 148 | −- map `item/tool/requestUserInput` to `user-input.requested` and `request.opened` | |
| 149 | −- map answer completion / cleanup to `user-input.resolved` and `request.resolved` | |
| 96 | +```ts | |
| 97 | +interface CanonicalPlanState { | |
| 98 | + explanation?: string | null; | |
| 99 | + steps: Array<{ | |
| 100 | + id?: string; | |
| 101 | + text: string; | |
| 102 | + status: 'pending' | 'inProgress' | 'completed'; | |
| 103 | + source: 'native' | 'synthesized'; | |
| 104 | + }>; | |
| 105 | +} | |
| 106 | +``` | |
| 150 | 107 | |
| 151 | −--- | |
| 108 | +### 3. Structured user-input prompt | |
| 152 | 109 | |
| 153 | −## Provider 2: Claude Agent SDK / Claude Code | |
| 110 | +This is the product-level question card model. | |
| 154 | 111 | |
| 155 | −### What exists natively | |
| 156 | − | |
| 157 | −Claude has an explicit plan permission mode: | |
| 158 | − | |
| 159 | −### Real local session evidence | |
| 160 | − | |
| 161 | −The sampled Claude session file provides concrete payloads for both key plan-mode tools: | |
| 162 | − | |
| 163 | −1. `AskUserQuestion` appears as a normal assistant `tool_use` block with input like: | |
| 164 | − | |
| 165 | −```json | |
| 166 | −{ | |
| 167 | − "type": "tool_use", | |
| 168 | − "name": "AskUserQuestion", | |
| 169 | − "input": { | |
| 170 | − "questions": [ | |
| 171 | − { | |
| 172 | − "question": "What does 'adding profiles to canvas' mean to you? What should profiles enable in canvas?", | |
| 173 | − "header": "Scope", | |
| 174 | − "options": [ | |
| 175 | − { "label": "Organize by profile", "description": "..." }, | |
| 176 | − { "label": "Profile icon bar in canvas", "description": "..." }, | |
| 177 | − { "label": "Per-profile canvas settings", "description": "..." }, | |
| 178 | − { "label": "All of the above", "description": "..." } | |
| 179 | − ], | |
| 180 | − "multiSelect": false | |
| 181 | − } | |
| 182 | − ] | |
| 183 | − } | |
| 184 | −} | |
| 185 | −``` | |
| 112 | +Canonical model: | |
| 186 | 113 | |
| 187 | −2. `ExitPlanMode` also appears as a `tool_use` block, with a long-form `plan` string payload containing the finalized implementation spec. | |
| 114 | +```ts | |
| 115 | +interface CanonicalUserInputPrompt { | |
| 116 | + promptId: string; | |
| 117 | + title?: string; | |
| 118 | + description?: string; | |
| 119 | + questions: Array<{ | |
| 120 | + id: string; | |
| 121 | + header?: string; | |
| 122 | + label: string; | |
| 123 | + description?: string; | |
| 124 | + options: Array<{ | |
| 125 | + id: string; | |
| 126 | + label: string; | |
| 127 | + description?: string; | |
| 128 | + recommended?: boolean; | |
| 129 | + }>; | |
| 130 | + multiSelect?: boolean; | |
| 131 | + allowFreeform?: boolean; | |
| 132 | + freeformPlaceholder?: string; | |
| 133 | + required?: boolean; | |
| 134 | + }>; | |
| 135 | + source: 'native' | 'tool-derived' | 'synthesized'; | |
| 136 | +} | |
| 188 | 137 | |
| 189 | −That gives us two strong adapter conclusions: | |
| 138 | +interface CanonicalUserInputAnswer { | |
| 139 | + promptId: string; | |
| 140 | + answers: Array<{ | |
| 141 | + questionId: string; | |
| 142 | + selectedOptionIds?: string[]; | |
| 143 | + text?: string; | |
| 144 | + }>; | |
| 145 | +} | |
| 146 | +``` | |
| 190 | 147 | |
| 191 | −- Claude user-input prompting is definitely tool-based and structurally parseable. | |
| 192 | −- Claude final plan handoff is tool-based too, not necessarily a dedicated transport event like Codex `turn/plan/updated`. | |
| 148 | +This should remain distinct from approvals. | |
| 193 | 149 | |
| 194 | −### Concrete adapter mapping from transcript evidence | |
| 150 | +--- | |
| 195 | 151 | |
| 196 | −Recommended Claude mapping based on real session data: | |
| 152 | +## Shared orchestration plan | |
| 197 | 153 | |
| 198 | −- `tool_use.name === "AskUserQuestion"` -> `user-input.requested` | |
| 199 | −- user answer submission / tool result -> `user-input.resolved` | |
| 200 | −- `tool_use.name === "ExitPlanMode"` with `input.plan` -> final plan handoff event or synthesized `turn.plan.updated` snapshot | |
| 154 | +## Summary | |
| 201 | 155 | |
| 202 | −The `AskUserQuestion` payload shape is already close to the UI we want: | |
| 156 | +Implement a canonical plan-mode interaction pipeline that starts in provider adapters, flows through provider runtime ingestion and orchestration projection, and terminates in capability-driven frontend rendering and response submission. | |
| 203 | 157 | |
| 204 | −- `questions[]` array | |
| 205 | −- each question has `header` and `question` | |
| 206 | −- each question has `options[]` with `label` + `description` | |
| 207 | −- `multiSelect` is explicit | |
| 158 | +### Shared goals | |
| 208 | 159 | |
| 209 | −The main remaining unknown is the exact runtime shape of the **answer** payload that Claude expects back for this tool. | |
| 160 | +- normalize plan mode state from each provider | |
| 161 | +- normalize structured prompts where possible | |
| 162 | +- preserve native payloads for later refinement | |
| 163 | +- avoid UI coupling to provider names | |
| 164 | +- support degraded conversational fallback where native structured prompts are unavailable | |
| 210 | 165 | |
| 166 | +--- | |
| 211 | 167 | |
| 212 | −- `PermissionMode = 'default' | 'acceptEdits' | 'bypassPermissions' | 'plan' | 'dontAsk'` | |
| 168 | +## Orchestration work items | |
| 213 | 169 | |
| 214 | −Confirmed from installed SDK typings: | |
| 170 | +### 1. Extend canonical provider runtime contracts | |
| 215 | 171 | |
| 216 | −- `/Users/julius/.bun/install/cache/@anthropic-ai/claude-agent-sdk@0.2.62@@@1/sdk.d.ts` | |
| 217 | −- `/Users/julius/.bun/install/cache/@anthropic-ai/claude-agent-sdk@0.2.62@@@1/sdk-tools.d.ts` | |
| 172 | +Files: | |
| 218 | 173 | |
| 219 | −Claude also exposes plan-related tools/types: | |
| 174 | +- `packages/contracts/src/providerRuntime.ts` | |
| 175 | +- `packages/contracts/src/provider.ts` | |
| 176 | +- `EVENTS.md` | |
| 220 | 177 | |
| 221 | −- `ExitPlanMode` | |
| 222 | −- `AskUserQuestion` | |
| 178 | +Add or formalize the following runtime concepts: | |
| 223 | 179 | |
| 224 | −These are strong signals that Claude’s plan UX is built around tool usage within a `permissionMode: 'plan'` session rather than a dedicated standalone `turn.plan.updated` event. | |
| 180 | +- provider interactive capability payload | |
| 181 | +- plan-mode state event or mode metadata surface | |
| 182 | +- canonical structured user-input payload schema | |
| 183 | +- canonical structured user-input answer payload schema | |
| 184 | +- final-plan handoff payload for providers that emit a completed plan as text or tool output | |
| 225 | 185 | |
| 226 | −### What the wire stream exposes | |
| 186 | +Required additions: | |
| 227 | 187 | |
| 228 | −Claude adapter input today is built from SDK messages like: | |
| 188 | +```ts | |
| 189 | +interface ProviderInteractiveCapabilities { | |
| 190 | + supportsPlanMode: boolean; | |
| 191 | + supportsAskMode: boolean; | |
| 192 | + supportsStructuredPlanUpdates: boolean; | |
| 193 | + supportsPlanTextStreaming: boolean; | |
| 194 | + supportsStructuredUserInput: boolean; | |
| 195 | + supportsFreeformUserInput: boolean; | |
| 196 | + supportsExitPlanMode: boolean; | |
| 197 | +} | |
| 198 | +``` | |
| 229 | 199 | |
| 230 | −- `system:init` | |
| 231 | −- `system:status` | |
| 232 | −- `stream_event` | |
| 233 | −- `assistant` | |
| 234 | −- `result` | |
| 235 | −- hook/task/tool summary events | |
| 200 | +Canonical runtime behavior: | |
| 236 | 201 | |
| 237 | −The SDK stream exposes: | |
| 202 | +- `request.opened/request.resolved` remain transport-oriented | |
| 203 | +- `user-input.requested/user-input.resolved` remain product-oriented | |
| 204 | +- `turn.plan.updated` remains the canonical structured plan update event | |
| 205 | +- `content.delta(streamKind=plan_text)` remains the canonical streaming plan text surface | |
| 238 | 206 | |
| 239 | −- message deltas | |
| 240 | −- tool-use starts/stops via content blocks | |
| 241 | −- permission-mode metadata | |
| 242 | −- final result and usage | |
| 207 | +### 2. Add canonical mode/capability publication from providers | |
| 243 | 208 | |
| 244 | −What it does **not** appear to expose as a first-class event: | |
| 209 | +Files: | |
| 245 | 210 | |
| 246 | −- no direct equivalent to Codex `turn/plan/updated` | |
| 247 | −- no direct equivalent to Codex `item/plan/delta` | |
| 248 | −- no dedicated top-level `user-input.requested` transport event in the SDK stream | |
| 211 | +- `apps/server/src/provider/Layers/*Adapter.ts` | |
| 212 | +- `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts` | |
| 249 | 213 | |
| 250 | −Instead, plan-mode-related behavior appears to surface through tool uses: | |
| 214 | +Each adapter should publish provider capabilities and current operating mode early in session startup and when mode changes. | |
| 251 | 215 | |
| 252 | −- `AskUserQuestion` tool | |
| 253 | −- `ExitPlanMode` tool | |
| 254 | −- regular content/tool stream events | |
| 216 | +Preferred shape: | |
| 255 | 217 | |
| 256 | −### Semantics | |
| 218 | +- capabilities included in `session.configured` | |
| 219 | +- current mode included in `session.configured`, `session.state.changed`, or a dedicated mode field on turn/session runtime metadata | |
| 257 | 220 | |
| 258 | −#### Plan mode | |
| 221 | +### 3. Preserve and project plan/user-input state in orchestration | |
| 259 | 222 | |
| 260 | −Claude’s plan mode is primarily a **permission / behavior mode**: | |
| 223 | +Files: | |
| 261 | 224 | |
| 262 | −- session or turn is created with `permissionMode: 'plan'` | |
| 263 | −- tool execution is prevented / constrained | |
| 264 | −- assistant can explore, reason, and propose a plan | |
| 265 | −- eventually it may call `ExitPlanMode` to transition toward implementation | |
| 225 | +- `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts` | |
| 226 | +- `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts` | |
| 227 | +- any orchestration projection models feeding the web socket domain events | |
| 266 | 228 | |
| 267 | −That means Claude plan mode is native, but **structured plan state is not guaranteed**. | |
| 229 | +Implementation requirements: | |
| 268 | 230 | |
| 269 | −In practice, there are two likely UI strategies: | |
| 231 | +- persist latest structured plan snapshot per active turn | |
| 232 | +- persist pending structured user-input prompts per turn | |
| 233 | +- persist resolved answers for history rendering | |
| 234 | +- preserve raw/native payloads for debugging and future adapter improvements | |
| 270 | 235 | |
| 271 | −1. **Best-effort structured extraction** | |
| 272 | − - detect plan-like assistant/tool outputs | |
| 273 | − - synthesize `turn.plan.updated` from them | |
| 274 | −2. **Text-first plan rendering** | |
| 275 | − - show reasoning / assistant output as plan narrative | |
| 276 | − - only emit `turn.plan.updated` when we can confidently extract steps | |
| 236 | +### 4. Add frontend state model for plan mode | |
| 277 | 237 | |
| 278 | −#### Asking user questions | |
| 238 | +Files likely involved: | |
| 279 | 239 | |
| 280 | −Claude’s `AskUserQuestion` appears to be a tool, not a dedicated transport-level question request primitive. | |
| 240 | +- web app session/thread stores | |
| 241 | +- conversation/event rendering layer | |
| 242 | +- input composer / pending interaction surfaces | |
| 281 | 243 | |
| 282 | −That means the adapter likely needs to: | |
| 244 | +Add frontend state for: | |
| 283 | 245 | |
| 284 | −- identify `tool_use` for `AskUserQuestion` | |
| 285 | −- parse its input payload into canonical question schema | |
| 286 | −- expose `user-input.requested` | |
| 287 | −- accept a user answer and feed it back as the tool result / synthetic user response, depending SDK control path | |
| 246 | +- current provider capabilities | |
| 247 | +- current operating mode | |
| 248 | +- pending structured prompt | |
| 249 | +- structured plan snapshot | |
| 250 | +- streaming plan text buffer | |
| 251 | +- final plan handoff state when present | |
| 288 | 252 | |
| 289 | −This is adapter work, not just schema plumbing. | |
| 253 | +### 5. Implement capability-driven UI rendering | |
| 290 | 254 | |
| 291 | −### Recommended stance for Claude | |
| 255 | +UI rules: | |
| 292 | 256 | |
| 293 | −Claude should support the same canonical user-facing contract, but the adapter will need to **manufacture structure from native tool activity**. | |
| 257 | +- if `supportsStructuredUserInput`, render the multi-question card UI | |
| 258 | +- if not, fall back to standard conversational reply flow | |
| 259 | +- if `supportsStructuredPlanUpdates`, render status-tracked plan steps | |
| 260 | +- if only `supportsPlanTextStreaming`, render streaming plan prose | |
| 261 | +- if neither exists, render standard assistant content in plan mode with clear mode indicator | |
| 294 | 262 | |
| 295 | −Recommended mapping: | |
| 263 | +### 6. Implement front-to-back response submission path | |
| 296 | 264 | |
| 297 | −- `permissionMode: 'plan'` => canonical session/turn metadata `mode = plan` | |
| 298 | −- `AskUserQuestion` tool use => canonical `user-input.requested` | |
| 299 | −- submitted answer => canonical `user-input.resolved` | |
| 300 | −- `ExitPlanMode` tool use => canonical state transition or request to continue with implementation | |
| 301 | −- assistant text / tool summaries in plan mode => optional synthesized `turn.plan.updated` | |
| 265 | +Requirements: | |
| 302 | 266 | |
| 303 | −### Risk / uncertainty | |
| 267 | +- question card submit sends canonical answer payload to server | |
| 268 | +- server routes answer to the correct provider adapter pending request/tool context | |
| 269 | +- resolved provider response emits both: | |
| 270 | + - `user-input.resolved` | |
| 271 | + - `request.resolved` when applicable | |
| 304 | 272 | |
| 305 | −Claude currently appears less deterministic than Codex for structured plans. | |
| 273 | +### 7. History and reconnect behavior | |
| 306 | 274 | |
| 307 | −Main unknowns to resolve during implementation: | |
| 275 | +Requirements: | |
| 308 | 276 | |
| 309 | −- exact `AskUserQuestion` tool input/output payload shapes at runtime | |
| 310 | −- whether freeform responses are supported natively or need synthetic augmentation | |
| 311 | −- whether `ExitPlanMode` should be exposed to UI explicitly or handled internally by the adapter/session controller | |
| 277 | +- pending structured prompts should survive reconnect/resubscribe if still active | |
| 278 | +- resolved prompts should be rendered in history as completed interactions | |
| 279 | +- plan snapshots should be replayable from persisted orchestration activity | |
| 280 | +- current streaming plan text should resume cleanly on reconnect if provider continues emitting deltas | |
| 312 | 281 | |
| 313 | 282 | --- |
| 314 | 283 | |
| 315 | −## Provider 3: Cursor ACP | |
| 284 | +## Provider adapter plans | |
| 316 | 285 | |
| 317 | −### What exists natively | |
| 286 | +## Provider 1: Codex App Server | |
| 318 | 287 | |
| 319 | −Cursor ACP does have native session modes. | |
| 288 | +### Summary | |
| 320 | 289 | |
| 321 | −Confirmed by local probe output in `.tmp/acp-plan-probe/summary.json`: | |
| 290 | +Codex is the reference implementation and should be wired as the strongest native provider. | |
| 322 | 291 | |
| 323 | −- `agent` | |
| 324 | −- `plan` | |
| 325 | −- `ask` | |
| 326 | − | |
| 327 | −The probe also confirmed current primary ACP update surfaces: | |
| 292 | +### Native protocol mapping | |
| 328 | 293 | |
| 329 | −- `available_commands_update` | |
| 330 | −- `agent_thought_chunk` | |
| 331 | −- `agent_message_chunk` | |
| 332 | −- `tool_call` | |
| 333 | −- `tool_call_update` | |
| 334 | −- `session/request_permission` | |
| 294 | +Source of truth: upstream open-source app-server protocol. | |
| 335 | 295 | |
| 336 | −### What the probe did not find | |
| 296 | +Native incoming surfaces: | |
| 337 | 297 | |
| 338 | −The existing probe did **not** surface: | |
| 298 | +- `turn/plan/updated` | |
| 299 | +- `item/plan/delta` | |
| 300 | +- `item/tool/requestUserInput` | |
| 301 | +- `serverRequest/resolved` | |
| 302 | +- lower-level `EventMsg.plan_update` | |
| 303 | +- lower-level `EventMsg.plan_delta` | |
| 304 | +- lower-level `EventMsg.request_user_input` | |
| 339 | 305 | |
| 340 | −- structured plan update events | |
| 341 | −- a dedicated “ask user question” request | |
| 342 | −- a dedicated plan delta stream | |
| 306 | +### Adapter implementation | |
| 343 | 307 | |
| 344 | −The current ACP surface therefore looks much thinner than Codex. | |
| 308 | +Files: | |
| 345 | 309 | |
| 346 | −### Semantics | |
| 310 | +- `apps/server/src/provider/Layers/CodexAdapter.ts` | |
| 311 | +- related tests in `apps/server/src/provider/Layers/*Codex*.test.ts` | |
| 347 | 312 | |
| 348 | −#### Plan mode | |
| 313 | +Implementation requirements: | |
| 349 | 314 | |
| 350 | −Cursor `plan` mode clearly exists as a session mode, but as currently observed it seems to mean: | |
| 315 | +1. Keep native structured plan updates as-is: | |
| 316 | +- `turn/plan/updated` -> `turn.plan.updated` | |
| 351 | 317 | |
| 352 | −- read-only / design-first behavior | |
| 353 | −- regular thought/message chunk streaming | |
| 354 | −- no dedicated structured plan payload | |
| 318 | +2. Keep native plan text streaming as-is: | |
| 319 | +- `item/plan/delta` -> `content.delta` with `streamKind: "plan_text"` | |
| 355 | 320 | |
| 356 | −So the plan is native as a mode, but not obviously as a structured protocol surface. | |
| 321 | +3. Keep native structured user-input as-is: | |
| 322 | +- `item/tool/requestUserInput` -> `user-input.requested` | |
| 323 | +- also emit `request.opened` with a transport request type | |
| 357 | 324 | |
| 358 | −#### Asking the user questions | |
| 325 | +4. On client answer submission: | |
| 326 | +- route answer to the corresponding Codex pending request id | |
| 327 | +- emit `user-input.resolved` | |
| 328 | +- observe/forward `serverRequest/resolved` -> `request.resolved` | |
| 359 | 329 | |
| 360 | −Cursor ACP currently gives us a nearby primitive only for **permission requests**: | |
| 330 | +5. Publish capabilities: | |
| 331 | +- `supportsPlanMode = true` | |
| 332 | +- `supportsAskMode = false` unless Codex exposes a distinct ask mode separately | |
| 333 | +- `supportsStructuredPlanUpdates = true` | |
| 334 | +- `supportsPlanTextStreaming = true` | |
| 335 | +- `supportsStructuredUserInput = true` | |
| 336 | +- `supportsFreeformUserInput = true` if answer payload supports note/text in practice | |
| 337 | +- `supportsExitPlanMode = false` unless a separate explicit tool/event is introduced | |
| 361 | 338 | |
| 362 | −- `session/request_permission` | |
| 339 | +### Codex-specific tests | |
| 363 | 340 | |
| 364 | −That is not the same as product-level question asking. | |
| 341 | +- `turn/plan/updated` maps to canonical structured plan state | |
| 342 | +- `item/plan/delta` maps to `plan_text` | |
| 343 | +- `item/tool/requestUserInput` maps to canonical structured prompt shape | |
| 344 | +- answer response resolves pending request and emits both canonical resolved events | |
| 345 | +- reconnect/history replay preserves plan snapshot and pending prompt | |
| 365 | 346 | |
| 366 | −The existence of `ask` mode suggests Cursor may handle asking by: | |
| 347 | +--- | |
| 367 | 348 | |
| 368 | −- normal assistant prose in ask mode | |
| 369 | −- possibly mode-switch semantics | |
| 370 | −- possibly skills/commands that we have not yet triggered | |
| 349 | +## Provider 2: Claude Agent SDK / Claude Code | |
| 371 | 350 | |
| 372 | −But based on current probe evidence, we should **not assume** a Codex-style structured question API exists in ACP. | |
| 351 | +### Summary | |
| 373 | 352 | |
| 374 | −### Recommended stance for Cursor | |
| 353 | +Claude requires tool-aware adaptation. Native structured interaction exists, but it is surfaced through `tool_use` blocks rather than dedicated transport events equivalent to Codex. | |
| 375 | 354 | |
| 376 | −Treat Cursor as: | |
| 355 | +### Real native evidence | |
| 377 | 356 | |
| 378 | −- native support for `mode = plan` | |
| 379 | −- no proven native support for structured plan steps | |
| 380 | −- no proven native support for structured user-input questions | |
| 357 | +From the local Claude session transcript: | |
| 381 | 358 | |
| 382 | −Therefore Cursor likely needs the most adapter synthesis: | |
| 359 | +- `AskUserQuestion` arrives as: | |
| 360 | + - assistant message | |
| 361 | + - content block type `tool_use` | |
| 362 | + - `name: "AskUserQuestion"` | |
| 363 | + - `input.questions[]` | |
| 364 | +- `ExitPlanMode` arrives as: | |
| 365 | + - assistant message | |
| 366 | + - content block type `tool_use` | |
| 367 | + - `name: "ExitPlanMode"` | |
| 368 | + - `input.plan` containing finalized plan text/spec | |
| 383 | 369 | |
| 384 | −- derive plan-mode state from ACP session mode | |
| 385 | −- optionally synthesize `turn.plan.updated` from assistant text if we can extract steps safely | |
| 386 | −- for interactive questions, likely fall back to regular conversational turns unless a richer ACP mechanism is discovered | |
| 370 | +Claude also has native: | |
| 387 | 371 | |
| 388 | −### Important product implication | |
| 372 | +- `permissionMode: 'plan'` | |
| 373 | +- `system:init.permissionMode` | |
| 374 | +- `system:status.permissionMode` | |
| 389 | 375 | |
| 390 | −If we want the same rich “multiple-choice question card” UX across all providers, Cursor may need one of these paths: | |
| 376 | +### Adapter implementation | |
| 391 | 377 | |
| 392 | −1. **Provider-native path** if later ACP exploration finds structured user question primitives | |
| 393 | −2. **Adapter-mediated path** where the provider is instructed to emit a machine-readable question block in text and adapter parses it | |
| 394 | −3. **Capability downgrade** where Cursor only supports conversational follow-up, not full structured plan-question cards | |
| 378 | +Files: | |
| 395 | 379 | |
| 396 | −Today, evidence supports option 3 as the safe baseline. | |
| 380 | +- `apps/server/src/provider/Layers/ClaudeCodeAdapter.ts` | |
| 381 | +- `apps/server/src/provider/Layers/ClaudeCodeAdapter.test.ts` | |
| 382 | + | |
| 383 | +Implementation requirements: | |
| 384 | + | |
| 385 | +1. Publish native operating mode: | |
| 386 | +- `permissionMode: 'plan'` -> canonical mode `plan` | |
| 387 | +- if future values map cleanly, publish `ask` or `default` accordingly | |
| 388 | + | |
| 389 | +2. Detect `AskUserQuestion` tool uses: | |
| 390 | +- inspect `tool_use` blocks in assistant messages / stream events | |
| 391 | +- when `name === 'AskUserQuestion'`, convert `input.questions[]` to canonical `user-input.requested` | |
| 392 | +- also emit `request.opened` using a Claude-specific tool-user-input request type | |
| 393 | + | |
| 394 | +3. Canonical question mapping for Claude: | |
| 395 | +- `header` -> canonical `header` | |
| 396 | +- `question` -> canonical `label` | |
| 397 | +- `options[].label` -> canonical option label | |
| 398 | +- `options[].description` -> canonical option description | |
| 399 | +- `multiSelect` -> canonical `multiSelect` | |
| 400 | +- source = `tool-derived` | |
| 401 | + | |
| 402 | +4. Implement answer submission path: | |
| 403 | +- on UI answer submit, convert canonical answers back into whatever Claude expects for the `AskUserQuestion` tool result path | |
| 404 | +- emit `user-input.resolved` | |
| 405 | +- emit `request.resolved` | |
| 406 | + | |
| 407 | +5. Detect `ExitPlanMode` tool uses: | |
| 408 | +- when `name === 'ExitPlanMode'`, parse `input.plan` | |
| 409 | +- emit a final plan handoff event and/or synthesize a `turn.plan.updated` snapshot if safe | |
| 410 | +- treat this as the boundary between planning and implementation readiness | |
| 411 | + | |
| 412 | +6. Structured plan behavior: | |
| 413 | +- Claude does not currently appear to expose a native `turn/plan/updated` equivalent | |
| 414 | +- initial implementation should not over-synthesize incremental steps | |
| 415 | +- use two-tier strategy: | |
| 416 | + - tier 1: preserve plan-related assistant text in `plan_text` only if clearly attributable | |
| 417 | + - tier 2: optionally synthesize `turn.plan.updated` from explicit structured plan strings only when parsing is robust | |
| 418 | + | |
| 419 | +7. Publish capabilities: | |
| 420 | +- `supportsPlanMode = true` | |
| 421 | +- `supportsAskMode = false` unless explicit Claude mode is observed | |
| 422 | +- `supportsStructuredPlanUpdates = partial/false initially` | |
| 423 | +- `supportsPlanTextStreaming = partial` | |
| 424 | +- `supportsStructuredUserInput = true` | |
| 425 | +- `supportsFreeformUserInput = unknown/false until answer path confirmed` | |
| 426 | +- `supportsExitPlanMode = true` | |
| 427 | + | |
| 428 | +### Claude-specific tests | |
| 429 | + | |
| 430 | +- `system:init` and `system:status` publish plan mode metadata | |
| 431 | +- `AskUserQuestion` tool maps into canonical prompt schema | |
| 432 | +- multi-select and option descriptions round-trip correctly | |
| 433 | +- answer submission resolves the pending prompt | |
| 434 | +- `ExitPlanMode` tool emits final plan handoff state | |
| 435 | +- ordinary tool uses do not get misclassified as prompts or plan exits | |
| 436 | + | |
| 437 | +### Claude-specific open question to resolve during implementation | |
| 438 | + | |
| 439 | +- exact runtime shape expected for the answer payload returned to `AskUserQuestion` | |
| 440 | + | |
| 441 | +Implementation default: | |
| 442 | + | |
| 443 | +- build adapter abstraction so only the final answer-serialization function is provider-specific and easily swappable once runtime shape is confirmed | |
| 397 | 444 | |
| 398 | 445 | --- |
| 399 | 446 | |
| 400 | −## Cross-provider comparison | |
| 447 | +## Provider 3: Cursor ACP | |
| 401 | 448 | |
| 402 | −### Capability matrix | |
| 449 | +### Summary | |
| 403 | 450 | |
| 404 | −| Capability | Codex App Server | Claude Agent SDK | Cursor ACP | | |
| 405 | −| --- | --- | --- | --- | | |
| 406 | −| Native plan mode | Yes | Yes | Yes | | |
| 407 | −| Structured plan update event | Yes | Not obvious / likely no | Not observed | | |
| 408 | −| Plan text delta stream | Yes | Not first-class | Not observed | | |
| 409 | −| Native structured user question API | Yes | Tool-based, likely yes-ish | Not observed | | |
| 410 | −| Native answer resolution lifecycle | Yes | Likely adapter-mediated | Not observed | | |
| 411 | −| Best provider for canonical contract | Strong | Medium | Weak | | |
| 451 | +Cursor should initially ship with degraded plan-mode support: native mode awareness, but no claim of structured prompt cards or structured plan steps until protocol evidence exists. | |
| 412 | 452 | |
| 413 | −### Core insight | |
| 453 | +### Real native evidence | |
| 414 | 454 | |
| 415 | −“Plan mode” means **different things** across providers: | |
| 455 | +From the ACP probe: | |
| 416 | 456 | |
| 417 | −- **Codex**: plan mode is a first-class runtime concept with structured plan and question surfaces. | |
| 418 | −- **Claude**: plan mode is a first-class permission mode, but structured plan/question UI likely needs adapter interpretation. | |
| 419 | −- **Cursor**: plan mode is a first-class session mode, but richer plan/question structure is not currently exposed. | |
| 457 | +- available modes include `agent`, `plan`, and `ask` | |
| 458 | +- observed updates include: | |
| 459 | + - `available_commands_update` | |
| 460 | + - `agent_thought_chunk` | |
| 461 | + - `agent_message_chunk` | |
| 462 | + - `tool_call` | |
| 463 | + - `tool_call_update` | |
| 464 | +- observed request type: | |
| 465 | + - `session/request_permission` | |
| 420 | 466 | |
| 421 | −--- | |
| 467 | +Not observed: | |
| 422 | 468 | |
| 423 | −## Recommended provider-agnostic contract | |
| 469 | +- native structured plan update events | |
| 470 | +- native plan text delta event distinct from normal message chunks | |
| 471 | +- native structured ask-user-question event | |
| 424 | 472 | |
| 425 | −We should separate three concerns that are currently easy to conflate. | |
| 473 | +### Adapter implementation | |
| 426 | 474 | |
| 427 | −### 1. Session / turn mode | |
| 475 | +Files: | |
| 428 | 476 | |
| 429 | −A canonical mode flag: | |
| 430 | − | |
| 431 | −- `default` | |
| 432 | −- `plan` | |
| 433 | −- `ask` | |
| 434 | −- `execute` | |
| 435 | −- `unknown` | |
| 477 | +- `apps/server/src/provider/Layers/CursorAdapter.ts` | |
| 478 | +- `apps/server/src/provider/Layers/CursorAdapter.test.ts` | |
| 479 | +- probe scripts under `scripts/` | |
| 436 | 480 | |
| 437 | −This is about **agent operating mode**, not UI widgets. | |
| 481 | +Implementation requirements: | |
| 438 | 482 | |
| 439 | −### 2. Structured plan state | |
| 483 | +1. Publish native operating mode: | |
| 484 | +- map ACP `plan` mode -> canonical `plan` | |
| 485 | +- map ACP `ask` mode -> canonical `ask` | |
| 486 | +- map ACP `agent` mode -> canonical `default` or `execute` depending product semantics | |
| 440 | 487 | |
| 441 | −A canonical plan model: | |
| 488 | +2. Do not claim unsupported structured capabilities: | |
| 489 | +- no native `turn.plan.updated` | |
| 490 | +- no native `user-input.requested` | |
| 491 | +- no native `ExitPlanMode` equivalent confirmed | |
| 442 | 492 | |
| 443 | −```ts | |
| 444 | −interface CanonicalPlanState { | |
| 445 | − explanation?: string | null; | |
| 446 | − steps: Array<{ | |
| 447 | − id?: string; | |
| 448 | − text: string; | |
| 449 | − status: 'pending' | 'inProgress' | 'completed'; | |
| 450 | − source: 'native' | 'synthesized'; | |
| 451 | − }>; | |
| 452 | −} | |
| 453 | −``` | |
| 493 | +3. Use graceful fallback behavior: | |
| 494 | +- render plan mode as mode state + assistant text stream | |
| 495 | +- render ask mode as conversational mode + normal user composer | |
| 496 | +- do not render structured question cards for Cursor initially | |
| 454 | 497 | |
| 455 | −Rules: | |
| 498 | +4. Preserve room for future enrichment: | |
| 499 | +- keep raw ACP notifications available in native event logs | |
| 500 | +- extend the ACP probe to search for hidden or uncommon prompt/mode-change surfaces | |
| 456 | 501 | |
| 457 | −- Codex can populate this natively. | |
| 458 | −- Claude/Cursor may populate it only when extraction is confident. | |
| 459 | −- UI should tolerate absence of structured plan state. | |
| 502 | +5. Publish capabilities: | |
| 503 | +- `supportsPlanMode = true` | |
| 504 | +- `supportsAskMode = true` | |
| 505 | +- `supportsStructuredPlanUpdates = false` | |
| 506 | +- `supportsPlanTextStreaming = false` initially | |
| 507 | +- `supportsStructuredUserInput = false` | |
| 508 | +- `supportsFreeformUserInput = false` via structured prompt path | |
| 509 | +- `supportsExitPlanMode = false` | |
| 460 | 510 | |
| 461 | −### 3. Structured user-input prompt | |
| 511 | +### Cursor-specific tests | |
| 462 | 512 | |
| 463 | −Canonical prompt should be modeled independently of approvals: | |
| 513 | +- ACP mode metadata maps correctly into canonical mode | |
| 514 | +- `agent_thought_chunk` and `agent_message_chunk` still render normally in plan mode | |
| 515 | +- UI does not try to open structured prompt cards for Cursor | |
| 516 | +- fallback conversational flow remains functional | |
| 464 | 517 | |
| 465 | −```ts | |
| 466 | −interface CanonicalUserInputPrompt { | |
| 467 | − promptId: string; | |
| 468 | − title?: string; | |
| 469 | − description?: string; | |
| 470 | − questions: Array<{ | |
| 471 | − id: string; | |
| 472 | − label: string; | |
| 473 | − description?: string; | |
| 474 | − options: Array<{ | |
| 475 | − id: string; | |
| 476 | − label: string; | |
| 477 | − description?: string; | |
| 478 | − recommended?: boolean; | |
| 479 | − }>; | |
| 480 | − allowFreeform?: boolean; | |
| 481 | − freeformPlaceholder?: string; | |
| 482 | − required?: boolean; | |
| 483 | − }>; | |
| 484 | − source: 'native' | 'tool-derived' | 'synthesized'; | |
| 485 | −} | |
| 486 | −``` | |
| 518 | +### Cursor-specific follow-up probe work | |
| 487 | 519 | |
| 488 | −And answers: | |
| 520 | +Add dedicated probes for: | |
| 489 | 521 | |
| 490 | −```ts | |
| 491 | −interface CanonicalUserInputAnswer { | |
| 492 | − promptId: string; | |
| 493 | − answers: Array<{ | |
| 494 | − questionId: string; | |
| 495 | − selectedOptionId?: string; | |
| 496 | − text?: string; | |
| 497 | − }>; | |
| 498 | −} | |
| 499 | −``` | |
| 522 | +- mode-switching during an active session | |
| 523 | +- ask-mode prompt behavior | |
| 524 | +- any request types besides `session/request_permission` | |
| 525 | +- whether specific built-in skills or prompt styles trigger structured question surfaces | |
| 500 | 526 | |
| 501 | −### Why separate `request.opened` from `user-input.requested` | |
| 527 | +--- | |
| 502 | 528 | |
| 503 | −Because they are not the same abstraction: | |
| 529 | +## Frontend implementation plan | |
| 504 | 530 | |
| 505 | −- approvals = “may I run/edit/do X?” | |
| 506 | −- user-input prompts = “which direction do you want?” | |
| 531 | +### Summary | |
| 507 | 532 | |
| 508 | −Codex already blurs these at transport level because `item/tool/requestUserInput` is a request. | |
| 509 | −But product and UI should keep them separate. | |
| 533 | +Render plan mode through a single UI model driven by canonical events and provider capabilities. | |
| 510 | 534 | |
| 511 | −Recommended rule: | |
| 535 | +### UI states to support | |
| 512 | 536 | |
| 513 | −- keep `request.opened/request.resolved` for transport-level correlation | |
| 514 | −- keep `user-input.requested/user-input.resolved` for product-level UX | |
| 537 | +- standard conversation | |
| 538 | +- plan mode with structured steps | |
| 539 | +- plan mode with text-only plan stream | |
| 540 | +- pending structured question card | |
| 541 | +- resolved question card in history | |
| 542 | +- final plan handoff / completed plan artifact | |
| 515 | 543 | |
| 516 | −--- | |
| 544 | +### Rendering rules | |
| 517 | 545 | |
| 518 | −## Adapter strategy by provider | |
| 546 | +1. Show operating mode indicator whenever current mode is `plan` or `ask` | |
| 547 | +2. Show structured question card only when a pending `user-input.requested` exists | |
| 548 | +3. Show structured plan step list when a current `turn.plan.updated` snapshot exists | |
| 549 | +4. Append `plan_text` streaming content beneath or alongside structured steps when both exist | |
| 550 | +5. Fall back to assistant text rendering when provider capabilities do not support structure | |
| 551 | +6. Show completed selected answers in history after `user-input.resolved` | |
| 519 | 552 | |
| 520 | −### Codex adapter | |
| 553 | +### Submission behavior | |
| 521 | 554 | |
| 522 | −Use native primitives directly. | |
| 555 | +- option click / freeform answer submits canonical answer payload | |
| 556 | +- disable duplicate submits while request is pending resolution | |
| 557 | +- preserve pending-card state across reconnects if request remains open | |
| 523 | 558 | |
| 524 | −Implementation target: | |
| 559 | +--- | |
| 525 | 560 | |
| 526 | −- preserve `turn.plan.updated` | |
| 527 | −- preserve `content.delta(plan_text)` | |
| 528 | −- preserve `user-input.requested` | |
| 529 | −- preserve `user-input.resolved` | |
| 530 | −- expose raw/native metadata for future UI improvements | |
| 561 | +## Data flow end to end | |
| 531 | 562 | |
| 532 | −### Claude adapter | |
| 563 | +1. Provider starts session and publishes capabilities + mode | |
| 564 | +2. User starts plan-mode turn or provider enters plan mode | |
| 565 | +3. Adapter emits structured plan and/or plan text events when available | |
| 566 | +4. Adapter emits `user-input.requested` when provider asks a structured question | |
| 567 | +5. Frontend renders question card or fallback conversational prompt based on capabilities | |
| 568 | +6. User submits answer | |
| 569 | +7. Server routes answer back to provider adapter pending request/tool context | |
| 570 | +8. Adapter emits `user-input.resolved` and `request.resolved` | |
| 571 | +9. Provider may continue planning, emit more plan updates, or emit final plan handoff | |
| 572 | +10. Frontend renders final completed plan state and history | |
| 533 | 573 | |
| 534 | −Implement tool-aware adaptation. | |
| 574 | +--- | |
| 535 | 575 | |
| 536 | −Implementation target: | |
| 576 | +## Test plan | |
| 537 | 577 | |
| 538 | −- treat `permissionMode: 'plan'` as native plan-mode state | |
| 539 | −- detect `AskUserQuestion` tool uses and convert to canonical user-input prompt | |
| 540 | −- convert question answers back into Claude-native tool response path | |
| 541 | −- detect `ExitPlanMode` and emit a canonical mode-change / handoff event | |
| 542 | −- optionally synthesize plan steps from assistant output in plan mode | |
| 578 | +### Contracts | |
| 543 | 579 | |
| 544 | −### Cursor adapter | |
| 580 | +Files: | |
| 545 | 581 | |
| 546 | −Implement conservative degraded support first. | |
| 582 | +- `packages/contracts/src/providerRuntime.test.ts` | |
| 583 | +- related schema tests | |
| 547 | 584 | |
| 548 | −Implementation target: | |
| 585 | +Add coverage for: | |
| 549 | 586 | |
| 550 | −- expose native session mode including `plan` and `ask` | |
| 551 | −- do not claim structured plan updates unless proven | |
| 552 | −- do not claim structured user-input prompts unless proven | |
| 553 | −- optionally add a provider capability flag so UI can fall back to conversational follow-up | |
| 587 | +- provider capabilities schema | |
| 588 | +- structured user-input prompt schema | |
| 589 | +- structured user-input answer schema | |
| 590 | +- mode metadata schema | |
| 554 | 591 | |
| 555 | −--- | |
| 592 | +### Server/provider tests | |
| 556 | 593 | |
| 557 | −## Capability flags we should add | |
| 594 | +Files: | |
| 558 | 595 | |
| 559 | −To avoid overpromising in the UI, add provider runtime capabilities such as: | |
| 596 | +- `apps/server/src/provider/Layers/CodexAdapter.test.ts` | |
| 597 | +- `apps/server/src/provider/Layers/ClaudeCodeAdapter.test.ts` | |
| 598 | +- `apps/server/src/provider/Layers/CursorAdapter.test.ts` | |
| 599 | +- `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts` | |
| 560 | 600 | |
| 561 | −```ts | |
| 562 | −interface ProviderInteractiveCapabilities { | |
| 563 | − supportsPlanMode: boolean; | |
| 564 | − supportsAskMode: boolean; | |
| 565 | − supportsStructuredPlanUpdates: boolean; | |
| 566 | − supportsPlanTextStreaming: boolean; | |
| 567 | − supportsStructuredUserInput: boolean; | |
| 568 | − supportsFreeformUserInput: boolean; | |
| 569 | − supportsExitPlanMode: boolean; | |
| 570 | −} | |
| 571 | −``` | |
| 601 | +Add coverage for: | |
| 572 | 602 | |
| 573 | −Expected initial values: | |
| 603 | +- capabilities emission | |
| 604 | +- mode propagation | |
| 605 | +- plan update propagation | |
| 606 | +- plan text propagation | |
| 607 | +- structured prompt lifecycle | |
| 608 | +- answer resolution lifecycle | |
| 609 | +- reconnect/history replay behavior | |
| 574 | 610 | |
| 575 | −- Codex: all or nearly all `true` | |
| 576 | −- Claude: `supportsPlanMode=true`, `supportsStructuredUserInput=likely true via adapter`, `supportsStructuredPlanUpdates=partial` | |
| 577 | −- Cursor: `supportsPlanMode=true`, `supportsAskMode=true`, most structured flags `false` | |
| 611 | +### Web/UI tests | |
| 578 | 612 | |
| 579 | −This lets the UI: | |
| 613 | +Add coverage for: | |
| 580 | 614 | |
| 581 | −- render rich plan cards only when supported | |
| 582 | −- fall back to plain assistant text + reply box otherwise | |
| 583 | −- avoid coupling the design to Codex-specific assumptions | |
| 615 | +- question card rendering from canonical prompt | |
| 616 | +- question answer submission | |
| 617 | +- resolved question history rendering | |
| 618 | +- structured plan step rendering | |
| 619 | +- text-only plan rendering | |
| 620 | +- capability-based fallback rendering for Cursor | |
| 584 | 621 | |
| 585 | −--- | |
| 622 | +### Probe / fixture tests | |
| 586 | 623 | |
| 587 | −## Suggested implementation order | |
| 588 | − | |
| 589 | −1. **Formalize provider capability flags** | |
| 590 | − - make support explicit before UI work | |
| 591 | −2. **Finish canonical user-input contracts** | |
| 592 | − - question schema, answer schema, source markers | |
| 593 | −3. **Codex end-to-end implementation first** | |
| 594 | − - closest match to desired UX | |
| 595 | −4. **Claude adapter translation second** | |
| 596 | − - parse `AskUserQuestion` and `ExitPlanMode` | |
| 597 | −5. **Cursor degraded plan-mode support third** | |
| 598 | − - mode awareness first, richer structure later | |
| 599 | −6. **UI renders by capability, not provider name** | |
| 600 | − - prevents provider-specific branching from leaking upward | |
| 624 | +- preserve the Claude transcript-derived `AskUserQuestion` and `ExitPlanMode` payloads as fixtures | |
| 625 | +- preserve Codex protocol fixtures for `turn/plan/updated`, `item/plan/delta`, and `item/tool/requestUserInput` | |
| 626 | +- preserve ACP probe summaries as fixtures for unsupported-capability assertions | |
| 601 | 627 | |
| 602 | 628 | --- |
| 603 | 629 | |
| 604 | −## Open questions | |
| 630 | +## Assumptions and defaults | |
| 605 | 631 | |
| 606 | −1. Claude runtime payloads: | |
| 607 | − - what exact `AskUserQuestion` input/output shapes arrive over the stream? | |
| 608 | −2. Claude answer submission path: | |
| 609 | − - should answers be injected as tool results, control responses, or synthetic user turns? | |
| 610 | −3. Cursor ACP: | |
| 611 | − - is there an undocumented or less common interactive prompt primitive beyond `session/request_permission`? | |
| 612 | −4. Cursor mode switching: | |
| 613 | − - can ACP session mode change mid-session, and is that surfaced as `session/update` or request/response only? | |
| 614 | −5. Plan synthesis: | |
| 615 | − - do we want best-effort extraction of numbered/bulleted plans into `turn.plan.updated` for Claude/Cursor, or should we keep structured plans native-only at first? | |
| 632 | +- Codex is the reference provider for full structured plan/question UX. | |
| 633 | +- Claude supports structured prompts via tool adaptation, but incremental structured plan updates are not assumed initially. | |
| 634 | +- Cursor supports plan and ask modes, but not structured prompt cards or structured plan step updates initially. | |
| 635 | +- Product/UI should degrade gracefully rather than invent unsupported provider behavior. | |
| 636 | +- Raw provider payloads should always be retained where feasible to support future adapter refinement. | |
| 616 | 637 | |
| 617 | 638 | --- |
| 618 | 639 | |
| 619 | −## Immediate recommendation | |
| 620 | − | |
| 621 | −Build the product contract around **capabilities**, not around the assumption that every provider has Codex-style plan/question primitives. | |
| 622 | − | |
| 623 | −Concretely: | |
| 624 | − | |
| 625 | −- Codex should drive the first full UX implementation. | |
| 626 | −- Claude should be supported with adapter-derived question handling and partial/synthesized plan state. | |
| 627 | −- Cursor should initially support mode-aware UX with graceful fallback to conversational replies rather than full structured question cards. | |
| 640 | +## Recommended execution order | |
| 628 | 641 | |
| 629 | −That gives us a provider-agnostic architecture without pretending the underlying protocols are already equivalent. | |
| 642 | +1. Formalize canonical capabilities + mode + structured prompt contracts | |
| 643 | +2. Wire capabilities/mode through orchestration and web socket projections | |
| 644 | +3. Finish Codex end-to-end plan mode implementation first | |
| 645 | +4. Implement Claude `AskUserQuestion` and `ExitPlanMode` adapter mapping | |
| 646 | +5. Add Cursor degraded plan/ask mode support | |
| 647 | +6. Build capability-driven frontend rendering and answer submission | |
| 648 | +7. Add reconnect/history coverage and transcript/protocol fixtures | |