## Why
When ordinary included usage is exhausted, eligible users need a way to keep working with the backend-provided Luna Reserve quota and return to their previous model once ordinary usage recovers.
## What changed
- Automatically switch eligible TUI tasks to Luna Reserve after an authoritative account usage read, preserving the prior model and reasoning effort per task.
- Restrict model selection while on Reserve to its supported reasoning levels, keep queued turns on the accepted model, and restore the saved model after a fresh read confirms recovery.
- Surface Reserve usage in the composer and `/status`, and expose `normalModelSlug` in app-server rate-limit snapshots so clients can use the associated model's display metadata without changing the request model.
- Poll account limits more frequently near exhaustion and retain compatibility with app servers that only accept the legacy null request parameters.
## Testing
- Added coverage for Reserve entry, queued-turn handling, task reconstruction, recovery authorization, account changes, model selection, usage rendering, and banner dismissal.
GitOrigin-RevId: 46a435e9531051f8cec3f6a46c4b9f7cfa479747
## What changed
- Accept optional `account/rateLimits/read` capabilities for Luna Reserve support
and lightweight background polling while preserving omitted and `null` params.
- Forward the Luna Reserve capability only for eligible ChatGPT sessions, and let
lightweight polls skip the separate reset-credit detail request.
- Return the backend's `ordinaryUsageAllowed` decision only when its account and
user identity match the active session.
## Testing
- Cover capability defaults, eligible and restricted authentication modes,
lightweight polling, identity filtering, and backend permission handling.
GitOrigin-RevId: f407a3ae1e70e411fdf245f072a16e453f59cd87
## What changed
- Carry optional backend-owned banner and account identity data through
`account/rateLimits/read`, filtering banners that do not match the authenticated
account and user.
- Render supported rate-limit notices above the composer with actions for usage,
credits, resets, owner notifications, and plan management while preserving the
existing fallback UI for absent or unsupported banners.
- Refresh usage after limit errors, reject stale responses, and keep queued input
paused until recovery completes. When instructed by the banner, switch to the
first available fallback model without changing unrelated thread settings.
## Testing
- Add protocol, banner parsing and rendering, CTA, identity filtering, refresh
ordering, fallback-model, account-change, and queued-input recovery coverage.
GitOrigin-RevId: e03d75a82e425deb87f4b120a71cf2286c0ef6cc
## What changed
- Recognize `edu_plus` and `edu_pro` as distinct education workspace plans across authentication, backend rate-limit mapping, and app-server account schemas.
- Include both plans in cloud configuration eligibility and use the education usage-limit behavior.
- Display the plans as `Edu Plus` and `Edu Pro` in the TUI status view.
## Testing
- Cover parsing, workspace classification, account and rate-limit responses, cloud configuration eligibility, usage-limit messages, and TUI display names for the new variants.
GitOrigin-RevId: bf8add780ae03a3c6d10c4f4afa0da262c37928e
## What changed
- Recognize `enterprise_cbp_automation` as an Enterprise workspace plan in authentication, backend responses, and app-server account and rate-limit APIs.
- Expose the plan in generated protocol schemas and display it as `Enterprise (Automation)` in account status.
- Apply existing workspace eligibility and Business usage-limit behavior to the new plan.
## Testing
- Cover JWT parsing, plan conversion and serialization, app-server responses, workspace eligibility, usage-limit formatting, and TUI status rendering.
GitOrigin-RevId: d84ae2be3dd60fe398f226e6dc434efe27a387a5
## What changed
- Recognize `self_serve_business_prolite` across authentication, account and rate-limit APIs, generated schemas, workspace classification, status display, cloud configuration gating, and usage-limit messaging.
- Keep the Python SDK's `PlanType` enum compatible with non-empty string values introduced by newer Codex runtimes while preserving its known constants.
## Testing
- Cover token parsing, account reads and notifications, backend rate-limit mapping, workspace behavior, error formatting, and Python SDK response coercion for the new plan value.
GitOrigin-RevId: 70bc17a7c4ba4028cb10e4333cc1f2ac64da361f
## What changed
- Add typed helpers for initialized app-server instances, JSON-RPC requests,
responses, notifications, and thread startup.
- Add a composable `MockResponsesConfig` builder while preserving the existing
mock config writers.
- Migrate the app-server integration suite to the shared helpers, removing
repeated request ID management, deserialization, initialization, and config
assembly.
## Testing
- Add unit coverage for composing mock provider, feature, and extra config, and
for compatibility of the existing mock config writer.
GitOrigin-RevId: 62da177c1d6e8299400de64bd6766567bf1ba3d8
## Why
Sparse and out-of-order rate-limit updates can omit account metadata or let an
older full read overwrite a newer workspace hard stop. Credit availability also
does not always include a displayable balance.
## What changed
- Propagate the backend's `spendControlReached` state through rate-limit
snapshots and the app-server protocol, preserving it across sparse updates.
- Keep workspace hard-stop classifications for error routing and ignore stale
full-read results after a newer hard-stop notification.
- Base TUI warnings and model-switch prompts on credit availability and hard-stop
state, and show `Credits: Available` when a finite balance is hidden.
## Testing
Added coverage for spend-control mapping, snapshot merging, out-of-order reads,
workspace error routing, warning and prompt behavior, and credit status display.
GitOrigin-RevId: 18b09d493d5a620a7c079fb721ec50b38dd5b04c
## Why
Supported clients currently receive only a reset-credit count from
`account/rateLimits/read`. The redemption UI and other app-server
clients need each available credit's expiry and ID so they can explain
what will expire and consume the credit a user selected. This
information belongs on the existing rate-limit read surface rather than
a second app-server list RPC that clients would need to coordinate.
## What changed
- extend `rateLimitResetCredits` on `account/rateLimits/read` with
nullable `credits` detail rows
- fetch usage and reset-credit details concurrently; if the detail
request fails, times out, or cannot be parsed, preserve the usage
response and return `credits: null`
- expose each credit's ID, reset type, status, grant time, expiry time,
title, and description
- add an optional nullable `creditId` to
`account/rateLimitResetCredit/consume`; omitting it preserves the
existing automatic-selection behavior
- forward a selected credit ID to the Codex backend and update the
app-server documentation and generated schemas
The TUI consumer is stacked in #30488.
## Validation
- `just test -p codex-app-server-protocol` (251 passed)
- `just test -p codex-backend-client` (16 passed)
- `just test -p codex-app-server rate_limit` (18 passed)
Part of #29618.
## Why
Codex users can earn personal rate-limit reset credits, but app-server
clients do not currently have an API for reading or redeeming them. This
adds the backend and protocol foundation used by the `/usage` TUI flow
in #28154.
## What changed
- Extend `account/rateLimits/read` with a nullable
`rateLimitResetCredits` summary sourced from the existing usage
response.
- Add backend-client and app-server support for consuming a reset with a
caller-generated idempotency key. A UUID is recommended, and clients
reuse the same key when retrying the same logical reset.
- Return only the consume `outcome`; clients refetch
`account/rateLimits/read` for updated window state.
- Document the response field and each consume outcome, and regenerate
the JSON and TypeScript schema fixtures.
- Clarify in `AGENTS.md` that new app-server string enum values use
camelCase on the wire.
- Update the existing TUI response fixture for the expanded protocol
shape.
- Add coverage for authentication, response mapping, backend failures,
consume outcomes, and request timeout behavior.
## Validation
- `just test -p codex-app-server-protocol` — 231 passed.
- `just test -p codex-backend-client` — 14 passed.
- Focused `codex-app-server` reset-credit tests — 5 passed.
- Focused `codex-tui` protocol response fixture test — passed.
- `just fix -p codex-backend-client -p codex-app-server-protocol -p
codex-app-server` — passed.
- `just fmt` — passed.
## Summary
Enterprise users can have an effective monthly credit limit, but Codex
`/status` currently drops that metadata from the account-usage response.
This change adds the optional `spend_control.individual_limit`
projection to the existing rate-limit snapshot flow. The backend client
reads the monthly limit, app-server exposes it as `individualLimit`, and
the TUI renders a `Monthly credit limit` row through the existing
progress-bar renderer.
When the backend does not return an effective monthly limit, existing
rate-limit behavior is unchanged.
## Existing backend state
The account-usage backend already returns the effective monthly limit
and current usage together:
```json
{
"spend_control": {
"reached": false,
"individual_limit": {
"limit": "25000",
"used": "8000",
"remaining": "17000",
"used_percent": 32,
"remaining_percent": 68,
"reset_after_seconds": 86400,
"reset_at": 1778137680
}
}
}
```
Before this change, Codex projected rolling `primary` and `secondary`
windows plus `credits`. It ignored `spend_control.individual_limit`, so
app-server clients and `/status` could not render the monthly cap.
The updated flow is:
```text
account usage backend
-> backend-client reads spend_control.individual_limit
-> existing rate-limit snapshot carries optional individual_limit
-> app-server exposes optional individualLimit
-> TUI renders Monthly credit limit
```
## App-server contract
`account/rateLimits/read` and sparse `account/rateLimits/updated`
notifications now include an additive nullable
`rateLimits.individualLimit` field:
```json
{
"individualLimit": {
"limit": "25000",
"used": "8000",
"remainingPercent": 68,
"resetsAt": 1778137680
}
}
```
In an `account/rateLimits/read` response, `null` means no monthly limit
is available. `account/rateLimits/updated` remains a sparse rolling
notification: clients merge available values into their most recent
`account/rateLimits/read` snapshot or refetch. Nullable account metadata
in a rolling notification does not clear a previously observed value.
## Design decisions
- Extend the existing rate-limit snapshot instead of introducing a
separate request or wire-level update protocol.
- Keep the Codex projection narrow: `/status` needs the effective limit,
current usage, remaining percentage, and reset timestamp.
- Render the monthly row through the existing progress-bar renderer,
with one optional detail line for `8,000 of 25,000 credits used`.
- Keep the backend response optional so existing accounts and older
usage states preserve their current behavior.
- Preserve cached monthly metadata when sparse rolling notifications
omit it. Live account-usage reads remain authoritative and can clear a
removed limit.
## Visual evidence
```text
Monthly credit limit: [██████████████░░░░░░] 68% left (resets 07:08 on 7 May)
8,000 of 25,000 credits used
```
Snapshot:
`codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_enterprise_monthly_credit_limit.snap`
## Testing
Tests: generated app-server schema verification, protocol tests,
backend-client tests, app-server integration coverage, TUI snapshot
coverage, formatting, and workspace lint cleanup.
This PR brought to you via VS Code rather than Codex...
- opened `codex-rs/app-server/tests/common/mcp_process.rs`
- put the cursor on `McpServer`
- hit `F2` and renamed the symbol to `TestAppServer`
- went to the file tree
- hit enter and renamed `mcp_process.rs` to `test_app_server.rs`
- ran **Save All Files** from the Command Palette
- ran `just fmt`
The End
(Admittedly, most of the local variables for `TestAppServer` are still
named `mcp`, though.)
## Summary
First PR in the split from #17956.
- adds the core/app-server `RateLimitReachedType` shape
- maps backend `rate_limit_reached_type` into Codex rate-limit snapshots
- carries the field through app-server notifications/responses and
generated schemas
- updates existing constructors/tests for the new optional field
## Validation
- `cargo test -p codex-backend-client`
- `cargo test -p codex-app-server-protocol`
- `cargo test -p codex-app-server rate_limits`
- `cargo test -p codex-tui workspace_`
- `cargo test -p codex-tui status_`
- `just fmt`
- `just fix -p codex-backend-client`
- `just fix -p codex-app-server-protocol`
- `just fix -p codex-app-server`
- `just fix -p codex-tui`
## Summary
- Replace the manual `/notify-owner` flow with an inline confirmation
prompt when a usage-based workspace member hits a credits-depleted
limit.
- Fetch the current workspace role from the live ChatGPT
`accounts/check/v4-2023-04-27` endpoint so owner/member behavior matches
the desktop and web clients.
- Keep owner, member, and spend-cap messaging distinct so we only offer
the owner nudge when the workspace is actually out of credits.
## What Changed
- `backend-client`
- Added a typed fetch for the current account role from
`accounts/check`.
- Mapped backend role values into a Rust workspace-role enum.
- `app-server` and protocol
- Added `workspaceRole` to `account/read` and `account/updated`.
- Derived `isWorkspaceOwner` from the live role, with a fallback to the
cached token claim when the role fetch is unavailable.
- `tui`
- Removed the explicit `/notify-owner` slash command.
- When a member is blocked because the workspace is out of credits, the
error now prompts:
- `Your workspace is out of credits. Request more from your workspace
owner? [y/N]`
- Choosing `y` sends the existing owner-notification request.
- Choosing `n`, pressing `Esc`, or accepting the default selection
dismisses the prompt without sending anything.
- Selection popups now honor explicit item shortcuts, which is how the
`y` / `n` interaction is wired.
## Reviewer Notes
- The main behavior change is scoped to usage-based workspace members
whose workspace credits are depleted.
- Spend-cap reached should not show the owner-notification prompt.
- Owners and admins should continue to see `/usage` guidance instead of
the member prompt.
- The live role fetch is best-effort; if it fails, we fall back to the
existing token-derived ownership signal.
## Testing
- Manual verification
- Workspace owner does not see the member prompt.
- Workspace member with depleted credits sees the confirmation prompt
and can send the nudge with `y`.
- Workspace member with spend cap reached does not see the
owner-notification prompt.
### Workspace member out of usage
https://github.com/user-attachments/assets/341ac396-eff4-4a7f-bf0c-60660becbea1
### Workspace owner
<img width="1728" height="1086" alt="Screenshot 2026-04-09 at 11 48
22 AM"
src="https://github.com/user-attachments/assets/06262a45-e3fc-4cc4-8326-1cbedad46ed6"
/>
## Why
`codex-core` was re-exporting APIs owned by sibling `codex-*` crates,
which made downstream crates depend on `codex-core` as a proxy module
instead of the actual owner crate.
Removing those forwards makes crate boundaries explicit and lets leaf
crates drop unnecessary `codex-core` dependencies. In this PR, this
reduces the dependency on `codex-core` to `codex-login` in the following
files:
```
codex-rs/backend-client/Cargo.toml
codex-rs/mcp-server/tests/common/Cargo.toml
```
## What
- Remove `codex-rs/core/src/lib.rs` re-exports for symbols owned by
`codex-login`, `codex-mcp`, `codex-rollout`, `codex-analytics`,
`codex-protocol`, `codex-shell-command`, `codex-sandboxing`,
`codex-tools`, and `codex-utils-path`.
- Delete the `default_client` forwarding shim in `codex-rs/core`.
- Update in-crate and downstream callsites to import directly from the
owning `codex-*` crate.
- Add direct Cargo dependencies where callsites now target the owner
crate, and remove `codex-core` from `codex-rs/backend-client`.
## Summary
This removes the old app-server v1 methods and notifications we no
longer need, while keeping the small set the main codex app client still
depends on for now.
The remaining legacy surface is:
- `initialize`
- `getConversationSummary`
- `getAuthStatus`
- `gitDiffToRemote`
- `fuzzyFileSearch`
- `fuzzyFileSearch/sessionStart`
- `fuzzyFileSearch/sessionUpdate`
- `fuzzyFileSearch/sessionStop`
And the raw `codex/event/*` notifications emitted from core. These
notifications will be removed in a followup PR.
## What changed
- removed deprecated v1 request variants from the protocol and
app-server dispatcher
- removed deprecated typed notifications: `authStatusChange`,
`loginChatGptComplete`, and `sessionConfigured`
- updated the app-server test client to use v2 flows instead of deleted
v1 flows
- deleted legacy-only app-server test suites and added focused coverage
for `getConversationSummary`
- regenerated app-server schema fixtures and updated the MCP interface
docs to match the remaining compatibility surface
## Testing
- `just write-app-server-schema`
- `cargo test -p codex-app-server-protocol`
- `cargo test -p codex-app-server`
Added multi-limit support end-to-end by carrying limit_name in
rate-limit snapshots and handling multiple buckets instead of only
codex.
Extended /usage client parsing to consume additional_rate_limits
Updated TUI /status and in-memory state to store/render per-limit
snapshots
Extended app-server rate-limit read response: kept rate_limits and added
rate_limits_by_name.
Adjusted usage-limit error messaging for non-default codex limit buckets
Expand the rate-limit cache/TUI: store credit snapshots alongside
primary and secondary windows, render “Credits” when the backend reports
they exist (unlimited vs rounded integer balances)