Skip to content

feat(usage): add Cursor to the Usage page - #6046

Open
GimpyHand wants to merge 8 commits into
pingdotgg:mainfrom
GimpyHand:feat/cursor-usage
Open

feat(usage): add Cursor to the Usage page#6046
GimpyHand wants to merge 8 commits into
pingdotgg:mainfrom
GimpyHand:feat/cursor-usage

Conversation

@GimpyHand

@GimpyHand GimpyHand commented Aug 10, 2026

Copy link
Copy Markdown

Summary

  • Adds Cursor as a third Usage provider (alongside Claude and Codex) on web and mobile.
  • Cursor agent transcripts have no token counts, so the server loads the same dashboard CSV export Cursor already exposes when desktop is signed in on the host (state.vscdb session → export-usage-events-csv), with a short on-disk cache and soft-fail so Claude/Codex still render if Cursor is missing.
  • Prices Cursor export slugs at API-equivalent rates: Cursor-published Auto Cost / Composer / Grok rates for product models, LiteLLM after stripping effort/thinking suffixes for third-party names, and provider-reported CSV dollars when present. Bumps usage contract to v4.

Why this path

Claude/Codex usage comes from local JSONL ledgers. Cursor does not write token totals locally, so a pure filesystem walk cannot cover it. Reusing the signed-in desktop session to fetch the user's own usage export matches the existing host-trust model (the environment machine already holds provider identity) without inventing a parallel billing system.

Costs remain estimates, not subscription invoices. Auto is priced as Auto Cost because the export does not say which Auto mode ran.

Test plan

  • vp test run apps/server/src/usage/usageCursorExport.test.ts apps/server/src/usage/usagePricing.test.ts apps/server/src/usage/usageAggregation.test.ts packages/shared/src/usageMerge.test.ts
  • On a machine with Cursor desktop signed in, open Usage and confirm a Cursor series with non-zero tokens
  • Confirm Included rows still contribute tokens and get API-equivalent dollars (not stuck at $0)
  • On a machine without Cursor login, Usage still shows Claude/Codex and marks Cursor uncovered
  • Multi-environment merge does not double-count the same Cursor export fingerprint

Note

Medium Risk
Reads local Cursor session tokens and calls Cursor’s API with session cookies; merge/pricing logic is non-trivial but heavily tested and Cursor failures are isolated from other providers.

Overview
Adds Cursor as a third Usage provider on web and mobile, alongside Claude Code and Codex. Usage contract bumps to v4 with cursor in UsageProviderKind.

Because Cursor transcripts have no token counts, the server loads usage from Cursor’s dashboard CSV export when desktop is signed in: read cursorAuth/accessToken from state.vscdb, call the export API with a session cookie, parse v1/v2/v3 CSV layouts, and cache per account under server state (5‑minute freshness, stale cache on network failure). Failures are soft (missing/failed sources) so Claude/Codex still render.

Pricing extends LiteLLM lookup with Cursor-published rates for Auto, Composer, and Grok; strips effort/thinking suffixes for third-party export slugs; drops zero/zero LiteLLM rows that would mask auto; and uses numeric CSV costs when present (Included/- stay API-estimated).

Merge treats Cursor as account-scoped (cursor-account fingerprint), dedupes the same export across environments by in-window richness, and lets failed Cursor sources lose to a successful sibling. isCursorCoverageGap drives coverage notices for Cursor only (Claude/Codex missing homes stay silent). UI adds Cursor to chart order, colors/icons, and coverage copy; docs describe sources and Cursor coverage requirements.

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

Note

Add Cursor to the Usage page with CSV export integration and pricing support

  • Adds a new usageCursorExport module that reads Cursor's SQLite session credentials, fetches the dashboard CSV export, parses multiple CSV format versions, and caches results on disk.
  • Extends UsageService to aggregate Cursor records alongside Claude and Codex, reporting Cursor as a source with distinct session counts; failures are soft so other providers still render.
  • Adds Cursor pricing via Cursor-published rates for Auto, Composer 2/2.5, and Grok 4.5, with fallback to LiteLLM mappings for other models after stripping effort/thinking/fast suffixes.
  • Updates usageMerge to deduplicate Cursor sources across environments by richness (records + sessions), and exports isCursorCoverageGap so the UI can surface missing/failed Cursor exports without flagging normal Claude/Codex missing homes.
  • Adds Cursor to PROVIDER_ORDER, colors, icons, and labels in both web and mobile chart/table components.

Macroscope summarized ae4ed38.

@coderabbitai

coderabbitai Bot commented Aug 10, 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: be49bb1e-986d-46cd-bbba-b57f0f4c13e9

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

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 Aug 10, 2026

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

One Effect-service convention issue found: loadCursorUsageRecords takes an HttpClient service instance as a parameter instead of acquiring it from the Effect environment, which erases the requirement from its Effect type. Details inline.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/usage/usageCursorExport.ts Outdated
Comment thread apps/server/src/usage/usageCursorExport.ts Outdated
message: result.fromCache ? "Served from cached Cursor usage export." : null,
};
}
return {

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.

🟠 High usage/UsageService.ts:92

toCursorUsageSource returns a UsageSource with status: "failed" but a resolvedHomePath of cursor-export:${userId}, the same fingerprint used for a successful Cursor source. During multi-environment merge, claimSources only skips missing sources, so a failed Cursor fetch in the lexicographically first environment claims the fingerprint and a successful fetch from another environment is treated as a duplicate — its Cursor buckets are dropped by ownedContribution. A transient failure in one worktree can hide valid Cursor usage from another worktree on the same account. Consider using a fingerprint for failed sources that cannot collide with a successful one, or excluding failed sources from fingerprint claiming.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/usage/UsageService.ts around line 92:

`toCursorUsageSource` returns a `UsageSource` with `status: "failed"` but a `resolvedHomePath` of `cursor-export:${userId}`, the same fingerprint used for a successful Cursor source. During multi-environment merge, `claimSources` only skips `missing` sources, so a failed Cursor fetch in the lexicographically first environment claims the fingerprint and a successful fetch from another environment is treated as a duplicate — its Cursor buckets are dropped by `ownedContribution`. A transient failure in one worktree can hide valid Cursor usage from another worktree on the same account. Consider using a fingerprint for failed sources that cannot collide with a successful one, or excluding `failed` sources from fingerprint claiming.

@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: 9cb738ead2

ℹ️ 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 +92 to +95
return {
fingerprint: { hostId, provider: "cursor", resolvedHomePath, volumeId: "" },
status: result.status,
scannedFiles: 0,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Skip failed Cursor sources during dedupe

When multiple environments report the same Cursor account and one of them has auth but the CSV fetch fails, this still emits the same claimable fingerprint with status: "failed". mergeUsage only skips missing sources when claiming fingerprints, so an earlier failed environment can own cursor-export:<user> and cause a later successful environment's Cursor buckets to be dropped from the totals; failed Cursor exports should not claim the shared source.

Useful? React with 👍 / 👎.

Comment on lines +349 to +350
const cached = readCsvCache(options.cachePath);
if (cached !== null && isFreshCsvCache(options.cachePath, nowMs)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Key the Cursor CSV cache by user

If the host switches Cursor accounts, this accepts a fresh usage-cursor-export.csv solely by path/mtime before fetching and then parses it under the current auth.userId. For the five-minute freshness window, and also when refresh fails and stale cache is used, the Usage page can show the previous account's export under the new user's fingerprint; include the user id in the cache path or metadata before trusting cached CSV.

Useful? React with 👍 / 👎.

Comment thread apps/server/src/usage/UsageService.ts Outdated
}
}
}
sources.push(toCursorUsageSource(hostId, cursorExport, cursorSessionIds.size));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Surface provider-level Cursor coverage failures

A machine without Cursor desktop signed in now returns a successful UsageSummary containing a missing Cursor source, but both usage clients only render environment RPC failures, stale versions, and duplicate sources, so this provider-level soft failure appears as ordinary zero Cursor usage instead of an uncovered provider. Please carry source statuses/messages through the merge/client notices so users can see why Cursor is absent.

Useful? React with 👍 / 👎.

Comment thread apps/server/src/usage/usageCursorExport.ts
Comment thread apps/server/src/usage/UsageService.ts
Comment thread apps/server/src/usage/UsageService.ts
Comment thread apps/web/src/components/usage/usageProviders.ts
@macroscopeapp

macroscopeapp Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

1 blocking correctness issue found. This PR introduces Cursor as a new usage provider with significant new code for CSV export parsing, pricing, and multi-environment deduplication. It's a substantial new capability rather than a minor change. Multiple unresolved high-severity findings identify bugs in fingerprint claiming and CSV token parsing that should be addressed.

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

Comment thread apps/web/src/components/usage/UsagePage.tsx
Comment thread packages/shared/src/usageMerge.ts
Cursor agent transcripts have no token counts, so usage comes from the
dashboard CSV export when Cursor desktop is signed in on the host.
API-equivalent costs use Cursor-published Auto/Composer/Grok rates and
LiteLLM aliases for third-party export slugs.
Key the CSV cache per Cursor user, skip failed sources when claiming
fingerprints, and treat Cursor exports as account-scoped so the same
login is not double-counted across hosts.
Stale-cache ok sources no longer claim an account fingerprint over a
sibling with real in-window spend. Coverage notice only flags Cursor
gaps so unused Claude/Codex homes stay quiet.
Comment thread docs/README.md
## Using T3 Code

- [Install and first run](./user/install.md)
- [Usage](./user/usage.md)

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.

🟢 Low docs/README.md:6

The newly added [Usage](./user/usage.md) bullet on line 6 duplicates the existing [Usage](./user/usage.md) entry on line 10, so the rendered docs index shows the same navigation item twice. Consider removing one of the two identical bullets.

Suggested change
- [Usage](./user/usage.md)
🤖 Copy this AI Prompt to have your agent fix this:
In file @docs/README.md around line 6:

The newly added `[Usage](./user/usage.md)` bullet on line 6 duplicates the existing `[Usage](./user/usage.md)` entry on line 10, so the rendered docs index shows the same navigation item twice. Consider removing one of the two identical bullets.

const totals: UsageTokenTotals = {
uncachedInputTokens: inputWithoutCacheWrite,
cachedInputTokens: cacheRead,
cacheCreationTokens: cacheWrite,

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 CSV undercounts cache tokens

High Severity

parseCursorUsageCsv treats Input (w/ Cache Write) as inclusive of Input (w/o Cache Write) and stores the difference as cacheCreationTokens. Cursor’s export columns are disjoint: the CSV Total Tokens equals both inputs plus cache read plus output. Subtracting drops cache-creation tokens whenever both input columns are non-zero, so Usage under-reports Cursor tokens and any LiteLLM-priced cost for those rows.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e38b80c. 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 2 total unresolved issues (including 1 from previous review).

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 815caca. Configure here.

provider: "cursor",
timestampMs,
model,
sessionId: `cursor-${userId}-${dateStr}`,

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 session counts misleading

Medium Severity

Cursor records set sessionId to cursor-${userId}-${dateStr}. Modern export rows use full ISO timestamps, so almost every event becomes its own session and distinctSessions / merged sessions inflate toward the event count. Legacy date-only rows collapse a whole day into one session. The Usage headline treats that figure like Claude/Codex transcript sessions, so Cursor-heavy totals misreport session volume.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 815caca. Configure here.

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

Labels

size:XL 500-999 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.

1 participant