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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

## [Windows] Unreleased

- **Claude**: OAuth usage now shows saved limit-reset credits, as the Web source already did. The usage request opts in with `cedar_ember=1` and identifies as the Claude CLI (`claude-cli/<version> (external, cli)`); when Claude rejects that optional query with HTTP 400 or a 403 that does not name the missing `user:profile` scope, it retries once without it, as `claude-code/<version>`, so quota and Extra usage still appear. The version is read from `claude --version`, or 2.1.0 when it cannot be read. Missing-scope 403, 401 and 429 keep their normal handling. The cached usage body keeps reset counts and dates but never the grant identifiers (upstream 0.72.0, #3895, #4232).
- Usage forecasts: an older session reset time that later moved backward no longer hides the estimated session quotas left and windows until reset for Codex and Claude. The estimator starts a new history segment at the correction and still needs three recent samples (upstream #4540).
- OpenCode Go: include OpenCode v2 session history in local cost and token totals, without counting migrated messages twice (upstream v0.77.0 #4526).
- Codex: session titles read from the Codex state database are capped at 256 characters when read, so a whole first prompt stored as the thread title is no longer carried into Usage & Spend session lists. Names from `session_index.jsonl` keep precedence and are not capped (upstream #4550).
Expand Down
32 changes: 32 additions & 0 deletions apps/desktop-tauri/scripts/proof-scenarios.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,38 @@
{ "screenshot": "tray-claude-plan-hidden", "clip": "panel" }
]
},
{
"name": "tray-claude-reset-credits",
"query": "window=main&fixture=claude-reset-credits",
"size": [310, 1032],
"steps": [
{ "click": { "selector": ".provider-grid__item", "label": "Claude" } },
{ "expectText": ["claude.user@example.com", "Limit Reset Credits", "2 available"] },
{ "screenshot": "tray-claude-reset-credits", "clip": "panel" }
]
},
{
"name": "tray-claude-reset-credits-hidden",
"query": "window=main&fixture=claude-reset-credits-hidden",
"size": [310, 1032],
"steps": [
{ "click": { "selector": ".provider-grid__item", "label": "Claude" } },
{ "expectText": ["claude.user@example.com", "Plan renews: Dec 1, 2026"] },
{ "expectNoText": ["Limit Reset Credits", "2 available"] },
{ "screenshot": "tray-claude-reset-credits-hidden", "clip": "panel" }
]
},
{
"name": "tray-claude-reset-credits-absent",
"query": "window=main&fixture=default",
"size": [310, 1032],
"steps": [
{ "click": { "selector": ".provider-grid__item", "label": "Claude" } },
{ "expectText": "claude.user@example.com" },
{ "expectNoText": ["Limit Reset Credits", "2 available"] },
{ "screenshot": "tray-claude-reset-credits-absent", "clip": "panel" }
]
},
{
"name": "tray-errors",
"query": "window=main&fixture=errors",
Expand Down
33 changes: 33 additions & 0 deletions apps/desktop-tauri/src/proof/fixtures/scenarios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export const PROOF_SCENARIO_NAMES = [
"errors",
"empty",
"claude-plan-hidden",
"claude-reset-credits",
"claude-reset-credits-hidden",
] as const;

export type ProofScenarioName = (typeof PROOF_SCENARIO_NAMES)[number];
Expand Down Expand Up @@ -344,12 +346,43 @@ function claudePlanHiddenScenario(now: Date): ProofScenario {
};
}

/**
* Default data with a saved Claude limit-reset credit (`reset-credits` inventory
* item, as the OAuth and Web sources produce). `hidden` adds the Claude
* "Limit reset credits" section to `hiddenUsageItemIds`.
*/
function claudeResetCreditsScenario(now: Date, hidden: boolean): ProofScenario {
const base = defaultScenario(now);
return {
...base,
name: hidden ? "claude-reset-credits-hidden" : "claude-reset-credits",
providers: base.providers.map((snapshot) =>
snapshot.providerId === "claude"
? {
...snapshot,
inventory: [
{
id: "reset-credits",
title: "Limit Reset Credits",
availableCount: 2,
nextExpiresAt: isoAfter(now, 3 * DAY_MINUTES),
},
],
hiddenUsageItemIds: hidden ? ["section:claude-reset-credits"] : [],
}
: snapshot,
),
};
}

const BUILDERS: Record<ProofScenarioName, (now: Date) => ProofScenario> = {
default: defaultScenario,
"two-codex-accounts": twoCodexAccountsScenario,
errors: errorsScenario,
empty: emptyScenario,
"claude-plan-hidden": claudePlanHiddenScenario,
"claude-reset-credits": (now) => claudeResetCreditsScenario(now, false),
"claude-reset-credits-hidden": (now) => claudeResetCreditsScenario(now, true),
};

export function isProofScenarioName(value: string): value is ProofScenarioName {
Expand Down
Loading