A few months ago, around two in the morning, I was sitting in front of my terminal watching three separate tmux panes hum with activity. In one window, Devin was refactoring an AST parser. In another, OpenCode was executing a test-driven loop against a flaky API endpoint. In a third, an experimental agent was indexing symbol references across a dozen repositories.
From the outside, it looked like the ultimate dream of modern software development. The screen was a cascading waterfall of syntax highlights, bash commands, unified diff blocks, and passing test assertions. The manual friction of writing boilerplate had vanished into thin air.
Then the monthly API invoices arrived.
The numbers were not ruinous, but they were deeply unsettling. Not because of the absolute dollar figure, but because I had no honest way of answering the most fundamental engineering question: what did each of those dollars actually purchase?
When you work alongside human engineers, you have an intuitive grasp of where time and energy go. You know whether an architectural refactor took three hours or three days. But with autonomous coding agents, token consumption happens entirely in the dark. As I learned firsthand when evaluating coding agents on real tasks, fluency is not the same as discipline. An agent gets trapped in an internal linting dispute, consumes six hundred thousand tokens quietly re-reading the entire project schema on every single turn, and reports back with a single-line patch. You pay for forty-seven model invocations, and you have no idea which prompt triggered the avalanche.
We had traded developer ergonomics for financial blindness. And the prescribed solutions (piping private codebases and session transcripts into yet another closed, cloud-hosted SaaS monitoring platform) felt like an insult to developer privacy and software craftsmanship.
So I decided to build Thermal.
The Compounding Geometry of Agent Spend
Most developers still calculate AI costs using a simple calculator mental model: you send a prompt of five hundred tokens, get back a thousand tokens, and pay pennies.
That model dies the moment you deploy autonomous agents.
In an agentic loop, token expenditure does not scale linearly with output code. It scales exponentially with execution uncertainty. As I explored when breaking down Princeton’s research on how agent skills stabilize execution, an agent does not merely answer questions; it observes, reflects, probes tool outputs, and adjusts its internal plan.
flowchart TD
subgraph StepLoop["Compounding Token Footprint"]
A["User Task: 'Fix flaky test'"] --> B["Step 1: Read Workspace Tree (45k tokens)"]
B --> C["Step 2: Run Test Runner (12k tokens output)"]
C --> D["Step 3: Reread File + Test Traces (65k tokens)"]
D --> E["Step 4: Attempt Speculative Edit (15k tokens)"]
E --> F["Step 5: Rerun Tests & Self-Correct (78k tokens)"]
end
F --> G["Total Billed: ~215,000 Tokens for a 2-Line Fix"]
Consider what happens inside a single agent session:
- Context Accumulation: On step one, the system prompt, tool schemas, and repository summary consume thirty thousand tokens.
- Execution Traces: By step five, the agent has executed three failing shell commands. The terminal stdout and stderr now live inside the message history.
- Repeated State Inspection: To decide what to do on step six, the model must re-read the entire accumulated dialogue from scratch.
- Subagent Spawning: When the primary agent spawns a subagent, a fresh context window opens, often inheriting another slice of the codebase.
- Hidden Reasoning Tokens: Frontier reasoning models (such as o3-mini or DeepSeek-R1) generate thousands of hidden thinking tokens per turn to deliberate before emitting a single line of actionable code.
A two-line patch in a test runner can easily burn three hundred thousand tokens across eight round-trips. If that agent runs on a frontier model, an afternoon of aggressive tinkering can quietly cost twenty or thirty dollars.
Multiply that across five tmux windows, seven days a week, and three different agent CLIs, and you are no longer doing software development. You are running an unmonitored compute furnace.
Why SaaS Dashboards Miss the Point
The standard Silicon Valley answer to this problem is predictable:
sign up for a monitoring service, install their npm package, configure
an HTTP reverse proxy (HTTP_PROXY), and stream every prompt, file
path, and agent transcript to their cloud.
I refuse to do that.
My local code is my own. The architecture decisions, private client keys, internal database schemas, and scratch notes that pass through my agent sessions have no business living on a third-party server. Furthermore, when you work far from central US data centers, adding an external telemetry proxy injects a punishing latency tax into every single turn, a reality I wrote about in Latency Is Political When You Live Far from the Center. It violates the offline-first resilience of Unix tools, breaks when tools enforce custom certificate verification, and creates yet another recurring subscription.
When I started digging into how agent harnesses work on disk, I noticed something fascinating: the telemetry was already there.
Every modern coding agent leaves a rich local trail on your filesystem:
- Claude Code streams JSONL event logs under
~/.claude/projects/. - Codex maintains
state_5.sqlitealongside rollout JSONL logs. - OpenCode / MiMoCode records session graphs in
~/.opencode/SQLite databases. - Grok logs completed turns in
updates.jsonl. - Devin, Gemini CLI / Antigravity, Nous Hermes, and DeepSeek all maintain local SQLite databases or structured JSON files.
The agents were not hiding their costs. They were diligently saving them to disk. We were simply ignoring the files.
We did not need another cloud service. We needed a fast, local-first lens.
Building Thermal: Architecture of a Zero-Telemetry FinOps Engine
When I sat down to design Thermal, I set three non-negotiable principles for myself:
- Zero Cloud Telemetry & Zero Daemons: Thermal is 100% offline. It never makes an outbound phone-home ping, requires no cloud accounts, and installs no persistent background processes eating battery and RAM.
- Sub-10ms Cached Invocations: In order to fit naturally into
interactive developer workflows (even rendering inside a shell
prompt like Starship or
.zshrc), invokingthermalmust return in single-digit milliseconds. - Strictly Non-Invasive & Read-Only: Thermal never injects wrappers or modifies user databases. It reads what exists without interfering with active tools.
To achieve this, I wrote Thermal in Go as a zero-CGO, statically linked binary.
flowchart LR
subgraph Harnesses["14 Local Agent Harnesses"]
H1["Claude Code (~/.claude)"]
H2["OpenCode / MiMoCode"]
H3["Codex (state_5.sqlite)"]
H4["Devin, Grok, Agy, Hermes..."]
end
subgraph ThermalCore["Thermal Go Core"]
D["Parallel Harness Scanner"] --> M["Read-Only SQLite mmap & JSONL Engine"]
M --> C["Delta Cache (~/.cache/thermal/)"]
C --> P["Disjoint Token Normalization & Pricing"]
P --> S["Unified FinOps Aggregator"]
end
subgraph Presentation["Terminal Output"]
S --> TUI["Interactive Bubble Tea TUI"]
S --> CLI["Terminal Heatmap & Scriptable JSON"]
end
Harnesses --> D
1. Pure-Go SQLite and Memory-Mapped Zero-Copy I/O
Many agent harnesses run on SQLite. If an agent is actively coding
while Thermal executes, opening the database with a standard
read-write connection causes immediate lock contention (database is locked).
Thermal opens all SQLite databases in pure read-only URI mode and
immediately activates memory mapping via PRAGMA mmap_size:
// Pure-Go SQLite (modernc.org/sqlite): CGO-free, read-only, zero lock contention
db, err := sql.Open("sqlite", "file:"+dbPath+"?mode=ro")
if err != nil {
return err
}
defer db.Close()
// Memory-map 256MB of page cache for instant scanning without heap allocation
if _, err := db.Exec("PRAGMA mmap_size=268435456"); err != nil {
return err
}
Because Thermal uses modernc.org/sqlite
compiled with CGO_ENABLED=0, the binary remains fully statically
linked. It compiles across Linux, macOS, and Windows without requiring
GCC toolchains or dynamic C libraries.
2. The Sub-10ms Delta Snapshot Cache
Scanning gigabytes of raw transcripts on every shell invocation is an
anti-pattern. To guarantee sub-10ms response times, Thermal employs an
incremental disk-backed delta snapshot cache in ~/.cache/thermal/<tool>/.
On startup, Thermal verifies the target file’s modification timestamp
(mod_time), byte size (size), or maximum transaction ID. If the file
has not changed, Thermal serves the normalized daily aggregation
instantly from cache. If new turns have occurred, it reads only the
unseen byte delta, appends the new rows, and updates the snapshot. Cold
scans of thousands of sessions take hundreds of milliseconds; warm
invocations take 4 to 7 milliseconds.
3. The 32 MiB JSONL Line Ceiling
Go’s standard library bufio.Scanner defaults to a maximum token
buffer of 64KB. When reading real agent transcripts from Claude Code or
Codex rollout logs, single lines containing whole-file reads or huge git
diffs easily reach 2MB to 10MB. A naive scanner crashes immediately
with bufio.Scanner: token too long.
Thermal implements a specialized streaming scanner constructor
(newJSONLScanner) that configures an explicit 32 MiB buffer
ceiling with bounded worker pools capped at the machine’s CPU
cores, guaranteeing that large agent payloads never abort scans
mid-stream.
4. Disjoint Token Arithmetic: Defeating Double-Counting
A major pitfall in building multi-agent telemetry is that AI providers disagree on token categorization:
- Codex and Grok nest
Reasoningtokens insideOutput, andCacheReadtokens insideInput. - ZCode nests
CacheReadinsideInput. - OpenCode and Claude separate them.
If you write a naive jq or Python script, you end up double-counting
reasoning and cache reads, producing wildly inflated token numbers.
Thermal enforces Disjoint Token Arithmetic directly inside its
loaders: nested tokens are subtracted at ingestion so that:
$$\text{Total Tokens} = \text{Input} + \text{Output} + \text{Reasoning} + \text{CacheRead} + \text{CacheWrite}$$
Renderers, financial aggregators, and pricing engines never have to perform defensive arithmetic adjustments.
5. Repository Attribution via Symlink-Aware Git Walking
Developers rarely run agents from repository roots. You run an agent
from src/components/, pkg/api/, or a symlinked workspace. If a tool
tracks paths naively, your project list fragments into dozens of
isolated subfolders.
Thermal routes every working directory through ProjectKey: it
resolves filesystem symlinks first (preventing macOS /var vs
/private/var duplication), then walks upwards through parent
directories until it finds a .git root. All deep subdirectories fold
cleanly into their canonical repository name without inventing
synthetic hashes.
6. FinOps Pricing via the models.dev Catalog
Tokens are an engineering metric; dollars are an operational reality. Some tools (like OpenCode or Grok) record their exact expenditure directly to disk. Others (like Claude Code or Devin) record raw token counts without dollar figures.
Thermal bridges this gap by prioritizing recorded costs when present,
and falling back to an offline-cached pricing catalog from
models.dev (~/.cache/thermal/models-dev-catalog.json).
It calculates granular rates across input, output, cache-read, and
cache-write tokens, stripping runtime variant suffixes (-high,
-mini) to match base tiers while strictly avoiding fuzzy regex
matching. Crucially, when tokens cannot be attributed to a recognized
model, Thermal prices them at $0.00 and discloses them honestly in the
footer as unattributed tokens, rather than assuming unpriced compute is
free.
The Terminal TUI Experience
I spend my workday in tmux and alacritty. I did not want to leave
the terminal to inspect a browser tab or log into a billing portal.
Using Charm’s Bubble Tea and Lip Gloss, Thermal categorizes agents into two distinct archetypes: Token Warriors (harnesses that report full token telemetry) and Activity Hunters (harnesses that measure turns, prompts, and tool steps).
THERMAL: Don't break the streak.
╭─ Token Warriors ────────────────────────────────── tokens & spend ─╮
│ # Tool Strk Best Days Tokens Cost │
│ ───────────────────────────────────────────────────────────────── │
│ 1. Devin 2d 33d 45d 19.2B tok ~$20134.07 │
│ 2. Codex 2d 7d 48d 1.0B tok ~$527.11 │
│ 3. OpenCode 1d 11d 30d 10.6B tok $62.91 │
│ 4. MiMoCode 1d 7d 16d 1.2B tok $56.14 │
│ 5. Claude 1d 1d 3d 55.5K tok ~$0.16 │
╰────────────────────────────────────────────────────────────────────╯
╭─ Activity Hunters ─────────────────── messages & steps ─╮
│ # Tool Strk Best Days Activity │
│ ─────────────────────────────────────────────────────── │
│ 1. Agy 46d 46d 56d 153.5K step │
│ 2. command-code 3d 9d 48d 10.9K msg │
╰─────────────────────────────────────────────────────────╯
>> Agy is on fire with a 46-day streak!
Running thermal <tool> renders a full 52-week ANSI contribution
heatmap of daily activity, prompt cache hit ratios, and streak
momentum. Subcommands provide immediate period rollups (daily,
weekly, monthly), git-level attribution (projects), and model
distribution audits (models).
What 14 Agent Harnesses Taught Me
Supporting 14 different tools (Devin, OpenCode, MiMoCode, Codex, Claude Code, Antigravity, command-code, codewhale, Droid, DeepSeek, Grok CLI, Muse, Nous Hermes, and ZCode) uncovered three inescapable truths about modern AI engineering:
1. Nobody Uses Just One Tool
Developers pick Claude Code for architectural refactoring, OpenCode for deterministic multi-step pipelines, and fast CLI harnesses for quick script generation. When your tools are fragmented, your mental model of expenditure is nonexistent. You assume you spent five dollars, but across four tools, you quietly spent fifty.
2. Prompt Cache Hit Rates Are the Ultimate FinOps Lever
When an agent harness properly structures its context to take advantage of prompt caching, input token costs drop by up to 90%. Watching your effective cache read rate climb from 25% to 85% in Thermal makes the economic difference tangible: it is the difference between a $0.03 turn and a $0.30 turn on the same model.
3. Visibility Directly Restores Intentionality
The moment you can see your daily token burn rate rendered as an honest terminal heatmap, your habits immediately improve. You stop letting agents loop aimlessly on ambiguous instructions. You write tighter specifications, provide targeted test constraints, and develop an instinct for when to delegate and when to intervene.
Software Sovereignty in the Age of Agents
There is a broader philosophical principle at stake here.
In the 1990s and 2000s, Unix taught us that you own your machine. You
inspect the running processes with ps, monitor memory with top, read
the system logs in /var/log, and maintain total sovereignty over what
executes.
The early AI era attempted to erode that discipline. As I argued in the open-source AI rebellion, we are encouraged to treat foundational models as opaque cloud magic, to stream our intellectual property into proprietary vendor proxies, and to accept whatever invoice arrives at the end of the month without question.
I built Thermal because I believe we can embrace the extraordinary power of autonomous coding agents without surrendering our autonomy as engineers.
I keep thinking back to that night at two in the morning, watching the terminal panes scroll in the quiet of my apartment. There is something exhilarating about watching code emerge from an agent’s reasoning loop. But engineering has never been about surrendering judgment to the machine; it is about understanding the cost, the structure, and the consequences of what we put into the world.
When you can see the numbers clearly, the spell breaks, and the agency returns to your hands. And in an era where software wants to think for you, keeping your eyes wide open might be the most rebellious thing you can do.
How do you keep track of what your autonomous agents are actually doing? Are you measuring your token burn locally, or are you still waiting for the monthly invoice to surprise you?
Thermal is open source and available as a standalone Go binary. If you want to inspect the architecture, run it against your own local agent history, or read the source, you can find it at jadmadi.net/project/thermal/.
Recommended
Continue reading
Selected from shared topics, related tags, and the recent archive.