Agent tooling · 2026
TokenTracker
One local ledger for Claude and Codex usage, priced against a versioned book.
2 Providers
Agent tooling
2026
Key decisions
- Cost is computed from a versioned, editable price book, so a provider price change is a new version rather than a silent retroactive rewrite of last month's numbers.
- Usage is attributed by project, provider, model, branch and session, because attribution is what turns a bill into a decision about what is actually expensive.
- Budgets compare against the committed subscription first, because knowing you are 60% through a weekly allowance on day three is actionable and theoretical API pricing is not.
- Real Claude and Codex transcripts run to hundreds of megabytes, so the Rust ingest path streams them line by line and retains only usage-relevant rows under hard byte caps, because the payload reaching the UI must stay bounded no matter what is sitting in the log directory.
Two providers, one ledger
Claude and Codex report usage in different shapes, at different granularities, with different notions of what a unit is. Comparing them by hand is arithmetic nobody does twice, so it does not get done, so nobody knows where the spend went.
TokenTracker reads the real thing rather than asking for clean exports. Claude
Code transcripts under ~/.claude/projects and Codex CLI sessions under
~/.codex/sessions are mixed-line JSONL logs where only some lines carry token
usage, so adapters in src/lib/logAdapters.ts detect each transcript shape,
keep only the usage-bearing rows, deduplicate Claude messages to their final
snapshot per message id, and split cached input tokens out of the input count.
Every imported row is hashed, which makes re-imports idempotent — the same
export dropped twice is skipped, not double-counted — and every ledger row
keeps its eventId and rawHash, so each dollar traces back to the exact line
it came from.
Cost is computed from a versioned, editable price book. Every price row
carries an effectiveAt, and the ledger builder prices each event against the
newest row in effect when it occurred — a provider price change is a new
version rather than a silent retroactive rewrite of last month’s numbers.
Editing the book rebuilds the ledger in place with price ids preserved, and
events whose model has no usable price are not folded quietly into the totals:
they surface in an explicit pricing-gap list until the book catches up.
Attribution is the actual product
Usage is attributed by project, provider, model, branch and session, with manual
session tagging and one-click reassignment for the cases the heuristics get
wrong. Sessions keep their repoPath, branch, outcome and accepted-change
counts next to up to twelve hand-applied tags, reassignment rewrites the ledger
rows along with the session so history stays consistent, and every tag or move
lands in a workspace audit log. That is what turns a bill into a decision:
which client, which workflow, which model choice is actually expensive.
Subscription-cycle reality
API pricing is not what most people pay. TokenTracker leads with committed plan spend — weekly and monthly reset windows anchored to a configurable date, allowances denominated in tokens, credits, messages or requests, daily pace guardrails graded good, watch or critical, and projected allowance use across the cycle. Budgets compare against the committed subscription first; API-priced ledger rows are retained underneath for the cases that genuinely bill per call.
Knowing you are 60% through a weekly allowance on day three is actionable. Knowing you spent $14.20 in theoretical API pricing is not.
A Rust core behind the glass
In the desktop shell the front end is React 19 wrapped in Tauri 2, and the
native side is a single src-tauri/src/lib.rs — roughly 3,100 lines of Rust
behind eleven commands. Real transcripts run to hundreds of megabytes, so the
ingest path streams them line by line and keeps only usage-relevant rows: a
2 GB source cap, 48 MB retained per file, 64 MB per scan and 1 MB per line
bound the payload reaching the UI no matter what is sitting in the log
directory. A notify watcher on the known log roots pushes change events so
the ledger can follow live sessions, and scanning stays opt-in from the
operations view.
The workspace persists as one gzip-compressed, AES-256-GCM-encrypted record in
a SQLite workspace_records table, written atomically and vacuumed when the
freelist grows; the key lives in the OS credential store via the keyring
crate. Provider API keys get the same treatment — held in the keyring, then
used to sync seven-day usage windows from the Anthropic and OpenAI usage APIs,
paginated under a 120-second deadline with caps on pages, rows and response
bytes. In the browser the same workspace record falls back to IndexedDB with a
localStorage compatibility path.