Jev turns design system into a decision engine for AI agents
TypeSafe AI's model doesn't generate interface: it chooses among options you define. This changes what it means to maintain a design system.

What changes when the model doesn't generate, it decides
Since TypeSafe AI launched Jev, on September 15, it has become common to hear people calling the model an "instant UI generator." That's not what it is. Jev doesn't design anything. It's trained exclusively for structured decisions, routing, and classification, without producing a single sentence. The difference seems subtle, but it changes the role of the design system within an AI-powered product: it stops being documentation for humans to read and becomes the menu of options the machine can choose from.
This matters because most of the "generative UI" experiments that circulated over the last two years relied on a general-purpose LLM deciding on a component through free inference, with all the risk of hallucinating an option that doesn't exist in your code. Jev tackles this problem at the root: the application declares the set of valid answers upfront, and the model can only choose from within it.
Inside Jev: typed questions, not free text
Jev's API works with typed questions, evaluated in parallel in a single call: choice selects one option from a list, score grades an ordered rubric, and boolean estimates the probability of something being true. You send a state (the user's request, the screen's context) and get back identifiers, not loose text to parse.
The cost declared by TypeSafe is $0.042 per million input tokens, with free output, which makes sense when the answer is always short and from a closed set. From an engineering standpoint, this solves two problems at once: predictability (the model doesn't invent a fifth option) and predictable cost per call, since there's no long text generation to pay for.
Building the manifest: my take on a support panel
TypeSafe's own tutorial uses a sales dashboard as an example. I prefer to think of a scenario more common in internal products: a support panel where the agent types something like "show me the tickets that are close to breaching SLA" and the app decides which design system piece to display.
The first step isn't code, it's writing. Each eligible component needs an identifier, a description, a "use when," and an "avoid when," and it's this last field that's usually missing from traditional design system documentation, because nobody writes exclusion rules for humans, only for machines.
// lib/component-manifest.ts
export const componentManifest = [
{
id: "sla_countdown",
description: "Countdown for tickets close to breaching SLA.",
useWhen: "The request mentions deadline, urgency, or tickets about to expire.",
avoidWhen: "The request asks for a general overview without a time frame.",
},
{
id: "ticket_table",
description: "Table with all fields for each ticket.",
useWhen: "The request asks for row-by-row detail or many fields.",
avoidWhen: "The request wants a quick summary, not a full list.",
},
{
id: "workload_chart",
description: "Bar chart with open tickets per agent.",
useWhen: "The request compares workload between people or teams.",
avoidWhen: "There's no comparison between groups.",
},
] as const;
export type ComponentId = (typeof componentManifest)[number]["id"] | "clarify";Notice that clarify isn't an accessory: it's the output for when the request is ambiguous. Without it, the model is forced to guess among the remaining options, and that's worse than simply asking back.
Implementing the decision in Next.js
With Node.js 22+, a Next.js project with App Router and TypeScript, and a key obtained from the quickstart at docs.typesafe.ai, the SDK goes in like this:
npm install @typesafe-ai/sdk@0.6.0The key stays only on the server, never in a NEXT_PUBLIC_ variable. The decision layer converts the manifest into a choice question:
// lib/jev-router.ts
import { TypeSafeClient, choice } from "@typesafe-ai/sdk";
import { componentManifest, type ComponentId } from "./component-manifest";
const client = new TypeSafeClient({ apiKey: process.env.TYPESAFE_API_KEY });
const knownIds = new Set<string>([...componentManifest.map((c) => c.id), "clarify"]);
export async function routeToComponent(request: string): Promise<ComponentId> {
const options = Object.fromEntries(
componentManifest.map((c) => [
c.id,
`${c.description} Use when: ${c.useWhen} Avoid when: ${c.avoidWhen}`,
]),
);
const result = await client.systemOne({
model: "jev-1.13.0",
state: { latestMessage: request },
questions: {
component: choice(
"Which design system component best answers the request? If ambiguous, choose clarify.",
{ ...options, clarify: "The request is ambiguous; ask for more details before deciding." },
),
},
});
const answer = result.answers.component;
if (answer?.type !== "choice" || !knownIds.has(answer.choice)) return "clarify";
return answer.choice as ComponentId;
}The route that exposes this to the frontend follows the same principle of never letting the interface break when something goes wrong:
// app/api/route-request/route.ts
import { routeToComponent } from "@/lib/jev-router";
export async function POST(req: Request) {
const { message } = await req.json();
if (typeof message !== "string" || !message.trim()) {
return Response.json({ error: "empty" }, { status: 400 });
}
try {
const component = await routeToComponent(message);
return Response.json({ component });
} catch {
return Response.json({ component: "clarify" });
}
}On the client, the point I wouldn't let slide is accessibility: when the result is clarify, the clarification request needs to go into an aria-live="polite" region, because it's a content change that happens without the user clicking anything. And since Jev's response arrives in a single call, the perceived performance cost is that of a simple network request, not that of a token stream: it's worth measuring the time until the component appears in your environment before assuming it's fast enough.
Confidence, calibration, and what to do when the model gets it wrong
Jev returns, along with the choice, a confidence probability (via Vercel's AI SDK, it shows up in result.providerMetadata.typesafe.confidence). The temptation is to set an arbitrary cutoff, like "above 80% I trust it," but Vercel itself recommends calibrating this number with labeled examples from your own flow, not with intuition.
In practice, the path I'd take is to set aside a batch of real user requests (30 to 40 already gives a signal), manually annotate which component would be correct for each one, and compare it with what Jev chose. The error is almost never in the model: it's in the component description, which turned out too vague or overlaps with another option in the manifest. This calibration process is the new design system maintenance work: it's not reviewing Figma, it's reviewing the "use when" and "avoid when" sentence by sentence.
Framework is a detail, but the limits are real
This pattern isn't exclusive to React. The call to Jev is a plain HTTP request: the same routeToComponent works behind a Nuxt route or a SvelteKit endpoint, changing only the registry that maps identifier to component at render time. The real coupling is in the manifest, not in the framework, which is good news for anyone maintaining a design system across a heterogeneous stack.
Anyone already using the Vercel ecosystem has an alternative path: AI SDK 7, starting with version 7.0.105, exposes Jev through the experimental evaluate API, as typesafe-ai/jev. The logic is the same, only the shape of the call changes.
It's also worth noting two limits. First, the performance gain numbers published by TypeSafe come from workflows built by the company's own team (it acknowledges this itself), so measure it in your own case before citing the number in any internal proposal. Second, it's a third-party hosted model: data leaves your infrastructure. Through the AI Gateway, Jev supports Zero Data Retention and No Training, enabled per request, which is relevant if the "user request" you send as state carries sensitive data.
What remains open for those who build
Jev doesn't solve genuine ambiguity: it only prevents it from turning into a guess, pushing it to clarify. And it doesn't replace the work of designing any component: it's still a human deciding what exists in the design system. What changes is that the quality of this automated decision will never exceed the quality of the descriptions someone wrote. A component without a clear "avoid when" is a component the agent will eventually use wrong, and that doesn't show up in any unit test: it only shows up when you audit the real choices against what a human would have chosen.
Translated from the Brazilian Portuguese original · Read the original
How Tailscale built a model router on Vercel's AI Gateway
The Aperture case shows a reusable pattern for teams that need to route hundreds of models, control access by identity, and run isolated agents without building the infrastructure from scratch.
