veto-sdk 2.8.1 → 2.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -1007
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +2 -3
- package/dist/cli/init.js.map +1 -1
- package/dist/core/protect.d.ts.map +1 -1
- package/dist/core/protect.js +53 -10
- package/dist/core/protect.js.map +1 -1
- package/dist/index.d.ts +7 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -18
- package/dist/index.js.map +1 -1
- package/dist/rules/policy-packs.d.ts.map +1 -1
- package/dist/rules/policy-packs.js +1 -0
- package/dist/rules/policy-packs.js.map +1 -1
- package/package.json +6 -5
- package/packs/safe-defaults.yaml +98 -0
package/README.md
CHANGED
|
@@ -3,1078 +3,156 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/veto-sdk)
|
|
4
4
|
[](../../LICENSE)
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
TypeScript policy runtime for AI agent tool calls. Veto wraps your tools, evaluates deterministic policy before each handler runs, and preserves the original tool interface.
|
|
7
7
|
|
|
8
|
-
##
|
|
9
|
-
|
|
10
|
-
1. **Initialize** Veto (loads your YAML rules).
|
|
11
|
-
2. **Wrap** your tools with `veto.wrap()`.
|
|
12
|
-
3. **Pass** the wrapped tools to your agent -- types preserved, interface unchanged.
|
|
13
|
-
|
|
14
|
-
When the AI calls a tool, Veto automatically:
|
|
15
|
-
|
|
16
|
-
1. Intercepts the call.
|
|
17
|
-
2. Validates arguments against your rules (deterministic conditions first, optional LLM for semantic rules).
|
|
18
|
-
3. **allow** -- executes. **block** -- denied with reason. **ask** -- approval queue.
|
|
19
|
-
|
|
20
|
-
## Installation
|
|
8
|
+
## Install
|
|
21
9
|
|
|
22
10
|
```bash
|
|
23
11
|
npm install veto-sdk
|
|
24
12
|
```
|
|
25
13
|
|
|
26
|
-
|
|
14
|
+
## Quick start
|
|
27
15
|
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
|
|
16
|
+
```ts
|
|
17
|
+
import { protect } from "veto-sdk";
|
|
18
|
+
|
|
19
|
+
const safeTools = await protect(tools);
|
|
20
|
+
const agent = createAgent({ tools: safeTools });
|
|
31
21
|
```
|
|
32
22
|
|
|
33
|
-
|
|
23
|
+
`protect(tools)` is the public entrypoint. It loads `./veto/veto.config.yaml` and `./veto/rules/*.yaml` when present. Without local policy or explicit options, it uses the built-in `@veto/safe-defaults` pack in observe mode: suspicious destructive shell, file, database, or money-movement network patterns are warned/logged, not blocked.
|
|
34
24
|
|
|
35
|
-
|
|
25
|
+
## 60-second denied call
|
|
36
26
|
|
|
37
27
|
```bash
|
|
28
|
+
npm i veto-sdk openai
|
|
38
29
|
npx veto init
|
|
30
|
+
node examples/60-second-denied-call/denied-call.mjs
|
|
39
31
|
```
|
|
40
32
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
### 2. Wrap your tools
|
|
44
|
-
|
|
45
|
-
`wrap()` is provider-agnostic -- works with LangChain, Vercel AI SDK, or any custom tool object.
|
|
33
|
+
`npx veto init` creates blocking local defaults in `veto/rules/defaults.yaml`, so the example deterministically denies `bash` with `rm -rf` before the handler runs.
|
|
46
34
|
|
|
47
|
-
|
|
48
|
-
import { Veto } from "veto-sdk";
|
|
49
|
-
|
|
50
|
-
const veto = await Veto.init();
|
|
35
|
+
## Python parity
|
|
51
36
|
|
|
52
|
-
|
|
53
|
-
|
|
37
|
+
```python
|
|
38
|
+
from veto import protect
|
|
54
39
|
|
|
55
|
-
|
|
40
|
+
safe = await protect(tools)
|
|
41
|
+
agent = create_agent(tools=safe)
|
|
56
42
|
```
|
|
57
43
|
|
|
58
|
-
|
|
44
|
+
## Local policy
|
|
59
45
|
|
|
60
|
-
|
|
46
|
+
```bash
|
|
47
|
+
npx veto init
|
|
48
|
+
```
|
|
61
49
|
|
|
62
50
|
```yaml
|
|
63
51
|
rules:
|
|
64
|
-
- id:
|
|
65
|
-
name:
|
|
52
|
+
- id: block-large-transfers
|
|
53
|
+
name: Block transfers over $1,000
|
|
54
|
+
enabled: true
|
|
55
|
+
severity: high
|
|
66
56
|
action: block
|
|
67
|
-
tools:
|
|
68
|
-
- transfer_funds
|
|
57
|
+
tools: [transfer_funds]
|
|
69
58
|
conditions:
|
|
70
59
|
- field: arguments.amount
|
|
71
60
|
operator: greater_than
|
|
72
61
|
value: 1000
|
|
73
62
|
```
|
|
74
63
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
### veto.config.yaml
|
|
78
|
-
|
|
79
|
-
```yaml
|
|
80
|
-
version: "1.0"
|
|
81
|
-
|
|
82
|
-
# "strict" blocks calls, "log" only logs them, "shadow" computes decisions but never blocks
|
|
83
|
-
mode: "strict"
|
|
84
|
-
|
|
85
|
-
# Validation backend
|
|
86
|
-
validation:
|
|
87
|
-
mode: "local" # "local" | "api" | "kernel" | "custom" | "cloud"
|
|
88
|
-
|
|
89
|
-
# Custom LLM provider (if mode is "custom")
|
|
90
|
-
custom:
|
|
91
|
-
provider: "gemini" # openai | anthropic | gemini | openrouter
|
|
92
|
-
model: "gemini-3-flash-preview"
|
|
93
|
-
|
|
94
|
-
# Cloud mode
|
|
95
|
-
cloud:
|
|
96
|
-
apiKey: "veto_..."
|
|
97
|
-
baseUrl: "https://api.veto.so"
|
|
98
|
-
|
|
99
|
-
logging:
|
|
100
|
-
level: "info"
|
|
101
|
-
|
|
102
|
-
rules:
|
|
103
|
-
directory: "./rules"
|
|
104
|
-
recursive: true
|
|
105
|
-
|
|
106
|
-
# Human-in-the-loop approval (for action: require_approval)
|
|
107
|
-
approval:
|
|
108
|
-
callbackUrl: "http://localhost:8787/approvals"
|
|
109
|
-
timeout: 30000
|
|
110
|
-
timeoutBehavior: "block" # "block" | "allow"
|
|
111
|
-
|
|
112
|
-
# Webhook event routing
|
|
113
|
-
events:
|
|
114
|
-
webhook:
|
|
115
|
-
url: "https://hooks.slack.com/services/..."
|
|
116
|
-
on: [deny, require_approval, budget_exceeded]
|
|
117
|
-
min_severity: high
|
|
118
|
-
format: slack # slack | pagerduty | generic | cef
|
|
119
|
-
|
|
120
|
-
# Tamper-evident audit log
|
|
121
|
-
audit:
|
|
122
|
-
enabled: true
|
|
123
|
-
path: ".veto/audit.log"
|
|
124
|
-
|
|
125
|
-
# Economic authorization (x402, MPP, AP2)
|
|
126
|
-
economic:
|
|
127
|
-
budgets:
|
|
128
|
-
session: { limit: 500, currency: "USD" }
|
|
129
|
-
cost_extraction:
|
|
130
|
-
field: "arguments.amount"
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
### VetoOptions
|
|
134
|
-
|
|
135
|
-
```typescript
|
|
136
|
-
const veto = await Veto.init({
|
|
137
|
-
configDir: "./veto",
|
|
138
|
-
mode: "strict", // 'strict' | 'log' | 'shadow'
|
|
139
|
-
logLevel: "info",
|
|
140
|
-
sessionId: "sess_123",
|
|
141
|
-
agentId: "agent_1",
|
|
142
|
-
userId: "user_42",
|
|
143
|
-
role: "trader",
|
|
144
|
-
apiKey: "veto_...", // auto-detects cloud mode
|
|
145
|
-
validators: [myCustomValidator],
|
|
146
|
-
onApprovalRequired: (ctx, approvalId) => {
|
|
147
|
-
/* show UI */
|
|
148
|
-
},
|
|
149
|
-
onDecisionMade: (result) => {
|
|
150
|
-
/* log, emit metrics */
|
|
151
|
-
},
|
|
152
|
-
telemetry: {
|
|
153
|
-
enabled: true,
|
|
154
|
-
serviceName: "my-agent",
|
|
155
|
-
},
|
|
156
|
-
audit: {
|
|
157
|
-
enabled: true,
|
|
158
|
-
path: ".veto/audit.log",
|
|
159
|
-
},
|
|
160
|
-
});
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
## API Reference
|
|
164
|
-
|
|
165
|
-
### `Veto.init(options?): Promise<Veto>`
|
|
166
|
-
|
|
167
|
-
Initialize Veto. Loads configuration and rules from `./veto` by default.
|
|
168
|
-
|
|
169
|
-
```typescript
|
|
170
|
-
const veto = await Veto.init();
|
|
171
|
-
const veto = await Veto.init({ configDir: "./policies", mode: "log" });
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
### `veto.wrap<T>(tools: T[]): T[]`
|
|
64
|
+
Actions are `block`, `allow`, `warn`, `log`, and `require_approval`.
|
|
175
65
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
```typescript
|
|
179
|
-
const wrappedForLangChain = veto.wrap(langChainTools);
|
|
180
|
-
const wrappedForVercel = veto.wrap(vercelTools);
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
### `veto.wrapTool<T>(tool: T): T`
|
|
184
|
-
|
|
185
|
-
Wrap a single tool.
|
|
186
|
-
|
|
187
|
-
```typescript
|
|
188
|
-
const safeTool = veto.wrapTool(myTool);
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
### `veto.guard(toolName, args, context?): Promise<GuardResult>`
|
|
192
|
-
|
|
193
|
-
Standalone validation without wrapping or executing a tool. Returns the raw decision.
|
|
194
|
-
|
|
195
|
-
```typescript
|
|
196
|
-
const result = await veto.guard("transfer_funds", { amount: 5000 });
|
|
197
|
-
// { decision: 'deny', reason: 'Amount exceeds limit', ruleId: 'limit-transfers', severity: 'high' }
|
|
198
|
-
|
|
199
|
-
const result = await veto.guard(
|
|
200
|
-
"send_email",
|
|
201
|
-
{ to: "ceo@corp.com" },
|
|
202
|
-
{
|
|
203
|
-
sessionId: "sess_123",
|
|
204
|
-
agentId: "agent_1",
|
|
205
|
-
userId: "user_42",
|
|
206
|
-
}
|
|
207
|
-
);
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
`GuardResult`:
|
|
211
|
-
|
|
212
|
-
```typescript
|
|
213
|
-
interface GuardResult {
|
|
214
|
-
decision: "allow" | "deny" | "require_approval";
|
|
215
|
-
reason?: string;
|
|
216
|
-
ruleId?: string;
|
|
217
|
-
severity?: "critical" | "high" | "medium" | "low" | "info";
|
|
218
|
-
approvalId?: string;
|
|
219
|
-
shadow?: boolean;
|
|
220
|
-
economicDenial?: EconomicDenialDetails;
|
|
221
|
-
}
|
|
222
|
-
```
|
|
66
|
+
## API
|
|
223
67
|
|
|
224
68
|
### `protect(tools, options?)`
|
|
225
69
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
```typescript
|
|
70
|
+
```ts
|
|
229
71
|
import { protect } from "veto-sdk";
|
|
230
72
|
|
|
231
|
-
const safeTools = await protect(
|
|
232
|
-
rules: [
|
|
233
|
-
{
|
|
234
|
-
id: "no-delete",
|
|
235
|
-
name: "Block deletes",
|
|
236
|
-
action: "block",
|
|
237
|
-
tools: ["delete_file"],
|
|
238
|
-
},
|
|
239
|
-
],
|
|
240
|
-
mode: "strict",
|
|
241
|
-
});
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
### `veto.getHistoryStats(): HistoryStats`
|
|
245
|
-
|
|
246
|
-
Statistics on allowed vs blocked calls.
|
|
247
|
-
|
|
248
|
-
```typescript
|
|
249
|
-
const stats = veto.getHistoryStats();
|
|
250
|
-
// { totalCalls: 5, allowedCalls: 4, deniedCalls: 1, ... }
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
### `veto.clearHistory()`
|
|
254
|
-
|
|
255
|
-
Reset decision history.
|
|
256
|
-
|
|
257
|
-
### `veto.exportDecisions(format): string`
|
|
258
|
-
|
|
259
|
-
Export decision history as JSON or CSV.
|
|
260
|
-
|
|
261
|
-
```typescript
|
|
262
|
-
const json = veto.exportDecisions("json");
|
|
263
|
-
const csv = veto.exportDecisions("csv");
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
### `veto.dispose()`
|
|
267
|
-
|
|
268
|
-
Clean up resources (timers, connections).
|
|
269
|
-
|
|
270
|
-
---
|
|
271
|
-
|
|
272
|
-
## Rate Limiting
|
|
273
|
-
|
|
274
|
-
Rules can include sliding-window rate limits. When the limit is exceeded, the rule's `action` fires.
|
|
275
|
-
|
|
276
|
-
```yaml
|
|
277
|
-
rules:
|
|
278
|
-
- id: api-rate-limit
|
|
279
|
-
name: Limit API calls
|
|
280
|
-
action: block
|
|
281
|
-
tools:
|
|
282
|
-
- call_external_api
|
|
283
|
-
rate_limits:
|
|
284
|
-
- scope: session # agent | user | session | global
|
|
285
|
-
max_calls: 10
|
|
286
|
-
window_seconds: 60
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
By default, rate limit state is stored in memory. For distributed systems, plug in Redis:
|
|
290
|
-
|
|
291
|
-
```typescript
|
|
292
|
-
import { RedisRateLimitStore } from "veto-sdk";
|
|
293
|
-
import { createClient } from "redis";
|
|
294
|
-
|
|
295
|
-
const redis = createClient();
|
|
296
|
-
await redis.connect();
|
|
297
|
-
|
|
298
|
-
const store = new RedisRateLimitStore(redis, "veto:rl:");
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
The `RedisRateLimitStore` uses a Lua script for atomic sliding-window checks (no TOCTOU race).
|
|
302
|
-
|
|
303
|
-
### `RateLimitStore` interface
|
|
304
|
-
|
|
305
|
-
Implement this to bring your own store:
|
|
306
|
-
|
|
307
|
-
```typescript
|
|
308
|
-
interface RateLimitStore {
|
|
309
|
-
checkAndRecord(
|
|
310
|
-
key: string,
|
|
311
|
-
maxCalls: number,
|
|
312
|
-
windowMs: number
|
|
313
|
-
): boolean | Promise<boolean>;
|
|
314
|
-
clear(): void | Promise<void>;
|
|
315
|
-
}
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
---
|
|
319
|
-
|
|
320
|
-
## Audit Chain
|
|
321
|
-
|
|
322
|
-
Tamper-evident append-only audit log. Each decision is hashed with SHA-256 over the previous hash + the record, forming a hash chain. Any mutation to a historical record invalidates all subsequent hashes.
|
|
323
|
-
|
|
324
|
-
```typescript
|
|
325
|
-
import { computeChainHash, GENESIS_HASH } from "veto-sdk";
|
|
326
|
-
|
|
327
|
-
let prevHash = GENESIS_HASH; // empty string
|
|
328
|
-
|
|
329
|
-
const entry1 = { tool: "transfer_funds", decision: "allow", ts: Date.now() };
|
|
330
|
-
prevHash = computeChainHash(prevHash, entry1);
|
|
331
|
-
|
|
332
|
-
const entry2 = { tool: "delete_account", decision: "deny", ts: Date.now() };
|
|
333
|
-
prevHash = computeChainHash(prevHash, entry2);
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
Enable via config:
|
|
337
|
-
|
|
338
|
-
```yaml
|
|
339
|
-
audit:
|
|
340
|
-
enabled: true
|
|
341
|
-
path: ".veto/audit.log"
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
Verify integrity from the CLI:
|
|
345
|
-
|
|
346
|
-
```bash
|
|
347
|
-
npx veto-cli audit verify
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
---
|
|
351
|
-
|
|
352
|
-
## OpenTelemetry
|
|
353
|
-
|
|
354
|
-
Optional tracing via `@opentelemetry/api`. Zero overhead when the package is not installed -- all calls become no-ops.
|
|
355
|
-
|
|
356
|
-
```typescript
|
|
357
|
-
import { tryLoadOtel, SpanStatusCode } from "veto-sdk";
|
|
358
|
-
|
|
359
|
-
const tracer = await tryLoadOtel("my-service");
|
|
360
|
-
|
|
361
|
-
const span = tracer.startSpan("validate-tool-call");
|
|
362
|
-
span.setAttribute("tool", "transfer_funds");
|
|
363
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
364
|
-
span.end();
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
Veto instruments itself automatically when `@opentelemetry/api` is present. Disable via config:
|
|
368
|
-
|
|
369
|
-
```typescript
|
|
370
|
-
const veto = await Veto.init({
|
|
371
|
-
telemetry: { enabled: false },
|
|
372
|
-
});
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
### `VetoTracer` / `VetoSpan`
|
|
376
|
-
|
|
377
|
-
```typescript
|
|
378
|
-
interface VetoTracer {
|
|
379
|
-
startSpan(name: string): VetoSpan;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
interface VetoSpan {
|
|
383
|
-
setAttribute(key: string, value: string | number | boolean): void;
|
|
384
|
-
setStatus(status: { code: number; message?: string }): void;
|
|
385
|
-
end(): void;
|
|
386
|
-
}
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
---
|
|
390
|
-
|
|
391
|
-
## SSE Proxy (`veto intercept`)
|
|
392
|
-
|
|
393
|
-
HTTP proxy that intercepts OpenAI and Anthropic streaming responses, validates tool calls in SSE streams before forwarding. Zero code changes to your agent.
|
|
394
|
-
|
|
395
|
-
### CLI usage
|
|
396
|
-
|
|
397
|
-
```bash
|
|
398
|
-
# Proxy OpenAI (default target: https://api.openai.com)
|
|
399
|
-
npx veto-cli intercept --port 8080
|
|
400
|
-
|
|
401
|
-
# Proxy Anthropic
|
|
402
|
-
npx veto-cli intercept --port 8080 --target https://api.anthropic.com --format anthropic
|
|
403
|
-
|
|
404
|
-
# Auto-detect format from target URL
|
|
405
|
-
npx veto-cli intercept --port 8080 --target https://api.anthropic.com
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
Then point your SDK at the proxy:
|
|
409
|
-
|
|
410
|
-
```typescript
|
|
411
|
-
const openai = new OpenAI({ baseURL: "http://localhost:8080/v1" });
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
### Programmatic usage
|
|
415
|
-
|
|
416
|
-
```typescript
|
|
417
|
-
import { startProxyServer } from "veto-sdk/proxy";
|
|
418
|
-
|
|
419
|
-
const stop = await startProxyServer({
|
|
420
|
-
port: 8080,
|
|
421
|
-
target: "https://api.openai.com",
|
|
422
|
-
maxBufferBytes: 1024 * 1024, // 1 MB
|
|
423
|
-
configDir: "./veto",
|
|
424
|
-
format: "auto", // 'openai' | 'anthropic' | 'auto'
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
// Later:
|
|
428
|
-
await stop();
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
### `ProxyConfig`
|
|
432
|
-
|
|
433
|
-
```typescript
|
|
434
|
-
interface ProxyConfig {
|
|
435
|
-
port: number; // default: 8080
|
|
436
|
-
target: string; // default: https://api.openai.com
|
|
437
|
-
maxBufferBytes: number; // default: 1 MB
|
|
438
|
-
configDir: string; // default: ./veto
|
|
439
|
-
format?: "openai" | "anthropic" | "auto";
|
|
440
|
-
}
|
|
441
|
-
```
|
|
442
|
-
|
|
443
|
-
Architecture: non-tool-call responses are passed through. Tool-call responses are buffered until the stream signals completion, validated, then either flushed (allow) or replaced with a synthetic error (block). Buffer overflow beyond `maxBufferBytes` flushes without validation.
|
|
444
|
-
|
|
445
|
-
---
|
|
446
|
-
|
|
447
|
-
## Policy Testing
|
|
448
|
-
|
|
449
|
-
Deterministic test runner for policy rules. Loads YAML fixture files and evaluates test cases against your rules. No LLM, no network. Pure local replay.
|
|
450
|
-
|
|
451
|
-
### CLI
|
|
452
|
-
|
|
453
|
-
```bash
|
|
454
|
-
npx veto-cli test
|
|
455
|
-
npx veto-cli test --fixtures ./veto/tests --policy ./veto --coverage
|
|
456
|
-
```
|
|
457
|
-
|
|
458
|
-
### Programmatic
|
|
459
|
-
|
|
460
|
-
```typescript
|
|
461
|
-
import { runTests } from "veto-sdk";
|
|
462
|
-
|
|
463
|
-
const result = await runTests({
|
|
464
|
-
fixturesPath: "./veto/tests",
|
|
465
|
-
policyPath: "./veto",
|
|
466
|
-
coverage: true,
|
|
467
|
-
quiet: false,
|
|
468
|
-
});
|
|
469
|
-
|
|
470
|
-
console.log(`${result.passed}/${result.total} passed, ${result.failed} failed`);
|
|
471
|
-
```
|
|
472
|
-
|
|
473
|
-
### Fixture format
|
|
474
|
-
|
|
475
|
-
```yaml
|
|
476
|
-
suite: Financial rules
|
|
477
|
-
tests:
|
|
478
|
-
- id: block-large-transfer
|
|
479
|
-
description: Block transfers over $1000
|
|
480
|
-
tool: transfer_funds
|
|
481
|
-
arguments:
|
|
482
|
-
amount: 5000
|
|
483
|
-
recipient: vendor_123
|
|
484
|
-
expect:
|
|
485
|
-
decision: block
|
|
486
|
-
rule_id: limit-transfers
|
|
487
|
-
|
|
488
|
-
- id: allow-small-transfer
|
|
489
|
-
tool: transfer_funds
|
|
490
|
-
arguments:
|
|
491
|
-
amount: 50
|
|
492
|
-
expect:
|
|
493
|
-
decision: allow
|
|
494
|
-
```
|
|
495
|
-
|
|
496
|
-
### `RunTestsOptions`
|
|
497
|
-
|
|
498
|
-
```typescript
|
|
499
|
-
interface RunTestsOptions {
|
|
500
|
-
fixturesPath?: string; // default: ./veto/tests
|
|
501
|
-
policyPath?: string; // default: ./veto
|
|
502
|
-
coverage?: boolean; // report untested rule IDs
|
|
503
|
-
quiet?: boolean; // suppress console output
|
|
504
|
-
}
|
|
505
|
-
```
|
|
506
|
-
|
|
507
|
-
### `VetoTestRunResult`
|
|
508
|
-
|
|
509
|
-
```typescript
|
|
510
|
-
interface VetoTestRunResult {
|
|
511
|
-
total: number;
|
|
512
|
-
passed: number;
|
|
513
|
-
failed: number;
|
|
514
|
-
results: VetoTestResult[];
|
|
515
|
-
loadErrors?: string[];
|
|
516
|
-
}
|
|
517
|
-
```
|
|
518
|
-
|
|
519
|
-
---
|
|
520
|
-
|
|
521
|
-
## Provider Adapters
|
|
522
|
-
|
|
523
|
-
Convert tool definitions and tool calls between Veto's internal format and provider-specific formats.
|
|
524
|
-
|
|
525
|
-
### OpenAI
|
|
526
|
-
|
|
527
|
-
```typescript
|
|
528
|
-
import { toOpenAITools, fromOpenAI, fromOpenAIToolCall } from "veto-sdk";
|
|
529
|
-
|
|
530
|
-
// Veto definitions -> OpenAI format
|
|
531
|
-
const openaiTools = toOpenAITools(definitions);
|
|
532
|
-
|
|
533
|
-
// OpenAI tool -> Veto definition
|
|
534
|
-
const vetoDef = fromOpenAI(openaiTool);
|
|
535
|
-
|
|
536
|
-
// OpenAI tool_call -> Veto ToolCall
|
|
537
|
-
const vetoCall = fromOpenAIToolCall(toolCall);
|
|
538
|
-
```
|
|
539
|
-
|
|
540
|
-
### Anthropic
|
|
541
|
-
|
|
542
|
-
```typescript
|
|
543
|
-
import {
|
|
544
|
-
toAnthropicTools,
|
|
545
|
-
fromAnthropic,
|
|
546
|
-
fromAnthropicToolUse,
|
|
547
|
-
} from "veto-sdk";
|
|
548
|
-
|
|
549
|
-
const anthropicTools = toAnthropicTools(definitions);
|
|
550
|
-
const vetoDef = fromAnthropic(anthropicTool);
|
|
551
|
-
const vetoCall = fromAnthropicToolUse(toolUseBlock);
|
|
552
|
-
```
|
|
553
|
-
|
|
554
|
-
### Google (Gemini)
|
|
555
|
-
|
|
556
|
-
```typescript
|
|
557
|
-
import { toGoogleTool, fromGoogleFunctionCall } from "veto-sdk";
|
|
558
|
-
|
|
559
|
-
const googleTool = toGoogleTool(definitions);
|
|
560
|
-
const vetoCall = fromGoogleFunctionCall(functionCall);
|
|
561
|
-
```
|
|
562
|
-
|
|
563
|
-
### MCP (Model Context Protocol)
|
|
564
|
-
|
|
565
|
-
```typescript
|
|
566
|
-
import { toMCPTools, fromMCP, fromMCPToolCall, isMCPTool } from "veto-sdk";
|
|
567
|
-
|
|
568
|
-
const mcpTools = toMCPTools(definitions);
|
|
569
|
-
const vetoDef = fromMCP(mcpTool);
|
|
570
|
-
const vetoCall = fromMCPToolCall("tool_name", mcpArgs);
|
|
571
|
-
```
|
|
572
|
-
|
|
573
|
-
---
|
|
574
|
-
|
|
575
|
-
## Output Redaction
|
|
576
|
-
|
|
577
|
-
Built-in regex patterns for detecting sensitive data in tool outputs.
|
|
578
|
-
|
|
579
|
-
```typescript
|
|
580
|
-
import {
|
|
581
|
-
OUTPUT_PATTERNS,
|
|
582
|
-
OUTPUT_PATTERN_SSN,
|
|
583
|
-
OUTPUT_PATTERN_CREDIT_CARD,
|
|
584
|
-
OUTPUT_PATTERN_OPENAI_API_KEY,
|
|
585
|
-
OUTPUT_PATTERN_GITHUB_API_KEY,
|
|
586
|
-
OUTPUT_PATTERN_AWS_API_KEY,
|
|
587
|
-
OUTPUT_PATTERN_EMAIL,
|
|
588
|
-
OUTPUT_PATTERN_US_PHONE,
|
|
589
|
-
} from "veto-sdk";
|
|
590
|
-
|
|
591
|
-
// OUTPUT_PATTERNS is an object with all patterns:
|
|
592
|
-
// { ssn, creditCard, openAIApiKey, githubApiKey, awsApiKey, email, usPhone }
|
|
593
|
-
```
|
|
594
|
-
|
|
595
|
-
Use these in output rules to block or redact sensitive data from tool responses:
|
|
596
|
-
|
|
597
|
-
```yaml
|
|
598
|
-
output_rules:
|
|
599
|
-
- id: redact-ssn
|
|
600
|
-
name: Redact SSNs from output
|
|
601
|
-
action: redact
|
|
602
|
-
redact_with: "[REDACTED SSN]"
|
|
603
|
-
output_conditions:
|
|
604
|
-
- field: output
|
|
605
|
-
operator: matches
|
|
606
|
-
value: '\b\d{3}-\d{2}-\d{4}\b'
|
|
607
|
-
|
|
608
|
-
- id: block-api-keys
|
|
609
|
-
name: Block responses containing API keys
|
|
610
|
-
action: block
|
|
611
|
-
severity: critical
|
|
612
|
-
output_conditions:
|
|
613
|
-
- field: output
|
|
614
|
-
operator: matches
|
|
615
|
-
value: '\bsk-(?:proj-)?[A-Za-z0-9]{20,}\b'
|
|
616
|
-
```
|
|
617
|
-
|
|
618
|
-
---
|
|
619
|
-
|
|
620
|
-
## Webhooks
|
|
621
|
-
|
|
622
|
-
Route policy decision events to external systems. Four payload formats are supported.
|
|
623
|
-
|
|
624
|
-
### Configuration
|
|
625
|
-
|
|
626
|
-
```yaml
|
|
627
|
-
events:
|
|
628
|
-
webhook:
|
|
629
|
-
url: "https://hooks.slack.com/services/T00/B00/xxx"
|
|
630
|
-
on: [deny, require_approval, budget_exceeded]
|
|
631
|
-
min_severity: high
|
|
632
|
-
format: slack
|
|
633
|
-
redact_arguments: true # or ["password", "ssn"] for selective redaction
|
|
634
|
-
```
|
|
635
|
-
|
|
636
|
-
Event types: `deny`, `require_approval`, `budget_exceeded`, `budget_warning`, `approval_triggered`, `spend_committed`, `protocol_detected`.
|
|
637
|
-
|
|
638
|
-
### Programmatic formatting
|
|
639
|
-
|
|
640
|
-
```typescript
|
|
641
|
-
import {
|
|
642
|
-
formatSlackPayload,
|
|
643
|
-
formatPagerDutyPayload,
|
|
644
|
-
formatGenericPayload,
|
|
645
|
-
formatCefPayload,
|
|
646
|
-
} from "veto-sdk";
|
|
647
|
-
|
|
648
|
-
const event = {
|
|
649
|
-
eventType: "deny",
|
|
650
|
-
toolName: "transfer_funds",
|
|
651
|
-
arguments: { amount: 5000 },
|
|
652
|
-
decision: "deny",
|
|
653
|
-
reason: "Amount exceeds limit",
|
|
654
|
-
ruleId: "limit-transfers",
|
|
655
|
-
severity: "high",
|
|
656
|
-
timestamp: new Date().toISOString(),
|
|
657
|
-
};
|
|
658
|
-
|
|
659
|
-
const slack = formatSlackPayload(event); // Slack Block Kit
|
|
660
|
-
const pd = formatPagerDutyPayload(event); // PagerDuty Events API v2
|
|
661
|
-
const json = formatGenericPayload(event); // Plain JSON
|
|
662
|
-
const cef = formatCefPayload(event); // ArcSight CEF string
|
|
663
|
-
```
|
|
664
|
-
|
|
665
|
-
---
|
|
666
|
-
|
|
667
|
-
## VetoAdmin (Cloud Management)
|
|
668
|
-
|
|
669
|
-
Management client for the Veto Cloud API. Full CRUD for policies, decisions, approvals, MCP upstreams, and API keys.
|
|
670
|
-
|
|
671
|
-
```typescript
|
|
672
|
-
import { VetoAdmin } from "veto-sdk";
|
|
673
|
-
|
|
674
|
-
const admin = new VetoAdmin({
|
|
675
|
-
apiKey: process.env.VETO_API_KEY!,
|
|
676
|
-
baseUrl: "https://api.veto.so", // optional, this is the default
|
|
677
|
-
timeout: 30000, // optional
|
|
678
|
-
});
|
|
73
|
+
const safeTools = await protect(tools);
|
|
679
74
|
```
|
|
680
75
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
```typescript
|
|
684
|
-
const policies = await admin.listPolicies();
|
|
685
|
-
const policy = await admin.getPolicy("transfer_funds");
|
|
686
|
-
|
|
687
|
-
await admin.createPolicy({
|
|
688
|
-
toolName: "transfer_funds",
|
|
689
|
-
mode: "deterministic",
|
|
690
|
-
constraints: [{ argumentName: "amount", maximum: 1000, enabled: true }],
|
|
691
|
-
});
|
|
76
|
+
Options mirror the advanced runtime configuration when you need explicit policy sources:
|
|
692
77
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
llmConfig: { description: "..." },
|
|
696
|
-
});
|
|
697
|
-
await admin.activatePolicy("transfer_funds");
|
|
698
|
-
await admin.deactivatePolicy("transfer_funds");
|
|
699
|
-
await admin.deletePolicy("transfer_funds");
|
|
700
|
-
const yaml = await admin.exportPolicies({ format: "yaml" });
|
|
701
|
-
```
|
|
702
|
-
|
|
703
|
-
### Decisions
|
|
704
|
-
|
|
705
|
-
```typescript
|
|
706
|
-
const decisions = await admin.listDecisions({
|
|
707
|
-
toolName: "transfer_funds",
|
|
708
|
-
limit: 50,
|
|
709
|
-
});
|
|
710
|
-
const decision = await admin.getDecision("dec_123");
|
|
711
|
-
const stats = await admin.getDecisionStats({ startDate: "2025-01-01" });
|
|
712
|
-
const csv = await admin.exportDecisions({ format: "csv" });
|
|
713
|
-
```
|
|
714
|
-
|
|
715
|
-
### Approvals
|
|
716
|
-
|
|
717
|
-
```typescript
|
|
718
|
-
const pending = await admin.listPendingApprovals();
|
|
719
|
-
const approval = await admin.getApproval("apr_123");
|
|
720
|
-
await admin.resolveApproval("apr_123", "approve", "user@corp.com");
|
|
721
|
-
await admin.batchResolveApprovals([
|
|
722
|
-
{ id: "apr_1", action: "approve", resolvedBy: "admin" },
|
|
723
|
-
{ id: "apr_2", action: "deny", resolvedBy: "admin" },
|
|
724
|
-
]);
|
|
725
|
-
```
|
|
726
|
-
|
|
727
|
-
### MCP Gateway
|
|
728
|
-
|
|
729
|
-
```typescript
|
|
730
|
-
const upstreams = await admin.listUpstreams();
|
|
731
|
-
await admin.createUpstream({
|
|
732
|
-
name: "my-server",
|
|
733
|
-
transport: "mcp-sse",
|
|
734
|
-
url: "http://localhost:3001/mcp",
|
|
735
|
-
});
|
|
736
|
-
const test = await admin.testUpstream("ups_123");
|
|
737
|
-
await admin.deleteUpstream("ups_123");
|
|
738
|
-
```
|
|
739
|
-
|
|
740
|
-
### API Keys
|
|
741
|
-
|
|
742
|
-
```typescript
|
|
743
|
-
const keys = await admin.listApiKeys();
|
|
744
|
-
const newKey = await admin.createApiKey({ name: "ci-pipeline" });
|
|
745
|
-
console.log(newKey.key); // only shown once
|
|
746
|
-
await admin.revokeApiKey(newKey._id);
|
|
747
|
-
```
|
|
748
|
-
|
|
749
|
-
### Policy Drafts
|
|
750
|
-
|
|
751
|
-
```typescript
|
|
752
|
-
const draft = await admin.createPolicyDraft({
|
|
753
|
-
name: "New financial rules",
|
|
78
|
+
```ts
|
|
79
|
+
const safeTools = await protect(tools, {
|
|
754
80
|
rules: [
|
|
755
81
|
{
|
|
756
|
-
|
|
82
|
+
id: "no-prod-deploy",
|
|
83
|
+
name: "Block direct production deploys",
|
|
84
|
+
enabled: true,
|
|
85
|
+
severity: "critical",
|
|
86
|
+
action: "block",
|
|
87
|
+
tools: ["deploy"],
|
|
88
|
+
conditions: [
|
|
89
|
+
{
|
|
90
|
+
field: "arguments.environment",
|
|
91
|
+
operator: "equals",
|
|
92
|
+
value: "production",
|
|
93
|
+
},
|
|
94
|
+
],
|
|
757
95
|
},
|
|
758
96
|
],
|
|
759
|
-
|
|
760
|
-
});
|
|
761
|
-
await admin.approvePolicyDraft(draft._id);
|
|
762
|
-
await admin.rejectPolicyDraft(draft._id, "Missing edge case coverage");
|
|
763
|
-
```
|
|
764
|
-
|
|
765
|
-
### Real-time Events (SSE)
|
|
766
|
-
|
|
767
|
-
```typescript
|
|
768
|
-
// Callback-based
|
|
769
|
-
const sub = admin.onEvent(["deny", "require_approval"], (event) => {
|
|
770
|
-
console.log(event.type, event.data);
|
|
771
|
-
});
|
|
772
|
-
sub.unsubscribe();
|
|
773
|
-
|
|
774
|
-
// Async iterator
|
|
775
|
-
for await (const event of admin.subscribeEvents({ types: ["deny"] })) {
|
|
776
|
-
console.log(event.type, event.data);
|
|
777
|
-
}
|
|
778
|
-
```
|
|
779
|
-
|
|
780
|
-
---
|
|
781
|
-
|
|
782
|
-
## Cloud Client
|
|
783
|
-
|
|
784
|
-
For direct integration with Veto Cloud's validation and approval workflow.
|
|
785
|
-
|
|
786
|
-
```typescript
|
|
787
|
-
import { VetoCloudClient, ApprovalTimeoutError } from "veto-sdk";
|
|
788
|
-
```
|
|
789
|
-
|
|
790
|
-
When you pass `apiKey` to `Veto.init()`, cloud mode is auto-detected. The SDK registers tools, validates calls against cloud policies, and polls for approval resolution.
|
|
791
|
-
|
|
792
|
-
```typescript
|
|
793
|
-
const veto = await Veto.init({
|
|
794
|
-
apiKey: "veto_...",
|
|
795
|
-
onApprovalRequired: (ctx, approvalId) => {
|
|
796
|
-
console.log(`Approval required: ${approvalId}`);
|
|
797
|
-
},
|
|
798
|
-
});
|
|
799
|
-
```
|
|
800
|
-
|
|
801
|
-
`ApprovalTimeoutError` is thrown when an approval poll exceeds the configured timeout.
|
|
802
|
-
|
|
803
|
-
---
|
|
804
|
-
|
|
805
|
-
## Economic Authorization
|
|
806
|
-
|
|
807
|
-
Protocol-agnostic economic policy enforcement for agent payments across x402 (EVM L2), Stripe MPP, and Google AP2.
|
|
808
|
-
|
|
809
|
-
```typescript
|
|
810
|
-
import {
|
|
811
|
-
LocalBudgetEngine,
|
|
812
|
-
EconomicEvaluator,
|
|
813
|
-
createX402Connector,
|
|
814
|
-
createMPPConnector,
|
|
815
|
-
createAP2Connector,
|
|
816
|
-
} from "veto-sdk";
|
|
817
|
-
```
|
|
818
|
-
|
|
819
|
-
### Budget tracking
|
|
820
|
-
|
|
821
|
-
```typescript
|
|
822
|
-
const budget = new LocalBudgetEngine({
|
|
823
|
-
budgets: { session: { limit: 500, currency: "USD" } },
|
|
97
|
+
mode: "strict",
|
|
824
98
|
});
|
|
825
|
-
|
|
826
|
-
const check = budget.check("session", 100);
|
|
827
|
-
// { allowed: true, remaining: 400 }
|
|
828
|
-
|
|
829
|
-
budget.commit("session", 100);
|
|
830
|
-
```
|
|
831
|
-
|
|
832
|
-
### Protocol connectors
|
|
833
|
-
|
|
834
|
-
```typescript
|
|
835
|
-
const x402 = createX402Connector({ chainId: 8453 });
|
|
836
|
-
const mpp = createMPPConnector({ sessionId: "sess_..." });
|
|
837
|
-
const ap2 = createAP2Connector({ mandateId: "mandate_..." });
|
|
838
|
-
```
|
|
839
|
-
|
|
840
|
-
### Rule-based payment gates
|
|
841
|
-
|
|
842
|
-
```yaml
|
|
843
|
-
rules:
|
|
844
|
-
- id: paid-api-call
|
|
845
|
-
name: Require payment for premium API
|
|
846
|
-
action: require_payment
|
|
847
|
-
tools:
|
|
848
|
-
- premium_search
|
|
849
|
-
payment:
|
|
850
|
-
protocol: x402
|
|
851
|
-
amount: 0.01
|
|
852
|
-
currency: USDC
|
|
853
|
-
chain_id: 8453
|
|
854
|
-
```
|
|
855
|
-
|
|
856
|
-
See the [economic authorization guide](../../docs/economic-authorization.md) for full details.
|
|
857
|
-
|
|
858
|
-
---
|
|
859
|
-
|
|
860
|
-
## Advanced
|
|
861
|
-
|
|
862
|
-
### Policy Compiler
|
|
863
|
-
|
|
864
|
-
AST-based policy expression engine. Compile expressions, evaluate against context, and type-check for errors. No runtime `eval()`.
|
|
865
|
-
|
|
866
|
-
```typescript
|
|
867
|
-
import { compile, evaluate, typeCheck } from "veto-sdk";
|
|
868
|
-
|
|
869
|
-
const ast = compile('amount > 1000 && currency == "USD"');
|
|
870
|
-
const result = evaluate(ast, { amount: 1500, currency: "USD" });
|
|
871
|
-
// result === true
|
|
872
|
-
|
|
873
|
-
const issues = typeCheck(ast);
|
|
874
|
-
// TypeCheckResult { valid: boolean, issues: TypeIssue[] }
|
|
875
|
-
```
|
|
876
|
-
|
|
877
|
-
Use expressions in rule conditions:
|
|
878
|
-
|
|
879
|
-
```yaml
|
|
880
|
-
rules:
|
|
881
|
-
- id: complex-check
|
|
882
|
-
name: Multi-field validation
|
|
883
|
-
action: block
|
|
884
|
-
conditions:
|
|
885
|
-
- expression: 'amount > 1000 && currency != "USD"'
|
|
886
99
|
```
|
|
887
100
|
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
Offline deterministic rule evaluation. No API calls, sub-millisecond latency. Supports all 14 condition operators, dot-notation field paths, AND/OR condition groups, and tool filtering.
|
|
891
|
-
|
|
892
|
-
```typescript
|
|
893
|
-
import { evaluateRulesLocally } from "veto-sdk";
|
|
894
|
-
|
|
895
|
-
const result = evaluateRulesLocally(rules, {
|
|
896
|
-
toolName: "transfer_funds",
|
|
897
|
-
arguments: { amount: 5000 },
|
|
898
|
-
});
|
|
899
|
-
// { decision: 'deny', reason: '...', ruleId: 'limit-transfers' }
|
|
900
|
-
```
|
|
101
|
+
Supported policy sources:
|
|
901
102
|
|
|
902
|
-
|
|
103
|
+
- `rules`: inline deterministic rules
|
|
104
|
+
- `pack`: built-in policy pack such as `@veto/coding-agent`
|
|
105
|
+
- `configDir`: explicit local config directory
|
|
106
|
+
- `apiKey` / `endpoint`: cloud or self-hosted PDP
|
|
903
107
|
|
|
904
|
-
|
|
108
|
+
### Advanced: `Veto.init()` + `.wrap()`
|
|
905
109
|
|
|
906
|
-
|
|
907
|
-
import type {
|
|
908
|
-
ArgumentConstraint,
|
|
909
|
-
SessionConstraints,
|
|
910
|
-
DeterministicPolicy,
|
|
911
|
-
} from "veto-sdk";
|
|
110
|
+
`Veto.init()` remains supported for advanced/internal-facing integrations that need a reusable instance, direct `guard()` calls, event hooks, audit export, or explicit self-host/cloud configuration.
|
|
912
111
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
enabled: true,
|
|
916
|
-
maximum: 1000,
|
|
917
|
-
dynamicMaximum: "session.remaining * 0.15",
|
|
918
|
-
};
|
|
112
|
+
```ts
|
|
113
|
+
import { Veto } from "veto-sdk";
|
|
919
114
|
|
|
920
|
-
const
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
cumulativeLimits: [{ argumentName: "amount", maxValue: 500 }],
|
|
924
|
-
counters: {
|
|
925
|
-
open_positions: {
|
|
926
|
-
increment: ["buy_shares"],
|
|
927
|
-
decrement: ["sell_shares"],
|
|
928
|
-
max: 3,
|
|
929
|
-
maxAction: "require_approval",
|
|
930
|
-
},
|
|
931
|
-
},
|
|
932
|
-
};
|
|
115
|
+
const veto = await Veto.init({ configDir: "./veto", mode: "strict" });
|
|
116
|
+
const safeTools = veto.wrap(tools);
|
|
117
|
+
const decision = await veto.guard("transfer_funds", { amount: 1500 });
|
|
933
118
|
```
|
|
934
119
|
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
### Content Extraction
|
|
938
|
-
|
|
939
|
-
Extract structured entities from text content.
|
|
940
|
-
|
|
941
|
-
```typescript
|
|
942
|
-
import { extractEntities } from "veto-sdk";
|
|
120
|
+
### `veto.guard(toolName, args, context?)`
|
|
943
121
|
|
|
944
|
-
|
|
945
|
-
|
|
122
|
+
```ts
|
|
123
|
+
const result = await veto.guard("transfer_funds", { amount: 5000 });
|
|
124
|
+
// { decision: 'deny', reason: 'Amount exceeds limit', ruleId: 'block-large-transfers' }
|
|
946
125
|
```
|
|
947
126
|
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
## Rule YAML Reference
|
|
951
|
-
|
|
952
|
-
### Input rules
|
|
953
|
-
|
|
954
|
-
```yaml
|
|
955
|
-
rules:
|
|
956
|
-
- id: unique-rule-id # required
|
|
957
|
-
name: Human readable name # required
|
|
958
|
-
description: "..." # optional, used for LLM validation
|
|
959
|
-
enabled: true # default: true
|
|
960
|
-
severity: high # critical | high | medium | low | info
|
|
961
|
-
action: block # block | warn | log | allow | require_approval | require_payment
|
|
127
|
+
### Decision history
|
|
962
128
|
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
# Agent scope (optional)
|
|
968
|
-
agents: [agent_1, agent_2]
|
|
969
|
-
# or exclude: agents: { not: [agent_3] }
|
|
970
|
-
|
|
971
|
-
# Static conditions (AND logic, zero latency)
|
|
972
|
-
conditions:
|
|
973
|
-
- field: arguments.amount
|
|
974
|
-
operator: greater_than
|
|
975
|
-
value: 1000
|
|
976
|
-
- field: arguments.currency
|
|
977
|
-
operator: in
|
|
978
|
-
value: [BTC, ETH]
|
|
979
|
-
|
|
980
|
-
# OR logic between groups
|
|
981
|
-
condition_groups:
|
|
982
|
-
- - field: arguments.amount
|
|
983
|
-
operator: greater_than
|
|
984
|
-
value: 10000
|
|
985
|
-
- - field: arguments.destination
|
|
986
|
-
operator: matches
|
|
987
|
-
value: '^offshore_.*'
|
|
988
|
-
|
|
989
|
-
# Expression syntax (alternative to field/operator/value)
|
|
990
|
-
conditions:
|
|
991
|
-
- expression: 'amount > 1000 && currency != "USD"'
|
|
992
|
-
|
|
993
|
-
# Rate limits (sliding window)
|
|
994
|
-
rate_limits:
|
|
995
|
-
- scope: session
|
|
996
|
-
max_calls: 10
|
|
997
|
-
window_seconds: 60
|
|
998
|
-
|
|
999
|
-
# Cross-tool sequence constraints
|
|
1000
|
-
blocked_by:
|
|
1001
|
-
- tool: disable_mfa
|
|
1002
|
-
within: 3600
|
|
1003
|
-
requires:
|
|
1004
|
-
- tool: verify_identity
|
|
1005
|
-
|
|
1006
|
-
# Payment gate
|
|
1007
|
-
payment:
|
|
1008
|
-
protocol: x402
|
|
1009
|
-
amount: 0.01
|
|
1010
|
-
currency: USDC
|
|
129
|
+
```ts
|
|
130
|
+
const stats = veto.getHistoryStats();
|
|
131
|
+
const json = veto.exportDecisions("json");
|
|
132
|
+
const csv = veto.exportDecisions("csv");
|
|
1011
133
|
```
|
|
1012
134
|
|
|
1013
|
-
|
|
135
|
+
Decision export is local to your process unless you explicitly configure a remote endpoint.
|
|
1014
136
|
|
|
1015
|
-
|
|
1016
|
-
| --------------------- | ------------------------------------------ |
|
|
1017
|
-
| `equals` | Exact match (case-insensitive for strings) |
|
|
1018
|
-
| `not_equals` | Inverse of equals |
|
|
1019
|
-
| `contains` | Substring match |
|
|
1020
|
-
| `not_contains` | Inverse of contains |
|
|
1021
|
-
| `starts_with` | Prefix match |
|
|
1022
|
-
| `ends_with` | Suffix match |
|
|
1023
|
-
| `matches` | Regex match |
|
|
1024
|
-
| `greater_than` | Numeric comparison |
|
|
1025
|
-
| `less_than` | Numeric comparison |
|
|
1026
|
-
| `in` | Value in array |
|
|
1027
|
-
| `not_in` | Value not in array |
|
|
1028
|
-
| `length_greater_than` | String/array length |
|
|
1029
|
-
| `percent_of` | Percentage of reference field |
|
|
1030
|
-
| `within_hours` | Time window match |
|
|
1031
|
-
| `outside_hours` | Inverse time window |
|
|
137
|
+
## Policy packs
|
|
1032
138
|
|
|
1033
|
-
|
|
139
|
+
Built-in packs ship in `packs/` and are referenced with `extends` or `protect(..., { pack })`:
|
|
1034
140
|
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
output_conditions:
|
|
1046
|
-
- field: output
|
|
1047
|
-
operator: matches
|
|
1048
|
-
value: '\b\d{3}-\d{2}-\d{4}\b'
|
|
1049
|
-
```
|
|
1050
|
-
|
|
1051
|
-
### Time window conditions
|
|
1052
|
-
|
|
1053
|
-
```yaml
|
|
1054
|
-
conditions:
|
|
1055
|
-
- field: "@timestamp"
|
|
1056
|
-
operator: outside_hours
|
|
1057
|
-
value:
|
|
1058
|
-
start: "09:00"
|
|
1059
|
-
end: "17:00"
|
|
1060
|
-
timezone: "America/New_York"
|
|
1061
|
-
days: [mon, tue, wed, thu, fri]
|
|
1062
|
-
```
|
|
1063
|
-
|
|
1064
|
-
## CLI Commands
|
|
141
|
+
- `@veto/safe-defaults` observe-mode zero-config defaults
|
|
142
|
+
- `@veto/coding-agent`
|
|
143
|
+
- `@veto/financial`
|
|
144
|
+
- `@veto/crypto-trading`
|
|
145
|
+
- `@veto/browser-automation`
|
|
146
|
+
- `@veto/data-access`
|
|
147
|
+
- `@veto/communication`
|
|
148
|
+
- `@veto/deployment`
|
|
149
|
+
- `@veto/economic-agent`
|
|
150
|
+
- `@veto/soc2-lite`, `@veto/hipaa-lite`, `@veto/eu-ai-act-starter` starter packs
|
|
1065
151
|
|
|
1066
|
-
|
|
1067
|
-
npx veto-cli@latest # Veto Studio (interactive TUI)
|
|
1068
|
-
npx veto-cli@latest policy generate --tool <name> # Generate policy from tool
|
|
1069
|
-
npx veto-cli@latest guard check --tool <name> --args <json> # Test a guard check
|
|
1070
|
-
npx veto-cli@latest scan --fail-uncovered # CI gate
|
|
1071
|
-
npx veto-cli@latest test # Run policy tests
|
|
1072
|
-
npx veto-cli@latest intercept --port 8080 # Start SSE proxy
|
|
1073
|
-
npx veto-cli@latest audit verify # Verify audit chain
|
|
1074
|
-
```
|
|
152
|
+
## Self-host / BYOC boundary
|
|
1075
153
|
|
|
1076
|
-
|
|
154
|
+
The SDK can point at a self-hosted PDP with `endpoint`, but customer-plane data stays in the customer environment unless you explicitly configure outbound integrations. Public BYOC artifacts in the repo state the boundary: customer policy, decision rows, tool args, agent IDs, user IDs, Slack content, prompts, environment variables, and secrets do not cross to Plaw.
|
|
1077
155
|
|
|
1078
156
|
## License
|
|
1079
157
|
|
|
1080
|
-
Apache-2.0
|
|
158
|
+
Apache-2.0
|