{"_id":"fast-jev-compaction","_rev":"3-3d730d8617a14b439fae8f10e819215a","name":"fast-jev-compaction","dist-tags":{"latest":"0.4.1"},"versions":{"0.3.0":{"name":"fast-jev-compaction","version":"0.3.0","license":"MIT","_id":"fast-jev-compaction@0.3.0","maintainers":[{"name":"aleksvega","email":"veleks.vega@gmail.com"}],"bin":{"jev-qa":"cli/jev-qa.mjs"},"dist":{"shasum":"d07f5327aff1aba0125717ec167f6b1f01a6873e","tarball":"https://registry.npmjs.org/fast-jev-compaction/-/fast-jev-compaction-0.3.0.tgz","fileCount":32,"integrity":"sha512-SbzT3J4EjAe4uDR/Nk+UlSAfd4DpLQIsEVHRQNbEZQtd9L+LUtTMc1+rmhGfGs8lhu+sRb8iG6/x8m0EBgBwuQ==","signatures":[{"sig":"MEYCIQDE4MrWr8egpgMcid2P4/YLiDPkRis3nPErbXuq/X2dvAIhALilWi0fr8ynbfvjUX8AA0lsbQYx0KKG3b04rYrTBOcM","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":89519},"type":"module","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"d53e40c1e88296315975501560f05eaec29734a6","scripts":{"demo":"tsx examples/demo.ts","test":"vitest run","build":"tsc","typecheck":"tsc --noEmit && npm run typecheck:hooks","typecheck:hooks":"tsc -p tsconfig.hooks.json","validate:plugin":"claude plugin validate .claude-plugin/plugin.json"},"_npmUser":{"name":"aleksvega","email":"veleks.vega@gmail.com"},"_npmVersion":"11.6.1","description":"Continuous, verbatim context compaction for LLM agents using TypeSafe's Jev model.","directories":{},"_nodeVersion":"24.10.0","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","vitest":"^2.1.8","typescript":"^5.7.2","@types/node":"^22.10.2"},"_npmOperationalInternal":{"tmp":"tmp/fast-jev-compaction_0.3.0_1789985019273_0.12459876444525153","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"fast-jev-compaction","version":"0.4.0","license":"MIT","_id":"fast-jev-compaction@0.4.0","maintainers":[{"name":"aleksvega","email":"veleks.vega@gmail.com"}],"bin":{"jev-qa":"cli/jev-qa.mjs","jev-find":"cli/jev-find.mjs"},"dist":{"shasum":"fe601af139df8de207e7660f2dd88513c4addb14","tarball":"https://registry.npmjs.org/fast-jev-compaction/-/fast-jev-compaction-0.4.0.tgz","fileCount":33,"integrity":"sha512-meyPArvG4sdGZnfuer9+jV1/JKfQTEfFxLzbOsBThi+I2snUxDOmp6MHC2Idl1Ng5/Ufl6xxz5X7koY3ukGWHA==","signatures":[{"sig":"MEUCIQDfws3pefSYLTqvyEx8fqHOiqKRdjEsS++B/jl6rD4AKAIgbRqjh6WL/L3XbWJpWifbeQR2LkG2TvDNmKkuNQVCU7k=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIF7kjZrA5vFE97DHthV4pNRrI7nqEp/QGnksD6y7AifXAiEAoM7mcg5f7s6f7UZHdy+jdZNuc9OJImkroiMB3gFyUMA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":94681},"type":"module","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"47d34cdb5e277b8a966b468e4c199df5aabbc6e2","scripts":{"demo":"tsx examples/demo.ts","test":"vitest run","build":"tsc","typecheck":"tsc --noEmit && npm run typecheck:hooks","typecheck:hooks":"tsc -p tsconfig.hooks.json","validate:plugin":"claude plugin validate .claude-plugin/plugin.json"},"_npmUser":{"name":"aleksvega","email":"veleks.vega@gmail.com"},"_npmVersion":"11.6.1","description":"Continuous, verbatim context compaction for LLM agents using TypeSafe's Jev model.","directories":{},"_nodeVersion":"24.10.0","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","vitest":"^2.1.8","typescript":"^5.7.2","@types/node":"^22.10.2"},"_npmOperationalInternal":{"tmp":"tmp/fast-jev-compaction_0.4.0_1790000495963_0.4204638628157531","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"_id":"fast-jev-compaction@0.4.1","bin":{"jev-qa":"cli/jev-qa.mjs","jev-find":"cli/jev-find.mjs","jev-setup":"cli/jev-setup.mjs"},"dist":{"shasum":"b22c4317b0643345226c6cdbd093d53e67db0b7f","tarball":"https://registry.npmjs.org/fast-jev-compaction/-/fast-jev-compaction-0.4.1.tgz","fileCount":57,"integrity":"sha512-JQkOlfl9wDzvhaxIIkyGO8QxBpkOesvcsUR5fg0CTpNrQ+hxpcFlOTBa4TM74sUR8k7s0kmm49w35Mg3VECy/A==","signatures":[{"sig":"MEQCIAs6Bmq1GeTgCshSGa6CJB8vdS4SmJ9IarZ+/8CaJs+JAiBNga/g7Z44e5RtA6B0wxsGSxJvdTd9aEAL9xrzk0oirQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDy610Mn0R8hf4AUO/gI9SecrPnfToP4cdProfNxZN0WgIhALQcQ1XvhKDHqnPhpsyMkkCvRwLiX6gReY1U08s9EfB5"}],"unpackedSize":703828},"name":"fast-jev-compaction","type":"module","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"5b8d1d08f29c026db176180afc79cada8f242760","license":"MIT","scripts":{"demo":"tsx examples/demo.ts","test":"vitest run","build":"tsc","typecheck":"tsc --noEmit && npm run typecheck:hooks","typecheck:hooks":"tsc -p tsconfig.hooks.json","validate:plugin":"claude plugin validate .claude-plugin/plugin.json"},"version":"0.4.1","_npmUser":{"name":"aleksvega","email":"veleks.vega@gmail.com"},"_npmVersion":"11.6.1","description":"Continuous, verbatim context compaction for LLM agents using TypeSafe's Jev model.","directories":{},"maintainers":[{"name":"aleksvega","email":"veleks.vega@gmail.com"}],"_nodeVersion":"24.10.0","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","vitest":"^2.1.8","typescript":"^5.7.2","@types/node":"^22.10.2"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/fast-jev-compaction_0.4.1_1790225680647_0.1402406930622735"}}},"time":{"created":"2026-09-21T10:03:39.215Z","modified":"2026-09-24T04:54:40.898Z","0.3.0":"2026-09-21T10:03:39.407Z","0.4.0":"2026-09-21T14:21:36.069Z","0.4.1":"2026-09-24T04:54:40.731Z"},"license":"MIT","description":"Continuous, verbatim context compaction for LLM agents using TypeSafe's Jev model.","maintainers":[{"name":"aleksvega","email":"veleks.vega@gmail.com"}],"readme":"[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\r\n[![npm](https://img.shields.io/npm/v/jev-compact?label=jev-compact)](https://www.npmjs.com/package/jev-compact)\r\n[![Fork of tamaratran/fast-jev-compaction](https://img.shields.io/badge/fork%20of-tamaratran%2Ffast--jev--compaction-blue)](https://github.com/tamaratran/fast-jev-compaction)\r\n[![no TypeSafe key](https://img.shields.io/badge/OpenRouter-no%20TypeSafe%20key-purple)](#fork-additions-openrouter-only-setup)\r\n\r\n# fast-jev-compaction\r\n\r\nClaude Code plugin that replaces the compaction summary with Jev decisions:\r\nevery tool call and result is scored in one fast request, stale ones are\r\ndropped or truncated, everything kept stays verbatim. Also usable as an npm\r\nlibrary.\r\n\r\n## Tool suite (this repo + two companion packages)\r\n\r\n| CLI | Package | Purpose |\r\n|---|---|---|\r\n| `jev-compact` / `jev-gate` | fast-jev-compaction (here) | Verbatim context compaction / dangerous-command gate |\r\n| `jev-qa`, `jev-find` | fast-jev-compaction (here) | Fast repo QA scan / semantic file search |\r\n| `jev-enhance` | [jev-prompt-enhancer](https://github.com/aleksvega/jev-prompt-enhancer) | Prompt refinement for vague/voice-transcribed input |\r\n| `jev-skill-router` | [jev-skill-router](https://github.com/aleksvega/jev-skill-router) | Route a request to the right tool/skill |\r\n\r\n## Install for any agent (one command)\r\n\r\n```bash\r\nnpx jev-setup <claude|opencode|codex|hermes|generic>\r\n```\r\n\r\nInstalls all CLIs globally and wires the chosen agent: Claude Code (plugin\r\nmarketplace), OpenCode (session hook), Codex (AGENTS.md), Hermes (plugin), or\r\na generic AGENTS.md. Requires only `OPENROUTER_API_KEY`.\r\n\r\n**Agent-friendly:** give your agent this repo link — it reads\r\n[`SKILL.md`](./SKILL.md) and installs everything itself:\r\n> \"Install Jev tools from https://github.com/aleksvega/fast-jev-compaction\"\r\n\r\n## Fork additions: jev-compact + jev-gate (OpenRouter, no TypeSafe key)\r\n\r\nThis fork (github.com/aleksvega) adds two small CLI tools in `cli/` that run\r\nJev through **OpenRouter's `/api/alpha/decisions`** (model `typesafe/jev-1.13`),\r\nso only an `OPENROUTER_API_KEY` is required — no TypeSafe API key. The upstream\r\nlibrary and Claude Code plugin below are unchanged; to point them at\r\nOpenRouter instead, set `baseUrl` (library option) or `TYPESAFE_BASE_URL` +\r\n`TYPESAFE_MODEL=typesafe/jev-1.13`.\r\n\r\n**Setup**\r\n\r\n```bash\r\nnpm install && npm run build\r\nexport OPENROUTER_API_KEY=\"sk-or-...\"   # your key; never commit it\r\n```\r\n\r\n**jev-compact — verbatim session compaction**\r\n\r\n```bash\r\nnode cli/jev-compact.mjs transcript.json -o dump.md\r\n```\r\n\r\nInput: a JSON array of `{role, text, toolUses, toolResults}`. One batched Jev\r\nrequest decides, per tool call, whether the call and/or its result must stay\r\nverbatim; the rest is dropped. Typical run: 62→6 messages, 41K→4.6K chars,\r\n~$0.00002, output begins with a stats line.\r\n\r\n**jev-gate — confidence-gated pre-push guardrail**\r\n\r\n```bash\r\npython cli/jev-gate.py /path/to/repo              # exit 0 = ALLOW, 1 = BLOCK\r\npython cli/jev-gate.py /path/to/repo --threshold 0.9\r\n```\r\n\r\nOne batched call (3 Noul + 1 Score over the last commit's diff): hardcoded\r\nsecrets, syntax errors, breaking changes, test-failure risk. Blocks at\r\nprobability ≥ threshold (default 0.85) or score ≥ 3; an API failure also\r\nblocks (fail-safe). ~400 ms, ~$0.00005. Verified: clean diff → ALLOW;\r\ndiff containing an `sk-...` key → BLOCK at p=0.99.\r\n\r\nMIT, upstream credit: [tamaratran/fast-jev-compaction](https://github.com/tamaratran/fast-jev-compaction).\r\n\r\n## What and why\r\n\r\nMost context compaction asks an LLM to summarize old turns. A summary is\r\nlossy: a file path, exact error, constraint, or command can disappear even when\r\nit matters later. This library never rewrites anything. It only deletes tool\r\ncalls and tool results Jev says are no longer needed, and it asks Jev while\r\nshowing it the whole conversation. User and assistant text stays verbatim and\r\nin order.\r\n\r\nThe repository is both an npm package (`src/`) and a Claude Code plugin\r\n(`hooks/`, `.claude-plugin/`) that uses the package to replace Claude Code's\r\nbuilt-in compaction summary with the original messages.\r\n\r\n## How it works\r\n\r\n1. Every `tool_use` is paired with its `tool_result` by `tool_use_id`. Calls in\r\n   the first message or in the newest `preserveRecentMessages` messages are\r\n   pinned and never touched.\r\n2. The **state** sent to Jev is the whole conversation so far, oldest first,\r\n   with every tool result replaced by a short note (`ok, 4213 chars (omitted)`).\r\n   Tool inputs are included, texts are included, nothing is summarized.\r\n3. The state is fitted into `maxStateTokens` (25k by default) in stages, each\r\n   applied only if the previous one was not enough: tool inputs truncated to\r\n   1000, then 200, then 60 characters; long texts abridged to head + tail,\r\n   oldest non-pinned messages first; old non-pinned messages collapsed to a\r\n   `[… N chars omitted …]` note; old tool calls reduced to one line each\r\n   (`t12 Read file_path=src/a.ts → ok 480ch`); old call-less messages left\r\n   out; runs of old call-only messages folded into one entry. If it still\r\n   does not fit, compaction throws. Tokens are estimated without a tokenizer (a\r\n   word per six letters, half a token per digit, ~one per other symbol),\r\n   calibrated to land a little above the counts Jev reports.\r\n4. For every non-pinned call Jev gets two `noul` questions: should the **call**\r\n   stay (knowing it was made, with its input, still matters), and should the\r\n   **result** stay verbatim (its contents are still needed and re-running the\r\n   tool would not do).\r\n5. Questions are split into as many requests as needed so state plus questions\r\n   stays under `maxRequestTokens` (30k by default, under Jev's 32k request\r\n   limit). The same full state is resent with every request; requests run\r\n   concurrently and their answers are merged.\r\n6. Decisions per call, against `keepThreshold`:\r\n   - `keepResult ≥ threshold` → keep call and result;\r\n   - else `keepCall ≥ threshold` → keep the call, truncate the result to its\r\n     first `truncateHeadChars` characters plus a one-line note;\r\n   - else → remove the call together with its result.\r\n7. The message list is rebuilt: a message that loses all its content is\r\n   removed, untouched messages are returned as the same objects, and no result\r\n   is ever left without its call.\r\n\r\nJev failures, malformed answers, a missing key, or a history that cannot be\r\nfitted throw; the caller (or the Claude Code hook) decides what to fall back to.\r\n\r\n\r\n### hermes-compact - OpenAI-chat transcripts (Hermes-compatible)\r\n\r\n    npm install -g jev-compact\r\n    OPENROUTER_API_KEY=... hermes-compact transcript.json -o compacted.json\r\n\r\nInput: JSON array / {\"messages\":[...]} / JSONL of OpenAI-chat messages\r\n(string or array content, nested or flat tool_calls). Output: compacted\r\ntranscript JSON with verbatim kept messages and stats. Adapter semantics\r\nported from [deadczarvc/hermes-jev-compaction](https://github.com/deadczarvc/hermes-jev-compaction) (MIT) - thanks! Difference: this build needs no\r\nTypeSafe key (OpenRouter backend).\r\n\r\n## Install and usage (upstream — TypeSafe endpoint)\r\n\r\nThe upstream library targets the official TypeSafe API. **This fork does not need\r\na TypeSafe key**: use the OpenRouter setup in the \"Fork additions\" section above\r\n(`OPENROUTER_API_KEY` only, model `typesafe/jev-1.13:latest` via\r\n`https://openrouter.ai/api/alpha/decisions`).\r\n\r\n```bash\r\nnpm install fast-jev-compaction\r\nexport TYPESAFE_API_KEY=...   # only needed for the upstream/official endpoint\r\n```\r\n\r\n```ts\r\nimport { compactMessages, reductionRatio, type Message } from 'fast-jev-compaction';\r\n\r\nconst transcript: Message[] = [\r\n  { role: 'user', text: 'Fix the failing test. Never edit src/generated.', toolUses: [] },\r\n  {\r\n    role: 'assistant',\r\n    text: '',\r\n    toolUses: [{ tool_use_id: 'toolu_1', tool: 'Read', input: { file_path: 'src/a.ts' } }],\r\n  },\r\n  { role: 'user', text: '', toolUses: [], toolResults: [{ tool_use_id: 'toolu_1', text: '…file…' }] },\r\n  // …\r\n];\r\n\r\nconst result = await compactMessages(transcript, { preserveRecentMessages: 4 });\r\nconsole.log(result.messages, result.decisions, result.stats);\r\nif (reductionRatio(result) < 0.25) {\r\n  // not worth it: keep the original transcript, or summarize instead\r\n}\r\n```\r\n\r\n`Message` is a subset of Claude Code's `SessionMessage`, so a session transcript\r\ncan be passed in as is.\r\n\r\nTo bring your own transport, implement `JevAsker` (one `ask(state, questions)`\r\nmethod) and call `compact(messages, asker, options)`; `buildJevRequest` and\r\n`parseJevResponse` give you the HTTP request body and response validation.\r\nThe building blocks (`collectToolCalls`, `fitState`, `batchCalls`,\r\n`decideCall`, `applyDecisions`) are exported too.\r\n\r\n`apiKey` defaults to `process.env.TYPESAFE_API_KEY`. Never commit the key or\r\nput it in a source file.\r\n\r\n## Options\r\n\r\n| Option | Default | Description |\r\n| --- | --- | --- |\r\n| `apiKey` | `TYPESAFE_API_KEY` | TypeSafe API key (`compactMessages`/`JevClient`) |\r\n| `model` | `jev-latest` | Jev model name |\r\n| `baseUrl` | `https://api.typesafe.ai/v1/systemone` | System One endpoint |\r\n| `fetch` | native `fetch` | Injectable fetch implementation for tests |\r\n| `goal` | last 3 user prompts | Ongoing task description included in the state |\r\n| `keepThreshold` | `0.5` | Minimum keep probability for a call or result to stay |\r\n| `preserveRecentMessages` | `6` | Newest messages never touched (the first is always kept) |\r\n| `maxStateTokens` | `25000` | Estimated token ceiling for the state |\r\n| `maxRequestTokens` | `30000` | Estimated ceiling for state plus one batch of questions |\r\n| `truncateHeadChars` | `300` | Characters of a dropped tool result retained before its note |\r\n\r\n`result.stats` reports message and character counts before and after, the\r\nper-reason decision counts, the state size in estimated tokens, which fitting\r\nstage was needed, and the number of requests.\r\n\r\n## Limitations\r\n\r\n- Only tool calls and results are candidates; text messages are never removed\r\n  or shortened in the output (they are only abridged in the state Jev sees).\r\n- Token sizes are estimates from character counts, not a tokenizer.\r\n- Calibration is at the request level; a probability is not a proof that a\r\n  result is safe to delete. The assistant can always re-run the tool.\r\n- The full state is repeated with every request, so a history near the state\r\n  ceiling costs one request per handful of questions.\r\n\r\n## Claude Code plugin\r\n\r\nThe repository root is a Claude Code function-hook plugin: `hooks/fast-jev.ts`\r\nis a thin adapter that feeds `session.compact` transcripts through `src/` and\r\nfalls back to Claude Code's built-in summary on errors or insufficient\r\nreduction. See [`hooks/README.md`](hooks/README.md) for configuration and the\r\nClaude Code 2.1.274 type reference.\r\n\r\n### Install in Claude Code\r\n\r\nFunction hooks are an early-access Claude Code feature (2.1.274+), so the\r\nopt-in flag must be set wherever Claude Code runs, e.g. in `~/.claude/settings.json`:\r\n\r\n```json\r\n{ \"env\": { \"CLAUDE_CODE_ENABLE_FUNCTION_HOOKS\": \"1\", \"TYPESAFE_API_KEY\": \"<your key>\" } }\r\n```\r\n\r\nThen add this repository as a plugin marketplace and install the plugin,\r\neither from the shell or as slash commands inside a session:\r\n\r\n```sh\r\nclaude plugin marketplace add aleksvega/fast-jev-compaction\r\nclaude plugin install fast-jev-compaction@fast-jev-compaction\r\n```\r\n\r\nThe install prompts for the plugin options (API key, thresholds, `truncateHeadChars`,\r\n…); leave them at their defaults to use `TYPESAFE_API_KEY` from the environment.\r\nRestart Claude Code or run `/reload-plugins`. From then on `/compact` (and\r\nauto-compaction) goes through Jev: the toast reads\r\n`fast-jev-compaction: kept N/M messages, no summary (…)` when the pruned history\r\nreplaced the built-in summary, or `fallback to built-in summary (…)` when Jev\r\ncould not remove enough (short sessions, or when it fails).\r\n\r\nTo run from a checkout without installing: `CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude --plugin-dir .`\r\nfrom the repository root. No publishing step is required; the marketplace is\r\njust the repo's `.claude-plugin/marketplace.json`.\r\n\r\n## Development\r\n\r\n```sh\r\nnpm install\r\nnpm run typecheck        # library + hook\r\nnpm test\r\nnpm run build\r\nnpm run validate:plugin  # claude plugin validate\r\nTYPESAFE_API_KEY=\"$(cat ~/.typesafe_key)\" npm run demo\r\n```\r\n\r\nThe unit tests use a fake Jev and never contact TypeSafe. The demo is the live\r\nnetwork check.\r\n\r\n## Animated demo (macOS)\r\n\r\n`demo/JevDemo` is a small native SwiftUI app that plays a scripted, dramatized\r\nversion of the compaction flow inside a Claude Code-style terminal: the tool\r\ncalls of a canned transcript are scored, results and calls Jev lets go turn red\r\nand collapse away, and the rest stays verbatim. It never calls the API; it\r\nexists to be screen recorded.\r\n\r\n```sh\r\ndemo/JevDemo/build.sh   # builds demo/JevDemo/build/JevDemo.app and launches it\r\n```\r\n\r\nPress space in the app to replay from the start.\r\n\r\n\r\n## jev-qa — fast repo QA scan\r\n\r\nFinds code errors in seconds: stage 1 — free syntax checks (py_compile / node --check), stage 2 — Jev semantic review per file (bugs, error-handling, security, logic — all questions in one batched request, parallel).\r\n\r\n```bash\r\nOPENROUTER_API_KEY=... jev-qa <repo> [--diff] [--out report.md] [--max N]\r\n```\r\n\r\nMeasured on our test set: 15 files in 5.4 s, ~$0.001; a file with a planted bug scored has_bug 0.95 / logic 0.90 vs 0.2–0.6 for clean files.\r\n\r\n### jev-find — natural-language file search\r\n\r\nFind code by meaning, not by name: a Jev walker ensemble walks the repo and reports where walkers landed.\r\n\r\n```\r\njev-find \"where is authentication handled?\" ./src --walkers 20\r\n```\r\n\r\nCheap (~$0.0005/search) and fast (~1-2 s on small repos). Pattern credit: ellipsis-dev/blink.\r\n","readmeFilename":"README.md"}