MIT · Pi extension · TypeSafe System One

Ask once.
Branch on a value.

jev_decide is a tool for Pi agents that turns a judgement call into a typed result. Ask TypeSafe's Jev System One a question and you get back a choice, a score or a noul — a number your code can compare. Jev never returns prose. The call runs through Pi's builtin classifier API with your TYPESAFE_API_KEY, so its usage lands in your session cost. If Jev is unavailable, rate-limited, out of credit or too slow, the same question is handed silently to your session model and the result is marked fallback.

$ pi install npm:pi-jev-extension
choice score noul

Why

Typed values.
Not paragraphs to re-parse.

A decision you cannot act on is just more text. Everything here is aimed at making the result something your code — and your model — can use immediately.

Three typed answers

choice picks a label, score places something on your own 2–10 level scale, noul returns a calibrated probability. Same shape every time.

Silent fallback

Jev down, rate-limited, out of credit, too slow or unreachable? The identical question goes to your session model and the result carries source: "fallback".

Bounded, then a breaker

A 30 s deadline on every request (configurable), plus a circuit breaker so a struggling endpoint is not retried on every turn — one probe per cooldown window.

Answers are validated

Every answer is checked against the question that was asked — a missing answer, an unknown choice, a non-numeric score or a noul outside 0..1 fails loudly instead of reaching your model.

Self-describing results

Each result ships a legend explaining its own fields, so the format travels with the data instead of relying on the model remembering it.

Bad input never ships

Arguments are normalized and re-validated before a request exists — malformed payloads fail locally with a message that says what was actually received.

How it works

One call, one value.

01

Ask a typed question

Pass the context as state and 1–8 questions keyed by a short identifier. The questions are a nested JSON object — never a string.

$ jev_decide questions: flaky: { type: noul, instructions: "Is this a real bug?" }
02

Jev returns a value

Not prose — a labelled field per question, with the distribution behind the answer so you can apply your own threshold.

answers.flaky = { type: "noul", noul: 0.88 } P(yes) = 0.88 → escalate if < 0.7
03

Branch on it

Compare against a threshold in code, or hand the typed result back to the model as the basis for its next action.

$ if [ "$(jq -r .answers.flaky.noul result.json)" \ -lt "07" ]; then escalate; fi

Reading the answer

Every field, spelled out.

The result carries a legend restating this at the moment of use — this table is the reference version.

fieldmeaning
answers[k].typemirrors the question type: choice, score or noul
answers[k].noulP(yes) in 0..1 — there is deliberately no confidence field; the probability is the certainty
answers[k].choicethe selected label, with probabilities giving the full distribution over your criteria keys
answers[k].scoreposition on your own scale and may be fractional (1.57) — its legend (always present, rebuilt from your criteria) maps level index to your label
answers[k].confidence0..1 for choice and score; absent for noul
sourcejev or fallback — treat fallback answers as less calibrated second opinions
legendtop-level: this documentation note, not part of any answer

Two things that trip people up

A noul answer has no confidence field — by design. The probability is the confidence, so asking for a second one is asking the same number twice.

A score is not an integer. It is a position on the rubric you supplied, so 1.57 means "a little past medium" — read it against the answer's own legend rather than rounding it yourself.

When Jev cannot answer

It falls back. You keep working.

Every failure mode has a defined outcome — a stall or an outage never becomes a dead agent turn.

conditionwhat happens
provider errorauth, invalid request, quota or upstream failure — fallback, and the circuit opens so the endpoint is not hammered; malformed questions are still rejected before the call
timeoutfallback after JEVMODEL_TIMEOUT_MS (default 30 s), circuit opens
no credentialsfallback with a stated preflight reason — the session model answers
bad questionsrejected before any request is sent
bad answerserror — a malformed answer never reaches your model silently

Privacy: what leaves your machine

state is sent verbatim to the endpoint behind your classifier credential — TypeSafe's System One with TYPESAFE_API_KEY, or whichever Jev-capable provider you configured in Pi. Do not pass material your organisation forbids sending off-box. With no classifier credential, no request is made at all: the session model answers.

Install

Three commands.

Pi loads TypeScript directly — there is no build step.

From npm

published package, listed in the Pi gallery

$ pi install npm:pi-jev-extension

Pi package

from GitHub

$ pi install git:github.com/rioliu/pi-jev-extension

Local checkout

for development

$ pi install ./pi-jev-extension
environmentpurpose
TYPESAFE_API_KEYrequired — Pi's TypeSafe credential (or another Jev-capable provider); without any classifier credential every call falls back to the session model
JEVMODEL_TIMEOUT_MSmax wait before handing off, default 30000
JEVMODEL_FALLBACK_MODELpin the fallback to provider/modelId, otherwise the session model answers

Model selection: the first credentialed classifier in Pi's registry, preferring typesafe/jev-latest; override with the tool's model argument as provider/id.

Unofficial

An independent integration. Not affiliated with, endorsed by, or supported by TypeSafe or the Jev project. Licensed MIT.

Source, tests and the full README live at github.com/rioliu/pi-jev-extension.