Let the model judge. Keep the decision in code.

JevLang is a Racket language for TypeSafe's Jev model. A policy asks typed questions, routes on the answers and drives actions. The compiler refuses one that acts on an unchecked answer, names an option that doesn't exist, or opens a door without a person's yes.

Racket 8.10 or later. Tests and replays run offline; live calls need a TypeSafe API key.

examples/ticket-router.rkt#lang jev
(choice department
  #:ask "Which team should handle this ticket?"
  [billing   "Payments, invoicing, refunds, payouts, card failures"]
  [technical "Bugs, outages, API errors, integration problems"]
  [sales     "Pricing, upgrades, quotas, new accounts"])

(noul refund-requested?
  #:ask "Is the customer asking for money back?"
  #:yes "Explicitly asks for a refund, credit, or chargeback"
  #:no  "No mention of getting money back")

(escalate-below
  [department  0.80 (escalate 'human-triage #:reason "unclear which team owns this")]
  [frustration 0.70 (escalate 'human-triage #:reason "unclear how upset they are")])

(route
  [(and (yes? refund-requested? 0.8)
        (most-likely frustration "Angry, threatening to leave"))
   (page 'retention-oncall #:reason "angry refund request")]
  [(is department 'billing)   (assign 'billing-queue)]
  [(is department 'technical) (assign 'engineering-oncall)]
  [(is department 'sales)     (assign 'sales-inbox)])
  • Every question in one request to Jev3 questions
  • Every answer the route reads has a confidence gateescalate-below
  • Every option and level it names existscompile time
$ raco jev replay examples/ticket-router.rkt examples/fixtures
PASS   ambiguous-team    escalate human-triage
PASS   angry-refund      page retention-oncall
PASS   calm-refund       assign billing-queue
PASS   plain-bug         assign engineering-oncall
PASS   upgrade-question  assign sales-inbox

5 fixtures, 0 failing
An excerpt of examples/ticket-router.rkt, and the real output of replaying it. These five fixtures hold synthetic answers.

How it works

Ask typed questions, route on the answers, replay every decision.

The whole file compiles to one request to Jev and one pure function from the answers to a decision. Every decision carries a trace that explains it afterwards.

(score frustration
  #:ask "How frustrated is the customer?"
  ("Calm and matter-of-fact"
   "Annoyed but polite"
   "Angry, threatening to leave"))

Declare

Questions with types

choice picks one option, score weighs ordered levels, noul answers yes or no. Jev returns a probability for each.

(noul spam? #:ask "Is this message spam?")

(route
  [(yes? spam? 0.9) (hold)]
  [else (assign 'inbox)])

Decide

Rules in plain code

Thresholds, gates and routes are Racket, not a prompt. Unsure answers escalate to a person before any route runs.

$ raco jev replay policy.rkt fixtures/
$ raco jev diff v1.rkt v2.rkt recorded/
$ raco jev tune policy.rkt labeled/

Check

Replay, diff and tune offline

Record real answers once, then replay them against every change. See which cases a new version moves, and sweep thresholds over labeled cases.

Compile-time checks

It won't compile until it's safe.

Each error points at the clause that broke the rule and names the fix. All three below are real compiler output from examples/broken/.

(route
  [(is department 'billing)
   (assign 'billing-queue)]
  [else (assign 'catch-all)])
is: `department' decides this clause but has no confidence gate
  add an entry to (escalate-below ...):
    [department 0.8 (escalate 'human-review)]
  or read (confidence-of department) in this same clause's test
  or declare it with #:ungated "why no gate is needed"

Gates

An answer with no gate

A route that reads an answer must say what happens when Jev isn't sure of it.

(score frustration #:ask "How upset?"
  ("Calm" "Annoyed" "Angry"))

[(at-least frustration "Furious") (page 'oncall)]
at-least: "Furious" is not a level of `frustration'
  levels: "Calm", "Annoyed", "Angry"
  at: "Furious"

Levels

A level that doesn't exist

Options and levels are checked against their declarations, so a typo fails the build instead of never matching.

(action unlock-door #:doc "Unlock a door"
  [door (one-of front back)] #:confirm)

[(yes? unlock? 0.9)
 (act unlock-door #:door 'front)]
act: `unlock-door' is declared #:confirm, so it can only run after a person approves it
  wrap it: (confirm (act unlock-door ...))

Confirmation

A door without a person

An action declared #:confirm only runs inside (confirm ...), checked again at dispatch.

Beyond routing

Then it runs for real.

The same policy can drive devices, APIs and agents. The safeguards are checked when it compiles, when it decides and again when an action is dispatched.

Typed actions

action, act and plan, with confirmation, minimum confidence, guards, cooldowns and dry runs.

Journals

Memory, SQLite or PostgreSQL. Idempotency, scheduling, and rollback through declared #:undo inverses.

Event loops

Events in, decisions out. Sessions ask a clarifying question and resume; budgets cap what a run may spend.

MCP

Serve a policy as MCP tools, dispatch actions to MCP tools, or gate an agent's tool calls as a Claude Code hook.

Webhooks

Signed webhooks in and out, and shadow policies that run beside the live one without acting.

Labels

Harvest labels from real outcomes, then tune and calibrate against them. Tools warn below 200 labels.

FAQ

Questions

What is Jev?

Jev is a model in TypeSafe's System One API. It turns natural language and application state into typed judgments with probabilities. JevLang is a language for writing policies against it; see docs.typesafe.ai.

Do I need an API key?

Only for live calls: raco jev run, record, a policy's evaluate, and the integrations that evaluate. Replay, diff, tune, check and all 543 tests run offline.

Which Racket?

Tested on Racket 9.3 and 8.10. Docs generation and the SQLite and PostgreSQL journals need the full distribution.

How much does a policy cost to run?

raco jev cost gives a rough estimate at about four characters per token; there is no public tokenizer. cost --from fits the estimate to calls you have recorded.

Clone it and replay a policy.

No key needed for this part. It installs the language and replays the ticket router against its fixtures.

shell
git clone https://github.com/TimMikeladze/JevLang && cd JevLang
raco pkg install --link --auto "$PWD"
raco jev replay examples/ticket-router.rkt examples/fixtures