Skip to content

feat(claude-accounts): move running sessions to the selected account - #29802

Closed
adifsgaid wants to merge 14 commits into
stablyai:mainfrom
adifsgaid:feat/claude-move-sessions-on-account-switch
Closed

adifsgaid wants to merge 14 commits into
stablyai:mainfrom
adifsgaid:feat/claude-move-sessions-on-account-switch

Conversation

@adifsgaid

@adifsgaid adifsgaid commented Oct 16, 2026 •

Copy link
Copy Markdown

ELI5

When you switch Claude accounts, the Claudes that are already running stay on the old one. That's by design: each claude picks its account folder when it starts. Codex has a "Restart N Sessions" prompt for this. Claude had nothing, so you had to quit each session and resume it yourself.

This adds the Claude version. The Claude account menu tells you how many sessions are still on the old account, and Move Sessions reopens each one's conversation on the new account with claude --resume. There's also an opt-in setting that does it on its own for sessions that are sitting idle.

What Changed

Before: after a switch, running Claudes kept the old account until you restarted them by hand.

After:

  • The Claude account menu (Usage › Claude) shows "N Claude sessions are still on the old account" and a Move Sessions button, worded like Codex's prompt.
  • A moved session reopens in the same pane with claude --resume <id>. It picks up the selected account the same way any new claude does.
  • It only moves a session when nothing would be lost: the turn is over, no subagents are running, and nothing is typed but unsent. A session that's mid-turn or waiting on a permission prompt moves once that turn ends.
  • Settings › Accounts › Claude › Move idle sessions to the selected account (off by default) does this without asking, for sessions idle for a minute in a tab you're not looking at.

How it decides, briefly:

  • A session's account comes from where its transcript lives (claude-profiles/<accountId>/home/projects/..., or System default for the user's own folder). That's claude-transcript-account.ts, one function, so it can switch to feat(accounts): keep a Claude or Codex agent on its account #28684's per-agent account once that lands.
  • Accounts are compared by login (email and organization), not by folder. So an account whose login is System default's, which Orca runs from the System default folder, never loops. Two folders holding the same login don't trigger a pointless move either.
  • "Safe to restart" is hibernation's own getEligiblePane. It gains one opt-in, allowEndedTurn, so a turn that ended in a failure or a Stop also counts. A usage limit ends a turn that way, and that's the session you most want to move. Hibernation itself is unchanged.
  • Before restarting, it checks that Claude is what's running in the pane, much like isCodexRestartEligiblePane: Claude by name, or node or python under a tab that launched Claude. A Claude inside tmux, a pane that's back at the shell, or another agent started in that tab is left alone. Like the Codex scan, a shell reading in a tab that launched Claude gets a fresh read first (Codex restart card silently skipped when inspectProcess flaps to a shell foreground #11064), and a read that can't tell is tried again later. Panes that aren't mounted are skipped before any process read.
  • It never restarts blank. If there's no resume command (no session id, WSL), the session stays where it is.
  • Moves go through the existing restart path (handleRestartCodexPane with an explicit startup), and only for mounted panes. They never join pendingCodexPaneRestartIds, because its detached path relaunches unmounted panes as Codex.

Two refactors keep files under the 300-line cap. The Claude account settings fields moved to claude-account-global-settings.ts, the way DeletePrefs did. The Claude settings-search entries moved to accounts-claude-search.ts. Main also reports System default's organization next to its email (systemDefaultOrganizationUuid), so one email in two organizations stays two logins.

Why

#19900 has the background. @Tauri-EPO confirmed on 1.4.227 that running sessions keep the old account by design now, and asked for something like Codex's restart prompt. @joefour is doing this today with a local hook, and listed what a built-in version has to handle. I went through that list:

  • Restarting kills background shells and subagents: a session with subagents waits.
  • The resume has to keep the pane's launch flags: it reuses the launch config Orca recorded for the pane, the same way Codex's restart does. A Claude someone started by hand with extra flags gets the default ones, which I call out below.
  • tmux panes: the process check above.
  • Text in the composer: unsent input holds the move, using hibernation's input guard. Nothing is typed or re-sent for you, so pasted images aren't an issue.

Linked Issue

Fixes #19900

Visual Proof

From the real app (headless e2e launch with the stub agent running as Claude). Before this change the menu ended at "Running sessions keep their account."

Session on the old account After Move Session
Claude account menu showing 1 Claude session still on the old account, with a Move Session button The same menu after Move Session: the pane resumed and the prompt is gone

Testing

  • Automated tests added. Unit tests cover reading the account from a transcript path, deciding by login (System default, an account holding the same login, one email in two organizations, a removed account), when a session may restart (finished, working, waiting on permission, subagents, unsent text, a failed turn, idle time, the tab on screen), the process check (claude, node under a Claude tab, tmux, the shell), and the coordinator (asked moves, busy then done, switching back, the setting, A to B to A to B, a failed or out-of-order account read, retries). For each guard I broke it on purpose and checked that a test went red.
  • e2e: tests/e2e/claude-session-move.spec.ts seeds two accounts with B selected and a Claude session that started on A, opens the menu, clicks Move Session, and checks that the pane relaunches with --resume and drops out of the count. codex-account-switch-dismiss.spec.ts still passes on the shared restart builder.
  • Tested with two real Claude Team accounts in an isolated dev profile. The first one was at its weekly limit, so its turn ended on the limit message. Move Session reopened the same conversation (same session id) on the second account, which answered using the earlier turn, and /status showed the second login. Then, with the setting on and the tab in the background, switching back moved it again after about a minute. Moving onto an account that's out of credits just brings up Claude's own limit prompt.

Ran locally on macOS: the Claude account, hibernation, settings, status-bar, provider-accounts and restart-startup suites, typecheck (node, web, cli), oxlint, oxfmt, the changed-code audit, the max-lines ratchet and the localization checks. One unrelated failure on this machine: runtime-home-service-per-account-migration.test.ts needs node:sqlite, which my local Node doesn't have.

Review

cc @nwparker @brennanb2025. @QichangZheng this is the switch-driven half I mentioned on #28684. If step 1 lands first, the account check becomes one line here, and kept agents would be skipped. Happy to rebase onto it.

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

Ensure no issues in: Security, Cross-platoform support (Linux, Windows, Mac), Remote SSH, Mobile, general backwards compatibility, performance

  • Security: no credential handling. The new process picks up the account the normal way.
  • Cross-platform: transcript paths parse on POSIX and Windows. WSL panes are left alone in this version.
  • Remote/SSH/mobile: only panes on this machine move; remote and SSH panes are skipped, and so are mobile-locked panes.
  • Compatibility: the setting is optional and off. systemDefaultOrganizationUuid is an optional field.
  • Performance: it only does work while a switch has left sessions behind, or while the setting is on. Each check is a scan of the panes already in the store. The account list is read when the selection changes.
  • Known limit: a Claude started by hand with extra flags (--mcp-config, --settings) is resumed with Orca's default flags, because Orca has no record of the ones it was given.
  • Not in here: typing "continue" for a session whose turn died on the limit. Orca maps StopFailure to done and drops the error type, so that needs a main-side hook change, and I'd rather do it on top of feat(accounts): keep a Claude or Codex agent on its account #28684.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • 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)

…ccount

After an account switch, a running Claude kept the account it started on (stablyai#19900). The Claude
account menu now says how many sessions are still on the old account and offers Move Sessions,
the way Codex offers Restart Sessions. Each one reopens its conversation with claude --resume
once it is idle, so nothing it was doing, and nothing typed but not sent, is lost. A new setting,
off by default, does that on its own for sessions idle for a minute in a tab you are not looking
at.

A session's account is read from where its transcript lives, and compared by login, so an account
served by the System default login, or two folders holding one login, never cause a move. Safe to
move is hibernation's own test for a pane it may stop, which this lets take a turn that ended on an
error or a Stop: that is how a usage limit ends one. Moves restart only mounted panes and never
join the Codex restart queue.
…ting moved sessions

The e2e spec seeds two accounts with B selected and a Claude session that started on A, opens the
Claude menu from the usage panel, moves the session, and checks that the pane relaunches with
claude --resume and that the menu no longer counts it. A moved session reads as the old account
until it reports again, so the menu now leaves settled sessions out.
…ll logins apart by organization

- Ask the pane's process before a restart, so a Claude inside tmux, or a pane back at the shell,
  is left alone instead of having its outer terminal restarted.
- Report System default's organization with its email, so one email in two organizations stays
  two logins.
- Hold a moved session only until it reports again, so A to B to A to B moves each time.
- Forget the account list when reading it fails, and try again, instead of acting on the old one.
- Retry a session you asked to move when its pane was not ready, with no store change needed.
- Give the setting its own search entry.
…art prompt does

A Claude installed through npm runs under node, so a name-only check skipped it. A pane now counts
as running Claude when its process is Claude, or when the tab launched Claude and an agent wrapper
runs there; tmux, the shell or another program still means leave it alone. The e2e spec launches
Orca's stub agent as Claude, so it no longer needs a real claude.
…nly the latest account list

- A session whose account was removed is offered a move: no saved login is served from its folder
  any more, so it cannot be on the selected one.
- When account-list replies to a quick A, B, A arrive out of order, only the latest one is kept.
- The menu's hint says unsent text holds a move too.
- The Claude accounts comment moved with its field, and AccountsPane imports the Claude search
  catalog directly.
…d keep retrying

- After the process check comes back, the session is checked again on fresh state, so text typed
  or a turn started meanwhile still holds the move.
- A process check that failed, or could not see the process, is retried instead of giving up.
- Move Sessions pressed while the account list cannot be read is kept, and acted on once it can.
…nd retry when a pane's processes are unreadable

After the process check, the session is re-checked against the newest account list and the
current setting, so switching back or turning moves off while it waits stops the move. An
inspection whose evidence says the process table could not be read is retried, like a failed one.
…nd read Windows panes by child evidence

A switch is what usually asks for a new account list, so the old one is dropped as the read
starts and nothing moves on it meanwhile. A local pane's inspection is judged by its child-process
evidence, as agent completion does: Windows marks the foreground evidence unverifiable on every
local answer, which blocked every move there.
… no longer runs Claude

A process-table read that failed can leave only the foreground evidence marked unverifiable. A
pane that does not read as Claude is now settled only on a verifiable answer, and retried
otherwise; a real Claude still moves, on Windows too.
…ts process read failed

Off Windows, an inspection whose foreground evidence is unverifiable now counts as unseen, so a
cached claude name from a failed read cannot restart a pane Claude already left; the session is
retried instead. Windows marks that evidence unverifiable on every local answer, so there the
child-process evidence decides, as before.
…ude pane

The cached foreground read can flap to the shell for a live agent (stablyai#11064), so a shell in a tab
that launched Claude now gets the fresh scan Codex's restart uses; a scan that cannot tell is
retried. Panes that are not mounted are skipped before any process read, and a settings save no
longer re-reads the accounts unless the selection or the account list changed.
…hell's child

A shell reading carries no children, so a Claude the fresh read finds running under node was taken
for something else and left on the old account.
…a Claude tab

A Codex someone started in a tab that launched Claude was taken for Claude and would have been
restarted as claude --resume.
@coderabbitai

coderabbitai Bot commented Oct 16, 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: 7fd9e755-e36d-46cb-b9cc-3d188c4fe183
📥 Commits

Reviewing files that changed from the base of the PR and between 49ab010 and cfc5ecb.

📒 Files selected for processing: 2
  • src/renderer/src/lib/claude-session-move-coordinator.test.ts
  • src/renderer/src/lib/claude-session-move-coordinator.ts
🚧 Files skipped from review as they are similar to previous changes: 1
  • src/renderer/src/lib/claude-session-move-coordinator.ts

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

📝 Walkthrough

Adds Claude session detection and account-move coordination. Users can move eligible sessions to the selected account by reopening them with resume startup. An optional setting enables automatic moves after the idle period. The change also adds account identity data, pane restart support, account-switcher and settings controls, localized text, tests, and documentation.

Fixed issue severity: Medium

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to cfc5e

No actionable issue was established in the reviewed changes; the PR is mergeable after normal checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 52 functions across 30 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Linked Issues check Passed The directly linked issue [#19900] accepts an explicit way to resume a running Claude conversation after an account switch. The PR implements this path. The account menu reports sessions on another lo…
Out of Scope Changes check Passed The changes remain connected to [#19900]. The transcript account parser and organization-aware login identity support correct session matching. The shared restart startup and mounted-pane callback sup…
Title check Passed The title clearly and concisely describes the primary change: moving running Claude sessions to the selected account.
Description check Passed The description is complete and follows the repository template. It explains the user impact, implementation, rationale, linked issue, visual proof, testing, compatibility considerations, known limita…
Full details: Linked Issues check

Explanation

The directly linked issue [#19900] accepts an explicit way to resume a running Claude conversation after an account switch. The PR implements this path. The account menu reports sessions on another login and starts a move. The coordinator matches account identity, checks session and pane eligibility, verifies Claude is running, and resumes the same session with claude --resume. It skips unsafe, non-local, non-mounted, non-Claude, and non-resumable cases. The PR adds unit tests and an end-to-end test for the flow.

Full details: Out of Scope Changes check

Explanation

The changes remain connected to [#19900]. The transcript account parser and organization-aware login identity support correct session matching. The shared restart startup and mounted-pane callback support conversation-preserving Claude resumes. The coordinator, account-menu prompt, optional setting, localization, documentation, refactoring, and tests support the session-move feature or preserve existing restart behavior. No unrelated change is demonstrated.

Full details: Description check

Explanation

The description is complete and follows the repository template. It explains the user impact, implementation, rationale, linked issue, visual proof, testing, compatibility considerations, known limitations, and checklist status.

  • Fix all pre-merge checks with AI
  • 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: c5e3af42-7a9f-4baf-bba5-65e899f0c688
📥 Commits

Reviewing files that changed from the base of the PR and between d5cd082 and 49ab010.

📒 Files selected for processing: 32
  • docs/site/content/docs/agents/claude-code.mdx
  • src/main/claude-accounts/claude-account-selection.ts
  • src/main/claude-accounts/claude-account-service.test.ts
  • src/renderer/src/app-shell/AppBackgroundServices.tsx
  • src/renderer/src/components/ClaudeSessionMoveGate.tsx
  • src/renderer/src/components/settings/AccountsPane.tsx
  • src/renderer/src/components/settings/accounts-claude-search.ts
  • src/renderer/src/components/settings/accounts-pane-claude-section.tsx
  • src/renderer/src/components/settings/accounts-pane-claude-session-moves.test.tsx
  • src/renderer/src/components/settings/accounts-pane-claude-session-moves.tsx
  • src/renderer/src/components/settings/accounts-search.test.ts
  • src/renderer/src/components/settings/accounts-search.ts
  • src/renderer/src/components/status-bar/ClaudeSwitcherMenu.tsx
  • src/renderer/src/components/status-bar/claude-session-move-prompt.test.tsx
  • src/renderer/src/components/status-bar/claude-session-move-prompt.tsx
  • src/renderer/src/components/terminal-pane/pane-account-move-restarts.ts
  • src/renderer/src/components/terminal-pane/use-terminal-pane-process-exit-actions.ts
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/lib/agent-hibernation-pane-eligibility.ts
  • src/renderer/src/lib/claude-session-move-coordinator.test.ts
  • src/renderer/src/lib/claude-session-move-coordinator.ts
  • src/renderer/src/lib/claude-session-move.test.ts
  • src/renderer/src/lib/claude-session-move.ts
  • src/renderer/src/lib/codex-account-restart-startup.test.ts
  • src/renderer/src/lib/codex-account-restart-startup.ts
  • src/shared/claude-account-global-settings.ts
  • src/shared/claude-transcript-account.test.ts
  • src/shared/claude-transcript-account.ts
  • src/shared/default-global-settings.ts
  • src/shared/global-settings-types.ts
  • src/shared/managed-account-types.ts
  • tests/e2e/claude-session-move.spec.ts

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

Comment thread src/renderer/src/lib/claude-session-move-coordinator.ts
…cted

A move restarts the pane and is recorded at once, but a pane that unmounts before the new PTY
connects keeps the old one. The record now names the PTY it replaced, so a pane still on it reads
as not moved.
@adifsgaid

Copy link
Copy Markdown
Author

Closing this in favor of #29898, which landed this morning and covers the same thing with a better UX (the dialog right after the switch, plus Ask / Always restart / Never restart). Thanks @Jinwoo-H.

For what it's worth, I ran this approach against two real accounts last night: the first one was at its weekly limit, and after the move the same session id carried on, on the second account, with its history. So restart plus --resume holds up outside the stub too.

A few edge cases came up in review here. I haven't checked yet whether any of them apply to #29898, but I'm happy to go through them and send small fixes if they do:

  • The cached foreground read can flap to the shell for a live agent (Codex restart card silently skipped when inspectProcess flaps to a shell foreground #11064). Skipping a pane on a shell reading without the fresh confirm can leave a live Claude behind.
  • A different agent started in a tab that launched Claude (Codex, say) shouldn't be restarted as claude --resume.
  • An account whose login is System default's runs from the System default folder, so two folder ids can be the same login.
  • If the pane unmounts before the restart's deferred connect runs, the pane can come back on the old PTY.
  • Moving onto an account that's out of credits just lands on Claude's own limit prompt.

I've also hooked #28044's automatic switch into your setting, so an auto-switch asks or restarts the same way a manual one does (details on #28044).

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.

Claude account switch does not reach a running session; two panes in one worktree can run on two accounts

1 participant