Skip to main content

Context compression (winnow)

Agents burn tokens on fat tool output — JSON dumps, logs, file reads, search results, conversation history. Vanta compresses that text before it reaches the model with winnow, a local-first compression engine with zero runtime dependencies. It cuts tokens by 40–95% while keeping the signal, is content-aware, reversible (originals are recoverable on demand), and runs entirely on your machine — no proxy, no API key, no egress.

winnow is its own MIT-licensed project; Vanta consumes it as a library and wires it into the agent loop.

It runs automatically

You don't invoke it. Every tool result passes through compressOutput in the agent loop, and conversation history is compacted the same way during long sessions.

  • On by default — set VANTA_COMPRESS=0 to disable all tool-output compression.
  • Only shrinks output that's large enough (above a size threshold) and the right shape. Small or genuinely unique text passes through untouched — winnow hands it back rather than mangling it.
  • Never lossy on precision reads. The compressors that elide content are an allow-list, not a deny-list, so a raw grep/read_file/diff is never silently corrupted.

How a result is routed:

Tool outputWhat happens
read_file (TS/JS)AST skeleton — every signature/type/import kept, bodies elided (recoverable)
read_file (large JSON)lossless TOON table view (every row kept); exact bytes stashed for retrieval
grep / searchlossless densify — collapses noise, preserves every file:line byte
object-array tool outputlossless TOON table (keys once, one record per row)
allow-listed toolscontent router — JSON crush, log squash, repeated-block dedupe
everything elsereturned unchanged

Real-headroom guard

Conversation compaction is judged by the next real provider input-token count, not only by how much message text appeared to shrink. This matters when the system prompt and tool schemas form a large fixed floor that message rewriting cannot reduce.

After a changed automatic compaction pass, Vanta waits for the provider's input usage (or its exact preflight token count):

  • Below the active trigger restores headroom and resets the episode.
  • At or above the trigger records an ineffective strike.
  • Two ineffective passes suppress further automatic compaction for that conversation.
  • A triggered pass that changes nothing counts as ineffective immediately.
  • Missing or zero usage falls back to the existing estimated-savings guard; it does not claim that headroom was restored.

Start a fresh session to reset the episode, or use /compact <focus> for an explicit manual compaction. Automatic suppression does not block that manual command.

Lossy inline, lossless on demand

The core contract: compression shrinks the inline view, but the full original is stashed locally under a content id. When the model needs detail it calls retrieve_original and gets the exact bytes back. A 200-row array becomes a head + tail sample inline — and row 137 is one retrieve away.

winnow is built on three ideas:

  1. Content-aware, lossy-but-reversible. Different compressors for JSON, logs, code, and binary; every original recoverable.
  2. Delivery is backbone-gated. Strong models get a short preview + a retrievable pointer; small/distilled models get a larger inline window and aren't handed a pointer they won't follow.
  3. Cache-aligned. A volatile segment (a clock, "current" state) early in a prompt invalidates the provider's KV cache every turn — winnow keeps the stable prefix leading so the cache survives.

Tuning

All of these are opt-in except the master switch:

Env varDefaultEffect
VANTA_COMPRESSonmaster switch; 0 disables all tool-output compression
VANTA_TOON_READFILEonTOON view for large JSON file reads; 0 disables
VANTA_TOON_DICToffcolumnar (dictionary) TOON — bigger lossless savings on low-cardinality tables, at some readability cost
VANTA_PRUNE_CONTEXToffLLMLingua-style score-and-drop prune on oversized inline context
VANTA_WINNOW_LOGPROBoffreal logprob/surprisal scoring for prune (needs a logprob endpoint)
VANTA_PRUNE_PROVIDERprovider for the prune scorer
VANTA_SKILL_DISTILLEDoffserve distilled (worked-example) skill bodies
VANTA_SKILL_SUBSEToffserve a task-relevant subset of skills

Use winnow standalone

winnow works outside Vanta — as a library, a CLI, or an MCP server. It's distributed from GitHub (not the npm registry — the winnow name there is an unrelated package).

npx github:jpoindexter/winnow bench # try the fidelity benchmark, no install
npm install github:jpoindexter/winnow # add it to a project
import { compress, retrieve, stats } from "winnow";

const r = await compress(hugeJsonOrLog); // { text, compressed, originalId, ... }
console.log(stats(hugeJsonOrLog, r.text)); // { tokensBefore, tokensAfter, tokensSaved, ratio }
const original = await retrieve(r.originalId!); // exact bytes, on demand

The benchmark measures the honest number — what survives compression inline, without a retrieval round-trip:

winnow fidelity — 7 cases
json-head json save 86% inline ✓
json-tail json save 86% inline ✓
json-middle json save 86% inline · (recoverable)
wide-table json save 97% inline · (recoverable)
log-error logs save 99% inline ✓
log-dupes logs save 99% inline ✓
text-prose text save 0% inline ✓

avg savings: 79% inline needle survival: 71%
recoverable fidelity: 100% (every elided original is retrievable from the store)

Repo: github.com/jpoindexter/winnow