Jev is TypeSafe AI’s decision model. It takes state plus named Noul, Choice, and Score questions, then returns structured answers for your code to use. It does not generate prose.
This guide runs TypeSafe’s JavaScript SDK inside a Freestyle VM while the real
TypeSafe API key stays in your trusted controller. The VM supplies a placeholder
key. A Freestyle HTTP egress rule replaces it only on
POST /v1/systemone requests to api.typesafe.ai.
Prepare A Snapshot With The TypeSafe SDK
Install the Freestyle SDK on your controller and export its API key:
npm install freestyle@latest
export FREESTYLE_API_KEY="your-freestyle-api-key"
export TYPESAFE_API_KEY="your-typesafe-api-key"
Create the TypeSafe key in the TypeSafe console. Keep both keys in the controller’s secret store, outside the VM and its snapshots.
Start with the reusable Node.js snapshot from the Node.js sandbox guide, then install the official TypeSafe SDK into a dedicated application directory. Set the Node snapshot ID before running this builder:
export FREESTYLE_NODE_SNAPSHOT_ID="snapshot-id-from-the-node-guide"
import { Freestyle } from "freestyle";
function requiredEnv(name: string): string {
const value = process.env[name]?.trim();
if (!value) throw new Error(`Missing ${name}`);
return value;
}
const freestyle = new Freestyle();
const { vm: builder } = await freestyle.vms.create({
snapshotId: requiredEnv("FREESTYLE_NODE_SNAPSHOT_ID"),
firewall: {
rules: [{ action: "allow", source: {}, destination: { public: true } }],
},
slug: "jev-builder",
});
const node = "export HOME=/root NVM_DIR=/opt/nvm && . $NVM_DIR/nvm.sh &&";
try {
const install = await builder.exec({
command:
`${node} mkdir -p /opt/jev-agent && cd /opt/jev-agent && ` +
"npm init -y && npm pkg set type=module && npm install @typesafe-ai/sdk",
timeoutMs: 300_000,
});
if (install.statusCode !== 0) {
throw new Error(install.stderr ?? "TypeSafe SDK installation failed");
}
const { snapshotId } = await builder.snapshot();
console.log({ snapshotId });
} finally {
await builder.delete();
}
Run the builder and save the printed ID as FREESTYLE_JEV_SNAPSHOT_ID. The
snapshot contains Node.js and the installed package, but neither API key.
Give One VM Access To Jev
Create a VM with no general Internet firewall rule. Then add one named TLS route that injects the TypeSafe Bearer key only on the System One endpoint:
import { Freestyle, type CreateTlsRuleOptions } from "freestyle";
function requiredEnv(name: string): string {
const value = process.env[name]?.trim();
if (!value) throw new Error(`Missing ${name}`);
return value;
}
const freestyle = new Freestyle();
const { vm, vmId } = await freestyle.vms.create({
snapshotId: requiredEnv("FREESTYLE_JEV_SNAPSHOT_ID"),
firewall: { rules: [] },
slug: "jev-agent",
});
function jevRule(apiKey: string): CreateTlsRuleOptions {
return {
action: "allow",
domain: "api.typesafe.ai",
source: { vmId },
destination: { public: true },
match: { method: ["POST"], path: { exact: "/v1/systemone" } },
transform: [
{ headers: { authorization: `Bearer ${apiKey}` } },
],
};
}
const rule = await freestyle.tls.rules.create(
jevRule(requiredEnv("TYPESAFE_API_KEY")),
);
console.log({ vmId, ruleId: rule.id });
The rule steers the exact hostname through the Freestyle edge, installs the
Freestyle CA in the VM’s system trust store, and opens the named route without
granting raw-IP Internet access. Header values are sealed at rest and read back
as "***".
Every process in this VM can use the grant. The secret is not readable from the guest, but the ability to spend it is available to code running there. Use a separate VM or VPC source when workloads need separate TypeSafe access.
Ask Several Typed Questions In One Request
Continue in jev-controller.ts after the rule creation. This writes an
agent-side script into the prepared application directory:
await vm.fs.writeTextFile(
"/opt/jev-agent/route-ticket.mjs",
`import { choice, noul, score, TypeSafeClient } from "@typesafe-ai/sdk";
const client = new TypeSafeClient();
const response = await client.systemOne({
state: {
subject: "Checkout failed twice",
body: "Our checkout has rejected every payment since this morning. Please help.",
},
questions: {
isUrgent: noul(
"Does this ticket describe an active problem that needs attention today?",
{
true: "The problem is active and time-sensitive",
false: "The problem is informational or can wait",
},
),
owner: choice("Which team should own this ticket?", {
billing: "Charges, invoices, refunds, or subscription state",
technical: "Product failures, bugs, outages, or integrations",
sales: "Pricing, plans, trials, or purchasing",
}),
frustration: score("How frustrated does the customer sound?", [
"Calm",
"Concerned",
"Frustrated",
"Angry",
]),
},
});
console.log(JSON.stringify({
model: response.model,
owner: response.answers.owner.choice,
ownerConfidence: response.answers.owner.confidence,
urgencyProbability: response.answers.isUrgent.noul,
frustrationScore: response.answers.frustration.score,
frustrationConfidence: response.answers.frustration.confidence,
}, null, 2));
`,
);
Run it inside the VM. The SDK requires a non-empty API key locally, so pass a
placeholder. Freestyle overwrites its Authorization header before the request
reaches TypeSafe:
const node = "export HOME=/root NVM_DIR=/opt/nvm && . $NVM_DIR/nvm.sh &&";
let output = "";
let lastError = "Jev request failed";
for (let attempt = 0; attempt < 10; attempt++) {
const result = await vm.exec({
command: `${node} node /opt/jev-agent/route-ticket.mjs`,
env: {
TYPESAFE_API_KEY: "unused-placeholder",
NODE_EXTRA_CA_CERTS: "/etc/ssl/certs/ca-certificates.crt",
},
timeoutMs: 60_000,
});
if (result.statusCode === 0) {
output = result.stdout ?? "";
break;
}
lastError = result.stderr ?? lastError;
await new Promise((resolve) => setTimeout(resolve, 2_000));
}
if (!output) throw new Error(lastError);
console.log(output);
The SDK keeps its default base URL, https://api.typesafe.ai, and defaults to
the jev-latest model alias. NODE_EXTRA_CA_CERTS makes Node trust the CA that
Freestyle installed for the named egress route. Keep certificate verification
enabled. The short retry loop allows the new route, hostname mapping, and CA
installation to propagate.
The output contains a selected owner, a yes-probability for urgency, a probability-weighted frustration score, and confidence for the Choice and Score answers. Define action thresholds from an evaluation set that matches your own traffic. Do not copy a threshold from an unrelated example.
Put Jev In An Agent Loop
Use Jev for bounded decisions such as choosing a handler, ranking a shortlist, or deciding when a person should review a task. Keep arithmetic, authorization, and irreversible actions in ordinary code. Jev is not a replacement for the generative model that writes code or prose.
Treat user-controlled state as untrusted. TypeSafe’s Jev limitations note that adversarial wording in state can move an answer. A Jev result can raise a review flag, but it should not by itself authorize a payment, delete data, or bypass a human approval step.
The exact-path matcher selects when Freestyle injects the credential. It is not
a deny rule. A different request to api.typesafe.ai, such as GET /v1/models,
still follows the named route but keeps the placeholder credential and should
fail authentication. Keep the VM’s firewall empty unless the workload needs
another destination, then add each required route explicitly.
Rotate The Key Or Remove Access
After creating a replacement key in TypeSafe, load it into the controller and replace the complete rule:
await freestyle.tls.rules.update(
rule.id,
jevRule(requiredEnv("TYPESAFE_API_KEY")),
);
The update can take about five seconds to reach every edge cache. Verify a new request before revoking the old key. A rule read cannot reconstruct its redacted secret, so every update must include the real replacement value.
Delete the rule and VM when the worker no longer needs them:
await freestyle.tls.rules.delete(rule.id);
await vm.delete();
Deleting the Freestyle rule removes the VM’s grant. Revoke the provider key in TypeSafe when the credential itself should stop working elsewhere.