Repository navigation
[Feature]: complete the OAuth account-pool lifecycle — session affinity, 401/403 rotation, pool health, and stable reset-credit identity #3375
Description
Activity
- addedenhancementNew feature or requestNew feature or requestaccount-poolOAuth, credentials, Codex pool, quota, failover, plansOAuth, credentials, Codex pool, quota, failover, plans
on Sep 3, 2026 coderabbitai commented
on Sep 3, 2026 coderabbitaiboton Sep 3, 2026 – with coderabbitaiContributorMore actions⚠️ Possible Duplicate Issue(s)
📝 Issue Planner
Check the box below or use the
@coderabbitai plancommand to generate an implementation plan and prompts that you can use with your favorite coding assistant.- Create Plan
🧪 Issue enrichment is currently in open beta.
To disable automatic issue enrichment, add the following to your
.coderabbit.yaml:issue_enrichment: auto_enrich: enabled: false
💬 Have feedback or questions? Drop into our discord!
리뷰 · 우선순위 72 / 80
이 이슈는 OAuth 계정 풀을 “고르고, 그 계정에 대화를 붙이고, 계정 하나가 죽어도 풀이 스스로 넘어가게” 만들려는 남은 작업을 한곳에 모은 것입니다. 지금
devHEAD(664d80c76)에는 1차 구현이 이미 있습니다.src/oauth/generic-account-failover.ts는 파일 머리부터 “Generic OAuth multi-account 429 failover”이고, 429 쿨다운·로스터 순회·쿼터로 첫 계정을 고르는 일만 합니다. 주석이 직접 “Anthropic 풀보다 좁다. 세션 고정 없음”이라고 말합니다. 그래서 긴 대화가 중간에 다른 계정으로 새는 구멍이 그대로입니다. 401이나 제공자가 분류한 403은 풀 전체 실패로 끝나고, 옆에 멀쩡한 계정이 있어도 안 넘어갑니다.본문이 가리키는 경로도 현재
dev와 맞습니다. 계정별 쿼터는src/providers/quota.ts와gui/src/components/provider-workspace/ProviderAuthPanel.tsx에 보입니다. 그런데 풀 전체 용량 화면과 “이 요청이 어느 계정 탓인지” 표시는 없습니다. 수동 reset-credit 쪽은 본문이 인용한 원장 커밋이 이미 들어 있다고 하고, 남은 것은 재시도가 같은 operation id로 붙는 내구성입니다. 흡수된 #695, #1062, #1977, #2275는 모두 닫혀 있고, 이 이슈가 그 남은 범위를 이어 받습니다. types/config 분할 캠페인에 치여 닫을 항목이 아닙니다. 오히려 나중에accountPool.sessionAffinity/rotateOn을 설정에 넣으면src/types/config.ts를 만지게 되므로, 그 패치는 분할 이후에 맞추면 됩니다.왜 지금 우선순위가 높은가. 운영자가 지금 쓰는 우회는 “프록시 하나 = 계정 하나”입니다. 그러면 쿼터로 고르는 이득이 사라지고, 풀 건강도 안 보입니다. 이미 있는 429 회전을 401/403과 대화 고정까지 넓히는 일은 새 제공자를 또 넣는 것보다, 있는 풀을 실제로 쓸 수 있게 만드는 일에 가깝습니다. 본문 예시의
rotateOn: ["429", "401", "403:quota"]처럼 클래스별로 나눈 것은 중요합니다. 모든 403을 회전하면 권한 없는 계정이 무한히 다음 계정으로 넘어갈 수 있습니다. “쿼터/구독 게이트로 분류된 403만”이 안전한 기본값입니다.세션 고정은 Anthropic 경로(
src/oauth/anthropic-routing.ts)가 이미 가지고 있으니, generic 풀이 그걸 복사할지, 공통 헬퍼로 올릴지가 설계 분기입니다. 파일 주석이 이미 Anthropic이 affinity를 가진다고 가리키므로, 중복 구현보다 그 규칙을 generic으로 여는 쪽이 덜 흔들립니다. 풀 건강 UX는 GUI 작업이라 회전 로직과 따로 열 수 있습니다. reset-credit operation id는 CLI 계약이라 세 번째 조각입니다. 한 PR에 다 넣지 않는 편이 맞습니다.src/oauth/generic-account-failover.ts- 429만 회전하고 세션 고정이 없습니다. 주석이 그 한계를 이미 적고 있습니다.src/providers/quota.ts,gui/src/components/provider-workspace/ProviderAuthPanel.tsx- 계정별 쿼터는 보이지만 풀 합산·계정 귀속은 없습니다.본문 예시
rotateOn- 401/403을 전부 회전할지, 쿼터로 분류된 403만 회전할지 기본값을 정해야 합니다.흡수된 #695/#1062/#1977/#2275 - 닫혀 있고 이 이슈가 남은 범위입니다. 다시 열 필요 없습니다.
메인테이너의 판단이 필요한 지점
- 세션 고정을 Anthropic 규칙과 공유할지, generic 풀만 따로 둘지.
403:quota만 회전할지, 제공자 분류 403 전체를 회전할지.- 풀 건강 GUI와 reset-credit operation id를 같은 트레인에 넣을지, 이슈 체크리스트만 공유한 채 PR을 나눌지.
너의 추천
이 이슈를 열어 두고, 첫 PR은 generic failover에 대화 고정 + 401 및 쿼터-403 회전만 넣으세요. 풀 건강 화면과 reset-credit 내구성은 후속 PR로 나누는 것이 현재dev와 맞습니다. 흡수된 원본 이슈는 다시 열지 마세요.이 댓글은 grok-bot이 작성했습니다
Live v2.42.0 follow-up for the scope consolidated from #695.
Environment (redacted): Debian 12 / Kubernetes container, provider
google-antigravity, five stored OAuth accounts. The provider config contains:{ "oauthAccountFailover": { "enabled": true, "strategy": "quota", "autoSwitchThreshold": 25 } }The installed
src/oauth/pool-settings-capability.tsDTO reportsinert: trueforstrategyandautoSwitchThreshold, andsrc/types/provider.tsstates that the selector does not consume them. The effective generic path can use cached per-account quota headroom for its existing initial preference and can rotate after a 429, but neither the configuredquotastrategy nor the 25% threshold controls that selector.This is not a claim that reactive 429 failover is broken; I did not manufacture quota exhaustion. It confirms that proactive generic OAuth selection cannot currently be relied on to obey the persisted strategy/threshold.
There is also a CLI visibility gap:
ocx account auto-switch <generic-oauth-provider> statusderivesenabledonly fromautoSwitchThreshold > 0and printsauto-switch: on(or"enabled": truein JSON), while dropping the API'sinertmarker. That can make a declared-but-unused threshold look active.The closing comment on #695 says to report any scope lost during consolidation here. Could #3375 explicitly retain selector consumption of
strategyandautoSwitchThresholdas an observable outcome? Until that lands, the CLI/API should refuse those writes or clearly label the values as declared/experimental and not active.A separate HTTP 400
User location is not supported for the API useaffected every tested account and is tracked in #3467. It is not a quota/429 signal, and this comment does not request rotating accounts on that error.No account identifiers, emails, project IDs, tokens, IP addresses, or private infrastructure details are included.
Retaining the selector-consumption requirement here: current dev still explicitly marks generic OAuth strategy and autoSwitchThreshold as declared/inert. A stored 25% value must not be treated as proof that the selector enforces a 25% switch.
The CLI visibility part has a separate landed fix: #3797 merged on September 6, 2026. I rechecked current dev src/cli/account-extended.ts: generic JSON output keeps enabled:false, exposes poolEnabled separately, and carries inert; text says inactive/not applied, or unavailable when capability is unknown. tests/cli/cli-account-pool-verbs.test.ts covers status, writes, unset versus zero, and malformed/unknown capability responses. That source-level result does not tell us which code your v2.42.0 process is running, so it does not invalidate your observation.
Keeping #3375 open for actual selector consumption and the remaining lifecycle scope. Reactive 429 handling remains distinct; the separate region/policy 400 is not a signal to rotate through accounts. No duplicate CLI PR or live account-rotation probe is needed.
Hi @lidge-jun and @Ingwannu 👋
I wanted to share a quick update on my end regarding the progress towards completing the OAuth account pool lifecycle (#3375), as I have been actively working on this:
- PR fix(google): allow structured output for Gemini models on Cloud Code Assist #4670 (Merged): Structured output handling for Gemini CCA is landed on
dev. - PR feat(gui): redesign provider accounts workspace with adaptive grid, collapsible rails, and quota controls #4560 (UI Foundation - Ready for Review): Redesigned the Accounts workspace with a responsive 2-column layout, collapsible rails, and full quota visibility. All 4/4 readiness checks and CI gates are green.
- PR feat(oauth): rank Antigravity failover by Gemini vs Claude quota family #4676 (Quota Family Isolation - Ready for Review): Fully resolved all 4 consolidation points from the review (rebased on latest
devhead183119329, ported missing regression tests from feat(oauth): model-family aware quota headroom ranking and failover #4299 with attribution, classified GPT-OSS to the Claude family, and fixed all fixtures). All 22/22 unit tests pass. It is ready for themaintainer-sponsoredlabel. - PR fix(combos): allow single-target combo with waitForCooldownMs to retry after cooling #4824 (Single-Target Cooldown Recovery - Ready for Review): Fixed the issue where single-target combos failed immediately on transient 503/429 errors instead of honoring
waitForCooldownMs, eliminating session stalls in Codex.
Next Step: Full Account Pool Rotation (Part 2)
I have already fully developed and tested the complete, seamless multi-account pool rotation system in my local environment today. It includes:
- The
reset-firstrotation strategy (prioritizing accounts whose 7-day weekly allowance resets earliest, ensuring expiring quotas are consumed first). - Zero-downtime failover across Gemini and Claude model families.
- Toolbar rotation controls and interactive strategy explanations in the GUI.
Everything is running smoothly in my daily workflow without stalls or interruptions. As soon as you review and merge the foundational PRs (#4560, #4676, and #4824), I am ready to submit this final piece as a clean PR to share it with the whole OpenCodeX community! 🚀
- PR fix(google): allow structured output for Gemini models on Cloud Code Assist #4670 (Merged): Structured output handling for Gemini CCA is landed on
Here are live screenshots from my working local dashboard (
http://localhost:10100/#providers) with Google Antigravity accounts and real-time quota telemetry across three languages (with all logins and credentials masked for privacy):1. English (EN)
2. 한국어 (KO - Korean)
3. Русский (RU - Russian)
The workspace shows:
- Complete Antigravity account pool telemetry (13 accounts, live Claude/Gemini availability, real-time token pool estimates).
- Full 4-window quota bars (5-hour and 7-day allowances for both Gemini and Claude with exact reset countdowns).
- Account pool auto-rotation controls with the
reset-firststrategy selector directly in the toolbar. - Responsive 2-column card view with masked account identities.
- addedpriority: P2Medium: provider/client-specific bug with a workaround, bounded enhancement tied to a tracked issue,Medium: provider/client-specific bug with a workaround, bounded enhancement tied to a tracked issue,
on Sep 24, 2026 devin-ai-integration commented
on Sep 24, 2026 ContributorMore actionsTriage:
priority: P2— OAuth account-pool lifecycle umbrella (several PRs build on it).Criteria (P2): Medium: provider/client-specific bug with a workaround, bounded enhancement tied to a tracked issue, perf, or CI reliability.
Matched pull requests:
- feat(cursor): add capability-gated account pool kernel #3742 (related / partial) — feat(cursor): add capability-gated account pool kernel [
priority: P3] - feat(oauth): support 403/401 failover with persistent health store and dynamic scaling #5099 (related / partial) — feat(oauth): support 403/401 failover with persistent health store and dynamic scaling [
priority: P2] - feat(gui): unified provider workspace with pool controls and reset-first rotation #5408 (related / partial) — feat(gui): unified provider workspace with pool controls and reset-first rotation [
priority: P3] - fix(usage): split the attempt row on account rotation, for every pool #5752 (related / partial) — fix(usage): split the attempt row on account rotation, for every pool [
priority: P1] - fix(oauth): honour a stated Retry-After in the generic account pool #5756 (related / partial) — fix(oauth): honour a stated Retry-After in the generic account pool [
priority: P1]
- feat(cursor): add capability-gated account pool kernel #3742 (related / partial) — feat(cursor): add capability-gated account pool kernel [
Release train 4 progress on this umbrella:
- feat(oauth): pause and resume generic OAuth accounts (carries #6087) #6106 (
555ef68ac6) adds operator pause/resume for generic OAuth pools. - fix(oauth): recover Antigravity 401s on a sibling account (narrow #5099 slice) #6132 (
ae6b0ba913) adds Antigravity-only pre-output 401 recovery onto a sibling account.
Still open: session affinity for generic pools, selectors that use the declared strategy and threshold, pool health and attribution, scheduled warmup, and 403 rotation (see #5099). Stable reset-credit identity was already on
devbefore this train.- feat(oauth): pause and resume generic OAuth accounts (carries #6087) #6106 (



CLI truthfulness slice landed; pool epic remains open
#3797 (
a92835dbc6) makes inert generic-pool thresholds explicit in CLI status. This does not complete the broader account-pool lifecycle epic. Existing reset-operation identity work must not be counted as unimplemented solely from the older summary.Verified against dev
5759d9ea2f1e7281cdc01eb9628f2e0a123fb59c. The attribution record was added in #3811.Area
Authentication and account pool
What are you trying to accomplish?
Run OpenCodex against a pool of OAuth/subscription accounts and have it pick the right account, keep a conversation on that account, and recover from per-account failures without operator intervention.
This consolidates the surviving scope from four reports whose first tranche already landed. Each is closed individually and absorbed here so the remaining work stays visible in one place:
What prevents this today?
Partial implementations exist and work, but four concrete gaps remain.
Session affinity is not reusable.
src/oauth/generic-account-failover.tsimplements 429 cooldown, rotation, and quota-ranked initial selection (landed in 816f3a1). It does not keep a multi-turn conversation pinned to the account that started it, so a long session can drift across accounts mid-conversation.Failover classes are too narrow. Only 429 rotates. A 401 and a provider-classified 403 are terminal for the pool even when another account in the pool would succeed.
Pool-level health and attribution are missing. Per-account quota is surfaced (
src/providers/quota.ts,gui/src/components/provider-workspace/ProviderAuthPanel.tsx), but there is no aggregate pool capacity view and no per-account attribution of usage, so an operator cannot see which account is carrying the load or why a selection was made.Manual reset-credit retries still mint a new identity per request. The durable ledger landed (
src/codex/reset-credit-operation-ledger.ts, 7c68768), but the manual consume endpoint generates a fresh UUID per call atsrc/codex/auth-api.ts, so a retried manual reset is not recognized as the same logical operation the ledger was built to track.Zero-usage accounts have no durable warmup scheduling.
src/codex/warmup.tsprovides a manual warmup primitive only; there is no one-shot scheduled shadow request to pin an account's re-initialization window.What should OpenCodex do?
Example usage or interface
# retrying this must settle the SAME ledger operation, not open a second one ocx codex reset-credit --account me@example.com --operation-id 6f1c1f2e-0f2a-4a1e-9a1b-0d2f7c8e5b31Alternatives or workarounds
Operators currently work around this by running one account per proxy instance and load-balancing externally, which defeats quota-aware selection and makes pool health invisible.
Additional context
Landed prior work referenced above: 816f3a1 (generic account failover), 7c68768 (reset-credit operation ledger).
Absorbs #695, #1062, #1977, #2275. Credit for the original analysis belongs to @luwei1990, @agentHits, @dbc-hbin, and @luvs01.
Checks