ai-agent-guardrails 0.0.1
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 +155 -0
- package/dist/index.d.ts +246 -0
- package/dist/index.js +358 -0
- package/dist/index.js.map +1 -0
- package/package.json +55 -0
- package/src/audit.ts +76 -0
- package/src/guard-tools.ts +158 -0
- package/src/index.ts +30 -0
- package/src/policy.ts +134 -0
- package/src/redaction.ts +107 -0
- package/src/types.ts +117 -0
- package/src/utils.ts +47 -0
- package/tsconfig.json +9 -0
- package/tsup.config.ts +10 -0
package/README.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# ai-agent-guardrails
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/ai-agent-guardrails)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
Security middleware for AI SDK that adds production-grade controls to agent tool calling.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- Policy Enforcement: Allowlists, denylists, and risk-based tool classification
|
|
11
|
+
- Human Approval Gates: Require user confirmation for high-risk operations
|
|
12
|
+
- Budget Controls: Max tool calls, time limits, per-tool timeouts
|
|
13
|
+
- Audit Logging: Structured events compatible with OpenTelemetry
|
|
14
|
+
- Secret Redaction: Automatic PII/secret removal from logs
|
|
15
|
+
- MCP Integration: Works with Model Context Protocol tools
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install ai-agent-guardrails ai zod
|
|
21
|
+
# or
|
|
22
|
+
pnpm add ai-agent-guardrails ai zod
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { streamText } from 'ai';
|
|
29
|
+
import { openai } from '@ai-sdk/openai';
|
|
30
|
+
import { guardTools, createSimplePolicy, ConsoleAuditSink } from 'ai-agent-guardrails';
|
|
31
|
+
|
|
32
|
+
// Define policy
|
|
33
|
+
const policy = createSimplePolicy({
|
|
34
|
+
denylist: ['delete_database'], // Block dangerous tools
|
|
35
|
+
requireApprovalForRisk: ['write', 'admin'], // Require approval for these
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Wrap tools with guardrails
|
|
39
|
+
const tools = guardTools(myTools, {
|
|
40
|
+
policy,
|
|
41
|
+
audit: new ConsoleAuditSink(),
|
|
42
|
+
timeoutMs: 10_000,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Use in AI SDK
|
|
46
|
+
const result = streamText({
|
|
47
|
+
model: openai('gpt-4o-mini'),
|
|
48
|
+
messages,
|
|
49
|
+
tools, // ← Guarded tools
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## With MCP Tools
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { createMCPClient } from '@ai-sdk/mcp';
|
|
57
|
+
import { guardTools, createSimplePolicy } from 'ai-agent-guardrails';
|
|
58
|
+
|
|
59
|
+
// Connect to MCP server
|
|
60
|
+
const mcp = await createMCPClient({ transport: ... });
|
|
61
|
+
const mcpTools = await mcp.tools();
|
|
62
|
+
|
|
63
|
+
// Apply guardrails
|
|
64
|
+
const policy = createSimplePolicy({
|
|
65
|
+
requireApprovalForRisk: ['write', 'admin'],
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const tools = guardTools(mcpTools, { policy });
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Approval Flow (React)
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
'use client';
|
|
75
|
+
|
|
76
|
+
import { useChat } from '@ai-sdk/react';
|
|
77
|
+
|
|
78
|
+
export default function Chat() {
|
|
79
|
+
const { messages, addToolApprovalResponse } = useChat();
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<>
|
|
83
|
+
{messages.map(m =>
|
|
84
|
+
m.parts?.map(part => {
|
|
85
|
+
if (part.state === 'approval-requested') {
|
|
86
|
+
return (
|
|
87
|
+
<div>
|
|
88
|
+
<p>Tool requires approval: {part.type}</p>
|
|
89
|
+
<button onClick={() => addToolApprovalResponse({ id: part.approval.id, approved: true })}>
|
|
90
|
+
Approve
|
|
91
|
+
</button>
|
|
92
|
+
<button onClick={() => addToolApprovalResponse({ id: part.approval.id, approved: false })}>
|
|
93
|
+
Deny
|
|
94
|
+
</button>
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
)}
|
|
100
|
+
</>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Budget Controls
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import { createDefaultContext } from 'ai-agent-guardrails';
|
|
109
|
+
|
|
110
|
+
const ctx = createDefaultContext();
|
|
111
|
+
ctx.maxToolCalls = 5; // Limit to 5 tool calls
|
|
112
|
+
ctx.maxDurationMs = 30_000; // 30 second timeout
|
|
113
|
+
|
|
114
|
+
const tools = guardTools(myTools, { policy, ctx });
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Audit Logging
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
import { InMemoryAuditSink, ConsoleAuditSink, FileAuditSink } from 'ai-agent-guardrails';
|
|
121
|
+
|
|
122
|
+
// Console (dev)
|
|
123
|
+
const audit = new ConsoleAuditSink();
|
|
124
|
+
|
|
125
|
+
// Memory (testing)
|
|
126
|
+
const audit = new InMemoryAuditSink();
|
|
127
|
+
const events = audit.getEvents();
|
|
128
|
+
|
|
129
|
+
// File (production)
|
|
130
|
+
const audit = new FileAuditSink('./audit.jsonl');
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Redaction
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
import { createDefaultRedactor, createFieldRedactor } from 'ai-agent-guardrails';
|
|
137
|
+
|
|
138
|
+
// Automatic pattern-based redaction
|
|
139
|
+
const redactor = createDefaultRedactor();
|
|
140
|
+
|
|
141
|
+
// Field-based redaction
|
|
142
|
+
const redactor = createFieldRedactor(['password', 'apiKey', 'secret']);
|
|
143
|
+
|
|
144
|
+
const tools = guardTools(myTools, { policy, redactor });
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Documentation
|
|
148
|
+
|
|
149
|
+
- [Full Documentation](https://github.com/yourusername/agent-guardrails-mcp)
|
|
150
|
+
- [Threat Model](https://github.com/yourusername/agent-guardrails-mcp/blob/main/docs/THREAT_MODEL.md)
|
|
151
|
+
- [Demo App](https://agent-guardrails-demo.vercel.app)
|
|
152
|
+
|
|
153
|
+
## License
|
|
154
|
+
|
|
155
|
+
MIT © Krish Gupta
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Risk classification for tools
|
|
3
|
+
*/
|
|
4
|
+
type Risk = 'read' | 'write' | 'admin';
|
|
5
|
+
/**
|
|
6
|
+
* Decision outcome from policy evaluation
|
|
7
|
+
*/
|
|
8
|
+
type GuardDecision = {
|
|
9
|
+
allow: true;
|
|
10
|
+
} | {
|
|
11
|
+
allow: false;
|
|
12
|
+
reason: string;
|
|
13
|
+
} | {
|
|
14
|
+
allow: true;
|
|
15
|
+
needsApproval: true;
|
|
16
|
+
reason: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Context passed through guard execution
|
|
20
|
+
*/
|
|
21
|
+
type GuardContext = {
|
|
22
|
+
requestId: string;
|
|
23
|
+
toolCalls: number;
|
|
24
|
+
maxToolCalls: number;
|
|
25
|
+
startTime: number;
|
|
26
|
+
maxDurationMs?: number;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Tool-like interface that matches AI SDK tool structure
|
|
30
|
+
*/
|
|
31
|
+
type ToolLike = {
|
|
32
|
+
description?: string;
|
|
33
|
+
inputSchema?: unknown;
|
|
34
|
+
parameters?: unknown;
|
|
35
|
+
needsApproval?: boolean | ((input: any) => boolean | Promise<boolean>);
|
|
36
|
+
execute?: (input: any) => Promise<any>;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Policy interface for classification and decision making
|
|
40
|
+
*/
|
|
41
|
+
type GuardPolicy = {
|
|
42
|
+
/**
|
|
43
|
+
* Classify a tool call by risk level
|
|
44
|
+
*/
|
|
45
|
+
classify: (toolName: string, input: unknown) => Promise<{
|
|
46
|
+
risk: Risk;
|
|
47
|
+
reason?: string;
|
|
48
|
+
}> | {
|
|
49
|
+
risk: Risk;
|
|
50
|
+
reason?: string;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Decide whether to allow, block, or require approval
|
|
54
|
+
*/
|
|
55
|
+
decide: (args: {
|
|
56
|
+
toolName: string;
|
|
57
|
+
input: unknown;
|
|
58
|
+
ctx: GuardContext;
|
|
59
|
+
risk: Risk;
|
|
60
|
+
reason?: string;
|
|
61
|
+
}) => Promise<GuardDecision> | GuardDecision;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Audit event types
|
|
65
|
+
*/
|
|
66
|
+
type AuditEvent = {
|
|
67
|
+
type: 'tool_call_attempted';
|
|
68
|
+
toolName: string;
|
|
69
|
+
input: unknown;
|
|
70
|
+
requestId: string;
|
|
71
|
+
timestamp: number;
|
|
72
|
+
} | {
|
|
73
|
+
type: 'tool_call_blocked';
|
|
74
|
+
toolName: string;
|
|
75
|
+
reason: string;
|
|
76
|
+
requestId: string;
|
|
77
|
+
timestamp: number;
|
|
78
|
+
} | {
|
|
79
|
+
type: 'tool_call_needs_approval';
|
|
80
|
+
toolName: string;
|
|
81
|
+
reason: string;
|
|
82
|
+
requestId: string;
|
|
83
|
+
timestamp: number;
|
|
84
|
+
} | {
|
|
85
|
+
type: 'tool_call_executed';
|
|
86
|
+
toolName: string;
|
|
87
|
+
durationMs: number;
|
|
88
|
+
requestId: string;
|
|
89
|
+
timestamp: number;
|
|
90
|
+
} | {
|
|
91
|
+
type: 'tool_call_timeout';
|
|
92
|
+
toolName: string;
|
|
93
|
+
timeoutMs: number;
|
|
94
|
+
requestId: string;
|
|
95
|
+
timestamp: number;
|
|
96
|
+
} | {
|
|
97
|
+
type: 'budget_exceeded';
|
|
98
|
+
reason: string;
|
|
99
|
+
requestId: string;
|
|
100
|
+
timestamp: number;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Audit sink interface for logging events
|
|
104
|
+
*/
|
|
105
|
+
type AuditSink = {
|
|
106
|
+
emit: (event: AuditEvent) => void | Promise<void>;
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Options for guardTools wrapper
|
|
110
|
+
*/
|
|
111
|
+
type GuardToolsOptions = {
|
|
112
|
+
policy: GuardPolicy;
|
|
113
|
+
ctx?: GuardContext;
|
|
114
|
+
audit?: AuditSink;
|
|
115
|
+
timeoutMs?: number;
|
|
116
|
+
redactor?: Redactor;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Redactor interface for PII/secret removal
|
|
120
|
+
*/
|
|
121
|
+
type Redactor = {
|
|
122
|
+
redact: (value: unknown) => unknown;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Wrap a toolset with guardrails enforcement
|
|
127
|
+
*
|
|
128
|
+
* This is the main entry point for the guardrails package. It wraps AI SDK tools
|
|
129
|
+
* with policy enforcement, budget checks, timeouts, and audit logging.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```ts
|
|
133
|
+
* const tools = guardTools(mcpTools, {
|
|
134
|
+
* policy: createSimplePolicy({ requireApprovalForRisk: ['write', 'admin'] }),
|
|
135
|
+
* audit: new ConsoleAuditSink(),
|
|
136
|
+
* timeoutMs: 10_000,
|
|
137
|
+
* });
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
declare function guardTools<T extends Record<string, ToolLike>>(tools: T, opts: GuardToolsOptions): T;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Create a default guard context with budget limits
|
|
144
|
+
*/
|
|
145
|
+
declare function createDefaultContext(requestId?: string): GuardContext;
|
|
146
|
+
/**
|
|
147
|
+
* Wrap a promise with a timeout
|
|
148
|
+
*/
|
|
149
|
+
declare function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T>;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Create a simple policy with allowlist/denylist
|
|
153
|
+
*/
|
|
154
|
+
declare function createSimplePolicy(options: {
|
|
155
|
+
allowlist?: string[];
|
|
156
|
+
denylist?: string[];
|
|
157
|
+
requireApprovalForRisk?: Risk[];
|
|
158
|
+
}): GuardPolicy;
|
|
159
|
+
/**
|
|
160
|
+
* Create a composable policy builder
|
|
161
|
+
*/
|
|
162
|
+
declare class PolicyBuilder {
|
|
163
|
+
private classifiers;
|
|
164
|
+
private rules;
|
|
165
|
+
/**
|
|
166
|
+
* Add a classifier function
|
|
167
|
+
*/
|
|
168
|
+
addClassifier(classifier: (toolName: string, input: unknown) => {
|
|
169
|
+
risk: Risk;
|
|
170
|
+
reason?: string;
|
|
171
|
+
} | null): this;
|
|
172
|
+
/**
|
|
173
|
+
* Add a decision rule
|
|
174
|
+
*/
|
|
175
|
+
addRule(rule: (args: {
|
|
176
|
+
toolName: string;
|
|
177
|
+
input: unknown;
|
|
178
|
+
ctx: GuardContext;
|
|
179
|
+
risk: Risk;
|
|
180
|
+
reason?: string;
|
|
181
|
+
}) => GuardDecision | null): this;
|
|
182
|
+
/**
|
|
183
|
+
* Build the final policy
|
|
184
|
+
*/
|
|
185
|
+
build(): GuardPolicy;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* In-memory audit sink that stores events in an array
|
|
190
|
+
*/
|
|
191
|
+
declare class InMemoryAuditSink implements AuditSink {
|
|
192
|
+
private events;
|
|
193
|
+
emit(event: AuditEvent): void;
|
|
194
|
+
/**
|
|
195
|
+
* Get all stored events
|
|
196
|
+
*/
|
|
197
|
+
getEvents(): ReadonlyArray<AuditEvent>;
|
|
198
|
+
/**
|
|
199
|
+
* Clear all stored events
|
|
200
|
+
*/
|
|
201
|
+
clear(): void;
|
|
202
|
+
/**
|
|
203
|
+
* Get events for a specific request
|
|
204
|
+
*/
|
|
205
|
+
getEventsForRequest(requestId: string): AuditEvent[];
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Console audit sink that logs events to console
|
|
209
|
+
*/
|
|
210
|
+
declare class ConsoleAuditSink implements AuditSink {
|
|
211
|
+
private prefix;
|
|
212
|
+
constructor(prefix?: string);
|
|
213
|
+
emit(event: AuditEvent): void;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* File audit sink that writes events to a JSONL file
|
|
217
|
+
* Note: This is for Node.js environments only
|
|
218
|
+
*/
|
|
219
|
+
declare class FileAuditSink implements AuditSink {
|
|
220
|
+
private writeStream;
|
|
221
|
+
constructor(filePath: string);
|
|
222
|
+
emit(event: AuditEvent): void;
|
|
223
|
+
/**
|
|
224
|
+
* Close the file stream
|
|
225
|
+
*/
|
|
226
|
+
close(): void;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Create a regex-based redactor
|
|
231
|
+
*/
|
|
232
|
+
declare function createRegexRedactor(patterns: RegExp[], replacement?: string): Redactor;
|
|
233
|
+
/**
|
|
234
|
+
* Create a default redactor with common secret and PII patterns
|
|
235
|
+
*/
|
|
236
|
+
declare function createDefaultRedactor(): Redactor;
|
|
237
|
+
/**
|
|
238
|
+
* Field-based redactor that redacts specific fields
|
|
239
|
+
*/
|
|
240
|
+
declare function createFieldRedactor(fieldsToRedact: string[], replacement?: string): Redactor;
|
|
241
|
+
/**
|
|
242
|
+
* Composite redactor that chains multiple redactors
|
|
243
|
+
*/
|
|
244
|
+
declare function composeRedactors(...redactors: Redactor[]): Redactor;
|
|
245
|
+
|
|
246
|
+
export { type AuditEvent, type AuditSink, ConsoleAuditSink, FileAuditSink, type GuardContext, type GuardDecision, type GuardPolicy, type GuardToolsOptions, InMemoryAuditSink, PolicyBuilder, type Redactor, type Risk, type ToolLike, composeRedactors, createDefaultContext, createDefaultRedactor, createFieldRedactor, createRegexRedactor, createSimplePolicy, guardTools, withTimeout };
|