@directive-run/knowledge 1.14.0 → 1.16.0
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 +50 -15
- package/ai/ai-adapters.md +7 -0
- package/ai/ai-agents-streaming.md +187 -149
- package/ai/ai-budget-resilience.md +305 -132
- package/ai/ai-communication.md +220 -197
- package/ai/ai-debug-observability.md +259 -173
- package/ai/ai-guardrails-memory.md +191 -153
- package/ai/ai-mcp-rag.md +204 -199
- package/ai/ai-multi-agent.md +254 -153
- package/ai/ai-orchestrator.md +353 -114
- package/ai/ai-security.md +287 -180
- package/ai/ai-tasks.md +8 -1
- package/ai/ai-testing-evals.md +363 -256
- package/core/anti-patterns.md +18 -2
- package/core/constraints.md +13 -2
- package/core/core-patterns.md +12 -0
- package/core/error-boundaries.md +8 -0
- package/core/history.md +7 -0
- package/core/multi-module.md +15 -3
- package/core/naming.md +128 -90
- package/core/plugins.md +7 -0
- package/core/react-adapter.md +256 -174
- package/core/resolvers.md +10 -0
- package/core/schema-types.md +8 -0
- package/core/system-api.md +10 -0
- package/core/testing.md +257 -143
- package/examples/checkers.ts +15 -16
- package/examples/contact-form.ts +2 -2
- package/examples/counter-react.ts +1 -1
- package/examples/counter-svelte.ts +1 -1
- package/examples/counter-vue.ts +1 -1
- package/examples/counter.ts +1 -1
- package/examples/data-triggers.ts +4 -4
- package/examples/feature-flags.ts +2 -2
- package/examples/form-wizard.ts +2 -2
- package/examples/newsletter.ts +2 -2
- package/examples/server.ts +2 -2
- package/examples/shopping-cart.ts +1 -1
- package/examples/topic-guard.ts +1 -1
- package/package.json +3 -3
- package/sitemap.md +17 -11
- package/examples/debounce-constraints.ts +0 -95
- package/examples/multi-module.ts +0 -58
package/ai/ai-security.md
CHANGED
|
@@ -1,293 +1,400 @@
|
|
|
1
|
-
# AI
|
|
1
|
+
# AI security
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Covers `@directive-run/ai` and `@directive-run/ai/guardrails` — `createPIIGuardrail`, `createPromptInjectionGuardrail`, `createAuditTrail`, `createCompliance` (GDPR/CCPA).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
PII detection and redaction, prompt-injection defense, audit trails with non-repudiation, and GDPR/CCPA compliance — all wired in via Directive's guardrail and instance APIs. Import the audit + compliance instances from `@directive-run/ai` (NOT `@directive-run/core/plugins`); import the security guardrails from `@directive-run/ai/guardrails`.
|
|
6
|
+
|
|
7
|
+
## Decision tree
|
|
6
8
|
|
|
7
9
|
```
|
|
8
10
|
What are you protecting against?
|
|
9
|
-
├── PII
|
|
10
|
-
├──
|
|
11
|
-
├──
|
|
12
|
-
├──
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
└──
|
|
21
|
-
(Plugins are Directive core plugins, not AI-specific)
|
|
11
|
+
├── PII in input → createPIIGuardrail() or createEnhancedPIIGuardrail()
|
|
12
|
+
├── PII in output → createOutputPIIGuardrail()
|
|
13
|
+
├── Prompt injection → createPromptInjectionGuardrail()
|
|
14
|
+
├── Audit / forensics → createAuditTrail(config)
|
|
15
|
+
├── GDPR / CCPA → createCompliance(config)
|
|
16
|
+
├── Tool restriction → createToolGuardrail({ allowlist | denylist })
|
|
17
|
+
└── Sensitive content → createContentFilterGuardrail({ blockedPatterns })
|
|
18
|
+
|
|
19
|
+
Where do these go?
|
|
20
|
+
├── Guardrails → orchestrator.guardrails.input / output / toolCall
|
|
21
|
+
├── Audit → record into auditInstance from your call sites (NOT a plugins:[…] entry)
|
|
22
|
+
└── Compliance → instance you call directly (exportData, deleteData, consent.*)
|
|
22
23
|
```
|
|
23
24
|
|
|
24
|
-
## PII
|
|
25
|
-
|
|
26
|
-
Enhanced PII guardrail with built-in patterns and custom regex support:
|
|
25
|
+
## PII detection + redaction
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
import { createPIIGuardrail } from "@directive-run/ai";
|
|
27
|
+
### Basic PII guardrail (input only)
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
// Redact PII instead of blocking (default: false = block)
|
|
33
|
-
redact: true,
|
|
29
|
+
`createPIIGuardrail` returns a `GuardrailFn<InputGuardrailData>` — input only. For output PII protection, use `createOutputPIIGuardrail`.
|
|
34
30
|
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
```typescript
|
|
32
|
+
import { createPIIGuardrail } from "@directive-run/ai/guardrails";
|
|
37
33
|
|
|
38
|
-
|
|
34
|
+
const piiInput = createPIIGuardrail({
|
|
39
35
|
patterns: [
|
|
40
|
-
/\b\d{3}-\d{2}-\d{4}\b/, // SSN
|
|
36
|
+
/\b\d{3}-\d{2}-\d{4}\b/, // SSN — already a default, here as a custom example
|
|
41
37
|
/\b[A-Z]{2}\d{6,8}\b/, // Passport
|
|
42
|
-
/ACCT-\d{10}/,
|
|
38
|
+
/ACCT-\d{10}/, // Internal account IDs
|
|
43
39
|
],
|
|
40
|
+
redact: true, // default: false (block instead of redact)
|
|
41
|
+
redactReplacement: "[REDACTED]",
|
|
44
42
|
});
|
|
45
43
|
```
|
|
46
44
|
|
|
47
|
-
Built-in patterns
|
|
48
|
-
- Email addresses
|
|
49
|
-
- Phone numbers (US, international)
|
|
50
|
-
- Credit card numbers (Visa, MC, Amex, Discover)
|
|
51
|
-
- IP addresses (v4, v6)
|
|
52
|
-
- Dates of birth (common formats)
|
|
45
|
+
Built-in default patterns: SSN, credit card numbers, email addresses. Custom `patterns` are added on top.
|
|
53
46
|
|
|
54
|
-
###
|
|
47
|
+
### Enhanced PII guardrails (input + output)
|
|
48
|
+
|
|
49
|
+
For production scenarios — context-aware detection, multi-region defaults, output coverage — use the enhanced factories:
|
|
55
50
|
|
|
56
51
|
```typescript
|
|
52
|
+
import { createEnhancedPIIGuardrail, createOutputPIIGuardrail } from "@directive-run/ai/guardrails";
|
|
53
|
+
|
|
54
|
+
const piiInput = createEnhancedPIIGuardrail({ /* …config… */ });
|
|
55
|
+
const piiOutput = createOutputPIIGuardrail({ /* …config… */ });
|
|
56
|
+
|
|
57
57
|
const orchestrator = createAgentOrchestrator({
|
|
58
58
|
runner,
|
|
59
59
|
guardrails: {
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
input: [piiInput],
|
|
61
|
+
output: [piiOutput],
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Wiring it in
|
|
62
67
|
|
|
63
|
-
|
|
64
|
-
|
|
68
|
+
```typescript
|
|
69
|
+
const orchestrator = createAgentOrchestrator({
|
|
70
|
+
runner,
|
|
71
|
+
guardrails: {
|
|
72
|
+
input: [piiInput],
|
|
73
|
+
output: [piiOutput],
|
|
65
74
|
},
|
|
66
75
|
});
|
|
67
76
|
```
|
|
68
77
|
|
|
69
|
-
## Prompt
|
|
78
|
+
## Prompt-injection defense
|
|
70
79
|
|
|
71
|
-
|
|
80
|
+
`createPromptInjectionGuardrail` ships with default + strict pattern sets and a 0–100 risk score. The real options are `strictMode`, `blockThreshold`, `additionalPatterns`, `replacePatterns`, `sanitize`, `onBlocked`, `ignoreCategories`. There is no `sensitivity` field and no `allowlist`.
|
|
72
81
|
|
|
73
82
|
```typescript
|
|
74
|
-
import { createPromptInjectionGuardrail } from "@directive-run/ai";
|
|
83
|
+
import { createPromptInjectionGuardrail } from "@directive-run/ai/guardrails";
|
|
75
84
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
sensitivity: "high",
|
|
85
|
+
// Basic — uses default patterns + 50 block threshold
|
|
86
|
+
const injection = createPromptInjectionGuardrail();
|
|
79
87
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
// Allow-list specific phrases that look like injections but are safe
|
|
88
|
-
allowlist: [
|
|
89
|
-
"you are now ready to proceed",
|
|
88
|
+
// Strict — for high-security applications
|
|
89
|
+
const strictInjection = createPromptInjectionGuardrail({
|
|
90
|
+
strictMode: true, // use STRICT_INJECTION_PATTERNS instead of DEFAULT
|
|
91
|
+
blockThreshold: 25, // lower threshold — more aggressive blocking
|
|
92
|
+
additionalPatterns: [ // your own patterns layered on top
|
|
93
|
+
{ name: "custom_role_override", regex: /you are now a /i, score: 70, category: "role_manipulation" },
|
|
90
94
|
],
|
|
95
|
+
sanitize: false, // when true, attempts to neutralize the input instead of blocking
|
|
96
|
+
onBlocked: (input, result) => {
|
|
97
|
+
metrics.increment("prompt_injection_blocked", { risk: result.riskScore });
|
|
98
|
+
},
|
|
91
99
|
});
|
|
92
|
-
```
|
|
93
100
|
|
|
94
|
-
|
|
101
|
+
// Roleplay app — allow role-manipulation patterns, keep everything else
|
|
102
|
+
const roleplayInjection = createPromptInjectionGuardrail({
|
|
103
|
+
ignoreCategories: ["role_manipulation"],
|
|
104
|
+
});
|
|
95
105
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
106
|
+
// Replace all defaults
|
|
107
|
+
const customOnly = createPromptInjectionGuardrail({
|
|
108
|
+
replacePatterns: myPatternList,
|
|
109
|
+
});
|
|
110
|
+
```
|
|
101
111
|
|
|
102
|
-
###
|
|
112
|
+
### Catching the block
|
|
103
113
|
|
|
104
114
|
```typescript
|
|
105
|
-
|
|
106
|
-
runner,
|
|
107
|
-
guardrails: {
|
|
108
|
-
// Check user input for injection attempts
|
|
109
|
-
input: [injectionGuardrail],
|
|
110
|
-
},
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
// Handle blocked input
|
|
114
|
-
import { GuardrailError } from "@directive-run/ai";
|
|
115
|
+
import { isGuardrailError } from "@directive-run/ai";
|
|
115
116
|
|
|
116
117
|
try {
|
|
117
|
-
|
|
118
|
+
await orchestrator.run(agent, userInput);
|
|
118
119
|
} catch (error) {
|
|
119
|
-
if (error
|
|
120
|
-
console.log(error.guardrailName);
|
|
121
|
-
console.log(error.
|
|
122
|
-
console.log(error.
|
|
120
|
+
if (isGuardrailError(error)) {
|
|
121
|
+
console.log(error.guardrailName); // "prompt-injection"
|
|
122
|
+
console.log(error.code); // "INPUT_GUARDRAIL_FAILED"
|
|
123
|
+
console.log(error.userMessage); // safe-to-display rejection reason
|
|
124
|
+
// error.input and error.data are NON-enumerable (won't leak via JSON.stringify)
|
|
123
125
|
}
|
|
124
126
|
}
|
|
125
127
|
```
|
|
126
128
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
Log all AI interactions for compliance and forensics:
|
|
130
|
-
|
|
131
|
-
```typescript
|
|
132
|
-
import { createAuditTrailPlugin } from "@directive-run/core/plugins";
|
|
133
|
-
|
|
134
|
-
const auditPlugin = createAuditTrailPlugin({
|
|
135
|
-
// Where to store audit logs
|
|
136
|
-
storage: "file", // "file" | "console" | custom handler
|
|
137
|
-
filePath: "./audit.jsonl", // For file storage
|
|
129
|
+
`GuardrailError` does not expose `errorCode` (it's `code`) or `reason` (the rejection reason becomes the error `message` / `userMessage`). See `ai-guardrails-memory.md` for the full error shape.
|
|
138
130
|
|
|
139
|
-
|
|
140
|
-
logInputs: true,
|
|
141
|
-
logOutputs: true,
|
|
142
|
-
logToolCalls: true,
|
|
143
|
-
logTokenUsage: true,
|
|
131
|
+
## Audit trail
|
|
144
132
|
|
|
145
|
-
|
|
146
|
-
redactPII: true,
|
|
133
|
+
`createAuditTrail(config)` returns an `AuditInstance` you record into from your call sites — it is NOT a plugin you put in `plugins: […]`. The factory is named `createAuditTrail`, NOT `createAuditTrailPlugin`, and it lives in `@directive-run/ai`, NOT `@directive-run/core/plugins`.
|
|
147
134
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
135
|
+
```typescript
|
|
136
|
+
import { createAuditTrail } from "@directive-run/ai";
|
|
137
|
+
|
|
138
|
+
const audit = createAuditTrail({
|
|
139
|
+
maxEntries: 10_000, // default 10000
|
|
140
|
+
retentionMs: 7 * 24 * 60 * 60 * 1000, // default: 7 days
|
|
141
|
+
exportInterval: 60_000, // default 60s — async exporter cadence
|
|
142
|
+
exporter: async (entries) => {
|
|
143
|
+
await sendToSIEM(entries);
|
|
144
|
+
},
|
|
145
|
+
piiMasking: {
|
|
146
|
+
enabled: true,
|
|
147
|
+
fields: ["input", "output"],
|
|
148
|
+
},
|
|
149
|
+
signing: { // optional non-repudiation chain
|
|
150
|
+
signFn: (hash) => signWithHSM(hash),
|
|
151
|
+
verifyFn: (hash, sig) => verifyWithHSM(hash, sig),
|
|
152
|
+
},
|
|
153
|
+
sessionId: currentSessionId,
|
|
154
|
+
actorId: currentUserId,
|
|
155
|
+
events: {
|
|
156
|
+
onEntryAdded: (entry) => log("audit:entry", entry.eventType),
|
|
157
|
+
onChainBroken: (result) => alertOnTamper(result),
|
|
158
|
+
onExportError: (err, entries) => log("audit:export:fail", err, entries.length),
|
|
151
159
|
},
|
|
152
160
|
});
|
|
161
|
+
|
|
162
|
+
// Query
|
|
163
|
+
const failed = audit.getEntries({ eventType: "guardrail_check" });
|
|
164
|
+
const verified = await audit.verifyChain();
|
|
165
|
+
const exported = await audit.export(Date.now() - 24 * 60 * 60 * 1000);
|
|
153
166
|
```
|
|
154
167
|
|
|
155
|
-
|
|
168
|
+
Each `AuditEntry` carries a hash-chained sequence number so `verifyChain()` can prove no entries were dropped or rewritten in flight.
|
|
169
|
+
|
|
170
|
+
### Recording entries
|
|
171
|
+
|
|
172
|
+
For most workflows the orchestrator records relevant events automatically when an audit instance is attached via your own integration code. The bare minimum from a call site:
|
|
156
173
|
|
|
157
174
|
```typescript
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
eventType: "agent_run"
|
|
161
|
-
agentName:
|
|
162
|
-
input
|
|
163
|
-
output
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
duration: number;
|
|
167
|
-
guardrails?: { name: string; passed: boolean; reason?: string }[];
|
|
168
|
-
error?: { message: string; code: string };
|
|
169
|
-
}
|
|
175
|
+
// In your resolver / event handler / orchestrator hook
|
|
176
|
+
audit.record({
|
|
177
|
+
eventType: "agent_run",
|
|
178
|
+
agentName: agent.name,
|
|
179
|
+
input,
|
|
180
|
+
output: result.output,
|
|
181
|
+
tokenUsage: result.tokenUsage,
|
|
182
|
+
});
|
|
170
183
|
```
|
|
171
184
|
|
|
172
|
-
|
|
185
|
+
See the API skeleton for the full `AuditEntry` shape.
|
|
186
|
+
|
|
187
|
+
## GDPR / CCPA compliance
|
|
173
188
|
|
|
174
|
-
|
|
189
|
+
`createCompliance(config)` returns a `ComplianceInstance` you call directly. The factory is named `createCompliance`, NOT `createCompliancePlugin`, and lives in `@directive-run/ai`. **`storage` is required.**
|
|
175
190
|
|
|
176
191
|
```typescript
|
|
177
|
-
import {
|
|
192
|
+
import { createCompliance } from "@directive-run/ai";
|
|
178
193
|
|
|
179
|
-
const
|
|
180
|
-
//
|
|
194
|
+
const compliance = createCompliance({
|
|
195
|
+
storage: myComplianceStorage, // REQUIRED — your ComplianceStorage adapter
|
|
181
196
|
retention: {
|
|
182
|
-
|
|
183
|
-
|
|
197
|
+
maxAgeMs: 30 * 24 * 60 * 60 * 1000, // 30 days
|
|
198
|
+
autoEnforce: true,
|
|
184
199
|
},
|
|
185
|
-
|
|
186
|
-
//
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
200
|
+
consentPurposes: ["analytics", "personalization", "training"],
|
|
201
|
+
exportExpirationMs: 24 * 60 * 60 * 1000, // signed export links expire in 24h (default)
|
|
202
|
+
auditOperations: true, // mirror every compliance op into the audit trail
|
|
203
|
+
events: {
|
|
204
|
+
onExport: (result) => log("gdpr:export", result.subjectId),
|
|
205
|
+
onDelete: (result) => log("gdpr:delete", result.certificate),
|
|
206
|
+
onConsentChange: (record) => log("consent:change", record),
|
|
207
|
+
onRetentionEnforced: (category, count) => log("retention:enforced", category, count),
|
|
190
208
|
},
|
|
209
|
+
});
|
|
191
210
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const data = await getUserData(userId);
|
|
211
|
+
// GDPR Article 20 — right to data portability
|
|
212
|
+
const exportResult = await compliance.exportData({ subjectId: "user-42", format: "json" });
|
|
195
213
|
|
|
196
|
-
|
|
197
|
-
|
|
214
|
+
// GDPR Article 17 — right to erasure (returns a signed deletion certificate)
|
|
215
|
+
const deletion = await compliance.deleteData({ subjectId: "user-42", scope: "all" });
|
|
216
|
+
console.log(deletion.certificate.signature);
|
|
198
217
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
218
|
+
// Consent
|
|
219
|
+
compliance.consent.grant({ subjectId: "user-42", purpose: "analytics" });
|
|
220
|
+
compliance.consent.revoke({ subjectId: "user-42", purpose: "personalization" });
|
|
221
|
+
const consents = compliance.consent.list("user-42");
|
|
203
222
|
```
|
|
204
223
|
|
|
205
|
-
|
|
224
|
+
The `storage` adapter is your responsibility — Directive provides the policy + signed-receipt machinery; you provide the database. The `ComplianceStorage` interface lives in `@directive-run/ai`.
|
|
206
225
|
|
|
207
|
-
|
|
226
|
+
## Combining the surface
|
|
208
227
|
|
|
209
228
|
```typescript
|
|
210
229
|
import { createAgentOrchestrator } from "@directive-run/ai";
|
|
230
|
+
import { createAuditTrail, createCompliance } from "@directive-run/ai";
|
|
231
|
+
import {
|
|
232
|
+
createPIIGuardrail,
|
|
233
|
+
createOutputPIIGuardrail,
|
|
234
|
+
createPromptInjectionGuardrail,
|
|
235
|
+
createToolGuardrail,
|
|
236
|
+
} from "@directive-run/ai/guardrails";
|
|
237
|
+
|
|
238
|
+
const audit = createAuditTrail({ /* ... */ });
|
|
239
|
+
const compliance = createCompliance({ storage: myStorage });
|
|
211
240
|
|
|
212
241
|
const orchestrator = createAgentOrchestrator({
|
|
213
242
|
runner,
|
|
214
243
|
guardrails: {
|
|
215
|
-
input:
|
|
216
|
-
output:
|
|
244
|
+
input: [createPIIGuardrail({ redact: true }), createPromptInjectionGuardrail({ strictMode: true })],
|
|
245
|
+
output: [createOutputPIIGuardrail()],
|
|
246
|
+
toolCall: [createToolGuardrail({ allowlist: ["search", "calculator"] })],
|
|
217
247
|
},
|
|
218
|
-
|
|
248
|
+
maxTokenBudget: 100_000,
|
|
249
|
+
budgetWarningThreshold: 0.8,
|
|
219
250
|
});
|
|
251
|
+
|
|
252
|
+
// Wire audit + compliance into your orchestrator hooks
|
|
253
|
+
orchestrator.run = (orig => async (agent, input, opts) => {
|
|
254
|
+
audit.record({ eventType: "agent_run", agentName: agent.name, input });
|
|
255
|
+
return orig(agent, input, opts);
|
|
256
|
+
})(orchestrator.run);
|
|
220
257
|
```
|
|
221
258
|
|
|
222
|
-
## Security
|
|
259
|
+
## Security best practices
|
|
223
260
|
|
|
224
|
-
###
|
|
261
|
+
### Validate input before dispatch
|
|
225
262
|
|
|
226
263
|
```typescript
|
|
227
|
-
// WRONG
|
|
228
|
-
|
|
264
|
+
// WRONG — raw user input goes straight to the agent
|
|
265
|
+
await orchestrator.run(agent, userInput);
|
|
229
266
|
|
|
230
|
-
// CORRECT
|
|
267
|
+
// CORRECT — sanitize / shape-check first, even with guardrails
|
|
231
268
|
const sanitized = sanitizeInput(userInput);
|
|
232
|
-
|
|
269
|
+
await orchestrator.run(agent, sanitized);
|
|
233
270
|
```
|
|
234
271
|
|
|
235
|
-
###
|
|
272
|
+
### Always set a token budget
|
|
236
273
|
|
|
237
274
|
```typescript
|
|
238
|
-
|
|
239
|
-
const orchestrator = createAgentOrchestrator({
|
|
275
|
+
createAgentOrchestrator({
|
|
240
276
|
runner,
|
|
241
|
-
maxTokenBudget:
|
|
277
|
+
maxTokenBudget: 100_000,
|
|
242
278
|
budgetWarningThreshold: 0.8,
|
|
243
|
-
|
|
279
|
+
onBudgetWarning: (e) => alertOps(e),
|
|
280
|
+
})
|
|
244
281
|
```
|
|
245
282
|
|
|
246
|
-
###
|
|
283
|
+
### Restrict tool access
|
|
247
284
|
|
|
248
285
|
```typescript
|
|
249
|
-
import { createToolGuardrail } from "@directive-run/ai";
|
|
286
|
+
import { createToolGuardrail } from "@directive-run/ai/guardrails";
|
|
250
287
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
allowedTools: ["search", "calculator", "readFile"],
|
|
254
|
-
// Tools not in this list are blocked
|
|
288
|
+
const tools = createToolGuardrail({
|
|
289
|
+
allowlist: ["search", "calculator", "readFile"],
|
|
255
290
|
});
|
|
256
291
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
toolConstraints: {
|
|
261
|
-
"tools/write-file": { requireApproval: true },
|
|
262
|
-
"tools/delete": { requireApproval: true, maxAttempts: 1 },
|
|
263
|
-
},
|
|
292
|
+
const orchestrator = createAgentOrchestrator({
|
|
293
|
+
runner,
|
|
294
|
+
guardrails: { toolCall: [tools] },
|
|
264
295
|
});
|
|
265
296
|
```
|
|
266
297
|
|
|
267
|
-
|
|
298
|
+
For MCP-mediated tools, see `ai-mcp-rag.md` for per-tool approval + risk-scoring.
|
|
299
|
+
|
|
300
|
+
### Always validate output
|
|
268
301
|
|
|
269
302
|
```typescript
|
|
270
|
-
|
|
271
|
-
|
|
303
|
+
import {
|
|
304
|
+
createOutputSchemaGuardrail,
|
|
305
|
+
createContentFilterGuardrail,
|
|
306
|
+
createOutputPIIGuardrail,
|
|
307
|
+
} from "@directive-run/ai/guardrails";
|
|
308
|
+
|
|
309
|
+
createAgentOrchestrator({
|
|
272
310
|
runner,
|
|
273
311
|
guardrails: {
|
|
274
312
|
output: [
|
|
275
|
-
createOutputSchemaGuardrail({
|
|
276
|
-
createContentFilterGuardrail({
|
|
277
|
-
|
|
313
|
+
createOutputSchemaGuardrail({ validate: zodAdapter(expectedSchema) }),
|
|
314
|
+
createContentFilterGuardrail({ blockedPatterns: [/eval\(/, /<script/i] }),
|
|
315
|
+
createOutputPIIGuardrail(),
|
|
278
316
|
],
|
|
279
317
|
},
|
|
280
318
|
});
|
|
281
319
|
```
|
|
282
320
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
321
|
+
(`createOutputSchemaGuardrail` takes `validate` — not `schema` + `retries`. For automatic retry on schema failure, set `outputSchema` + `maxSchemaRetries` directly on the orchestrator.)
|
|
322
|
+
|
|
323
|
+
## Anti-patterns
|
|
324
|
+
|
|
325
|
+
### Importing audit/compliance from `@directive-run/core/plugins`
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
// WRONG — these names + path don't exist
|
|
329
|
+
import { createAuditTrailPlugin, createCompliancePlugin } from "@directive-run/core/plugins";
|
|
330
|
+
|
|
331
|
+
// CORRECT — createAuditTrail / createCompliance from @directive-run/ai
|
|
332
|
+
import { createAuditTrail, createCompliance } from "@directive-run/ai";
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Putting `createAuditTrail()` into `plugins: [...]`
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
// WRONG — createAuditTrail returns an AuditInstance, not a Directive Plugin
|
|
339
|
+
plugins: [createAuditTrail({ /* … */ })],
|
|
340
|
+
|
|
341
|
+
// CORRECT — keep the instance, record into it
|
|
342
|
+
const audit = createAuditTrail({ /* … */ });
|
|
343
|
+
audit.record({ /* … */ });
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### `createPromptInjectionGuardrail({ sensitivity, allowlist })`
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
// WRONG — sensitivity + allowlist are hallucinated
|
|
350
|
+
createPromptInjectionGuardrail({ sensitivity: "high", allowlist: ["safe phrase"] })
|
|
351
|
+
|
|
352
|
+
// CORRECT — strictMode + blockThreshold + ignoreCategories + replacePatterns
|
|
353
|
+
createPromptInjectionGuardrail({
|
|
354
|
+
strictMode: true,
|
|
355
|
+
blockThreshold: 25,
|
|
356
|
+
ignoreCategories: ["role_manipulation"],
|
|
357
|
+
})
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Using `createPIIGuardrail` for output
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
// WRONG — createPIIGuardrail is input-only (GuardrailFn<InputGuardrailData>)
|
|
364
|
+
guardrails: { output: [createPIIGuardrail({ redact: true })] }
|
|
365
|
+
|
|
366
|
+
// CORRECT — createOutputPIIGuardrail for output
|
|
367
|
+
guardrails: { output: [createOutputPIIGuardrail()] }
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### `createCompliance` without `storage`
|
|
371
|
+
|
|
372
|
+
```typescript
|
|
373
|
+
// WRONG — storage is required
|
|
374
|
+
createCompliance({ retention: { maxAgeMs: 30 * 24 * 60 * 60 * 1000 } })
|
|
375
|
+
|
|
376
|
+
// CORRECT — pass your ComplianceStorage adapter
|
|
377
|
+
createCompliance({
|
|
378
|
+
storage: myStorage,
|
|
379
|
+
retention: { maxAgeMs: 30 * 24 * 60 * 60 * 1000, autoEnforce: true },
|
|
380
|
+
})
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## Quick reference
|
|
384
|
+
|
|
385
|
+
| API | Import path | Returns | Purpose |
|
|
386
|
+
|---|---|---|---|
|
|
387
|
+
| `createPIIGuardrail` | `@directive-run/ai/guardrails` | `GuardrailFn<InputGuardrailData>` | Basic input PII detection (block or redact) |
|
|
388
|
+
| `createEnhancedPIIGuardrail` | `@directive-run/ai/guardrails` | guardrail | Context-aware input PII |
|
|
389
|
+
| `createOutputPIIGuardrail` | `@directive-run/ai/guardrails` | guardrail | Output PII coverage |
|
|
390
|
+
| `createPromptInjectionGuardrail` | `@directive-run/ai/guardrails` | guardrail | Risk-scored injection defense (`strictMode`, `blockThreshold`) |
|
|
391
|
+
| `createAuditTrail` | `@directive-run/ai` | `AuditInstance` | Hash-chained audit log w/ signing + PII masking + async export |
|
|
392
|
+
| `createCompliance` | `@directive-run/ai` | `ComplianceInstance` | GDPR exportData / deleteData / consent + retention enforcement |
|
|
393
|
+
| `createToolGuardrail` | `@directive-run/ai/guardrails` | guardrail | Tool allow/deny lists |
|
|
394
|
+
| `createContentFilterGuardrail` | `@directive-run/ai/guardrails` | guardrail | Block sensitive patterns (block-only — no redact) |
|
|
395
|
+
| `GuardrailError` / `isGuardrailError` | `@directive-run/ai` | error class + guard | Carries `code`, `guardrailName`, `userMessage` |
|
|
396
|
+
|
|
397
|
+
## See also
|
|
398
|
+
|
|
399
|
+
- [`ai-guardrails-memory.md`](./ai-guardrails-memory.md) — the broader guardrail surface (length, schema, content filter, tool allowlist) this file's security-flavored guardrails compose with
|
|
400
|
+
- [`ai-mcp-rag.md`](./ai-mcp-rag.md) — `toolConstraints[…].requireApproval` is where MCP tool calls thread into the approval workflow surfaced here
|
package/ai/ai-tasks.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# AI Tasks
|
|
2
2
|
|
|
3
|
+
> Covers `@directive-run/ai` — deterministic non-LLM task registration with cancellation, retry, and scratchpad access; composes with multi-agent patterns.
|
|
4
|
+
|
|
3
5
|
Tasks are deterministic, non-LLM work units that run alongside agents in orchestration patterns. Use tasks for data transformation, API calls, file I/O, or any work that does not need an LLM.
|
|
4
6
|
|
|
5
7
|
## Decision Tree: "Should this be an agent or a task?"
|
|
@@ -65,7 +67,7 @@ interface TaskContext {
|
|
|
65
67
|
## Registering Tasks
|
|
66
68
|
|
|
67
69
|
```typescript
|
|
68
|
-
import { createMultiAgentOrchestrator } from "@directive-run/ai";
|
|
70
|
+
import { createMultiAgentOrchestrator } from "@directive-run/ai/multi-agent";
|
|
69
71
|
|
|
70
72
|
const orchestrator = createMultiAgentOrchestrator({
|
|
71
73
|
agents: {
|
|
@@ -259,3 +261,8 @@ tasks: {
|
|
|
259
261
|
| Concurrency control | Via patterns | `maxConcurrent` |
|
|
260
262
|
| Scratchpad access | No | Yes |
|
|
261
263
|
| Works in all patterns | Yes | Yes |
|
|
264
|
+
|
|
265
|
+
## See also
|
|
266
|
+
|
|
267
|
+
- [`ai-multi-agent.md`](./ai-multi-agent.md) — tasks share the `handler:` namespace with agents inside `dag` / `sequential` / `parallel` patterns
|
|
268
|
+
- [`ai-communication.md`](./ai-communication.md) — tasks read the scratchpad this file's `context.scratchpad` surface exposes
|