Add Usage & Limits: tokens, cost and rate limits per agent CLI - #383
Open
0x92 wants to merge 2 commits into
Open
Add Usage & Limits: tokens, cost and rate limits per agent CLI#3830x92 wants to merge 2 commits into
0x92 wants to merge 2 commits into
Conversation
Pane runs Claude and Codex as PTYs, so no token accounting flows through the app. The authoritative record is each CLI's own transcript, which this indexes read-only and incrementally. - Reads ~/.claude/projects/**/*.jsonl and ~/.codex/sessions/**/*.jsonl, resuming from a byte offset per file so a re-scan is cheap - Totals, a hoverable time series, per-model, per-provider and per-project breakdowns, and the cache hit rate with the money it saved - Provider-reported quota where it exists (Codex writes it into every token_count event); a rolling 5h window with a settable limit otherwise - Costs are labelled "at API prices", not as a bill: these tokens are usually paid for by a flat-rate plan Prices are a bundled table with a visible as-of date, and any model without an entry is reported as unpriced rather than silently costing zero. Tests: parser (20), scanner (15), pricing (13), aggregator (23), plus a schema guard for the semicolon-in-comment rule that splitting schema.sql on ";" imposes.
Codex states the model, session id and cwd once, on the session_meta line at the top of a transcript; its token_count events carry none of them. scanJsonlFile built a fresh CodexParseContext on every pass, so a scan resuming at a stored byte offset was past those lines with nothing to stand in for them. Every event it produced fell back to model "codex", which matches no entry in the price table, with a null session and a null cwd — so the cost went unpriced and the per-project breakdown lost the work. The watcher rescans roughly every three seconds while an agent is running, which made this the normal case: only the first token_count of a session was ever attributed correctly. The context now travels with the cursor it belongs to. scanJsonlFile takes the previous pass's snapshot and returns the one at its own end; usageManager stores it in usage_files.parse_context and hands it back, except when the file is being read from byte 0 again, where a stored context would describe bytes that are gone — the rotation and truncation case. Quota samples are deliberately left out of the snapshot: they have their own table and their own capture time, and replaying one would keep re-announcing a reading nobody took. USAGE_PARSER_VERSION goes to 3 so the rows the old parser mis-filed are read again rather than silently trusted.
11 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Pane runs Claude and Codex as PTYs, so no token accounting flows through the app
itself. The authoritative record is each CLI's own transcript, which this indexes
read-only and incrementally.
~/.claude/projects/**/*.jsonland~/.codex/sessions/**/*.jsonl,resuming from a byte offset per file so a re-scan is cheap
breakdowns, and the cache hit rate with the money it saved
token_countevent); a rolling 5h window with a settable limit otherwisepaid for by a flat-rate plan
Prices are a bundled table with a visible as-of date; a model without an entry is
reported as unpriced rather than silently costing zero. A
parser_versioncolumnlets a parser fix invalidate already-indexed files instead of only applying to
future transcripts.
Type of Change
Checklist
pnpm typecheckandpnpm lintlocallypnpm electron-devCritical Areas Modified
usage:channels through the sharedregistry; new
usage_files,usage_eventsandusage_rate_limitstables)Screenshots (if applicable)
Additional Notes
Tests: parser (20), scanner (15), pricing (13), aggregator (23), plus a schema
guard for the rule that
schema.sqlis split on;at startup — a semicoloninside a comment silently breaks database initialisation.
Known limitation, noted in the UI: the scanner reads this machine's home
directory only, so agents running inside a WSL distro are not counted.
These four feature PRs are independent but all add an entry to the same
navigation plumbing (
navigationStore'sActiveView, the two sidebarcomponents,
preload.ts,api.ts,electron.d.ts). Whichever lands first,the others need a small rebase there — no logic overlaps.
Not done: the packaged-build check from CONTRIBUTING. I develop on Windows,
so
pnpm build:macwas not run.