Quick Start
Five minutes from npm install to a working agent with typed tools, a policy gate, and a signed audit trace.
1. Install
npm install @veridex/agents zod
# or
pnpm add @veridex/agents zod
# or
bun add @veridex/agents zod2. Define a tool
import { tool } from '@veridex/agents';
import { z } from 'zod';
const getWeather = tool({
name: 'get_weather',
description: 'Look up the current weather for a city.',
safetyClass: 'read',
input: z.object({ city: z.string().min(2).max(64) }),
output: z.object({ tempC: z.number(), summary: z.string() }),
async execute({ input }) {
// any I/O here goes through the sandbox in production
return { success: true, llmOutput: { tempC: 18, summary: 'partly cloudy' } };
},
});Every tool declares its safetyClass (read | write | network | financial | privileged). The runtime uses it to decide policy, approval, and sandbox defaults. See Tools.
3. Compose the agent
import { createAgent, OpenAIProvider } from '@veridex/agents';
const agent = createAgent(
{
name: 'weather-assistant',
instructions: 'You answer weather questions. Use the get_weather tool. Be concise.',
tools: [getWeather],
},
{
modelProviders: {
default: new OpenAIProvider({ apiKey: process.env.OPENAI_API_KEY! }),
},
},
);4. Run it
const result = await agent.run("What's the weather in Tokyo?");
console.log(result.output);
// → "Tokyo is partly cloudy, around 18°C."5. Add a policy rule
Block all financial tools outside business hours:
import { createAgent, policyRule } from '@veridex/agents';
const businessHoursOnly = policyRule({
id: 'business-hours-financial',
priority: 10,
evaluate(ctx) {
if (ctx.proposal.kind !== 'tool') return { kind: 'allow' };
if (ctx.proposal.contract.safetyClass !== 'financial') return { kind: 'allow' };
const hour = new Date().getUTCHours();
return hour >= 13 && hour < 21
? { kind: 'allow' }
: { kind: 'deny', reason: 'Financial tools allowed only 13–21 UTC.' };
},
});
const agent = createAgent(
{ name: '...', instructions: '...', tools: [...], policies: [businessHoursOnly] },
{ modelProviders: { default: provider } },
);Every evaluation emits a policy_decision event you can inspect. See Policy.
6. Read the trace
agent.events.on('*', (event) => {
console.log(event.type, event.payload);
});
// Or after the run:
console.log(result.trace.events.length, 'events emitted');You will see at minimum:
run_started → turn_started → context_compiled → model_call_started → model_call_completed
→ tool_proposed → policy_decision → tool_executed → turn_completed → run_completedEvery event is content-hashed and chained with the previous event's hash. See Observability.
7. (Optional) human approval
Set a route on the tool and add an approval handler:
const transfer = tool({
name: 'transfer',
safetyClass: 'financial',
approval: { route: 'human_required', timeoutMs: 24 * 60 * 60_000 },
input: z.object({ to: z.string(), amount: z.string() }),
async execute({ input, secrets }) { /* ... */ },
});When the model proposes a transfer, the run suspends, the checkpoint is saved, and you resume after a human decides:
const run = await agent.run("Send $100 to alice@example.com");
if (run.status === 'suspended') {
// …days later, in any process:
const final = await agent.resume(run.runId, run.approvalId, {
decision: 'allow', approver: 'manny', reason: 'verified counterparty',
});
}See Approvals and Checkpoints.
What you just got
- A typed tool that cannot be called with the wrong arguments.
- A policy that gates consequential calls — replayably.
- An audit trace that proves what happened.
- A run that survives a redeploy.
That is what production-shaped means. Continue to Context Compilation to learn how Veridex keeps the agent coherent over hours and days.