Ship Typed Jev Decisions on Vercel, Cloudflare or OpenRouter — Test the Dialect Before You Pick the Door

Vercel AI Gateway's evaluation API calls Jev's Noul a boolean. Run one fixture set through Vercel, Cloudflare, OpenRouter and TypeSafe, then pin one wire shape.

Jev on Vercel AI Gateway, Cloudflare, OpenRouter and TypeSafe drawn as four doors fed by one fixture set, with the yes/no answer labelled noul at three doors and boolean or noul at Vercel
One model, four doors, two words for the same yes/no answer. The fixture set is the key you try in every lock.

Choice / Score / Noul — code owns side effects.

Six days after its Sep 15 release, TypeSafe’s Jev answers at six documented endpoints behind four doors, under four model-ID spellings, in two vocabularies for one yes/no question. Call Jev on Vercel AI Gateway through the AI SDK or the evaluation API and a Noul comes back as a boolean with a probability. Through TypeSafe, Vercel’s /typesafe path, Cloudflare or OpenRouter it is a noul. Your policy code reads one of those names and treats the other as missing.

This is the runbook for picking a door on evidence: one fixture set run through every door you might use, every answer normalized into one record, a conformance sheet for each door’s unsupported fields, experimental pins, retention, post-promo price, limits and record export, and one pinned wire shape so a single policy file decides the same way everywhere. After that, choosing a door is procurement. Before it, you are guessing at field names.

Sep 15–18: Jev reaches four doors in four days

TypeSafe’s own door is POST /v1/systemone: a state, a model and a map of typed questions keyed by IDs you choose, with answers returned under the same keys, per the API reference. Choice and Score answers carry a confidence; Noul answers carry only their probability. The models page lists jev-1.13.0 behind the jev-latest alias at $0.042 per million input tokens with output free; 250,000 tokens per second and 1,200 requests per minute, “adjusting dynamically”; a 64K budget per request and 32K for state plus the longest question; and ZDR for enterprise customers.

Vercel’s changelog put typesafe-ai/jev on AI Gateway on Sep 16, reached from the AI SDK through experimental_evaluate (AI SDK 7.0.105 onward) with primitives named Choice, Score and Boolean. The evaluation docs show the wire result, { type: 'boolean', probability: 0.99 } with camelCase usage, while the TypeSafe-compatible path under /typesafe keeps noul, snake_case usage and provider_metadata. The model page lists a Sep 15 release and, on Sep 21, a price of Free beside “Promotional pricing ends on September 25, 2026”. No Vercel page I read states the price after that; the page metadata still says $0.042 per million input tokens.

Vercel AI Gateway model page for Jev showing the price as Free, a banner reading Promotional pricing ends on September 25, 2026, and a TypeSafe AI provider row with 32K context, Free input and output, ZDR and No Training checked, and a 09/15/2026 release date Screenshot: Vercel AI Gateway, “Jev API, Pricing & Playground” (undated), captured Sep 21, 2026.

Cloudflare lists typesafe/jev in its AI model catalog with a “Third-party” badge, a 32,000-token context window, TypeSafe’s noul, choice and score names, and “View pricing in the Cloudflare dashboard” in place of a price. The page is undated; Cloudflare staff announced it on Sep 17. Over REST, the request body is wrapped in input.

OpenRouter lists typesafe/jev-1.13 and ~typesafe/jev-latest at $0.042 per million input tokens, output free, 32K context, and its Sep 18 launch post called it a beta. Its TypeSafe-compatible door at /api/v1/systemone takes TypeSafe’s SDKs with a base-URL change, and “jev-1.13 is routed as typesafe/jev-1.13.” Its native Decisions API at /api/alpha/decisions is tagged alpha and adds 402 for exhausted credits, plus 413, 502, 503 and 524, to the error list. Both report a dated model: typesafe/jev-1.13-20260917.

A Jev answer is read by code, so a field name is behavior

Nobody reads a Jev answer. Code acts on it, between an agent and a tool, a task and a model tier, an alert and a human. So a dialect difference is a behavior difference.

answers.destructive.noul >= 0.9 against an evaluation-API response is undefined >= 0.9: false in JavaScript, no exception, and the tool call goes wherever your else branch sends it. Confidence moves too. On the AI SDK path Vercel reports Choice and Score confidence under providerMetadata.typesafe.confidence, so a router reading answer.confidence falls through the same way.

A demo reads one door’s happy path; a fleet reads whichever door someone configured for that lane. The broad gateway question, which layer you refuse to lock in and how you would leave, is covered in open gateway versus vendor suite. This is the narrower test for a model whose output is a number your code trusts.

Run one fixture set through every Jev door before you choose

Step 1: Write the fixture set once, as data

A fixture is a state, a question map and the decision your policy should reach. Start with twenty to forty, covering:

  • Each primitive. A Noul, a Choice with a catch-all option (Cloudflare’s own routing example includes other), and a Score with three to five levels; the API takes two to ten.
  • Both state forms. A string and a structured object. Only a test shows that a door passes structure through intact.
  • The budgets. One request just under 32K tokens for state plus one question, one just over, and a multi-question request between 32K and 64K. TypeSafe documents both budgets; the gateways list only 32K.
  • The limits. A 255-option Choice, TypeSafe’s maximum, and a malformed question, which TypeSafe answers with a 422.
  • Your own traffic. Real decisions from the lanes that will use the door, scrubbed of credentials first. Every door is a third party.
  • Language. One non-English state if your fleet sees them; TypeSafe says other languages work, though not equally well.

Label decisions, not floats: deny, route_billing or an error class, never “0.93”.

# fixtures/jev-conformance.yaml (illustrative shape)
- id: refund-noul-01
  state: "I was charged twice for my subscription."
  questions:
    refund: { type: noul, instructions: "Is the customer asking for money back?" }
  expect: { decision: route_billing }

- id: route-choice-02
  state: { ticket: "Export button returns a 500 since Friday", plan: team }
  questions:
    department:
      type: choice
      instructions: "Which team should handle this?"
      criteria: { billing: "Charges and refunds", technical: "Bugs and outages", other: "Anything else" }
  expect: { decision: route_technical }

- id: budget-over-32k-single
  state_file: states/long-incident-log.txt   # sized so state + question lands just over 32K tokens
  questions:
    stuck: { type: noul, instructions: "Has the session stopped making progress?" }
  expect: { error_class: too_large }

Step 2: Call every door with the same fixtures and keep the raw bytes

One runner sends each fixture to each door and stores status, headers, latency and the raw body before anything parses it. The raw bytes are what you diff when a door changes shape. The calls, as documented on Sep 21:

Door Endpoint or call Model ID you send What differs on the way in
TypeSafe direct POST /v1/systemone jev-1.13.0, or the moving alias jev-latest nothing; this is the reference shape
Vercel, evaluation API experimental_evaluate in AI SDK 7.0.105+, or POST /v1/evaluate typesafe-ai/jev yes/no questions are typed boolean
Vercel, TypeSafe-compatible POST /typesafe/v1/systemone typesafe-ai/jev base URL only
Cloudflare Worker binding env.AI.run('typesafe/jev', …), or REST …/ai/run typesafe/jev REST body wrapped in input
OpenRouter, TypeSafe-compatible POST /api/v1/systemone jev-1.13, routed as typesafe/jev-1.13 base URL only
OpenRouter, Decisions POST /api/alpha/decisions typesafe/jev-1.13 alpha API; OpenRouter’s own error shape

Two gaps sit in that table. Vercel documents only typesafe-ai/jev, so whether a versioned TypeSafe ID passes through is unknown, and OpenRouter doesn’t say whether the three-part jev-1.13.0 works. Make both of them fixtures. Run each fixture three times per door, because a re-ask is a new sample rather than a replay, and keep each door’s call identifiers: OpenRouter’s id and usage.cost, Vercel’s generationId and cost fields.

Step 3: Normalize every answer into one record shape

The chart shows where the wire format changes. Amber cells need adapter code; gray cells are undocumented and become fixtures.

Dialect matrix for Jev on Vercel AI Gateway, TypeSafe, Cloudflare and OpenRouter: rows for yes/no type, yes/no value, usage keys, confidence, model ID sent, model reported, REST body and price on Sep 21, with cells that differ from TypeSafe direct marked amber Documented fields per endpoint, read Sep 21, 2026. OpenRouter’s alpha Decisions API is left out; it keeps TypeSafe’s names but not its error shape.

The mapping to one record:

Field TypeSafe shape: TypeSafe direct, Vercel /typesafe, Cloudflare, OpenRouter Vercel evaluation API and AI SDK Normalized record
Yes/no type noul boolean noul
Yes/no value noul, probability of yes probability, probability of true p_yes
Choice/Score confidence confidence on the answer (Vercel /typesafe: not stated; make it a fixture) providerMetadata.typesafe.confidence on the AI SDK path; absent from the raw-HTTP sample confidence or null
Noul confidence none none always null
Usage input_tokens, output_tokens inputTokens, outputTokens input_tokens, output_tokens
Door metadata provider_metadata (Vercel /typesafe); id, provider, usage.cost (OpenRouter) providerMetadata.gateway door_meta, kept raw
Model reported jev-1.13.0 (TypeSafe, Cloudflare); typesafe/jev-1.13-20260917 (OpenRouter); typesafe-ai/jev (Vercel /typesafe) typesafe-ai/jev model_reported plus version_known

The normalizer’s most important line is the one that throws:

// normalize.ts (illustrative): every door's answer becomes one record, or throws
type Kind = 'noul' | 'choice' | 'score';

export function normalizeAnswer(door: string, id: string, a: any, meta: any) {
  const kind: Kind = a?.type === 'boolean' ? 'noul' : a?.type;   // evaluation API renames Noul
  if (!['noul', 'choice', 'score'].includes(kind)) throw new Error(`${door}/${id}: unknown type ${a?.type}`);

  const pYes = kind !== 'noul' ? null : a.type === 'boolean' ? a.probability : a.noul;
  if (kind === 'noul' && typeof pYes !== 'number') throw new Error(`${door}/${id}: missing probability`);

  // Noul never carries confidence; the AI SDK path moves Choice/Score confidence into metadata
  const confidence = kind === 'noul' ? null : (a.confidence ?? meta?.typesafe?.confidence?.[id] ?? null);

  return { door, id, kind, p_yes: pYes, choice: a.choice ?? null, score: a.score ?? null,
           probabilities: a.probabilities ?? null, confidence };
}

Three rules ride with it. Never default a missing probability to 0 or 1; throw, and let the gate’s written fail mode take the exception. confidence: null means the door surfaced none, which is always true for a Noul, and Choice gates treat it as low. Store model_reported verbatim, with version_known: false wherever a door reports an unversioned slug.

Step 4: Diff decisions across doors, not floats

Apply the policy to every normalized record and compare decisions fixture by fixture. My starting pass criteria, to tune against your own spread:

  1. The same decision on every repeat, on every door.
  2. Each probability within ±0.10 of TypeSafe direct’s median for the same question.
  3. The same error class on the budget and malformed fixtures, or a mapped equivalent.
  4. model_reported logged for every call.

A decision that differs between doors blocks go-live: usually a normalizer bug, sometimes a door that truncates state or enforces another budget. Float drift inside tolerance is noise. Drift outside it on Vercel, while TypeSafe direct and Cloudflare report jev-1.13.0 and hold steady, is a version question that Vercel’s typesafe-ai/jev can’t answer.

Step 5: Write the conformance sheet for what fixtures can’t see

Some terms never show up in a response. Write them per door with the date you read them; every “not stated” becomes a vendor question or a fixture.

Term TypeSafe direct Vercel AI Gateway Cloudflare OpenRouter
Unsupported or undocumented nothing found not reachable through its OpenAI-, Anthropic- or Cohere-compatible endpoints; no documented version pin 64K request budget not stated three-part jev-1.13.0 not stated; 64K not stated
Pre-GA labels to pin around none experimental_evaluate none on the page launch post says beta; Decisions API is alpha
Data retention ZDR for enterprise customers ZDR and No Training per request not stated on the page not stated on the pages read
Price read on Sep 21 $0.042/M input, output free Free until Sep 25; after that, not stated Cloudflare dashboard only $0.042/M input, output free
Rate limits 250K tokens/s and 1,200 RPM, adjusting dynamically not stated; calls count toward budgets not stated not stated; Decisions returns 402 when credits run out
Per-call record and export versioned model; SDKs expose a request ID; export not stated gateway logs and custom reporting, generationId, per-call cost; unversioned model slug versioned model; export not stated id, usage.cost, dated model; export not stated

Then read it row by row:

  • Price has a date. Vercel’s Free ends Sep 25 with no stated successor. Re-run on Sep 26 with cost capture on. Vercel’s evaluation sample prices 275 input tokens at 0.00001155, exactly $0.042/M, so the per-call cost field shows the new price before any invoice does. Reconciling per-call readings with a monthly bill is covered in credit-versus-token meter dialects.
  • Unpublished limits still bind. The gateways publish none for Jev, but Vercel counts evaluation calls toward budgets and OpenRouter’s Decisions API documents a 402 when credits run out: two ways a verdict fails to arrive that TypeSafe’s 429 doesn’t cover.
  • Pre-GA labels are version constraints. experimental is Vercel’s word. Pin ai and @ai-sdk/typesafe-ai exactly, not by caret, and treat a bump as a door change.
  • Retention is per door. Direct ZDR is an enterprise contract; Vercel’s is a request option (providerOptions.gateway.zeroDataRetention); Cloudflare’s and OpenRouter’s pages are silent. Customer text goes only through a door whose retention you can cite.
  • Records differ. Vercel logs evaluation calls in its own reporting, a second record you didn’t have to build, but none of its fields names the Jev version. The other three doors state no export, so there the only record is the one you write.

Step 6: Pin one wire shape so one policy file runs on every door

Pick one shape for the fleet and make it the only shape policy code sees. TypeSafe’s own is the strongest candidate, because it reaches TypeSafe direct, Vercel’s /typesafe path and OpenRouter’s /api/v1/systemone with a base-URL change. Vercel’s docs say so: “Already using TypeSafe? The TypeSafe API accepts TypeSafe’s own request and response shapes, so an existing client only needs its base URL changed.” That leaves two adapters: Cloudflare’s input wrapper, and boolean-to-noul for lanes that must use Vercel’s evaluation API.

Vercel points greenfield code the other way: “If you are writing new code rather than migrating, use the evaluation API instead.” Sound advice if Vercel is your only door. With two or more, the TypeSafe shape needs fewer adapters. Either works; both don’t, because two shapes mean two policy files, and two policy files drift.

Vercel docs page for the TypeSafe API with AI Gateway: a TypeScript client call whose refund answer logs as type noul with noul 0.98, above a Supported endpoints list naming POST /typesafe/v1/systemone and GET /typesafe/v1/models Screenshot: Vercel Docs, “TypeSafe API with AI Gateway” (undated), captured Sep 21, 2026.

The door file is the only place that names a door:

# doors.yaml (illustrative): the only file that names a door
wire_shape: typesafe-systemone
doors:
  typesafe:
    endpoint: https://api.typesafe.ai/v1/systemone
    model: jev-1.13.0            # versioned; the alias moves on release
    adapter: none
  vercel:
    endpoint: https://ai-gateway.vercel.sh/typesafe/v1/systemone
    model: typesafe-ai/jev       # the only ID Vercel documents
    adapter: none
    version_known: false
  openrouter:
    endpoint: https://openrouter.ai/api/v1/systemone
    model: typesafe/jev-1.13
    adapter: none
  cloudflare:
    call: workers-ai-binding     # REST callers wrap the body in `input`
    model: typesafe/jev
    adapter: cloudflare-input

The policy file speaks the pinned shape and addresses answers by question ID:

# jev-policy.yaml (illustrative): questions and rules, one shape, every door
policy_version: 2026-09-21.1
questions:
  destructive:
    type: noul
    instructions: "Does this command delete, overwrite or move files outside the build directory?"
  lane:
    type: choice
    instructions: "Which lane should take this task?"
    criteria: { docs: "Documentation only", code: "Changes source code", unclear: "Not enough information to tell" }
rules:                            # threshold values live in the per-tool-class table
  - when: destructive.noul >= thresholds.destructive.deny
    then: deny
  - when: lane.confidence == null or lane.confidence < thresholds.route.floor
    then: route_default
on_error: gate_fail_mode          # written per gate before go-live

Diagram of Jev on Vercel AI Gateway, Cloudflare, OpenRouter and TypeSafe behind one policy file: the policy file feeds a thin adapter, the adapter calls three door groups, answers return to one normalized record that feeds the decision log, and code applies the decision The adapter is the only code that knows a door exists. Everything above it speaks one shape; everything below it reads one record.

Three things stay out of the adapter. Threshold values per tool class come from the confidence-gate table, including thresholding a Noul on its probability, since it has no confidence. The fields each call’s record carries belong to the Jev decision log. What a gate does on a 429, a 402 or a timeout is written per gate in the fail-mode table.

The adapter’s job is one shape out, one record back, and an exception on a missing field. That exception is not enforcement. When the adapter throws, the gate applies its written fail mode, and the permission rules or sandbox behind a destructive tool stay in place whatever any door answers.

Step 7: Re-run the set on every change that can move a door

Make the conformance run a CI job. Trigger it on:

  • any lockfile change to ai, @ai-sdk/typesafe-ai or TypeSafe’s SDKs;
  • Sep 26, and any date a door announces a price or label change;
  • any alias move; in TypeSafe’s words, “An alias moves when a new release ships, so the answers behind it can change without a change on your side.”;
  • a weekly schedule, because docs change without changelog entries (on Sep 21, Cloudflare’s changelogs had no Jev entry at all).

Fail the job on a flipped decision, an unmapped field or error class, or a model_reported value you haven’t seen. Pinning versioned IDs where a door allows it is the provider-cutoff rehearsal applied to a classifier: know what each lane runs, and keep a tested second route.

Where Jev doors disagree silently, and the signal for each

The renamed field. Policy reads noul, the door returns probability, the else branch runs. Signal: the normalizer throws “missing probability” on the first fixture; in production, a gate that never fires on one door.

Confidence in another pocket. A Choice router reads answer.confidence where the door reports it in metadata. Signal: confidence: null on every Choice answer after a lane changes doors.

The experimental bump. A caret range pulls a new AI SDK release and the evaluation call changes shape. Signal: the conformance job fails on a lockfile-only pull request.

The promo cliff. Signal: Vercel’s per-call cost turns non-zero after Sep 25. Check it against input tokens times $0.042/M before an invoice arrives.

The version you can’t see. Vercel reports typesafe-ai/jev, so a new Jev behind the slug looks like the old one. Signal: Vercel probabilities drift outside tolerance while TypeSafe direct and Cloudflare still report jev-1.13.0 and hold. TypeSafe’s advice: “If you have tuned confidence thresholds against a specific version, pin that version’s ID instead of the alias and move to the new one on your own schedule.” Where a door can’t pin, decide in writing whether a gate tuned on 1.13 may run there.

The door-specific error. OpenRouter’s Decisions API returns 402, 413 or 524, none of them in TypeSafe’s direct error table; Vercel’s /typesafe path passes provider errors through unchanged; on Cloudflare, learn the shape from your own failing fixtures. Signal: an unmapped error class in the run. Map it, then hand the behavior to the fail-mode table.

The unlisted budget. The gateways list 32K; only TypeSafe states the 64K request budget. Signal: the 32K–64K fixture passes on one door and fails on another. Fix: size multi-question requests to the smallest budget any lane’s door honors.

A Jev door is a fleet dependency, so inventory it like one

Two doors are two dependencies with different prices, retention terms, error vocabularies and version visibility, and each lane’s config decides which one a gate hits. Record the door per lane beside the model per lane, keep the conformance sheet with the lane manifest, and treat a door change like a model change: re-run, diff, sign off. That record doesn’t live in the model or in any vendor’s console. It lives in the layer that runs many agents across many CLIs and must answer, for any gate decision last Tuesday, which door answered and in which dialect: the multi-agent command center, whether or not it has a screen.

Pick Vercel, Cloudflare, OpenRouter or TypeSafe direct on price, retention or where your traffic already flows. Just pick after the fixture set has run through all of them.

FAQ

Is Jev free on Vercel AI Gateway?

On Sep 21, 2026, Vercel’s model page shows Jev as Free, with a note that promotional pricing ends September 25, 2026. Vercel doesn’t state the price after that. Its page metadata still lists $0.042 per million input tokens, the rate TypeSafe and OpenRouter charge, with output free. Watch the per-call cost field.

Which Jev endpoint should a new project use?

If Vercel is your only door, Vercel recommends its evaluation API for new code. If a second door is possible, pin TypeSafe’s request and response shape: it works on TypeSafe direct, Vercel’s /typesafe path and OpenRouter with a base-URL change, leaving adapters only for Cloudflare’s input wrapper and Vercel’s evaluation API.

Sources