From a76e928f7bac7c52f5876d51a4526f9ed0cf2bec Mon Sep 17 00:00:00 2001 From: JUN Date: Thu, 15 Oct 2026 17:07:44 +0900 Subject: [PATCH 01/17] feat(accounts): opt in to auto-pause permanent account refusals --- .../docs/reference/cli/providers-accounts.md | 4 +- .../docs/reference/configuration/providers.md | 34 +++ .../components/codex-account-pool-cards.tsx | 2 +- .../provider-workspace/ProviderAuthPanel.tsx | 7 +- .../components/provider-workspace/types.ts | 3 + gui/src/hooks/useCodexAccountPool.ts | 1 + gui/src/hooks/useProviderAccountPools.ts | 10 +- .../provider-account-pause-refresh.test.tsx | 24 ++ scripts/test-layout/layout.json | 2 +- skills/ocx/references/01_surface_accounts.md | 10 +- src/adapters/google-http.ts | 5 +- src/adapters/upstream-http-error.ts | 5 +- src/cli/account-api.ts | 11 +- src/cli/account-extended.ts | 13 +- src/cli/account.ts | 2 +- src/cli/capabilities-base.ts | 10 +- src/codex/account-pause.ts | 7 +- src/codex/auth-api/account-list.ts | 3 + src/codex/auth-context.ts | 4 +- src/config/schema/account-auto-pause.ts | 14 + src/config/schema/config-schema.ts | 2 + src/config/schema/leaf-validators.ts | 3 + src/lib/account-auto-pause-response.ts | 12 + src/oauth/account-auto-pause-policy.ts | 66 +++++ src/oauth/account-auto-pause.ts | 128 ++++++++ src/oauth/anthropic-account-refusal.ts | 6 + src/oauth/store.ts | 22 +- src/oauth/types.ts | 2 + src/providers/api-key-pause.ts | 30 ++ src/providers/api-key-selection.ts | 3 +- src/providers/api-keys.ts | 12 +- src/providers/key-failover.ts | 14 +- src/providers/model-rename-fields.ts | 1 + src/server/auth-cors.ts | 1 + src/server/chat-native.ts | 26 +- src/server/management/oauth-account-routes.ts | 10 +- src/server/messages-native.ts | 15 +- .../responses/account-auto-pause-recovery.ts | 26 ++ src/server/responses/adapter-dispatch.ts | 23 +- src/server/responses/compact.ts | 13 +- src/server/responses/core-codex-account.ts | 8 +- src/server/responses/fetch-helpers.ts | 10 +- src/server/responses/passthrough-delivery.ts | 5 +- src/server/responses/passthrough-dispatch.ts | 62 ++-- .../responses/passthrough-observations.ts | 38 +++ src/server/responses/request-transport.ts | 19 ++ src/types/account-auto-pause.ts | 15 + src/types/config.ts | 1 + src/types/provider.ts | 5 +- structure/INDEX.md | 6 + structure/account-auto-pause.md | 71 +++++ structure/config.md | 2 +- structure/dashboard-and-usage.md | 2 +- structure/gui-and-management-api.md | 2 +- structure/manifest.json | 13 + structure/providers-and-adapters.md | 2 +- structure/providers/anthropic-account-pool.md | 9 + structure/providers/openai-accounts.md | 11 + structure/runtime.md | 2 +- structure/transports/responses-failover.md | 2 +- tests/fixtures/test-layout-expected.json | 2 +- tests/oauth/oauth-account-auto-pause.test.ts | 278 ++++++++++++++++++ .../responses-account-auto-pause.test.ts | 228 ++++++++++++++ 63 files changed, 1279 insertions(+), 100 deletions(-) create mode 100644 src/config/schema/account-auto-pause.ts create mode 100644 src/lib/account-auto-pause-response.ts create mode 100644 src/oauth/account-auto-pause-policy.ts create mode 100644 src/oauth/account-auto-pause.ts create mode 100644 src/providers/api-key-pause.ts create mode 100644 src/server/responses/account-auto-pause-recovery.ts create mode 100644 src/server/responses/passthrough-observations.ts create mode 100644 src/types/account-auto-pause.ts create mode 100644 structure/account-auto-pause.md create mode 100644 tests/oauth/oauth-account-auto-pause.test.ts create mode 100644 tests/responses/responses-account-auto-pause.test.ts diff --git a/docs-site/src/content/docs/reference/cli/providers-accounts.md b/docs-site/src/content/docs/reference/cli/providers-accounts.md index 27387b52951..1e9b7f034de 100644 --- a/docs-site/src/content/docs/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/reference/cli/providers-accounts.md @@ -734,7 +734,9 @@ Clear the manual Codex account selection without resolving an account id, so it ### `ocx account pause|resume [--json]` -Pause or resume one account in the Codex, Anthropic, or generic OAuth provider pool, including +Accounts paused by the opt-in [per-provider auto-pause policy](/reference/configuration/providers/#auto-pause-permanently-refused-accounts) use the same commands. Listing shows the fixed pause reason; resume clears it. + +Pause or resume one account in the Codex, Anthropic, generic OAuth, or API-key provider pool, including `google-antigravity`. For the Codex pool, `main` identifies only the built-in Codex account; OAuth accounts must be identified by id or a unique alias. A paused OAuth account is excluded from request selection, 429 failover, and proactive token refresh, and cannot be diff --git a/docs-site/src/content/docs/reference/configuration/providers.md b/docs-site/src/content/docs/reference/configuration/providers.md index 50c3520b0df..7c99ef4b09b 100644 --- a/docs-site/src/content/docs/reference/configuration/providers.md +++ b/docs-site/src/content/docs/reference/configuration/providers.md @@ -1730,3 +1730,37 @@ raised output allowance would exceed that ceiling. } } ``` + +## Auto-pause permanently refused accounts + +`providers..accountAutoPause` is opt-in and defaults to off. It applies to that +provider's OAuth accounts, Codex pool accounts (including the main account in pool mode), +and configured API-key pool entries. For example: + +```sh +ocx config set providers.anthropic.accountAutoPause.enabled true +ocx config set providers.anthropic.accountAutoPause.consecutiveFailures 2 +``` + +`consecutiveFailures` is an integer from 1 to 100; omitting it uses 2. Only exact, +account-scoped HTTP 401/403 refusals qualify: an organization forbidding OAuth, an inactive +or expired subscription, an account denied API/Claude Code access, or a downgrade to a free +plan with no credits. A model permission error, expired access token, quota window, 429, +5xx, malformed response or network failure never pauses an account. + +The counter is process-local, serialized per account, and resets on a successful HTTP +response, explicit resume or a new login. Token refresh alone does not reset an OAuth +account's streak. Transient errors leave the counter unchanged. Restarting the proxy clears +counters; completed pauses, their fixed reason codes and timestamps persist. Independent +proxy processes maintain independent counters. + +At the threshold, OpenCodex excludes the account using its existing pause mechanism and +attempts an eligible sibling for the current request within the existing retry/send budget. +Fixed Codex account selectors and account-bound uploaded files retain their restrictions. +A pause never cancels already dispatched work or changes credentials. It emits one generic +log line without account identifiers or upstream error text. + +`ocx account list ` shows the pause reason. The dashboard's Paused badge exposes +the reason and time. Use `ocx account resume ` to clear the +reason and allow selection again. On an API-key row, clicking Resume also activates the key. +This policy never resumes an account automatically. diff --git a/gui/src/components/codex-account-pool-cards.tsx b/gui/src/components/codex-account-pool-cards.tsx index e607afbf1bf..daa95e26e84 100644 --- a/gui/src/components/codex-account-pool-cards.tsx +++ b/gui/src/components/codex-account-pool-cards.tsx @@ -118,7 +118,7 @@ export function CodexAccountPoolCards({ )} {a.paused && ( - + {t("codexAuth.paused")} )} diff --git a/gui/src/components/provider-workspace/ProviderAuthPanel.tsx b/gui/src/components/provider-workspace/ProviderAuthPanel.tsx index da5419c975c..5f1d45863e1 100644 --- a/gui/src/components/provider-workspace/ProviderAuthPanel.tsx +++ b/gui/src/components/provider-workspace/ProviderAuthPanel.tsx @@ -588,7 +588,7 @@ export default function ProviderAuthPanel({ )} {showReauth && !healthLabel && {t("pws.reauth")}} {kiroSkipReasonKey(account, item.name) && {t(kiroSkipReasonKey(account, item.name)!)}} - {account.paused && {t("codexAuth.paused")}} + {account.paused && {t("codexAuth.paused")}} {account.active && !account.paused && {t("prov.accountActive")}} {switching && {t("pws.accountSwitching")}} @@ -721,13 +721,14 @@ export default function ProviderAuthPanel({
@@ -722,13 +724,13 @@ export default function ProviderAuthPanel({