feat: mid-session provider switching with provider-owned history seeding - #133
Conversation
session.set_provider + /provider <id>: swap a live session's backend (claude ⇄ pi ⇄ gemini ⇄ openai) keeping the session id, scrollback, transcript, and identity. - Generic switch loop in Session.switchProvider: fail-closed registry resolve, rejected mid-turn (pending approvals/dialogs included), serialized on the send chain against racing prompts, teardown → fresh backing id (incoming backend never resumes the outgoing one's native state) → model reset to the new default → rebuild via the registry - Provider-owned fidelity: new optional seedFromHistory(history) on the provider interface. Stateless backends no-op (TurnOpts.history is native for them); ClaudeProvider and PiProvider prepend a rendered transcript (canonical renderHistorySeed, newest-turns-kept truncation) to their first post-switch prompt. Best-effort by contract: a seed failure degrades to an unseeded switch, never a wedged session - Every switch audited + announced in the transcript with structured metadata (from/to/seeded); transcript meta persists the new providerId for restart resume - Session retains provider-construction inputs (registry, fleet, compression, onModels) so #createProvider is callable post-constructor
|
Warning Review limit reached
Next review available in: 45 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughAdds a ChangesMid-session provider switching
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/daemon/session-manager.ts`:
- Around line 2036-2048: Update the inline trust/scope comment in `#setProvider`
so it no longer claims parity with `#setModel`, since `#setModel` uses
SCOPES.SESSION_SEND while `#setMode` uses SCOPES.SESSION_APPROVE; adjust the
wording to reflect that only `#setMode` matches the current session-config write
scope, keeping the note accurate for future changes.
In `@src/daemon/session.ts`:
- Around line 719-728: Reset the rotation state when the provider changes in
session switching logic so a stale `#justRotated` flag cannot affect the next
send(). Update the provider-switch path in Session to clear `#justRotated`
alongside `#model` and `#fallbackModel`, and make sure the send()/#buildRotationSeed
flow only seeds once per provider lifecycle so it does not prepend the old
Claude-specific rotation message after a switch.
- Around line 657-772: The mid-turn protection in switchProvider is only checked
before the request is serialized, so a queued send() can start a turn before
`#switchProviderInner` runs and still get torn down. Re-check the
active/waiting/pending guard inside `#switchProviderInner` (or immediately before
`#teardownProvider`) using the same `#status`, `#pendingApprovals`, and
`#pendingUiRequests` conditions, and return the invalid_request error there if a
turn has started.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 8acc4bf8-9989-4678-9112-955185370053
📒 Files selected for processing (18)
CHANGELOG.mddocs/providers-pi.mdpackages/core/src/slash.test.tspackages/core/src/slash.tspackages/protocol/src/schemas.test.tspackages/protocol/src/schemas.tspackages/protocol/src/types.tssrc/daemon/providers/canonical.tssrc/daemon/providers/claude/index.tssrc/daemon/providers/interface.tssrc/daemon/providers/mock/session-provider.tssrc/daemon/providers/pi/index.tssrc/daemon/session-manager.tssrc/daemon/session.tssrc/tests/fixtures/fake-pi.tssrc/tests/pi-translate.test.tssrc/tests/provider-pi.test.tssrc/tests/session-provider-switch.test.ts
…on-seed clear, scope comment - Re-run the busy guard INSIDE the chain-serialized switch: a send() queued ahead starts its turn and returns mid-stream, so the pre-check alone could tear down an actively-running provider. New S6 test reproduces the race deterministically (stalling provider + racing switch) and proves the rejection - Clear the pending rotation seed on switch: a post-rotation switch would otherwise stack the Claude-worded rotation anchor on top of the new provider's own transcript seed (S7 proves the prompt is clean) - Correct the set_provider scope comment: set_model gates on SESSION_SEND, not SESSION_APPROVE — only set_mode is the true peer
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #133 +/- ##
==========================================
+ Coverage 80.60% 81.54% +0.94%
==========================================
Files 94 94
Lines 16215 16376 +161
==========================================
+ Hits 13070 13354 +284
+ Misses 3145 3022 -123
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
What
The "clean design" for harness switching: a generic switch loop in the daemon plus provider-owned seeding fidelity —
/provider pimid-conversation,/provider claudeto come back, same session id / scrollback / transcript / identity throughout.The generic loop (
Session.switchProvider)invalid_requestnaming the available backends; same-id → ack no-op)provider.switched, from/to/seeded); transcript meta persists the new providerId so restarts resume on the right backendProvider-owned fidelity (
seedFromHistory?)New optional on the provider interface, called once on the incoming backend with the session's canonical history:
TurnOpts.historynatively every turn, so switching to them is inherently correctrenderHistorySeed: turn-by-turn with flattened tool calls, oldest turns elided past ~24k chars with a note) to their first post-switch prompt — the same mechanism rotation's task anchor usesseeded: falsein the announcement — never a wedged sessionThe fidelity contract is stated where users see it (protocol docs,
docs/providers-pi.md, the transcript message): the incoming backend receives a faithful transcript, not a native continuation —tool_usestructures, prompt-cache state, and extension state don't cross.Exposure
session.set_providerverb (session-config trust class, same asset_model) and/provider <id>in the shared slash layer (web gets it viadispatchSlash; TUI pickers ride the existingauth.ok.providersadvertisement as follow-up).Tests
renderHistorySeedunits: envelope/tool rendering, empty history, oldest-first truncation with elision note/providerparse + dispatch; schema fidelity (compile-time exhaustiveness held)🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
/provider <id>command to switch the active backend during a live session.Bug Fixes