Skip to content

Commit eeac9c0

Browse files
committed
feat: add /openai-reset command to spend rate-limit reset credits
1 parent 6559eb4 commit eeac9c0

19 files changed

Lines changed: 5565 additions & 64 deletions

ARCHITECTURE.md

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,8 @@
104104
**Commands (dialogs):**
105105
- Purpose: Per-slash-command payload builders producing `OpenDialogPayload` (text + knobs) and applying user selections to storage. Copies the command context copy per invocation to prevent concurrent sessions from crossing feedback.
106106
- Location: `packages/opencode/src/commands.ts`
107-
- Contains: Command name constants (`OPENAI_*_COMMAND_NAME`), `MODAL_COMMANDS`, `CommandContext` DI shape, `buildDialogPayload`, `applyCommand`, `executeQuotaCommand`/`executeAccountCommand`/`executeRoutingCommand`/`executeKillswitchCommand`/`executeDumpCommand`/`executeLoggingCommand`/`executeCachekeepCommand`.
108-
- Depends on: `core/accounts.ts`, `core/cachekeep.ts`, `core/oauth.ts`, `core/refresh-all-quota.ts`, `quota-manager.ts`, `rpc/protocol.ts`, `logger.ts`, `config.ts`.
107+
- Contains: Command name constants (`OPENAI_*_COMMAND_NAME`), `MODAL_COMMANDS`, `CommandContext` DI shape, `buildDialogPayload`, `applyCommand`, `executeQuotaCommand`/`executeAccountCommand`/`executeRoutingCommand`/`executeKillswitchCommand`/`executeDumpCommand`/`executeLoggingCommand`/`executeCachekeepCommand`/`executeResetCommand`.
108+
- Depends on: `core/accounts.ts`, `core/cachekeep.ts`, `core/oauth.ts`, `core/refresh-all-quota.ts`, `core/reset-credits.ts`, `quota-manager.ts`, `rpc/protocol.ts`, `logger.ts`, `config.ts`.
109109
- Used by: Plugin loader (`auth.loader`), RPC `apply` dispatch.
110110

111111
**CLI (`openai-auth`):**
@@ -157,6 +157,18 @@
157157
4. TUI's `tui.tsx` polls the loader's loopback RPC (`/rpc/pending-notifications`), receives the dialog, and renders it via `command-dialogs.tsx`.
158158
5. User clicks Apply → TUI POSTs `/rpc/apply` → loader's `apply` calls `buildDialogPayload`, mutates storage via `mutateAccounts`, and returns updated knobs for the TUI to re-render.
159159

160+
**`/openai-reset` credit redemption:**
161+
162+
1. The account list reuses each account's valid L1 access token to fetch `wham/usage` and reset-credit inventory in parallel, producing a per-account preview. Only exhausted accounts with an applicable, eligible credit and a stable ChatGPT account identity can continue.
163+
2. Selecting an account opens an explicit L2 confirmation bound to its stable `chatgptAccountId`; the dialog states that one reset credit will be spent and that the action is irreversible.
164+
3. Confirmation resolves the target again and rejects the redemption if its ChatGPT identity no longer matches the bound identity.
165+
4. A new attempt re-fetches quota and credits and re-checks exhaustion and applicable-credit preconditions immediately before claiming a credit. Under the persisted-pair retry rule (3a), an explicit retry instead requires an active in-flight attempt and reuses its `creditId` and `redeemRequestId` pair.
166+
5. `consumeResetCredit` sends the explicit credit ID and redemption UUID to the consume endpoint in a POST bounded by a 60-second timeout.
167+
6. Terminal server outcomes (`reset`, `already_redeemed`, `nothing_to_reset`, `no_credit`) clear the matching in-flight pair and persist `lastOutcome`; only the credit-spending `reset` and `already_redeemed` outcomes start cooldown. HTTP and ambiguous outcomes preserve the pair so a retry can reuse the same identifiers; an expired unreconciled pair requires an explicit replay, while corrupt local state is recorded as locally ambiguous instead of issuing a consume request.
168+
7. A successful or already-redeemed outcome runs the normal targeted quota refresh for the selected account, pushes the result through `QuotaManager`, refreshes the sidebar snapshot, and fetches the remaining applicable-credit count.
169+
170+
Verification debt: server-side deduplication of a repeated `redeem_request_id` is assumed from Codex client source, not live-verified. Before trust expands beyond operator-confirmed interactive use, one safe check must re-POST a consumed UUID and `creditId` and verify an `already_redeemed` response with no second count decrement.
171+
160172
**Cache keep-warm (idle session):**
161173

162174
1. Every main-agent (and optionally subagent) request is captured by `buildKeepwarmCapture` from `sendWithAccessToken`. Outside of the configured clock window, capture is skipped.
@@ -187,6 +199,11 @@
187199
- Location: `packages/opencode/src/core/cachekeep.ts`
188200
- Pattern: Target map keyed by session id; interval timer; bounded (`maxTargets`, `maxBytes`) so a long-lived process cannot leak; model-aware TTL adjustment (30-min TTL for GPT-5.6 models) and gpt-5.6 subagent 2-warm limits.
189201

202+
**Reset credit redemption coordinator:**
203+
- Purpose: Preview reset-credit eligibility and redeem exactly one explicit credit for an exhausted account after identity-bound confirmation.
204+
- Location: `packages/opencode/src/core/reset-credits.ts`; command orchestration in `packages/opencode/src/commands.ts` `executeResetCommand`.
205+
- Pattern: Persisted `(creditId, redeemRequestId)` claim before the consume POST; confirm-time identity and new-attempt precondition checks; terminal-only finalization with bounded, identifier-stable retry for ambiguous outcomes.
206+
190207
**`OpenAIWebSocketPool` / `createWebSocketFetch`:**
191208
- Purpose: Session-keyed WebSocket pool with continuation chaining (`previous_response_id`), per-account discriminator so a switch forces a fresh socket, and stream-failure retries.
192209
- Location: `packages/opencode/src/ws-pool.ts`
@@ -255,4 +272,4 @@
255272

256273
**Versioning & build:** `packages/opencode/src/version.ts` exposes `PackageVersion` (currently `0.3.4`); the TUI plugin header reads `package.json` at runtime via `import.meta.url` so the version badge tracks the package version without baking it into the dist. Use `packages/opencode/scripts/build-tui.ts` during the build to precompile TUI Solid JSX source files into `packages/opencode/src/tui-compiled/` using the `@opentui/solid` compiler transform, binding Solid/OpenTUI imports to the host's virtual runtime registry (`opentui:runtime-module:<specifier>`) so the TUI shares the host's single Solid/OpenTUI runtime. The release pipeline is tag-driven (`.github/workflows` + `scripts/release.sh`); see `README.md` for the exact command surface.
257274

258-
**Formatting/linting:** Biome 2.4.16 (single quotes, no semicolons, trailing commas, 2-space indent). Lefthook runs `biome check` on staged files. Tests run via `bun test src/tests`; typecheck via `tsc`.
275+
**Formatting/linting:** Biome 2.4.16 (single quotes, no semicolons, trailing commas, 2-space indent). Lefthook runs `biome check` on staged files. Tests run via `bun test src/tests`; typecheck via `tsc`.

STRUCTURE.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@
136136
- `packages/opencode/src/core/oauth.ts` — PKCE, OAuth flow, JWT parsing.
137137
- `packages/opencode/src/core/quota-manager.ts` — quota cache, backoff, and mid-stream rate limit marking.
138138
- `packages/opencode/src/core/cachekeep.ts` — prompt-cache warmer with model-aware TTL, clock window, and subagent warm caps.
139+
- `packages/opencode/src/core/reset-credits.ts` — reset-credit listing, eligibility checks, persisted redemption claims, bounded consume requests, and terminal-outcome finalization.
139140
- `packages/opencode/src/prompt-context.ts` — assistant model/variant resolver for synthetic command replies.
140141
- `packages/opencode/src/core/provider.ts` — Codex injection seam (`codexRefreshFn`, `whamUsageFn`).
141142
- `packages/opencode/src/core/backoff.ts` — retry/backoff math.
@@ -152,6 +153,7 @@
152153

153154
**Tests:**
154155
- `packages/opencode/src/tests/` — co-located bun tests (`*.test.ts`).
156+
- `packages/opencode/src/tests/reset-credits.test.ts` — reset-credit listing and consumption, redemption preconditions, and atomic persisted redemption state.
155157
- `packages/opencode/bunfig.toml` — bun test config.
156158
- Run: `bun run test` (root) → `cd packages/opencode && bun run test`.
157159

@@ -169,7 +171,7 @@ Example: `packages/opencode/src/tests/accounts-store.test.ts` tests `packages/op
169171
**Types/classes:** PascalCase (`CodexAuthPlugin`, `FallbackAccountManager`, `QuotaManager`, `CacheKeepManager`, `OpenAIWebSocketPool`, `ResponseStreamError`).
170172
Example: `packages/opencode/src/core/cachekeep.ts` exports `CacheKeepManager`.
171173

172-
**Command name constants:** SCREAMING_SNAKE_CASE prefixed with `OPENAI_` (`OPENAI_QUOTA_COMMAND_NAME`, `OPENAI_ACCOUNT_COMMAND_NAME`, `OPENAI_ROUTING_COMMAND_NAME`, `OPENAI_KILLSWITCH_COMMAND_NAME`, `OPENAI_DUMP_COMMAND_NAME`, `OPENAI_LOGGING_COMMAND_NAME`, `OPENAI_CACHEKEEP_COMMAND_NAME`).
174+
**Command name constants:** SCREAMING_SNAKE_CASE prefixed with `OPENAI_` (`OPENAI_QUOTA_COMMAND_NAME`, `OPENAI_ACCOUNT_COMMAND_NAME`, `OPENAI_ROUTING_COMMAND_NAME`, `OPENAI_KILLSWITCH_COMMAND_NAME`, `OPENAI_DUMP_COMMAND_NAME`, `OPENAI_LOGGING_COMMAND_NAME`, `OPENAI_CACHEKEEP_COMMAND_NAME`, `OPENAI_RESET_COMMAND_NAME`).
173175
Example: `packages/opencode/src/commands.ts`.
174176

175177
**Environment variables:** SCREAMING_SNAKE_CASE with the `CORTEXKIT_OPENAI_AUTH_*` and `OPENCODE_OPENAI_AUTH_*` prefixes (negative-prefixed `CORTEXKIT_OPENAI_AUTH_NO_WEB_SEARCH` for the default-on cache fix).
@@ -199,4 +201,4 @@ Example: `CORTEXKIT_OPENAI_AUTH_WEBSOCKETS`, `CORTEXKIT_OPENAI_AUTH_RAW_WS`, `OP
199201

200202
**New package (sibling to `opencode` or `pi`):** create `packages/<name>/` with its own `package.json`, `src/`, `tsconfig.json`, `tsconfig.build.json`, and add it under `workspaces` in the root `package.json`. Mirror the existing `opencode` or `pi` layout — Bun workspaces, `bun run build`, `bun run typecheck`.
201203

202-
**New plugin command constant / TUI preferences key:** add to `packages/opencode/src/tui-preferences.ts` (`DEFAULT_PREFS` + a typed key under the `PLUGIN_KEY = 'openai-auth'` top-level key in `~/.config/opencode/tui-preferences.jsonc`); the schema-validated reader will accept the new key automatically because `resolveOpenaiAuthPrefs` per-key defaults.
204+
**New plugin command constant / TUI preferences key:** add to `packages/opencode/src/tui-preferences.ts` (`DEFAULT_PREFS` + a typed key under the `PLUGIN_KEY = 'openai-auth'` top-level key in `~/.config/opencode/tui-preferences.jsonc`); the schema-validated reader will accept the new key automatically because `resolveOpenaiAuthPrefs` per-key defaults.

packages/opencode/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ Restart OpenCode after changing plugin config, then authenticate:
3535

3636
## Commands
3737

38-
Each opens an interactive dialog in the TUI and also accepts explicit arguments:
38+
Each opens an interactive dialog in the TUI. Commands with listed arguments also accept those explicit arguments:
3939

4040
| Command | Arguments | Purpose |
4141
| --- | --- | --- |
@@ -44,6 +44,7 @@ Each opens an interactive dialog in the TUI and also accepts explicit arguments:
4444
| `/openai-routing` | `main-first` · `fallback-first` | Routing mode: which account is tried first. |
4545
| `/openai-killswitch` | `on` · `off` · `set <acct>:<5h>,<1w> ...` | Hard-block accounts below quota thresholds. |
4646
| `/openai-cachekeep` | `on` · `off` · `subagents on` · `subagents off` | Idle prompt-cache keep-warm. |
47+
| `/openai-reset` | Modal only | Spend one applicable reset credit for an exhausted account after explicit confirmation. |
4748
| `/openai-logging` | `<level>` | Set log level live. |
4849
| `/openai-dump` | `on` · `off` | Toggle transport request dumps. |
4950

packages/opencode/scripts/build-tui.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const shippedSourceFiles = [
1616
'rpc/rpc-dir.ts',
1717
'rpc/port-file.ts',
1818
'rpc/protocol.ts',
19+
'util/error.ts',
1920
'util/open-url.ts',
2021
] as const
2122
const runtimeSpecifiers = new Set([

0 commit comments

Comments
 (0)