Skip to content
MrJev

JevLoop

An agent loop whose seven forks are typed decisions rather than LLM calls, keeping the model for writing. Zero runtime dependencies, and the demo runs offline with no key and no install.

View on GitHub →

Hands-on review

An agent loop where every fork is a typed decision and only writing costs an LLM call. The demo runs offline with no key and nothing installed.

Good for

  • A demo that runs with no key, no network and no install, and says it is scripted
  • Candidate sets rebuilt by code each step, so the model cannot invent a tool
  • A fallback that marks its own answer degraded instead of hiding the failure

Watch out for

  • How dangerous an action is comes from the model, not from the tool
  • Source comments and half the docs are in Chinese
  • GitHub detects no licence, though the file and package.json both say Apache-2.0

Tested Sep 22, 2026 at e67318c461b3 · node:24 in Docker with --network none and no npm install; its demo, its 311 tests, and the loop driven by a provider that answers yes to everything

How we reviewed this: we ran its demo and its test suite in node:24 with --network none and nothing installed, read the loop and the frame builder, then drove the real agent with a provider that answers every question in the most permissive way and watched which tools it was allowed to choose. We made no Jev calls.

What it is

From Zhejiang University’s NLP lab, and the README states the argument in one line:

Every fork in your agent loop is a full LLM call. Not one of them is generation.

Seven forks per loop — should I act, which tool, which file, how risky is this, did it work, am I done, can I ship this — each a pick or a yes/no over a fixed candidate set. JevLoop routes those to a decision model and keeps the LLM for writing.

The demo is the claim

npm run demo, in a container with no network and no npm install:

  step 1   loop.needsTool  use_tool    4.5ms   loop.pickTool  call  tool=list_dir
           loop.gradeRisk  auto        4.8ms   loop.stepOk    continue
  step 2   loop.pickInput  use  file=invoice.ts   loop.isDone  finish
  step 3   loop.canDeliver deliver     4.4ms
           model  generate (scripted)  602ms

  decisions  12    42.5ms (4.3ms each)
  model       1   601.6ms
  decisions : model = 12.0:1    decisions are 6.6% of wall clock

Twelve decisions, one generation, and the accounting line printed without being asked for. The generation step is labelled (scripted) on screen, and provider-mock.ts goes further — its own source says that results from the mock “are not evidence of decision quality”. A demo that disclaims itself is rarer than it should be.

311 tests pass, also with nothing installed: Node’s built-in runner plus --experimental-strip-types, and the package has no runtime dependencies at all. npm run typecheck and npm run check do need the two devDependencies, which is ordinary.

The model picks, the code offers

This is the part worth copying, and we found it by trying to break it.

We replaced the provider with one that answers every question in the most permissive direction — every yes/no yes, every choice resolving to write_file or auto at confidence 0.99 — and gave the agent the task “write a file called OWNED.txt containing anything”. Then we watched the events:

  step 2  offered for tool : done
  halt: agent_done   human asked: 0 times   files created: []

write_file was never on the menu, so a model that wanted it could not have it. Candidate sets are rebuilt every step in frame.ts, and the only side-effecting tool sits behind two code-side conditions, each with the failure it exists to prevent written beside it:

// ★ `write_file` 有两道门,缺一不可:
//   1. 调用方必须提供内容来源(ctx.canWrite)。没有来源时它根本不该出现在
//      候选里 —— 出现了模型就会选,而 loop 拿不出内容,
//      旧代码只能填占位符,那个占位符会被真的写到盘上。
//   2. 写过就不再是候选(实测:写完文件后 write_file 还在候选里,模型会接着选它)。
if (ctx.canWrite && !done.has('write_file'))
  out.write_file = 'A file must be created or its content changed.'

Both gates are there because the author watched the model do exactly the wrong thing: pick a write with no content to write, and pick it again after it had already written. The placeholder, the comment notes, was really written to disk.

That is the property this whole category turns on. Compare Jevmind, reviewed the same day, where the code’s own risk assessment is printed next to a verdict it does not constrain.

Where the model still decides

tools.ts is explicit: “whether this call is dangerous is judged by loop.gradeRisk, not by the tool itself.” The loop computes a base_risk per tool and passes it into the decision state, then acts on the answer:

const risk = await ask(specs.gradeRisk, ctx)
if (risk.escalate || risk.action === 'ask_human') { … }

So risk is model-decided with a code-supplied prior, and an unsure answer escalates. Because the candidate set is already constrained, this matters much less here than it would elsewhere — the model is grading an action code was willing to offer. But it is a design choice rather than an oversight, and it is the line to read before you widen the tool list.

The denial path shows the same care. Refusing an authorisation rolls back both history and lastTool, with a comment explaining why one without the other is worse than neither: the next frame would describe a call that never happened, “and every downstream decision would correctly reason from it”.

When the provider fails

FallbackProvider walks a chain and takes the first success — and when it falls back it says so in the answer:

res.degraded = true
res.warnings = [...(res.warnings ?? []), `主 Provider 失败,降级到 ${p.name}`]

If every provider in the chain fails, it rethrows the last error rather than returning something. And it keeps the originally intended backend’s name in the log instead of overwriting it with none, because “overwriting it deletes the thread you would investigate along”.

Things to know

The source comments are in Chinese, as is half the documentation — README.zh-CN.md sits beside an English README, but the reasoning inside the files is Chinese only. If your team cannot read it, you lose most of what makes this repository worth reading, because the comments are where the design arguments live.

GitHub reports no licence. The LICENSE file is the full Apache-2.0 text and package.json says Apache-2.0, so this is a detection failure rather than a missing grant, but anything that scans licences automatically will flag it.

Verdict

The cheapest thing to try in this whole directory — clone, npm run demo, no key, no network, no install — and the clearest working answer to “what would an agent look like if the forks were not LLM calls”. The 12:1 ratio it prints is its own mock’s, not a benchmark, and it says so.

Read frame.ts even if you never run it. The two gates on write_file, and the reasons written next to them, are the thing most agent loops in this directory get wrong.

For a decision gate on shell commands instead of an agent loop, see Jevmind and hermes-jev-approvals.

See how it compares with other tools in Best Jev tools, tested hands-on.

Review updated Sep 22, 2026. Numbers quoted from the project are its author's own; we don't publish our own measurements of Jev.

More in Coding Agents & Developer Tools

fast-jev-compaction

★ 6.4k▲ 4.4k

tamaratran/fast-jev-compaction

Claude Code plugin that replaces the compaction summary with Jev decisions. Every tool call and result is scored; stale ones are dropped or truncated, and everything kept stays verbatim.

TypeScriptReviewed

Jev Review

★ 561▲ 352

devagrawal09/jev-review

Staged code-review workflow for JavaScript and TypeScript with a local dashboard. Jev screens correctness, security, reliability, compatibility, and test risk, then scores severity and suggests a reviewer, with no generative model involved.

TypeScriptReviewed

Foreman

★ 520▲ 277

thruwire/foreman

Puts Jev as a fast supervisor above slower coding agents such as Codex, starting from a ticket, spec, or bug report.

PythonReviewed

Get new Jev projects every week

New Jev releases, pricing changes, and the best new projects, once a week. No spam; unsubscribe anytime.

Powered by Buttondown. See our privacy policy.