Veto/docs

TypeScript SDK

Full API reference for the Veto TypeScript SDK (v1.12.0).

Installation

npm install veto-sdk

protect(tools, options?)

Recommended entrypoint for new integrations.

import { protect } from 'veto-sdk';

const safeTools = await protect(tools);

Single-tool overload:

const safeTool = await protect(tool);

Common options:

const safeTools = await protect(tools, { pack: 'financial' });
const safeToolsCloud = await protect(tools, { apiKey: 'veto_...' });
const safeToolsLog = await protect(tools, { mode: 'log' });
const safeToolsShadow = await protect(tools, { mode: 'shadow' });

Use Veto.init() directly when you need full lifecycle or initialization control.

Veto.init(options?)

Initialize Veto. Loads configuration from ./veto by default.

import { Veto } from 'veto-sdk';

// Local mode (default — no API key needed)
const vetoLocal = await Veto.init();

// Cloud mode
const vetoCloud = await Veto.init({ apiKey: "veto_..." });

// Self-hosted mode
const vetoSelfHosted = await Veto.init({ endpoint: "https://veto.internal.corp.com" });

Options

OptionTypeDefaultDescription
configDirstring"./veto"Path to config directory
mode"strict" | "log" | "shadow""strict"Operating mode
apiKeystringAPI key — triggers cloud mode
endpointstringServer URL — triggers self-hosted mode
logLevelstring"info"Log level
sessionIdstringSession ID for tracking
agentIdstringAgent ID for tracking
validatorsValidator[]Additional validators
cloudClientVetoCloudClientInjected cloud client
onApprovalRequired(context, approvalId) => voidHook fired when a tool call needs human approval

Mode auto-detection: endpoint > apiKey > explicit config > VETO_API_KEY env > local fallback. See SDK Modes for full precedence rules.

Operating mode precedence: explicit mode option > config mode > VETO_MODE env > strict.

veto.wrap<T>(tools: T[]): T[]

Wraps an array of tools with validation. The returned tools have identical types — fully compatible with your AI framework.

const wrappedForOpenAI = veto.wrap(openAITools);
const wrappedForAnthropic = veto.wrap(anthropicTools);
const wrappedForVercel = veto.wrap(vercelTools);
const wrappedForMCP = veto.wrap(mcpTools);

Works with OpenAI function calling, Anthropic tool use, Vercel AI SDK, LangChain, MCP tools, and custom tool objects.

MCP tool definitions (using inputSchema instead of parameters) are auto-detected and converted transparently. See MCP Integration.

veto.wrapTool<T>(tool: T): T

Wraps a single tool instance.

const safeTool = veto.wrapTool(myTool);

veto.guard(toolName, args, context?)

Run Veto validation as a standalone check without wrapping or executing a tool.

import { Veto, type GuardResult } from 'veto-sdk';

const veto = await Veto.init({ mode: 'log' });

const result: GuardResult = await veto.guard(
  'wire_transfer',
  { amount: 25000, recipient: 'vendor-123' },
  { sessionId: 'session-42', agentId: 'agent-7' }
);

if (result.decision === 'deny') {
  console.error(result.reason);
}

GuardResult

type GuardResult = {
  decision: 'allow' | 'deny' | 'require_approval';
  reason?: string;
  ruleId?: string;
  severity?: 'critical' | 'high' | 'medium' | 'low' | 'info';
  approvalId?: string;
  shadow?: boolean;
  shadowDecision?: string;
};

Guard behavior (important)

  • Uses the same internal ValidationEngine.validate() pipeline as wrapped tool calls.
  • Records every guard check in HistoryTracker (getHistoryStats() and exportDecisions() include guard calls).
  • Never throws ToolCallDeniedError; deny outcomes are returned in GuardResult.
  • In log mode and shadow mode, guard() still returns the real policy verdict (deny / require_approval) instead of converting to allow.
  • In shadow mode, guard() includes shadow: true and shadowDecision for non-allow outcomes.
  • context.sessionId / context.agentId override instance-level tracking values for that call only.
  • require_approval is returned directly for local approval rules and cloud approval flow checks. approvalId is populated when available (for example, cloud responses with approval_id).
  • ruleId and severity are populated from validation metadata when present.

veto.getHistoryStats()

Returns statistics about validation decisions.

const stats = veto.getHistoryStats();
// { totalCalls: 5, allowedCalls: 4, deniedCalls: 1, ... }

veto.clearHistory()

Resets the history statistics.

veto.clearHistory();

Error handling

When a tool call is blocked in strict mode, Veto throws a ToolCallDeniedError:

import { ToolCallDeniedError } from 'veto-sdk';

try {
  await wrappedTool.invoke(args);
} catch (error) {
  if (error instanceof ToolCallDeniedError) {
    console.log(error.toolName);  // "transfer_funds"
    console.log(error.reason);    // "Amount 5000 exceeds limit of 1000"
    console.log(error.callId);    // "tc_abc123"
  }
}

See the Error Handling Guide for strategies on retry, graceful degradation, and framework-specific patterns (OpenAI, Anthropic, LangChain).

When a budget limit is exceeded, Veto throws a BudgetExceededError:

import { BudgetExceededError } from 'veto-sdk';

try {
  await wrappedTool.invoke(args);
} catch (error) {
  if (error instanceof BudgetExceededError) {
    console.log(error.spent);     // 48.50
    console.log(error.limit);     // 50
    console.log(error.remaining); // 1.50
    console.log(error.toolName);  // "purchase"
    console.log(error.toolCost);  // 25
  }
}

When an approval poll times out, Veto throws an ApprovalTimeoutError:

import { ApprovalTimeoutError } from 'veto-sdk';

try {
  await wrappedTool.invoke(args);
} catch (error) {
  if (error instanceof ApprovalTimeoutError) {
    console.log(error.approvalId);  // "apr_abc123"
    console.log(error.timeoutMs);   // 300000
  }
}

Cloud validation and approvals

When using cloud or self-hosted mode, the SDK routes tool calls through the Veto server. The server can return three decisions: allow, deny, or require_approval.

When a tool call requires approval, the SDK:

  1. Fires the onApprovalRequired hook (so your app can show approval UI)
  2. Polls GET /v1/approvals/:id until a human approves or denies
  3. Returns the final decision to the agent
const veto = await Veto.init({
  apiKey: "veto_...",
  onApprovalRequired: (context, approvalId) => {
    console.log(`Tool "${context.toolName}" needs approval: ${approvalId}`);
  },
});

Configuration

Configure approval polling in veto.config.yaml:

validation:
  mode: "cloud"

cloud:
  apiKey: "veto_abc123..."
  baseUrl: "https://api.runveto.com"

approval:
  pollInterval: 2000   # ms between polls (default: 2000)
  timeout: 300000      # max ms to wait (default: 300000 = 5 min)

Approval preference cache

Cache per-tool preferences to auto-resolve approvals without server polling:

veto.setApprovalPreference('read_file', 'approve_all');
veto.setApprovalPreference('delete_database', 'deny_all');
veto.getApprovalPreference('read_file'); // "approve_all"
veto.clearApprovalPreferences('read_file');
veto.clearApprovalPreferences(); // Clear all

Client-side deterministic validation

When using cloud mode, the SDK evaluates deterministic constraints locally — no network round-trip needed. Policies are fetched from the cloud and cached with a stale-while-revalidate strategy.

This is automatic. When the SDK has a cached policy for a tool and that policy uses deterministic mode without session constraints or rate limits, validation runs entirely in the SDK.

Agent calls tool


Check PolicyCache ─── cache miss ─── POST /v1/validate (server)

  cache hit (deterministic, no session/rate constraints)


Run local validation

       ├── allow ─── Tool executes

       └── deny ─── ToolCallDeniedError

  POST /v1/decisions (fire-and-forget)

Supported constraint types

ConstraintApplies toDescription
requiredallArgument must be present
notNullallArgument cannot be null
minimumnumbersLower bound (inclusive)
maximumnumbersUpper bound (inclusive)
greaterThannumbersLower bound (exclusive)
lessThannumbersUpper bound (exclusive)
minLengthstringsMinimum string length
maxLengthstringsMaximum string length
enumstringsAllowed exact values
regexstringsPattern match (max 256 chars, ReDoS-safe)
minItemsarraysMinimum array length
maxItemsarraysMaximum array length

Policy cache

Policies are cached with two time windows:

WindowDefaultBehavior
Fresh60sReturns cached policy immediately
Max age5minReturns stale policy while refreshing in background

After max age, the cache entry expires and the next validation falls through to the server.

VetoCloudClient

Standalone client for direct cloud API interaction. Used internally by Veto, but also available for advanced use cases.

import { VetoCloudClient } from 'veto-sdk';

const client = new VetoCloudClient({
  config: {
    apiKey: 'veto_abc123...',
    baseUrl: 'https://api.runveto.com',
    timeout: 30000,
    retries: 2,
    retryDelay: 1000,
  },
  logger,
});

client.validate(toolName, args, context?)

Validate a tool call against cloud policies.

const result = await client.validate('send_email', {
  to: 'user@example.com',
  subject: 'Hello',
});
// { decision: "allow" | "deny" | "require_approval", reason?, approval_id? }

client.pollApproval(approvalId, options?)

Poll an approval record until resolved or timed out.

const approval = await client.pollApproval('apr_abc123', {
  pollInterval: 2000,
  timeout: 300000,
});
// { id, toolName, status: "approved" | "denied" | "expired", resolvedBy? }

client.fetchPolicy(toolName)

Fetch a tool's policy from the server.

const policy = await client.fetchPolicy('send_email');
// { toolName, mode, constraints, sessionConstraints?, rateLimits?, version }

client.logDecision(request)

Log a client-side validation decision to the server. Fire-and-forget.

client.logDecision({
  tool_name: 'send_email',
  arguments: { to: 'user@example.com' },
  decision: 'allow',
  mode: 'deterministic',
  latency_ms: 2,
  source: 'client',
});

client.registerTools(tools)

Register tool signatures with the cloud for policy generation.

await client.registerTools([{
  name: 'send_email',
  description: 'Send an email',
  parameters: [
    { name: 'to', type: 'string', required: true },
    { name: 'subject', type: 'string', required: true },
    { name: 'body', type: 'string', required: true },
  ],
}]);

veto.exportDecisions(options?)

Export the local decision history as JSON or CSV. Added in v1.6.0.

const json = veto.exportDecisions(); // JSON by default
const csv = veto.exportDecisions({ format: 'csv' });

Options

OptionTypeDefaultDescription
format"json" | "csv""json"Output format

Each record includes normalized audit fields: timestamp, tool_name, arguments, policy_version, rule_id, decision, reason.

Framework integrations

The SDK provides deep integrations with popular AI frameworks. Each integration intercepts tool calls at the framework level — no changes to your agent code.

FrameworkImportGuide
LangChainveto-sdk/integrations/langchainLangChain Integration
Vercel AI SDKveto-sdk/integrations/vercel-aiVercel AI SDK Integration
browser-useveto-sdk/integrations/browser-useBrowser-Use Integration
MCPveto-sdk/providersMCP Integration

Provider adapters

The SDK includes adapters that convert tool definitions between formats:

import { toAnthropic, toOpenAI } from 'veto-sdk/providers/adapters';

const anthropicTools = toAnthropic(openAITools);
const openAITools = toOpenAI(anthropicTools);

MCP tools are converted automatically by veto.wrap(). For manual use:

import { fromMCP, isMCPTool } from 'veto-sdk/providers';

if (isMCPTool(tool)) {
  const normalized = fromMCP(tool);
}

Exports

ImportWhat it provides
veto-sdkCore Veto class, ToolCallDeniedError, BudgetExceededError, BudgetStatus, VetoCloudClient, ApprovalTimeoutError
veto-sdk/providers/*Provider adapters (toOpenAI, fromOpenAI, toAnthropic, fromAnthropic, fromMCP, isMCPTool)
veto-sdk/rulesRule parsing and matching utilities
veto-sdk/kernelLocal model evaluation via Ollama
veto-sdk/customDirect LLM provider integration
veto-sdk/compilerRule compiler utilities
veto-sdk/benchmarkPerformance benchmarking
veto-sdk/integrations/langchainLangChain middleware, ToolNode wrapper, callback handler
veto-sdk/integrations/vercel-aiVercel AI SDK middleware with streaming support
veto-sdk/integrations/browser-useBrowser-use controller wrapper