From e04cb73403458e1195d8a059a6f07436845a6230 Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Sun, 6 Sep 2026 20:47:16 +0900 Subject: [PATCH 1/4] fix: unify manual and automatic credential selection --- .../000_plan.md | 31 ++ .../010_implementation.md | 63 ++++ .../src/content/docs/guides/web-dashboard.md | 9 + .../content/docs/ko/guides/web-dashboard.md | 8 + gui/src/hooks/useProviderAccountPools.ts | 191 ++++++++++-- gui/src/pages/Providers.tsx | 131 ++++++++- gui/src/pages/use-providers-oauth.ts | 6 +- .../provider-account-quota-loading.test.tsx | 140 ++++++++- .../provider-revalidation-policy.test.tsx | 99 ++++++- src/lib/account-selection-events.ts | 32 ++ src/oauth/account-quota-rank.ts | 10 +- src/oauth/anthropic-routing.ts | 106 +++++-- src/oauth/generic-account-failover.ts | 68 +---- src/oauth/pool-settings-capability.ts | 3 +- src/oauth/store.ts | 98 ++++++- src/oauth/types.ts | 8 + src/providers/api-key-selection.ts | 57 ++++ src/providers/api-keys.ts | 69 +++-- src/providers/key-failover.ts | 44 +-- src/router.ts | 2 + src/server/auth-cors.ts | 3 + src/server/chat-native.ts | 1 + .../management/account-selection-stream.ts | 59 ++++ src/server/management/oauth-account-routes.ts | 7 + src/server/management/route-registry.ts | 1 + src/server/responses/core.ts | 131 ++++++--- src/types/config.ts | 8 +- src/types/provider.ts | 17 +- structure/04_transports-and-sidecars.md | 8 +- structure/05_gui-and-management-api.md | 16 + .../anthropic/anthropic-account-pool.test.ts | 135 ++++++++- tests/adapters/key-failover.test.ts | 81 ++++- .../adapter-event-oauth-failover.test.ts | 21 +- tests/oauth/generic-oauth-failover.test.ts | 45 ++- tests/oauth/oauth-accounts-api.test.ts | 57 ++++ tests/oauth/oauth-store-multi.test.ts | 276 +++++++++++++++++- tests/providers/kiro/kiro-pool-rank.test.ts | 31 +- tests/server/server-key-failover-e2e.test.ts | 38 ++- 38 files changed, 1845 insertions(+), 265 deletions(-) create mode 100644 devlog/_plan/260906_manual_account_selection/000_plan.md create mode 100644 devlog/_plan/260906_manual_account_selection/010_implementation.md create mode 100644 src/lib/account-selection-events.ts create mode 100644 src/providers/api-key-selection.ts create mode 100644 src/server/management/account-selection-stream.ts diff --git a/devlog/_plan/260906_manual_account_selection/000_plan.md b/devlog/_plan/260906_manual_account_selection/000_plan.md new file mode 100644 index 0000000000..f46765a744 --- /dev/null +++ b/devlog/_plan/260906_manual_account_selection/000_plan.md @@ -0,0 +1,31 @@ +# Manual account selection must control dispatch + +Loop: single-cycle satisfy-spec, C4 (credential/account allocation). Trigger: the user selected a healthy OAuth account in the dashboard, but every request was silently assigned to another account. Goal: disabled pools do not proactively reassign healthy requests; explicit selection wins; permitted automatic assignment is reflected in active-account state and dashboard. One cohesive PR targeting dev, pushed with `--no-verify` and merged as explicitly authorized. No stacks. + +Boundaries: existing account/key owners, request credential pairing, dashboard account synchronization, regression coverage, matching public docs. No provider API changes, real-account configuration changes, paid inference probes, releases, service restarts, or unrelated refactors. Use existing dependencies and temp credentials. No token, cost, or wall-clock budget was set; no paid oracle is required. Completion requires fresh direct verification and actual remote merge; a pending PR is not DONE. Escalate only a genuine tool/access block or a necessary authority not already granted. Main reclaims a lane after two distinct worker failures; new worker scope requires a P amendment. + +Memory: this numbered unit, `.tmp/manual-account-selection/` evidence, session-bound goalplan. The implementation and test map is in 010. One PABCD work-phase covers this single contract across its existing owners; frontend/runtime lanes are subtasks rather than separate deliverables. + +## Evidence and rival hypotheses + +GUI `useProviderAccountPools.ts:247` sends selected account to PUT `/api/oauth/accounts/active`. `oauth-account-routes.ts:322` calls `setActiveAccount`, which saves the selected id. `generic-account-failover.ts:185-196` defaults healthy proactive selection on when two accounts exist. `preferredInitialAccount` ranks by quota and can replace the selected healthy account. `responses/core.ts` resolves that other credential and logs it without updating stored selection. Provider quota reads stored active selection separately. Exact user-provided request IDs matched another account on attempt1, sendCount1, no retry. This is not an upstream429 recovery or a failed GUI save. + +H1 failed persistence is disproved by stored selected id and successful route semantics. H2 only a2s roster cache is insufficient: quota ranking would choose the other account after the cache expires. H3 manual authority absent from the selector is supported by the complete route-to-store-to-request chain. We will prove H2/H3 with isolated real owner functions before implementation. + +## Contract + +- Presence of multiple stored accounts does not enable proactive healthy-request allocation. Absent generic OAuth proactive enablement is off; explicit false wins for proactive allocation. REACTIVE429 recovery remains mandatory whenever another usable account exists, regardless of the pool switch, per the latest explicit user correction. +- With a pool enabled, the healthy active/manual account stays eligible and preferred. A quota percentage alone below exhaustion must not replace it. On a real account-scoped refusal or known exhaustion, enabled rotation may choose another usable account. +- Committing an automatic account selection must be conditional on the selection generation that produced the request. A newer manual selection (including A→B→A) wins. +- The selected account and its access token/project/origin metadata travel together; unsupported/unknown quota is not exhaustion. +- Codex, Anthropic, and API keys keep their own established contracts, but any contradiction with these user requirements is repaired in the same PR. Their specific findings must be folded into010 before their edits. + +Enforcement: runtime selectors plus guarded persisted active-account transition; execution surface covers first dispatch and every reactive replay. Known bypass: external callers can intentionally route exact account-targeting selectors, which remain their own explicit contract. Residual: concurrent requests can already be in flight on different credentials; dashboard reports the latest committed allocation, never retroactively cancels an already sent request. Wording: no claim that a UI highlight can reassign a request already upstream. + +Verification: focused Bun store/management/selection/retry tests first; full `bun run typecheck` and `bun run test` before PR review-ready; relevant GUI tests/lint/build and a rendered state transition if frontend changes. Regression tests use synthetic identities only. Public SoT: `structure/05_gui-and-management-api.md` and existing account/pool guide pages. Final record includes rejected hypotheses, unmodified owners with proof, security/concurrency audit, and remote merge. + +Latest steering: the user reports Codex works correctly. Preserve Codex routing/controller semantics and use its existing manual-selection behavior as the reference; include regression-only checks for Codex. Concentrate changes on the other quota-aware account paths where a concrete mismatch is established. + +Latest explicit design instruction: GUI selection and pool selection must share one selection owner, as Codex does. Both must commit the same authoritative selection BEFORE dispatch; requests use the committed account. Do not bolt on a separate UI-only mirror or route around manual selection while pretending the old active account remains selected. A concurrent newer user selection wins. This strengthens the existing planned store-owned selection transaction and applies with pool on or off. + +Latest correction: 429 automatic account switching is ALWAYS allowed, including poolOFF. Withdraw the planned reactive disable gate. Manual selection wins ordinary dispatch; a real429 may replace it using the same guarded selection owner, and the GUI follows that committed replacement. diff --git a/devlog/_plan/260906_manual_account_selection/010_implementation.md b/devlog/_plan/260906_manual_account_selection/010_implementation.md new file mode 100644 index 0000000000..e31bc2643a --- /dev/null +++ b/devlog/_plan/260906_manual_account_selection/010_implementation.md @@ -0,0 +1,63 @@ +# Implementation — one account-selection contract + +Depends on000. One PABCD work-phase, one PR. Existing subsystem owners stay intact. + +## Shared OAuth store and generic allocation (main) + +MODIFY `src/oauth/types.ts`, `src/oauth/store.ts`: add optional non-secret `ProviderAccountSet.selectionRevision` and a typed selection snapshot `{ accountId, revision? }`. Legacy files without the field remain valid. Normalize/persist/copy it through the existing auth-store boundary; every active-selection change (including re-selecting the same account) advances it. Add a store-owned capture function and conditional active-selection commit that compares both original active id and revision inside `mutateStore` before writing. Credential-only refresh must preserve the selection revision. Consumers: generic and Anthropic request admission/promotion. Management DTO need not expose the revision: existing active id remains the public selection. No credential value is logged. + +MODIFY `src/oauth/generic-account-failover.ts`: proactive allocation requires effective pool `enabled === true` (provider override then global). Merely storing2 accounts does not enable healthy-request steering. Preserve presence-based REACTIVE429 switching even when disabled, as the user expressly requires. Keep a healthy manually active account first; use quota ranking only when the chosen account is ineligible/exhausted or when an enabled pool recovers a real refusal. Unknown quota never implies exhaustion. Preserve existing cooldowns and bounded attempts. Clear roster on manual selection so an old2s cache cannot dispatch the previously selected account. + +MODIFY `src/server/management/oauth-account-routes.ts`: manual selection retains successful persistence and cache invalidation, and invalidates relevant generic selection state. Existing Anthropic manual handler remains its owner. Update outdated pool-settings contract comments/DTO docs in `src/oauth/pool-settings-capability.ts`, `src/types/provider.ts`, `src/types/config.ts` to match effective enablement; do not introduce a new UI toggle simply to repair a default. + +MODIFY `src/server/responses/core.ts`: capture the OAuth selection snapshot before awaited token materialization; preserve token/project/origin pairing. For actual automatic initial/retry selection, await the guarded active-account commit before publishing that allocation. A rejected promotion caused by a newer user selection must not overwrite it; continue via current valid selected account or return the original request failure as appropriate. Apply consistently to direct upstream errors, runTurn on429 callback, passthrough/combo retries and downstream stream recovery call sites. The same shared core serves Responses/Chat/Messages surfaces. + +## Anthropic and API keys (backend lane) + +MODIFY `src/oauth/anthropic-routing.ts` and its existing tests: preserve reactive429 rotation even when the pool is off (latest user correction). Manual selection must seed a preference that wins the next eligible dispatch, including quota strategy, clearing stale affinities. Guard automatic active-account promotion with the same store selection snapshot; main owns core call-site integration. Maintain account-scoped refresh restrictions. + +MODIFY `src/providers/key-failover.ts` and relevant caller/rotation types only if the isolated repro confirms env/keychain reference identity mismatch: match failed attempts by stable pool-entry identity/reference, never by comparing a resolved secret with a stored reference. Preserve newer manual key selection and committed config-to-dashboard mapping. API-key pools have no separate enable boolean: an explicitly configured multi-key pool remains their existing enable contract. No speculative new mode is added. Codex is unchanged and regression-only, per latest user steering. + +## Dashboard (frontend lane) + +MODIFY existing `gui/src/hooks/useProviderAccountPools.ts` / `gui/src/pages/Providers.tsx` runtime-read integration and existing tests as needed: periodically reconcile cheap local OAuth/key roster active state through the shared scheduler, without forcing upstream quota probes on every tick. Reuse quota rows and reject stale read results around manual mutation. Do not alter Codex controller behavior. Keep layout and existing localized labels. Render a synthetic selected-account transition and retain a screenshot in the unit for PR evidence. + +## Proof / reachable cases + +- Pool absent/false with2 accounts: manual A30%, B11%; ordinary dispatch staysA. Simulated429 MUST auto-switch to another usable account even with pool off, and persist that selection. Pool enabled: manual A remains preferred; known exhaustedA or actual refusal chooses usableB and active DTO becomesB. +- A delayed token refresh/429 starts onA; manual choiceB or A→B→A occurs before completion; older selection revision cannot change active state. Removal/reauth during candidate resolution cannot promote an invalid target. +- Generic matrix runs xAI, Cursor, Kimi, Copilot, Antigravity, Nous and representative passive-quota provider using synthetic snapshots. Copilot regional origin and Antigravity project remain paired to the selected bearer. +- Anthropic off/on, quota/RR manual priority, stale affinity, failed token resolution, and promotion races; Codex direct/manual/pin existing regressions stay green. +- Literal/env/keychain-supported API-key identities: rejected attempted key rotates to another distinct key, newer manual key wins, chosen key is the persisted active key. +- Dashboard backendA→B read updates highlighted selection and current quota association; an older quota/roster poll cannot revert a newer manual choice; a roster-only poll never initiates a paid/upstream quota read. + +Reuse existing tests: `tests/oauth/generic-oauth-failover.test.ts`, `tests/oauth/oauth-store-multi.test.ts`, `tests/oauth/adapter-event-oauth-failover.test.ts`, `tests/server/account-pool-management-api.test.ts`, provider quota/Anthropic/key failover suites discovered by owner search, and existing `gui/tests/provider-account-quota-loading.test.tsx` / provider revalidation tests. Prefer these files to new layout entries. Verify focused red before repair, then green; run `bun run typecheck`, `bun run test`, `bun run privacy:scan`, and relevant GUI tests/lint/build. New failures outside scope are diagnosed and recorded, not ignored. Fresh independent security/concurrency review before delivery. + +SoT sync: `structure/05_gui-and-management-api.md`, existing English configuration/account pool guide plus translated statements that would otherwise contradict changed enablement. Record provider coverage and limitations in011 evidence. Push single branch with `git push --no-verify`; create one PR using repository template againstdev, attach rendered UI evidence if GUI changed, verify remote head/CI, then merge as authorized and verify integration SHA. + +Known design risk for A audit: making selection persistence part of dispatch must not serialize all independent successful requests; commit only actual account changes, and use the existing guarded store writer. Manual selection while an upstream request is already running applies to subsequent allocation; no retroactive cancellation claim. + +## P clarification from frontend owner + +Current scheduler is `useKeyedClientResource`/`client-resource.ts`, not `useRuntimeRead`. Exact frontend writes: `gui/src/hooks/useProviderAccountPools.ts`, `gui/src/pages/Providers.tsx`, and `gui/src/pages/use-providers-oauth.ts`; tests: existing `provider-account-quota-loading.test.tsx` and `provider-revalidation-policy.test.tsx`. Register one local-roster refresh through App's existing30s shared scheduler, key by server+sorted provider list, not active ids. Preserve initial quota enrichment; never add quota=1 to periodic reads. Invalidate per-provider read generation at manual PUT start; apply successful response active id; late login-status hydration may seed only a missing roster. Codex controller stays unchanged. Existing relevant GUI baseline52 tests passed in separate file processes; grouped globals can collide, so run each file separately. + +## Shared-selection requirement (latest steering) + +GUI PUT and pool choice both use the store-owned active-selection transaction. Make the common operation return/confirm the committed selection, and dispatch from its matching credential snapshot; a pool proposal is not authoritative until it commits. Do not mutate per-request bearer first and asynchronously update GUI afterward. The generation guard is a concurrency condition inside that same shared operation, not a competing selection state. Keep the public active-id DTO unchanged. Main and backend lane must align on this seam before writing callers. + +Authoritative429 exception: poolOFF suppresses only proactive steering. Every generic/Anthropic/key recovery test must preserve automatic429 failover. Any earlier statement blocking429 whileOFF is superseded. + +## Immediate synchronization amendment + +Latest user rejects waiting for a poll after automatic selection. The repository has no dashboard EventSource subscription to reuse. Add a narrow authenticated management SSE invalidation channel for committed account/key selection (`/api/accounts/events`) with bounded subscriber count, lightweight heartbeat, disconnect cleanup, and no credentials/account identifiers in events (provider plus kind/revision only). A dependency-leaf `src/lib/account-selection-events.ts` owns subscription/publication; it must not import server or Lab. Shared authoritative OAuth/key selection writers publish only after successful persistence. `src/server/management/oauth-account-routes.ts` serves the channel behind existing management auth; close it through existing optional shutdown hooks if necessary. Frontend lane adds a single lifecycle-owned EventSource for this screen, invalidates cheap roster via current generation guards, reconnects with a full local refresh, and keeps30s scheduler as recovery only. Existing test files cover event arrival→highlight change without advancing poll clock, blocked/failed writes emitting no selection event, and subscription cleanup. New endpoint is authenticated and carries no authority to select; data-plane keys cannot subscribe. This replaces the earlier30s-only plan. + +## A synthesis — accepted bounded corrections + +Independent reviewer verdict: GO-WITH-FIXES(blockers=5). All five are folded into the implementation, none rebutted: +1. Revision lifecycle covers manual reselect, new activation, removal promotion, replacement/recreation; rollback replacement receives a new revision, never resurrects an old one. Credential-only writes preserve it. +2. Common store operation `commitOAuthAccountSelection(provider, accountId, {expectedSelection?, expectedCredentialGeneration?, requireUsableAccount?})` returns committed `{accountId,revision?}` ornull. GUI's existing `setActiveAccount` boolean API wraps this same operation. `captureOAuthAccountSelection` supplies the expected snapshot. Validate unchanged-account admission too; retry current selection after a failed CAS, never send the rejected candidate. Cover generic core sites4868/5753/6100/6834/7244 and initial selection. Failed CAS emits nothing. GUI invalidates reads at both PUT start and settle and preserves settled quota state. +3. Anthropic affinity/rotation success bookkeeping occurs only after selection commits. All four promotion callers await it; background local-CLI token restrictions remain checked before commit. +4. API-key attempt carries stable pool identity/reference plus selection generation across all callers including nativeChat; common manual/automatic selection commit guards ABA and notifies only after persistence. +5. Quota eligibility explicitly distinguishes known exhaustion from unknown, including Kiro overage rules. Parameterized provider coverage includes Kiro and passive providers. + +B lane allocation (approved plan): main owns core.ts, OAuth management route/SSE route registration, generic selector/rank and integration proof/docs; store lane owns oauth/types.ts+store.ts, leaf account-selection event bus, and oauth-store-multi.test.ts; backend lane owns Anthropic routing+tests and API-key source/router/transport+tests including types/provider.ts; frontend lane owns the3GUI sourcefiles and2testfiles above. No worker changes maincore or another lane's files. Independent context review follows integration. diff --git a/docs-site/src/content/docs/guides/web-dashboard.md b/docs-site/src/content/docs/guides/web-dashboard.md index 3c46748a31..aef01e2fec 100644 --- a/docs-site/src/content/docs/guides/web-dashboard.md +++ b/docs-site/src/content/docs/guides/web-dashboard.md @@ -89,6 +89,15 @@ host and port over a LAN IP or an alias. | **Storage** | Read-only CODEX_HOME disk breakdown (sessions, archives, DBs, attachments). Optional archived cleanup: preview the oldest N%, then quarantine to `CODEX_HOME/.trash` (default) or permanently delete behind an explicit checkbox. **Auto-cleanup policy** is opt-in and **default OFF** (`storageCleanupPolicy.enabled`); configure threshold/target/schedule/mode on the Storage page, or trigger **Run now**. Quarantined entries can be restored from the Storage page (JSONL + threads). Active sessions stay read-only. Cleanup and restore are refused while Codex holds the newest/active `state_*.sqlite` locked. | | **Stop** | Gracefully stop the proxy and installed background service, restore native Codex, and exit (`POST /api/stop`). On Windows with the Task Scheduler backend the dashboard refuses and asks you to run `ocx stop` instead: that wrapper can respawn the proxy after the task ends, and only a stop running outside this process can verify the restart window before restoring your client config. Nothing is changed when it refuses. | +### Account selection + +Account selection is shared with request routing. Selecting an OAuth account takes effect on the +next request even when a pool is enabled. A healthy selection is not replaced merely because +another generic OAuth account has more unused quota. If the account returns 429, automatic +failover can still select another usable account with the pool off. A committed automatic +selection updates the dashboard immediately; account changes do not wait for the quota refresh +timer. Requests already sent upstream retain their original credentials. + ### Filtering request logs Logs filters combine surface, intercepted requests, provider, exact model, status, time, diff --git a/docs-site/src/content/docs/ko/guides/web-dashboard.md b/docs-site/src/content/docs/ko/guides/web-dashboard.md index dee6fc2e2a..efdd80179f 100644 --- a/docs-site/src/content/docs/ko/guides/web-dashboard.md +++ b/docs-site/src/content/docs/ko/guides/web-dashboard.md @@ -221,3 +221,11 @@ GUI는 프록시의 JSON 관리 API를 사용하는 얇은 클라이언트입니 프로바이더 설정에 복사됩니다. 별도 분류 작업 없이도 [비전 사이드카](/ko/guides/sidecars/)가 올바른 조건에서만 실행됩니다. ::: + +### 계정 선택과 자동 전환 + +GUI에서 OAuth 계정을 선택하면 풀 모드에서도 다음 요청에 반영돼요. 일반 OAuth 계정은 +정상적으로 사용할 수 있는 선택 계정을 유지하며, 다른 계정의 남은 할당량이 더 많다는 +이유만으로 바꾸지 않아요. 선택 계정이 429를 반환하면 풀이 꺼져 있어도 사용 가능한 다른 +계정으로 자동 전환해요. 자동 선택이 저장되면 GUI의 활성 표시도 즉시 바뀌어요. +이미 서버로 보낸 요청의 인증 정보는 바꾸지 않아요. diff --git a/gui/src/hooks/useProviderAccountPools.ts b/gui/src/hooks/useProviderAccountPools.ts index 255ad4dfbe..e66c464481 100644 --- a/gui/src/hooks/useProviderAccountPools.ts +++ b/gui/src/hooks/useProviderAccountPools.ts @@ -24,6 +24,32 @@ export interface OAuthAccount extends AccountQuotaReading { healthAction?: string; } export interface ApiKeyEntry extends AccountQuotaReading { id: string; label?: string; masked: string; active: boolean } +export interface AccountSelectionTarget { provider: string; kind: "oauth" | "api-key" } + +function selectionRows(rows: T[], id: string | null | undefined): T[] { + return id === undefined ? rows : rows.map(row => ({ ...row, active: row.id === id })); +} + +/** An invalidation read changes membership/selection, not quota probe state. */ +function mergeRosterRows(rows: T[], previous: T[]): T[] { + const prior = new Map(previous.map(row => [row.id, row])); + return mergeQuotaRows(rows, previous, false).map(row => supportsQuotaRead(row) ? { + ...row, + quotaPending: prior.get(row.id)?.quotaPending ?? false, + quotaUnavailable: prior.get(row.id)?.quotaUnavailable ?? false, + } : row); +} + +/** A probe started before a newer roster may update quota only on surviving IDs. */ +function mergeLateQuotaRows(rows: T[], enriched: T[]): T[] { + const byId = new Map(enriched.map(row => [row.id, row])); + return rows.map(row => { + const incoming = byId.get(row.id); + if (!incoming) return row; + const quota = mergeQuotaRows([incoming], [row], true)[0]; + return { ...row, quota: quota.quota, quotaPending: quota.quotaPending, quotaUnavailable: quota.quotaUnavailable }; + }); +} type QuotaRow = AccountQuotaReading & { id: string }; const supportsQuotaRead = (row: AccountQuotaReading) => row.quotaMode === "probe" || row.quotaMode === "passive"; @@ -91,12 +117,16 @@ export function useProviderAccountPools(deps: { const [addingKeyFor, setAddingKeyFor] = useState(null); const [newKeyValue, setNewKeyValue] = useState(""); const accountRequestGenerationRef = useRef>({}); + const rosterGenerationRef = useRef>({}); + const selectionMutationsRef = useRef(new Map()); const requestsRef = useRef(new Set()); const mountedRef = useRef(true); const serverRef = useRef(apiBase); useEffect(() => { const generations = accountRequestGenerationRef.current; const requests = requestsRef.current; + const rosterGenerations = rosterGenerationRef.current; + const mutations = selectionMutationsRef.current; mountedRef.current = true; const serverChanged = serverRef.current !== apiBase; serverRef.current = apiBase; @@ -109,6 +139,8 @@ export function useProviderAccountPools(deps: { return () => { mountedRef.current = false; for (const key of Object.keys(generations)) generations[key] += 1; + for (const key of Object.keys(rosterGenerations)) rosterGenerations[key] += 1; + mutations.clear(); for (const controller of requests) controller.abort(); requests.clear(); }; @@ -119,8 +151,11 @@ export function useProviderAccountPools(deps: { const keyPoolsKeyRef = useRef(null); const switchingAccountRef = useRef<{ provider: string; accountId: string } | null>(null); - const readRoster = useCallback(async (url: string): Promise => { + const readRoster = useCallback(async (url: string, signal?: AbortSignal): Promise => { const bounded = createBoundedFetch(20_000); + const abort = () => bounded.controller.abort(); + if (signal?.aborted) abort(); + signal?.addEventListener("abort", abort, { once: true }); requestsRef.current.add(bounded.controller); try { const response = await fetch(url, { signal: bounded.signal }); @@ -130,6 +165,7 @@ export function useProviderAccountPools(deps: { return data; } finally { bounded.clear(); + signal?.removeEventListener("abort", abort); requestsRef.current.delete(bounded.controller); } }, []); @@ -146,7 +182,10 @@ export function useProviderAccountPools(deps: { const key = `oauth:${provider}`; const generation = (accountRequestGenerationRef.current[key] ?? 0) + 1; accountRequestGenerationRef.current[key] = generation; + const rosterGeneration = (rosterGenerationRef.current[key] ?? 0) + 1; + rosterGenerationRef.current[key] = rosterGeneration; const currentRequest = () => aliveRef.current && mountedRef.current && serverRef.current === apiBase && accountRequestGenerationRef.current[key] === generation; + const currentRoster = () => currentRequest() && rosterGenerationRef.current[key] === rosterGeneration; const url = `${apiBase}/api/oauth/accounts?provider=${encodeURIComponent(provider)}`; try { // Cheap local read first so account switch / reauth / remove controls appear @@ -154,12 +193,12 @@ export function useProviderAccountPools(deps: { const data = await readRoster<{ activeAccountId?: string | null; accounts?: OAuthAccount[] }>(url); if (!Array.isArray(data.accounts)) throw new Error("Invalid account roster"); if (!currentRequest()) return false; - const rows = data.accounts; - setAccountSets(current => currentRequest() ? { ...current, [provider]: { + const rows = selectionRows(data.accounts, data.activeAccountId); + setAccountSets(current => currentRoster() ? { ...current, [provider]: { activeAccountId: data.activeAccountId ?? null, accounts: mergeQuotaRows(rows, current[provider]?.accounts ?? [], false), } } : current); - setAccountLoadStates(current => currentRequest() ? { ...current, [provider]: "ready" } : current); + setAccountLoadStates(current => currentRoster() ? { ...current, [provider]: "ready" } : current); if (!rows.some(supportsQuotaRead)) return true; const enrich = async (): Promise => { @@ -167,14 +206,16 @@ export function useProviderAccountPools(deps: { const quotaData = await readRoster<{ activeAccountId?: string | null; accounts?: OAuthAccount[] }>(`${url}"a=1${refresh ? "&refresh=1" : ""}`); if (!Array.isArray(quotaData.accounts)) throw new Error("Invalid account quota roster"); if (!currentRequest()) return false; - const enriched = quotaData.accounts; - setAccountSets(current => currentRequest() ? { + const enriched = selectionRows(quotaData.accounts, quotaData.activeAccountId); + setAccountSets(current => !currentRequest() ? current : !currentRoster() ? { + ...current, [provider]: { ...current[provider], accounts: mergeLateQuotaRows(current[provider]?.accounts ?? [], enriched) }, + } : { ...current, [provider]: { - activeAccountId: quotaData.activeAccountId ?? data.activeAccountId ?? null, + activeAccountId: quotaData.activeAccountId === undefined ? data.activeAccountId ?? null : quotaData.activeAccountId, accounts: mergeQuotaRows(enriched, current[provider]?.accounts ?? [], true), }, - } : current); + }); return !enriched.some(row => row.quotaUnavailable === true); } catch { if (!currentRequest()) return false; @@ -188,9 +229,9 @@ export function useProviderAccountPools(deps: { void enrich(); return true; } catch { - if (!currentRequest()) return false; - setAccountLoadStates(current => currentRequest() ? { ...current, [provider]: "error" } : current); - setAccountSets(current => currentRequest() && current[provider] ? { + if (!currentRoster()) return false; + setAccountLoadStates(current => currentRoster() ? { ...current, [provider]: "error" } : current); + setAccountSets(current => currentRoster() && current[provider] ? { ...current, [provider]: { ...current[provider], accounts: unavailableQuotaRows(current[provider].accounts) }, } : current); return false; @@ -205,29 +246,35 @@ export function useProviderAccountPools(deps: { const key = `key:${name}`; const generation = (accountRequestGenerationRef.current[key] ?? 0) + 1; accountRequestGenerationRef.current[key] = generation; + const rosterGeneration = (rosterGenerationRef.current[key] ?? 0) + 1; + rosterGenerationRef.current[key] = rosterGeneration; const currentRequest = () => aliveRef.current && mountedRef.current && serverRef.current === apiBase && accountRequestGenerationRef.current[key] === generation; + const currentRoster = () => currentRequest() && rosterGenerationRef.current[key] === rosterGeneration; const url = `${apiBase}/api/providers/keys?name=${encodeURIComponent(name)}`; - const failed = () => { - if (currentRequest()) setKeyPools(current => currentRequest() + const failed = (enrichment = false) => { + const accepted = () => currentRequest() && (enrichment || currentRoster()); + if (accepted()) setKeyPools(current => accepted() ? { ...current, [name]: unavailableQuotaRows(current[name] ?? []) } : current); return false; }; try { - const data = await readRoster<{ keys?: ApiKeyEntry[] }>(url); + const data = await readRoster<{ activeId?: string | null; keys?: ApiKeyEntry[] }>(url); if (!Array.isArray(data.keys)) throw new Error("Invalid key roster"); if (!currentRequest()) return false; - const rows = data.keys; - setKeyPools(current => currentRequest() ? { ...current, [name]: mergeQuotaRows(rows, current[name] ?? [], false) } : current); + const rows = selectionRows(data.keys, data.activeId); + setKeyPools(current => currentRoster() ? { ...current, [name]: mergeQuotaRows(rows, current[name] ?? [], false) } : current); if (!rows.some(supportsQuotaRead)) return true; const enrich = async (): Promise => { try { - const data = await readRoster<{ keys?: ApiKeyEntry[] }>(`${url}"a=1${refresh ? "&refresh=1" : ""}`); + const data = await readRoster<{ activeId?: string | null; keys?: ApiKeyEntry[] }>(`${url}"a=1${refresh ? "&refresh=1" : ""}`); if (!Array.isArray(data.keys)) throw new Error("Invalid key quota roster"); if (!currentRequest()) return false; - const enriched = data.keys; - setKeyPools(current => currentRequest() ? { ...current, [name]: mergeQuotaRows(enriched, current[name] ?? [], true) } : current); + const enriched = selectionRows(data.keys, data.activeId); + setKeyPools(current => currentRequest() ? { ...current, [name]: currentRoster() + ? mergeQuotaRows(enriched, current[name] ?? [], true) + : mergeLateQuotaRows(current[name] ?? [], enriched) } : current); return !enriched.some(row => row.quotaUnavailable === true); - } catch { return failed(); } + } catch { return failed(true); } }; if (refresh) return await enrich(); void enrich(); @@ -237,22 +284,92 @@ export function useProviderAccountPools(deps: { return results.every(Boolean); }, [apiBase, aliveRef, readRoster]); + const refreshAccountRosters = useCallback(async (target?: AccountSelectionTarget, signal?: AbortSignal): Promise => { + if (!aliveRef.current || !mountedRef.current || serverRef.current !== apiBase || signal?.aborted) return false; + const targets: AccountSelectionTarget[] = target ? [target] : Object.entries(config?.providers ?? {}).flatMap(([provider, p]) => + p.authMode === "oauth" && provider !== "openai" ? [{ provider, kind: "oauth" as const }] + : p.hasApiKey && p.authMode !== "oauth" && p.authMode !== "forward" ? [{ provider, kind: "api-key" as const }] : []); + const results = await Promise.all(targets.map(async ({ provider, kind }) => { + const key = `${kind === "oauth" ? "oauth" : "key"}:${provider}`; + // PUT settlement always reconciles, including events received while it is pending. + if (selectionMutationsRef.current.has(key)) return false; + const generation = (rosterGenerationRef.current[key] ?? 0) + 1; + rosterGenerationRef.current[key] = generation; + const currentRequest = () => aliveRef.current && mountedRef.current && serverRef.current === apiBase + && !signal?.aborted && rosterGenerationRef.current[key] === generation && !selectionMutationsRef.current.has(key); + try { + if (kind === "oauth") { + const data = await readRoster<{ activeAccountId?: string | null; accounts?: OAuthAccount[] }>( + `${apiBase}/api/oauth/accounts?provider=${encodeURIComponent(provider)}`, signal); + if (!Array.isArray(data.accounts) || !currentRequest()) return false; + const rows = selectionRows(data.accounts, data.activeAccountId); + setAccountSets(current => currentRequest() ? { ...current, [provider]: { + activeAccountId: data.activeAccountId === undefined ? rows.find(row => row.active)?.id ?? null : data.activeAccountId, + accounts: mergeRosterRows(rows, current[provider]?.accounts ?? []), + } } : current); + setAccountLoadStates(current => currentRequest() ? { ...current, [provider]: "ready" } : current); + } else { + const data = await readRoster<{ activeId?: string | null; keys?: ApiKeyEntry[] }>( + `${apiBase}/api/providers/keys?name=${encodeURIComponent(provider)}`, signal); + if (!Array.isArray(data.keys) || !currentRequest()) return false; + const rows = selectionRows(data.keys, data.activeId); + setKeyPools(current => currentRequest() ? { ...current, [provider]: mergeRosterRows(rows, current[provider] ?? []) } : current); + } + return true; + } catch { + // A missed invalidation read does not change quota health; recovery retries it. + return false; + } + })); + return results.every(Boolean); + }, [aliveRef, apiBase, config, readRoster]); + + const invalidateSelectionReads = (provider: string, kind: AccountSelectionTarget["kind"]) => { + const key = `${kind === "oauth" ? "oauth" : "key"}:${provider}`; + accountRequestGenerationRef.current[key] = (accountRequestGenerationRef.current[key] ?? 0) + 1; + rosterGenerationRef.current[key] = (rosterGenerationRef.current[key] ?? 0) + 1; + // An invalidated probe will never settle its old flags. + const settle = (rows: T[]) => rows.map(row => row.quotaPending ? { ...row, quotaPending: false } : row); + if (kind === "oauth") setAccountSets(current => current[provider] ? { + ...current, [provider]: { ...current[provider], accounts: settle(current[provider].accounts) }, + } : current); + else setKeyPools(current => current[provider] ? { ...current, [provider]: settle(current[provider]) } : current); + return key; + }; + const switchAccount = async (provider: string, account: OAuthAccount) => { if (account.active || account.needsReauth || switchingAccountRef.current) return; const target = { provider, accountId: account.id }; switchingAccountRef.current = target; setSwitchingAccount(target); + const key = invalidateSelectionReads(provider, "oauth"); + const mutation = Symbol(); + selectionMutationsRef.current.set(key, mutation); + const currentMutation = () => aliveRef.current && mountedRef.current && serverRef.current === apiBase && selectionMutationsRef.current.get(key) === mutation; const label = oauthAccountDisplayLabel(accountSets[provider]?.accounts ?? [account], account, t); try { const res = await fetch(`${apiBase}/api/oauth/accounts/active`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ provider, accountId: account.id }) }); + const result = await res.json().catch(() => ({})) as { activeAccountId?: string | null }; + if (!currentMutation()) return; + invalidateSelectionReads(provider, "oauth"); if (!res.ok) { notify(t("prov.accountSwitchFail"), false); return; } - const refreshed = await fetchAccountSets([provider]); + const selected = result.activeAccountId === undefined ? account.id : result.activeAccountId; + setAccountSets(current => current[provider] ? { ...current, [provider]: { + ...current[provider], activeAccountId: selected, accounts: selectionRows(current[provider].accounts, selected), + } } : current); + selectionMutationsRef.current.delete(key); + const refreshed = await refreshAccountRosters({ provider, kind: "oauth" }); await Promise.all([fetchOauth(), fetchProviderQuotas(true)]); if (!refreshed) { notify(t("pws.accountsLoadFailed"), false); return; } notify(t("prov.accountSwitched", { email: label }), true); } catch { - notify(t("prov.accountSwitchFail"), false); + if (currentMutation()) notify(t("prov.accountSwitchFail"), false); } finally { + if (currentMutation()) { + invalidateSelectionReads(provider, "oauth"); + selectionMutationsRef.current.delete(key); + void refreshAccountRosters({ provider, kind: "oauth" }); + } if (switchingAccountRef.current?.provider === target.provider && switchingAccountRef.current.accountId === target.accountId) { switchingAccountRef.current = null; if (aliveRef.current) setSwitchingAccount(null); @@ -261,15 +378,29 @@ export function useProviderAccountPools(deps: { }; const switchApiKey = async (provider: string, entry: ApiKeyEntry) => { - if (entry.active) return; - const res = await fetch(`${apiBase}/api/providers/keys/active`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: provider, id: entry.id }) }); - if (res.ok) { + if (entry.active || selectionMutationsRef.current.has(`key:${provider}`)) return; + const key = invalidateSelectionReads(provider, "api-key"); + const mutation = Symbol(); + selectionMutationsRef.current.set(key, mutation); + const currentMutation = () => aliveRef.current && mountedRef.current && serverRef.current === apiBase && selectionMutationsRef.current.get(key) === mutation; + try { + const res = await fetch(`${apiBase}/api/providers/keys/active`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: provider, id: entry.id }) }); + const data = await res.json().catch(() => ({})) as { activeId?: string | null; error?: string }; + if (!currentMutation()) return; + invalidateSelectionReads(provider, "api-key"); + if (!res.ok) { notify(data.error || t("prov.keySwitchFail"), false); return; } + const selected = data.activeId === undefined ? entry.id : data.activeId; + setKeyPools(current => current[provider] ? { ...current, [provider]: selectionRows(current[provider], selected) } : current); notify(t("prov.keySwitched", { key: entry.label ?? entry.masked }), true); - void fetchKeyPools(Object.keys(keyPools)); void fetchProviderQuotas(true); - } else { - const data = await res.json().catch(() => ({})); - notify(data.error || t("prov.keySwitchFail"), false); + } catch { + if (currentMutation()) notify(t("prov.keySwitchFail"), false); + } finally { + if (currentMutation()) { + invalidateSelectionReads(provider, "api-key"); + selectionMutationsRef.current.delete(key); + void refreshAccountRosters({ provider, kind: "api-key" }); + } } }; @@ -382,7 +513,7 @@ export function useProviderAccountPools(deps: { return { accountSets, accountLoadStates, switchingAccount, openAccounts, keyPools, addingKeyFor, newKeyValue, setAccountSets, setAccountLoadStates, setSwitchingAccount, setOpenAccounts, setKeyPools, setAddingKeyFor, setNewKeyValue, - fetchAccountSets, fetchKeyPools, switchAccount, switchApiKey, removeApiKey, addApiKeyValue, addApiKey, editCredentialAlias, removeAccount, + fetchAccountSets, fetchKeyPools, refreshAccountRosters, switchAccount, switchApiKey, removeApiKey, addApiKeyValue, addApiKey, editCredentialAlias, removeAccount, oauthCardProviders, keyCardProviders, activeAccountNeedsReauth, }; } diff --git a/gui/src/pages/Providers.tsx b/gui/src/pages/Providers.tsx index 779bc99289..b760a822e9 100644 --- a/gui/src/pages/Providers.tsx +++ b/gui/src/pages/Providers.tsx @@ -1,5 +1,5 @@ import { usageSummary30dResourceKey } from "../usage-summary-resource"; -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import ProviderWorkspaceShell, { type AddProviderIntent } from "../components/provider-workspace/ProviderWorkspaceShell"; import ProviderDetails from "../components/provider-workspace/ProviderDetails"; import { isAccountProvider, type WorkspaceProvider } from "../provider-workspace/catalog"; @@ -8,7 +8,7 @@ import { oauthTosRisk } from "../oauth-tos-risk"; import { ToastNotice, type NoticeTone } from "../ui"; import { IconPlus } from "../icons"; import { useT } from "../i18n/shared"; -import { useProviderAccountPools } from "../hooks/useProviderAccountPools"; +import { useProviderAccountPools, type AccountSelectionTarget } from "../hooks/useProviderAccountPools"; import { useCodexAccountPool } from "../hooks/useCodexAccountPool"; import { useJsonConfigEditor } from "../hooks/useJsonConfigEditor"; import { useKeyedClientResource } from "../client-resource"; @@ -75,6 +75,119 @@ export function useQuotaRefreshCoordinator(apiBase: string) { return { quotaRefresh, invalidateProviderQuotas, settleQuotaRefresh, beginQuotaRefresh }; } +/** One authenticated SSE connection for the page. The existing scheduler owns recovery. */ +function useAccountSelectionEvents( + apiBase: string, + enabled: boolean, + refresh: (target?: AccountSelectionTarget) => Promise, +) { + const refreshRef = useRef(refresh); + useLayoutEffect(() => { refreshRef.current = refresh; }); + const recoverRef = useRef<() => void>(() => {}); + useEffect(() => { + if (!enabled) return; + let stopped = false; + type Connection = { controller: AbortController; reader?: ReadableStreamDefaultReader; lastActivity: number }; + let connection: Connection | null = null; + const close = (current: Connection) => { + current.controller.abort(); + void current.reader?.cancel().catch(() => {}); + }; + const connect = () => { + if (stopped || connection) return; + const current: Connection = { controller: new AbortController(), lastActivity: Date.now() }; + connection = current; + void (async () => { + try { + // Native EventSource cannot send the session/relay headers installed by api.ts. + const response = await fetch(`${apiBase}/api/accounts/events`, { + signal: current.controller.signal, credentials: "same-origin", headers: { Accept: "text/event-stream" }, + }); + if (!response.ok || !response.headers.get("content-type")?.includes("text/event-stream") || !response.body) { + throw new Error("Account selection stream unavailable"); + } + if (stopped || current.controller.signal.aborted) { await response.body.cancel(); return; } + const reader = response.body.getReader(); + current.reader = reader; + const decoder = new TextDecoder(); + const revisions = new Map(); + const pending = new Map(); + let refreshAll = false; + let queued = false; + const flush = () => { + if (queued) return; + queued = true; + void Promise.resolve().then(async () => { + queued = false; + if (stopped || current.controller.signal.aborted) return; + const targets = refreshAll ? [undefined] : [...pending.values()]; + refreshAll = false; + pending.clear(); + await Promise.all(targets.map(target => refreshRef.current(target))); + }).catch(() => { /* The recovery tick retries failed invalidation reads. */ }); + }; + let buffer = ""; + let event = ""; + let data: string[] = []; + let frameSize = 0; + const dispatch = () => { + let value: { provider?: unknown; kind?: unknown; revision?: unknown }; + try { value = JSON.parse(data.join("\n")) as typeof value; } catch { return; } + if (!value || typeof value !== "object" || typeof value.revision !== "number" || !Number.isSafeInteger(value.revision) || value.revision < 0) return; + if (event === "ready") { + revisions.clear(); + refreshAll = true; + flush(); + } else if (event === "account-selection" && typeof value.provider === "string" && value.provider + && (value.kind === "oauth" || value.kind === "api-key")) { + const key = `${value.kind}:${value.provider}`; + if (value.revision <= (revisions.get(key) ?? -1)) return; + revisions.set(key, value.revision); + pending.set(key, { provider: value.provider, kind: value.kind }); + flush(); + } + }; + while (!stopped && !current.controller.signal.aborted) { + const chunk = await reader.read(); + if (chunk.done) break; + current.lastActivity = Date.now(); + buffer += decoder.decode(chunk.value, { stream: true }); + let end: number; + while ((end = buffer.indexOf("\n")) !== -1) { + const line = buffer.slice(0, end).replace(/\r$/, ""); + buffer = buffer.slice(end + 1); + frameSize += line.length; + if (frameSize > 16_384) throw new Error("Account selection event too large"); + if (!line) { dispatch(); event = ""; data = []; frameSize = 0; } + else if (line.startsWith("event:")) event = line.slice(6).replace(/^ /, ""); + else if (line.startsWith("data:")) data.push(line.slice(5).replace(/^ /, "")); + } + if (buffer.length + frameSize > 16_384) throw new Error("Account selection event too large"); + } + } catch { + // Authentication recovery is owned by api.ts; disconnected streams use the shared tick. + } finally { + close(current); + if (connection === current) connection = null; + } + })(); + }; + const recover = () => { + // Also retire a hung handshake or a silent connection that lost its heartbeat. + if (connection && Date.now() - connection.lastActivity > 60_000) { close(connection); connection = null; } + connect(); + }; + recoverRef.current = recover; + connect(); + return () => { + stopped = true; + recoverRef.current = () => {}; + if (connection) close(connection); + }; + }, [apiBase, enabled]); + return useCallback(() => recoverRef.current(), []); +} + export default function Providers({ apiBase }: { apiBase: string }) { const t = useT(); const configCacheKey = `ocx.providers.config.v1:${apiBase}`; @@ -243,9 +356,23 @@ export default function Providers({ apiBase }: { apiBase: string }) { }); const { accountSets, setAccountSets, accountLoadStates, switchingAccount, keyPools, fetchAccountSets, fetchKeyPools, + refreshAccountRosters, oauthCardProviders, keyCardProviders, switchAccount, switchApiKey, removeApiKey, addApiKeyValue, editCredentialAlias, removeAccount, activeAccountNeedsReauth, } = pools; + const refreshSelection = useCallback((target?: AccountSelectionTarget) => { + if (target && !(target.kind === "oauth" ? oauthCardProviders : keyCardProviders).includes(target.provider)) return Promise.resolve(true); + return refreshAccountRosters(target); + }, [refreshAccountRosters, oauthCardProviders, keyCardProviders]); + const recoverSelectionStream = useAccountSelectionEvents(apiBase, config !== null, refreshSelection); + const rosterKey = JSON.stringify([apiBase, [...oauthCardProviders].sort(), [...keyCardProviders].sort()]); + const rosterRecoveryKeyRef = useRef(null); + useKeyedClientResource(`provider-rosters:${rosterKey}`, [rosterKey], async signal => { + // Existing bootstrap effects own the first enriched reads. This resource is recovery only. + if (rosterRecoveryKeyRef.current !== rosterKey) { rosterRecoveryKeyRef.current = rosterKey; return true; } + recoverSelectionStream(); + return refreshAccountRosters(undefined, signal); + }, { enabled: config !== null, pollMs: 30_000 }); const jsonEditor = useJsonConfigEditor({ apiBase, config, notify, diff --git a/gui/src/pages/use-providers-oauth.ts b/gui/src/pages/use-providers-oauth.ts index d804ec3739..3440939ef1 100644 --- a/gui/src/pages/use-providers-oauth.ts +++ b/gui/src/pages/use-providers-oauth.ts @@ -143,11 +143,11 @@ export function useProvidersOAuth({ finished = true; break; } - // Seed the account list from the status poll immediately so Accounts does not - // briefly render empty while the follow-up /api/oauth/accounts round-trip runs. + // Seed only a missing roster. This status read can predate a manual selection; + // an existing roster is reconciled by the guarded /accounts read below. if (s.accounts) { const activeFromRow = s.accounts.find(a => a.active)?.id ?? null; - setAccountSets(current => ({ + setAccountSets(current => current[provider] ? current : ({ ...current, [provider]: { activeAccountId: s.activeAccountId ?? activeFromRow, diff --git a/gui/tests/provider-account-quota-loading.test.tsx b/gui/tests/provider-account-quota-loading.test.tsx index 5b856945b4..d5df0ee4a8 100644 --- a/gui/tests/provider-account-quota-loading.test.tsx +++ b/gui/tests/provider-account-quota-loading.test.tsx @@ -5,13 +5,13 @@ import { createRoot, type Root } from "react-dom/client"; import { useProviderAccountPools, type OAuthAccount, type ApiKeyEntry } from "../src/hooks/useProviderAccountPools"; const globals = ["document", "window", "navigator", "fetch", "IS_REACT_ACT_ENVIRONMENT"] as const; -let previous: Record<(typeof globals)[number], unknown>; +let previous: Record<(typeof globals)[number], PropertyDescriptor | undefined>; let win: Window; let root: Root | null; let host: HTMLElement; let pools: ReturnType; let requests: Array<{ url: string; signal?: AbortSignal | null }>; -let respond: (url: string, signal?: AbortSignal | null) => Promise; +let respond: (url: string, signal?: AbortSignal | null, init?: RequestInit) => Promise; const noop = async () => {}; const reading = { fiveHourPercent: 21, weeklyPercent: 34, updatedAt: 1_700_000_000_000 }; @@ -29,7 +29,7 @@ function Harness({ apiBase = "/quota-hook" }: { apiBase?: string }) { return null; } beforeEach(async () => { - previous = Object.fromEntries(globals.map(key => [key, Reflect.get(globalThis, key)])) as typeof previous; + previous = Object.fromEntries(globals.map(key => [key, Object.getOwnPropertyDescriptor(globalThis, key)])) as typeof previous; win = new Window({ url: "http://localhost" }); Object.defineProperties(globalThis, { document: { configurable: true, value: win.document }, window: { configurable: true, value: win }, @@ -39,18 +39,148 @@ beforeEach(async () => { respond = async () => Response.json({ accounts: [], keys: [] }); Object.defineProperty(globalThis, "fetch", { configurable: true, value: (input: RequestInfo | URL, init?: RequestInit) => { requests.push({ url: String(input), signal: init?.signal }); - return respond(String(input), init?.signal); + return respond(String(input), init?.signal, init); } }); host = win.document.createElement("div") as unknown as HTMLElement; win.document.body.appendChild(host as never); await act(async () => { root = createRoot(host); root.render(); }); }); + +for (const kind of ["oauth", "api-key"] as const) { + test(`${kind} stale base failure cannot mark a newer successful selection read unavailable`, async () => { + const rows = ["a", "b"].map(id => ({ id, masked: id, active: id === "a", quotaMode: "probe" as const, + quota: reading, quotaPending: false, quotaUnavailable: false })); + await act(async () => { + if (kind === "oauth") pools.setAccountSets({ fixture: { activeAccountId: "a", accounts: rows } }); + else pools.setKeyPools({ fixture: rows }); + }); + const old = deferred(); + respond = async () => old.promise; + let full!: Promise; + await act(async () => { full = kind === "oauth" ? pools.fetchAccountSets(["fixture"]) : pools.fetchKeyPools(["fixture"]); }); + respond = async () => Response.json({ activeAccountId: "b", activeId: "b", accounts: rows, keys: rows }); + await act(async () => { await pools.refreshAccountRosters({ provider: "fixture", kind }); }); + await act(async () => { old.resolve(new Response(null, { status: 503 })); expect(await full).toBe(false); }); + const result = kind === "oauth" ? pools.accountSets.fixture.accounts : pools.keyPools.fixture; + expect(result.find(row => row.active)?.id).toBe("b"); + expect(result[0]).toMatchObject({ quota: reading, quotaUnavailable: false, quotaPending: false }); + }); + + test(`${kind} selection reads preserve settled quota flags without enrichment`, async () => { + const rows = ["a", "b"].map(id => ({ id, active: id === "a", masked: id, + quotaMode: "probe" as const, quota: reading, quotaPending: false, quotaUnavailable: true })); + await act(async () => { + if (kind === "oauth") pools.setAccountSets({ fixture: { activeAccountId: "a", accounts: rows } }); + else pools.setKeyPools({ fixture: rows }); + }); + respond = async () => Response.json({ activeAccountId: "b", activeId: "b", + accounts: rows.map(row => ({ id: row.id, active: row.id === "b", quotaMode: "probe" })), + keys: rows.map(row => ({ id: row.id, masked: row.id, active: row.id === "b", quotaMode: "probe" })), + }); + await act(async () => { await pools.refreshAccountRosters({ provider: "fixture", kind }); }); + const result = kind === "oauth" ? pools.accountSets.fixture.accounts : pools.keyPools.fixture; + expect(result.find(row => row.active)?.id).toBe("b"); + expect(result[0]).toMatchObject({ quota: reading, quotaPending: false, quotaUnavailable: true }); + expect(requests).toHaveLength(1); + expect(requests[0].url).not.toContain("quota="); + }); + + test(`${kind} PUT invalidates old reads at start and settle and publishes the accepted selection immediately`, async () => { + const rows = ["a", "b"].map(id => ({ id, active: id === "a", masked: id, quotaMode: "unsupported" as const })); + await act(async () => { + if (kind === "oauth") pools.setAccountSets({ fixture: { activeAccountId: "a", accounts: rows } }); + else pools.setKeyPools({ fixture: rows }); + }); + const beforePut = deferred(); + const duringPut = deferred(); + const afterPut = deferred(); + const put = deferred(); + let reads = 0; + respond = async (_url, _signal, init) => init?.method === "PUT" ? put.promise + : ++reads === 1 ? beforePut.promise : reads === 2 ? duringPut.promise : afterPut.promise; + const read = () => kind === "oauth" ? pools.fetchAccountSets(["fixture"]) : pools.fetchKeyPools(["fixture"]); + let old!: Promise; + let during!: Promise; + let switched!: Promise; + await act(async () => { old = read(); }); + await act(async () => { switched = kind === "oauth" ? pools.switchAccount("fixture", rows[1]) : pools.switchApiKey("fixture", rows[1]); }); + const roster = (id: string) => Response.json({ activeAccountId: id, activeId: id, + accounts: rows.map(row => ({ ...row, active: row.id === id })), keys: rows.map(row => ({ ...row, active: row.id === id })) }); + await act(async () => { beforePut.resolve(roster("b")); expect(await old).toBe(false); }); + expect((kind === "oauth" ? pools.accountSets.fixture.accounts : pools.keyPools.fixture).find(row => row.active)?.id).toBe("a"); + await act(async () => { during = read(); }); + await act(async () => { put.resolve(Response.json({ ok: true, activeAccountId: "b", activeId: "b" })); }); + expect((kind === "oauth" ? pools.accountSets.fixture.accounts : pools.keyPools.fixture).find(row => row.active)?.id).toBe("b"); + await act(async () => { duringPut.resolve(roster("a")); expect(await during).toBe(false); }); + expect((kind === "oauth" ? pools.accountSets.fixture.accounts : pools.keyPools.fixture).find(row => row.active)?.id).toBe("b"); + await act(async () => { afterPut.resolve(roster("b")); await switched; }); + }); +} afterEach(async () => { if (root) await act(async () => { root!.unmount(); root = null; }); - for (const key of globals) Object.defineProperty(globalThis, key, { configurable: true, value: previous[key] }); + for (const key of globals) { + const descriptor = previous[key]; + if (descriptor) Object.defineProperty(globalThis, key, descriptor); + else Reflect.deleteProperty(globalThis, key); + } await win.happyDOM.close(); }); +for (const kind of ["oauth", "api-key"] as const) { + test(`${kind} push refresh overtakes a slow quota probe while late quota cannot change selection or resurrect rows`, async () => { + const quota = deferred(); + const started = deferred(); + const original = ["a", "b", "removed"].map(id => ({ id, masked: id, active: id === "a", quotaMode: "probe" as const })); + respond = async url => { + if (url.includes("quota=1")) { started.resolve(); return quota.promise; } + return Response.json({ activeAccountId: "a", activeId: "a", accounts: original, keys: original }); + }; + let full!: Promise; + await act(async () => { + full = kind === "oauth" ? pools.fetchAccountSets(["fixture"], true) : pools.fetchKeyPools(["fixture"], true); + await started.promise; + }); + const latest = original.filter(row => row.id !== "removed").map(row => ({ ...row, active: row.id === "b" })); + respond = async () => Response.json({ activeAccountId: "b", activeId: "b", accounts: latest, keys: latest }); + await act(async () => { expect(await pools.refreshAccountRosters({ provider: "fixture", kind })).toBe(true); }); + const current = () => kind === "oauth" ? pools.accountSets.fixture.accounts : pools.keyPools.fixture; + expect(current().find(row => row.active)?.id).toBe("b"); + expect(current()[0].quotaPending).toBe(true); + const enriched = original.map(row => ({ ...row, quota: reading })); + await act(async () => { + quota.resolve(Response.json({ activeAccountId: "a", activeId: "a", accounts: enriched, keys: enriched })); + expect(await full).toBe(true); + }); + expect(current().map(row => row.id)).toEqual(["a", "b"]); + expect(current().find(row => row.active)?.id).toBe("b"); + expect(current()[0]).toMatchObject({ quota: reading, quotaPending: false }); + expect(requests.filter(request => request.url.includes("quota=1"))).toHaveLength(1); + }); + + test(`${kind} rejected manual PUT retires reads begun during the write and reconciles without changing quota health`, async () => { + const rows = ["a", "b"].map(id => ({ id, masked: id, active: id === "a", quotaMode: "probe" as const, + quota: reading, quotaUnavailable: true, quotaPending: false })); + await act(async () => { + if (kind === "oauth") pools.setAccountSets({ fixture: { activeAccountId: "a", accounts: rows } }); + else pools.setKeyPools({ fixture: rows }); + }); + const put = deferred(); + const during = deferred(); + let reads = 0; + respond = async (_url, _signal, init) => init?.method === "PUT" ? put.promise : ++reads === 1 ? during.promise + : Response.json({ activeAccountId: "a", activeId: "a", accounts: rows, keys: rows }); + let changed!: Promise; + let old!: Promise; + await act(async () => { changed = kind === "oauth" ? pools.switchAccount("fixture", rows[1]) : pools.switchApiKey("fixture", rows[1]); }); + await act(async () => { old = kind === "oauth" ? pools.fetchAccountSets(["fixture"]) : pools.fetchKeyPools(["fixture"]); }); + await act(async () => { put.resolve(new Response(null, { status: 409 })); await changed; }); + await act(async () => { during.resolve(Response.json({ activeAccountId: "b", activeId: "b", accounts: [], keys: [] })); expect(await old).toBe(false); }); + const current = kind === "oauth" ? pools.accountSets.fixture.accounts : pools.keyPools.fixture; + expect(current.find(row => row.active)?.id).toBe("a"); + expect(current[0]).toMatchObject({ quota: reading, quotaUnavailable: true, quotaPending: false }); + }); +} + test("cheap probe rows paint with same-ID last-good; forced enrichment awaits and HTTP failure clears pending", async () => { const account: OAuthAccount = { id: "same", active: true, quotaMode: "probe", quota: reading }; await act(async () => { pools.setAccountSets({ oauth: { activeAccountId: "same", accounts: [account, { ...account, id: "removed" }] } }); }); diff --git a/gui/tests/provider-revalidation-policy.test.tsx b/gui/tests/provider-revalidation-policy.test.tsx index df5a983d80..d29860929f 100644 --- a/gui/tests/provider-revalidation-policy.test.tsx +++ b/gui/tests/provider-revalidation-policy.test.tsx @@ -4,7 +4,7 @@ import { act } from "react"; import { createRoot, type Root } from "react-dom/client"; import Providers from "../src/pages/Providers"; import { LanguageProvider } from "../src/i18n/provider"; -import { clearClientResourceStoresForTests } from "../src/client-resource"; +import { clearClientResourceStoresForTests, pollBucketCountForTests } from "../src/client-resource"; /** * Quota revalidation policy. @@ -19,7 +19,7 @@ import { clearClientResourceStoresForTests } from "../src/client-resource"; */ const globals = ["document", "window", "navigator", "localStorage", "sessionStorage", "fetch", "IS_REACT_ACT_ENVIRONMENT"] as const; -let previousGlobals: Record<(typeof globals)[number], unknown>; +let previousGlobals: Record<(typeof globals)[number], PropertyDescriptor | undefined>; let testWindow: Window; let container: HTMLElement; let root: Root | null = null; @@ -28,7 +28,7 @@ let quotaCalls: string[] = []; const PROVIDERS = ["anthropic", "cursor", "kimi"]; beforeEach(() => { - previousGlobals = Object.fromEntries(globals.map(k => [k, Reflect.get(globalThis, k)])) as typeof previousGlobals; + previousGlobals = Object.fromEntries(globals.map(k => [k, Object.getOwnPropertyDescriptor(globalThis, k)])) as typeof previousGlobals; clearClientResourceStoresForTests(); testWindow = new Window({ url: "http://localhost/#providers" }); Object.defineProperty(testWindow.navigator, "language", { configurable: true, value: "en-US" }); @@ -100,7 +100,9 @@ afterEach(async () => { } clearClientResourceStoresForTests(); for (const key of globals) { - Object.defineProperty(globalThis, key, { configurable: true, value: previousGlobals[key] }); + const descriptor = previousGlobals[key]; + if (descriptor) Object.defineProperty(globalThis, key, descriptor); + else Reflect.deleteProperty(globalThis, key); } }); @@ -221,3 +223,92 @@ for (const kind of ["oauth", "key", "codex"] as const) { expect(container.textContent).toContain("Quota check completed"); }); } + +for (const kind of ["oauth", "api-key"] as const) { + test(`${kind} selection event updates the rendered active row without a poll tick or quota probe`, async () => { + const name = kind === "oauth" ? "cursor" : "deepseek"; + let selected = "a"; + let stream!: ReadableStreamDefaultController; + let streamSignal: AbortSignal | null | undefined; + let subscriptions = 0; + let cancelled = false; + const reads: string[] = []; + const encoder = new TextEncoder(); + const send = (event: string, data: unknown) => { + const bytes = encoder.encode(`event: ${event}\r\ndata: ${JSON.stringify(data)}\r\n\r\n`); + // Network chunks need not end on either a line or an event boundary. + stream.enqueue(bytes.slice(0, 11)); + stream.enqueue(bytes.slice(11)); + }; + Object.defineProperty(globalThis, "fetch", { configurable: true, value: async (input: RequestInfo | URL, init?: RequestInit) => { + const url = new URL(String(input), "http://localhost"); + reads.push(url.pathname + url.search); + if (url.pathname === "/api/accounts/events") { + subscriptions += 1; + streamSignal = init?.signal; + return new Response(new ReadableStream({ + start(controller) { stream = controller; }, cancel() { cancelled = true; }, + }), { headers: { "Content-Type": "text/event-stream" } }); + } + if (url.pathname === "/api/config") return Response.json({ port: 10100, defaultProvider: name, providers: { + [name]: { adapter: "openai-chat", authMode: kind === "oauth" ? "oauth" : "key", hasApiKey: kind === "api-key", baseUrl: "https://fixture.test/v1" }, + } }); + if (url.pathname === "/api/oauth/providers") return Response.json({ providers: kind === "oauth" ? [name] : [] }); + if (url.pathname === "/api/oauth/status") return Response.json({ loggedIn: true }); + if (url.pathname === "/api/oauth/accounts" || url.pathname === "/api/providers/keys") { + const rows = ["a", "b"].map(id => ({ id, alias: `Choice ${id.toUpperCase()}`, label: `Choice ${id.toUpperCase()}`, masked: id, + active: selected === id, quotaMode: "probe", ...(url.searchParams.has("quota") ? { + quota: { fiveHourPercent: id === "a" ? 30 : 11, updatedAt: 1_700_000_000_000 }, + } : {}), + })); + return Response.json({ activeAccountId: selected, activeId: selected, accounts: rows, keys: rows }); + } + if (url.pathname === "/api/codex-auth/accounts") return Response.json({ accounts: [] }); + if (url.pathname === "/api/codex-auth/active") return Response.json({ activeCodexAccountId: null }); + if (url.pathname === "/api/provider-quotas") return Response.json({ reports: [] }); + if (url.pathname === "/api/selected-models") return Response.json({ models: {} }); + if (url.pathname === "/api/usage") return Response.json({ providers: [], models: [] }); + return Response.json({}); + } }); + await act(async () => { root = createRoot(container); root.render(); }); + await act(async () => { container.querySelector(".providers-workspace-rail-row")!.click(); }); + const accountsTab = Array.from(container.querySelectorAll("[role=tab]")) + .find(button => /Accounts|Keys/.test(button.textContent ?? "")); + expect(accountsTab).toBeDefined(); + await act(async () => { accountsTab!.click(); }); + expect(container.querySelector(".pwi-auth-acct--active")?.textContent).toContain("Choice A"); + expect(subscriptions).toBe(1); + const quotaReads = () => reads.filter(path => path.includes("quota=1") || path.startsWith("/api/provider-quotas")).length; + const initialQuotas = quotaReads(); + const initialReads = reads.length; + await act(async () => { send("ready", { revision: 0 }); }); + expect(reads.length).toBeGreaterThan(initialReads); + selected = "b"; + await act(async () => { send("account-selection", { provider: name, kind, revision: 1 }); }); + expect(container.querySelector(".pwi-auth-acct--active")?.textContent).toContain("Choice B"); + expect(container.querySelector(".pwi-auth-acct--active")?.textContent).not.toContain("Choice A"); + expect(quotaReads()).toBe(initialQuotas); + expect(subscriptions).toBe(1); + expect(pollBucketCountForTests()).toBe(1); + const afterEvent = reads.length; + await act(async () => { + send("account-selection", { provider: name, kind, revision: 1 }); + send("account-selection", { provider: "unknown-provider", kind, revision: 2 }); + }); + expect(reads).toHaveLength(afterEvent); + // A disconnected connection is recovered by the existing scheduler's visibility wake. + await act(async () => { stream.close(); }); + selected = "a"; + for (const state of ["hidden", "visible"]) { + Object.defineProperty(testWindow.document, "visibilityState", { configurable: true, value: state }); + await act(async () => { testWindow.document.dispatchEvent(new testWindow.Event("visibilitychange")); }); + } + expect(subscriptions).toBe(2); + await act(async () => { send("ready", { revision: 0 }); }); + expect(container.querySelector(".pwi-auth-acct--active")?.textContent).toContain("Choice A"); + expect(quotaReads()).toBe(initialQuotas); + await act(async () => { root!.unmount(); root = null; }); + expect(streamSignal?.aborted).toBe(true); + expect(cancelled).toBe(true); + }); +} diff --git a/src/lib/account-selection-events.ts b/src/lib/account-selection-events.ts new file mode 100644 index 0000000000..655dd9aa99 --- /dev/null +++ b/src/lib/account-selection-events.ts @@ -0,0 +1,32 @@ +/** Process-local invalidations. Connection limits, buffering, and timers belong to consumers. */ +export type AccountSelectionEvent = { + provider: string; + kind: "oauth" | "api-key"; + revision: number; +}; + +const listeners = new Set<(event: AccountSelectionEvent) => void>(); +let revision = 0; + +/** Call only after the authoritative selection has been persisted. */ +export function publishAccountSelection(provider: string, kind: AccountSelectionEvent["kind"]): void { + const event: AccountSelectionEvent = Object.freeze({ provider, kind, revision: ++revision }); + for (const listener of [...listeners]) { + try { + listener(event); + } catch { + // A disconnected consumer must not turn a committed write into a reported failure. + } + } +} + +export function subscribeAccountSelections(listener: (event: AccountSelectionEvent) => void): () => void { + // Give each subscription its own lifetime, even when a callback is reused. + const subscription = (event: AccountSelectionEvent) => listener(event); + listeners.add(subscription); + return () => { listeners.delete(subscription); }; +} + +export function currentAccountSelectionRevision(): number { + return revision; +} diff --git a/src/oauth/account-quota-rank.ts b/src/oauth/account-quota-rank.ts index 36360c655b..978f73de9d 100644 --- a/src/oauth/account-quota-rank.ts +++ b/src/oauth/account-quota-rank.ts @@ -67,6 +67,14 @@ function headroomOf(provider: string, accountId: string): number | null { return 100 - Math.max(...percents); } +/** Unknown usage is not exhaustion; Kiro's explicit overage verdict is authoritative. */ +export function isAccountQuotaExhausted(provider: string, accountId: string): boolean { + const exhaustion = provider === "kiro" ? getKiroAccountExhaustion(`${provider}\u0000${accountId}`) : null; + if (exhaustion !== null) return exhaustion.exhausted; + const headroom = headroomOf(provider, accountId); + return headroom !== null && headroom <= 0; +} + /** * Order candidates best-first. * @@ -90,7 +98,7 @@ export function rankAccountsByHeadroom(provider: string, ring: readonly string[] const headroom = headroomOf(provider, id); if (exhaustion !== null || headroom !== null) sawEvidence = true; - if (exhaustion?.exhausted === true) return { id, bucket: RANK_EXHAUSTED, headroom: 0, index }; + if (isAccountQuotaExhausted(provider, id)) return { id, bucket: RANK_EXHAUSTED, headroom: 0, index }; if (headroom === null) return { id, bucket: RANK_UNKNOWN, headroom: 0, index }; return { id, bucket: RANK_HEALTHY, headroom, index }; }); diff --git a/src/oauth/anthropic-routing.ts b/src/oauth/anthropic-routing.ts index 95197ef1f1..7474e533ef 100644 --- a/src/oauth/anthropic-routing.ts +++ b/src/oauth/anthropic-routing.ts @@ -15,7 +15,8 @@ * store (existing OAuth path) so the account is excluded from eligibility. */ import { createHash } from "node:crypto"; -import { setActiveAccount, getAccountSet, getAccountCredential } from "./store"; +import { captureOAuthAccountSelection, commitOAuthAccountSelection, credentialGeneration, getAccountSet, getAccountCredential, getAccountCredentialWithStatus } from "./store"; +import type { OAuthAccessSnapshot } from "./index"; import { getCachedProviderAccountQuota } from "../providers/quota"; import { fallbackCodexAccountLogLabel } from "../codex/account-label"; import { @@ -24,6 +25,7 @@ import { notePoolRotationFailure, notePoolRotationSuccess, pickRoundRobinAccount, + peekRoundRobinAccount, POOL_KEY_ANTHROPIC, seedPoolRotationAccount, } from "../codex/pool-rotation"; @@ -68,6 +70,8 @@ interface AffinityEntry { const upstreamHealth = new Map(); const sessionAffinity = new Map(); +type OAuthAccountSelection = NonNullable>; +let manualPreference: OAuthAccountSelection | null = null; function normalizeAffinityComponent(value: string | null | undefined): string { const normalized = value?.trim() ?? ""; @@ -152,6 +156,7 @@ export function sweepExpiredAnthropicRoutingHealth(now = Date.now()): number { export function clearAnthropicAccountPoolState(): void { upstreamHealth.clear(); sessionAffinity.clear(); + manualPreference = null; quorumCache = null; } @@ -409,7 +414,7 @@ function pickAlternateAnthropicAccount( const strategy = anthropicPoolStrategy(config); const eligible = getEligibleAnthropicAccounts(now).filter(id => id !== excludeId); if (strategy === "round-robin") { - return pickRoundRobinAccount(POOL_KEY_ANTHROPIC, eligible, stickyLimitForPool(config)); + return peekRoundRobinAccount(POOL_KEY_ANTHROPIC, eligible, stickyLimitForPool(config)); } if (strategy === "fill-first") { return pickNextFillFirstAnthropicAccount(config, excludeId, eligible); @@ -434,6 +439,7 @@ export type AnthropicAccountSelectionReason = | "lowest-usage" | "only-eligible" | "round-robin" + | "manual" | "fill-first" | "none" | "all-cooled"; @@ -500,9 +506,8 @@ function pickUnboundStrategyAccount( if (strategy === "round-robin") { const eligible = getEligibleAnthropicAccounts(now); const limit = stickyLimitForPool(config); - const picked = pickRoundRobinAccount(POOL_KEY_ANTHROPIC, eligible, limit); + const picked = peekRoundRobinAccount(POOL_KEY_ANTHROPIC, eligible, limit); if (!picked) return null; - notePoolRotationSuccess(POOL_KEY_ANTHROPIC, picked, limit); return { accountId: picked, reason: "round-robin" }; } @@ -532,13 +537,29 @@ export function resolveAnthropicAccountForSession( return { accountId: set.activeAccountId, reason: "pool-disabled" }; } + // A manual choice is a one-dispatch preference, not a lower-priority quota hint. + // Consume it only after admission commits, so a failed token lookup cannot spend it. + if (manualPreference) { + if (manualPreference.accountId !== set.activeAccountId || manualPreference.revision !== set.selectionRevision) { + manualPreference = null; + } else { + const chosen = manualPreference.accountId; + const quota = getCachedProviderAccountQuota(PROVIDER, chosen); + const exhausted = [quota?.fiveHourPercent, quota?.weeklyPercent, quota?.monthlyPercent, + ...(quota?.customWindows ?? []).map(window => window.percent)] + .some(percent => typeof percent === "number" && percent >= 100); + if (!exhausted && getEligibleAnthropicAccounts(now).includes(chosen)) { + return { accountId: chosen, reason: "manual" }; + } + } + } + const key = normalizeAffinityComponent(sessionKey); if (key) { const affined = sessionAffinity.get(key); if (affined && now - affined.lastUsedAt <= AFFINITY_IDLE_TTL_MS) { const stillThere = set.accounts.some(a => a.id === affined.accountId && a.needsReauth !== true); if (stillThere && !isCooled(affined.accountId, now) && isPoolCredentialUsable(affined.accountId, now)) { - affined.lastUsedAt = now; return { accountId: affined.accountId, reason: "affinity" }; } sessionAffinity.delete(key); @@ -560,12 +581,6 @@ export function resolveAnthropicAccountForSession( const strategyPick = pickUnboundStrategyAccount(config, now); if (strategyPick) { - // Do not promote active here — token validation may still fail. Callers - // (responses/core) promote after getAnthropicPoolAccessToken succeeds. - if (key && normalizeAffinityComponent(strategyPick.accountId)) { - sessionAffinity.set(key, { accountId: strategyPick.accountId, lastUsedAt: now }); - pruneExpiredAffinity(now); - } return { accountId: strategyPick.accountId, reason: strategyPick.reason }; } @@ -611,10 +626,6 @@ export function resolveAnthropicAccountForSession( return { accountId: null, reason: anyCooled ? "all-cooled" : "none" }; } - if (key && normalizeAffinityComponent(accountId)) { - sessionAffinity.set(key, { accountId, lastUsedAt: now }); - pruneExpiredAffinity(now); - } return { accountId, reason }; } @@ -684,20 +695,57 @@ export function rotateAnthropicAccountOn429( return null; } - const affinityKey = normalizeAffinityComponent(sessionKey); - if (affinityKey && normalizeAffinityComponent(next)) { - sessionAffinity.set(affinityKey, { accountId: next, lastUsedAt: now }); - pruneExpiredAffinity(now); - } console.warn( `[anthropic-pool] 429 on ${formatAnthropicAccountOrdinal(failedAccountId)}; failing over to ${formatAnthropicAccountOrdinal(next)}`, ); return next; } -/** Promote dashboard active account after a validated failover target is usable. */ -export function promoteAnthropicActiveAccount(accountId: string): void { - void setActiveAccount(PROVIDER, accountId).catch(() => { /* best-effort */ }); +export interface AnthropicSelectionRoutingOptions { + config: OcxConfig; + sessionKey?: string | null; + reason?: AnthropicAccountSelectionReason; + expectedCredentialGeneration?: string; +} + +/** Commit the selected account before dispatch; rejected proposals have no routing side effects. */ +export async function promoteAnthropicActiveAccount( + accountId: string, + expectedSelection: OAuthAccountSelection | null, + options: AnthropicSelectionRoutingOptions, +): Promise { + if (!expectedSelection || !isPoolCredentialUsable(accountId, Date.now()) || isCooled(accountId, Date.now())) return null; + const committed = await commitOAuthAccountSelection(PROVIDER, accountId, { + expectedSelection, + expectedCredentialGeneration: options.expectedCredentialGeneration, + requireUsableAccount: true, + }); + if (!committed) return null; + return commitAnthropicSelectionRouting(accountId, expectedSelection, committed, options) ? committed : null; +} + +/** Main's shared selection owner calls this only after its authoritative commit succeeds. */ +export function commitAnthropicSelectionRouting( + accountId: string, + expectedSelection: OAuthAccountSelection, + committed: OAuthAccountSelection, + options: AnthropicSelectionRoutingOptions, +): boolean { + if (committed.accountId !== accountId) return false; + const current = captureOAuthAccountSelection(PROVIDER); + if (current?.accountId !== committed.accountId || current.revision !== committed.revision) return false; + if (isAnthropicAccountPoolEnabled(options.config)) { + if (anthropicPoolStrategy(options.config) === "round-robin" && options.reason !== "affinity") { + const limit = stickyLimitForPool(options.config); + const picked = pickRoundRobinAccount(POOL_KEY_ANTHROPIC, getEligibleAnthropicAccounts(), limit); + if (picked !== accountId) seedPoolRotationAccount(POOL_KEY_ANTHROPIC, accountId); + notePoolRotationSuccess(POOL_KEY_ANTHROPIC, accountId, limit); + } + bindAnthropicSessionAffinity(options.sessionKey, accountId); + } + if (manualPreference?.accountId === expectedSelection.accountId + && manualPreference.revision === expectedSelection.revision) manualPreference = null; + return true; } /** @@ -706,6 +754,7 @@ export function promoteAnthropicActiveAccount(accountId: string): void { */ export function resetAnthropicRoutingForManualSelection(accountId: string): void { sessionAffinity.clear(); + manualPreference = captureOAuthAccountSelection(PROVIDER); seedPoolRotationAccount(POOL_KEY_ANTHROPIC, accountId); // A manual account selection is an operator statement about the roster; do not answer the // next activation question from a count read before it. @@ -731,6 +780,17 @@ export async function getAnthropicPoolAccessToken(accountId: string): Promise { + const accessToken = await getAnthropicPoolAccessToken(accountId); + const row = getAccountCredentialWithStatus(PROVIDER, accountId); + if (!row || row.needsReauth || row.credential.access !== accessToken + || row.credential.expires <= Date.now()) { + throw new Error("Anthropic pool credential changed during account selection"); + } + return { provider: PROVIDER, accountId, accessToken, generation: credentialGeneration(row.credential) }; +} + /** * Whether the pool may refresh this account's token. Background `local-cli` slots must not * adopt the global Claude CLI credential (same fail-closed rule as quota probes). diff --git a/src/oauth/generic-account-failover.ts b/src/oauth/generic-account-failover.ts index 321b5f92a1..1ccfaf71df 100644 --- a/src/oauth/generic-account-failover.ts +++ b/src/oauth/generic-account-failover.ts @@ -16,7 +16,7 @@ */ import { getAccountSet } from "./store"; import { getValidAccessSnapshotForAccount, type OAuthAccessSnapshot } from "./index"; -import { exhaustedCooldownMs, hasHeadroomEvidence, rankAccountsByHeadroom } from "./account-quota-rank"; +import { exhaustedCooldownMs, hasHeadroomEvidence, isAccountQuotaExhausted, rankAccountsByHeadroom } from "./account-quota-rank"; import { parseRetryAfterMs } from "../combos/failover"; import { sweepExpiredOnWrite } from "../lib/state-store-sweeper"; import type { OcxConfig, OcxProviderConfig } from "../types"; @@ -61,29 +61,12 @@ interface PresenceEntry { readAt: number; } -/** - * Ordered roster plus the active id, for the pre-dispatch preference. - * - * Same reasoning as the presence cache: `getAccountSet` reads through `loadAuthStore`, - * which chmods and re-parses the whole credential file on every call. Selection needs the - * ORDER and the active id, which the presence count cannot supply, so it gets its own - * TTL-bounded row. Ids and an active pointer only — never a credential. - */ -interface RosterEntry { - ids: string[]; - activeId: string | null; - readAt: number; -} - /** Process-local, like the Anthropic pool's: a restart is allowed to forget a cooldown. */ const health = new Map(); /** Provider -> recent eligible-account count. TTL-bounded; never holds credential material. */ const presence = new Map(); -/** Provider -> recently read roster. TTL-bounded; never holds credential material. */ -const roster = new Map(); - const healthKey = (provider: string, accountId: string) => `${provider}\u0000${accountId}`; function isCooled(provider: string, accountId: string, now: number): boolean { @@ -118,24 +101,6 @@ function eligibleAccountCount(providerName: string, now: number): number { return eligible; } -/** - * Roster ids and the active pointer, read at most once per TTL window. - * - * `needsReauth` accounts are excluded for the same reason the presence count excludes - * them: a revoked credential cannot serve the request we are about to send. - */ -function cachedRoster(providerName: string, now: number): { ids: string[]; activeId: string | null } { - const cached = roster.get(providerName); - if (cached && now >= cached.readAt && now - cached.readAt < PRESENCE_CACHE_TTL_MS) { - return { ids: cached.ids, activeId: cached.activeId }; - } - const set = getAccountSet(providerName); - const ids = set ? set.accounts.filter(a => a.needsReauth !== true).map(a => a.id) : []; - const activeId = set?.activeAccountId ?? null; - roster.set(providerName, { ids, activeId, readAt: now }); - return { ids, activeId }; -} - /** * Presence IS consent (#2568d). * @@ -192,8 +157,7 @@ function isProactivePreferenceEnabled(config: OcxConfig, providerName: string, n if (typeof perProvider === "boolean") { return perProvider && hasFailoverAccountQuorum(providerName, now); } - if (config.oauthAccountFailover?.enabled === false) return false; - return hasFailoverAccountQuorum(providerName, now); + return config.oauthAccountFailover?.enabled === true && hasFailoverAccountQuorum(providerName, now); } /** Accounts that may serve traffic right now: not cooled, not flagged for reauth. */ @@ -240,8 +204,6 @@ export function rotateGenericOAuthAccountOn429( // A rotation means the roster in use just changed; do not answer the next activation question // from a count read before the failure. presence.delete(providerName); - // Same for the selection roster: the next request must not pick from a pre-failure read. - roster.delete(providerName); // Deterministic: start after the failed account so repeated 429s walk the roster instead of // hammering whichever id happens to sort first. The ring is built BEFORE ranking — ranking // the store's own order would change which account a quota-less provider rotates to. @@ -289,13 +251,19 @@ export function preferredInitialAccount( // The PROACTIVE predicate, not the reactive one: this steers a request upstream has not // refused, so `oauthAccountFailover.enabled: false` must still be able to refuse it. if (!isProactivePreferenceEnabled(config, providerName, now)) return null; - // This runs on the initial resolution of EVERY request, and `loadAuthStore` has no - // cache: each call chmods the config dir, chmods the secret, reads the whole file and - // normalizes it (store.ts:136-151). So the store is consulted at most ONCE here, behind - // the same TTL the presence check uses, and never at all for a single-account provider. - const { ids: order, activeId: active } = cachedRoster(providerName, now); + // Read the same authoritative selection the management writer commits. Caching the + // active id separately would delay manual selection and account removal. + const selected = getAccountSet(providerName); + if (!selected) return null; + const active = selected.activeAccountId; + const order = selected.accounts.filter(account => account.needsReauth !== true).map(account => account.id); if (order.length < 2) return null; + const activeRow = selected.accounts.find(account => account.id === active); + if (activeRow && activeRow.needsReauth !== true + && !isCooled(providerName, activeRow.id, now) + && !isAccountQuotaExhausted(providerName, activeRow.id)) return null; + // Evidence is required BEFORE eligibility narrows the field. Without this, a provider // with no quota data at all could still be redirected: cool the active account with a // 429 and the eligible list collapses to one candidate, which any ranking returns @@ -318,11 +286,8 @@ export function preferredInitialAccount( const best = rankAccountsByHeadroom(providerName, candidates)[0] ?? null; // Nothing to do when the ranking agrees with the account we would have used anyway. // - // The roster may be up to PRESENCE_CACHE_TTL_MS old, so this answer is a PREFERENCE the - // caller must be able to abandon: it resolves the account with `requireUsableAccount`, - // which rejects a removed or reauth-flagged account inside the store read it was already - // performing, and falls back to the active account. Validating here instead would mean a - // second uncached read of the credential file on every redirected request. + // A proposal still needs guarded selection commit after credential resolution: a + // removal, reauth verdict, or manual choice can arrive during that await. return best && best !== active ? best : null; } @@ -341,7 +306,6 @@ export function genericFailoverRetryAfterSeconds(providerName: string, now = Dat /** Test seam and manual-recovery hook. */ export function forgetGenericFailoverRoster(providerName: string): void { - roster.delete(providerName); presence.delete(providerName); } @@ -350,11 +314,9 @@ export function clearGenericFailoverHealth(providerName?: string): void { if (!providerName) { health.clear(); presence.clear(); - roster.clear(); return; } presence.delete(providerName); - roster.delete(providerName); for (const key of [...health.keys()]) { if (key.startsWith(`${providerName}\u0000`)) health.delete(key); } diff --git a/src/oauth/pool-settings-capability.ts b/src/oauth/pool-settings-capability.ts index 82f8c06863..210870b718 100644 --- a/src/oauth/pool-settings-capability.ts +++ b/src/oauth/pool-settings-capability.ts @@ -10,7 +10,8 @@ import type { OcxProviderConfig } from "../types"; * * `strategy` and `autoSwitchThreshold` are still a declared contract the selector does not * consume — that is what `inert` reports. `enabled` is NOT inert any more: an explicit - * `false` refuses the pre-dispatch account preference (`preferredInitialAccount`). What it can + * `true` enables pre-dispatch exhaustion avoidance (`preferredInitialAccount`); absence is off. + * Healthy manual selections remain authoritative. What the switch can * no longer do is refuse reactive 429 rotation, which activates on account presence and is not * disableable. */ diff --git a/src/oauth/store.ts b/src/oauth/store.ts index bc9e0e11c1..e641b81be7 100644 --- a/src/oauth/store.ts +++ b/src/oauth/store.ts @@ -23,12 +23,13 @@ import { getConfigDir, atomicWriteFile, backupInvalidConfig, hardenConfigDir, ha import { assertNotRealHomeUnderTest } from "../lib/test-home-guard"; import { recordOwnedConfigPath } from "../lib/config-ownership"; import { MAX_PENDING_OAUTH_MUTATIONS } from "../lib/translator-budget"; +import { publishAccountSelection } from "../lib/account-selection-events"; import { captureConfigGeneration, type GenerationContext, } from "../lib/state-store-sweeper"; import { validateCopilotApiBaseUrl } from "./github-copilot"; -import type { OAuthCredentialSource, OAuthCredentials, ProviderAccount, ProviderAccountSet } from "./types"; +import type { OAuthAccountSelection, OAuthCredentialSource, OAuthCredentials, ProviderAccount, ProviderAccountSet } from "./types"; export type AuthStore = Record; @@ -536,7 +537,15 @@ function normalizeAccountSet(raw: unknown): { set: ProviderAccountSet | null; wa const active = typeof candidate.activeAccountId === "string" && accounts.some(a => a.id === candidate.activeAccountId) ? candidate.activeAccountId : accounts[0]!.id; - return { set: { activeAccountId: active, accounts }, wasLegacy: false }; + const set: ProviderAccountSet = { activeAccountId: active, accounts }; + // Healing a dangling active id invalidates its old selection generation. Reads stay + // deterministic; only the serialized writer creates new revisions. + if (active === candidate.activeAccountId + && typeof candidate.selectionRevision === "string" + && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(candidate.selectionRevision)) { + set.selectionRevision = candidate.selectionRevision; + } + return { set, wasLegacy: false }; } // Legacy single-credential value. const cred = normalizeCredential(raw); @@ -670,9 +679,35 @@ function serializeMutation(work: () => Promise, retainedValues: readonly u export function mutateStore(fn:(store:AuthStore)=>T|Promise, retainedValues: readonly unknown[] = [], options?: { waitMs?: number; assertBeforePersist?: () => void }):Promise{return serializeMutation(async()=>{const guard=await createOAuthFileLock({path:getAuthStoreLockPath(),staleAfterMs:30000}).acquire();try{ const { store, hadLegacy } = loadAuthStoreInternal(); if (hadLegacy) backupLegacyOnce(); + const selections = new Map(Object.entries(store).map(([provider, set]) => [provider, { + set, + accountId: set.activeAccountId, + revision: set.selectionRevision, + accountIds: set.accounts.map(account => account.id), + }])); const result = await fn(store); options?.assertBeforePersist?.(); + const changedProviders: string[] = []; + for (const provider of new Set([...selections.keys(), ...Object.keys(store)])) { + const before = selections.get(provider); + const after = store[provider]; + if (!after) { + if (before) changedProviders.push(provider); + continue; + } + const replaced = before?.set !== after; + const removed = before?.accountIds.some(id => !after.accounts.some(account => account.id === id)); + if (replaced || removed || before?.accountId !== after.activeAccountId) { + // Replacement/rollback must never restore a previous generation. A common + // selection commit already assigned its revision before forming its result. + if (replaced || before?.revision === after.selectionRevision) after.selectionRevision = randomUUID(); + } + if (!before || before.accountId !== after.activeAccountId || before.revision !== after.selectionRevision) { + changedProviders.push(provider); + } + } persist(store); + for (const provider of changedProviders) publishAccountSelection(provider, "oauth"); return result; }finally{guard.release();}}, retainedValues, options?.waitMs); } @@ -700,6 +735,8 @@ export async function saveCredential( if (!safe) return; await mutateStore(store => { const set = store[provider]; + // Login explicitly selects an account, including a re-login to the same slot. + if (set) set.selectionRevision = randomUUID(); const identity = safe.accountId ?? safe.email; if (!set || SINGLE_SLOT_PROVIDERS.has(provider)) { const id = newAccountId(safe); @@ -876,13 +913,60 @@ export async function saveAccountCredential( }, [provider, accountId, safe], { assertBeforePersist: opts.assertBeforePersist }); } -export async function setActiveAccount(provider: string, accountId: string): Promise { +function accountSelection(set: ProviderAccountSet): OAuthAccountSelection { + return { + accountId: set.activeAccountId, + ...(set.selectionRevision !== undefined ? { revision: set.selectionRevision } : {}), + }; +} + +export function captureOAuthAccountSelection(provider: string): OAuthAccountSelection | null { + const set = getAccountSet(provider); + return set ? accountSelection(set) : null; +} + +/** + * Shared manual/automatic selection owner. An expected snapshot marks an automatic + * proposal: validating an unchanged selection preserves its revision. An unconditional + * (manual) selection always advances it, even when reselecting the current account. + */ +export async function commitOAuthAccountSelection( + provider: string, + accountId: string, + options: { + expectedSelection?: OAuthAccountSelection; + expectedCredentialGeneration?: string; + requireUsableAccount?: boolean; + } = {}, +): Promise { + // Snapshot caller-owned options before waiting for the serialized writer. + const expected = options.expectedSelection ? { ...options.expectedSelection } : undefined; + const { expectedCredentialGeneration, requireUsableAccount } = options; + const valid = (set: ProviderAccountSet): boolean => { + if (expected && (set.activeAccountId !== expected.accountId || set.selectionRevision !== expected.revision)) return false; + const account = set.accounts.find(account => account.id === accountId); + if (!account || (requireUsableAccount && account.needsReauth === true)) return false; + return expectedCredentialGeneration === undefined || credentialGeneration(account.credential) === expectedCredentialGeneration; + }; + if (expected?.accountId === accountId) { + // Ordinary admission is one synchronous read/validation, with no await, writer + // queue, or persistence. A changed selection must still take the guarded writer. + const set = getAccountSet(provider); + return set && valid(set) ? accountSelection(set) : null; + } return await mutateStore(store => { const set = store[provider]; - if (!set || !set.accounts.some(a => a.id === accountId)) return false; - set.activeAccountId = accountId; - return true; - }, [provider, accountId]); + if (!set || !valid(set)) return null; + if (!expected || set.activeAccountId !== accountId) { + set.activeAccountId = accountId; + set.selectionRevision = randomUUID(); + } + return accountSelection(set); + }, [provider, accountId, expected, expectedCredentialGeneration]); +} + +export async function setActiveAccount(provider: string, accountId: string): Promise { + return (await commitOAuthAccountSelection(provider, accountId)) !== null; } export async function setAccountAlias(provider: string, accountId: string, alias: string | undefined): Promise { diff --git a/src/oauth/types.ts b/src/oauth/types.ts index 5c2dac541b..19cf435d54 100644 --- a/src/oauth/types.ts +++ b/src/oauth/types.ts @@ -59,9 +59,17 @@ export interface ProviderAccount { /** auth.json value per provider: N accounts + which one requests use. */ export interface ProviderAccountSet { activeAccountId: string; + /** Opaque selection generation; absent in legacy stores, independent of token refresh. */ + selectionRevision?: string; accounts: ProviderAccount[]; } +/** Non-secret snapshot used to condition a selection on the choice that started a request. */ +export interface OAuthAccountSelection { + accountId: string; + revision?: string; +} + export interface OAuthController { onAuth?(info: { url: string; instructions?: string; deviceCode?: string }): void; onProgress?(message: string): void; diff --git a/src/providers/api-key-selection.ts b/src/providers/api-key-selection.ts new file mode 100644 index 0000000000..800d67bf7c --- /dev/null +++ b/src/providers/api-key-selection.ts @@ -0,0 +1,57 @@ +import { randomUUID } from "node:crypto"; +import { mutatePersistedConfig } from "../config"; +import { publishAccountSelection } from "../lib/account-selection-events"; +import type { OcxConfig, OcxProviderConfig } from "../types"; +import type { ProviderApiKeySelection } from "../types/provider"; + +export function captureProviderApiKeySelection(provider: OcxProviderConfig): ProviderApiKeySelection { + return { + entryId: provider.apiKeyPool?.find(entry => entry.key === provider.apiKey)?.id, + reference: provider.apiKey, + revision: provider.apiKeySelectionRevision, + }; +} + +function matchesSelection(provider: OcxProviderConfig, expected: ProviderApiKeySelection): boolean { + const current = captureProviderApiKeySelection(provider); + return current.entryId === expected.entryId && current.reference === expected.reference + && current.revision === expected.revision; +} + +type SelectionMutation = { changed: boolean; value: T; selectionChanged?: boolean }; +export type ProviderApiKeyCommit = + | { status: "committed"; provider: OcxProviderConfig; value: T } + | { status: "superseded"; provider: OcxProviderConfig } + | { status: "unavailable" }; + +/** GUI and recovery share one persisted selection transaction and post-commit notification. */ +export function commitProviderApiKeySelection( + config: OcxConfig, + name: string, + mutation: (provider: OcxProviderConfig) => SelectionMutation, + expectedSelection?: ProviderApiKeySelection, +): ProviderApiKeyCommit { + const outcome = mutatePersistedConfig & { notify?: boolean }>(fresh => { + const provider = fresh.providers[name]; + if (!provider || provider.authMode === "oauth" || provider.authMode === "forward") { + return { changed: false, value: { status: "unavailable" } }; + } + if (expectedSelection && !matchesSelection(provider, expectedSelection)) { + return { changed: false, value: { status: "superseded", provider: structuredClone(provider) } }; + } + const before = provider.apiKey; + const result = mutation(provider); + const notify = result.selectionChanged === true || before !== provider.apiKey; + if (notify) provider.apiKeySelectionRevision = randomUUID(); + delete provider._apiKeyAttempt; + return { + changed: result.changed || notify, + value: { status: "committed", provider: structuredClone(provider), value: result.value, notify }, + }; + }); + if (outcome.status === "unavailable") return { status: "unavailable" }; + const committed = outcome.value; + if (committed.status !== "unavailable") config.providers[name] = structuredClone(committed.provider); + if (committed.status === "committed" && committed.notify) publishAccountSelection(name, "api-key"); + return committed; +} diff --git a/src/providers/api-keys.ts b/src/providers/api-keys.ts index d138cdbf50..ceb5522df2 100644 --- a/src/providers/api-keys.ts +++ b/src/providers/api-keys.ts @@ -10,6 +10,7 @@ import { createHash } from "node:crypto"; import { saveConfigPreservingClaudeCode } from "../config"; import type { OcxConfig, OcxProviderConfig } from "../types"; import type { AccountQuotaFields } from "./quota-types"; +import { commitProviderApiKeySelection } from "./api-key-selection"; export interface ProviderApiKeyInfo extends AccountQuotaFields { id: string; @@ -85,28 +86,33 @@ export function addProviderApiKey(config: OcxConfig, name: string, key: string, if (typeof key !== "string" || !key.trim()) return { error: "key is required" }; const trimmed = sanitizeApiKeyValue(key); if (!trimmed) return { error: "key must not include line breaks" }; - const pool = ensurePool(provider); const id = apiKeyPoolEntryId(trimmed); - const existing = pool.find(e => e.id === id); - if (existing) { - if (label?.trim()) existing.label = label.trim(); - } else { - pool.push({ id, key: trimmed, ...(label?.trim() ? { label: label.trim() } : {}), addedAt: Date.now() }); - } - provider.apiKey = trimmed; - saveConfigPreservingClaudeCode(config); - return { id }; + const committed = commitProviderApiKeySelection(config, name, fresh => { + const pool = ensurePool(fresh); + const existing = pool.find(e => e.id === id); + if (existing) { + if (label?.trim()) existing.label = label.trim(); + } else { + pool.push({ id, key: trimmed, ...(label?.trim() ? { label: label.trim() } : {}), addedAt: Date.now() }); + } + fresh.apiKey = trimmed; + return { changed: true, selectionChanged: true, value: id }; + }); + return committed.status === "committed" ? { id } : { error: "provider selection unavailable" }; } /** Switch the ACTIVE key (mirrors into `provider.apiKey`). Persists config. */ export function setActiveProviderApiKey(config: OcxConfig, name: string, id: string): boolean { - const provider = config.providers[name]; - if (!provider || !isKeyAuthProvider(provider)) return false; - const entry = ensurePool(provider).find(e => e.id === id); - if (!entry) return false; - provider.apiKey = entry.key; - saveConfigPreservingClaudeCode(config); - return true; + const committed = commitProviderApiKeySelection(config, name, provider => { + const entry = provider.apiKeyPool?.find(e => e.id === id) + ?? (!provider.apiKeyPool?.length && provider.apiKey && apiKeyPoolEntryId(provider.apiKey) === id + ? { id, key: provider.apiKey } : undefined); + if (!entry) return { changed: false, value: false }; + ensurePool(provider); + provider.apiKey = entry.key; + return { changed: true, selectionChanged: true, value: true }; + }); + return committed.status === "committed" && committed.value; } /** Rename a key slot without changing its id, secret, or active routing state. */ @@ -123,18 +129,19 @@ export function setProviderApiKeyLabel(config: OcxConfig, name: string, id: stri /** Remove one key; removing the active one promotes the first remaining. Persists config. */ export function removeProviderApiKey(config: OcxConfig, name: string, id: string): boolean { - const provider = config.providers[name]; - if (!provider || !isKeyAuthProvider(provider)) return false; - const pool = ensurePool(provider); - const entry = pool.find(e => e.id === id); - if (!entry) return false; - provider.apiKeyPool = pool.filter(e => e.id !== id); - if (provider.apiKey === entry.key) { - const next = provider.apiKeyPool[0]; - if (next) provider.apiKey = next.key; - else delete provider.apiKey; - } - if (provider.apiKeyPool.length === 0) delete provider.apiKeyPool; - saveConfigPreservingClaudeCode(config); - return true; + const committed = commitProviderApiKeySelection(config, name, provider => { + const pool = provider.apiKeyPool?.length ? provider.apiKeyPool + : provider.apiKey ? [{ id: apiKeyPoolEntryId(provider.apiKey), key: provider.apiKey }] : []; + const entry = pool.find(e => e.id === id); + if (!entry) return { changed: false, value: false }; + provider.apiKeyPool = pool.filter(e => e.id !== id); + if (provider.apiKey === entry.key) { + const next = provider.apiKeyPool[0]; + if (next) provider.apiKey = next.key; + else delete provider.apiKey; + } + if (provider.apiKeyPool.length === 0) delete provider.apiKeyPool; + return { changed: true, value: true }; + }); + return committed.status === "committed" && committed.value; } diff --git a/src/providers/key-failover.ts b/src/providers/key-failover.ts index 9c7a42e11b..ad4d61ba8d 100644 --- a/src/providers/key-failover.ts +++ b/src/providers/key-failover.ts @@ -8,7 +8,8 @@ * * Modelled after src/codex/routing.ts cooldown logic but scoped to plain API-key pools. */ -import { mutatePersistedConfig } from "../config"; +import { commitProviderApiKeySelection } from "./api-key-selection"; +import type { ProviderApiKeySelection } from "../types/provider"; import { routedProviderConfig } from "../router"; import type { OcxConfig, OcxProviderConfig, RateLimitRetryPolicy, TransientRetryPolicy } from "../types"; import { OPENCODE_GO_SESSION_HEADER } from "./opencode-go-transport"; @@ -186,33 +187,32 @@ function rotateKeyAfterFailure( retryAfterHeader: string | null | undefined, now = Date.now(), attemptedKey?: string, + attemptedSelection?: ProviderApiKeySelection, ): OcxProviderConfig | null { const provider = config.providers[providerName]; if (!provider) return null; if (provider.authMode === "oauth" || provider.authMode === "forward") return null; - const failedKey = attemptedKey ?? provider.apiKey; + const failedKey = attemptedSelection?.reference ?? attemptedKey ?? provider.apiKey; type Rotation = - | { provider: OcxProviderConfig; failedId?: string; candidateId?: string } + | { failedId?: string; candidateId?: string } | { exhaustedCount: number; failedId?: string }; - const outcome = mutatePersistedConfig(fresh => { - const freshProvider = fresh.providers[providerName]; - if (!freshProvider || freshProvider.authMode === "oauth" || freshProvider.authMode === "forward") { - return { changed: false, value: null }; - } + const outcome = commitProviderApiKeySelection(config, providerName, freshProvider => { const pool = freshProvider.apiKeyPool; if (!pool || pool.length < 2) return { changed: false, value: null }; // The callback can be rerun after rebasing, so identify the failed key here but // defer the in-memory cooldown side effect until persistence has succeeded. - const failedEntry = pool.find(entry => entry.key === failedKey); + const failedEntry = attemptedSelection?.entryId + ? pool.find(entry => entry.id === attemptedSelection.entryId && entry.key === failedKey) + : pool.find(entry => entry.key === failedKey); if (freshProvider.apiKey !== failedKey) { const activeEntry = pool.find(entry => entry.key === freshProvider.apiKey); if (activeEntry && !isKeyInCooldown(providerName, activeEntry.id, now)) { return { changed: false, - value: { provider: structuredClone(freshProvider), failedId: failedEntry?.id }, + value: { failedId: failedEntry?.id }, }; } } @@ -226,15 +226,20 @@ function rotateKeyAfterFailure( return { changed: true, value: { - provider: structuredClone(freshProvider), failedId: failedEntry?.id, candidateId: candidate.id, }, }; } return { changed: false, value: { exhaustedCount: pool.length, failedId: failedEntry?.id } }; - }); - if (outcome.status === "unavailable" || outcome.value === null) return null; + }, attemptedSelection); + if (outcome.status === "unavailable") return null; + if (outcome.status === "superseded") { + // A newer manual selection (including A→B→A) owns subsequent dispatch. Reusing the + // same failed key here would loop forever; preserve its original failure instead. + return outcome.provider.apiKey !== failedKey ? structuredClone(outcome.provider) : null; + } + if (outcome.value === null) return null; if (outcome.value.failedId) { // A 401 is a verdict about the credential itself, not a timing signal: the key is rejected // until an operator replaces it, and upstreams send no Retry-After for it. Hold it for the @@ -250,7 +255,7 @@ function rotateKeyAfterFailure( return null; } - const committed = structuredClone(outcome.value.provider); + const committed = structuredClone(outcome.provider); config.providers[providerName] = committed; if (outcome.value.candidateId) { console.warn( @@ -267,8 +272,9 @@ export function rotateKeyOn429( retryAfterHeader: string | null | undefined, now = Date.now(), attemptedKey?: string, + attemptedSelection?: ProviderApiKeySelection, ): OcxProviderConfig | null { - return rotateKeyAfterFailure(config, providerName, 429, retryAfterHeader, now, attemptedKey); + return rotateKeyAfterFailure(config, providerName, 429, retryAfterHeader, now, attemptedKey, attemptedSelection); } /** @@ -284,8 +290,9 @@ export function rotateKeyOn401( providerName: string, now = Date.now(), attemptedKey?: string, + attemptedSelection?: ProviderApiKeySelection, ): OcxProviderConfig | null { - return rotateKeyAfterFailure(config, providerName, 401, null, now, attemptedKey); + return rotateKeyAfterFailure(config, providerName, 401, null, now, attemptedKey, attemptedSelection); } export function sweepExpiredApiKeyCooldowns(now = Date.now()): number { @@ -302,6 +309,7 @@ interface RotateProviderTransportOptions { retryAfter?: string | null; now?: number; attemptedKey?: string; + attemptedSelection?: ProviderApiKeySelection; promptCacheKey?: string; } @@ -323,6 +331,7 @@ export function rotateProviderTransportOn429( options.retryAfter, options.now, options.attemptedKey, + options.attemptedSelection ?? routedProvider._apiKeyAttempt, ); if (!rotated) return null; return applyRotatedTransport(providerName, routedProvider, rotated, options.promptCacheKey); @@ -335,7 +344,8 @@ export function rotateProviderTransportOn401( routedProvider: OcxProviderTransport, options: Omit = {}, ): OcxProviderTransport | null { - const rotated = rotateKeyOn401(config, providerName, options.now, options.attemptedKey); + const rotated = rotateKeyOn401(config, providerName, options.now, options.attemptedKey, + options.attemptedSelection ?? routedProvider._apiKeyAttempt); if (!rotated) return null; return applyRotatedTransport(providerName, routedProvider, rotated, options.promptCacheKey); } diff --git a/src/router.ts b/src/router.ts index 758f34e751..b2f887f0a9 100644 --- a/src/router.ts +++ b/src/router.ts @@ -10,6 +10,7 @@ import { import type { NormalizedComboConfig } from "./combos/types"; import { hasOwnProvider } from "./config/provider-name"; import { providerUsesKeyAuthOverride, resolveProviderApiKey } from "./providers/key-store"; +import { captureProviderApiKeySelection } from "./providers/api-key-selection"; import { assertProviderDestinationAllowed } from "./lib/destination-policy"; import { redactSecretString, redactUrlForLog } from "./lib/redact"; import { @@ -297,6 +298,7 @@ function usableResolvedApiKey(apiKey: string | undefined): string | undefined { } export function routedProviderConfig(providerName: string, provider: OcxProviderConfig): OcxProviderConfig { + provider = { ...provider, _apiKeyAttempt: provider._apiKeyAttempt ?? captureProviderApiKeySelection(provider) }; const registryEntry = PROVIDER_REGISTRY.find(entry => entry.id === providerName); if (!registryEntry || !providerMatchesRegistryTransportWithStaticGuards(providerName, provider)) { assertProviderDestinationAllowed(providerName, provider); diff --git a/src/server/auth-cors.ts b/src/server/auth-cors.ts index 0677722979..0dd49910fb 100644 --- a/src/server/auth-cors.ts +++ b/src/server/auth-cors.ts @@ -530,6 +530,7 @@ export function requireResponsesApiAuth(req: Request, config: RequestPolicyView) const FORBIDDEN_PROVIDER_RUNTIME_FIELDS = [ "virtualModels", "codexAuthContext", "selectedForwardHeaders", "sidecarOutcomeRecorder", "_codexAccountOverride", "_codexAccountRequired", + "_apiKeyAttempt", ] as const; function sameCanonicalProviderSeed(actual: Record, expected: OcxProviderConfig): boolean { @@ -795,6 +796,8 @@ const PROVIDER_CONFIG_FIELD_POLICY = { apiKey: "redacted", apiKeyTransport: "editor", apiKeyPool: "redacted", + apiKeySelectionRevision: "runtime", + _apiKeyAttempt: "runtime", defaultModel: "editor", models: "editor", liveModels: "editor", diff --git a/src/server/chat-native.ts b/src/server/chat-native.ts index 513a9d0913..3c44a66628 100644 --- a/src/server/chat-native.ts +++ b/src/server/chat-native.ts @@ -286,6 +286,7 @@ export async function handleNativeChatCompletions(options: HandleNativeChatOptio retryAfter: response.headers.get("retry-after"), now: Date.now(), attemptedKey: activeProvider.apiKey, + attemptedSelection: activeProvider._apiKeyAttempt, promptCacheKey: typeof options.chatBody.prompt_cache_key === "string" ? options.chatBody.prompt_cache_key : undefined, }); if (!rotated) break; diff --git a/src/server/management/account-selection-stream.ts b/src/server/management/account-selection-stream.ts new file mode 100644 index 0000000000..94a1890a47 --- /dev/null +++ b/src/server/management/account-selection-stream.ts @@ -0,0 +1,59 @@ +import { currentAccountSelectionRevision, subscribeAccountSelections } from "../../lib/account-selection-events"; +import { registerOptionalShutdownHook } from "../../lib/optional-shutdown-hooks"; + +const MAX_SELECTION_STREAMS = 64; +const HEARTBEAT_MS = 15_000; +const encoder = new TextEncoder(); +const connections = new Set<() => void>(); + +/** Auth is enforced by the management boundary before this read-only stream is opened. */ +export function accountSelectionStream(request: Request): Response { + if (connections.size >= MAX_SELECTION_STREAMS) { + return Response.json({ error: "Too many account selection streams" }, { + status: 429, headers: { "Retry-After": "15" }, + }); + } + let cleanup = () => {}; + const body = new ReadableStream({ + start(controller) { + let closed = false; + let unsubscribe = () => {}; + let heartbeat: ReturnType | undefined; + const close = () => { + if (closed) return; + closed = true; + unsubscribe(); + if (heartbeat) clearInterval(heartbeat); + request.signal.removeEventListener("abort", close); + connections.delete(close); + try { controller.close(); } catch { /* The consumer may already have cancelled. */ } + }; + cleanup = close; + const send = (frame: string) => { + if (closed) return; + // Reconnection sends a ready event, so a slow reader can reconcile without an + // unbounded queue or silently dropping a provider's latest invalidation. + if (controller.desiredSize !== null && controller.desiredSize <= 0) { close(); return; } + try { controller.enqueue(encoder.encode(frame)); } catch { close(); } + }; + connections.add(close); + registerOptionalShutdownHook("account-selection-streams", () => { + for (const finish of [...connections]) finish(); + }); + if (request.signal.aborted) { close(); return; } + request.signal.addEventListener("abort", close, { once: true }); + unsubscribe = subscribeAccountSelections(event => { + send(`event: account-selection\ndata: ${JSON.stringify(event)}\n\n`); + }); + send(`event: ready\ndata: ${JSON.stringify({ revision: currentAccountSelectionRevision() })}\n\n`); + heartbeat = setInterval(() => send(": heartbeat\n\n"), HEARTBEAT_MS); + heartbeat.unref?.(); + }, + cancel() { cleanup(); }, + }, { highWaterMark: 16 }); + return new Response(body, { headers: { + "Content-Type": "text/event-stream; charset=utf-8", + "Cache-Control": "no-cache, no-transform", + "X-Accel-Buffering": "no", + } }); +} diff --git a/src/server/management/oauth-account-routes.ts b/src/server/management/oauth-account-routes.ts index 07884d5f4f..63993fd6e2 100644 --- a/src/server/management/oauth-account-routes.ts +++ b/src/server/management/oauth-account-routes.ts @@ -129,6 +129,11 @@ function validateKeyName( export async function handleOauthAccountRoutes(ctx: ManagementContext): Promise { const { req, url, config, deps, syncClaudeAgentDefsBestEffort } = ctx; + if (url.pathname === "/api/accounts/events" && req.method === "GET") { + const { accountSelectionStream } = await import("./account-selection-stream"); + return accountSelectionStream(req); + } + // Which providers support real OAuth login (drives the GUI's "Log in with …" buttons). if (url.pathname === "/api/oauth/providers" && req.method === "GET") { return jsonResponse({ providers: listOAuthProviders() }); @@ -320,6 +325,8 @@ export async function handleOauthAccountRoutes(ctx: ManagementContext): Promise< if (!body.accountId) return jsonResponse({ error: "missing accountId" }, 400); const { setActiveAccount } = await import("../../oauth/store"); if (!(await setActiveAccount(provider, body.accountId))) return jsonResponse({ error: "account not found" }, 404); + const { forgetGenericFailoverRoster } = await import("../../oauth/generic-account-failover"); + forgetGenericFailoverRoster(provider); if (provider === "anthropic") { const { resetAnthropicRoutingForManualSelection } = await import("../../oauth/anthropic-routing"); resetAnthropicRoutingForManualSelection(body.accountId); diff --git a/src/server/management/route-registry.ts b/src/server/management/route-registry.ts index 6bd0a95a52..00b72bd35d 100644 --- a/src/server/management/route-registry.ts +++ b/src/server/management/route-registry.ts @@ -256,6 +256,7 @@ export const MANAGEMENT_ROUTES: readonly ManagementRoute[] = [ { method: "GET", path: "/api/key-providers", module: "server/management/oauth-account-routes", mutates: false }, { method: "GET", path: "/api/keys", module: "server/management/oauth-account-routes", mutates: false }, { method: "GET", path: "/api/oauth/accounts", module: "server/management/oauth-account-routes", mutates: false }, + { method: "GET", path: "/api/accounts/events", module: "server/management/oauth-account-routes", mutates: false }, { method: "GET", path: "/api/oauth/accounts/pool", module: "server/management/oauth-account-routes", mutates: false }, { method: "GET", path: "/api/oauth/providers", module: "server/management/oauth-account-routes", mutates: false }, { method: "GET", path: "/api/oauth/status", module: "server/management/oauth-account-routes", mutates: false }, diff --git a/src/server/responses/core.ts b/src/server/responses/core.ts index b48438deb5..d961562ff2 100644 --- a/src/server/responses/core.ts +++ b/src/server/responses/core.ts @@ -118,18 +118,19 @@ import { type OAuthAccessSnapshot, UnsupportedOAuthProviderError, } from "../../oauth"; +import { captureOAuthAccountSelection, commitOAuthAccountSelection } from "../../oauth/store"; import { ANTHROPIC_POOL_MAX_FAILOVERS_PER_REQUEST, anthropicSessionKeyFromParts, - bindAnthropicSessionAffinity, + commitAnthropicSelectionRouting, formatAnthropicProviderForLog, - getAnthropicPoolAccessToken, + getAnthropicPoolAccessSnapshot, getAnthropicPoolRetryAfterSeconds, isAnthropicAccountPoolEnabled, hasAnthropicFailoverQuorum, - promoteAnthropicActiveAccount, resolveAnthropicAccountForSession, rotateAnthropicAccountOn429, + type AnthropicAccountSelectionReason, } from "../../oauth/anthropic-routing"; import { stampOAuthAccountLabel } from "../../providers/label"; import { @@ -3673,6 +3674,52 @@ async function handleResponsesInner( // the request actually used, so a concurrent rotation cannot cool an innocent replacement. let genericFailoverAccountId: string | null = null; let genericFailovers = 0; + let oauthSelection = route.provider.authMode === "oauth" + ? captureOAuthAccountSelection(route.providerName) : null; + const commitResolvedOAuthSelection = async ( + candidate: OAuthAccessSnapshot, + proactive = false, + anthropicReason?: AnthropicAccountSelectionReason, + ): Promise => { + const maxSelectionAttempts = 3; + for (let attempt = 0; attempt < maxSelectionAttempts; attempt++) { + if (!oauthSelection) return null; + const proactiveEnabled = route.providerName === "anthropic" + ? isAnthropicAccountPoolEnabled(config) + : (config.providers[route.providerName]?.oauthAccountFailover?.enabled + ?? config.oauthAccountFailover?.enabled) === true; + if (proactive && candidate.accountId !== oauthSelection.accountId && !proactiveEnabled) { + oauthSelection = captureOAuthAccountSelection(route.providerName); + if (!oauthSelection) return null; + candidate = route.providerName === "anthropic" + ? await getAnthropicPoolAccessSnapshot(oauthSelection.accountId) + : await getValidAccessSnapshotForAccount(route.providerName, oauthSelection.accountId, { requireUsableAccount: true }); + } + const committed = await commitOAuthAccountSelection(route.providerName, candidate.accountId, { + expectedSelection: oauthSelection, + expectedCredentialGeneration: candidate.generation, + requireUsableAccount: true, + }); + if (committed) { + if (route.providerName === "anthropic" && !commitAnthropicSelectionRouting( + candidate.accountId, oauthSelection, committed, + { config, sessionKey: anthropicSessionKey, reason: anthropicReason, expectedCredentialGeneration: candidate.generation }, + )) return null; + oauthSelection = committed; + forgetGenericFailoverRoster(route.providerName); + return candidate; + } + // A newer manual choice wins over this request's old proposal, including A→B→A. + // Resolve that choice, not the rejected candidate, before trying admission again. + oauthSelection = captureOAuthAccountSelection(route.providerName); + if (!oauthSelection) return null; + candidate = route.providerName === "anthropic" + ? await getAnthropicPoolAccessSnapshot(oauthSelection.accountId) + : await getValidAccessSnapshotForAccount(route.providerName, oauthSelection.accountId, { requireUsableAccount: true }); + if (route.provider.googleMode === "cloud-code-assist" && !candidate.projectId) return null; + } + return null; + }; /** * Config generation captured where the serving credential is RESOLVED, not where the * quota is written. A streaming turn is a long await, so a generation captured at write @@ -3701,11 +3748,14 @@ async function handleResponsesInner( * tolerates project discovery failing, so a stored account can legitimately have no project; * sending that account's bearer with the FAILED account's project is worse than not rotating. */ - const applyFailoverSnapshot = ( + const applyFailoverSnapshot = async ( snapshot: OAuthAccessSnapshot, retryParsed: OcxParsedRequest = parsed, - ): boolean => { + ): Promise => { if (route.provider.googleMode === "cloud-code-assist" && !snapshot.projectId) return false; + const committed = await commitResolvedOAuthSelection(snapshot); + if (!committed) return false; + snapshot = committed; let rotatedProvider: OcxProviderConfig = { ...route.provider, apiKey: snapshot.accessToken }; if (route.providerName === "github-copilot") { rotatedProvider = resolveProviderTransport( @@ -3729,6 +3779,9 @@ async function handleResponsesInner( // served it. All three rotation sites funnel through here, so this is the only re-stamp // needed -- and putting it anywhere else would let one of the three drift. stampOAuthAccountLabel(logCtx, route.providerName, route.provider, snapshot.accountId); + genericFailoverAccountId = snapshot.accountId; + sentOAuthSnapshot = snapshot; + replayOAuthCredentialSnapshot = { accountId: snapshot.accountId, generation: snapshot.generation }; return true; }; const anthropicSessionKey = route.providerName === "anthropic" && route.provider.authMode === "oauth" @@ -3756,12 +3809,11 @@ async function handleResponsesInner( } return formatErrorResponse(401, "authentication_error", "No eligible Anthropic OAuth account available"); } - const accessToken = await getAnthropicPoolAccessToken(selection.accountId); - anthropicPoolAccountId = selection.accountId; - bindAnthropicSessionAffinity(anthropicSessionKey, selection.accountId); - promoteAnthropicActiveAccount(selection.accountId); - route.provider = { ...route.provider, apiKey: accessToken }; - logCtx.provider = formatAnthropicProviderForLog("anthropic", selection.accountId, config); + const admitted = await commitResolvedOAuthSelection(await getAnthropicPoolAccessSnapshot(selection.accountId), true, selection.reason); + if (!admitted) return formatErrorResponse(409, "conflict_error", "OAuth account selection changed; retry the request"); + anthropicPoolAccountId = admitted.accountId; + route.provider = { ...route.provider, apiKey: admitted.accessToken }; + logCtx.provider = formatAnthropicProviderForLog("anthropic", admitted.accountId, config); } else { // Prefer the account with known headroom BEFORE the first attempt. Rotation alone // only reacts to a 429, so a turn could open on an account a previous probe already @@ -3810,6 +3862,10 @@ async function handleResponsesInner( resolved = await getValidAccessTokenSnapshot(route.providerName); usedPreferredAccount = false; } + const admitted = await commitResolvedOAuthSelection(resolved, true); + if (!admitted) return formatErrorResponse(409, "conflict_error", "OAuth account selection changed; retry the request"); + if (admitted.accountId !== resolved.accountId) usedPreferredAccount = true; + resolved = admitted; replayOAuthCredentialSnapshot = { accountId: resolved.accountId, generation: resolved.generation, @@ -4868,13 +4924,10 @@ async function handleResponsesInner( try { snapshot = await failoverAccountSnapshot(route.providerName, nextAccountId); } catch { /* Keep the original 429 body readable when the next credential is unavailable. */ } } - if (snapshot && applyFailoverSnapshot(snapshot)) { - genericFailoverAccountId = snapshot.accountId; + if (snapshot && await applyFailoverSnapshot(snapshot)) { genericFailovers += 1; - sentOAuthSnapshot = snapshot; - replayOAuthCredentialSnapshot = { accountId: snapshot.accountId, generation: snapshot.generation }; route.provider = resolveProviderTransport( - route.providerName, route.provider, parsed.options.promptCacheKey, snapshot.apiBaseUrl, + route.providerName, route.provider, parsed.options.promptCacheKey, sentOAuthSnapshot?.apiBaseUrl, ); bindRouteReasoningReplayScope({ parsed, providerName: route.providerName, provider: route.provider, @@ -5751,9 +5804,8 @@ async function handleResponsesInner( if (!nextAccountId) return null; try { const snapshot = await failoverAccountSnapshot(route.providerName, nextAccountId); - genericFailoverAccountId = nextAccountId; genericFailovers += 1; - if (!applyFailoverSnapshot(snapshot)) return null; + if (!await applyFailoverSnapshot(snapshot)) return null; } catch { return null; } @@ -5777,12 +5829,12 @@ async function handleResponsesInner( // carries none, and getAnthropicPoolAccessToken is what enforces its fail-closed // local-cli credential rule. Both existing Anthropic rotation sites apply the token the // same way. - const accessToken = await getAnthropicPoolAccessToken(nextAccountId); - anthropicPoolAccountId = nextAccountId; + const admitted = await commitResolvedOAuthSelection(await getAnthropicPoolAccessSnapshot(nextAccountId)); + if (!admitted) throw new Error("OAuth selection changed during recovery"); + anthropicPoolAccountId = admitted.accountId; anthropicPoolFailovers += 1; - route.provider = { ...route.provider, apiKey: accessToken }; - promoteAnthropicActiveAccount(nextAccountId); - logCtx.provider = formatAnthropicProviderForLog("anthropic", nextAccountId, config); + route.provider = { ...route.provider, apiKey: admitted.accessToken }; + logCtx.provider = formatAnthropicProviderForLog("anthropic", admitted.accountId, config); } catch { return null; } @@ -6098,9 +6150,8 @@ async function handleResponsesInner( if (!nextAccountId) return false; try { const snapshot = await failoverAccountSnapshot(route.providerName, nextAccountId); - genericFailoverAccountId = nextAccountId; genericFailovers += 1; - if (!applyFailoverSnapshot(snapshot)) return false; + if (!await applyFailoverSnapshot(snapshot)) return false; // A Cursor conversation/checkpoint is credential-scoped. The failed attempt emitted no // client-visible bytes, so replay is safe, but carrying its account identity into the next // account would not be. Let the rotated adapter derive a fresh identity and conversation. @@ -6785,13 +6836,13 @@ async function handleResponsesInner( if (!nextAccountId) break; try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already consumed/closed */ } try { - const accessToken = await getAnthropicPoolAccessToken(nextAccountId); - anthropicPoolAccountId = nextAccountId; + const admitted = await commitResolvedOAuthSelection(await getAnthropicPoolAccessSnapshot(nextAccountId)); + if (!admitted) throw new Error("OAuth selection changed during recovery"); + anthropicPoolAccountId = admitted.accountId; anthropicPoolFailovers += 1; - route.provider = { ...route.provider, apiKey: accessToken }; + route.provider = { ...route.provider, apiKey: admitted.accessToken }; invalidateSameTargetRequest(); - promoteAnthropicActiveAccount(nextAccountId); - logCtx.provider = formatAnthropicProviderForLog("anthropic", nextAccountId, config); + logCtx.provider = formatAnthropicProviderForLog("anthropic", admitted.accountId, config); activeAdapter = resolveAdapter( resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire), config.cacheRetention, @@ -6832,9 +6883,8 @@ async function handleResponsesInner( // projectId with its token and Kiro carries routing metadata, so a token-only swap // would mix one account's credential with another's routing data. const snapshot = await failoverAccountSnapshot(route.providerName, nextAccountId); - genericFailoverAccountId = nextAccountId; - genericFailovers += 1; - if (!applyFailoverSnapshot(snapshot)) break; + genericFailovers += 1; + if (!await applyFailoverSnapshot(snapshot)) break; invalidateSameTargetRequest(); activeAdapter = resolveAdapter( resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire), @@ -7197,13 +7247,13 @@ async function handleResponsesInner( if (nextAccountId) { try { void response.body?.cancel().catch(() => {}); } catch { /* already closed */ } try { - const accessToken = await getAnthropicPoolAccessToken(nextAccountId); - anthropicPoolAccountId = nextAccountId; + const admitted = await commitResolvedOAuthSelection(await getAnthropicPoolAccessSnapshot(nextAccountId)); + if (!admitted) throw new Error("OAuth selection changed during recovery"); + anthropicPoolAccountId = admitted.accountId; anthropicPoolFailovers += 1; - route.provider = { ...route.provider, apiKey: accessToken }; + route.provider = { ...route.provider, apiKey: admitted.accessToken }; invalidateSameTargetRequest(); - promoteAnthropicActiveAccount(nextAccountId); - logCtx.provider = formatAnthropicProviderForLog("anthropic", nextAccountId, config); + logCtx.provider = formatAnthropicProviderForLog("anthropic", admitted.accountId, config); activeAdapter = resolveAdapter( resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire), config.cacheRetention, @@ -7242,9 +7292,8 @@ async function handleResponsesInner( // metadata, so a token-only swap would mix one account's credential with another's // routing data. const snapshot = await failoverAccountSnapshot(route.providerName, nextAccountId); - genericFailoverAccountId = nextAccountId; - genericFailovers += 1; - if (applyFailoverSnapshot(snapshot, nextParsed)) { + genericFailovers += 1; + if (await applyFailoverSnapshot(snapshot, nextParsed)) { invalidateSameTargetRequest(); activeAdapter = resolveAdapter( resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire), diff --git a/src/types/config.ts b/src/types/config.ts index 27b1a8ec81..c6fdfb063a 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -815,10 +815,10 @@ export interface OcxConfig { * provider has 2 or more eligible stored accounts, the same consent rule an `apiKeyPool` of * two keys already applies, and a single account remains a strict no-op. * - * What `enabled: false` still refuses is the PRE-DISPATCH preference: steering a request - * upstream has not refused toward the account with more known headroom. That moves a healthy - * request, so it stays a real choice. `providers..oauthAccountFailover` overrides this - * per provider in either direction; reactive 429 rotation remains presence-driven. + * Proactive avoidance of an exhausted selected account requires `enabled: true`. + * A healthy selected account retains priority; an unknown quota is not exhaustion. + * `providers..oauthAccountFailover` overrides this per provider in either direction. + * Reactive 429 rotation remains presence-driven even when proactive routing is disabled. */ oauthAccountFailover?: { enabled?: boolean; diff --git a/src/types/provider.ts b/src/types/provider.ts index cb2abc1f0c..97a359506a 100644 --- a/src/types/provider.ts +++ b/src/types/provider.ts @@ -8,6 +8,13 @@ import type { UpstreamHttpVersion, ReasoningSummaryDelivery, CodexAccountMode } */ export type RefreshPolicy = "proactive" | "lazy-only" | "disabled"; +/** Request-owned identity of the configured key, before env/keychain resolution. */ +export interface ProviderApiKeySelection { + entryId?: string; + reference?: string; + revision?: string; +} + export interface OpenRouterProviderRouting { /** OpenRouter provider slugs to try first, in priority order. */ order?: string[]; @@ -329,6 +336,10 @@ export interface OcxProviderConfig { * `apiKey` seeds a one-entry pool on first management touch. */ apiKeyPool?: Array<{ id: string; key: string; label?: string; addedAt?: number }>; + /** Changes on manual selection (including re-selection) and committed automatic allocation. */ + apiKeySelectionRevision?: string; + /** Runtime only. Never expose in management responses or persist a routed provider. */ + _apiKeyAttempt?: ProviderApiKeySelection; defaultModel?: string; models?: string[]; /** @@ -441,9 +452,9 @@ export interface OcxProviderConfig { * * Reactive 429 rotation is presence-driven and cannot be refused here — 2+ logged-in accounts * activate it, and a 429 with an idle second account is a defect rather than a preference. - * What an explicit `false` still refuses is the pre-dispatch preference that steers a HEALTHY - * request toward the account with more known headroom. It beats the global - * `oauthAccountFailover` in either direction; reactive 429 rotation remains presence-driven. + * Proactive exhaustion avoidance requires explicit `true`; a healthy selected account + * retains priority. This overrides global `oauthAccountFailover` in either direction. + * Reactive 429 rotation remains available even when proactive routing is disabled. */ oauthAccountFailover?: { enabled?: boolean; diff --git a/structure/04_transports-and-sidecars.md b/structure/04_transports-and-sidecars.md index f02a2cf5c2..4716c447bb 100644 --- a/structure/04_transports-and-sidecars.md +++ b/structure/04_transports-and-sidecars.md @@ -1555,7 +1555,7 @@ surface is listed here so a maintainer can find the owner without grepping: | Hosted search relay | `src/server/search.ts` | Direct relay; distinct from the web-search sidecar loop below. | | Image/video generation loop | `src/images/loop.ts`, `src/images/plan.ts`, `src/images/fulfill.ts`, `src/images/xai-client.ts`, `src/images/xai-video-client.ts`, `src/images/artifacts.ts` | A provider-returned image URL is downloaded into a local artifact once, then served locally; warnings stay URL-free because provider CDN URLs may embed credentials. | | GitHub Copilot | `src/providers/xai-transport.ts` (`resolveProviderTransport`), `src/providers/github-copilot-transport.ts` | `resolveProviderTransport` selects the Copilot transport when the routed provider name is `github-copilot`; the Copilot module then resolves its headers and base URL, and the registry seeds the provider row and model fallback. | -| API-key pools | `src/providers/key-failover.ts` | A 429 rotates the active key and records a cooldown; `provider.apiKey` keeps mirroring the active entry so routing stays single-key. | +| API-key pools | `src/providers/api-key-selection.ts`, `src/providers/key-failover.ts` | A 429 rotates the active key and records a cooldown; `provider.apiKey` keeps mirroring the active entry so routing stays single-key. | | OAuth account failover | `src/oauth/generic-account-failover.ts`, `src/oauth/anthropic-routing.ts` | Reactive pre-output 429 recovery is presence-driven with 2+ eligible accounts. Pool and `oauthAccountFailover` flags govern proactive routing, not the reactive retry: a disabled Anthropic pool recovers through quota ordering rather than its dormant strategy, and a per-provider `enabled` beats the global default in either direction. | | Alibaba regions | `src/providers/alibaba-region-backup.ts`, `src/providers/alibaba-region-migration.ts`, `src/providers/alibaba-region-startup.ts` | Region migration backs up before rewriting and is idempotent across restarts. | | Discovery and quota | `src/providers/model-discovery.ts`, `src/providers/quota.ts` | Discovery rejects a response over 4 MiB or past 2,000 raw rows before caching it. | @@ -1639,3 +1639,9 @@ object guard, JSON block encoder and retained-item shape live in the dependency- free `responses-snapshot-codec.ts`. Core imports each tracker directly. No existing snapshot export moves, and neither tracker imports the core dispatcher. The Grok marker selects compatibility behavior and conveys no authenticated client identity. + +Manual and automatic OAuth/API-key selection commit through their shared selection owners before +dispatch. Selection revisions fence stale retries and reselection; request identity includes the +actual committed account/key. Generic proactive selection is opt-in and preserves a healthy active +account, while reactive429 recovery remains enabled even with the pool off. Post-commit selection +events immediately invalidate dashboard roster state; see`05_gui-and-management-api.md`. diff --git a/structure/05_gui-and-management-api.md b/structure/05_gui-and-management-api.md index 9ca0fd0a23..c7674a507e 100644 --- a/structure/05_gui-and-management-api.md +++ b/structure/05_gui-and-management-api.md @@ -165,6 +165,22 @@ User aliases are display metadata only. Codex pool aliases live on `CodexAccount identity, active selection, and routing never consult these fields. The matching CLI is `ocx account alias ` (`rename` is accepted as a synonym). +OAuth manual and automatic selection share `commitOAuthAccountSelection` in the auth store. +The caller resolves a usable credential, commits its matching selection, then dispatches it; +request-local token replacement must not leave a different dashboard account selected. +Opaque selection revisions protect manual reselection and A→B→A changes from older requests. +Credential-only refresh preserves the revision. Generic proactive routing is opt-in and retains +a healthy selected account; reactive 429 recovery remains available even when the pool is off. +API-key manual selection and failover similarly share `commitProviderApiKeySelection`, carrying +stable entry identity and selection revision instead of comparing a resolved secret with an env reference. + +The authenticated `GET /api/accounts/events` stream invalidates account/key selection after +successful persistence. Events contain provider/kind/revision only. The dashboard immediately +reconciles the cheap local roster and preserves its quota rows; no upstream quota probe is caused +by an event. One screen-owned stream has disconnect cleanup and bounded server subscribers; +reconnection and the existing shared scheduler provide recovery. Codex retains its own established +selection controller. These events cannot change credentials or select an account. + Selection order is the opposite case and must not be folded into the alias route. `codexAccountPriorities` is routing metadata that Pool selection consults, it lives in config rather than on `CodexAccount` so the `__main__` Desktop login can carry one, and the alias route's rejection of `__main__` would be wrong for diff --git a/tests/adapters/anthropic/anthropic-account-pool.test.ts b/tests/adapters/anthropic/anthropic-account-pool.test.ts index 7817fd7104..c4665b3ea1 100644 --- a/tests/adapters/anthropic/anthropic-account-pool.test.ts +++ b/tests/adapters/anthropic/anthropic-account-pool.test.ts @@ -16,8 +16,12 @@ import { resolveAnthropicAccountForSession, resetAnthropicRoutingForManualSelection, rotateAnthropicAccountOn429, + getAnthropicPoolAccessSnapshot, + promoteAnthropicActiveAccount, + anthropicSessionAffinitySizeForTests, } from "../../../src/oauth/anthropic-routing"; -import { getAccountSet, saveCredential, setActiveAccount } from "../../../src/oauth/store"; +import { captureOAuthAccountSelection, getAccountSet, markAccountNeedsReauth, saveCredential, saveAccountCredential, setActiveAccount } from "../../../src/oauth/store"; +import { subscribeAccountSelections } from "../../../src/lib/account-selection-events"; import { clearAccountQuotaCache, setCachedProviderAccountQuotaForTests } from "../../../src/providers/quota"; import type { OcxAccountPoolQuotaWindow, OcxAccountPoolRotationStrategy, OcxConfig } from "../../../src/types"; import { removeTreeWithRetry } from "../../helpers/remove-tree"; @@ -25,6 +29,18 @@ import { removeTreeWithRetry } from "../../helpers/remove-tree"; const originalHome = process.env.OPENCODEX_HOME; let home: string; +async function admitAnthropic(sessionKey: string, config: OcxConfig) { + const expected = captureOAuthAccountSelection("anthropic"); + const choice = resolveAnthropicAccountForSession(sessionKey, config); + if (choice.accountId) { + const snapshot = await getAnthropicPoolAccessSnapshot(choice.accountId); + expect(await promoteAnthropicActiveAccount(choice.accountId, expected, { + config, sessionKey, reason: choice.reason, expectedCredentialGeneration: snapshot.generation, + })).not.toBeNull(); + } + return choice; +} + beforeEach(() => { home = mkdtempSync(join(tmpdir(), "ocx-anthropic-pool-")); process.env.OPENCODEX_HOME = home; @@ -121,6 +137,97 @@ async function seedThreeAccounts() { } describe("anthropic account pool", () => { + test("pool-off 429 recovery commits the replacement and notifies before dispatch", async () => { + const { aId, bId } = await seedTwoAccounts(); + const config = cfg(false); + const expected = captureOAuthAccountSelection("anthropic"); + const next = rotateAnthropicAccountOn429(config, aId, "30", "off-retry"); + expect(next).toBe(bId); + const snapshot = await getAnthropicPoolAccessSnapshot(bId); + let notifications = 0; + const unsubscribe = subscribeAccountSelections(event => { + if (event.provider === "anthropic") { + notifications++; + expect(captureOAuthAccountSelection("anthropic")?.accountId).toBe(bId); + } + }); + try { + expect(await promoteAnthropicActiveAccount(bId, expected, { + config, sessionKey: "off-retry", expectedCredentialGeneration: snapshot.generation, + })).not.toBeNull(); + expect(captureOAuthAccountSelection("anthropic")?.accountId).toBe(bId); + expect(notifications).toBe(1); + expect(anthropicSessionAffinitySizeForTests()).toBe(0); + } finally { unsubscribe(); } + }); + + test.each([false, true])("stale promotion cannot replace a newer manual choice (ABA=%s)", async aba => { + const { aId, bId } = await seedTwoAccounts(); + const expected = captureOAuthAccountSelection("anthropic"); + const snapshot = await getAnthropicPoolAccessSnapshot(bId); + await setActiveAccount("anthropic", bId); + if (aba) await setActiveAccount("anthropic", aId); + const manual = captureOAuthAccountSelection("anthropic"); + resetAnthropicRoutingForManualSelection(manual!.accountId); + let notifications = 0; + const unsubscribe = subscribeAccountSelections(() => { notifications++; }); + try { + expect(await promoteAnthropicActiveAccount(bId, expected, { + config: cfg(true), sessionKey: "stale", expectedCredentialGeneration: snapshot.generation, + })).toBeNull(); + expect(captureOAuthAccountSelection("anthropic")).toEqual(manual); + expect(anthropicSessionAffinitySizeForTests()).toBe(0); + expect(notifications).toBe(0); + expect(resolveAnthropicAccountForSession("next", cfg(true)).accountId).toBe(manual!.accountId); + } finally { unsubscribe(); } + }); + + test("token rejection does not consume the manual preference or install affinity", async () => { + const { aId, bId } = await seedTwoAccounts(); + await setActiveAccount("anthropic", aId); + resetAnthropicRoutingForManualSelection(aId); + const expected = captureOAuthAccountSelection("anthropic"); + const snapshot = await getAnthropicPoolAccessSnapshot(aId); + await markAccountNeedsReauth("anthropic", aId, true); + expect(await promoteAnthropicActiveAccount(aId, expected, { + config: cfg(true), sessionKey: "rejected", expectedCredentialGeneration: snapshot.generation, + })).toBeNull(); + expect(anthropicSessionAffinitySizeForTests()).toBe(0); + await markAccountNeedsReauth("anthropic", aId, false); + setCachedProviderAccountQuotaForTests("anthropic", aId, { fiveHourPercent: 30 }); + setCachedProviderAccountQuotaForTests("anthropic", bId, { fiveHourPercent: 11 }); + expect(resolveAnthropicAccountForSession("recovered", cfg(true, 20)).accountId).toBe(aId); + }); + + test("account snapshot refuses expired background local-CLI credentials without refreshing", async () => { + const { aId, bId } = await seedTwoAccounts(); + const account = getAccountSet("anthropic")!.accounts.find(account => account.id === bId)!; + await saveAccountCredential("anthropic", bId, { ...account.credential, source: "local-cli", expires: 1 }); + await expect(getAnthropicPoolAccessSnapshot(bId)).rejects.toThrow("background local-cli token expired"); + expect(captureOAuthAccountSelection("anthropic")?.accountId).toBe(aId); + }); + + test("manual choice wins the next healthy quota dispatch above the automatic threshold", async () => { + const { aId, bId } = await seedTwoAccounts(); + setCachedProviderAccountQuotaForTests("anthropic", aId, { fiveHourPercent: 30 }); + setCachedProviderAccountQuotaForTests("anthropic", bId, { fiveHourPercent: 11 }); + await setActiveAccount("anthropic", aId); + resetAnthropicRoutingForManualSelection(aId); + expect(resolveAnthropicAccountForSession("manual-quota", cfg(true, 20)).accountId).toBe(aId); + }); + + test("uncommitted proposals neither bind affinity nor advance round-robin", async () => { + const { aId, bId } = await seedTwoAccounts(); + const config = cfg(true, 80, { strategy: "round-robin", stickyLimit: 1 }); + const first = resolveAnthropicAccountForSession("uncommitted", config); + expect(resolveAnthropicAccountForSession("another-uncommitted", config).accountId).toBe(first.accountId); + // A failed candidate must not capture the task's affinity before its selection commits. + setCachedProviderAccountQuotaForTests("anthropic", aId, { fiveHourPercent: 90 }); + setCachedProviderAccountQuotaForTests("anthropic", bId, { fiveHourPercent: 5 }); + const quota = cfg(true); + expect(resolveAnthropicAccountForSession("uncommitted", quota).accountId).toBe(bId); + }); + test("default off always returns the active account", async () => { const { aId, bId } = await seedTwoAccounts(); expect(isAnthropicAccountPoolEnabled(cfg(false))).toBe(false); @@ -135,7 +242,7 @@ describe("anthropic account pool", () => { // Force lowest-usage toward B for a cold start with high active usage. setCachedProviderAccountQuotaForTests("anthropic", aId, { fiveHourPercent: 95 }); setCachedProviderAccountQuotaForTests("anthropic", bId, { fiveHourPercent: 10 }); - const first = resolveAnthropicAccountForSession("sess-sticky", cfg(true)); + const first = await admitAnthropic("sess-sticky", cfg(true)); expect(first.accountId).toBe(bId); // Even if A becomes "better", affinity keeps B. setCachedProviderAccountQuotaForTests("anthropic", aId, { fiveHourPercent: 1 }); @@ -213,9 +320,9 @@ describe("anthropic account pool", () => { const config = cfg(true, 80, { strategy: "round-robin" }); const picks = [ - resolveAnthropicAccountForSession("sess-1", config).accountId, - resolveAnthropicAccountForSession("sess-2", config).accountId, - resolveAnthropicAccountForSession("sess-3", config).accountId, + (await admitAnthropic("sess-1", config)).accountId, + (await admitAnthropic("sess-2", config)).accountId, + (await admitAnthropic("sess-3", config)).accountId, ]; expect(new Set(picks).size).toBe(3); }); @@ -239,7 +346,7 @@ describe("anthropic account pool", () => { setCachedProviderAccountQuotaForTests("anthropic", cId, { fiveHourPercent: 10 }); const config = cfg(true, 80, { strategy: "round-robin" }); - const first = resolveAnthropicAccountForSession("T", config); + const first = await admitAnthropic("T", config); expect(first.accountId).toBeTruthy(); const pinned = first.accountId!; await setActiveAccount("anthropic", pinned === aId ? bId : aId); @@ -293,11 +400,11 @@ describe("anthropic account pool", () => { setCachedProviderAccountQuotaForTests("anthropic", cId, { fiveHourPercent: 10 }); const config = cfg(true, 80, { strategy: "round-robin", stickyLimit: 3 }); - const first = resolveAnthropicAccountForSession("s1", config).accountId; + const first = (await admitAnthropic("s1", config)).accountId; expect(first).toBeTruthy(); - expect(resolveAnthropicAccountForSession("s2", config).accountId).toBe(first); - expect(resolveAnthropicAccountForSession("s3", config).accountId).toBe(first); - const fourth = resolveAnthropicAccountForSession("s4", config).accountId; + expect((await admitAnthropic("s2", config)).accountId).toBe(first); + expect((await admitAnthropic("s3", config)).accountId).toBe(first); + const fourth = (await admitAnthropic("s4", config)).accountId; expect(fourth).not.toBe(first); }); @@ -308,17 +415,17 @@ describe("anthropic account pool", () => { setCachedProviderAccountQuotaForTests("anthropic", cId, { fiveHourPercent: 10 }); const config = cfg(true, 80, { strategy: "round-robin", stickyLimit: 10 }); - const sticky = resolveAnthropicAccountForSession("sticky-1", config).accountId!; + const sticky = (await admitAnthropic("sticky-1", config)).accountId!; expect(resolveAnthropicAccountForSession("sticky-2", config).accountId).toBe(sticky); notePoolRotationFailure(POOL_KEY_ANTHROPIC, sticky); - const afterClear = resolveAnthropicAccountForSession("sticky-3", config).accountId; + const afterClear = (await admitAnthropic("sticky-3", config)).accountId; expect(afterClear).toBeTruthy(); expect(afterClear).not.toBe(sticky); // Re-establish sticky, then 429-cool the sticky account — failover + ring must leave it. clearPoolRotationState(); - const again = resolveAnthropicAccountForSession("again-1", config).accountId!; + const again = (await admitAnthropic("again-1", config)).accountId!; expect(resolveAnthropicAccountForSession("again-2", config).accountId).toBe(again); const failover = rotateAnthropicAccountOn429(config, again, "30"); expect(failover).toBeTruthy(); @@ -369,7 +476,7 @@ describe("anthropic account pool", () => { const before = getAccountSet("anthropic")!.activeAccountId; const picks = Array.from({ length: 3 }, (_, i) => resolveAnthropicAccountForSession(`promo-${i}`, config)); - expect(new Set(picks.map(p => p.accountId)).size).toBe(3); + expect(new Set(picks.map(p => p.accountId)).size).toBe(1); expect(getAccountSet("anthropic")!.activeAccountId).toBe(before); }); diff --git a/tests/adapters/key-failover.test.ts b/tests/adapters/key-failover.test.ts index 7f4a77ef55..8efb505aa6 100644 --- a/tests/adapters/key-failover.test.ts +++ b/tests/adapters/key-failover.test.ts @@ -20,7 +20,11 @@ import { } from "../../src/providers/key-failover"; import { resolveOpenCodeGoTransport } from "../../src/providers/opencode-go-transport"; import { deriveXaiConvId } from "../../src/providers/xai-transport"; -import { routeModel } from "../../src/router"; +import { routeModel, routedProviderConfig } from "../../src/router"; +import { setProviderKeychainEntryFactoryForTests } from "../../src/providers/key-store"; +import { setActiveProviderApiKey } from "../../src/providers/api-keys"; +import { subscribeAccountSelections } from "../../src/lib/account-selection-events"; +import { providerManagementConfigError, safeConfigDTO } from "../../src/server/auth-cors"; import type { OcxConfig, OcxParsedRequest, OcxProviderConfig } from "../../src/types"; import { removeTreeWithRetry } from "../helpers/remove-tree"; @@ -63,6 +67,17 @@ afterEach(() => { }); describe("hasKeyPoolFailover", () => { + test("request key identity is rejected by management and stripped from the public config", () => { + const config = makeConfig({ apiKey: "synthetic-first", apiKeyPool: [{ id: "first", key: "synthetic-first" }] }); + const routed = routedProviderConfig("p", config.providers.p); + expect(providerManagementConfigError("p", routed)).toContain("runtime field"); + const dto = JSON.stringify(safeConfigDTO({ ...config, providers: { p: { + ...routed, apiKeySelectionRevision: "internal-revision", + } } })); + expect(dto).not.toContain("_apiKeyAttempt"); + expect(dto).not.toContain("apiKeySelectionRevision"); + expect(dto).not.toContain("synthetic-first"); + }); test("true only for key-auth providers with 2+ pool entries", () => { expect(hasKeyPoolFailover({ adapter: "openai-chat", baseUrl: "x", apiKeyPool: pool3() } as OcxProviderConfig)).toBe(true); expect(hasKeyPoolFailover({ adapter: "openai-chat", baseUrl: "x", apiKeyPool: [pool3()![0]] } as OcxProviderConfig)).toBe(false); @@ -73,6 +88,70 @@ describe("hasKeyPoolFailover", () => { }); describe("rotateKeyOn429", () => { + test("an old attempt cannot overwrite a newer manual key selection or its ABA revision", () => { + const config = makeConfig({ apiKey: "key-alpha-000111222333", apiKeyPool: pool3() }); + const routed = routedProviderConfig("p", config.providers.p); + const events: string[] = []; + const unsubscribe = subscribeAccountSelections(event => { + if (event.provider === "p") events.push(loadConfig().providers.p.apiKey!); + }); + try { + expect(setActiveProviderApiKey(config, "p", "k2")).toBe(true); + expect(rotateProviderTransportOn429(config, "p", routed, { attemptedKey: routed.apiKey })?.apiKey) + .toBe("key-beta-444555666777"); + expect(events).toEqual(["key-beta-444555666777"]); + expect(setActiveProviderApiKey(config, "p", "k1")).toBe(true); + expect(rotateProviderTransportOn429(config, "p", routed, { attemptedKey: routed.apiKey })).toBeNull(); + expect(loadConfig().providers.p.apiKey).toBe("key-alpha-000111222333"); + expect(getKeyCooldownUntil("p", "k1")).toBeNull(); + expect(events).toEqual(["key-beta-444555666777", "key-alpha-000111222333"]); + } finally { unsubscribe(); } + }); + + test("manual and automatic selection events observe committed disk state", () => { + const config = makeConfig({ apiKey: "key-alpha-000111222333", apiKeyPool: pool3() }); + const events: string[] = []; + const unsubscribe = subscribeAccountSelections(event => { + if (event.provider === "p") { + expect(event.kind).toBe("api-key"); + expect(Object.keys(event).sort()).toEqual(["kind", "provider", "revision"]); + events.push(loadConfig().providers.p.apiKey!); + } + }); + try { + const routed = routedProviderConfig("p", config.providers.p); + expect(rotateProviderTransportOn429(config, "p", routed)?.apiKey).toBe("key-beta-444555666777"); + expect(events).toEqual(["key-beta-444555666777"]); + unlinkSync(getConfigPath()); + expect(rotateKeyOn429(config, "p", null)).toBeNull(); + expect(events).toHaveLength(1); + } finally { unsubscribe(); } + }); + + test.each(["env", "keychain"])("rotates a rejected %s reference instead of reusing its resolved credential", kind => { + const reference = kind === "env" ? "${OCX_SELECTION_TEST_KEY}" : "keychain:p/k1"; + process.env.OCX_SELECTION_TEST_KEY = "synthetic-resolved-first"; + setProviderKeychainEntryFactoryForTests(() => ({ + getPassword: () => "synthetic-resolved-first", + setPassword: () => {}, + deletePassword: () => true, + })); + try { + const config = makeConfig({ apiKey: reference, apiKeyPool: [ + { id: "k1", key: reference }, { id: "k2", key: "synthetic-second" }, + ] }); + const routed = routedProviderConfig("p", config.providers.p); + expect(routed.apiKey).toBe("synthetic-resolved-first"); + const rotated = rotateProviderTransportOn429(config, "p", routed, { attemptedKey: routed.apiKey }); + expect(rotated?.apiKey).toBe("synthetic-second"); + expect(loadConfig().providers.p.apiKey).toBe("synthetic-second"); + expect(getKeyCooldownUntil("p", "k1")).not.toBeNull(); + } finally { + delete process.env.OCX_SELECTION_TEST_KEY; + setProviderKeychainEntryFactoryForTests(null); + } + }); + test("rotates to the next key and cools down the exhausted one", () => { const config = makeConfig({ apiKey: "key-alpha-000111222333", apiKeyPool: pool3() }); const now = 1_000_000; diff --git a/tests/oauth/adapter-event-oauth-failover.test.ts b/tests/oauth/adapter-event-oauth-failover.test.ts index 8309d9ffd3..a12449786e 100644 --- a/tests/oauth/adapter-event-oauth-failover.test.ts +++ b/tests/oauth/adapter-event-oauth-failover.test.ts @@ -4,7 +4,7 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; import type { ProviderAdapter } from "../../src/adapters/base"; import { clearGenericFailoverHealth } from "../../src/oauth/generic-account-failover"; -import { saveCredential } from "../../src/oauth/store"; +import { getAccountSet, getCredential, saveCredential, setActiveAccount } from "../../src/oauth/store"; import type { AdapterEvent, OcxConfig, OcxProviderConfig } from "../../src/types"; import { removeTreeWithRetry } from "../helpers/remove-tree"; @@ -113,9 +113,28 @@ describe("#2568 adapter-event OAuth failover", () => { expect(attemptKeys).toEqual(["cursor-access-1", "cursor-access-0"]); expect(body).toContain("alternate answer"); expect(body).not.toContain("Cursor rate limit exceeded"); + expect(getCredential("cursor")?.access).toBe("cursor-access-0"); }); } + test("a newer manual choice wins a pending request's 429 proposal", async () => { + await seedAccounts(3); + const accounts = getAccountSet("cursor")!.accounts; + slowAttempt = async emit => { + if (attemptKeys.length === 1) { + await setActiveAccount("cursor", accounts[1]!.id); + emit({ type: "error", message: "Cursor rate limit exceeded: resource_exhausted" }); + } else { + emit({ type: "text_delta", text: "manual choice answered" }); + emit({ type: "done" }); + } + }; + const response = await handleResponses(request(false), config(false), { model: "", provider: "" }); + expect(await response.text()).toContain("manual choice answered"); + expect(attemptKeys).toEqual(["cursor-access-2", "cursor-access-1"]); + expect(getCredential("cursor")?.access).toBe("cursor-access-1"); + }); + test("a single account is a strict no-op", async () => { await seedAccounts(1); attempts = [[{ type: "error", message: "Cursor rate limit exceeded: resource_exhausted" }]]; diff --git a/tests/oauth/generic-oauth-failover.test.ts b/tests/oauth/generic-oauth-failover.test.ts index 3761d65ce3..70ffff6d09 100644 --- a/tests/oauth/generic-oauth-failover.test.ts +++ b/tests/oauth/generic-oauth-failover.test.ts @@ -71,6 +71,45 @@ async function seed(count: number, offset = 0): Promise { } describe("#2568 generic OAuth account failover", () => { + for (const provider of ["xai", "cursor", "kimi", "github-copilot", "google-antigravity", "nous", "kiro", "meta-muse"]) { + test(`manual selection owns healthy dispatch for ${provider}, with pool off or on`, async () => { + for (const accountId of ["selected", "spare"]) { + await saveCredential(provider, { + access: `synthetic-${accountId}`, refresh: `refresh-${accountId}`, + expires: Date.now() + 3_600_000, accountId, + }); + } + const ids = getAccountSet(provider)!.accounts.map(a => a.id); + await setActiveAccount(provider, ids[0]!); + setCachedProviderAccountQuotaForTests(provider, ids[0]!, { weeklyPercent: 30, updatedAt: Date.now() }); + setCachedProviderAccountQuotaForTests(provider, ids[1]!, { weeklyPercent: 11, updatedAt: Date.now() }); + for (const enabled of [undefined, false, true]) { + const cfg = { providers: { [provider]: { ...OAUTH_PROVIDER, + ...(enabled === undefined ? {} : { oauthAccountFailover: { enabled } }), + } } } as OcxConfig; + expect(preferredInitialAccount(cfg, provider)).toBeNull(); + } + clearAccountQuotaCache(provider); + }); + } + + test("proactive exhaustion avoidance requires explicit pool enablement", async () => { + const [selected, spare] = await seed(2); + await setActiveAccount("xai", selected!); + setCachedProviderAccountQuotaForTests("xai", selected!, { weeklyPercent: 100, updatedAt: Date.now() }); + setCachedProviderAccountQuotaForTests("xai", spare!, { weeklyPercent: 11, updatedAt: Date.now() }); + expect(preferredInitialAccount(config(), "xai")).toBeNull(); + expect(preferredInitialAccount(config(false), "xai")).toBeNull(); + expect(preferredInitialAccount(config(true), "xai")).toBe(spare); + }); + + test("unknown selected quota is not permission to replace the account", async () => { + const [selected, spare] = await seed(2); + await setActiveAccount("xai", selected!); + setCachedProviderAccountQuotaForTests("xai", spare!, { weeklyPercent: 11, updatedAt: Date.now() }); + expect(preferredInitialAccount(config(true), "xai")).toBeNull(); + }); + test("two logged-in accounts rotate with NO configuration at all (#2568d)", async () => { // The reported workflow: three xAI accounts are logged in, the active one hits its limit, and // the operator never went looking for a toggle. Presence is the consent signal. @@ -145,7 +184,7 @@ describe("#2568 generic OAuth account failover", () => { const ids = await seed(2); await setActiveAccount("xai", ids[0]!); clearGenericFailoverHealth("xai"); - setCachedProviderAccountQuotaForTests("xai", ids[0]!, { fiveHourPercent: 99 }); + setCachedProviderAccountQuotaForTests("xai", ids[0]!, { fiveHourPercent: 100 }); setCachedProviderAccountQuotaForTests("xai", ids[1]!, { fiveHourPercent: 1 }); expect(preferredInitialAccount(config(false, true), "xai")).toBe(ids[1]); @@ -390,7 +429,9 @@ describe("sidecar on429 wiring", () => { // to the configured account's project — #2841 in its original shape. const start = coreSource.indexOf("const preferredAccountId ="); expect(start).toBeGreaterThan(-1); - const region = coreSource.slice(start, start + 6000); + const end = coreSource.indexOf("\n route.provider = resolveProviderTransport(", start); + expect(end).toBeGreaterThan(start); + const region = coreSource.slice(start, end); expect(region).toContain("usedPreferredAccount && resolved.projectId"); // A project-less preferred account falls BACK to the ordinary active-account resolution // rather than erroring: a preference must never turn a working request into a failure, diff --git a/tests/oauth/oauth-accounts-api.test.ts b/tests/oauth/oauth-accounts-api.test.ts index cbeb165e59..edc332378e 100644 --- a/tests/oauth/oauth-accounts-api.test.ts +++ b/tests/oauth/oauth-accounts-api.test.ts @@ -66,6 +66,63 @@ afterEach(() => { }); describe("multiauth accounts API", () => { + test("selection events require management authentication", async () => { + const server = startServer(0); + try { + const response = await originalFetch(new URL("/api/accounts/events", server.url)); + expect(response.status).toBe(401); + await response.body?.cancel(); + } finally { await server.stop(true); } + }); + + test("selection event streams bound subscribers and release cancelled connections", async () => { + const { accountSelectionStream } = await import("../../src/server/management/account-selection-stream"); + const streams: Response[] = []; + try { + for (let i = 0; i < 64; i++) { + const response = accountSelectionStream(new Request("http://localhost/api/accounts/events")); + expect(response.status).toBe(200); + streams.push(response); + } + expect(accountSelectionStream(new Request("http://localhost/api/accounts/events")).status).toBe(429); + } finally { + await Promise.all(streams.map(response => response.body!.cancel())); + } + const response = accountSelectionStream(new Request("http://localhost/api/accounts/events")); + expect(response.status).toBe(200); + await response.body!.cancel(); + }); + + test("selection events notify only after the new active account is committed", async () => { + const server = startServer(0); + const abort = new AbortController(); + let reader: ReadableStreamDefaultReader | undefined; + try { + const response = await fetch(new URL("/api/accounts/events", server.url), { signal: abort.signal }); + expect(response.status).toBe(200); + expect(response.headers.get("content-type")).toContain("text/event-stream"); + reader = response.body!.getReader(); + const ready = new TextDecoder().decode((await reader.read()).value); + expect(ready).toContain("event: ready"); + const eventRead = reader.read(); + const selected = await fetch(new URL("/api/oauth/accounts/active", server.url), { + method: "PUT", headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ provider: "anthropic", accountId: "bbbb2222" }), + }); + expect(selected.status).toBe(200); + const notification = new TextDecoder().decode((await eventRead).value); + expect(notification).toContain("event: account-selection"); + expect(notification).toContain('"provider":"anthropic"'); + expect(notification).not.toContain("bbbb2222"); + expect(notification).not.toContain("t2"); + expect(getAccountSet("anthropic")?.activeAccountId).toBe("bbbb2222"); + } finally { + await reader?.cancel(); + abort.abort(); + await server.stop(true); + } + }); + test("GET lists masked accounts with active flag", async () => { const server = startServer(0); try { diff --git a/tests/oauth/oauth-store-multi.test.ts b/tests/oauth/oauth-store-multi.test.ts index 58791494de..6cd3f21dbe 100644 --- a/tests/oauth/oauth-store-multi.test.ts +++ b/tests/oauth/oauth-store-multi.test.ts @@ -1,7 +1,9 @@ -import { afterEach, beforeEach, describe, expect, test } from "bun:test"; +import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test"; import { INTERNAL_DEADLINE_MS, STORE_BUDGET_MS } from "../helpers/test-budget"; import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; +import * as atomicWrite from "../../src/config/atomic-write"; +import * as oauthStore from "../../src/oauth/store"; import { resetHardenedStateForTests, setIcaclsRunnerForTests, @@ -14,16 +16,19 @@ import { listAccounts, markAccountNeedsReauth, markAccountNeedsReauthIfGeneration, + mergeAccountCredential, mutateStore, OAuthMutationBusyError, oauthMutationTailSnapshot, reconcileOAuthReauthState, removeAccount, removeCredential, + replaceProviderAccountSet, saveAccountCredential, saveCredential, setAccountAlias, setActiveAccount, + upsertCredentialByIdentity, } from "../../src/oauth/store"; import type { OAuthCredentials } from "../../src/oauth/types"; import { removeTreeWithRetry } from "../helpers/remove-tree"; @@ -38,6 +43,17 @@ const cred = (over: Partial = {}): OAuthCredentials => ({ ...over, }); +const SELECTION_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; + +async function selectionAccounts() { + await saveCredential("xai", cred({ accountId: "selection-a" })); + const idA = getAccountSet("xai")!.activeAccountId; + await saveCredential("xai", cred({ accountId: "selection-b", access: "access-b" })); + const idB = getAccountSet("xai")!.activeAccountId; + await setActiveAccount("xai", idA); + return { idA, idB }; +} + describe("multi-account auth store", () => { beforeEach(() => { previousOpencodexHome = process.env.OPENCODEX_HOME; @@ -306,6 +322,264 @@ describe("multi-account auth store", () => { expect(set.activeAccountId).toBe("ok"); // dangling active healed }); + test("selection revision rejects an automatic promotion after manual A-B-A", async () => { + const { idA, idB } = await selectionAccounts(); + const before = getAccountSet("xai")!.selectionRevision; + expect(before).toMatch(SELECTION_UUID); + const expectedSelection = oauthStore.captureOAuthAccountSelection("xai")!; + await setActiveAccount("xai", idB); + await setActiveAccount("xai", idA); + expect(getAccountSet("xai")!.selectionRevision).not.toBe(before); + expect(await oauthStore.commitOAuthAccountSelection("xai", idB, { expectedSelection })).toBeNull(); + expect(oauthStore.captureOAuthAccountSelection("xai")?.accountId).toBe(idA); + }); + + test("selection revision preserves credential-only refresh and unrelated account metadata", async () => { + const { idA, idB } = await selectionAccounts(); + // Seed a persisted revision independently to catch normalization dropping it. + const authPath = join(TEST_DIR, "auth.json"); + const raw = JSON.parse(readFileSync(authPath, "utf8")); + const revision = "f4abbddc-5c7c-4e87-bd8a-b5775a182860"; + raw.xai.selectionRevision = revision; + writeFileSync(authPath, JSON.stringify(raw)); + await saveAccountCredential("xai", idA, cred({ accountId: "selection-a", access: "refreshed-a" })); + expect(getAccountSet("xai")!.selectionRevision).toBe(revision); + await mergeAccountCredential("xai", idB, cred({ accountId: "selection-b", access: "refreshed-b" })); + await setAccountAlias("xai", idA, "Selection test"); + await markAccountNeedsReauth("xai", idB, true); + await upsertCredentialByIdentity("xai", cred({ accountId: "selection-a", access: "import-refreshed" })); + expect(getAccountSet("xai")!.selectionRevision).toBe(revision); + expect(JSON.parse(readFileSync(authPath, "utf8")).xai.selectionRevision).toBe(revision); + expect(oauthStore.captureOAuthAccountSelection("xai")).toEqual({ accountId: idA, revision }); + }); + + test("selection revision advances for removal, recreation, and rollback replacement", async () => { + const { idA, idB } = await selectionAccounts(); + const original = getAccountSet("xai")!; + expect(original.selectionRevision).toMatch(SELECTION_UUID); + const expectedSelection = oauthStore.captureOAuthAccountSelection("xai")!; + await removeAccount("xai", idA); + expect(getAccountSet("xai")!.activeAccountId).toBe(idB); + const promoted = getAccountSet("xai")!.selectionRevision; + expect(promoted).not.toBe(original.selectionRevision); + await removeCredential("xai"); + expect(oauthStore.captureOAuthAccountSelection("xai")).toBeNull(); + await saveCredential("xai", cred({ accountId: "selection-a" })); + const recreated = getAccountSet("xai")!; + expect(recreated.activeAccountId).toBe(idA); + expect(recreated.selectionRevision).not.toBe(original.selectionRevision); + await replaceProviderAccountSet("xai", original); + const restored = getAccountSet("xai")!; + expect(restored.selectionRevision).toMatch(SELECTION_UUID); + expect([original.selectionRevision, promoted, recreated.selectionRevision]).not.toContain(restored.selectionRevision); + expect(original.selectionRevision).toBe(expectedSelection.revision); + expect(await oauthStore.commitOAuthAccountSelection("xai", idB, { expectedSelection })).toBeNull(); + }); + + test("selection revision advances on same-id manual reselect but not automatic validation", async () => { + const { idA } = await selectionAccounts(); + const before = getAccountSet("xai")!.selectionRevision; + await setActiveAccount("xai", idA); + const after = getAccountSet("xai")!.selectionRevision; + expect(after).not.toBe(before); + expect(after).toMatch(SELECTION_UUID); + const expectedSelection = oauthStore.captureOAuthAccountSelection("xai")!; + expect(await oauthStore.commitOAuthAccountSelection("xai", idA, { + expectedSelection, + expectedCredentialGeneration: credentialGeneration(getAccountCredential("xai", idA)!), + requireUsableAccount: true, + })).toEqual(expectedSelection); + expect(oauthStore.captureOAuthAccountSelection("xai")).toEqual(expectedSelection); + }); + + test("selection commit supports revisionless legacy snapshots and guards the original id", async () => { + const authPath = join(TEST_DIR, "auth.json"); + writeFileSync(authPath, JSON.stringify({ xai: { + activeAccountId: "legacy-a", + accounts: [{ id: "legacy-a", credential: cred() }, { id: "legacy-b", credential: cred({ access: "b" }) }], + } })); + const expectedSelection = oauthStore.captureOAuthAccountSelection("xai")!; + expect(expectedSelection).toEqual({ accountId: "legacy-a" }); + expect(await oauthStore.commitOAuthAccountSelection("xai", "legacy-b", { + expectedSelection: { accountId: "wrong-id" }, + })).toBeNull(); + expect(await oauthStore.commitOAuthAccountSelection("xai", "legacy-a", { expectedSelection })).toEqual(expectedSelection); + const committed = await oauthStore.commitOAuthAccountSelection("xai", "legacy-b", { expectedSelection }); + expect(committed?.accountId).toBe("legacy-b"); + expect(committed?.revision).toMatch(SELECTION_UUID); + expect(oauthStore.captureOAuthAccountSelection("xai")).toEqual(committed); + }); + + test.each(["manual", "refresh", "reauth", "remove"] as const)("selection commit rechecks queued %s changes under the writer", async change => { + const { idA, idB } = await selectionAccounts(); + const expectedSelection = oauthStore.captureOAuthAccountSelection("xai")!; + const expectedCredentialGeneration = credentialGeneration(getAccountCredential("xai", idB)!); + let release!: () => void; + let entered!: () => void; + const gate = new Promise(resolve => { release = resolve; }); + const started = new Promise(resolve => { entered = resolve; }); + const blocker = mutateStore(async () => { entered(); await gate; }); + await started; + const mutation = change === "manual" ? setActiveAccount("xai", idA) + : change === "refresh" ? saveAccountCredential("xai", idB, cred({ accountId: "selection-b", access: "fresh-b" })) + : change === "reauth" ? markAccountNeedsReauth("xai", idB, true) + : removeAccount("xai", idB); + const pending = oauthStore.commitOAuthAccountSelection("xai", idB, { + expectedSelection, expectedCredentialGeneration, requireUsableAccount: true, + }); + try { + release(); + await blocker; + await mutation; + expect(await pending).toBeNull(); + expect(getAccountSet("xai")!.activeAccountId).toBe(idA); + } finally { + release(); + await Promise.allSettled([blocker, mutation, pending]); + } + }); + + test("selection commit checks same-account usability and refreshed credential generation", async () => { + const { idA, idB } = await selectionAccounts(); + const expectedSelection = oauthStore.captureOAuthAccountSelection("xai")!; + const oldGeneration = credentialGeneration(getAccountCredential("xai", idA)!); + await saveAccountCredential("xai", idA, cred({ accountId: "selection-a", access: "rotated-a" })); + expect(await oauthStore.commitOAuthAccountSelection("xai", idA, { + expectedSelection, expectedCredentialGeneration: oldGeneration, requireUsableAccount: true, + })).toBeNull(); + await markAccountNeedsReauth("xai", idA, true); + expect(await oauthStore.commitOAuthAccountSelection("xai", idA, { + expectedSelection, requireUsableAccount: true, + })).toBeNull(); + const committed = await oauthStore.commitOAuthAccountSelection("xai", idB, { + expectedSelection, + expectedCredentialGeneration: credentialGeneration(getAccountCredential("xai", idB)!), + requireUsableAccount: true, + }); + expect(committed?.accountId).toBe(idB); + expect(committed?.revision).not.toBe(expectedSelection.revision); + expect(oauthStore.captureOAuthAccountSelection("xai")).toEqual(committed); + }); + + test("unchanged selection admission neither joins a busy writer nor persists", async () => { + const { idA } = await selectionAccounts(); + const expectedSelection = oauthStore.captureOAuthAccountSelection("xai")!; + const expectedCredentialGeneration = credentialGeneration(getAccountCredential("xai", idA)!); + let release!: () => void; + let entered!: () => void; + const gate = new Promise(resolve => { release = resolve; }); + const started = new Promise(resolve => { entered = resolve; }); + const blocker = mutateStore(async () => { entered(); await gate; }); + await started; + const write = spyOn(atomicWrite, "atomicWriteFile"); + const pending = oauthStore.commitOAuthAccountSelection("xai", idA, { + expectedSelection, expectedCredentialGeneration, requireUsableAccount: true, + }); + try { + expect(oauthMutationTailSnapshot().active).toBe(1); + expect(await pending).toEqual(expectedSelection); + expect(write).not.toHaveBeenCalled(); + } finally { + write.mockRestore(); + release(); + await Promise.allSettled([blocker, pending]); + } + }); + + test("selection events follow persistence and omit failed commits, refreshes, and credentials", async () => { + const { idA, idB } = await selectionAccounts(); + const { subscribeAccountSelections, currentAccountSelectionRevision } = await import("../../src/lib/account-selection-events"); + const events: unknown[] = []; + const observedSelections: unknown[] = []; + const start = currentAccountSelectionRevision(); + const unsubscribe = subscribeAccountSelections(event => { + events.push(event); + observedSelections.push(oauthStore.captureOAuthAccountSelection("xai")); + }); + try { + const expectedSelection = oauthStore.captureOAuthAccountSelection("xai")!; + await setActiveAccount("xai", idA); + const manual = oauthStore.captureOAuthAccountSelection("xai")!; + expect(events).toEqual([{ provider: "xai", kind: "oauth", revision: start + 1 }]); + expect(observedSelections).toEqual([manual]); + expect(await oauthStore.commitOAuthAccountSelection("xai", idB, { expectedSelection })).toBeNull(); + expect(await oauthStore.commitOAuthAccountSelection("xai", "missing")).toBeNull(); + expect(await setActiveAccount("xai", "missing")).toBe(false); + await oauthStore.commitOAuthAccountSelection("xai", idA, { expectedSelection: manual }); + await saveAccountCredential("xai", idA, cred({ accountId: "selection-a", access: "event-refresh" })); + expect(events).toHaveLength(1); + + // Only the I/O boundary is faulted; the actual commit, locks, and store stay real. + const write = spyOn(atomicWrite, "atomicWriteFile").mockImplementation(() => { throw new Error("selection persist failed"); }); + try { + await expect(oauthStore.commitOAuthAccountSelection("xai", idB, { expectedSelection: manual })).rejects.toThrow("selection persist failed"); + } finally { + write.mockRestore(); + } + expect(oauthStore.captureOAuthAccountSelection("xai")).toEqual(manual); + expect(events).toHaveLength(1); + expect(currentAccountSelectionRevision()).toBe(start + 1); + await expect(saveCredential("xai", cred({ accountId: "blocked-login" }), { + assertBeforePersist: () => { throw new Error("selection pre-persist rejected"); }, + })).rejects.toThrow("selection pre-persist rejected"); + expect(events).toHaveLength(1); + + await oauthStore.commitOAuthAccountSelection("xai", idB, { expectedSelection: manual }); + expect(events).toEqual([ + { provider: "xai", kind: "oauth", revision: start + 1 }, + { provider: "xai", kind: "oauth", revision: start + 2 }, + ]); + unsubscribe(); + unsubscribe(); + await setActiveAccount("xai", idA); + expect(events).toHaveLength(2); + } finally { + unsubscribe(); + } + }); + + test("selection events cover create, inactive removal, replacement, clear, and recreate", async () => { + const { subscribeAccountSelections, currentAccountSelectionRevision, publishAccountSelection } = await import("../../src/lib/account-selection-events"); + const events: unknown[] = []; + const start = currentAccountSelectionRevision(); + const unsubscribe = subscribeAccountSelections(event => { events.push(event); }); + try { + await upsertCredentialByIdentity("xai", cred({ accountId: "selection-a" })); + const original = getAccountSet("xai")!; + await upsertCredentialByIdentity("xai", cred({ accountId: "selection-b" })); + expect(events).toHaveLength(1); // Importing an inactive account preserves the selection. + const inactive = listAccounts("xai").find(account => account.id !== original.activeAccountId)!; + await removeAccount("xai", inactive.id); + expect(getAccountSet("xai")!.selectionRevision).not.toBe(original.selectionRevision); + await replaceProviderAccountSet("xai", original); + await replaceProviderAccountSet("xai", null); + await replaceProviderAccountSet("xai", null); + await saveCredential("xai", cred({ accountId: "selection-a" })); + publishAccountSelection("key-provider", "api-key"); + expect(events).toEqual([ + ...Array.from({ length: 5 }, (_, index) => ({ provider: "xai", kind: "oauth", revision: start + index + 1 })), + { provider: "key-provider", kind: "api-key", revision: start + 6 }, + ]); + } finally { + unsubscribe(); + } + }); + + test("selection subscriber failure cannot fail a persisted selection or block other subscribers", async () => { + const { idA } = await selectionAccounts(); + const { subscribeAccountSelections } = await import("../../src/lib/account-selection-events"); + const stopBroken = subscribeAccountSelections(() => { throw new Error("disconnected consumer"); }); + const seen: unknown[] = []; + const stopHealthy = subscribeAccountSelections(event => { seen.push(event); }); + try { + expect(await setActiveAccount("xai", idA)).toBe(true); + expect(seen).toHaveLength(1); + } finally { + stopBroken(); + stopHealthy(); + } + }); + test("queued generation-checked reauth mutation rechecks liveness after reconciliation", async () => { await saveCredential("xai", cred({ email: "race@example.com", accountId: "race-account" })); const accountId = getAccountSet("xai")!.activeAccountId; diff --git a/tests/providers/kiro/kiro-pool-rank.test.ts b/tests/providers/kiro/kiro-pool-rank.test.ts index b42eb969d0..554972704a 100644 --- a/tests/providers/kiro/kiro-pool-rank.test.ts +++ b/tests/providers/kiro/kiro-pool-rank.test.ts @@ -143,7 +143,7 @@ describe("pre-dispatch account preference", () => { authMode: "oauth", } as unknown as OcxProviderConfig; - const config = { providers: { xai: OAUTH_PROVIDER } } as unknown as OcxConfig; + const config = { providers: { xai: OAUTH_PROVIDER }, oauthAccountFailover: { enabled: true } } as unknown as OcxConfig; const originalHome = process.env.OPENCODEX_HOME; let home: string; @@ -159,7 +159,7 @@ describe("pre-dispatch account preference", () => { return getAccountSet(providerName)?.accounts.map(a => a.id) ?? []; } - test("the account with more headroom is chosen before the first request", async () => { + test("an enabled pool avoids a known-exhausted selected account", async () => { home = mkdtempSync(join(tmpdir(), "ocx-predispatch-")); process.env.OPENCODEX_HOME = home; clearGenericFailoverHealth(); @@ -167,7 +167,7 @@ describe("pre-dispatch account preference", () => { try { const ids = await seedAccounts(2); await setActiveAccount("xai", ids[0]!); - setCachedProviderAccountQuotaForTests("xai", ids[0]!, { monthlyPercent: 95, updatedAt: Date.now() }); + setCachedProviderAccountQuotaForTests("xai", ids[0]!, { monthlyPercent: 100, updatedAt: Date.now() }); setCachedProviderAccountQuotaForTests("xai", ids[1]!, { monthlyPercent: 5, updatedAt: Date.now() }); expect(preferredInitialAccount(config, "xai")).toBe(ids[1]); } finally { @@ -365,14 +365,7 @@ describe("pre-dispatch account preference", () => { } }); - test("neither a redirecting nor a non-redirecting selection touches the credential store", async () => { - // loadAuthStore chmods the config dir, chmods the secret, and re-parses the whole - // credential file on every call — and this runs on the initial resolution of EVERY - // request. The steady state of this feature is a pool where one account consistently - // ranks higher, so the REDIRECTING path must be cached too — validating the winner here - // would put a second uncached read in front of every such request. Deleting the store - // proves it: an uncached path could not answer at all. Staleness is caught at - // resolution instead, inside a store read the resolver already performs. + test("removing the credential store invalidates an earlier selection proposal", async () => { home = mkdtempSync(join(tmpdir(), "ocx-predispatch-")); process.env.OPENCODEX_HOME = home; clearGenericFailoverHealth(); @@ -381,11 +374,11 @@ describe("pre-dispatch account preference", () => { const ids = await seedAccounts(2); await setActiveAccount("xai", ids[0]!); // Redirecting: the other account holds more headroom on every call. - setCachedProviderAccountQuotaForTests("xai", ids[0]!, { monthlyPercent: 95, updatedAt: Date.now() }); + setCachedProviderAccountQuotaForTests("xai", ids[0]!, { monthlyPercent: 100, updatedAt: Date.now() }); setCachedProviderAccountQuotaForTests("xai", ids[1]!, { monthlyPercent: 5, updatedAt: Date.now() }); expect(preferredInitialAccount(config, "xai")).toBe(ids[1]); rmSync(join(home, "auth.json"), { force: true }); - for (let i = 0; i < 4; i++) expect(preferredInitialAccount(config, "xai")).toBe(ids[1]); + for (let i = 0; i < 4; i++) expect(preferredInitialAccount(config, "xai")).toBeNull(); // Non-redirecting: the active account already ranks best. setCachedProviderAccountQuotaForTests("xai", ids[0]!, { monthlyPercent: 5, updatedAt: Date.now() }); @@ -412,15 +405,14 @@ describe("pre-dispatch account preference", () => { try { const ids = await seedAccounts(2); await setActiveAccount("xai", ids[0]!); - setCachedProviderAccountQuotaForTests("xai", ids[0]!, { monthlyPercent: 95, updatedAt: Date.now() }); + setCachedProviderAccountQuotaForTests("xai", ids[0]!, { monthlyPercent: 100, updatedAt: Date.now() }); setCachedProviderAccountQuotaForTests("xai", ids[1]!, { monthlyPercent: 5, updatedAt: Date.now() }); expect(preferredInitialAccount(config, "xai")).toBe(ids[1]); await removeAccount("xai", ids[1]!); - // Selection is a cached PREFERENCE, so it may still name the removed account... - expect(preferredInitialAccount(config, "xai")).toBe(ids[1]); - // ...and resolution is where that is caught. The request path absorbs this throw and - // falls back to the active account. + // Selection reads the authoritative roster; a removed target is never proposed. + expect(preferredInitialAccount(config, "xai")).toBeNull(); + // The credential resolver independently rejects the removed identity. await expect( getValidAccessSnapshotForAccount("xai", ids[1]!, { requireUsableAccount: true }), ).rejects.toThrow(); @@ -446,11 +438,12 @@ describe("pre-dispatch account preference", () => { try { const ids = await seedAccounts(2); await setActiveAccount("xai", ids[0]!); - setCachedProviderAccountQuotaForTests("xai", ids[0]!, { monthlyPercent: 95, updatedAt: Date.now() }); + setCachedProviderAccountQuotaForTests("xai", ids[0]!, { monthlyPercent: 100, updatedAt: Date.now() }); setCachedProviderAccountQuotaForTests("xai", ids[1]!, { monthlyPercent: 5, updatedAt: Date.now() }); expect(preferredInitialAccount(config, "xai")).toBe(ids[1]); await markAccountNeedsReauth("xai", ids[1]!, true); + expect(preferredInitialAccount(config, "xai")).toBeNull(); // An ordinary resolve SUCCEEDS — the credential is still readable — which is exactly // why the flag must be checked inside the resolver rather than trusted to throw. await expect(getValidAccessSnapshotForAccount("xai", ids[1]!)).resolves.toBeDefined(); diff --git a/tests/server/server-key-failover-e2e.test.ts b/tests/server/server-key-failover-e2e.test.ts index 57ab87b2cb..66cbedd199 100644 --- a/tests/server/server-key-failover-e2e.test.ts +++ b/tests/server/server-key-failover-e2e.test.ts @@ -2,7 +2,7 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test"; import { mkdtempSync} from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { saveConfig } from "../../src/config"; +import { loadConfig, saveConfig } from "../../src/config"; import { clearKeyCooldowns } from "../../src/providers/key-failover"; import { deriveXaiConvId } from "../../src/providers/xai-transport"; import { clearReasoningReplayCacheForTests } from "../../src/responses/reasoning-replay-cache"; @@ -38,6 +38,42 @@ afterEach(() => { }); describe("server 429 key failover (end-to-end)", () => { + test.each(["responses", "chat/completions"])("%s carries the configured env-key identity through 429 recovery", async inbound => { + const seen: string[] = []; + process.env.OCX_SELECTION_E2E_KEY = "synthetic-env-first"; + upstream = Bun.serve({ hostname: "127.0.0.1", port: 0, fetch(req) { + seen.push(req.headers.get("authorization") ?? ""); + if (seen.length === 1) return Response.json({ error: { message: "rate limited" } }, { status: 429 }); + return Response.json({ id: "chatcmpl-env", object: "chat.completion", + choices: [{ index: 0, message: { role: "assistant", content: "recovered" }, finish_reason: "stop" }], + usage: { prompt_tokens: 1, completion_tokens: 1 }, + }); + } }); + saveConfig({ port: 0, hostname: "127.0.0.1", defaultProvider: "pooled", providers: { pooled: { + adapter: "openai-chat", baseUrl: `http://127.0.0.1:${upstream.port}/v1`, allowPrivateNetwork: true, + authMode: "key", apiKey: "${OCX_SELECTION_E2E_KEY}", apiKeyPool: [ + { id: "first", key: "${OCX_SELECTION_E2E_KEY}" }, { id: "second", key: "synthetic-second" }, + ], + } } } as OcxConfig); + const server = startServer(0); + try { + const response = await fetch(new URL(`/v1/${inbound}`, server.url), { + method: "POST", headers: { "content-type": "application/json" }, + body: JSON.stringify({ model: "pooled/test", stream: false, + ...(inbound === "responses" ? { input: "hello" } : { messages: [{ role: "user", content: "hello" }] }), + }), + }); + expect(response.status).toBe(200); + expect(await response.text()).toContain("recovered"); + expect(seen).toEqual(["Bearer synthetic-env-first", "Bearer synthetic-second"]); + expect(loadConfig().providers.pooled.apiKey).toBe("synthetic-second"); + expect(loadConfig().providers.pooled._apiKeyAttempt).toBeUndefined(); + } finally { + await server.stop(true); + delete process.env.OCX_SELECTION_E2E_KEY; + } + }); + test("xAI API-key rotation preserves cache affinity and never adds OAuth CLI headers", async () => { const originalFetch = globalThis.fetch; const promptCacheKey = "codex-session-high-entropy-429-e2e"; From 46332a656cccdb5ec5ecd9e6a993e4255aea69bb Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Sun, 6 Sep 2026 20:55:22 +0900 Subject: [PATCH 2/4] fix: honor manual selection during OAuth credential recovery --- .../000_plan.md | 2 ++ .../010_implementation.md | 2 ++ .../evidence/011_selection-before.png | Bin 0 -> 101062 bytes .../evidence/012_selection-after.png | Bin 0 -> 100958 bytes src/server/management/route-registry.ts | 4 +++- src/server/responses/core.ts | 15 +++++++++++++-- tests/cli/cli-headless-parity.test.ts | 1 + .../github-copilot-account-origin.test.ts | 10 +++++----- 8 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 devlog/_plan/260906_manual_account_selection/evidence/011_selection-before.png create mode 100644 devlog/_plan/260906_manual_account_selection/evidence/012_selection-after.png diff --git a/devlog/_plan/260906_manual_account_selection/000_plan.md b/devlog/_plan/260906_manual_account_selection/000_plan.md index f46765a744..3ee8ea01cd 100644 --- a/devlog/_plan/260906_manual_account_selection/000_plan.md +++ b/devlog/_plan/260906_manual_account_selection/000_plan.md @@ -29,3 +29,5 @@ Latest steering: the user reports Codex works correctly. Preserve Codex routing/ Latest explicit design instruction: GUI selection and pool selection must share one selection owner, as Codex does. Both must commit the same authoritative selection BEFORE dispatch; requests use the committed account. Do not bolt on a separate UI-only mirror or route around manual selection while pretending the old active account remains selected. A concurrent newer user selection wins. This strengthens the existing planned store-owned selection transaction and applies with pool on or off. Latest correction: 429 automatic account switching is ALWAYS allowed, including poolOFF. Withdraw the planned reactive disable gate. Manual selection wins ordinary dispatch; a real429 may replace it using the same guarded selection owner, and the GUI follows that committed replacement. + +Latest explicit verification restriction: do not run repository-wide tests. The earlier full-suite requirement is superseded. One attempted full run was interrupted by user at exit130; it is not completion proof. Resolve observed failures with their specific test files, run focused affected checks and typecheck, then push --no-verify and merge the single PR. diff --git a/devlog/_plan/260906_manual_account_selection/010_implementation.md b/devlog/_plan/260906_manual_account_selection/010_implementation.md index e31bc2643a..79a6b282aa 100644 --- a/devlog/_plan/260906_manual_account_selection/010_implementation.md +++ b/devlog/_plan/260906_manual_account_selection/010_implementation.md @@ -61,3 +61,5 @@ Independent reviewer verdict: GO-WITH-FIXES(blockers=5). All five are folded int 5. Quota eligibility explicitly distinguishes known exhaustion from unknown, including Kiro overage rules. Parameterized provider coverage includes Kiro and passive providers. B lane allocation (approved plan): main owns core.ts, OAuth management route/SSE route registration, generic selector/rank and integration proof/docs; store lane owns oauth/types.ts+store.ts, leaf account-selection event bus, and oauth-store-multi.test.ts; backend lane owns Anthropic routing+tests and API-key source/router/transport+tests including types/provider.ts; frontend lane owns the3GUI sourcefiles and2testfiles above. No worker changes maincore or another lane's files. Independent context review follows integration. + +Latest explicit verification restriction: do not run repository-wide tests. The earlier full-suite requirement is superseded. One attempted full run was interrupted by user at exit130; it is not completion proof. Resolve observed failures with their specific test files, run focused affected checks and typecheck, then push --no-verify and merge the single PR. diff --git a/devlog/_plan/260906_manual_account_selection/evidence/011_selection-before.png b/devlog/_plan/260906_manual_account_selection/evidence/011_selection-before.png new file mode 100644 index 0000000000000000000000000000000000000000..ef881a8fc524edffec77f662de804e9e8a3f9100 GIT binary patch literal 101062 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\gkmG*~DzNgAJB$S;?ty1aAyhRYQ6p zY{bQmE8Vz@lK?eqS0m>wmE?a^VGXJ7eLyLTv^W9W5K!^J_Seh6#f_{tST*n!H#UP& z0)2+gw96t(PU?)2ru3>Pf>%=Cx=8QnfS&wc)HJa6rBZ6&=wsA7!W0;_@@x^yvEjQ_lDSXHzm<#x8^;JOq(){b1VFRKS_pvAVFf~Zy-8{Y@4 zmwJl=Eo}LUztO#60lVZ|amAsUz~N=Zv5aP6VG8G=XWR$Nyfwfp>2J(d;{*ugK=#6Q zNttG8937@zhvcZ*L3q5&fbe?$D)T=21Lc#gIjfWruUrFSNwc=2H2VbH_- zeroo>u$EL_!ceY6#UDcRp^O=+TRHXPwStAC?2kqm+GDFEEn?)WSCWzb2F+ z_-~!L8`n~a<4TWadxIDESVDWf9_ndO)y34oMffc*!`2@k6WtrPXjq4_3vWPD}v1Ta_s0EJ%lVJQb~1!+Wz3Eh7E`C94n zBrx#B{n+foXQFE6Hau-3&Y}h&j_kG!51dp&6Wdb%{)ngEDCHT8Z7;O?b>z0~Bgan~ zqPlxNJsC=WKJ$-PYw?J?$$0%IUN|O0K2Uc|S^EwV)A{#=Md0ZWkiyen4ue5Cvy`Qv^X zE9|Pj@#@%cc^Ob$80sljU4V!eMh~_*ofXffPZ^$*NY3-~h~JmVAG}kNVb;}4LUPv8 Vht~wANWWZCf2yrg`s79U{{d~_5VHUP literal 0 HcmV?d00001 diff --git a/devlog/_plan/260906_manual_account_selection/evidence/012_selection-after.png b/devlog/_plan/260906_manual_account_selection/evidence/012_selection-after.png new file mode 100644 index 0000000000000000000000000000000000000000..e67e0676e6771213d81f052025bf4788c3d5fbc8 GIT binary patch literal 100958 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 => { + const current = captureOAuthAccountSelection(route.providerName); + const unchanged = current?.accountId === oauthSelection?.accountId + && current?.revision === oauthSelection?.revision; + const candidate = unchanged ? await forceRefreshOAuthAccessSnapshot(sent) : sent; + const admitted = await commitResolvedOAuthSelection(candidate); + if (!admitted) throw new Error("OAuth selection changed during credential recovery"); + genericFailoverAccountId = admitted.accountId; + stampOAuthAccountLabel(logCtx, route.providerName, route.provider, admitted.accountId); + return admitted; + }; /** * Config generation captured where the serving credential is RESOLVED, not where the * quota is written. A streaming turn is a long await, so a generation captured at write @@ -4804,7 +4815,7 @@ async function handleResponsesInner( try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already consumed/closed */ } let refreshed: OAuthAccessSnapshot; try { - refreshed = await forceRefreshOAuthAccessSnapshot(sentOAuthSnapshot); + refreshed = await refreshResolvedOAuthSelection(sentOAuthSnapshot); } catch (err) { upstream.abort(); releaseCodexAuthContextProbeLease(authCtx); @@ -6671,7 +6682,7 @@ async function handleResponsesInner( try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already consumed/closed */ } let refreshed: OAuthAccessSnapshot; try { - refreshed = await forceRefreshOAuthAccessSnapshot(sentOAuthSnapshot); + refreshed = await refreshResolvedOAuthSelection(sentOAuthSnapshot); } catch (err) { cleanupUpstreamAbort(); return formatErrorResponse(401, "authentication_error", publicOAuthAuthenticationErrorMessage(err)); diff --git a/tests/cli/cli-headless-parity.test.ts b/tests/cli/cli-headless-parity.test.ts index 44302ea884..dab85a132c 100644 --- a/tests/cli/cli-headless-parity.test.ts +++ b/tests/cli/cli-headless-parity.test.ts @@ -232,6 +232,7 @@ describe("headless GUI parity CLI", () => { // skipping the endpoint. ["/api/github/star", "(none — GUI-only)"], ["/api/oauth", "ocx account"], + ["/api/accounts/events", "(none — dashboard invalidation; ocx account reads current selection)"], ["/api/providers/keys", "ocx account"], ["/api/providers", "ocx provider"], ["/api/provider-", "ocx provider/models"], diff --git a/tests/providers/github-copilot/github-copilot-account-origin.test.ts b/tests/providers/github-copilot/github-copilot-account-origin.test.ts index 5f464a1d7e..c820827991 100644 --- a/tests/providers/github-copilot/github-copilot-account-origin.test.ts +++ b/tests/providers/github-copilot/github-copilot-account-origin.test.ts @@ -161,7 +161,7 @@ afterEach(() => { describe("GitHub Copilot bearer/origin snapshot atomicity", () => { for (const wire of ["chat", "responses"] as const) { - test(`${wire} initial refresh keeps account A's origin after B becomes active`, async () => { + test(`${wire} initial admission follows a newer manual selection with its matching origin`, async () => { const accounts = await seedAccounts(0); const observed = installFetch({ wire, @@ -175,12 +175,12 @@ describe("GitHub Copilot bearer/origin snapshot atomicity", () => { expect(response.status).toBe(200); expect(observed.dispatches).toEqual([{ - origin: ACCOUNT_A_ORIGIN, - authorization: bearer("copilot-access-a-refreshed"), + origin: ACCOUNT_B_ORIGIN, + authorization: bearer("copilot-access-b"), }]); }); - test(`${wire} 401 replay keeps refreshed account A's origin after B becomes active`, async () => { + test(`${wire} 401 replay follows a newer manual selection with its matching origin`, async () => { const accounts = await seedAccounts(); const observed = installFetch({ wire, @@ -195,7 +195,7 @@ describe("GitHub Copilot bearer/origin snapshot atomicity", () => { expect(response.status).toBe(200); expect(observed.dispatches).toEqual([ { origin: ACCOUNT_A_ORIGIN, authorization: bearer("copilot-access-a") }, - { origin: ACCOUNT_A_ORIGIN, authorization: bearer("copilot-access-a-refreshed") }, + { origin: ACCOUNT_B_ORIGIN, authorization: bearer("copilot-access-b") }, ]); }); } From dd5aec57106e2186714fda94cf6c8004eb9231a2 Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Sun, 6 Sep 2026 21:14:06 +0900 Subject: [PATCH 3/4] fix: revalidate queued selection and repair live account updates --- .../010_implementation.md | 4 + gui/src/hooks/useProviderAccountPools.ts | 63 +++++---- gui/src/pages/Providers.tsx | 28 +++- .../provider-account-quota-loading.test.tsx | 87 ++++++++++++ .../provider-revalidation-policy.test.tsx | 61 ++++++++- src/client/hub-relay.ts | 44 +++++- src/oauth/anthropic-routing.ts | 16 ++- src/server/management-auth.ts | 16 +++ .../management/account-selection-stream.ts | 15 ++- src/server/management/oauth-account-routes.ts | 2 +- src/server/responses/core.ts | 123 ++++++++++++++--- src/server/responses/fetch-helpers.ts | 7 +- .../anthropic/anthropic-account-pool.test.ts | 24 ++++ tests/clients/client-hub-relay.test.ts | 127 +++++++++++++++++- tests/gui/provider-workspace-auth.test.ts | 9 +- .../adapter-event-oauth-failover.test.ts | 29 +++- tests/oauth/generic-oauth-failover.test.ts | 3 +- tests/oauth/oauth-accounts-api.test.ts | 116 +++++++++++++++- .../oauth-upsert-preserves-api-key.test.ts | 12 ++ .../github-copilot-account-origin.test.ts | 53 +++++++- ...oogle-antigravity-oauth-401-replay.test.ts | 34 ++--- 21 files changed, 779 insertions(+), 94 deletions(-) diff --git a/devlog/_plan/260906_manual_account_selection/010_implementation.md b/devlog/_plan/260906_manual_account_selection/010_implementation.md index 79a6b282aa..324258ba50 100644 --- a/devlog/_plan/260906_manual_account_selection/010_implementation.md +++ b/devlog/_plan/260906_manual_account_selection/010_implementation.md @@ -63,3 +63,7 @@ Independent reviewer verdict: GO-WITH-FIXES(blockers=5). All five are folded int B lane allocation (approved plan): main owns core.ts, OAuth management route/SSE route registration, generic selector/rank and integration proof/docs; store lane owns oauth/types.ts+store.ts, leaf account-selection event bus, and oauth-store-multi.test.ts; backend lane owns Anthropic routing+tests and API-key source/router/transport+tests including types/provider.ts; frontend lane owns the3GUI sourcefiles and2testfiles above. No worker changes maincore or another lane's files. Independent context review follows integration. Latest explicit verification restriction: do not run repository-wide tests. The earlier full-suite requirement is superseded. One attempted full run was interrupted by user at exit130; it is not completion proof. Resolve observed failures with their specific test files, run focused affected checks and typecheck, then push --no-verify and merge the single PR. + +## C corrective review amendment + +Accepted independent review findings: dispatch must revalidate after pacing/build waits;401 replay must use the common selection owner; CCA project must always come from the admitted account; Anthropic initial manual choice must survive restart; selection SSE must stop on session revocation/expiry; hub relay must not apply its15s total deadline to an established selection stream; late initial quota data must survive manual selection without restoring old active flags. Main owns physical dispatch,401,CCA; backend lane owns Anthropic/API-key corrections; frontend lane owns reconnect/quota fixes. For bounded parallel C repair, the completed store worker is reassigned to SSE/management session liveness and hub-relay fixes only; no concurrent write ownership overlaps. All verification remains focused; full suite is prohibited. Draft PR3768 is open and CI runs asynchronously. diff --git a/gui/src/hooks/useProviderAccountPools.ts b/gui/src/hooks/useProviderAccountPools.ts index e66c464481..0197a547d5 100644 --- a/gui/src/hooks/useProviderAccountPools.ts +++ b/gui/src/hooks/useProviderAccountPools.ts @@ -45,7 +45,7 @@ function mergeLateQuotaRows(rows: T[], enriched: T[]): T[] { const byId = new Map(enriched.map(row => [row.id, row])); return rows.map(row => { const incoming = byId.get(row.id); - if (!incoming) return row; + if (!incoming || incoming.quotaMode !== row.quotaMode) return row; const quota = mergeQuotaRows([incoming], [row], true)[0]; return { ...row, quota: quota.quota, quotaPending: quota.quotaPending, quotaUnavailable: quota.quotaUnavailable }; }); @@ -73,8 +73,9 @@ function mergeQuotaRows(rows: T[], previous: T[], enriched: }); } -function unavailableQuotaRows(rows: T[]): T[] { - return rows.map(row => supportsQuotaRead(row) +function unavailableQuotaRows(rows: T[], attempted?: T[]): T[] { + const attemptedModes = attempted && new Map(attempted.map(row => [row.id, row.quotaMode])); + return rows.map(row => supportsQuotaRead(row) && (!attemptedModes || attemptedModes.get(row.id) === row.quotaMode) ? { ...row, quotaUnavailable: true, quotaPending: false } : row); } @@ -118,6 +119,7 @@ export function useProviderAccountPools(deps: { const [newKeyValue, setNewKeyValue] = useState(""); const accountRequestGenerationRef = useRef>({}); const rosterGenerationRef = useRef>({}); + const quotaGenerationRef = useRef>({}); const selectionMutationsRef = useRef(new Map()); const requestsRef = useRef(new Set()); const mountedRef = useRef(true); @@ -126,6 +128,7 @@ export function useProviderAccountPools(deps: { const generations = accountRequestGenerationRef.current; const requests = requestsRef.current; const rosterGenerations = rosterGenerationRef.current; + const quotaGenerations = quotaGenerationRef.current; const mutations = selectionMutationsRef.current; mountedRef.current = true; const serverChanged = serverRef.current !== apiBase; @@ -140,6 +143,7 @@ export function useProviderAccountPools(deps: { mountedRef.current = false; for (const key of Object.keys(generations)) generations[key] += 1; for (const key of Object.keys(rosterGenerations)) rosterGenerations[key] += 1; + for (const key of Object.keys(quotaGenerations)) quotaGenerations[key] += 1; mutations.clear(); for (const controller of requests) controller.abort(); requests.clear(); @@ -202,12 +206,17 @@ export function useProviderAccountPools(deps: { if (!rows.some(supportsQuotaRead)) return true; const enrich = async (): Promise => { + // Manual selection invalidates roster reads, not a per-ID quota probe already sent. + const quotaGeneration = (quotaGenerationRef.current[key] ?? 0) + 1; + quotaGenerationRef.current[key] = quotaGeneration; + const currentQuota = () => aliveRef.current && mountedRef.current && serverRef.current === apiBase + && quotaGenerationRef.current[key] === quotaGeneration; try { const quotaData = await readRoster<{ activeAccountId?: string | null; accounts?: OAuthAccount[] }>(`${url}"a=1${refresh ? "&refresh=1" : ""}`); if (!Array.isArray(quotaData.accounts)) throw new Error("Invalid account quota roster"); - if (!currentRequest()) return false; + if (!currentQuota()) return false; const enriched = selectionRows(quotaData.accounts, quotaData.activeAccountId); - setAccountSets(current => !currentRequest() ? current : !currentRoster() ? { + setAccountSets(current => !currentQuota() ? current : !currentRoster() ? { ...current, [provider]: { ...current[provider], accounts: mergeLateQuotaRows(current[provider]?.accounts ?? [], enriched) }, } : { ...current, @@ -218,9 +227,9 @@ export function useProviderAccountPools(deps: { }); return !enriched.some(row => row.quotaUnavailable === true); } catch { - if (!currentRequest()) return false; - setAccountSets(current => currentRequest() && current[provider] ? { - ...current, [provider]: { ...current[provider], accounts: unavailableQuotaRows(current[provider].accounts) }, + if (!currentQuota()) return false; + setAccountSets(current => currentQuota() && current[provider] ? { + ...current, [provider]: { ...current[provider], accounts: unavailableQuotaRows(current[provider].accounts, rows) }, } : current); return false; } @@ -251,9 +260,8 @@ export function useProviderAccountPools(deps: { const currentRequest = () => aliveRef.current && mountedRef.current && serverRef.current === apiBase && accountRequestGenerationRef.current[key] === generation; const currentRoster = () => currentRequest() && rosterGenerationRef.current[key] === rosterGeneration; const url = `${apiBase}/api/providers/keys?name=${encodeURIComponent(name)}`; - const failed = (enrichment = false) => { - const accepted = () => currentRequest() && (enrichment || currentRoster()); - if (accepted()) setKeyPools(current => accepted() + const failed = () => { + if (currentRoster()) setKeyPools(current => currentRoster() ? { ...current, [name]: unavailableQuotaRows(current[name] ?? []) } : current); return false; }; @@ -265,16 +273,24 @@ export function useProviderAccountPools(deps: { setKeyPools(current => currentRoster() ? { ...current, [name]: mergeQuotaRows(rows, current[name] ?? [], false) } : current); if (!rows.some(supportsQuotaRead)) return true; const enrich = async (): Promise => { + const quotaGeneration = (quotaGenerationRef.current[key] ?? 0) + 1; + quotaGenerationRef.current[key] = quotaGeneration; + const currentQuota = () => aliveRef.current && mountedRef.current && serverRef.current === apiBase + && quotaGenerationRef.current[key] === quotaGeneration; try { const data = await readRoster<{ activeId?: string | null; keys?: ApiKeyEntry[] }>(`${url}"a=1${refresh ? "&refresh=1" : ""}`); if (!Array.isArray(data.keys)) throw new Error("Invalid key quota roster"); - if (!currentRequest()) return false; + if (!currentQuota()) return false; const enriched = selectionRows(data.keys, data.activeId); - setKeyPools(current => currentRequest() ? { ...current, [name]: currentRoster() + setKeyPools(current => currentQuota() ? { ...current, [name]: currentRoster() ? mergeQuotaRows(enriched, current[name] ?? [], true) : mergeLateQuotaRows(current[name] ?? [], enriched) } : current); return !enriched.some(row => row.quotaUnavailable === true); - } catch { return failed(true); } + } catch { + if (currentQuota()) setKeyPools(current => currentQuota() + ? { ...current, [name]: unavailableQuotaRows(current[name] ?? [], rows) } : current); + return false; + } }; if (refresh) return await enrich(); void enrich(); @@ -328,12 +344,7 @@ export function useProviderAccountPools(deps: { const key = `${kind === "oauth" ? "oauth" : "key"}:${provider}`; accountRequestGenerationRef.current[key] = (accountRequestGenerationRef.current[key] ?? 0) + 1; rosterGenerationRef.current[key] = (rosterGenerationRef.current[key] ?? 0) + 1; - // An invalidated probe will never settle its old flags. - const settle = (rows: T[]) => rows.map(row => row.quotaPending ? { ...row, quotaPending: false } : row); - if (kind === "oauth") setAccountSets(current => current[provider] ? { - ...current, [provider]: { ...current[provider], accounts: settle(current[provider].accounts) }, - } : current); - else setKeyPools(current => current[provider] ? { ...current, [provider]: settle(current[provider]) } : current); + // The independent quota generation still owns pending/error flags for surviving IDs. return key; }; @@ -349,10 +360,11 @@ export function useProviderAccountPools(deps: { const label = oauthAccountDisplayLabel(accountSets[provider]?.accounts ?? [account], account, t); try { const res = await fetch(`${apiBase}/api/oauth/accounts/active`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ provider, accountId: account.id }) }); + if (!currentMutation()) return; + if (!res.ok) { notify(t("prov.accountSwitchFail"), false); return; } const result = await res.json().catch(() => ({})) as { activeAccountId?: string | null }; if (!currentMutation()) return; invalidateSelectionReads(provider, "oauth"); - if (!res.ok) { notify(t("prov.accountSwitchFail"), false); return; } const selected = result.activeAccountId === undefined ? account.id : result.activeAccountId; setAccountSets(current => current[provider] ? { ...current, [provider]: { ...current[provider], activeAccountId: selected, accounts: selectionRows(current[provider].accounts, selected), @@ -385,10 +397,15 @@ export function useProviderAccountPools(deps: { const currentMutation = () => aliveRef.current && mountedRef.current && serverRef.current === apiBase && selectionMutationsRef.current.get(key) === mutation; try { const res = await fetch(`${apiBase}/api/providers/keys/active`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: provider, id: entry.id }) }); - const data = await res.json().catch(() => ({})) as { activeId?: string | null; error?: string }; + if (!currentMutation()) return; + if (!res.ok) { + const failure = await res.json().catch(() => ({})) as { error?: string }; + if (currentMutation()) notify(failure.error || t("prov.keySwitchFail"), false); + return; + } + const data = await res.json().catch(() => ({})) as { activeId?: string | null }; if (!currentMutation()) return; invalidateSelectionReads(provider, "api-key"); - if (!res.ok) { notify(data.error || t("prov.keySwitchFail"), false); return; } const selected = data.activeId === undefined ? entry.id : data.activeId; setKeyPools(current => current[provider] ? { ...current, [provider]: selectionRows(current[provider], selected) } : current); notify(t("prov.keySwitched", { key: entry.label ?? entry.masked }), true); diff --git a/gui/src/pages/Providers.tsx b/gui/src/pages/Providers.tsx index b760a822e9..78b2096d95 100644 --- a/gui/src/pages/Providers.tsx +++ b/gui/src/pages/Providers.tsx @@ -75,7 +75,7 @@ export function useQuotaRefreshCoordinator(apiBase: string) { return { quotaRefresh, invalidateProviderQuotas, settleQuotaRefresh, beginQuotaRefresh }; } -/** One authenticated SSE connection for the page. The existing scheduler owns recovery. */ +/** One authenticated SSE connection, with bounded reconnect backoff and scheduler recovery. */ function useAccountSelectionEvents( apiBase: string, enabled: boolean, @@ -87,14 +87,21 @@ function useAccountSelectionEvents( useEffect(() => { if (!enabled) return; let stopped = false; - type Connection = { controller: AbortController; reader?: ReadableStreamDefaultReader; lastActivity: number }; + let retryTimer: ReturnType | null = null; + let retryDelay = 250; + type Connection = { controller: AbortController; reader?: ReadableStreamDefaultReader; lastActivity: number; openedAt?: number }; let connection: Connection | null = null; + const clearRetry = () => { + if (retryTimer !== null) window.clearTimeout(retryTimer); + retryTimer = null; + }; const close = (current: Connection) => { current.controller.abort(); void current.reader?.cancel().catch(() => {}); }; const connect = () => { if (stopped || connection) return; + clearRetry(); const current: Connection = { controller: new AbortController(), lastActivity: Date.now() }; connection = current; void (async () => { @@ -109,6 +116,7 @@ function useAccountSelectionEvents( if (stopped || current.controller.signal.aborted) { await response.body.cancel(); return; } const reader = response.body.getReader(); current.reader = reader; + current.openedAt = Date.now(); const decoder = new TextDecoder(); const revisions = new Map(); const pending = new Map(); @@ -165,10 +173,19 @@ function useAccountSelectionEvents( if (buffer.length + frameSize > 16_384) throw new Error("Account selection event too large"); } } catch { - // Authentication recovery is owned by api.ts; disconnected streams use the shared tick. + // api.ts owns authentication. Transport failures follow the same retry path as EOF. } finally { close(current); - if (connection === current) connection = null; + if (connection === current) { + connection = null; + if (!stopped) { + // Only a stable connection resets backoff; repeated ready-then-EOF cannot spin. + if (current.openedAt !== undefined && Date.now() - current.openedAt >= 10_000) retryDelay = 250; + const delay = retryDelay; + retryDelay = Math.min(retryDelay * 2, 5_000); + retryTimer = window.setTimeout(() => { retryTimer = null; connect(); }, delay); + } + } } })(); }; @@ -182,6 +199,7 @@ function useAccountSelectionEvents( return () => { stopped = true; recoverRef.current = () => {}; + clearRetry(); if (connection) close(connection); }; }, [apiBase, enabled]); @@ -365,7 +383,7 @@ export default function Providers({ apiBase }: { apiBase: string }) { return refreshAccountRosters(target); }, [refreshAccountRosters, oauthCardProviders, keyCardProviders]); const recoverSelectionStream = useAccountSelectionEvents(apiBase, config !== null, refreshSelection); - const rosterKey = JSON.stringify([apiBase, [...oauthCardProviders].sort(), [...keyCardProviders].sort()]); + const rosterKey = JSON.stringify([apiBase, oauthCardProviders.toSorted(), keyCardProviders.toSorted()]); const rosterRecoveryKeyRef = useRef(null); useKeyedClientResource(`provider-rosters:${rosterKey}`, [rosterKey], async signal => { // Existing bootstrap effects own the first enriched reads. This resource is recovery only. diff --git a/gui/tests/provider-account-quota-loading.test.tsx b/gui/tests/provider-account-quota-loading.test.tsx index d5df0ee4a8..bfd811ae7f 100644 --- a/gui/tests/provider-account-quota-loading.test.tsx +++ b/gui/tests/provider-account-quota-loading.test.tsx @@ -15,6 +15,93 @@ let respond: (url: string, signal?: AbortSignal | null, init?: RequestInit) => P const noop = async () => {}; const reading = { fiveHourPercent: 21, weeklyPercent: 34, updatedAt: 1_700_000_000_000 }; +for (const kind of ["oauth", "api-key"] as const) { + for (const quotaMode of ["probe", "passive"] as const) { + test(`${kind} ${quotaMode} newer quota failure cannot be cleared by a pre-selection probe`, async () => { + let selected = "a"; + const oldQuota = deferred(); + const oldStarted = deferred(); + let probes = 0; + const rows = () => ["a", "b"].map(id => ({ id, masked: id, active: id === selected, quotaMode })); + respond = async (url, _signal, init) => { + if (init?.method === "PUT") { selected = "b"; return Response.json({ ok: true, activeAccountId: "b", activeId: "b" }); } + if (url.includes("quota=1")) { + if (++probes === 1) { oldStarted.resolve(); return oldQuota.promise; } + return new Response(null, { status: 503 }); + } + return Response.json({ activeAccountId: selected, activeId: selected, accounts: rows(), keys: rows() }); + }; + const load = () => kind === "oauth" ? pools.fetchAccountSets(["fixture"], true) : pools.fetchKeyPools(["fixture"], true); + let old!: Promise; + await act(async () => { old = load(); await oldStarted.promise; }); + const before = rows(); + await act(async () => { + if (kind === "oauth") await pools.switchAccount("fixture", before[1]); + else await pools.switchApiKey("fixture", before[1]); + }); + await act(async () => { expect(await load()).toBe(false); }); + await act(async () => { + const enriched = before.map(row => ({ ...row, quota: reading })); + oldQuota.resolve(Response.json({ activeAccountId: "a", activeId: "a", accounts: enriched, keys: enriched })); + expect(await old).toBe(false); + }); + const current = kind === "oauth" ? pools.accountSets.fixture.accounts : pools.keyPools.fixture; + expect(current.find(row => row.active)?.id).toBe("b"); + expect(current[0]).toMatchObject({ quotaPending: false, quotaUnavailable: true }); + expect(current[0].quota).toBeUndefined(); + }); + + for (const outcome of ["success", "unavailable", "null", "http-error"] as const) { + test(`${kind} ${quotaMode} manual selection preserves initial quota ownership (${outcome})`, async () => { + let selected = "a"; + let ids = ["a", "b", "removed"]; + const quota = deferred(); + const started = deferred(); + const roster = () => ids.map(id => ({ id, masked: id, active: id === selected, quotaMode })); + respond = async (url, _signal, init) => { + if (init?.method === "PUT") { + selected = "b"; + ids = ["a", "b", "new"]; + return Response.json({ ok: true, activeAccountId: selected, activeId: selected }); + } + if (url.includes("quota=1")) { started.resolve(); return quota.promise; } + return Response.json({ activeAccountId: selected, activeId: selected, accounts: roster(), keys: roster() }); + }; + let full!: Promise; + await act(async () => { + full = kind === "oauth" ? pools.fetchAccountSets(["fixture"], true) : pools.fetchKeyPools(["fixture"], true); + await started.promise; + }); + const before = roster(); + const current = () => kind === "oauth" ? pools.accountSets.fixture.accounts : pools.keyPools.fixture; + expect(current()[0].quotaPending).toBe(quotaMode === "probe"); + await act(async () => { + if (kind === "oauth") await pools.switchAccount("fixture", before[1]); + else await pools.switchApiKey("fixture", before[1]); + }); + expect(current().find(row => row.active)?.id).toBe("b"); + expect(current()[0].quotaPending).toBe(quotaMode === "probe"); + const enriched = before.map(row => ({ ...row, + quota: outcome === "null" ? null : reading, + quotaUnavailable: outcome === "unavailable" || outcome === "null", + })); + await act(async () => { + quota.resolve(outcome === "http-error" ? new Response(null, { status: 503 }) + : Response.json({ activeAccountId: "a", activeId: "a", accounts: enriched, keys: enriched })); + expect(await full).toBe(outcome === "success"); + }); + expect(current().map(row => row.id)).toEqual(["a", "b", "new"]); + expect(current().find(row => row.active)?.id).toBe("b"); + expect(current()[0]).toMatchObject({ quotaPending: false, quotaUnavailable: outcome !== "success" }); + expect(current()[0].quota).toEqual(outcome === "null" ? null : outcome === "http-error" ? undefined : reading); + expect(current()[2].quota).toBeUndefined(); + expect(current()[2].quotaUnavailable).toBe(false); + expect(requests.filter(request => request.url.includes("quota=1"))).toHaveLength(1); + }); + } + } +} + function deferred() { let resolve!: (value: T) => void; const promise = new Promise(done => { resolve = done; }); diff --git a/gui/tests/provider-revalidation-policy.test.tsx b/gui/tests/provider-revalidation-policy.test.tsx index d29860929f..e6be1936e1 100644 --- a/gui/tests/provider-revalidation-policy.test.tsx +++ b/gui/tests/provider-revalidation-policy.test.tsx @@ -231,6 +231,7 @@ for (const kind of ["oauth", "api-key"] as const) { let stream!: ReadableStreamDefaultController; let streamSignal: AbortSignal | null | undefined; let subscriptions = 0; + let rejectConnection = false; let cancelled = false; const reads: string[] = []; const encoder = new TextEncoder(); @@ -246,6 +247,7 @@ for (const kind of ["oauth", "api-key"] as const) { if (url.pathname === "/api/accounts/events") { subscriptions += 1; streamSignal = init?.signal; + if (rejectConnection) return new Response(null, { status: 503 }); return new Response(new ReadableStream({ start(controller) { stream = controller; }, cancel() { cancelled = true; }, }), { headers: { "Content-Type": "text/event-stream" } }); @@ -296,19 +298,68 @@ for (const kind of ["oauth", "api-key"] as const) { send("account-selection", { provider: "unknown-provider", kind, revision: 2 }); }); expect(reads).toHaveLength(afterEvent); - // A disconnected connection is recovered by the existing scheduler's visibility wake. + // Only the reconnect timeout advances. No 30-second scheduler tick or visibility wake. + const retries = new Map void; delay: number }>(); + let timerId = 10_000; + const setTimeoutBefore = testWindow.setTimeout.bind(testWindow); + const clearTimeoutBefore = testWindow.clearTimeout.bind(testWindow); + Object.defineProperty(testWindow, "setTimeout", { configurable: true, value: (run: () => void, delay: number) => { + if (delay > 5_000) return setTimeoutBefore(run, delay); + const id = ++timerId; + retries.set(id, { run, delay }); + return id; + } }); + Object.defineProperty(testWindow, "clearTimeout", { configurable: true, value: (id: number) => { + if (!retries.delete(id)) clearTimeoutBefore(id); + } }); + const retry = async (delay: number) => { + expect(retries.size).toBe(1); + const [id, pending] = [...retries][0]; + expect(pending.delay).toBe(delay); + retries.delete(id); + await act(async () => { pending.run(); }); + }; await act(async () => { stream.close(); }); selected = "a"; - for (const state of ["hidden", "visible"]) { - Object.defineProperty(testWindow.document, "visibilityState", { configurable: true, value: state }); - await act(async () => { testWindow.document.dispatchEvent(new testWindow.Event("visibilitychange")); }); - } + await retry(250); expect(subscriptions).toBe(2); await act(async () => { send("ready", { revision: 0 }); }); expect(container.querySelector(".pwi-auth-acct--active")?.textContent).toContain("Choice A"); expect(quotaReads()).toBe(initialQuotas); + + await act(async () => { stream.error(new Error("connection interrupted")); }); + rejectConnection = true; + await retry(500); + expect(subscriptions).toBe(3); + for (const delay of [1_000, 2_000, 4_000, 5_000]) await retry(delay); + expect(subscriptions).toBe(7); + rejectConnection = false; + await retry(5_000); + expect(subscriptions).toBe(8); + selected = "b"; + await act(async () => { send("ready", { revision: 0 }); }); + expect(container.querySelector(".pwi-auth-acct--active")?.textContent).toContain("Choice B"); + expect(quotaReads()).toBe(initialQuotas); + + // Cleanup cancels scheduled retries, and a callback already dequeued cannot orphan a loop. + await act(async () => { stream.close(); }); + expect(retries.size).toBe(1); + const queuedRetry = [...retries.values()][0].run; + await act(async () => { root!.unmount(); root = null; }); + expect(retries.size).toBe(0); + await act(async () => { queuedRetry(); }); + expect(subscriptions).toBe(8); + expect(streamSignal?.aborted).toBe(true); + // Closed/error streams need no cancel callback; also prove cleanup of a live reader. + await act(async () => { + root = createRoot(container); + root.render(); + }); + expect(subscriptions).toBe(9); + expect(streamSignal?.aborted).toBe(false); await act(async () => { root!.unmount(); root = null; }); expect(streamSignal?.aborted).toBe(true); expect(cancelled).toBe(true); + expect(retries.size).toBe(0); }); } diff --git a/src/client/hub-relay.ts b/src/client/hub-relay.ts index 820ffd3845..95ab7b0c0d 100644 --- a/src/client/hub-relay.ts +++ b/src/client/hub-relay.ts @@ -164,6 +164,7 @@ function boundedRelayResponseStream( body: ReadableStream, limit: number, signal: AbortSignal, + cleanup: () => void, ): ReadableStream { const reader = body.getReader(); let bytes = 0; @@ -172,6 +173,7 @@ function boundedRelayResponseStream( if (finished) return; finished = true; signal.removeEventListener("abort", onAbort); + cleanup(); try { reader.releaseLock(); } catch { /* a pending read may still own it */ } }; const onAbort = () => { @@ -245,9 +247,19 @@ export async function relayHubManagementRequest( ? Math.min(Math.floor(deps.timeoutMs), 120_000) : HUB_RELAY_DEFAULT_TIMEOUT_MS; const timeoutSignal = AbortSignal.timeout(timeoutMs); - const signal = req.signal - ? AbortSignal.any([req.signal, timeoutSignal]) - : timeoutSignal; + const relayAbort = new AbortController(); + const signal = relayAbort.signal; + const stopDeadline = () => timeoutSignal.removeEventListener("abort", onTimeout); + const cleanup = () => { + stopDeadline(); + req.signal.removeEventListener("abort", onClientAbort); + }; + const onTimeout = () => { relayAbort.abort(timeoutSignal.reason); cleanup(); }; + const onClientAbort = () => { relayAbort.abort(req.signal.reason); cleanup(); }; + timeoutSignal.addEventListener("abort", onTimeout, { once: true }); + req.signal.addEventListener("abort", onClientAbort, { once: true }); + if (req.signal.aborted) onClientAbort(); + else if (timeoutSignal.aborted) onTimeout(); let upstream: Response; try { upstream = await (deps.fetchImpl ?? fetch)(destination, { @@ -258,9 +270,16 @@ export async function relayHubManagementRequest( signal, }); } catch { + cleanup(); + return relayError(502, "hub relay unavailable"); + } + if (signal.aborted) { + cleanup(); + try { await upstream.body?.cancel(); } catch { /* best effort */ } return relayError(502, "hub relay unavailable"); } if (upstream.status >= 300 && upstream.status < 400) { + cleanup(); try { await upstream.body?.cancel(); } catch { /* best effort */ } return relayError(502, "hub relay redirect refused"); } @@ -268,18 +287,31 @@ export async function relayHubManagementRequest( const responseConnectionNamed = new Set((upstream.headers.get("connection") ?? "").split(",").map(value => value.trim().toLowerCase()).filter(Boolean)); const responseHeaders = filteredHeaders(upstream.headers, RESPONSE_HEADERS, responseConnectionNamed); if (!headersWithinLimit(responseHeaders)) { + cleanup(); try { await upstream.body?.cancel(); } catch { /* best effort */ } return relayError(502, "hub relay response headers too large"); } const declaredResponseLength = upstream.headers.get("content-length"); if (declaredResponseLength !== null && (!/^\d+$/.test(declaredResponseLength) || Number(declaredResponseLength) > HUB_RELAY_RESPONSE_BODY_MAX_BYTES)) { + cleanup(); try { await upstream.body?.cancel(); } catch { /* best effort */ } return relayError(502, "hub relay response body too large"); } - const responseBody = method === "HEAD" || !upstream.body - ? null - : boundedRelayResponseStream(upstream.body, HUB_RELAY_RESPONSE_BODY_MAX_BYTES, signal); + // Only this known, successfully established SSE endpoint outlives the handshake. + // Its body remains byte-bounded and connected to the browser's abort signal. + if (method === "GET" && destination.pathname === "/api/accounts/events" && !destination.search + && upstream.status === 200 + && responseHeaders.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase() === "text/event-stream") { + stopDeadline(); + } + let responseBody: ReadableStream | null = null; + if (method === "HEAD" || !upstream.body) { + cleanup(); + try { await upstream.body?.cancel(); } catch { /* best effort */ } + } else { + responseBody = boundedRelayResponseStream(upstream.body, HUB_RELAY_RESPONSE_BODY_MAX_BYTES, signal, cleanup); + } return new Response(responseBody, { status: upstream.status, statusText: upstream.statusText, diff --git a/src/oauth/anthropic-routing.ts b/src/oauth/anthropic-routing.ts index 7474e533ef..a029207be5 100644 --- a/src/oauth/anthropic-routing.ts +++ b/src/oauth/anthropic-routing.ts @@ -71,7 +71,9 @@ interface AffinityEntry { const upstreamHealth = new Map(); const sessionAffinity = new Map(); type OAuthAccountSelection = NonNullable>; -let manualPreference: OAuthAccountSelection | null = null; +// Undefined means this runtime has not admitted a selection yet; null means consumed. +// The startup baseline comes from the authoritative store, never a second persisted pin. +let manualPreference: OAuthAccountSelection | null | undefined; function normalizeAffinityComponent(value: string | null | undefined): string { const normalized = value?.trim() ?? ""; @@ -156,7 +158,7 @@ export function sweepExpiredAnthropicRoutingHealth(now = Date.now()): number { export function clearAnthropicAccountPoolState(): void { upstreamHealth.clear(); sessionAffinity.clear(); - manualPreference = null; + manualPreference = undefined; quorumCache = null; } @@ -533,6 +535,12 @@ export function resolveAnthropicAccountForSession( const set = getAccountSet(PROVIDER); if (!set || set.accounts.length === 0) return { accountId: null, reason: "none" }; + if (manualPreference === undefined) { + manualPreference = set.selectionRevision !== undefined + ? { accountId: set.activeAccountId, revision: set.selectionRevision } + : null; + } + if (!isAnthropicAccountPoolEnabled(config)) { return { accountId: set.activeAccountId, reason: "pool-disabled" }; } @@ -743,8 +751,8 @@ export function commitAnthropicSelectionRouting( } bindAnthropicSessionAffinity(options.sessionKey, accountId); } - if (manualPreference?.accountId === expectedSelection.accountId - && manualPreference.revision === expectedSelection.revision) manualPreference = null; + if (manualPreference === undefined || (manualPreference?.accountId === expectedSelection.accountId + && manualPreference.revision === expectedSelection.revision)) manualPreference = null; return true; } diff --git a/src/server/management-auth.ts b/src/server/management-auth.ts index 09127b0b58..58ae3f7d60 100644 --- a/src/server/management-auth.ts +++ b/src/server/management-auth.ts @@ -251,10 +251,26 @@ export function issueGuiSession( export interface ManagementSessionControl { revokeCurrent(req: Request): boolean; + /** Revalidate a long-lived request against current authority, without cached admission or renewal. */ + isCurrent(req: Request, config: OcxConfig): boolean; } export function createManagementSessionControl(state: ManagementAuthState): ManagementSessionControl { return { + isCurrent(req: Request, config: OcxConfig): boolean { + if (!state.available) return false; + const credential = requestManagementCredential(req); + if (!credential) return false; + if (equalSecret(credential, state.token)) return true; + const session = state.sessions.get(credential); + if (!session) return false; + // Reuse the full origin/expiry/CSRF predicate against the current record, but + // isolate its sliding-expiry mutation: SSE heartbeats are not browser activity. + return authorizeGuiSessionRequest(req, config, { + sessions: new Map([[credential, { ...session }]]), + pairingGrants: state.pairingGrants, + }).ok; + }, revokeCurrent(req: Request): boolean { if (!state.available) return false; const credential = requestManagementCredential(req); diff --git a/src/server/management/account-selection-stream.ts b/src/server/management/account-selection-stream.ts index 94a1890a47..e04e8a3e14 100644 --- a/src/server/management/account-selection-stream.ts +++ b/src/server/management/account-selection-stream.ts @@ -6,8 +6,12 @@ const HEARTBEAT_MS = 15_000; const encoder = new TextEncoder(); const connections = new Set<() => void>(); -/** Auth is enforced by the management boundary before this read-only stream is opened. */ -export function accountSelectionStream(request: Request): Response { +/** The management boundary admits the request; every frame revalidates current authority. */ +export function accountSelectionStream(request: Request, validate: () => boolean): Response { + const authorized = () => { + try { return validate() === true; } catch { return false; } + }; + if (!authorized()) return Response.json({ error: "Management session is no longer authorized" }, { status: 401 }); if (connections.size >= MAX_SELECTION_STREAMS) { return Response.json({ error: "Too many account selection streams" }, { status: 429, headers: { "Retry-After": "15" }, @@ -31,6 +35,12 @@ export function accountSelectionStream(request: Request): Response { cleanup = close; const send = (frame: string) => { if (closed) return; + if (!authorized()) { + // Error clears queued frames as well, so a revoked consumer cannot drain them. + try { controller.error(new DOMException("Management session is no longer authorized", "NotAllowedError")); } + finally { close(); } + return; + } // Reconnection sends a ready event, so a slow reader can reconcile without an // unbounded queue or silently dropping a provider's latest invalidation. if (controller.desiredSize !== null && controller.desiredSize <= 0) { close(); return; } @@ -46,6 +56,7 @@ export function accountSelectionStream(request: Request): Response { send(`event: account-selection\ndata: ${JSON.stringify(event)}\n\n`); }); send(`event: ready\ndata: ${JSON.stringify({ revision: currentAccountSelectionRevision() })}\n\n`); + if (closed) return; heartbeat = setInterval(() => send(": heartbeat\n\n"), HEARTBEAT_MS); heartbeat.unref?.(); }, diff --git a/src/server/management/oauth-account-routes.ts b/src/server/management/oauth-account-routes.ts index 63993fd6e2..874e3e24fa 100644 --- a/src/server/management/oauth-account-routes.ts +++ b/src/server/management/oauth-account-routes.ts @@ -131,7 +131,7 @@ export async function handleOauthAccountRoutes(ctx: ManagementContext): Promise< if (url.pathname === "/api/accounts/events" && req.method === "GET") { const { accountSelectionStream } = await import("./account-selection-stream"); - return accountSelectionStream(req); + return accountSelectionStream(req, () => ctx.sessionControl?.isCurrent(req, config) === true); } // Which providers support real OAuth login (drives the GUI's "Log in with …" buttons). diff --git a/src/server/responses/core.ts b/src/server/responses/core.ts index 3cb666650f..e563ec5c4a 100644 --- a/src/server/responses/core.ts +++ b/src/server/responses/core.ts @@ -118,7 +118,7 @@ import { type OAuthAccessSnapshot, UnsupportedOAuthProviderError, } from "../../oauth"; -import { captureOAuthAccountSelection, commitOAuthAccountSelection } from "../../oauth/store"; +import { captureOAuthAccountSelection, commitOAuthAccountSelection, credentialGeneration, getAccountCredentialWithStatus } from "../../oauth/store"; import { ANTHROPIC_POOL_MAX_FAILOVERS_PER_REQUEST, anthropicSessionKeyFromParts, @@ -359,7 +359,7 @@ import type { EffectiveSubagentRoster, SpawnAgentSurface } from "../../codex/cat import { buildToolBridgeMaps, collabSurface, injectDeveloperMessage, multiAgentGuidanceText } from "./collaboration"; import { mapCodexAuthContextErrorToResponse, nativeMainRefreshFailureResponse } from "./codex-auth-error"; import { hasUnreadableEncryptedAgentTask, looksLikeBackendCiphertext, sanitizeEncryptedContentInPlace } from "./encrypted-payload"; -import { fetchWithHeaderTimeout, providerFetch, safeHostLabel, safeOriginLabel, storedPoolReplayDispatchNotifier } from "./fetch-helpers"; +import { fetchWithHeaderTimeout, providerFetch, safeHostLabel, safeOriginLabel, storedPoolReplayDispatchNotifier, type ProviderFetchOptions } from "./fetch-helpers"; import { classifyTransportFailureKind, transportErrorCode } from "../../lib/upstream-reachability"; import { acquireUpstreamHostAdmission, @@ -3676,6 +3676,7 @@ async function handleResponsesInner( let genericFailovers = 0; let oauthSelection = route.provider.authMode === "oauth" ? captureOAuthAccountSelection(route.providerName) : null; + let servingOAuthSnapshot: OAuthAccessSnapshot | undefined; const commitResolvedOAuthSelection = async ( candidate: OAuthAccessSnapshot, proactive = false, @@ -3706,6 +3707,7 @@ async function handleResponsesInner( { config, sessionKey: anthropicSessionKey, reason: anthropicReason, expectedCredentialGeneration: candidate.generation }, )) return null; oauthSelection = committed; + servingOAuthSnapshot = candidate; forgetGenericFailoverRoster(route.providerName); return candidate; } @@ -3790,11 +3792,59 @@ async function handleResponsesInner( // served it. All three rotation sites funnel through here, so this is the only re-stamp // needed -- and putting it anywhere else would let one of the three drift. stampOAuthAccountLabel(logCtx, route.providerName, route.provider, snapshot.accountId); - genericFailoverAccountId = snapshot.accountId; + if (route.providerName === "anthropic") { + anthropicPoolAccountId = snapshot.accountId; + logCtx.provider = formatAnthropicProviderForLog("anthropic", snapshot.accountId, config); + } else { + genericFailoverAccountId = snapshot.accountId; + } sentOAuthSnapshot = snapshot; replayOAuthCredentialSnapshot = { accountId: snapshot.accountId, generation: snapshot.generation }; return true; }; + const oauthSelectionIsCurrent = (): boolean => { + if (route.provider.authMode !== "oauth") return true; + if (!oauthSelection || !servingOAuthSnapshot) return false; + const selected = captureOAuthAccountSelection(route.providerName); + const row = getAccountCredentialWithStatus(route.providerName, servingOAuthSnapshot.accountId); + return selected?.accountId === oauthSelection.accountId && selected?.revision === oauthSelection.revision + && !!row && !row.needsReauth && row.credential.expires > Date.now() + && credentialGeneration(row.credential) === servingOAuthSnapshot.generation; + }; + const oauthDispatch = (wireRequest: AdapterRequest, requestParsed = parsed): ProviderFetchOptions["dispatchOverride"] => { + if (route.provider.authMode !== "oauth") return undefined; + return async (input, init, execute) => { + let destination = input; + let dispatchInit = init; + for (let attempt = 0; attempt < 3; attempt++) { + if (oauthSelectionIsCurrent()) { + const fetchImpl = (route.provider as OcxProviderConfig & { fetch?: typeof globalThis.fetch }).fetch ?? execute; + return fetchImpl(destination, dispatchInit); + } + if (!servingOAuthSnapshot || !await applyFailoverSnapshot(servingOAuthSnapshot, requestParsed)) { + throw new Error("OAuth account selection changed before dispatch"); + } + const nextAdapter = resolveAdapter(resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire), config.cacheRetention); + const rebuilt = await nextAdapter.buildRequest(requestParsed, { headers: selectedForwardHeaders, translatorBudget }); + const bodySize = checkOutboundBodySize(rebuilt.body, config.maxUpstreamBodyBytes); + if (!bodySize.admitted) { + rebuilt.releaseBodyObservation?.(); + return formatErrorResponse(413, "outbound_body_too_large", describeOutboundBodyRefusal(bodySize)); + } + const headers = new Headers(dispatchInit.headers); + for (const name of Object.keys(wireRequest.headers)) headers.delete(name); + for (const [name, value] of Object.entries(rebuilt.headers)) headers.set(name, value); + wireRequest.releaseBodyObservation?.(); + Object.assign(wireRequest, rebuilt); + destination = rebuilt.url; + dispatchInit = { ...dispatchInit, method: rebuilt.method, headers, body: rebuilt.body }; + bindRouteReasoningReplayScope({ parsed: requestParsed, providerName: route.providerName, provider: route.provider, + adapterName: nextAdapter.name, oauthCredentialSnapshot: replayOAuthCredentialSnapshot }); + // The next iteration validates synchronously and calls fetch in that same turn. + } + throw new Error("OAuth account selection changed repeatedly before dispatch"); + }; + }; const anthropicSessionKey = route.providerName === "anthropic" && route.provider.authMode === "oauth" ? anthropicSessionKeyFromParts({ sessionIdHeader: sessionIdHeaderFromRequest(req.headers), @@ -3909,22 +3959,11 @@ async function handleResponsesInner( // Only genuinely accountless adapter calls leave the context undefined and use local/env fallback. parsed._kiroAuthContext = { ...(resolved.kiro ?? {}) }; } - // Antigravity (cloud-code-assist) needs the discovered Cloud Code Assist project id in the - // CCA envelope. Keep it paired with the token snapshot so an account rotation cannot mix - // a fresh token with project metadata re-read from a different credential generation. + // Project identity belongs to the admitted account on EVERY request, including + // the request after a pool transition made that account the persisted active one. if (route.provider.googleMode === "cloud-code-assist") { - // When pre-dispatch chose a DIFFERENT account, the configured project belongs to - // the account we did not use, and `!route.provider.project` would skip right past - // it — installing B's bearer alongside A's project. That is the #2841 pairing bug - // in its original shape, so the preferred-account path replaces the project - // unconditionally and refuses to dispatch at all if the chosen account has none. - // A project-less preferred account already fell back above, so by here the - // preferred path always has one. - if (usedPreferredAccount && resolved.projectId) { - route.provider = { ...route.provider, project: resolved.projectId }; - } else if (!route.provider.project && resolved.projectId) { - route.provider = { ...route.provider, project: resolved.projectId }; - } + if (!resolved.projectId) return formatErrorResponse(401, "authentication_error", publicOAuthAuthenticationErrorMessage(new Error("Cloud Code Assist account project is unavailable"))); + route.provider = { ...route.provider, project: resolved.projectId }; } } } catch (err) { @@ -4596,6 +4635,7 @@ async function handleResponsesInner( body: request.body, }, recovery), upstream.signal, connectMs, parsed.stream, providerFetch(route.provider, options.codexWsRuntimeIdentity, { + dispatchOverride: oauthDispatch(request), providerName: route.providerName, modelId: route.modelId, onCodexWsQuota: codexWsQuotaObserver(authCtx, route.provider), @@ -4673,6 +4713,7 @@ async function handleResponsesInner( body: request.body, }, innerRecovery), upstream.signal, connectMs, parsed.stream, providerFetch(route.provider, options.codexWsRuntimeIdentity, { + dispatchOverride: oauthDispatch(request), providerName: route.providerName, modelId: route.modelId, onCodexWsQuota: codexWsQuotaObserver(authCtx, route.provider), @@ -4778,6 +4819,7 @@ async function handleResponsesInner( // here on is a genuine transport attempt. storedPoolReplayDispatchNotifier( providerFetch(route.provider, options.codexWsRuntimeIdentity, { + dispatchOverride: oauthDispatch(request), providerName: route.providerName, modelId: route.modelId, onCodexWsQuota: codexWsQuotaObserver(authCtx, route.provider), @@ -4897,6 +4939,7 @@ async function handleResponsesInner( body: request.body, }, recovery), upstream.signal, connectMs, parsed.stream, providerFetch(route.provider, options.codexWsRuntimeIdentity, { + dispatchOverride: oauthDispatch(request), providerName: route.providerName, modelId: route.modelId, onCodexWsQuota: codexWsQuotaObserver(authCtx, route.provider), @@ -4996,6 +5039,7 @@ async function handleResponsesInner( body: request.body, }, recovery), upstream.signal, connectMs, parsed.stream, providerFetch(route.provider, options.codexWsRuntimeIdentity, { + dispatchOverride: oauthDispatch(request), providerName: route.providerName, modelId: route.modelId, onCodexWsQuota: codexWsQuotaObserver(authCtx, route.provider), @@ -6073,6 +6117,24 @@ async function handleResponsesInner( const queue = createAdapterEventQueue({ onBacklogExceeded: () => runTurnAbort.abort(), }); + let selectionDispatchRefused = false; + let selectionDispatchRetries = 0; + const refreshRunTurnSelection = async (): Promise => { + if (oauthSelectionIsCurrent()) return; + if (!servingOAuthSnapshot || !await applyFailoverSnapshot(servingOAuthSnapshot)) { + throw new Error("OAuth account selection changed before dispatch"); + } + parsed._cursorIdentityScope = undefined; + parsed._cursorConversationId = undefined; + if (parsed._providerContinuation?.cursor) { + const { cursor: _oldCursor, ...rest } = parsed._providerContinuation; + parsed._providerContinuation = rest; + } + runTurnAdapter = resolveAdapter(resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire), config.cacheRetention); + bindRouteReasoningReplayScope({ parsed, providerName: route.providerName, provider: route.provider, + adapterName: runTurnAdapter.name, oauthCredentialSnapshot: replayOAuthCredentialSnapshot }); + sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, runTurnAdapter.name, logCtx.accountLogLabel); + }; // Initial admission must settle before the streaming Response commits HTTP 200. // Let the outer Responses facade preserve the local retryable-429 contract. try { @@ -6096,7 +6158,9 @@ async function handleResponsesInner( if (!pacingSlotAcquired) { await waitForProviderRequestSlot(route.providerName, route.provider, route.modelId, runTurnAbort.signal); } + await refreshRunTurnSelection(); noteAttemptSend(logCtx.activeAttempt, logCtx.usageLogInputTokens, recovery); + let firstPhysicalSend = true; const runTurnProviderFetch = providerFetch( route.provider, options.codexWsRuntimeIdentity, @@ -6107,6 +6171,14 @@ async function handleResponsesInner( // Cursor HTTP/1.1 consumes it for RunSSE; every BidiAppend and redial then waits on // the same provider queue through this stateful wrapper. pacingSlotAcquired: true, + beforeDispatch: () => { + if (!firstPhysicalSend) return; // A started multi-message turn keeps its credential. + if (!oauthSelectionIsCurrent()) { + selectionDispatchRefused = true; + throw new Error("OAuth account selection changed before dispatch"); + } + firstPhysicalSend = false; + }, }, ); await runTurnAdapter.runTurn?.( @@ -6145,6 +6217,11 @@ async function handleResponsesInner( const rotateRunTurnAdapterOnPreflight429 = async ( error: Extract, ): Promise => { + if (selectionDispatchRefused && selectionDispatchRetries < 3) { + selectionDispatchRefused = false; + selectionDispatchRetries += 1; + try { await refreshRunTurnSelection(); return true; } catch { return false; } + } const status = error.status ?? adapterFailureFromMessage(error.message).httpStatus; if ( status !== 429 @@ -6228,7 +6305,7 @@ async function handleResponsesInner( if (parsed.stream) { void runTurn(); let eventSource: AsyncIterable = queue.stream(); - if (genericFailoverAccountId && isGenericOAuthFailoverEnabled(config, route.providerName)) { + if (route.provider.authMode === "oauth" || (genericFailoverAccountId && isGenericOAuthFailoverEnabled(config, route.providerName))) { // Preflight holds only heartbeats and the first meaningful event. A first-event 429 can be // replayed transparently; after any output reaches the bridge, a later error stays terminal. eventSource = await preflightRunTurnFailover(eventSource); @@ -6311,7 +6388,7 @@ async function handleResponsesInner( await runTurn(); const firstAttemptEvents = await queue.collect(); let runTurnEvents: AdapterEvent[] = firstAttemptEvents; - if (genericFailoverAccountId && isGenericOAuthFailoverEnabled(config, route.providerName)) { + if (route.provider.authMode === "oauth" || (genericFailoverAccountId && isGenericOAuthFailoverEnabled(config, route.providerName))) { runTurnEvents = []; for await (const event of await preflightRunTurnFailover( (async function* () { yield* firstAttemptEvents; })(), @@ -6502,6 +6579,7 @@ async function handleResponsesInner( timeoutMs: connectMs, stream: parsed.stream, executor: providerFetch(route.provider, options.codexWsRuntimeIdentity, { + dispatchOverride: oauthDispatch(builtInitialRequest), providerName: route.providerName, modelId: route.modelId, }), @@ -6527,6 +6605,7 @@ async function handleResponsesInner( body: builtInitialRequest.body, }, recovery), upstream.signal, connectMs, parsed.stream, providerFetch(route.provider, options.codexWsRuntimeIdentity, { + dispatchOverride: oauthDispatch(builtInitialRequest), providerName: route.providerName, modelId: route.modelId, })); @@ -6622,6 +6701,7 @@ async function handleResponsesInner( timeoutMs: connectMs, stream: parsed.stream, executor: providerFetch(route.provider, options.codexWsRuntimeIdentity, { + dispatchOverride: oauthDispatch(retryRequest), providerName: route.providerName, modelId: route.modelId, }), @@ -6643,6 +6723,7 @@ async function handleResponsesInner( method: retryRequest.method, headers: retryRequest.headers, body: retryRequest.body, }, recoveryKind), upstream.signal, connectMs, parsed.stream, providerFetch(route.provider, options.codexWsRuntimeIdentity, { + dispatchOverride: oauthDispatch(retryRequest), providerName: route.providerName, modelId: route.modelId, })), @@ -7102,6 +7183,7 @@ async function handleResponsesInner( timeoutMs: connectMs, stream: nextParsed.stream, executor: providerFetch(route.provider, options.codexWsRuntimeIdentity, { + dispatchOverride: oauthDispatch(builtContinuationRequest, nextParsed), providerName: route.providerName, modelId: nextParsed.modelId, }), @@ -7127,6 +7209,7 @@ async function handleResponsesInner( connectMs, nextParsed.stream, providerFetch(route.provider, options.codexWsRuntimeIdentity, { + dispatchOverride: oauthDispatch(builtContinuationRequest, nextParsed), providerName: route.providerName, modelId: nextParsed.modelId, }), diff --git a/src/server/responses/fetch-helpers.ts b/src/server/responses/fetch-helpers.ts index e1423d3311..b6365be4be 100644 --- a/src/server/responses/fetch-helpers.ts +++ b/src/server/responses/fetch-helpers.ts @@ -61,6 +61,8 @@ export interface ProviderFetchOptions { onCodexWsQuota?: CodexWsQuotaObserver; /** Synchronous admission at actual credential dispatch, after pacing/backoff. */ beforeDispatch?: (headers: Headers) => void; + /** Revalidate/rebuild a queued request at its physical send boundary, after pacing. */ + dispatchOverride?: (input: Parameters[0], init: RequestInit, execute: typeof globalThis.fetch) => Promise; } export function providerFetch( @@ -75,7 +77,10 @@ export function providerFetch( const httpFetch = Object.assign( async (input: Parameters[0], init?: RequestInit) => { options.beforeDispatch?.(new Headers(init?.headers ?? (input instanceof Request ? input.headers : undefined))); - return base(input, { ...withUpstreamHttpVersion(input, init, provider), timeout: 0 }); + const dispatchInit = { ...withUpstreamHttpVersion(input, init, provider), timeout: 0 }; + return options.dispatchOverride + ? options.dispatchOverride(input, dispatchInit, base) + : base(input, dispatchInit); }, { preconnect }, ) as typeof globalThis.fetch; diff --git a/tests/adapters/anthropic/anthropic-account-pool.test.ts b/tests/adapters/anthropic/anthropic-account-pool.test.ts index c4665b3ea1..61396bd71e 100644 --- a/tests/adapters/anthropic/anthropic-account-pool.test.ts +++ b/tests/adapters/anthropic/anthropic-account-pool.test.ts @@ -79,6 +79,9 @@ async function seedTwoAccounts() { const a = set.accounts.find(acc => acc.credential.accountId === "uuid-aaaa")!; const b = set.accounts.find(acc => acc.credential.accountId === "uuid-bbbb")!; await setActiveAccount("anthropic", a.id); + // Ordinary policy cases start after the initial stored selection has been admitted. + // Restart cases explicitly clear runtime state below to exercise first admission again. + await admitAnthropic("", cfg(false)); return { aId: a.id, bId: b.id }; } @@ -133,10 +136,31 @@ async function seedThreeAccounts() { const b = set.accounts.find(acc => acc.credential.accountId === "uuid-bbbb")!; const c = set.accounts.find(acc => acc.credential.accountId === "uuid-cccc")!; await setActiveAccount("anthropic", a.id); + await admitAnthropic("", cfg(false)); return { aId: a.id, bId: b.id, cId: c.id }; } describe("anthropic account pool", () => { + test.each(["round-robin", "quota"] as const)("persisted manual choice survives restart before the first %s dispatch", async strategy => { + const { aId, bId } = await seedTwoAccounts(); + setCachedProviderAccountQuotaForTests("anthropic", aId, { fiveHourPercent: 11 }); + setCachedProviderAccountQuotaForTests("anthropic", bId, { fiveHourPercent: 30 }); + await setActiveAccount("anthropic", bId); + resetAnthropicRoutingForManualSelection(bId); + const persisted = captureOAuthAccountSelection("anthropic"); + // A process restart loses both local preference and the shared RR cursor. + clearAnthropicAccountPoolState(); + clearPoolRotationState(); + const config = cfg(true, 20, { strategy, stickyLimit: 1 }); + expect(resolveAnthropicAccountForSession("restart-first", config).accountId).toBe(bId); + expect(resolveAnthropicAccountForSession("restart-proposal", config).accountId).toBe(bId); + expect(captureOAuthAccountSelection("anthropic")).toEqual(persisted); + expect((await admitAnthropic("restart-first", config)).accountId).toBe(bId); + // Once the authoritative first selection commits, the ordinary algorithm resumes. + expect((await admitAnthropic("restart-next", config)).accountId).toBe(aId); + expect(resolveAnthropicAccountForSession("restart-first", config).accountId).toBe(bId); + }); + test("pool-off 429 recovery commits the replacement and notifies before dispatch", async () => { const { aId, bId } = await seedTwoAccounts(); const config = cfg(false); diff --git a/tests/clients/client-hub-relay.test.ts b/tests/clients/client-hub-relay.test.ts index 442bc91810..9baab569fc 100644 --- a/tests/clients/client-hub-relay.test.ts +++ b/tests/clients/client-hub-relay.test.ts @@ -1,7 +1,8 @@ -import { describe, expect, test } from "bun:test"; +import { describe, expect, spyOn, test } from "bun:test"; import { HUB_RELAY_REQUEST_BODY_MAX_BYTES, HUB_RELAY_RESPONSE_BODY_MAX_BYTES, + HUB_RELAY_DEFAULT_TIMEOUT_MS, relayHubManagementRequest, validateHubRelayRequestHeaders, } from "../../src/client/hub-relay"; @@ -144,4 +145,128 @@ describe("fixed-target hub management relay", () => { await reader.cancel(); expect(cancelled).toBe(true); }); + + test("established account SSE outlives the handshake deadline and still cancels on client abort", async () => { + const deadline = new AbortController(); + const timeout = spyOn(AbortSignal, "timeout").mockReturnValue(deadline.signal); + const browser = new AbortController(); + let upstreamSignal!: AbortSignal; + let upstreamController!: ReadableStreamDefaultController; + let cancelled = false; + let reader: ReadableStreamDefaultReader | undefined; + try { + const response = await relayHubManagementRequest(relayRequest("/api/accounts/events", { signal: browser.signal }), "/api/accounts/events", target, { + fetchImpl: (async (_input, init) => { + upstreamSignal = init!.signal!; + return new Response(new ReadableStream({ + start(controller) { upstreamController = controller; controller.enqueue(new TextEncoder().encode("event: ready\n\n")); }, + cancel() { cancelled = true; }, + }), { headers: { "Content-Type": "text/event-stream; charset=utf-8" } }); + }) as typeof fetch, + }); + expect(timeout).toHaveBeenCalledWith(HUB_RELAY_DEFAULT_TIMEOUT_MS); + reader = response.body!.getReader(); + expect(new TextDecoder().decode((await reader.read()).value)).toContain("ready"); + deadline.abort(new DOMException("Handshake deadline", "TimeoutError")); + expect(upstreamSignal.aborted).toBe(false); + expect(cancelled).toBe(false); + upstreamController.enqueue(new TextEncoder().encode("event: account-selection\n\n")); + expect(new TextDecoder().decode((await reader.read()).value)).toContain("account-selection"); + const pending = reader.read(); + browser.abort(); + await pending.catch(() => undefined); + expect(upstreamSignal.aborted).toBe(true); + expect(cancelled).toBe(true); + } finally { + await reader?.cancel().catch(() => undefined); + browser.abort(); + timeout.mockRestore(); + } + }); + + test.each([ + ["/api/config", "GET", 200, "application/json"], + ["/api/config", "GET", 200, "text/event-stream"], + ["/api/accounts/events", "POST", 200, "text/event-stream"], + ["/api/accounts/events", "GET", 201, "text/event-stream"], + ["/api/accounts/events", "GET", 401, "text/event-stream"], + ["/api/accounts/events", "GET", 200, "application/json"], + ["/api/accounts/events", "GET", 200, "text/event-streamish"], + ["/api/accounts/events?other=1", "GET", 200, "text/event-stream"], + ] as const)("relay keeps the total deadline for %s %s %i %s", async (path, method, status, contentType) => { + const deadline = new AbortController(); + const timeout = spyOn(AbortSignal, "timeout").mockReturnValue(deadline.signal); + let upstreamSignal!: AbortSignal; + let cancelled = false; + let reader: ReadableStreamDefaultReader | undefined; + try { + const response = await relayHubManagementRequest(relayRequest(path, { method }), path, target, { + fetchImpl: (async (_input, init) => { + upstreamSignal = init!.signal!; + return new Response(new ReadableStream({ cancel() { cancelled = true; } }), { + status, headers: { "Content-Type": contentType }, + }); + }) as typeof fetch, + }); + reader = response.body!.getReader(); + const pending = reader.read(); + deadline.abort(new DOMException("Total deadline", "TimeoutError")); + await pending.catch(() => undefined); + expect(upstreamSignal.aborted).toBe(true); + expect(cancelled).toBe(true); + } finally { + await reader?.cancel().catch(() => undefined); + timeout.mockRestore(); + } + }); + + test("selection SSE still has a handshake deadline and a response body cap", async () => { + const deadline = new AbortController(); + const timeout = spyOn(AbortSignal, "timeout").mockReturnValue(deadline.signal); + let handshakeStarted!: () => void; + const started = new Promise(resolve => { handshakeStarted = resolve; }); + try { + const pending = relayHubManagementRequest(relayRequest("/api/accounts/events"), "/api/accounts/events", target, { + fetchImpl: (async (_input, init) => new Promise((_resolve, reject) => { + init!.signal!.addEventListener("abort", () => reject(init!.signal!.reason), { once: true }); + handshakeStarted(); + })) as typeof fetch, + }); + await started; + deadline.abort(new DOMException("Handshake deadline", "TimeoutError")); + expect((await pending).status).toBe(502); + } finally { timeout.mockRestore(); } + + let cancelled = false; + const response = await relayHubManagementRequest(relayRequest("/api/accounts/events"), "/api/accounts/events", target, { + fetchImpl: (async () => new Response(new ReadableStream({ + start(controller) { controller.enqueue(new Uint8Array(HUB_RELAY_RESPONSE_BODY_MAX_BYTES + 1)); }, + cancel() { cancelled = true; }, + }), { headers: { "Content-Type": "text/event-stream" } })) as typeof fetch, + }); + await expect(response.arrayBuffer()).rejects.toThrow("response body too large"); + expect(cancelled).toBe(true); + }); + + test.each(["complete", "cancel"] as const)("relay detaches deadline and client listeners after body %s", async disposition => { + const deadline = new AbortController(); + const timeout = spyOn(AbortSignal, "timeout").mockReturnValue(deadline.signal); + const browser = new AbortController(); + let upstreamSignal!: AbortSignal; + try { + const response = await relayHubManagementRequest(relayRequest("/api/config", { signal: browser.signal }), "/api/config", target, { + fetchImpl: (async (_input, init) => { + upstreamSignal = init!.signal!; + return new Response(new ReadableStream({ + start(controller) { if (disposition === "complete") controller.close(); }, + }), { headers: { "Content-Type": "application/json" } }); + }) as typeof fetch, + }); + if (disposition === "complete") await response.text(); + else await response.body!.cancel(); + deadline.abort(); + browser.abort(); + expect(upstreamSignal.aborted).toBe(false); + } finally { timeout.mockRestore(); } + }); }); diff --git a/tests/gui/provider-workspace-auth.test.ts b/tests/gui/provider-workspace-auth.test.ts index edc407906b..e121142c01 100644 --- a/tests/gui/provider-workspace-auth.test.ts +++ b/tests/gui/provider-workspace-auth.test.ts @@ -82,13 +82,18 @@ async function providersPageSeam(): Promise { describe("workspace account integration seam", () => { test("passes account state and handlers into provider details", async () => { const source = await providersPageSeam(); + const page = await Bun.file("gui/src/pages/Providers.tsx").text(); + // Additional type imports must not obscure the runtime hook binding and its caller. + const poolBindings = page.match(/import\s*\{([^}]+)\}\s*from\s*["']\.\.\/hooks\/useProviderAccountPools["']/)?.[1]; + expect(poolBindings?.split(",").map(binding => binding.trim())).toContain("useProviderAccountPools"); + expect(page).toContain("const pools = useProviderAccountPools({"); expect(source).toContain("accountLoadState={accountLoadStates[item.name]"); expect(source).toContain("switchingAccountId={switchingAccount?.provider === item.name"); expect(source).toContain("onRetryAccounts: async provider => { await fetchAccountSets([provider]); }"); expect(source).toContain("key={item.name}"); expect(source).toContain("switchingAccountRef.current"); - expect(source).toContain("const refreshed = await fetchAccountSets([provider])"); - expect(source).toContain("if (!refreshed)"); + expect(source).toContain('const refreshed = await refreshAccountRosters({ provider, kind: "oauth" })'); + expect(source).toContain('if (!refreshed) { notify(t("pws.accountsLoadFailed"), false); return; }'); }); test("owns an accessible dynamic account panel instead of nesting auth in Settings", async () => { diff --git a/tests/oauth/adapter-event-oauth-failover.test.ts b/tests/oauth/adapter-event-oauth-failover.test.ts index a12449786e..2a97f90ff3 100644 --- a/tests/oauth/adapter-event-oauth-failover.test.ts +++ b/tests/oauth/adapter-event-oauth-failover.test.ts @@ -12,6 +12,7 @@ const actualResolver = await import("../../src/server/adapter-resolve"); const actualResolveAdapter = actualResolver.resolveAdapter; let attempts: AdapterEvent[][] = []; let attemptKeys: string[] = []; +let attemptProjects: Array = []; /** Set by the delivery test: an attempt that emits, then blocks before completing the turn. */ let slowAttempt: ((emit: (event: AdapterEvent) => void) => Promise) | undefined; @@ -25,6 +26,7 @@ function fixtureAdapter(provider: OcxProviderConfig): ProviderAdapter { async runTurn(_parsed, _incoming, emit) { const index = attemptKeys.length; attemptKeys.push(provider.apiKey ?? ""); + attemptProjects.push(provider.project); if (slowAttempt) return await slowAttempt(emit); for (const event of attempts[index] ?? []) emit(event); }, @@ -34,7 +36,7 @@ function fixtureAdapter(provider: OcxProviderConfig): ProviderAdapter { mock.module("../../src/server/adapter-resolve", () => ({ ...actualResolver, resolveAdapter(provider: OcxProviderConfig, cacheRetention?: "none" | "short" | "long") { - if (provider.adapter === "cursor") return fixtureAdapter(provider); + if (provider.adapter === "cursor" || provider.googleMode === "cloud-code-assist") return fixtureAdapter(provider); return actualResolveAdapter(provider, cacheRetention); }, })); @@ -88,6 +90,7 @@ beforeEach(() => { clearGenericFailoverHealth(); attempts = []; attemptKeys = []; + attemptProjects = []; slowAttempt = undefined; }); @@ -99,6 +102,30 @@ afterEach(() => { }); describe("#2568 adapter-event OAuth failover", () => { + test("every CCA request pairs the persisted active account with its own project", async () => { + for (const id of ["a", "b"]) await saveCredential("google-antigravity", { + access: `ga-access-${id}`, refresh: `ga-refresh-${id}`, expires: Date.now() + 3_600_000, + accountId: id, projectId: `project-${id}`, + }); + const cfg = config(); + cfg.defaultProvider = "google-antigravity"; + cfg.providers = { "google-antigravity": { ...cfg.providers.cursor!, googleMode: "cloud-code-assist", project: "project-a" } }; + attempts = [ + [{ type: "text_delta", text: "first" }, { type: "done" }], + [{ type: "text_delta", text: "second" }, { type: "done" }], + ]; + for (let i = 0; i < 2; i++) { + const req = new Request("http://localhost/v1/responses", { + method: "POST", headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ model: "google-antigravity/model", input: "answer", stream: false }), + }); + const res = await handleResponses(req, cfg, { model: "", provider: "" }); + const responseBody = await res.text(); + expect(res.status, responseBody).toBe(200); + } + expect(attemptKeys).toEqual(["ga-access-b", "ga-access-b"]); + expect(attemptProjects).toEqual(["project-b", "project-b"]); + }); for (const stream of [true, false]) { test(`${stream ? "streaming" : "non-streaming"} first-event 429 rotates and replays`, async () => { await seedAccounts(2); diff --git a/tests/oauth/generic-oauth-failover.test.ts b/tests/oauth/generic-oauth-failover.test.ts index 70ffff6d09..48fb2782ec 100644 --- a/tests/oauth/generic-oauth-failover.test.ts +++ b/tests/oauth/generic-oauth-failover.test.ts @@ -432,7 +432,8 @@ describe("sidecar on429 wiring", () => { const end = coreSource.indexOf("\n route.provider = resolveProviderTransport(", start); expect(end).toBeGreaterThan(start); const region = coreSource.slice(start, end); - expect(region).toContain("usedPreferredAccount && resolved.projectId"); + expect(region).toContain("project: resolved.projectId"); + expect(region).not.toContain("!route.provider.project"); // A project-less preferred account falls BACK to the ordinary active-account resolution // rather than erroring: a preference must never turn a working request into a failure, // and Antigravity tolerates project discovery failing, so an account with no project is diff --git a/tests/oauth/oauth-accounts-api.test.ts b/tests/oauth/oauth-accounts-api.test.ts index edc332378e..75e88d7dbb 100644 --- a/tests/oauth/oauth-accounts-api.test.ts +++ b/tests/oauth/oauth-accounts-api.test.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, describe, expect, test } from "bun:test"; +import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test"; import { managementFetch as fetch } from "../helpers/management-auth"; import { mkdtempSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; @@ -14,6 +14,35 @@ import { withStubbedProviderFetch } from "../helpers/catalog-provider-fetch"; import { getAccountSet } from "../../src/oauth/store"; import { ACCOUNT_IMPORT_DEADLINE_MS, ACCOUNT_IMPORT_MAX_BYTES, ACCOUNT_IMPORT_MAX_REQUEST_BYTES } from "../../src/oauth/account-import/types"; import { handleOauthAccountRoutes } from "../../src/server/management/oauth-account-routes"; +import { createManagementSessionControl, requireManagementAuth, type ManagementAuthState } from "../../src/server/management-auth"; +import { handleSessionRoutes } from "../../src/server/management/session-routes"; +import type { ManagementContext } from "../../src/server/management/context"; +import { publishAccountSelection } from "../../src/lib/account-selection-events"; + +function selectionSessionFixture() { + const origin = "http://127.0.0.1:10100"; + const token = "ocx_session_selection_liveness_test"; + const state: Extract = { + available: true, token: "ocx_admin_selection_test", source: "environment", + sessions: new Map([[token, { + serverOrigin: origin, browserOrigin: origin, csrfToken: "selection-csrf", + expiresAt: Date.now() + 60_000, issuance: "loopback", + }]]), pairingGrants: new Map(), + }; + const req = new Request(`${origin}/api/accounts/events`, { headers: { + Host: "127.0.0.1:10100", Origin: origin, "x-opencodex-gui-origin": origin, + "x-opencodex-api-key": token, "x-opencodex-csrf-token": "selection-csrf", + } }); + const ctx: ManagementContext = { + req, url: new URL(req.url), config: baseConfig(), deps: {}, version: "test", + principal: "gui-session", sessionControl: createManagementSessionControl(state), + convergeCodexCatalog: async () => ({ status: "failed", reason: "disk" }), + syncClaudeAgentDefsBestEffort: async () => {}, + }; + // Cache this Request's original admission: subsequent stream checks must ignore it. + expect(requireManagementAuth(req, state, ctx.config)).toBeNull(); + return { ctx, state, token }; +} let testDir = ""; let previousHome: string | undefined; @@ -80,19 +109,98 @@ describe("multiauth accounts API", () => { const streams: Response[] = []; try { for (let i = 0; i < 64; i++) { - const response = accountSelectionStream(new Request("http://localhost/api/accounts/events")); + const response = accountSelectionStream(new Request("http://localhost/api/accounts/events"), () => true); expect(response.status).toBe(200); streams.push(response); } - expect(accountSelectionStream(new Request("http://localhost/api/accounts/events")).status).toBe(429); + expect(accountSelectionStream(new Request("http://localhost/api/accounts/events"), () => true).status).toBe(429); } finally { await Promise.all(streams.map(response => response.body!.cancel())); } - const response = accountSelectionStream(new Request("http://localhost/api/accounts/events")); + const response = accountSelectionStream(new Request("http://localhost/api/accounts/events"), () => true); expect(response.status).toBe(200); await response.body!.cancel(); }); + test("selection event route denies admission without a current session validator", async () => { + const { ctx } = selectionSessionFixture(); + const response = await handleOauthAccountRoutes({ ...ctx, sessionControl: undefined }); + try { expect(response?.status).toBe(401); } + finally { await response?.body?.cancel(); } + }); + + test.each(["false", "throw"] as const)("selection stream denies an initial validator result of %s", async result => { + const { accountSelectionStream } = await import("../../src/server/management/account-selection-stream"); + const response = accountSelectionStream(new Request("http://localhost/api/accounts/events"), () => { + if (result === "throw") throw new Error("validator unavailable"); + return false; + }); + try { expect(response.status).toBe(401); } + finally { await response.body?.cancel(); } + }); + + test.each(["logout", "expiry"] as const)("selection stream stops publishing after GUI %s despite cached request admission", async change => { + const { ctx, state, token } = selectionSessionFixture(); + const response = await handleOauthAccountRoutes(ctx); + expect(response?.status).toBe(200); + const reader = response!.body!.getReader(); + try { + expect(new TextDecoder().decode((await reader.read()).value)).toContain("event: ready"); + if (change === "logout") { + const req = new Request(new URL("/api/session/logout", ctx.req.url), { method: "POST", headers: ctx.req.headers }); + expect(requireManagementAuth(req, state, ctx.config)).toBeNull(); + expect(handleSessionRoutes({ ...ctx, req, url: new URL(req.url) })?.status).toBe(200); + } else { + state.sessions.get(token)!.expiresAt = Date.now() - 1; + } + expect(requireManagementAuth(ctx.req, state, ctx.config)).toBeNull(); // Deliberately memoized. + const pending = reader.read(); + publishAccountSelection("private-provider", "oauth"); + await expect(pending).rejects.toMatchObject({ name: "NotAllowedError" }); + } finally { await reader.cancel().catch(() => undefined); } + }); + + test("selection heartbeat revalidates expiry without extending a remote session", async () => { + const { ctx, state, token } = selectionSessionFixture(); + const session = state.sessions.get(token)!; + session.issuance = "pairing"; + const expiresAt = session.expiresAt; + const interval = spyOn(globalThis, "setInterval"); + let reader: ReadableStreamDefaultReader | undefined; + try { + const response = await handleOauthAccountRoutes(ctx); + reader = response!.body!.getReader(); + await reader.read(); + const tick = interval.mock.calls.find(call => call[1] === 15_000)?.[0]; + if (typeof tick !== "function") throw new Error("selection heartbeat not registered"); + tick(); + expect(new TextDecoder().decode((await reader.read()).value)).toContain(": heartbeat"); + expect(session.expiresAt).toBe(expiresAt); + session.expiresAt = Date.now() - 1; + const pending = reader.read(); + tick(); + await expect(pending).rejects.toMatchObject({ name: "NotAllowedError" }); + } finally { + await reader?.cancel().catch(() => undefined); + interval.mockRestore(); + } + }); + + test("management session liveness rereads revoked sessions and the current admin token", () => { + const { ctx, state, token } = selectionSessionFixture(); + const control = ctx.sessionControl!; + expect(control.isCurrent(ctx.req, ctx.config)).toBe(true); + expect(control.revokeCurrent(ctx.req)).toBe(true); + expect(control.isCurrent(ctx.req, ctx.config)).toBe(false); + expect(state.sessions.has(token)).toBe(false); + const adminReq = new Request(ctx.req.url, { headers: { "x-opencodex-api-key": state.token } }); + expect(requireManagementAuth(adminReq, state, ctx.config)).toBeNull(); + expect(control.isCurrent(adminReq, ctx.config)).toBe(true); + state.token = "ocx_admin_rotated_selection_test"; + expect(control.isCurrent(adminReq, ctx.config)).toBe(false); + expect(createManagementSessionControl({ available: false, reason: "test" }).isCurrent(adminReq, ctx.config)).toBe(false); + }); + test("selection events notify only after the new active account is committed", async () => { const server = startServer(0); const abort = new AbortController(); diff --git a/tests/oauth/oauth-upsert-preserves-api-key.test.ts b/tests/oauth/oauth-upsert-preserves-api-key.test.ts index caec3363b6..a22239dd69 100644 --- a/tests/oauth/oauth-upsert-preserves-api-key.test.ts +++ b/tests/oauth/oauth-upsert-preserves-api-key.test.ts @@ -3,6 +3,7 @@ import { mkdtempSync} from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { OAUTH_PROVIDERS, upsertOAuthProvider } from "../../src/oauth"; +import { loadConfig, saveConfig } from "../../src/config"; import { migrateXaiResponsesDefault } from "../../src/providers/xai-responses-opt-in"; import { resolveWireProtocolOverride } from "../../src/server/adapter-resolve"; import { @@ -240,8 +241,13 @@ describe("upsertOAuthProvider credential preservation", () => { expect(listed.keys.find(entry => entry.id === activeId)?.active).toBe(true); expect(listed.keys.find(entry => entry.id === "pool-visible")?.active).toBe(false); + // runLogin persists the upsert before GUI key mutations. The shared selection + // transaction requires that authoritative file; it must not recreate missing config. + saveConfig(config); + expect(loadConfig().providers.xai!.apiKeyPool).toEqual(provider.apiKeyPool); expect(setActiveProviderApiKey(config, "xai", "pool-visible")).toBe(true); expect(config.providers.xai!.apiKey).toBe("pool-visible-key"); + expect(loadConfig().providers.xai!.apiKey).toBe("pool-visible-key"); expect(listProviderApiKeys(config, "xai").activeId).toBe("pool-visible"); expect(setActiveProviderApiKey(config, "xai", activeId)).toBe(true); @@ -250,6 +256,7 @@ describe("upsertOAuthProvider credential preservation", () => { expect(config.providers.xai!.apiKey).toBe("pool-visible-key"); expect(config.providers.xai!.apiKeyPool).toEqual([{ id: "pool-visible", key: "pool-visible-key" }]); expect(listProviderApiKeys(config, "xai").activeId).toBe("pool-visible"); + expect(loadConfig().providers.xai!.apiKeyPool).toEqual(config.providers.xai!.apiKeyPool); } finally { if (previousHome === undefined) delete process.env.OPENCODEX_HOME; else process.env.OPENCODEX_HOME = previousHome; @@ -329,16 +336,21 @@ describe("upsertOAuthProvider credential preservation", () => { const testHome = mkdtempSync(join(tmpdir(), "ocx-oauth-upsert-")); process.env.OPENCODEX_HOME = testHome; try { + saveConfig(config); expect(removeProviderApiKey(config, "xai", "aaaaaaaa")).toBe(true); expect(config.providers.xai!.authMode).toBe("key"); expect(config.providers.xai!.apiKey).toBeUndefined(); expect(config.providers.xai!.apiKeyPool).toBeUndefined(); + expect(loadConfig().providers.xai!.apiKey).toBeUndefined(); + expect(loadConfig().providers.xai!.apiKeyPool).toBeUndefined(); upsertOAuthProvider(config, "xai"); const provider = config.providers.xai!; expect(provider.authMode).toBe("oauth"); expect(provider.apiKey).toBeUndefined(); expect(provider.apiKeyPool).toBeUndefined(); + saveConfig(config); + expect(loadConfig().providers.xai!.authMode).toBe("oauth"); } finally { if (previousHome === undefined) delete process.env.OPENCODEX_HOME; else process.env.OPENCODEX_HOME = previousHome; diff --git a/tests/providers/github-copilot/github-copilot-account-origin.test.ts b/tests/providers/github-copilot/github-copilot-account-origin.test.ts index c820827991..1c0f614bd2 100644 --- a/tests/providers/github-copilot/github-copilot-account-origin.test.ts +++ b/tests/providers/github-copilot/github-copilot-account-origin.test.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, describe, expect, test } from "bun:test"; +import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test"; import { mkdtempSync} from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; @@ -16,6 +16,37 @@ const GITHUB_USER_URL = "https://api.github.com/user"; const originalFetch = globalThis.fetch; const originalHome = process.env.OPENCODEX_HOME; let home = ""; +let beforeBuildReturns: (() => Promise) | undefined; +let beforePacingReturns: (() => Promise) | undefined; +const actualPacing = await import("../../../src/providers/request-pacing"); +const originalWaitForSlot = actualPacing.waitForProviderRequestSlot; +mock.module("../../../src/providers/request-pacing", () => ({ + ...actualPacing, + waitForProviderRequestSlot: async (...args: Parameters) => { + const result = await originalWaitForSlot(...args); + const gate = beforePacingReturns; + beforePacingReturns = undefined; + await gate?.(); + return result; + }, +})); +const actualAdapterResolver = await import("../../../src/server/adapter-resolve"); +const originalResolveAdapter = actualAdapterResolver.resolveAdapter; +mock.module("../../../src/server/adapter-resolve", () => ({ + ...actualAdapterResolver, + resolveAdapter: (...args: Parameters) => { + const adapter = originalResolveAdapter(...args); + const build = adapter.buildRequest.bind(adapter); + adapter.buildRequest = async (...buildArgs) => { + const built = await build(...buildArgs); + const gate = beforeBuildReturns; + beforeBuildReturns = undefined; + await gate?.(); + return built; + }; + return adapter; + }, +})); type Wire = "chat" | "responses"; @@ -146,6 +177,8 @@ function installFetch(options: { } beforeEach(() => { + beforeBuildReturns = undefined; + beforePacingReturns = undefined; home = mkdtempSync(join(tmpdir(), "ocx-copilot-origin-")); process.env.OPENCODEX_HOME = home; clearGenericFailoverHealth(); @@ -161,6 +194,24 @@ afterEach(() => { describe("GitHub Copilot bearer/origin snapshot atomicity", () => { for (const wire of ["chat", "responses"] as const) { + test(`${wire} revalidates selection after pacing and before physical dispatch`, async () => { + const accounts = await seedAccounts(); + beforePacingReturns = async () => { await setActiveAccount("github-copilot", accounts.b); }; + const observed = installFetch({ wire, statuses: [200], switchOn: "never" }); + const response = await handleResponses(request(wire), config(wire), { model: "", provider: "" }); + await response.text(); + expect(response.status).toBe(200); + expect(observed.dispatches).toEqual([{ origin: ACCOUNT_B_ORIGIN, authorization: bearer("copilot-access-b") }]); + }); + test(`${wire} rebuilds after a manual selection during asynchronous request building`, async () => { + const accounts = await seedAccounts(); + beforeBuildReturns = async () => { await setActiveAccount("github-copilot", accounts.b); }; + const observed = installFetch({ wire, statuses: [200], switchOn: "never" }); + const response = await handleResponses(request(wire), config(wire), { model: "", provider: "" }); + await response.text(); + expect(response.status).toBe(200); + expect(observed.dispatches).toEqual([{ origin: ACCOUNT_B_ORIGIN, authorization: bearer("copilot-access-b") }]); + }); test(`${wire} initial admission follows a newer manual selection with its matching origin`, async () => { const accounts = await seedAccounts(0); const observed = installFetch({ diff --git a/tests/server/server-google-antigravity-oauth-401-replay.test.ts b/tests/server/server-google-antigravity-oauth-401-replay.test.ts index 18c0162f9a..b1f55c238c 100644 --- a/tests/server/server-google-antigravity-oauth-401-replay.test.ts +++ b/tests/server/server-google-antigravity-oauth-401-replay.test.ts @@ -353,7 +353,7 @@ describe("Google Antigravity OAuth upstream 401 replay", () => { } }); - test.each([false, true])("401 stays pinned to rejected account A after active switches to B (newer A generation=%s)", async newerGeneration => { + test.each([false, true])("401 recovery follows the newly selected account and its project (newer A generation=%s)", async newerGeneration => { await seedOAuth(); const accountA = getAccountSet("google-antigravity")!.activeAccountId; const config = antigravityConfig(); @@ -382,17 +382,17 @@ describe("Google Antigravity OAuth upstream 401 replay", () => { const response = await postResponses(server); expect(response.status).toBe(200); expect(await response.text()).toContain("ok after google refresh"); - expect(observed.counts.refresh).toBe(newerGeneration ? 0 : 1); - expect(observed.chatAuth).toEqual(["Bearer rejected-access", newerGeneration ? "Bearer newer-access-a" : "Bearer fresh-access"]); - expect(observed.chatProjects).toEqual(["initial-project-id", newerGeneration ? "newer-project-a" : "refreshed-project-a"]); + expect(observed.counts.refresh).toBe(0); + expect(observed.chatAuth).toEqual(["Bearer rejected-access", "Bearer access-b"]); + expect(observed.chatProjects).toEqual(["initial-project-id", "project-b"]); const accounts = getAccountSet("google-antigravity")!; expect(accounts.activeAccountId).not.toBe(accountA); expect(accounts.accounts.find(account => account.id === accounts.activeAccountId)?.credential).toMatchObject({ access: "access-b", projectId: "project-b", }); expect(accounts.accounts.find(account => account.id === accountA)?.credential).toMatchObject({ - access: newerGeneration ? "newer-access-a" : "fresh-access", - projectId: newerGeneration ? "newer-project-a" : "refreshed-project-a", + access: newerGeneration ? "newer-access-a" : "rejected-access", + projectId: newerGeneration ? "newer-project-a" : "initial-project-id", }); } finally { await server.stop(true); @@ -594,45 +594,45 @@ describe("Google Antigravity OAuth upstream 401 replay", () => { } }); - test("negative project-less refresh rejects replay in native Responses passthrough", async () => { + test("project-less account is refused before dispatch in native Responses passthrough", async () => { await seedOAuth(undefined, null); saveConfig(antigravityPassthroughConfig()); const observed = installOAuthFetch([401], { refreshedProjectId: null }); const server = startServer(0); try { const response = await postResponses(server); - expect(observed.requestPaths).toEqual(["/v1/responses"]); + expect(observed.requestPaths).toEqual([]); const json = await response.json() as { error?: { code?: string; message?: string; type?: string } }; expect(response.status).toBe(401); expect(json.error?.type).toBe("authentication_error"); expect(json.error?.message).toBe(PUBLIC_OAUTH_AUTHENTICATION_ERROR); - expect(observed.counts.refresh).toBe(1); - expect(observed.chatAuth).toEqual(["Bearer rejected-access"]); + expect(observed.counts.refresh).toBe(0); + expect(observed.chatAuth).toEqual([]); } finally { await server.stop(true); } }); - test("negative project-less refresh rejects replay in generic adapter", async () => { + test("project-less account is refused before dispatch in generic adapter", async () => { await seedOAuth(undefined, null); saveConfig(antigravityConfig()); const observed = installOAuthFetch([401], { refreshedProjectId: null }); const server = startServer(0); try { const response = await postResponses(server); - expect(observed.requestPaths).toEqual(["/v1internal:generateContent"]); + expect(observed.requestPaths).toEqual([]); const json = await response.json() as { error?: { code?: string; message?: string; type?: string } }; expect(response.status).toBe(401); expect(json.error?.type).toBe("authentication_error"); expect(json.error?.message).toBe(PUBLIC_OAUTH_AUTHENTICATION_ERROR); - expect(observed.counts.refresh).toBe(1); - expect(observed.chatAuth).toEqual(["Bearer rejected-access"]); + expect(observed.counts.refresh).toBe(0); + expect(observed.chatAuth).toEqual([]); } finally { await server.stop(true); } }); - test("negative project-less refresh rejects replay in chat completions", async () => { + test("project-less account is refused before dispatch in chat completions", async () => { await seedOAuth(undefined, null); saveConfig(antigravityConfig()); const observed = installOAuthFetch([401], { refreshedProjectId: null }); @@ -643,8 +643,8 @@ describe("Google Antigravity OAuth upstream 401 replay", () => { expect(response.status).toBe(401); expect(json.error?.type).toBe("authentication_error"); expect(json.error?.message).toBe(PUBLIC_OAUTH_AUTHENTICATION_ERROR); - expect(observed.counts.refresh).toBe(1); - expect(observed.chatAuth).toEqual(["Bearer rejected-access"]); + expect(observed.counts.refresh).toBe(0); + expect(observed.chatAuth).toEqual([]); } finally { await server.stop(true); } From 4b3b2fbb75ea52b3695772fca2e698856eea3100 Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Sun, 6 Sep 2026 21:33:55 +0900 Subject: [PATCH 4/4] fix: retain selected credential across cached adapters and sidecar loops --- .../000_plan.md | 0 .../010_implementation.md | 2 + .../011_verification.md | 50 ++++ .../evidence/011_selection-before.png | Bin 0 -> 81804 bytes .../evidence/012_selection-after.png | Bin 0 -> 81852 bytes .../evidence/011_selection-before.png | Bin 101062 -> 0 bytes .../evidence/012_selection-after.png | Bin 100958 -> 0 bytes src/images/loop.ts | 7 +- src/providers/api-key-selection.ts | 53 +++++ src/server/chat-native.ts | 32 ++- src/server/responses/core.ts | 216 +++++++++++++----- src/web-search/loop.ts | 7 +- .../adapter-event-oauth-failover.test.ts | 58 ++++- .../github-copilot-account-origin.test.ts | 143 +++++++++++- tests/server/server-key-failover-e2e.test.ts | 100 ++++++++ 15 files changed, 598 insertions(+), 70 deletions(-) rename devlog/{_plan => _fin}/260906_manual_account_selection/000_plan.md (100%) rename devlog/{_plan => _fin}/260906_manual_account_selection/010_implementation.md (95%) create mode 100644 devlog/_fin/260906_manual_account_selection/011_verification.md create mode 100644 devlog/_fin/260906_manual_account_selection/evidence/011_selection-before.png create mode 100644 devlog/_fin/260906_manual_account_selection/evidence/012_selection-after.png delete mode 100644 devlog/_plan/260906_manual_account_selection/evidence/011_selection-before.png delete mode 100644 devlog/_plan/260906_manual_account_selection/evidence/012_selection-after.png diff --git a/devlog/_plan/260906_manual_account_selection/000_plan.md b/devlog/_fin/260906_manual_account_selection/000_plan.md similarity index 100% rename from devlog/_plan/260906_manual_account_selection/000_plan.md rename to devlog/_fin/260906_manual_account_selection/000_plan.md diff --git a/devlog/_plan/260906_manual_account_selection/010_implementation.md b/devlog/_fin/260906_manual_account_selection/010_implementation.md similarity index 95% rename from devlog/_plan/260906_manual_account_selection/010_implementation.md rename to devlog/_fin/260906_manual_account_selection/010_implementation.md index 324258ba50..85bf466a3c 100644 --- a/devlog/_plan/260906_manual_account_selection/010_implementation.md +++ b/devlog/_fin/260906_manual_account_selection/010_implementation.md @@ -67,3 +67,5 @@ Latest explicit verification restriction: do not run repository-wide tests. The ## C corrective review amendment Accepted independent review findings: dispatch must revalidate after pacing/build waits;401 replay must use the common selection owner; CCA project must always come from the admitted account; Anthropic initial manual choice must survive restart; selection SSE must stop on session revocation/expiry; hub relay must not apply its15s total deadline to an established selection stream; late initial quota data must survive manual selection without restoring old active flags. Main owns physical dispatch,401,CCA; backend lane owns Anthropic/API-key corrections; frontend lane owns reconnect/quota fixes. For bounded parallel C repair, the completed store worker is reassigned to SSE/management session liveness and hub-relay fixes only; no concurrent write ownership overlaps. All verification remains focused; full suite is prohibited. Draft PR3768 is open and CI runs asynchronously. + +C second-review correction: a rebuilt adapter must replace the active adapter/cache, and physical admission must be bound to the particular wire request's originating credential, not merely shared request state. Image/search model loops need the same request-specific executor. The runtime reviewer is reassigned as an exclusive repair worker for core/fetch-helpers and those two loops plus focused regression tests; main pauses edits there and independently verifies the returned delta. API-key helper/native Chat remains the backend lane; runtime worker integrates its exported helpers. Codex forward path remains unchanged. No full local tests. diff --git a/devlog/_fin/260906_manual_account_selection/011_verification.md b/devlog/_fin/260906_manual_account_selection/011_verification.md new file mode 100644 index 0000000000..ac944beeb8 --- /dev/null +++ b/devlog/_fin/260906_manual_account_selection/011_verification.md @@ -0,0 +1,50 @@ +# Verification and delivery record + +The fix uses a common committed selection for manual and automatic OAuth/API-key allocation. +A healthy manual selection has priority; reactive429 recovery remains enabled with poolOFF. +The physical request carries the binding of the adapter that built it. A stale binding is rebuilt, +and the new adapter and request cache remain authoritative for later retries and continuations. +Image/search loops share the request-specific executor. Codex routing/controller semantics are unchanged. + +## Focused evidence + +| Surface | Evidence | +| --- | --- | +| Generic OAuth | Parameterized xAI, Cursor, Kimi, Copilot, Antigravity, Nous, Kiro, Meta-Muse manual priority;36 focused checks passed | +| Store |13 failing regression cases before repair;36 store checks passed, including ABA/removal/recreation and refresh-only preservation | +| Actual dispatch | Copilot build/pacing races,413 follow-up, image/search pacing, and runTurn first-send coverage;31 checks passed with3 final boundary cases demonstrated RED→GREEN | +| API keys | Literal/env/keychain identity and newer manual selection; native Chat pacing revalidation; focused12+59 checks passed | +| Anthropic | Manual selection, guarded promotion, restart bootstrap, and always-on429; focused96-test group passed | +| Antigravity |20 OAuth401/project tests passed; a project-less account is refused before dispatch; every admitted request uses its account's project | +| Image/search | Image loops31, search61, timeout contract7 passed in separate processes | +| Management/relay |40 focused checks passed; authenticated invalidation stream, client cancellation, byte/subscriber bounds, expiration/revocation, and established SSE lifetime | +| Dashboard | Probe/passive quota hydration, stale selection guards and immediate event/reconnect behavior;38 roster+8 page checks passed | +| CI fixes | Upsert fixtures now persist like the real login flow and verify disk; GUI source binding check updated; React Doctor0.9.11 changed-file scan has0 errors/0 warnings | +| Static/privacy | Typecheck, privacy scan and diff check passed at integration checkpoints | + +Counts identify each recorded check group; they overlap and must not be added into a unique-test total. +The user prohibited repository-wide local tests. An earlier full run was interrupted with exit130; +it is not completion evidence and was not repeated. CI runs asynchronously on PR3768. +At dd5aec571, all23 applicable CI checks passed, with2 intentional skips. + +## Current browser proof + +Aside opened a local synthetic fixture rendering the real Providers component and styles at1440×900. +The current GUI moved ChoiceA→ChoiceB from a selection event without advancing the poll clock; +upstream quota-read count stayed2→2. Both screenshots were inspected by main; no Korean clipping or +incorrect active indicator was observed. The fixture and owned browser tabs were stopped afterward. +The screenshots contain only synthetic account names and masked IDs. + +- [Before](evidence/011_selection-before.png) +- [After](evidence/012_selection-after.png) + +Independent C reviews identified and drove repairs for cached adapter reuse, sidecar dispatch, +credential refresh priority, account/project pairing, restart priority, stream lifetime and quota +hydration. All identified findings were implemented and the repaired cases were exercised. + +## Delivery scope + +One PR: https://github.com/lidge-jun/opencodex/pull/3768 . Every push uses`git push --no-verify` as +explicitly requested. The maintainer explicitly authorized an administrator merge. Remote merge +state and its final SHA are verified separately from local implementation proof; no runtime service +restart or real-account configuration mutation is part of this change. diff --git a/devlog/_fin/260906_manual_account_selection/evidence/011_selection-before.png b/devlog/_fin/260906_manual_account_selection/evidence/011_selection-before.png new file mode 100644 index 0000000000000000000000000000000000000000..e48fdf946f4fbc254d15fef7103322f191f2a31a GIT binary patch literal 81804 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 Gn129q@KpH# literal 0 HcmV?d00001 diff --git a/devlog/_fin/260906_manual_account_selection/evidence/012_selection-after.png b/devlog/_fin/260906_manual_account_selection/evidence/012_selection-after.png new file mode 100644 index 0000000000000000000000000000000000000000..1806056ab73ef10f453413074f045189edd43765 GIT binary patch literal 81852 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\gkmG*~DzNgAJB$S;?ty1aAyhRYQ6p zY{bQmE8Vz@lK?eqS0m>wmE?a^VGXJ7eLyLTv^W9W5K!^J_Seh6#f_{tST*n!H#UP& z0)2+gw96t(PU?)2ru3>Pf>%=Cx=8QnfS&wc)HJa6rBZ6&=wsA7!W0;_@@x^yvEjQ_lDSXHzm<#x8^;JOq(){b1VFRKS_pvAVFf~Zy-8{Y@4 zmwJl=Eo}LUztO#60lVZ|amAsUz~N=Zv5aP6VG8G=XWR$Nyfwfp>2J(d;{*ugK=#6Q zNttG8937@zhvcZ*L3q5&fbe?$D)T=21Lc#gIjfWruUrFSNwc=2H2VbH_- zeroo>u$EL_!ceY6#UDcRp^O=+TRHXPwStAC?2kqm+GDFEEn?)WSCWzb2F+ z_-~!L8`n~a<4TWadxIDESVDWf9_ndO)y34oMffc*!`2@k6WtrPXjq4_3vWPD}v1Ta_s0EJ%lVJQb~1!+Wz3Eh7E`C94n zBrx#B{n+foXQFE6Hau-3&Y}h&j_kG!51dp&6Wdb%{)ngEDCHT8Z7;O?b>z0~Bgan~ zqPlxNJsC=WKJ$-PYw?J?$$0%IUN|O0K2Uc|S^EwV)A{#=Md0ZWkiyen4ue5Cvy`Qv^X zE9|Pj@#@%cc^Ob$80sljU4V!eMh~_*ofXffPZ^$*NY3-~h~JmVAG}kNVb;}4LUPv8 Vht~wANWWZCf2yrg`s79U{{d~_5VHUP diff --git a/devlog/_plan/260906_manual_account_selection/evidence/012_selection-after.png b/devlog/_plan/260906_manual_account_selection/evidence/012_selection-after.png deleted file mode 100644 index e67e0676e6771213d81f052025bf4788c3d5fbc8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 100958 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 typeof globalThis.fetch; /** Reserve the routed provider's next request-start slot before each adapter dispatch. */ waitForRequestSlot?: (signal?: AbortSignal) => Promise; /** Raw adapter usage at the terminal event, pre wire-normalization (see bridgeToResponsesSSE onUsage). */ @@ -499,6 +501,7 @@ export async function runWithImageBridge(deps: ImageBridgeDeps): Promise key.toLowerCase() === header.toLowerCase()); + const value = Object.entries(runtime.headers ?? {}).find(([key]) => key.toLowerCase() === header.toLowerCase())?.[1]; + if (!configured && value !== undefined) headers[header] = value; + } + const fetch = (current as OcxProviderTransport).fetch ?? runtime.fetch; + return resolveProviderTransport(name, { + ...current, + ...(Object.keys(headers).length ? { headers } : {}), + ...(fetch ? { fetch } : {}), + }); +} + type SelectionMutation = { changed: boolean; value: T; selectionChanged?: boolean }; export type ProviderApiKeyCommit = | { status: "committed"; provider: OcxProviderConfig; value: T } diff --git a/src/server/chat-native.ts b/src/server/chat-native.ts index 3c44a66628..30ff39ff44 100644 --- a/src/server/chat-native.ts +++ b/src/server/chat-native.ts @@ -37,6 +37,8 @@ import { transientRetryPolicyFor, } from "../providers/key-failover"; import { fastPolicyForModel } from "../providers/service-tier"; +import { providerApiKeySelectionIsCurrent, resolveCurrentProviderApiKeyTransport } from "../providers/api-key-selection"; +import type { OcxProviderTransport } from "../providers/xai-transport"; import type { RouteResult } from "../router"; import type { OcxConfig, OcxProviderConfig } from "../types"; import { fetchWithHeaderTimeout, providerFetch, safeHostLabel } from "./responses/fetch-helpers"; @@ -228,7 +230,6 @@ export async function handleNativeChatCompletions(options: HandleNativeChatOptio const fetchWithPolicy = requestTransientPolicy ? fetchWithTransientRetry : fetchWithResetRetry; return await fetchWithPolicy( (transportRecovery?: UpstreamSendRecovery) => { - noteAttemptSend(attempt, logCtx.usageLogInputTokens, transportRecovery ?? recovery); return fetchWithHeaderTimeout( request.url, applyUpstreamRecoveryInit({ @@ -242,6 +243,32 @@ export async function handleNativeChatCompletions(options: HandleNativeChatOptio providerFetch(activeProvider, undefined, { providerName: route.providerName, modelId: route.modelId, + dispatchOverride: async (_input, init, execute) => { + if (!providerApiKeySelectionIsCurrent(config, route.providerName, activeProvider)) { + const current = resolveCurrentProviderApiKeyTransport(config, route.providerName, activeProvider); + if (!current || !isNativeChatRouteEligible({ ...route, provider: current }, options.chatBody)) { + throw new Error("Provider key selection is no longer available for native Chat"); + } + activeProvider = current; + activeAdapter = createOpenAIChatAdapter(current); + activeRequest.releaseBodyObservation?.(); + releaseRetainedRequest(); + activeRequest = buildActiveRequest(); + try { retainRequest(activeRequest); } + catch (error) { activeRequest.releaseBodyObservation?.(); throw error; } + } + // The retry closure may still hold a pre-pacing request. Replace its entire + // wire shape, not just Authorization, and retain transport recovery flags. + request = activeRequest; + const headers = new Headers(request.headers); + const encoding = new Headers(init.headers).get("accept-encoding"); + if (!headers.has("accept-encoding") && encoding) headers.set("accept-encoding", encoding); + if (init.signal?.aborted) throw init.signal.reason; + noteAttemptSend(attempt, logCtx.usageLogInputTokens, transportRecovery ?? recovery); + return ((activeProvider as OcxProviderTransport).fetch ?? execute)(request.url, applyUpstreamRecoveryInit({ + ...init, method: request.method, headers, body: request.body, + }, transportRecovery)); + }, }), ); }, @@ -307,6 +334,9 @@ export async function handleNativeChatCompletions(options: HandleNativeChatOptio cleanupAbort(); upstream.abort(); if (req.signal.aborted) return fail(499, "Client cancelled request", "client_cancelled"); + if (isTranslatorBudgetExceededError(error)) { + return fail(413, "request translation buffer exceeded the safe limit", "request_too_large", "translation_buffer_limit"); + } return fail(502, error instanceof Error ? error.message : String(error), "server_error"); } releaseRetainedRequest(); diff --git a/src/server/responses/core.ts b/src/server/responses/core.ts index e563ec5c4a..debba5c707 100644 --- a/src/server/responses/core.ts +++ b/src/server/responses/core.ts @@ -242,6 +242,7 @@ import { type InboundWire, } from "../../providers/registry"; import type { AdapterRequest, ProviderAdapter } from "../../adapters/base"; +import { providerApiKeySelectionIsCurrent, resolveCurrentProviderApiKeyTransport } from "../../providers/api-key-selection"; import { hasKeyPoolFailover, rateLimitRetryDelayMs, @@ -3677,6 +3678,23 @@ async function handleResponsesInner( let oauthSelection = route.provider.authMode === "oauth" ? captureOAuthAccountSelection(route.providerName) : null; let servingOAuthSnapshot: OAuthAccessSnapshot | undefined; + // These owners also serve early passthrough and sidecar sends. A dispatch-time + // rebuild must update every later builder, without entering a later block's TDZ. + let adapter: ProviderAdapter; + let activeAdapter: ProviderAdapter; + let runTurnAdapter: ProviderAdapter; + let sameTargetRequest: AdapterRequest | undefined; + let sameTargetParsed: OcxParsedRequest | undefined; + let sameTargetToken = 0; + let transportToken = 0; + let imageTierBias = 0; + const invalidateSameTargetRequest = (): void => { transportToken += 1; }; + type DispatchBinding = + | { kind: "oauth"; selection: NonNullable; snapshot: OAuthAccessSnapshot } + | { kind: "api-key"; provider: OcxProviderConfig }; + const requestBindings = new WeakMap(); + const adapterBindings = new WeakMap(); + const rawRunTurns = new WeakMap>(); const commitResolvedOAuthSelection = async ( candidate: OAuthAccessSnapshot, proactive = false, @@ -3802,30 +3820,114 @@ async function handleResponsesInner( replayOAuthCredentialSnapshot = { accountId: snapshot.accountId, generation: snapshot.generation }; return true; }; - const oauthSelectionIsCurrent = (): boolean => { - if (route.provider.authMode !== "oauth") return true; - if (!oauthSelection || !servingOAuthSnapshot) return false; + const selectionIsCurrent = (binding: DispatchBinding | undefined): boolean => { + if (route.provider.authMode === "forward") return true; + if (!binding) return false; + if (binding.kind === "api-key") return providerApiKeySelectionIsCurrent(config, route.providerName, binding.provider); const selected = captureOAuthAccountSelection(route.providerName); - const row = getAccountCredentialWithStatus(route.providerName, servingOAuthSnapshot.accountId); - return selected?.accountId === oauthSelection.accountId && selected?.revision === oauthSelection.revision + const row = getAccountCredentialWithStatus(route.providerName, binding.snapshot.accountId); + return selected?.accountId === binding.selection.accountId && selected?.revision === binding.selection.revision && !!row && !row.needsReauth && row.credential.expires > Date.now() - && credentialGeneration(row.credential) === servingOAuthSnapshot.generation; + && credentialGeneration(row.credential) === binding.snapshot.generation; + }; + const resolveSelectionAdapter = (provider: OcxProviderConfig, retention = config.cacheRetention): ProviderAdapter => { + const resolved = resolveAdapter(provider, retention); + if (route.provider.authMode === "forward") return resolved; + const binding: DispatchBinding | undefined = route.provider.authMode === "oauth" + ? oauthSelection && servingOAuthSnapshot + ? { kind: "oauth", selection: { ...oauthSelection }, snapshot: servingOAuthSnapshot } + : undefined + : { kind: "api-key", provider: { ...route.provider } }; + if (binding) adapterBindings.set(resolved, binding); + const build = resolved.buildRequest.bind(resolved); + resolved.buildRequest = async (requestParsed, incoming) => { + const request = await build(requestParsed, incoming); + // Capture at adapter creation, never from mutable serving state after an await. + if (binding) requestBindings.set(request, binding); + return request; + }; + if (resolved.runTurn) { + rawRunTurns.set(resolved, resolved.runTurn.bind(resolved)); + resolved.runTurn = (requestParsed, incoming, emit) => runSelectedTurn(resolved, requestParsed, incoming, emit); + } + return resolved; + }; + const refreshDispatchAdapter = async (requestParsed: OcxParsedRequest): Promise => { + if (route.provider.authMode === "oauth") { + if (!servingOAuthSnapshot || !await applyFailoverSnapshot(servingOAuthSnapshot, requestParsed)) { + throw new Error("OAuth account selection changed before dispatch"); + } + } else { + const current = resolveCurrentProviderApiKeyTransport(config, route.providerName, route.provider); + if (!current) throw new Error("API key selection is unavailable before dispatch"); + route.provider = current; + } + adapter = activeAdapter = runTurnAdapter = resolveSelectionAdapter( + resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire), + ); + invalidateSameTargetRequest(); + return adapter; + }; + const refreshRunTurnAdapter = async (requestParsed: OcxParsedRequest): Promise => { + requestParsed._cursorIdentityScope = undefined; + requestParsed._cursorConversationId = undefined; + if (requestParsed._providerContinuation?.cursor) { + const { cursor: _oldCursor, ...rest } = requestParsed._providerContinuation; + requestParsed._providerContinuation = rest; + } + return refreshDispatchAdapter(requestParsed); + }; + const runSelectedTurn = async ( + selectedAdapter: ProviderAdapter, + ...[requestParsed, incoming, emit]: Parameters> + ): Promise => { + for (let attempt = 0; attempt < 3; attempt++) { + if (!selectionIsCurrent(adapterBindings.get(selectedAdapter))) selectedAdapter = await refreshRunTurnAdapter(requestParsed); + const binding = adapterBindings.get(selectedAdapter); + const run = rawRunTurns.get(selectedAdapter); + if (!run) throw new Error("Selected provider no longer supports this turn transport"); + let sent = false; + let refused = false; + // Both main and image-loop callers already acquired the initial pacing slot. + // Subsequent physical messages retain this adapter/credential and are paced normally. + const fetch = providerFetch(route.provider, options.codexWsRuntimeIdentity, { + providerName: route.providerName, modelId: route.modelId, pacingSlotAcquired: true, + beforeDispatch: () => { + if (sent) return; + if (!selectionIsCurrent(binding)) { + refused = true; + throw new Error("Account selection changed before the first turn dispatch"); + } + sent = true; + }, + }); + try { + await run(requestParsed, { ...incoming, providerFetch: fetch }, event => { if (!refused) emit(event); }); + } catch (error) { + if (!refused) throw error; + } + if (!refused) return; + // The adapter may map the guard's exception to an error event. Neither that + // event nor a refused send may escape before retrying the newly selected account. + selectedAdapter = await refreshRunTurnAdapter(requestParsed); + } + throw new Error("Account selection changed repeatedly before turn dispatch"); }; const oauthDispatch = (wireRequest: AdapterRequest, requestParsed = parsed): ProviderFetchOptions["dispatchOverride"] => { - if (route.provider.authMode !== "oauth") return undefined; + if (route.provider.authMode === "forward") return undefined; return async (input, init, execute) => { let destination = input; let dispatchInit = init; for (let attempt = 0; attempt < 3; attempt++) { - if (oauthSelectionIsCurrent()) { + if (selectionIsCurrent(requestBindings.get(wireRequest))) { const fetchImpl = (route.provider as OcxProviderConfig & { fetch?: typeof globalThis.fetch }).fetch ?? execute; return fetchImpl(destination, dispatchInit); } - if (!servingOAuthSnapshot || !await applyFailoverSnapshot(servingOAuthSnapshot, requestParsed)) { - throw new Error("OAuth account selection changed before dispatch"); - } - const nextAdapter = resolveAdapter(resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire), config.cacheRetention); - const rebuilt = await nextAdapter.buildRequest(requestParsed, { headers: selectedForwardHeaders, translatorBudget }); + const nextAdapter = await refreshDispatchAdapter(requestParsed); + const rebuilt = await nextAdapter.buildRequest(requestParsed, { + headers: selectedForwardHeaders, translatorBudget, + ...(imageTierBias > 0 ? { imageTierBias } : {}), + }); const bodySize = checkOutboundBodySize(rebuilt.body, config.maxUpstreamBodyBytes); if (!bodySize.admitted) { rebuilt.releaseBodyObservation?.(); @@ -3836,6 +3938,12 @@ async function handleResponsesInner( for (const [name, value] of Object.entries(rebuilt.headers)) headers.set(name, value); wireRequest.releaseBodyObservation?.(); Object.assign(wireRequest, rebuilt); + const binding = requestBindings.get(rebuilt); + if (binding) requestBindings.set(wireRequest, binding); + else requestBindings.delete(wireRequest); + sameTargetRequest = wireRequest; + sameTargetParsed = requestParsed; + sameTargetToken = transportToken; destination = rebuilt.url; dispatchInit = { ...dispatchInit, method: rebuilt.method, headers, body: rebuilt.body }; bindRouteReasoningReplayScope({ parsed: requestParsed, providerName: route.providerName, provider: route.provider, @@ -4005,7 +4113,7 @@ async function handleResponsesInner( logCtx.provider = route.providerName; delete logCtx.accountLogLabel; } - const adapter = resolveAdapter(adapterProvider, config.cacheRetention); + adapter = resolveSelectionAdapter(adapterProvider, config.cacheRetention); bindRouteReasoningReplayScope({ parsed, providerName: route.providerName, @@ -4034,7 +4142,7 @@ async function handleResponsesInner( } sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, adapter.name, logCtx.accountLogLabel); recordAttemptCredentialSource(logCtx.activeAttempt, route.providerName, adapterProvider, adapter.name); - let runTurnAdapter = adapter; + runTurnAdapter = adapter; if (adapter.runTurn) { recordAdapterTierMetadata(logCtx, adapter.tierLogForRunTurn?.(parsed)); } @@ -4666,7 +4774,7 @@ async function handleResponsesInner( const rebuildAndRefetch = async ( recovery: AttemptRecoveryKind, ): Promise => { - const retryAdapter = resolveAdapter( + const retryAdapter = resolveSelectionAdapter( resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire), config.cacheRetention, ); @@ -4772,7 +4880,7 @@ async function handleResponsesInner( authCtx = replay.authCtx; route.provider = replay.provider; selectedForwardHeaders = replay.headers; - const replayAdapter = resolveAdapter( + const replayAdapter = resolveSelectionAdapter( resolveWireProtocolOverride(route.providerName, route.modelId, replay.provider, inboundWire), config.cacheRetention, ); @@ -4889,7 +4997,7 @@ async function handleResponsesInner( : undefined, ); route.provider = refreshedProvider; - const refreshedAdapter = resolveAdapter( + const refreshedAdapter = resolveSelectionAdapter( resolveWireProtocolOverride(route.providerName, route.modelId, refreshedProvider, inboundWire), config.cacheRetention, ); @@ -5898,7 +6006,7 @@ async function handleResponsesInner( // credential. The 429 is terminal for this sidecar turn. return null; } - const rotatedAdapter = resolveAdapter( + const rotatedAdapter = resolveSelectionAdapter( resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire), config.cacheRetention, ); @@ -5982,6 +6090,13 @@ async function handleResponsesInner( stallTimeoutSec: config.stallTimeoutSec, waitForRequestSlot: imageProviderFetch.waitForPacing, fetchImpl: imageProviderFetch.unpacedFetch ?? imageProviderFetch, + fetchForRequest: (request, iterParsed) => { + const fetch = providerFetch(route.provider, options.codexWsRuntimeIdentity, { + dispatchOverride: oauthDispatch(request, iterParsed), + providerName: route.providerName, modelId: route.modelId, + }); + return fetch.unpacedFetch ?? fetch; + }, onRequestBuilt: request => { recordAdapterReasoning(logCtx, request); recordAdapterTier(logCtx, request); @@ -6042,6 +6157,10 @@ async function handleResponsesInner( })(input, init)) as typeof globalThis.fetch; const wsResponse = await runWithWebSearch({ parsed, adapter, + fetchForRequest: (request, iterParsed) => providerFetch(route.provider, options.codexWsRuntimeIdentity, { + dispatchOverride: oauthDispatch(request, iterParsed), + providerName: route.providerName, modelId: route.modelId, + }), incomingMeta: { headers: selectedForwardHeaders, abortSignal: options.abortSignal, @@ -6117,20 +6236,9 @@ async function handleResponsesInner( const queue = createAdapterEventQueue({ onBacklogExceeded: () => runTurnAbort.abort(), }); - let selectionDispatchRefused = false; - let selectionDispatchRetries = 0; const refreshRunTurnSelection = async (): Promise => { - if (oauthSelectionIsCurrent()) return; - if (!servingOAuthSnapshot || !await applyFailoverSnapshot(servingOAuthSnapshot)) { - throw new Error("OAuth account selection changed before dispatch"); - } - parsed._cursorIdentityScope = undefined; - parsed._cursorConversationId = undefined; - if (parsed._providerContinuation?.cursor) { - const { cursor: _oldCursor, ...rest } = parsed._providerContinuation; - parsed._providerContinuation = rest; - } - runTurnAdapter = resolveAdapter(resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire), config.cacheRetention); + if (selectionIsCurrent(adapterBindings.get(runTurnAdapter))) return; + await refreshRunTurnAdapter(parsed); bindRouteReasoningReplayScope({ parsed, providerName: route.providerName, provider: route.provider, adapterName: runTurnAdapter.name, oauthCredentialSnapshot: replayOAuthCredentialSnapshot }); sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, runTurnAdapter.name, logCtx.accountLogLabel); @@ -6160,7 +6268,6 @@ async function handleResponsesInner( } await refreshRunTurnSelection(); noteAttemptSend(logCtx.activeAttempt, logCtx.usageLogInputTokens, recovery); - let firstPhysicalSend = true; const runTurnProviderFetch = providerFetch( route.provider, options.codexWsRuntimeIdentity, @@ -6171,14 +6278,6 @@ async function handleResponsesInner( // Cursor HTTP/1.1 consumes it for RunSSE; every BidiAppend and redial then waits on // the same provider queue through this stateful wrapper. pacingSlotAcquired: true, - beforeDispatch: () => { - if (!firstPhysicalSend) return; // A started multi-message turn keeps its credential. - if (!oauthSelectionIsCurrent()) { - selectionDispatchRefused = true; - throw new Error("OAuth account selection changed before dispatch"); - } - firstPhysicalSend = false; - }, }, ); await runTurnAdapter.runTurn?.( @@ -6217,11 +6316,6 @@ async function handleResponsesInner( const rotateRunTurnAdapterOnPreflight429 = async ( error: Extract, ): Promise => { - if (selectionDispatchRefused && selectionDispatchRetries < 3) { - selectionDispatchRefused = false; - selectionDispatchRetries += 1; - try { await refreshRunTurnSelection(); return true; } catch { return false; } - } const status = error.status ?? adapterFailureFromMessage(error.message).httpStatus; if ( status !== 429 @@ -6255,7 +6349,7 @@ async function handleResponsesInner( route.provider, inboundWire, ); - const rotatedAdapter = resolveAdapter(rotatedProvider, config.cacheRetention); + const rotatedAdapter = resolveSelectionAdapter(rotatedProvider, config.cacheRetention); if (!rotatedAdapter.runTurn) return false; runTurnAdapter = rotatedAdapter; bindRouteReasoningReplayScope({ @@ -6460,7 +6554,7 @@ async function handleResponsesInner( const stallTimeoutMs = typeof config.stallTimeoutSec === "number" && Number.isFinite(config.stallTimeoutSec) && config.stallTimeoutSec > 0 ? Math.floor(config.stallTimeoutSec * 1000) : 300_000; - let activeAdapter = adapter; + activeAdapter = adapter; // One immutable, body-safe outbound request per same-target sequence (URL, serialized body, // auth headers, generated compat headers). Same-target 429 replays reuse it verbatim; the @@ -6559,16 +6653,15 @@ async function handleResponsesInner( // Capture it in a const so the fetch callbacks read a narrowed, immutable value // (TypeScript drops narrowing for a `let` captured by a nested function). const builtInitialRequest = initialRequest; - let sameTargetRequest: AdapterRequest | undefined = builtInitialRequest; - let sameTargetParsed: OcxParsedRequest | undefined = parsed; - let sameTargetToken = 0; - let transportToken = 0; + sameTargetRequest = builtInitialRequest; + sameTargetParsed = parsed; + sameTargetToken = transportToken; /** * Invalidate the same-target request cache. Every credential/adapter/parsed mutation MUST * go through here: the cache keys on `parsed` REFERENCE identity, so an in-place mutation * is invisible to it and a missed bump would replay a request built with a stale key. */ - const invalidateSameTargetRequest = (): void => { transportToken += 1; }; + let upstreamResponse: Response; try { if (activeAdapter.fetchResponse) { @@ -6637,7 +6730,6 @@ async function handleResponsesInner( let rateLimitRetries = 0; // Shared with the terminal-guard continuation below: an image-tier reduction that let the // main request clear a 413 must not be forgotten on the very next continuation build. - let imageTierBias = 0; if (!upstreamResponse.ok) { // Recovery loop: multi-key 429 failover + at most ONE opaque-state rebuild and ONE // anthropic 413 tightened retry @@ -6794,7 +6886,7 @@ async function handleResponsesInner( ); route.provider = refreshedProvider; invalidateSameTargetRequest(); - activeAdapter = resolveAdapter( + activeAdapter = resolveSelectionAdapter( resolveWireProtocolOverride(route.providerName, route.modelId, refreshedProvider, inboundWire), config.cacheRetention, ); @@ -6827,7 +6919,7 @@ async function handleResponsesInner( try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already consumed/closed */ } route.provider = rotated; invalidateSameTargetRequest(); - activeAdapter = resolveAdapter( + activeAdapter = resolveSelectionAdapter( resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire), config.cacheRetention, ); @@ -6897,7 +6989,7 @@ async function handleResponsesInner( try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already consumed/closed */ } route.provider = rotated; invalidateSameTargetRequest(); - activeAdapter = resolveAdapter( + activeAdapter = resolveSelectionAdapter( resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire), config.cacheRetention, ); @@ -6935,7 +7027,7 @@ async function handleResponsesInner( route.provider = { ...route.provider, apiKey: admitted.accessToken }; invalidateSameTargetRequest(); logCtx.provider = formatAnthropicProviderForLog("anthropic", admitted.accountId, config); - activeAdapter = resolveAdapter( + activeAdapter = resolveSelectionAdapter( resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire), config.cacheRetention, ); @@ -6978,7 +7070,7 @@ async function handleResponsesInner( genericFailovers += 1; if (!await applyFailoverSnapshot(snapshot)) break; invalidateSameTargetRequest(); - activeAdapter = resolveAdapter( + activeAdapter = resolveSelectionAdapter( resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire), config.cacheRetention, ); @@ -7305,7 +7397,7 @@ async function handleResponsesInner( try { void response.body?.cancel().catch(() => {}); } catch { /* already closed */ } route.provider = rotated; invalidateSameTargetRequest(); - activeAdapter = resolveAdapter( + activeAdapter = resolveSelectionAdapter( resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire), config.cacheRetention, ); @@ -7348,7 +7440,7 @@ async function handleResponsesInner( route.provider = { ...route.provider, apiKey: admitted.accessToken }; invalidateSameTargetRequest(); logCtx.provider = formatAnthropicProviderForLog("anthropic", admitted.accountId, config); - activeAdapter = resolveAdapter( + activeAdapter = resolveSelectionAdapter( resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire), config.cacheRetention, ); @@ -7389,7 +7481,7 @@ async function handleResponsesInner( genericFailovers += 1; if (await applyFailoverSnapshot(snapshot, nextParsed)) { invalidateSameTargetRequest(); - activeAdapter = resolveAdapter( + activeAdapter = resolveSelectionAdapter( resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire), config.cacheRetention, ); diff --git a/src/web-search/loop.ts b/src/web-search/loop.ts index 682e482eea..3a2c5e99b4 100644 --- a/src/web-search/loop.ts +++ b/src/web-search/loop.ts @@ -301,6 +301,8 @@ export interface WebSearchLoopDeps { onUsage?: (usage: OcxUsage | undefined) => void; /** Observe the exact adapter request selected for each routed-model iteration. */ onRequestBuilt?: (request: AdapterRequest) => void; + /** Request-scoped executor retains the core's selection binding across loop retries. */ + fetchForRequest?: (request: AdapterRequest, parsed: OcxParsedRequest) => typeof globalThis.fetch; /** Called before each routed-model dispatch in the loop, for attempt telemetry. Same-target 429 replays pass the `rate-limit-429` recovery kind. */ onAttemptSend?: (recovery?: AttemptRecoveryKind) => void; /** @@ -440,6 +442,7 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise = []; /** Set by the delivery test: an attempt that emits, then blocks before completing the turn. */ let slowAttempt: ((emit: (event: AdapterEvent) => void) => Promise) | undefined; +let beforePhysicalSend: (() => Promise) | undefined; +let physicalSends = 0; +const originalFetch = globalThis.fetch; function fixtureAdapter(provider: OcxProviderConfig): ProviderAdapter { return { @@ -23,10 +26,18 @@ function fixtureAdapter(provider: OcxProviderConfig): ProviderAdapter { async *parseStream() { yield { type: "error", message: "fixture uses runTurn" } as AdapterEvent; }, - async runTurn(_parsed, _incoming, emit) { + async runTurn(_parsed, incoming, emit) { const index = attemptKeys.length; attemptKeys.push(provider.apiKey ?? ""); attemptProjects.push(provider.project); + const gate = beforePhysicalSend; + beforePhysicalSend = undefined; + await gate?.(); + for (let send = 0; send < physicalSends; send++) { + await incoming.providerFetch!(provider.baseUrl, { + method: "POST", headers: { Authorization: `Bearer ${provider.apiKey}` }, body: "{}", + }); + } if (slowAttempt) return await slowAttempt(emit); for (const event of attempts[index] ?? []) emit(event); }, @@ -92,9 +103,12 @@ beforeEach(() => { attemptKeys = []; attemptProjects = []; slowAttempt = undefined; + beforePhysicalSend = undefined; + physicalSends = 0; }); afterEach(() => { + globalThis.fetch = originalFetch; clearGenericFailoverHealth(); if (originalHome === undefined) delete process.env.OPENCODEX_HOME; else process.env.OPENCODEX_HOME = originalHome; @@ -102,6 +116,48 @@ afterEach(() => { }); describe("#2568 adapter-event OAuth failover", () => { + test.each([false, true])("runTurn first physical send follows a changed selection (image loop=%s)", async imageLoop => { + await seedAccounts(2); + const accounts = getAccountSet("cursor")!.accounts; + beforePhysicalSend = async () => { await setActiveAccount("cursor", accounts[0]!.id); }; + physicalSends = 1; + slowAttempt = async emit => { emit({ type: "text_delta", text: "selected answer" }); emit({ type: "done" }); }; + const sent: string[] = []; + globalThis.fetch = (async (_input, init) => { + sent.push(new Headers(init?.headers).get("authorization") ?? ""); + return new Response("{}"); + }) as typeof fetch; + const cfg = config(false); + if (imageLoop) { + cfg.images = { bridgeEnabled: true }; + cfg.providers.xai = { adapter: "openai-chat", baseUrl: "https://api.x.ai/v1", authMode: "key", apiKey: "synthetic-image-key" }; + } + const req = imageLoop ? new Request("http://localhost/v1/responses", { + method: "POST", headers: { "content-type": "application/json" }, + body: JSON.stringify({ model: "cursor/model", input: "answer", stream: true, tools: [{ type: "image_generation" }] }), + }) : request(true); + const response = await handleResponses(req, cfg, { model: "", provider: "" }); + expect(await response.text()).toContain("selected answer"); + expect(sent).toEqual(["Bearer cursor-access-0"]); + }); + + test("an already started multi-message turn keeps its original credential", async () => { + await seedAccounts(2); + const accounts = getAccountSet("cursor")!.accounts; + physicalSends = 2; + slowAttempt = async emit => { emit({ type: "text_delta", text: "same turn" }); emit({ type: "done" }); }; + const sent: string[] = []; + globalThis.fetch = (async (_input, init) => { + sent.push(new Headers(init?.headers).get("authorization") ?? ""); + if (sent.length === 1) await setActiveAccount("cursor", accounts[0]!.id); + return new Response("{}"); + }) as typeof fetch; + const response = await handleResponses(request(false), config(false), { model: "", provider: "" }); + expect(await response.text()).toContain("same turn"); + expect(sent).toEqual(["Bearer cursor-access-1", "Bearer cursor-access-1"]); + expect(getCredential("cursor")?.access).toBe("cursor-access-0"); + }); + test("every CCA request pairs the persisted active account with its own project", async () => { for (const id of ["a", "b"]) await saveCredential("google-antigravity", { access: `ga-access-${id}`, refresh: `ga-refresh-${id}`, expires: Date.now() + 3_600_000, diff --git a/tests/providers/github-copilot/github-copilot-account-origin.test.ts b/tests/providers/github-copilot/github-copilot-account-origin.test.ts index 1c0f614bd2..4da89c816e 100644 --- a/tests/providers/github-copilot/github-copilot-account-origin.test.ts +++ b/tests/providers/github-copilot/github-copilot-account-origin.test.ts @@ -5,6 +5,8 @@ import { join } from "node:path"; import { clearGenericFailoverHealth } from "../../../src/oauth/generic-account-failover"; import { getAccountSet, saveCredential, setActiveAccount } from "../../../src/oauth/store"; import { handleResponses } from "../../../src/server/responses"; +import { saveConfig } from "../../../src/config"; +import { setActiveProviderApiKey } from "../../../src/providers/api-keys"; import type { OcxConfig } from "../../../src/types"; import { removeTreeWithRetry } from "../../helpers/remove-tree"; @@ -71,12 +73,12 @@ function config(wire: Wire): OcxConfig { } as OcxConfig; } -function request(wire: Wire): Request { +function request(wire: Wire, extra: Record = {}): Request { const model = wire === "chat" ? "gpt-4o" : "gpt-5.4"; return new Request("http://localhost/v1/responses", { method: "POST", headers: { "content-type": "application/json" }, - body: JSON.stringify({ model: `github-copilot/${model}`, input: "hello", stream: false }), + body: JSON.stringify({ model: `github-copilot/${model}`, input: "hello", stream: false, ...extra }), }); } @@ -135,6 +137,7 @@ function installFetch(options: { statuses: number[]; switchToAccountId?: string; switchOn: "refresh" | "first-dispatch" | "never"; + emptyFirst?: boolean; }): { dispatches: { origin: string; authorization: string }[] } { const dispatches: { origin: string; authorization: string }[] = []; let refreshSwitched = false; @@ -169,6 +172,14 @@ function installFetch(options: { headers: status === 429 ? { "retry-after": "1" } : undefined, }); } + if (options.emptyFirst && dispatches.length === 1) { + return Response.json({ choices: [{ index: 0, message: { role: "assistant", content: "" }, finish_reason: "stop" }] }); + } + if (JSON.parse(String(init?.body ?? "{}")).stream === true && options.wire === "chat") { + return new Response('data: {"choices":[{"index":0,"delta":{"content":"ok"},"finish_reason":null}]}\n\ndata: {"choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}\n\ndata: [DONE]\n\n', { + headers: { "content-type": "text/event-stream" }, + }); + } return successResponse(options.wire); } return originalFetch(input, init); @@ -193,6 +204,134 @@ afterEach(() => { }); describe("GitHub Copilot bearer/origin snapshot atomicity", () => { + test.each(["chat", "responses", "image", "web-search"] as const)("%s API-key dispatch uses a selection committed during pacing", async path => { + const cfg = config("chat"); + cfg.defaultProvider = "fixture"; + cfg.providers = { fixture: { + adapter: path === "responses" ? "openai-responses" : "openai-chat", authMode: "key", + baseUrl: "https://fixture.invalid/v1", apiKey: "synthetic-a", models: ["model"], + apiKeyPool: [{ id: "a", key: "synthetic-a" }, { id: "b", key: "synthetic-b" }], + } }; + if (path === "image") { + cfg.images = { bridgeEnabled: true }; + cfg.providers.xai = { adapter: "openai-chat", authMode: "key", apiKey: "synthetic-image-key", baseUrl: "https://api.x.ai/v1" }; + } else if (path === "web-search") cfg.webSearchSidecar = { enabled: true, backend: "exa", exaApiKey: "synthetic-search-key" }; + saveConfig(cfg); + beforePacingReturns = async () => { expect(setActiveProviderApiKey(cfg, "fixture", "b")).toBe(true); }; + const sent: string[] = []; + globalThis.fetch = (async (_input, init) => { + sent.push(new Headers(init?.headers).get("authorization") ?? ""); + if (path === "image" || path === "web-search") return new Response('data: {"choices":[{"index":0,"delta":{"content":"ok"},"finish_reason":"stop"}]}\n\ndata: [DONE]\n\n', { headers: { "content-type": "text/event-stream" } }); + return successResponse(path); + }) as typeof fetch; + const response = await handleResponses(request("chat", { + model: "fixture/model", stream: path === "image" || path === "web-search", + ...(path === "image" || path === "web-search" ? { tools: [{ type: path === "image" ? "image_generation" : "web_search" }] } : {}), + }), cfg, { model: "", provider: "" }); + expect(await response.text()).toContain("ok"); + expect(sent).toEqual(["Bearer synthetic-b"]); + }); + + test("a pacing switch to B keeps B when Anthropic rebuilds an image after 413", async () => { + for (const id of ["a", "b"]) await saveCredential("anthropic", { + access: `synthetic-anthropic-${id}`, refresh: `synthetic-refresh-${id}`, + expires: Date.now() + 3_600_000, accountId: id, + }); + const rows = getAccountSet("anthropic")!.accounts; + await setActiveAccount("anthropic", rows[0]!.id); + beforePacingReturns = async () => { await setActiveAccount("anthropic", rows[1]!.id); }; + const sent: string[] = []; + globalThis.fetch = (async (_input, init) => { + sent.push(new Headers(init?.headers).get("authorization") ?? ""); + if (sent.length === 1) return Response.json({ error: { type: "request_too_large", message: "too large" } }, { status: 413 }); + return Response.json({ id: "message-selection", type: "message", role: "assistant", + content: [{ type: "text", text: "ok" }], stop_reason: "end_turn", usage: { input_tokens: 1, output_tokens: 1 } }); + }) as typeof fetch; + const cfg = config("chat"); + cfg.providers = { anthropic: { adapter: "anthropic", authMode: "oauth", baseUrl: "https://api.anthropic.com", models: ["claude-fable-5"] } }; + cfg.defaultProvider = "anthropic"; + const response = await handleResponses(request("chat", { + model: "anthropic/claude-fable-5", + input: [{ role: "user", content: [ + { type: "input_text", text: "look" }, + { type: "input_image", image_url: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==" }, + ] }], + }), cfg, { model: "", provider: "" }); + expect(await response.text()).toContain("ok"); + expect(sent).toEqual(["Bearer synthetic-anthropic-b", "Bearer synthetic-anthropic-b"]); + }); + + test("a pacing switch to B keeps B for an empty-completion continuation", async () => { + const accounts = await seedAccounts(); + beforePacingReturns = async () => { await setActiveAccount("github-copilot", accounts.b); }; + const observed = installFetch({ wire: "chat", statuses: [200, 200], switchOn: "never", emptyFirst: true }); + const cfg = config("chat"); + cfg.emptyCompletionRetry = true; + const response = await handleResponses(request("chat"), cfg, { model: "", provider: "" }); + expect(await response.text()).toContain("ok"); + expect(observed.dispatches).toEqual([ + { origin: ACCOUNT_B_ORIGIN, authorization: bearer("copilot-access-b") }, + { origin: ACCOUNT_B_ORIGIN, authorization: bearer("copilot-access-b") }, + ]); + }); + + test.each(["image", "web-search"] as const)("%s main-model dispatch follows a manual choice made during pacing", async path => { + const accounts = await seedAccounts(); + beforePacingReturns = async () => { await setActiveAccount("github-copilot", accounts.b); }; + const observed = installFetch({ wire: "chat", statuses: [200], switchOn: "never" }); + const cfg = config("chat"); + if (path === "image") { + cfg.images = { bridgeEnabled: true }; + cfg.providers.xai = { adapter: "openai-chat", authMode: "key", apiKey: "synthetic-image-key", baseUrl: "https://api.x.ai/v1" }; + } else { + cfg.webSearchSidecar = { enabled: true, backend: "exa", exaApiKey: "synthetic-search-key" }; + } + const response = await handleResponses(request("chat", { + stream: true, tools: [{ type: path === "image" ? "image_generation" : "web_search" }], + }), cfg, { model: "", provider: "" }); + expect(await response.text()).toContain("ok"); + expect(observed.dispatches).toEqual([{ origin: ACCOUNT_B_ORIGIN, authorization: bearer("copilot-access-b") }]); + }); + + test("a cached search-loop adapter cannot bless A wire with B's current snapshot", async () => { + const accounts = await seedAccounts(); + beforePacingReturns = async () => { await setActiveAccount("github-copilot", accounts.b); }; + const sent: string[] = []; + const bodies: Array<{ messages: Array<{ role: string }> }> = []; + globalThis.fetch = (async (input, init) => { + const url = new URL(input instanceof Request ? input.url : String(input)); + if (url.hostname === "api.exa.ai") return Response.json({ results: [] }); + if (!url.hostname.endsWith(".githubcopilot.com")) throw new Error("Unexpected fixture request"); + sent.push(new Headers(init?.headers).get("authorization") ?? ""); + bodies.push(JSON.parse(String(init?.body))); + const delta = sent.length === 1 + ? { tool_calls: [{ index: 0, id: "search-1", type: "function", function: { name: "web_search", arguments: '{"query":"fixture"}' } }] } + : { content: "ok after search" }; + return new Response(`data: ${JSON.stringify({ choices: [{ index: 0, delta, finish_reason: sent.length === 1 ? "tool_calls" : "stop" }] })}\n\ndata: [DONE]\n\n`, { + headers: { "content-type": "text/event-stream" }, + }); + }) as typeof fetch; + const cfg = config("chat"); + cfg.webSearchSidecar = { enabled: true, backend: "exa", exaApiKey: "synthetic-search-key" }; + const response = await handleResponses(request("chat", { stream: true, tools: [{ type: "web_search" }] }), cfg, { model: "", provider: "" }); + expect(await response.text()).toContain("ok after search"); + expect(sent).toEqual(["Bearer copilot-access-b", "Bearer copilot-access-b"]); + expect(bodies[1]!.messages.some(message => message.role === "tool")).toBe(true); + }); + + test("a key removed during pacing is never dispatched", async () => { + const cfg = config("chat"); + cfg.defaultProvider = "fixture"; + cfg.providers = { fixture: { adapter: "openai-chat", authMode: "key", baseUrl: "https://fixture.invalid/v1", apiKey: "synthetic-a" } }; + beforePacingReturns = async () => { delete cfg.providers.fixture; }; + let sends = 0; + globalThis.fetch = (async () => { sends++; return successResponse("chat"); }) as typeof fetch; + const response = await handleResponses(request("chat", { model: "fixture/model" }), cfg, { model: "", provider: "" }); + await response.text(); + expect(response.status).not.toBe(200); + expect(sends).toBe(0); + }); + for (const wire of ["chat", "responses"] as const) { test(`${wire} revalidates selection after pacing and before physical dispatch`, async () => { const accounts = await seedAccounts(); diff --git a/tests/server/server-key-failover-e2e.test.ts b/tests/server/server-key-failover-e2e.test.ts index 66cbedd199..d23bf848dd 100644 --- a/tests/server/server-key-failover-e2e.test.ts +++ b/tests/server/server-key-failover-e2e.test.ts @@ -10,6 +10,11 @@ import { startServer } from "../../src/server"; import type { OcxConfig } from "../../src/types"; import { installIsolatedCodexHome, type IsolatedCodexHome } from "../helpers/isolated-codex-home"; import { removeTreeWithRetry } from "../helpers/remove-tree"; +import { managementFetch } from "../helpers/management-auth"; +import { resetProviderRequestPacingForTest, setProviderRequestPacingRuntimeForTest, waitForProviderRequestSlot } from "../../src/providers/request-pacing"; +import { providerApiKeySelectionIsCurrent, resolveCurrentProviderApiKeyTransport } from "../../src/providers/api-key-selection"; +import { routedProviderConfig } from "../../src/router"; +import type { OcxProviderTransport } from "../../src/providers/xai-transport"; let testDir = ""; let previousHome: string | undefined; @@ -38,6 +43,101 @@ afterEach(() => { }); describe("server 429 key failover (end-to-end)", () => { + test("physical key selection rejects disabled, removed, and changed-auth providers", () => { + const provider = { adapter: "openai-chat", baseUrl: "https://example.test/v1", authMode: "key", apiKey: "synthetic-first" } as const; + const config = { providers: { current: { ...provider } } } as unknown as OcxConfig; + const routed = routedProviderConfig("current", config.providers.current); + expect(providerApiKeySelectionIsCurrent(config, "current", routed)).toBe(true); + // A model-level wire override does not change which key was selected. + expect(providerApiKeySelectionIsCurrent(config, "current", { ...routed, adapter: "openai-responses" })).toBe(true); + for (const replacement of [{ ...provider, disabled: true }, { ...provider, authMode: "oauth" }, { ...provider, apiKey: undefined }]) { + config.providers.current = replacement as OcxConfig["providers"][string]; + expect(providerApiKeySelectionIsCurrent(config, "current", routed)).toBe(false); + expect(resolveCurrentProviderApiKeyTransport(config, "current", routed)).toBeNull(); + } + delete config.providers.current; + expect(providerApiKeySelectionIsCurrent(config, "current", routed)).toBe(false); + expect(resolveCurrentProviderApiKeyTransport(config, "current", routed)).toBeNull(); + }); + + test("physical transport refresh keeps its executor and affinity but takes current static headers", () => { + const config = { providers: { current: { + adapter: "openai-chat", baseUrl: "https://example.test/v1", authMode: "key", apiKey: "synthetic-first", + headers: { "x-old-static": "old" }, apiKeySelectionRevision: "first-revision", + } } } as unknown as OcxConfig; + const executor = (async () => Response.json({})) as typeof fetch; + const routed: OcxProviderTransport = { + ...routedProviderConfig("current", config.providers.current), fetch: executor, + headers: { "x-old-static": "old", "x-opencode-session": "runtime-session" }, + }; + config.providers.current = { ...config.providers.current, apiKey: "synthetic-second", + apiKeySelectionRevision: "second-revision", headers: { "x-new-static": "new" }, + }; + expect(providerApiKeySelectionIsCurrent(config, "current", routed)).toBe(false); + const current = resolveCurrentProviderApiKeyTransport(config, "current", routed) as OcxProviderTransport; + expect(current.apiKey).toBe("synthetic-second"); + expect(current.fetch).toBe(executor); + expect(current.headers).toEqual({ "x-new-static": "new", "x-opencode-session": "runtime-session" }); + expect(providerApiKeySelectionIsCurrent(config, "current", current)).toBe(true); + }); + + test("native Chat rebuilds a queued request after a manual key selection during pacing", async () => { + let now = 0; + let resumePacing: (() => void) | undefined; + const queued = Promise.withResolvers(); + setProviderRequestPacingRuntimeForTest({ + now: () => now, + setTimer(callback, delayMs) { + resumePacing = () => { now += delayMs; callback(); }; + queued.resolve(); + return callback; + }, + clearTimer() {}, + enqueueMicrotask: queueMicrotask, + }); + const seen: Headers[] = []; + upstream = Bun.serve({ hostname: "127.0.0.1", port: 0, fetch(req) { + seen.push(new Headers(req.headers)); + return Response.json({ id: "chatcmpl-paced", object: "chat.completion", + choices: [{ index: 0, message: { role: "assistant", content: "current selection" }, finish_reason: "stop" }], + }); + } }); + const config = { port: 0, hostname: "127.0.0.1", defaultProvider: "paced", providers: { paced: { + adapter: "openai-chat", baseUrl: `http://127.0.0.1:${upstream.port}/v1`, allowPrivateNetwork: true, + authMode: "key", apiKey: "synthetic-first", headers: { "x-static-test": "retained" }, + apiKeyPool: [{ id: "first", key: "synthetic-first" }, { id: "second", key: "synthetic-second" }], + requestPacing: { enabled: true, minIntervalMs: 100 }, + } } } as OcxConfig; + saveConfig(config); + const server = startServer(0); + const abort = new AbortController(); + try { + await waitForProviderRequestSlot("paced", config.providers.paced); + const pending = fetch(new URL("/v1/chat/completions", server.url), { + method: "POST", headers: { "content-type": "application/json" }, signal: abort.signal, + body: JSON.stringify({ model: "paced/test", stream: false, messages: [{ role: "user", content: "hello" }] }), + }); + await queued.promise; + expect(seen).toHaveLength(0); + const selected = await managementFetch(new URL("/api/providers/keys/active", server.url), { + method: "PUT", headers: { "content-type": "application/json" }, + body: JSON.stringify({ name: "paced", id: "second" }), + }); + expect(selected.status).toBe(200); + await selected.text(); + resumePacing!(); + const response = await pending; + expect(response.status).toBe(200); + expect(await response.text()).toContain("current selection"); + expect(seen.map(headers => headers.get("authorization"))).toEqual(["Bearer synthetic-second"]); + expect(seen[0]!.get("x-static-test")).toBe("retained"); + } finally { + abort.abort(); + await server.stop(true); + resetProviderRequestPacingForTest(); + } + }); + test.each(["responses", "chat/completions"])("%s carries the configured env-key identity through 429 recovery", async inbound => { const seen: string[] = []; process.env.OCX_SELECTION_E2E_KEY = "synthetic-env-first";