From 5b36322ca5263e5147300e4a6545fa1df75af088 Mon Sep 17 00:00:00 2001 From: jun Date: Sat, 5 Sep 2026 04:58:26 +0900 Subject: [PATCH] docs: stop promising a 429 failover kill switch that no longer exists The configuration and CLI references said oauthAccountFailover.enabled: false turns rotation off, and that anthropicAccountPool.enabled gates 429 failover. Both were true when written and both stopped being true when reactive and proactive activation were split. Public docs that contradict the runtime are worse than missing docs: an operator reads them, concludes a rate limit is terminal, and enables an experimental pool to buy recovery they already have. Corrected across en plus seven translated locales, with a test pinning the claim so a nine-locale set cannot drift back one file at a time. --- .../fr/reference/cli/providers-accounts.md | 2 +- .../fr/reference/configuration/providers.md | 2 +- .../ja/reference/cli/providers-accounts.md | 2 +- .../ja/reference/configuration/providers.md | 2 +- .../ko/reference/cli/providers-accounts.md | 2 +- .../ko/reference/configuration/providers.md | 2 +- .../docs/reference/cli/providers-accounts.md | 3 +- .../docs/reference/configuration/providers.md | 25 +++++--- .../ru/reference/cli/providers-accounts.md | 2 +- .../ru/reference/configuration/providers.md | 2 +- .../tr/reference/cli/providers-accounts.md | 3 +- .../tr/reference/configuration/providers.md | 2 +- .../zh-cn/reference/cli/providers-accounts.md | 2 +- .../reference/configuration/providers.md | 2 +- .../zh-tw/reference/cli/providers-accounts.md | 2 +- .../reference/configuration/providers.md | 2 +- tests/docs-429-failover-claims.test.ts | 62 +++++++++++++++++++ 17 files changed, 94 insertions(+), 25 deletions(-) create mode 100644 tests/docs-429-failover-claims.test.ts diff --git a/docs-site/src/content/docs/fr/reference/cli/providers-accounts.md b/docs-site/src/content/docs/fr/reference/cli/providers-accounts.md index 9f865c7f0d..53c5b94b91 100644 --- a/docs-site/src/content/docs/fr/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/fr/reference/cli/providers-accounts.md @@ -149,7 +149,7 @@ sont ignorés à moins que `--all` soit présent. Avec un fournisseur, répertor La sortie destinée aux utilisateurs utilise `PROVIDER TYPE ID PLAN/LABEL PRIORITY STATUS` ; une ligne Codex sélectionnée manuellement porte la mention `selected`. `PRIORITY` est l'ordre de sélection Codex signé (`0` lorsqu'il n'est pas défini) et affiche `-` pour les lignes où l'ordre ne s'applique pas, comme les comptes OAuth et les clés API. Avec au moins deux comptes Kiro enregistrés et éligibles, par défaut une réponse 429 entraîne automatiquement une rotation vers un autre -compte, en privilégiant celui dont l'allocation restante connue est la plus élevée ; la rotation est activée par la présence de plusieurs comptes et peut être désactivée avec `oauthAccountFailover.enabled: false` ; `ocx account login kiro` ajoute les comptes au pool un par un. Un résultat vide est toujours un succès. `--json` +compte, en privilégiant celui dont l'allocation restante connue est la plus élevée ; la rotation est activée par la présence de plusieurs comptes et ne peut pas être désactivée — `oauthAccountFailover.enabled: false` refuse la préférence de compte avant envoi, pas la récupération après un 429 ; `ocx account login kiro` ajoute les comptes au pool un par un. Un résultat vide est toujours un succès. `--json` renvoie : ```text diff --git a/docs-site/src/content/docs/fr/reference/configuration/providers.md b/docs-site/src/content/docs/fr/reference/configuration/providers.md index ed6658c5e3..5948574eea 100644 --- a/docs-site/src/content/docs/fr/reference/configuration/providers.md +++ b/docs-site/src/content/docs/fr/reference/configuration/providers.md @@ -198,7 +198,7 @@ rotation automatique peut déclencher des restrictions du fournisseur. | Clé | Type | Par défaut | Description | | --- | --- | --- | --- | -| `anthropicAccountPool.enabled?` | `boolean` | `false` | Active l'affinité persistante et le basculement après une temporisation 429. | +| `anthropicAccountPool.enabled?` | `boolean` | `false` | Active l'affinité de session persistante et la sélection des nouvelles sessions basée sur l'usage. **Le basculement sur 429 n'est pas contrôlé ici** : il s'active dès que deux comptes utilisables sont enregistrés, comme pour tout autre fournisseur multi-identifiants, et ne peut pas être désactivé. | | `anthropicAccountPool.autoSwitchThreshold?` | `number` | `80` | Pour les nouvelles sessions, lorsque le compte actif atteint ce seuil, choisir la plus faible utilisation connue et mise en cache dans la fenêtre configurée. `0` désactive la sélection selon le quota. | | `anthropicAccountPool.strategy?` | `"quota" \| "round-robin" \| "fill-first"` | `"quota"` | Stratégie des nouvelles sessions ; `quota` classe les comptes selon la fenêtre définie par `quotaWindow`, par défaut les barres sur 5 heures, et `fill-first` évalue son seuil d'évacuation dans cette même fenêtre. | | `anthropicAccountPool.quotaWindow?` | `"five-hour" \| "weekly" \| "max-utilization"` | `"five-hour"` | Barre d'utilisation signalée par le fournisseur, mise en cache et utilisée pour la sélection selon l'utilisation. `five-hour` conserve le comportement actuel. `weekly` utilise la barre hebdomadaire et ignore les comptes dont la barre sur 5 heures est épuisée tant qu'un autre compte admissible reste disponible, mais y revient si aucun autre ne reste. `max-utilization` utilise la valeur connue la plus élevée et peut donc employer la barre sur 5 heures avant que la barre hebdomadaire soit disponible ; si aucune n'est connue, le compte suit l'ordre des utilisations inconnues. Les utilisations connues précèdent les inconnues, mais si tous les comptes admissibles sont inconnus, la sélection en renvoie tout de même un dans leur ordre admissible. Après le départage documenté par la plus faible utilisation sur 5 heures, une égalité exacte conserve cet ordre. Une session saine avec affinité n'est pas rééquilibrée de manière proactive. Pour l'affectation des nouvelles sessions et la reprise du routage après un remplacement admissible à la suite d'un 429, `quota` classe directement les candidats admissibles avec cette fenêtre ; `fill-first` avance dans un ordre stable selon le seuil et les règles d'épuisement de cette fenêtre ; `round-robin` l'ignore. Le délai de récupération, les limites de basculement et l'éligibilité de réauthentification restent des états locaux distincts. Les barres hebdomadaires ne sont connues qu'après leur interrogation dans la page Fournisseurs du tableau de bord. | diff --git a/docs-site/src/content/docs/ja/reference/cli/providers-accounts.md b/docs-site/src/content/docs/ja/reference/cli/providers-accounts.md index babd71c61f..f1b5ec4ff6 100644 --- a/docs-site/src/content/docs/ja/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/ja/reference/cli/providers-accounts.md @@ -113,7 +113,7 @@ Codex pool selection applies to the next request after clearing existing affinit ### `ocx account list [provider] [--json] [--all] [--quota [--refresh]]` -プロバイダーを使用しない場合、Codex プール、OAuth アカウント、および設定された API キー プールが一覧表示されます。 `--all` が存在しない限り、空のプロバイダーはスキップされます。プロバイダーを使用すると、その資格情報ファミリーのみがリストされます。人間の出力では `PROVIDER TYPE ID PLAN/LABEL PRIORITY STATUS` を使用します。手動で選択した Codex 行には `selected` というマークが付けられます。利用可能な Kiro アカウントが 2 つ以上保存されている場合、既定では 429 を受けると別のアカウントへ自動的に切り替え、既知の残り利用枠が最も多いアカウントを優先します。この切り替えはアカウントの存在によって有効になり、`oauthAccountFailover.enabled: false` で無効にできます。`ocx account login kiro` はアカウントを 1 件ずつプールへ追加します。結果が空であっても成功です。 `--json` は次を返します: +プロバイダーを使用しない場合、Codex プール、OAuth アカウント、および設定された API キー プールが一覧表示されます。 `--all` が存在しない限り、空のプロバイダーはスキップされます。プロバイダーを使用すると、その資格情報ファミリーのみがリストされます。人間の出力では `PROVIDER TYPE ID PLAN/LABEL PRIORITY STATUS` を使用します。手動で選択した Codex 行には `selected` というマークが付けられます。利用可能な Kiro アカウントが 2 つ以上保存されている場合、既定では 429 を受けると別のアカウントへ自動的に切り替え、既知の残り利用枠が最も多いアカウントを優先します。この切り替えはアカウントの存在によって有効になり、無効にはできません。`oauthAccountFailover.enabled: false` が断るのは 429 復旧ではなく、送信前にアカウントを選ぶ動作だけです。`ocx account login kiro` はアカウントを 1 件ずつプールへ追加します。結果が空であっても成功です。 `--json` は次を返します: ```text { accounts: AccountRow[], notes: string[] } diff --git a/docs-site/src/content/docs/ja/reference/configuration/providers.md b/docs-site/src/content/docs/ja/reference/configuration/providers.md index b837a57017..298c41f0f0 100644 --- a/docs-site/src/content/docs/ja/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ja/reference/configuration/providers.md @@ -165,7 +165,7 @@ affinity を維持します。これらの戦略は provider enforcement を回 |キー |タイプ |デフォルト |説明 | | --- | --- | --- | --- | -| `anthropicAccountPool.enabled?` | `boolean` | `false` |スティッキー アフィニティと 429 クールダウン フェイルオーバーを有効にします。 | +| `anthropicAccountPool.enabled?` | `boolean` | `false` | スティッキー セッション アフィニティと使用量に基づく新規セッション選択を有効にします。**429 フェイルオーバーはここでは制御しません**: 使用可能なアカウントが 2 つ以上あれば他の複数資格情報プロバイダーと同様に有効になり、無効にはできません。 | | `anthropicAccountPool.autoSwitchThreshold?` | `number` | `80` |新しいセッションでは、アクティブなアカウントがこのしきい値に達すると、設定した期間で既知のキャッシュ使用量が最も低いアカウントを選択します。 `0` はクォータ選択を無効にします。 | | `anthropicAccountPool.strategy?` | `"quota" \| "round-robin" \| "fill-first"` | `"quota"` |新しいセッション戦略。`quota` は `quotaWindow` で指定した期間(既定は 5 時間足)でアカウントを順位付けし、`fill-first` も同じ期間で使い切りのしきい値を判定します。 | | `anthropicAccountPool.quotaWindow?` | `"five-hour" \| "weekly" \| "max-utilization"` | `"five-hour"` |使用量ベースのアカウント選択で使う、プロバイダー報告のキャッシュ済み使用率です。`five-hour` は従来の動作を維持します。`weekly` は週次使用量を使い、他に対象アカウントが残る間だけ 5 時間使用量が上限に達したアカウントを除外し、残らない場合はそれらへフォールバックします。`max-utilization` は判明している値のうち最も高いものを使うため、週次使用量が未取得でも 5 時間使用量を利用できます。どちらも不明なら unknown の順位付けに従います。既知の使用量は unknown より先ですが、対象がすべて unknown でも対象順の先頭を選択します。記載した 5 時間使用量による同点判定後も完全に同点なら、対象順を維持します。正常な affinity セッションを先回りして再配置することはありません。新規セッションの割り当てと、対象となる 429 代替後のルーティング復旧では、`quota` はこの期間で対象候補を直接順位付けし、`fill-first` はこの期間のしきい値と上限到達ルールを使って安定順に進み、`round-robin` はこの設定を無視します。クールダウン、フェイルオーバー上限、再認証の適格性は別のローカル状態です。アカウント別の週次使用量は、ダッシュボードのプロバイダーページで取得した後にのみ利用できます。 | diff --git a/docs-site/src/content/docs/ko/reference/cli/providers-accounts.md b/docs-site/src/content/docs/ko/reference/cli/providers-accounts.md index 67f4562617..240ec19edc 100644 --- a/docs-site/src/content/docs/ko/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/ko/reference/cli/providers-accounts.md @@ -113,7 +113,7 @@ Codex pool selection applies to the next request after clearing existing affinit ### `ocx account list [provider] [--json] [--all] [--quota [--refresh]]` -제공자를 지정하지 않으면 Codex 풀, OAuth 계정, 설정된 API 키 풀을 나열합니다. `--all`이 없으면 비어 있는 제공자는 건너뜁니다. 제공자를 지정하면 해당 자격 증명 계열만 나열합니다. 사람이 보는 출력은 `PROVIDER TYPE ID PLAN/LABEL PRIORITY STATUS` 형식을 사용하며, 수동으로 선택한 Codex 행에는 `selected`가 표시됩니다. 사용 가능한 Kiro 계정이 두 개 이상 저장되어 있으면 기본적으로 429 응답 시 다른 계정으로 자동 전환하며, 알려진 잔여 할당량이 가장 많은 계정을 우선합니다. 이 전환은 계정 존재만으로 활성화되며 `oauthAccountFailover.enabled: false`로 끌 수 있습니다. `ocx account login kiro`는 계정을 한 번에 하나씩 풀에 추가합니다. 빈 결과도 성공입니다. `--json`은 다음을 반환합니다: +제공자를 지정하지 않으면 Codex 풀, OAuth 계정, 설정된 API 키 풀을 나열합니다. `--all`이 없으면 비어 있는 제공자는 건너뜁니다. 제공자를 지정하면 해당 자격 증명 계열만 나열합니다. 사람이 보는 출력은 `PROVIDER TYPE ID PLAN/LABEL PRIORITY STATUS` 형식을 사용하며, 수동으로 선택한 Codex 행에는 `selected`가 표시됩니다. 사용 가능한 Kiro 계정이 두 개 이상 저장되어 있으면 기본적으로 429 응답 시 다른 계정으로 자동 전환하며, 알려진 잔여 할당량이 가장 많은 계정을 우선합니다. 이 전환은 계정 존재만으로 활성화되며 끌 수 없습니다. `oauthAccountFailover.enabled: false`는 429 복구가 아니라 요청을 보내기 전 계정을 고르는 동작만 거부합니다. `ocx account login kiro`는 계정을 한 번에 하나씩 풀에 추가합니다. 빈 결과도 성공입니다. `--json`은 다음을 반환합니다: ```text { accounts: AccountRow[], notes: string[] } diff --git a/docs-site/src/content/docs/ko/reference/configuration/providers.md b/docs-site/src/content/docs/ko/reference/configuration/providers.md index 877085782d..81cd158268 100644 --- a/docs-site/src/content/docs/ko/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ko/reference/configuration/providers.md @@ -169,7 +169,7 @@ affinity 초기화 뒤의 기존 작업도 포함될 수 있습니다. 출력 | 키 | 타입 | 기본값 | 설명 | | --- | --- | --- | --- | -| `anthropicAccountPool.enabled?` | `boolean` | `false` | sticky 결속과 429 쿨다운 failover를 켭니다. | +| `anthropicAccountPool.enabled?` | `boolean` | `false` | sticky 세션 결속과 사용량 기반 새 세션 선택을 켭니다. **429 failover는 여기서 제어되지 않습니다**: 쓸 수 있는 계정이 둘 이상이면 다른 다중 자격 증명 제공자와 똑같이 자동으로 켜지며, 끌 수 없습니다. | | `anthropicAccountPool.autoSwitchThreshold?` | `number` | `80` | 새 세션에서는 활성 계정이 이 임계값에 도달하면 설정된 창의 알려진 캐시 사용량이 가장 낮은 계정을 고릅니다. `0`이면 quota 선택을 끕니다. | | `anthropicAccountPool.strategy?` | `"quota" \| "round-robin" \| "fill-first"` | `"quota"` | 새 세션 전략입니다. `quota`는 `quotaWindow`로 지정한 창(기본값은 5시간 막대)으로 계정 순위를 매기고, `fill-first`도 같은 창에서 소진 임계값을 판정합니다. | | `anthropicAccountPool.quotaWindow?` | `"five-hour" \| "weekly" \| "max-utilization"` | `"five-hour"` | 사용량 기반 계정 선택에 사용하는, 공급자가 보고한 캐시 사용률 막대입니다. `five-hour`는 기존 동작을 유지합니다. `weekly`는 주간 막대를 사용하며 다른 사용 가능한 계정이 남아 있을 때만 5시간 막대가 소진된 계정을 건너뛰고, 아무 계정도 남지 않으면 해당 계정으로 폴백합니다. `max-utilization`은 알려진 값 중 가장 높은 값을 사용하므로 주간 사용량을 알기 전에도 5시간 사용량을 쓸 수 있고, 둘 다 모르면 unknown 순서를 따릅니다. 알려진 사용량은 unknown보다 앞서지만, 사용 가능한 계정이 모두 unknown이어도 사용 가능한 순서의 계정을 선택합니다. 앞서 설명한 5시간 사용량 동점 판정 뒤에도 완전히 같으면 사용 가능한 순서를 유지합니다. 정상 affinity 세션을 선제적으로 재배치하지 않습니다. 새 세션 배정과 가능한 429 대체 이후 라우팅 복구에서 `quota`는 이 창으로 사용 가능한 후보의 순위를 직접 매기고, `fill-first`는 이 창의 임계값과 소진 규칙에 따라 안정 순서로 이동하며, `round-robin`은 이 설정을 무시합니다. 쿨다운, failover 한도, 재인증 가능 여부는 별도의 로컬 상태로 유지됩니다. 계정별 주간 막대는 대시보드의 프로바이더 페이지에서 조회한 뒤에만 알 수 있습니다. | 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 98b5972bf6..a343c01d71 100644 --- a/docs-site/src/content/docs/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/reference/cli/providers-accounts.md @@ -148,7 +148,8 @@ Human output uses `PROVIDER TYPE ID PLAN/LABEL PRIORITY STATUS`; a manually chos `selected`. `PRIORITY` is the signed Codex selection order (`0` when unset) and shows `-` for rows where ordering does not apply, such as OAuth accounts and API keys. By default, with two or more eligible stored Kiro accounts, a 429 rotates automatically to another account and prefers the one with the most known remaining allowance; rotation is -presence-driven and can be turned off with `oauthAccountFailover.enabled: false`; `ocx account login kiro` +presence-driven and cannot be turned off — `oauthAccountFailover.enabled: false` declines the +pre-dispatch account preference, not 429 recovery; `ocx account login kiro` adds accounts to the pool one at a time. An empty result is still success. `--json` returns: diff --git a/docs-site/src/content/docs/reference/configuration/providers.md b/docs-site/src/content/docs/reference/configuration/providers.md index cfd5135ae9..8618dd1522 100644 --- a/docs-site/src/content/docs/reference/configuration/providers.md +++ b/docs-site/src/content/docs/reference/configuration/providers.md @@ -352,7 +352,7 @@ rotation may trigger provider restrictions. | Key | Type | Default | Description | | --- | --- | --- | --- | -| `anthropicAccountPool.enabled?` | `boolean` | `false` | Enable sticky affinity and 429 cooldown failover. | +| `anthropicAccountPool.enabled?` | `boolean` | `false` | Enable sticky session affinity and quota-ranked new-session selection. **429 failover is not gated here**: it activates whenever two or more usable accounts are stored, exactly like every other multi-credential provider, and cannot be switched off. | | `anthropicAccountPool.autoSwitchThreshold?` | `number` | `80` | For new sessions, when the active account reaches this threshold, choose the lowest known cached usage in the configured window; the account chosen does not itself have to be at or above the threshold. `0` disables **proactive** usage-based switching only — new-session selection and routing recovery after an eligible 429 still consult `quotaWindow`. | | `anthropicAccountPool.strategy?` | `"quota" \| "round-robin" \| "fill-first"` | `"quota"` | New-session strategy; `quota` ranks accounts by the window set by `quotaWindow`, and `fill-first` evaluates its drain threshold in that same window. | | `anthropicAccountPool.quotaWindow?` | `"five-hour" \| "weekly" \| "max-utilization"` | `"five-hour"` | The cached provider-reported utilization bar used for usage-aware account selection. `five-hour` keeps the original behavior. `weekly` scores the weekly bar and skips accounts whose 5-hour bar is exhausted while another eligible account remains, but falls back to exhausted candidates when none do. `max-utilization` scores the highest known bar, so it can use 5-hour usage before weekly usage is available; if neither is known, the account follows unknown-usage ordering. Known usage ranks before unknown usage under the opt-in `weekly` and `max-utilization` windows only; an omitted or explicit `five-hour` preserves the legacy ordering. If every eligible account is unknown, selection still returns one in eligible order. After the documented lower-5-hour tie-break, exact ties preserve eligible order. A healthy affinity-bound session is not proactively rebalanced. For new-session assignment and routing recovery after an eligible 429 replacement, `quota` ranks eligible candidates directly with this window; `fill-first` advances in stable order using this window's threshold and exhaustion rules; `round-robin` ignores it. Cooldown, failover limits, and reauthentication eligibility remain separate local state. Per-account weekly bars are only known once the dashboard Providers page has polled them. | @@ -374,19 +374,25 @@ Rotates to another logged-in account of the same provider when one is rate-limit providers that have no pool of their own — xAI, Cursor, Kimi, GitHub Copilot, Google Antigravity, and Nous. -**Logging in a second account is what turns this on.** With no configuration, rotation activates -for any of those providers holding 2 or more accounts that are not flagged for reauthentication — -the same rule `apiKeyPool` already applies to a 2+ key pool. A provider with one stored account -behaves exactly as before. +**Logging in a second account is what turns this on, and nothing turns it off.** Rotation +activates for any of those providers holding 2 or more accounts that are not flagged for +reauthentication — the same rule `apiKeyPool` already applies to a 2+ key pool. A provider with +one stored account behaves exactly as before. + +Rotation here runs only *after* upstream has already refused the request, so the only choice a +disable switch could offer is between retrying on a second account you deliberately logged in and +returning a 429 while that account sits idle. Refusing rotation is expressed by not storing a +second account. | Key | Type | Default | Description | | --- | --- | --- | --- | -| `oauthAccountFailover.enabled?` | `boolean` | presence-driven | Global override. `false` forces single-account behaviour everywhere; `true` forces rotation on. | -| `providers..oauthAccountFailover.enabled?` | `boolean` | inherits | Per-provider override; beats the global setting and beats account presence. | +| `oauthAccountFailover.enabled?` | `boolean` | presence-driven | Global override for the **pre-dispatch account preference** only. `false` stops a healthy request being steered toward the account with more known headroom. It does **not** disable 429 rotation. | +| `providers..oauthAccountFailover.enabled?` | `boolean` | inherits | Per-provider override for the same preference; beats the global setting. Only `false` is meaningful — `true` adds nothing over account presence. | | `providers..oauthAccountFailover.strategy?` | `"quota" \| "round-robin" \| "fill-first"` | — | Declared pool strategy for a generic OAuth provider (#695). Persisted through `ocx account strategy ` or `PUT /api/oauth/accounts/pool`; the generic selector does not act on it yet, so omitted and set behave the same today. | | `providers..oauthAccountFailover.autoSwitchThreshold?` | `number` | — | Declared 0–100 usage percent for a proactive switch on a generic OAuth provider (#695). Set with `ocx account auto-switch threshold `; inert until the selector consumes it. | -To keep strict single-account behaviour for one provider whose terms you would rather not test: +To decline proactive account steering for one provider whose terms you would rather not test, +while still recovering from a rate limit: ```json { @@ -404,7 +410,8 @@ Generic OAuth providers (Google Antigravity, xAI, Cursor, Kimi, GitHub Copilot, other OAuth provider outside the Codex and Anthropic pools) also accept `strategy` and `autoSwitchThreshold` on the same key, through `GET`/`PUT /api/oauth/accounts/pool?provider=` and the `ocx account strategy` / `ocx account auto-switch` verbs. The response carries -`"inert": true` while the generic selector ignores those two fields; `stickyLimit` and +`"inert": true` for those two fields only — `enabled` is live and governs the pre-dispatch +preference. `stickyLimit` and `quotaWindow` are not part of the generic contract. Codex (`/api/codex-auth`) and Anthropic (`anthropicAccountPool`) keep their own contracts unchanged. diff --git a/docs-site/src/content/docs/ru/reference/cli/providers-accounts.md b/docs-site/src/content/docs/ru/reference/cli/providers-accounts.md index 1dd33a4da4..3ad8e527f7 100644 --- a/docs-site/src/content/docs/ru/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/ru/reference/cli/providers-accounts.md @@ -138,7 +138,7 @@ label и masked key. Пустые провайдеры пропускаются, если не задан `--all`. С провайдером выводится только это семейство credential'ов. Human-output использует формат `PROVIDER TYPE ID PLAN/LABEL PRIORITY STATUS`; строка Codex, выбранная вручную, помечается `selected`. -При наличии двух или более подходящих сохранённых аккаунтов Kiro по умолчанию ответ 429 автоматически переключает запрос на другой аккаунт, предпочитая аккаунт с наибольшим известным остатком лимита; ротация включается самим наличием аккаунтов и отключается через `oauthAccountFailover.enabled: false`; `ocx account login kiro` добавляет аккаунты в пул по одному. Пустой результат всё равно считается успехом. +При наличии двух или более подходящих сохранённых аккаунтов Kiro по умолчанию ответ 429 автоматически переключает запрос на другой аккаунт, предпочитая аккаунт с наибольшим известным остатком лимита; ротация включается самим наличием аккаунтов и не отключается — `oauthAccountFailover.enabled: false` отклоняет предварительный выбор аккаунта, а не восстановление после 429; `ocx account login kiro` добавляет аккаунты в пул по одному. Пустой результат всё равно считается успехом. `--json` возвращает: ```text diff --git a/docs-site/src/content/docs/ru/reference/configuration/providers.md b/docs-site/src/content/docs/ru/reference/configuration/providers.md index ce41f34940..b07ea3b8bc 100644 --- a/docs-site/src/content/docs/ru/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ru/reference/configuration/providers.md @@ -200,7 +200,7 @@ reauth или порога исчерпания; здоровые привяза | Ключ | Тип | По умолчанию | Описание | | --- | --- | --- | --- | -| `anthropicAccountPool.enabled?` | `boolean` | `false` | Включить sticky affinity и cooldown failover на 429. | +| `anthropicAccountPool.enabled?` | `boolean` | `false` | Включить закрепление сессии и выбор новой сессии по использованию. **Переключение при 429 здесь не управляется**: оно работает, как только сохранено два и более пригодных аккаунта, как и у любого другого провайдера с несколькими учётными данными, и не отключается. | | `anthropicAccountPool.autoSwitchThreshold?` | `number` | `80` | Для новых сессий выбирать аккаунт с наименьшим известным cached usage в настроенном окне, если активный аккаунт достиг порога. `0` отключает выбор по quota. | | `anthropicAccountPool.strategy?` | `"quota" \| "round-robin" \| "fill-first"` | `"quota"` | Стратегия для новых сессий; `quota` ранжирует аккаунты по окну, заданному в `quotaWindow` (по умолчанию это 5-hour bar'ы), а `fill-first` в этом же окне оценивает свой порог исчерпания. | | `anthropicAccountPool.quotaWindow?` | `"five-hour" \| "weekly" \| "max-utilization"` | `"five-hour"` | Кешированная полоса использования, сообщённая провайдером и применяемая при выборе по использованию. `five-hour` сохраняет прежнее поведение. `weekly` использует недельный bar и пропускает аккаунты с исчерпанным 5-hour bar, пока остаётся другой доступный аккаунт, но возвращается к ним, если других нет. `max-utilization` использует наибольшее известное значение, поэтому до появления недельных данных может использовать 5-hour usage; если неизвестны оба значения, аккаунт следует порядку unknown usage. Известное использование ранжируется раньше unknown, но если у всех доступных аккаунтов оно неизвестно, выбирается аккаунт в доступном порядке. После описанного сравнения по меньшему значению 5-hour usage полное равенство также сохраняет этот порядок. Здоровая сессия с affinity не перебалансируется заранее. При назначении новой сессии и восстановлении маршрутизации после допустимой замены при 429 `quota` напрямую ранжирует доступных кандидатов по этому окну, `fill-first` идёт в стабильном порядке с учётом порога и правил исчерпания этого окна, а `round-robin` игнорирует настройку. Cooldown, лимиты failover и допустимость повторной аутентификации остаются отдельным локальным состоянием. Недельные bar'ы аккаунтов известны только после опроса на странице Providers в dashboard. | diff --git a/docs-site/src/content/docs/tr/reference/cli/providers-accounts.md b/docs-site/src/content/docs/tr/reference/cli/providers-accounts.md index d9881286b3..9c2e9378b7 100644 --- a/docs-site/src/content/docs/tr/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/tr/reference/cli/providers-accounts.md @@ -164,7 +164,7 @@ bir Codex satırı `selected` olarak işaretlenir. `PRIORITY`, imzalı Codex se sırasıdır (ayarlanmadığında `0`) ve OAuth hesapları ve API anahtarları gibi sıralamanın geçerli olmadığı satırlar için `-` gösterir. İki veya daha fazla uygun Kiro hesabı saklandığında, varsayılan olarak 429 yanıtı otomatik olarak başka bir hesaba geçer ve bilinen kalan -kotası en yüksek hesabı tercih eder; rotasyon hesapların varlığıyla etkinleşir ve `oauthAccountFailover.enabled: false` ile kapatılabilir; `ocx account login kiro` hesapları havuza teker teker ekler. Boş bir sonuç +kotası en yüksek hesabı tercih eder; rotasyon hesapların varlığıyla etkinleşir ve kapatılamaz — `oauthAccountFailover.enabled: false` gönderim öncesi hesap tercihini reddeder, 429 kurtarmasını değil; `ocx account login kiro` hesapları havuza teker teker ekler. Boş bir sonuç yine de başarıdır. `--json` şunu döndürür: ```text @@ -450,4 +450,3 @@ kataloğu reddeder, bu nedenle `add`, `edit` ve yönetim API'si katalog yazıcısının daha sonra çıkarması gereken bir şeyi saklamak yerine hatalı değeri reddeder (#759). - diff --git a/docs-site/src/content/docs/tr/reference/configuration/providers.md b/docs-site/src/content/docs/tr/reference/configuration/providers.md index b84e783c22..c90ffc8dd5 100644 --- a/docs-site/src/content/docs/tr/reference/configuration/providers.md +++ b/docs-site/src/content/docs/tr/reference/configuration/providers.md @@ -224,7 +224,7 @@ ve otomatik rotasyon sağlayıcı kısıtlamalarını tetikleyebilir. | Anahtar | Tip | Varsayılan | Açıklama | | --- | --- | --- | --- | -| `anthropicAccountPool.enabled?` | `boolean` | `false` | Yapışkan bağlılığı ve 429 soğuma yük devretmesini etkinleştirin. | +| `anthropicAccountPool.enabled?` | `boolean` | `false` | Yapışkan oturum bağlılığını ve kullanıma dayalı yeni oturum seçimini etkinleştirir. **429 yük devretmesi buradan kontrol edilmez**: iki veya daha fazla kullanılabilir hesap saklandığında, diğer çok kimlikli sağlayıcılarda olduğu gibi devreye girer ve kapatılamaz. | | `anthropicAccountPool.autoSwitchThreshold?` | `number` | `80` | Yeni oturumlarda etkin hesap bu eşiğe ulaştığında, yapılandırılan penceredeki bilinen en düşük önbelleğe alınmış kullanımı seçin. `0` kota seçimini devre dışı bırakır. | | `anthropicAccountPool.strategy?` | `"quota" \| "round-robin" \| "fill-first"` | `"quota"` | Yeni oturum stratejisi; `quota`, `quotaWindow` ile belirlenen pencereye (varsayılan 5 saatlik çubuklar) göre hesapları sıralar ve `fill-first` de tükenme eşiğini aynı pencerede değerlendirir. | | `anthropicAccountPool.quotaWindow?` | `"five-hour" \| "weekly" \| "max-utilization"` | `"five-hour"` | Kullanıma dayalı hesap seçiminde kullanılan, sağlayıcının bildirdiği önbelleğe alınmış kullanım çubuğu. `five-hour` mevcut davranışı korur. `weekly` haftalık çubuğu kullanır ve başka uygun hesap kaldığı sürece 5 saatlik çubuğu tükenmiş hesapları atlar; hiçbiri kalmazsa bu hesaplara geri döner. `max-utilization` bilinen en yüksek değeri kullanır; haftalık değer henüz yokken 5 saatlik değeri kullanabilir, ikisi de bilinmiyorsa hesap unknown kullanım sırasını izler. Bilinen kullanım unknown değerlerden önce gelir; tüm uygun hesaplar unknown olsa bile uygun sıradaki bir hesap seçilir. Belgelenen daha düşük 5 saatlik kullanım eşitlik bozmasından sonra tam eşitlikte de uygun sıra korunur. Sağlıklı affinity oturumları önceden yeniden dengelenmez. Yeni oturum ataması ve uygun bir 429 yedeğine geçildikten sonraki yönlendirme kurtarmasında `quota`, uygun adayları doğrudan bu pencereye göre sıralar; `fill-first`, bu pencerenin eşik ve tükenme kurallarıyla kararlı sırada ilerler; `round-robin` ayarı yok sayar. Cooldown, yük devretme sınırları ve yeniden kimlik doğrulama uygunluğu ayrı yerel durum olarak kalır. Hesap başına haftalık çubuklar ancak dashboard Sağlayıcılar sayfasında sorgulandıktan sonra bilinir. | diff --git a/docs-site/src/content/docs/zh-cn/reference/cli/providers-accounts.md b/docs-site/src/content/docs/zh-cn/reference/cli/providers-accounts.md index 984ac8c2ec..9601b0766b 100644 --- a/docs-site/src/content/docs/zh-cn/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/zh-cn/reference/cli/providers-accounts.md @@ -126,7 +126,7 @@ OAuth 账号会显示为 `Account N`,而 plan/label 列会在 plan、屏蔽后 不指定提供方时,会列出 Codex 池、OAuth 账号和已配置的 API 密钥池。除非提供 `--all`,否则会跳过空的提供方。指定提供方时,只列出该凭据家族。人类可读输出 使用 `PROVIDER TYPE ID PLAN/LABEL PRIORITY STATUS`;手动选中的 Codex 行会标记为 `selected`。 -当存有两个或更多符合条件的 Kiro 账号时,默认情况下 429 会自动轮换到另一个账号,并优先选择已知剩余额度最多的账号;轮换由账号存在与否驱动,可通过 `oauthAccountFailover.enabled: false` 关闭。`ocx account login kiro` 每次向池中添加一个账号。空结果仍然算成功。`--json` 返回: +当存有两个或更多符合条件的 Kiro 账号时,默认情况下 429 会自动轮换到另一个账号,并优先选择已知剩余额度最多的账号;轮换由账号存在与否驱动,且无法关闭;`oauthAccountFailover.enabled: false` 拒绝的是发送前的账号优选,而非 429 恢复。`ocx account login kiro` 每次向池中添加一个账号。空结果仍然算成功。`--json` 返回: ```text { accounts: AccountRow[], notes: string[] } diff --git a/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md b/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md index d3cc8d9c14..f3ca5d2ebd 100644 --- a/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md +++ b/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md @@ -163,7 +163,7 @@ affinity。这些策略不能规避 provider enforcement。 | 键 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| `anthropicAccountPool.enabled?` | `boolean` | `false` | 启用粘性亲和性和 429 冷却故障转移。 | +| `anthropicAccountPool.enabled?` | `boolean` | `false` | 启用粘性会话亲和性与基于用量的新会话选择。**429 故障转移不由此开关控制**:只要存有两个及以上可用账号就会生效,与其他多凭据提供方一致,且无法关闭。 | | `anthropicAccountPool.autoSwitchThreshold?` | `number` | `80` | 对于新会话,当活动账户达到此阈值时,选择配置窗口中已知缓存使用率最低的账户。`0` 会禁用配额选择。 | | `anthropicAccountPool.strategy?` | `"quota" \| "round-robin" \| "fill-first"` | `"quota"` | 新会话策略;`quota` 按 `quotaWindow` 指定的窗口(默认是 5 小时条形数据)对账户排序,`fill-first` 也在同一窗口中判定其耗尽阈值。 | | `anthropicAccountPool.quotaWindow?` | `"five-hour" \| "weekly" \| "max-utilization"` | `"five-hour"` | 基于用量选择账户时使用的、由提供商报告并缓存的用量条。`five-hour` 保持原有行为。`weekly` 使用每周用量条,并在仍有其他可用账户时跳过 5 小时用量已耗尽的账户;若没有其他账户,则回退使用这些账户。`max-utilization` 使用已知值中的最高值,因此每周用量尚不可用时仍可使用 5 小时用量;两者都未知时,账户遵循 unknown 用量排序。已知用量排在 unknown 之前,但如果所有可用账户都未知,仍会按可用顺序选择一个账户。在前述较低 5 小时用量的同分判定之后,完全相同时也保留可用顺序。不会主动重新平衡健康且已建立亲和性的会话。在新会话分配和符合条件的 429 替代后的路由恢复中,`quota` 直接按此窗口对可用候选账户排序;`fill-first` 按此窗口的阈值和耗尽规则以稳定顺序前进;`round-robin` 忽略此设置。冷却状态、故障转移上限和重新认证资格仍是独立的本地状态。各账户的每周用量只有在控制面板的提供商页面完成查询后才可用。 | diff --git a/docs-site/src/content/docs/zh-tw/reference/cli/providers-accounts.md b/docs-site/src/content/docs/zh-tw/reference/cli/providers-accounts.md index 33722358f3..d8d45133b6 100644 --- a/docs-site/src/content/docs/zh-tw/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/zh-tw/reference/cli/providers-accounts.md @@ -94,7 +94,7 @@ Codex 池選擇套用於清除既有親和性後的下一個請求;進行中 ### `ocx account list [provider] [--json] [--all] [--quota [--refresh]]` -未指定供應商時,列出 Codex 池、OAuth 帳號與已設定的 API-key 池。除非存在 `--all`,否則空的供應商會被跳過。指定供應商時,僅列出該憑證家族。人類輸出使用 `PROVIDER TYPE ID PLAN/LABEL STATUS`;手動選擇的 Codex 列標記為 `selected`。當儲存了兩個以上符合資格的 Kiro 帳號時,預設情況下 429 會自動輪換至另一個帳號,並優先選擇已知剩餘額度最多的帳號;輪換由帳號存在與否驅動,可透過 `oauthAccountFailover.enabled: false` 關閉。`ocx account login kiro` 每次將一個帳號加入池中。空結果仍為成功。`--json` 回傳: +未指定供應商時,列出 Codex 池、OAuth 帳號與已設定的 API-key 池。除非存在 `--all`,否則空的供應商會被跳過。指定供應商時,僅列出該憑證家族。人類輸出使用 `PROVIDER TYPE ID PLAN/LABEL STATUS`;手動選擇的 Codex 列標記為 `selected`。當儲存了兩個以上符合資格的 Kiro 帳號時,預設情況下 429 會自動輪換至另一個帳號,並優先選擇已知剩餘額度最多的帳號;輪換由帳號存在與否驅動,且無法關閉;`oauthAccountFailover.enabled: false` 拒絕的是送出前的帳號優選,而非 429 復原。`ocx account login kiro` 每次將一個帳號加入池中。空結果仍為成功。`--json` 回傳: ```text { accounts: AccountRow[], notes: string[] } diff --git a/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md b/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md index 509d4c88c4..fba727e667 100644 --- a/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md +++ b/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md @@ -131,7 +131,7 @@ API-key 供應商可持有字面值金鑰或環境參考。OAuth 供應商使用 | Key | 型別 | 預設值 | 說明 | | --- | --- | --- | --- | -| `anthropicAccountPool.enabled?` | `boolean` | `false` | 啟用 sticky 親和性與 429 冷卻容錯移轉。 | +| `anthropicAccountPool.enabled?` | `boolean` | `false` | 啟用 sticky 工作階段親和性與依用量的新工作階段選擇。**429 容錯移轉不由此開關控制**:只要儲存了兩個以上可用帳號就會生效,與其他多憑證供應商一致,且無法關閉。 | | `anthropicAccountPool.autoSwitchThreshold?` | `number` | `80` | 對於新 session,當目前帳號達到此閾值時,選擇設定視窗中最低的已知快取用量。`0` 停用配額挑選。 | | `anthropicAccountPool.strategy?` | `"quota" \| "round-robin" \| "fill-first"` | `"quota"` | 新 session 策略;`quota` 依 `quotaWindow` 指定的視窗(預設為 5 小時列)為帳號排序,`fill-first` 也在同一視窗中判定其排空閾值。 | | `anthropicAccountPool.quotaWindow?` | `"five-hour" \| "weekly" \| "max-utilization"` | `"five-hour"` | 使用量型帳號選擇所採用、由供應商回報並快取的用量列。`five-hour` 保留原有行為。`weekly` 使用每週用量列,並在仍有其他可用帳號時略過 5 小時用量已用盡的帳號;若沒有其他帳號,則退回使用這些帳號。`max-utilization` 使用已知值中的最高值,因此每週用量尚未取得時仍可使用 5 小時用量;兩者都未知時,帳號遵循 unknown 用量排序。已知用量排在 unknown 之前,但若所有可用帳號都是 unknown,仍會依可用順序選出一個。完成前述較低 5 小時用量的同分判定後,完全相同時也保留可用順序。不會主動重新平衡健康且已有 affinity 的 session。在分配新 session 與符合條件的 429 替代後進行路由復原時,`quota` 直接依此視窗排序可用候選帳號;`fill-first` 依此視窗的門檻與用盡規則按穩定順序前進;`round-robin` 忽略此設定。冷卻狀態、容錯移轉上限與重新驗證資格仍是獨立的本機狀態。每個帳號的每週用量只有在 dashboard 的供應商頁面完成查詢後才可得知。 | diff --git a/tests/docs-429-failover-claims.test.ts b/tests/docs-429-failover-claims.test.ts new file mode 100644 index 0000000000..5a0adb2914 --- /dev/null +++ b/tests/docs-429-failover-claims.test.ts @@ -0,0 +1,62 @@ +/** + * The published docs must not promise a 429-failover kill switch. + * + * Reactive rotation activates on account presence and cannot be disabled. Every locale of the + * configuration and CLI reference previously said `oauthAccountFailover.enabled: false` turns + * rotation off, and that `anthropicAccountPool.enabled` gates 429 failover. Both were true when + * written and both stopped being true when reactive and proactive activation were split. + * + * Public docs that contradict the runtime are worse than missing docs: an operator reads them, + * concludes a rate limit is terminal, and turns on an EXPERIMENTAL pool to buy recovery they + * already have. This pins the corrected claim in the source locale and asserts the translated + * locales carry the same shape, since a nine-locale set drifts one file at a time. + */ +import { describe, expect, test } from "bun:test"; + +const CONFIG_REFERENCE = "docs-site/src/content/docs/reference/configuration/providers.md"; +const CLI_REFERENCE = "docs-site/src/content/docs/reference/cli/providers-accounts.md"; +const TRANSLATED = ["ko", "ja", "zh-cn", "zh-tw", "fr", "ru", "tr"] as const; + +describe("429 failover docs", () => { + test("the config reference states the failover is not gated and not disableable", async () => { + const source = await Bun.file(CONFIG_REFERENCE).text(); + const anthropicRow = source + .split("\n") + .find(line => line.includes("`anthropicAccountPool.enabled?`")); + expect(anthropicRow).toBeDefined(); + expect(anthropicRow).toContain("not gated here"); + expect(anthropicRow).toContain("cannot be switched off"); + + // The generic knob keeps a real meaning -- the pre-dispatch preference -- so the row must say + // what it still refuses rather than simply deleting the switch. + const genericRow = source + .split("\n") + .find(line => line.includes("| `oauthAccountFailover.enabled?`")); + expect(genericRow).toBeDefined(); + expect(genericRow).toContain("pre-dispatch account preference"); + expect(genericRow).toContain("does **not** disable 429 rotation"); + }); + + test("the CLI reference no longer says rotation can be turned off", async () => { + const source = await Bun.file(CLI_REFERENCE).text(); + expect(source).toContain("cannot be turned off"); + }); + + test("every translated locale carries the corrected claim", async () => { + // Shape, not wording: each locale phrases it natively, but none may still present the knob + // as a rotation kill switch. The English marker strings are the ones a drift would drop. + for (const locale of TRANSLATED) { + const config = await Bun.file( + `docs-site/src/content/docs/${locale}/reference/configuration/providers.md`, + ).text(); + const row = config + .split("\n") + .find(line => line.includes("`anthropicAccountPool.enabled?`")); + expect(row, `${locale} is missing the anthropicAccountPool row`).toBeDefined(); + // Every translation keeps the literal `429` and marks the sentence bold, so a locale that + // silently reverted to the old one-line description fails here. + expect(row, `${locale} lost the 429 carve-out`).toContain("**"); + expect(row, `${locale} lost the 429 carve-out`).toContain("429"); + } + }); +});