feat(app-server): expose rate-limit reset credits (#28143)

## 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.
This commit is contained in:
jay
2026-06-15 21:54:01 +00:00
committed by GitHub
parent af99f6a72f
commit bef99f861b
31 changed files with 1060 additions and 84 deletions
@@ -1857,6 +1857,30 @@
"title": "Account/rateLimits/readRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/v2/RequestId"
},
"method": {
"enum": [
"account/rateLimitResetCredit/consume"
],
"title": "Account/rateLimitResetCredit/consumeRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/v2/ConsumeAccountRateLimitResetCreditParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "Account/rateLimitResetCredit/consumeRequest",
"type": "object"
},
{
"properties": {
"id": {
@@ -8453,6 +8477,65 @@
],
"type": "object"
},
"ConsumeAccountRateLimitResetCreditOutcome": {
"oneOf": [
{
"description": "A reset credit was consumed and the eligible rate-limit windows were reset.",
"enum": [
"reset"
],
"type": "string"
},
{
"description": "No current rate-limit window is eligible for a reset.",
"enum": [
"nothingToReset"
],
"type": "string"
},
{
"description": "The account has no earned reset credits available.",
"enum": [
"noCredit"
],
"type": "string"
},
{
"description": "The same idempotency key already completed a reset successfully.",
"enum": [
"alreadyRedeemed"
],
"type": "string"
}
]
},
"ConsumeAccountRateLimitResetCreditParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"idempotencyKey": {
"description": "Identifies one logical reset attempt. A UUID is recommended; reuse the same value when retrying that attempt.",
"type": "string"
}
},
"required": [
"idempotencyKey"
],
"title": "ConsumeAccountRateLimitResetCreditParams",
"type": "object"
},
"ConsumeAccountRateLimitResetCreditResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"outcome": {
"$ref": "#/definitions/v2/ConsumeAccountRateLimitResetCreditOutcome"
}
},
"required": [
"outcome"
],
"title": "ConsumeAccountRateLimitResetCreditResponse",
"type": "object"
},
"ContentItem": {
"oneOf": [
{
@@ -9990,6 +10073,16 @@
"GetAccountRateLimitsResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"rateLimitResetCredits": {
"anyOf": [
{
"$ref": "#/definitions/v2/RateLimitResetCreditsSummary"
},
{
"type": "null"
}
]
},
"rateLimits": {
"allOf": [
{
@@ -13893,6 +13986,18 @@
],
"type": "string"
},
"RateLimitResetCreditsSummary": {
"properties": {
"availableCount": {
"format": "int64",
"type": "integer"
}
},
"required": [
"availableCount"
],
"type": "object"
},
"RateLimitSnapshot": {
"properties": {
"credits": {