Skip to content

Open chats move to the account you switch to, before their next message - #30955

Open
brennanb2025 wants to merge 5 commits into
mainfrom
brennanb2025/chat-account-switch
Open

brennanb2025 wants to merge 5 commits into
mainfrom
brennanb2025/chat-account-switch

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 3 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​148 $\color{#cf222e}{\Huge{\mathbf{−}}}$​3 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​145
Prod 24 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​180 $\color{#cf222e}{\Huge{\mathbf{−}}}$​39 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​141

ELI5

When you switch Claude to another account, Orca already offers to restart your Claude terminals on the new account. Your open chats were left out: they kept answering on the old account until something happened to restart them. Now each open chat moves to the account you switched to the next time you send it a message. It continues the same conversation, and a chat that's in the middle of a reply finishes that reply first.

What Changed

The problem. After a switch in Settings > Accounts or the status bar:

  • An idle Claude chat kept running on the old account. You saw it only through which account's usage went up, or through the old account's limits.
  • A chat in the middle of a reply kept the old account even after the reply finished.
  • The restart offer from feat(accounts): offer to restart running Claude sessions on the new account after a switch #29898 lists terminal panes only, so chats never appeared in it. It also never ran for a switch made from the CLI, from a paired client, or on a remote server.

Why. An agent reads its account only when it starts. A chat's agent process is owned by the chat's host (this computer, or the server it runs on). Nothing on that host ever compared the account a running agent started on with the account a new start would use now. The restart offer runs in the window and walks its terminal panes, so the host-owned chats were invisible to it.

What you see now.

  • The next message you send to an open Claude chat after a switch starts its agent on the new account and continues the same conversation.
  • A chat in the middle of a reply, or one with an answer still pending (a question to you, a helper agent still running), finishes on the old account. Its next message moves.
  • This works wherever the switch comes from (Settings, the status bar, the CLI, a paired phone or computer) and wherever the chat runs, including on a server you're paired with, because the chat's own host makes the comparison.
  • Nothing new appears on screen: the chat just continues.
  • Terminals are unchanged: they keep their restart offer, because restarting a terminal interrupts it.

Mechanism.

  • When an agent process starts, its adapter now reports the account folder it actually launched under. Claude's launch already resolved it. The host keeps it on the running agent, in memory only, beside the permission mode it already keeps there.
  • The existing "replace an outgrown agent before the next message" check gains one reason. It compares that account with what a start of this chat would use now (the same resolver a new chat is created with), and replaces the agent only when it's idle, under the same rules that already protect a reply in progress.
  • If the account can't be read at that moment (for example, a Claude account folder still being set up), nothing is replaced.
  • An agent declares whether its chats follow the selected account at all. Claude does when Orca's account routing is installed; without it, a Claude launch keeps the chat's saved account, so it is never compared.
  • Model list (tracked in STA-11281). A chat's model list was read and saved under the account the chat was created on. A chat that had moved could therefore save its new account's models under its first account. A chat's model-list reads and saves now use the account it runs on: the running agent's account, or, for a chat at rest, the account its next start will use. Keep model choices coherent across Claude updates #30107 rewrites these files and will rebase over this.

What doesn't change, and why.

  • Codex chats still run on the account they were created on, for now. A Codex conversation is resumed through Codex's own per-account database, so moving it needs that history to be shared across accounts. The Codex history work (STA-11187) is doing that, and a follow-up PR makes Codex chats follow once it lands.
  • OpenCode chats keep the account they started on, pending a product decision. OpenCode keeps its login and its conversations in the same database, so a conversation can't simply move to another account.
  • Grok, OMP and Pi have no account switcher in Orca.
  • Antigravity chats use their own sign-in, and signing in already stops them.

Why

The common pattern is to compare the settings a running agent was actually built with against the ones a start would use now, and rebuild it only before the next turn, never during one. Orca already had exactly that check for sign-in and permission-mode changes. It was missing the account. Adding the account to that check:

  • puts the decision on the host that owns the chat;
  • covers every way a switch can happen, with no new message between the window and the host, so older servers and newer clients are unaffected;
  • is one reason in one check, not a second mechanism.

Considered and rejected: extending the window's terminal scan to chats and sending each chat a restart request. That needs a new message type that older servers don't know. It only works for switches made in a window on this computer and for chats open in a tab. And it keeps the decision away from the host that owns the process.

Linked Issue

Tracked in STA-11211 (and STA-11281 for the model list).

Visual Proof

Real-agent hidden-window QA on M4Air passed on d95126414d5b against main e01a5392d42d: idle and previously busy Claude chats follow A→B→A with context retained, the busy reply completes, the terminal restart offer and model picker work, and Codex remains on its original account. A custom-folder chat gives the plain switch-back refusal on an account without its history and resumes the same conversation after returning to System default. Narrow real-agent verification also passed on merged head a9d0cba264ff: idle A→B→A with context, uninterrupted busy completion followed by B, custom-folder refusal and same-chat recovery, picker, terminal restart offer and pinned Codex.

Main: next message stays on A after selecting B Branch: next message uses B with APPLE remembered
Main keeps the old account Branch follows the selected account
Custom-folder history unavailable on B Same chat recovered on System default
Plain switch-back refusal Conversation resumes with context

QA used real Claude and Codex through the load balancers, with fabricated account identities in isolated test homes. Both builds completed the same 120-second busy task. The first idle-chat model differed between builds; both busy chats used the same model, and an additional explicit-model control reproduced main staying on A. The custom-folder setup needed a harmless initial environment seed because the Environment editor is hidden until a value exists; the actual path and all account switches were then entered through the UI. Shared approval reprompts were observed on both builds. SSH, WSL, Windows, mixed hosts, folder workspaces and accepting the terminal restart action were not exercised by this macOS run.

Final-head verification

On a9d0cba264ffd639854cc4876992459da5c48fd3, real-agent QA passed every required continuation and recovery path on the admitted M4Air after rebuilding that commit. The earlier main comparison remains the baseline; the final-head run repeated the normal and custom-folder main paths. The 45-second busy shell task finished on A without interruption, and its next turn used B. Full PR CI and the separate WSL workflow passed on this head.

Final head: selected B, context retained Final head: switched back to A
Next chat turn uses B Same chat returns to A
Final head: custom-folder history refusal Final head: same chat recovers
Plain switch-back message CHERRY and original folder recovered

Readiness remains blocked on the pending chat-versus-terminal policy and OpenCode scope decisions. QA also confirmed the existing account-switch toast incorrectly says all running sessions keep their account; its wording and the terminal-only setting title await the same policy decision. No review completion marker has been posted.

Testing

  • Host flow: after a switch, the next message goes to a new agent on the new account, and later messages stay on it.

  • The outgrown check: it replaces an idle agent on another account and keeps it in these cases:

    • mid-reply, with open work, or while starting;
    • when the account can't be read;
    • when the agent keeps its saved account.
  • Claude contract: at each route (managed account, System default), the account the check reads equals the folder the launch uses. Without account routing, Claude chats are not compared.

  • Model list: a moved chat saves and reads under the account it runs on, and the account it was created on stays untouched.

  • Neighbouring host, Claude adapter and model-list suites pass.

  • Custom home: four routing tests pass. A source and filesystem reproduction verifies the initial custom-home resume, the plain refusal on the managed account, and resuming the same conversation after switching back, with the transcript unchanged. Real Electron QA also confirmed the plain refusal and same-conversation recovery. The byte-unchanged transcript result applies only to the source/filesystem reproduction; the real CLI writes its transcript during normal turns.

  • Real Electron QA on M4Air: all requested normal and custom-folder paths passed on d951264 against main e01a539; 51 full-window screenshots uploaded. Narrow final-head continuation QA passed on a9d0cba; 22 fresh full-window screenshots uploaded. The exact-head PR workflow aggregate and separate WSL workflow also passed.

  • I manually tested these changes locally (real-agent QA on the admitted M4Air)

  • Automated tests added/updated, or explained why not below

Review

Agent skill upstream boundary

  • Not applicable, or this change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

  • No new saved field and no wire change. The account a running agent launched under is held in memory only. A chat's saved account still records the account it was created on, which the create-replay identity check relies on.
  • Mixed versions. Old clients are unaffected because only the host decides. An old server keeps today's behaviour.
  • SSH. A remote server with account services compares against its own account selection. A server running inside WSL has no account services, so its Claude chats stay on the account they were created on.
  • History visibility. If a selected account cannot see a known transcript, the existing launch refusal says “This chat's history is in another Claude account. Switch back to that account to continue it.” Before this change it appeared on the first start after an idle sweep or app restart; it now appears at the next message (intended). Normal provisioned accounts share conversation history. A chat started with a custom per-agent CLAUDE_CONFIG_DIR outside the shared history folders now gets the same refusal when it switches to an account that cannot see its transcript. Switching back to System default resumes the original conversation. Sharing history with those custom folders is temporary follow-up work tracked in STA-11300; this PR does not change users' folder links.
  • Differences from the common pattern:
    • No note in the chat when its agent moves (intended: the user just chose the account, and nothing is interrupted).
    • A message queued during a reply joins that reply on the old account (intended: it belongs to the running turn).
    • A chat held by an open orchestration task moves only after the task ends (intended: a worker is never swapped mid-task).
    • Codex chats don't move yet (temporary, removed by the follow-up after STA-11187).
    • Claude's slash-command list for a chat at rest is still read from its saved account (temporary, pre-existing, tracked in STA-11298).

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

@brennanb2025
brennanb2025 marked this pull request as ready for review October 20, 2026 01:47
@brennanb2025
brennanb2025 marked this pull request as draft October 20, 2026 01:50
@brennanb2025
brennanb2025 marked this pull request as ready for review October 20, 2026 01:50
@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 25ca82dd-b5e6-40b1-9b1e-25c8f654cabd
📥 Commits

Reviewing files that changed from the base of the PR and between 1f8beb9 and a9d0cba.

📒 Files selected for processing: 4
  • src/main/claude/claude-structured-launch-account-routing.test.ts
  • src/main/claude/claude-structured-launch-home.ts
  • src/main/claude/claude-structured-launch-resolution.ts
  • src/main/runtime/workspaces/workspace-process-listing-dependencies.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.

📝 Walkthrough

The change tracks account-home metadata from Claude session acquisition through provider-child attachment. Runtime account selection can resolve the account home for a launch, and child retirement compares it with the account used to launch the child. Model catalog operations use the live account when available and otherwise use the next-launch account for reads.

Priority: ➖ Normal

Merge Risk: 🟡 Moderate · up to a9d0c

Account switches may still cause model catalogs or account probes to use the previous account.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 65.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 20 functions across 27 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the primary change: open chats move to the selected account before the next message.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The description is complete and relevant. It covers the user impact, implementation, rationale, linked issue identifiers, visual proof, testing, scope limits, compatibility considerations, and checkli…
Full details: Description check

Explanation

The description is complete and relevant. It covers the user impact, implementation, rationale, linked issue identifiers, visual proof, testing, scope limits, compatibility considerations, and checklist items. The issue references are plain identifiers rather than explicit links, and the AI Disclosure heading is omitted, but these do not prevent review.

  • Fix all pre-merge checks with AI
✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: eba12076-fbbc-4648-b565-ce58e12f4bd0
📥 Commits

Reviewing files that changed from the base of the PR and between 907694d and 8ef8f44.

📒 Files selected for processing: 24
  • src/main/claude/claude-structured-launch-account-routing.test.ts
  • src/main/claude/claude-structured-session-acquisition.ts
  • src/main/claude/claude-structured-session-publication.ts
  • src/main/native-chat/agent-model-catalog/agent-model-catalog-fingerprint.ts
  • src/main/native-chat/agent-model-catalog/agent-model-catalog-prewarm.test.ts
  • src/main/native-chat/agent-model-catalog/agent-model-catalog-running-account.ts
  • src/main/native-chat/agent-model-catalog/agent-model-catalog-service.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-acquisition.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-adapter.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-attach-flow.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-attach-orchestration.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-attach-publish.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-host-delivery.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-host-types.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-host.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-outgrown-child.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-outgrown-child.ts
  • src/main/runtime/session-tabs/structured-session-account-home.ts
  • src/main/runtime/structured-agent-model-catalog-wiring.ts
  • src/main/runtime/structured-agent-runtime-metadata.ts
  • src/main/runtime/structured-agent-runtime-registration-types.ts
  • src/main/runtime/structured-agent-session-runtime.ts
  • src/main/runtime/workspaces/workspace-process-listing.ts
  • src/shared/agent-session-account-home.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 0 remain after this review.

Comment on lines +31 to +32
const accountHome = await deps.launchAccountHome?.(record).catch(() => null)
return accountHome ? { ...record, accountHome } : record

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Do not use the saved account after a failed launch-account read.

If launchAccountHome rejects after an account switch, this catch selects record.accountHome. AgentModelCatalogService.read can then return the old account’s saved models or start a probe under that account. Keep null as the intentional “use the saved account” result, but distinguish a failed lookup and return an unknown catalog result instead.

@brennanb2025
brennanb2025 marked this pull request as draft October 20, 2026 01:53
@brennanb2025
brennanb2025 marked this pull request as ready for review October 20, 2026 01:53
…ore its next message

A running chat's agent read its account only when it started, and nothing compared it with the
account a start would use now, so open chats kept answering on the old account after a switch.

The agent a chat runs now records the account its launch used, and the host's replace-before-the-
next-send check replaces an idle one whose account is no longer what a start would use. A chat
mid-reply finishes on the old account first. Claude chats follow; agents whose chats keep the
account they were created on are never compared.

A chat's model-list reads and saves now use the account it runs on, so a moved chat no longer
writes its new account's list under its first account (STA-11281).

Tracked in STA-11211.
@brennanb2025
brennanb2025 force-pushed the brennanb2025/chat-account-switch branch from 8ef8f44 to 1f8beb9 Compare October 20, 2026 01:57

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant