acpmux: a Codex chat's plan usage limits (5-hour, weekly, credits) are its usageLimits, as a Claude chat's

Merged
TE
2d ago
+282-7

Leo, 2026-11-04: "are we able to disable the context thing on the bottom right? i want to be able to right click disable it. what's its name?" and "likewise, i guess we can turn that into a 5h/weekly usage type thing instead as well for some people. i suppose it'd be relevant sometimes for SOME people to have that surfaced e.g in their session info thing."

References: Codex's ring popover ("Usage source: Your Pro plan / Weekly limit 10% left / Credits 56,766 credits / Manage usage") and Claude desktop's ("Plan usage limits 路 Ultra / 5-hour limit Resets at 10:00 PM 22% / Weekly 路 all models Resets Mon 3:00 PM 41%").

This is the acpmux half: the data. The composer ring's plan-usage mode, its popover and the Session Info section come in a follow-up web PR that reads this.

What changed

  • A Claude chat already kept its plan limits as the session detail's usageLimits (claude_stdio/usage_limits.rs, from Claude Code's rate_limit_event). A Codex chat now does too: codex app-server's account/rateLimits/updated, which cmux-codex-acp passes on as _meta.codexAppServer.rateLimits, is normalized (codex_app_server/usage_limits.rs) into the same shape: {windows: [{id, usedPercent, resetsAt?, windowMins?}], plan?, credits?}. The 5-hour window is session and the week weekly, as Claude's; any other length is named by its minutes. plan is Codex's planType; credits is {balance} or {unlimited: true}, only when the account holds some.
  • Nothing reported is nothing (no window and no credits gives no usageLimits), never a 0%. A snapshot for another limit id than codex is not the plan's and is ignored.
  • Workflow activities treated any reported usageLimits as "waiting on a limit". Every Claude Code chat reports limits, so its activities were always held as waiting, and Codex chats would now be too. They now read a limit as reached only when a window is used up (usage_limit_reached: a window at 100%).
  • No new reads, no scraping, no new auth: only what the harness already sends.

Tests

  • codex_app_server::usage_limits_tests (normalizer: Pro 5h+weekly+credits, weekly-only with unlimited credits, other window lengths, nothing reported is nothing, credits alone).
  • claude_stdio::usage_limits_tests::only_a_used_up_window_is_a_limit_reached.
  • hub_integration codex_usage_limits::a_codex_chat_keeps_its_accounts_usage_limits: the fake app-server ("codex-limits") sends two updates in a turn; the session detail holds the newest, and a chat with none has none.

Red: 6455e4f (tests only; the normalizer and helper do not exist). Green: the head.

Observed / Not run

  • Observed: the focused tests through the fleet runner on this PR's head (results in a comment below).
  • Not run: a live Codex session against a real account in a DEV build (comes with the web PR's verification).

Changelog

none (the data is shown by the follow-up web PR)

Agent session: agent session

馃 Generated with Claude Code


Autofix with [code]smith
Open in [code]smith 路 Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Makes a Codex chat's plan usage limits (5-hour, weekly, credits) available as its usageLimits, matching how a Claude chat's are kept, so the composer ring and Session Info read one shape for both.

  • Normalizes Codex's account/rateLimits/updated, passed on by cmux-codex-acp as _meta.codexAppServer.rateLimits, into usageLimits: the 5-hour window becomes session and the week weekly, plus plan and credits.
  • Nothing reported is nothing (never a 0%), and snapshots for a limit id other than codex are ignored; an empty plan snapshot clears the limits saved so far.
  • Workflow activities now treat a limit as reached only when a window is used up (100%) and has not reset; every Claude Code chat reports limits, which previously held its activities as waiting.