fast-jev-compaction Deletes Your Context Instead of Summarizing It, and That Is the Safer Failure
A Claude Code plugin that scores every tool call, drops the stale ones, and refuses to rewrite a single word of what survives
Every long agent session ends the same way. The context fills, compaction fires, an LLM reads the transcript and writes a summary, and the session continues on a fluent paragraph that reads perfectly and has silently dropped the one thing that mattered. The exact error string. The file path with the unusual spelling. The constraint you stated once in message three and never repeated.
A repository that reached third place on Trendshift this week takes the opposite position, and the position is sharper than it first looks. tamaratran/fast-jev-compaction never rewrites anything. It reads your transcript, asks a fast decision model two yes-or-no questions about every tool call, and then deletes. Whatever survives survives verbatim, in order, untouched. The bet is that losing a tool result outright is a better failure than paraphrasing it, and once you say that out loud it is hard to argue with.
Why deletion beats paraphrase
The asymmetry is the whole idea, and it is worth stating plainly because most compaction implementations do not act like they believe it.
If compaction deletes the result of a Read on src/auth.ts, the agent has lost something recoverable. It can read the file again. The cost is one tool call and some tokens. If compaction summarizes that result into "reviewed the auth module, which handles token validation," the agent has lost something unrecoverable, and worse, it does not know it has lost anything. It now holds a confident sentence in place of the code, and confident sentences do not trigger re-reads. The agent will reason from the summary and produce something plausible and wrong.
Deletion produces a gap the agent can notice and fill. Summarization produces a substitute the agent will trust. That is the argument, and fast-jev-compaction's README makes it directly: a summary is lossy, and "a file path, exact error, constraint, or command can disappear even when it matters later."
The mechanism
The implementation is more careful than the pitch suggests, and the careful parts are where the engineering is.
Every tool_use is paired with its tool_result by tool_use_id. Calls in the first message, and calls in the newest preserveRecentMessages messages (six by default), are pinned and never touched. So the task framing at the top and the immediate working context at the bottom are both out of scope by construction.
What goes to the decision model is the whole conversation, oldest first, with every tool result replaced by a short note like ok, 4213 chars (omitted). Tool inputs stay. Text stays. Nothing is summarized on the way in either. The model is being asked to judge relevance while looking at the shape of the entire session rather than a window of it.
That state has to fit inside maxStateTokens, 25,000 by default, and the fitting is staged: truncate tool inputs to 1000 characters, then 200, then 60; abridge long texts to head plus tail, oldest non-pinned first; collapse old non-pinned messages to a [… N chars omitted …] note; reduce old tool calls to one line each, like t12 Read file_path=src/a.ts → ok 480ch; drop old call-less messages; fold runs of old call-only messages into one entry. Each stage applies only if the previous one was not enough. If it still does not fit, compaction throws rather than guessing.
Then the questions. For each non-pinned call, two of them: should the call stay, meaning the fact that it was made, with its input, still matters, and should the result stay verbatim, meaning its contents are still needed and re-running the tool would not do. Both are Noul questions in TypeSafe's Jev API, which returns a probability rather than a token. Questions are batched so state plus questions stays under maxRequestTokens, 30,000 by default, which the README says sits under Jev's 32,000-token request limit. Batches run concurrently and their answers merge.
The decision ladder against keepThreshold, 0.5 by default, has three rungs rather than two. If the result scores at or above threshold, call and result both stay whole. Otherwise, if the call scores at or above threshold, the call stays and the result truncates to its first truncateHeadChars characters (300 by default) plus a one-line note. Otherwise both go.
That middle rung is the clever bit. There is a real category of tool call where knowing it happened matters and the output does not: you need to know the agent already ran the test suite, you do not need 4,000 lines of passing test output. Most compaction schemes cannot express that distinction because they only have summarize-or-keep. Two independent questions per call gives it for free.
Rebuilding the message list is where the safety invariants live. A message that loses all its content is removed. Untouched messages are returned as the same objects. And no result is ever left without its call, which is the failure that produces the unexpected tool_use_id errors anyone who has hand-edited a transcript has met.
Put this into practice
The lowest-friction path is to not install it first.
Run it as a library on a transcript you already have. npm install fast-jev-compaction, set TYPESAFE_API_KEY, and call compactMessages(transcript, { preserveRecentMessages: 4 }). The Message type is a subset of Claude Code's own SessionMessage, so a saved session transcript goes in as-is. What comes back includes result.decisions and result.stats, and the stats are the reason to do it this way: message and character counts before and after, per-reason decision counts, the state size in estimated tokens, which fitting stage was needed, and how many requests it took. You get to read what it chose to throw away on a session whose outcome you already know, which is the only honest way to evaluate a deletion policy.
Use the escape hatch it gives you. The README suggests if (reductionRatio(result) < 0.25) you keep the original transcript or summarize instead. Take that seriously. On a session that is mostly conversation and thinking rather than tool output, there is nothing for this to delete, and you will have paid for a Jev call to learn that.
Then install the plugin, and start the threshold high. Function hooks are opt-in, so set CLAUDE_CODE_ENABLE_FUNCTION_HOOKS to 1 in ~/.claude/settings.json alongside your key, then claude plugin marketplace add tamaratran/fast-jev-compaction and claude plugin install fast-jev-compaction@fast-jev-compaction. The install prompts for thresholds. A keepThreshold above the 0.5 default makes it more conservative, keeping more and deleting less, which is the direction you want while you are still learning whether you trust it. You will see one of two toasts: kept N/M messages, no summary when the pruned history replaced the built-in summary, or fallback to built-in summary when Jev could not remove enough or failed outright.
Watch the fallback rate more than the reduction rate. The hook falls back to Claude Code's built-in summary on errors or insufficient reduction, which is the right default and also means a broken configuration looks like normal operation. If you never see the no-summary toast, something is wrong and nothing will tell you.
The honest limitations
This is a young repository, and its README is more forthcoming about its own ceiling than most, which I take as a good sign rather than a bad one. Several of these come straight from it.
Token counting is an estimate, not a tokenizer. The library computes sizes with a heuristic the README describes as "a word per six letters, half a token per digit, ~one per other symbol," calibrated to land a little above the counts Jev reports. That is a reasonable engineering call for a budget check, and it means your 25,000-token state ceiling is approximate. A transcript full of base64, minified JSON, or non-Latin text is exactly where a character heuristic drifts, and those are common tool outputs.
A probability is not a proof. The README says this itself: "a probability is not a proof that a result is safe to delete." Calibration is at the request level, which means the aggregate behavior can be well-calibrated while any individual deletion is wrong. The mitigation is real (the agent can re-run the tool) but it is not free, and on a tool call that is expensive or not idempotent it is not really a mitigation at all.
Only tool calls are candidates. Text messages are never removed or shortened in the output. If your context pressure comes from long assistant reasoning rather than large tool results, this does nothing for you.
The state is resent with every request. Because each batch of questions carries the full state, a history near the ceiling costs one full-state request per handful of questions. The economics stay good only because Jev's input pricing is $0.042 per million tokens with output free. If that pricing is introductory, this design gets expensive faster than a summarizer would.
It depends on an undocumented surface. The plugin hooks session.compact through what its README calls an early-access Claude Code function-hook feature requiring 2.1.274 or later. Claude Code's public hooks documentation covers PreCompact, whose only decision control is to block compaction, and does not mention function hooks or the CLAUDE_CODE_ENABLE_FUNCTION_HOOKS flag at all as of this writing. So the capability this plugin is built on is real enough to have a version number in a README and is not yet a documented, stable API. Early-access surfaces change.
And the license line is unfilled. The LICENSE is MIT with the copyright reading "Copyright (c) 2025" and no name after it. That is almost certainly a template that never got edited rather than anything sinister, and it is still the kind of detail worth noticing before a dependency goes into something you ship. There are no tagged releases either, so npm install and the marketplace both hand you whatever is on main.
I have not run this against a long session of my own, so everything above is a reading of the source and the README rather than a measurement. Nobody has published a comparison of task outcomes under keep-or-drop compaction versus summarization, which is the number that would actually settle the argument.
What to take from it
The reason this repository is worth your attention even if you never install it is that it makes a design argument most harnesses have never explicitly made. Compaction is a lossy compression problem, and there are two families of answer: throw away whole items and keep the rest exact, or keep everything approximately. Nearly every implementation shipped so far picked the second without appearing to notice it was a choice.
The first family has a property the second does not, and it is the property you want when the system is going to be wrong sometimes: its failures are visible and repairable from inside the loop. An agent that lost a file can read the file. An agent that was handed a paraphrase of the file has no signal that anything happened.
Go point it at one finished transcript and read the decisions. You will learn more about what your sessions are actually made of in ten minutes than the reduction percentage will ever tell you, and you will find out whether you agree with what it deleted while it still costs nothing to disagree.
Sources: tamaratran/fast-jev-compaction · TypeSafe AI, "Introducing System One Models & Jev" · Claude Code hooks documentation · LangChain, "Building a Harness with Jev"
Medium metadata
Title: fast-jev-compaction Deletes Your Context Instead of Summarizing It, and That Is the Safer Failure Subtitle: A Claude Code plugin that scores every tool call, drops the stale ones, and refuses to rewrite a single word of what survives Tags: Claude Code, AI Agents, Developer Tools, Context Engineering, Open Source Canonical: fervorai.dev