@directive-run/knowledge 0.2.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/LICENSE +21 -0
- package/README.md +63 -0
- package/ai/ai-adapters.md +250 -0
- package/ai/ai-agents-streaming.md +269 -0
- package/ai/ai-budget-resilience.md +235 -0
- package/ai/ai-communication.md +281 -0
- package/ai/ai-debug-observability.md +243 -0
- package/ai/ai-guardrails-memory.md +332 -0
- package/ai/ai-mcp-rag.md +288 -0
- package/ai/ai-multi-agent.md +274 -0
- package/ai/ai-orchestrator.md +227 -0
- package/ai/ai-security.md +293 -0
- package/ai/ai-tasks.md +261 -0
- package/ai/ai-testing-evals.md +378 -0
- package/api-skeleton.md +5 -0
- package/core/anti-patterns.md +382 -0
- package/core/constraints.md +263 -0
- package/core/core-patterns.md +228 -0
- package/core/error-boundaries.md +322 -0
- package/core/multi-module.md +315 -0
- package/core/naming.md +283 -0
- package/core/plugins.md +344 -0
- package/core/react-adapter.md +262 -0
- package/core/resolvers.md +357 -0
- package/core/schema-types.md +262 -0
- package/core/system-api.md +271 -0
- package/core/testing.md +257 -0
- package/core/time-travel.md +238 -0
- package/dist/index.cjs +111 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +102 -0
- package/dist/index.js.map +1 -0
- package/examples/ab-testing.ts +385 -0
- package/examples/ai-checkpoint.ts +509 -0
- package/examples/ai-guardrails.ts +319 -0
- package/examples/ai-orchestrator.ts +589 -0
- package/examples/async-chains.ts +287 -0
- package/examples/auth-flow.ts +371 -0
- package/examples/batch-resolver.ts +341 -0
- package/examples/checkers.ts +589 -0
- package/examples/contact-form.ts +176 -0
- package/examples/counter.ts +393 -0
- package/examples/dashboard-loader.ts +512 -0
- package/examples/debounce-constraints.ts +105 -0
- package/examples/dynamic-modules.ts +293 -0
- package/examples/error-boundaries.ts +430 -0
- package/examples/feature-flags.ts +220 -0
- package/examples/form-wizard.ts +347 -0
- package/examples/fraud-analysis.ts +663 -0
- package/examples/goal-heist.ts +341 -0
- package/examples/multi-module.ts +57 -0
- package/examples/newsletter.ts +241 -0
- package/examples/notifications.ts +210 -0
- package/examples/optimistic-updates.ts +317 -0
- package/examples/pagination.ts +260 -0
- package/examples/permissions.ts +337 -0
- package/examples/provider-routing.ts +403 -0
- package/examples/server.ts +316 -0
- package/examples/shopping-cart.ts +422 -0
- package/examples/sudoku.ts +630 -0
- package/examples/theme-locale.ts +204 -0
- package/examples/time-machine.ts +225 -0
- package/examples/topic-guard.ts +306 -0
- package/examples/url-sync.ts +333 -0
- package/examples/websocket.ts +404 -0
- package/package.json +65 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# AI Debug and Observability
|
|
2
|
+
|
|
3
|
+
Debug timeline with 25+ event types, breakpoints, checkpoints, and OpenTelemetry integration for AI orchestrators.
|
|
4
|
+
|
|
5
|
+
## Decision Tree: "How do I observe what's happening?"
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
What do you need?
|
|
9
|
+
├── Event stream of all AI activity → createDebugTimeline()
|
|
10
|
+
├── Pause execution at specific points → breakpoints config
|
|
11
|
+
├── Save/restore orchestrator state → checkpoint() / restore
|
|
12
|
+
├── Export traces to observability platform → createOTLPExporter()
|
|
13
|
+
│
|
|
14
|
+
Which events to watch?
|
|
15
|
+
├── Agent lifecycle → agent_start, agent_complete, agent_error, agent_retry
|
|
16
|
+
├── Guardrails → guardrail_check
|
|
17
|
+
├── Constraints/Resolvers → constraint_evaluate, resolver_start, resolver_complete
|
|
18
|
+
├── Approval workflow → approval_request, approval_response
|
|
19
|
+
├── Multi-agent patterns → pattern_start, pattern_complete, dag_node_update
|
|
20
|
+
├── Composition patterns → race_start, race_winner, race_cancelled,
|
|
21
|
+
│ debate_round, reflection_iteration
|
|
22
|
+
├── Handoffs → handoff_start, handoff_complete, reroute
|
|
23
|
+
├── Tasks → task_start, task_complete, task_error, task_progress, goal_step
|
|
24
|
+
├── Checkpoints → checkpoint_save, checkpoint_restore
|
|
25
|
+
└── Breakpoints → breakpoint_hit, breakpoint_resumed
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Debug Timeline
|
|
29
|
+
|
|
30
|
+
Subscribe to a real-time event stream of all orchestrator activity:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { createDebugTimeline } from "@directive-run/ai";
|
|
34
|
+
|
|
35
|
+
const timeline = createDebugTimeline({
|
|
36
|
+
maxEvents: 2000, // Ring buffer size (oldest evicted first)
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Subscribe to all events
|
|
40
|
+
const unsubscribe = timeline.subscribe((event) => {
|
|
41
|
+
console.log(`[${event.timestamp}] ${event.type}`, event);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Filter by event type
|
|
45
|
+
const agentEvents = timeline.subscribe(
|
|
46
|
+
(event) => {
|
|
47
|
+
console.log(`Agent: ${event.agentName} → ${event.type}`);
|
|
48
|
+
},
|
|
49
|
+
{ filter: (event) => event.type.startsWith("agent_") },
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// Query past events
|
|
53
|
+
const errors = timeline.query({ type: "agent_error" });
|
|
54
|
+
const recentAgentStarts = timeline.query({
|
|
55
|
+
type: "agent_start",
|
|
56
|
+
since: Date.now() - 60000,
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Event Types Reference
|
|
61
|
+
|
|
62
|
+
All 25+ event types emitted by the timeline:
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
// Agent lifecycle
|
|
66
|
+
type AgentEvents =
|
|
67
|
+
| { type: "agent_start"; agentName: string; prompt: string }
|
|
68
|
+
| { type: "agent_complete"; agentName: string; output: string; tokens: number; duration: number }
|
|
69
|
+
| { type: "agent_error"; agentName: string; error: Error }
|
|
70
|
+
| { type: "agent_retry"; agentName: string; attempt: number; maxRetries: number; error: Error };
|
|
71
|
+
|
|
72
|
+
// Guardrails
|
|
73
|
+
type GuardrailEvents =
|
|
74
|
+
| { type: "guardrail_check"; guardrailName: string; phase: "input" | "output"; passed: boolean; reason?: string };
|
|
75
|
+
|
|
76
|
+
// Constraints and resolvers
|
|
77
|
+
type ConstraintEvents =
|
|
78
|
+
| { type: "constraint_evaluate"; constraintId: string; result: boolean }
|
|
79
|
+
| { type: "resolver_start"; resolverType: string; requirementKey: string }
|
|
80
|
+
| { type: "resolver_complete"; resolverType: string; duration: number };
|
|
81
|
+
|
|
82
|
+
// Approval workflow
|
|
83
|
+
type ApprovalEvents =
|
|
84
|
+
| { type: "approval_request"; agentName: string; prompt: string }
|
|
85
|
+
| { type: "approval_response"; agentName: string; approved: boolean; reason?: string };
|
|
86
|
+
|
|
87
|
+
// Multi-agent patterns
|
|
88
|
+
type PatternEvents =
|
|
89
|
+
| { type: "pattern_start"; patternName: string; agents: string[] }
|
|
90
|
+
| { type: "pattern_complete"; patternName: string; duration: number }
|
|
91
|
+
| { type: "dag_node_update"; nodeId: string; status: "pending" | "running" | "complete" | "error" };
|
|
92
|
+
|
|
93
|
+
// Composition patterns
|
|
94
|
+
type CompositionEvents =
|
|
95
|
+
| { type: "race_start"; agents: string[] }
|
|
96
|
+
| { type: "race_winner"; agentName: string; duration: number }
|
|
97
|
+
| { type: "race_cancelled"; agentName: string; reason: string }
|
|
98
|
+
| { type: "debate_round"; round: number; agentName: string; position: string }
|
|
99
|
+
| { type: "reflection_iteration"; iteration: number; agentName: string };
|
|
100
|
+
|
|
101
|
+
// Handoffs and routing
|
|
102
|
+
type HandoffEvents =
|
|
103
|
+
| { type: "handoff_start"; from: string; to: string }
|
|
104
|
+
| { type: "handoff_complete"; from: string; to: string; duration: number }
|
|
105
|
+
| { type: "reroute"; from: string; to: string; reason: string };
|
|
106
|
+
|
|
107
|
+
// Checkpoints and breakpoints
|
|
108
|
+
type CheckpointEvents =
|
|
109
|
+
| { type: "checkpoint_save"; checkpointId: string }
|
|
110
|
+
| { type: "checkpoint_restore"; checkpointId: string }
|
|
111
|
+
| { type: "breakpoint_hit"; breakpointId: string; agentName: string }
|
|
112
|
+
| { type: "breakpoint_resumed"; breakpointId: string };
|
|
113
|
+
|
|
114
|
+
// Tasks
|
|
115
|
+
type TaskEvents =
|
|
116
|
+
| { type: "task_start"; taskId: string; label?: string }
|
|
117
|
+
| { type: "task_complete"; taskId: string; duration: number }
|
|
118
|
+
| { type: "task_error"; taskId: string; error: Error }
|
|
119
|
+
| { type: "task_progress"; taskId: string; percent: number; message?: string }
|
|
120
|
+
| { type: "goal_step"; iteration: number; goalMet: boolean };
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Attaching Timeline to Orchestrator
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { createAgentOrchestrator, createDebugTimeline } from "@directive-run/ai";
|
|
127
|
+
|
|
128
|
+
const timeline = createDebugTimeline({ maxEvents: 2000 });
|
|
129
|
+
|
|
130
|
+
const orchestrator = createAgentOrchestrator({
|
|
131
|
+
runner,
|
|
132
|
+
debug: {
|
|
133
|
+
timeline,
|
|
134
|
+
verbose: true, // Log all events to console
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Breakpoints
|
|
140
|
+
|
|
141
|
+
Pause execution at specific points for human inspection:
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
const orchestrator = createMultiAgentOrchestrator({
|
|
145
|
+
agents: { researcher, writer, editor },
|
|
146
|
+
runner,
|
|
147
|
+
debug: {
|
|
148
|
+
timeline,
|
|
149
|
+
breakpoints: [
|
|
150
|
+
{
|
|
151
|
+
id: "before-write",
|
|
152
|
+
// Pause before the writer agent runs
|
|
153
|
+
when: (event) => {
|
|
154
|
+
return event.type === "agent_start" && event.agentName === "writer";
|
|
155
|
+
},
|
|
156
|
+
onHit: async (event, resume) => {
|
|
157
|
+
console.log("Paused before writer. Review researcher output.");
|
|
158
|
+
console.log("Press enter to continue...");
|
|
159
|
+
|
|
160
|
+
await waitForInput();
|
|
161
|
+
resume();
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
id: "on-error",
|
|
166
|
+
when: (event) => {
|
|
167
|
+
return event.type === "agent_error";
|
|
168
|
+
},
|
|
169
|
+
onHit: async (event, resume) => {
|
|
170
|
+
console.error("Agent error:", event.error);
|
|
171
|
+
// Decide whether to continue or abort
|
|
172
|
+
resume();
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Checkpoints
|
|
181
|
+
|
|
182
|
+
Save and restore full orchestrator state for debugging or recovery:
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
// Save checkpoint
|
|
186
|
+
const checkpoint = orchestrator.checkpoint();
|
|
187
|
+
const serialized = JSON.stringify(checkpoint);
|
|
188
|
+
|
|
189
|
+
// Store to disk, database, etc.
|
|
190
|
+
await fs.writeFile("checkpoint.json", serialized);
|
|
191
|
+
|
|
192
|
+
// Restore from checkpoint
|
|
193
|
+
const saved = JSON.parse(await fs.readFile("checkpoint.json", "utf-8"));
|
|
194
|
+
const restored = createMultiAgentOrchestrator({
|
|
195
|
+
agents,
|
|
196
|
+
runner,
|
|
197
|
+
checkpoint: saved,
|
|
198
|
+
});
|
|
199
|
+
restored.start();
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## OpenTelemetry Integration
|
|
203
|
+
|
|
204
|
+
Export traces to any OpenTelemetry-compatible backend:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
import { createOTLPExporter } from "@directive-run/ai";
|
|
208
|
+
|
|
209
|
+
const exporter = createOTLPExporter({
|
|
210
|
+
endpoint: "http://localhost:4318/v1/traces",
|
|
211
|
+
serviceName: "my-ai-app",
|
|
212
|
+
// Uses GenAI semantic conventions
|
|
213
|
+
// https://opentelemetry.io/docs/specs/semconv/gen-ai/
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
const orchestrator = createAgentOrchestrator({
|
|
217
|
+
runner,
|
|
218
|
+
debug: {
|
|
219
|
+
timeline,
|
|
220
|
+
exporter, // Automatically exports spans
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
The exporter maps Directive events to GenAI semantic conventions:
|
|
226
|
+
|
|
227
|
+
| Directive Event | OTel Span | GenAI Attribute |
|
|
228
|
+
|---|---|---|
|
|
229
|
+
| `agent_start` → `agent_complete` | `gen_ai.chat` | `gen_ai.system`, `gen_ai.request.model` |
|
|
230
|
+
| `resolver_start` → `resolver_complete` | `gen_ai.tool` | `gen_ai.tool.name` |
|
|
231
|
+
| `guardrail_check` | `gen_ai.guardrail` | `gen_ai.guardrail.name`, `gen_ai.guardrail.passed` |
|
|
232
|
+
| `pattern_start` → `pattern_complete` | `gen_ai.orchestration` | `gen_ai.orchestration.pattern` |
|
|
233
|
+
|
|
234
|
+
## Quick Reference
|
|
235
|
+
|
|
236
|
+
| API | Purpose | Key Options |
|
|
237
|
+
|---|---|---|
|
|
238
|
+
| `createDebugTimeline()` | Event stream for all activity | `maxEvents` |
|
|
239
|
+
| `timeline.subscribe()` | Listen to events in real time | callback, filter |
|
|
240
|
+
| `timeline.query()` | Search past events | type, since |
|
|
241
|
+
| `orchestrator.checkpoint()` | Serialize full state | returns JSON-safe object |
|
|
242
|
+
| `createOTLPExporter()` | Export traces to OTel backend | `endpoint`, `serviceName` |
|
|
243
|
+
| breakpoints config | Pause at specific events | `when`, `onHit`, `resume` |
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# AI Guardrails and Memory
|
|
2
|
+
|
|
3
|
+
Built-in guardrails validate/transform input and output. Memory strategies manage conversation history with configurable summarization.
|
|
4
|
+
|
|
5
|
+
## Decision Tree: "Which guardrail do I need?"
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
What are you guarding against?
|
|
9
|
+
├── PII in input/output → createPIIGuardrail()
|
|
10
|
+
├── Harmful content → createModerationGuardrail()
|
|
11
|
+
├── Rate limits → createRateLimitGuardrail()
|
|
12
|
+
├── Unauthorized tool use → createToolGuardrail()
|
|
13
|
+
├── Output format validation → createOutputSchemaGuardrail()
|
|
14
|
+
├── Output type checking → createOutputTypeGuardrail()
|
|
15
|
+
├── Response length → createLengthGuardrail()
|
|
16
|
+
└── Banned words/patterns → createContentFilterGuardrail()
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## GuardrailResult Shape
|
|
20
|
+
|
|
21
|
+
Every guardrail returns this shape:
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
interface GuardrailResult {
|
|
25
|
+
// Did the input/output pass?
|
|
26
|
+
passed: boolean;
|
|
27
|
+
|
|
28
|
+
// Why it failed (when passed: false)
|
|
29
|
+
reason?: string;
|
|
30
|
+
|
|
31
|
+
// Modified data — guardrail can transform the input/output
|
|
32
|
+
transformed?: unknown;
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
When `transformed` is set, the modified value replaces the original for downstream processing.
|
|
37
|
+
|
|
38
|
+
## Built-In Guardrails
|
|
39
|
+
|
|
40
|
+
### PII Detection and Redaction
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { createPIIGuardrail } from "@directive-run/ai";
|
|
44
|
+
|
|
45
|
+
const piiGuardrail = createPIIGuardrail({
|
|
46
|
+
// Additional regex patterns beyond defaults
|
|
47
|
+
patterns: [/CUSTOM-\d{8}/g],
|
|
48
|
+
|
|
49
|
+
// Redact instead of blocking (default: false)
|
|
50
|
+
redact: true,
|
|
51
|
+
|
|
52
|
+
// Replacement string (default: "[REDACTED]")
|
|
53
|
+
redactReplacement: "***",
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Content Moderation
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { createModerationGuardrail } from "@directive-run/ai";
|
|
61
|
+
|
|
62
|
+
const moderationGuardrail = createModerationGuardrail({
|
|
63
|
+
// Custom check function — return true if content is safe
|
|
64
|
+
checkFn: async (content) => {
|
|
65
|
+
const result = await moderationAPI.check(content);
|
|
66
|
+
|
|
67
|
+
return result.safe;
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
// Custom rejection message
|
|
71
|
+
message: "Content flagged by moderation",
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Rate Limiting
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { createRateLimitGuardrail } from "@directive-run/ai";
|
|
79
|
+
|
|
80
|
+
const rateLimitGuardrail = createRateLimitGuardrail({
|
|
81
|
+
maxTokensPerMinute: 50000,
|
|
82
|
+
maxRequestsPerMinute: 10,
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Tool Allowlist
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { createToolGuardrail } from "@directive-run/ai";
|
|
90
|
+
|
|
91
|
+
const toolGuardrail = createToolGuardrail({
|
|
92
|
+
allowedTools: ["search", "calculator", "readFile"],
|
|
93
|
+
// Any tool not in this list is blocked
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Output Schema Validation
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { createOutputSchemaGuardrail } from "@directive-run/ai";
|
|
101
|
+
|
|
102
|
+
const schemaGuardrail = createOutputSchemaGuardrail({
|
|
103
|
+
schema: {
|
|
104
|
+
type: "object",
|
|
105
|
+
properties: {
|
|
106
|
+
title: { type: "string" },
|
|
107
|
+
score: { type: "number", minimum: 0, maximum: 100 },
|
|
108
|
+
},
|
|
109
|
+
required: ["title", "score"],
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
// Retry with schema feedback if validation fails (default: 0)
|
|
113
|
+
retries: 2,
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Output Type Guard
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
import { createOutputTypeGuardrail } from "@directive-run/ai";
|
|
121
|
+
|
|
122
|
+
const typeGuardrail = createOutputTypeGuardrail({
|
|
123
|
+
type: "object", // "string" | "number" | "boolean" | "object" | "array"
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Length Constraints
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { createLengthGuardrail } from "@directive-run/ai";
|
|
131
|
+
|
|
132
|
+
const lengthGuardrail = createLengthGuardrail({
|
|
133
|
+
minChars: 100,
|
|
134
|
+
maxChars: 5000,
|
|
135
|
+
minTokens: 50,
|
|
136
|
+
maxTokens: 1000,
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Content Filter
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { createContentFilterGuardrail } from "@directive-run/ai";
|
|
144
|
+
|
|
145
|
+
const contentFilter = createContentFilterGuardrail({
|
|
146
|
+
patterns: [/badword/i, /sensitive-term/gi],
|
|
147
|
+
|
|
148
|
+
// "block" (default) or "redact"
|
|
149
|
+
action: "redact",
|
|
150
|
+
|
|
151
|
+
// Replacement for redact mode
|
|
152
|
+
replacement: "[FILTERED]",
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Applying Guardrails
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
const orchestrator = createAgentOrchestrator({
|
|
160
|
+
runner,
|
|
161
|
+
guardrails: {
|
|
162
|
+
// Run before the agent receives the prompt
|
|
163
|
+
input: [piiGuardrail, rateLimitGuardrail],
|
|
164
|
+
|
|
165
|
+
// Run after the agent produces output
|
|
166
|
+
output: [lengthGuardrail, schemaGuardrail, contentFilter],
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Anti-Pattern #25: Catching Error Instead of GuardrailError
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
// WRONG — loses guardrail-specific metadata
|
|
175
|
+
try {
|
|
176
|
+
const result = await orchestrator.run(agent, prompt);
|
|
177
|
+
} catch (error) {
|
|
178
|
+
if (error instanceof Error) {
|
|
179
|
+
console.log(error.message); // No guardrail context
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// CORRECT — catch GuardrailError for full context
|
|
184
|
+
import { GuardrailError } from "@directive-run/ai";
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
const result = await orchestrator.run(agent, prompt);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
if (error instanceof GuardrailError) {
|
|
190
|
+
console.log(error.guardrailName); // "pii-detection"
|
|
191
|
+
console.log(error.errorCode); // "GUARDRAIL_INPUT_BLOCKED"
|
|
192
|
+
console.log(error.reason); // "PII detected in input"
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Memory Strategies
|
|
200
|
+
|
|
201
|
+
Memory strategies control how conversation history is managed when it grows too large.
|
|
202
|
+
|
|
203
|
+
## Decision Tree: "Which memory strategy?"
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
How should history be trimmed?
|
|
207
|
+
├── Keep N most recent messages → createSlidingWindowStrategy()
|
|
208
|
+
├── Keep within token budget → createTokenBasedStrategy()
|
|
209
|
+
└── Both constraints → createHybridStrategy()
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Sliding Window
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
import { createAgentMemory, createSlidingWindowStrategy } from "@directive-run/ai";
|
|
216
|
+
|
|
217
|
+
const memory = createAgentMemory({
|
|
218
|
+
strategy: createSlidingWindowStrategy({
|
|
219
|
+
maxMessages: 50,
|
|
220
|
+
|
|
221
|
+
// Always keep the N most recent (default: 5)
|
|
222
|
+
preserveRecentCount: 10,
|
|
223
|
+
}),
|
|
224
|
+
});
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Token-Based
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
import { createAgentMemory, createTokenBasedStrategy } from "@directive-run/ai";
|
|
231
|
+
|
|
232
|
+
const memory = createAgentMemory({
|
|
233
|
+
strategy: createTokenBasedStrategy({
|
|
234
|
+
maxTokens: 8000,
|
|
235
|
+
preserveRecentCount: 5,
|
|
236
|
+
}),
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Hybrid (Both Constraints)
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
import { createAgentMemory, createHybridStrategy } from "@directive-run/ai";
|
|
244
|
+
|
|
245
|
+
const memory = createAgentMemory({
|
|
246
|
+
strategy: createHybridStrategy({
|
|
247
|
+
maxMessages: 100,
|
|
248
|
+
maxTokens: 16000,
|
|
249
|
+
}),
|
|
250
|
+
});
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Summarizers
|
|
254
|
+
|
|
255
|
+
When messages are evicted, a summarizer condenses them:
|
|
256
|
+
|
|
257
|
+
### Truncation (Default)
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
import { createTruncationSummarizer } from "@directive-run/ai";
|
|
261
|
+
|
|
262
|
+
// Simply drops old messages — no summary generated
|
|
263
|
+
const summarizer = createTruncationSummarizer();
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Key Points Extraction
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
import { createKeyPointsSummarizer } from "@directive-run/ai";
|
|
270
|
+
|
|
271
|
+
// Extracts bullet points from evicted messages (rule-based, no LLM)
|
|
272
|
+
const summarizer = createKeyPointsSummarizer();
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### LLM-Based Summarization
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
import { createLLMSummarizer } from "@directive-run/ai";
|
|
279
|
+
|
|
280
|
+
// Uses the runner to summarize evicted messages via LLM
|
|
281
|
+
const summarizer = createLLMSummarizer(runner);
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Applying to Memory
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
const memory = createAgentMemory({
|
|
288
|
+
strategy: createSlidingWindowStrategy({ maxMessages: 50 }),
|
|
289
|
+
summarizer: createKeyPointsSummarizer(),
|
|
290
|
+
autoManage: true, // Automatically trim + summarize (default: true)
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
const orchestrator = createAgentOrchestrator({
|
|
294
|
+
runner,
|
|
295
|
+
memory,
|
|
296
|
+
});
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Anti-Pattern #31: Async Summarizer Without autoManage: false
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
// WRONG — LLM summarizer is async but autoManage runs synchronously
|
|
303
|
+
const memory = createAgentMemory({
|
|
304
|
+
strategy: createSlidingWindowStrategy({ maxMessages: 20 }),
|
|
305
|
+
summarizer: createLLMSummarizer(runner),
|
|
306
|
+
autoManage: true, // Will not await the summarizer properly
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// CORRECT — disable autoManage, call memory.manage() manually
|
|
310
|
+
const memory = createAgentMemory({
|
|
311
|
+
strategy: createSlidingWindowStrategy({ maxMessages: 20 }),
|
|
312
|
+
summarizer: createLLMSummarizer(runner),
|
|
313
|
+
autoManage: false,
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
// After each run, manually manage memory
|
|
317
|
+
const result = await orchestrator.run(agent, prompt);
|
|
318
|
+
await memory.manage(); // Awaits the async summarizer
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Quick Reference
|
|
322
|
+
|
|
323
|
+
| Guardrail | Input/Output | Key Option |
|
|
324
|
+
|---|---|---|
|
|
325
|
+
| `createPIIGuardrail` | Both | `redact`, `patterns` |
|
|
326
|
+
| `createModerationGuardrail` | Both | `checkFn` |
|
|
327
|
+
| `createRateLimitGuardrail` | Input | `maxTokensPerMinute` |
|
|
328
|
+
| `createToolGuardrail` | Input | `allowedTools` |
|
|
329
|
+
| `createOutputSchemaGuardrail` | Output | `schema`, `retries` |
|
|
330
|
+
| `createOutputTypeGuardrail` | Output | `type` |
|
|
331
|
+
| `createLengthGuardrail` | Output | `minChars`, `maxChars` |
|
|
332
|
+
| `createContentFilterGuardrail` | Both | `patterns`, `action` |
|