Context Batteries
Your agent's context window is filling up. Every turn adds messages, tool results, retrieved chunks — and sooner or later a dispatch (the model-execution cycle inside one turn: the loop that calls the model, chases tool calls, and exits on a terminal signal) has more candidate content than its window can hold. At that point you have exactly three options: cut something out, summarize what's aging out, or let it ride and hope the window is big enough. The third option isn't a strategy — it's a slow-motion failure that only announces itself once a model is already drowning in bloated context. A context battery is the code that picks between the two options that actually are strategies — not "what happened in this conversation" (your durable record already has that), but narrower and per-dispatch: this one call, this one window, what goes in.
Two batteries answer it differently, at genuinely different costs:
- Token Thrift — a pure, model-free algorithm. Hold a large working set (messages, memories, retrievables, thoughts, an image, tools — everything reasonable to send, before any shedding) and SUBTRACT it down to the highest-signal slice that fits, shedding the lowest-signal content first. Every decision is a measured token comparison; zero extra model calls.
- Compact — instead of shedding old turns outright, pay for a model call to compress them into a running summary, trading a real API cost for retaining compressed signal from everything folded away.
These are not two maturity levels of the same idea. They are different bets, evaluated head-to-head, and this page reports the honest result of that evaluation — including where the more expensive strategy won.
Concept vs. reference
This page (and its two children) is API/usage reference — what to import, what the options do, how to call it. The why — the research behind subtractive context, the case against naive accumulation, when thrift is the wrong policy entirely — lives on Token Thrift (The Loop), and the live proof (a real 2B model, a window you drag) is on the Punching Above Its Weights showcase. Read those for the narrative; read these three pages when you're wiring the battery into your own pipeline.
The matrix
Scope first, so the table can't oversell itself: five cells don't cover every workload, and they're not meant to. This matrix exists to answer one question — is the thrift method worth shipping as a battery? — and the answer it gives is yes, not as a silver bullet, but as a competent generalist: never the strategy that collapses, paying benefits at both ends of the spectrum — the capacity-starved edge (an E2B at 8k) and the capability-rich cloud (a 1M window at 2.7× less cost for tied quality).
Both strategies were evaluated head-to-head against each other AND against a naive-recency baseline (selectNaiveTurns, thrift's own honest FIFO comparison arm — one strategy under test in a given cell) across five model/window cells (a fixed model paired with a fixed window size, e.g. "gemma e2b @ 8k"), on a shared 94-turn stress corpus. Each score is a cell's average over all 94 turns, 0-3 per turn, doc-verified by a blind committee (judge models that never see which strategy produced an answer) — compare within a row, not across rows. Scoring is a 3-judge panel (gpt-5.5 + opus + deepseek, per-turn scores merged) except the one row noted as a 2-judge pool (gpt-5.5 + opus) — that row is final as-is: its thrift run transcript aged out of temporary storage before the third judge's pass. Re-running the arm wouldn't recover it — judges score each turn as a blind comparison across all three arms, so fresh answers would invalidate the existing per-turn scores and force a full three-judge re-evaluation of the cell. Quality alone is half the bill, so each cell also carries what it cost to earn the score: Σ is the arm's total token bill for the whole 94-turn run (input + output, summed from the engine's per-dispatch counts in the run dumps, dedup-swept and reconciled against the harness reports — compact's Σ includes its summarizer dispatches), d/t is model dispatches per answered turn (per-turn harness reports; every cell 94/94 settled, zero errored turns), and compact's cells name that summarizer overhead separately. Score higher-is-better, Σ and dispatches lower-is-better:
| model / window | thrift | compact | naive | committee |
|---|---|---|---|---|
| gemma e2b @ 8k | 0.82 Σ 3.5M · 5.8 d/t | 0.51 Σ 4.1M · 7.1 d/t +85 summ · ~375k tok | 0.75 Σ 3.6M · 5.9 d/t | 3-judge |
| gemma 31b @ 32k | 1.55 Σ 12.2M · 5.3 d/t | 1.49 Σ 14.5M · 7.8 d/t +86 summ · ~412k tok | 1.65 Σ 16.5M · 6.2 d/t | 2-judge (final) |
| gemma 31b @ 128k | 1.35 Σ 38.4M · 6.4 d/t | 1.48 Σ ≥39.6M† · 10.7 d/t +80 summ · ~394k tok | 1.15 Σ 65.6M · 6.8 d/t | 3-judge |
| kimi-k2.5 @ 128k | 1.13 Σ 46.6M · 12.3 d/t | 1.48 Σ 23.3M · 11.5 d/t +89 summ · ~546k tok | 0.08 Σ 58.1M · 11.4 d/t | 3-judge |
| gemini-flash @ 1M | 1.14 Σ 65.9M · 5.1 d/t | 1.34 Σ 41.0M · 11.9 d/t 0 summ (never fired) | 1.13 Σ 176.3M · 9.7 d/t | 3-judge |
† Lower bound: this arm's dump lost 74 of 1,006 dispatch records to a killed harness wedge mid-run; the sum covers the 932 recovered records. Everything else reconciles against its harness report within 1%.
Read it both ways, honestly:
- Thrift never collapses. Across every cell, thrift stays in-band with the other strategies — it is never the worst option by a wide margin, and at the tightest window (gemma e2b @ 8k) it is the outright best (0.82).
- Compact tops two cells outright — kimi-k2.5 @ 128k (1.48 vs. thrift's 1.13) and the 128k gemma-31b control (1.48 vs. thrift's 1.35). Both wins are on cells with real context pressure and a model that can make use of paid compression.
- Naive recency is the one that actually breaks. It nominally "wins" the 32k gemma-31b cell (1.65) and stays competitive at 1M (1.13, where the window barely fills), but on kimi-k2.5 @ 128k it COLLAPSES to 0.08 — a reasoning model trapped in scaffolding by bloated, unfiltered context. That collapse is the warning this whole domain exists to prevent: recency-only accumulation is not a safe default, it is a latent failure mode that only shows up once you push a reasoning model under real pressure.
Cost
The d/t and Σ columns in the table above are the effort story. On dispatches, thrift is the lightest arm on every cell except kimi-k2.5 @ 128k, where all three arms thrash roughly equally (11.4–12.3) — reasoning-model dispatch counts are model personality, not strategy. On total tokens:
- Naive posts the biggest bill on every cell from 32k up (16.5M / 65.6M / 58.1M / 176.3M) despite paying zero summarizer calls and running midfield on dispatches — re-sending everything every dispatch is the cost model. At gemini-flash @ 1M that's 176.3M vs. thrift's 65.9M — 2.7× — at statistically tied quality (1.13 vs. 1.14).
- Compact's Σ cuts both ways. On the small-model cells it costs more than thrift (its summary rides on top of recent verbatim turns, plus the summarizer calls themselves). On the reasoning/cloud cells the summary replaces enough long history to undercut everyone — 23.3M on kimi vs. thrift's 46.6M, and 41.0M at 1M where the compaction threshold never even fired (the low bill there is fewer/cheaper re-sends, not compression).
- Compact's overhead: ~80-89 summarizer calls and roughly 380k-550k extra tokens per 94-turn run, on every cell where the threshold actually fires. The one exception: at gemini-flash @ 1M, the older-region threshold never crosses across the whole run (0 summarizer calls) — with that much headroom, compact degenerates to naive unbounded accumulation. Caveat noted, not hidden: a 1M window is big enough that compact-as-compaction never earns its keep there.
- Provenance: sums come from the engine's own per-dispatch token counts in the run dumps, dedup-swept (stale appended rows from killed harness relaunches isolated by sequence-lane analysis) and reconciled against the per-turn harness reports; the cloud cell was additionally cross-checked against the serving gateway's independent per-request accounting. One arm (compact @ 31b/128k) lost 74 of 1,006 dispatch records to a mid-run harness wedge — its Σ is reported as a lower bound.
When to choose which
Choose thrift when: cost matters, you're on a constrained or edge deployment, or you simply want the always-safe default. It pays zero extra model calls, and the matrix shows it never collapses — band membership holds across every cell, tight window or huge one. This is the strategy to reach for first.
Choose compact when: the target model is a reasoning model AND your pipeline can afford a summarizer budget (extra latency + tokens per compaction) AND the window is small enough that the summarization threshold will actually fire. The one cell where it clearly wins (kimi-k2.5 @ 128k, 1.48 vs. 1.13) is exactly that profile.
Don't reach for naive recency. It exists in this domain as selectNaiveTurns — thrift's own honest comparison baseline, not a recommended policy — precisely so the kimi 0.08 collapse is on the record as a documented warning, not folklore. If you're accumulating chat history with plain FIFO truncation today, the matrix is the concrete argument for moving off it.
The two compose
Thrift and compact are not mutually exclusive. Thrift's subtractive pass carries one hook specifically so it can coexist with a summarizing strategy running upstream: isSummaryMessage (default predicate: id === '__compact-summary', matching compact's DEFAULT_SUMMARY_MESSAGE_ID) tells the subtractive pass "this message is a rolling summary, not an ordinary old turn — never shed it like one, even though it renders as the chronologically oldest message in the working set."
A pipeline that wants both runs compact FIRST (turn-level assembly: fold old turns into a summary once they cross the threshold) and thrift SECOND (final per-dispatch budget fit, over whatever compact handed it, including that summary message). Neither strategy needs to know the other exists beyond this one shared id convention — see thrift.md and compact.md for the exact option names.
Where to go next
- Token Thrift (The Loop) — the concept: why subtractive beats additive, the research behind it (hallucination, "lost in the middle", RECOMP/LLMLingua), and when NOT to thrift (coding agents want breadth, not minimalism).
- Punching Above Its Weights showcase — the live demo: a real Gemma-4 E2B running in a browser tab with a context window you drag, watching the subtractive pass shed content in real time.
- Token Thrift battery reference —
subtractToFit,selectRelevantTurns, the calibrated relevance floor. - Compact battery reference —
assembleCompactedTurns, theSummarizeFnseam, the canonicalDispatchRunnerrecipe.