[defer] feat(usage): display and redeem Claude reset credits in dashboard - #14728
fouadSalkini wants to merge 10 commits into
Conversation
|
Thanks @fouadSalkini — great idea to surface the banked reset credits in the existing reset modal. Before we can merge: (1) the new query string on |
… and translate reset credit i18n keys
|
Addressed all points:
|
…st request The regular /api/oauth/usage poll was restored to the base URL and User-Agent, but upstream only fills `cedar_ember` and `juniper_tide` when the reset-credit query string is sent. On the base URL both come back null, so `bankedResetCredits` was never set, the reset-credit quota row was never built, and the dashboard hid the redeem button for every Claude connection. The count now comes from the dedicated reset-credit list request (query string + CLI headers), memoised per access token: one list call per 30 minutes, a 15 minute back-off after a failure that keeps serving the last known count, and one shared in-flight request for parallel pollers. Opening the credit list re-seeds the count and a redeem clears it. The regular poller's URL, User-Agent and headers are unchanged, and a test now pins them together with the list and redeem request shape.
…o only The regular usage poller runs from background schedulers, so fetching the reset-credit list from it sent a CLI-UA `at_wall=1&cedar_ember=1` request for every Claude account with nobody looking. The poller is now byte-identical to base and never sends that request. - The banked count lives in a per-connection memo that only the user-opened reset-credit list and the existing opt-in auto-reset seed; the provider-limits refresh just reads it. Unknown counts are omitted. - Redeem and auto-claim forget the count; a per-connection generation keeps a list started before a redeem from storing the old count, and forget also drops the in-flight request. Concurrent lists share one request. - A count older than one hour, or refused with a non-429 4xx, becomes unknown; 429/5xx keep the last count until then. - The request deadline now covers reading the body, for the list, status and claim requests. - List, claim and organization lookups run in the connection's proxy context. - Dashboard: a Claude OAuth card shows the reset-credits entry point while the count is unknown or above zero and hides it only after an authoritative empty list; no count is ever invented. - Classify the Claude reset-credit connection lookup in the hard-lease inventory next to its codex/grok/glm siblings.
The opt-in auto-reset (status read and juniper_tide claim) is back to the base request shape: `?at_wall=1&skip_spend=1` with the axios-style `claude-code/<version>` User-Agent and no `x-app`. Base's parser already reads `juniper_tide` from that response. Only the dashboard's user-initiated reset-credit list and redeem send `cedar_ember=1` and the CLI headers; the claim picks its headers from a `profile` option that defaults to the base shape. The auto-reset no longer seeds the dashboard count, but an auto-claim still forgets it. - A usage refresh that falls back to the stale cached entry no longer serves (or keeps storing) a cached Claude count the memo does not confirm, so a redeem followed by a 429 cannot bring back the pre-redeem count. - Only dashboard list calls can join an in-flight list request, and they all share one deadline. - Drop the per-connection generation map: a list result is stored only while its request is still the registered in-flight one, so there is no bounded state whose eviction could admit a stale result.
|
Follow-up on point (2) and point (3): Point (2) — request shapes. Restoring the regular poller to the base URL/User-Agent had a side effect:
Point (3) — Tests: |
Shows Claude reset credits in the provider limits dashboard and lets an admin redeem them through the reset-credit route, as deployed.
…iegosouzapw#14860, diegosouzapw#14863 and diegosouzapw#14864 Locale files as deployed, covering all locales.
Brings diegosouzapw#14728 from be3d814 to its final head d04c11a: - the reset-credit count comes from the dashboard's reset-credit list request (at_wall=1&cedar_ember=1&skip_spend=1 with the CLI headers), in the new claudeResetCreditCount.ts, and is kept in a memo keyed by connection id; - the regular usage poller (usage/claude.ts) is back to base's request shape; - the opt-in auto-reset keeps base's at_wall=1&skip_spend=1 request with the claude-code/<version> UA and no x-app header. Also adds the provider-limits UI assertion that Claude reset credits redeem through the shared picker.
|
Re-homed to |
Summary
Anthropic offers session-limit and usage resets for Claude accounts, surfaced through the OAuth usage endpoint (
/api/oauth/usage?at_wall=1&cedar_ember=1&skip_spend=1):cedar_ember): Usage grants carryingid,resets_left,starts_at,ends_at, andclearsscopes (e.g.opus55-launch-promax-20260921for Pro/Max andopus55-launch-team-20260921for Team seats).juniper_tide): The weekly 5-hour wall reset (counterpart of Claude Code's hidden/limit-resetcommand introduced in feat(sse): Claude OAuth lower-priority lane + weekly session-limit reset #13074).Previously, OmniRoute only supported auto-claiming
juniper_tideat the wall during execution. This PR integrates Claude reset credits into the existing reset credit architecture alongside Codex and Grok:/api/oauth/usage?at_wall=1&cedar_ember=1&skip_spend=1withx-app: cliandUser-Agent: claude-cli/...(Anthropic checks the CLI surface before returning grants, otherwise returningineligible_reason: "surface").bankedResetCreditson Claude usage responses and displays them in the Provider Limits dashboard with a "Redeem" button.src/lib/usage/claudeResetCredits.tsto list available credits and redeem grants (cedar_ember) or weekly session resets (juniper_tide)./api/usage/codex-reset-credit(GET list, POST consume) for providerclaude.CodexResetCreditsModalto support Claude reset confirmation and window descriptions.Tests
tests/unit/claude-reset-credits.test.ts(9/9 pass):parseAllClaudeResetCreditsextractingcedar_embergrants andjuniper_tideresets.claimClaudeResetCreditdispatchingcedar_emberpayload with grant ID and request ID, vsjuniper_tidepayload.canProviderRedeemResetCredit,getResetCreditEndpoint, andcomputeCanRedeemResetCreditrecognizingclaude.parseQuotaDataconvertingbankedResetCreditsinto a quota row for Claude cards.CodexResetCreditsModalformatting titles and confirmation text for Claude.getClaudeUsageextractingbankedResetCreditsfrom usage response.npm run check:any-budget:t11,npm run check:cycles,npm run check:changelog-integrity, and ESLint clean.Live Evidence
Deployed to both operator production nodes ahead of merge:
3deb8d4ac077a10429f6eb4d6f96d117a682643aacf19731b614200623bc0245).opus55-launch-promax-20260921(resets_left: 1,usable_now: true).opus55-launch-team-20260921(resets_left: 1,usable_now: true)./api/usage/codex-reset-credit?connectionId=<id>returns HTTP 200 with the active grant details.