You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Replace stateless global round-robin failure handling with health-aware Codex auth selection, account-wide cooldowns, sticky-session failover, deterministic final errors, and the three-layer retry behavior proven by CLIProxyAPI (CPA).
Decisions
Health state
Auth health and cooldown apply globally to the auth across all sessions.
Request-scoped 4xx errors do not penalize an auth.
429 honors Retry-After or an explicit quota recovery time.
Network failures, 408, and retryable 5xx use short in-memory cooldowns.
Persist only authoritative or longer-lived states in SQLite: explicit 429 recovery deadlines, invalid_grant, and continued unauthorized state after refresh.
Clear credential-related persisted unavailability when auth material changes or a refresh/request proves the auth healthy; preserve unexpired quota/429 cooldowns across same-account token updates.
Model-not-supported errors are model capability exclusions, not auth-global cooldowns.
If every auth is blocked, do not force-select one.
Health, cooldown, affinity, and retry behavior are enabled by default with no legacy feature flags.
Retry layers
Same-auth repair: after 401, refresh once and retry the same auth once. This is not a credential-switch budget.
Credential failover: one execution round tries distinct currently eligible auths, limited by max-retry-credentials; 0 means all eligible auths.
Cooldown retry: after a round exhausts eligible auths, wait for the nearest cooldown only when it does not exceed max-retry-interval, then start another round up to request-retry times.
Waiting and retries stop immediately when the client context is canceled.
Do not retry after any downstream response bytes have been committed.
Do not retry after a WebSocket upgrade succeeds.
Do not repeatedly select the same auth for the same failure within one round.
Replay eligibility
Apply cross-auth retries only to an explicit replayable allowlist: /v1/chat/completions, HTTP Responses, Responses compact, alpha search, JSON image generation, trace summarization, read-only GET/HEAD, and WebSocket handshake before successful upgrade.
Do not retry multipart image edits, file upload/finalization, realtime calls/connections, side-effecting wham actions, hosted MCP tool calls, unknown non-read-only routes, or bodies that cannot be recreated safely.
Buffer at most 32 MiB for retry. A known body over the limit or an unknown/chunked body is still proxied once but is not eligible for automatic retry; do not spool replay bodies to disk and do not reject the request merely because replay is unavailable.
For allowlisted buffered requests, transient network errors may retry without distinguishing whether the request body had already been fully written. This intentionally favors availability and accepts a rare duplicate generation/billing risk.
Non-replayable requests still receive proactive token refresh and update auth health from their one upstream result.
Final error selection
Use deterministic final errors rather than whichever auth failed last:
Mixed quota and transient failures returns 429 when there is a clear near-term recovery deadline; otherwise 502.
Client cancellation does not synthesize a new proxy error.
Only safe aggregate errors are returned to clients. Per-auth details remain in safe logs and management status.
Acceptance Criteria
Selection skips auths in active global cooldown and preserves healthy session affinity.
401, 429, transient transport errors, retryable 5xx, model capability errors, and request-scoped 4xx produce the documented distinct state transitions.
Explicit upstream recovery deadlines survive proxy restarts; short transient cooldowns do not.
Restarting the proxy cannot bypass an unexpired persisted Retry-After deadline.
request-retry, max-retry-credentials, and max-retry-interval follow the three independent CPA-style meanings above.
Every allowed and denied replay route has regression coverage, including the 32 MiB boundary and unknown-length bodies.
Network retry tests document the accepted duplicate-execution tradeoff for eligible buffered requests.
Streaming and WebSocket tests prove no replay occurs after client-visible commitment or successful upgrade.
Final client status, safe error code, and Retry-After are deterministic under mixed multi-auth failures and independent of auth ordering.
Logs identify account IDs, health transitions, retry rounds, and safe cooldown reasons without exposing tokens or raw sensitive bodies.
English and Chinese configuration and architecture documentation remain synchronized.
Non-goals
CPA's generic multi-provider scheduler, plugin system, or management UI.
Distributed cooldown coordination between multiple COP processes.
Persistent storage for second-scale transport cooldowns.
Retrying request-invalid errors.
Disk spooling for retry bodies.
httptrace-based detection of ambiguous post-write transport failures.
Requirement
Replace stateless global round-robin failure handling with health-aware Codex auth selection, account-wide cooldowns, sticky-session failover, deterministic final errors, and the three-layer retry behavior proven by CLIProxyAPI (CPA).
Decisions
Health state
4xxerrors do not penalize an auth.401first uses the same-auth refresh-and-retry behavior from fix(auth): make OAuth refresh concurrent-safe and recoverable #4; continued unauthorized responses make that auth unavailable for selection.429honorsRetry-Afteror an explicit quota recovery time.408, and retryable5xxuse short in-memory cooldowns.429recovery deadlines,invalid_grant, and continued unauthorized state after refresh.429cooldowns across same-account token updates.Retry layers
401, refresh once and retry the same auth once. This is not a credential-switch budget.max-retry-credentials;0means all eligible auths.max-retry-interval, then start another round up torequest-retrytimes.Use these defaults:
Replay eligibility
/v1/chat/completions, HTTP Responses, Responses compact, alpha search, JSON image generation, trace summarization, read-onlyGET/HEAD, and WebSocket handshake before successful upgrade.Final error selection
Use deterministic final errors rather than whichever auth failed last:
404 model_not_found.429with the earliest known recovery time inRetry-After.503 auth_unavailable.502 upstream_unavailable.429when there is a clear near-term recovery deadline; otherwise502.Only safe aggregate errors are returned to clients. Per-auth details remain in safe logs and management status.
Acceptance Criteria
401,429, transient transport errors, retryable5xx, model capability errors, and request-scoped4xxproduce the documented distinct state transitions.Retry-Afterdeadline.request-retry,max-retry-credentials, andmax-retry-intervalfollow the three independent CPA-style meanings above.Retry-Afterare deterministic under mixed multi-auth failures and independent of auth ordering.Non-goals
httptrace-based detection of ambiguous post-write transport failures.Dependencies
Verification
gofmt -w .go test ./...go build -o test-output ./cmd/server && rm test-output