Skip to content

Claude unified terminal: in-session agent switch starts Claude with no carry-over context #332

Closed

Description

@karolzlot

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

  1. In provider settings for Claude, enable "Use unified terminal runtime".
  2. Run a session with a different agent (Codex in this report) until it has a transcript worth carrying over.
  3. Switch that session to Claude with an in-session agent switch, and send a short message such as continue along with it.
  4. 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 with backendTarget: {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 replaySeedV1 of 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 UserPromptSubmit hook records the user's text alone, and the runner logs unified.injection.outcome with inputByteLength equal to the length of that text.
  • The seed is still live after the turn. replaySeedV1.seedText is unchanged and appliedToLocalId is 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:81 states the contract: a fresh target starts from the brief in metadata.replaySeedV1, which the seed owner prefixes onto the first provider-accepted prompt.
  • apps/cli/src/agent/runtime/prompt/resolveProviderPromptForDispatch.ts:25 names 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:200 hands the session to claudeUnifiedTerminalLauncher and returns, so claudeRemoteLauncher at line 258, which resolves the seed at apps/cli/src/backends/claude/claudeRemoteLauncher.ts:1678, is never reached.
  • apps/cli/src/backends/claude/unifiedTerminal/claudeUnifiedTerminalLauncher.ts:1017 is that launcher's own nextMessage, and it returns batch.message verbatim at line 1073. The whole apps/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

  1. happier-bot commented on Aug 30, 2026

    @happier-bot
    Contributor

    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:

    Unified-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

  2. added
    needs:reporterWaiting for new information or confirmation from an external participant.
    and removed
    needs:maintainerProject review or action is required.
    on Aug 30, 2026
  3. happier-bot commented on Oct 11, 2026

    @happier-bot
    Contributor

    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.

  4. removed
    needs:reporterWaiting for new information or confirmation from an external participant.
    on Oct 11, 2026
  5. RogueKostas commented on Oct 20, 2026

    @RogueKostas
    Contributor

    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.

    1. Create a disposable test conversation with Codex and an empty test project.
    2. Teach it two invented facts: the test colour is violet and the test number is 73. Add a few harmless messages that refer to those facts.
    3. Switch that conversation to Claude using the Agent SDK runtime.
    4. In a controlled test harness, force the first target-provider launch to fail before it can accept any input. Do not change real credentials or existing user configuration.
    5. Remove the injected failure and restart only the test conversation's target runner.
    6. Ask Claude to identify the invented test colour and number.
    7. Compare its answer with a control run in which the initial target launch succeeds.
    8. Record only whether context was available, whether the handoff was marked consumed, and the ordering of consumption versus provider acceptance. Use newly generated identifiers and omit all conversation contents from diagnostic output.

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions