Skip to content

[Feature]: complete the OAuth account-pool lifecycle — session affinity, 401/403 rotation, pool health, and stable reset-credit identity #3375

Description

@lidge-jun

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.ts implements 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 at src/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.ts provides 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?

  1. Keep a conversation on its selected account for the life of the session, with an explicit, configurable release condition, and fall over only when that account genuinely cannot serve.
  2. Treat 401 and provider-classified 403 as rotatable within the pool, with the credential marked unhealthy rather than the request failed.
  3. Expose aggregate pool health — capacity, per-account quota state, and which account served a request — in the dashboard and in request history.
  4. Accept a caller-supplied stable operation ID on the manual reset-credit endpoint and CLI, and open/settle that exact ID in the existing ledger so a retry is idempotent.
  5. Allow a durable one-shot warmup to be scheduled for a zero-usage account so its window starts predictably.

Example usage or interface

// config.json
{
  "providers": {
    "example": {
      "accountPool": {
        "strategy": "quota",
        "sessionAffinity": "conversation",       // pin a turn chain to one account
        "rotateOn": ["429", "401", "403:quota"]  // classes that rotate instead of failing
      }
    }
  }
}
# 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-0d2f7c8e5b31

Alternatives 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

  • I searched existing issues and documentation.
  • This request describes a concrete OpenCodex workflow rather than merely naming a desired technology.
  • I removed secrets and personal data.

Activity

  1. added
    enhancementNew feature or request
    account-poolOAuth, credentials, Codex pool, quota, failover, plans
    on Sep 3, 2026
  2. coderabbitai commented on Sep 3, 2026

    @coderabbitai
    Contributor
    ⚠️ Possible Duplicate Issue(s)

    📝 Issue Planner

    Check the box below or use the @coderabbitai plan command 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!

  3. lidge-jun commented on Sep 3, 2026

    @lidge-jun
    OwnerAuthor

    리뷰 · 우선순위 72 / 80

    이 이슈는 OAuth 계정 풀을 “고르고, 그 계정에 대화를 붙이고, 계정 하나가 죽어도 풀이 스스로 넘어가게” 만들려는 남은 작업을 한곳에 모은 것입니다. 지금 dev HEAD(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이 작성했습니다

  4. nordz0r commented on Sep 5, 2026

    @nordz0r
    Contributor

    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.ts DTO reports inert: true for strategy and autoSwitchThreshold, and src/types/provider.ts states 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 configured quota strategy 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> status derives enabled only from autoSwitchThreshold > 0 and prints auto-switch: on (or "enabled": true in JSON), while dropping the API's inert marker. 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 strategy and autoSwitchThreshold as 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 use affected 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.

  5. Ingwannu commented on Sep 9, 2026

    @Ingwannu
    Owner

    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.

  6. agentHits commented on Sep 16, 2026

    @agentHits
    Contributor

    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:

    1. 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.
    2. 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.
    3. 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 dev head 183119329, 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 the maintainer-sponsored label.
    4. 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-first rotation 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! 🚀

  7. agentHits commented on Sep 16, 2026

    @agentHits
    Contributor

    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)

    English Accounts Workspace

    2. 한국어 (KO - Korean)

    Korean Accounts Workspace

    3. Русский (RU - Russian)

    Russian Accounts Workspace

    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-first strategy selector directly in the toolbar.
    • Responsive 2-column card view with masked account identities.
  8. added
    priority: P2Medium: provider/client-specific bug with a workaround, bounded enhancement tied to a tracked issue,
    on Sep 24, 2026
  9. devin-ai-integration commented on Sep 24, 2026

    @devin-ai-integration
    Contributor

    Triage: 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:

  10. lidge-jun commented on Sep 27, 2026

    @lidge-jun
    OwnerAuthor

    Release train 4 progress on this umbrella:

    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 dev before this train.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    account-poolOAuth, credentials, Codex pool, quota, failover, plansenhancementNew feature or requestpriority: P2Medium: provider/client-specific bug with a workaround, bounded enhancement tied to a tracked issue,

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions