Problem
Switching or forking a session onto a warm backend (Claude, pi, Codex, Gemini CLI) seeds the incoming backend's first prompt with a rendered text transcript of the conversation (seedFromHistory → renderHistorySeed). Two costs:
- Front-loads the context window. The seed is budgeted at ~70% of the target model's window (
seedBudgetChars = window × 0.7 × 3.5, src/daemon/providers/context-windows.ts). For a long session on Codex (256k window) that is up to ~180k tokens consumed before the model does any work.
- Lossy at the tail, with no recovery. When the session exceeds the seed budget, the oldest turns are dropped (newest-first truncation + a user notice). The dropped turns are still in the verbatim memory store, but the recall tools (
recall / recall_file / timeline) are wired only into the Claude provider (src/daemon/providers/claude/index.ts ~L326). A switched-in Codex/pi/OpenAI/Gemini cannot pull them back.
Net: the cross-backend switch is the lowest-fidelity path in the meta-harness. The per-turn path (canonical history → each backend's native messages + tool calls, never summarized) is unaffected and stays lossless — this is only about the handoff.
Proposed design
Flip the handoff from "push the whole transcript" to "seed a compact map, pull detail on demand", uniformly across all backends.
1. Expose the recall tools on every backend, not just Claude.
recall(query), recall_file(path), timeline(limit), plus a new exact-retrieval get_turn(id) / get_turns(ids). Exposure per backend:
- Codex — native MCP via the app-server (it already surfaces
mcpToolCall).
- pi — the extension/tool bridge (same path the approval bridge already uses).
- OpenAI / Gemini — as function/tool declarations added to each
runTurn() request.
- Claude — already done (in-process MCP); keep as the reference implementation.
All read the same workspace-scoped episode store, so recall returns the real bytes regardless of which backend asks.
2. Seed a compact session map, not the full transcript.
On switch/fork, the incoming backend's first prompt carries a short anchor:
- the last user turn, verbatim;
- a one-line-per-turn index of the session (turn id, role, provider/model, a short label, files touched);
- the workspace memory index.
The model starts with a near-empty context and a directory of what happened, and calls recall / get_turn to pull any message in full when it needs it.
3. Retrieval by id.
The turn index gives each turn a stable id; get_turn(id) returns the exact canonical turn (content + structured tool calls + results). "Jump to any message in history" becomes a single tool call, and it behaves as if that turn were native to this model's session.
Result: cross-backend switching is context-light (near-empty start) and lossless on demand (any turn retrievable) — the ~180k-token front-load and the tail-truncation both disappear.
Prior art in the codebase (reuse, don't reinvent)
MemoryEngine + the verbatim episode store already hold the full history; the Claude memory MCP already exposes recall / recall_file / timeline.
- The rotation seed strategy B: task-anchor (Layer D,
/rotate + auto-rotation) already does exactly this shape within Claude: capture the last user turn, inject a compact anchor, let the model recall prior detail. This issue generalizes that from "Claude rotation" to "any cross-backend switch on any backend."
- The workspace memory index (Layer C) is already the compact per-turn map to reuse for the anchor.
Conceptually: a cross-backend switch should behave like a /rotate or a compaction — the new backend inherits a compact anchor plus the ability to recall everything, not a wall of transcript.
Scope / acceptance
Why now
This is the highest-leverage follow-up to the v0.3.0 multi-provider release: it closes the one real fidelity gap in the cross-backend story and makes the "any harness, full history, never summarized" claim hold on every backend, not just Claude.
Problem
Switching or forking a session onto a warm backend (Claude, pi, Codex, Gemini CLI) seeds the incoming backend's first prompt with a rendered text transcript of the conversation (
seedFromHistory→renderHistorySeed). Two costs:seedBudgetChars = window × 0.7 × 3.5,src/daemon/providers/context-windows.ts). For a long session on Codex (256k window) that is up to ~180k tokens consumed before the model does any work.recall/recall_file/timeline) are wired only into the Claude provider (src/daemon/providers/claude/index.ts~L326). A switched-in Codex/pi/OpenAI/Gemini cannot pull them back.Net: the cross-backend switch is the lowest-fidelity path in the meta-harness. The per-turn path (canonical history → each backend's native messages + tool calls, never summarized) is unaffected and stays lossless — this is only about the handoff.
Proposed design
Flip the handoff from "push the whole transcript" to "seed a compact map, pull detail on demand", uniformly across all backends.
1. Expose the recall tools on every backend, not just Claude.
recall(query),recall_file(path),timeline(limit), plus a new exact-retrievalget_turn(id)/get_turns(ids). Exposure per backend:mcpToolCall).runTurn()request.All read the same workspace-scoped episode store, so recall returns the real bytes regardless of which backend asks.
2. Seed a compact session map, not the full transcript.
On switch/fork, the incoming backend's first prompt carries a short anchor:
The model starts with a near-empty context and a directory of what happened, and calls
recall/get_turnto pull any message in full when it needs it.3. Retrieval by id.
The turn index gives each turn a stable id;
get_turn(id)returns the exact canonical turn (content + structured tool calls + results). "Jump to any message in history" becomes a single tool call, and it behaves as if that turn were native to this model's session.Result: cross-backend switching is context-light (near-empty start) and lossless on demand (any turn retrievable) — the ~180k-token front-load and the tail-truncation both disappear.
Prior art in the codebase (reuse, don't reinvent)
MemoryEngine+ the verbatim episode store already hold the full history; the Claude memory MCP already exposesrecall/recall_file/timeline./rotate+ auto-rotation) already does exactly this shape within Claude: capture the last user turn, inject a compact anchor, let the modelrecallprior detail. This issue generalizes that from "Claude rotation" to "any cross-backend switch on any backend."Conceptually: a cross-backend switch should behave like a
/rotateor a compaction — the new backend inherits a compact anchor plus the ability to recall everything, not a wall of transcript.Scope / acceptance
recall/recall_file/timeline+ a newget_turn(id)available on Codex, pi, OpenAI, Gemini, Gemini CLI (Claude already has them).get_turnreturns the exact canonical turn (content + structured tool calls + results).renderHistorySeedtranscript.renderHistorySeedtranscript path as the fallback when memory is disabled (CODEOID_MEMORY=0), since there is no episode store to recall from.Why now
This is the highest-leverage follow-up to the v0.3.0 multi-provider release: it closes the one real fidelity gap in the cross-backend story and makes the "any harness, full history, never summarized" claim hold on every backend, not just Claude.