Repository navigation
feat(server): hand a thread off to a linked environment - #16751
juliusmarminge wants to merge 1 commit into
Conversation
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: unavailable · PR result: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
ac02683 to
2319036
Compare
5c253ba to
b626bf2
Compare
A thread can now move to a linked environment with its conversation and its git work, with exactly one copy live. ThreadHandoff marks it departing, which refuses new turns and keeps queued runs from starting; packs the branch and working tree with HandoffGit; uploads the bundle through the other side's signed attachment route; and calls t3_thread_import there, which now takes the bundle and applies it in a new worktree before creating the thread. Only then is the thread departed here and read-only. A failed move marks it failed, which takes turns again, and nothing is left there. An agent can move its own thread with t3_thread_handoff. The move then waits as pending until its turn ends, starts the thread there with the agent's continuationPrompt, and is cancelled by a user message sent before the turn ends. A startup sweep runs moves that a restart cut short; import ids derive from the handoff, so a retry finds the thread already there. threadHandoff.options / start / cancel serve the clients. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
b626bf2 to
29af53f
Compare
End-to-end run, two real serversTwo
Three bugs found and fixed in this PR:
CI also flagged chained Opus 5.5 via Claude Code. |
Part of cross-environment orchestration. A thread can now move to a linked environment with its conversation (#16731) and its git work (#16734), with exactly one copy live.
How a move goes
ThreadHandoffmarks the threaddepartingwith the internalthread.handoff.updatecommand. The orchestrator refusesmessage.dispatchfor it, andstartNextQueuedRunstarts nothing on it.HandoffGit.t3_attachment_prepare_uploadplus a POST.t3_thread_importthere. That tool now takes the bundle, applies it in a new worktree, and only then creates the thread with the history and a continuation prompt. If the import itself fails, the worktree is removed.departedand read-only, with the other thread's id. A send is refused withOrchestratorThreadMovedError("This thread moved to Box. Continue it there.").failedwith the reason. That state takes turns again, and nothing is left on the other side.The agent moving its own thread ("I need to wrap here, move this to my VPS")
t3_thread_handoffwith nothreadIdmoves the calling thread. The link and the target project are checked right away, so a bad target fails while the agent can still say so.pendinguntil the agent's turn ends. Then the thread there starts with the agent'scontinuationPrompt, so it picks up where it said it would.Surfaces
t3_thread_handoff.threadHandoff.options(where the thread can go, with reasons when it can't),threadHandoff.startandthreadHandoff.cancel. The UI comes in the next PR.docs/user/remote-access.md, and the one-live-copy invariant indocs/internals/remote.md.Verification
peer/handoff/ThreadHandoff.test.tsruns two real orchestrators. The box sits behind its real/mcp, OAuth and upload route; the laptop is linked to it. Each has a real clone of the same origin. Three tests:optionslists the box with its matching project. On the box, the thread has the history, its first provider turn carries the laptop's question plus the continuation, and its worktree is onfix/loginwith the commit, the edit and the file. On the laptop, the thread isdepartedwith the box's thread id, and a new message is refused with "moved to Box".failedand takes a new turn. No thread exists on the box.pending. An agent message steered into that same turn cancels it once the turn ends. Asked again with nothing after, a settle while the turn still runs leaves itpending; when the turn ends it departs, and the box's first turn is the agent's continuation prompt.mcp,peerandcli, plusRpcAuthorization,ThreadImportService,ThreadManagementService,ProjectionStore,Orchestrator, the V1 cutover integration and sharedt3McpToolPresentation(50 files, 485 tests), and contractsrpc,orchestrationV2andorchestratorMcp(45 tests). All passed. Server, contracts, client-runtime, web and mobile typecheck clean, and lint is clean on the changed lines.Opus 5.5 via Claude Code.
🤖 Generated with Claude Code