Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions devlog/_plan/260927_merge_train_3/030_batch3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# B3 — quota activation, update launcher, link join, settings reads, account selection, combo exhaustion

Base: `dev` `06d7914e6a` (after B2 #6061). Branch `codex/train3-b3`.

Previous D (B1+B2): both batches landed with exact-head CI (35 success, 5 path-skipped each). Direction kept: serialized
carries with review fixes as separate commits. Lesson: build only inside B, after A.

| Item | Author | Plan | Fixes to fold |
|---|---|---|---|
| #6049 | luvs01 | Carry. Bounded, non-blocking read of the global Codex config on the settings poll path. | Layout registries: union with #6048's compaction. |
| #6037 | luvs01 | Carry. `systemd-run` resolves only from trusted root-owned paths, probed off the event loop. | `src/update/job.ts` import conflict: keep both imports (file lands at 1999 of 2000 lines). |
| #6042 | luvs01 | Carry. The Remote Link join key leaves only after the tunnel's listener ownership is proven twice. | None required; the connect-phase race stays documented in `structure/remote-link.md` as the PR states. |
| #6020 | terrytan95 | Carry; resolves #6018. Deadline-first quota activation with bounded backoff. | Retry records carry the credential generation, so an old credential's failure cannot hold back a replacement; a local `native main busy` refusal retries in one minute without growing the backoff; drop the duplicate delete. |
| #6056 | luvs01 | Close as superseded by #6020. On dev the retained earliest deadline already starts an idle window once, and #6020 stops the polling. | — |
| #6050 | luvs01 | Carry. `ocx account clear`; an account id `auto` wins over the reserved word; clearing works while main is paused. | Rewrite the dev test that pinned the old 409; revert its unrelated `shadow` default and `strategy` doc hunks; union the layout registries. |
| #5494 | (issue, found through Aside) | Implement. A 429 whose body says the token-plan quota "has been exhausted" is account exhaustion, so the combo target takes the long hold instead of being offered again every 60 s. | Regression test next to the combo exhaustion tests. |

Held: #6027 (owner's three blockers are still open on a draft head), #6030 and #6003 (drafts), GUI PRs.

Security-boundary items: #6037 (updater command execution) and #6042 (link join credential) have dedicated Kimi
security reviews with no blocker recorded in this unit.

## Audit (Kimi, NEAR-PASS) and folded decisions

- #6020 busy path: a named `NativeMainBusyError`; the retry record keeps its prior `delay` and sets `after = now + 60 s`.
- #6020 `main account unavailable`: stays in the growing backoff. It is keyed by generation, so a token that
arrives later starts clean.
- #6020 both retry maps (`retryAfterByAccount`, `quotaRefreshAfterByAccount`) carry the credential generation; a
record from another generation is dropped when read. The generation is captured before `warm()`/`refresh()` and a
failure is not recorded when it changed during the await. The second same-tick `hasScheduledWindows` delete goes,
because the generation check covers it and a leftover metadata backoff cannot gate a scheduled account.
- #6020 tests: replacement during the await, repeated busy refusal then release, reauth then rotation.
- #5494: the regex is anchored to the token-plan phrasing,
`/usage limit (?:has been )?reached|token-plan\s+\S+\s+quota has been exhausted/`, with a negative case for
"quota exhausted for this minute". The hold is the existing ten-minute exhaustion cap, not the announced reset.

## Build and evidence

| Commit | What |
|---|---|
| `c79fe409c6` | #6049 (layout registries unioned) |
| `33b1920cce` | #6042 |
| `b697756cdb` | #6037 (`job.ts` import conflict: both imports kept; 1999 lines) |
| `8e08f26f86` | #6020 |
| `2a49525f1d` | #6020 review fix: generation-keyed retries, flat one-minute retry on `NativeMainBusyError`, three regression tests in `codex-quota-auto-refresh-generation.test.ts` (all three fail without the fix) |
| `9fdc0c4582` | #5494: token-plan exhaustion takes the ten-minute hold; positive and per-minute negative tests (the positive fails without the fix) |
| `cafe6202ad` | #6050 (the dev test pinning the old paused-main 409 on clear is removed; the new file covers the contract) |

Kimi's note that #6050 regressed the `shadow` defaults and the Kiro-only `strategy` note came from diffing against
an older base; the squash onto current `dev` changes only the account-selection lines in the eight locales.

Security receipts: #6042 dedicated review, BLOCKER no (connect-phase race stays documented, as the PR states). #6037
review found no blocking defect; the updater launcher now trusts only root-owned absolute paths, and Ingwannu's
earlier CHANGES_REQUESTED findings (lexical ancestors, synchronous probes) are fixed at the carried head.

Aside: #6037 still shows one CHANGES_REQUESTED review and #6020 two, both from earlier heads; this batch answers
#6020's findings in `2a49525f1d`. #5494's page shows the reporter's two messages and no maintainer reply; the fix
covers the part the repository can prove (the 60-second re-offer). Why the official DeepSeek stream ended early needs
the reporter's logs.

Local proof at `cafe6202ad`: typecheck, structure and privacy exit 0; 13 focused files 619 pass, 3 skip, 0 fail;
combo failover files 297 pass; layout and ratchet guards 27 pass.
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,13 @@ Répertoriez et changez de compte de fournisseur et de pools de clés API via le
la surface est :

```text
Usage: ocx account <list|history|current|use|refresh|auto-switch|alias|priority|pause|resume|pause-exhausted|strategy|sticky|remove|clear-cooldown|add-key|import|import-orca|login|reauth|code|cancel|reset-credits|grok-reset-coupons|main> ...
Usage: ocx account <list|history|current|use|clear|refresh|auto-switch|alias|priority|pause|resume|pause-exhausted|strategy|sticky|remove|clear-cooldown|add-key|import|import-orca|login|reauth|code|cancel|reset-credits|grok-reset-coupons|main> ...

list [provider] Codex account pool, OAuth accounts and API keys (identifiers shown masked as the API returns them).
history openai <pool-account-id> [--limit <1-200>] Recent routing decisions for one Codex pool account.
current <provider> Show the active account or key.
use <provider> <id|alias|main|auto> Switch the active credential; 'main' selects the Codex App login, 'auto' clears the selection.
use <provider> <id|alias|main|auto> Switch the active credential; 'main' selects the Codex App login, 'auto' clears the selection unless an account carries that id.
clear <provider> Clear the manual Codex account selection unconditionally.
refresh <provider> Force-refresh Codex or provider quota reports.
auto-switch <provider> <on|off|status|threshold N> Control the Codex pool threshold.
alias <provider> <id|alias> <display-name|-> Set or clear an account's display name; '-' clears it.
Expand Down Expand Up @@ -192,7 +193,7 @@ cet état et quitte toujours 0. `--json` renvoie :

### `ocx account use <provider> <account-or-key-id|alias|main|auto> [--json]`

`auto` efface la sélection manuelle pour que le pool place à nouveau le travail selon sa propre stratégie. Un compte Codex peut être désigné par l'alias défini avec `ocx account alias` au lieu de son id ; cela vaut aussi pour `priority`, `pause`, `resume`, `clear-cooldown`, `remove` et `alias`. Pour les comptes Codex, `auto`, `main` et `__main__` sont réservés sans distinction de casse et ne peuvent pas être attribués comme alias. Les noms affichés des comptes OAuth et des clés API conservent leurs règles existantes.
`auto` efface la sélection manuelle pour que le pool place à nouveau le travail selon sa propre stratégie — sauf si un compte Codex porte littéralement l'id `auto`, qui l'emporte par correspondance exacte d'id ; `ocx account clear <provider>` rétablit toujours la sélection automatique. Un compte Codex peut être désigné par l'alias défini avec `ocx account alias` au lieu de son id ; cela vaut aussi pour `priority`, `pause`, `resume`, `clear-cooldown`, `remove` et `alias`. Pour les comptes Codex, `auto`, `main` et `__main__` sont réservés sans distinction de casse et ne peuvent pas être attribués comme alias. Les noms affichés des comptes OAuth et des clés API conservent leurs règles existantes.

Sélectionne un compte Codex, un compte OAuth ou une clé API existant. Pour `openai`, `main` sélectionne la
connexion Codex App. Une sélection en mode Codex Pool efface l'affinité locale du processus et s'applique à la requête suivante,
Expand All @@ -213,6 +214,10 @@ faire basculer la requête vers un autre compte de pool admissible. Ces transiti
{ ok: true, provider, type, activeId }
```

### `ocx account clear <provider> [--json]`

Efface la sélection manuelle du compte Codex sans résoudre d'id de compte, donc fonctionne même lorsqu'un compte s'appelle littéralement `auto`. Pools Codex uniquement ; les autres types de fournisseur n'ont pas de sélection automatique à rétablir.

### `ocx account refresh <provider> [--json]`

Pour le groupe de comptes Codex, utilisez `ocx account refresh openai [--json]`. Cette commande force l'actualisation des quotas de compte et
Expand Down
9 changes: 7 additions & 2 deletions docs-site/src/content/docs/getting-started/how-it-works.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,13 @@ account before the request is forwarded upstream. The rule is intentionally spli
coalesces simultaneous windows into one request, and durably persists both reset timestamps to
prevent duplicate work after restarts. Paused accounts and accounts
requiring reauthentication are skipped. Activation captures successful response quota headers;
opted-in idle accounts also refresh stale quota metadata at most once every five minutes,
without needing an open dashboard. Observed reset boundaries are retained across restarts
known reset times are checked locally each minute without periodic quota queries, even when
the cached usage is old or the proxy restarts. Only missing reset times need a metadata query
after the five-minute freshness guard. Unresolved discovery and failed activations retry after
5, 10, 20, 40, then at most every 60 minutes; these retry delays reset on proxy restart.
Successful response headers seed the next window without an extra query when available.
Dashboard refreshes and optional reset-notification polling remain independent.
Observed reset boundaries are retained across restarts
until completed, so a moving idle-window timestamp cannot erase a pending activation.
Metadata refresh uses the existing bounded authentication recovery; an inference 401 marks
the rejected credential for reauthentication instead of repeatedly spending retries on it.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,13 @@ ocx login anthropic
実行中のプロキシを介してプロバイダー アカウントと API キー プールを一覧表示し、切り替えます。出荷されたヘルプ画面は次のとおりです。

```text
Usage: ocx account <list|history|current|use|refresh|auto-switch|alias|priority|pause|resume|pause-exhausted|strategy|sticky|remove|clear-cooldown|add-key|import|import-orca|login|reauth|code|cancel|reset-credits|grok-reset-coupons|main> ...
Usage: ocx account <list|history|current|use|clear|refresh|auto-switch|alias|priority|pause|resume|pause-exhausted|strategy|sticky|remove|clear-cooldown|add-key|import|import-orca|login|reauth|code|cancel|reset-credits|grok-reset-coupons|main> ...

list [provider] Codex account pool, OAuth accounts and API keys (identifiers shown masked as the API returns them).
history openai <pool-account-id> [--limit <1-200>] Recent routing decisions for one Codex pool account.
current <provider> Show the active account or key.
use <provider> <id|alias|main|auto> Switch the active credential; 'main' selects the Codex App login, 'auto' clears the selection.
use <provider> <id|alias|main|auto> Switch the active credential; 'main' selects the Codex App login, 'auto' clears the selection unless an account carries that id.
clear <provider> Clear the manual Codex account selection unconditionally.
refresh <provider> Force-refresh Codex or provider quota reports.
auto-switch <provider> <on|off|status|threshold N> Control the Codex pool threshold.
alias <provider> <id|alias> <display-name|-> Set or clear an account's display name; '-' clears it.
Expand Down Expand Up @@ -144,7 +145,7 @@ Codex pool selection applies to the next request after clearing existing affinit

### `ocx account use <provider> <account-or-key-id|alias|main|auto> [--json]`

`auto` は手動の選択を解除し、プールが自身の戦略で再び配置するようにします。Codex アカウントは id の代わりに `ocx account alias` で付けたエイリアスでも指定でき、`priority`、`pause`、`resume`、`clear-cooldown`、`remove`、`alias` でも同様です。Codex アカウントでは `auto`、`main`、`__main__` は大文字・小文字を区別せず予約語として扱われるため、エイリアスとして設定できません。OAuth アカウントと API キーの表示名には従来のルールが適用されます。
`auto` は手動の選択を解除し、プールが自身の戦略で再び配置するようにします — ただし id が `auto` の Codex アカウントが存在する場合は完全一致の id が優先され、`ocx account clear <provider>` が常に自動選択を復元します。Codex アカウントは id の代わりに `ocx account alias` で付けたエイリアスでも指定でき、`priority`、`pause`、`resume`、`clear-cooldown`、`remove`、`alias` でも同様です。Codex アカウントでは `auto`、`main`、`__main__` は大文字・小文字を区別せず予約語として扱われるため、エイリアスとして設定できません。OAuth アカウントと API キーの表示名には従来のルールが適用されます。

既存の Codex アカウント、OAuth アカウント、または API key を選びます。`openai` で `main` は Codex App ログインを
選択します。Codex Pool の選択は process-local affinity を消去し、既存の表示タスクを含む次のリクエストから適用されます。プロキシ再起動や affinity eviction 後もタスクは未紐付けになり得ますが、処理中のリクエストは取得済みアカウントを維持します。この選択は Pool routing のみを制御し、Direct mode は caller-owned/native main credential を使い続けます。使用量ベースのプロアクティブ切り替え、401/403 再認証、429/retry-after cooldown、除外、出力前 429/402 の障害回復により、後で別の適格 Pool アカウントが選ばれる場合があります。これらの回復経路は使用量ベース切り替えが off でも有効です。アカウント変更後も OpenCodex は会話コンテキストを再生しますが、provider prompt cache は再ウォームアップが必要な場合があります。
Expand All @@ -158,6 +159,10 @@ Codex pool selection applies to the next request after clearing existing affinit
{ ok: true, provider, type, activeId }
```

### `ocx account clear <provider> [--json]`

アカウント id を解決せずに Codex アカウントの手動選択を解除するため、`auto` という id のアカウントが存在しても機能します。Codex プール専用です。他のプロバイダー種別には復元する自動選択がありません。

### `ocx account refresh <provider> [--json]`

Codex プールの場合は、`ocx account refresh openai [--json]` を使用します。アカウント クォータを強制的に更新し、利用可能な週次/月次のパーセンテージとリセット時間を出力します。不足しているクォータ データは、0% ではなく不明として報告されます。その JSON エンベロープは `{ accounts: AccountRow[] }` で、Codex の各行に `quota` があります。
Expand Down
Loading
Loading