Omniscio documentation
Browse all documentation
  1. Getting Started13
  2. Sessions & Agents115
  3. Inbox & Notifications59
  4. Projects & Tasks95
  5. Automation & Scheduling75
  6. Knowledge & Memory26
  7. AI Features60
  8. Integrations100
  9. Plugins & Marketplace33
  10. Cloud & Teams56
  11. Settings & Customization58
  12. Account & Billing28
  13. Troubleshooting84
  14. CLI & API Reference22
  15. Legal & Policies4
  16. Uncategorised22

Account Pool (multiple Claude accounts + exhaustion alert)

How Omniscio holds several Claude logins and picks one per session spawn: the tiered default versus the load-balanced opt-in, pinning a preferred account, the all-accounts-at-their-limit banner, dead-login quarantine and failover, and how rate-limited or burst-throttled accounts drop out of the pool.

What it is

Omniscio can hold more than one Claude login and silently pick the best one for each session spawn based on usage headroom. When every tracked account has hit its 5-hour and 7-day cap (including any remaining extra-usage credits), Omniscio doesn't just fail — it surfaces an amber "All accounts are at their usage limit" banner at the top of the sessions sidebar, fires a 15-second in-app toast with a View Accounts button, and pings the OS notification center so you know to add capacity before queued sessions stall.

How a spawn actually picks an account depends on one setting, and the default matters. That setting is OFF by default. With "Distribute sessions across accounts" ON (distributeSessionsAcrossAccounts — the owner call of 2026-08-29 flipped it, so z.boolean().default(false) is the schema default for a fresh install; an install that predates the flip is deliberately kept ON, because the load-save pre-parse seed re-seeds true for a config that never persisted the key) — the pick runs through the load balancer, which spreads new sessions across logins with capacity. The load balancer never pins, and it weighs more than raw utilization: standing sessions per account, in-flight spawns, a per-account ceiling, hot-account avoidance, and upcoming reset-window harvesting. See distribute-sessions-across-accounts.md.

The tiered list is the default pick: pinned account first (if it has capacity) → OAuth logins with primary capacity (sorted freshest-utilization-first) → OAuth logins relying on extra-usage credits → API-key accounts (only if allowApiKeySessionSpawn is enabled). The load balancer delegates to this same tier list when its own candidate pool comes up empty — so the tiers describe both the default path and the load balancer's fallback. If every tier is empty, the exhausted alert fires.

Where to find it

How to use it

  1. Add accounts. Settings → Accounts → Log In with Anthropic (OAuth) for each extra Claude.ai subscription you want in the rotation. You can also paste an API key, but API-key accounts are the fallback of last resort and the allowApiKeySessionSpawn setting gates whether they can spawn sessions at all (default: off).
  2. (Optional) Pin a preferred account. In the Accounts list, set one account as Pinned — the tiered picker will always try it first if it has primary capacity, falling through to the next-best when it hits its cap. Pinning only applies when "Distribute sessions across accounts" is OFF — which is the default — because the load balancer deliberately never pins, and pinning and spreading are opposite goals.
  3. Just work. You don't have to pick an account per session. On the default tiered path, Omniscio scores each account by max(5h_utilization, 7d_utilization) and uses the freshest one. Turn the setting ON and it instead spreads new sessions across logins that have capacity, factoring in how many sessions each is already carrying. Either way, running sessions don't move between accounts mid-turn; the pick happens at spawn time and on Continue after a rate-limit recovery (a session can silently resume on a different account — see session-stuck-in-needs-you.md rate-limited case).
  4. Read the usage bar. Each account row and the session-panel usage widget show a percentage bar. Over 100% turns red; the tooltip shows an absolute reset time ("Thursday 5:00 PM") rather than a relative countdown so you can actually plan.
  5. When the exhausted banner appears. Click View Accounts to add another account, wait for one of the reset times, or dismiss the banner (X). A dismissed banner stays dismissed for that exhaustion event — if any account regains capacity and then a new exhaustion happens later, the banner comes back. An OS notification only fires once per 30-minute window so you don't get spammed.
  6. Muting. The exhausted alert respects the global notification controls — notificationBehavior: 'never' | 'backgrounded' | 'always' and silenceUntil — same as other OS toasts. The in-app banner itself cannot be globally muted (it's load-bearing UX: you need to know sessions can't spawn).

How it behaves

When a login goes bad (dead-login quarantine + failover)

Separate from running out of capacity, a login can go dead — its saved credential is no longer valid and even a token refresh is rejected (you'd typically have to sign in again). Omniscio handles this automatically:

  • Quarantine. When a refresh is rejected twice in a row on the same token, Omniscio marks that login dead and every account picker skips it from then on, so new sessions never spawn against a credential that can only fail. (The first rejection is held, not acted on, in case another refresh had already replaced the token.) Signing in again clears the quarantine automatically. The 15-minute background refresh never marks a login dead on its own — once it has seen the same token rejected twice, it simply stops re-sending that token for an hour at a time, and in the log such a skipped attempt reads skipped_known_dead, not dead_token.
  • Failover. Any sessions that were stuck on the dead login are moved onto a healthy login and resume on their own — you don't have to retry them. (Omniscio won't loop forever on a dead credential.)
  • Idle heads-up. Even if you're not actively using a login, if Omniscio notices in the background that it has gone dead, it surfaces a re-login prompt proactively — so you can fix it before your next session fails instead of being surprised mid-work.
  • Auto-resume on re-login. Once you sign that account back in, its sessions pick back up automatically.
  • Signing back in without doing it by hand. If you hold a pool of accounts, Settings → Accounts offers Sign them back in — it drives the whole sign-in in your real Chrome, reads the sign-in email from your connected Google account, and captures each account back into the pool; you only do the two clicks Cloudflare and the consent screen insist on. See account-relogin.md.
  • Nowhere healthy to go. If a session needs to move but every login is dead or capped, it lands in a visible recovery-failed state (orange dot in the inbox) rather than silently idling — your cue to add capacity or re-authenticate.
  • A busy computer can't log you out (2026-09-28). Every sign-in and every token refresh is sent from a small background process of its own, so when Omniscio's main process is frozen or overloaded, Anthropic's reply still gets saved. Before, a refresh that ran past its 10-second limit while the app was frozen could throw away the only copy of the new login, and the account dropped to "needs re-login" soon after. If a sign-in genuinely runs out of time, it now says Sign-in timed out instead of "try again in a moment". Engine detail: auth-login-contract.md token-requests-run-in-their-own-process.

How it works

The pure selection logic is in /src/shared/account-pool.ts: pickBestAccount() walks a pinned → primary-capacity → extra-capacity → api-key tier list and returns the first capable account, or null if every tier is empty. hasPrimaryCapacity() requires both the 5-hour and 7-day utilization under 100%; hasExtraCapacity() checks whether the account has extra-usage credits enabled with remaining balance; primaryScore() is max(5h, 7d) so a 99% 5-hour account loses to an 80% 7-day account even if its 7-day number is fresher. The allowApiKeySessionSpawn gate is enforced upstream, not inside pickBestAccount — the process manager calls filterAccountsForSpawn() from /src/main/process/api-key-guard.ts FIRST to drop api-key accounts when the setting is off, so the pool's api-key tier only sees accounts that are already eligible to spawn.

The process manager calls this on every spawn — a synchronous pickActiveAccount() (/src/main/process/account-selection.ts) reads the cached usage snapshot and kicks off a background refresh so the next spawn sees fresh numbers. Which picker it delegates to is decided per-call by isDistributeSessionsEnabled(getSettings()): ON → pickLoadBalancedAccount() in /src/shared/account-pool-load-balance.ts, which passes no pinnedAccountId and itself falls back to pickBestAccount when its candidate pool is empty; OFF (the default, or AMC_DISABLE_DISTRIBUTE_SESSIONS=1, the operator rollback kill switch) → pickBestAccount() and the pinned-first tier list above. If the pick returns null, the rate-limit recovery service (/src/main/services/rate-limit-recovery-service.ts) calls emitAllExhaustedAlert(), which always pushes the IPC event (so the banner shows instantly) but only triggers the OS notification when a 30-minute cooldown has elapsed. clearAllExhaustedState() fires the inverse push when any account recovers capacity, so the banner auto-hides globally without user action. countActionableSessions() tallies the queued/waiting count included in the banner copy ("N sessions waiting").

Rate-limited accounts drop out of the pool (2026-07-07). Separate from the 5h/7d usage tiers above: the moment an account is seen rate-limited — the usage-stats poll gets a 429 (the same "Rate limited (429)" state the account row shows), or a session on it hits its cap — Omniscio drops that account from the picker via the in-memory account breaker, and auto-returns it when the limit's window clears (the per-account Retry-After, capped 5 min). A rate-limited account is a strict skip: the picker never spawns onto it, even as a last resort. So when every account is rate-limited the pick returns null and the session parks (needs_you + rate_limited) and auto-resumes on the reset timer / next usage poll — instead of being shoved onto a limited account to 429 again. (This matters because a rate-limited account otherwise reads as unknown usage, which primaryScore treats optimistically as 0% "fresh" — so without both the breaker arming AND this strict skip, a rate-limited account would rank top of the pool and get preferred.) Engine detail + invariants: account-switch-contract.md.

Burst vs. cap — a healthy account isn't benched for hours (2026-07-22). A session-turn 429 on an account that still has usage headroom (both the 5-hour and 7-day meters under 100%) is a short-lived burst throttle — Anthropic's short-term request-rate limit from many requests at once (e.g. a subagent fan-out), NOT the usage cap the 429's far reset time describes. So Omniscio benches that account only ~60 seconds instead of until the far reset, and — the moment a usage poll re-confirms it has headroom — returns it to the pool early. That keeps an account with plenty of quota left from being stranded out of rotation for hours over a momentary throttle (the "sessions waiting for capacity while accounts are free" case). Genuinely capped accounts (a meter at 100%) still bench until their real reset. Both behaviors are kill-switched. Engine detail: recovery-storm-control-contract.md a-headroom-429-is-a-short-burst-bench / a-healthy-usage-poll-re-admits-early.

Fleet-wide meter throttle — a healthy account isn't benched when Anthropic throttles our usage polling (2026-07-23). Distinct from a per-account cap: Anthropic/Cloudflare can rate-limit Omniscio's own usage-meter poll (the lightweight call behind the percentage bars) across many accounts at once — a 429 that means "the meter is temporarily unreadable," not "this account is out of capacity." Benching accounts off that (as the 2026-07-07 rule did unconditionally) strands healthy accounts: on 2026-07-23 it benched ~30 accounts, one sitting at 3% usage, while real capacity was abundant. So when several accounts' polls get throttled together with no usable Retry-After, Omniscio recognizes a fleet-wide meter throttle, keeps those accounts in the pool, and shows a neutral "Usage temporarily unavailable" on the row instead of "Rate limited (429)" (the renderer also stops auto-retrying that state so it can't re-hammer the throttled endpoint). A single account's poll 429 — or one carrying a real reset time — still benches, so the 2026-07-07 crash-on-spawn signal is preserved. Kill-switched (AMC_DISABLE_USAGE_POLL_FLEET_THROTTLE_GUARD). Engine detail: recovery-storm-control-contract.md a-fleet-wide-throttle-does-not-bench.

On the renderer, push events land on IPC.ACCOUNT_POOL_EXHAUSTED (defined in /src/shared/ipc-channels/account.ts as 'account:pool-exhausted') with a Zod-validated payload { exhausted, at, trackedSessionCount, message }. The /src/renderer/src/stores/account-pool-exhausted-store.ts Zustand slice holds the state plus a dismissedAt timestamp; the selector selectIsExhaustedVisible() is true only when exhausted && at > dismissedAt. /src/renderer/src/hooks/useAccountPoolExhaustedAlert.ts mounts the listener and fires the toast only when the alert is fresh (at > previousAt) so a backend re-push doesn't re-toast. The banner itself is /src/renderer/src/features/dashboard/AccountPoolExhaustedBanner.tsx, mounted above the sidebar in /src/renderer/src/features/dashboard/SessionsSidebar.tsx so it's visible across every sidebar mode.

Full design record and invariants: account-pool-exhaustion-alert postmortem. Related: auto-account-switch postmortem covers the silent cross-account resume on rate-limit recovery.

Related

Last verified 2026-10-01