Skip to content

feat: display provider usage limits in settings - #1732

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

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

Conversation

@Aditya190803

@Aditya190803 Aditya190803 commented Apr 4, 2026

Copy link
Copy Markdown
Contributor

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
Provider status checks now spawn external CLIs and parse fragile TUI text; failures are degraded but probes add latency and Windows PTY behavior was changed to avoid process crashes.

Overview
Adds subscription/quota usage to provider status snapshots and surfaces it on each provider card in Settings.

The contracts layer gains optional usageLimits (windows with used %, labels, reset times, and availability reasons). During status checks, Codex reads app-server rate limits; OpenCode Go/Zen aggregates managed-provider inventory usage (BYO upstreams omit usage entirely); Claude, Cursor, and Grok optionally spawn their CLIs via PTY to parse /usage or print-mode output, with shared probe helpers and graceful “unavailable” fallbacks when PTY is missing or accounts are API-key-only. Grok also picks up ACP-based auth metadata from the same discovery pass.

Settings UI renders progress bars (% remaining, color thresholds, reset dates). Windows PTY fixes cover .cmd shim launching and safer process teardown so usage probes do not crash the server.

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

Note

Display provider usage limits in the settings UI for Claude, Cursor, Grok, Codex, and OpenCode providers

  • Adds usageLimits to the ServerProvider contract schema with validated usedPercent (0–100), session/weekly windows, availability flags, and reset timestamps.
  • Implements PTY-based usage probes for Claude (claudeUsageProbe.ts), Cursor (cursorUsageProbe.ts), and Grok (grokTuiUsageProbe.ts), each parsing CLI/TUI output with ANSI stripping, IANA timezone handling, and year roll-forward logic.
  • Adds a shared PTY probe infrastructure in ptyProbeSupport.ts: Windows .cmd/.bat shim routing, settle-after-output semantics, retry logic, and injectable clock for testing.
  • Codex derives usage from an app-server account/rateLimits/read endpoint; OpenCode aggregates usage from connected managed providers (opencode-go/opencode-zen).
  • Renders per-window progress bars with color thresholds (≥90% destructive, ≥70% warning) and optional reset dates in ProviderInstanceCard.tsx.
  • API-key accounts for all providers return an explicit unavailable usage descriptor rather than omitting the field.
  • Risk: each provider status check now optionally spawns a PTY subprocess; PtyAdapterLive is injected into the provider registry hydration layer, adding process-spawn overhead to every provider refresh.

Macroscope summarized 4b7792d.


Open in Devin Review

@coderabbitai

coderabbitai Bot commented Apr 4, 2026

Copy link
Copy Markdown

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 Plus

Run ID: 7af22611-26c0-426a-9797-9b708e333e1d

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

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.

@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
Contributor 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

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

Copy link
Copy Markdown

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 thread apps/server/src/provider/Layers/CodexProvider.ts Outdated
Comment thread apps/server/src/provider/Layers/CodexProvider.ts Outdated
@macroscopeapp

macroscopeapp Bot commented Apr 4, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

3 blocking correctness issues found. This PR introduces a new feature for displaying provider usage limits with substantial new capabilities including PTY-based probes, new schema types, and UI components. Multiple High severity findings remain unresolved, and a human reviewer has raised architectural concerns about the PTY approach versus using SDKs directly.

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

Comment thread apps/server/src/provider/codexAppServer.ts Outdated
@Aditya190803

Copy link
Copy Markdown
Contributor Author

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

@Marve10s

Marve10s commented Apr 6, 2026

Copy link
Copy Markdown
Contributor

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

Marve10s commented Apr 6, 2026

Copy link
Copy Markdown
Contributor

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

Marve10s commented Apr 6, 2026

Copy link
Copy Markdown
Contributor

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

@juliusmarminge juliusmarminge left a comment

Copy link
Copy Markdown
Member

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
@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
@Aditya190803
Aditya190803 force-pushed the feat/provider-usage-limits branch from 036a9b9 to ed12bb8 Compare April 17, 2026 07:18
@Aditya190803
Aditya190803 force-pushed the feat/provider-usage-limits branch from e0ea7e5 to cfd2e99 Compare July 18, 2026 12:17
};
}

return yield* Effect.promise(() => runGrokUsageProbeLoop(child, input, clock));

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.

🟡 Medium provider/grokTuiUsageProbe.ts:157

probeGrokUsageLimits ignores the AbortSignal provided by Effect.promise, so interrupting the effect does not stop collectPtyProbeOutput or kill the spawned Grok PTY process. The child process and its 10-second timer keep running until the timeout fires, and repeated interrupted checks can leave multiple orphaned PTY processes. The runGrokUsageProbeLoop call should pass the signal through and wire interruption to child.kill().

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/grokTuiUsageProbe.ts around line 157:

`probeGrokUsageLimits` ignores the `AbortSignal` provided by `Effect.promise`, so interrupting the effect does not stop `collectPtyProbeOutput` or kill the spawned Grok PTY process. The child process and its 10-second timer keep running until the timeout fires, and repeated interrupted checks can leave multiple orphaned PTY processes. The `runGrokUsageProbeLoop` call should pass the signal through and wire interruption to `child.kill()`.

return undefined;
}

const authenticated = value.authenticated === true;

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.

🟡 Medium provider/grokUsageProbe.ts:36

parseGrokAuthCheckSubscription returns { authenticated: false } for any payload that lacks the literal boolean authenticated: true, including malformed responses like {} or { authenticated: "true" }. These flow through grokAuthFromSubscriptionProbe as status: "unauthenticated", incorrectly reporting the user is logged out instead of treating the response as unknown. The check value.authenticated === true silently coerces every non-true value to false rather than rejecting non-boolean values. Consider validating that value.authenticated is a boolean and returning undefined when it is absent or not a boolean.

-  const authenticated = value.authenticated === true;
+  if (typeof value.authenticated !== "boolean") {
+    return undefined;
+  }
+  const authenticated = value.authenticated;
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/grokUsageProbe.ts around line 36:

`parseGrokAuthCheckSubscription` returns `{ authenticated: false }` for any payload that lacks the literal boolean `authenticated: true`, including malformed responses like `{}` or `{ authenticated: "true" }`. These flow through `grokAuthFromSubscriptionProbe` as `status: "unauthenticated"`, incorrectly reporting the user is logged out instead of treating the response as unknown. The check `value.authenticated === true` silently coerces every non-`true` value to `false` rather than rejecting non-boolean values. Consider validating that `value.authenticated` is a boolean and returning `undefined` when it is absent or not a boolean.

}

function extractResetTimestamp(value: string, checkedAt: string): string | undefined {
const resetMatch = value.match(/\breset(?:s|ting)?(?:\s+(?:at|on|in))?[:\s-]*([^\n.;]+)/i);

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.

🟡 Medium provider/claudeUsageProbe.ts:188

extractResetTimestamp truncates valid ISO timestamps with fractional seconds before they can be parsed. The capture group [^\n.;]+ stops at any period, so resets at 2026-04-17T14:00:00.000Z becomes 2026-04-17T14:00:00. The truncated value has no explicit UTC offset, so the hasExplicitOffset check fails and resetsAt is silently omitted even though the original timestamp was valid. Consider allowing . within the capture so fractional seconds survive.

-  const resetMatch = value.match(/\breset(?:s|ting)?(?:\s+(?:at|on|in))?[:\s-]*([^\n.;]+)/i);
+  const resetMatch = value.match(/\breset(?:s|ting)?(?:\s+(?:at|on|in))?[:\s-]*([^\n;]+)/i);
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/claudeUsageProbe.ts around line 188:

`extractResetTimestamp` truncates valid ISO timestamps with fractional seconds before they can be parsed. The capture group `[^\n.;]+` stops at any period, so `resets at 2026-04-17T14:00:00.000Z` becomes `2026-04-17T14:00:00`. The truncated value has no explicit UTC offset, so the `hasExplicitOffset` check fails and `resetsAt` is silently omitted even though the original timestamp was valid. Consider allowing `.` within the capture so fractional seconds survive.

],
unavailableReason: "Could not read usage limits for this Claude account.",
});
}

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.

Unused runtime usage-limits parser

Low Severity

The parseClaudeRuntimeUsageLimits function is dead code. It's never called by any server path, including the Claude status probe, and its intended role in processing live rate_limit_event traffic is already handled by existing mechanisms. This means it doesn't contribute to updating Claude usage limits.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d8331d5. Configure here.

return "finish";
}
return parsed.available ? { settleAfterMs: GROK_USAGE_OUTPUT_SETTLE_MS } : "continue";
},

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.

Grok usage write races TUI startup

High Severity

probeGrokUsageLimits writes /usage in onStart immediately after spawn, before the Grok TUI is ready to accept slash commands. If that input is dropped, the probe waits out the timeout and reports usage unavailable even for authenticated accounts.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d8331d5. Configure here.

…imits

# Conflicts:
#	apps/server/src/provider/Drivers/ClaudeDriver.ts
#	apps/server/src/provider/Layers/ClaudeProvider.ts
#	apps/server/src/provider/Layers/CodexProvider.ts
Cursor's CLI now supports an interactive `/usage` command; replace the
hardcoded "Cursor does not expose subscription usage limits" snapshot
with a PTY probe that spawns cursor-agent, reads the `/usage` panel,
and parses the Included window's percent and reset date, matching how
Claude and Grok already surface usage.

Fixes pingdotgg#1732.
);

return parseGrokAuthCheckSubscription(authRaw);
});

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.

Grok auth probe fails discovery

High Severity

probeGrokAuthViaAcp only swallows AcpRequestError and AcpTransportError. Other ACP failures such as AcpProtocolParseError, AcpProcessExitedError, or AcpInputStreamEndedError still fail the effect. Because auth is probed after models are already built inside discoverGrokProviderViaAcp, those failures discard successful model discovery and mark Grok as errored.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1993350. Configure here.

checkedAt,
reason: "Upstream providers did not report usage information",
})
: undefined);

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.

OpenCode usage shown incorrectly

Medium Severity

When any upstream provider is connected, missing managed usage falls back to an unavailable snapshot with reason Upstream providers did not report usage information. That also triggers for inventories that only connect non-managed providers such as OpenAI, even though this feature only supports OpenCode Go/Zen.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1993350. Configure here.

);
const rateLimitsResponse = yield* client
.request("account/rateLimits/read", undefined)
.pipe(Effect.catch(() => Effect.void));

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.

Codex rate-limit catch too narrow

Medium Severity

The optional account/rateLimits/read call uses Effect.catch to degrade to no snapshot. That only handles typed failures, so defects skip the handler and can fail the whole Codex provider probe instead of leaving usage unavailable.

Fix in Cursor Fix in Web

Triggered by learned rule: Effect.exit not Effect.result for recording dispatch outcomes

Reviewed by Cursor Bugbot for commit 1993350. Configure here.

>
<div
className={cn("h-full rounded-full transition-all", color)}
style={{ width: `${Math.max(0, Math.min(100, window.usedPercent))}%` }}

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.

Remaining percent rounds poorly

Low Severity

The label uses 100 - Math.round(usedPercent) while the bar width uses the raw usedPercent. Near full usage, values like 99.6 render as 0% remaining even though the bar is not completely full.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1993350. Configure here.

Comment thread apps/server/src/server.ts
Layer.provideMerge(ProviderInstanceRegistryHydrationLive),
// PtyAdapter must be in the driver create context (BuiltInDriversEnv) so Grok/Claude
// usage probes can spawn TUIs. TerminalLayerLive only provide()s it privately.
Layer.provideMerge(ProviderInstanceRegistryHydrationLive.pipe(Layer.provide(PtyAdapterLive))),

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.

PtyAdapter never reaches usage probes

High Severity

PtyAdapterLive is attached with Layer.provide on a Layer.unwrap hydration layer, but Claude/Cursor/Grok drivers never declare PtyAdapter in their env and only read it via serviceOption. Registry construction captures Effect.context&lt;BuiltInDriversEnv&gt; after unwrap, so the adapter is absent at driver.create. PTY usage probes always fall back to “unavailable in this runtime,” which matches the Claude usage failure in review.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1993350. Configure here.

Aditya190803 and others added 2 commits July 25, 2026 20:51
Addresses the outstanding Cursor Bugbot / Macroscope findings on the
provider usage-limits work.

- Grok auth probe: catch every typed ACP failure instead of only
  AcpRequestError/AcpTransportError. The probe runs after model discovery
  on the same session, so an unhandled parse/exit/stream error discarded
  successfully discovered models and marked the provider errored.
- OpenCode: gate the "did not report usage" fallback on connected
  *managed* providers (Go / Zen). Inventories connected only to BYO
  upstreams such as OpenAI no longer claim usage was withheld for a
  feature that never covered them.
- Codex: recover the full cause around the optional
  account/rateLimits/read call so a defect degrades to "no usage"
  instead of failing the whole provider probe.
- Settings usage bars: derive the bar width and the "% remaining" label
  from one rounded value. 99.6% used previously rendered "0% remaining"
  beside a visibly unfilled bar.
- Remove parseClaudeRuntimeUsageLimits and its orphaned helpers. Nothing
  in production called it; ClaudeAdapter already emits
  account.rate-limits.updated straight from the raw SDK message.
- TUI probes: re-issue /usage on a bounded timer. Grok and Cursor both
  wrote the command at spawn time, and a TUI that had not finished
  installing its key handlers dropped it, turning a healthy account into
  "usage unavailable" after the full probe timeout.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Running the app on Windows surfaced three defects that made the usage
feature unusable there. Codex was unaffected throughout because its usage
comes from an app-server RPC rather than a PTY probe.

- Binary resolution: the probes passed `binaryPath` straight to node-pty,
  but on Windows these CLIs install as `name.cmd` shims next to an
  extensionless shell script that PATH lookup finds first. node-pty
  executes the target directly, so every PTY-backed provider reported
  "Failed to spawn ... for usage probe". Resolve the executable and route
  `.cmd`/`.bat` shims through ComSpec.

  This deliberately avoids `resolveSpawnCommand`, which escapes the
  executable for `ChildProcess` with `shell: true` where the command is a
  single string. node-pty takes an argv array and quotes each element
  itself, so a pre-escaped path arrives double-quoted and cmd.exe answers
  "is not recognized as an internal or external command".

- Claude probe timeout: `claude --print /usage` performs a real API round
  trip, measured at ~2.7s warm and ~8s cold. The 4s budget expired before
  the CLI answered on anything but a warm cache, so a working account
  showed "Could not read usage limits". Raised to 15s.

- Server crash: killing a PTY on Windows makes node-pty 1.1.0 resolve its
  console process list as `undefined` and call `.forEach` on it inside its
  own promise continuation (windowsPtyAgent.js:141). Nothing handles that
  rejection, so a probe timeout terminated the whole server, which then
  crash-looped under `node --watch`. The throw is asynchronous, so no
  try/catch around `kill()` can intercept it; signal the OS process
  directly on Windows instead, and never kill a PTY that already exited.

Verified end to end: Claude now renders Session and Weekly bars in
Settings, and the server survives repeated probe cycles without crashing.
Probe tests now pin `HostProcessPlatform` so binary resolution does not
depend on the host OS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@dtown123

Copy link
Copy Markdown

Following up on my earlier comment: this looks like a great foundation, and I’m not asking to expand this PR’s scope before merge.

With T3 Connect now live to the masses, I think usage becomes decision-time information - not just a Settings diagnostic. Some of us are juggling $20 plans and need to know whether the selected server/account/model is already close to its limit.

I’d love a follow-up where:

  • Current usage has a compact, glanceable “Apple Fitness”-style ring/progress treatment.
  • The model picker shows the relevant remaining capacity and reset time, not only the model name.
  • In a remote/Connect setup, that information comes from the selected server’s authenticated provider account—so it reflects the quota the agent will actually consume.
  • A client can eventually compare availability across reachable T3 environments, helping answer “should this task run on my home MBP, work MBP, or with a different model?”
  • Two servers using the same provider account are treated as one shared quota, not double-counted.

Settings is still the right place for the full breakdown. But the compact, live version belongs where I choose the model and environment imo.

The snapshot/probe work in this PR feels like the important foundation for that future UX.

@jo-chemla

jo-chemla commented Jul 31, 2026

Copy link
Copy Markdown

It seems that @NicL9923 addresses allows (gated setting) displaying session/weekly usage limits within both:

  • the settings under each provider
  • as well as on hover of the current context-window popover,

All while addressing several comments of this PR thread.

@macroscopeapp macroscopeapp Bot left a comment

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.

Reviewed the new provider usage-limit probes against the Effect service conventions. Three findings, all about how PtyAdapter is acquired and imported; the pure helpers (providerUsageLimits.ts, ptyProbeSupport.ts, codexUsageProbe.ts, openCodeUsageLimits.ts) and the contract/schema additions look fine.

Posted via Macroscope — Effect Service Conventions

type ProviderSnapshotSettings,
} from "../providerUpdateSettings.ts";
import { makeClaudeCapabilitiesCacheKey, makeClaudeContinuationGroupKey } from "./ClaudeHome.ts";
import { PtyAdapter } from "../../terminal/PtyAdapter.ts";

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.

At a service boundary the service module should be imported as a namespace and used through its module shape, which is also what the rest of the repo does (import * as PtyAdapter from "../../terminal/PtyAdapter.ts" in Layers/ClaudeProvider.ts and terminal/Manager.ts). Consider switching to the namespace import and referencing PtyAdapter.PtyAdapter at the use site (line 130). Same in CursorDriver.ts:27 and GrokDriver.ts:14.

Posted via Macroscope — Effect Service Conventions

Comment on lines +411 to +413
export function probeClaudeUsageLimits(
input: ClaudeUsageProbeInput,
ptyAdapter: PtyAdapter.PtyAdapter["Service"],

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.

This production probe takes the PtyAdapter implementation as a value parameter, so the dependency never appears in the effect's requirements (Effect.Effect<ClaudeUsageProbeResult>) and every caller has to thread the instance by hand.

Consider acquiring it from the environment instead — declare the requirement (Effect.Effect<ClaudeUsageProbeResult, never, PtyAdapter.PtyAdapter>) and do const ptyAdapter = yield* PtyAdapter.PtyAdapter; inside the Effect.gen — and let the tests supply their stub via Layer.succeed(PtyAdapter.PtyAdapter, ...) rather than as an argument. Same shape in probeCursorUsageLimits (cursorUsageProbe.ts:135) and probeGrokUsageLimits (grokTuiUsageProbe.ts:131).

Posted via Macroscope — Effect Service Conventions

) => Effect.Effect<ClaudeCapabilitiesProbe | undefined>,
environment?: NodeJS.ProcessEnv,
cwd?: string,
ptyAdapter?: PtyAdapter.PtyAdapter["Service"],

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.

Threading the service instance through an optional parameter hides the dependency: ClaudeDriver resolves the tag with Effect.serviceOption, flattens it to undefined, and passes it two levels down, so PtyAdapter shows up in neither this function's nor the probe's requirements.

Consider dropping the parameter and acquiring it here, keeping the optionality inside Effect:

const ptyAdapter = Option.getOrUndefined(yield* Effect.serviceOption(PtyAdapter.PtyAdapter));

That keeps the "usage limits unavailable in this runtime" fallback exactly as-is while making the requirement visible and layer-testable. Same for checkCursorProviderStatus (CursorProvider.ts:996) and checkGrokProviderStatus (GrokProvider.ts:201).

Posted via Macroscope — Effect Service Conventions

...(subscriptionTier !== undefined ? { subscriptionTier } : {}),
...(authMode !== undefined ? { authMode } : {}),
};
}

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.

Grok auth false negatives

High Severity

parseGrokAuthCheckSubscription treats any object response without authenticated === true as explicitly unauthenticated. Missing or differently shaped payloads therefore force Grok into the warning unauthenticated path even after ACP model discovery succeeded, a regression from the prior ready/unknown auth behavior.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 228e10f. Configure here.

…provider-usage-limits

# Conflicts:
#	packages/contracts/src/server.test.ts
],
},
}),
).toThrow();

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.

Forward-compat tests deleted

High Severity

Usage-limit tests replaced the previous forward-compatibility coverage instead of adding beside it. Helpers like decodeServerProviders, decodeUpsertKeybindingResult, decodeAvailableEditors, and baseProviderSnapshot remain imported/defined but unused, which strongly suggests those regression tests were removed by mistake.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e08399b. Configure here.

@cursor cursor Bot left a comment

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.

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

There are 18 total unresolved issues (including 17 from previous reviews).

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 4b7792d. Configure here.

// degrade to "no usage" instead.
const rateLimitsResponse = yield* client
.request("account/rateLimits/read", undefined)
.pipe(Effect.catchCause(() => Effect.void));

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.

Rate-limit catch swallows interrupts

Medium Severity

Switching the Codex rate-limits request to Effect.catchCause(() => Effect.void) recovers interrupts as well as decode defects. An interrupted provider probe can continue and finish successfully instead of aborting, which fights shutdown and parent timeouts.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4b7792d. Configure here.

@juliusmarminge juliusmarminge changed the title feat: display provider usage limits in settings feat: display provider usage limits in settings (test) Aug 12, 2026
@juliusmarminge juliusmarminge changed the title feat: display provider usage limits in settings (test) feat: display provider usage limits in settings Aug 12, 2026
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

7 participants