v2.7.1

August 13, 2026 · View on GitHub

A bug-hunt release. No new features, no config changes.

npm i -g codex-multi-auth

Two of these could silently send your requests to the wrong account or stall token refreshes for every process on the machine. Worth upgrading if you run more than one account.

The two that matter

A manual pin could route to a different account than the one you pinned. Pins are held by position, but account removal only remapped the active index — never the pin. So when the runtime automatically removed an account with a revoked token, everything after it shifted, and your pin quietly pointed somewhere else. Pool [A,B,C,D] pinned to C, A gets removed, and every request goes to D. No error, no warning. Pins now follow the account by identity and clear themselves if that account is gone.

One failed refresh blocked refreshes everywhere for 20 seconds. Processes share a refresh result so they don't all hammer the token endpoint at once — but it was caching failures too. A single network blip got served to every other process for the full 20-second window, blocking a retry that would have worked. A cached 429 was worse: it could push a healthy account into a multi-minute cooldown. Only successes are cached now.

Also fixed

Refresh

  • A refresh waiting on the shared lease could be evicted at 30 seconds and start a second refresh for the same token. Because the provider rotates refresh tokens on first use, that second one fails with invalid_grant on a perfectly healthy account. Eviction now waits for the real budget.

Quota display

  • Monthly (Codex Business) windows were labelled 5h. The row read 5h ██------ 18% reset 28d 11h — a five-hour window resetting in 28 days. The numbers were always right; only the label lied. Labels now come from the window's actual duration.
  • An account at 100% used with no reset time stayed marked ready and could be recommended as your best option, because there was no reset to wait for.
  • Exhaustion was decided on a rounded percentage, so 99.6% used rounded to 0% left and benched an account that still had quota — potentially for a month on a Business window.

Storage

  • import and legacy migration dropped your pin and reset the affinity counter to zero. The counter reset was the dangerous half: a running proxy could then overwrite a newer pin with a stale one.

Request pipeline

  • Session trimming kept your leading instructions, then sliced the tail to fit the budget — cutting the instructions it had just decided to keep.
  • A genuinely empty completion skipped the empty-response retry, because output: [] counted as "has output".

Rate limiting and budgets

  • A timed-out request never got its token back. The refund window was 30 seconds but the request timeout is 60, so the refund always arrived too late. Over time this starved the bucket and caused spurious token-exhausted skips.
  • Project- and profile-scoped budgets never applied. They're stored under a normalised key but looked up with the raw one, so any budget whose key had uppercase or spaces — a project directory named MyApp, say — silently did nothing.

Two visible changes

  • Monthly-quota accounts now show 30d where they showed 5h. The numbers underneath are the same.
  • Project- and profile-scoped budgets that silently did nothing will now actually enforce. If you set one earlier and saw no effect, check it before upgrading.

Closes #635 · PRs #636, #637, #638