$jevwiki.ai#an LLM wiki about Jev, written for agents rather than people

Agents: read the raw Markdown of this page, or start at llms.txt.

~/wiki/guides

Playbook for LLM agents building with Jev

[ mixed tier ][ guide ][ updated 2026-10-24 ][ confidence high ][ jev-1.14.0 ][ python sdk 0.7.5 ][ js sdk 0.7.0 ]#playbook · agent · integration · checklist · decision-table

TL;DR Jev answers narrow, typed questions about one state: a Noul returns one probability (noul); a Choice or Score returns its choice/score plus probabilities and confidence. It never generates text, does arithmetic, or picks its own next action. Decompose the job into atomic questions, filter the state to what they need, ask them all in one POST /v1/systemone, and branch in code on the values and on confidence. Everything deterministic stays in code. No TypeSafe key yet? Direct signup reopened on 2026-09-27, with no free credits for new signups; Step 4 also gives the gateway route. Links written [[dir/slug]] resolve to https://jevwiki.ai/raw/wiki/dir/slug.md. Longer than most guides on purpose: it is the one-stop entry page for coding agents.

Official skill. Building inside a codebase? Also give your coding agent TypeSafe's agent skill (Claude Code: claude plugin marketplace add typesafe-ai/skills, then claude plugin install typesafe@typesafe-ai; other agents: npx skills add typesafe-ai/skills --skill typesafe-ai). It points the agent at TypeSafe's live docs, which win any conflict; this wiki adds the contracts in one place, community evidence and the consult. Ask the user before installing anything into their project (The typesafe-ai agent skill and Claude Code plugin).

Step 0 — Is Jev the right tool?

Start from the behavior wanted: what will the app show, select, change, or hand off? (The typesafe-ai agent skill and Claude Code plugin). Place each decision below; #n = failure mode n of nine in Jev 1.13 jaggedness: known failure modes.

Task shape Verdict Why
Classify into known categories Jev (Choice questions) Known answer space; code consumes it (System One Models)
Detect whether a property holds Jev (Noul (yes/no) questions) The probability is the signal
Rate on a rubric describable level by level Jev (Score questions) Ordered, thresholdable, comparable
Route to a handler, queue, or model Jev + code Intent routing, Confidence-gated routing
Rank or shortlist on several dimensions Jev + code Composite scoring; weights live in code
Verify another model's output, citations, tool calls Jev "Universal Verification" (Use-case map by industry); Cookbook: Double-checking citations
Remember facts across calls (per customer, account, device) Jev gate + code store A Noul decides what to save; code adds saved items to later state (Cookbook: Simple memory system)
Select a value already in the text Jev, Choice over candidates Extraction becomes selection (Cookbook: Pre-parsed value extraction)
Score millions of records cheaply Jev $0.042/Mtok input, output free (Models, aliases, pricing, rate limits, context)
Write a reply, summary, explanation, or code LLM "jev-1.13 is not trained to generate text" (#9)
Decide its own next action in a loop LLM agent System One "does not generate code or choose its own next action" (How to build software with System One)
Power the coding agent writing this integration LLM Not a drop-in (Jev with coding agents: not a drop-in for the LLM behind Claude Code, Cursor, Copilot); install the skill instead
Multi-hop reasoning over inferred facts LLM, or decompose Indirection costs accuracy (#4)
Arithmetic, sums, counting, percentages Code "Jev is not a calculator" (#2)
Date ordering, durations, windows, weekdays Code Dates read as text (#3); extract parts, compare in code (Cookbook: Date extraction)
Exact lookups, regex matches, status checks, thresholds Code "Use code when you can" (How to build software with System One step 1)
Audio, video Pre-process to text Not supported (State: what you send Jev)
Images: what is shown, does it match, where is it Pre-process to text for production; Jev images or box only for prototypes Images (both versions) and box (Jev 1.14 only, per raw/docs/images.md) are experimental behind TypeSafe-Danger-Zone-Images: true; TypeSafe: "aren't yet up to our standards to call ready for production" (raw/docs/images.md; Images and assets (experimental), Box questions (experimental)). Tell the user it is experimental before proposing it
state over the 64k / 32k limits Filter or split Long context (TypeSafe-Danger-Zone-Long-Context: true, 262k) is experimental and "slower and less accurate" (Models, aliases, pricing, rate limits, context)

Also avoid: reconstructing an exact number by interpolating between Score levels; double negatives; a huge state of material the question does not need (#5). When a rule, embedding, trained classifier or chat model beats Jev: When not to use Jev: rules, embeddings, trained classifiers, small and frontier LLMs; measured comparisons against small, fast and local models: Head-to-head: Jev vs small, fast and local models; other vendors' decision models: Rival decision models: Clef, Liquid d1, GLiDE, Drex and other non-Jev System One models; reports where Jev was given the wrong job or options that did not fit: Failure reports: wrong role, or options that don't fit (all community tier). Non-English (incl. CJK) is accepted but less accurate (Models, aliases, pricing, rate limits, context).

Already on Zapier, n8n, LangChain, Spring AI, the Vercel AI SDK, Mastra, LiteLLM, BAML, DSPy, Cloudflare Workers AI, Netlify, Vercel, OpenRouter, Fly.io, OpenCode Zen or Pydantic AI? Read Framework integrations: Pydantic AI, LangChain, Spring AI, Vercel AI SDK, eve, LiteLLM, BAML, TanStack AI, DSPy, Langfuse and other framework-shipped Jev support (frameworks), Apps with a built-in Jev option (apps such as n8n, Langfuse, MLflow with a built-in Jev option) or Platforms and gateways: Zapier, Cloudflare, Netlify, Vercel AI Gateway, OpenRouter, Pydantic AI Gateway, Opper, Fly.io, OpenCode Zen and other hosted routes to Jev (hosted routes) before writing a client: the platform may already wrap Jev, with its own key, billing and deviations from TypeSafe's docs (community tier: vendor pages checked against the docs; TypeSafe's docs win). Community MCP servers, skills and connectors: Tools and integrations hub: MCP servers, plugins and connectors (hub); unofficial SDKs by language: Community SDKs and libraries.

Adding Jev to an agent as a gate or router? Wire it with Jev inside your agent harness: hooks, tools and fail-safes (which hook in Claude Code, Codex, Cursor, Pi or opencode; a complete gate in Python and TypeScript; deadlines and fail direction). Read the community evidence before you promise a better agent (community tier): whole-agent A/B tests mostly cut cost or context without raising task success (Head-to-head: Jev inside agents, routers and tool gates); the builds and their measured results (Builds: permission gates, approvals and model routers for agents); the safety-gate pattern, never the sole control (Patterns: agent internals, routing, gates, context and memory P03); existing gates, hooks and injection screens with their fail modes (Tools: guardrails for agents (tool-call gates, permission hooks, injection screens, rule checks)).

Step 1 — Decompose the job into questions

"Probably the most important concept" (How to build software with System One step 4).

  1. List the decisions. Each is a code rule or a judgment; control flow, rules, and side effects stay in code.
  2. Split each judgment until it tests one property you can name. "Rate this startup pitch" becomes market size, technical feasibility, differentiation, weighted in code (Primitives: Choice, Score, Noul).
  3. Pick a primitive (Choosing between Choice, Score, Noul): unordered options → Choice; described spectrum → Score; clean yes/no → Noul; a count → one Noul per item plus sum() in code; free text → candidates proposed in code, a Choice picks one.
  4. Ask each decision one way. Structural invariants are not guaranteed: a Noul and a yes/no Choice on the same text returned 0.22 vs probabilities["yes"] = 0.01; a question plus its negation summed to 1.19 (TypeSafe's jaggedness page until 2026-10-02). Within one Noul, "no" is 1 − noul; never swap in a differently worded question's result (such as a negation) for it, or reuse a Noul threshold on a Choice. Enforce identities, totals, mutual exclusion in code.
  5. Add speculative questions. Parallel, near-free in latency (Speculative fan-out); state each premise explicitly, ignore answers on branches not taken.

Step 2 — Shape the state

state is the single input every question in the request sees (State: what you send Jev).

Step 3 — Write instructions and criteria

Full guide: Writing instructions and criteria that Jev reads correctly. Key rules:

Step 4 — Call it

Access (signup state as of 2026-09-27, the last change on record).

Environment variables read by both SDKs (TYPESAFE_* environment variables across SDKs):

Variable Default Effect
TYPESAFE_API_KEY none, required Sent as Authorization: Bearer <key>
TYPESAFE_BASE_URL https://api.typesafe.ai API root
TYPESAFE_DEFAULT_MODEL jev-latest Used when a call omits model
TYPESAFE_LOG_LEVEL JS warn; Python unset debug logs request and response bodies, unredacted

GET /v1/models (client.models.list() in both SDKs) lists the model names and aliases your key may send (HTTP API: POST /v1/systemone and GET /v1/models). Since the 2026-10-24 capture both jev-latest (the SDK default) and jev-preview point to jev-1.14.0 (released 2026-10-14); jev-latest pointed to jev-1.13.0 until the 2026-10-21 capture, so unpinned code changed model with no deploy (Blog: Jev 1.14 (2026-10-14)). Both versions have the same price and default limits. Pin a versioned ID (jev-1.13.0 or jev-1.14.0; jev-1.13.0 keeps the pre-move model) if thresholds are tuned (direct API and SDKs; on a gateway, its own versioned ID where one exists); response model shows the version that answered (Models, aliases, pricing, rate limits, context).

Bind base URL, key and model ID together (one config entry per environment), and log the returned model on every call. Open replicas and "Jev-compatible" servers copy the wire shape; several accept jev-latest (some also jev-preview) and answer locally with no error, and the response model is whatever that server writes (inferred), so a pinned model ID alone does not prove you reached Jev. Changing only the base URL can send your TypeSafe key to another operator. Revalidate thresholds and confidence semantics per endpoint: most replicas compute confidence their own way (Open replicas and Jev-compatible servers, community tier). TypeSafe's captured docs mention no on-premises or self-hosted deployment (FAQ for agents and developers Q43).

curl

export TYPESAFE_API_KEY="..."   # from console.typesafe.ai

curl -X POST https://api.typesafe.ai/v1/systemone \
  -H "Authorization: Bearer $TYPESAFE_API_KEY" \
  -H "Content-Type: application/json" \
  -d @- <<'EOF'
{
  "state": {
    "ticket_message": "I was charged twice for order A-104. Please refund the duplicate.",
    "refund_policy": "Duplicate charges are eligible for a refund."
  },
  "model": "jev-latest",
  "questions": {
    "refund_requested": {
      "type": "noul",
      "instructions": "Does `ticket_message` request a refund?"
    },
    "department": {
      "type": "choice",
      "instructions": "Which team should handle `ticket_message`?",
      "criteria": {
        "billing": "Charges, invoices, refunds, subscriptions",
        "technical": "Bugs, outages, integrations",
        "other": "Anything else"
      }
    },
    "frustration": {
      "type": "score",
      "instructions": "How frustrated does the customer appear in `ticket_message`?",
      "criteria": [
        "Calm, just stating facts",
        "Frustrated but civil",
        "Very angry, strong language"
      ]
    }
  }
}
EOF

The response is {model, answers, usage}, with answers keyed by your question ids. TypeSafe's own example of a Score answer, from the API reference (your numbers will differ):

{
  "model": "jev-1.13.0",
  "answers": {
    "frustration": {
      "type": "score",
      "score": 1.05,
      "legend": { "0": "Calm", "1": "Frustrated", "2": "Very angry" },
      "probabilities": { "0": 0.0, "1": 0.95, "2": 0.05 },
      "confidence": 0.92
    }
  },
  "usage": { "input_tokens": 304, "output_tokens": 18 }
}

A Noul answer is {"type": "noul", "noul": 0.95}; a Choice answer carries choice, probabilities keyed by option name, and confidence (same reference). Score legend and probabilities keys are strings on the wire ("0", "1"); the Python SDK turns them into int keys (legend[0]); the JS SDK keeps the wire's keys and, for a fixed rubric, types them as "0", "1", ... (HTTP API: POST /v1/systemone and GET /v1/models, Python SDK responses, answers, usage, models).

Python — typesafe-sdk 0.7.x, sync (written for 0.7.4; 0.7.5 only adds default zstd compression underneath it)

# pip install typesafe-sdk    (import name: typesafe_sdk; requires Python >= 3.10)
from typesafe_sdk import Choice, Noul, Score, TypeSafeAPIError, TypeSafeClient

STATE = {
    "ticket_message": "I was charged twice for order A-104. Please refund the duplicate.",
    "refund_policy": "Duplicate charges are eligible for a refund.",
}

QUESTIONS = {
    "refund_requested": Noul(instructions="Does `ticket_message` request a refund?"),
    "department": Choice(
        instructions="Which team should handle `ticket_message`?",
        criteria={
            "billing": "Charges, invoices, refunds, subscriptions",
            "technical": "Bugs, outages, integrations",
            "other": "Anything else",
        },
    ),
    "frustration": Score(
        instructions="How frustrated does the customer appear in `ticket_message`?",
        criteria=[
            "Calm, just stating facts",
            "Frustrated but civil",
            "Very angry, strong language",
        ],
    ),
}

try:
    with TypeSafeClient() as client:  # reads TYPESAFE_API_KEY; defaults to jev-latest
        response = client.system_one(state=STATE, questions=QUESTIONS, model="jev-1.13.0")
except TypeSafeAPIError as error:  # HTTP error after the SDK's retries
    print(f"API error {error.status} (request {error.request_id}):", error.body)
    raise

print(response.model, response.usage.input_tokens)
print(response.answers["refund_requested"].noul)
print(response.answers["department"].choice, response.answers["department"].confidence)
print(response.answers["frustration"].score, response.answers["frustration"].legend)

Python — async

import asyncio

from typesafe_sdk import AsyncTypeSafeClient, Noul


async def main() -> None:
    async with AsyncTypeSafeClient() as client:  # await client.aclose() if not using `async with`
        response = await client.system_one(
            state="Help! My payouts have been failing for 3 days.",
            questions={"is_urgent": Noul(instructions="Does this convey urgency?")},
        )
        print(response.answers["is_urgent"].noul)


asyncio.run(main())

Build one client per process and reuse it (Python SDK: install, clients, system_one()).

TypeScript — @typesafe-ai/sdk 0.7.0

// npm install @typesafe-ai/sdk   (Node >= 22 since 0.7.0; server-side only unless you opt into
// dangerouslyAllowBrowser, which exposes your key). Uses top-level await, so run it as an
// ES module (.mts, or "type": "module" in package.json).
import { APIError, choice, noul, score, TypeSafeClient } from "@typesafe-ai/sdk";

const client = new TypeSafeClient(); // reads TYPESAFE_API_KEY, TYPESAFE_BASE_URL, TYPESAFE_DEFAULT_MODEL

try {
  const { answers, usage, model } = await client.systemOne({
    state: {
      ticketMessage: "I was charged twice for order A-104. Please refund the duplicate.",
      refundPolicy: "Duplicate charges are eligible for a refund.",
    },
    model: "jev-1.13.0",
    questions: {
      refundRequested: noul("Does `ticketMessage` request a refund?"),
      department: choice("Which team should handle `ticketMessage`?", {
        billing: "Charges, invoices, refunds, subscriptions",
        technical: "Bugs, outages, integrations",
        other: "Anything else",
      }),
      frustration: score("How frustrated does the customer appear in `ticketMessage`?", [
        "Calm, just stating facts",
        "Frustrated but civil",
        "Very angry, strong language",
      ]),
    },
  });

  console.log(model, usage.input_tokens);
  console.log(answers.refundRequested.noul);
  console.log(answers.department.choice, answers.department.confidence);
  console.log(answers.frustration.score, answers.frustration.legend);
} catch (err) {
  if (err instanceof APIError) {
    console.error(`API error ${err.status} (request ${err.requestId ?? "unknown"}):`, err.body);
  } else {
    throw err;
  }
}

Answer types are inferred from the questions: answers.department.choice narrows to "billing" | "technical" | "other" (JavaScript/TypeScript SDK quick contract: install, client, choice/score/noul, answers, errors (the page a builder reads)). Since 0.7.0, usage.input_tokens and usage.output_tokens are typed number | null ("null when the API did not report it"); guard before summing them for cost. 0.7.0 also "stop[s] exporting request payload types": SystemOneRequestPayload is gone from src/types.ts, so code that imported it no longer compiles (raw/github/typesafe-sdk-js/src/types.ts at b844cd1). Both SDKs now zstd-compress bodies of 1 KiB or more by default; against a gateway or replica that rejects them, pass compression: false / compression=False (Running Jev in production: versioning, caching, retries, monitoring and fallbacks §4).

Step 5 — Use the answers

Type Read Notes
Choice choice, probabilities, confidence probabilities maps every option to 0–1, summing to ~1
Score score, legend, probabilities, confidence score may fall between levels; normalize score / (len(criteria) - 1) before combining rubrics of different lengths. Need one discrete level? Take the most probable level from probabilities (inferred): rounding score can land on a level neither peak supports (0.5 on level 0 and 0.5 on level 2 rounds to 1)
Noul noul P(yes). No confidence field. 0.5 = equally likely, not "medium"

Python: response.answers[...] is the mixed map; response.nouls / response.choices / response.scores are per-type views (Python SDK responses, answers, usage, models).

Confidence gating. Check the confidence floor before branching, then size each action's threshold to the cost of being wrong (Confidence-gated routing). Examples from the sources, not defaults:

Tune on your labelled data (Testing and evaluating a Jev workflow); keep questions and thresholds in one reviewable file (The typesafe-ai agent skill and Claude Code plugin). Which signal gates best is per task: the docs leave room for other measures computed from probabilities (Confidence vs probability), and builders found cases where a stock confidence cut misfired (Failure reports: confidence misread and calibration, community tier).

Failure check, mandatory for a consequential gate (one that blocks, pays, deletes or sends): before building it, read Failure reports: confidence misread and calibration (on JevBench v1.4.2's sealed hard set, Jev's highest-confidence answers were right less than half the time: Eval boards: JevBench by version, Jevals.com, typed-decisions and other independent suites) and validate the cut on your own labels. Any other gate: check its threshold on your own labels before copying it.

Two cuts on a Noul gate. Noul has no confidence: act above a high cut, pass below a low one, and send the band between to review. The docs' starting point (since the 2026-10-24 capture) is 0.7 for yes and 0.3 for no, values between to a person, moved by the cost of a false yes or a missed yes, and "Avoid fitting thresholds closely to a handful of test cases" (Noul (yes/no) questions; raw/docs/primitives__noul.md). Cookbook: Guardrails for LLMs has the complete version (review_threshold and action_threshold per policy, in its routing code). To gate Nouls and Choices with the same code, the docs' confidence-style stand-in for a Noul is |2p − 1| (0 at noul = 0.5, 1 at 0 or 1; the Choice formula applied to a yes/no Choice) (Confidence vs probability).

Fan-out. One request for every question the decision tree could need: batching 13 questions about a 54k-character document was 12.2x cheaper and 10.0x faster than 13 single-question calls, answers unchanged (Cookbook: Parallel questions, Speculative fan-out).

Composite scoring. Normalize Scores to 0–1, weight in code; re-rank by changing a coefficient, not re-running inference. Weighted sums suit compensating preferences; an "any serious violation" rule needs separate conditions (Composite scoring).

State and question wording from the spam example in How to build software with System One; the weights are that page's.

from typesafe_sdk import Noul, TypeSafeClient

STATE = {
    "message": {
        "sender": {"display_name": "Acme Payroll", "email": "rewards@claim-bonus.example"},
        "subject": "Urgent: claim your employee bonus",
        "body": "You have been selected for a $1,000 bonus. Confirm your payroll password today to receive it.",
        "links": [{"text": "Claim bonus", "url": "http://claim-bonus.example/acme"}],
    }
}

QUESTIONS = {
    "requests_credentials": Noul(
        instructions="Does `message.body` ask the recipient to provide a password or other login credential?"
    ),
    "sender_identity_mismatch": Noul(
        instructions="Does the organization named in `message.sender.display_name` conflict with the domain in `message.sender.email`?"
    ),
    "offers_unexpected_reward": Noul(
        instructions="Does `message.body` claim the recipient received an unexpected prize, payment, or reward?"
    ),
}

with TypeSafeClient() as client:
    response = client.system_one(state=STATE, questions=QUESTIONS, model="jev-1.13.0")

answers = response.answers
spam_risk = (
    0.45 * answers["requests_credentials"].noul
    + 0.30 * answers["sender_identity_mismatch"].noul
    + 0.25 * answers["offers_unexpected_reward"].noul
)
print(f"spam_risk = {spam_risk:.2f}")  # thresholds on this live in code; tune them on labelled mail

Step 6 — Handle errors and limits

Status Retry? Action
400 No Since 2026-10-14 in the API reference: the body breaks a request rule (invalid UTF-8 or a BOM, more than 64 nesting levels, NaN/Infinity/1e400, a malformed compressed body), or max_tokens_exceeded (below). Images add their own 400 causes, including a missing images header (HTTP API: POST /v1/systemone and GET /v1/models)
403, 404 (SDK error classes; not in the API reference's status table) No Fix the key's access or TYPESAFE_BASE_URL
413, 415 No 413: body over 32 MiB; 415: unsupported Content-Encoding or two compression layers (raw/docs/api__errors.md)
401 No Check TYPESAFE_API_KEY and the Bearer prefix
422 No Body is {"detail": [{loc, msg, type, ...}]}; fix the field at loc
400 with {"detail":{"error_type":"max_tokens_exceeded"}} No Input over the context length (64k per request; 32k for state plus any one question). The status and max_tokens_exceeded are in TypeSafe's docs since 2026-10-14 ("With max_tokens_exceeded, the request is longer than the model's context length"; raw/docs/api__errors.md); the body shape is community-observed (unverified). Shorten or split state; a byte count is no safe proxy for tokens (Request mechanics: billing, limits, latency, calibration and stability, HTTP status codes, rate limits, retry semantics)
408 (in the SDKs' retry set), 429, 5xx (incl. 529 Overloaded) Yes Exponential backoff; honor retry-after-ms, then retry-after
Connection error / timeout Yes Retry; raise the client timeout if it recurs

Both SDKs already retry {408, 429, 500–599} (2 retries, 500 ms backoff doubling to a 5,000 ms cap, 25% jitter) with a 10 s per-attempt timeout; details in HTTP status codes, rate limits, retry semantics. Python also has a 30 s total retry budget per call (RetryPolicy.timeout = 30.0; None disables it), checked before each retry: a retry whose delay would reach the budget is not attempted and the last error is raised, but an attempt already running is not cut, so it is no wall-clock cap (Python SDK retries, exceptions, constants; for a hard deadline see Jev inside your agent harness: hooks, tools and fail-safes Step 3). JS has no total retry budget: its timeout is per attempt, so pass an AbortSignal (signal in the per-call options) for a wall-clock ceiling; it cancels pending retries too (JavaScript SDK error classes, RetryPolicy, RequestOptions). Log x-typesafe-request-id (response.request_id / err.requestId) on every failure.

Limits and cost (Models, aliases, pricing, rate limits, context): rate limits are "set per organization" and shown in the console (console.typesafe.ai/rate-limits); either breach (tokens or requests per second) returns 429. Until the 2026-10-14 docs refresh the docs published 100K tokens/second and 80 requests/second for everyone (since 2026-10-02; 40/s from 2026-09-30; previously 250,000 tokens/second and 1,200 requests/minute). Treat 429 as normal; do not hardcode a client budget. Request body at most 32 MiB. Price: $42 per Btok / $0.042 per Mtok, input tokens only; output free. No uptime SLA (Legal: MCA, DPA, privacy, data retention). Every response carries usage.input_tokens: meter it for cost, and log it against the 64k / 32k budgets to see how close a growing state is getting.

Many calls at once. Cap requests in flight in your own code and let the SDK's retries absorb occasional 429s. A bounded-concurrency sketch with the async client (the cap is yours to choose, not a TypeSafe number):

import asyncio

from typesafe_sdk import AsyncTypeSafeClient, Noul

MAX_IN_FLIGHT = 8  # your choice; lower it if 429s persist
QUESTIONS = {"is_urgent": Noul(instructions="Does this convey urgency?")}


async def classify_all(texts: list[str]) -> list[float]:
    gate = asyncio.Semaphore(MAX_IN_FLIGHT)
    async with AsyncTypeSafeClient() as client:

        async def one(text: str) -> float:
            async with gate:
                response = await client.system_one(state=text, questions=QUESTIONS, model="jev-1.13.0")
                return response.answers["is_urgent"].noul

        return await asyncio.gather(*(one(text) for text in texts))


print(asyncio.run(classify_all(["Help! My payouts have been failing for 3 days.", "Thanks, all sorted now."])))

Python errors subclass TypeSafeError; HTTP failures after retries raise TypeSafeAPIError (status, request_id), 429 raises TypeSafeRateLimitError (adds retry_after_ms), network failures raise TypeSafeAPIConnectionError / TypeSafeAPITimeoutError. Hierarchy and a complete catch block: Python SDK retries, exceptions, constants. JS: APIError (status, requestId, body), RateLimitError on 429 (adds retryAfterMs), APIConnectionError / APITimeoutError, APIUserAbortError when your signal fires (JavaScript SDK error classes, RetryPolicy, RequestOptions).

Step 7 — Test before shipping

Copy-paste checklist

Common mistakes

Mistake Symptom Fix
One broad question hiding several judgments Confident, wrong, unexplainable Decompose; the tool-call example finds the unit mismatch only as nine questions (How to build software with System One)
Acting on choice without confidence 0.61/0.35 treated like 1.00/0.00 Gate on confidence first (Confidence-gated routing); tune the cut per task (Failure reports: confidence misread and calibration)
.confidence on a Noul Attribute error / undefined Threshold noul, or compute the docs' stand-in from Step 5 (Confidence vs probability)
Treating noul = 0.5 as "medium" Mid-range read as moderate intensity Use a Score for magnitude (Choosing between Choice, Score, Noul)
Asking for a count, sum, or date comparison Errors grow with size One Noul per item plus sum(); extract date parts as Choices, compare in code (Jev 1.13 jaggedness: known failure modes)
Numeric Score levels ("0", "1", "2") Low confidence, mid-drift Describe a situation per level (Writing instructions and criteria that Jev reads correctly)
No other option on a Choice Mass lands on the least-wrong option Add a catch-all (Choosing between Choice, Score, Noul)
Whole record in state Accuracy drops; wrong answers hard to localize Filter in code first (State: what you send Jev)
Asking X and 1 - not_X, expecting agreement Probabilities summing to 1.19 Ask the one question you want (TypeSafe's jaggedness page until 2026-10-02)
Trusting one option order The first option wins more than it should Reorder the options in evals and check the answer holds (#8 since 2026-10-02)
Serial single-question calls ~12x cost, ~10x latency Batch into one request (Cookbook: Parallel questions)
Thresholds tuned against jev-latest Silent shift when the alias moves (it moved to jev-1.14.0 between the 2026-10-21 and 2026-10-24 captures) Pin the version you tuned on, log response.model (Models, aliases, pricing, rate limits, context)
Swapping only the base URL Key sent to another operator; a replica answers under Jev's model name Bind base URL, key and model ID together (Open replicas and Jev-compatible servers)
Retrying 401 or 422 Repeat failures, wasted quota Retry only 408/429/5xx (HTTP status codes, rate limits, retry semantics)
Expecting Score.criteria as an int-keyed dict TypeSafeError at build or validation Both SDKs take an ordered sequence (Python SDK: install, clients, system_one(), JavaScript/TypeScript SDK quick contract: install, client, choice/score/noul, answers, errors (the page a builder reads))
Trusting a cookbook's demo threshold Wrong in your domain "Treat cookbook thresholds and demo results as examples to evaluate" (The typesafe-ai agent skill and Claude Code plugin)

Where to look next

Closest recipe by task:

Sources