Repository files navigation

relayburn

Understand how you're spending tokens in agent CLIs. Burn ingests Claude Code, Codex, and OpenCode session logs into a local ledger, then shows cost by model, provider, tool, file, workflow, agent, session, and overhead file.

Quick Start

npm i -g relayburn
burn summary

Burn stores data under ~/.agentworkforce/burn/ by default. Set RELAYBURN_HOME to use a different location.

Commands

CommandUse it to
burn summarySee total usage and cost by model or provider.
burn hotspotsFind expensive files, commands, and subagents.
burn overheadAttribute cached prompt cost to CLAUDE.md, .claude/CLAUDE.md, and AGENTS.md.
burn compareCompare observed model performance by activity: cost per turn, one-shot rate, and sample size.
burn stateInspect, fingerprint, rebuild, prune, or reset local ledger state.
burn sessionsFind recent session IDs for drill-down queries.
burn flowRender a session's inference and subagent flow as Mermaid, SVG, or JSON.
burn stampsExport enrichment stamps as JSONL.
burn ingestImport existing or live session logs without wrapping the harness.
burn mcp-serverExpose read-only cost queries to an agent through stdio MCP.
burn updateCheck for releases, install an update, or configure automatic checks.

Every command accepts --json for machine-readable output, --ledger-path <path> to select a Burn home for that invocation, and --no-color to disable ANSI styling.

burn summary

Use burn summary when you want the fast answer: how many turns ran, how many tokens they used, and what they cost.

OptionWhat it does
--since <range>Limit to a relative range like 24h, 7d, or 4w, or an ISO timestamp.
--project <path>Limit to a project path or git-canonical project key.
--session <id>Limit to one session.
--workflow <id>Limit to turns folded under a workflowId enrichment stamp.
--tag k=vFilter by folded enrichment tag. Repeatable; all tags must match.
--group-by-tag <key>Group totals by a folded enrichment tag value.
--by-providerGroup totals by provider instead of model.
--by-toolAttribute each turn's ingest cost to the preceding tool calls.
--by-subagent-typeGroup totals by subagent type.
--by-relationship [subagent]Group by session relationship, optionally drilling into subagent leaves.
--subagent-tree [session]Render the subagent spawn tree. Uses --session when passed without a value.
--agent <id>Limit subagent-tree or relationship views to one agent.
--provider <csv>Limit results to effective providers.
--qualityAppend one-shot and completion-outcome metrics.
--bucket <duration>Emit fixed-width time buckets across the --since window.
--ingestRun one ingest sweep before querying.
--no-archiveAccepted for CLI parity; it is a no-op because the Rust SDK is SQLite-native.
--jsonEmit machine-readable output.
ExampleResult
burn summaryAll-time cost by model.
burn summary --since 24hCost from the last 24 hours.
burn summary --by-providerCost grouped by effective provider.
burn summary --by-toolCost grouped by the tool calls that preceded each turn.
burn summary --qualityUsage totals with one-shot and completion outcomes.
burn summary --since 24h --bucket 1hHourly usage and cost for the last day.
burn summary --tag persona=code-reviewerCost for sessions stamped with that persona tag.
burn summary --group-by-tag personaCost grouped by persona value.

Synthetic-routed models are recognized from hf:*, accounts/fireworks/models/*, and synthetic/*.

burn hotspots

Use burn hotspots when you want to know what made a session or time window expensive. The default view attributes spend to files, bash commands, and subagents.

OptionWhat it does
--since <range>Limit to a relative range or ISO timestamp.
--project <path>Limit to a project.
--session <id>Limit to a single session id.
--workflow <id>Limit to turns folded under a workflowId enrichment stamp.
--provider <csv>Restrict to providers (case-insensitive CSV — e.g. anthropic,openai).
--allShow every row instead of the top 10.
--group-by <dim>Focus one rollup: attribution, bash, bash-verb, file, or subagent.
--patterns [csv]Run hotspot-pattern detectors instead of the attribution view. Pass without a value to enable every detector, or pass a CSV (e.g. retry-loop,failure-run).
--findingsEmit the unified findings table instead of the per-detector grouping. Implies --patterns if not already set.
--rank-by <cost|bytes>Rank per-tool tables by USD or raw output bytes. Default: cost.
--ingestRun one ingest sweep before querying.
--explain-driftReserved for relationship-drift analysis; currently exits with a directed unsupported message.
--jsonEmit machine-readable output.
ExampleResult
burn hotspots --since 7dTop costly files, bash commands, and subagents for the week.
burn hotspots --all --project .Full project hotspot list.
burn hotspots --group-by bash-verb --since 7dBash verbs ranked by cost.
burn hotspots --session demo-sessionRestrict the standard attribution view to one session.
burn hotspots --patterns retry-loop,failure-runSurface retry/failure hotspot findings only.
burn hotspots --findings --since 7dUnified severity-ranked findings list across every detector.
burn hotspots --provider anthropicRestrict attribution to Anthropic-served turns.
burn hotspots --rank-by bytesSurface large tool outputs even when truncation kept their token count small.

burn overhead

Use burn overhead when you want to know how much standing instruction files cost. Burn discovers CLAUDE.md, .claude/CLAUDE.md, and AGENTS.md, then attributes cached prompt cost to files and headed sections.

OptionWhat it does
trimPrint projected-savings diffs for high-cost headed sections. Burn does not modify files.
deltasAttribute context growth between consecutive inferences to intervening prompts, tool results, reminders, and compactions.
--project <path>Project to inspect. Defaults to the current directory.
--since <range>Limit attribution to a time window.
--kind <k>Limit to claude-md or agents-md.
trim --top <n>Recommendations per file. Default: 3.
deltas --session <id>Limit context deltas to one session.
deltas --top <n>Context-delta row cap. Default: 20.
deltas --min-delta <tokens>Hide smaller increases. Default: 1000; compactions always remain visible.
deltas --owner <all|main|subagent>Select inference rails. Default: all.
deltas --explainExpand the intervening steps behind each delta.
--jsonEmit machine-readable attribution for report mode or structured trim recommendations in trim mode.
ExampleResult
burn overheadCost per overhead file and section.
burn overhead --since 30dOverhead cost from the last 30 days.
burn overhead --kind claude-mdClaude instruction files only.
burn overhead trim --top 3Top three trim recommendations per file.
burn overhead trim --jsonStructured trim recommendations with projected savings and unified diffs.
burn overhead deltas --top 10 --owner mainLargest context-window increases on the main conversation rail.

Harnesses pay for different files: Claude Code pays for CLAUDE.md; Codex and OpenCode pay for AGENTS.md.

burn compare

Use burn compare when you want evidence for model choice. It compares models on the work you actually ran, grouped by activity such as coding, debugging, testing, review, exploration, docs, and refactoring.

OptionWhat it does
<model_a,model_b[,...]>Required comma-separated model list. At least two models.
--since <range>Limit to a relative range or ISO timestamp.
--project <path>Limit to a project.
--session <id>Limit to one session.
--workflow <id>Limit to turns folded with stamp workflowId=<id>.
--agent <id>Limit to turns folded with stamp agentId=<id>.
--provider <csv>Comma-separated effective providers (case-insensitive).
--min-sample <n>Flag cells below the sample threshold. Default: 5.
--fidelity <class>Minimum data quality: full, usage-only, aggregate-only, cost-only, or partial.
--include-partialInclude every turn. Shorthand for --fidelity partial.
--jsonEmit a stable JSON object.
--csvEmit one row per model/activity pair.
--bucket <duration>Emit a time series across --since instead of one comparison.
--no-archiveAccepted for CLI parity; it is a no-op because the Rust SDK is SQLite-native.

burn compare reads the ledger as-is — it does not run an ingest sweep first. Run burn ingest && burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 30d (or keep burn ingest --watch running) when you need the freshest data.

ExampleResult
burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 30dSide-by-side activity table.
burn compare claude-opus-4-7,claude-sonnet-4-6 --project . --jsonProject-scoped JSON comparison.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --fidelity fullCompare only full-fidelity turns.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --include-partialInclude lower-fidelity records too.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 7d --bucket 1dDaily comparison buckets for the last week.

Run burn summary --by-provider to discover model IDs present in your ledger.

burn ingest

Use burn ingest when sessions already exist, or when another process owns the harness spawn. Default mode scans Claude Code, Codex, and OpenCode stores once.

OptionWhat it does
--watchKeep polling session stores in the foreground.
--interval <ms>Poll interval in milliseconds. Default: 1000.
--quietSuppress stderr progress spinner / breadcrumbs. One-shot mode still writes the final summary on stdout.
--hook claudeRead one Claude Code hook payload from stdin and ingest its single transcript via the SDK fast-path.
--no-fseventsIn watch mode, use polling instead of filesystem events.
ExampleResult
burn ingestScan all known session stores once.
burn ingest --watchKeep the ingest loop running.
burn ingest --watch --no-fseventsPoll session stores when filesystem events are unreliable.
burn ingest --hook claude --quietClaude Code hook path for orchestrators.

burn mcp-server

Use burn mcp-server when an agent should query its own spend mid-session via MCP. The server is stdio-only and read-only.

OptionWhat it does
--session-id <uuid>Default session ID used by session cost, summary, and hotspots when the caller omits one.
--debugEmit protocol diagnostics to stderr.
ToolWhat it returns
burn__sessionCostTotal USD, tokens, turns, and models for a session.
burn__fingerprintCheap change-detection fingerprint for the ledger or a session/project scope.
burn__summaryToken use and cost by tool and model, with optional session, project, time, and enrichment filters.
burn__hotspotsAttribution, grouped hotspots, or findings for expensive and repeated activity.
burn__overheadInstruction-file token overhead and cost by file and section.
burn__overheadTrimRanked instruction-file trimming recommendations and projected savings.
burn__comparePer-model, per-activity cost and outcome comparison.
ExampleResult
burn mcp-server --session-id <uuid>Start a stdio server whose session cost, summary, and hotspots tools default to that session.
burn mcp-serverStart an unscoped stdio server; callers can pass filters explicitly, while overhead and compare remain cross-session by design.

burn state

Use burn state when reports look stale or you want to inspect or rebuild derived storage. Burn stores events and stamps in burn.sqlite, and content/search data in content.sqlite.

Subcommand or optionWhat it does
burn state or burn state statusPrint status for indexes, content, classifier, and archive.
--jsonEmit machine-readable output.
fingerprint [--session <id> | --project <path>]Print a low-cost change token for polling the ledger.
rebuild index|classify|content|archive|allDrop derivable rows and stage the ledger for a fresh ingest. All targets use the same SQLite rebuild transaction.
prune [--days <n|forever>]Delete content rows older than the retention window.
reset [--force] [--reingest] [--json]Preview or perform a wipe of derived events, stamps, content, and ingest cursors. Config, pricing overrides, and source harness logs remain.
ExampleResult
burn stateDerived artifact status.
burn state status --jsonMachine-readable status.
burn state fingerprint --project .Project-scoped ledger change token.
burn state rebuild classifyDrop derivable rows so the next ingest applies current classifier rules.
burn state prune --days 30Prune content older than 30 days.

Follow any state rebuild command with burn ingest to repopulate derived tables from the harness session stores. Before a forced reset, back up enrichment with burn stamps export; ingest cannot reconstruct stamps from harness logs.

burn sessions

burn sessions list prints recent sessions newest-first. The default window is seven days and the default limit is 20 rows. Use --project, --grep, --since, and --limit to narrow or widen the list; --json returns the same records for scripts.

burn sessions list --since 30d --limit 10
burn sessions list --project . --json

The full session IDs copy directly into summary --session, hotspots --session, overhead deltas --session, and flow --session.

burn flow

burn flow --session <id> emits a Mermaid inference-flow DAG to stdout. Use --output <path> for SVG, --json for the graph payload, and --max-turns to cap wide sessions (default: 50; 0 disables the cap). --mermaid forces Mermaid on stdout even when --output also writes an SVG.

burn flow --session demo-session --json

burn stamps

burn stamps export streams every enrichment stamp as JSONL. --out <path> writes the same export to a file.

burn stamps export

burn update

burn update --check reports whether a release is available without installing it. Bare burn update installs the latest release through the package manager that installed Burn; --force reinstalls the latest release when already current. Automatic launch checks are controlled by burn update toggle-auto-update --on and burn update toggle-auto-update --off.

burn update --check
burn update toggle-auto-update --off

Local Data

Burn keeps its 2.x ledger in two SQLite databases. Both use WAL mode, so reporting readers can run while ingest writes. burn.sqlite is the event and metadata database; content.sqlite separates larger prompt, response, and search content from the compact analytical rows.

Path or settingPurpose
~/.agentworkforce/burn/burn.sqliteEvents, stamps, sessions, relationships, and archive metadata.
~/.agentworkforce/burn/content.sqlitePrompt/response content and the FTS5 search index.
~/.agentworkforce/burn/config.jsonContent-storage and retention configuration.
~/.agentworkforce/burn/pending-stamps/Temporary manifests used by launchers that do not expose a session ID before spawn.
RELAYBURN_HOMEOverride the whole Burn data directory.
RELAYBURN_SQLITE_PATHOverride the events database path.
RELAYBURN_CONTENT_PATHOverride the content database path.
RELAYBURN_CONTENT_STORE=full|hash-only|offControl content payload storage. Default: full.
RELAYBURN_CONTENT_TTL_DAYS=<days|forever>Content retention. Default: 90.

RELAYBURN_HOME relocates the complete layout. The two per-database overrides can place event and content data on different volumes. SQLite may create -wal and -shm files beside each open database; they are part of normal WAL operation.

Harness transcripts remain the upstream input for ingest. burn state status shows database paths, row counts, schema metadata, and resolved retention. burn state rebuild ... clears derivable tables for re-ingest, burn state prune applies content retention, and burn state reset previews or performs a full derived-state wipe.

Packages

PackagePurpose
relayburnnpm install wrapper that resolves the prebuilt Rust burn binary from @relayburn/cli-<platform> optional dependencies.
@relayburn/sdkNode facade over the Rust SDK, resolved through @relayburn/sdk-<platform> optional dependencies.
@relayburn/cli-<platform>Prebuilt burn binary packages for supported OS/CPU targets.
@relayburn/sdk-<platform>Prebuilt napi-rs packages for supported OS/CPU targets.
relayburn-sdkRust crate with the embedding API and internal reader/ledger/analyze/ingest modules.
relayburn-cliRust crate that produces the burn binary.

Development

Run pnpm run verify to run every check CI runs.

pnpm install
cargo build --workspace
cargo test --workspace
pnpm run test
pnpm run build:napi

The npm workspace contains the Node SDK facade, the relayburn install wrapper, and the platform package manifests used by release automation.

Pricing

Burn ships with a vendored models.dev pricing snapshot. Refresh it with:

pnpm run pricing:update

The weekly Update models.dev pricing workflow runs the same command and opens or updates a review PR when the snapshot changes. Before opening the PR, it checks the upstream payload for regressions and runs the Rust SDK test suite.

User overrides live at $RELAYBURN_HOME/models.dev.json and take precedence at lookup time.

What this is for

Agent spend happens in a blind spot. You can see a daily dollar total, and maybe a breakdown by model. You cannot see which tool call, file, subagent, or workflow step drove the cost. Burn makes that question answerable.

The deeper question burn is built around is:

Would the same work cost less with a different model, harness, or tool choice - in dollars or token usage?

You cannot answer that from aggregate spend. It requires attribution at the level of the actual work: this Read cost $0.47 because it added 8,200 tokens to context that rode in every one of the next 23 turns' cache-reads. Once spend is visible at that grain, the choice between Opus and Haiku, between Claude Code and another harness, or between letting an agent re-read a file and passing it a cached summary, becomes a decision you can reason about instead of a guess.

Three concrete questions follow:

  1. How much did I spend? - per agent, workflow, session, model, and tool call.
  2. Why was it spent? - tool calls, files, subagents, cache-hit vs fresh input, and persistence.
  3. Where can I save? - redundant reads, bloated context prefixes, retry loops, and model choices that cost more than they needed to.

Burn is local-first. Data lives in SQLite databases on your machine. Burn never phones home. Pricing is looked up at query time from a vendored snapshot, so rate corrections never require rewriting the ledger.

What burn is not

  • Not a dashboard or a product with a UI of its own.
  • Not an automatic optimizer. It surfaces the choices; you decide.
  • Not a leaderboard or a social service.

Composability: how burn plugs into a spawner

Burn is designed to be called by whatever code spawns agent sessions. If you control the spawn, you know things the session log never records on its own: the workflow this session is part of, the persona it is running as, the agent ID, and the tier. Burn accepts that context, attaches it to the session, and makes it queryable later alongside the usage data from the session log.

The primitive is stamping: attach metadata to a session by ID, before or after any turns have been recorded. Launchers that do not know the session ID before spawn should call @relayburn/sdkwritePendingStamp() before starting the agent, then run burn ingest / ingest() to fold the tags onto the discovered turns. Direct Rust embedders with an exact session ID can use relayburn_sdk::Stamp and relayburn_sdk::StampSelector against a LedgerHandle.

Stamp selectors:

  • { sessionId } - all turns in a session.
  • { messageId } - exactly one turn.
  • { sessionId, range: { fromTs, toTs } } - a time window, such as a single workflow step within a long-lived session.

Enrichment values are plain strings (Record<string, string>). Burn does not care what keys you use. Typical keys: agentId, parentAgentId, workflowId, stepId, persona, tier, harness, userLabel.

This is the composability surface. Burn stays small; the spawner owns the context and decides what to attach.

Spawner-integrated ingest

The recommended launcher integration is the Node SDK pending-stamp primitive:

import{writePendingStamp}from"@relayburn/sdk";awaitwritePendingStamp({harness: "codex",cwd: process.cwd(),enrichment: {persona: "code-reviewer",personaTier: "senior",agentworkforce: "1",},});

Then spawn the harness normally and let burn ingest or ingest() scan the session stores. Claude launchers can either preallocate --session-id and write an exact session stamp from Rust, or use writePendingStamp({ harness: "claude", ... }) when the final session ID is not available before spawn.

Codex and OpenCode do not expose a pre-spawn session ID. writePendingStamp() writes a pending-stamp manifest under $RELAYBURN_HOME/pending-stamps/ before the launcher spawns the agent. Ingest resolves the manifest against the first matching session file before the first turn is appended. Claude launchers can use the same pending-stamp path when the final session ID is not available before spawn. Abandoned pending manifests are cleaned up after 24 hours.

For passive ingest, run:

burn ingest
burn ingest --watch --interval 1000

burn ingest scans Claude, Codex, and OpenCode stores once and uses the same cursor and dedup path as the reporting commands. burn ingest --watch keeps that scan loop running in the foreground.

Hook-based ingest for orchestrators

If your code already controls the Claude Code spawn, you can install burn's hooks per invocation via Claude's --settings flag without mutating global ~/.claude/settings.json. Wire the hook command to:

burn ingest --hook claude --quiet

Hook payloads land on stdin and get forwarded to burn ingest. The command is safe to re-fire on every hook; the ledger cursor and dedup path keep ingestion idempotent, so the hook path and normal session-store path reconcile against the same session.

FAQ

What does burn ingest do?

Each harness (Claude Code, Codex, OpenCode) writes its own session transcripts to disk in its own format. burn ingest reads those transcripts, normalizes them, and writes them into burn's local SQLite ledger so the query commands (summary, hotspots, overhead, compare) have something to read against. Three modes:

  • One-shotburn ingest scans every known session store once and exits. Good for backfilling or catching up before a query.
  • Watchburn ingest --watch keeps a loop running in the foreground and picks up new turns as harnesses write them.
  • Hookburn ingest --hook claude --quiet reads a single Claude Code hook payload from stdin and ingests the transcript it points at.

What is a "hook"?

Claude Code (and similar harnesses) lets users register shell commands that fire on lifecycle events — turn start, tool use, session end, etc. When an event fires, Claude pipes a JSON payload describing what just happened into the registered command's stdin.

burn ingest --hook claude is burn registering itself as one of those commands. Instead of polling the transcript file looking for new turns, burn gets pushed each turn the moment it lands. Lower latency, no missed turns, no FS-watching contortions.

Why both watch and hook?

  • Hook is the preferred path when the harness supports it: push-based, exact, no polling.
  • Watch is the fallback for harnesses without hooks, or when the user hasn't wired one up. It polls the session store directories on an interval.
  • One-shot is for backfill, cron, or "just give me numbers now."

For most solo users, burn ingest --watch in a background terminal is simplest. The hook path matters most when you're building a launcher that spawns Claude Code programmatically and wants per-invocation control without mutating global config.

How is the hook installed?

Burn does not ship an auto-installer. There are two integration paths:

Per-invocation (orchestrators / launchers). If you control the Claude Code spawn, pass --settings <path> to Claude with a JSON file that registers burn ingest --hook claude --quiet against the hook events you care about. This is the recommended path because it avoids touching global config and stays scoped to the invocation.

Global (manual). Edit ~/.claude/settings.json (or the project-level .claude/settings.json) and add burn ingest --hook claude --quiet to the hook events you want to wire. Burn's hook policy is to always exit 0 so it can never break the surrounding Claude Code session, and payloads missing session_id or transcript_path are ignored, so wiring it onto extra events is safe.

If you don't want to deal with hook config at all, run burn ingest --watch instead — same data, slightly higher latency.

License

Apache-2.0 - Copyright 2026 Agent Workforce Incorporated

About

The first step to burning less tokens is understanding how many you burn to begin with

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

relayburn

Understand how you're spending tokens in agent CLIs. Burn ingests Claude Code, Codex, and OpenCode session logs into a local ledger, then shows cost by model, provider, tool, file, workflow, agent, session, and overhead file.

Quick Start

npm i -g relayburn
burn summary

Burn stores data under ~/.agentworkforce/burn/ by default. Set RELAYBURN_HOME to use a different location.

Commands

CommandUse it to
burn summarySee total usage and cost by model or provider.
burn hotspotsFind expensive files, commands, and subagents.
burn overheadAttribute cached prompt cost to CLAUDE.md, .claude/CLAUDE.md, and AGENTS.md.
burn compareCompare observed model performance by activity: cost per turn, one-shot rate, and sample size.
burn stateInspect, fingerprint, rebuild, prune, or reset local ledger state.
burn sessionsFind recent session IDs for drill-down queries.
burn flowRender a session's inference and subagent flow as Mermaid, SVG, or JSON.
burn stampsExport enrichment stamps as JSONL.
burn ingestImport existing or live session logs without wrapping the harness.
burn mcp-serverExpose read-only cost queries to an agent through stdio MCP.
burn updateCheck for releases, install an update, or configure automatic checks.

Every command accepts --json for machine-readable output, --ledger-path <path> to select a Burn home for that invocation, and --no-color to disable ANSI styling.

burn summary

Use burn summary when you want the fast answer: how many turns ran, how many tokens they used, and what they cost.

OptionWhat it does
--since <range>Limit to a relative range like 24h, 7d, or 4w, or an ISO timestamp.
--project <path>Limit to a project path or git-canonical project key.
--session <id>Limit to one session.
--workflow <id>Limit to turns folded under a workflowId enrichment stamp.
--tag k=vFilter by folded enrichment tag. Repeatable; all tags must match.
--group-by-tag <key>Group totals by a folded enrichment tag value.
--by-providerGroup totals by provider instead of model.
--by-toolAttribute each turn's ingest cost to the preceding tool calls.
--by-subagent-typeGroup totals by subagent type.
--by-relationship [subagent]Group by session relationship, optionally drilling into subagent leaves.
--subagent-tree [session]Render the subagent spawn tree. Uses --session when passed without a value.
--agent <id>Limit subagent-tree or relationship views to one agent.
--provider <csv>Limit results to effective providers.
--qualityAppend one-shot and completion-outcome metrics.
--bucket <duration>Emit fixed-width time buckets across the --since window.
--ingestRun one ingest sweep before querying.
--no-archiveAccepted for CLI parity; it is a no-op because the Rust SDK is SQLite-native.
--jsonEmit machine-readable output.
ExampleResult
burn summaryAll-time cost by model.
burn summary --since 24hCost from the last 24 hours.
burn summary --by-providerCost grouped by effective provider.
burn summary --by-toolCost grouped by the tool calls that preceded each turn.
burn summary --qualityUsage totals with one-shot and completion outcomes.
burn summary --since 24h --bucket 1hHourly usage and cost for the last day.
burn summary --tag persona=code-reviewerCost for sessions stamped with that persona tag.
burn summary --group-by-tag personaCost grouped by persona value.

Synthetic-routed models are recognized from hf:*, accounts/fireworks/models/*, and synthetic/*.

burn hotspots

Use burn hotspots when you want to know what made a session or time window expensive. The default view attributes spend to files, bash commands, and subagents.

OptionWhat it does
--since <range>Limit to a relative range or ISO timestamp.
--project <path>Limit to a project.
--session <id>Limit to a single session id.
--workflow <id>Limit to turns folded under a workflowId enrichment stamp.
--provider <csv>Restrict to providers (case-insensitive CSV — e.g. anthropic,openai).
--allShow every row instead of the top 10.
--group-by <dim>Focus one rollup: attribution, bash, bash-verb, file, or subagent.
--patterns [csv]Run hotspot-pattern detectors instead of the attribution view. Pass without a value to enable every detector, or pass a CSV (e.g. retry-loop,failure-run).
--findingsEmit the unified findings table instead of the per-detector grouping. Implies --patterns if not already set.
--rank-by <cost|bytes>Rank per-tool tables by USD or raw output bytes. Default: cost.
--ingestRun one ingest sweep before querying.
--explain-driftReserved for relationship-drift analysis; currently exits with a directed unsupported message.
--jsonEmit machine-readable output.
ExampleResult
burn hotspots --since 7dTop costly files, bash commands, and subagents for the week.
burn hotspots --all --project .Full project hotspot list.
burn hotspots --group-by bash-verb --since 7dBash verbs ranked by cost.
burn hotspots --session demo-sessionRestrict the standard attribution view to one session.
burn hotspots --patterns retry-loop,failure-runSurface retry/failure hotspot findings only.
burn hotspots --findings --since 7dUnified severity-ranked findings list across every detector.
burn hotspots --provider anthropicRestrict attribution to Anthropic-served turns.
burn hotspots --rank-by bytesSurface large tool outputs even when truncation kept their token count small.

burn overhead

Use burn overhead when you want to know how much standing instruction files cost. Burn discovers CLAUDE.md, .claude/CLAUDE.md, and AGENTS.md, then attributes cached prompt cost to files and headed sections.

OptionWhat it does
trimPrint projected-savings diffs for high-cost headed sections. Burn does not modify files.
deltasAttribute context growth between consecutive inferences to intervening prompts, tool results, reminders, and compactions.
--project <path>Project to inspect. Defaults to the current directory.
--since <range>Limit attribution to a time window.
--kind <k>Limit to claude-md or agents-md.
trim --top <n>Recommendations per file. Default: 3.
deltas --session <id>Limit context deltas to one session.
deltas --top <n>Context-delta row cap. Default: 20.
deltas --min-delta <tokens>Hide smaller increases. Default: 1000; compactions always remain visible.
deltas --owner <all|main|subagent>Select inference rails. Default: all.
deltas --explainExpand the intervening steps behind each delta.
--jsonEmit machine-readable attribution for report mode or structured trim recommendations in trim mode.
ExampleResult
burn overheadCost per overhead file and section.
burn overhead --since 30dOverhead cost from the last 30 days.
burn overhead --kind claude-mdClaude instruction files only.
burn overhead trim --top 3Top three trim recommendations per file.
burn overhead trim --jsonStructured trim recommendations with projected savings and unified diffs.
burn overhead deltas --top 10 --owner mainLargest context-window increases on the main conversation rail.

Harnesses pay for different files: Claude Code pays for CLAUDE.md; Codex and OpenCode pay for AGENTS.md.

burn compare

Use burn compare when you want evidence for model choice. It compares models on the work you actually ran, grouped by activity such as coding, debugging, testing, review, exploration, docs, and refactoring.

OptionWhat it does
<model_a,model_b[,...]>Required comma-separated model list. At least two models.
--since <range>Limit to a relative range or ISO timestamp.
--project <path>Limit to a project.
--session <id>Limit to one session.
--workflow <id>Limit to turns folded with stamp workflowId=<id>.
--agent <id>Limit to turns folded with stamp agentId=<id>.
--provider <csv>Comma-separated effective providers (case-insensitive).
--min-sample <n>Flag cells below the sample threshold. Default: 5.
--fidelity <class>Minimum data quality: full, usage-only, aggregate-only, cost-only, or partial.
--include-partialInclude every turn. Shorthand for --fidelity partial.
--jsonEmit a stable JSON object.
--csvEmit one row per model/activity pair.
--bucket <duration>Emit a time series across --since instead of one comparison.
--no-archiveAccepted for CLI parity; it is a no-op because the Rust SDK is SQLite-native.

burn compare reads the ledger as-is — it does not run an ingest sweep first. Run burn ingest && burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 30d (or keep burn ingest --watch running) when you need the freshest data.

ExampleResult
burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 30dSide-by-side activity table.
burn compare claude-opus-4-7,claude-sonnet-4-6 --project . --jsonProject-scoped JSON comparison.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --fidelity fullCompare only full-fidelity turns.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --include-partialInclude lower-fidelity records too.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 7d --bucket 1dDaily comparison buckets for the last week.

Run burn summary --by-provider to discover model IDs present in your ledger.

burn ingest

Use burn ingest when sessions already exist, or when another process owns the harness spawn. Default mode scans Claude Code, Codex, and OpenCode stores once.

OptionWhat it does
--watchKeep polling session stores in the foreground.
--interval <ms>Poll interval in milliseconds. Default: 1000.
--quietSuppress stderr progress spinner / breadcrumbs. One-shot mode still writes the final summary on stdout.
--hook claudeRead one Claude Code hook payload from stdin and ingest its single transcript via the SDK fast-path.
--no-fseventsIn watch mode, use polling instead of filesystem events.
ExampleResult
burn ingestScan all known session stores once.
burn ingest --watchKeep the ingest loop running.
burn ingest --watch --no-fseventsPoll session stores when filesystem events are unreliable.
burn ingest --hook claude --quietClaude Code hook path for orchestrators.

burn mcp-server

Use burn mcp-server when an agent should query its own spend mid-session via MCP. The server is stdio-only and read-only.

OptionWhat it does
--session-id <uuid>Default session ID used by session cost, summary, and hotspots when the caller omits one.
--debugEmit protocol diagnostics to stderr.
ToolWhat it returns
burn__sessionCostTotal USD, tokens, turns, and models for a session.
burn__fingerprintCheap change-detection fingerprint for the ledger or a session/project scope.
burn__summaryToken use and cost by tool and model, with optional session, project, time, and enrichment filters.
burn__hotspotsAttribution, grouped hotspots, or findings for expensive and repeated activity.
burn__overheadInstruction-file token overhead and cost by file and section.
burn__overheadTrimRanked instruction-file trimming recommendations and projected savings.
burn__comparePer-model, per-activity cost and outcome comparison.
ExampleResult
burn mcp-server --session-id <uuid>Start a stdio server whose session cost, summary, and hotspots tools default to that session.
burn mcp-serverStart an unscoped stdio server; callers can pass filters explicitly, while overhead and compare remain cross-session by design.

burn state

Use burn state when reports look stale or you want to inspect or rebuild derived storage. Burn stores events and stamps in burn.sqlite, and content/search data in content.sqlite.

Subcommand or optionWhat it does
burn state or burn state statusPrint status for indexes, content, classifier, and archive.
--jsonEmit machine-readable output.
fingerprint [--session <id> | --project <path>]Print a low-cost change token for polling the ledger.
rebuild index|classify|content|archive|allDrop derivable rows and stage the ledger for a fresh ingest. All targets use the same SQLite rebuild transaction.
prune [--days <n|forever>]Delete content rows older than the retention window.
reset [--force] [--reingest] [--json]Preview or perform a wipe of derived events, stamps, content, and ingest cursors. Config, pricing overrides, and source harness logs remain.
ExampleResult
burn stateDerived artifact status.
burn state status --jsonMachine-readable status.
burn state fingerprint --project .Project-scoped ledger change token.
burn state rebuild classifyDrop derivable rows so the next ingest applies current classifier rules.
burn state prune --days 30Prune content older than 30 days.

Follow any state rebuild command with burn ingest to repopulate derived tables from the harness session stores. Before a forced reset, back up enrichment with burn stamps export; ingest cannot reconstruct stamps from harness logs.

burn sessions

burn sessions list prints recent sessions newest-first. The default window is seven days and the default limit is 20 rows. Use --project, --grep, --since, and --limit to narrow or widen the list; --json returns the same records for scripts.

burn sessions list --since 30d --limit 10
burn sessions list --project . --json

The full session IDs copy directly into summary --session, hotspots --session, overhead deltas --session, and flow --session.

burn flow

burn flow --session <id> emits a Mermaid inference-flow DAG to stdout. Use --output <path> for SVG, --json for the graph payload, and --max-turns to cap wide sessions (default: 50; 0 disables the cap). --mermaid forces Mermaid on stdout even when --output also writes an SVG.

burn flow --session demo-session --json

burn stamps

burn stamps export streams every enrichment stamp as JSONL. --out <path> writes the same export to a file.

burn stamps export

burn update

burn update --check reports whether a release is available without installing it. Bare burn update installs the latest release through the package manager that installed Burn; --force reinstalls the latest release when already current. Automatic launch checks are controlled by burn update toggle-auto-update --on and burn update toggle-auto-update --off.

burn update --check
burn update toggle-auto-update --off

Local Data

Burn keeps its 2.x ledger in two SQLite databases. Both use WAL mode, so reporting readers can run while ingest writes. burn.sqlite is the event and metadata database; content.sqlite separates larger prompt, response, and search content from the compact analytical rows.

Path or settingPurpose
~/.agentworkforce/burn/burn.sqliteEvents, stamps, sessions, relationships, and archive metadata.
~/.agentworkforce/burn/content.sqlitePrompt/response content and the FTS5 search index.
~/.agentworkforce/burn/config.jsonContent-storage and retention configuration.
~/.agentworkforce/burn/pending-stamps/Temporary manifests used by launchers that do not expose a session ID before spawn.
RELAYBURN_HOMEOverride the whole Burn data directory.
RELAYBURN_SQLITE_PATHOverride the events database path.
RELAYBURN_CONTENT_PATHOverride the content database path.
RELAYBURN_CONTENT_STORE=full|hash-only|offControl content payload storage. Default: full.
RELAYBURN_CONTENT_TTL_DAYS=<days|forever>Content retention. Default: 90.

RELAYBURN_HOME relocates the complete layout. The two per-database overrides can place event and content data on different volumes. SQLite may create -wal and -shm files beside each open database; they are part of normal WAL operation.

Harness transcripts remain the upstream input for ingest. burn state status shows database paths, row counts, schema metadata, and resolved retention. burn state rebuild ... clears derivable tables for re-ingest, burn state prune applies content retention, and burn state reset previews or performs a full derived-state wipe.

Packages

PackagePurpose
relayburnnpm install wrapper that resolves the prebuilt Rust burn binary from @relayburn/cli-<platform> optional dependencies.
@relayburn/sdkNode facade over the Rust SDK, resolved through @relayburn/sdk-<platform> optional dependencies.
@relayburn/cli-<platform>Prebuilt burn binary packages for supported OS/CPU targets.
@relayburn/sdk-<platform>Prebuilt napi-rs packages for supported OS/CPU targets.
relayburn-sdkRust crate with the embedding API and internal reader/ledger/analyze/ingest modules.
relayburn-cliRust crate that produces the burn binary.

Development

Run pnpm run verify to run every check CI runs.

pnpm install
cargo build --workspace
cargo test --workspace
pnpm run test
pnpm run build:napi

The npm workspace contains the Node SDK facade, the relayburn install wrapper, and the platform package manifests used by release automation.

Pricing

Burn ships with a vendored models.dev pricing snapshot. Refresh it with:

pnpm run pricing:update

The weekly Update models.dev pricing workflow runs the same command and opens or updates a review PR when the snapshot changes. Before opening the PR, it checks the upstream payload for regressions and runs the Rust SDK test suite.

User overrides live at $RELAYBURN_HOME/models.dev.json and take precedence at lookup time.

What this is for

Agent spend happens in a blind spot. You can see a daily dollar total, and maybe a breakdown by model. You cannot see which tool call, file, subagent, or workflow step drove the cost. Burn makes that question answerable.

The deeper question burn is built around is:

Would the same work cost less with a different model, harness, or tool choice - in dollars or token usage?

You cannot answer that from aggregate spend. It requires attribution at the level of the actual work: this Read cost $0.47 because it added 8,200 tokens to context that rode in every one of the next 23 turns' cache-reads. Once spend is visible at that grain, the choice between Opus and Haiku, between Claude Code and another harness, or between letting an agent re-read a file and passing it a cached summary, becomes a decision you can reason about instead of a guess.

Three concrete questions follow:

  1. How much did I spend? - per agent, workflow, session, model, and tool call.
  2. Why was it spent? - tool calls, files, subagents, cache-hit vs fresh input, and persistence.
  3. Where can I save? - redundant reads, bloated context prefixes, retry loops, and model choices that cost more than they needed to.

Burn is local-first. Data lives in SQLite databases on your machine. Burn never phones home. Pricing is looked up at query time from a vendored snapshot, so rate corrections never require rewriting the ledger.

What burn is not

  • Not a dashboard or a product with a UI of its own.
  • Not an automatic optimizer. It surfaces the choices; you decide.
  • Not a leaderboard or a social service.

Composability: how burn plugs into a spawner

Burn is designed to be called by whatever code spawns agent sessions. If you control the spawn, you know things the session log never records on its own: the workflow this session is part of, the persona it is running as, the agent ID, and the tier. Burn accepts that context, attaches it to the session, and makes it queryable later alongside the usage data from the session log.

The primitive is stamping: attach metadata to a session by ID, before or after any turns have been recorded. Launchers that do not know the session ID before spawn should call @relayburn/sdkwritePendingStamp() before starting the agent, then run burn ingest / ingest() to fold the tags onto the discovered turns. Direct Rust embedders with an exact session ID can use relayburn_sdk::Stamp and relayburn_sdk::StampSelector against a LedgerHandle.

Stamp selectors:

  • { sessionId } - all turns in a session.
  • { messageId } - exactly one turn.
  • { sessionId, range: { fromTs, toTs } } - a time window, such as a single workflow step within a long-lived session.

Enrichment values are plain strings (Record<string, string>). Burn does not care what keys you use. Typical keys: agentId, parentAgentId, workflowId, stepId, persona, tier, harness, userLabel.

This is the composability surface. Burn stays small; the spawner owns the context and decides what to attach.

Spawner-integrated ingest

The recommended launcher integration is the Node SDK pending-stamp primitive:

import{writePendingStamp}from"@relayburn/sdk";awaitwritePendingStamp({harness: "codex",cwd: process.cwd(),enrichment: {persona: "code-reviewer",personaTier: "senior",agentworkforce: "1",},});

Then spawn the harness normally and let burn ingest or ingest() scan the session stores. Claude launchers can either preallocate --session-id and write an exact session stamp from Rust, or use writePendingStamp({ harness: "claude", ... }) when the final session ID is not available before spawn.

Codex and OpenCode do not expose a pre-spawn session ID. writePendingStamp() writes a pending-stamp manifest under $RELAYBURN_HOME/pending-stamps/ before the launcher spawns the agent. Ingest resolves the manifest against the first matching session file before the first turn is appended. Claude launchers can use the same pending-stamp path when the final session ID is not available before spawn. Abandoned pending manifests are cleaned up after 24 hours.

For passive ingest, run:

burn ingest
burn ingest --watch --interval 1000

burn ingest scans Claude, Codex, and OpenCode stores once and uses the same cursor and dedup path as the reporting commands. burn ingest --watch keeps that scan loop running in the foreground.

Hook-based ingest for orchestrators

If your code already controls the Claude Code spawn, you can install burn's hooks per invocation via Claude's --settings flag without mutating global ~/.claude/settings.json. Wire the hook command to:

burn ingest --hook claude --quiet

Hook payloads land on stdin and get forwarded to burn ingest. The command is safe to re-fire on every hook; the ledger cursor and dedup path keep ingestion idempotent, so the hook path and normal session-store path reconcile against the same session.

FAQ

What does burn ingest do?

Each harness (Claude Code, Codex, OpenCode) writes its own session transcripts to disk in its own format. burn ingest reads those transcripts, normalizes them, and writes them into burn's local SQLite ledger so the query commands (summary, hotspots, overhead, compare) have something to read against. Three modes:

  • One-shotburn ingest scans every known session store once and exits. Good for backfilling or catching up before a query.
  • Watchburn ingest --watch keeps a loop running in the foreground and picks up new turns as harnesses write them.
  • Hookburn ingest --hook claude --quiet reads a single Claude Code hook payload from stdin and ingests the transcript it points at.

What is a "hook"?

Claude Code (and similar harnesses) lets users register shell commands that fire on lifecycle events — turn start, tool use, session end, etc. When an event fires, Claude pipes a JSON payload describing what just happened into the registered command's stdin.

burn ingest --hook claude is burn registering itself as one of those commands. Instead of polling the transcript file looking for new turns, burn gets pushed each turn the moment it lands. Lower latency, no missed turns, no FS-watching contortions.

Why both watch and hook?

  • Hook is the preferred path when the harness supports it: push-based, exact, no polling.
  • Watch is the fallback for harnesses without hooks, or when the user hasn't wired one up. It polls the session store directories on an interval.
  • One-shot is for backfill, cron, or "just give me numbers now."

For most solo users, burn ingest --watch in a background terminal is simplest. The hook path matters most when you're building a launcher that spawns Claude Code programmatically and wants per-invocation control without mutating global config.

How is the hook installed?

Burn does not ship an auto-installer. There are two integration paths:

Per-invocation (orchestrators / launchers). If you control the Claude Code spawn, pass --settings <path> to Claude with a JSON file that registers burn ingest --hook claude --quiet against the hook events you care about. This is the recommended path because it avoids touching global config and stays scoped to the invocation.

Global (manual). Edit ~/.claude/settings.json (or the project-level .claude/settings.json) and add burn ingest --hook claude --quiet to the hook events you want to wire. Burn's hook policy is to always exit 0 so it can never break the surrounding Claude Code session, and payloads missing session_id or transcript_path are ignored, so wiring it onto extra events is safe.

If you don't want to deal with hook config at all, run burn ingest --watch instead — same data, slightly higher latency.

License

Apache-2.0 - Copyright 2026 Agent Workforce Incorporated

About

The first step to burning less tokens is understanding how many you burn to begin with

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

relayburn

Understand how you're spending tokens in agent CLIs. Burn ingests Claude Code, Codex, and OpenCode session logs into a local ledger, then shows cost by model, provider, tool, file, workflow, agent, session, and overhead file.

Quick Start

npm i -g relayburn
burn summary

Burn stores data under ~/.agentworkforce/burn/ by default. Set RELAYBURN_HOME to use a different location.

Commands

CommandUse it to
burn summarySee total usage and cost by model or provider.
burn hotspotsFind expensive files, commands, and subagents.
burn overheadAttribute cached prompt cost to CLAUDE.md, .claude/CLAUDE.md, and AGENTS.md.
burn compareCompare observed model performance by activity: cost per turn, one-shot rate, and sample size.
burn stateInspect, fingerprint, rebuild, prune, or reset local ledger state.
burn sessionsFind recent session IDs for drill-down queries.
burn flowRender a session's inference and subagent flow as Mermaid, SVG, or JSON.
burn stampsExport enrichment stamps as JSONL.
burn ingestImport existing or live session logs without wrapping the harness.
burn mcp-serverExpose read-only cost queries to an agent through stdio MCP.
burn updateCheck for releases, install an update, or configure automatic checks.

Every command accepts --json for machine-readable output, --ledger-path <path> to select a Burn home for that invocation, and --no-color to disable ANSI styling.

burn summary

Use burn summary when you want the fast answer: how many turns ran, how many tokens they used, and what they cost.

OptionWhat it does
--since <range>Limit to a relative range like 24h, 7d, or 4w, or an ISO timestamp.
--project <path>Limit to a project path or git-canonical project key.
--session <id>Limit to one session.
--workflow <id>Limit to turns folded under a workflowId enrichment stamp.
--tag k=vFilter by folded enrichment tag. Repeatable; all tags must match.
--group-by-tag <key>Group totals by a folded enrichment tag value.
--by-providerGroup totals by provider instead of model.
--by-toolAttribute each turn's ingest cost to the preceding tool calls.
--by-subagent-typeGroup totals by subagent type.
--by-relationship [subagent]Group by session relationship, optionally drilling into subagent leaves.
--subagent-tree [session]Render the subagent spawn tree. Uses --session when passed without a value.
--agent <id>Limit subagent-tree or relationship views to one agent.
--provider <csv>Limit results to effective providers.
--qualityAppend one-shot and completion-outcome metrics.
--bucket <duration>Emit fixed-width time buckets across the --since window.
--ingestRun one ingest sweep before querying.
--no-archiveAccepted for CLI parity; it is a no-op because the Rust SDK is SQLite-native.
--jsonEmit machine-readable output.
ExampleResult
burn summaryAll-time cost by model.
burn summary --since 24hCost from the last 24 hours.
burn summary --by-providerCost grouped by effective provider.
burn summary --by-toolCost grouped by the tool calls that preceded each turn.
burn summary --qualityUsage totals with one-shot and completion outcomes.
burn summary --since 24h --bucket 1hHourly usage and cost for the last day.
burn summary --tag persona=code-reviewerCost for sessions stamped with that persona tag.
burn summary --group-by-tag personaCost grouped by persona value.

Synthetic-routed models are recognized from hf:*, accounts/fireworks/models/*, and synthetic/*.

burn hotspots

Use burn hotspots when you want to know what made a session or time window expensive. The default view attributes spend to files, bash commands, and subagents.

OptionWhat it does
--since <range>Limit to a relative range or ISO timestamp.
--project <path>Limit to a project.
--session <id>Limit to a single session id.
--workflow <id>Limit to turns folded under a workflowId enrichment stamp.
--provider <csv>Restrict to providers (case-insensitive CSV — e.g. anthropic,openai).
--allShow every row instead of the top 10.
--group-by <dim>Focus one rollup: attribution, bash, bash-verb, file, or subagent.
--patterns [csv]Run hotspot-pattern detectors instead of the attribution view. Pass without a value to enable every detector, or pass a CSV (e.g. retry-loop,failure-run).
--findingsEmit the unified findings table instead of the per-detector grouping. Implies --patterns if not already set.
--rank-by <cost|bytes>Rank per-tool tables by USD or raw output bytes. Default: cost.
--ingestRun one ingest sweep before querying.
--explain-driftReserved for relationship-drift analysis; currently exits with a directed unsupported message.
--jsonEmit machine-readable output.
ExampleResult
burn hotspots --since 7dTop costly files, bash commands, and subagents for the week.
burn hotspots --all --project .Full project hotspot list.
burn hotspots --group-by bash-verb --since 7dBash verbs ranked by cost.
burn hotspots --session demo-sessionRestrict the standard attribution view to one session.
burn hotspots --patterns retry-loop,failure-runSurface retry/failure hotspot findings only.
burn hotspots --findings --since 7dUnified severity-ranked findings list across every detector.
burn hotspots --provider anthropicRestrict attribution to Anthropic-served turns.
burn hotspots --rank-by bytesSurface large tool outputs even when truncation kept their token count small.

burn overhead

Use burn overhead when you want to know how much standing instruction files cost. Burn discovers CLAUDE.md, .claude/CLAUDE.md, and AGENTS.md, then attributes cached prompt cost to files and headed sections.

OptionWhat it does
trimPrint projected-savings diffs for high-cost headed sections. Burn does not modify files.
deltasAttribute context growth between consecutive inferences to intervening prompts, tool results, reminders, and compactions.
--project <path>Project to inspect. Defaults to the current directory.
--since <range>Limit attribution to a time window.
--kind <k>Limit to claude-md or agents-md.
trim --top <n>Recommendations per file. Default: 3.
deltas --session <id>Limit context deltas to one session.
deltas --top <n>Context-delta row cap. Default: 20.
deltas --min-delta <tokens>Hide smaller increases. Default: 1000; compactions always remain visible.
deltas --owner <all|main|subagent>Select inference rails. Default: all.
deltas --explainExpand the intervening steps behind each delta.
--jsonEmit machine-readable attribution for report mode or structured trim recommendations in trim mode.
ExampleResult
burn overheadCost per overhead file and section.
burn overhead --since 30dOverhead cost from the last 30 days.
burn overhead --kind claude-mdClaude instruction files only.
burn overhead trim --top 3Top three trim recommendations per file.
burn overhead trim --jsonStructured trim recommendations with projected savings and unified diffs.
burn overhead deltas --top 10 --owner mainLargest context-window increases on the main conversation rail.

Harnesses pay for different files: Claude Code pays for CLAUDE.md; Codex and OpenCode pay for AGENTS.md.

burn compare

Use burn compare when you want evidence for model choice. It compares models on the work you actually ran, grouped by activity such as coding, debugging, testing, review, exploration, docs, and refactoring.

OptionWhat it does
<model_a,model_b[,...]>Required comma-separated model list. At least two models.
--since <range>Limit to a relative range or ISO timestamp.
--project <path>Limit to a project.
--session <id>Limit to one session.
--workflow <id>Limit to turns folded with stamp workflowId=<id>.
--agent <id>Limit to turns folded with stamp agentId=<id>.
--provider <csv>Comma-separated effective providers (case-insensitive).
--min-sample <n>Flag cells below the sample threshold. Default: 5.
--fidelity <class>Minimum data quality: full, usage-only, aggregate-only, cost-only, or partial.
--include-partialInclude every turn. Shorthand for --fidelity partial.
--jsonEmit a stable JSON object.
--csvEmit one row per model/activity pair.
--bucket <duration>Emit a time series across --since instead of one comparison.
--no-archiveAccepted for CLI parity; it is a no-op because the Rust SDK is SQLite-native.

burn compare reads the ledger as-is — it does not run an ingest sweep first. Run burn ingest && burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 30d (or keep burn ingest --watch running) when you need the freshest data.

ExampleResult
burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 30dSide-by-side activity table.
burn compare claude-opus-4-7,claude-sonnet-4-6 --project . --jsonProject-scoped JSON comparison.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --fidelity fullCompare only full-fidelity turns.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --include-partialInclude lower-fidelity records too.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 7d --bucket 1dDaily comparison buckets for the last week.

Run burn summary --by-provider to discover model IDs present in your ledger.

burn ingest

Use burn ingest when sessions already exist, or when another process owns the harness spawn. Default mode scans Claude Code, Codex, and OpenCode stores once.

OptionWhat it does
--watchKeep polling session stores in the foreground.
--interval <ms>Poll interval in milliseconds. Default: 1000.
--quietSuppress stderr progress spinner / breadcrumbs. One-shot mode still writes the final summary on stdout.
--hook claudeRead one Claude Code hook payload from stdin and ingest its single transcript via the SDK fast-path.
--no-fseventsIn watch mode, use polling instead of filesystem events.
ExampleResult
burn ingestScan all known session stores once.
burn ingest --watchKeep the ingest loop running.
burn ingest --watch --no-fseventsPoll session stores when filesystem events are unreliable.
burn ingest --hook claude --quietClaude Code hook path for orchestrators.

burn mcp-server

Use burn mcp-server when an agent should query its own spend mid-session via MCP. The server is stdio-only and read-only.

OptionWhat it does
--session-id <uuid>Default session ID used by session cost, summary, and hotspots when the caller omits one.
--debugEmit protocol diagnostics to stderr.
ToolWhat it returns
burn__sessionCostTotal USD, tokens, turns, and models for a session.
burn__fingerprintCheap change-detection fingerprint for the ledger or a session/project scope.
burn__summaryToken use and cost by tool and model, with optional session, project, time, and enrichment filters.
burn__hotspotsAttribution, grouped hotspots, or findings for expensive and repeated activity.
burn__overheadInstruction-file token overhead and cost by file and section.
burn__overheadTrimRanked instruction-file trimming recommendations and projected savings.
burn__comparePer-model, per-activity cost and outcome comparison.
ExampleResult
burn mcp-server --session-id <uuid>Start a stdio server whose session cost, summary, and hotspots tools default to that session.
burn mcp-serverStart an unscoped stdio server; callers can pass filters explicitly, while overhead and compare remain cross-session by design.

burn state

Use burn state when reports look stale or you want to inspect or rebuild derived storage. Burn stores events and stamps in burn.sqlite, and content/search data in content.sqlite.

Subcommand or optionWhat it does
burn state or burn state statusPrint status for indexes, content, classifier, and archive.
--jsonEmit machine-readable output.
fingerprint [--session <id> | --project <path>]Print a low-cost change token for polling the ledger.
rebuild index|classify|content|archive|allDrop derivable rows and stage the ledger for a fresh ingest. All targets use the same SQLite rebuild transaction.
prune [--days <n|forever>]Delete content rows older than the retention window.
reset [--force] [--reingest] [--json]Preview or perform a wipe of derived events, stamps, content, and ingest cursors. Config, pricing overrides, and source harness logs remain.
ExampleResult
burn stateDerived artifact status.
burn state status --jsonMachine-readable status.
burn state fingerprint --project .Project-scoped ledger change token.
burn state rebuild classifyDrop derivable rows so the next ingest applies current classifier rules.
burn state prune --days 30Prune content older than 30 days.

Follow any state rebuild command with burn ingest to repopulate derived tables from the harness session stores. Before a forced reset, back up enrichment with burn stamps export; ingest cannot reconstruct stamps from harness logs.

burn sessions

burn sessions list prints recent sessions newest-first. The default window is seven days and the default limit is 20 rows. Use --project, --grep, --since, and --limit to narrow or widen the list; --json returns the same records for scripts.

burn sessions list --since 30d --limit 10
burn sessions list --project . --json

The full session IDs copy directly into summary --session, hotspots --session, overhead deltas --session, and flow --session.

burn flow

burn flow --session <id> emits a Mermaid inference-flow DAG to stdout. Use --output <path> for SVG, --json for the graph payload, and --max-turns to cap wide sessions (default: 50; 0 disables the cap). --mermaid forces Mermaid on stdout even when --output also writes an SVG.

burn flow --session demo-session --json

burn stamps

burn stamps export streams every enrichment stamp as JSONL. --out <path> writes the same export to a file.

burn stamps export

burn update

burn update --check reports whether a release is available without installing it. Bare burn update installs the latest release through the package manager that installed Burn; --force reinstalls the latest release when already current. Automatic launch checks are controlled by burn update toggle-auto-update --on and burn update toggle-auto-update --off.

burn update --check
burn update toggle-auto-update --off

Local Data

Burn keeps its 2.x ledger in two SQLite databases. Both use WAL mode, so reporting readers can run while ingest writes. burn.sqlite is the event and metadata database; content.sqlite separates larger prompt, response, and search content from the compact analytical rows.

Path or settingPurpose
~/.agentworkforce/burn/burn.sqliteEvents, stamps, sessions, relationships, and archive metadata.
~/.agentworkforce/burn/content.sqlitePrompt/response content and the FTS5 search index.
~/.agentworkforce/burn/config.jsonContent-storage and retention configuration.
~/.agentworkforce/burn/pending-stamps/Temporary manifests used by launchers that do not expose a session ID before spawn.
RELAYBURN_HOMEOverride the whole Burn data directory.
RELAYBURN_SQLITE_PATHOverride the events database path.
RELAYBURN_CONTENT_PATHOverride the content database path.
RELAYBURN_CONTENT_STORE=full|hash-only|offControl content payload storage. Default: full.
RELAYBURN_CONTENT_TTL_DAYS=<days|forever>Content retention. Default: 90.

RELAYBURN_HOME relocates the complete layout. The two per-database overrides can place event and content data on different volumes. SQLite may create -wal and -shm files beside each open database; they are part of normal WAL operation.

Harness transcripts remain the upstream input for ingest. burn state status shows database paths, row counts, schema metadata, and resolved retention. burn state rebuild ... clears derivable tables for re-ingest, burn state prune applies content retention, and burn state reset previews or performs a full derived-state wipe.

Packages

PackagePurpose
relayburnnpm install wrapper that resolves the prebuilt Rust burn binary from @relayburn/cli-<platform> optional dependencies.
@relayburn/sdkNode facade over the Rust SDK, resolved through @relayburn/sdk-<platform> optional dependencies.
@relayburn/cli-<platform>Prebuilt burn binary packages for supported OS/CPU targets.
@relayburn/sdk-<platform>Prebuilt napi-rs packages for supported OS/CPU targets.
relayburn-sdkRust crate with the embedding API and internal reader/ledger/analyze/ingest modules.
relayburn-cliRust crate that produces the burn binary.

Development

Run pnpm run verify to run every check CI runs.

pnpm install
cargo build --workspace
cargo test --workspace
pnpm run test
pnpm run build:napi

The npm workspace contains the Node SDK facade, the relayburn install wrapper, and the platform package manifests used by release automation.

Pricing

Burn ships with a vendored models.dev pricing snapshot. Refresh it with:

pnpm run pricing:update

The weekly Update models.dev pricing workflow runs the same command and opens or updates a review PR when the snapshot changes. Before opening the PR, it checks the upstream payload for regressions and runs the Rust SDK test suite.

User overrides live at $RELAYBURN_HOME/models.dev.json and take precedence at lookup time.

What this is for

Agent spend happens in a blind spot. You can see a daily dollar total, and maybe a breakdown by model. You cannot see which tool call, file, subagent, or workflow step drove the cost. Burn makes that question answerable.

The deeper question burn is built around is:

Would the same work cost less with a different model, harness, or tool choice - in dollars or token usage?

You cannot answer that from aggregate spend. It requires attribution at the level of the actual work: this Read cost $0.47 because it added 8,200 tokens to context that rode in every one of the next 23 turns' cache-reads. Once spend is visible at that grain, the choice between Opus and Haiku, between Claude Code and another harness, or between letting an agent re-read a file and passing it a cached summary, becomes a decision you can reason about instead of a guess.

Three concrete questions follow:

  1. How much did I spend? - per agent, workflow, session, model, and tool call.
  2. Why was it spent? - tool calls, files, subagents, cache-hit vs fresh input, and persistence.
  3. Where can I save? - redundant reads, bloated context prefixes, retry loops, and model choices that cost more than they needed to.

Burn is local-first. Data lives in SQLite databases on your machine. Burn never phones home. Pricing is looked up at query time from a vendored snapshot, so rate corrections never require rewriting the ledger.

What burn is not

  • Not a dashboard or a product with a UI of its own.
  • Not an automatic optimizer. It surfaces the choices; you decide.
  • Not a leaderboard or a social service.

Composability: how burn plugs into a spawner

Burn is designed to be called by whatever code spawns agent sessions. If you control the spawn, you know things the session log never records on its own: the workflow this session is part of, the persona it is running as, the agent ID, and the tier. Burn accepts that context, attaches it to the session, and makes it queryable later alongside the usage data from the session log.

The primitive is stamping: attach metadata to a session by ID, before or after any turns have been recorded. Launchers that do not know the session ID before spawn should call @relayburn/sdkwritePendingStamp() before starting the agent, then run burn ingest / ingest() to fold the tags onto the discovered turns. Direct Rust embedders with an exact session ID can use relayburn_sdk::Stamp and relayburn_sdk::StampSelector against a LedgerHandle.

Stamp selectors:

  • { sessionId } - all turns in a session.
  • { messageId } - exactly one turn.
  • { sessionId, range: { fromTs, toTs } } - a time window, such as a single workflow step within a long-lived session.

Enrichment values are plain strings (Record<string, string>). Burn does not care what keys you use. Typical keys: agentId, parentAgentId, workflowId, stepId, persona, tier, harness, userLabel.

This is the composability surface. Burn stays small; the spawner owns the context and decides what to attach.

Spawner-integrated ingest

The recommended launcher integration is the Node SDK pending-stamp primitive:

import{writePendingStamp}from"@relayburn/sdk";awaitwritePendingStamp({harness: "codex",cwd: process.cwd(),enrichment: {persona: "code-reviewer",personaTier: "senior",agentworkforce: "1",},});

Then spawn the harness normally and let burn ingest or ingest() scan the session stores. Claude launchers can either preallocate --session-id and write an exact session stamp from Rust, or use writePendingStamp({ harness: "claude", ... }) when the final session ID is not available before spawn.

Codex and OpenCode do not expose a pre-spawn session ID. writePendingStamp() writes a pending-stamp manifest under $RELAYBURN_HOME/pending-stamps/ before the launcher spawns the agent. Ingest resolves the manifest against the first matching session file before the first turn is appended. Claude launchers can use the same pending-stamp path when the final session ID is not available before spawn. Abandoned pending manifests are cleaned up after 24 hours.

For passive ingest, run:

burn ingest
burn ingest --watch --interval 1000

burn ingest scans Claude, Codex, and OpenCode stores once and uses the same cursor and dedup path as the reporting commands. burn ingest --watch keeps that scan loop running in the foreground.

Hook-based ingest for orchestrators

If your code already controls the Claude Code spawn, you can install burn's hooks per invocation via Claude's --settings flag without mutating global ~/.claude/settings.json. Wire the hook command to:

burn ingest --hook claude --quiet

Hook payloads land on stdin and get forwarded to burn ingest. The command is safe to re-fire on every hook; the ledger cursor and dedup path keep ingestion idempotent, so the hook path and normal session-store path reconcile against the same session.

FAQ

What does burn ingest do?

Each harness (Claude Code, Codex, OpenCode) writes its own session transcripts to disk in its own format. burn ingest reads those transcripts, normalizes them, and writes them into burn's local SQLite ledger so the query commands (summary, hotspots, overhead, compare) have something to read against. Three modes:

  • One-shotburn ingest scans every known session store once and exits. Good for backfilling or catching up before a query.
  • Watchburn ingest --watch keeps a loop running in the foreground and picks up new turns as harnesses write them.
  • Hookburn ingest --hook claude --quiet reads a single Claude Code hook payload from stdin and ingests the transcript it points at.

What is a "hook"?

Claude Code (and similar harnesses) lets users register shell commands that fire on lifecycle events — turn start, tool use, session end, etc. When an event fires, Claude pipes a JSON payload describing what just happened into the registered command's stdin.

burn ingest --hook claude is burn registering itself as one of those commands. Instead of polling the transcript file looking for new turns, burn gets pushed each turn the moment it lands. Lower latency, no missed turns, no FS-watching contortions.

Why both watch and hook?

  • Hook is the preferred path when the harness supports it: push-based, exact, no polling.
  • Watch is the fallback for harnesses without hooks, or when the user hasn't wired one up. It polls the session store directories on an interval.
  • One-shot is for backfill, cron, or "just give me numbers now."

For most solo users, burn ingest --watch in a background terminal is simplest. The hook path matters most when you're building a launcher that spawns Claude Code programmatically and wants per-invocation control without mutating global config.

How is the hook installed?

Burn does not ship an auto-installer. There are two integration paths:

Per-invocation (orchestrators / launchers). If you control the Claude Code spawn, pass --settings <path> to Claude with a JSON file that registers burn ingest --hook claude --quiet against the hook events you care about. This is the recommended path because it avoids touching global config and stays scoped to the invocation.

Global (manual). Edit ~/.claude/settings.json (or the project-level .claude/settings.json) and add burn ingest --hook claude --quiet to the hook events you want to wire. Burn's hook policy is to always exit 0 so it can never break the surrounding Claude Code session, and payloads missing session_id or transcript_path are ignored, so wiring it onto extra events is safe.

If you don't want to deal with hook config at all, run burn ingest --watch instead — same data, slightly higher latency.

License

Apache-2.0 - Copyright 2026 Agent Workforce Incorporated

About

The first step to burning less tokens is understanding how many you burn to begin with

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

relayburn

Understand how you're spending tokens in agent CLIs. Burn ingests Claude Code, Codex, and OpenCode session logs into a local ledger, then shows cost by model, provider, tool, file, workflow, agent, session, and overhead file.

Quick Start

npm i -g relayburn
burn summary

Burn stores data under ~/.agentworkforce/burn/ by default. Set RELAYBURN_HOME to use a different location.

Commands

CommandUse it to
burn summarySee total usage and cost by model or provider.
burn hotspotsFind expensive files, commands, and subagents.
burn overheadAttribute cached prompt cost to CLAUDE.md, .claude/CLAUDE.md, and AGENTS.md.
burn compareCompare observed model performance by activity: cost per turn, one-shot rate, and sample size.
burn stateInspect, fingerprint, rebuild, prune, or reset local ledger state.
burn sessionsFind recent session IDs for drill-down queries.
burn flowRender a session's inference and subagent flow as Mermaid, SVG, or JSON.
burn stampsExport enrichment stamps as JSONL.
burn ingestImport existing or live session logs without wrapping the harness.
burn mcp-serverExpose read-only cost queries to an agent through stdio MCP.
burn updateCheck for releases, install an update, or configure automatic checks.

Every command accepts --json for machine-readable output, --ledger-path <path> to select a Burn home for that invocation, and --no-color to disable ANSI styling.

burn summary

Use burn summary when you want the fast answer: how many turns ran, how many tokens they used, and what they cost.

OptionWhat it does
--since <range>Limit to a relative range like 24h, 7d, or 4w, or an ISO timestamp.
--project <path>Limit to a project path or git-canonical project key.
--session <id>Limit to one session.
--workflow <id>Limit to turns folded under a workflowId enrichment stamp.
--tag k=vFilter by folded enrichment tag. Repeatable; all tags must match.
--group-by-tag <key>Group totals by a folded enrichment tag value.
--by-providerGroup totals by provider instead of model.
--by-toolAttribute each turn's ingest cost to the preceding tool calls.
--by-subagent-typeGroup totals by subagent type.
--by-relationship [subagent]Group by session relationship, optionally drilling into subagent leaves.
--subagent-tree [session]Render the subagent spawn tree. Uses --session when passed without a value.
--agent <id>Limit subagent-tree or relationship views to one agent.
--provider <csv>Limit results to effective providers.
--qualityAppend one-shot and completion-outcome metrics.
--bucket <duration>Emit fixed-width time buckets across the --since window.
--ingestRun one ingest sweep before querying.
--no-archiveAccepted for CLI parity; it is a no-op because the Rust SDK is SQLite-native.
--jsonEmit machine-readable output.
ExampleResult
burn summaryAll-time cost by model.
burn summary --since 24hCost from the last 24 hours.
burn summary --by-providerCost grouped by effective provider.
burn summary --by-toolCost grouped by the tool calls that preceded each turn.
burn summary --qualityUsage totals with one-shot and completion outcomes.
burn summary --since 24h --bucket 1hHourly usage and cost for the last day.
burn summary --tag persona=code-reviewerCost for sessions stamped with that persona tag.
burn summary --group-by-tag personaCost grouped by persona value.

Synthetic-routed models are recognized from hf:*, accounts/fireworks/models/*, and synthetic/*.

burn hotspots

Use burn hotspots when you want to know what made a session or time window expensive. The default view attributes spend to files, bash commands, and subagents.

OptionWhat it does
--since <range>Limit to a relative range or ISO timestamp.
--project <path>Limit to a project.
--session <id>Limit to a single session id.
--workflow <id>Limit to turns folded under a workflowId enrichment stamp.
--provider <csv>Restrict to providers (case-insensitive CSV — e.g. anthropic,openai).
--allShow every row instead of the top 10.
--group-by <dim>Focus one rollup: attribution, bash, bash-verb, file, or subagent.
--patterns [csv]Run hotspot-pattern detectors instead of the attribution view. Pass without a value to enable every detector, or pass a CSV (e.g. retry-loop,failure-run).
--findingsEmit the unified findings table instead of the per-detector grouping. Implies --patterns if not already set.
--rank-by <cost|bytes>Rank per-tool tables by USD or raw output bytes. Default: cost.
--ingestRun one ingest sweep before querying.
--explain-driftReserved for relationship-drift analysis; currently exits with a directed unsupported message.
--jsonEmit machine-readable output.
ExampleResult
burn hotspots --since 7dTop costly files, bash commands, and subagents for the week.
burn hotspots --all --project .Full project hotspot list.
burn hotspots --group-by bash-verb --since 7dBash verbs ranked by cost.
burn hotspots --session demo-sessionRestrict the standard attribution view to one session.
burn hotspots --patterns retry-loop,failure-runSurface retry/failure hotspot findings only.
burn hotspots --findings --since 7dUnified severity-ranked findings list across every detector.
burn hotspots --provider anthropicRestrict attribution to Anthropic-served turns.
burn hotspots --rank-by bytesSurface large tool outputs even when truncation kept their token count small.

burn overhead

Use burn overhead when you want to know how much standing instruction files cost. Burn discovers CLAUDE.md, .claude/CLAUDE.md, and AGENTS.md, then attributes cached prompt cost to files and headed sections.

OptionWhat it does
trimPrint projected-savings diffs for high-cost headed sections. Burn does not modify files.
deltasAttribute context growth between consecutive inferences to intervening prompts, tool results, reminders, and compactions.
--project <path>Project to inspect. Defaults to the current directory.
--since <range>Limit attribution to a time window.
--kind <k>Limit to claude-md or agents-md.
trim --top <n>Recommendations per file. Default: 3.
deltas --session <id>Limit context deltas to one session.
deltas --top <n>Context-delta row cap. Default: 20.
deltas --min-delta <tokens>Hide smaller increases. Default: 1000; compactions always remain visible.
deltas --owner <all|main|subagent>Select inference rails. Default: all.
deltas --explainExpand the intervening steps behind each delta.
--jsonEmit machine-readable attribution for report mode or structured trim recommendations in trim mode.
ExampleResult
burn overheadCost per overhead file and section.
burn overhead --since 30dOverhead cost from the last 30 days.
burn overhead --kind claude-mdClaude instruction files only.
burn overhead trim --top 3Top three trim recommendations per file.
burn overhead trim --jsonStructured trim recommendations with projected savings and unified diffs.
burn overhead deltas --top 10 --owner mainLargest context-window increases on the main conversation rail.

Harnesses pay for different files: Claude Code pays for CLAUDE.md; Codex and OpenCode pay for AGENTS.md.

burn compare

Use burn compare when you want evidence for model choice. It compares models on the work you actually ran, grouped by activity such as coding, debugging, testing, review, exploration, docs, and refactoring.

OptionWhat it does
<model_a,model_b[,...]>Required comma-separated model list. At least two models.
--since <range>Limit to a relative range or ISO timestamp.
--project <path>Limit to a project.
--session <id>Limit to one session.
--workflow <id>Limit to turns folded with stamp workflowId=<id>.
--agent <id>Limit to turns folded with stamp agentId=<id>.
--provider <csv>Comma-separated effective providers (case-insensitive).
--min-sample <n>Flag cells below the sample threshold. Default: 5.
--fidelity <class>Minimum data quality: full, usage-only, aggregate-only, cost-only, or partial.
--include-partialInclude every turn. Shorthand for --fidelity partial.
--jsonEmit a stable JSON object.
--csvEmit one row per model/activity pair.
--bucket <duration>Emit a time series across --since instead of one comparison.
--no-archiveAccepted for CLI parity; it is a no-op because the Rust SDK is SQLite-native.

burn compare reads the ledger as-is — it does not run an ingest sweep first. Run burn ingest && burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 30d (or keep burn ingest --watch running) when you need the freshest data.

ExampleResult
burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 30dSide-by-side activity table.
burn compare claude-opus-4-7,claude-sonnet-4-6 --project . --jsonProject-scoped JSON comparison.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --fidelity fullCompare only full-fidelity turns.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --include-partialInclude lower-fidelity records too.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 7d --bucket 1dDaily comparison buckets for the last week.

Run burn summary --by-provider to discover model IDs present in your ledger.

burn ingest

Use burn ingest when sessions already exist, or when another process owns the harness spawn. Default mode scans Claude Code, Codex, and OpenCode stores once.

OptionWhat it does
--watchKeep polling session stores in the foreground.
--interval <ms>Poll interval in milliseconds. Default: 1000.
--quietSuppress stderr progress spinner / breadcrumbs. One-shot mode still writes the final summary on stdout.
--hook claudeRead one Claude Code hook payload from stdin and ingest its single transcript via the SDK fast-path.
--no-fseventsIn watch mode, use polling instead of filesystem events.
ExampleResult
burn ingestScan all known session stores once.
burn ingest --watchKeep the ingest loop running.
burn ingest --watch --no-fseventsPoll session stores when filesystem events are unreliable.
burn ingest --hook claude --quietClaude Code hook path for orchestrators.

burn mcp-server

Use burn mcp-server when an agent should query its own spend mid-session via MCP. The server is stdio-only and read-only.

OptionWhat it does
--session-id <uuid>Default session ID used by session cost, summary, and hotspots when the caller omits one.
--debugEmit protocol diagnostics to stderr.
ToolWhat it returns
burn__sessionCostTotal USD, tokens, turns, and models for a session.
burn__fingerprintCheap change-detection fingerprint for the ledger or a session/project scope.
burn__summaryToken use and cost by tool and model, with optional session, project, time, and enrichment filters.
burn__hotspotsAttribution, grouped hotspots, or findings for expensive and repeated activity.
burn__overheadInstruction-file token overhead and cost by file and section.
burn__overheadTrimRanked instruction-file trimming recommendations and projected savings.
burn__comparePer-model, per-activity cost and outcome comparison.
ExampleResult
burn mcp-server --session-id <uuid>Start a stdio server whose session cost, summary, and hotspots tools default to that session.
burn mcp-serverStart an unscoped stdio server; callers can pass filters explicitly, while overhead and compare remain cross-session by design.

burn state

Use burn state when reports look stale or you want to inspect or rebuild derived storage. Burn stores events and stamps in burn.sqlite, and content/search data in content.sqlite.

Subcommand or optionWhat it does
burn state or burn state statusPrint status for indexes, content, classifier, and archive.
--jsonEmit machine-readable output.
fingerprint [--session <id> | --project <path>]Print a low-cost change token for polling the ledger.
rebuild index|classify|content|archive|allDrop derivable rows and stage the ledger for a fresh ingest. All targets use the same SQLite rebuild transaction.
prune [--days <n|forever>]Delete content rows older than the retention window.
reset [--force] [--reingest] [--json]Preview or perform a wipe of derived events, stamps, content, and ingest cursors. Config, pricing overrides, and source harness logs remain.
ExampleResult
burn stateDerived artifact status.
burn state status --jsonMachine-readable status.
burn state fingerprint --project .Project-scoped ledger change token.
burn state rebuild classifyDrop derivable rows so the next ingest applies current classifier rules.
burn state prune --days 30Prune content older than 30 days.

Follow any state rebuild command with burn ingest to repopulate derived tables from the harness session stores. Before a forced reset, back up enrichment with burn stamps export; ingest cannot reconstruct stamps from harness logs.

burn sessions

burn sessions list prints recent sessions newest-first. The default window is seven days and the default limit is 20 rows. Use --project, --grep, --since, and --limit to narrow or widen the list; --json returns the same records for scripts.

burn sessions list --since 30d --limit 10
burn sessions list --project . --json

The full session IDs copy directly into summary --session, hotspots --session, overhead deltas --session, and flow --session.

burn flow

burn flow --session <id> emits a Mermaid inference-flow DAG to stdout. Use --output <path> for SVG, --json for the graph payload, and --max-turns to cap wide sessions (default: 50; 0 disables the cap). --mermaid forces Mermaid on stdout even when --output also writes an SVG.

burn flow --session demo-session --json

burn stamps

burn stamps export streams every enrichment stamp as JSONL. --out <path> writes the same export to a file.

burn stamps export

burn update

burn update --check reports whether a release is available without installing it. Bare burn update installs the latest release through the package manager that installed Burn; --force reinstalls the latest release when already current. Automatic launch checks are controlled by burn update toggle-auto-update --on and burn update toggle-auto-update --off.

burn update --check
burn update toggle-auto-update --off

Local Data

Burn keeps its 2.x ledger in two SQLite databases. Both use WAL mode, so reporting readers can run while ingest writes. burn.sqlite is the event and metadata database; content.sqlite separates larger prompt, response, and search content from the compact analytical rows.

Path or settingPurpose
~/.agentworkforce/burn/burn.sqliteEvents, stamps, sessions, relationships, and archive metadata.
~/.agentworkforce/burn/content.sqlitePrompt/response content and the FTS5 search index.
~/.agentworkforce/burn/config.jsonContent-storage and retention configuration.
~/.agentworkforce/burn/pending-stamps/Temporary manifests used by launchers that do not expose a session ID before spawn.
RELAYBURN_HOMEOverride the whole Burn data directory.
RELAYBURN_SQLITE_PATHOverride the events database path.
RELAYBURN_CONTENT_PATHOverride the content database path.
RELAYBURN_CONTENT_STORE=full|hash-only|offControl content payload storage. Default: full.
RELAYBURN_CONTENT_TTL_DAYS=<days|forever>Content retention. Default: 90.

RELAYBURN_HOME relocates the complete layout. The two per-database overrides can place event and content data on different volumes. SQLite may create -wal and -shm files beside each open database; they are part of normal WAL operation.

Harness transcripts remain the upstream input for ingest. burn state status shows database paths, row counts, schema metadata, and resolved retention. burn state rebuild ... clears derivable tables for re-ingest, burn state prune applies content retention, and burn state reset previews or performs a full derived-state wipe.

Packages

PackagePurpose
relayburnnpm install wrapper that resolves the prebuilt Rust burn binary from @relayburn/cli-<platform> optional dependencies.
@relayburn/sdkNode facade over the Rust SDK, resolved through @relayburn/sdk-<platform> optional dependencies.
@relayburn/cli-<platform>Prebuilt burn binary packages for supported OS/CPU targets.
@relayburn/sdk-<platform>Prebuilt napi-rs packages for supported OS/CPU targets.
relayburn-sdkRust crate with the embedding API and internal reader/ledger/analyze/ingest modules.
relayburn-cliRust crate that produces the burn binary.

Development

Run pnpm run verify to run every check CI runs.

pnpm install
cargo build --workspace
cargo test --workspace
pnpm run test
pnpm run build:napi

The npm workspace contains the Node SDK facade, the relayburn install wrapper, and the platform package manifests used by release automation.

Pricing

Burn ships with a vendored models.dev pricing snapshot. Refresh it with:

pnpm run pricing:update

The weekly Update models.dev pricing workflow runs the same command and opens or updates a review PR when the snapshot changes. Before opening the PR, it checks the upstream payload for regressions and runs the Rust SDK test suite.

User overrides live at $RELAYBURN_HOME/models.dev.json and take precedence at lookup time.

What this is for

Agent spend happens in a blind spot. You can see a daily dollar total, and maybe a breakdown by model. You cannot see which tool call, file, subagent, or workflow step drove the cost. Burn makes that question answerable.

The deeper question burn is built around is:

Would the same work cost less with a different model, harness, or tool choice - in dollars or token usage?

You cannot answer that from aggregate spend. It requires attribution at the level of the actual work: this Read cost $0.47 because it added 8,200 tokens to context that rode in every one of the next 23 turns' cache-reads. Once spend is visible at that grain, the choice between Opus and Haiku, between Claude Code and another harness, or between letting an agent re-read a file and passing it a cached summary, becomes a decision you can reason about instead of a guess.

Three concrete questions follow:

  1. How much did I spend? - per agent, workflow, session, model, and tool call.
  2. Why was it spent? - tool calls, files, subagents, cache-hit vs fresh input, and persistence.
  3. Where can I save? - redundant reads, bloated context prefixes, retry loops, and model choices that cost more than they needed to.

Burn is local-first. Data lives in SQLite databases on your machine. Burn never phones home. Pricing is looked up at query time from a vendored snapshot, so rate corrections never require rewriting the ledger.

What burn is not

  • Not a dashboard or a product with a UI of its own.
  • Not an automatic optimizer. It surfaces the choices; you decide.
  • Not a leaderboard or a social service.

Composability: how burn plugs into a spawner

Burn is designed to be called by whatever code spawns agent sessions. If you control the spawn, you know things the session log never records on its own: the workflow this session is part of, the persona it is running as, the agent ID, and the tier. Burn accepts that context, attaches it to the session, and makes it queryable later alongside the usage data from the session log.

The primitive is stamping: attach metadata to a session by ID, before or after any turns have been recorded. Launchers that do not know the session ID before spawn should call @relayburn/sdkwritePendingStamp() before starting the agent, then run burn ingest / ingest() to fold the tags onto the discovered turns. Direct Rust embedders with an exact session ID can use relayburn_sdk::Stamp and relayburn_sdk::StampSelector against a LedgerHandle.

Stamp selectors:

  • { sessionId } - all turns in a session.
  • { messageId } - exactly one turn.
  • { sessionId, range: { fromTs, toTs } } - a time window, such as a single workflow step within a long-lived session.

Enrichment values are plain strings (Record<string, string>). Burn does not care what keys you use. Typical keys: agentId, parentAgentId, workflowId, stepId, persona, tier, harness, userLabel.

This is the composability surface. Burn stays small; the spawner owns the context and decides what to attach.

Spawner-integrated ingest

The recommended launcher integration is the Node SDK pending-stamp primitive:

import{writePendingStamp}from"@relayburn/sdk";awaitwritePendingStamp({harness: "codex",cwd: process.cwd(),enrichment: {persona: "code-reviewer",personaTier: "senior",agentworkforce: "1",},});

Then spawn the harness normally and let burn ingest or ingest() scan the session stores. Claude launchers can either preallocate --session-id and write an exact session stamp from Rust, or use writePendingStamp({ harness: "claude", ... }) when the final session ID is not available before spawn.

Codex and OpenCode do not expose a pre-spawn session ID. writePendingStamp() writes a pending-stamp manifest under $RELAYBURN_HOME/pending-stamps/ before the launcher spawns the agent. Ingest resolves the manifest against the first matching session file before the first turn is appended. Claude launchers can use the same pending-stamp path when the final session ID is not available before spawn. Abandoned pending manifests are cleaned up after 24 hours.

For passive ingest, run:

burn ingest
burn ingest --watch --interval 1000

burn ingest scans Claude, Codex, and OpenCode stores once and uses the same cursor and dedup path as the reporting commands. burn ingest --watch keeps that scan loop running in the foreground.

Hook-based ingest for orchestrators

If your code already controls the Claude Code spawn, you can install burn's hooks per invocation via Claude's --settings flag without mutating global ~/.claude/settings.json. Wire the hook command to:

burn ingest --hook claude --quiet

Hook payloads land on stdin and get forwarded to burn ingest. The command is safe to re-fire on every hook; the ledger cursor and dedup path keep ingestion idempotent, so the hook path and normal session-store path reconcile against the same session.

FAQ

What does burn ingest do?

Each harness (Claude Code, Codex, OpenCode) writes its own session transcripts to disk in its own format. burn ingest reads those transcripts, normalizes them, and writes them into burn's local SQLite ledger so the query commands (summary, hotspots, overhead, compare) have something to read against. Three modes:

  • One-shotburn ingest scans every known session store once and exits. Good for backfilling or catching up before a query.
  • Watchburn ingest --watch keeps a loop running in the foreground and picks up new turns as harnesses write them.
  • Hookburn ingest --hook claude --quiet reads a single Claude Code hook payload from stdin and ingests the transcript it points at.

What is a "hook"?

Claude Code (and similar harnesses) lets users register shell commands that fire on lifecycle events — turn start, tool use, session end, etc. When an event fires, Claude pipes a JSON payload describing what just happened into the registered command's stdin.

burn ingest --hook claude is burn registering itself as one of those commands. Instead of polling the transcript file looking for new turns, burn gets pushed each turn the moment it lands. Lower latency, no missed turns, no FS-watching contortions.

Why both watch and hook?

  • Hook is the preferred path when the harness supports it: push-based, exact, no polling.
  • Watch is the fallback for harnesses without hooks, or when the user hasn't wired one up. It polls the session store directories on an interval.
  • One-shot is for backfill, cron, or "just give me numbers now."

For most solo users, burn ingest --watch in a background terminal is simplest. The hook path matters most when you're building a launcher that spawns Claude Code programmatically and wants per-invocation control without mutating global config.

How is the hook installed?

Burn does not ship an auto-installer. There are two integration paths:

Per-invocation (orchestrators / launchers). If you control the Claude Code spawn, pass --settings <path> to Claude with a JSON file that registers burn ingest --hook claude --quiet against the hook events you care about. This is the recommended path because it avoids touching global config and stays scoped to the invocation.

Global (manual). Edit ~/.claude/settings.json (or the project-level .claude/settings.json) and add burn ingest --hook claude --quiet to the hook events you want to wire. Burn's hook policy is to always exit 0 so it can never break the surrounding Claude Code session, and payloads missing session_id or transcript_path are ignored, so wiring it onto extra events is safe.

If you don't want to deal with hook config at all, run burn ingest --watch instead — same data, slightly higher latency.

License

Apache-2.0 - Copyright 2026 Agent Workforce Incorporated

About

The first step to burning less tokens is understanding how many you burn to begin with

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

relayburn

Understand how you're spending tokens in agent CLIs. Burn ingests Claude Code, Codex, and OpenCode session logs into a local ledger, then shows cost by model, provider, tool, file, workflow, agent, session, and overhead file.

Quick Start

npm i -g relayburn
burn summary

Burn stores data under ~/.agentworkforce/burn/ by default. Set RELAYBURN_HOME to use a different location.

Commands

CommandUse it to
burn summarySee total usage and cost by model or provider.
burn hotspotsFind expensive files, commands, and subagents.
burn overheadAttribute cached prompt cost to CLAUDE.md, .claude/CLAUDE.md, and AGENTS.md.
burn compareCompare observed model performance by activity: cost per turn, one-shot rate, and sample size.
burn stateInspect, fingerprint, rebuild, prune, or reset local ledger state.
burn sessionsFind recent session IDs for drill-down queries.
burn flowRender a session's inference and subagent flow as Mermaid, SVG, or JSON.
burn stampsExport enrichment stamps as JSONL.
burn ingestImport existing or live session logs without wrapping the harness.
burn mcp-serverExpose read-only cost queries to an agent through stdio MCP.
burn updateCheck for releases, install an update, or configure automatic checks.

Every command accepts --json for machine-readable output, --ledger-path <path> to select a Burn home for that invocation, and --no-color to disable ANSI styling.

burn summary

Use burn summary when you want the fast answer: how many turns ran, how many tokens they used, and what they cost.

OptionWhat it does
--since <range>Limit to a relative range like 24h, 7d, or 4w, or an ISO timestamp.
--project <path>Limit to a project path or git-canonical project key.
--session <id>Limit to one session.
--workflow <id>Limit to turns folded under a workflowId enrichment stamp.
--tag k=vFilter by folded enrichment tag. Repeatable; all tags must match.
--group-by-tag <key>Group totals by a folded enrichment tag value.
--by-providerGroup totals by provider instead of model.
--by-toolAttribute each turn's ingest cost to the preceding tool calls.
--by-subagent-typeGroup totals by subagent type.
--by-relationship [subagent]Group by session relationship, optionally drilling into subagent leaves.
--subagent-tree [session]Render the subagent spawn tree. Uses --session when passed without a value.
--agent <id>Limit subagent-tree or relationship views to one agent.
--provider <csv>Limit results to effective providers.
--qualityAppend one-shot and completion-outcome metrics.
--bucket <duration>Emit fixed-width time buckets across the --since window.
--ingestRun one ingest sweep before querying.
--no-archiveAccepted for CLI parity; it is a no-op because the Rust SDK is SQLite-native.
--jsonEmit machine-readable output.
ExampleResult
burn summaryAll-time cost by model.
burn summary --since 24hCost from the last 24 hours.
burn summary --by-providerCost grouped by effective provider.
burn summary --by-toolCost grouped by the tool calls that preceded each turn.
burn summary --qualityUsage totals with one-shot and completion outcomes.
burn summary --since 24h --bucket 1hHourly usage and cost for the last day.
burn summary --tag persona=code-reviewerCost for sessions stamped with that persona tag.
burn summary --group-by-tag personaCost grouped by persona value.

Synthetic-routed models are recognized from hf:*, accounts/fireworks/models/*, and synthetic/*.

burn hotspots

Use burn hotspots when you want to know what made a session or time window expensive. The default view attributes spend to files, bash commands, and subagents.

OptionWhat it does
--since <range>Limit to a relative range or ISO timestamp.
--project <path>Limit to a project.
--session <id>Limit to a single session id.
--workflow <id>Limit to turns folded under a workflowId enrichment stamp.
--provider <csv>Restrict to providers (case-insensitive CSV — e.g. anthropic,openai).
--allShow every row instead of the top 10.
--group-by <dim>Focus one rollup: attribution, bash, bash-verb, file, or subagent.
--patterns [csv]Run hotspot-pattern detectors instead of the attribution view. Pass without a value to enable every detector, or pass a CSV (e.g. retry-loop,failure-run).
--findingsEmit the unified findings table instead of the per-detector grouping. Implies --patterns if not already set.
--rank-by <cost|bytes>Rank per-tool tables by USD or raw output bytes. Default: cost.
--ingestRun one ingest sweep before querying.
--explain-driftReserved for relationship-drift analysis; currently exits with a directed unsupported message.
--jsonEmit machine-readable output.
ExampleResult
burn hotspots --since 7dTop costly files, bash commands, and subagents for the week.
burn hotspots --all --project .Full project hotspot list.
burn hotspots --group-by bash-verb --since 7dBash verbs ranked by cost.
burn hotspots --session demo-sessionRestrict the standard attribution view to one session.
burn hotspots --patterns retry-loop,failure-runSurface retry/failure hotspot findings only.
burn hotspots --findings --since 7dUnified severity-ranked findings list across every detector.
burn hotspots --provider anthropicRestrict attribution to Anthropic-served turns.
burn hotspots --rank-by bytesSurface large tool outputs even when truncation kept their token count small.

burn overhead

Use burn overhead when you want to know how much standing instruction files cost. Burn discovers CLAUDE.md, .claude/CLAUDE.md, and AGENTS.md, then attributes cached prompt cost to files and headed sections.

OptionWhat it does
trimPrint projected-savings diffs for high-cost headed sections. Burn does not modify files.
deltasAttribute context growth between consecutive inferences to intervening prompts, tool results, reminders, and compactions.
--project <path>Project to inspect. Defaults to the current directory.
--since <range>Limit attribution to a time window.
--kind <k>Limit to claude-md or agents-md.
trim --top <n>Recommendations per file. Default: 3.
deltas --session <id>Limit context deltas to one session.
deltas --top <n>Context-delta row cap. Default: 20.
deltas --min-delta <tokens>Hide smaller increases. Default: 1000; compactions always remain visible.
deltas --owner <all|main|subagent>Select inference rails. Default: all.
deltas --explainExpand the intervening steps behind each delta.
--jsonEmit machine-readable attribution for report mode or structured trim recommendations in trim mode.
ExampleResult
burn overheadCost per overhead file and section.
burn overhead --since 30dOverhead cost from the last 30 days.
burn overhead --kind claude-mdClaude instruction files only.
burn overhead trim --top 3Top three trim recommendations per file.
burn overhead trim --jsonStructured trim recommendations with projected savings and unified diffs.
burn overhead deltas --top 10 --owner mainLargest context-window increases on the main conversation rail.

Harnesses pay for different files: Claude Code pays for CLAUDE.md; Codex and OpenCode pay for AGENTS.md.

burn compare

Use burn compare when you want evidence for model choice. It compares models on the work you actually ran, grouped by activity such as coding, debugging, testing, review, exploration, docs, and refactoring.

OptionWhat it does
<model_a,model_b[,...]>Required comma-separated model list. At least two models.
--since <range>Limit to a relative range or ISO timestamp.
--project <path>Limit to a project.
--session <id>Limit to one session.
--workflow <id>Limit to turns folded with stamp workflowId=<id>.
--agent <id>Limit to turns folded with stamp agentId=<id>.
--provider <csv>Comma-separated effective providers (case-insensitive).
--min-sample <n>Flag cells below the sample threshold. Default: 5.
--fidelity <class>Minimum data quality: full, usage-only, aggregate-only, cost-only, or partial.
--include-partialInclude every turn. Shorthand for --fidelity partial.
--jsonEmit a stable JSON object.
--csvEmit one row per model/activity pair.
--bucket <duration>Emit a time series across --since instead of one comparison.
--no-archiveAccepted for CLI parity; it is a no-op because the Rust SDK is SQLite-native.

burn compare reads the ledger as-is — it does not run an ingest sweep first. Run burn ingest && burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 30d (or keep burn ingest --watch running) when you need the freshest data.

ExampleResult
burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 30dSide-by-side activity table.
burn compare claude-opus-4-7,claude-sonnet-4-6 --project . --jsonProject-scoped JSON comparison.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --fidelity fullCompare only full-fidelity turns.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --include-partialInclude lower-fidelity records too.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 7d --bucket 1dDaily comparison buckets for the last week.

Run burn summary --by-provider to discover model IDs present in your ledger.

burn ingest

Use burn ingest when sessions already exist, or when another process owns the harness spawn. Default mode scans Claude Code, Codex, and OpenCode stores once.

OptionWhat it does
--watchKeep polling session stores in the foreground.
--interval <ms>Poll interval in milliseconds. Default: 1000.
--quietSuppress stderr progress spinner / breadcrumbs. One-shot mode still writes the final summary on stdout.
--hook claudeRead one Claude Code hook payload from stdin and ingest its single transcript via the SDK fast-path.
--no-fseventsIn watch mode, use polling instead of filesystem events.
ExampleResult
burn ingestScan all known session stores once.
burn ingest --watchKeep the ingest loop running.
burn ingest --watch --no-fseventsPoll session stores when filesystem events are unreliable.
burn ingest --hook claude --quietClaude Code hook path for orchestrators.

burn mcp-server

Use burn mcp-server when an agent should query its own spend mid-session via MCP. The server is stdio-only and read-only.

OptionWhat it does
--session-id <uuid>Default session ID used by session cost, summary, and hotspots when the caller omits one.
--debugEmit protocol diagnostics to stderr.
ToolWhat it returns
burn__sessionCostTotal USD, tokens, turns, and models for a session.
burn__fingerprintCheap change-detection fingerprint for the ledger or a session/project scope.
burn__summaryToken use and cost by tool and model, with optional session, project, time, and enrichment filters.
burn__hotspotsAttribution, grouped hotspots, or findings for expensive and repeated activity.
burn__overheadInstruction-file token overhead and cost by file and section.
burn__overheadTrimRanked instruction-file trimming recommendations and projected savings.
burn__comparePer-model, per-activity cost and outcome comparison.
ExampleResult
burn mcp-server --session-id <uuid>Start a stdio server whose session cost, summary, and hotspots tools default to that session.
burn mcp-serverStart an unscoped stdio server; callers can pass filters explicitly, while overhead and compare remain cross-session by design.

burn state

Use burn state when reports look stale or you want to inspect or rebuild derived storage. Burn stores events and stamps in burn.sqlite, and content/search data in content.sqlite.

Subcommand or optionWhat it does
burn state or burn state statusPrint status for indexes, content, classifier, and archive.
--jsonEmit machine-readable output.
fingerprint [--session <id> | --project <path>]Print a low-cost change token for polling the ledger.
rebuild index|classify|content|archive|allDrop derivable rows and stage the ledger for a fresh ingest. All targets use the same SQLite rebuild transaction.
prune [--days <n|forever>]Delete content rows older than the retention window.
reset [--force] [--reingest] [--json]Preview or perform a wipe of derived events, stamps, content, and ingest cursors. Config, pricing overrides, and source harness logs remain.
ExampleResult
burn stateDerived artifact status.
burn state status --jsonMachine-readable status.
burn state fingerprint --project .Project-scoped ledger change token.
burn state rebuild classifyDrop derivable rows so the next ingest applies current classifier rules.
burn state prune --days 30Prune content older than 30 days.

Follow any state rebuild command with burn ingest to repopulate derived tables from the harness session stores. Before a forced reset, back up enrichment with burn stamps export; ingest cannot reconstruct stamps from harness logs.

burn sessions

burn sessions list prints recent sessions newest-first. The default window is seven days and the default limit is 20 rows. Use --project, --grep, --since, and --limit to narrow or widen the list; --json returns the same records for scripts.

burn sessions list --since 30d --limit 10
burn sessions list --project . --json

The full session IDs copy directly into summary --session, hotspots --session, overhead deltas --session, and flow --session.

burn flow

burn flow --session <id> emits a Mermaid inference-flow DAG to stdout. Use --output <path> for SVG, --json for the graph payload, and --max-turns to cap wide sessions (default: 50; 0 disables the cap). --mermaid forces Mermaid on stdout even when --output also writes an SVG.

burn flow --session demo-session --json

burn stamps

burn stamps export streams every enrichment stamp as JSONL. --out <path> writes the same export to a file.

burn stamps export

burn update

burn update --check reports whether a release is available without installing it. Bare burn update installs the latest release through the package manager that installed Burn; --force reinstalls the latest release when already current. Automatic launch checks are controlled by burn update toggle-auto-update --on and burn update toggle-auto-update --off.

burn update --check
burn update toggle-auto-update --off

Local Data

Burn keeps its 2.x ledger in two SQLite databases. Both use WAL mode, so reporting readers can run while ingest writes. burn.sqlite is the event and metadata database; content.sqlite separates larger prompt, response, and search content from the compact analytical rows.

Path or settingPurpose
~/.agentworkforce/burn/burn.sqliteEvents, stamps, sessions, relationships, and archive metadata.
~/.agentworkforce/burn/content.sqlitePrompt/response content and the FTS5 search index.
~/.agentworkforce/burn/config.jsonContent-storage and retention configuration.
~/.agentworkforce/burn/pending-stamps/Temporary manifests used by launchers that do not expose a session ID before spawn.
RELAYBURN_HOMEOverride the whole Burn data directory.
RELAYBURN_SQLITE_PATHOverride the events database path.
RELAYBURN_CONTENT_PATHOverride the content database path.
RELAYBURN_CONTENT_STORE=full|hash-only|offControl content payload storage. Default: full.
RELAYBURN_CONTENT_TTL_DAYS=<days|forever>Content retention. Default: 90.

RELAYBURN_HOME relocates the complete layout. The two per-database overrides can place event and content data on different volumes. SQLite may create -wal and -shm files beside each open database; they are part of normal WAL operation.

Harness transcripts remain the upstream input for ingest. burn state status shows database paths, row counts, schema metadata, and resolved retention. burn state rebuild ... clears derivable tables for re-ingest, burn state prune applies content retention, and burn state reset previews or performs a full derived-state wipe.

Packages

PackagePurpose
relayburnnpm install wrapper that resolves the prebuilt Rust burn binary from @relayburn/cli-<platform> optional dependencies.
@relayburn/sdkNode facade over the Rust SDK, resolved through @relayburn/sdk-<platform> optional dependencies.
@relayburn/cli-<platform>Prebuilt burn binary packages for supported OS/CPU targets.
@relayburn/sdk-<platform>Prebuilt napi-rs packages for supported OS/CPU targets.
relayburn-sdkRust crate with the embedding API and internal reader/ledger/analyze/ingest modules.
relayburn-cliRust crate that produces the burn binary.

Development

Run pnpm run verify to run every check CI runs.

pnpm install
cargo build --workspace
cargo test --workspace
pnpm run test
pnpm run build:napi

The npm workspace contains the Node SDK facade, the relayburn install wrapper, and the platform package manifests used by release automation.

Pricing

Burn ships with a vendored models.dev pricing snapshot. Refresh it with:

pnpm run pricing:update

The weekly Update models.dev pricing workflow runs the same command and opens or updates a review PR when the snapshot changes. Before opening the PR, it checks the upstream payload for regressions and runs the Rust SDK test suite.

User overrides live at $RELAYBURN_HOME/models.dev.json and take precedence at lookup time.

What this is for

Agent spend happens in a blind spot. You can see a daily dollar total, and maybe a breakdown by model. You cannot see which tool call, file, subagent, or workflow step drove the cost. Burn makes that question answerable.

The deeper question burn is built around is:

Would the same work cost less with a different model, harness, or tool choice - in dollars or token usage?

You cannot answer that from aggregate spend. It requires attribution at the level of the actual work: this Read cost $0.47 because it added 8,200 tokens to context that rode in every one of the next 23 turns' cache-reads. Once spend is visible at that grain, the choice between Opus and Haiku, between Claude Code and another harness, or between letting an agent re-read a file and passing it a cached summary, becomes a decision you can reason about instead of a guess.

Three concrete questions follow:

  1. How much did I spend? - per agent, workflow, session, model, and tool call.
  2. Why was it spent? - tool calls, files, subagents, cache-hit vs fresh input, and persistence.
  3. Where can I save? - redundant reads, bloated context prefixes, retry loops, and model choices that cost more than they needed to.

Burn is local-first. Data lives in SQLite databases on your machine. Burn never phones home. Pricing is looked up at query time from a vendored snapshot, so rate corrections never require rewriting the ledger.

What burn is not

  • Not a dashboard or a product with a UI of its own.
  • Not an automatic optimizer. It surfaces the choices; you decide.
  • Not a leaderboard or a social service.

Composability: how burn plugs into a spawner

Burn is designed to be called by whatever code spawns agent sessions. If you control the spawn, you know things the session log never records on its own: the workflow this session is part of, the persona it is running as, the agent ID, and the tier. Burn accepts that context, attaches it to the session, and makes it queryable later alongside the usage data from the session log.

The primitive is stamping: attach metadata to a session by ID, before or after any turns have been recorded. Launchers that do not know the session ID before spawn should call @relayburn/sdkwritePendingStamp() before starting the agent, then run burn ingest / ingest() to fold the tags onto the discovered turns. Direct Rust embedders with an exact session ID can use relayburn_sdk::Stamp and relayburn_sdk::StampSelector against a LedgerHandle.

Stamp selectors:

  • { sessionId } - all turns in a session.
  • { messageId } - exactly one turn.
  • { sessionId, range: { fromTs, toTs } } - a time window, such as a single workflow step within a long-lived session.

Enrichment values are plain strings (Record<string, string>). Burn does not care what keys you use. Typical keys: agentId, parentAgentId, workflowId, stepId, persona, tier, harness, userLabel.

This is the composability surface. Burn stays small; the spawner owns the context and decides what to attach.

Spawner-integrated ingest

The recommended launcher integration is the Node SDK pending-stamp primitive:

import{writePendingStamp}from"@relayburn/sdk";awaitwritePendingStamp({harness: "codex",cwd: process.cwd(),enrichment: {persona: "code-reviewer",personaTier: "senior",agentworkforce: "1",},});

Then spawn the harness normally and let burn ingest or ingest() scan the session stores. Claude launchers can either preallocate --session-id and write an exact session stamp from Rust, or use writePendingStamp({ harness: "claude", ... }) when the final session ID is not available before spawn.

Codex and OpenCode do not expose a pre-spawn session ID. writePendingStamp() writes a pending-stamp manifest under $RELAYBURN_HOME/pending-stamps/ before the launcher spawns the agent. Ingest resolves the manifest against the first matching session file before the first turn is appended. Claude launchers can use the same pending-stamp path when the final session ID is not available before spawn. Abandoned pending manifests are cleaned up after 24 hours.

For passive ingest, run:

burn ingest
burn ingest --watch --interval 1000

burn ingest scans Claude, Codex, and OpenCode stores once and uses the same cursor and dedup path as the reporting commands. burn ingest --watch keeps that scan loop running in the foreground.

Hook-based ingest for orchestrators

If your code already controls the Claude Code spawn, you can install burn's hooks per invocation via Claude's --settings flag without mutating global ~/.claude/settings.json. Wire the hook command to:

burn ingest --hook claude --quiet

Hook payloads land on stdin and get forwarded to burn ingest. The command is safe to re-fire on every hook; the ledger cursor and dedup path keep ingestion idempotent, so the hook path and normal session-store path reconcile against the same session.

FAQ

What does burn ingest do?

Each harness (Claude Code, Codex, OpenCode) writes its own session transcripts to disk in its own format. burn ingest reads those transcripts, normalizes them, and writes them into burn's local SQLite ledger so the query commands (summary, hotspots, overhead, compare) have something to read against. Three modes:

  • One-shotburn ingest scans every known session store once and exits. Good for backfilling or catching up before a query.
  • Watchburn ingest --watch keeps a loop running in the foreground and picks up new turns as harnesses write them.
  • Hookburn ingest --hook claude --quiet reads a single Claude Code hook payload from stdin and ingests the transcript it points at.

What is a "hook"?

Claude Code (and similar harnesses) lets users register shell commands that fire on lifecycle events — turn start, tool use, session end, etc. When an event fires, Claude pipes a JSON payload describing what just happened into the registered command's stdin.

burn ingest --hook claude is burn registering itself as one of those commands. Instead of polling the transcript file looking for new turns, burn gets pushed each turn the moment it lands. Lower latency, no missed turns, no FS-watching contortions.

Why both watch and hook?

  • Hook is the preferred path when the harness supports it: push-based, exact, no polling.
  • Watch is the fallback for harnesses without hooks, or when the user hasn't wired one up. It polls the session store directories on an interval.
  • One-shot is for backfill, cron, or "just give me numbers now."

For most solo users, burn ingest --watch in a background terminal is simplest. The hook path matters most when you're building a launcher that spawns Claude Code programmatically and wants per-invocation control without mutating global config.

How is the hook installed?

Burn does not ship an auto-installer. There are two integration paths:

Per-invocation (orchestrators / launchers). If you control the Claude Code spawn, pass --settings <path> to Claude with a JSON file that registers burn ingest --hook claude --quiet against the hook events you care about. This is the recommended path because it avoids touching global config and stays scoped to the invocation.

Global (manual). Edit ~/.claude/settings.json (or the project-level .claude/settings.json) and add burn ingest --hook claude --quiet to the hook events you want to wire. Burn's hook policy is to always exit 0 so it can never break the surrounding Claude Code session, and payloads missing session_id or transcript_path are ignored, so wiring it onto extra events is safe.

If you don't want to deal with hook config at all, run burn ingest --watch instead — same data, slightly higher latency.

License

Apache-2.0 - Copyright 2026 Agent Workforce Incorporated

About

The first step to burning less tokens is understanding how many you burn to begin with

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

relayburn

Understand how you're spending tokens in agent CLIs. Burn ingests Claude Code, Codex, and OpenCode session logs into a local ledger, then shows cost by model, provider, tool, file, workflow, agent, session, and overhead file.

Quick Start

npm i -g relayburn
burn summary

Burn stores data under ~/.agentworkforce/burn/ by default. Set RELAYBURN_HOME to use a different location.

Commands

CommandUse it to
burn summarySee total usage and cost by model or provider.
burn hotspotsFind expensive files, commands, and subagents.
burn overheadAttribute cached prompt cost to CLAUDE.md, .claude/CLAUDE.md, and AGENTS.md.
burn compareCompare observed model performance by activity: cost per turn, one-shot rate, and sample size.
burn stateInspect, fingerprint, rebuild, prune, or reset local ledger state.
burn sessionsFind recent session IDs for drill-down queries.
burn flowRender a session's inference and subagent flow as Mermaid, SVG, or JSON.
burn stampsExport enrichment stamps as JSONL.
burn ingestImport existing or live session logs without wrapping the harness.
burn mcp-serverExpose read-only cost queries to an agent through stdio MCP.
burn updateCheck for releases, install an update, or configure automatic checks.

Every command accepts --json for machine-readable output, --ledger-path <path> to select a Burn home for that invocation, and --no-color to disable ANSI styling.

burn summary

Use burn summary when you want the fast answer: how many turns ran, how many tokens they used, and what they cost.

OptionWhat it does
--since <range>Limit to a relative range like 24h, 7d, or 4w, or an ISO timestamp.
--project <path>Limit to a project path or git-canonical project key.
--session <id>Limit to one session.
--workflow <id>Limit to turns folded under a workflowId enrichment stamp.
--tag k=vFilter by folded enrichment tag. Repeatable; all tags must match.
--group-by-tag <key>Group totals by a folded enrichment tag value.
--by-providerGroup totals by provider instead of model.
--by-toolAttribute each turn's ingest cost to the preceding tool calls.
--by-subagent-typeGroup totals by subagent type.
--by-relationship [subagent]Group by session relationship, optionally drilling into subagent leaves.
--subagent-tree [session]Render the subagent spawn tree. Uses --session when passed without a value.
--agent <id>Limit subagent-tree or relationship views to one agent.
--provider <csv>Limit results to effective providers.
--qualityAppend one-shot and completion-outcome metrics.
--bucket <duration>Emit fixed-width time buckets across the --since window.
--ingestRun one ingest sweep before querying.
--no-archiveAccepted for CLI parity; it is a no-op because the Rust SDK is SQLite-native.
--jsonEmit machine-readable output.
ExampleResult
burn summaryAll-time cost by model.
burn summary --since 24hCost from the last 24 hours.
burn summary --by-providerCost grouped by effective provider.
burn summary --by-toolCost grouped by the tool calls that preceded each turn.
burn summary --qualityUsage totals with one-shot and completion outcomes.
burn summary --since 24h --bucket 1hHourly usage and cost for the last day.
burn summary --tag persona=code-reviewerCost for sessions stamped with that persona tag.
burn summary --group-by-tag personaCost grouped by persona value.

Synthetic-routed models are recognized from hf:*, accounts/fireworks/models/*, and synthetic/*.

burn hotspots

Use burn hotspots when you want to know what made a session or time window expensive. The default view attributes spend to files, bash commands, and subagents.

OptionWhat it does
--since <range>Limit to a relative range or ISO timestamp.
--project <path>Limit to a project.
--session <id>Limit to a single session id.
--workflow <id>Limit to turns folded under a workflowId enrichment stamp.
--provider <csv>Restrict to providers (case-insensitive CSV — e.g. anthropic,openai).
--allShow every row instead of the top 10.
--group-by <dim>Focus one rollup: attribution, bash, bash-verb, file, or subagent.
--patterns [csv]Run hotspot-pattern detectors instead of the attribution view. Pass without a value to enable every detector, or pass a CSV (e.g. retry-loop,failure-run).
--findingsEmit the unified findings table instead of the per-detector grouping. Implies --patterns if not already set.
--rank-by <cost|bytes>Rank per-tool tables by USD or raw output bytes. Default: cost.
--ingestRun one ingest sweep before querying.
--explain-driftReserved for relationship-drift analysis; currently exits with a directed unsupported message.
--jsonEmit machine-readable output.
ExampleResult
burn hotspots --since 7dTop costly files, bash commands, and subagents for the week.
burn hotspots --all --project .Full project hotspot list.
burn hotspots --group-by bash-verb --since 7dBash verbs ranked by cost.
burn hotspots --session demo-sessionRestrict the standard attribution view to one session.
burn hotspots --patterns retry-loop,failure-runSurface retry/failure hotspot findings only.
burn hotspots --findings --since 7dUnified severity-ranked findings list across every detector.
burn hotspots --provider anthropicRestrict attribution to Anthropic-served turns.
burn hotspots --rank-by bytesSurface large tool outputs even when truncation kept their token count small.

burn overhead

Use burn overhead when you want to know how much standing instruction files cost. Burn discovers CLAUDE.md, .claude/CLAUDE.md, and AGENTS.md, then attributes cached prompt cost to files and headed sections.

OptionWhat it does
trimPrint projected-savings diffs for high-cost headed sections. Burn does not modify files.
deltasAttribute context growth between consecutive inferences to intervening prompts, tool results, reminders, and compactions.
--project <path>Project to inspect. Defaults to the current directory.
--since <range>Limit attribution to a time window.
--kind <k>Limit to claude-md or agents-md.
trim --top <n>Recommendations per file. Default: 3.
deltas --session <id>Limit context deltas to one session.
deltas --top <n>Context-delta row cap. Default: 20.
deltas --min-delta <tokens>Hide smaller increases. Default: 1000; compactions always remain visible.
deltas --owner <all|main|subagent>Select inference rails. Default: all.
deltas --explainExpand the intervening steps behind each delta.
--jsonEmit machine-readable attribution for report mode or structured trim recommendations in trim mode.
ExampleResult
burn overheadCost per overhead file and section.
burn overhead --since 30dOverhead cost from the last 30 days.
burn overhead --kind claude-mdClaude instruction files only.
burn overhead trim --top 3Top three trim recommendations per file.
burn overhead trim --jsonStructured trim recommendations with projected savings and unified diffs.
burn overhead deltas --top 10 --owner mainLargest context-window increases on the main conversation rail.

Harnesses pay for different files: Claude Code pays for CLAUDE.md; Codex and OpenCode pay for AGENTS.md.

burn compare

Use burn compare when you want evidence for model choice. It compares models on the work you actually ran, grouped by activity such as coding, debugging, testing, review, exploration, docs, and refactoring.

OptionWhat it does
<model_a,model_b[,...]>Required comma-separated model list. At least two models.
--since <range>Limit to a relative range or ISO timestamp.
--project <path>Limit to a project.
--session <id>Limit to one session.
--workflow <id>Limit to turns folded with stamp workflowId=<id>.
--agent <id>Limit to turns folded with stamp agentId=<id>.
--provider <csv>Comma-separated effective providers (case-insensitive).
--min-sample <n>Flag cells below the sample threshold. Default: 5.
--fidelity <class>Minimum data quality: full, usage-only, aggregate-only, cost-only, or partial.
--include-partialInclude every turn. Shorthand for --fidelity partial.
--jsonEmit a stable JSON object.
--csvEmit one row per model/activity pair.
--bucket <duration>Emit a time series across --since instead of one comparison.
--no-archiveAccepted for CLI parity; it is a no-op because the Rust SDK is SQLite-native.

burn compare reads the ledger as-is — it does not run an ingest sweep first. Run burn ingest && burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 30d (or keep burn ingest --watch running) when you need the freshest data.

ExampleResult
burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 30dSide-by-side activity table.
burn compare claude-opus-4-7,claude-sonnet-4-6 --project . --jsonProject-scoped JSON comparison.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --fidelity fullCompare only full-fidelity turns.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --include-partialInclude lower-fidelity records too.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 7d --bucket 1dDaily comparison buckets for the last week.

Run burn summary --by-provider to discover model IDs present in your ledger.

burn ingest

Use burn ingest when sessions already exist, or when another process owns the harness spawn. Default mode scans Claude Code, Codex, and OpenCode stores once.

OptionWhat it does
--watchKeep polling session stores in the foreground.
--interval <ms>Poll interval in milliseconds. Default: 1000.
--quietSuppress stderr progress spinner / breadcrumbs. One-shot mode still writes the final summary on stdout.
--hook claudeRead one Claude Code hook payload from stdin and ingest its single transcript via the SDK fast-path.
--no-fseventsIn watch mode, use polling instead of filesystem events.
ExampleResult
burn ingestScan all known session stores once.
burn ingest --watchKeep the ingest loop running.
burn ingest --watch --no-fseventsPoll session stores when filesystem events are unreliable.
burn ingest --hook claude --quietClaude Code hook path for orchestrators.

burn mcp-server

Use burn mcp-server when an agent should query its own spend mid-session via MCP. The server is stdio-only and read-only.

OptionWhat it does
--session-id <uuid>Default session ID used by session cost, summary, and hotspots when the caller omits one.
--debugEmit protocol diagnostics to stderr.
ToolWhat it returns
burn__sessionCostTotal USD, tokens, turns, and models for a session.
burn__fingerprintCheap change-detection fingerprint for the ledger or a session/project scope.
burn__summaryToken use and cost by tool and model, with optional session, project, time, and enrichment filters.
burn__hotspotsAttribution, grouped hotspots, or findings for expensive and repeated activity.
burn__overheadInstruction-file token overhead and cost by file and section.
burn__overheadTrimRanked instruction-file trimming recommendations and projected savings.
burn__comparePer-model, per-activity cost and outcome comparison.
ExampleResult
burn mcp-server --session-id <uuid>Start a stdio server whose session cost, summary, and hotspots tools default to that session.
burn mcp-serverStart an unscoped stdio server; callers can pass filters explicitly, while overhead and compare remain cross-session by design.

burn state

Use burn state when reports look stale or you want to inspect or rebuild derived storage. Burn stores events and stamps in burn.sqlite, and content/search data in content.sqlite.

Subcommand or optionWhat it does
burn state or burn state statusPrint status for indexes, content, classifier, and archive.
--jsonEmit machine-readable output.
fingerprint [--session <id> | --project <path>]Print a low-cost change token for polling the ledger.
rebuild index|classify|content|archive|allDrop derivable rows and stage the ledger for a fresh ingest. All targets use the same SQLite rebuild transaction.
prune [--days <n|forever>]Delete content rows older than the retention window.
reset [--force] [--reingest] [--json]Preview or perform a wipe of derived events, stamps, content, and ingest cursors. Config, pricing overrides, and source harness logs remain.
ExampleResult
burn stateDerived artifact status.
burn state status --jsonMachine-readable status.
burn state fingerprint --project .Project-scoped ledger change token.
burn state rebuild classifyDrop derivable rows so the next ingest applies current classifier rules.
burn state prune --days 30Prune content older than 30 days.

Follow any state rebuild command with burn ingest to repopulate derived tables from the harness session stores. Before a forced reset, back up enrichment with burn stamps export; ingest cannot reconstruct stamps from harness logs.

burn sessions

burn sessions list prints recent sessions newest-first. The default window is seven days and the default limit is 20 rows. Use --project, --grep, --since, and --limit to narrow or widen the list; --json returns the same records for scripts.

burn sessions list --since 30d --limit 10
burn sessions list --project . --json

The full session IDs copy directly into summary --session, hotspots --session, overhead deltas --session, and flow --session.

burn flow

burn flow --session <id> emits a Mermaid inference-flow DAG to stdout. Use --output <path> for SVG, --json for the graph payload, and --max-turns to cap wide sessions (default: 50; 0 disables the cap). --mermaid forces Mermaid on stdout even when --output also writes an SVG.

burn flow --session demo-session --json

burn stamps

burn stamps export streams every enrichment stamp as JSONL. --out <path> writes the same export to a file.

burn stamps export

burn update

burn update --check reports whether a release is available without installing it. Bare burn update installs the latest release through the package manager that installed Burn; --force reinstalls the latest release when already current. Automatic launch checks are controlled by burn update toggle-auto-update --on and burn update toggle-auto-update --off.

burn update --check
burn update toggle-auto-update --off

Local Data

Burn keeps its 2.x ledger in two SQLite databases. Both use WAL mode, so reporting readers can run while ingest writes. burn.sqlite is the event and metadata database; content.sqlite separates larger prompt, response, and search content from the compact analytical rows.

Path or settingPurpose
~/.agentworkforce/burn/burn.sqliteEvents, stamps, sessions, relationships, and archive metadata.
~/.agentworkforce/burn/content.sqlitePrompt/response content and the FTS5 search index.
~/.agentworkforce/burn/config.jsonContent-storage and retention configuration.
~/.agentworkforce/burn/pending-stamps/Temporary manifests used by launchers that do not expose a session ID before spawn.
RELAYBURN_HOMEOverride the whole Burn data directory.
RELAYBURN_SQLITE_PATHOverride the events database path.
RELAYBURN_CONTENT_PATHOverride the content database path.
RELAYBURN_CONTENT_STORE=full|hash-only|offControl content payload storage. Default: full.
RELAYBURN_CONTENT_TTL_DAYS=<days|forever>Content retention. Default: 90.

RELAYBURN_HOME relocates the complete layout. The two per-database overrides can place event and content data on different volumes. SQLite may create -wal and -shm files beside each open database; they are part of normal WAL operation.

Harness transcripts remain the upstream input for ingest. burn state status shows database paths, row counts, schema metadata, and resolved retention. burn state rebuild ... clears derivable tables for re-ingest, burn state prune applies content retention, and burn state reset previews or performs a full derived-state wipe.

Packages

PackagePurpose
relayburnnpm install wrapper that resolves the prebuilt Rust burn binary from @relayburn/cli-<platform> optional dependencies.
@relayburn/sdkNode facade over the Rust SDK, resolved through @relayburn/sdk-<platform> optional dependencies.
@relayburn/cli-<platform>Prebuilt burn binary packages for supported OS/CPU targets.
@relayburn/sdk-<platform>Prebuilt napi-rs packages for supported OS/CPU targets.
relayburn-sdkRust crate with the embedding API and internal reader/ledger/analyze/ingest modules.
relayburn-cliRust crate that produces the burn binary.

Development

Run pnpm run verify to run every check CI runs.

pnpm install
cargo build --workspace
cargo test --workspace
pnpm run test
pnpm run build:napi

The npm workspace contains the Node SDK facade, the relayburn install wrapper, and the platform package manifests used by release automation.

Pricing

Burn ships with a vendored models.dev pricing snapshot. Refresh it with:

pnpm run pricing:update

The weekly Update models.dev pricing workflow runs the same command and opens or updates a review PR when the snapshot changes. Before opening the PR, it checks the upstream payload for regressions and runs the Rust SDK test suite.

User overrides live at $RELAYBURN_HOME/models.dev.json and take precedence at lookup time.

What this is for

Agent spend happens in a blind spot. You can see a daily dollar total, and maybe a breakdown by model. You cannot see which tool call, file, subagent, or workflow step drove the cost. Burn makes that question answerable.

The deeper question burn is built around is:

Would the same work cost less with a different model, harness, or tool choice - in dollars or token usage?

You cannot answer that from aggregate spend. It requires attribution at the level of the actual work: this Read cost $0.47 because it added 8,200 tokens to context that rode in every one of the next 23 turns' cache-reads. Once spend is visible at that grain, the choice between Opus and Haiku, between Claude Code and another harness, or between letting an agent re-read a file and passing it a cached summary, becomes a decision you can reason about instead of a guess.

Three concrete questions follow:

  1. How much did I spend? - per agent, workflow, session, model, and tool call.
  2. Why was it spent? - tool calls, files, subagents, cache-hit vs fresh input, and persistence.
  3. Where can I save? - redundant reads, bloated context prefixes, retry loops, and model choices that cost more than they needed to.

Burn is local-first. Data lives in SQLite databases on your machine. Burn never phones home. Pricing is looked up at query time from a vendored snapshot, so rate corrections never require rewriting the ledger.

What burn is not

  • Not a dashboard or a product with a UI of its own.
  • Not an automatic optimizer. It surfaces the choices; you decide.
  • Not a leaderboard or a social service.

Composability: how burn plugs into a spawner

Burn is designed to be called by whatever code spawns agent sessions. If you control the spawn, you know things the session log never records on its own: the workflow this session is part of, the persona it is running as, the agent ID, and the tier. Burn accepts that context, attaches it to the session, and makes it queryable later alongside the usage data from the session log.

The primitive is stamping: attach metadata to a session by ID, before or after any turns have been recorded. Launchers that do not know the session ID before spawn should call @relayburn/sdkwritePendingStamp() before starting the agent, then run burn ingest / ingest() to fold the tags onto the discovered turns. Direct Rust embedders with an exact session ID can use relayburn_sdk::Stamp and relayburn_sdk::StampSelector against a LedgerHandle.

Stamp selectors:

  • { sessionId } - all turns in a session.
  • { messageId } - exactly one turn.
  • { sessionId, range: { fromTs, toTs } } - a time window, such as a single workflow step within a long-lived session.

Enrichment values are plain strings (Record<string, string>). Burn does not care what keys you use. Typical keys: agentId, parentAgentId, workflowId, stepId, persona, tier, harness, userLabel.

This is the composability surface. Burn stays small; the spawner owns the context and decides what to attach.

Spawner-integrated ingest

The recommended launcher integration is the Node SDK pending-stamp primitive:

import{writePendingStamp}from"@relayburn/sdk";awaitwritePendingStamp({harness: "codex",cwd: process.cwd(),enrichment: {persona: "code-reviewer",personaTier: "senior",agentworkforce: "1",},});

Then spawn the harness normally and let burn ingest or ingest() scan the session stores. Claude launchers can either preallocate --session-id and write an exact session stamp from Rust, or use writePendingStamp({ harness: "claude", ... }) when the final session ID is not available before spawn.

Codex and OpenCode do not expose a pre-spawn session ID. writePendingStamp() writes a pending-stamp manifest under $RELAYBURN_HOME/pending-stamps/ before the launcher spawns the agent. Ingest resolves the manifest against the first matching session file before the first turn is appended. Claude launchers can use the same pending-stamp path when the final session ID is not available before spawn. Abandoned pending manifests are cleaned up after 24 hours.

For passive ingest, run:

burn ingest
burn ingest --watch --interval 1000

burn ingest scans Claude, Codex, and OpenCode stores once and uses the same cursor and dedup path as the reporting commands. burn ingest --watch keeps that scan loop running in the foreground.

Hook-based ingest for orchestrators

If your code already controls the Claude Code spawn, you can install burn's hooks per invocation via Claude's --settings flag without mutating global ~/.claude/settings.json. Wire the hook command to:

burn ingest --hook claude --quiet

Hook payloads land on stdin and get forwarded to burn ingest. The command is safe to re-fire on every hook; the ledger cursor and dedup path keep ingestion idempotent, so the hook path and normal session-store path reconcile against the same session.

FAQ

What does burn ingest do?

Each harness (Claude Code, Codex, OpenCode) writes its own session transcripts to disk in its own format. burn ingest reads those transcripts, normalizes them, and writes them into burn's local SQLite ledger so the query commands (summary, hotspots, overhead, compare) have something to read against. Three modes:

  • One-shotburn ingest scans every known session store once and exits. Good for backfilling or catching up before a query.
  • Watchburn ingest --watch keeps a loop running in the foreground and picks up new turns as harnesses write them.
  • Hookburn ingest --hook claude --quiet reads a single Claude Code hook payload from stdin and ingests the transcript it points at.

What is a "hook"?

Claude Code (and similar harnesses) lets users register shell commands that fire on lifecycle events — turn start, tool use, session end, etc. When an event fires, Claude pipes a JSON payload describing what just happened into the registered command's stdin.

burn ingest --hook claude is burn registering itself as one of those commands. Instead of polling the transcript file looking for new turns, burn gets pushed each turn the moment it lands. Lower latency, no missed turns, no FS-watching contortions.

Why both watch and hook?

  • Hook is the preferred path when the harness supports it: push-based, exact, no polling.
  • Watch is the fallback for harnesses without hooks, or when the user hasn't wired one up. It polls the session store directories on an interval.
  • One-shot is for backfill, cron, or "just give me numbers now."

For most solo users, burn ingest --watch in a background terminal is simplest. The hook path matters most when you're building a launcher that spawns Claude Code programmatically and wants per-invocation control without mutating global config.

How is the hook installed?

Burn does not ship an auto-installer. There are two integration paths:

Per-invocation (orchestrators / launchers). If you control the Claude Code spawn, pass --settings <path> to Claude with a JSON file that registers burn ingest --hook claude --quiet against the hook events you care about. This is the recommended path because it avoids touching global config and stays scoped to the invocation.

Global (manual). Edit ~/.claude/settings.json (or the project-level .claude/settings.json) and add burn ingest --hook claude --quiet to the hook events you want to wire. Burn's hook policy is to always exit 0 so it can never break the surrounding Claude Code session, and payloads missing session_id or transcript_path are ignored, so wiring it onto extra events is safe.

If you don't want to deal with hook config at all, run burn ingest --watch instead — same data, slightly higher latency.

License

Apache-2.0 - Copyright 2026 Agent Workforce Incorporated

About

The first step to burning less tokens is understanding how many you burn to begin with

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

relayburn

Understand how you're spending tokens in agent CLIs. Burn ingests Claude Code, Codex, and OpenCode session logs into a local ledger, then shows cost by model, provider, tool, file, workflow, agent, session, and overhead file.

Quick Start

npm i -g relayburn
burn summary

Burn stores data under ~/.agentworkforce/burn/ by default. Set RELAYBURN_HOME to use a different location.

Commands

CommandUse it to
burn summarySee total usage and cost by model or provider.
burn hotspotsFind expensive files, commands, and subagents.
burn overheadAttribute cached prompt cost to CLAUDE.md, .claude/CLAUDE.md, and AGENTS.md.
burn compareCompare observed model performance by activity: cost per turn, one-shot rate, and sample size.
burn stateInspect, fingerprint, rebuild, prune, or reset local ledger state.
burn sessionsFind recent session IDs for drill-down queries.
burn flowRender a session's inference and subagent flow as Mermaid, SVG, or JSON.
burn stampsExport enrichment stamps as JSONL.
burn ingestImport existing or live session logs without wrapping the harness.
burn mcp-serverExpose read-only cost queries to an agent through stdio MCP.
burn updateCheck for releases, install an update, or configure automatic checks.

Every command accepts --json for machine-readable output, --ledger-path <path> to select a Burn home for that invocation, and --no-color to disable ANSI styling.

burn summary

Use burn summary when you want the fast answer: how many turns ran, how many tokens they used, and what they cost.

OptionWhat it does
--since <range>Limit to a relative range like 24h, 7d, or 4w, or an ISO timestamp.
--project <path>Limit to a project path or git-canonical project key.
--session <id>Limit to one session.
--workflow <id>Limit to turns folded under a workflowId enrichment stamp.
--tag k=vFilter by folded enrichment tag. Repeatable; all tags must match.
--group-by-tag <key>Group totals by a folded enrichment tag value.
--by-providerGroup totals by provider instead of model.
--by-toolAttribute each turn's ingest cost to the preceding tool calls.
--by-subagent-typeGroup totals by subagent type.
--by-relationship [subagent]Group by session relationship, optionally drilling into subagent leaves.
--subagent-tree [session]Render the subagent spawn tree. Uses --session when passed without a value.
--agent <id>Limit subagent-tree or relationship views to one agent.
--provider <csv>Limit results to effective providers.
--qualityAppend one-shot and completion-outcome metrics.
--bucket <duration>Emit fixed-width time buckets across the --since window.
--ingestRun one ingest sweep before querying.
--no-archiveAccepted for CLI parity; it is a no-op because the Rust SDK is SQLite-native.
--jsonEmit machine-readable output.
ExampleResult
burn summaryAll-time cost by model.
burn summary --since 24hCost from the last 24 hours.
burn summary --by-providerCost grouped by effective provider.
burn summary --by-toolCost grouped by the tool calls that preceded each turn.
burn summary --qualityUsage totals with one-shot and completion outcomes.
burn summary --since 24h --bucket 1hHourly usage and cost for the last day.
burn summary --tag persona=code-reviewerCost for sessions stamped with that persona tag.
burn summary --group-by-tag personaCost grouped by persona value.

Synthetic-routed models are recognized from hf:*, accounts/fireworks/models/*, and synthetic/*.

burn hotspots

Use burn hotspots when you want to know what made a session or time window expensive. The default view attributes spend to files, bash commands, and subagents.

OptionWhat it does
--since <range>Limit to a relative range or ISO timestamp.
--project <path>Limit to a project.
--session <id>Limit to a single session id.
--workflow <id>Limit to turns folded under a workflowId enrichment stamp.
--provider <csv>Restrict to providers (case-insensitive CSV — e.g. anthropic,openai).
--allShow every row instead of the top 10.
--group-by <dim>Focus one rollup: attribution, bash, bash-verb, file, or subagent.
--patterns [csv]Run hotspot-pattern detectors instead of the attribution view. Pass without a value to enable every detector, or pass a CSV (e.g. retry-loop,failure-run).
--findingsEmit the unified findings table instead of the per-detector grouping. Implies --patterns if not already set.
--rank-by <cost|bytes>Rank per-tool tables by USD or raw output bytes. Default: cost.
--ingestRun one ingest sweep before querying.
--explain-driftReserved for relationship-drift analysis; currently exits with a directed unsupported message.
--jsonEmit machine-readable output.
ExampleResult
burn hotspots --since 7dTop costly files, bash commands, and subagents for the week.
burn hotspots --all --project .Full project hotspot list.
burn hotspots --group-by bash-verb --since 7dBash verbs ranked by cost.
burn hotspots --session demo-sessionRestrict the standard attribution view to one session.
burn hotspots --patterns retry-loop,failure-runSurface retry/failure hotspot findings only.
burn hotspots --findings --since 7dUnified severity-ranked findings list across every detector.
burn hotspots --provider anthropicRestrict attribution to Anthropic-served turns.
burn hotspots --rank-by bytesSurface large tool outputs even when truncation kept their token count small.

burn overhead

Use burn overhead when you want to know how much standing instruction files cost. Burn discovers CLAUDE.md, .claude/CLAUDE.md, and AGENTS.md, then attributes cached prompt cost to files and headed sections.

OptionWhat it does
trimPrint projected-savings diffs for high-cost headed sections. Burn does not modify files.
deltasAttribute context growth between consecutive inferences to intervening prompts, tool results, reminders, and compactions.
--project <path>Project to inspect. Defaults to the current directory.
--since <range>Limit attribution to a time window.
--kind <k>Limit to claude-md or agents-md.
trim --top <n>Recommendations per file. Default: 3.
deltas --session <id>Limit context deltas to one session.
deltas --top <n>Context-delta row cap. Default: 20.
deltas --min-delta <tokens>Hide smaller increases. Default: 1000; compactions always remain visible.
deltas --owner <all|main|subagent>Select inference rails. Default: all.
deltas --explainExpand the intervening steps behind each delta.
--jsonEmit machine-readable attribution for report mode or structured trim recommendations in trim mode.
ExampleResult
burn overheadCost per overhead file and section.
burn overhead --since 30dOverhead cost from the last 30 days.
burn overhead --kind claude-mdClaude instruction files only.
burn overhead trim --top 3Top three trim recommendations per file.
burn overhead trim --jsonStructured trim recommendations with projected savings and unified diffs.
burn overhead deltas --top 10 --owner mainLargest context-window increases on the main conversation rail.

Harnesses pay for different files: Claude Code pays for CLAUDE.md; Codex and OpenCode pay for AGENTS.md.

burn compare

Use burn compare when you want evidence for model choice. It compares models on the work you actually ran, grouped by activity such as coding, debugging, testing, review, exploration, docs, and refactoring.

OptionWhat it does
<model_a,model_b[,...]>Required comma-separated model list. At least two models.
--since <range>Limit to a relative range or ISO timestamp.
--project <path>Limit to a project.
--session <id>Limit to one session.
--workflow <id>Limit to turns folded with stamp workflowId=<id>.
--agent <id>Limit to turns folded with stamp agentId=<id>.
--provider <csv>Comma-separated effective providers (case-insensitive).
--min-sample <n>Flag cells below the sample threshold. Default: 5.
--fidelity <class>Minimum data quality: full, usage-only, aggregate-only, cost-only, or partial.
--include-partialInclude every turn. Shorthand for --fidelity partial.
--jsonEmit a stable JSON object.
--csvEmit one row per model/activity pair.
--bucket <duration>Emit a time series across --since instead of one comparison.
--no-archiveAccepted for CLI parity; it is a no-op because the Rust SDK is SQLite-native.

burn compare reads the ledger as-is — it does not run an ingest sweep first. Run burn ingest && burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 30d (or keep burn ingest --watch running) when you need the freshest data.

ExampleResult
burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 30dSide-by-side activity table.
burn compare claude-opus-4-7,claude-sonnet-4-6 --project . --jsonProject-scoped JSON comparison.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --fidelity fullCompare only full-fidelity turns.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --include-partialInclude lower-fidelity records too.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 7d --bucket 1dDaily comparison buckets for the last week.

Run burn summary --by-provider to discover model IDs present in your ledger.

burn ingest

Use burn ingest when sessions already exist, or when another process owns the harness spawn. Default mode scans Claude Code, Codex, and OpenCode stores once.

OptionWhat it does
--watchKeep polling session stores in the foreground.
--interval <ms>Poll interval in milliseconds. Default: 1000.
--quietSuppress stderr progress spinner / breadcrumbs. One-shot mode still writes the final summary on stdout.
--hook claudeRead one Claude Code hook payload from stdin and ingest its single transcript via the SDK fast-path.
--no-fseventsIn watch mode, use polling instead of filesystem events.
ExampleResult
burn ingestScan all known session stores once.
burn ingest --watchKeep the ingest loop running.
burn ingest --watch --no-fseventsPoll session stores when filesystem events are unreliable.
burn ingest --hook claude --quietClaude Code hook path for orchestrators.

burn mcp-server

Use burn mcp-server when an agent should query its own spend mid-session via MCP. The server is stdio-only and read-only.

OptionWhat it does
--session-id <uuid>Default session ID used by session cost, summary, and hotspots when the caller omits one.
--debugEmit protocol diagnostics to stderr.
ToolWhat it returns
burn__sessionCostTotal USD, tokens, turns, and models for a session.
burn__fingerprintCheap change-detection fingerprint for the ledger or a session/project scope.
burn__summaryToken use and cost by tool and model, with optional session, project, time, and enrichment filters.
burn__hotspotsAttribution, grouped hotspots, or findings for expensive and repeated activity.
burn__overheadInstruction-file token overhead and cost by file and section.
burn__overheadTrimRanked instruction-file trimming recommendations and projected savings.
burn__comparePer-model, per-activity cost and outcome comparison.
ExampleResult
burn mcp-server --session-id <uuid>Start a stdio server whose session cost, summary, and hotspots tools default to that session.
burn mcp-serverStart an unscoped stdio server; callers can pass filters explicitly, while overhead and compare remain cross-session by design.

burn state

Use burn state when reports look stale or you want to inspect or rebuild derived storage. Burn stores events and stamps in burn.sqlite, and content/search data in content.sqlite.

Subcommand or optionWhat it does
burn state or burn state statusPrint status for indexes, content, classifier, and archive.
--jsonEmit machine-readable output.
fingerprint [--session <id> | --project <path>]Print a low-cost change token for polling the ledger.
rebuild index|classify|content|archive|allDrop derivable rows and stage the ledger for a fresh ingest. All targets use the same SQLite rebuild transaction.
prune [--days <n|forever>]Delete content rows older than the retention window.
reset [--force] [--reingest] [--json]Preview or perform a wipe of derived events, stamps, content, and ingest cursors. Config, pricing overrides, and source harness logs remain.
ExampleResult
burn stateDerived artifact status.
burn state status --jsonMachine-readable status.
burn state fingerprint --project .Project-scoped ledger change token.
burn state rebuild classifyDrop derivable rows so the next ingest applies current classifier rules.
burn state prune --days 30Prune content older than 30 days.

Follow any state rebuild command with burn ingest to repopulate derived tables from the harness session stores. Before a forced reset, back up enrichment with burn stamps export; ingest cannot reconstruct stamps from harness logs.

burn sessions

burn sessions list prints recent sessions newest-first. The default window is seven days and the default limit is 20 rows. Use --project, --grep, --since, and --limit to narrow or widen the list; --json returns the same records for scripts.

burn sessions list --since 30d --limit 10
burn sessions list --project . --json

The full session IDs copy directly into summary --session, hotspots --session, overhead deltas --session, and flow --session.

burn flow

burn flow --session <id> emits a Mermaid inference-flow DAG to stdout. Use --output <path> for SVG, --json for the graph payload, and --max-turns to cap wide sessions (default: 50; 0 disables the cap). --mermaid forces Mermaid on stdout even when --output also writes an SVG.

burn flow --session demo-session --json

burn stamps

burn stamps export streams every enrichment stamp as JSONL. --out <path> writes the same export to a file.

burn stamps export

burn update

burn update --check reports whether a release is available without installing it. Bare burn update installs the latest release through the package manager that installed Burn; --force reinstalls the latest release when already current. Automatic launch checks are controlled by burn update toggle-auto-update --on and burn update toggle-auto-update --off.

burn update --check
burn update toggle-auto-update --off

Local Data

Burn keeps its 2.x ledger in two SQLite databases. Both use WAL mode, so reporting readers can run while ingest writes. burn.sqlite is the event and metadata database; content.sqlite separates larger prompt, response, and search content from the compact analytical rows.

Path or settingPurpose
~/.agentworkforce/burn/burn.sqliteEvents, stamps, sessions, relationships, and archive metadata.
~/.agentworkforce/burn/content.sqlitePrompt/response content and the FTS5 search index.
~/.agentworkforce/burn/config.jsonContent-storage and retention configuration.
~/.agentworkforce/burn/pending-stamps/Temporary manifests used by launchers that do not expose a session ID before spawn.
RELAYBURN_HOMEOverride the whole Burn data directory.
RELAYBURN_SQLITE_PATHOverride the events database path.
RELAYBURN_CONTENT_PATHOverride the content database path.
RELAYBURN_CONTENT_STORE=full|hash-only|offControl content payload storage. Default: full.
RELAYBURN_CONTENT_TTL_DAYS=<days|forever>Content retention. Default: 90.

RELAYBURN_HOME relocates the complete layout. The two per-database overrides can place event and content data on different volumes. SQLite may create -wal and -shm files beside each open database; they are part of normal WAL operation.

Harness transcripts remain the upstream input for ingest. burn state status shows database paths, row counts, schema metadata, and resolved retention. burn state rebuild ... clears derivable tables for re-ingest, burn state prune applies content retention, and burn state reset previews or performs a full derived-state wipe.

Packages

PackagePurpose
relayburnnpm install wrapper that resolves the prebuilt Rust burn binary from @relayburn/cli-<platform> optional dependencies.
@relayburn/sdkNode facade over the Rust SDK, resolved through @relayburn/sdk-<platform> optional dependencies.
@relayburn/cli-<platform>Prebuilt burn binary packages for supported OS/CPU targets.
@relayburn/sdk-<platform>Prebuilt napi-rs packages for supported OS/CPU targets.
relayburn-sdkRust crate with the embedding API and internal reader/ledger/analyze/ingest modules.
relayburn-cliRust crate that produces the burn binary.

Development

Run pnpm run verify to run every check CI runs.

pnpm install
cargo build --workspace
cargo test --workspace
pnpm run test
pnpm run build:napi

The npm workspace contains the Node SDK facade, the relayburn install wrapper, and the platform package manifests used by release automation.

Pricing

Burn ships with a vendored models.dev pricing snapshot. Refresh it with:

pnpm run pricing:update

The weekly Update models.dev pricing workflow runs the same command and opens or updates a review PR when the snapshot changes. Before opening the PR, it checks the upstream payload for regressions and runs the Rust SDK test suite.

User overrides live at $RELAYBURN_HOME/models.dev.json and take precedence at lookup time.

What this is for

Agent spend happens in a blind spot. You can see a daily dollar total, and maybe a breakdown by model. You cannot see which tool call, file, subagent, or workflow step drove the cost. Burn makes that question answerable.

The deeper question burn is built around is:

Would the same work cost less with a different model, harness, or tool choice - in dollars or token usage?

You cannot answer that from aggregate spend. It requires attribution at the level of the actual work: this Read cost $0.47 because it added 8,200 tokens to context that rode in every one of the next 23 turns' cache-reads. Once spend is visible at that grain, the choice between Opus and Haiku, between Claude Code and another harness, or between letting an agent re-read a file and passing it a cached summary, becomes a decision you can reason about instead of a guess.

Three concrete questions follow:

  1. How much did I spend? - per agent, workflow, session, model, and tool call.
  2. Why was it spent? - tool calls, files, subagents, cache-hit vs fresh input, and persistence.
  3. Where can I save? - redundant reads, bloated context prefixes, retry loops, and model choices that cost more than they needed to.

Burn is local-first. Data lives in SQLite databases on your machine. Burn never phones home. Pricing is looked up at query time from a vendored snapshot, so rate corrections never require rewriting the ledger.

What burn is not

  • Not a dashboard or a product with a UI of its own.
  • Not an automatic optimizer. It surfaces the choices; you decide.
  • Not a leaderboard or a social service.

Composability: how burn plugs into a spawner

Burn is designed to be called by whatever code spawns agent sessions. If you control the spawn, you know things the session log never records on its own: the workflow this session is part of, the persona it is running as, the agent ID, and the tier. Burn accepts that context, attaches it to the session, and makes it queryable later alongside the usage data from the session log.

The primitive is stamping: attach metadata to a session by ID, before or after any turns have been recorded. Launchers that do not know the session ID before spawn should call @relayburn/sdkwritePendingStamp() before starting the agent, then run burn ingest / ingest() to fold the tags onto the discovered turns. Direct Rust embedders with an exact session ID can use relayburn_sdk::Stamp and relayburn_sdk::StampSelector against a LedgerHandle.

Stamp selectors:

  • { sessionId } - all turns in a session.
  • { messageId } - exactly one turn.
  • { sessionId, range: { fromTs, toTs } } - a time window, such as a single workflow step within a long-lived session.

Enrichment values are plain strings (Record<string, string>). Burn does not care what keys you use. Typical keys: agentId, parentAgentId, workflowId, stepId, persona, tier, harness, userLabel.

This is the composability surface. Burn stays small; the spawner owns the context and decides what to attach.

Spawner-integrated ingest

The recommended launcher integration is the Node SDK pending-stamp primitive:

import{writePendingStamp}from"@relayburn/sdk";awaitwritePendingStamp({harness: "codex",cwd: process.cwd(),enrichment: {persona: "code-reviewer",personaTier: "senior",agentworkforce: "1",},});

Then spawn the harness normally and let burn ingest or ingest() scan the session stores. Claude launchers can either preallocate --session-id and write an exact session stamp from Rust, or use writePendingStamp({ harness: "claude", ... }) when the final session ID is not available before spawn.

Codex and OpenCode do not expose a pre-spawn session ID. writePendingStamp() writes a pending-stamp manifest under $RELAYBURN_HOME/pending-stamps/ before the launcher spawns the agent. Ingest resolves the manifest against the first matching session file before the first turn is appended. Claude launchers can use the same pending-stamp path when the final session ID is not available before spawn. Abandoned pending manifests are cleaned up after 24 hours.

For passive ingest, run:

burn ingest
burn ingest --watch --interval 1000

burn ingest scans Claude, Codex, and OpenCode stores once and uses the same cursor and dedup path as the reporting commands. burn ingest --watch keeps that scan loop running in the foreground.

Hook-based ingest for orchestrators

If your code already controls the Claude Code spawn, you can install burn's hooks per invocation via Claude's --settings flag without mutating global ~/.claude/settings.json. Wire the hook command to:

burn ingest --hook claude --quiet

Hook payloads land on stdin and get forwarded to burn ingest. The command is safe to re-fire on every hook; the ledger cursor and dedup path keep ingestion idempotent, so the hook path and normal session-store path reconcile against the same session.

FAQ

What does burn ingest do?

Each harness (Claude Code, Codex, OpenCode) writes its own session transcripts to disk in its own format. burn ingest reads those transcripts, normalizes them, and writes them into burn's local SQLite ledger so the query commands (summary, hotspots, overhead, compare) have something to read against. Three modes:

  • One-shotburn ingest scans every known session store once and exits. Good for backfilling or catching up before a query.
  • Watchburn ingest --watch keeps a loop running in the foreground and picks up new turns as harnesses write them.
  • Hookburn ingest --hook claude --quiet reads a single Claude Code hook payload from stdin and ingests the transcript it points at.

What is a "hook"?

Claude Code (and similar harnesses) lets users register shell commands that fire on lifecycle events — turn start, tool use, session end, etc. When an event fires, Claude pipes a JSON payload describing what just happened into the registered command's stdin.

burn ingest --hook claude is burn registering itself as one of those commands. Instead of polling the transcript file looking for new turns, burn gets pushed each turn the moment it lands. Lower latency, no missed turns, no FS-watching contortions.

Why both watch and hook?

  • Hook is the preferred path when the harness supports it: push-based, exact, no polling.
  • Watch is the fallback for harnesses without hooks, or when the user hasn't wired one up. It polls the session store directories on an interval.
  • One-shot is for backfill, cron, or "just give me numbers now."

For most solo users, burn ingest --watch in a background terminal is simplest. The hook path matters most when you're building a launcher that spawns Claude Code programmatically and wants per-invocation control without mutating global config.

How is the hook installed?

Burn does not ship an auto-installer. There are two integration paths:

Per-invocation (orchestrators / launchers). If you control the Claude Code spawn, pass --settings <path> to Claude with a JSON file that registers burn ingest --hook claude --quiet against the hook events you care about. This is the recommended path because it avoids touching global config and stays scoped to the invocation.

Global (manual). Edit ~/.claude/settings.json (or the project-level .claude/settings.json) and add burn ingest --hook claude --quiet to the hook events you want to wire. Burn's hook policy is to always exit 0 so it can never break the surrounding Claude Code session, and payloads missing session_id or transcript_path are ignored, so wiring it onto extra events is safe.

If you don't want to deal with hook config at all, run burn ingest --watch instead — same data, slightly higher latency.

License

Apache-2.0 - Copyright 2026 Agent Workforce Incorporated

About

The first step to burning less tokens is understanding how many you burn to begin with

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

relayburn

Understand how you're spending tokens in agent CLIs. Burn ingests Claude Code, Codex, and OpenCode session logs into a local ledger, then shows cost by model, provider, tool, file, workflow, agent, session, and overhead file.

Quick Start

npm i -g relayburn
burn summary

Burn stores data under ~/.agentworkforce/burn/ by default. Set RELAYBURN_HOME to use a different location.

Commands

CommandUse it to
burn summarySee total usage and cost by model or provider.
burn hotspotsFind expensive files, commands, and subagents.
burn overheadAttribute cached prompt cost to CLAUDE.md, .claude/CLAUDE.md, and AGENTS.md.
burn compareCompare observed model performance by activity: cost per turn, one-shot rate, and sample size.
burn stateInspect, fingerprint, rebuild, prune, or reset local ledger state.
burn sessionsFind recent session IDs for drill-down queries.
burn flowRender a session's inference and subagent flow as Mermaid, SVG, or JSON.
burn stampsExport enrichment stamps as JSONL.
burn ingestImport existing or live session logs without wrapping the harness.
burn mcp-serverExpose read-only cost queries to an agent through stdio MCP.
burn updateCheck for releases, install an update, or configure automatic checks.

Every command accepts --json for machine-readable output, --ledger-path <path> to select a Burn home for that invocation, and --no-color to disable ANSI styling.

burn summary

Use burn summary when you want the fast answer: how many turns ran, how many tokens they used, and what they cost.

OptionWhat it does
--since <range>Limit to a relative range like 24h, 7d, or 4w, or an ISO timestamp.
--project <path>Limit to a project path or git-canonical project key.
--session <id>Limit to one session.
--workflow <id>Limit to turns folded under a workflowId enrichment stamp.
--tag k=vFilter by folded enrichment tag. Repeatable; all tags must match.
--group-by-tag <key>Group totals by a folded enrichment tag value.
--by-providerGroup totals by provider instead of model.
--by-toolAttribute each turn's ingest cost to the preceding tool calls.
--by-subagent-typeGroup totals by subagent type.
--by-relationship [subagent]Group by session relationship, optionally drilling into subagent leaves.
--subagent-tree [session]Render the subagent spawn tree. Uses --session when passed without a value.
--agent <id>Limit subagent-tree or relationship views to one agent.
--provider <csv>Limit results to effective providers.
--qualityAppend one-shot and completion-outcome metrics.
--bucket <duration>Emit fixed-width time buckets across the --since window.
--ingestRun one ingest sweep before querying.
--no-archiveAccepted for CLI parity; it is a no-op because the Rust SDK is SQLite-native.
--jsonEmit machine-readable output.
ExampleResult
burn summaryAll-time cost by model.
burn summary --since 24hCost from the last 24 hours.
burn summary --by-providerCost grouped by effective provider.
burn summary --by-toolCost grouped by the tool calls that preceded each turn.
burn summary --qualityUsage totals with one-shot and completion outcomes.
burn summary --since 24h --bucket 1hHourly usage and cost for the last day.
burn summary --tag persona=code-reviewerCost for sessions stamped with that persona tag.
burn summary --group-by-tag personaCost grouped by persona value.

Synthetic-routed models are recognized from hf:*, accounts/fireworks/models/*, and synthetic/*.

burn hotspots

Use burn hotspots when you want to know what made a session or time window expensive. The default view attributes spend to files, bash commands, and subagents.

OptionWhat it does
--since <range>Limit to a relative range or ISO timestamp.
--project <path>Limit to a project.
--session <id>Limit to a single session id.
--workflow <id>Limit to turns folded under a workflowId enrichment stamp.
--provider <csv>Restrict to providers (case-insensitive CSV — e.g. anthropic,openai).
--allShow every row instead of the top 10.
--group-by <dim>Focus one rollup: attribution, bash, bash-verb, file, or subagent.
--patterns [csv]Run hotspot-pattern detectors instead of the attribution view. Pass without a value to enable every detector, or pass a CSV (e.g. retry-loop,failure-run).
--findingsEmit the unified findings table instead of the per-detector grouping. Implies --patterns if not already set.
--rank-by <cost|bytes>Rank per-tool tables by USD or raw output bytes. Default: cost.
--ingestRun one ingest sweep before querying.
--explain-driftReserved for relationship-drift analysis; currently exits with a directed unsupported message.
--jsonEmit machine-readable output.
ExampleResult
burn hotspots --since 7dTop costly files, bash commands, and subagents for the week.
burn hotspots --all --project .Full project hotspot list.
burn hotspots --group-by bash-verb --since 7dBash verbs ranked by cost.
burn hotspots --session demo-sessionRestrict the standard attribution view to one session.
burn hotspots --patterns retry-loop,failure-runSurface retry/failure hotspot findings only.
burn hotspots --findings --since 7dUnified severity-ranked findings list across every detector.
burn hotspots --provider anthropicRestrict attribution to Anthropic-served turns.
burn hotspots --rank-by bytesSurface large tool outputs even when truncation kept their token count small.

burn overhead

Use burn overhead when you want to know how much standing instruction files cost. Burn discovers CLAUDE.md, .claude/CLAUDE.md, and AGENTS.md, then attributes cached prompt cost to files and headed sections.

OptionWhat it does
trimPrint projected-savings diffs for high-cost headed sections. Burn does not modify files.
deltasAttribute context growth between consecutive inferences to intervening prompts, tool results, reminders, and compactions.
--project <path>Project to inspect. Defaults to the current directory.
--since <range>Limit attribution to a time window.
--kind <k>Limit to claude-md or agents-md.
trim --top <n>Recommendations per file. Default: 3.
deltas --session <id>Limit context deltas to one session.
deltas --top <n>Context-delta row cap. Default: 20.
deltas --min-delta <tokens>Hide smaller increases. Default: 1000; compactions always remain visible.
deltas --owner <all|main|subagent>Select inference rails. Default: all.
deltas --explainExpand the intervening steps behind each delta.
--jsonEmit machine-readable attribution for report mode or structured trim recommendations in trim mode.
ExampleResult
burn overheadCost per overhead file and section.
burn overhead --since 30dOverhead cost from the last 30 days.
burn overhead --kind claude-mdClaude instruction files only.
burn overhead trim --top 3Top three trim recommendations per file.
burn overhead trim --jsonStructured trim recommendations with projected savings and unified diffs.
burn overhead deltas --top 10 --owner mainLargest context-window increases on the main conversation rail.

Harnesses pay for different files: Claude Code pays for CLAUDE.md; Codex and OpenCode pay for AGENTS.md.

burn compare

Use burn compare when you want evidence for model choice. It compares models on the work you actually ran, grouped by activity such as coding, debugging, testing, review, exploration, docs, and refactoring.

OptionWhat it does
<model_a,model_b[,...]>Required comma-separated model list. At least two models.
--since <range>Limit to a relative range or ISO timestamp.
--project <path>Limit to a project.
--session <id>Limit to one session.
--workflow <id>Limit to turns folded with stamp workflowId=<id>.
--agent <id>Limit to turns folded with stamp agentId=<id>.
--provider <csv>Comma-separated effective providers (case-insensitive).
--min-sample <n>Flag cells below the sample threshold. Default: 5.
--fidelity <class>Minimum data quality: full, usage-only, aggregate-only, cost-only, or partial.
--include-partialInclude every turn. Shorthand for --fidelity partial.
--jsonEmit a stable JSON object.
--csvEmit one row per model/activity pair.
--bucket <duration>Emit a time series across --since instead of one comparison.
--no-archiveAccepted for CLI parity; it is a no-op because the Rust SDK is SQLite-native.

burn compare reads the ledger as-is — it does not run an ingest sweep first. Run burn ingest && burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 30d (or keep burn ingest --watch running) when you need the freshest data.

ExampleResult
burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 30dSide-by-side activity table.
burn compare claude-opus-4-7,claude-sonnet-4-6 --project . --jsonProject-scoped JSON comparison.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --fidelity fullCompare only full-fidelity turns.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --include-partialInclude lower-fidelity records too.
burn compare claude-sonnet-4-6,claude-haiku-4-5 --since 7d --bucket 1dDaily comparison buckets for the last week.

Run burn summary --by-provider to discover model IDs present in your ledger.

burn ingest

Use burn ingest when sessions already exist, or when another process owns the harness spawn. Default mode scans Claude Code, Codex, and OpenCode stores once.

OptionWhat it does
--watchKeep polling session stores in the foreground.
--interval <ms>Poll interval in milliseconds. Default: 1000.
--quietSuppress stderr progress spinner / breadcrumbs. One-shot mode still writes the final summary on stdout.
--hook claudeRead one Claude Code hook payload from stdin and ingest its single transcript via the SDK fast-path.
--no-fseventsIn watch mode, use polling instead of filesystem events.
ExampleResult
burn ingestScan all known session stores once.
burn ingest --watchKeep the ingest loop running.
burn ingest --watch --no-fseventsPoll session stores when filesystem events are unreliable.
burn ingest --hook claude --quietClaude Code hook path for orchestrators.

burn mcp-server

Use burn mcp-server when an agent should query its own spend mid-session via MCP. The server is stdio-only and read-only.

OptionWhat it does
--session-id <uuid>Default session ID used by session cost, summary, and hotspots when the caller omits one.
--debugEmit protocol diagnostics to stderr.
ToolWhat it returns
burn__sessionCostTotal USD, tokens, turns, and models for a session.
burn__fingerprintCheap change-detection fingerprint for the ledger or a session/project scope.
burn__summaryToken use and cost by tool and model, with optional session, project, time, and enrichment filters.
burn__hotspotsAttribution, grouped hotspots, or findings for expensive and repeated activity.
burn__overheadInstruction-file token overhead and cost by file and section.
burn__overheadTrimRanked instruction-file trimming recommendations and projected savings.
burn__comparePer-model, per-activity cost and outcome comparison.
ExampleResult
burn mcp-server --session-id <uuid>Start a stdio server whose session cost, summary, and hotspots tools default to that session.
burn mcp-serverStart an unscoped stdio server; callers can pass filters explicitly, while overhead and compare remain cross-session by design.

burn state

Use burn state when reports look stale or you want to inspect or rebuild derived storage. Burn stores events and stamps in burn.sqlite, and content/search data in content.sqlite.

Subcommand or optionWhat it does
burn state or burn state statusPrint status for indexes, content, classifier, and archive.
--jsonEmit machine-readable output.
fingerprint [--session <id> | --project <path>]Print a low-cost change token for polling the ledger.
rebuild index|classify|content|archive|allDrop derivable rows and stage the ledger for a fresh ingest. All targets use the same SQLite rebuild transaction.
prune [--days <n|forever>]Delete content rows older than the retention window.
reset [--force] [--reingest] [--json]Preview or perform a wipe of derived events, stamps, content, and ingest cursors. Config, pricing overrides, and source harness logs remain.
ExampleResult
burn stateDerived artifact status.
burn state status --jsonMachine-readable status.
burn state fingerprint --project .Project-scoped ledger change token.
burn state rebuild classifyDrop derivable rows so the next ingest applies current classifier rules.
burn state prune --days 30Prune content older than 30 days.

Follow any state rebuild command with burn ingest to repopulate derived tables from the harness session stores. Before a forced reset, back up enrichment with burn stamps export; ingest cannot reconstruct stamps from harness logs.

burn sessions

burn sessions list prints recent sessions newest-first. The default window is seven days and the default limit is 20 rows. Use --project, --grep, --since, and --limit to narrow or widen the list; --json returns the same records for scripts.

burn sessions list --since 30d --limit 10
burn sessions list --project . --json

The full session IDs copy directly into summary --session, hotspots --session, overhead deltas --session, and flow --session.

burn flow

burn flow --session <id> emits a Mermaid inference-flow DAG to stdout. Use --output <path> for SVG, --json for the graph payload, and --max-turns to cap wide sessions (default: 50; 0 disables the cap). --mermaid forces Mermaid on stdout even when --output also writes an SVG.

burn flow --session demo-session --json

burn stamps

burn stamps export streams every enrichment stamp as JSONL. --out <path> writes the same export to a file.

burn stamps export

burn update

burn update --check reports whether a release is available without installing it. Bare burn update installs the latest release through the package manager that installed Burn; --force reinstalls the latest release when already current. Automatic launch checks are controlled by burn update toggle-auto-update --on and burn update toggle-auto-update --off.

burn update --check
burn update toggle-auto-update --off

Local Data

Burn keeps its 2.x ledger in two SQLite databases. Both use WAL mode, so reporting readers can run while ingest writes. burn.sqlite is the event and metadata database; content.sqlite separates larger prompt, response, and search content from the compact analytical rows.

Path or settingPurpose
~/.agentworkforce/burn/burn.sqliteEvents, stamps, sessions, relationships, and archive metadata.
~/.agentworkforce/burn/content.sqlitePrompt/response content and the FTS5 search index.
~/.agentworkforce/burn/config.jsonContent-storage and retention configuration.
~/.agentworkforce/burn/pending-stamps/Temporary manifests used by launchers that do not expose a session ID before spawn.
RELAYBURN_HOMEOverride the whole Burn data directory.
RELAYBURN_SQLITE_PATHOverride the events database path.
RELAYBURN_CONTENT_PATHOverride the content database path.
RELAYBURN_CONTENT_STORE=full|hash-only|offControl content payload storage. Default: full.
RELAYBURN_CONTENT_TTL_DAYS=<days|forever>Content retention. Default: 90.

RELAYBURN_HOME relocates the complete layout. The two per-database overrides can place event and content data on different volumes. SQLite may create -wal and -shm files beside each open database; they are part of normal WAL operation.

Harness transcripts remain the upstream input for ingest. burn state status shows database paths, row counts, schema metadata, and resolved retention. burn state rebuild ... clears derivable tables for re-ingest, burn state prune applies content retention, and burn state reset previews or performs a full derived-state wipe.

Packages

PackagePurpose
relayburnnpm install wrapper that resolves the prebuilt Rust burn binary from @relayburn/cli-<platform> optional dependencies.
@relayburn/sdkNode facade over the Rust SDK, resolved through @relayburn/sdk-<platform> optional dependencies.
@relayburn/cli-<platform>Prebuilt burn binary packages for supported OS/CPU targets.
@relayburn/sdk-<platform>Prebuilt napi-rs packages for supported OS/CPU targets.
relayburn-sdkRust crate with the embedding API and internal reader/ledger/analyze/ingest modules.
relayburn-cliRust crate that produces the burn binary.

Development

Run pnpm run verify to run every check CI runs.

pnpm install
cargo build --workspace
cargo test --workspace
pnpm run test
pnpm run build:napi

The npm workspace contains the Node SDK facade, the relayburn install wrapper, and the platform package manifests used by release automation.

Pricing

Burn ships with a vendored models.dev pricing snapshot. Refresh it with:

pnpm run pricing:update

The weekly Update models.dev pricing workflow runs the same command and opens or updates a review PR when the snapshot changes. Before opening the PR, it checks the upstream payload for regressions and runs the Rust SDK test suite.

User overrides live at $RELAYBURN_HOME/models.dev.json and take precedence at lookup time.

What this is for

Agent spend happens in a blind spot. You can see a daily dollar total, and maybe a breakdown by model. You cannot see which tool call, file, subagent, or workflow step drove the cost. Burn makes that question answerable.

The deeper question burn is built around is:

Would the same work cost less with a different model, harness, or tool choice - in dollars or token usage?

You cannot answer that from aggregate spend. It requires attribution at the level of the actual work: this Read cost $0.47 because it added 8,200 tokens to context that rode in every one of the next 23 turns' cache-reads. Once spend is visible at that grain, the choice between Opus and Haiku, between Claude Code and another harness, or between letting an agent re-read a file and passing it a cached summary, becomes a decision you can reason about instead of a guess.

Three concrete questions follow:

  1. How much did I spend? - per agent, workflow, session, model, and tool call.
  2. Why was it spent? - tool calls, files, subagents, cache-hit vs fresh input, and persistence.
  3. Where can I save? - redundant reads, bloated context prefixes, retry loops, and model choices that cost more than they needed to.

Burn is local-first. Data lives in SQLite databases on your machine. Burn never phones home. Pricing is looked up at query time from a vendored snapshot, so rate corrections never require rewriting the ledger.

What burn is not

  • Not a dashboard or a product with a UI of its own.
  • Not an automatic optimizer. It surfaces the choices; you decide.
  • Not a leaderboard or a social service.

Composability: how burn plugs into a spawner

Burn is designed to be called by whatever code spawns agent sessions. If you control the spawn, you know things the session log never records on its own: the workflow this session is part of, the persona it is running as, the agent ID, and the tier. Burn accepts that context, attaches it to the session, and makes it queryable later alongside the usage data from the session log.

The primitive is stamping: attach metadata to a session by ID, before or after any turns have been recorded. Launchers that do not know the session ID before spawn should call @relayburn/sdkwritePendingStamp() before starting the agent, then run burn ingest / ingest() to fold the tags onto the discovered turns. Direct Rust embedders with an exact session ID can use relayburn_sdk::Stamp and relayburn_sdk::StampSelector against a LedgerHandle.

Stamp selectors:

  • { sessionId } - all turns in a session.
  • { messageId } - exactly one turn.
  • { sessionId, range: { fromTs, toTs } } - a time window, such as a single workflow step within a long-lived session.

Enrichment values are plain strings (Record<string, string>). Burn does not care what keys you use. Typical keys: agentId, parentAgentId, workflowId, stepId, persona, tier, harness, userLabel.

This is the composability surface. Burn stays small; the spawner owns the context and decides what to attach.

Spawner-integrated ingest

The recommended launcher integration is the Node SDK pending-stamp primitive:

import{writePendingStamp}from"@relayburn/sdk";awaitwritePendingStamp({harness: "codex",cwd: process.cwd(),enrichment: {persona: "code-reviewer",personaTier: "senior",agentworkforce: "1",},});

Then spawn the harness normally and let burn ingest or ingest() scan the session stores. Claude launchers can either preallocate --session-id and write an exact session stamp from Rust, or use writePendingStamp({ harness: "claude", ... }) when the final session ID is not available before spawn.

Codex and OpenCode do not expose a pre-spawn session ID. writePendingStamp() writes a pending-stamp manifest under $RELAYBURN_HOME/pending-stamps/ before the launcher spawns the agent. Ingest resolves the manifest against the first matching session file before the first turn is appended. Claude launchers can use the same pending-stamp path when the final session ID is not available before spawn. Abandoned pending manifests are cleaned up after 24 hours.

For passive ingest, run:

burn ingest
burn ingest --watch --interval 1000

burn ingest scans Claude, Codex, and OpenCode stores once and uses the same cursor and dedup path as the reporting commands. burn ingest --watch keeps that scan loop running in the foreground.

Hook-based ingest for orchestrators

If your code already controls the Claude Code spawn, you can install burn's hooks per invocation via Claude's --settings flag without mutating global ~/.claude/settings.json. Wire the hook command to:

burn ingest --hook claude --quiet

Hook payloads land on stdin and get forwarded to burn ingest. The command is safe to re-fire on every hook; the ledger cursor and dedup path keep ingestion idempotent, so the hook path and normal session-store path reconcile against the same session.

FAQ

What does burn ingest do?

Each harness (Claude Code, Codex, OpenCode) writes its own session transcripts to disk in its own format. burn ingest reads those transcripts, normalizes them, and writes them into burn's local SQLite ledger so the query commands (summary, hotspots, overhead, compare) have something to read against. Three modes:

  • One-shotburn ingest scans every known session store once and exits. Good for backfilling or catching up before a query.
  • Watchburn ingest --watch keeps a loop running in the foreground and picks up new turns as harnesses write them.
  • Hookburn ingest --hook claude --quiet reads a single Claude Code hook payload from stdin and ingests the transcript it points at.

What is a "hook"?

Claude Code (and similar harnesses) lets users register shell commands that fire on lifecycle events — turn start, tool use, session end, etc. When an event fires, Claude pipes a JSON payload describing what just happened into the registered command's stdin.

burn ingest --hook claude is burn registering itself as one of those commands. Instead of polling the transcript file looking for new turns, burn gets pushed each turn the moment it lands. Lower latency, no missed turns, no FS-watching contortions.

Why both watch and hook?

  • Hook is the preferred path when the harness supports it: push-based, exact, no polling.
  • Watch is the fallback for harnesses without hooks, or when the user hasn't wired one up. It polls the session store directories on an interval.
  • One-shot is for backfill, cron, or "just give me numbers now."

For most solo users, burn ingest --watch in a background terminal is simplest. The hook path matters most when you're building a launcher that spawns Claude Code programmatically and wants per-invocation control without mutating global config.

How is the hook installed?

Burn does not ship an auto-installer. There are two integration paths:

Per-invocation (orchestrators / launchers). If you control the Claude Code spawn, pass --settings <path> to Claude with a JSON file that registers burn ingest --hook claude --quiet against the hook events you care about. This is the recommended path because it avoids touching global config and stays scoped to the invocation.

Global (manual). Edit ~/.claude/settings.json (or the project-level .claude/settings.json) and add burn ingest --hook claude --quiet to the hook events you want to wire. Burn's hook policy is to always exit 0 so it can never break the surrounding Claude Code session, and payloads missing session_id or transcript_path are ignored, so wiring it onto extra events is safe.

If you don't want to deal with hook config at all, run burn ingest --watch instead — same data, slightly higher latency.

License

Apache-2.0 - Copyright 2026 Agent Workforce Incorporated

About

The first step to burning less tokens is understanding how many you burn to begin with

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages