Typed questions
noul, choice and score ask a decision model for probabilities, not prose. Answer types follow from the questions, so a label that was never offered is a compile error. Questions and answers
import { choice, huncho, noul } from "huncho";import { jev } from "huncho/jev";
const route = huncho("support.route", { model: jev() }) .ask({ urgent: noul("Does this need a human within the hour?"), topic: choice("What is it about?", ["billing", "bug", "other"]), }) .when((a) => a.urgent.p, { enter: 0.8, exit: 0.6 }, "page") .when((a) => a.topic.is("billing", 0.7), "billing") .else("triage");
const decision = await route.decide(ticket, { key: ticket.id });decision.outcome; // "page" | "billing" | "triage"Typed questions
noul, choice and score ask a decision model for probabilities, not prose. Answer types follow from the questions, so a label that was never offered is a compile error. Questions and answers
Thresholds that do not flap
Clauses in order, enter and exit thresholds per outcome, a hold per key. Combining answers happens in your code; the model never combines anything. Policy
Journal, replay, calibrate
Every decision is a language-neutral record. Change a threshold and replay the journal with no model call; when you know what really happened, measure whether the probabilities meant anything. Journal and replay
Providers behind one seam
TypeSafe Jev direct, through OpenRouter, through Vercel AI Gateway, or anything with an evaluate function. Nothing above the Model seam knows which vendor answered. Zero runtime dependencies. Providers