Conversation
Codex refuses a turn on a spent window with `codexErrorInfo: "usageLimitExceeded"`, and that error carries no reset time. The reset is on the app-server's `account/rateLimits/updated`, which the agent received and merged but never forwarded: it only fed the `/status` command's text. A client that wants to park the session and resume it when the window clears had nothing structured to schedule against. Push the merged snapshot as a connection-scoped notification, the same contract shape as `authStatus`: advertised under `agentCapabilities._meta.rateLimits`, one payload per `limitId` (`normalModelSlug` names the model of a model-specific limit), pushed only when that limit's payload changed. The sparse notification is observed once at the app-server connection, before it fans out to the per-session handlers (`CodexAppServerClient.onAccountNotification`), and merged against a connection-level baseline per `limitId`. Neither per-session state nor the fan-out can serve here: session state is reset on every session create, so a younger session would report `planType: null` for the account the older one knew, and the copies a fan-out delivers through independent asynchronous queues can arrive out of order and let a stale snapshot overwrite a newer one. The baseline and the duplicate filter are dropped on logout or an `authStatus` change to another account. `limitName` and the account metadata carry forward; the windows are taken as reported. Field names and units are codex's own `RateLimitSnapshot`. Credits and spend controls are billing state, not usage windows, and stay out of the payload; the doc says which reached types therefore carry no reset. The per-session handler keeps merging into `sessionState.rateLimits` for `/status` and is otherwise unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A push-only ACP extension, the account-level companion of
authStatus: the agent pushes the account's usage windows (primary/secondary:usedPercent,windowDurationMins,resetsAt) as_account/rate_limits_update, advertised underagentCapabilities._meta.rateLimits. One payload perlimitId, pushed only when that limit's payload changed. Spec:docs/rate-limits-extension.md.Why
Codex refuses a turn on a spent window with
codexErrorInfo: "usageLimitExceeded", and that error carries no reset time. The reset is on the app-server'saccount/rateLimits/updated, which codex-acp already receives and merges, but only feeds the/statuscommand's text with. A client that wants to park the session and resume it when the window clears (agent-of-empires does this for Claude today, from_claude/rateLimit) has nothing structured to schedule against.Attaching it as
_metaonusage_update(claude-agent-acp's approach) was considered and rejected: a refused first prompt produces no token-usage update, and that is exactly the moment the reset matters.Design notes
CodexAppServerClient.onAccountNotification, run in receive order before the per-session fan-out), and merged against a connection-level baseline perlimitId. Per-session state cannot be the baseline: it is reset on every session create, so a younger session would reportplanType: nullfor the account the older one knew, and the fan-out's independent queues can reorder copies. The per-session merge stays for/status.limitNameand the account metadata carry forward through the existingmergeRateLimitSnapshot; the windows are taken as reported.authStatuschange to another account.rateLimitReachedTypevariants therefore carry no reset in this payload and clients must not derive one from the rolling windows.versionfield, followingauthStatus; happy to add one if the repo would rather standardise on thegoal/diffStatsconvention.Codex maps exhausted quota and plans without included usage onto the same
usageLimitExceeded; this extension reports the windows and the reached type as codex reports them and does not try to distinguish those cases.Testing
src/__tests__/CodexACPAgent/rate-limits-events.test.ts(12 cases): capability marker; payload subset; duplicate suppression; sparse merge keeping name and plan; all-carried-forward update producing no push; per-limitIdindependence; model-specific limits (normalModelSlug); and the connection seam, driven through the mock app-server, including a two-session fan-out producing exactly one push and the logout reset. With the connection subscription removed, four of the twelve fail. Full suite: 711 passed.AI usage
Drafted with Claude Code; independently reviewed by two model reviewers (Claude, Codex) before opening, whose findings (the connection-level observation, per-
limitIdsemantics, the account-change reset, the seam tests) are in this version.🤖 Generated with Claude Code