Reference
September 20, 2026 · View on GitHub
jev_judge
Batch every question about one state into one call — latency is flat in question count (measured: 1/4/8 questions ≈ 274 ms median), and cost amortizes across the shared state.
| Param | Type | Notes |
|---|---|---|
state | string | everything Jev may consider — facts, tool output, file excerpts (≤ ~30k tokens) |
questions[] | array | each: {id?, type, question, options?, levels?, criteria?} |
questions[].type | noul | choice | score | noul = probability a statement is true; choice = pick one enumerated option; score = position on an ordered list of levels |
questions[].options | choice only | ≥ 2 labels, or a label → meaning map |
questions[].levels | score only | ≥ 2 ordered level descriptions |
questions[].criteria | noul only, optional | {true, false} meanings, to sharpen calibration |
confidence_threshold | number, default per confidence source — 0.5 reported, 0.4 estimated | verdicts below it escalate |
model | string, optional | backend model override |
// input
{
"state": "CI run #142: build ok, 214 tests passed, 0 failed; 1 test quarantined as flaky last week",
"questions": [
{ "id": "passed", "type": "noul", "question": "Did the run fully succeed?" },
{ "id": "next", "type": "choice", "question": "Next action?",
"options": { "merge": "everything green", "rerun": "looks flaky", "hold": "needs attention" } },
{ "id": "risk", "type": "score", "question": "How risky is merging now?",
"levels": ["routine", "worth a look", "incident"] }
]
}
// result (shape exact, values illustrative)
{
"verdicts": [
{ "id": "passed", "type": "noul", "answer": 0.97, "confidence": 0.94,
"confidenceFrom": "estimated", "escalate": false },
{ "id": "next", "type": "choice", "answer": "merge", "confidence": 0.34,
"confidenceFrom": "reported", "escalate": true,
"reason": "unsure", "distribution": { "merge": 0.55, "rerun": 0.41, "hold": 0.04 },
"hint": "Jev answered (merge) at reported confidence 0.34 < 0.5. Treat the answer as a prior, not a decision — reason it out yourself." },
{ "id": "risk", "type": "score", "answer": 0.8, "confidence": 0.81,
"confidenceFrom": "reported", "escalate": false,
"distribution": { "0": 0.35, "1": 0.5, "2": 0.15 },
"legend": { "0": "routine", "1": "worth a look", "2": "incident" } }
],
"escalated": true,
"backend": "typesafe",
"latencyMs": 187
}
A score answer is the distribution's expected position on your levels —
0.8 means "between routine and worth a look, closer to the latter";
legend maps indices back to your words.
jev_gate
One proposed action, one risk check.
| Param | Type |
|---|---|
state | string — current task context |
tool, input | the action, verbatim |
description | optional intent |
confidence_threshold | default per confidence source (0.5 reported / 0.4 estimated) |
Returns {decision: allow | deny | escalate, confidence, confidenceFrom, hint} — one
allow/deny choice under the hood, escalate when confidence falls below
the threshold. allow stays silent and falls through to your normal
permission flow — the gate can never grant anything, only deny or ask — and
if Jev is down it fails open. As a PreToolUse hook it spends zero LLM
tokens on the allow path (a deny/ask feeds its reason back to the model —
that is the point) and adds one ~100 ms round trip per gated call, so scope
the matcher to tools worth gating.
The verdict contract
Every verdict:
{id, type, answer, confidence, confidenceFrom, escalate, reason?, hint?, distribution?, legend?}.
reason/hint appear exactly when escalate is true; distribution/legend
whenever the provider returns them; confidenceFrom whenever the question
actually reached Jev.
| reason | when | meaning |
|---|---|---|
writing | pre-call | the step must produce new text/code — structurally the LLM's |
open_ended | pre-call | not expressible as noul/choice/score (nothing to enumerate) |
oversized | pre-call | the state exceeds ~30k tokens — shrink it or take the questions over |
unsure | post-call | answer too flat to act on; it stays in answer as a prior |
unreachable | on failure | Jev unreachable — proceed as if it didn't exist |
Pre-call reasons come from a deterministic router (no request spent); each handback is a normal verdict with a hint, never an exception.
What the confidence scalar is, and where it came from. Every verdict
says so itself, in confidenceFrom:
confidenceFrom | who produced it | escalates below |
|---|---|---|
reported | Jev's own confidence head, returned for choice and score answers | 0.5 |
estimated | jev-use, from the answer's own distribution: top-minus-runner-up for choice/score, 2·|p − 0.5| for noul | 0.4 |
noul answers carry no reported confidence from any provider, so they are
always estimated; one batch mixing check with pick/rate therefore comes
back part reported, part estimated, and each verdict is judged against its own
number. Through the Vercel gateway the head arrives out-of-band in
providerMetadata.typesafe.confidence, keyed by question id.
The two are the same scale read two ways, measured over 318 live
choice/score answers (bench/RESULTS.md): on a
two-option question they agree to the wire's 2-decimal rounding, and on
three or more the margin reads a median 0.05 (up to 0.17) lower, because
it also subtracts however the losing mass splits. Hence the lower bar for
the estimate. An explicit confidence_threshold covers every verdict
whatever its source, and always wins.
Library
import { Jev, check, pick, rate } from "jev-use";
const jev = new Jev(); // backend resolved from the environment
const { answers, verdicts } = await jev.judge(state, {
next: pick("Next action?", { merge: "all green", rerun: "looks flaky", hold: "needs attention" }),
risk: rate("How risky?", ["routine", "worth a look", "incident"]),
passed: check("Did the run fully succeed?"),
});
answers.next; // { answer: "merge", confidence: 0.93, confidenceFrom: "reported", escalate: false }
const verdict = await jev.gate(state, { tool: "Bash", input: { command } });
verdict.decision; // "allow" | "deny" | "escalate"
Both halves of the handoff are in the surface. Before writing a question,
route says whether the step is Jev-shaped at all — no client, no key, no
call:
import { route } from "jev-use";
route({ producesContent: true, enumerable: true }); // { to: "llm", reason: "writing" }
route({ producesContent: false, enumerable: false }); // { to: "llm", reason: "open_ended" }
route({ producesContent: false, enumerable: true }); // { to: "jev" }
After the call, every answer carries the other half — escalate, reason,
hint, and Jev's answer kept as a prior (the table above).
The three builders write the three primitives — check → noul, pick →
choice, rate → score — and the wire vocabulary stays exactly that.
answers is keyed by the names you asked under; verdicts is the same
verdicts in the order you asked them, alongside escalated, backend,
model, latencyMs and usage.
new Jev({ backend: "mock" }) judges with no key at all, and
new Jev({ backend: myBackend }) takes any JevBackend (tests, custom
transports). Defaults set on the client — confidenceThreshold, model —
are overridable per call: jev.judge(state, questions, { model }).
Configuration
| Setting | Default | Meaning |
|---|---|---|
JEV_BACKEND | auto-detect | typesafe | openrouter | vercel | mock |
TYPESAFE_API_KEY / OPENROUTER_API_KEY / AI_GATEWAY_API_KEY | — | provider credential; auto-detected in this order |
JEV_MODEL | provider default (jev-latest) | model override |
JEV_GATE_THRESHOLD | per confidence source (0.5 / 0.4) | hook-gate escalation threshold, for both sources at once |
Provider dialects: TypeSafe and OpenRouter share the native wire shape
(OpenRouter's decisions endpoint is alpha and may move), carrying
confidence on the answer itself; Vercel's gateway renames noul→boolean,
moves the model into a header, drops the legend echo, and relays the
confidence head in providerMetadata.typesafe.confidence instead — a map keyed
by question id, with boolean answers absent from it. All three are normalized
by the adapters, provenance included; wire shapes are
pinned by fixture tests against documented formats — jev-use doctor is the
live check.
CLI
jev-use install [claude|codex|pi] wire the server into your harness via its own CLI (all found, if no target)
jev-use serve stdio MCP server
jev-use hook gate PreToolUse hook adapter (Claude Code / Codex)
jev-use judge ['{...}'] one-shot JudgeRequest from argv or stdin
jev-use doctor backend resolution + one live round trip