Agent tooling  ·  2026

TokenTracker

One local ledger for Claude and Codex usage, priced against a versioned book.

2 Providers
6 Attribution axes
258 Tests

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.