| ... | ... | @@ -15,12 +15,12 @@ Claude integration must plug into this path instead of reintroducing legacy prov |
| 15 | 15 | |
| 16 | 16 | --- |
| 17 | 17 | |
| 18 | | ## Current constraints to design around |
| 18 | ## Current constraints to design around (post-Stage 1) |
| 19 | 19 | |
| 20 | 20 | 1. Provider runtime ingestion expects canonical `ProviderRuntimeEvent` shapes, not provider-native payloads. |
| 21 | | 2. `ProviderService.startSession` currently defaults to `provider: "codex"` unless explicitly provided. |
| 22 | | 3. `thread.turn.start` has no provider field today, so first-turn provider selection cannot be explicitly requested. |
| 23 | | 4. `ProviderService` requires adapter `startSession()` to return a `ProviderSession` with `threadId`. |
| 21 | 2. Start input now uses typed `providerOptions` and generic `resumeCursor`; top-level provider-specific fields were removed. |
| 22 | 3. `resumeCursor` is intentionally opaque outside adapters and must never be synthesized from `providerThreadId`. |
| 23 | 4. `ProviderService` still requires adapter `startSession()` to return a `ProviderSession` with `threadId`. |
| 24 | 24 | 5. Checkpoint revert currently calls `providerService.rollbackConversation()`, so Claude adapter needs a rollback strategy compatible with current reactor behavior. |
| 25 | 25 | 6. Web currently marks Claude as unavailable (`"Claude Code (soon)"`) and model picker is Codex-only. |
| 26 | 26 | |
| ... | ... | @@ -61,21 +61,24 @@ Update `packages/contracts/src/orchestration.ts`: |
| 61 | 61 | |
| 62 | 62 | This removes the implicit “Codex unless session already exists” behavior as the only path. |
| 63 | 63 | |
| 64 | | ### 1.3 Provider session start input for Claude runtime knobs |
| 64 | ### 1.3 Provider session start input for Claude runtime knobs (completed) |
| 65 | 65 | |
| 66 | 66 | Update `packages/contracts/src/provider.ts`: |
| 67 | 67 | |
| 68 | | 1. Extend `ProviderSessionStartInput` with optional Claude-specific fields (for example `claudeBinaryPath`, `permissionMode`, `maxThinkingTokens`). |
| 69 | | 2. Keep fields optional so current call sites remain valid. |
| 70 | | 3. Continue using generic `resumeThreadId` + `resumeCursor` as the cross-provider recovery mechanism. |
| 68 | 1. Move provider-specific start fields into typed `providerOptions`: |
| 69 | - `providerOptions.codex` |
| 70 | - `providerOptions.claudeCode` |
| 71 | 2. Keep `resumeCursor` as the single cross-provider resume input in `ProviderSessionStartInput`. |
| 72 | 3. Deprecate/remove `resumeThreadId` from the generic start contract. |
| 73 | 4. Treat `resumeCursor` as adapter-owned opaque state. |
| 71 | 74 | |
| 72 | | ### 1.4 Contract tests |
| 75 | ### 1.4 Contract tests (completed) |
| 73 | 76 | |
| 74 | 77 | Update/add tests in `packages/contracts/src/*.test.ts` for: |
| 75 | 78 | |
| 76 | 79 | 1. New command payload shape. |
| 77 | 80 | 2. Provider-aware model resolution behavior. |
| 78 | | 3. Backward compatibility of existing command/schema decoding. |
| 81 | 3. Breaking-change expectations for removed top-level provider fields. |
| 79 | 82 | |
| 80 | 83 | --- |
| 81 | 84 | |
| ... | ... | @@ -100,9 +103,9 @@ Baseline adapter options to support from day one: |
| 100 | 103 | |
| 101 | 104 | 1. `cwd` |
| 102 | 105 | 2. `model` |
| 103 | | 3. `pathToClaudeCodeExecutable` (from `claudeBinaryPath`) |
| 104 | | 4. `permissionMode` |
| 105 | | 5. `maxThinkingTokens` |
| 106 | 3. `pathToClaudeCodeExecutable` (from `providerOptions.claudeCode.binaryPath`) |
| 107 | 4. `permissionMode` (from `providerOptions.claudeCode.permissionMode`) |
| 108 | 5. `maxThinkingTokens` (from `providerOptions.claudeCode.maxThinkingTokens`) |
| 106 | 109 | 6. `resume` |
| 107 | 110 | 7. `resumeSessionAt` |
| 108 | 111 | 8. `includePartialMessages` |
| ... | ... | @@ -120,7 +123,7 @@ Required capabilities: |
| 120 | 123 | 2. Multi-turn input queue. |
| 121 | 124 | 3. Interrupt support. |
| 122 | 125 | 4. Approval request/response bridge. |
| 123 | | 5. Resume support via `resumeThreadId` / `resumeCursor`. |
| 126 | 5. Resume support via opaque `resumeCursor` (parsed inside Claude adapter only). |
| 124 | 127 | |
| 125 | 128 | #### 2.2.a Agent SDK details to preserve |
| 126 | 129 | |
| ... | ... | @@ -129,7 +132,7 @@ The adapter should explicitly rely on these SDK capabilities: |
| 129 | 132 | 1. `query()` returns an async iterable message stream and control methods (`interrupt`, `setModel`, `setPermissionMode`, `setMaxThinkingTokens`, account/status helpers). |
| 130 | 133 | 2. Multi-turn input is supported via async-iterable prompt input. |
| 131 | 134 | 3. Tool approval decisions are provided via `canUseTool`. |
| 132 | | 4. Resume support uses `resume` and optional `resumeSessionAt`. |
| 135 | 4. Resume support uses `resume` and optional `resumeSessionAt`, both derived by parsing adapter-owned `resumeCursor`. |
| 133 | 136 | 5. Hooks can be used for lifecycle signals (`Stop`, `PostToolUse`, etc.) when we need adapter-originated checkpoint/runtime events. |
| 134 | 137 | |
| 135 | 138 | #### 2.2.b Effect-native session lifecycle skeleton |
| ... | ... | @@ -142,21 +145,23 @@ const acquireSession = (input: ProviderSessionStartInput) => |
| 142 | 145 | Effect.acquireRelease( |
| 143 | 146 | Effect.tryPromise({ |
| 144 | 147 | try: async () => { |
| 148 | const claudeOptions = input.providerOptions?.claudeCode; |
| 149 | const resumeState = readClaudeResumeState(input.resumeCursor); |
| 145 | 150 | const abortController = new AbortController(); |
| 146 | 151 | const result = query({ |
| 147 | | prompt: makePromptAsyncIterable(input.sessionId), |
| 152 | prompt: makePromptAsyncIterable(), |
| 148 | 153 | options: { |
| 149 | 154 | cwd: input.cwd, |
| 150 | 155 | model: input.model, |
| 151 | | permissionMode: input.permissionMode, |
| 152 | | maxThinkingTokens: input.maxThinkingTokens, |
| 153 | | pathToClaudeCodeExecutable: input.claudeBinaryPath, |
| 154 | | resume: input.resumeThreadId, |
| 155 | | resumeSessionAt: readResumeCursor(input.resumeCursor), |
| 156 | permissionMode: claudeOptions?.permissionMode, |
| 157 | maxThinkingTokens: claudeOptions?.maxThinkingTokens, |
| 158 | pathToClaudeCodeExecutable: claudeOptions?.binaryPath, |
| 159 | resume: resumeState?.threadId, |
| 160 | resumeSessionAt: resumeState?.sessionAt, |
| 156 | 161 | signal: abortController.signal, |
| 157 | 162 | includePartialMessages: true, |
| 158 | | canUseTool: makeCanUseTool(input.sessionId), |
| 159 | | hooks: makeClaudeHooks(input.sessionId), |
| 163 | canUseTool: makeCanUseTool(), |
| 164 | hooks: makeClaudeHooks(), |
| 160 | 165 | }, |
| 161 | 166 | }); |
| 162 | 167 | return { abortController, result }; |
| ... | ... | @@ -345,7 +350,8 @@ Define explicit adapter semantics: |
| 345 | 350 | |
| 346 | 351 | 1. `sessionId`: adapter-owned stable session id. |
| 347 | 352 | 2. `threadId`: Claude conversation/session identifier returned as `ProviderThreadId`. |
| 348 | | 3. `resumeCursor`: provider-specific cursor (for example message id) needed for precise recovery/rollback. |
| 353 | 3. `resumeCursor`: provider-specific cursor (for example thread id + message cursor) needed for precise recovery/rollback. |
| 354 | 4. Orchestration/shared services persist and forward `resumeCursor` unchanged without provider-specific parsing. |
| 349 | 355 | |
| 350 | 356 | ### 2.5 Rollback/read strategy |
| 351 | 357 | |
| ... | ... | @@ -402,11 +408,12 @@ Update integration tests to ensure: |
| 402 | 408 | |
| 403 | 409 | ## Phase 4: Orchestration command/reactor updates |
| 404 | 410 | |
| 405 | | ### 4.1 Decider propagation |
| 411 | ### 4.1 Decider propagation (completed) |
| 406 | 412 | |
| 407 | 413 | Update `apps/server/src/orchestration/decider.ts`: |
| 408 | 414 | |
| 409 | 415 | 1. Carry optional `provider` from `thread.turn.start` command into `thread.turn-start-requested` event payload. |
| 416 | 2. Keep this behavior provider-agnostic (no provider-specific runtime fields in the event payload). |
| 410 | 417 | |
| 411 | 418 | ### 4.2 ProviderCommandReactor provider selection |
| 412 | 419 | |
| ... | ... | @@ -415,6 +422,7 @@ Update `apps/server/src/orchestration/Layers/ProviderCommandReactor.ts`: |
| 415 | 422 | 1. Prefer provider from turn-start event payload when starting a new session. |
| 416 | 423 | 2. Fallback to existing thread session provider when payload omitted. |
| 417 | 424 | 3. Fallback to default provider only when neither is present. |
| 425 | 4. On restart/rebind, forward the runtime session's persisted `resumeCursor` as-is (no reconstruction from `providerThreadId`). |
| 418 | 426 | |
| 419 | 427 | Switch behavior policy (explicit in implementation): |
| 420 | 428 | |
| ... | ... | @@ -516,31 +524,26 @@ Confirm both native and canonical provider logs remain useful with multi-adapter |
| 516 | 524 | |
| 517 | 525 | ## File checklist |
| 518 | 526 | |
| 519 | | Likely files to touch: |
| 520 | | |
| 521 | | 1. `packages/contracts/src/model.ts` |
| 522 | | 2. `packages/contracts/src/orchestration.ts` |
| 523 | | 3. `packages/contracts/src/provider.ts` |
| 524 | | 4. `apps/server/src/provider/Services/ClaudeCodeAdapter.ts` (new) |
| 525 | | 5. `apps/server/src/provider/Layers/ClaudeCodeAdapter.ts` (new) |
| 526 | | 6. `apps/server/src/provider/Layers/ProviderAdapterRegistry.ts` |
| 527 | | 7. `apps/server/src/serverLayers.ts` |
| 528 | | 8. `apps/server/src/orchestration/decider.ts` |
| 529 | | 9. `apps/server/src/orchestration/Layers/ProviderCommandReactor.ts` |
| 530 | | 10. `apps/web/src/session-logic.ts` |
| 531 | | 11. `apps/web/src/components/ChatView.tsx` |
| 532 | | 12. Related tests under `packages/contracts/src`, `apps/server/src/provider/Layers`, `apps/server/src/orchestration/Layers`, `apps/server/integration`, and `apps/web/src`. |
| 527 | Likely remaining files to touch: |
| 528 | |
| 529 | 1. `apps/server/src/provider/Services/ClaudeCodeAdapter.ts` (new) |
| 530 | 2. `apps/server/src/provider/Layers/ClaudeCodeAdapter.ts` (new) |
| 531 | 3. `apps/server/src/provider/Layers/ProviderAdapterRegistry.ts` |
| 532 | 4. `apps/server/src/serverLayers.ts` |
| 533 | 5. `apps/server/src/orchestration/Layers/ProviderCommandReactor.ts` |
| 534 | 6. `apps/web/src/session-logic.ts` |
| 535 | 7. `apps/web/src/components/ChatView.tsx` |
| 536 | 8. Related tests under `apps/server/src/provider/Layers`, `apps/server/src/orchestration/Layers`, `apps/server/integration`, and `apps/web/src`. |
| 533 | 537 | |
| 534 | 538 | --- |
| 535 | 539 | |
| 536 | 540 | ## Delivery order |
| 537 | 541 | |
| 538 | | 1. Contracts for provider selection + models. |
| 539 | | 2. Claude adapter + unit tests. |
| 540 | | 3. Registry/layer wiring. |
| 541 | | 4. Reactor updates for provider-aware session start. |
| 542 | | 5. Web provider picker + provider-aware models. |
| 543 | | 6. Checkpoint/revert compatibility. |
| 544 | | 7. End-to-end integration tests and stabilization. |
| 542 | 1. Claude adapter + unit tests on top of the new `providerOptions`/opaque-cursor contracts. |
| 543 | 2. Registry/layer wiring. |
| 544 | 3. Remaining reactor updates for provider-aware session selection/switching invariants. |
| 545 | 4. Web provider picker + provider-aware models. |
| 546 | 5. Checkpoint/revert compatibility. |
| 547 | 6. End-to-end integration tests and stabilization. |
| 545 | 548 | |
| 546 | 549 | This order keeps risk isolated and maintains a working orchestrated path at each stage. |