feat(desktop,host-service): Usage tab — per-account quota meters and token-cost analytics
Links
- Research & decisions:
plans/20260815-token-spend-twitter-feedback.md(Twitter-thread sentiment, tool landscape, ToS analysis, accuracy verification) - UI references:
plans/20260816-quota-ui-mobbin-research.md· Marketing research:plans/20260816-quota-tools-marketing-research.md
Summary
This PR bundles two features behind a new top-level Usage dashboard tab:
- Official quota meters — per-account session/weekly % used with reset times, read from the providers' own quota endpoints (the number the CLI enforces, not a log-file estimate)
- Token/cost analytics — cost & token history from the providers' transcript logs, priced at API list rates, with a per-provider chart, model breakdown, and per-project attribution
Both handle multiple accounts: default homes, CLAUDE_CONFIG_DIR (comma-list), auto-discovered profile dirs (runway's model), per-profile Keychain items, and multiple ~/.codex* homes.
Part 1: host-service usage router
Why
Top user ask from the Twitter thread + Slack feedback: quota-limit % with reset countdowns beats raw token counts, and multi-account users pick the subscription with the most headroom when starting a task. Prior attempts existed (#5798, alchemistchaos/feat/minimal-ai-usage-meter) but placed the data layer in Electron main, which can only ever see the local machine — this router runs per-host.
What / How
usage.quota— fetchesapi.anthropic.com/api/oauth/usageandchatgpt.com/backend-api/wham/usagewith the CLIs' own tokens. Tokens are strictly read-only: expired →token_expiredstatus ("run `claude` to refresh"), never refreshed by us — a second client refreshing trips provider token-reuse protection and signs the user's CLI out. 60s promise-cache with rejection eviction.usage.history— walks every profile'sprojects//sessions/transcripts and prices them at API list rates. Runs as a worker-pool task (workers/tasks/usage.ts): the trees reach multiple GB (~4.6s scan on a heavy user), so parsing must stay off the host-service event loop. Deduped per window viadedupeKey.profiles.ts— multi-account discovery: dot-dirs at~+ dirs under~/.config, accepted only when the dir's own.claude.jsonnames an OAuth account (also yields the email with no API call); credentials from the dir's.credentials.jsonor its Keychain itemClaude Code-credentials-<sha256(literal path)[0:8]>probed across path spellings. Codex:~/.codex*dirs with a token-bearingauth.json, deduped by account email.
Key decisions
| Decision | Choice | Rationale |
|---|---|---|
| Quota source | Official OAuth endpoints | The loudest criticism of T3 Code's usage launch was accuracy; log-derived estimates double-count. We show the number the CLI enforces |
| Claude dedupe | message.id + requestId, keep last occurrence | Claude Code writes one line per content block with a growing usage snapshot; first-wins undercounts output ~10% (found during verification) |
| Codex parsing | last_token_usage deltas + consecutive-duplicate suppression | Codex re-emits identical token_count events; after suppression, delta sums land within ~1% of Codex's own cumulative counters |
| Cache-write pricing | Per 5m/1h tier from cache_creation | Flat 1.25x (what #5798 did) misprices 1h writes, which bill 2x |
| Placement | host-service, not Electron main | Remote hosts have their own logins/transcripts; the desktop is a pure consumer |
Accuracy verification
Per-model comparison against ccusage v20 for a full day matches token-exact and cent-exact (all fields, all three Claude models). Codex self-check across 136 sessions: within ~1% of Codex's own cumulative counters. Details in the plans doc.
Part 2: desktop Usage tab
What / How
- New nav entry (gauge icon) in both sidebar variants →
/usageroute. - Quota cards per account: email + plan badge + source dir, Gemini-style meters (label, "N% used", thin bar, "Resets in 5d 13h · Aug 21" caption), amber ≥70% / red ≥90%, distinct expired-token and org-plan states.
- History dashboard (layout follows the strongest patterns from the Mobbin/T3/ccusage research): API-rate cost headline with the honesty footnote ("* if billed at full API rate"), provider share rows, layered (not stacked) monotone area chart with Cost|Tokens + 7d/30d/90d toggles and crosshair tooltip, five metric tiles (incl. cache savings in dollars), per-model table, per-project bars — the attribution no menu-bar tracker has.
- Chart palette (
#d06a48Claude /#1596d6Codex) validated for CVD separation (ΔE ≥ 20), lightness band, and ≥3:1 contrast on both light and dark surfaces.
New components
usage/page.tsx+UsageView— route shell, quota cardsUsageHistorySection(+UsageAreaChart,UsageMetricTiles,UsageModelTable,UsageProjectBars) — analyticsuseHostUsageQuota/useHostUsageHistory— per-host queries viagetHostServiceClientByUrl; history has no interval poll (expensive) and keeps previous data across range switches
Manual QA Checklist
Quota
- Nav entry renders in expanded sidebar and collapsed icon rail, with active state
- Real Codex account renders weekly windows, reset dates, credits balance
- Expired Claude token shows the
token_expiredcard with refresh hint - Multi-account: synthetic
~/.claude-testprofile(identity + expired credential) discovered and rendered as a second card with its email - A live second account with a valid token shows its own quota meters (needs a second real login)
History
- 30d scan of 5GB / ~1,300 files completes in ~4.6s in the worker pool; UI stays responsive
- Cost/Tokens toggle drives headline, share rows, and chart together
- 7d/30d/90d range switch refetches; previous chart holds while loading
- Synthetic transcript priced exactly ($1.026 hand-computed = $1.03 shown)
- Verified against ccusage per-model to the cent (see plans doc)
- Demo film + screenshots captured via CDP
General
- App launches clean; no renderer console errors on the route
- Windows/Linux host: Keychain probes correctly no-op (darwin-gated; not exercised on a real machine)
Testing
bunx turbo typecheck— host-service + desktop greenbun test— usage utils (15 tests) + host-service ratchets (no-main-loop-blocking,no-electron-coupling, worker pool) green- Biome clean on all touched paths
- Live CDP verification against real accounts and real transcript data
Known Limitations
- Cost is an API-list-rate estimate and labeled as such everywhere; subscription users don't spend this money.
- Per-account history attribution is only possible per profile-directory — transcripts don't record which account made the call (same limit as every tool in this space).
- Quota endpoints are undocumented and can change; failures degrade to
unavailablecards, never crash the page. - History covers Claude + Codex only; other agents' transcripts are follow-ups.
Follow-ups (deferred to keep this reviewable)
- Compact quota meter in the model/agent dropdown ("start on the account with most headroom") and sidebar-footer ambient meter — mount + policy logic already prototyped on
alchemistchaos/feat/minimal-ai-usage-meter - Pace projection ("lasts until reset") on quota meters — pure 47-line formula extractable from #5798
- Near-limit notifications, tray badge
Risks / Rollout
- Risk: low — read-only feature, new routes/routers only; no schema or existing-behavior changes. Heaviest operation is sandboxed in the worker pool with a 110s task timeout.
- Rollback: revert the PR; no persisted state to clean up.
Summary by cubic
Adds a Usage tab with per-account quota meters (official endpoints) and token/cost analytics from local transcripts, so users can see reset times and where usage happens by model and workspace. Also fixes manual quota refresh handling and replaces a forbidden project-name snapshot read with a basename(repoPath) fallback.
- Host-service
usagerouter: reads Anthropicapi/oauth/usageand ChatGPTbackend-api/wham/usagewith existing CLI tokens (read-only; expired →token_expired), caches for 5 minutes, and discovers multiple accounts via profile dirs,CLAUDE_CONFIG_DIR(comma-list), macOS Keychain, and~/.codex*. - History: worker task scans transcript logs, prices at API list rates, and returns daily series with provider/model/workspace breakdowns and per-session IDs; joins transcript cwd to workspace worktrees/projects; hardens scans (no symlink traversal, path dedupe, timestamp fallbacks, negative-token clamping) and merges default-slot credentials to avoid phantom expired accounts.
- Desktop
/usage: compact quota cards (reset captions, expired/unavailable states) and a history dashboard with Cost|Tokens and 7/30/90d, a layered per‑provider area chart with day inspection and provider toggles, metric tiles (incl. cache savings), per‑model table, and top workspaces/projects; drilldowns at/usage/workspace/:name,/usage/model/:providerModelKey, and/usage/workspaceswith a Workspaces|Projects pivot and filters; clicking a session row copies its session ID. - Polling and refresh: quota polls every 5 minutes; manual refresh bypasses the host cache and now catches rejected updates.
- Adds
recharts.
Summary by CodeRabbit
- New Features
- Added a Usage dashboard accessible from the sidebar for Claude and Codex quota information, including usage meters, credits, account status, and reset times.
- Added usage history with selectable 7-, 30-, and 90-day ranges and cost or token views.
- Added charts and breakdowns by provider, model, and project, plus token, cache, savings, and estimated cost metrics.
- Added refresh controls and clear loading, error, and unavailable states.