Skip to content

feat(claude-accounts): turn on account auto-switch for a remote server's accounts - #29060

Draft
adifsgaid wants to merge 20 commits into
stablyai:mainfrom
adifsgaid:feat/claude-auto-switch-remote
Draft

adifsgaid wants to merge 20 commits into
stablyai:mainfrom
adifsgaid:feat/claude-auto-switch-remote

Conversation

@adifsgaid

@adifsgaid adifsgaid commented Oct 15, 2026 •

Copy link
Copy Markdown

ELI5

#28044 adds a toggle that switches Claude accounts before a usage limit. But if your accounts live on a remote Orca server, you couldn't turn it on: the toggle was hidden there. This lets you turn it on for the server, so the server switches its own accounts.

Stacked on #28044. What's new here is the last two commits (f0ba9ec, f2371a6) plus a merge of main; I'll update it once #28044 lands.

What Changed

Before: Settings › Accounts › Claude hid the toggle while showing a remote server's accounts, and no CLI command could set it, so the switch the server would run could never be turned on. The browser client did show it, but saved it in browser storage, so the server never saw it.

After: the toggle shows for a remote server's accounts too, and it reads and writes the server's own setting.

  • The host that owns the accounts keeps the setting. ClaudeAccountSelection.list() now reports autoSwitchEnabled with the accounts, and setAutoSwitch writes it.
  • The toggle writes it the same way a select does: IPC on the desktop (claudeAccounts:setAutoSwitch), and a new accounts.setClaudeAutoSwitch RPC (accounts-admin, like accounts.selectClaude) for a remote server or the browser client.
  • The toggle moved into its own file, accounts-pane-claude-auto-switch.tsx.
  • An older server doesn't report the field, so the toggle stays hidden there, as it is today.
  • One sentence in the docs.

Why

@bacaxnot asked on #28044 whether this works for accounts on a remote host. The answer was "it would run there, but you can't turn it on", which isn't much of an answer.

Linked Issue

Follow-up to #28044, part of #20512 (point 4 in #20512 (comment)).

Visual Proof

N/A: it's the same toggle as #28044 (screenshots there). The only visible change is that it also appears when Settings shows a remote server's accounts.

Testing

  • Automated tests added. The service reports and writes the setting, the RPC method exists with accounts-admin and checks its params, the client sends the call to the right host, the browser client sends it to its server, and the real Accounts pane on a remote scope sends the change to that server and shows it on. A server that reports no setting hides the toggle. I broke each of those on purpose and checked that a test went red.
  • I manually tested these changes locally. Not against a real orca serve host, I don't have one set up.

Ran locally: the Claude account, accounts RPC, mobile RPC allowlist, provider-accounts client, Accounts pane and browser-client suites, typecheck (node, web, cli), oxlint, oxfmt, the changed-code audit, the max-lines ratchet, the RPC params catalog check and the localization checks.

Review

cc @nwparker @Jinwoo-H @brennanb2025. If you'd rather have this folded into #28044, say so and I'll move it there.

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: the new method needs accounts-admin, the same as selecting an account. It isn't on the mobile allowlist.
  • Compatibility: autoSwitchEnabled is optional on the accounts state. Old clients ignore it, and new clients hide the toggle when an old server leaves it out.
  • Remote: plain SSH keeps its accounts on the desktop, so nothing changes there.
  • Performance: one more boolean on the accounts state.
  • Known limit: a second Settings that's already open (another window, or another client of the same server) shows the change once it reloads its accounts, same as the account rows do today. Clicking it from there still saves the value you picked.

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)

Opt-in setting. When the selected managed Claude account passes 90% of its
5-hour or weekly window, new Claude sessions move to the managed account on
the same runtime with the most room left. A 10-point margin and a 5-minute
cooldown (also after a manual pick) stop two accounts trading places; at a
hard limit any account with room wins. Accounts that need a sign-in, sit on
another runtime, or have no reading from the last 15 minutes are skipped;
stale readings are refreshed before ranking. Only readings tagged with the
selected account count, and a switch queued behind a sign-in re-decides on
the latest usage and yields to anything the user did meanwhile.
…der way

The usage service skips a read it debounces, one already running, or one where
every account is inside Retry-After. The auto-switch listener recorded the ask
as a read anyway and would not ask again for 15 minutes. It now counts a read
only when the published state shows one in flight.
An idle Claude session keeps reporting the usage of a window that has since
rolled over (the stale statusline snapshots stablyai#23421 describes), and an inactive
account's cached reading can outlive its window. The auto-switch policy counted
both as current, so a stale 96% could trigger a switch and an account whose
window had reset could be skipped as exhausted. A window past its reset time
no longer counts.
A failed read and a live session's statusline post both carry the account they
belong to (usageMetadata.authProvenance), but a successful OAuth read carried
no metadata at all. The auto-switch only trusts a reading tagged with the
selected account, so it never acted on a polled reading: with no session
posting, as when the account has already hit its limit, it stayed put. A
successful read now carries the same credential source and account tag.
…cted account's runtime

Two cases left the auto-switch with nothing to pick. With the default WSL
distro the usage target names no distro while accounts do, so every other
account was filtered out; candidates are now the accounts on the selected
account's own runtime. And an account inside its Retry-After cannot be read,
so waiting for its reading stalled ranking while a usable account was right
there; it no longer waits for those. The docs paragraph now says the switch
needs another account with clearly more room.
…ublish

The auto-switch listener asked the usage service for a read from inside the
service's own publish loop. The service publishes again from within that call,
so the window and paired clients were handed the newer state first and the
older one last. The ask now runs after the publish it reacted to. A read also
counts as tried only when one of the accounts being ranked is fetching, not an
account on another runtime.
…r's accounts

The setting lived in the desktop's own settings, so Settings hid the toggle when it showed a
remote Orca server's accounts, and the browser client saved it in browser storage. The host that
owns the accounts runs the switch, so it now keeps the setting too: the accounts list reports it,
and the toggle writes it through the same account calls a select uses (IPC on the desktop,
accounts.setClaudeAutoSwitch on a server or from the browser client). An older server reports no
setting, so the toggle stays hidden there.
…changes

Writes the setting the way a select does, so other windows' settings follow it, and covers the
browser client's call. The toggle reads its title and description once.
Keeps both sides' new account methods (accounts.setClaudeAutoSwitch and main's
accounts.setHostLaunchEnv) in the regenerated params catalog, and both sides' additions to the
owner-scope pane test.
# Conflicts:
#	src/main/claude-accounts/claude-account-selection.ts
#	src/main/claude-accounts/claude-account-service.test.ts
#	src/main/claude-accounts/service.ts
#	src/main/startup/main-process-account-services.ts
#	src/renderer/src/components/settings/accounts-claude-search.ts
#	src/renderer/src/components/settings/accounts-pane-claude-section.test.tsx
#	src/renderer/src/components/settings/accounts-pane-claude-section.tsx
#	src/renderer/src/i18n/locales/en.json
#	src/shared/managed-account-types.ts
…ch as after one by hand

The automatic switch runs in main, so the running-session question from stablyai#29898 never came up for
it. Main now tells the renderer which accounts it switched between, and the renderer hands that to
the same step a switch from the menu runs: ask, always restart, or never restart.
…unt's own distro

With the default WSL distro the usage target names no distro, so the running-session step looked in
the wrong lane and missed every session in the account's distro. A switch from the menu passes the
concrete distro; the automatic one now does too.
# Conflicts:
#	docs/site/content/docs/agents/claude-code.mdx
#	src/preload/api/agent-account-api.ts
#	src/preload/api/claude-accounts-bridge.ts
#	src/renderer/src/components/settings/accounts-pane-claude-section.test.tsx
#	src/renderer/src/components/settings/accounts-pane-claude-section.tsx
#	src/renderer/src/web/preload-api/web-agent-accounts-api.ts
…following it waits on nothing

Following the switch read the account list first, so a switch by hand during that read was
overtaken: the follow then asked about the automatic one, and a failed read was left unhandled.
Main has the list at the switch and now sends it along.
After main moved on, the Claude accounts section and the settings type
passed their max-lines caps. The toggle now sits in its own component and
its setting in ClaudeAccountSwitchSettings, the same way the running-session
setting does.

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