feat(usage): show remaining quota instead of used - #9889
Conversation
aadfc96 to
47835ab
Compare
47835ab to
dbdf483
Compare
e9c81ae to
69fb3dc
Compare
|
|
||
| function accountLabel(account: UsageLimitsReport["accounts"][number]): string { | ||
| return account.instanceId | ||
| ? providerLimitsLabel(account, (driver) => getDriverOption(driver)?.label) |
There was a problem hiding this comment.
🟡 Medium chat/ComposerUsageLimits.tsx:12
Native accounts with the same driver render the same providerLimitsLabel (and often plan), so users cannot identify which account owns a quota or reset-credit action. Because the instanceId branch ignores account.label, the instance identifier collected by collectProviderUsageLimits is lost; include that label in the native-account heading.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/components/chat/ComposerUsageLimits.tsx around line 12:
Native accounts with the same driver render the same `providerLimitsLabel` (and often plan), so users cannot identify which account owns a quota or reset-credit action. Because the `instanceId` branch ignores `account.label`, the instance identifier collected by `collectProviderUsageLimits` is lost; include that label in the native-account heading.
There was a problem hiding this comment.
Fixed on the base branch (#9875): the panel heading now shows the instance id whenever a native account has no display name and its id differs from the driver, so siblings of the same driver and plan read apart.
There was a problem hiding this comment.
Sorry, I'm unable to act on this request because you do not have permissions within this repository.
69fb3dc to
c4eb7bb
Compare
ddeabea to
193fff2
Compare
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This PR adds a new Not approved because:
Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more. |
193fff2 to
1d1c782
Compare
8d052b6 to
df8e6cf
Compare
| } | ||
| // The message is leaving, so that thread's limits snapshot is now stale. The | ||
| // uploads above may have outlasted a navigation, so only that thread's clears. | ||
| clearUsageLimitsFor(routeThreadKey); |
There was a problem hiding this comment.
🟡 Medium components/ChatView.tsx:6678
clearUsageLimitsFor removes the open limits panel even when persistThreadSettingsForNextTurn or startThreadTurn returns Failure, so a message that never starts and spends quota still closes the panel. The same ordering occurs in onSubmitPlanFollowUp; defer both clears until after a successful startThreadTurn.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/components/ChatView.tsx around line 6678:
`clearUsageLimitsFor` removes the open limits panel even when `persistThreadSettingsForNextTurn` or `startThreadTurn` returns `Failure`, so a message that never starts and spends quota still closes the panel. The same ordering occurs in `onSubmitPlanFollowUp`; defer both clears until after a successful `startThreadTurn`.
d80b244 to
1ca3548
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 1ca3548. Configure here.
22ac779 to
509fd55
Compare
Sending /usage-limits in a thread opens the current model's limits above the composer, built from the same snapshot as Usage > Limits. The command resolves in the client without starting a turn, so nothing is written to the thread; the panel closes on dismiss or the next send. Web renders it as a composer notice so it stacks under warnings and uses the standard dismiss. Mobile docks an opaque card in the approval slot. The server only advertises the command for providers present in Limits. Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Review follow-ups for /usage-limits: drop the snapshot when the thread or model changes, keep it hidden rather than dropped while the provider list is unavailable, clear it only when a message actually leaves, clear it when a later request has nothing to show, let submissions carrying attachments or contexts send as prompts, and report a usage-limit source that failed to read even though it has no accounts left to match. Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Seed the provider status stream with the current providers so a usage limit source refresh reaches clients before any provider change, with a test. Tie the /usage-limits snapshot to the turn and clear it once an approval or question is answered, since the agent then spends quota. Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
The /usage-limits panel now reads provider data at render, so a redeemed reset credit or refreshed probe shows through instead of a frozen snapshot. Mobile New Task no longer offers the command and refuses to send it, since only the thread composer can answer it. Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Choosing /usage-limits from the composer menu opens the panel at once instead of leaving the command in the draft to be sent. Typing it out and sending still works as a fallback. Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Compare provider status pairings against the snapshot the client holds instead of dropping the first one, so a refresh between snapshot and subscription still goes out. A usage-limit source that failed to read now counts for every driver, so its error reaches the panel. Only a successful approval or answer clears the panel, and New Task lets a prompt with attachments through. Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Native accounts in the panel show their instance id when they share a driver and have no display name. A response that resolves after navigating away clears only the originating thread's panel. The catalog republish comparator now lives in shared, counts a failed source as covering every driver, and is tested. Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
A report with no accounts, such as a failed hub and no native windows, now renders its own heading row with the close control. Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
A send can outlast a navigation while attachments upload, so it clears only the originating thread's limits panel instead of whatever is open. Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Only a delivered approval or answer resumes the agent, so a failed reply leaves the snapshot in place. Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Source-backed rows showed only the hub kind, so two accounts from one hub were indistinguishable. They now carry the hub and account id. Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Ownership was a prefix match on a composite key; thread ids may contain the delimiter, so the panel now stores its thread key and compares it. Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
A send that fails before the turn starts spends no quota, so it leaves the panel in place. Both the composer and plan follow-up paths clear after a successful start. Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
The command name is only T3's where Limits has data for the selected provider; elsewhere a provider's own command of that name is sent through untouched, on the menu, on send, and in New Task. The panel key now includes any pending approval or question, so an answer from any client closes it, which also retires the local response wrappers. The web key stays hidden rather than dropped while a server thread is not yet loaded. Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Clients that handle the command locally say so on the config subscription; the server injects it into provider catalogs only for them. An older client, which would send the injected command to the provider as a prompt, never sees it. The one-shot config fetch stays without it. Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
The menu handler and the send intercept now require an available panel key, so a reconnect or loading thread leaves the text in the composer. Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Picking /usage-limits with attachments or contexts aboard inserts the command text instead, so it sends as a prompt, matching the typed path. Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Limits bars and labels now show what is left in each window, matching Codex and T3 Chat. The even-spending mark moves to the time left so the fill sits on the line when spending is on pace, and mobile warns as the remainder drops below 30% and 10%. Applies to Usage > Limits and the /usage-limits panel on web and mobile. Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
509fd55 to
56127ea
Compare

What Changed
/usage-limitspanel, on web and mobile. Labels read "60% left".Why
Codex reports remaining quota, and T3 Chat settled on the same framing. Showing the same number in the same direction everywhere removes a mental flip between products.
UI Changes
Web, before and after:
iOS, before and after:
Checklist
Built with Claude Fable 5.1 in Claude Code via T3 Code.
Note
Add
/usage-limitscomposer command and show remaining quotaUSAGE_LIMITS_COMMANDdescriptor andisUsageLimitsCommandpredicate so typing/usage-limitsopens a local usage report instead of starting an agent turn.collectProviderUsageLimitsbuilds a timestamped report with native and pooled accounts; the web and mobile composers render it as a dismissible banner/card.WindowBarandWindowRowfrom used percentage toremainingPercent(inverted, clamped, rounded) and moves the timeline marker to time-left.loadServerConfignow takes an opt-in argument;subscribeServerConfigfilters source events by command coverage and may publish provider-status updates without a provider-registry event.LimitWindowsnow shows a reset countdown on every row.Macroscope summarized 56127ea.
Note
Medium Risk
Touches WebSocket
subscribeServerConfigmerging/deduping and send interception in composers; behavior is gated by client opt-in, but mis-sync could affect slash-command catalogs or when limits panels dismiss.Overview
Usage limits now show quota remaining (e.g. “60% left”) instead of percent used on web and mobile Usage → Limits and in composer panels. Bars and tooltips use a shared
remainingPercenthelper; the pace hairline tracks time left in the window, and mobile warning colors key off low remainder.Adds a client-handled
/usage-limitsslash command for providers that appear under Usage → Limits. Web and mobile thread composers build a liveUsageLimitsReportfrom server config snapshots and show it above the composer (dismiss or clear on send / turn or pending-approval changes). Typing or picking the command does not start an agent turn when there are no attachments; New task blocks it with guidance to use a thread or Settings.Clients opt in with
usageLimitsCommand: trueonsubscribeServerConfig; the server injects the command into provider slash catalogs viawithUsageLimitsCommandsand republishes when limit-source coverage changes (zipped with provider status). Older clients that do not opt in keep unchanged catalogs.Shared package adds
UsageLimitsReport, command detection, report collection, and tests; user docs describe the composer flow.Reviewed by Cursor Bugbot for commit 56127ea. Bugbot is set up for automated code reviews on this repo. Configure here.