v2.3.0-beta.2
Pre-release
Pre-release
Runtime Rotation
Bugfixes
- Fixed the sequential drain-first pointer advancing during a within-request fallback in legacy routing mode.
persistRuntimeActiveAccountcalledmarkSwitchedLockedunconditionally whenroutingMutex !== "enabled", including whenschedulingStrategy: "sequential"was set; a transient token-bucket fallback to a secondary account would permanently move the primary and break the #509 drain-first invariant. The sameschedulingStrategy !== "sequential"guard already present in the enabled-mutex branch now applies to the legacy branch too. - Fixed
ensureFreshAccessTokenforwarding an expired access token whencommitRefreshedAuthOncereturnednull(account not found in storage after persist). The fallback usedupdatedAccount.access, which was the original stale token, causing a downstream 401 and incorrectly triggering invalidation-cooldown logic. The function now usesrefreshResult.access(the just-issued token) on the null-commit path while preserving the committed account's stored token on the success path (required for the dedup-commit case where two concurrent callers share one commit).
Storage
Bugfixes
- Fixed
normalizeFlaggedStoragesilently droppingworkspaces,currentWorkspaceIndex,accessToken, andexpiresAtfrom flagged accounts. The function built a hardcoded field list that omitted these fields, defeating the Zod schema's.passthrough()intent; multi-workspace accounts permanently lost their workspace list and active-workspace index after a flag→restore round-trip. All four fields are now preserved. - Fixed
refreshQuotaCacheForMenuwiping all other accounts' quota data when a transient disk failure occurred during cache reload. The rebase-merge block had a deadcatchbecauseloadQuotaCache()never throws — it returns empty maps on any read failure. When the persisted cache came back empty, only this run's probed entries were written back, discarding every other account's still-valid quota data. The empty-load case is now detected explicitly and falls back to the full in-memory snapshot.
Security
Hardening
- All atomic write helpers (
storage.ts,recovery/storage.ts,quota-cache.ts,unified-settings.ts, and twelve other call sites) now usecrypto.randomBytesfor staging-path nonces instead ofMath.random(), preventing a local attacker who can observe one staged path from predicting the next one (#517). - All GitHub Actions workflow steps are pinned to exact commit SHAs rather than floating version tags, hardening against supply-chain tag drift (#519).
- Upstream response headers matching
x-codex-multi-auth-account-*are now blocked by prefix match rather than an allowlist; a future header added under that namespace is blocked by default rather than leaking until someone extends a list (#546). - Fixed
withTimeoutrejecting after callingonTimeout: cancelling a stream reader inonTimeoutcan settle the raced promise with a cleandone: true, and a settlement enqueued ahead of the rejection would win the race, turning a stall into a silent success. Reject is now issued beforeonTimeout(#546).
Refactoring
Improvements
- Decomposed
lib/codex-manager.tsacross four phases: login control loop, OAuth machinery, command registry, and formatter modules extracted intolib/codex-manager/submodules. The file shrinks from 2,266 lines to 690 lines (-82%) (#525, #535, #540, #547). - Decomposed
lib/runtime-rotation-proxy.tsacross two phases: rate-limit decision logic, stream-failover runtime, and rotation-proxy closure state extracted intolib/request/andlib/runtime/submodules (#532, #548). - Eliminated all detected import cycles;
eslint-plugin-import-xno-cycle rule is now enforced in CI so regressions are caught at commit time (#541). - Replaced local
isRecordguards inruntime-current-account.ts,codex-manager/commands/rotation.ts, andrefresh-lease.tswith the canonicallib/utils.tsversion. The local copies accepted arrays (typeof v === "object" && v !== null); the canonical version correctly rejects them (#544, #545).
CLI & Tests
Improvements
- Added shared
cli-test-fixturesmock factory infrastructure; all manager test suites now use a consistent set of factories, removing per-suite boilerplate (#537, #539, #550). - Pinned the machine-readable JSON output contract (
status,report,doctor,forecast,why-selected) with snapshot tests that catch shape regressions (#533).
Build & Packaging
Improvements
- Stripped JS source maps from the published package; declaration maps are kept for go-to-definition. Reduces published size (#527).
- Pinned
@types/nodeto the supported runtime floor major to prevent silent type drift from newer Node API additions (#528). - Raised
engines.nodeto>=18.17.0, reflecting the actual tested minimum and aligning withundici@6and thenode18-smokeCI job (#518). config/schema/config.schema.jsonis now generated from the Zod schema with a byte-exact drift-guard test; the schema and its source of truth can never silently diverge (#536).
Notes
- Prerelease published under the
betadist-tag (npm i -g codex-multi-auth@beta). - The #509 sequential drain-first feature from
2.3.0-beta.0is unchanged; the pointer-corruption bug above is now fixed.