Documentation Index
Fetch the complete documentation index at: https://mintlify.com/plawio/veto/llms.txt
Use this file to discover all available pages before exploring further.
Veto sits between your AI agent and tool execution, intercepting every tool call to validate it against your rules before allowing it to run.
Architecture overview
Veto acts as a transparent proxy layer that wraps your tools without changing their interface:
┌───────────┐ ┌────────────┐ ┌──────────────┐
│ AI Agent │────────▶│ Veto │────────▶│ Your Tools │
│ (LLM) │ │ (Guard) │ │ (Handlers) │
└───────────┘ └────────────┘ └──────────────┘
│
┌─────┴──────┐
│ YAML Rules │ block · allow · ask
└────────────┘
The interception flow
When you wrap your tools with Veto, it creates a validation layer that intercepts every call:
import { Veto } from 'veto-sdk';
const veto = await Veto.init();
const guarded = veto.wrap(tools); // Inject guardrails
// Pass guarded tools to your agent
const agent = createAgent({ tools: guarded });
The agent is unaware it’s being governed. Your tools are unchanged. No behavior change for the AI.
Validation process
When the agent calls a tool, Veto follows this validation flow:
Tool call intercepted
Veto captures the tool name and arguments before execution
Rule matching
Veto finds all rules that apply to this tool
Condition evaluation
Deterministic conditions are checked first (local, zero latency)
Decision made
Based on the first matching rule:
- allow → Tool executes normally
- block → Throws
ToolCallDeniedError with reason
- require_approval → Routes to human approval queue
- warn → Logs warning, executes tool
- log → Logs info, executes tool
Rule matching
Veto uses a two-step process to determine which rules apply:
- Tool-specific rules — Rules with a matching
tools list
- Global rules — Rules with no
tools field (apply to every call)
rules:
# Tool-specific: only applies to transfer_funds
- id: block-large-transfers
action: block
tools: [transfer_funds]
conditions:
- field: arguments.amount
operator: greater_than
value: 10000
# Global: applies to ALL tools
- id: log-all-calls
action: log
# No tools field = global
Condition evaluation
Conditions are evaluated in order until one matches. Veto prioritizes deterministic conditions for performance:
Static conditions
Expression conditions
conditions:
- field: arguments.amount
operator: greater_than
value: 1000
- field: arguments.currency
operator: in
value: ["USD", "EUR", "GBP"]
Static conditions run locally with zero latency and no API calls. They’re perfect for numeric limits, string matching, and structural validation.conditions:
- expression: "arguments.amount > 10000"
- expression: "arguments.to_account starts_with 'EXT-'"
Expression conditions use Veto’s AST-based compiler for complex logic. They’re evaluated locally with minimal overhead.
Decision flow
Once a rule matches, Veto takes action based on the rule’s action field:
Block
Denies the tool call and throws an error:
try {
await tools[0].handler({ amount: 15000, recipient: "ACME" });
} catch (error) {
if (error instanceof ToolCallDeniedError) {
console.log(error.reason); // "Transfer amount exceeds $10,000 limit"
console.log(error.validationResult.ruleId); // "block-large-transfers"
}
}
Allow
Explicitly allows the tool call to proceed. Useful for whitelisting specific patterns:
- id: allow-read-operations
action: allow
tools: [get_balance, list_transactions]
Require approval
Routes the call to a human approval workflow. See Human-in-the-loop for details.
- id: require-approval-production
action: require_approval
tools: [deploy]
conditions:
- field: arguments.environment
operator: equals
value: production
Warn and Log
Non-blocking actions that record the event without stopping execution:
- warn — Logs a warning message (severity: warning)
- log — Logs an info message (severity: info)
- id: warn-external-api
action: warn
conditions:
- field: arguments.url
operator: starts_with
value: "https://external."
History tracking
Veto maintains a call history for each session, enabling:
- Sequential rules — Block actions based on previous calls
- Rate limiting — Prevent repeated sensitive operations
- Audit trails — Export complete decision history
Sequential rules example
- id: block-send-after-secret-read
name: Block email after reading secrets
action: block
tools: [send_email]
blocked_by:
- tool: read_file
within: 3600 # seconds
conditions:
- field: arguments.path
operator: starts_with
value: "/etc/secrets"
If the agent reads a secret file and then tries to send an email within 1 hour, Veto blocks the email.
Exporting decisions
const veto = await Veto.init();
// After some tool calls...
const json = veto.exportDecisions("json");
const csv = veto.exportDecisions("csv");
console.log(json);
// [
// {
// timestamp: "2024-03-04T10:30:00Z",
// tool_name: "transfer_funds",
// arguments: { amount: 15000 },
// decision: "deny",
// rule_id: "block-large-transfers",
// reason: "Transfer amount exceeds limit"
// }
// ]
- Deterministic conditions: ~0.1ms overhead (local evaluation)
- Expression conditions: ~0.5ms overhead (AST evaluation)
- No network calls for local validation mode
- Type preservation: Wrapped tools maintain full TypeScript types
- Zero runtime dependencies beyond your chosen validation mode
Next steps
Rules
Learn the complete YAML rule format
Validation modes
Choose between local, API, or cloud validation
Human-in-the-loop
Set up approval workflows
Writing rules
Best practices for rule design