Skip to content

[Feature]: Automatic account failover on rate limit for managed Claude/Codex subscription accounts #20512

Description

@JAEYOONSUNG

Problem or use case

I have several managed subscription accounts registered on one Orca host (2 Claude Max, 4 Codex). Long-running agent work still stops dead when the active account hits its 5-hour or weekly window, even though the other accounts have plenty of quota left. Recovering means noticing the stall, opening the account switcher, picking another account by hand, and restarting the work — and on a fleet of parallel agents that happens several times a day.

What makes this frustrating is that Orca already has every piece needed except the policy that connects them:

  • Per-account isolated homes already exist: claude-accounts/<id>/auth/ and codex-accounts/<id>/home/ with its own CODEX_HOME per pane.
  • Usage and rate-limit tracking already exists, including reset times for the 5-hour, daily, and weekly windows, and an 80% warning chip in the status bar.
  • The raw signal is already on disk and already parsed. Codex session logs carry, verbatim:
    "rate_limits":{"limit_id":"codex","primary":{"used_percent":59.0,"window_minutes":10080,"resets_at":1789698647}}

So Orca knows which account is about to run out, knows when it resets, and knows which other accounts exist. It just never acts on that. Switching remains manual and UI-only.

Proposed solution

An opt-in auto-failover policy per provider (Claude, Codex), default off:

  1. Threshold setting — "Auto-switch account at N% of the tightest window" (suggested default 90%). Pre-emptive, so a turn in flight is not lost. Reacting only to a hard 429 is too late and loses work.
  2. Target selection — switch to the account with the most remaining quota in the relevant window. Skip accounts whose window has not reset yet; if every account is exhausted, surface the earliest resets_at instead of thrashing.
  3. Reach running sessions — the switch must apply to sessions already in flight, not just newly spawned ones. This is the blocker today (see Claude account switch does not reach a running session; two panes in one worktree can run on two accounts #19900: a switch does not reach a running session, and two panes in one worktree can end up on two different accounts).
  4. Cooldown / circuit breaker — mark a switched-away account as cooling until its resets_at, so failover cannot ping-pong between two nearly-exhausted accounts.
  5. Headless and CLI parity — scripted runs, automations, and orchestration worker-start need the same behavior. At minimum orca account use <id|email> plus a policy flag; today no CLI command selects an account at all (related: [Feature]: Select the Claude account for headless/agent spawns via CLI (--account / orca account list) #8770).
  6. Respect explicit pinning — a pane or worktree deliberately bound to one account opts out of failover (related: [Feature]: Per-worktree account pinning — run different Claude/Codex accounts concurrently #9496).

Alternatives or additional context

Existing issues cover only fragments of this. #8121 and #9565 both ask for "auto switch account when limit exceed" but are scoped to Grok and Kimi specifically. #15467 asks for a switch shortcut, #10688 for per-account usage in the Accounts pane, #8770 for CLI account selection, and #19900 documents that a switch does not reach a running session. This request is the generalized version for the two first-class providers with subscription OAuth accounts.

Community tools solve half of it and actively conflict with Orca's account management, which is why I think this belongs in Orca rather than beside it:

Tool What it does Why it does not work under Orca
claude-swap (2.5k★) Pre-emptive switch at 90% of the 5h/7d window Writes ~/.claude and macOS Keychain directly, fighting Orca's managed auth. Compounded by #16016, where the active managed account is materialized into the default CLAUDE_CONFIG_DIR.
cc-switch (132k★) Failover queue with a circuit breaker Works through a local proxy over API-key providers (ANTHROPIC_BASE_URL → 127.0.0.1). Subscription OAuth accounts are read for quota display but are not failover-queue members.
coding_agent_account_manager Cooldowns an account on a rate-limit error, re-runs on another Per-command wrapper, reactive only, and unaware of Orca's per-account CODEX_HOME layout.

None of them are safe to combine with Orca's managed account homes. Orca is the only component that already owns both the credentials and the usage data.

Environment

macOS, Orca 1.4.200, host runtime. 2 managed Claude accounts (Max), 4 managed Codex accounts.

Activity

  1. JAEYOONSUNG commented on Sep 15, 2026

    @JAEYOONSUNG
    Author

    Follow-up with a correction and a concrete implementation note, after prototyping this locally.

    Do not source per-account quota from the session logs. My original post pointed at the rate_limits payload in Codex session files. That payload is real, but it is not attributable to an account by its path. Orca hardlinks session files across account homes — the same file appears under several codex-accounts/<id>/home/sessions trees with the same inode:

    0e900b25: inode=569031446 nlink=3
    3b6eb331: inode=569031446 nlink=3
    9a05c8a7: inode=569031446 nlink=3
    

    So a rate_limits entry found under account <id> may well belong to a different account. Three of my four accounts reported byte-identical "latest" numbers this way, which is how I noticed.

    The authoritative source is the app-server RPC. account/rateLimits/read returns the backend's own accountId alongside the numbers, and honours CODEX_HOME, so it isolates cleanly per managed account home:

    initialize                    {"clientInfo":{"name":"...","version":"..."}}
    account/rateLimits/read       params: null
    

    The response carries ordinaryUsageAllowed, rateLimitsByLimitId, and per-window usedPercent / resetsAt. Probing four account homes in parallel takes about 5 seconds.

    One scoring trap worth encoding in the feature. Accounts report more than one bucket. Only codex gates ordinary work; codex_bengalfox (the Spark tier) sits at 0% even when the main bucket is fully spent. Scoring on the wrong bucket makes an exhausted account look like the best failover target. Two of my accounts right now:

    account codex codex_bengalfox ordinaryUsageAllowed
    A 100%, rate_limit_reached 0% false
    B 0% 0% true

    A percentage alone is not enough — ordinaryUsageAllowed and rateLimitReachedType are the real gate.

    On the CLI gap. As a stopgap I spawn the target account with orca terminal create --command 'CODEX_HOME="…" codex'. Codex runs and the account binding is genuinely correct, but the tab is not tracked as an agent, since agent detection keys on the command's leading token — the same problem #8770 describes. A supported --account on spawn, or an orca account use, would remove the need for the env-var prefix entirely and let auto-failover work headlessly.

  2. homofaber-tech commented on Sep 18, 2026

    @homofaber-tech

    Independent data point from the Claude side. We run an external failover tool (cron on a Windows Orca host with WSL, plus a second Orca running headless as orca-ide serve in a Linux container), two subscription accounts per agent. The RPC and bucket findings above match what we see; the gaps that actually broke us are on the CLI surface.

    1. orca account list --json returns one reading per agent, with no account identity. rateLimits.claude / rateLimits.codex carry weekly / session usedPercent, resetsAt, status and updatedAt, but nothing says which account the numbers were measured for, and there is no entry for the inactive ones (inactiveClaudeAccounts was empty in every sample here). So an external policy cannot answer the two questions it needs: whose numbers are these, and does the other account have room?

    What that cost us: right after an account change, the reading still described the previous account. Our checker read 100% weekly, attributed it to the account that had just become live (which was nearly unused) and switched to the account that was genuinely exhausted. Hysteresis alone would have flip-flopped it every 30 minutes. We now discard any reading whose updatedAt predates the last account change, and cache the last reading per account so we never switch into one that is still over its limit. Both workarounds disappear if the payload carries an accountId.

    2. A dead login is indistinguishable from a limit without matching English strings. When the live credential expires, the same field reports:

    • claude: status: "error", error: "OAuth access token has expired. Re-authenticate to continue."
    • codex: status: "unavailable", error: "Codex not signed in"

    Our first version classified the codex wording as a limit and switched accounts, which did not help and left the other account marked invalid when its post-switch verification failed. A typed discriminator (auth / limit / unknown) alongside the human-readable message would make this safe to automate.

    3. Readings go stale with no way to ask for a refresh. updatedAt is routinely 10+ minutes old here, and stays put for 30+ minutes while the app is idle, so a 5-minute checker mostly re-reads the same numbers — and, with the attribution guard above, skips entirely. A --refresh flag on orca account list, or a small orca account usage command, would let a headless checker poll on its own cadence.

    4. Applying a switch headlessly. Same conclusion as the comment above for codex. On the Claude side claude.activeAccountIdsByRuntime is null in our setup, so the system credential file is what actually runs and switching means swapping that file's contents. Orca's picker only reaches new terminals, so afterwards the UI label disagrees with the identity running inside existing ones. An orca account use <agent> <id> that applies on the running host would remove that entire class of workaround.

    If maintainers want it, I'm happy to open a PR for (1) and (2) — accountId and a typed status on the CLI payload, with tests. Both are small, provider-neutral, and independent of whether the failover policy itself ever ships inside Orca.

  3. JAEYOONSUNG commented on Sep 18, 2026

    @JAEYOONSUNG
    Author

    Thanks — this is a useful independent data point, and it sharpens what the CLI would need. A few observations from the Codex side that bear directly on your (1) and (2), because the app-server surface already has both properties the CLI lacks.

    On (1), attribution. codex app-server → account/rateLimits/read returns the backend's own accountId alongside the numbers, so the reading carries its identity. Run once per managed home (CODEX_HOME=<account>/home) and you get a per-account table with no inference. That is the mechanism I'd suggest the CLI payload borrow rather than a new field being invented — the value already exists on the wire. Your stale-after-switch failure is exactly what this prevents: the number and the identity arrive together, so nothing can be attributed to the wrong account.

    On (2), auth vs. limit. The same RPC already discriminates them. A home with no credential returns a JSON-RPC error (-32600, "codex account authentication required to read rate limits") while an exhausted account returns a normal result with ordinaryUsageAllowed: false and rateLimitReachedType set. I verified this against a deliberately empty home this morning. So a typed status on the CLI could be derived mechanically: RPC error → auth, ordinaryUsageAllowed === false → limit, else ok. No English matching needed.

    Two things your comment made me check in my own external tool, for anyone building the same:

    • A dead login must land in the "unknown" bucket, not "blocked", or you will switch away from an account that just needs re-auth and then into one that may itself be dead. Mine treats any RPC error as unreadable and excludes such accounts from both the active verdict and the candidate list.
    • After switching away from account A, park A in a cooldown until its own resetsAt and never select it as a target inside that window, whatever the next reading says. Combined with a minimum interval between switches, that closes the flip-flop you describe even if a reading is briefly misattributed.

    On (3), staleness. For Codex the app-server read is live, so there is nothing to refresh. For Claude, claude -p "/usage" --output-format json is also live and free (total_cost_usd: 0, num_turns: 0), and it returns per-model weekly windows — on this machine the Fable window sits at 100% while "all models" is at ~65%. That distinction matters for policy: it is a reason to switch model, not account, and an account-level checker would burn a healthy account on it. Only the active account is readable this way, which is the gap that leaves your point (1) genuinely open on the Claude side.

    On (4), agreed — same conclusion as #19900. A switch reaches new terminals only; existing panes keep the identity they were given (for Codex that is per-pane CODEX_HOME, which is actually the better model and is why Codex panes can already run different accounts side by side).

    I'd support the PR for (1) and (2). If it sources accountId and the typed status from the app-server response rather than the CLI's cached reading, both fall out for free and stay correct after a switch.

  4. ryofukutani commented on Sep 19, 2026

    @ryofukutani

    Data point from a macOS host (Orca 1.4.205, nine managed Claude accounts), on the "applying a switch headlessly" gap raised above.

    • Swapping ~/.claude/.credentials.json (and the Claude Code-credentials keychain item) from outside does not stick here. Orca keeps the selection in settings.activeClaudeManagedAccountId and rewrites the system credentials from the selected account on its periodic sync, so an external swap gets reverted and the status bar keeps showing the old account.
    • The runtime already has the switch as an RPC: accounts.selectClaude with {"accountId": "<uuid>"}, on the same socket orca account list uses. Reading the source, it goes through the same serialized path as the picker (write-back of the outgoing account's refreshed token, the live-PTY guard around refresh, ~/.claude.json oauthAccount). That is exactly the part an external failover tool should not reimplement.

    Request: expose it on the CLI, e.g. orca account use <id|email> [--provider claude|codex] --json, returning the new activeAccountId. It is a thin wrapper over an existing method, it keeps external failover tools away from credential files entirely, and it is useful whether or not the in-app failover policy ships.

    — Claude (Claude Code), posting for @ryofukutani (session: dev-project-42)

  5. JAEYOONSUNG commented on Sep 19, 2026

    @JAEYOONSUNG
    Author

    Confirming from a macOS host (1.4.205, four Codex + two Claude accounts) that accounts.selectClaude / accounts.selectCodex over the runtime socket is what an external tool should call, and +1 to orca account use. Two additions.

    Why the RPC and not the files, stated once for anyone landing here. Your point about the periodic sync is the decisive one: the selection lives in Orca's settings, and the system credential is derived from it, so anything written to ~/.claude/.credentials.json from outside is not a switch, it is a race Orca wins on the next sync. The RPC is the switch. It also carries the parts nobody should reimplement — the outgoing account's refreshed-token write-back and the live-PTY guard around the refresh — which is what makes it safe to fire from a scheduled job while sessions are open. I have had it running on a 15-minute schedule for several days without a live session being disturbed.

    A per-pane Claude account already exists in the codebase — on WSL. Reading the bundle, the WSL runtime pins each Claude session to its managed account with envPatch: {CLAUDE_CONFIG_DIR: <account auth dir>}, and there is even an invariant for it (claude sessions pin CLAUDE_CONFIG_DIR, not …). The host runtime instead does the auth-only swap inside the default config dir, which is the root of #14254 and #7782 (both closed) and of the "every session on the machine follows the switcher" complaint. The two runtimes could share the WSL model: hand each Claude pane a CLAUDE_CONFIG_DIR the way Codex panes already get a CODEX_HOME. That would give concurrent Claude accounts per pane with no new mechanism, and the sync problem above disappears because nothing is swapped in place any more.

    For what it's worth, that is also the workaround that survives the sync today: a second CLAUDE_CONFIG_DIR with its own sign-in is invisible to Orca and does not get reverted, at the cost of Orca not recognising the pane as an agent (#8770).

    So the CLI ask would be two thin wrappers over things that already exist: orca account use <id|email> --provider … --json over accounts.select*, and, for the host runtime, the per-pane CLAUDE_CONFIG_DIR pinning WSL already does.

  6. added theissue type on Sep 19, 2026
  7. karoliang commented on Sep 20, 2026

    @karoliang

    Strong agree on the framing, and the part I would underline is your own sentence: Orca already has every piece except the policy that connects them.

    I have been building that policy layer outside Orca for a slightly wider case, and two things from doing it may be useful here.

    The numbers differ in quality, and a failover policy has to carry that. Reading the three providers I use:

    • OpenCode Go answers GET https://opencode.ai/zen/go/v1/usage with rolling, weekly and monthly percentages and an ISO reset for each. Exact and live.
    • Codex writes rate_limits.primary.used_percent with window_minutes and resets_at into its newest session rollout JSONL. Exact, but only written when Codex runs, so between runs it is a lower bound on usage, never an upper one. That is the same behaviour as [Bug]: Codex quota refresh is not timely #11538.
    • Claude Code caches nothing, so its usage has to be reconstructed from transcript token counts. An estimate.

    Failing over onto an account whose headroom is an estimate can strand a run just as badly as not failing over at all. What has worked for me is treating an estimated number as usable only for work that already justifies an escalation, and never as ordinary spare capacity.

    Ordering matters more than availability. Once more than one account has room, the useful rule turned out to be: spend the bucket that expires first. A window resetting in three hours is capacity about to be thrown away; a monthly window is the scarce thing. Picking the account with the most headroom does the opposite of the right thing, because the monthly bucket usually looks the emptiest.

    Also worth separating from failover proper: dropping a tier before the wall rather than at it. With two readings of the same bucket you have a burn rate, and if the projection crosses 100% before the reset you can stop handing that account expensive work while it still has room for cheap work.

    I filed two adjacent issues that this one would compose with rather than duplicate: exposing the usage numbers over the CLI and RPC (#21746), and an optional agent-selection hook on worker-start (#21747). The first is what any external policy needs and is the smaller half; this issue stays the in-app behaviour.

    Happy to share the standalone implementation once it is public if that is useful as a reference for the ordering rules.

  8. karoliang commented on Sep 20, 2026

    @karoliang

    Following up on the offer above: the standalone implementation is public now at https://github.com/karoliang/rightsize (MIT, Python standard library only, no dependencies).

    Relevant to this issue rather than the whole tool:

    • rightsize.py headroom() and pick() hold the ordering rules, roughly forty lines between them. Candidates are filtered by a per-provider reserve, then sorted by which bucket resets soonest.
    • The probes show what each provider actually gives you today, which is the part that took the longest to find. OpenCode Go answers GET https://opencode.ai/zen/go/v1/usage; Codex only leaves rate_limits behind in its session rollout, so it is a lower bound; Claude Code caches nothing.
    • test_rightsize.py runs the policy against synthetic quota states with no network, including the two cases that are easy to get wrong: an unknown headroom must not be treated as spare capacity, and an exhausted ladder must escalate rather than return nothing.

    None of it is an Orca plugin and it does not need to be. It reasons about host-level quota, which is exactly the limitation #21746 would remove, since Orca is the only thing that knows which managed account a given pane will use.

  9. JAEYOONSUNG commented on Sep 20, 2026

    @JAEYOONSUNG
    Author

    The ordering rule is the part I had backwards, so thank you for stating it plainly. My tool sorted candidates by most headroom and handed work to the account that looked emptiest — which, exactly as you say, is usually the one whose scarce long window is emptiest. It now sorts eligible candidates by soonest reset, with headroom only as a tie-break, and logs the reason it chose ("window resets 09-27 08:46 with 90 points usable, so spend it before it expires"). The burn-rate skip is in too: two readings of the same bucket, project to the reset, pass the account over while it still looks fine. Tests cover both, including the two you flagged as easy to get wrong.

    Two of your three quality tiers are better than the probes suggest, and it changes the policy. This is the part worth checking before anyone builds on the estimate path.

    Codex is exact and live, not a lower bound. The session-rollout rate_limits payload is the wrong source — and worse than stale, it is unattributable, because Orca hardlinks session files across account homes (same inode under several codex-accounts/<id>/home/sessions trees), so a payload found under one account may belong to another. The right source is codex app-server → account/rateLimits/read, which answers on demand whether or not Codex has run, and returns the backend's own accountId alongside the numbers. Run it once per CODEX_HOME and you get an exact per-account table with no inference and no attribution problem. I verified just now against a home that had not run Codex in days: usedPercent, resetsAt, and accountId all present.

    Claude Code is exact, live, and free. claude -p "/usage" --output-format json returns the same windows the TUI shows. Measured on this machine a moment ago: total_cost_usd: 0, num_turns: 0, duration_api_ms: 0. No transcript reconstruction, no estimate. The catch is different from the one you describe: only the ACTIVE account answers, because Orca swaps one credential in place rather than keeping one per account — which is precisely the gap #21746 would close.

    So on my machine the tiers are: Codex exact per-account, Claude exact but active-account-only. The "unknown is not spare capacity" rule still earns its place, but it now guards a much smaller set.

    One dimension the model is missing: the binding bucket can be per-model. Claude reports a shared weekly window and a per-model weekly window. Here, right now, the Fable window sits at 100% while all-models reads 25%. Treating that as one number gives the wrong action either way: account-level failover burns a healthy account for what is a model problem, and ignoring it strands the run. The fix is to let the verdict name what to change — model or account — and my policy splits model_exhausted from account_exhausted for that reason. If rightsize grows a per-model bucket id, the existing ordering rules apply to it unchanged.

    Happy to send the per-provider probe notes as a PR to rightsize if the app-server approach is useful to you — it removes the lower-bound caveat for Codex entirely.

  10. karoliang commented on Sep 20, 2026

    @karoliang

    My tool sorted candidates by most headroom and handed work to the account that looked emptiest — it now sorts eligible candidates by soonest reset, with headroom only as a tie-break.

    I owe you a correction on that, because I have since found the rule I gave you is right for cheap work and wrong for expensive work, and you have now shipped the half I got wrong.

    Soonest reset is right until the dispatch is expensive. The rule exists so capacity about to vanish is not wasted, and the way to waste it is to spend it on the priciest thing you run. A repo of mine hit exactly that: OpenCode's weekly at 77 per cent used with eight points left and nineteen hours to run, Codex at 0 per cent with a week of room, and a design-tier task banded onto the emptying bucket because its window closed sooner. The cheap work that bucket could have absorbed went elsewhere, and the expensive work would have been fine on the plan that was sitting idle.

    So the ordering is now split by cost: cheap bands take the soonest reset, and the expensive rung takes the most headroom. A candidate whose bucket cannot cover the dispatch's estimated cost is passed over rather than merely ranked low.

    The windows are nested, and pacing only the binding one misses it. This is the part I would most encourage you to copy, because it is invisible until you look for it. Same account, same moment:

    bucket used elapsed pace projected at reset
    rolling 17% 83% 0.2x 20%
    weekly 77% 89% 0.9x 87%
    monthly 38% 10% 3.7x 371%

    The weekly looked nearly spent and was perfectly on pace. The binding bucket was the weekly, so a policy that paces only the bucket that binds sees a healthy account. The month those same tokens also came out of was on course to be gone in a week. projected = percent / elapsed_fraction needs no history, so it costs nothing to run on every window rather than one.

    Two different overruns, and only one may be relaxed. Whole-window pace is a forecast from an average; the two-reading burn rate you mention is a measurement. They disagree usefully: a month at 40 per cent with half its window gone is fine on average and on course for 115 per cent at the rate of the last two days. I now run both on every bucket. Where a policy lets a task ignore pacing rather than be stranded, it should ignore the average and never the measurement.

    One trap worth naming since your burn-rate skip is in: a sample has to be worth extrapolating. Two minutes of a seven-day window projected to five thousand per cent and marked a just-reset bucket as overrunning. Mine now requires the sample to cover at least five per cent of the window.

    On sourcing Codex, the thread was right and I was wrong. I had been reading rate_limits out of the newest rollout, with the staleness caveat. @JAEYOONSUNG's point about hardlinking is confirmed here even with a single account: one rollout under the Orca account home has nlink=2, the same inode as the copy under ~/.codex. With one account that is harmless; with several the block carries no identity to check.

    codex app-server → account/rateLimits/read is the answer, as suggested upthread, and it is better than I expected: 0.74 seconds, no interactive session needed, and it carries its own accountId.

    {"rateLimits":{"primary":{"usedPercent":7,"windowDurationMins":10080,"resetsAt":1790480193},
     "planType":"pro"},"accountId":"e81eb3ba-..."}

    Live it reads 7 per cent; the newest rollout file reads 0. That endpoint is now the first source in my tool with the file kept only as a fallback, which removes the failure mode I described earlier in this thread, where a nineteen-hour-old reading of 93 per cent kept a provider out of every ladder after its quota had moved on.

    All of the above is in https://github.com/karoliang/rightsize if it is useful to read rather than re-derive: the ordering lives in pick(), the pacing in bucket_pace() and headroom(), and each rule has a regression test written from the state that broke it. Twelve of those tests exist because I had agents attack the policy and they found two rules meeting to produce an answer nobody would defend.

  11. thinklanguages commented on Oct 11, 2026

    @thinklanguages

    Windows data point (Orca 1.4.205, four managed Claude accounts), on external switchers.

    I tried claude-swap to get proactive switching at a usage threshold. The switch does not survive on a host where Orca manages the accounts:

    • After /login to a second account, Orca copied the live credentials into that account's claude-accounts/<id>/auth/.credentials.json, and 4 ms later rewrote ~/.claude/.credentials.json from the account in settings.activeClaudeManagedAccountId.
    • This happened within about two minutes of the external change, with no switch made in the Orca UI.

    So the revert @ryofukutani describes on macOS happens on Windows too, and there is no supported way today to get threshold-based switching next to Orca's account management.

    +1 to items 1, 2 and 5 of the proposal: an opt-in "switch at N%" policy that picks the account with quota left, and orca account use <id|email> so a script or the agent itself can apply a switch. orca account add and orca account list are the only account commands in the CLI today.

  12. adifsgaid commented on Oct 12, 2026

    @adifsgaid

    I had a go at the Claude half of this in #28044.

    It's opt-in. Once the selected account passes 90% of its 5-hour or weekly window, the next Claude you start goes to whichever managed account has the most room. There's a small margin and a cooldown so two accounts near the line don't ping-pong. No Codex yet, and running sessions keep their account, same as a manual switch today (#19900).

    @JAEYOONSUNG @karoliang @ryofukutani your notes in this thread saved me a lot of digging, especially the point about never trusting usage you can't tie to an account. That ended up being the trickiest part to get right.

    If any of you runs several Claude accounts and has time to try the branch, I'd really like to know whether it picks the account you would have picked.

  13. bacaxnot commented on Oct 14, 2026

    @bacaxnot

    +1. Another data point, from a setup where Orca drives a shared Linux box over SSH and rotation runs outside Orca.

    On that box, cswap auto (claude-swap) runs as a systemd service. Orca has no Claude account registered there and only reads the credentials, so the two never fight over them. It works, but it's a second system to keep alive. I'd much rather Orca owned this. #28044 looks like the right start, and these are the parts we rely on daily that it doesn't cover yet:

    1. The Fable weekly window. Fable's weekly limit is far lower than the overall weekly one, so it's usually the first wall we hit. Besides the 5-hour and weekly windows, we also rotate when Fable passes 90%, and never land on an account whose Fable window is spent. Otherwise a switch lands on an account that still has 5h/weekly room but no Fable left. Per-model windows (Fable today, whatever comes next) should count for both the trigger and the choice of target.
    2. Selectable policy. A simple mode switch between "5h + weekly only" and "also count Fable". When we're not using Fable, there's no reason to rotate away from an account just because its Fable window is spent.
    3. Hold an account out of rotation. A disabled account keeps its slot and credentials, but auto-switching never picks it, e.g. an account without a paid plan or one reserved for something else. A manual switch to it should still be possible.
    4. Accounts on an SSH host / managed server. Here the accounts live on the remote host, not on the desktop. It would help to know whether feat(claude-accounts): switch accounts before a usage limit #28044's policy runs where the accounts are (the relay/orcad side) or only on the desktop, and whether it still works when the desktop is closed.
    5. Running sessions follow the switch. On our box every session moves to the new account together. For long agent runs, "applies to the next Claude you start" only covers half the problem (Claude account switch does not reach a running session; two panes in one worktree can run on two accounts #19900).

    Happy to test #28044 against an SSH-host setup once it's in a build.

  14. hyeonjong-ai commented on Oct 15, 2026

    @hyeonjong-ai

    +1 from our team — this is a real pain point for us too.

    We run several Claude accounts in Orca and keep 6–8 agent sessions going in parallel most of the day. When the active account hits its usage window, the session just stops until someone notices and switches accounts by hand. It would be great if Orca could automatically switch to another registered Claude account with remaining quota and keep the work going without interruption (ideally at a turn boundary, with a short notice of which account it switched to and why).

    We'd really love to see this land soon. If there's anything we can do to help — testing a preview build, sharing usage patterns or logs, or giving feedback on the design — we're happy to help.

  15. adifsgaid commented on Oct 17, 2026

    @adifsgaid

    @hyeonjong-ai two open PRs cover what you describe:

    What it looked like with two real accounts last night: account A was at its weekly limit, so the turn ended with "You've hit your weekly limit". I opened the Claude menu, it said 1 session was still on the old account, I clicked Move Session, and a few seconds later the same session id was running on account B and answered from the earlier turn. With "Move idle sessions" on, a session sitting in a background tab moved by itself about a minute after the switch.

    With 6 to 8 sessions, the idle ones in background tabs move on their own and the one you're looking at moves when you click. One gap for now: a turn that already died on the limit gets moved, but you still type "continue" yourself.

    If you can try it with that many sessions, that would help a lot. Both are on my fork for now (claude-auto-switch and feat/claude-move-sessions-on-account-switch).

  16. adifsgaid commented on Oct 17, 2026

    @adifsgaid

    @hyeonjong-ai small update to the above: a maintainer merged #29898 this morning, which does the running-session part on main, so I closed #29802. After a switch, Orca now offers to restart the Claude sessions still on the old account, or does it without asking if you pick Always restart in Settings › Accounts.

    #28044 now hands its automatic switch to that same step. So with Always restart, the full flow is: the account runs low, Orca switches, and the running sessions resume on the new one. If you want to try it, the claude-auto-switch branch on my fork has all of it on top of current main.

  17. AmethystLiang commented on Oct 19, 2026

    @AmethystLiang
    Contributor

    We've improved the manual switching process in recent releases.

    And will look into how to reliably do the automated switching. Thanks everyone for sending in all your research. Much appreciated!

  18. adifsgaid commented on Oct 19, 2026

    @adifsgaid

    @AmethystLiang thanks. For the automated part, #28044 is up to date with main and now goes through #29898's setting: once the selected account passes 90% of its 5-hour or weekly window, Orca switches to the account with the most room left, and running sessions get the same Ask / Always restart / Never restart treatment as a switch by hand. It's off by default. If you'd rather it work differently, I'm happy to rework it.

  19. ng-technology-llc commented on Oct 21, 2026

    @ng-technology-llc

    Please include Antigravity in this failover. On macOS every parallel Antigravity worker shares one machine-wide credential, so when the selected account's 5-hour quota runs out all workers stop at once ("Individual quota reached"), and switching to another saved account only helps workers launched afterwards; running ones stay blocked until restarted. Automatic failover to the next saved account (and restarting or re-binding blocked sessions) would remove this. Related bug on account selection being lost after token refresh: #31579

  20. RYUGUNN77 commented on Oct 22, 2026

    @RYUGUNN77

    Data point from a different angle: an agent that is not Claude Code consuming Orca's managed Claude accounts, on macOS (Orca 1.4.237, two managed Claude accounts). We run Hermes Agent inside Orca panes; it talks to the Anthropic API directly with the OAuth login Orca manages, so none of the Claude-Code-side fixes (#29898 restart, claude --resume) reach it. What we ended up building on our side, and the two gaps that only Orca can close:

    What works today, from the pane's point of view. Orca launches each pane with CLAUDE_CONFIG_DIR=<claude-profiles>/<id>/home and ORCA_CLAUDE_PROFILE_POINTER=<claude-profiles>/selected-host, and rewrites selected-host on every status-bar switch. Reading that pointer on every credential resolve (instead of CLAUDE_CONFIG_DIR once at startup) is enough for a running non-Claude-Code session to follow a manual switch without a restart — no files under ~/.claude touched, no fight with the periodic sync described above. The sibling <id>/home directories beside the pointer give the agent its 429 fallbacks: on a hard 429 it rotates to another profile's login, marks the selected one as cooling until the reset the API reports, and moves back when the window reopens. Each profile's refreshed tokens are written back only into that profile's own dir / keychain item.

    Gap 1 — the pointer is not a documented contract. Everything above hangs on selected-host and the two env vars, which I found by reading the launch environment, not the docs. If Orca could state that an external agent may read the pointer (or expose the same thing as an event on the runtime socket), tools like ours would not break silently on the next refactor.

    Gap 2 — no way to tell Orca "this pane is on account B now". When the agent fails over on a 429 the status bar still shows account A, and the usage chip keeps attributing the traffic to A. The RPC @ryofukutani found (accounts.selectClaude) is the right primitive, but there is no CLI in front of it: orca account select --agent accepts opencode|devin only (1.4.237). A orca account use <id|email> --agent claude (or exposing selectClaude on the CLI) would let an external failover do the one thing we cannot — keep the status bar and usage attribution truthful — and it is the same command the headless/CLI-parity item (5) in the original post needs. Even without failover, it is what lets a script set the active account before orchestration worker-start.

    Opt-in 90% pre-emptive switching (#28044) and hard-429 failover are not in conflict: the first avoids losing a turn, the second is the floor when the probe is stale or the agent is not Claude Code. Both need Gap 2 to report what they did.

    Happy to test a build or share the pointer-following logic if it helps.

  21. RYUGUNN77 commented on Oct 22, 2026

    @RYUGUNN77

    Follow-up on the account-selection surface (same request as above, one more angle):

    With a paired remote environment, the Claude account picked in the status bar applies only to the host Orca runs on — the remote daemon has no selection (orca account list/select also reject --environment, and the remote claude-profiles/selected-host stays empty). Tools on the remote machine that follow ORCA_CLAUDE_PROFILE_POINTER therefore see nothing.

    Request: let the status-bar Claude account selection apply per environment (write the remote daemon's pointer / expose orca account select --agent claude --environment <env>). Credentials should stay per machine — only the which account choice needs to travel; each machine already holds its own login for that account.

    We currently bridge this ourselves by syncing the selected account's email between machines and resolving it to each machine's own login, so an official per-environment selection would let us drop that shim.

  22. AmethystLiang commented on Oct 28, 2026

    @AmethystLiang
    Contributor

    Looking into this 👀

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

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions