Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 30 additions & 10 deletions packages/agents-usage/src/collectors/cursor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,10 @@ describe("parseCursorUsage", () => {
expect(api.resetsAt).toBe(1_719_600_000_000);
});

it("treats breakdown.total as real spend and derives the allowance from the percent", () => {
// Live payload shape: used/limit/remaining clamp at the included $20 while
// breakdown.total carries the real spend ($20 included + $8.75 bonus
// consumed = $28.75). The authoritative 55.07% then implies a ~$52.21
// allowance (28.75 / 0.5507), keeping the dollars consistent with the bar.
it("treats breakdown.total as real spend against the vendor plan limit", () => {
// Live payload: used/limit clamp at included $20 while breakdown.total is
// real spend ($20 included + $8.75 bonus = $28.75). API % is a separate
// meter — do not invent limit = spend / percent ($28.75 / 55% ≈ $52).
const body = {
membershipType: "pro",
individualUsage: {
Expand All @@ -96,12 +95,11 @@ describe("parseCursorUsage", () => {
const api = snap.windows.find((w) => w.id === "cursor-api")!;
expect(api.usedPercent).toBeCloseTo(55.07);
expect(api.used).toBeCloseTo(28.75);
expect(api.limit).toBeCloseTo(52.21, 1);
expect(api.limit).toBeCloseTo(20);
});

it("derives the allowance when the clamped dollars disagree with the percent (no breakdown)", () => {
// Older payloads without a breakdown: used/limit cap at the plan price
// ($20/$20) while the percent says 44% consumed → allowance ≈ $45.45.
it("keeps plan dollars when they disagree with API percent (no breakdown)", () => {
// Clamped plan price $20/$20; API bar can still be 44% of a different pool.
const body = {
membershipType: "pro",
individualUsage: {
Expand All @@ -112,7 +110,29 @@ describe("parseCursorUsage", () => {
const api = snap.windows.find((w) => w.id === "cursor-api")!;
expect(api.usedPercent).toBe(44);
expect(api.used).toBeCloseTo(20);
expect(api.limit).toBeCloseTo(45.45, 1);
expect(api.limit).toBeCloseTo(20);
});

it("shows overspend past the plan limit without inventing a percent-derived ceiling", () => {
// User case: ~$35.61 total cost, $20 Pro included, API bar ~4.5%.
// Old math: $35.61 / 0.04555 ≈ $782 nonsense allowance.
const body = {
membershipType: "pro",
individualUsage: {
plan: {
used: 2000,
limit: 2000,
breakdown: { included: 2000, bonus: 1561, total: 3561 },
autoPercentUsed: 22.37,
apiPercentUsed: 4.555,
},
},
};
const snap = parseCursorUsage(body, {}, NOW);
const api = snap.windows.find((w) => w.id === "cursor-api")!;
expect(api.usedPercent).toBeCloseTo(4.555);
expect(api.used).toBeCloseTo(35.61);
expect(api.limit).toBeCloseTo(20);
});

it("keeps the vendor limit when the dollars already agree with the percent", () => {
Expand Down
43 changes: 17 additions & 26 deletions packages/agents-usage/src/collectors/cursor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ import type { UsageSnapshot, UsageWindow } from "../types";
* Schema (per codexbar) of GET /api/usage-summary → individualUsage.plan:
* { used (cents), limit (cents), breakdown { included, bonus, total },
* totalPercentUsed, autoPercentUsed, apiPercentUsed } + billingCycleEnd +
* membershipType. Surfaced as Auto and API breakdown windows. The dollar
* allowance belongs to API usage; see `apiDollars` for how the clamped
* dollar fields are reconciled with the authoritative percents.
* membershipType. Surfaced as Auto and API windows. API dollars use real
* spend over the vendor plan limit; the bar uses apiPercentUsed separately.
*/

export const CURSOR_USAGE_ENDPOINT = "https://cursor.com/api/usage-summary";
Expand Down Expand Up @@ -63,21 +62,22 @@ function clampPercent(value: number | undefined): number | undefined {
}

/**
* Cursor's plan dollar fields need reconciliation (verified against the live
* payload and CodexBar's Cursor probe, which scrapes the same endpoint):
* Cursor plan dollars vs API percent are different meters:
*
* - `used`/`limit`/`remaining` clamp at the nominal plan price (e.g. $20/$20
* while the percent says 55%), so they understate real spend.
* - `breakdown.total` is the total credits *consumed* (included + bonus spend),
* not the allowance — treating it as the limit was CodexBar regression #240.
* Prefer it as the real spend when it exceeds the clamped `used`.
* - `apiPercentUsed` is the authoritative consumed fraction (it's what
* Cursor's own dashboard messages report). When the dollar pair disagrees
* with it, keep the spend and derive the allowance as `spend / percent` so
* the dollar text always matches the bar (e.g. $28.75 / $52.21 at 55%,
* instead of a clamped, full-looking $20 / $20).
* - `used`/`limit` clamp at the nominal plan price (e.g. $20/$20) and can
* understate real spend once bonus credit is consumed.
* - `breakdown.total` is credits *consumed* (included + bonus spend), not the
* allowance — treating it as the limit was CodexBar regression #240.
* - `apiPercentUsed` drives the bar / "% by reset" pace; it is not a fraction
* of the plan dollar cap. Deriving `limit = spend / apiPercent` invents a
* nonsense ceiling (e.g. $35.61 / $775 at 4.5%) that is neither spend nor
* the Pro included allowance.
*
* Surface honest money: real spend over the vendor plan limit. The bar may
* then disagree with the dollar ratio — that is correct; they measure different
* things.
*/
function apiDollars(plan: CursorPlanUsage, percent: number): { used?: number; limit?: number } {
function apiDollars(plan: CursorPlanUsage): { used?: number; limit?: number } {
const reportedUsed = centsToUsd(plan.used);
const reportedLimit = centsToUsd(plan.limit);
const breakdownTotal = centsToUsd(plan.breakdown?.total);
Expand All @@ -89,15 +89,6 @@ function apiDollars(plan: CursorPlanUsage, percent: number): { used?: number; li
if (spend === undefined) {
return reportedLimit !== undefined && reportedLimit > 0 ? { limit: reportedLimit } : {};
}
if (spend > 0 && percent > 0) {
const impliedPercent =
reportedLimit !== undefined && reportedLimit > 0 ? (spend / reportedLimit) * 100 : undefined;
// 1-point tolerance so ordinary rounding drift in the reported percent
// doesn't override a limit the spend already agrees with.
if (impliedPercent === undefined || Math.abs(impliedPercent - percent) > 1) {
return { used: spend, limit: spend / (percent / 100) };
}
}
return {
used: spend,
...(reportedLimit !== undefined && reportedLimit > 0 ? { limit: reportedLimit } : {}),
Expand Down Expand Up @@ -146,7 +137,7 @@ export function parseCursorUsage(
usedPercent: apiPercent,
unit: "percent",
currency: "USD",
...apiDollars(plan, apiPercent),
...apiDollars(plan),
...withReset,
});
}
Expand Down