Tutorials
Jev + Vercel AI SDK: Route Agents Before They Run
The Vercel AI SDK handles the generation side of an agent well: ToolLoopAgent, tools with schemas, loop control. What it deliberately leaves to you is the control flow around the loop — which agent runs, whether it should run at all, and when to stop.
That control flow is Jev's job. This tutorial wires the two together.
The pattern
Jev is not a chat model, so you do not pass it as model to an agent. You call it in your own code, before or between agent runs:
user request
│
▼
Jev: Choice + confidence ← routing decision
│
┌───┴────────────────┐
▼ ▼
high confidence low confidence
▼ ▼
ToolLoopAgent clarify or escalate
Step 1: A normal AI SDK agent
Start with the AI SDK's documented agent class. Per the AI SDK agents docs, ToolLoopAgent handles the loop, context, and stopping conditions, and tool() takes a description plus a schema:
import { ToolLoopAgent, tool } from "ai";
import { z } from "zod";
const kubernetesAgent = new ToolLoopAgent({
model: "openai/gpt-5.5",
tools: {
kubectl: tool({
description: "Inspect Kubernetes cluster state: pods, logs, events",
inputSchema: z.object({
command: z.string().describe("kubectl arguments to run"),
}),
execute: async ({ command }) => runKubectl(command),
}),
},
});
Build one agent per capability: a Kubernetes agent, a coding agent, a research agent. Each is self-contained, which is what makes them routable.
Step 2: Jev picks the agent
Define the agents as Choice criteria. The descriptions are the boundary between agents, so keep them specific:
const AGENTS = {
kubernetes: "Cluster, pods, deployments, CrashLoopBackOff, resource limits",
coding: "Writing or refactoring application code, tests, code review",
research: "Finding and summarizing external information, docs, papers",
general: "Anything that does not clearly fit another agent",
} as const;
async function decideAgent(request: string) {
const response = await fetch("https://api.typesafe.ai/v1/systemone", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TYPESAFE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
state: request,
model: "jev-latest",
questions: {
agent: {
type: "choice",
instructions: "Which agent should handle this request",
criteria: AGENTS,
},
urgent: {
type: "noul",
instructions: "The request conveys urgency or time-sensitivity",
},
},
}),
});
if (!response.ok) {
throw new Error(`TypeSafe API error: ${response.status}`);
}
const { answers } = await response.json();
return {
agent: answers.agent.choice as keyof typeof AGENTS,
confidence: answers.agent.confidence as number,
urgent: answers.urgent.noul as number,
};
}
Two questions, one request, two typed answers. The AI SDK never sees an ambiguous prompt.
Step 3: Gate on confidence before spending tokens
The routing decision is only useful if it changes behavior. Per TypeSafe's confidence guidance, divide confidence into ranges and give each a different response:
const agents = {
kubernetes: kubernetesAgent,
coding: codingAgent,
research: researchAgent,
general: generalAgent,
};
export async function handle(request: string) {
const { agent, confidence, urgent } = await decideAgent(request);
if (confidence < 0.5) {
// Jev is telling you the request does not map cleanly to any agent.
return { action: "clarify" as const };
}
if (confidence < 0.85) {
return { action: "confirm" as const, agent };
}
const result = await agents[agent].generate({ prompt: request });
return { action: "answered" as const, agent, urgent, text: result.text };
}
Look at what that does to cost. The expensive, slow, non-deterministic part of the stack now runs only for requests Jev is confident about. An ambiguous request costs one cheap decision instead of a full agent run.
Step 4: Per-step decisions with the loop hooks
The AI SDK documents loop control through stopWhen and prepareStep, and notes that runtimeContext flows through the loop and is available in prepareStep and lifecycle callbacks. That is where a Jev gate belongs when you want it inside the loop rather than before it:
- Next tool — a Choice question over your tool set, injected as the step's instruction instead of letting the model choose
- Continue or stop — a Noul question ("has the task been completed?") to end the loop early
- Guardrail — a Noul check on the pending tool call, especially before anything destructive
Keep the questions atomic and let your code own the policy, exactly as in the tool selection tutorial.
Step 5: Log decisions alongside agent steps
result.steps gives you the agent's trace. Pair it with the Jev decision:
console.log(
JSON.stringify({
decision: agent,
confidence,
steps: result.steps.length,
}),
);
When routing goes wrong, that line tells you whether the problem was the decision (flat probabilities, low confidence) or the agent (wrong tool calls at high confidence). Different bugs, different fixes.
Where the AI Gateway fits
If your AI SDK traffic already goes through the Vercel AI Gateway, Jev is listed there as typesafe-ai/jev (input $0.04/1M, output free). The decision calls and the generation calls can sit behind the same credential and the same bill, with two very different cost profiles.
Try it
The playground ships an agent-router scenario you can run with your own key. Paste a request, watch the probabilities, and see how the confidence gate would have decided. If you would rather start from the raw primitives, the official TypeSafe playground is the fastest path.