cd /news/ai-agents/jev-give-any-coding-agent-claude-cod… · home topics ai-agents article
[ARTICLE · art-136284] src=gist.github.com ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

jev: give any coding agent (Claude Code, Codex, OpenCode) a calibrated gut check. A zero-dependency CLI + a global skill that wraps TypeSafe's Jev decision model, so an agent can classify, filter,…

A developer released jev, a zero-dependency Python CLI and library that wraps TypeSafe's Jev decision model so coding agents like Claude Code, Codex, and OpenCode can classify, filter, rank, or verify hundreds of items in a single ~250 ms call without loading them into context. The tool returns calibrated probabilities for yes/no, choice, and rubric-score questions at $0.042 per million input tokens, and ships with usage and cost tracking, a one-line installer, and documentation of a measured pitfall the vendor docs omit.

by read94 min views1 publishedSep 19, 2026

| | #!/usr/bin/env python3 | | | """jev: calibrated, typed decisions from TypeSafe's Jev model, for agents. | | | | | | Jev is a "System One" model. It does not generate text. You hand it a STATE | | | (text or JSON) and typed QUESTIONS, and it returns typed answers with | | | calibrated probabilities in ~250 ms for $0.042 per million input tokens. | | | | | | noul yes/no -> probability that the answer is yes (0..1) | | | choice pick one -> chosen option + probability per option + confidence | | | score rate on rubric-> position on your ordered levels + confidence | | | | | | Progressive disclosure: | | | jev short help (this) | | | jev guide the agent playbook: when to use it, how to ask, how to read | | | jev guide <topic> depth on one topic (jev guide --list) | | | jev examples copy-paste examples for the common jobs | | | jev docs live index of the vendor docs | | | | | | Zero dependencies. Importable as a library: | | | sys.path.insert(0, "/path/to/dir/containing/jev.py") | | | from jev import JevClient, noul, choice, score | | | | | | Auth: --api-key, else $TYPESAFE_API_KEY, else ~/.config/typesafe/api_key (0600). | | | """ | | | | | | from future import annotations | | | | | | import argparse | | | import concurrent.futures as cf | | | import json | | | import os | | | import random | | | import sys | | | import time | | | import urllib.error | | | import urllib.request | | | from datetime import datetime, timedelta | | | from pathlib import Path | | | | | | VERSION = "1.0.0" |

|  | API_BASE = os.environ.get("TYPESAFE_BASE_URL", "https://api.typesafe.ai").rstrip("/") | 
|  | DEFAULT_MODEL = os.environ.get("TYPESAFE_DEFAULT_MODEL", "jev-latest") | 
|  | CONFIG_DIR = Path.home() / ".config" / "typesafe" | 

| | KEY_FILE = CONFIG_DIR / "api_key" | | | USAGE_FILE = CONFIG_DIR / "usage.jsonl" | | | CONFIG_FILE = CONFIG_DIR / "config.json" | | | PRICE_PER_MTOK_IN = 0.042 # USD per million input tokens (jev-1.13 list price); output tokens are free | | | RATE_LIMIT_RPM = 1200 # vendor-published, adjusts without notice | | | RATE_LIMIT_TPS = 250_000 | | | DOCS = "https://docs.typesafe.ai" | | | MAX_CHOICE_OPTIONS = 255 | | | MAX_SCORE_LEVELS = 10 | | | STATE_CHAR_BUDGET = 120_000 # ~30k tokens; the state+longest-question budget is 32k tokens | | | | | | # --------------------------------------------------------------------------- errors | | | | | | | | | class JevError(Exception): | | | exit_code = 4 # API error after retries | | | | | | | | | class UsageError(JevError): | | | exit_code = 2 | | | | | | | | | class AuthError(JevError): | | | exit_code = 3 | | | | | | | | | class NetworkError(JevError): | | | exit_code = 5 | | | | | | | | | # --------------------------------------------------------------------------- auth | | | | | | | | | def resolve_key(explicit: str | None = None) -> tuple[str, str]: | | | """Return (key, source). Order: explicit, $TYPESAFE_API_KEY, key file.""" | | | if explicit: |

|  | return explicit.strip(), "--api-key" | 
|  | env = os.environ.get("TYPESAFE_API_KEY", "").strip() | 

| | if env: | | | return env, "$TYPESAFE_API_KEY" |

|  | if KEY_FILE.exists(): | 
|  | key = KEY_FILE.read_text().strip() | 

| | if key: | | | return key, str(KEY_FILE) | | | raise AuthError( | | | "no API key. Run jev auth set <key> (stored 0600 in " | | | f"{KEY_FILE}) or export TYPESAFE_API_KEY. Keys: https://console.typesafe.ai/keys" | | | ) | | | | | | |

|  | def mask(key: str) -> str: | 
|  | return key[:10] + "…" + key[-4:] if len(key) > 16 else "…" | 

| | | | | | | | # --------------------------------------------------------------------------- question builders | | | | | | | | | def noul(instructions, true=None, false=None) -> dict: | | | """Yes/no question. Returns P(yes). Optional criteria say what yes and no mean.""" | | | q = {"type": "noul", "instructions": instructions} | | | if true is not None or false is not None: | | | crit = {} | | | if true is not None: | | | crit["true"] = true | | | if false is not None: |

|  | crit["false"] = false | 
|  | q["criteria"] = crit | 

| | return q | | | | | | | | | def choice(instructions, options) -> dict: | | | """Pick one option. options is a dict {name: description|None} or a list of names.""" |

|  | if isinstance(options, (list, tuple)): | 
|  | options = {str(o): None for o in options} | 
|  | return {"type": "choice", "instructions": instructions, "criteria": dict(options)} | 

| | | | | | | | def score(instructions, levels) -> dict: | | | """Rate on an ordered rubric. levels is an ordered list, low to high, 2..10 entries.""" | | | return {"type": "score", "instructions": instructions, "criteria": list(levels)} | | | | | | | | | def validate_questions(questions: dict) -> None: | | | if not isinstance(questions, dict) or not questions: |

|  | raise UsageError("questions must be a non-empty map of id -> question") | 
|  | for qid, q in questions.items(): | 
|  | if not isinstance(q, dict) or "type" not in q: | 
|  | raise UsageError(f"question {qid!r}: must be an object with a `type`") | 
|  | t = q["type"] | 
|  | if t not in ("noul", "choice", "score"): | 
|  | raise UsageError(f"question {qid!r}: type must be noul, choice or score, not {t!r}") | 

| | if "instructions" not in q: |

|  | raise UsageError(f"question {qid!r}: missing `instructions`") | 
|  | crit = q.get("criteria") | 
|  | if t == "choice": | 
|  | if not isinstance(crit, dict) or len(crit) < 2: | 
|  | raise UsageError(f"choice {qid!r}: criteria must map at least 2 options to descriptions (null allowed)") | 
|  | if len(crit) > MAX_CHOICE_OPTIONS: | 
|  | raise UsageError(f"choice {qid!r}: at most {MAX_CHOICE_OPTIONS} options") | 
|  | elif t == "score": | 
|  | if not isinstance(crit, list) or not 2 <= len(crit) <= MAX_SCORE_LEVELS: | 
|  | raise UsageError(f"score {qid!r}: criteria must be an ordered list of 2..{MAX_SCORE_LEVELS} level descriptions") | 
|  | elif t == "noul" and crit is not None: | 
|  | if not isinstance(crit, dict) or not set(crit) <= {"true", "false"}: | 
|  | raise UsageError(f"noul {qid!r}: criteria may only have `true` and `false` keys") | 

| | | | | | | | # --------------------------------------------------------------------------- client | | | | | | | | | class JevClient: | | | """Thin HTTP client with retries. ask() returns the raw API response dict.""" | | | | | | def init(self, api_key=None, model=None, timeout=30.0, retries=5, base_url=None, record=True, label="lib"): | | | self.api_key, self.key_source = resolve_key(api_key) | | | self.model = model or DEFAULT_MODEL | | | self.timeout = timeout | | | self.retries = retries | | | self.base_url = (base_url or API_BASE).rstrip("/") | | | self.record = record | | | self.label = label | | | self.last_ms = 0.0 | | | self.last_request_id = "" | | | self.last_attempts = 0 | | | |

|  | def _request(self, method: str, path: str, body=None) -> dict: | 
|  | data = json.dumps(body).encode() if body is not None else None | 
|  | headers = { | 
|  | "Authorization": f"Bearer {self.api_key}", | 

| | "Content-Type": "application/json", | | | "User-Agent": f"jev-cli/{VERSION}", | | | } | | | attempt = 0 | | | while True: | | | attempt += 1 |

|  | req = urllib.request.Request(self.base_url + path, data=data, method=method, headers=headers) | 
|  | t0 = time.time() | 

| | try: |

|  | with urllib.request.urlopen(req, timeout=self.timeout) as r: | 
|  | self.last_ms = (time.time() - t0) * 1000 | 
|  | self.last_request_id = r.headers.get("x-typesafe-request-id", "") | 

| | self.last_attempts = attempt | | | return json.loads(r.read().decode()) | | | except urllib.error.HTTPError as e: |

|  | self.last_ms = (time.time() - t0) * 1000 | 
|  | text = e.read().decode(errors="replace") | 
|  | if e.code in (401, 403): | 
|  | raise AuthError(f"HTTP {e.code}: API key rejected ({self.key_source}). {text[:300]}") | 
|  | if e.code in (429, 529) or e.code >= 500: | 
|  | if attempt <= self.retries: | 
|  | ra = e.headers.get("retry-after") | 
|  | delay = float(ra) if ra and ra.replace(".", "", 1).isdigit() else min(8.0, 0.5 * 2 ** (attempt - 1)) | 
|  | time.sleep(delay + random.uniform(0, 0.25)) | 

| | continue |

|  | raise JevError(f"HTTP {e.code} after {attempt} attempts: {text[:300]}") | 
|  | raise JevError(f"HTTP {e.code}: {text[:600]}") | 

| | except (urllib.error.URLError, TimeoutError, OSError) as e: |

|  | if attempt <= self.retries: | 
|  | time.sleep(min(8.0, 0.5 * 2 ** (attempt - 1))) | 

| | continue | | | raise NetworkError(f"network error after {attempt} attempts: {e}") | | | | | | def models(self) -> dict: | | | return self._request("GET", "/v1/models") | | | | | | def ask(self, state, questions: dict, model: str | None = None) -> dict: | | | """POST /v1/systemone. state is str|dict|list. Returns the raw response.""" | | | if state is None or state == "" or state == {} or state == []: | | | raise UsageError("state is empty") |

|  | validate_questions(questions) | 
|  | body = {"state": state, "model": model or self.model, "questions": questions} | 

| | try: | | | resp = self._request("POST", "/v1/systemone", body) | | | except JevError as e: | | | if self.record: | | | record_usage(None, self, len(questions), err=e) | | | raise | | | if self.record: | | | record_usage(resp, self, len(questions)) | | | return resp | | | | | | | | | def load_config() -> dict: | | | try: |

|  | return json.loads(CONFIG_FILE.read_text()) if CONFIG_FILE.exists() else {} | 
|  | except (OSError, json.JSONDecodeError): | 
|  | return {} | 

| | | | | |

|  | def save_config(cfg: dict) -> None: | 
|  | CONFIG_DIR.mkdir(parents=True, exist_ok=True) | 
|  | CONFIG_FILE.write_text(json.dumps(cfg, indent=2) + "\n") | 

| | | | | |

|  | def price_per_mtok() -> float: | 
|  | v = load_config().get("price_per_mtok_in") | 

| | return float(v) if v else PRICE_PER_MTOK_IN | | | | | | | | | def record_usage(resp: dict | None, client: "JevClient", n_questions: int, err: Exception | None = None) -> None: | | | """Append one row per request (success or failure) to the local ledger.""" | | | try: |

|  | CONFIG_DIR.mkdir(parents=True, exist_ok=True) | 
|  | row = { | 
|  | "ts": datetime.now().isoformat(timespec="seconds"), | 

| | "cmd": client.label, | | | "agent": os.environ.get("JEV_AGENT") or os.environ.get("AGENT_ID") or "", | | | "q": n_questions, | | | "ms": round(client.last_ms), | | | } | | | if resp is not None: |

|  | u = resp.get("usage") or {} | 
|  | row.update({"model": resp.get("model"), "in": u.get("input_tokens", 0), "out": u.get("output_tokens", 0)}) | 

| | if client.last_request_id: |

|  | row["rid"] = client.last_request_id | 
|  | if client.last_attempts > 1: | 
|  | row["attempts"] = client.last_attempts | 

| | else: |

|  | msg = str(err) | 
|  | code = msg.split(":", 1)[0].replace("HTTP ", "").strip() if msg.startswith("HTTP") else type(err).__name__ | 
|  | row.update({"in": 0, "out": 0, "err": code}) | 
|  | with USAGE_FILE.open("a") as f: | 
|  | f.write(json.dumps(row) + "\n") | 

| | except OSError: | | | pass | | | | | | | | | def cost_usd(input_tokens: int) -> float: | | | return input_tokens / 1_000_000 * price_per_mtok() | | | | | | | | | # --------------------------------------------------------------------------- input parsing | | | | | | | | | def parse_value(v: str): | | | """@path reads a file (.json parsed). JSON-looking text is parsed. Else string.""" |

|  | if v.startswith("@"): | 
|  | p = Path(v[1:]).expanduser() | 
|  | if not p.exists(): | 
|  | raise UsageError(f"file not found: {p}") | 
|  | txt = p.read_text() | 
|  | if p.suffix.lower() in (".json", ".jsonl"): | 

| | try: | | | return json.loads(txt) if p.suffix.lower() == ".json" else [json.loads(line) for line in txt.splitlines() if line.strip()] | | | except json.JSONDecodeError as e: | | | raise UsageError(f"{p}: invalid JSON: {e}") | | | return txt |

|  | s = v.strip() | 
|  | if s and s[0] in "{[" or s in ("true", "false", "null"): | 

| | try: | | | return json.loads(s) | | | except json.JSONDecodeError: | | | pass | | | return v | | | | | | |

|  | def build_state(args) -> object: | 
|  | srcs = [n for n in ("state", "state_file", "state_json") if getattr(args, n, None)] | 
|  | if len(srcs) > 1: | 
|  | raise UsageError("give the state one way: --state, --state-file, or --state-json (--field may be added to any of them, and becomes `text`)") | 

| | state = None |

|  | if getattr(args, "state", None): | 
|  | state = sys.stdin.read() if args.state == "-" else (parse_value(args.state) if args.state.startswith("@") else args.state) | 
|  | elif getattr(args, "state_file", None): | 
|  | state = parse_value("@" + args.state_file) | 
|  | elif getattr(args, "state_json", None): | 

| | try: | | | state = json.loads(args.state_json) | | | except json.JSONDecodeError as e: |

|  | raise UsageError(f"--state-json: invalid JSON: {e}") | 
|  | if getattr(args, "field", None): | 
|  | obj = {} | 

| | if state is not None: | | | obj["text"] = state | | | for kv in args.field: |

|  | if "=" not in kv: | 
|  | raise UsageError(f"--field expects key=value, got {kv!r}") | 
|  | k, v = kv.split("=", 1) | 
|  | obj[k.strip()] = parse_value(v) | 

| | state = obj | | | if state is None: | | | state = implicit_stdin() | | | if not state: |

|  | raise UsageError("no state. Use --state TEXT, --state-file PATH, --state-json JSON, --field k=v, or pipe text on stdin") | 
|  | n = len(state) if isinstance(state, str) else len(json.dumps(state)) | 
|  | if n > STATE_CHAR_BUDGET: | 

| | eprint(f"jev: warning: state is {n:,} chars (~{n // 4:,} tokens); the per-request budget is ~32k tokens for state + longest question. Filter first, or use jev batch/ jev rank which chunk.") | | | return state | | | | | | | | | def implicit_stdin(timeout: float = 1.5) -> str | None: | | | """Read stdin only when it is not a terminal AND has data ready. A harness that leaves | | | stdin open with no writer would otherwise block forever; --state - forces a blocking read.""" | | | if sys.stdin is None or sys.stdin.isatty(): | | | return None | | | try: | | | import select | | | ready, _, _ = select.select([sys.stdin], [], [], timeout) | | | if not ready: | | | return None | | | except (OSError, ValueError): | | | pass | | | return sys.stdin.read() | | | | | | |

|  | def parse_kv_options(items: list[str]) -> dict: | 
|  | """['billing=Payments', 'sales'] -> {'billing': 'Payments', 'sales': None}""" | 
|  | out = {} | 

| | for it in items: |

|  | if "=" in it: | 
|  | k, v = it.split("=", 1) | 
|  | out[k.strip()] = parse_value(v) if v != "" else None | 

| | else: | | | out[it.strip()] = None | | | return out | | | | | | |

|  | class QAction(argparse.Action): | 
|  | """Collects --noul/--choice/--score in declaration order.""" | 

| | |

|  | def __call__(self, parser, ns, values, option_string=None): | 
|  | lst = getattr(ns, "qlist", None) or [] | 
|  | lst.append((self.dest, list(values))) | 

| | ns.qlist = lst | | | | | | |

|  | def questions_from_args(args) -> dict: | 
|  | qs: dict = {} | 
|  | if getattr(args, "questions_file", None): | 
|  | v = parse_value("@" + args.questions_file) | 
|  | if not isinstance(v, dict): | 
|  | raise UsageError("--questions-file must contain a JSON object of id -> question") | 
|  | qs.update(v) | 
|  | if getattr(args, "questions_json", None): | 

| | try: | | | qs.update(json.loads(args.questions_json)) | | | except json.JSONDecodeError as e: |

|  | raise UsageError(f"--questions-json: invalid JSON: {e}") | 
|  | for kind, vals in getattr(args, "qlist", None) or []: | 
|  | if len(vals) < 2: | 
|  | raise UsageError(f"--{kind} needs ID INSTRUCTIONS [...]") | 
|  | qid, instr, rest = vals[0], parse_value(vals[1]), vals[2:] | 
|  | if kind == "noul": | 
|  | kv = parse_kv_options(rest) | 
|  | extra = set(kv) - {"true", "false"} | 

| | if extra: |

|  | raise UsageError(f"--noul {qid}: after the instructions only true=... and false=... are allowed, got {sorted(extra)}") | 
|  | qs[qid] = noul(instr, kv.get("true"), kv.get("false")) | 
|  | elif kind == "choice": | 
|  | if len(rest) < 2: | 
|  | raise UsageError(f"--choice {qid}: give at least 2 options as NAME or NAME=description") | 
|  | qs[qid] = choice(instr, parse_kv_options(rest)) | 
|  | elif kind == "score": | 
|  | if len(rest) < 2: | 
|  | raise UsageError(f"--score {qid}: give 2..{MAX_SCORE_LEVELS} level descriptions, low to high") | 
|  | qs[qid] = score(instr, [parse_value(r) for r in rest]) | 

| | if not qs: | | | raise UsageError("no questions. Use --noul/--choice/--score, --questions-file, or --questions-json (see jev ask --help)") | | | return qs | | | | | | | | | # --------------------------------------------------------------------------- rendering | | | | | | |

|  | def eprint(*a, **k): | 
|  | print(*a, file=sys.stderr, **k) | 

| | | | | |

|  | def describe_level(desc) -> str: | 
|  | if isinstance(desc, dict): | 
|  | for k in ("what", "level", "description", "name"): | 

| | if k in desc: |

|  | return str(desc[k]) | 
|  | return json.dumps(desc)[:80] | 
|  | return str(desc) | 

| | | | | |

|  | def render_answer(qid: str, a: dict) -> str: | 
|  | t = a.get("type") | 
|  | if t == "noul": | 
|  | p = a["noul"] | 
|  | return f"{qid:<24} noul    {p:.2f}   {'yes' if p >= 0.5 else 'no'}" | 
|  | if t == "choice": | 
|  | probs = a.get("probabilities", {}) | 
|  | best = a["choice"] | 
|  | others = ", ".join(f"{k} {v:.2f}" for k, v in sorted(probs.items(), key=lambda kv: -kv[1]) if k != best) | 
|  | return f"{qid:<24} choice  {best}   p={probs.get(best, 0):.2f} conf={a.get('confidence', 0):.2f}   [{others}]" | 
|  | if t == "score": | 
|  | legend = a.get("legend", {}) | 
|  | top = max(len(legend) - 1, 1) | 
|  | s = a["score"] | 
|  | nearest = describe_level(legend.get(str(int(round(s))), "")) | 
|  | return f"{qid:<24} score   {s:.2f}/{top}   conf={a.get('confidence', 0):.2f}   ≈ {nearest}" | 
|  | return f"{qid:<24} {t}     {json.dumps(a)}" | 

| | | | | |

|  | def render_response(resp: dict, ms: float, verbose: bool) -> str: | 
|  | lines = [render_answer(qid, a) for qid, a in resp.get("answers", {}).items()] | 
|  | u = resp.get("usage", {}) | 
|  | lines.append(f"· {resp.get('model')} · {u.get('input_tokens', 0)} in / {u.get('output_tokens', 0)} out tokens · {ms:.0f} ms · ${cost_usd(u.get('input_tokens', 0)):.6f}") | 
|  | return "\n".join(lines) | 

| | | | | | | | def emit(args, resp: dict, ms: float, extra: dict | None = None) -> None: | | | if args.json: | | | out = dict(resp) | | | if extra: |

|  | out.update(extra) | 
|  | print(json.dumps(out, indent=None if args.compact else 2)) | 

| | else: | | | print(render_response(resp, ms, args.verbose)) | | | | | | | | | # --------------------------------------------------------------------------- commands | | | | | | |

|  | def cmd_ask(args) -> int: | 
|  | state = build_state(args) | 
|  | qs = questions_from_args(args) | 
|  | c = JevClient(args.api_key, args.model, timeout=args.timeout, label="ask") | 
|  | resp = c.ask(state, qs) | 
|  | emit(args, resp, c.last_ms) | 

| | return 0 | | | | | | |

|  | def cmd_yes(args) -> int: | 
|  | state = build_state(args) | 
|  | q = noul(parse_value(args.instructions), args.true, args.false) | 
|  | c = JevClient(args.api_key, args.model, timeout=args.timeout, label="yes") | 
|  | resp = c.ask(state, {"q": q}) | 
|  | p = resp["answers"]["q"]["noul"] | 
|  | lo, hi = (args.band if args.band else (args.threshold, args.threshold)) | 
|  | if p >= hi: | 

| | verdict, code = "yes", 0 | | | elif p < lo: | | | verdict, code = "no", 1 | | | else: | | | verdict, code = "uncertain", 1 | | | if args.json: | | | print(json.dumps({"noul": p, "verdict": verdict, "threshold": args.threshold, "band": args.band, "model": resp["model"], "usage": resp["usage"]})) | | | else: | | | print(f"{p:.3f} {verdict}") | | | return code | | | | | | |

|  | def cmd_pick(args) -> int: | 
|  | state = build_state(args) | 
|  | opts = parse_kv_options(args.options) | 
|  | if len(opts) < 2: | 
|  | raise UsageError("pick needs at least 2 options (NAME or NAME=description)") | 
|  | c = JevClient(args.api_key, args.model, timeout=args.timeout, label="pick") | 
|  | resp = c.ask(state, {"q": choice(parse_value(args.instructions), opts)}) | 
|  | a = resp["answers"]["q"] | 
|  | conf = a.get("confidence", 0.0) | 
|  | ok = conf >= args.min_confidence | 

| | if args.json: | | | print(json.dumps({"choice": a["choice"], "confidence": conf, "probabilities": a["probabilities"], "verdict": "ok" if ok else "uncertain", "min_confidence": args.min_confidence, "model": resp["model"], "usage": resp["usage"]})) | | | else: | | | ranked = ", ".join(f"{k} {v:.2f}" for k, v in sorted(a["probabilities"].items(), key=lambda kv: -kv[1])) | | | if ok: | | | print(f"{a['choice']} conf={conf:.2f} [{ranked}]") | | | else: | | | print(f"uncertain best={a['choice']} conf={conf:.2f} < {args.min_confidence} [{ranked}]") | | | return 0 if ok else 1 | | | | | | |

|  | def cmd_rate(args) -> int: | 
|  | state = build_state(args) | 
|  | levels = [parse_value(lv) for lv in args.levels] | 
|  | if not 2 <= len(levels) <= MAX_SCORE_LEVELS: | 

| | raise UsageError(f"rate needs 2..{MAX_SCORE_LEVELS} level descriptions, low to high") |

|  | c = JevClient(args.api_key, args.model, timeout=args.timeout, label="rate") | 
|  | resp = c.ask(state, {"q": score(parse_value(args.instructions), levels)}) | 
|  | a = resp["answers"]["q"] | 
|  | conf = a.get("confidence", 0.0) | 
|  | top = len(levels) - 1 | 
|  | ok = conf >= args.min_confidence | 
|  | nearest = int(round(a["score"])) | 

| | if args.json: | | | print(json.dumps({"score": a["score"], "top": top, "normalized": a["score"] / top, "nearest_level": nearest, "nearest": describe_level(levels[nearest]), "confidence": conf, "probabilities": a["probabilities"], "verdict": "ok" if ok else "uncertain", "model": resp["model"], "usage": resp["usage"]})) | | | else: |

|  | flag = "" if ok else f"  uncertain (conf < {args.min_confidence})" | 
|  | print(f"{a['score']:.2f}/{top}  conf={conf:.2f}  ≈ {describe_level(levels[nearest])}{flag}") | 

| | return 0 if ok else 1 | | | | | | | | | def read_candidates(args) -> list: | | | if args.candidates_file: |

|  | v = parse_value("@" + args.candidates_file) | 
|  | if isinstance(v, list): | 

| | return v | | | return [line for line in str(v).splitlines() if line.strip()] | | | if args.candidates: |

|  | return list(args.candidates) | 
|  | txt = implicit_stdin() | 

| | if txt: |

|  | return [line.rstrip("\n") for line in txt.splitlines() if line.strip()] | 
|  | raise UsageError("no candidates: give them as arguments, --candidates-file (lines, or a JSON array), or on stdin") | 

| | | | | |

|  | def chunk_candidates(cands: list, max_chars: int, max_n: int) -> list[list[tuple[int, object]]]: | 
|  | chunks, cur, size = [], [], 0 | 
|  | for i, c in enumerate(cands): | 
|  | n = len(c) if isinstance(c, str) else len(json.dumps(c)) | 
|  | if cur and (size + n > max_chars or len(cur) >= max_n): | 
|  | chunks.append(cur) | 
|  | cur, size = [], 0 | 
|  | cur.append((i, c)) | 

| | size += n | | | if cur: | | | chunks.append(cur) | | | return chunks | | | | | | |

|  | def cmd_rank(args) -> int: | 
|  | cands = read_candidates(args) | 

| | if not cands: | | | raise UsageError("no candidates") |

|  | c = JevClient(args.api_key, args.model, timeout=args.timeout, label="rank") | 
|  | levels = [parse_value(lv) for lv in args.levels] if args.levels else None | 

| | question_text = args.instructions or ("How well does candidate match query?" if levels else "Is candidate relevant to query?") |

|  | if "{i}" in question_text or "candidates[" in question_text: | 
|  | eprint("jev: warning: refer to the item as `candidate`; each question carries its own item. Positional `candidates[i]` references mis-locate past ~20 items (measured 27% wrong at 150).") | 
|  | results: list[dict] = [] | 
|  | usage_in = usage_out = 0 | 
|  | t0 = time.time() | 

| | chunks = chunk_candidates(cands, args.chunk_chars, args.chunk_size) | | | | | | def run(chunk): | | | # The item lives INSIDE its question, not at an index in the state: Jev mis-locates | | | # candidates[i] in long arrays (measured 2026-09-18: 86/320 wrong at 150 per request, | | | # 29/320 at 25) while an embedded or keyed item scored 0/320 wrong at 320 per request. | | | state = {"query": parse_value(args.query)} | | | if args.context: |

|  | state["context"] = parse_value(args.context) | 
|  | qs = {} | 
|  | for j, (i, cand) in enumerate(chunk): | 
|  | instr = {"question": question_text, "candidate": cand} | 
|  | qs[f"c{j}"] = score(instr, levels) if levels else noul(instr) | 
|  | resp = c.ask(state, qs) | 
|  | out = [] | 
|  | for j, (i, cand) in enumerate(chunk): | 
|  | a = resp["answers"][f"c{j}"] | 

| | if levels: |

|  | top = len(levels) - 1 | 
|  | out.append({"i": i, "p": a["score"] / top, "score": a["score"], "confidence": a.get("confidence"), "candidate": cand}) | 

| | else: |

|  | out.append({"i": i, "p": a["noul"], "candidate": cand}) | 
|  | return out, resp.get("usage", {}) | 

| | |

|  | with cf.ThreadPoolExecutor(max_workers=args.concurrency) as ex: | 
|  | for out, u in ex.map(run, chunks): | 
|  | results.extend(out) | 
|  | usage_in += u.get("input_tokens", 0) | 
|  | usage_out += u.get("output_tokens", 0) | 
|  | results.sort(key=lambda r: -r["p"]) | 

| | if args.min is not None: | | | results = [r for r in results if r["p"] >= args.min] | | | if args.top: |

|  | results = results[: args.top] | 
|  | ms = (time.time() - t0) * 1000 | 

| | if args.json: | | | print(json.dumps({"results": results, "n": len(cands), "requests": len(chunks), "usage": {"input_tokens": usage_in, "output_tokens": usage_out}, "ms": round(ms)}, indent=None if args.compact else 2)) | | | else: | | | for r in results: |

|  | cand = r["candidate"] if isinstance(r["candidate"], str) else json.dumps(r["candidate"]) | 
|  | cand = cand.replace("\n", " ") | 
|  | if len(cand) > args.width: | 
|  | cand = cand[: args.width - 1] + "…" | 
|  | print(f"{r['p']:.3f}  #{r['i']:<4} {cand}") | 
|  | eprint(f"· {len(cands)} candidates · {len(chunks)} request(s) · {usage_in} in tokens · {ms:.0f} ms · ${cost_usd(usage_in):.5f}") | 

| | return 0 | | | | | | |

|  | def cmd_batch(args) -> int: | 
|  | qs = questions_from_args(args) | 
|  | if args.input == "-": | 
|  | raw = sys.stdin.read() | 

| | else: |

|  | p = Path(args.input).expanduser() | 
|  | if not p.exists(): | 
|  | raise UsageError(f"input not found: {p}") | 
|  | raw = p.read_text() | 
|  | items = [] | 
|  | for n, line in enumerate(raw.splitlines()): | 
|  | if not line.strip(): | 

| | continue | | | if args.text_lines: | | | items.append((n, None, line)) | | | continue | | | try: | | | obj = json.loads(line) | | | except json.JSONDecodeError: | | | items.append((n, None, line)) | | | continue | | | if isinstance(obj, dict) and args.state_key: | | | if args.state_key not in obj: |

|  | raise UsageError(f"line {n}: no key {args.state_key!r}") | 
|  | items.append((n, obj.get(args.id_key), obj[args.state_key])) | 

| | else: | | | items.append((n, obj.get(args.id_key) if isinstance(obj, dict) else None, obj)) | | | if not items: | | | raise UsageError("no input rows") | | | c = JevClient(args.api_key, args.model, timeout=args.timeout, label="batch") | | | out_f = open(args.out, "w") if args.out else sys.stdout | | | usage_in = 0 | | | t0 = time.time() | | | errors = 0 | | | | | | def run(item): | | | n, ident, state = item | | | try: |

|  | resp = c.ask(state, qs) | 
|  | row = {"line": n, "answers": resp["answers"], "usage": resp["usage"]} | 

| | except JevError as e: | | | row = {"line": n, "error": str(e)} | | | if ident is not None: | | | row["id"] = ident | | | if args.echo: | | | row["state"] = state | | | return row | | | | | | try: |

|  | with cf.ThreadPoolExecutor(max_workers=args.concurrency) as ex: | 
|  | for row in ex.map(run, items): | 

| | if "error" in row: | | | errors += 1 | | | else: |

|  | usage_in += row["usage"].get("input_tokens", 0) | 
|  | out_f.write(json.dumps(row) + "\n") | 
|  | out_f.flush() | 

| | finally: | | | if args.out: |

|  | out_f.close() | 
|  | ms = (time.time() - t0) * 1000 | 
|  | eprint(f"· {len(items)} rows · {errors} errors · {usage_in} in tokens · {ms:.0f} ms · ${cost_usd(usage_in):.5f}" + (f" · wrote {args.out}" if args.out else "")) | 
|  | return 0 if errors == 0 else 4 | 

| | | | | |

|  | def cmd_models(args) -> int: | 
|  | c = JevClient(args.api_key, args.model, timeout=args.timeout, record=False) | 
|  | resp = c.models() | 

| | if args.json: | | | print(json.dumps(resp, indent=2)) | | | else: |

|  | for m in resp.get("models", []): | 
|  | print(f"{m.get('name'):<14} {str(m.get('release_date', ''))[:10]}  {m.get('description', '')}") | 

| | return 0 | | | | | | |

|  | def cmd_auth(args) -> int: | 
|  | if args.action == "set": | 
|  | key = args.key or (sys.stdin.readline().strip() if not sys.stdin.isatty() else "") | 

| | if not key: |

|  | raise UsageError("jev auth set <key>  (or pipe the key on stdin)") | 
|  | CONFIG_DIR.mkdir(parents=True, exist_ok=True) | 
|  | old = os.umask(0o077) | 

| | try: | | | KEY_FILE.write_text(key.strip() + "\n") | | | finally: |

|  | os.umask(old) | 
|  | os.chmod(KEY_FILE, 0o600) | 
|  | print(f"stored {mask(key)} in {KEY_FILE} (0600)") | 

| | return 0 |

|  | if args.action == "clear": | 
|  | if KEY_FILE.exists(): | 
|  | KEY_FILE.unlink() | 
|  | print(f"removed {KEY_FILE}") | 

| | else: | | | print("no key file") | | | return 0 | | | try: | | | key, src = resolve_key(args.api_key) | | | except AuthError as e: | | | print(f"no key: {e}") | | | return 3 |

|  | print(f"key {mask(key)} from {src}") | 
|  | if src == str(KEY_FILE): | 
|  | mode = oct(KEY_FILE.stat().st_mode & 0o777) | 
|  | print(f"file mode {mode}" + ("" if mode == "0o600" else "  <- should be 0600")) | 

| | return 0 | | | | | | | | | def cmd_doctor(args) -> int: | | | ok = True | | | | | | def step(name, fn): | | | nonlocal ok | | | try: |

|  | msg = fn() | 
|  | print(f"ok    {name}: {msg}") | 

| | except Exception as e: # noqa: BLE001 | | | ok = False | | | print(f"FAIL {name}: {e}") | | | |

|  | step("python", lambda: sys.version.split()[0]) | 
|  | def key(): | 
|  | k, src = resolve_key(args.api_key) | 
|  | return f"{mask(k)} from {src}" | 

| | | | | step("key", key) | | | c = None | | | | | | def models(): | | | nonlocal c |

|  | c = JevClient(args.api_key, args.model, timeout=args.timeout, record=False) | 
|  | r = c.models() | 
|  | return f"{[m['name'] for m in r.get('models', [])]} in {c.last_ms:.0f} ms" | 

| | | | | step("GET /v1/models", models) | | | | | | def roundtrip(): | | | r = c.ask("The export button crashes the settings page in Safari but works in Chrome.", |

|  | {"is_bug": noul("Does this describe a software bug?"), | 
|  | "severity": score("How severe?", ["Cosmetic", "Degraded, workaround exists", "Blocking"])}) | 
|  | a = r["answers"] | 
|  | return f"model={r['model']} is_bug={a['is_bug']['noul']:.2f} severity={a['severity']['score']:.2f} in {c.last_ms:.0f} ms, {r['usage']['input_tokens']} tokens" | 

| | | | | if c is not None: | | | step("POST /v1/systemone", roundtrip) | | | step("usage ledger", lambda: f"{USAGE_FILE} ({USAGE_FILE.stat().st_size if USAGE_FILE.exists() else 0} bytes)") | | | step("skill", lambda: ", ".join(str(p) for p in (Path.home() / ".claude/skills/jev", Path.home() / ".codex/skills/jev", Path.home() / ".config/opencode/skills/jev") if p.exists()) or "not linked (see jev guide install)") | | | print("healthy" if ok else "problems found") | | | return 0 if ok else 4 | | | | | | |

|  | def load_ledger() -> list[dict]: | 
|  | if not USAGE_FILE.exists(): | 
|  | return [] | 
|  | rows = [] | 
|  | for line in USAGE_FILE.read_text().splitlines(): | 

| | try: |

|  | r = json.loads(line) | 
|  | r["_t"] = datetime.fromisoformat(r["ts"]) | 
|  | rows.append(r) | 

| | except (json.JSONDecodeError, KeyError, ValueError): | | | continue | | | return rows | | | | | | | | | def agent_names(ids: set[str]) -> dict[str, str]: | | | """Agents are attributed by $JEV_AGENT (or $AGENT_ID). Names are whatever you export.""" | | | return {i: (i if i else "(no agent)") for i in ids} | | | | | | |

|  | def _agg(sel: list[dict]) -> dict: | 
|  | ok = [r for r in sel if "err" not in r] | 
|  | tin = sum(r.get("in", 0) for r in ok) | 
|  | ms = sorted(r["ms"] for r in ok if r.get("ms")) | 
|  | pct = lambda p: ms[min(len(ms) - 1, int(len(ms) * p))] if ms else 0  # noqa: E731 | 

| | return { |

|  | "requests": len(sel), "errors": len(sel) - len(ok), "questions": sum(r.get("q", 0) for r in ok), | 
|  | "input_tokens": tin, "output_tokens": sum(r.get("out", 0) for r in ok), "usd": round(cost_usd(tin), 6), | 
|  | "avg_ms": round(sum(ms) / len(ms)) if ms else 0, "p50_ms": pct(0.5), "p95_ms": pct(0.95), "max_ms": ms[-1] if ms else 0, | 

| | } | | | | | | |

|  | def _peaks(sel: list[dict]) -> dict: | 
|  | per_min: dict[str, int] = {} | 
|  | per_sec: dict[str, int] = {} | 

| | for r in sel: |

|  | per_min[r["ts"][:16]] = per_min.get(r["ts"][:16], 0) + 1 | 
|  | per_sec[r["ts"]] = per_sec.get(r["ts"], 0) + r.get("in", 0) | 
|  | rpm = max(per_min.values()) if per_min else 0 | 
|  | tps = max(per_sec.values()) if per_sec else 0 | 
|  | return {"peak_rpm": rpm, "peak_rpm_at": max(per_min, key=per_min.get) if per_min else None, "rpm_limit": RATE_LIMIT_RPM, | 
|  | "peak_tps": tps, "peak_tps_at": max(per_sec, key=per_sec.get) if per_sec else None, "tps_limit": RATE_LIMIT_TPS} | 

| | | | | |

|  | def cmd_usage(args) -> int: | 
|  | rows = load_ledger() | 

| | if args.label: | | | rows = [r for r in rows if r.get("cmd") == args.label] | | | if args.agent: | | | rows = [r for r in rows if args.agent in (r.get("agent") or "")] | | | if args.since: | | | try: | | | since = datetime.fromisoformat(args.since) | | | except ValueError: |

|  | raise UsageError("--since expects YYYY-MM-DD or an ISO timestamp") | 
|  | rows = [r for r in rows if r["_t"] >= since] | 

| | if not rows: | | | print("no usage recorded" + (" for that filter" if (args.label or args.agent or args.since) else " yet")) | | | return 0 |

|  | now = datetime.now() | 
|  | day0 = now.replace(hour=0, minute=0, second=0, microsecond=0) | 
|  | cfg = load_config() | 

| | | | | if args.tail: | | | sel = rows[-args.tail:] | | | if args.json: | | | print(json.dumps([{k: v for k, v in r.items() if k != "_t"} for r in sel], indent=2)) | | | return 0 |

|  | names = agent_names({r.get("agent", "") for r in sel}) | 
|  | print(f"{'ts':<19} {'cmd':<12} {'agent':<22} {'q':>4} {'in':>7} {'ms':>5}  rid/err") | 

| | for r in sel: |

|  | tail = r.get("err") and f"ERR {r['err']}" or r.get("rid", "") | 
|  | print(f"{r['ts']:<19} {r.get('cmd', '?')[:12]:<12} {names[r.get('agent', '')][:22]:<22} {r.get('q', 0):>4} {r.get('in', 0):>7} {r.get('ms', 0):>5}  {tail}") | 

| | return 0 | | | | | | if args.by: | | | key = args.by |

|  | if key == "day": | 
|  | groups = {} | 

| | for r in rows: |

|  | if r["_t"] >= day0 - timedelta(days=args.days - 1): | 
|  | groups.setdefault(r["ts"][:10], []).append(r) | 
|  | elif key == "hour": | 
|  | groups = {} | 

| | for r in rows: |

|  | if r["_t"] >= now - timedelta(hours=24): | 
|  | groups.setdefault(r["ts"][:13] + ":00", []).append(r) | 
|  | elif key in ("cmd", "model"): | 
|  | groups = {} | 

| | for r in rows: |

|  | groups.setdefault(str(r.get(key) or "?"), []).append(r) | 
|  | elif key == "agent": | 
|  | names = agent_names({r.get("agent", "") for r in rows}) | 
|  | groups = {} | 

| | for r in rows: | | | groups.setdefault(names[r.get("agent", "")], []).append(r) | | | else: |

|  | raise UsageError("--by expects day, hour, cmd, agent or model") | 
|  | table = {k: _agg(v) for k, v in groups.items()} | 
|  | order = sorted(table) if key in ("day", "hour") else sorted(table, key=lambda k: -table[k]["input_tokens"]) | 

| | if args.json: | | | print(json.dumps({k: table[k] for k in order}, indent=2)) | | | return 0 | | | print(f"{key:<24} {'requests':>8} {'errors':>6} {'questions':>9} {'in tokens':>10} {'usd':>9} {'avg ms':>6}") | | | for k in order: |

|  | a = table[k] | 
|  | print(f"{k[:24]:<24} {a['requests']:>8} {a['errors']:>6} {a['questions']:>9} {a['input_tokens']:>10,} {a['usd']:>9.4f} {a['avg_ms']:>6}") | 

| | return 0 | | | |

|  | windows = { | 
|  | "today": day0, "yesterday": (day0 - timedelta(days=1), day0), "7d": now - timedelta(days=7), | 
|  | "30d": now - timedelta(days=30), "month": day0.replace(day=1), "all": datetime.min, | 

| | } |

|  | summary = {} | 
|  | for name, w in windows.items(): | 
|  | lo, hi = (w if isinstance(w, tuple) else (w, None)) | 
|  | sel = [r for r in rows if r["_t"] >= lo and (hi is None or r["_t"] < hi)] | 
|  | summary[name] = _agg(sel) | 
|  | last7 = [r for r in rows if r["_t"] >= now - timedelta(days=7)] | 
|  | last30 = [r for r in rows if r["_t"] >= now - timedelta(days=30)] | 
|  | first_ts = rows[0]["_t"] | 
|  | days_observed = max(1e-9, (now - first_ts).total_seconds() / 86400) | 
|  | rate7 = summary["7d"]["usd"] / min(7, days_observed) | 
|  | rate30 = summary["30d"]["usd"] / min(30, days_observed) | 
|  | forecast = {"usd_per_day_7d": round(rate7, 6), "usd_per_day_30d": round(rate30, 6), | 
|  | "usd_per_month_at_7d_rate": round(rate7 * 30, 4), "usd_per_month_at_30d_rate": round(rate30 * 30, 4), | 
|  | "days_observed": round(days_observed, 2)} | 

| | credits = None |

|  | if cfg.get("credits_usd") is not None: | 
|  | as_of = datetime.fromisoformat(cfg.get("credits_as_of") or first_ts.isoformat()) | 
|  | spent = cost_usd(sum(r.get("in", 0) for r in rows if r["_t"] >= as_of)) | 
|  | remaining = float(cfg["credits_usd"]) - spent | 
|  | credits = {"credits_usd": float(cfg["credits_usd"]), "as_of": as_of.isoformat(timespec="seconds"), "spent_since": round(spent, 6), | 
|  | "remaining_usd": round(remaining, 6), "remaining_mtok": round(remaining / price_per_mtok(), 3) if remaining > 0 else 0.0, | 
|  | "days_left_at_7d_rate": round(remaining / rate7, 1) if rate7 > 0 and remaining > 0 else None} | 

| | budget = None |

|  | if cfg.get("budget_usd_month"): | 
|  | b = float(cfg["budget_usd_month"]) | 
|  | mtd = summary["month"]["usd"] | 
|  | dim = ((day0.replace(day=28) + timedelta(days=4)).replace(day=1) - timedelta(days=1)).day | 
|  | budget = {"budget_usd_month": b, "mtd_usd": mtd, "pct_used": round(100 * mtd / b, 1) if b else None, | 
|  | "projected_month_end_usd": round(mtd + rate7 * (dim - now.day), 4), "days_left_in_month": dim - now.day} | 
|  | errors = {} | 

| | for r in last7: | | | if "err" in r: |

|  | errors[r["err"]] = errors.get(r["err"], 0) + 1 | 
|  | by_cmd = {} | 

| | for r in rows: |

|  | by_cmd.setdefault(r.get("cmd") or "?", []).append(r) | 
|  | names = agent_names({r.get("agent", "") for r in rows}) | 
|  | by_agent = {} | 

| | for r in rows: |

|  | by_agent.setdefault(names[r.get("agent", "")], []).append(r) | 
|  | by_model = {} | 

| | for r in rows: |

|  | if r.get("model"): | 
|  | by_model.setdefault(r["model"], []).append(r) | 
|  | peaks = _peaks(last30) | 
|  | out = { | 
|  | "price_per_mtok_in": price_per_mtok(), "ledger": str(USAGE_FILE), "rows": len(rows), "first": rows[0]["ts"], "last": rows[-1]["ts"], | 
|  | "windows": summary, "by_cmd": {k: _agg(v) for k, v in by_cmd.items()}, "by_agent": {k: _agg(v) for k, v in by_agent.items()}, | 
|  | "by_model": {k: _agg(v) for k, v in by_model.items()}, "errors_7d": errors, "throughput_30d": peaks, | 

| | "forecast": forecast, "credits": credits, "budget": budget, | | | } | | | if args.json: | | | print(json.dumps(out, indent=2)) | | | return 0 | | | |

|  | print(f"jev usage · ${price_per_mtok()}/Mtok in, output free · {len(rows)} requests since {rows[0]['ts'][:16]} · {USAGE_FILE}") | 
|  | print(f"\n{'window':<10} {'requests':>8} {'errors':>6} {'questions':>9} {'in tokens':>10} {'out':>7} {'usd':>9} {'avg ms':>6}") | 
|  | for name, a in summary.items(): | 
|  | print(f"{name:<10} {a['requests']:>8} {a['errors']:>6} {a['questions']:>9} {a['input_tokens']:>10,} {a['output_tokens']:>7,} {a['usd']:>9.4f} {a['avg_ms']:>6}") | 
|  | a7 = summary["7d"] | 

| | print(f"\nlatency 7d p50 {a7['p50_ms']} ms · p95 {a7['p95_ms']} ms · max {a7['max_ms']} ms · avg questions/request {a7['questions'] / max(1, a7['requests'] - a7['errors']):.1f} · avg tokens/request {a7['input_tokens'] / max(1, a7['requests'] - a7['errors']):,.0f}") | | | print(f"throughput peak {peaks['peak_rpm']} req/min ({100 * peaks['peak_rpm'] / RATE_LIMIT_RPM:.1f}% of {RATE_LIMIT_RPM}) at {peaks['peak_rpm_at']} · peak {peaks['peak_tps']:,} tok/s ({100 * peaks['peak_tps'] / RATE_LIMIT_TPS:.2f}% of {RATE_LIMIT_TPS:,}) · 30d window") | | | print("errors 7d " + (", ".join(f"{k} x{v}" for k, v in sorted(errors.items(), key=lambda kv: -kv[1])) if errors else "none")) | | | print(f"forecast ${forecast['usd_per_day_7d']:.4f}/day at the 7d rate -> ${forecast['usd_per_month_at_7d_rate']:.2f}/month · ${forecast['usd_per_month_at_30d_rate']:.2f}/month at the 30d rate") | | | if credits: |

|  | dl = f" · ~{credits['days_left_at_7d_rate']} days left at the 7d rate" if credits["days_left_at_7d_rate"] else "" | 
|  | print(f"credits      ${credits['remaining_usd']:.4f} remaining of ${credits['credits_usd']:.2f} set {credits['as_of'][:16]} ({credits['remaining_mtok']:.1f} Mtok){dl}") | 

| | else: | | | print("credits not configured. The API exposes no balance; read it at https://console.typesafe.ai and run jev config set credits <usd>") | | | if budget: |

|  | print(f"budget       ${budget['mtd_usd']:.4f} of ${budget['budget_usd_month']:.2f} this month ({budget['pct_used']}%) · projected month end ${budget['projected_month_end_usd']:.2f}") | 
|  | print(f"\n{'by command':<24} {'requests':>8} {'questions':>9} {'in tokens':>10} {'usd':>9}") | 
|  | for k, v in sorted(by_cmd.items(), key=lambda kv: -sum(r.get('in', 0) for r in kv[1]))[:12]: | 
|  | a = _agg(v) | 
|  | print(f"{k[:24]:<24} {a['requests']:>8} {a['questions']:>9} {a['input_tokens']:>10,} {a['usd']:>9.4f}") | 
|  | print(f"\n{'by agent':<24} {'requests':>8} {'questions':>9} {'in tokens':>10} {'usd':>9}") | 
|  | for k, v in sorted(by_agent.items(), key=lambda kv: -sum(r.get('in', 0) for r in kv[1]))[:12]: | 
|  | a = _agg(v) | 
|  | print(f"{k[:24]:<24} {a['requests']:>8} {a['questions']:>9} {a['input_tokens']:>10,} {a['usd']:>9.4f}") | 
|  | if len(by_model) > 1: | 
|  | print("\nby model: " + ", ".join(f"{k} {len(v)}" for k, v in by_model.items())) | 
|  | print("\nmore: --by day\|hour\|cmd\|agent\|model · --tail N · --since DATE · --label X · --agent X · --json · jev config · jev cost") | 

| | return 0 | | | | | | | | | CONFIG_KEYS = { | | | "credits": ("credits_usd", float, "prepaid balance in USD, read off https://console.typesafe.ai; jev usage counts down from it"), | | | "credits_as_of": ("credits_as_of", str, "ISO timestamp the balance was read (default: when you set it)"), | | | "budget": ("budget_usd_month", float, "monthly spend budget in USD; jev usage shows % used and a month-end projection"), | | | "price": ("price_per_mtok_in", float, f"USD per million input tokens if the vendor changes it (default {PRICE_PER_MTOK_IN})"), | | | } | | | | | | |

|  | def cmd_config(args) -> int: | 
|  | cfg = load_config() | 
|  | if args.action == "show" or not args.action: | 

| | if args.json: | | | print(json.dumps(cfg, indent=2)) | | | return 0 |

|  | print(f"{CONFIG_FILE}" + ("" if CONFIG_FILE.exists() else " (not created yet)")) | 
|  | for k, (real, _, desc) in CONFIG_KEYS.items(): | 
|  | print(f"  {k:<14} {str(cfg.get(real, '-')):<24} {desc}") | 

| | return 0 | | | if args.action == "unset": | | | if not args.key or args.key not in CONFIG_KEYS: |

|  | raise UsageError(f"jev config unset <{'\|'.join(CONFIG_KEYS)}>") | 
|  | cfg.pop(CONFIG_KEYS[args.key][0], None) | 
|  | if args.key == "credits": | 
|  | cfg.pop("credits_as_of", None) | 
|  | save_config(cfg) | 
|  | print(f"unset {args.key}") | 

| | return 0 | | | if args.action == "set": | | | if not args.key or args.key not in CONFIG_KEYS or args.value is None: |

|  | raise UsageError(f"jev config set <{'\|'.join(CONFIG_KEYS)}> <value>") | 
|  | real, typ, _ = CONFIG_KEYS[args.key] | 

| | try: | | | val = typ(args.value) | | | except ValueError: |

|  | raise UsageError(f"{args.key} expects a {typ.__name__}") | 
|  | if args.key == "credits_as_of": | 

| | try: | | | datetime.fromisoformat(val) | | | except ValueError: |

|  | raise UsageError("credits_as_of expects an ISO timestamp, e.g. 2026-09-19T14:00") | 
|  | cfg[real] = val | 
|  | if args.key == "credits": | 
|  | cfg["credits_as_of"] = args.as_of or datetime.now().isoformat(timespec="seconds") | 
|  | save_config(cfg) | 
|  | print(f"set {args.key} = {val}" + (f" (as of {cfg['credits_as_of']})" if args.key == "credits" else "")) | 

| | return 0 | | | raise UsageError("jev config [show|set|unset]") | | | | | | | | | CHARS_PER_TOKEN = 4.0 # rough English heuristic; --exact measures the real count with one tiny call | | | | | | | | | def cmd_cost(args) -> int: | | | """Estimate tokens and cost for a prospective job before running it.""" |

|  | state = build_state(args) | 
|  | text = state if isinstance(state, str) else json.dumps(state) | 
|  | est_state = int(len(text) / CHARS_PER_TOKEN) | 

| | q_tokens = int(args.question_chars / CHARS_PER_TOKEN) | | | exact = None | | | if args.exact: |

|  | c = JevClient(args.api_key, args.model, timeout=args.timeout, label="cost") | 
|  | r = c.ask(state, {"probe": noul("Is this text written in English?")}) | 
|  | exact = r["usage"]["input_tokens"] | 

| | est_state = max(0, exact - q_tokens - 40) # subtract the probe question and request overhead | | | per_request = est_state + args.questions * q_tokens | | | fits = per_request <= 64_000 and (est_state + q_tokens) <= 32_000 | | | n_items = args.items or 1 | | | if args.items: | | | # rank/batch shape: each item is its own request if --per-item, else items are packed with the state repeated per request | | | if args.per_item: | | | total = per_request * n_items | | | requests = n_items | | | else: |

|  | per_req_items = max(1, min(250, (64_000 - est_state) // max(1, q_tokens))) | 
|  | requests = -(-n_items // per_req_items) | 

| | total = requests * est_state + n_items * q_tokens | | | else: | | | total, requests = per_request, 1 | | | out = {"state_tokens": est_state, "exact": exact is not None, "question_tokens_each": q_tokens, "questions": args.questions, | | | "tokens_per_request": per_request, "fits_request_budget": fits, "items": n_items, "requests": requests, | | | "total_input_tokens": total, "usd": round(cost_usd(total), 6), "price_per_mtok_in": price_per_mtok()} | | | if args.json: | | | print(json.dumps(out, indent=2)) | | | return 0 |

|  | how = "measured" if exact is not None else f"estimated at {CHARS_PER_TOKEN:g} chars/token (add --exact to measure)" | 
|  | print(f"state        {est_state:,} tokens ({how})") | 
|  | print(f"per request  {per_request:,} tokens = state + {args.questions} question(s) x ~{q_tokens} · {'fits' if fits else 'EXCEEDS'} the 64k/32k request budget") | 

| | if args.items: |

|  | print(f"job          {n_items:,} items -> {requests:,} request(s), {total:,} input tokens") | 
|  | print(f"cost         ${cost_usd(total):.6f} at ${price_per_mtok()}/Mtok" + (f" (${cost_usd(total) / n_items:.7f} per item)" if args.items else "")) | 

| | return 0 | | | | | | |

|  | def cmd_schema(args) -> int: | 
|  | print(json.dumps({ | 

| | "state": "string | object | array (the content to judge; use an object with named fields; reference them in instructions with backticks like ticket.message)", |

|  | "model": "jev-latest", | 
|  | "questions": { | 
|  | "is_urgent": {"type": "noul", "instructions": "Does `ticket.message` convey urgency?", "criteria": {"true": "optional: what yes means", "false": "optional: what no means"}}, | 

| | "department": {"type": "choice", "instructions": "Which team should handle ticket.message?", "criteria": {"billing": "Charges, invoices, refunds", "technical": "Bugs, outages, integrations", "other": None}}, | | | "frustration": {"type": "score", "instructions": "How frustrated does the customer appear in ticket.message?", "criteria": ["Calm and matter-of-fact", "Frustrated but civil", "Very angry or threatening to leave"]}, | | | }, | | | }, indent=2)) | | | print("\nAnswers come back under the same ids: noul -> {noul}; choice -> {choice, probabilities, confidence}; score -> {score, legend, probabilities, confidence}.\ninstructions and every criteria entry may be a string OR a JSON object/array (see jev guide structure).") | | | return 0 | | | | | | |

|  | def cmd_docs(args) -> int: | 
|  | url = f"{DOCS}/llms.txt" | 

| | try: |

|  | with urllib.request.urlopen(urllib.request.Request(url, headers={"User-Agent": f"jev-cli/{VERSION}"}), timeout=20) as r: | 
|  | txt = r.read().decode() | 

| | except Exception as e: # noqa: BLE001 | | | raise NetworkError(f"could not fetch {url}: {e}") | | | if args.grep: |

|  | needle = args.grep.lower() | 
|  | txt = "\n".join(line for line in txt.splitlines() if needle in line.lower()) | 
|  | print(txt) | 

| | return 0 | | | | | | | | | def cmd_guide(args) -> int: | | | if args.list: |

|  | for k, (title, _, live) in GUIDE.items(): | 
|  | print(f"{k:<12} {title}" + (f"   (live: {live if live.startswith('http') else DOCS + live + '.md'})" if live else "")) | 

| | return 0 | | | topic = args.topic or "when" | | | if topic not in GUIDE: |

|  | raise UsageError(f"unknown topic {topic!r}. Topics: {', '.join(GUIDE)}") | 
|  | title, body, live = GUIDE[topic] | 
|  | if args.live or topic == "vendor": | 

| | if not live: |

|  | raise UsageError(f"topic {topic!r} has no live page") | 
|  | url = live if live.startswith("http") else f"{DOCS}{live}.md" | 

| | try: |

|  | with urllib.request.urlopen(urllib.request.Request(url, headers={"User-Agent": f"jev-cli/{VERSION}"}), timeout=20) as r: | 
|  | print(r.read().decode()) | 

| | except Exception as e: # noqa: BLE001 | | | raise NetworkError(f"could not fetch {url}: {e}") | | | return 0 | | | print(f"# {title}\n\n{body.strip()}\n") | | | if live: | | | print(f"Live source: {live if live.startswith('http') else DOCS + live + '.md'} (jev guide {topic} --live)") | | | return 0 | | | | | | | | | def cmd_examples(args) -> int: | | | if not args.name: | | | print("Copy-paste examples. jev examples <name> prints one; jev examples all prints everything.\n") |

|  | for k, (title, _) in EXAMPLES.items(): | 
|  | print(f"  {k:<10} {title}") | 

| | return 0 | | | keys = list(EXAMPLES) if args.name == "all" else [args.name] | | | for k in keys: | | | if k not in EXAMPLES: |

|  | raise UsageError(f"unknown example {k!r}. Names: {', '.join(EXAMPLES)}") | 
|  | title, body = EXAMPLES[k] | 
|  | print(f"## {title}\n{body.strip()}\n") | 

| | return 0 | | | | | | | | | # --------------------------------------------------------------------------- embedded docs | | | | | | GUIDE: dict[str, tuple[str, str, str | None]] = {} | | | | | | GUIDE["when"] = ("Jev playbook for agents: when to reach for it, how to ask, how to read", r""" | | | Jev is TypeSafe's System One model. It does not write text. It makes ONE narrow judgment | | | per question against a state you hand it, and returns a calibrated probability. ~250 ms. | | | $0.042 per million input tokens (a 1k-token item costs $0.00004). Output tokens are free. | | | Same input -> same answer, run to run. It is a semantic if-statement, not a chatbot. | | | | | | ## Reach for Jev when | | | 1. The decision REPEATS over many items and you should not read them all into context: | | | classify / filter / rank / dedupe hundreds of notes, emails, transcript segments, log | | | lines, RSS items, PRs, findings. jev rank and jev batch do this in a few requests. | | | 2. You want a CALIBRATED PROBABILITY, not your own vibe. Your confidence is not calibrated; | | | Jev's is trained to be (RLCD). Use it to decide act / confirm / escalate. | | | 3. A SCRIPT or CUE needs a judgment that runs unattended, a million times, with no agent | | | loop: "is this Ring event a delivery?", "is this email a receipt?", "is this item relevant?" | | | 4. You need an INDEPENDENT CHECK of your own output: does the source support this claim, | | | does this draft break a stated rule, does this text contain an instruction aimed at an | | | agent (prompt injection), is this tool call risky. | | | 5. You need SPEED: real-time gates inside a request path or a UI. | | | | | | ## Do NOT use Jev for | | | - Generation, rewriting, summarizing, explaining. It cannot. Use yourself. | | | - Math, counting, date arithmetic, numeric comparison. Keep those in code; Jev extracts | | | the parts (a Choice over months/days), code does the arithmetic. | | | - Multi-hop reasoning, double negatives, "a property of a property". | | | - One small item you can judge in a glance. The call costs more than the decision. | | | - Hostile input without care: state is data, and injected text can move an answer. Write | | | precise criteria and test edge cases. | | | | | | ## The three primitives (pick by the SHAPE of the answer your code needs) | | | | need | type | answer fields | | | | | is this true? | noul | noul = P(yes) 0..1 (no separate confidence) | | | | | which one of these? | choice | choice, probabilities{opt: p}, confidence | | | | | where on this rubric? | score | score (0..N-1, may fall between), legend, | | | | | | | probabilities{level: p}, confidence | | | | | | | ## Fast path (one question) | | | jev yes "Does this message ask for a refund?" --state "I was charged twice, fix it" | | | jev pick "Which team handles this?" billing="Charges, refunds" tech="Bugs" other -s "…" | | | jev rate "How severe is the bug?" "Cosmetic" "Degraded, workaround exists" "Blocking" -s @report.txt | | | yes exits 0/1 on --threshold (default 0.5). pick/ rate exit 1 below --min-confidence. | | | Pipe text on stdin instead of --state: cat email.txt | jev yes "Is this a receipt?" | | | | | | ## Real path (many questions, one call: SPECULATIVE FAN-OUT) | | | Ask EVERY question you might need in ONE request. They run in parallel and independently; | | | adding questions barely changes latency and costs only the question tokens. Let code ignore | | | the answers it does not need. | | | jev ask --field message=@ticket.txt --field policy=@refund_policy.md \ | | | --noul refund 'Does message request a refund?' \ | | | --noul covered 'Does policy cover the refund requested in message?' \ | | | --choice team 'Which team should handle message?' billing=Charges tech=Bugs other \ | | | --score anger 'How angry is message?' "Calm" "Frustrated but civil" "Hostile" --json | | | Shell rule: SINGLE-QUOTE any instruction containing backticks, or zsh runs them as commands. | | | | | | ## Writing a question that works (Jev reads LITERALLY) | | | - One judgment per question. "Analyze this and decide what to do" is not a question; split | | | it. "Does message ask for money back?" is. | | | - Say the exact condition. If you find yourself explaining what you "really meant", that | | | explanation belongs in the instructions or criteria. | | | - Point at the field with a backticked path: ticket.messages[0].text. Question IDs are | | | NOT sent to the model; the instructions must carry the whole meaning. | | | - NEVER index into a long array (items[137]). Measured 2026-09-18: 27% wrong at 150 | | | items per request, still 9% at 25. Key the object instead (items.k137) or put the | | | item inside the question itself; both scored 0 wrong out of 320. jev rank does the | | | latter for you. | | | - Choice: describe each option so the boundary with its neighbour is clear; add other / | | | none when the list may not cover the input. Up to 255 options. | | | - Score: levels describe SITUATIONS, low to high, 2..10 of them. "Broken but a workaround | | | exists" works; "moderately bad" and bare numbers do not. One dimension per score. | | | - Noul: phrase it so high = yes. Optional true=/false= criteria pin a subtle boundary. | | | - Send only the state the questions need. Irrelevant context lowers accuracy (context rot). | | | Filter in code first. Budget: ~32k tokens for state + longest question. | | | | | | ## Reading the answer | | | - noul: p >= 0.5 is yes, but pick the threshold by stakes. Do not read 0.5 as "medium". | | | - choice: choice is the argmax; confidence says how peaked the distribution is. A second | | | option with real mass is signal, not noise. | | | - score: a position between levels (0.71 + 0.32 = 1.3). Round for one outcome; compare or | | | sort for ranking; divide by N-1 to normalize before weighting several scores together. | | | - THREE BANDS: high confidence -> act; medium -> confirm or gather more; low -> do not act, | | | escalate or ask. Thresholds scale with risk: read-only at 0.6, destructive at 0.9. | | | - Do not carry a threshold tuned on a noul over to a choice, and do not expect P(x) and | | | 1 - P(not x) from two questions to agree. Ask the question you want, one way. | | | - Combine independent answers in CODE with weights you own (composite scoring). A weight | | | change is a code change, not a prompt rewrite, and needs no re-inference. | | | | | | ## Build it into a tool, not just a chat turn | | | sys.path.insert(0, "/path/to/dir/containing/jev.py") | | | from jev import JevClient, noul, choice, score |

|  | r = JevClient(label="my_tool").ask(state, {"ok": noul("Is `text` a receipt?")}) | 
|  | p = r["answers"]["ok"]["noul"] | 

| | Keep the QUESTIONS and THRESHOLDS in one constant block at the top of the tool so a human | | | can review them. That is the part that needs eyes; the rest is plumbing. | | | | | | ## Go deeper | | | jev guide --list topics: questions choice score noul state confidence patterns | | | design pitfalls structure cookbooks api library usage | | | exit-codes install vendor | | | jev guide design BUILDING a tool or cron job on Jev, not just calling it once | | | jev guide vendor TypeSafe's own agent skill, fetched live | | | jev guide <topic> --live the vendor's page for that topic, current | | | jev examples copy-paste recipes: triage rank filter guardrail verify batch python | | | jev docs live index of every vendor page and cookbook | | | """, "/concepts/how-to-build-with-system-one") | | | | | | GUIDE["questions"] = ("Writing instructions and criteria", r""" | | | A question is: an ID you choose (not sent to the model), a type, instructions, and | | | criteria (options for choice, ordered levels for score, optional true/false for noul). | | | | | | ## Rules that come from measured failure modes (jev-1.13) | | | 1. LITERAL READING. Jev answers the words, not the intent. Scoping words, negations and | | | implied conditions are taken at face value. Write the exact condition. Put boundary | | | cases in the criteria. Split any interpretation into two literal questions. | | | 2. ATOMIC. Broad questions hide several judgments behind one number. "Is this spam?" | | | becomes: requests a credential? sender identity conflicts with domain? announces an | | | unexpected reward? Then weight them in code. | | | 3. NAME THE FIELD. With an object state, reference path.to[0].field in backticks. | | | Structured state + named references removes ambiguity about what to judge. But do |

|  | not rely on POSITIONAL indexes in long arrays: `items[137]` mis-locates (27% wrong | 
|  | at 150 items, 9% at 25, measured 2026-09-18). Use keyed objects (`items.k137`) or | 

| | embed the item in the question; both 0% wrong at 320. | | | 4. ALIGN INSTRUCTIONS AND CRITERIA. A noul where true means "no" confuses it. The | | | criteria are an extension of the instruction, in the same direction. | | | 5. CONTRASTIVE OPTIONS. For a choice, say what each option covers, what belongs to the | | | neighbour instead, and 1-3 examples. Same field names on every option. | | | 6. SITUATIONS, NOT DEGREES. Score levels are matched one by one against the state; the | | | model does not see the level numbers or neighbours. "Cosmetic; no impact" beats "1". | | | 7. COVER THE SPACE. Add other / none_of_the_above to a choice when inputs may not fit. | | | For selection-from-candidates, the model cannot pick a value you did not offer. | | | 8. KEEP MATH OUT. Counting, dates, magnitudes, hex colours, assembly: convert or compute in | | | code, hand Jev the semantic part ("does this colour read as a warning?"). | | | 9. LESS STATE. Send only what the question needs. Retrieve and filter first. | | | | | | ## Shapes | | | noul {"type":"noul","instructions":"Does msg request a refund?", | | | "criteria":{"true":"asks for money back or a credit","false":"complains without asking for a remedy"}} | | | choice {"type":"choice","instructions":"Which team should handle msg?", | | | "criteria":{"billing":"charges, invoices, refunds","orders":"status, delivery, returns","other":null}} | | | score {"type":"score","instructions":"How severe is the bug in report?", | | | "criteria":["Cosmetic; no functional impact","Broken, workaround exists","Blocking; no workaround"]} | | | | | | instructions and every criteria entry may be a JSON object or array instead of a string | | | (jev guide structure). Start with strings; add structure when two options keep getting | | | confused or a level needs examples. | | | """, "/primitives") | | | | | | GUIDE["choice"] = ("Choice: pick one option from a defined set", r""" | | | Use when the answer is one of a known set with NO order between them: routing, category, | | | language, entity type, which candidate span is the value. Up to 255 options; adding an | | | option costs a few tokens, so give the FULL list rather than a shortlist. | | | | | | Answer: choice (argmax), probabilities (every option, sums to 1), confidence | | | (how peaked). Read the runner-up: 0.60 vs 0.38 means two teams want this ticket. | | | | | | jev pick "Which team should handle this?" \ | | | returns="Exchanges, refunds, wrong or damaged items" \ | | | shipping="Delivery status, delays, lost packages" \ | | | billing="Charges, invoices, payment problems" other \ | | | -s "My shoes came in the wrong size and I was charged twice" --min-confidence 0.4 | | | | | | - Confusable neighbours: give each option an object {what, not_for, examples}. | | | - Deep taxonomy: one choice per level, options = children, value = the subtree; walk in | | | code, keep several branches alive when probabilities are close (beam search). | | | - Relative vs absolute: a choice says WHICH option is best even when all are bad. Pair it | | | with one noul per option (or a none option) to know whether ANY fits. | | | - Speculative: "If this is a return, why?" is fine to ask on every ticket; ignore the | | | answer when the department was not returns. | | | """, "/primitives/choice") | | | | | | GUIDE["score"] = ("Score: rate the state on an ordered rubric", r""" | | | Use when the answer is a position on a spectrum you can describe in steps: severity, | | | frustration, relevance, seniority, formality. 2..10 levels, low to high. Level N is its | | | index in the array. The model judges each level's description on its own against the | | | state; it never sees the numbers or the neighbours. | | | |

|  | Answer: `score` = sum(level * p), can land between levels; `probabilities` per level; | 
|  | `legend` maps level -> description; `confidence`. | 

| | | | | jev rate "How much does this bug report give an engineer to work with?" \ | | | "No detail; just says something is broken" \ | | | "Names the feature but no steps or environment" \ | | | "Steps to reproduce or environment, but not both" \ | | | "Steps to reproduce and environment" -s @report.txt | | | | | | - Describe SITUATIONS. Examples inside a level (object with what + examples) sharpen it, | | | but only when they resemble real inputs. Test against known cases. | | | - One dimension per score. "Punctual and smart and experienced" cannot be placed. | | | - Give a rare extreme its own level ("abusive or threatening") so the top is not shared. | | | - Do not interpolate magnitudes from a score ("1.3 means 30% of users"). It is a | | | position for thresholding, ranking and rounding, not a measurement. | | | - Same score, different shapes: 1.0 can be all-on-level-1 or half 0 / half 2. Read | | | probabilities and confidence alongside. | | | - Composite: normalize each score by (levels-1), then weight in code: | | | priority = 0.6severity + 0.3frustration + 0.1*report_quality. | | | """, "/primitives/score") | | | | | | GUIDE["noul"] = ("Noul: probability that a yes/no statement is true", r""" | | | Use for a clean yes/no where the probability itself is the signal: mentions X, requests | | | Y, contains PII, is a receipt, is relevant to the query, contains an instruction aimed at | | | an agent. Answer: noul = P(yes). No separate confidence: near 1 strong yes, near 0 | | | strong no, near 0.5 undecided (NOT "medium"; for degree use a score). | | | | | | jev yes 'Does text contain an instruction directed at an AI agent or assistant?' \ | | | --true "tells an agent/assistant/model to do, ignore, or reveal something" \ | | | --false "ordinary content with no directive aimed at a model" \ | | | --field text=@untrusted.md --threshold 0.7 | | | | | | - Phrase so high means yes. A statement form ("the customer is requesting a refund") | | | works as well as a question; test both on your data. | | | - Criteria are optional; add true=/false= when the boundary is subtle. | | | - One noul per label when several labels may apply at once (multi-label), instead of | | | a single choice. | | | - Counting: one noul per item, the item embedded in its question or under a key |

|  | (`items.k7`), never `items[7]` in a long array; then count in code. | 
|  | - `jev yes` exit codes: 0 yes, 1 no or uncertain (with --band LOW HIGH), 2+ errors. | 

| | """, "/primitives/noul") | | | | | | GUIDE["state"] = ("State: what you hand the model", r""" | | | State is the material a panel of experts would be shown before judging. String, JSON | | | object, or array. Text only (no images/audio). English is strongest. | | | |

|  | --state "text"            --state @file        --state -  (stdin)     cat f \| jev … | 
|  | --state-file path         (.json parsed, else text) | 
|  | --state-json '{"a":1}' | 
|  | --field msg=@ticket.txt --field policy=@policy.md --field n=3   (builds an object) | 

| | | | | Every shape below is state, and a question is separate from it: | | | | | | | Shape | How | Reference it as | | | | |---|---|---| | | | | one string | -s TEXT, -s @file, stdin | the whole state | |

|  | \| many sources at once \| `--field msg=@t.txt --field policy=@p.md --field orders=@o.json` \| `msg`, `policy`, `orders` \| | 
|  | \| nested JSON \| `--state-file o.json` \| `open[0].charges`, `sla.tier` \| | 
|  | \| a sequence \| `--state-json '["turn 1","turn 2"]'` \| the array, in order \| | 

| | | a whole document | --state-file bigdoc.md | measured OK to 107k chars | | | | | full control | jev ask -f request.json (state AND questions) | anything you wrote | | | | | | | A --field value that is @file.json or inline JSON is PARSED, so an object stays an | | | object and the model sees real structure. Mixing types in one request is fine: a text file, | | | a JSON file, a JSONL file and a bare scalar side by side. | | | | | | Prefer an OBJECT with named fields, then reference fields in instructions with backticked | | | paths: ticket.messages[0].text, policy. Small arrays index fine; long ones do not | | | (items[137] is wrong 27% of the time at 150 items). Key them: {"k137": ...}. | | | | | | - One request = one state, any number of questions, all independent, all parallel. | | | - Budget, MEASURED 2026-09-20 by binary search rather than taken from the docs: the largest | | | state accepted was 107,500 chars = 32,388 input tokens (765 ms); ~110k chars returns | | | HTTP 400 max_tokens_exceeded. English measured at 3.32 chars/token, not 4, so the | | | real ceiling is nearer 107k chars than the 150k a 4-char rule of thumb implies. jev | | | warns above 120k chars; rank and batch chunk under it. | | | - Irrelevant material costs accuracy. Filter in code. If you cannot, ask a noul for | | | relevance first and drop what fails. | | | - State is data, not instructions, but adversarial text inside it can still steer an | | | answer. Be explicit in criteria; test edge cases before trusting a guardrail. | | | - When a later question depends on an earlier ANSWER (you need it to fetch data or pick | | | the next options), make a second request. Otherwise ask everything at once. | | | """, "/concepts/state") | | | | | | GUIDE["confidence"] = ("Probabilities vs confidence, and how to act on them", r""" | | | Every choice/score answer carries probabilities (the distribution) and confidence | | | (one number, 0..1, derived from how peaked that distribution is). Nouls carry only P(yes). | | | | | | Calibration: across many predictions, outcomes given 0.8 happen ~80% of the time. It | | | describes groups, not any single answer; confidence 1.0 is not a guarantee. | | | | | | ## Three bands (draw the lines by stakes, then test on your data) | | | high act automatically | | | medium act with a confirmation, or gather more, or flag | | | low do not act: escalate to a person or a reasoning model, or ask | | | | | | ## Thresholds scale with risk | | | if a.confidence < 0.5: escalate() # genuinely unsure |

|  | elif a.choice == "read_only":  do_it()             # cheap to be wrong | 
|  | elif a.choice == "destroy": | 
|  | if a.confidence > 0.9:     confirm_then_do() | 
|  | else:                      ask_user() | 

| | | | | - Low choice confidence: no clear winner, OR several acceptable answers (harmless). | | | - Low score confidence: levels overlap, question measures >1 thing, or the state does | | | not say enough. | | | - If all you need is the best option, take the argmax; thresholds are for deciding | | | whether to ACT. If you have a statistical procedure in mind, use probabilities, not | | | the confidence summary. |

|  | - Aliases move (`jev-latest` -> new version). Thresholds tuned on one version: pin the | 
|  | versioned id (`jev-1.13.0`) with --model and migrate on your schedule. | 

| | """, "/confidence") | | | | | | GUIDE["patterns"] = ("Patterns that compose Jev into systems", r""" | | | 1. SPECULATIVE FAN-OUT. Put every question the workflow might need in one request, | | | including ones that matter only on some branches. Parallel, independent, near-zero | | | latency cost. 13 questions in one call measured 12x cheaper and 10x faster than 13 | | | calls, with identical answers. Code decides which answers count. | | | 2. CONFIDENCE-GATED ROUTING. The answer says WHAT; confidence says WHETHER to act. Gate | | | each action at its own threshold by consequence. (jev guide confidence) | | | 3. COMPOSITE SCORING. Break a judgment into atomic scores, normalize by (levels-1), | | | weight in code. Weights are reviewable and tunable without re-inference. | | | 4. INTENT ROUTING. Cheap classifier in front of expensive handlers: deterministic code | | | for one intent, a specialist LLM for another, a human for the low-confidence rest. | | | 5. SELECT, DO NOT GENERATE. Find candidate spans/values with regex or a generative model, | | | then let Jev CHOOSE the right one. Extraction becomes a choice over enumerated options | | | with an explicit "not stated". | | | 6. RERANK / FILTER. Cheap retrieval (BM25, embeddings, grep) makes a shortlist; one Jev | | | question per query-candidate pair grades it. jev rank does this in one request per | | | chunk. Measured on a legal corpus: top-1 5% -> 18%, top-10 38% -> 62%. | | | 7. VERIFY AND ESCALATE. Check a claim against its source (supports / contradicts / says | | | nothing), an extraction against the document, a tool-call trace against its intent. | | | Low confidence goes to a human or a reasoning model. Cascade: small model extracts, | | | Jev verifies, big model only on the uncertain slice. | | | 8. GUARDRAIL. One request per message, in and out of an LLM: a battery of nouls (jailbreak? | | | harm? self-harm? instruction aimed at the model?) plus a severity score. Threshold in | | | code: pass / review / block / route. | | | 9. MAP-REDUCE. Thousands of items x same questions -> jev batch, then aggregate in code. | | | Probabilities become features for a classical model when you have labels. | | | 10. TWO-STAGE SELECTION. Rank everything cheaply, then re-judge the top K with fuller | | | evidence and let the second stage reject all of them. (skill suggestion cookbook: | | | wrong loads down by more than half.) | | | """, "/patterns") | | | | | | GUIDE["design"] = ("Designing a NEW integration (tool, cron job, pipeline)", r""" | | | Runtime use is jev yes\|pick\|rate\|ask\|rank\|batch. This topic is for the other job: you | | | are about to BUILD something that calls Jev on every run. Much of it is distilled from | | | TypeSafe's own agent skill (github.com/typesafe-ai/skills, MIT), which is a docs-oriented | | | skill for exactly this job; jev guide vendor fetches it live. | | | | | | ## Start from the behaviour, work back to the judgments | | | What will the tool show, select, change, or hand off? List the judgments that decision | | | needs. Keep known rules, calculations, exact lookups and execution in code. Preserve the | | | stack and scope already in place; add Jev only where semantic understanding helps. | | | | | | ## Shapes beyond "classify" | | | - Route AND fill arguments: one request selects the handler and its closed-set params. | | | - Select instead of generate: code finds candidate spans/values, Jev picks the intended | | | one. Check candidate COVERAGE: the model cannot choose a value you did not offer. | | | - Find and judge evidence: cheap retrieval, then per-candidate relevance, then select. | | | - Judgments as reusable data: score dimensions once; weights, thresholds, rankings and | | | views change in code with no re-inference while the evidence and questions stand. | | | - Verify and escalate: check a claim/field/tool-call against its evidence; send the | | | uncertain slice to a person or a reasoning model. | | | - Respond to changing state: keep INFERRED state distinct from OBSERVED facts, and check | | | freshness before applying an old answer to a changed situation. | | | | | | ## Atomic is not "one fact per question" | | | A bounded action selection or a contextual interpretation is a valid single judgment. | | | Split independently useful dimensions; do not split a relationship you need judged whole. | | | | | | ## Policy stays explicit in code | | | Weighted sums suit compensating preferences. An "any serious violation blocks" rule needs | | | separate conditions, not a weight. Ignore uncertainty on branches you do not take. Several | | | acceptable alternatives can spread probability; low confidence on a harmless preference | | | choice is not a reason to stop. Typed output guarantees the INTERFACE, not truth: | | | validate on your own data before trusting it unattended. | | | | | | ## Ship checklist | | | 1. Questions + thresholds in ONE constant block at the top of the file. Agents write | | | mediocre questions; expect a human to edit them, so make them easy to find. | | | 2. --model jev-1.13.0 pinned once thresholds are tuned. jev-latest moves. | | | 3. A handful of known cases run before the first unattended run; when one fails, separate | | | missing evidence / model error / code error / service failure before changing anything. | | | 4. label= set on the client so jev usage attributes the spend. | | | 5. Long lists: never items[i]; key the object or embed the item (see pitfalls row 10). | | | 6. Never embed the key in a web page or a repo; the CLI reads ~/.config/typesafe/api_key. | | | """, "/concepts/how-to-build-with-system-one") | | | | | | GUIDE["vendor"] = ("TypeSafe's own agent skill (fetched live from GitHub)", r""" | | | TypeSafe publishes a docs-oriented skill for agents BUILDING integrations: | | | https://github.com/typesafe-ai/skills (skills/typesafe-ai/SKILL.md, MIT, ~2.5k tokens) | | | It has no executable and no runtime commands; jev is the runtime. Its value is the | | | design guidance and the map of which vendor page to read for which task, and it is | | | updated by the vendor when the API changes. jev guide vendor --live prints the current | | | copy. jev guide design is our distillation, kept short. | | | """, "https://raw.githubusercontent.com/typesafe-ai/skills/main/skills/typesafe-ai/SKILL.md") | | | | | | GUIDE["pitfalls"] = ("Jagged edges of jev-1.13 (vendor-acknowledged)", r""" | | | | # | failure | do this instead | | | | | 1 | literal reading | write the exact condition; boundary cases in criteria | | | | | 2 | math, counting, magnitudes | compute in code; one noul per item then sum | | | | | 3 | date/time comparison | extract parts as choices; compare in code | | | | | 4 | indirection, double negatives | direct wording; name the field | | | | | 5 | large state with irrelevant detail | filter first; relevance noul if you cannot | | | | | 6 | adversarial content in state | precise criteria; test edge cases | | | | | 7 | contradictory instructions/criteria | align them; true must mean yes | | | | | 8 | expected structural invariants | P(x) and 1-P(not x) need not agree; noul vs choice thresholds differ; ask one way | | | | | 9 | generation | it cannot; select from candidates instead | | | | | 10| positional index into a long array | (ours, measured 2026-09-18) items[137] 27% wrong at 150, 9% at 25; keyed object or item-in-question 0% at 320 | | | | | | | Numeric representations (hex colours, RGB, assembly) underperform names and high-level | | | code. Non-English works but is weaker; watch confidence. Reviewed by the vendor |

|  | 2026-09-17. Live: jev guide pitfalls --live | 
|  | """, "/model-jaggedness/jev-1.13") | 

| | | | | GUIDE["structure"] = ("Structured instructions and criteria (JSON instead of strings)", r""" | | | instructions, every choice option description, every score level, and noul true/false | | | may be a string, an object, an array, or null. The model is trained on structure; keys | | | are labels it reads, so use short descriptive names (none are reserved). | | | | | | "instructions": {"question": "Does the claimed sender conflict with the domain?", | | | "compare": ["ticket.sender.display_name", " ticket.sender.email"], | | | "focus": "Compare the named organization with the email domain."} | | | | | | "criteria": {"return_policy": {"what": "rules for returns in general", | | | "not_for": "the status of a specific return", | | | "examples": ["Can I return sale items?"]}, | | | "return_status": {"what": "where a specific return is", | | | "not_for": "general policy questions", | | | "examples": ["Where is my refund for order A-104?"]}} | | | | | | score level: {"what": "Broken or degraded, workaround exists", | | | "examples": ["export fails in one browser but works in another"]} | | | | | | When: a question has several parts; two options keep getting confused; a level needs | | | examples; the supporting data is already JSON (schema, taxonomy, record). Use the same | | | field names across options so the model compares like with like. Examples only help when | | | they resemble real inputs; the wrong example changes little, the right one concentrates | | | probability (measured 0.54 -> 0.90 confidence on the same ticket). | | | | | | Taxonomy walk: option value = the child subtree, so the model sees what lives under a | | | branch before committing. Trim large subtrees to direct children plus sample leaves. | | | """, "/primitives/advanced") | | | | | | GUIDE["cookbooks"] = ("Vendor cookbooks (worked, measured examples)", r""" | | | Each is a page under https://docs.typesafe.ai/cookbooks/<slug>.md | | | parallel_questions 13 questions, one call: 12.2x cheaper, 10x faster, same answers | | | skill_suggestion rank 182 agent skills, re-judge top 3, may reject all; wrong loads halved | | | rerank_typesafe BM25 shortlist -> one question per pair; top-1 5% -> 18% | | | semantic_find score 218 line ids vs a query in ONE choice; noul: is there an answer at all | | | classifying_rag_passages per passage: relevant? usable? contradicts premise? instructs the model? | | | citation_check supports / contradicts / says nothing; confidence flags for review | | | llm_guardrails noul battery + severity score on every LLM input and output | | | function_calling NL request -> function name + closed-set args, confidence-aware | | | pre_parsed_value_extraction regex finds candidates, Jev selects the intended span | | | date_extraction_cookbook month/day/year as choices with "not stated"; code assembles and compares | | | autoformat recover Markdown structure: line-join nouls, then block-type choices | | | hierarchical_classification beam search over choice probabilities through deep taxonomies | | | entity_alignment one 3-level score decides merge / leave / send to curator | | | sde_cascade mini extracts -> Jev verifies -> reasoning model only where needed | | | consistency_noul_cookbook 14 nouls x 15 runs: std dev 0.0102, below every LLM; route 0.3-0.7 to review | | | consistency_choice_cookbook add an explicit uncertain outcome to moderation decisions | | | classification_using_confidence 75 industry groups; low confidence -> report the parent division | | | autoresearch_feature_discovery propose questions, turn text into features, train CatBoost on them | | | Read one with: jev docs --grep <slug> then fetch the .md URL. | | | """, "/cookbooks/parallel_questions") | | | | | | GUIDE["api"] = ("HTTP API, limits, pricing", r""" | | | POST https://api.typesafe.ai/v1/systemone Authorization: Bearer <key> |

|  | body   {"state": str\|obj\|arr, "model": "jev-latest", "questions": {id: Question}} | 
|  | answer {"model": "jev-1.13.0", "answers": {id: Answer}, "usage": {"input_tokens", "output_tokens"}} | 
|  | GET  /v1/models   lists aliases (jev-latest, jev-preview); versioned ids like jev-1.13.0 also accepted | 

| | | | | Errors: 401 bad key · 422 malformed request (body names the field) · 429 rate limit · | | | 529 overloaded. jev retries 429/529/5xx with backoff and honours retry-after. | | | Limits (jev-1.13, adjust without notice): 250k tokens/s, 1200 req/min; 64k tokens per | | | request, 32k for state + longest question. MEASURED 2026-09-20: 107,500 chars of English | | | state = 32,388 tokens went through; ~110k chars returned 400 max_tokens_exceeded, an error | | | code the docs do not list. English runs 3.32 chars/token, so budget on tokens, not on the | | | 4-chars-per-token rule of thumb. Text only. | | | Price: $42 per billion input tokens ($0.042/Mtok). Output free. jev usage shows spend, | | | jev cost estimates a job, jev config set credits tracks a balance (no balance endpoint | | | exists; see jev guide usage). | | | Model is not fine-tuned per customer; you shape it through state, instructions, criteria. | | | Not trained on customer traffic. jev schema prints a request skeleton. | | | Full JSON path: jev ask -f request.json (or jev ask with the JSON body on stdin) | | | """, "/api") | | | | | | GUIDE["library"] = ("Using jev from Python (tools, Cues, pipelines)", r""" | | | import sys; sys.path.insert(0, "/path/to/dir/containing/jev.py") | | | from jev import JevClient, noul, choice, score, JevError | | | | | | QUESTIONS = { # keep questions + thresholds together, reviewable | | | "is_receipt": noul("Is email.body a purchase receipt or order confirmation?"), |

|  | "vendor": choice("Which vendor sent `email`?", {"amazon": None, "apple": None, "other": None}), | 
|  | "urgency": score("How urgent is `email.body`?", ["None", "This week", "Today"]), | 

| | } | | | THRESHOLDS = {"is_receipt": 0.7, "vendor_conf": 0.6} | | | | | | client = JevClient(label="mail_triage") # label shows up in jev usage |

|  | r = client.ask({"email": {"subject": s, "body": b}}, QUESTIONS) | 
|  | a = r["answers"] | 
|  | if a["is_receipt"]["noul"] >= THRESHOLDS["is_receipt"]: ... | 
|  | if a["vendor"]["confidence"] < THRESHOLDS["vendor_conf"]: escalate() | 

| | | | | - ask() raises JevError (exit_code attr: 2 usage, 3 auth, 4 api, 5 network). | | | - Many items: a ThreadPoolExecutor over client.ask (8 workers is safe), or shell out | | | to jev batch. Every call appends one line to ~/.config/typesafe/usage.jsonl. | | | - Official SDKs exist (pip install typesafe-sdk, npm i @typesafe-ai/sdk) with typed | | | answers and retries; this module is the zero-dependency path that works from any agent. | | | - Pin --model jev-1.13.0 in a pipeline whose thresholds you tuned. | | | """, "/sdk/python") | | | | | | GUIDE["usage"] = ("Usage, cost, credits and rate limits", r""" | | | Every request appends one row to ~/.config/typesafe/usage.jsonl: ts, cmd (the label), agent | | | ($JEV_AGENT), q (questions), in/out tokens, ms, model, rid (x-typesafe-request-id), | | | attempts when retried, or err on failure. Nothing secret is in it. record=False on | | | JevClient skips it. | | | | | | jev usage windows (today/yesterday/7d/30d/month/all), latency p50/p95, | | | peak req/min and tok/s vs the published limits, errors, forecast, | | | credits countdown, budget, by command, by agent (names resolved) | | | jev usage --by day also hour, cmd, agent, model --days N | | | jev usage --tail 20 recent requests with request ids (quote a rid to the vendor) |

|  | jev usage --since 2026-09-01 --label rank --agent 8ba5 --json | 
|  | jev cost --state-file f --questions 12 [--exact]      tokens + usd before you run it | 

| | jev cost -s "…" --items 5000 a rank-shaped job over 5,000 items | | | jev config set credits 50 balance read off the console; usage counts down from it | | | jev config set budget 5 monthly USD budget; usage shows % used and a projection | | | jev config set price 0.042 only if the vendor changes list price | | | | | | ## What the API does NOT give you (measured 2026-09-19) | | | No balance, usage, or quota endpoint exists (/v1/usage, /v1/balance, /v1/account ... all | | | 404) and responses carry no rate-limit headers, only x-typesafe-request-id. "Tokens | | | remaining" therefore means one of three things here: | | | 1. credits remaining: a balance YOU snapshot from https://console.typesafe.ai via | | | jev config set credits, minus spend recorded since. Drift = calls made from | | | another machine or key. Re-snapshot when you look at the console. | | | 2. request budget: 64k tokens per request, 32k for state + longest question. jev cost | | | says whether a state fits and how many items pack per request. | | | 3. rate headroom: 1,200 req/min and 250k tok/s (adjust without notice). jev usage | | | shows your measured peaks as a percentage of both. | | | Price: $42 per billion input tokens ($0.042/Mtok). Output is free. A 1k-token item costs | | | $0.00004; a thousand of them cost four cents. | | | """, "/models") | | | | | | GUIDE["exit-codes"] = ("Exit codes", r""" | | | 0 success / yes / confident | | | 1 yes: no or uncertain · pick/ rate: confidence below --min-confidence | | | 2 usage error (bad flags, empty state, malformed question) | | | 3 auth error (no key, key rejected) | | | 4 API error after retries (429/529/5xx exhausted, 422) · batch: some rows failed | | | 5 network error after retries | | | Errors print one line to stderr prefixed jev:; with --json the line is JSON. | | | """, None) | | | | | | GUIDE["install"] = ("Where jev lives and how it is maintained", r""" | | | Single file jev.py, anywhere on disk; symlink it onto PATH: ln -s $PWD/jev.py ~/.local/bin/jev | | | Requires Python 3.10+, standard library only | | | Key jev auth set <key> -> ~/.config/typesafe/api_key (0600), or export TYPESAFE_API_KEY | | | Keys: https://console.typesafe.ai/keys | | | Usage ledger ~/.config/typesafe/usage.jsonl (jev usage) | | | Config ~/.config/typesafe/config.json (jev config: credits, budget, price; no secrets) | | | Skill copy the jev/ skill folder (SKILL.md) into your agent's skills dir: | | | Claude Code ~/.claude/skills/jev/ | | | Codex ~/.agents/skills/jev/ (documented user path; ~/.codex/skills/ also read) | | | OpenCode ~/.config/opencode/skills/jev/ | | | One canonical copy, symlinked into each, is easiest to keep current. | | | Attribution export JEV_AGENT=<name> so jev usage --by agent can tell your agents apart | | | Health jev doctor | | | Vendor docs https://docs.typesafe.ai (Markdown at any page path + .md; index at /llms.txt) | | | Vendor skill github.com/typesafe-ai/skills is a docs-orientation skill for BUILDING integrations, | | | with no executable. jev guide vendor fetches it live. Installing both puts two skills | | | on the same triggers; pick one. | | | """, None) | | | | | | EXAMPLES: dict[str, tuple[str, str]] = {} | | | | | | EXAMPLES["gutcheck"] = ("One calibrated yes/no, usable in an if", r""" | | | if jev yes 'Does text describe a package delivery?' --field text="$DESC" --threshold 0.7; then | | | echo delivery | | | fi | | | cat draft.md | jev yes "Does the text contain an em dash character or the phrase 'you're absolutely right'?" | | | # (that one is a lint, and grep does it better; use jev for judgments grep cannot make) | | | cat draft.md | jev yes "Does the text flatter the reader or agree without giving a reason?" --json | | | """) | | | | | | EXAMPLES["triage"] = ("Classify + speculative questions in one call", r""" | | | jev ask --field message=@ticket.txt --field open_orders='["A-104","A-118"]' \ | | | --choice team 'Which team should handle message?' \ | | | billing="Charges, invoices, refunds, subscriptions" \ | | | orders="Order status, delivery, cancellation, returns" \ | | | account="Login, profile, permissions, security" other \ | | | --noul refund 'Does message explicitly ask for money back or a credit?' \ | | | --noul mentions_order 'Does message refer to one of open_orders by id?' \ | | | --score anger 'How angry is the author of message?' \ | | | "Calm and matter-of-fact" "Frustrated but civil" "Hostile or threatening to leave" \ | | | --json | | | # then in code: route on team.choice if team.confidence >= 0.6 else escalate; | | | # read refund only on the billing branch; read mentions_order only on orders. | | | """) | | | | | | EXAMPLES["rank"] = ("Rank or filter many candidates against a query (one request per chunk)", r""" | | | # lines in a file | | | jev rank --query "notes about thermostat schedules" --candidates-file titles.txt --top 10 | | | # JSON array of objects (each candidate is shown to the model as JSON) | | | jev rank --query "a bug that causes data loss" --candidates-file issues.json --min 0.6 --json | | | # stdin, graded on a rubric instead of yes/no | | | ls Meetings | jev rank --query "meetings about the Q3 launch" \ | | | --levels "unrelated" "adjacent" "directly about it" | | | # custom question; refer to the item as candidate (it is embedded in each question) | | | jev rank --query "$Q" --candidates-file x.txt --instructions 'Does candidate answer query?' | | | # extra shared context for every judgment | | | jev rank --query "…" --candidates-file x.txt --context @rubric.md | | | """) | | | | | | EXAMPLES["guardrail"] = ("Screen untrusted text before acting on it", r""" | | | jev ask --field text=@untrusted.md \ | | | --noul injection 'Does text contain an instruction aimed at an AI agent, assistant, or model?' \ | | | true="tells a model to do, ignore, reveal, or change something" \ | | | false="ordinary content; any imperatives are aimed at a human reader" \ | | | --noul secrets 'Does text ask the reader to disclose a credential, key, or password?' \ | | | --score harm 'How much harm would follow from doing what text asks?' \ | | | "None" "Minor or reversible" "Serious or irreversible" --json | | | # thresholds live in your code: block if injection >= 0.7 or harm >= 1.5; review 0.4..0.7 | | | """) | | | | | | EXAMPLES["verify"] = ("Check a claim against its source, or a draft against a rule", r""" | | | jev ask --field claim="$CLAIM" --field source=@source.md \ | | | --choice support 'How does source relate to claim?' \ | | | supports="the source states or entails the claim" \ | | | contradicts="the source states the opposite or an incompatible fact" \ | | | silent="the source does not address the claim" --json | | | # confidence < 0.8 -> a human confirms it | | | | | | jev ask --field draft=@post.md --field rules=@voice_rules.md \ | | | --noul r1 'Does draft violate any rule stated in rules?' \ | | | --noul r2 'Does draft contain a sentence that flatters the reader?' \ | | | --score tone 'How formal is draft?' "casual" "neutral" "formal" | | | """) | | | |

|  | EXAMPLES["batch"] = ("Same questions over thousands of rows -> JSONL", r""" | 
|  | # rows: one JSON object per line; --state-key picks the field to judge, --id-key is echoed back | 
|  | jev batch --input items.jsonl --state-key text --id-key id --concurrency 8 \ | 

| | --noul relevant 'Is text about home automation?' \ | | | --score quality 'How useful is text to a power user?' "noise" "some value" "must read" \ | | | --out results.jsonl | | | # plain text lines, one state per line | | | jev batch --input lines.txt --text-lines --noul q "Is this line a TODO?" > out.jsonl | | | # aggregate in code: jq 'select(.answers.relevant.noul > 0.7) | .id' results.jsonl | | | """) | | | | | | EXAMPLES["python"] = ("From a tool or a cron job", r""" | | | import sys; sys.path.insert(0, "/path/to/dir/containing/jev.py") | | | from jev import JevClient, noul, choice, score | | | Q = {"delivery": noul("Does event.description describe a package being delivered?"), | | | "who": choice("Who is at the door in event.description?", |

|  | {"courier": None, "family": None, "stranger": None, "nobody": None})} | 
|  | r = JevClient(label="ring_watch").ask({"event": ev}, Q) | 
|  | if r["answers"]["delivery"]["noul"] > 0.8: notify() | 

| | """) | | | | | | # --------------------------------------------------------------------------- argparse | | | | | | SHORT_HELP = """jev: calibrated, typed decisions from TypeSafe's Jev model (~250 ms, $0.042/Mtok) | | | | | | jev yes "question?" -s TEXT P(yes); exit 0 yes / 1 no | | | jev pick "which?" a=desc b=desc other -s TEXT one option + probabilities + confidence | | | jev rate "how much?" "low" "mid" "high" -s TEXT position on your rubric + confidence |

|  | jev ask  --noul/--choice/--score … -s TEXT   many questions, one call (fan-out), --json | 
|  | jev rank --query Q --candidates-file F       rank/filter many items, chunked | 
|  | jev batch --input rows.jsonl --noul …        same questions over many rows -> JSONL | 

| | | | | jev guide the playbook (read once per session) jev guide --list | | | jev examples copy-paste recipes jev docs live vendor index | | | jev usage spend, tokens, latency, throughput, credits, budget (--by, --tail, --json) | | | jev cost estimate a job before running it jev config credits / budget / price | | | jev doctor · models · schema · auth set|status|clear · version | | | | | | State: -s/--state TEXT | --state-file F | --state-json J | --field k=v (v=@file) | stdin. | | | Questions reference fields with backticks; SINGLE-QUOTE them in a shell: 'Does message ask for a refund?' | | | """ | | | | | | |

|  | def add_state_args(p): | 
|  | g = p.add_argument_group("state") | 
|  | g.add_argument("-s", "--state", help="text to judge; @path reads a file; '-' reads stdin") | 
|  | g.add_argument("--state-file", help="file to judge (.json is parsed, else text)") | 
|  | g.add_argument("--state-json", help="inline JSON state") | 
|  | g.add_argument("--field", action="append", metavar="KEY=VALUE", help="build an object state; VALUE may be @file or JSON; repeatable; combines with --state as `text`") | 

| | | | | |

|  | def add_question_args(p): | 
|  | g = p.add_argument_group("questions (repeatable, any mix)") | 
|  | g.add_argument("--noul", dest="noul", action=QAction, nargs="+", metavar="ARG", help="ID INSTRUCTIONS [true=…] [false=…]") | 
|  | g.add_argument("--choice", dest="choice", action=QAction, nargs="+", metavar="ARG", help="ID INSTRUCTIONS OPTION[=desc] OPTION[=desc] …") | 
|  | g.add_argument("--score", dest="score", action=QAction, nargs="+", metavar="ARG", help="ID INSTRUCTIONS LEVEL LEVEL … (low to high, 2..10)") | 
|  | g.add_argument("--questions-file", "-q", help="JSON file: {id: question}") | 
|  | g.add_argument("--questions-json", help="inline JSON: {id: question}") | 

| | | | | |

|  | def add_common(p): | 
|  | p.add_argument("--model", default=None, help=f"model id or alias (default {DEFAULT_MODEL}; pin e.g. jev-1.13.0)") | 
|  | p.add_argument("--api-key", default=None, help="override key resolution") | 
|  | p.add_argument("--timeout", type=float, default=30.0) | 
|  | p.add_argument("--json", action="store_true", help="machine output (raw API response where applicable)") | 
|  | p.add_argument("--compact", action="store_true", help="single-line JSON") | 
|  | p.add_argument("-v", "--verbose", action="store_true") | 

| | | | | | | | def build_parser() -> argparse.ArgumentParser: | | | p = argparse.ArgumentParser(prog="jev", description=SHORT_HELP, formatter_class=argparse.RawDescriptionHelpFormatter, add_help=True) |

|  | p.add_argument("--version", action="version", version=f"jev {VERSION}") | 
|  | sub = p.add_subparsers(dest="cmd") | 

| | | | | a = sub.add_parser("ask", help="many questions, one call", description="Ask any mix of noul/choice/score questions about one state in ONE request (speculative fan-out). Also: jev ask -f request.json or a full JSON body on stdin.", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=EXAMPLES["triage"][1]) |

|  | add_state_args(a) | 
|  | add_question_args(a) | 
|  | a.add_argument("-f", "--request", help="full request JSON file (state+questions); '-' for stdin") | 
|  | add_common(a) | 
|  | a.set_defaults(fn=cmd_ask_entry) | 

| | |

|  | y = sub.add_parser("yes", help="one yes/no -> P(yes); exit 0/1", description="One noul question. Prints `<p> yes\|no\|uncertain`. Exit 0 when p >= --threshold (or >= HIGH of --band), else 1.") | 
|  | y.add_argument("instructions", help="the yes/no question; phrase so that high = yes") | 
|  | y.add_argument("--true", help="what a yes means (optional criteria)") | 
|  | y.add_argument("--false", help="what a no means (optional criteria)") | 
|  | y.add_argument("--threshold", type=float, default=0.5) | 
|  | y.add_argument("--band", type=float, nargs=2, metavar=("LOW", "HIGH"), help="p < LOW no · LOW..HIGH uncertain · >= HIGH yes") | 
|  | add_state_args(y) | 
|  | add_common(y) | 
|  | y.set_defaults(fn=cmd_yes) | 

| | | | | k = sub.add_parser("pick", help="one choice -> option + confidence", description="One choice question. Options as NAME or NAME=description (null description is fine when the name is clear). Exit 1 when confidence < --min-confidence.") |

|  | k.add_argument("instructions") | 
|  | k.add_argument("options", nargs="+", metavar="OPTION[=desc]") | 
|  | k.add_argument("--min-confidence", type=float, default=0.0) | 
|  | add_state_args(k) | 
|  | add_common(k) | 
|  | k.set_defaults(fn=cmd_pick) | 

| | | | | r = sub.add_parser("rate", help="one score -> position on rubric", description="One score question. Levels low to high, 2..10, each describing a situation. Exit 1 when confidence < --min-confidence.") |

|  | r.add_argument("instructions") | 
|  | r.add_argument("levels", nargs="+", metavar="LEVEL") | 
|  | r.add_argument("--min-confidence", type=float, default=0.0) | 
|  | add_state_args(r) | 
|  | add_common(r) | 
|  | r.set_defaults(fn=cmd_rate) | 

| | | | | rk = sub.add_parser("rank", help="rank/filter many candidates vs a query", description="Grades every candidate against --query with one question per candidate (the item embedded in its question), packed into as few requests as the token budget allows. Default: noul relevance. With --levels: a score rubric (normalized to 0..1 as p).", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=EXAMPLES["rank"][1]) |

|  | rk.add_argument("--query", required=True, help="what you are looking for (text, JSON, or @file)") | 
|  | rk.add_argument("--candidates-file", help="one candidate per line, or a .json array") | 
|  | rk.add_argument("candidates", nargs="*", help="candidates as arguments (or stdin)") | 

| | rk.add_argument("--instructions", help="the question, referring to candidate and query, e.g. 'Does candidate answer query?' (the item is embedded in each question)") |

|  | rk.add_argument("--levels", nargs="+", metavar="LEVEL", help="use a score rubric instead of yes/no") | 
|  | rk.add_argument("--context", help="extra shared state (text, JSON, or @file) available as `context`") | 
|  | rk.add_argument("--top", type=int, help="keep the best N") | 
|  | rk.add_argument("--min", type=float, help="keep p >= MIN") | 
|  | rk.add_argument("--chunk-size", type=int, default=250, help="max candidates per request (default 250; 320 measured at 0.4 s)") | 
|  | rk.add_argument("--chunk-chars", type=int, default=90_000, help="max candidate chars per request (default 90k)") | 
|  | rk.add_argument("--concurrency", type=int, default=6) | 
|  | rk.add_argument("--width", type=int, default=110, help="truncate candidate display") | 
|  | add_common(rk) | 
|  | rk.set_defaults(fn=cmd_rank) | 

| | | | | b = sub.add_parser("batch", help="same questions over many rows -> JSONL", description="One request per input row, in parallel. Rows: JSONL (object = state, or --state-key picks a field), or plain text lines (--text-lines). Output: JSONL with answers per row.", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=EXAMPLES["batch"][1]) |

|  | b.add_argument("--input", "-i", required=True, help="JSONL or text file; '-' for stdin") | 
|  | b.add_argument("--state-key", help="field of each JSON row to use as the state") | 
|  | b.add_argument("--id-key", default="id", help="field echoed back as `id` (default id)") | 
|  | b.add_argument("--text-lines", action="store_true", help="treat every line as a plain-text state") | 
|  | b.add_argument("--echo", action="store_true", help="include the state in each output row") | 
|  | b.add_argument("--out", "-o", help="write JSONL here instead of stdout") | 
|  | b.add_argument("--concurrency", type=int, default=8) | 
|  | add_question_args(b) | 
|  | add_common(b) | 
|  | b.set_defaults(fn=cmd_batch) | 

| | |

|  | m = sub.add_parser("models", help="list models/aliases") | 
|  | add_common(m) | 
|  | m.set_defaults(fn=cmd_models) | 

| | |

|  | au = sub.add_parser("auth", help="set \| status \| clear the API key") | 
|  | au.add_argument("action", choices=["set", "status", "clear"], nargs="?", default="status") | 
|  | au.add_argument("key", nargs="?") | 
|  | add_common(au) | 
|  | au.set_defaults(fn=cmd_auth) | 

| | | | | d = sub.add_parser("doctor", help="check key, connectivity, one round trip") |

|  | add_common(d) | 
|  | d.set_defaults(fn=cmd_doctor) | 

| | | | | u = sub.add_parser("usage", help="spend, tokens, latency, throughput, credits and budget from the local ledger", description="Every request appends a row to ~/.config/typesafe/usage.jsonl. Default prints the summary; --by breaks it down; --tail lists recent requests. Credits and budget come from jev config because the API exposes no balance.") |

|  | u.add_argument("--by", choices=["day", "hour", "cmd", "agent", "model"], help="breakdown table") | 
|  | u.add_argument("--days", type=int, default=14, help="days for --by day (default 14)") | 
|  | u.add_argument("--tail", type=int, metavar="N", help="last N requests with request ids") | 
|  | u.add_argument("--since", help="only rows at or after YYYY-MM-DD / ISO timestamp") | 
|  | u.add_argument("--label", help="only rows with this cmd/label") | 
|  | u.add_argument("--agent", help="only rows whose agent id contains this") | 
|  | add_common(u) | 
|  | u.set_defaults(fn=cmd_usage) | 

| | | | | cf = sub.add_parser("config", help="show | set | unset credits, budget, price", description="Values live in ~/.config/typesafe/config.json (no secrets). jev config set credits 50 after reading the balance off the console; jev usage then counts down.") | | | cf.add_argument("action", nargs="?", choices=["show", "set", "unset"], default="show") | | | cf.add_argument("key", nargs="?", help="credits | credits_as_of | budget | price") |

|  | cf.add_argument("value", nargs="?") | 
|  | cf.add_argument("--as-of", help="ISO timestamp the credits balance was read (with `set credits`)") | 
|  | add_common(cf) | 
|  | cf.set_defaults(fn=cmd_config) | 

| | | | | co = sub.add_parser("cost", help="estimate tokens and cost for a job before running it", description="Estimates from characters (4 per token) or measures with one tiny real call (--exact). --items N and --per-item model a rank/batch job.") |

|  | co.add_argument("--questions", type=int, default=1, help="questions per request (default 1)") | 
|  | co.add_argument("--question-chars", type=int, default=120, help="average characters per question incl. criteria (default 120)") | 
|  | co.add_argument("--items", type=int, help="number of items for a rank/batch-shaped job") | 
|  | co.add_argument("--per-item", action="store_true", help="each item is its own request with the full state (batch shape) rather than packed (rank shape)") | 
|  | co.add_argument("--exact", action="store_true", help="measure the state's real token count with one tiny call (costs a fraction of a cent)") | 
|  | add_state_args(co) | 
|  | add_common(co) | 
|  | co.set_defaults(fn=cmd_cost) | 

| | |

|  | s = sub.add_parser("schema", help="print a request skeleton") | 
|  | s.set_defaults(fn=cmd_schema) | 

| | |

|  | g = sub.add_parser("guide", help="the agent playbook; `guide <topic>`; `guide --list`") | 
|  | g.add_argument("topic", nargs="?") | 
|  | g.add_argument("--list", action="store_true") | 
|  | g.add_argument("--live", action="store_true", help="fetch the vendor's current page for this topic") | 
|  | g.set_defaults(fn=cmd_guide) | 

| | |

|  | e = sub.add_parser("examples", help="copy-paste recipes; `examples <name>` or `examples all`") | 
|  | e.add_argument("name", nargs="?") | 
|  | e.set_defaults(fn=cmd_examples) | 

| | |

|  | dc = sub.add_parser("docs", help="live vendor doc index (llms.txt); --grep to filter") | 
|  | dc.add_argument("--grep") | 
|  | dc.set_defaults(fn=cmd_docs) | 

| | |

|  | ver = sub.add_parser("version") | 
|  | ver.set_defaults(fn=lambda a: print(f"jev {VERSION}") or 0) | 

| | return p | | | | | | | | | def cmd_ask_entry(args) -> int: | | | if args.request: | | | raw = sys.stdin.read() if args.request == "-" else Path(args.request).expanduser().read_text() | | | try: | | | body = json.loads(raw) | | | except json.JSONDecodeError as e: | | | raise UsageError(f"request JSON invalid: {e}") | | | if not isinstance(body, dict) or "state" not in body or "questions" not in body: | | | raise UsageError("request JSON must have state and questions") |

|  | c = JevClient(args.api_key, args.model or body.get("model"), timeout=args.timeout, label="ask") | 
|  | resp = c.ask(body["state"], body["questions"]) | 
|  | emit(args, resp, c.last_ms) | 

| | return 0 | | | # a full JSON body on stdin (no flags) is also accepted | | | if not (args.state or args.state_file or args.state_json or args.field) and not getattr(args, "qlist", None) and not args.questions_file and not args.questions_json and not sys.stdin.isatty(): | | | raw = implicit_stdin() or "" | | | try: | | | body = json.loads(raw) | | | if isinstance(body, dict) and "state" in body and "questions" in body: |

|  | c = JevClient(args.api_key, args.model or body.get("model"), timeout=args.timeout, label="ask") | 
|  | resp = c.ask(body["state"], body["questions"]) | 
|  | emit(args, resp, c.last_ms) | 

| | return 0 | | | except json.JSONDecodeError: | | | pass |

|  | raise UsageError("stdin was not a request JSON; pass questions with --noul/--choice/--score and the state with --state or stdin") | 
|  | return cmd_ask(args) | 

| | | | | |

|  | def main(argv=None) -> int: | 
|  | parser = build_parser() | 
|  | args = parser.parse_args(argv) | 

| | if not args.cmd: | | | print(SHORT_HELP.rstrip()) | | | return 0 | | | if not hasattr(args, "json"): | | | args.json = False | | | if not hasattr(args, "compact"): | | | args.compact = False | | | if not hasattr(args, "verbose"): | | | args.verbose = False | | | try: | | | return int(args.fn(args) or 0) | | | except JevError as e: |

|  | if getattr(args, "json", False): | 
|  | eprint(json.dumps({"error": str(e), "exit_code": e.exit_code})) | 

| | else: | | | eprint(f"jev: {e}") | | | return e.exit_code | | | except KeyboardInterrupt: | | | return 130 | | | | | | |

|  | if __name__ == "__main__": | 
|  | sys.exit(main()) |
── more in #ai-agents 4 stories · sorted by recency
── more on @typesafe 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/jev-give-any-coding-…] indexed:0 read:94min 2026-09-19 ·