← All Projects

ContextLedger: Reversible Token Compression for Agent Tool I/O

The dominant cost in a coding-agent session is not the user prompt, it is the output of tool calls: file reads, git status, test runs, JSON blobs. ContextLedger compresses that output before it reaches the model, caches the original so nothing is lost, and gives the model a retrieve(id) escape hatch. It unifies three patterns the field arrived at independently (RTK's shell compression, Headroom's reversible Compress-Cache-Retrieve, Code Mode's typed-tool execution). On a real 127-payload dev session it measured a 44.0% tool-output token reduction, fully reversible.

44.0% tool-output token reduction, fully reversible
127-payload real session, re-runnable harness
72% on code payloads via AST strip
~1% target retrieve rate, nothing lost
Python Pydantic SQLite tiktoken MCP pytest

Overview#

Everyone running agents at scale worries about the token bill, and almost everyone misreads where it comes from. The instinct is to shorten the prompt. The actual spend is somewhere else: the output of tool calls. Every file read, git status, test run, JSON response, and web scrape dumps its full text into context, and the harness re-feeds that accumulated history on every subsequent turn. The prompt is a sliver. The tool noise is the bill.

ContextLedger is our tool for cutting that bill without touching the agent. It compresses each tool result before it reaches the model, caches the original in a local store so nothing is ever lost, and hands the model a retrieve(id) instruction it can call if a payload was trimmed too hard. The result is fewer tokens into context, full recoverability, and no change to the agent’s own code.

Diagram of ContextLedger's thesis: a verbose tool output flows into a compression layer that caches the original and emits a compact payload carrying a retrieve id, which then enters the model context

Figure 1 - The bill is tool output, not the prompt: A verbose tool result flows into the ContextLedger layer, which caches the untouched original and emits a compact payload carrying a retrieve(id) handle. Only the compact form enters the model. Nothing is discarded.

The Problem#

The cost is invisible because of how agent harnesses work. They accumulate history and re-feed it every turn, so prompt-side tokens pile up while output stays small. Sebastian Raschka’s widely-shared observation of a roughly 128:1 input-to-output ratio on a single session names the shape exactly: input dominates the bill, and the largest controllable share of that input is tool output being re-fed turn after turn. RTK’s own measurement puts one 30-minute session at roughly 118,000 tokens spent on shell output alone.

There is a second, quieter cost. Stuffing verbose tool noise into context also pushes the agent past the practitioner “smart zone” of roughly 120,000 tokens, where attention diffuses and answer quality drops. So the tool-output problem is charged twice: once on the invoice, once on the reasoning.

BeforeAfter
Token bill treated as a prompt-length problemCompression aimed where the tokens actually are, tool output
Full tool output re-fed into context every turnCompact payloads enter context, originals cached out of band
”Compression might drop what I needed”Every original cached, retrieve(id) returns it byte-for-byte
Vendor savings figures you cannot reproduceYour own re-runnable before/after number on your own session
One-size-fits-all truncationA content router picks the right compressor per payload type

KEY INSIGHT: The expensive thing in an agent session is not what you ask, it is what the tools answer with, re-fed on every turn. Compress that, cache the original, and the bill drops without the agent losing access to a single byte.

The Convergence It Unifies#

ContextLedger is not a novel trick. It is the synthesis of three fixes three different teams reached independently, each from a different layer of the stack. That independent convergence is the reason it is worth building as one coherent reference rather than a one-off hook.

PatternLayerWhat it doesReversible?
RTKShellRewrites git status / cat / test output via a Bash hook before the agent sees itNo
HeadroomAPI proxyType-aware compressors plus Compress-Cache-RetrieveYes
Code ModeTool surfaceAgent runs code against typed MCP APIs; only the result returnsN/A

Three teams, three layers, one conclusion: compress tool I/O before it hits the model. ContextLedger combines the shell-layer cheapness of RTK with the reversibility of Headroom’s Compress-Cache-Retrieve, the trust unlock RTK lacks, and exposes the whole thing as portable MCP middleware.

KEY INSIGHT: When three teams reinvent the same fix from three different layers, the pattern is validated and the opportunity is the unification. ContextLedger is the stackable library that has the shell layer’s cost, the proxy layer’s reversibility, and the tool layer’s portability at once.

What We Built: Compress-Cache-Retrieve#

A content router dispatches each tool result to the compressor that fits its type, because the documented failure mode of this space is one-size-fits-all truncation. Four compressors sit behind it: a shell smart-filter and grouper, a JSON field-pruner, an AST-strip for source code built on the stdlib ast module, and a truncate-with-summary fallback for everything else.

Reversibility is the part that makes aggressive compression safe. Every original payload lands in a local SQLite store keyed by an id, and the compact text that goes to the model always carries a retrieve(id) instruction. If the model decides it needs the full thing back, it calls retrieve and gets the original byte-for-byte. In practice the model pulls originals only about 1% of the time, which is exactly why the compression can be as aggressive as it is.

Diagram of the Compress-Cache-Retrieve loop: a raw payload enters the router, is compressed, the original is written to a SQLite cache keyed by id, the compact payload plus retrieve handle goes to the model, and a retrieve call pulls the original back from the cache

Figure 2 - Nothing is lost: The router compresses the payload and writes the untouched original to a local SQLite store keyed by an id. The compact payload carries that id. When the model calls retrieve(id), the original comes back byte-for-byte. Reversibility is the trust unlock that lets the compression be aggressive.

The Number#

ContextLedger is a measurement engine as much as a compressor. Every public figure in this space is vendor-self-reported (RTK’s roughly 80%, Headroom’s 200 billion tokens saved, Code Mode’s 98.7%), so the project’s whole value is producing an independent, re-runnable number instead of borrowing one.

On a real captured dev session of 127 tool payloads, ContextLedger’s own harness measured a 44.0% tool-output token reduction, fully reversible: 22,093 tokens before, 12,383 after, 9,710 saved. The reduction is not uniform, and the breakdown is the interesting part.

Screenshot of ContextLedger's benchmark report showing a 44.0 percent headline, 22093 tokens before and 12383 after across 127 payloads, and a by-type breakdown of 72 percent on code, 32 percent on text, and 17 percent on shell

Figure 3 - The actual benchmark report: The 44.0% headline, the before and after totals, and the reduction by payload type, rendered as a self-contained HTML report with no server and no external assets. Code compresses hardest (72%) because AST-strip discards structure the model can reconstruct; shell is the floor (17%) because much of it is already terse.

Payload typeCountBeforeAfterReduction
code579,2102,58572%
text285,9924,09332%
shell426,8915,70517%
TOTAL12722,09312,38344%

The whole table regenerates on demand with contextledger benchmark --session <capture> --html report.html. The number is not a screenshot; it is a re-runnable result.

KEY INSIGHT: Lead with your own measured number, never a borrowed one. Every headline in this space is self-reported by the vendor selling it. The defensible figure is the one your own harness prints on a re-run of the client’s own session.

Two Ways to Use It#

There are two distinct client scenarios, and conflating them is a mistake. The first requires no integration at all; the second cuts live tokens.

Audit first. Point ContextLedger at a real Claude Code session transcript, extract its tool outputs into a corpus, and produce the headline number plus a shareable HTML report. This changes nothing in the client’s agent and is the fastest possible win. It is the Agent Token-Cost Audit deliverable, and it pairs directly with TraceKit: TraceKit measures the bill, ContextLedger cuts it.

Then integrate, at whichever layer matches the coverage the client needs.

Diagram of the three integration layers: a library wrapper for custom Python agents with full coverage, an MCP server exposing typed compress and retrieve tools for any MCP agent, and a shell hook covering Bash output only, arranged from most to least coverage

Figure 4 - Pick the layer by coverage: The library wrapper covers every tool result in a custom Python agent. The MCP server exposes typed compress and retrieve tools to any MCP-speaking agent, cross-vendor, including native Read, Grep, and Glob. The shell hook is the cheapest but sees only Bash output. Coverage decreases left to right; portability is highest in the middle.

The honest limit lives in that last layer: a Bash hook only sees Bash. Native Read, Grep, and Glob bypass it, so shell-layer savings are bounded by the Bash-versus-native ratio of the session. The MCP layer is the portable path that closes that gap, which is why we lead with it.

What It Does Not Do#

These caveats travel with every client-facing deliverable, because they are the difference between a defensible number and an overstated one.

  • The 44.0% is ContextLedger’s own self-measured figure on one captured session. It regenerates on demand, but it is a measurement of that corpus, not a universal savings rate. The rate for any client is whatever the harness prints on their session.
  • Borrowed vendor figures are narrative context, never the claim. RTK’s 80%, Headroom’s $700K, Code Mode’s 98.7% are self-reported by the teams that shipped them. They motivate the work; they are never presented as ContextLedger’s result.
  • Shell-layer coverage is bounded. A Bash hook cannot see native file tools. Real live savings depend on the layer chosen and the tool mix of the workload.
  • The smart-zone threshold is a practitioner heuristic. The roughly 120,000-token figure is a community observation, not a vendor specification. The attention-diffusion mechanism behind it is sound; the exact number is not.

Platform Portability#

ContextLedger operates on tool I/O, not on any vendor’s internals, so it is model- and harness-agnostic by nature. The most portable delivery form is MCP middleware: interpose on tool calls for any MCP-speaking agent (OpenAI Codex, Claude Code, OpenCode, and others) so the Compress-Cache-Retrieve layer works cross-vendor without a per-harness hook. The shell-hook and library variants remain per-environment options. We position it publicly as a platform-agnostic agent token-cost layer.

As a Consulting Engagement#

ContextLedger is the instrument behind our Agent Token-Cost Audit. The engagement starts with zero integration: capture a real session, run the benchmark, and hand back the client’s own measured before-and-after number with a self-contained HTML report. If the client wants live savings, we wire ContextLedger in at the layer that matches their coverage need, library, MCP, or shell hook, and track the headline over time as their usage evolves. It sits alongside TraceKit and RubricGate as one suite: TraceKit measures the token bill and grades the trajectory, ContextLedger cuts the bill, RubricGate grades the final artifact.

Technologies#

LayerTechnology
RuntimePython 3.11+, uv
RouterContent-type dispatch to four compressors
CompressorsShell smart-filter/group, JSON field-pruner, stdlib ast code strip, truncate-with-summary fallback
Reversible storeSQLite Compress-Cache-Retrieve cache, id-keyed, retrieve(id) handle
Instrumentationtiktoken exact counts (optional extra), stdlib estimate fallback
PortabilityMCP adapter (contextledger serve) exposing typed compress / retrieve
Qualitypytest benchmark harness, re-runnable before/after table, ruff clean

The throughline: the agent’s token bill is dominated by tool output, re-fed on every turn. ContextLedger compresses that output before it reaches the model, caches every original so nothing is lost, and publishes its own re-runnable number instead of borrowing anyone else’s.

Have a project like this in mind?

That is the kind of work we do at Dotzlaw Consulting. Book a free 20-minute intro call and tell us what you are trying to build, or what is slowing you down.

← Back to Projects