What Is a Causal Decision Model [CDM]? Reverse-Engineering Jev, in TypeScript
Nobody outside TypeSafe knows how Jev works, but people have probed it. Here is what is published, what is observed, and what is guesswork, plus tested TypeScript for calling it safely.
![What Is a Causal Decision Model [CDM]? Reverse-Engineering Jev, in TypeScript](https://cdn.hashnode.com/uploads/covers/695e128ccec990f2aa7512b9/ba67a0e5-29bf-480f-997b-743f4dd7f92d.png)
Put a secret code inside one question, then ask a different question what the code is, and Jev's probability for it comes back as 0.00. Put the same code in the shared state instead, and the probability jumps to between 0.90 and 0.92. That experiment, from Archer Hume's teardown (five repeats per condition, on jev-1.13.0), is the cleanest behavioral evidence I've seen for how TypeSafe's Jev handles a request: questions can't see each other, they all read one shared state, and the answers come out as probabilities with no text generated. Hume is careful to say this shows the behavior, not the mechanism.
This post works out what that implies. I'll separate what TypeSafe has published about Jev, what outsiders have observed by probing the API, and what is still guesswork about how it is built. The community calls the inferred design a causal decision model. Then I'll show how to call Jev from TypeScript with confidence gating and failure handling, and what I'd validate before trusting it in production.
A note on evidence: I typechecked every snippet below against the real @typesafe-ai/sdk package and ran the router tests against a local mock server. I have not run anything against the live Jev API, so every claim about model behavior comes from the sources linked at the end, not from my own experiments.
The problem: we keep asking text generators for decisions
Suppose you route support tickets. The usual pattern is to prompt a chat model, ask for JSON, parse the reply, validate it, and retry when parsing fails. The model also writes "I'm 91% confident" as text, and that number is just another generated string. Hume makes the point well: the probability of producing the words "91% confident" says nothing about a 91% chance of being right.
Jev, released on September 15, 2026, takes the other route. You send a piece of text or JSON (the state) and a set of typed questions, and you get back typed answers with probabilities. No prose comes back, and nothing needs parsing.
What TypeSafe actually shipped
These are the facts from TypeSafe's own docs and blog:
Jev is the first "System One" model. It reads text and returns typed answers, and it does not write replies, code or explanations.
The API is one endpoint,
POST /v1/systemone. Each question is one of three types: Choice (pick one of up to 255 options, with a probability for each), Score (a position on an ordered rubric of 2 to 10 levels) or Noul (the probability that a statement is true).All questions in a request run in parallel against the same state, and each is evaluated in isolation.
Pricing is $0.042 per million input tokens, output is free, with context limits of 64k per request and 32k per question.
Jev 1.14.0 arrived on October 14 with experimental, header-gated features: images, a
boxquestion type and longer context (262k). TypeSafe says plainly that these are not for production.TypeSafe says it built a new architecture, a parallel sampler and a training method called RLCD (Reinforcement Learning for Calibrated Decisions). It has not published details.
The speed claim is 70 to 500 ms end to end and "40x to 200x faster" than frontier models. Treat that as a vendor number: Wikipedia's summary notes TypeSafe itself says the test workflows were written by its own team and may be biased.
The causal decision model hypothesis, as an evidence ladder
TypeSafe won't say how Jev works, so people probed it. I find it easiest to sort the claims by how much evidence sits behind each one.
Published by TypeSafe. Jev outputs all probabilities in parallel, trained for calibrated decisions. The docs also say questions are evaluated independently against the same state.
Observed by Hume (from roughly 10,000 API calls, on jev-1.13.0):
Putting a secret code in a sibling question left its probability at 0.00, while putting it in the state raised it to 0.90 to 0.92 (five repeats per condition). Questions seem to be isolated from each other and share the state.
Server time barely moved up to about 100 questions, then rose steadily. That fits "process the state once, batch the questions."
Adding an irrelevant fifth option shifted the log-odds between two existing options from +0.38 to +0.11, with a drop in all ten randomized blocks. If each option had an independent fixed score, that ratio could not change, so the options appear to interact before the readout.
The token counts matched none of 192 public tokenizers he tested.
Inferred. The leading reconstruction, which people have started calling a causal decision model, is a causal transformer that encodes the state once, runs each question as a branch that can read the state but not its siblings, and reads probabilities directly from a prediction head instead of running a decode loop.
This diagram shows the community hypothesis, not TypeSafe's published design. Read it left to right: the shared state is processed once, each question forks off with its own text and allowed answers, and each fork ends in a probability readout instead of generated text. Your code, not the model, decides what happens next.
Two details deserve care. First, the "block-diagonal causal tree attention mask" you may see mentioned is not something anyone has established. Hume says outright that his experiments don't expose the exact mask, and that separate calls or another mechanism could produce the same behavior. A tree mask is one natural implementation, not a finding. Second, the sparse mixture-of-experts backbone is, in Hume's own words, the least certain part.
The Oumi team adds a complementary and more specific guess: a decision model is an ordinary LLM with reserved answer tokens scored at one readout position, and Jev is probably a Qwen 3.5 model (35B-A3B) with a swapped vocabulary. Their evidence is circumstantial: Jev names Qwen when asked about itself, its token counts fit a trimmed Qwen vocabulary, and its answers on sensitive questions and rare facts track Qwen's. Note the tension with Hume, who found no public tokenizer that matched, with Qwen closest. Oumi's vocabulary-swap theory is their explanation for that gap. Both are inference from outside a black box, and both tested Jev 1.13.
My reading: the shared-state, isolated-question, direct-readout picture is well supported by behavior. Everything more specific (exact mask, experts, base model) is a hypothesis, and none of it changes how you should use the API.
Confidence is arithmetic, not a second opinion
Each Choice and Score answer carries a confidence field. TypeSafe's docs define it as a statistic computed from the answer's own probabilities. For a Choice with n options and top probability p, it is (p - 1/n) / (1 - 1/n).
That has two consequences. A concentrated distribution can still be confidently wrong, so confidence tells you how peaked the answer is, not whether it is correct. And Noul answers carry no confidence field at all; the probability near 0.5 is the uncertainty signal.
Calibration, which is the actual claim about whether a 0.8 means 80%, is a property across many predictions. TypeSafe's own docs say it does not guarantee any single answer. Hume measured an expected calibration error of 0.0313 on a 1,200-item MMLU sample, and also found modular exponent problems where Jev was right 56% of the time at an average probability of 35%. The Oumi post cites an outside study ("Just Ask Jev") reporting good calibration when results are pooled but not benchmark by benchmark. So calibrate per task on your own data.
TypeScript: the official SDK and what its types promise
TypeSafe ships an official JavaScript and TypeScript SDK, @typesafe-ai/sdk. The docs say Node.js 22 or newer on the SDK page, while the quickstart says 20 or later for plain JavaScript, so use 22 to be safe. I installed version 0.7.0 from npm.
npm install @typesafe-ai/sdk
export TYPESAFE_API_KEY="your-key"
The client reads TYPESAFE_API_KEY, and config options I used or checked include defaultModel, baseURL, timeout (10 seconds per attempt by default) and retry. Request bodies of 1 KiB or more are zstd-compressed on Node 22.15+.
This sequence follows the documented API contract. Your service builds the questions, the SDK sends one request, and the response comes back with one answer per question. The last step happens in your code, which is where the architecture puts control flow.
Compile-time types versus runtime guarantees
The SDK infers answer types from the questions you write. If you declare options billing, technical and sales, then answers.department.choice is typed as that union, and a typo is a compile error.
That is compile-time safety about your declarations. The runtime claim is separate and comes from TypeSafe: because the answer space is declared in the request, the model can't return a label outside it. That is a design claim from the vendor, and a third-party explainer notes it is asserted from the schema design rather than measured. Either way, a valid label is not a correct label. Types stop malformed output. They do not stop wrong answers.
Example 1: the smallest useful call
This is adapted from TypeSafe's quickstart. It asks three questions about one ticket in a single request.
// Example 1: the smallest useful call. Adapted from TypeSafe's quickstart.
// Requires TYPESAFE_API_KEY in the environment.
import { choice, noul, score, TypeSafeClient } from "@typesafe-ai/sdk";
const ticket =
"Our API integration started returning 500 errors on every request about 20 minutes ago, " +
"and we can't process any customer orders until this is fixed.";
const client = new TypeSafeClient({ defaultModel: "jev-1.13.0" }); // pin a version, not an alias
const result = await client.systemOne({
state: ticket,
questions: {
department: choice("Which team should handle this", {
billing: "Payment or subscription issues",
technical: "Bugs or integration problems",
sales: "Pricing or account questions",
}),
frustration: score("How frustrated the customer appears", [
"Calm, just stating facts",
"Frustrated but civil",
"Very angry, strong language",
]),
isUrgent: noul("The message conveys urgency or time-sensitivity"),
},
});
// Inferred as "billing" | "technical" | "sales" at compile time.
console.log(result.answers.department.choice, result.answers.department.confidence);
console.log(result.answers.frustration.score);
console.log(result.answers.isUrgent.noul);
console.log("answered by", result.model);
Decisions worth noting:
I pinned
jev-1.13.0instead of the defaultjev-latest. The docs say aliases move when new releases ship, and TypeSafe planned to movejev-latestto 1.14.0 about a week after October 14. If you tuned thresholds on one version, pin it.Each question is one narrow judgment. TypeSafe's docs recommend decomposing broad questions and combining the results in code.
isUrgentis a Noul, so it returns only a probability.
The quickstart's own example output for this ticket is technical, 0.67 and 0.99. Those come from TypeSafe's docs, not from my run, and the docs note values can change between releases.
Example 2: a confidence-gated router
The first example is a demo. Real systems need a policy for uncertainty and a plan for failure. This router sends confident answers to a team automatically and everything else to a person.
// Example 2: a confidence-gated ticket router.
// The model makes judgments; this file owns every consequence.
import {
APIConnectionError,
APIError,
RateLimitError,
choice,
noul,
TypeSafeClient,
type TypeSafeClientConfig,
} from "@typesafe-ai/sdk";
export type Route =
| { kind: "auto"; team: "billing" | "technical" | "account"; urgent: boolean }
| { kind: "human"; reason: string };
export const CONFIDENCE_FLOOR = 0.5; // below this the model is reporting real uncertainty
export const URGENT_AT = 0.8;
// Pure policy: no network, so it is unit-testable without any model.
export function decide(input: {
team: string;
teamConfidence: number;
urgentProbability: number;
}): Route {
if (input.team === "other") return { kind: "human", reason: "no matching team" };
if (input.teamConfidence < CONFIDENCE_FLOOR) {
return { kind: "human", reason: `low confidence (${input.teamConfidence.toFixed(2)})` };
}
if (input.team !== "billing" && input.team !== "technical" && input.team !== "account") {
return { kind: "human", reason: `unexpected label ${input.team}` };
}
return { kind: "auto", team: input.team, urgent: input.urgentProbability >= URGENT_AT };
}
export function makeRouter(config: TypeSafeClientConfig = {}) {
const client = new TypeSafeClient({ defaultModel: "jev-1.13.0", ...config });
return async function route(message: string): Promise<Route> {
try {
const { answers } = await client.systemOne({
state: { message },
questions: {
team: choice("Which team should handle this message?", {
billing: "Charges, refunds, invoices, payouts",
technical: "Bugs, errors, integrations not working",
account: "Login, password, account access",
other: "Anything that fits none of the above",
}),
urgent: noul("The sender needs a response urgently"),
},
});
return decide({
team: answers.team.choice,
teamConfidence: answers.team.confidence,
urgentProbability: answers.urgent.noul,
});
} catch (err) {
// Fail toward a person, never toward an automatic action.
if (err instanceof RateLimitError) return { kind: "human", reason: "rate limited" };
if (err instanceof APIConnectionError) return { kind: "human", reason: "network error" };
if (err instanceof APIError) return { kind: "human", reason: `api error ${err.status}` };
throw err;
}
};
}
How it works, step by step:
decideis a pure function with no network calls. It is where your risk tolerance lives, so it is the part worth unit testing heavily.The 0.5 floor follows TypeSafe's suggested starting pattern for confidence. TypeSafe also says thresholds depend on stakes and your data, so treat 0.5 and 0.8 as placeholders to tune.
I added an explicit
otheroption, because the docs and community guides both warn that a Choice has to pick something.Every failure path returns
human. A rate limit or network error should never turn into an automatic action.
I tested it with node:test against a local server that returns the documented response shape:
npx tsx --test test/router.test.ts
# tests 7, pass 7, fail 0
That result checks my policy and error handling. It tells you nothing about how accurate Jev is.
Example 3: a probe you can run to test the isolation claim
The most interesting claim above is that sibling questions can't see each other. You can test it on your own account. This script is a proposed experiment, and I have not run it, because I had no API key.
// Example 3: a probe for the "isolated question branches" hypothesis.
// Inspired by the visibility probe in Archer Hume's teardown (archerhume.com).
// NOT RUN against the live API: it needs a TYPESAFE_API_KEY and I did not have one.
// Hypothesis: a secret in a SIBLING question is invisible, a secret in the STATE is visible.
import { choice, TypeSafeClient } from "@typesafe-ai/sdk";
const SECRET = "ZEBRA-7741";
const OPTIONS = { [SECRET]: null, "ALPHA-1020": null, "OMEGA-5588": null, none: null } as const;
const probe = choice("Which secret code is declared in this request?", { ...OPTIONS });
type Condition = "sibling" | "state" | "absent";
async function trial(client: TypeSafeClient, condition: Condition): Promise<number> {
const base = "The weather is nice today and the park is full of people.";
const { answers } = await client.systemOne({
state: condition === "state" ? `${base} The secret code is ${SECRET}.` : base,
questions: {
probe,
...(condition === "sibling"
? { decoy: choice(`The secret code is ${SECRET}. Is the weather nice?`, { yes: null, no: null }) }
: {}),
},
});
return answers.probe.probabilities[SECRET] ?? 0;
}
const client = new TypeSafeClient({ defaultModel: "jev-1.13.0" });
const REPEATS = 5;
for (const condition of ["sibling", "absent", "state"] as const) {
const runs: number[] = [];
for (let i = 0; i < REPEATS; i++) runs.push(await trial(client, condition));
const mean = runs.reduce((a, b) => a + b, 0) / runs.length;
console.log(condition.padEnd(8), "P(secret) mean", mean.toFixed(2), "runs", runs.join(", "));
}
If Hume's observation holds, sibling and absent should sit near zero and state should be high. A high sibling result would contradict the isolation claim for your model version. Run it on a pinned version, and repeat enough times that a single noisy request can't fool you.
Where I would use it
I'm limiting this to uses TypeSafe or an independent party has documented.
Support and intent routing. This is TypeSafe's flagship pattern, with intent routing and confidence-gated routing in its docs. Example 2 is my version of it. Status: demonstrated by TypeSafe.
Guardrails around an LLM app. TypeSafe's guardrails cookbook screens each message in and out with one request. The catch is its own docs: state is data, and adversarial text can steer the answer, so test injection cases. Status: demonstrated by TypeSafe, resistance to adversarial input unproven.
Agent harness decisions. LangChain's Jev integration uses Jev for model routing, and InfoQ reports a middleware that screens tool calls before they run, so the agent loop doesn't pay for a full LLM call per decision. Status: integrated by a third party.
Filtering retrieved passages. TypeSafe's RAG cookbook scores each retrieved passage and lets code decide which reach the answering model. A related re-ranking cookbook reports top-1 accuracy going from 5% to 18% on 40 legal queries, which is a small vendor-run sample. Status: vendor-demonstrated, not independently validated.
A note on the acronym: what "CDM" does and doesn't mean
You may see "CDM" attached to Jev. It is not a TypeSafe term: I went through TypeSafe's full documentation index and its blog listing, and the acronym isn't in either. The docs list Jev, its three primitives (Choice, Score, Noul), a box type, SDKs and cookbooks, and the blog lists the launch, a Series A, two essays and the Jev 1.14 post. The acronym does appear in two other places:
| Meaning | Who uses it | Status |
|---|---|---|
| Causal decision model | Community shorthand. TLDR Dev's summary of Hume's teardown describes Jev this way | A descriptive label for an inferred architecture. TypeSafe has not confirmed it, and I found no source that defines it as a formal paradigm |
| Constrained Decoding Model | ScaleDown, launched October 13, 2026 with a LangChain integration | A real product line, unrelated to TypeSafe |
The causal-decision-model idea traces to Hume's September 17 essay, and the Oumi post (October 19, by Jeremy Greer and Matthew Persons) builds on it. The second meaning is covered next.
ScaleDown's CDMs: same instinct, different design
On October 13, ScaleDown launched four small task models and called them Constrained Decoding Models: Summarize, Compress, Extract and Classify. Per LangChain's post, each model is trained for one task and only returns answers that task allows, such as a label from your list or spans copied from the input. ScaleDown reports 200x faster and 467x cheaper than frontier models on these narrow tasks, up to 1M tokens of input, and image input on some models.
Classify will feel familiar if you know Jev: it returns a label with probabilities. It also adds a one-sentence reason, which Jev deliberately doesn't produce. Pay attention to the critiques, though. One analysis argues that "constrained decoding" is an established technique and CDM is a product name rather than a new architecture, and that ScaleDown's own docs show one of the models (Summarize) does write text. A DEV Community review notes Classify always returns a winner and that no calibration numbers are published. LangChain's own social posts also call them "constrained decision models" in places, so expect naming drift.
So the two meanings of CDM overlap in goal but not in mechanism, and neither is TypeSafe's.
How the approaches compare
| Jev directly | Chat LLM API | Rules and regex | Schema-constrained LLM | ScaleDown CDMs | |
|---|---|---|---|---|---|
| Purpose | Typed decisions with probabilities | Open-ended generation | Deterministic checks | Structured output from a generator | Narrow tasks: classify, extract, summarize, compress |
| Output shape | Label, score or probability, fixed by your request | Free text | Whatever you code | Valid JSON, content still generated | Labels, spans, or short summaries |
| Calibrated probability | Yes, claimed and partly measured | No (text claims only) | Not applicable | Not by default | Classify returns scores; calibration unpublished |
| Latency and cost | 70 to 500 ms and $0.042 per Mtok input, vendor-reported | Seconds, token-priced | Microseconds | Same as the LLM | Vendor-reported 200x faster, 467x cheaper |
| Flexibility | Judgment over natural language | Highest | Lowest | High | Four fixed tasks |
| Main limits | Text only, hosted only, weak at math and counting | Cost, latency, parse risk | Brittle on language | Still generates, so slower | Closed benchmarks, naming and architecture claims disputed |
These approaches mostly complement each other. Jev's own docs say to keep arithmetic in code and use a generative model when you need text. A sensible stack uses rules where rules suffice, a decision model for fast semantic judgments, and an LLM for the generation and hard reasoning.
What I would verify before production
Treat every number in this post that came from a vendor as a hypothesis for your workload. Here is the checklist I would run:
Accuracy on your labels. Build a few hundred hand-labeled examples and measure. Public benchmarks don't transfer. Abstract-level findings from independent papers say Jev matches general LLMs on knowledge-style decisions but trails on multi-step calculation, and a stance-detection study found it behind GPT-5.6 and Claude Sonnet 5 on conversational stance.
Calibration per task. Bin predictions by probability and compare to observed accuracy, per question type and per task.
Option order. TypeSafe's known-weaknesses page says Choice answers can lean toward the first option. Shuffle options and check the answer stays stable. Hume saw one ticket classification move from roughly 0.84 to 0.89 up to 0.93 to 0.96 when he reversed the options.
Adversarial input. Inject instructions into the state and see whether answers move.
Version pinning. Pin
jev-1.13.0orjev-1.14.0and re-validate before moving.Latency under your load. Measure your own p95, not the headline range.
Data handling. Jev is hosted only, with no open weights. TypeSafe says it doesn't train on customer requests and offers zero data retention for enterprise customers; read the DPA.
Rate limits and retries. The SDK retries 429s by default. Decide what your service does when retries run out (Example 2 routes to a human).
Takeaways
The acronym is muddled, but the idea behind it holds up. A model that returns a probability distribution over answers you declared makes AI behave like a function your code can branch on, and that moves the hard part from parsing to policy.
My own conclusions, with the confidence each deserves:
Fairly sure: the shared-state, isolated-question, direct-readout behavior is real on 1.13, because TypeSafe's docs and Hume's probes agree.
Plausible: the architecture details and the Qwen base. They fit the evidence and nothing has confirmed them.
Unknown: how well it holds up on your data. That is the one thing no blog post, including this one, can tell you.
If you try it, start with Example 3 on a pinned model. It takes minutes and tells you whether the mental model in the diagram matches the model you'll be paying for. Then build the router, log every decision with its probabilities, and tune your thresholds from real traffic.
FAQ
What does CDM stand for in the context of Jev? Nothing official. TypeSafe doesn't use the term. "Causal decision model" is community shorthand for Jev's inferred architecture, and "Constrained Decoding Model" is ScaleDown's product category.
Is Jev an LLM? TypeSafe says it is not an LLM and that it generates no text. Outside analyses suggest a causal transformer with a probability readout, possibly built on an open-weight base, but TypeSafe hasn't confirmed this.
Does Jev have an official TypeScript SDK? Yes, @typesafe-ai/sdk, with answer types inferred from your questions. Community SDKs exist for several other languages.
Can I self-host Jev? No. It is a proprietary hosted model. Open-weight imitations exist, but they are different models.
References
TypeSafe (primary)
Teardowns (community, inference from a black box)
Archer Hume, Jev's Architecture Unmasked, Sept 17, 2026
Jeremy Greer and Matthew Persons, How to Build a Decision Model Like Jev, Oct 19, 2026
ScaleDown CDMs
LangChain: Building Long-Running Agents with ScaleDown, Oct 13, 2026
Other
Independent papers (abstract-level reading only): arXiv 2610.11978, arXiv 2610.11901, arXiv 2609.29429
Comments
Join the discussionNo comments yet. Be the first to comment.