Repository navigation
Claude unified terminal: in-session agent switch starts Claude with no carry-over context #332
Description
Summary
Switching a running session to Claude while the unified terminal runtime is enabled starts the target agent with no carry-over context, so it continues the conversation as if the session had just begun.
What happened (current behavior)
The switch completed and the clients kept showing the full session transcript, but the new Claude runtime received only the plain text of the first message and answered without any knowledge of what the previous agent had done.
Expected behavior
A target agent that never ran the session receives the transition's replay brief together with its first prompt, so the conversation continues with the context the switch promises.
Reproduction steps
- In provider settings for Claude, enable "Use unified terminal runtime".
- Run a session with a different agent (Codex in this report) until it has a transcript worth carrying over.
- Switch that session to Claude with an in-session agent switch, and send a short message such as
continuealong with it. - The Claude runtime starts and the transcript stays visible in the client, but the agent has no context from before the switch: it does not know the topic, the earlier findings, or the files already produced in the session.
Severity
high
Frequency
always
Happier version
dev @ 3c88b60
Platform
Linux x64
Server version
No response
Deployment type
self-hosted
What changed recently?
No response
Diagnostics ID
No response
Additional context
Where the replay brief stops, and a control switch that consumes it
Version and line numbers refer to dev at 3c88b60. The observation came from a build whose affected corridor is byte-identical to that commit, and the same gap is already present at the published 0.2.10-dev.80, so it is not a recent regression. The switch was requested from a local client experiment rather than the official app, but the request is the daemon's session.agentTransition and everything described below happens after it, inside the runner.
Observed on the live system
- The transition itself is healthy. The daemon handles
session.agentTransition, stops the source runner, and spawns the target withbackendTarget: {kind: builtInAgent, agentId: claude}and no vendor resume key, which is correct for a target that never ran this session. - The brief is built and stored. Session metadata gains a complete
replaySeedV1of roughly 10 KB, with the handoff header, the source agent name, and an inlined transcript tail. - The prompt that reaches the provider carries none of it. The
UserPromptSubmithook records the user's text alone, and the runner logsunified.injection.outcomewithinputByteLengthequal to the length of that text. - The seed is still live after the turn.
replaySeedV1.seedTextis unchanged andappliedToLocalIdis absent, so retirement never ran.
Controls
Two switches on the same machine and relay retired the seed within seconds of the transition, blanking seedText and stamping appliedToLocalId. The first had Codex as its target.
The second is the decisive one. It reused this very session and the seed the failed switch had left pending, with the unified terminal runtime disabled, so the target agent, the session, the seed and the transition record were all identical to the failing run and only the runtime differed. There the session resumed on agent-sdk, the first prompt carried the full brief (10031 characters, opening with the seed's own header), and the seed was retired against that message's local id.
The boundary is therefore the runtime alone. The brief builder, the transition and the retirement path all work.
Where it stops, in the code
apps/cli/src/session/agentTransition/sessionAgentTransitionCoordinator.ts:81states the contract: a fresh target starts from the brief inmetadata.replaySeedV1, which the seed owner prefixes onto the first provider-accepted prompt.apps/cli/src/agent/runtime/prompt/resolveProviderPromptForDispatch.ts:25names the four prompt-dispatch owners it encloses: the ACP family, Codex, Claude and Gemini. The unified terminal is a fifth one and is not among them.apps/cli/src/backends/claude/loop.ts:200hands the session toclaudeUnifiedTerminalLauncherand returns, soclaudeRemoteLauncherat line 258, which resolves the seed atapps/cli/src/backends/claude/claudeRemoteLauncher.ts:1678, is never reached.apps/cli/src/backends/claude/unifiedTerminal/claudeUnifiedTerminalLauncher.ts:1017is that launcher's ownnextMessage, and it returnsbatch.messageverbatim at line 1073. The wholeapps/cli/src/backends/claude/unifiedTerminal/tree contains no reference to the replay seed.
The reported frequency is read from that corridor rather than from repeated attempts: this launcher has no branch that could consult the seed, so there is no comparable run that could succeed, and the one observed switch behaved accordingly. Neither control is a passing run of the same configuration: one changed the target, the other the runtime.
Because the seed is never applied it is also never retired, so it stays pending for the session indefinitely unless that session later runs on a runtime that does consume it.
Suggested direction
The unified terminal launcher's nextMessage is the counterpart of the agent-SDK one and could route through the same existing owner, holding settleReplaySeedOnProviderAcceptance until the terminal injection is confirmed accepted rather than settling it at composition time.
Not tested
Whether this launcher also drops the brief for the other producers of replaySeedV1, such as a replay-seeded session fork whose target lands on this runtime. The prompt path is shared, so the same drop is expected, but this report only exercised the in-session switch.
Activity
fix(cli): carry replay context into Claude unified terminal
fix(cli): retire steer context on exact acceptance
fix(cli): settle replay context on provider acceptance
fix(cli): settle replay context on provider acceptance
happier-bot commentedon Aug 30, 2026
Thanks for tracking this down—the comparison between the unified-terminal and agent-SDK runs isolated the missing prompt owner and directly informed the correction.
The fix is now integrated in both current source lines:
devcorrectionv0.3adaptationUnified-terminal prompts now resolve the existing replay seed before provider dispatch and retire it only from the provider’s exact accepted outcome, correlated by the existing Pending local ID. This keeps Pending as the delivery owner while preventing delayed or out-of-order acceptance from retiring context for the wrong prompt. The same acceptance rule now covers Claude remote, Codex, and permission-mode steering.
Focused acceptance, prompt-loop, and in-flight queue tests pass on both source lines. This is source-only for now and will reach the dev channel on the next dev release. Please retry the original switch after this issue reaches
stage:dev; we’ll keep it open for that availability and confirmation.cc: @leeroybrun
happier-bot commentedon Oct 11, 2026
Thanks for the comparison with the working SDK agent switch. CLI 0.2.16 carries conversation context into Claude when switching agents in a unified-terminal session.
Please reply or reopen with your component versions if this still happens after updating.
Posted on behalf of @leeroybrun.
RogueKostas commentedon Oct 20, 2026
Subject: Investigate handoff retention when a target-provider launch fails
Summary
There may be a context-recovery gap when switching an existing conversation from Codex to Claude through the Agent SDK runtime. If the first target launch fails and the target runner is subsequently restarted, the visible conversation can remain intact while the target provider lacks its earlier working context.
This may be related to the existing agent-switch context issue, but involves a different runtime and a failed-launch recovery sequence. The root cause is not established.
Expected behavior
Conversation context should remain recoverable until the target provider has demonstrably accepted it. A failed startup should not silently consume the only handoff. Recovery should distinguish the visible transcript from the context actually available to the provider.
Proposed synthetic reproduction
This recipe uses only invented test data and has not yet been run as a complete clean-environment reproduction.
Evidence and limits
Local diagnosis found a handoff marked consumed after a failed startup, followed by a responding target without the preceding context. That does not establish when the context was lost, whether consumption occurred before acceptance, or whether restarting contributed. The synthetic comparison above is needed to isolate the boundary.
Provider-native goals are outside this report. No assumption is made that switching providers should migrate or automatically restart those goals.
Scope
Happier stable 0.2.16; Windows; Claude Code 2.1.270; target Agent SDK runtime.
Disclosure
Prepared with OpenAI Codex. Every example and test datum in this recipe is invented. This report contains no copied conversation messages, log excerpts, stack traces, file paths, project identifiers, machine identifiers, session identifiers, private addresses, account identifiers, or credentials.