opencode-fast-jev-compaction
v0.1.0
Published
OpenCode plugin: Jev scores every tool call and result, stale ones are dropped or truncated, everything kept stays verbatim.
Maintainers
Readme
opencode-fast-jev-compaction
OpenCode plugin that replaces lossy compaction summaries with Jev decisions: every tool call and result is scored in one fast request, stale ones are dropped or truncated, everything kept stays verbatim.
OpenCode port of tamaratran/fast-jev-compaction. The compaction engine (vendor/fast-jev-compaction) and the Jev scoring design are by @tamaratran. This repository is the OpenCode adapter.
What and why
OpenCode's built-in compaction asks the session model to write a checkpoint summary. A summary is lossy: a file path, exact error, constraint, or command can disappear even when it matters later. This plugin never rewrites user or assistant text. It only deletes or truncates tool calls and tool results Jev says are no longer needed.
Claude Code can replace the compact result with the pruned transcript. OpenCode cannot: experimental.session.compacting only customizes the summary prompt. So this port does two things:
experimental.chat.messages.transform— when the live session is overcompactAtPercentof the context budget, prune tool parts in place. This is the real analog of the Claude plugin. Auto-compaction often never fires because the request stays under the ceiling.experimental.session.compacting— if OpenCode still summarizes (manual compact or overflow), run the same prune and inject a keep/drop list into the summary context. If Jev cannot save enough, fall through to the built-in summary.
Install
Needs a TypeSafe API key. Jev does not generate text; it returns probabilities.
export TYPESAFE_API_KEY=...Never put the key in source or commit it. Plugin option apiKey works; the environment variable is the right default.
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-fast-jev-compaction"]
}From a local checkout, before the package is on the registry:
{
"plugin": ["file:///absolute/path/to/opencode-fast-jev-compaction/src/index.ts"]
}Or with options:
{
"plugin": [
["opencode-fast-jev-compaction", {
"keepThreshold": 0.5,
"preserveRecentMessages": 6,
"minReductionRatio": 0.25
}]
]
}Restart OpenCode. A toast reads kept verbatim, no summary (…) when Jev pruned the live transcript, or fallback to OpenCode summary (…) when it could not.
How it works
OpenCode stores a tool call and its result on the same tool part (callID + state.output). The adapter maps that onto the library's { toolUses, toolResults } messages, then:
- Pair every completed/error tool by
callID. Calls in the first message or the newestpreserveRecentMessagesmessages are pinned. - Send Jev the whole conversation as state (tool outputs replaced by a one-line note), fitted into
maxStateTokens. - For every non-pinned call, two
noulquestions: should the call stay, should the result stay verbatim. - Against
keepThreshold: keep both; or keep the call and truncate the result totruncateHeadChars; or remove the tool part. User/assistant text is never edited. Messages are not deleted, so OpenCode parent IDs stay valid. - If the character reduction is below
minReductionRatio, nothing is applied and OpenCode's summarizer remains the fallback.
Jev failures, a missing key, a history that cannot be fitted, or FAST_JEV_OFFLINE=1 all fall through.
Configuration
Plugin options win over environment variables.
| Option / env | Default | Description |
| --- | ---: | --- |
| apiKey / TYPESAFE_API_KEY | — | TypeSafe API key |
| model / FAST_JEV_MODEL | jev-latest | Jev model |
| FAST_JEV_BASE_URL | https://api.typesafe.ai/v1/systemone | System One endpoint |
| keepThreshold / FAST_JEV_KEEP_THRESHOLD | 0.5 | Minimum keep probability |
| preserveRecentMessages / FAST_JEV_PRESERVE_RECENT | 6 | Newest messages never touched (first is always kept) |
| compactAtPercent / FAST_JEV_COMPACT_AT_PERCENT | 60 | Transform prune when estimated tokens reach this % of contextTokens |
| contextTokens / FAST_JEV_CONTEXT_TOKENS | 128000 | Budget used only for that percent trigger |
| minReductionRatio / FAST_JEV_MIN_REDUCTION | 0.25 | Below this, do not apply |
| maxStateTokens / FAST_JEV_MAX_STATE_TOKENS | 25000 | Jev state ceiling |
| maxRequestTokens / FAST_JEV_MAX_REQUEST_TOKENS | 30000 | State + one batch of questions |
| truncateHeadChars / FAST_JEV_TRUNCATE_HEAD_CHARS | 300 | Head kept on a dropped result |
| goal / FAST_JEV_GOAL | last 3 user prompts | Task description in the Jev state |
| FAST_JEV_OFFLINE | unset | Set to 1 to disable the plugin |
Limitations
- OpenCode cannot substitute the compacted history the way Claude Code function hooks can. The transform hook is the verbatim path; the compacting hook is insurance when a summary still runs.
- Only tool parts are candidates. Text, reasoning, and attachments are never removed.
- Token sizes are estimates, not tokenizer counts.
- A probability is not proof that a result is safe to drop. The assistant can re-run the tool.
- Pending/running tools are ignored until they complete.
Development
npm install
npm test
npm run typecheckUnit tests use a fake Jev and never contact TypeSafe.
The engine under vendor/fast-jev-compaction is an unmodified snapshot of the upstream library (revision e3f262a). Prefer updating that snapshot over editing it.
Publish
Create an npm account at npmjs.com/signup, confirm the email, and enable 2FA (npm requires 2FA or a granular token to publish). Then:
npm login
npm publish --access publicAfter that, OpenCode loads "plugin": ["opencode-fast-jev-compaction"].
Credits
- Compaction engine and Jev question design: tamaratran/fast-jev-compaction (MIT).
- OpenCode session mapping, transform/compacting hooks, and this package: this repository (MIT).
