Skip to content

feat: display provider usage limits in settings#1732

Open
Aditya190803 wants to merge 76 commits into
pingdotgg:mainfrom
Aditya190803:feat/provider-usage-limits
Open

feat: display provider usage limits in settings#1732
Aditya190803 wants to merge 76 commits into
pingdotgg:mainfrom
Aditya190803:feat/provider-usage-limits

Conversation

@Aditya190803
Copy link
Copy Markdown

@Aditya190803 Aditya190803 commented Apr 4, 2026

Fixes #228.

What Changed

Added provider usage limits to the settings flow end to end for all 4 providers (Codex, Claude, Cursor, OpenCode):

  • the shared contract now includes the usage-limits schema
  • the server persists and exposes provider usage limits
  • the web app renders the limits in Settings

Note: For OpenCode, only the official OpenCode-managed providers (OpenCode Go, OpenCode Zen) are shown.

Why

Users need a visible place to confirm provider usage limits without digging through logs or backend state. This keeps the value available across sessions and makes the current limits easy to inspect from the UI.

UI Changes

The Settings panel now shows provider usage limits in the provider section.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

Medium Risk
Adds a new usageLimits field across contracts, server probes, runtime event ingestion, and UI rendering; risk is mainly around provider status probing latency/fragility (PTY-based Claude probe, extra Codex API call) and correctness of new event parsing.

Overview
Provider snapshots now optionally include usageLimits (session/weekly windows with % used + optional reset time), with schema support added to @t3tools/contracts and snapshot/cache plumbing updated to preserve it.

Server-side probes now populate usage where possible: Codex fetches subscription rate-limit windows, Claude can probe quota via a PTY-driven /status/usage flow (with parsing + fallbacks) and also stamps runtime events with providerInstanceId; Cursor parses ACP usage_update notifications into thread.token-usage.updated, and OpenCode derives usage for official managed providers.

Adds ProviderUsageStateLive to ingest runtime events and remember the latest per-provider/per-instance usage, wires it into server.ts, and updates the Settings ProviderInstanceCard to render usage progress bars (warn/destructive thresholds) while filtering quota telemetry from the chat work log.

Reviewed by Cursor Bugbot for commit a89f9b4. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Display provider usage limits (session/weekly windows) in provider settings cards

  • Adds ServerProviderUsageLimits to the contracts schema and propagates it through provider probes for Codex, Cursor, Claude, and OpenCode providers.
  • Introduces ProviderUsageStateLive, a new server-side service that maintains per-provider usage state from runtime events (Cursor token usage, Claude rate limit telemetry) and exposes get/set/clear effects.
  • Adds claudeUsageProbe.ts, which spawns a PTY to parse Claude's /status and /usage output into normalized session/weekly usage windows.
  • Renders usage bars in ProviderInstanceCard with color-coded fill (warning ≥70%, destructive ≥90%) and reset timestamps; shows an unavailable message when data cannot be obtained.
  • Filters account quota telemetry activities (account.rate-limits.updated, account quota updated) from the session work log via isHiddenWorkLogTelemetryActivity.
  • Behavioral Change: provider status checks for Claude and Cursor now accept optional ProviderUsageState and PTY adapter dependencies wired in from the server layer.

Macroscope summarized a89f9b4.


Open in Devin Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 4, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6bb4855a-791f-4fe7-a882-0e85e20fd5c9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XL 500-999 changed lines (additions + deletions). labels Apr 4, 2026
@Aditya190803
Copy link
Copy Markdown
Author

For now, I’ve added two images to illustrate the UI:

1. Free tier view

This screenshot reflects my current setup. I don’t have subscriptions to Codex or Claude Code, I’m using Copilot Pro (available to me as a student). It shows how the weekly usage limit appears in the interface.

Free Tier Screenshot

2. Pro tier (mocked example)

This second screenshot uses dummy data to demonstrate how the UI could look for users on a Pro plan (Codex or Claude Code). It includes both session-based limits and weekly limits for clarity.

Pro Tier Mock Screenshot

Comment thread apps/server/src/persistence/Layers/ProviderUsageLimits.ts Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 80515efa38

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +574 to +578
state?.usageLimits
? usageLimitsRepository
.upsert({
provider: PROVIDER,
usageLimits: state.usageLimits,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Stop re-persisting stale cached Codex usage limits

This Effect.tap writes the accountProbeCache value back into provider_usage_limits on every Codex status check. Because the probe cache is valid for 5 minutes, a newer usage-limit update persisted from runtime events (via ProviderService's account.rate-limits.updated handling) can be overwritten by this older cached snapshot, causing limits to regress in storage and UI until cache expiry. Please avoid upserting cached probe data unconditionally (or at least reject older timestamps).

Useful? React with 👍 / 👎.

Comment on lines +494 to +496
const usageLimits = isResolvedCodexAccountState(account)
? (account.usageLimits ?? cachedUsageLimits)
: cachedUsageLimits;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Prefer persisted usage limits over account-probe cache

When both values exist, this branch prioritizes account.usageLimits from the 5-minute accountProbeCache and ignores cachedUsageLimits loaded from persistence. That means even after a fresh runtime usage-limit event is stored, provider refreshes keep serving stale probe data instead of the latest persisted snapshot. Reversing this precedence prevents stale limits from being shown after updates.

Useful? React with 👍 / 👎.

@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp Bot commented Apr 4, 2026

Approvability

Verdict: Needs human review

This PR introduces a new feature for displaying provider usage limits with significant new logic across multiple provider drivers and state management. Multiple unresolved P1 review comments identify potential bugs in usage limit caching and persistence logic that require attention.

You can customize Macroscope's approvability policy. Learn more.

Comment thread apps/server/src/provider/codexAppServer.ts Outdated
@Aditya190803
Copy link
Copy Markdown
Author

Hey @juliusmarminge, could you take a look at this PR when you get a moment? Thanks!

@Marve10s
Copy link
Copy Markdown
Contributor

Marve10s commented Apr 6, 2026

Randomly found this PR because wanted to add usage to T3. I think this is great as a user but also I think the UI should match the Codex App usage,it's minimal and clean, would be better than AI tabs
image

You would need to redo the settings button though,something Julius needs to approve. I genuinely think settings button should match Codex App, would love to open PR for that.

@Marve10s
Copy link
Copy Markdown
Contributor

Marve10s commented Apr 6, 2026

A few concerns after checking the code:

  1. Codex can prefer stale persisted usage over freshly probed usage in the same refresh path.
    checkCodexProviderStatus() reads cachedUsageLimits first, then later resolves account state, but ultimately uses cachedUsageLimits ?? accountUsageLimits. That means a refresh can still emit an older persisted value even when the same refresh just fetched newer limits from the provider probe. If the goal is to show the freshest available snapshot, I think the precedence likely needs to be reversed, or persisted data should only be used when no fresh usage was fetched.
  2. The persisted cache looks too coarse-grained for account/config changes.
    The new table is keyed only by provider_name, and the repo API is also getByProvider only. That seems risky if the user changes auth state, swaps Codex homePath, changes binary path, or otherwise changes the effective account behind a provider. In those cases we can still surface previously persisted usage for the provider even though it may belong to a different account/config. Is that safe, or should this cache be scoped more tightly (for example by provider + account/config fingerprint) or suppressed when provider health/auth no longer matches the cached source?
  3. A usage-limit update currently looks like it can trigger full provider refresh work.
    ProviderService persists account.rate-limits.updated, then ProviderRegistry listens to repository changes and calls refresh(change.provider). That refresh path eventually reruns the provider's checkProvider flow rather than just updating already-known snapshot state. For a quota-bar style UI update, this feels heavier than necessary and could add extra CLI churn to a hot path. Would it be simpler to keep usage attached to the provider snapshot/update flow directly instead of going through persistence -> repository stream -> provider refresh?

I can test further and commit to this PR if you're okay with it, @Aditya190803. Genuinely want to see this merged - it would be super useful

@juliusmarminge
Copy link
Copy Markdown
Member

this is on my list to review still! Just dealing with some larger prep work so haven't had time yet.

As for the "it must be more visible and sjhould be 1:1 like codex app":

How often do you guys check your limits to warrant it being one click ??? I check it at most a few times a week, so hiding it in settings next to the provider status is fine!

@Marve10s
Copy link
Copy Markdown
Contributor

Marve10s commented Apr 6, 2026

this is on my list to review still! Just dealing with some larger prep work so haven't had time yet.

As for the "it must be more visible and sjhould be 1:1 like codex app":

How often do you guys check your limits to warrant it being one click ??? I check it at most a few times a week, so hiding it in settings next to the provider status is fine!

I've been checking usage a lot in Codex - a couple of times daily since I was on the $20 plan with 2x limits till April, so I was curious how it was going. My take is that the current settings are genuinely fine, but I think it'll get crowded soon. With usage, OpenCode and Cursor support, and I'm sure there are PRs that extend the settings further it's going to contain a lot of content before long.
Codex shows a small tab with everything you need: usage, general info, and language (something I'd tuck away in settings). I think the Cursor settings approach is a good reference here too.

image

@juliusmarminge
Copy link
Copy Markdown
Member

Yes when the single settings page gets too large we'll split it to subpages. We already did the work adding the sidebar when adding the archive

@juliusmarminge
Copy link
Copy Markdown
Member

can't see claude limits:
CleanShot 2026-04-06 at 15 21 34@2x

Copy link
Copy Markdown
Member

@juliusmarminge juliusmarminge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implementation seems way overcomplicated.

why cache the data? the provider check runs once per minute. we can get fresh data on every tick?

how i imaagined this working:

  • extend the checkProvider probe in ServerProvider to include a new usageLimits property.
  • on the auth check probes (app server / claude), extract usage data
  • stream it down to client as part of the normal provider snapshot
  • render the UI on settings page

given i haven't looked into exactly what's possible to probe and not, why is this PR so much more than that?

@github-actions github-actions Bot added size:XXL 1,000+ changed lines (additions + deletions). and removed size:XL 500-999 changed lines (additions + deletions). labels Apr 7, 2026
Comment thread apps/server/src/provider/Layers/ClaudeProvider.ts
Comment thread apps/server/src/server.ts
Berkay2002 added a commit to Berkay2002/bcode that referenced this pull request Apr 16, 2026
Cherry-picked from upstream pingdotgg#1732
Conflicts resolved manually.
@Aditya190803 Aditya190803 force-pushed the feat/provider-usage-limits branch from f9aabcd to 171df70 Compare April 17, 2026 06:07
@github-actions github-actions Bot added size:XL 500-999 changed lines (additions + deletions). and removed size:XXL 1,000+ changed lines (additions + deletions). labels Apr 17, 2026
Comment thread apps/server/src/provider/Layers/ClaudeProvider.ts Outdated
Comment thread apps/server/src/provider/providerUsageLimits.ts Outdated
@github-actions github-actions Bot added size:XXL 1,000+ changed lines (additions + deletions). and removed size:XL 500-999 changed lines (additions + deletions). labels Apr 17, 2026
Comment thread apps/server/src/provider/codexAppServer.ts Outdated
Comment thread apps/server/src/provider/Layers/ClaudeProvider.ts Outdated
- Refactor claudeUsageProbe to use shared PtyAdapter instead of ad-hoc node-pty
- Add parseClaudeRuntimeUsageLimits for SDK rate-limit event parsing
- Add ProviderUsageState layer with tests
- Integrate usage state into ClaudeDriver and ClaudeProvider
- Add migration 029 for auth session compatibility columns (keep 021 intact)
- Update server and tests for usage limits wiring
Comment thread apps/server/src/provider/Layers/CursorProvider.ts Outdated
Comment thread apps/server/src/provider/Layers/ProviderUsageState.ts
Comment thread apps/server/src/persistence/Migrations/029_AuthCompatibilityColumns.ts Outdated
Comment thread apps/server/src/provider/claudeUsageProbe.ts Outdated
Comment thread apps/server/src/server.ts Outdated
Comment thread apps/web/src/components/settings/ProviderInstanceCard.tsx
Comment thread apps/web/src/components/settings/SettingsPanels.tsx Outdated
Comment thread apps/server/src/provider/providerUsageLimits.ts
Comment thread apps/web/src/components/settings/ProviderInstanceCard.tsx Outdated
Comment thread apps/server/src/persistence/Migrations/030_AuthCompatibilityColumns.ts Outdated
Comment thread apps/server/src/provider/Layers/ClaudeProvider.ts Outdated
Comment thread apps/server/src/server.ts Outdated
@Aditya190803
Copy link
Copy Markdown
Author

@juliusmarminge
Could you review it once more.

Resolved merge conflicts and fixed typecheck errors introduced by
origin/main's stricter Effect lint rules (importFromBarrel, globalDate,
globalDateInEffect). Migrated barrel imports to subpath imports and
replaced Date.now()/new Date() with Effect DateTime/Clock APIs.
Comment thread apps/server/src/provider/Layers/ClaudeAdapter.ts
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a89f9b4. Configure here.

method: "session/update",
payload: input.rawPayload,
},
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unconditional turnId inclusion inconsistent with sibling functions

Low Severity

makeAcpUsageUpdatedEvent always sets turnId: input.turnId even when the value is undefined. Other functions in the same file with optional turnId (e.g., makeAcpToolCallEvent) use the conditional spread pattern ...(input.turnId ? { turnId: input.turnId } : {}). Including turnId: undefined produces a different runtime object than omitting the key, which can affect serialization and consumers checking key presence with "turnId" in event.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a89f9b4. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: add usage / quota visibility for Codex sessions and accounts

4 participants