Why Your Coding Agents Burn Tokens in the Dark

By Jad • • 12 min read
Why Your Coding Agents Burn Tokens in the Dark

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:

  1. Context Accumulation: On step one, the system prompt, tool schemas, and repository summary consume thirty thousand tokens.
  2. Execution Traces: By step five, the agent has executed three failing shell commands. The terminal stdout and stderr now live inside the message history.
  3. Repeated State Inspection: To decide what to do on step six, the model must re-read the entire accumulated dialogue from scratch.
  4. Subagent Spawning: When the primary agent spawns a subagent, a fresh context window opens, often inheriting another slice of the codebase.
  5. 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.sqlite alongside 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:

  1. 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.
  2. Sub-10ms Cached Invocations: In order to fit naturally into interactive developer workflows (even rendering inside a shell prompt like Starship or .zshrc), invoking thermal must return in single-digit milliseconds.
  3. 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 Reasoning tokens inside Output, and CacheRead tokens inside Input.
  • ZCode nests CacheRead inside Input.
  • 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.

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

Selected from shared topics, related tags, and the recent archive.