@directive-run/ai 0.1.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/LICENSE +21 -0
- package/README.md +146 -0
- package/dist/anthropic.cjs +3 -0
- package/dist/anthropic.cjs.map +1 -0
- package/dist/anthropic.d.cts +103 -0
- package/dist/anthropic.d.ts +103 -0
- package/dist/anthropic.js +3 -0
- package/dist/anthropic.js.map +1 -0
- package/dist/index.cjs +78 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +4663 -0
- package/dist/index.d.ts +4663 -0
- package/dist/index.js +78 -0
- package/dist/index.js.map +1 -0
- package/dist/ollama.cjs +3 -0
- package/dist/ollama.cjs.map +1 -0
- package/dist/ollama.d.cts +47 -0
- package/dist/ollama.d.ts +47 -0
- package/dist/ollama.js +3 -0
- package/dist/ollama.js.map +1 -0
- package/dist/openai.cjs +3 -0
- package/dist/openai.cjs.map +1 -0
- package/dist/openai.d.cts +127 -0
- package/dist/openai.d.ts +127 -0
- package/dist/openai.js +3 -0
- package/dist/openai.js.map +1 -0
- package/dist/testing.cjs +14 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +345 -0
- package/dist/testing.d.ts +345 -0
- package/dist/testing.js +14 -0
- package/dist/testing.js.map +1 -0
- package/dist/types-BKCdgKC-.d.cts +300 -0
- package/dist/types-BKCdgKC-.d.ts +300 -0
- package/package.json +83 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4663 @@
|
|
|
1
|
+
import { Requirement, ModuleSchema, Plugin, System } from '@directive-run/core';
|
|
2
|
+
import { CircuitState, ObservabilityInstance, TraceSpan, AggregatedMetric, CircuitBreakerConfig, CircuitBreaker, AlertConfig } from '@directive-run/core/plugins';
|
|
3
|
+
export { AggregatedMetric, AlertConfig, AlertEvent, CircuitBreaker, CircuitBreakerConfig, CircuitBreakerOpenError, CircuitBreakerStats, CircuitState, DashboardData, MetricDataPoint, MetricType, OTLPExporter, OTLPExporterConfig, ObservabilityConfig, ObservabilityInstance, TraceSpan, createAgentMetrics, createCircuitBreaker, createOTLPExporter, createObservability } from '@directive-run/core/plugins';
|
|
4
|
+
import { M as Message$1, g as RunResult, b as AgentLike, d as GuardrailFn, O as OutputGuardrailData, I as InputGuardrailData, S as SchemaValidator, T as ToolCallGuardrailData, f as AdapterHooks, c as AgentRunner, h as ApprovalState, i as AgentState, j as OrchestratorState, k as OrchestratorConstraint, R as RunOptions, N as NamedGuardrail, l as OrchestratorResolver, A as ApprovalRequest, m as AgentRetryConfig, n as OrchestratorLifecycleHooks, o as GuardrailsConfig } from './types-BKCdgKC-.js';
|
|
5
|
+
export { e as GuardrailContext, p as GuardrailError, q as GuardrailErrorCode, G as GuardrailResult, r as GuardrailRetryConfig, s as OrchestratorResolverContext, t as RejectedRequest, u as SchemaValidationResult, v as TokenUsage, a as ToolCall, w as isGuardrailError } from './types-BKCdgKC-.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Agent Memory System
|
|
9
|
+
*
|
|
10
|
+
* Provides sliding window message management and automatic summarization
|
|
11
|
+
* for long-running agent conversations.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { createAgentMemory, createSlidingWindowStrategy } from '@directive-run/ai';
|
|
16
|
+
*
|
|
17
|
+
* const memory = createAgentMemory({
|
|
18
|
+
* strategy: createSlidingWindowStrategy({ maxMessages: 50 }),
|
|
19
|
+
* summarizer: async (messages) => {
|
|
20
|
+
* // Call LLM to summarize older messages
|
|
21
|
+
* return await summarizeWithLLM(messages);
|
|
22
|
+
* },
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* // Use with orchestrator
|
|
26
|
+
* const orchestrator = createAgentOrchestrator({
|
|
27
|
+
* memory,
|
|
28
|
+
* runner: run,
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* Memory-compatible message type.
|
|
34
|
+
* Extends the standard Message type to include system messages for summaries.
|
|
35
|
+
*/
|
|
36
|
+
interface MemoryMessage {
|
|
37
|
+
role: "user" | "assistant" | "tool" | "system";
|
|
38
|
+
content: string;
|
|
39
|
+
toolCallId?: string;
|
|
40
|
+
}
|
|
41
|
+
type Message = MemoryMessage;
|
|
42
|
+
/** Configuration for memory management strategies */
|
|
43
|
+
interface MemoryStrategyConfig {
|
|
44
|
+
/** Maximum number of messages to keep in active memory */
|
|
45
|
+
maxMessages?: number;
|
|
46
|
+
/** Maximum total tokens to keep in active memory */
|
|
47
|
+
maxTokens?: number;
|
|
48
|
+
/** Number of recent messages to always keep (protected from summarization) */
|
|
49
|
+
preserveRecentCount?: number;
|
|
50
|
+
/** Whether to include system messages in token count */
|
|
51
|
+
countSystemMessages?: boolean;
|
|
52
|
+
}
|
|
53
|
+
/** Result of a memory strategy evaluation */
|
|
54
|
+
interface MemoryStrategyResult {
|
|
55
|
+
/** Messages to keep in active memory */
|
|
56
|
+
keep: Message[];
|
|
57
|
+
/** Messages to summarize or discard */
|
|
58
|
+
toSummarize: Message[];
|
|
59
|
+
/** Estimated token count of kept messages */
|
|
60
|
+
estimatedTokens: number;
|
|
61
|
+
}
|
|
62
|
+
/** Memory management strategy function */
|
|
63
|
+
type MemoryStrategy = (messages: Message[], config: MemoryStrategyConfig) => MemoryStrategyResult;
|
|
64
|
+
/** Summarizer function to compress older messages */
|
|
65
|
+
type MessageSummarizer = (messages: Message[]) => Promise<string>;
|
|
66
|
+
/** Agent memory configuration */
|
|
67
|
+
interface AgentMemoryConfig {
|
|
68
|
+
/** Memory management strategy */
|
|
69
|
+
strategy: MemoryStrategy;
|
|
70
|
+
/** Optional summarizer for compressing old messages */
|
|
71
|
+
summarizer?: MessageSummarizer;
|
|
72
|
+
/** Strategy configuration */
|
|
73
|
+
strategyConfig?: MemoryStrategyConfig;
|
|
74
|
+
/** Whether to auto-manage memory after each interaction */
|
|
75
|
+
autoManage?: boolean;
|
|
76
|
+
/** Callback when memory is managed */
|
|
77
|
+
onMemoryManaged?: (result: MemoryManageResult) => void;
|
|
78
|
+
/** Callback when auto-manage encounters an error */
|
|
79
|
+
onManageError?: (error: Error) => void;
|
|
80
|
+
/** Maximum context window tokens (triggers additional summarization if exceeded) */
|
|
81
|
+
maxContextTokens?: number;
|
|
82
|
+
}
|
|
83
|
+
/** Result of memory management */
|
|
84
|
+
interface MemoryManageResult {
|
|
85
|
+
/** Number of messages before management */
|
|
86
|
+
messagesBefore: number;
|
|
87
|
+
/** Number of messages after management */
|
|
88
|
+
messagesAfter: number;
|
|
89
|
+
/** Number of messages summarized */
|
|
90
|
+
messagesSummarized: number;
|
|
91
|
+
/** The summary that was generated (if any) */
|
|
92
|
+
summary?: string;
|
|
93
|
+
/** Estimated tokens before */
|
|
94
|
+
estimatedTokensBefore: number;
|
|
95
|
+
/** Estimated tokens after */
|
|
96
|
+
estimatedTokensAfter: number;
|
|
97
|
+
}
|
|
98
|
+
/** Memory state for a conversation */
|
|
99
|
+
interface MemoryState {
|
|
100
|
+
/** Active messages in memory */
|
|
101
|
+
messages: Message[];
|
|
102
|
+
/** Summaries of older messages */
|
|
103
|
+
summaries: Array<{
|
|
104
|
+
content: string;
|
|
105
|
+
messagesCount: number;
|
|
106
|
+
createdAt: number;
|
|
107
|
+
}>;
|
|
108
|
+
/** Total messages ever processed */
|
|
109
|
+
totalMessagesProcessed: number;
|
|
110
|
+
/** Estimated current token count */
|
|
111
|
+
estimatedTokens: number;
|
|
112
|
+
}
|
|
113
|
+
/** Agent memory instance */
|
|
114
|
+
interface AgentMemory {
|
|
115
|
+
/** Get current memory state */
|
|
116
|
+
getState(): MemoryState;
|
|
117
|
+
/** Add a message to memory */
|
|
118
|
+
addMessage(message: Message): void;
|
|
119
|
+
/** Check if memory management is currently in progress */
|
|
120
|
+
isManaging(): boolean;
|
|
121
|
+
/** Add multiple messages to memory */
|
|
122
|
+
addMessages(messages: Message[]): void;
|
|
123
|
+
/** Get messages for context (includes summaries as system messages) */
|
|
124
|
+
getContextMessages(): Message[];
|
|
125
|
+
/** Manually trigger memory management */
|
|
126
|
+
manage(): Promise<MemoryManageResult>;
|
|
127
|
+
/** Clear all memory */
|
|
128
|
+
clear(): void;
|
|
129
|
+
/** Export memory state for persistence */
|
|
130
|
+
export(): MemoryState;
|
|
131
|
+
/** Import memory state from persistence */
|
|
132
|
+
import(state: MemoryState): void;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Create a sliding window memory strategy.
|
|
136
|
+
*
|
|
137
|
+
* Keeps the most recent N messages, moving older ones to summarization.
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```typescript
|
|
141
|
+
* const strategy = createSlidingWindowStrategy({
|
|
142
|
+
* maxMessages: 50,
|
|
143
|
+
* preserveRecentCount: 10,
|
|
144
|
+
* });
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
declare function createSlidingWindowStrategy(defaultConfig?: MemoryStrategyConfig): MemoryStrategy;
|
|
148
|
+
/**
|
|
149
|
+
* Create a token-based memory strategy.
|
|
150
|
+
*
|
|
151
|
+
* Keeps messages until a token limit is reached, then moves older ones to summarization.
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* const strategy = createTokenBasedStrategy({
|
|
156
|
+
* maxTokens: 4000,
|
|
157
|
+
* preserveRecentCount: 5,
|
|
158
|
+
* });
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
declare function createTokenBasedStrategy(defaultConfig?: MemoryStrategyConfig): MemoryStrategy;
|
|
162
|
+
/**
|
|
163
|
+
* Create a hybrid strategy that combines message count and token limits.
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```typescript
|
|
167
|
+
* const strategy = createHybridStrategy({
|
|
168
|
+
* maxMessages: 50,
|
|
169
|
+
* maxTokens: 4000,
|
|
170
|
+
* preserveRecentCount: 5,
|
|
171
|
+
* });
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
declare function createHybridStrategy(defaultConfig?: MemoryStrategyConfig): MemoryStrategy;
|
|
175
|
+
/**
|
|
176
|
+
* Create an agent memory instance.
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```typescript
|
|
180
|
+
* const memory = createAgentMemory({
|
|
181
|
+
* strategy: createSlidingWindowStrategy({ maxMessages: 50 }),
|
|
182
|
+
* summarizer: async (messages) => {
|
|
183
|
+
* const response = await openai.chat.completions.create({
|
|
184
|
+
* model: 'gpt-4o-mini',
|
|
185
|
+
* messages: [
|
|
186
|
+
* { role: 'system', content: 'Summarize the following conversation concisely.' },
|
|
187
|
+
* ...messages.map(m => ({ role: m.role, content: m.content })),
|
|
188
|
+
* ],
|
|
189
|
+
* });
|
|
190
|
+
* return response.choices[0].message.content;
|
|
191
|
+
* },
|
|
192
|
+
* autoManage: true,
|
|
193
|
+
* });
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
declare function createAgentMemory(config: AgentMemoryConfig): AgentMemory;
|
|
197
|
+
/**
|
|
198
|
+
* Create a simple truncation "summarizer" that just returns key points.
|
|
199
|
+
* Useful for testing or when LLM summarization isn't needed.
|
|
200
|
+
*/
|
|
201
|
+
declare function createTruncationSummarizer(maxLength?: number): MessageSummarizer;
|
|
202
|
+
/**
|
|
203
|
+
* Create a summarizer that extracts only user questions and key assistant answers.
|
|
204
|
+
*/
|
|
205
|
+
declare function createKeyPointsSummarizer(): MessageSummarizer;
|
|
206
|
+
/**
|
|
207
|
+
* Create a summarizer factory for LLM-based summarization.
|
|
208
|
+
* You provide the LLM call function, this handles the prompt.
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* ```typescript
|
|
212
|
+
* const summarizer = createLLMSummarizer(async (prompt) => {
|
|
213
|
+
* const response = await openai.chat.completions.create({
|
|
214
|
+
* model: 'gpt-4o-mini',
|
|
215
|
+
* messages: [{ role: 'user', content: prompt }],
|
|
216
|
+
* });
|
|
217
|
+
* return response.choices[0].message.content ?? '';
|
|
218
|
+
* });
|
|
219
|
+
* ```
|
|
220
|
+
*/
|
|
221
|
+
declare function createLLMSummarizer(llmCall: (prompt: string) => Promise<string>, options?: {
|
|
222
|
+
maxSummaryLength?: number;
|
|
223
|
+
preserveKeyFacts?: boolean;
|
|
224
|
+
}): MessageSummarizer;
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* OpenAI Agents Streaming - Token-by-token streaming with backpressure support
|
|
228
|
+
*
|
|
229
|
+
* Provides async iterators for streaming agent responses with guardrail evaluation
|
|
230
|
+
* on partial output and configurable backpressure handling.
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```typescript
|
|
234
|
+
* import { createAgentOrchestrator } from '@directive-run/ai';
|
|
235
|
+
* import { createStreamingRunner } from '@directive-run/ai';
|
|
236
|
+
*
|
|
237
|
+
* const { stream, result } = orchestrator.runStream(agent, input);
|
|
238
|
+
*
|
|
239
|
+
* for await (const chunk of stream) {
|
|
240
|
+
* if (chunk.type === 'token') process.stdout.write(chunk.data);
|
|
241
|
+
* if (chunk.type === 'guardrail_triggered') handleGuardrail(chunk);
|
|
242
|
+
* }
|
|
243
|
+
*
|
|
244
|
+
* const finalResult = await result;
|
|
245
|
+
* ```
|
|
246
|
+
*/
|
|
247
|
+
|
|
248
|
+
/** Token chunk from streaming response */
|
|
249
|
+
interface TokenChunk {
|
|
250
|
+
type: "token";
|
|
251
|
+
data: string;
|
|
252
|
+
/** Running total of tokens received */
|
|
253
|
+
tokenCount: number;
|
|
254
|
+
}
|
|
255
|
+
/** Tool execution started */
|
|
256
|
+
interface ToolStartChunk {
|
|
257
|
+
type: "tool_start";
|
|
258
|
+
tool: string;
|
|
259
|
+
toolCallId: string;
|
|
260
|
+
arguments: string;
|
|
261
|
+
}
|
|
262
|
+
/** Tool execution completed */
|
|
263
|
+
interface ToolEndChunk {
|
|
264
|
+
type: "tool_end";
|
|
265
|
+
tool: string;
|
|
266
|
+
toolCallId: string;
|
|
267
|
+
result: string;
|
|
268
|
+
}
|
|
269
|
+
/** Message added to conversation */
|
|
270
|
+
interface MessageChunk {
|
|
271
|
+
type: "message";
|
|
272
|
+
message: Message$1;
|
|
273
|
+
}
|
|
274
|
+
/** Guardrail was triggered during streaming */
|
|
275
|
+
interface GuardrailTriggeredChunk {
|
|
276
|
+
type: "guardrail_triggered";
|
|
277
|
+
guardrailName: string;
|
|
278
|
+
reason: string;
|
|
279
|
+
/** Partial output at the time of trigger */
|
|
280
|
+
partialOutput: string;
|
|
281
|
+
/** Whether the stream was stopped */
|
|
282
|
+
stopped: boolean;
|
|
283
|
+
}
|
|
284
|
+
/** Progress update for UI feedback */
|
|
285
|
+
interface ProgressChunk {
|
|
286
|
+
type: "progress";
|
|
287
|
+
phase: "starting" | "generating" | "tool_calling" | "finishing";
|
|
288
|
+
/** Percentage complete (0-100), if known */
|
|
289
|
+
percent?: number;
|
|
290
|
+
/** Human-readable status message */
|
|
291
|
+
message?: string;
|
|
292
|
+
}
|
|
293
|
+
/** Stream completed */
|
|
294
|
+
interface DoneChunk {
|
|
295
|
+
type: "done";
|
|
296
|
+
totalTokens: number;
|
|
297
|
+
duration: number;
|
|
298
|
+
/** Number of tokens dropped due to backpressure (only with 'drop' strategy) */
|
|
299
|
+
droppedTokens: number;
|
|
300
|
+
}
|
|
301
|
+
/** Error during streaming */
|
|
302
|
+
interface ErrorChunk {
|
|
303
|
+
type: "error";
|
|
304
|
+
error: Error;
|
|
305
|
+
/** Partial output before error */
|
|
306
|
+
partialOutput?: string;
|
|
307
|
+
}
|
|
308
|
+
/** Union of all stream chunk types */
|
|
309
|
+
type StreamChunk = TokenChunk | ToolStartChunk | ToolEndChunk | MessageChunk | GuardrailTriggeredChunk | ProgressChunk | DoneChunk | ErrorChunk;
|
|
310
|
+
/** Backpressure strategy when consumer is slow */
|
|
311
|
+
type BackpressureStrategy =
|
|
312
|
+
/** Drop tokens when buffer is full (lossy, fast) */
|
|
313
|
+
"drop"
|
|
314
|
+
/** Block producer when buffer is full (lossless, may slow response) */
|
|
315
|
+
| "block"
|
|
316
|
+
/** Buffer all tokens (lossless, uses memory) */
|
|
317
|
+
| "buffer";
|
|
318
|
+
/** Streaming run options */
|
|
319
|
+
interface StreamRunOptions {
|
|
320
|
+
/** Maximum turns before stopping */
|
|
321
|
+
maxTurns?: number;
|
|
322
|
+
/** Abort signal for cancellation */
|
|
323
|
+
signal?: AbortSignal;
|
|
324
|
+
/** Backpressure strategy (default: 'buffer') */
|
|
325
|
+
backpressure?: BackpressureStrategy;
|
|
326
|
+
/** Buffer size for 'drop' and 'block' strategies */
|
|
327
|
+
bufferSize?: number;
|
|
328
|
+
/** Evaluate guardrails every N tokens (default: 50) */
|
|
329
|
+
guardrailCheckInterval?: number;
|
|
330
|
+
/** Stop stream on guardrail trigger (default: true for critical) */
|
|
331
|
+
stopOnGuardrail?: boolean | ((chunk: GuardrailTriggeredChunk) => boolean);
|
|
332
|
+
}
|
|
333
|
+
/** Stream run function type (mirrors OpenAI Agents streaming API) */
|
|
334
|
+
type StreamRunner = <T = unknown>(agent: AgentLike, input: string, options?: StreamRunOptions) => StreamingRunResult<T>;
|
|
335
|
+
/** Result from a streaming run */
|
|
336
|
+
interface StreamingRunResult<T = unknown> {
|
|
337
|
+
/** Async iterator for streaming chunks */
|
|
338
|
+
stream: AsyncIterable<StreamChunk>;
|
|
339
|
+
/** Promise that resolves to the final result */
|
|
340
|
+
result: Promise<RunResult<T>>;
|
|
341
|
+
/** Abort the stream */
|
|
342
|
+
abort: () => void;
|
|
343
|
+
}
|
|
344
|
+
/** Streaming guardrail that evaluates partial output */
|
|
345
|
+
interface StreamingGuardrail {
|
|
346
|
+
/** Unique name for this guardrail */
|
|
347
|
+
name: string;
|
|
348
|
+
/** Check partial output (called every guardrailCheckInterval tokens) */
|
|
349
|
+
check: (partialOutput: string, tokenCount: number) => StreamingGuardrailResult | Promise<StreamingGuardrailResult>;
|
|
350
|
+
/** Whether to stop the stream on failure (default: true) */
|
|
351
|
+
stopOnFail?: boolean;
|
|
352
|
+
}
|
|
353
|
+
/** Result from a streaming guardrail check */
|
|
354
|
+
interface StreamingGuardrailResult {
|
|
355
|
+
passed: boolean;
|
|
356
|
+
reason?: string;
|
|
357
|
+
/** Severity level for UI display */
|
|
358
|
+
severity?: "warning" | "error" | "critical";
|
|
359
|
+
/** Warning message (guardrail passed but wants to emit a warning) */
|
|
360
|
+
warning?: string;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Create a streaming runner that wraps a base run function.
|
|
364
|
+
* This is used internally by the orchestrator but can be used standalone.
|
|
365
|
+
*
|
|
366
|
+
* @param baseRunner - The underlying non-streaming runner
|
|
367
|
+
* @param options - Configuration options
|
|
368
|
+
*/
|
|
369
|
+
declare function createStreamingRunner(baseRunner: (agent: AgentLike, input: string, callbacks: {
|
|
370
|
+
onToken?: (token: string) => void;
|
|
371
|
+
onToolStart?: (tool: string, id: string, args: string) => void;
|
|
372
|
+
onToolEnd?: (tool: string, id: string, result: string) => void;
|
|
373
|
+
onMessage?: (message: Message$1) => void;
|
|
374
|
+
signal?: AbortSignal;
|
|
375
|
+
}) => Promise<RunResult<unknown>>, options?: {
|
|
376
|
+
streamingGuardrails?: StreamingGuardrail[];
|
|
377
|
+
}): StreamRunner;
|
|
378
|
+
/**
|
|
379
|
+
* Create a streaming guardrail that detects toxic content.
|
|
380
|
+
*
|
|
381
|
+
* @example
|
|
382
|
+
* ```typescript
|
|
383
|
+
* const toxicityGuardrail = createToxicityStreamingGuardrail({
|
|
384
|
+
* threshold: 0.9,
|
|
385
|
+
* checkFn: async (text) => myToxicityModel.score(text),
|
|
386
|
+
* });
|
|
387
|
+
* ```
|
|
388
|
+
*/
|
|
389
|
+
declare function createToxicityStreamingGuardrail(options: {
|
|
390
|
+
/** Toxicity scoring function (returns 0-1) */
|
|
391
|
+
checkFn: (text: string) => number | Promise<number>;
|
|
392
|
+
/** Threshold above which content is flagged (default: 0.8) */
|
|
393
|
+
threshold?: number;
|
|
394
|
+
/** Stop the stream on detection (default: true) */
|
|
395
|
+
stopOnFail?: boolean;
|
|
396
|
+
}): StreamingGuardrail;
|
|
397
|
+
/**
|
|
398
|
+
* Create a streaming guardrail that limits output length.
|
|
399
|
+
*
|
|
400
|
+
* @example
|
|
401
|
+
* ```typescript
|
|
402
|
+
* const lengthGuardrail = createLengthStreamingGuardrail({
|
|
403
|
+
* maxTokens: 4000,
|
|
404
|
+
* warnAt: 3500,
|
|
405
|
+
* });
|
|
406
|
+
* ```
|
|
407
|
+
*/
|
|
408
|
+
declare function createLengthStreamingGuardrail(options: {
|
|
409
|
+
/** Maximum tokens before stopping */
|
|
410
|
+
maxTokens: number;
|
|
411
|
+
/** Warn at this token count (optional) */
|
|
412
|
+
warnAt?: number;
|
|
413
|
+
/** Stop the stream on max (default: true) */
|
|
414
|
+
stopOnFail?: boolean;
|
|
415
|
+
}): StreamingGuardrail;
|
|
416
|
+
/**
|
|
417
|
+
* Create a streaming guardrail that detects patterns (regex-based).
|
|
418
|
+
*
|
|
419
|
+
* @example
|
|
420
|
+
* ```typescript
|
|
421
|
+
* const piiGuardrail = createPatternStreamingGuardrail({
|
|
422
|
+
* patterns: [
|
|
423
|
+
* { regex: /\b\d{3}-\d{2}-\d{4}\b/, name: 'SSN' },
|
|
424
|
+
* { regex: /\b\d{16}\b/, name: 'Credit Card' },
|
|
425
|
+
* ],
|
|
426
|
+
* stopOnFail: true,
|
|
427
|
+
* });
|
|
428
|
+
* ```
|
|
429
|
+
*/
|
|
430
|
+
declare function createPatternStreamingGuardrail(options: {
|
|
431
|
+
patterns: Array<{
|
|
432
|
+
regex: RegExp;
|
|
433
|
+
name: string;
|
|
434
|
+
}>;
|
|
435
|
+
stopOnFail?: boolean;
|
|
436
|
+
}): StreamingGuardrail;
|
|
437
|
+
/**
|
|
438
|
+
* Combine multiple streaming guardrails into one.
|
|
439
|
+
*
|
|
440
|
+
* @example
|
|
441
|
+
* ```typescript
|
|
442
|
+
* const combined = combineStreamingGuardrails([
|
|
443
|
+
* createToxicityStreamingGuardrail({ ... }),
|
|
444
|
+
* createLengthStreamingGuardrail({ ... }),
|
|
445
|
+
* ]);
|
|
446
|
+
* ```
|
|
447
|
+
*/
|
|
448
|
+
declare function combineStreamingGuardrails(guardrails: StreamingGuardrail[], options?: {
|
|
449
|
+
name?: string;
|
|
450
|
+
stopOnFirstFail?: boolean;
|
|
451
|
+
}): StreamingGuardrail;
|
|
452
|
+
/**
|
|
453
|
+
* Convert a regular output guardrail to a streaming guardrail.
|
|
454
|
+
* Useful for reusing existing guardrails in streaming context.
|
|
455
|
+
*
|
|
456
|
+
* @example
|
|
457
|
+
* ```typescript
|
|
458
|
+
* const streamingPII = adaptOutputGuardrail(
|
|
459
|
+
* "pii-streaming",
|
|
460
|
+
* createPIIGuardrail({ redact: false }),
|
|
461
|
+
* { checkInterval: 100 }
|
|
462
|
+
* );
|
|
463
|
+
* ```
|
|
464
|
+
*/
|
|
465
|
+
declare function adaptOutputGuardrail(name: string, guardrail: GuardrailFn<OutputGuardrailData>, options?: {
|
|
466
|
+
/** Only run after this many tokens (optimization) */
|
|
467
|
+
minTokens?: number;
|
|
468
|
+
stopOnFail?: boolean;
|
|
469
|
+
}): StreamingGuardrail;
|
|
470
|
+
/**
|
|
471
|
+
* Collect all tokens from a stream into a string.
|
|
472
|
+
*
|
|
473
|
+
* @example
|
|
474
|
+
* ```typescript
|
|
475
|
+
* const { stream, result } = orchestrator.runStream(agent, input);
|
|
476
|
+
* const fullOutput = await collectTokens(stream);
|
|
477
|
+
* ```
|
|
478
|
+
*/
|
|
479
|
+
declare function collectTokens(stream: AsyncIterable<StreamChunk>): Promise<string>;
|
|
480
|
+
/**
|
|
481
|
+
* Tap into a stream without consuming it.
|
|
482
|
+
* Useful for logging or side effects.
|
|
483
|
+
*
|
|
484
|
+
* @example
|
|
485
|
+
* ```typescript
|
|
486
|
+
* const { stream } = orchestrator.runStream(agent, input);
|
|
487
|
+
* const tapped = tapStream(stream, (chunk) => console.log(chunk));
|
|
488
|
+
* for await (const chunk of tapped) { ... }
|
|
489
|
+
* ```
|
|
490
|
+
*/
|
|
491
|
+
declare function tapStream(stream: AsyncIterable<StreamChunk>, fn: (chunk: StreamChunk) => void | Promise<void>): AsyncIterable<StreamChunk>;
|
|
492
|
+
/**
|
|
493
|
+
* Filter stream chunks by type.
|
|
494
|
+
*
|
|
495
|
+
* @example
|
|
496
|
+
* ```typescript
|
|
497
|
+
* const tokensOnly = filterStream(stream, ['token']);
|
|
498
|
+
* ```
|
|
499
|
+
*/
|
|
500
|
+
declare function filterStream<T extends StreamChunk["type"]>(stream: AsyncIterable<StreamChunk>, types: T[]): AsyncIterable<Extract<StreamChunk, {
|
|
501
|
+
type: T;
|
|
502
|
+
}>>;
|
|
503
|
+
/**
|
|
504
|
+
* Transform stream chunks.
|
|
505
|
+
*
|
|
506
|
+
* @example
|
|
507
|
+
* ```typescript
|
|
508
|
+
* const upperTokens = mapStream(stream, (chunk) => {
|
|
509
|
+
* if (chunk.type === 'token') return { ...chunk, data: chunk.data.toUpperCase() };
|
|
510
|
+
* return chunk;
|
|
511
|
+
* });
|
|
512
|
+
* ```
|
|
513
|
+
*/
|
|
514
|
+
declare function mapStream<R>(stream: AsyncIterable<StreamChunk>, fn: (chunk: StreamChunk) => R | Promise<R>): AsyncIterable<R>;
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Built-in guardrails for AI adapter — PII, moderation, rate limiting, tool allowlists, schema validation.
|
|
518
|
+
*/
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Create a PII detection guardrail.
|
|
522
|
+
*
|
|
523
|
+
* @example
|
|
524
|
+
* ```typescript
|
|
525
|
+
* const piiGuardrail = createPIIGuardrail({
|
|
526
|
+
* patterns: [
|
|
527
|
+
* /\b\d{3}-\d{2}-\d{4}\b/, // SSN
|
|
528
|
+
* /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i, // Email
|
|
529
|
+
* ],
|
|
530
|
+
* redact: true,
|
|
531
|
+
* });
|
|
532
|
+
* ```
|
|
533
|
+
*/
|
|
534
|
+
declare function createPIIGuardrail(options: {
|
|
535
|
+
patterns?: RegExp[];
|
|
536
|
+
redact?: boolean;
|
|
537
|
+
redactReplacement?: string;
|
|
538
|
+
}): GuardrailFn<InputGuardrailData>;
|
|
539
|
+
/**
|
|
540
|
+
* Create a content moderation guardrail.
|
|
541
|
+
*
|
|
542
|
+
* @example
|
|
543
|
+
* ```typescript
|
|
544
|
+
* const moderationGuardrail = createModerationGuardrail({
|
|
545
|
+
* checkFn: async (text) => {
|
|
546
|
+
* const result = await openai.moderations.create({ input: text });
|
|
547
|
+
* return result.results[0].flagged;
|
|
548
|
+
* },
|
|
549
|
+
* });
|
|
550
|
+
* ```
|
|
551
|
+
*/
|
|
552
|
+
declare function createModerationGuardrail(options: {
|
|
553
|
+
checkFn: (text: string) => boolean | Promise<boolean>;
|
|
554
|
+
message?: string;
|
|
555
|
+
}): GuardrailFn<InputGuardrailData | OutputGuardrailData>;
|
|
556
|
+
/** Rate limiter with reset capability for testing */
|
|
557
|
+
interface RateLimitGuardrail extends GuardrailFn<InputGuardrailData> {
|
|
558
|
+
reset(): void;
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Create a rate limit guardrail based on token usage.
|
|
562
|
+
* Returns a guardrail function with an additional `reset()` method for testing.
|
|
563
|
+
*/
|
|
564
|
+
declare function createRateLimitGuardrail(options: {
|
|
565
|
+
maxTokensPerMinute?: number;
|
|
566
|
+
maxRequestsPerMinute?: number;
|
|
567
|
+
}): RateLimitGuardrail;
|
|
568
|
+
/**
|
|
569
|
+
* Create a tool allowlist/denylist guardrail.
|
|
570
|
+
*/
|
|
571
|
+
declare function createToolGuardrail(options: {
|
|
572
|
+
allowlist?: string[];
|
|
573
|
+
denylist?: string[];
|
|
574
|
+
/** @default false */
|
|
575
|
+
caseSensitive?: boolean;
|
|
576
|
+
}): GuardrailFn<ToolCallGuardrailData>;
|
|
577
|
+
/**
|
|
578
|
+
* Create an output schema validation guardrail.
|
|
579
|
+
*/
|
|
580
|
+
declare function createOutputSchemaGuardrail<T = unknown>(options: {
|
|
581
|
+
validate: SchemaValidator<T>;
|
|
582
|
+
errorPrefix?: string;
|
|
583
|
+
}): GuardrailFn<OutputGuardrailData>;
|
|
584
|
+
/**
|
|
585
|
+
* Create a simple type check guardrail for common output types.
|
|
586
|
+
*/
|
|
587
|
+
declare function createOutputTypeGuardrail(options: {
|
|
588
|
+
type: "string" | "number" | "boolean" | "object" | "array";
|
|
589
|
+
requiredFields?: string[];
|
|
590
|
+
minLength?: number;
|
|
591
|
+
maxLength?: number;
|
|
592
|
+
minStringLength?: number;
|
|
593
|
+
maxStringLength?: number;
|
|
594
|
+
}): GuardrailFn<OutputGuardrailData>;
|
|
595
|
+
/**
|
|
596
|
+
* Create a length guardrail that limits output size.
|
|
597
|
+
*
|
|
598
|
+
* @example
|
|
599
|
+
* ```typescript
|
|
600
|
+
* const lengthGuardrail = createLengthGuardrail({
|
|
601
|
+
* maxCharacters: 5000,
|
|
602
|
+
* });
|
|
603
|
+
* ```
|
|
604
|
+
*/
|
|
605
|
+
declare function createLengthGuardrail(options: {
|
|
606
|
+
/** Maximum characters in output */
|
|
607
|
+
maxCharacters?: number;
|
|
608
|
+
/** Maximum estimated tokens in output */
|
|
609
|
+
maxTokens?: number;
|
|
610
|
+
/** Custom token estimator (default: chars / 4) */
|
|
611
|
+
estimateTokens?: (text: string) => number;
|
|
612
|
+
}): GuardrailFn<OutputGuardrailData>;
|
|
613
|
+
/**
|
|
614
|
+
* Create a content filter guardrail that blocks output matching specific patterns.
|
|
615
|
+
*
|
|
616
|
+
* @example
|
|
617
|
+
* ```typescript
|
|
618
|
+
* const contentFilter = createContentFilterGuardrail({
|
|
619
|
+
* blockedPatterns: [
|
|
620
|
+
* /\bpassword\b/i,
|
|
621
|
+
* /\bsecret\b/i,
|
|
622
|
+
* 'internal-only',
|
|
623
|
+
* ],
|
|
624
|
+
* });
|
|
625
|
+
* ```
|
|
626
|
+
*/
|
|
627
|
+
declare function createContentFilterGuardrail(options: {
|
|
628
|
+
/** Patterns to block — strings or RegExp */
|
|
629
|
+
blockedPatterns: Array<string | RegExp>;
|
|
630
|
+
/** Case-sensitive matching for string patterns (default: false) */
|
|
631
|
+
caseSensitive?: boolean;
|
|
632
|
+
}): GuardrailFn<OutputGuardrailData>;
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Helper functions for AI adapter — createRunner, estimateCost, state queries, validation.
|
|
636
|
+
*/
|
|
637
|
+
|
|
638
|
+
/** Check if agent is currently running. */
|
|
639
|
+
declare function isAgentRunning(state: AgentState): boolean;
|
|
640
|
+
/** Check if there are pending approvals. */
|
|
641
|
+
declare function hasPendingApprovals(state: ApprovalState): boolean;
|
|
642
|
+
/**
|
|
643
|
+
* Get total cost estimate based on token usage.
|
|
644
|
+
*
|
|
645
|
+
* @param tokenUsage - Total token count
|
|
646
|
+
* @param ratePerMillionTokens - Cost per million tokens (required, no default to avoid stale pricing)
|
|
647
|
+
* @returns Estimated cost in dollars
|
|
648
|
+
*/
|
|
649
|
+
declare function estimateCost(tokenUsage: number, ratePerMillionTokens: number): number;
|
|
650
|
+
/**
|
|
651
|
+
* Validate that a baseURL uses http or https.
|
|
652
|
+
* Throws immediately at adapter creation time (not at call time) to catch config errors early.
|
|
653
|
+
*/
|
|
654
|
+
declare function validateBaseURL(baseURL: string): void;
|
|
655
|
+
/** Parsed response from an LLM provider */
|
|
656
|
+
interface ParsedResponse {
|
|
657
|
+
text: string;
|
|
658
|
+
totalTokens: number;
|
|
659
|
+
/** Input token count, when available from the provider */
|
|
660
|
+
inputTokens?: number;
|
|
661
|
+
/** Output token count, when available from the provider */
|
|
662
|
+
outputTokens?: number;
|
|
663
|
+
}
|
|
664
|
+
/** Options for creating an AgentRunner from buildRequest/parseResponse */
|
|
665
|
+
interface CreateRunnerOptions {
|
|
666
|
+
fetch?: typeof globalThis.fetch;
|
|
667
|
+
buildRequest: (agent: AgentLike, input: string, messages: Message$1[]) => {
|
|
668
|
+
url: string;
|
|
669
|
+
init: RequestInit;
|
|
670
|
+
};
|
|
671
|
+
parseResponse: (response: Response, messages: Message$1[]) => Promise<ParsedResponse>;
|
|
672
|
+
parseOutput?: <T>(text: string) => T;
|
|
673
|
+
/** Lifecycle hooks for tracing, logging, and metrics */
|
|
674
|
+
hooks?: AdapterHooks;
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Create an AgentRunner from buildRequest/parseResponse helpers.
|
|
678
|
+
* Reduces ~50 lines of fetch boilerplate to ~20 lines of configuration.
|
|
679
|
+
*
|
|
680
|
+
* Supports lifecycle hooks for observability:
|
|
681
|
+
* - `onBeforeCall` fires before each API request
|
|
682
|
+
* - `onAfterCall` fires after a successful response (includes token breakdown)
|
|
683
|
+
* - `onError` fires when the request fails
|
|
684
|
+
*
|
|
685
|
+
* @example
|
|
686
|
+
* ```typescript
|
|
687
|
+
* const runClaude = createRunner({
|
|
688
|
+
* buildRequest: (agent, input) => ({
|
|
689
|
+
* url: "/api/claude",
|
|
690
|
+
* init: {
|
|
691
|
+
* method: "POST",
|
|
692
|
+
* headers: { "Content-Type": "application/json" },
|
|
693
|
+
* body: JSON.stringify({
|
|
694
|
+
* model: agent.model ?? "claude-haiku-4-5-20251001",
|
|
695
|
+
* system: agent.instructions ?? "",
|
|
696
|
+
* messages: [{ role: "user", content: input }],
|
|
697
|
+
* }),
|
|
698
|
+
* },
|
|
699
|
+
* }),
|
|
700
|
+
* parseResponse: async (res) => {
|
|
701
|
+
* const data = await res.json();
|
|
702
|
+
* const inputTokens = data.usage?.input_tokens ?? 0;
|
|
703
|
+
* const outputTokens = data.usage?.output_tokens ?? 0;
|
|
704
|
+
* return {
|
|
705
|
+
* text: data.content?.[0]?.text ?? "",
|
|
706
|
+
* totalTokens: inputTokens + outputTokens,
|
|
707
|
+
* inputTokens,
|
|
708
|
+
* outputTokens,
|
|
709
|
+
* };
|
|
710
|
+
* },
|
|
711
|
+
* hooks: {
|
|
712
|
+
* onAfterCall: ({ durationMs, tokenUsage }) => {
|
|
713
|
+
* console.log(`LLM call: ${durationMs}ms, ${tokenUsage.inputTokens}in/${tokenUsage.outputTokens}out`);
|
|
714
|
+
* },
|
|
715
|
+
* },
|
|
716
|
+
* });
|
|
717
|
+
* ```
|
|
718
|
+
*/
|
|
719
|
+
declare function createRunner(options: CreateRunnerOptions): AgentRunner;
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* Constraint Helper Functions — Ergonomic builders for OrchestratorConstraint
|
|
723
|
+
*
|
|
724
|
+
* @example
|
|
725
|
+
* ```typescript
|
|
726
|
+
* import { constraint, when } from '@directive-run/ai';
|
|
727
|
+
*
|
|
728
|
+
* constraints: {
|
|
729
|
+
* // Builder pattern
|
|
730
|
+
* escalate: constraint<MyFacts>()
|
|
731
|
+
* .when(f => f.confidence < 0.7)
|
|
732
|
+
* .require({ type: 'ESCALATE' })
|
|
733
|
+
* .priority(50)
|
|
734
|
+
* .build(),
|
|
735
|
+
*
|
|
736
|
+
* // Quick shorthand
|
|
737
|
+
* pause: when<MyFacts>(f => f.errors > 3)
|
|
738
|
+
* .require({ type: 'PAUSE' }),
|
|
739
|
+
* }
|
|
740
|
+
* ```
|
|
741
|
+
*/
|
|
742
|
+
|
|
743
|
+
interface ConstraintBuilderWithWhen<F extends Record<string, unknown>> {
|
|
744
|
+
require(req: Requirement | ((facts: F & OrchestratorState) => Requirement)): ConstraintBuilderWithRequire<F>;
|
|
745
|
+
}
|
|
746
|
+
interface ConstraintBuilderWithRequire<F extends Record<string, unknown>> {
|
|
747
|
+
priority(p: number): ConstraintBuilderWithRequire<F>;
|
|
748
|
+
build(): OrchestratorConstraint<F>;
|
|
749
|
+
}
|
|
750
|
+
interface ConstraintBuilder<F extends Record<string, unknown>> {
|
|
751
|
+
when(condition: (facts: F & OrchestratorState) => boolean | Promise<boolean>): ConstraintBuilderWithWhen<F>;
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Fluent builder for creating orchestrator constraints.
|
|
755
|
+
*
|
|
756
|
+
* @example
|
|
757
|
+
* ```typescript
|
|
758
|
+
* const myConstraint = constraint<MyFacts>()
|
|
759
|
+
* .when(f => f.confidence < 0.7)
|
|
760
|
+
* .require({ type: 'ESCALATE' })
|
|
761
|
+
* .priority(50)
|
|
762
|
+
* .build();
|
|
763
|
+
* ```
|
|
764
|
+
*/
|
|
765
|
+
declare function constraint<F extends Record<string, unknown> = Record<string, never>>(): ConstraintBuilder<F>;
|
|
766
|
+
interface WhenResult<F extends Record<string, unknown>> {
|
|
767
|
+
require(req: Requirement | ((facts: F & OrchestratorState) => Requirement)): WhenWithRequire<F>;
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* Result of `when().require()` — a valid `OrchestratorConstraint<F>` directly,
|
|
771
|
+
* or chain `.withPriority(n)` to get a constraint with priority set.
|
|
772
|
+
*/
|
|
773
|
+
interface WhenWithRequire<F extends Record<string, unknown>> extends OrchestratorConstraint<F> {
|
|
774
|
+
/** Return a new constraint with the given priority */
|
|
775
|
+
withPriority(p: number): OrchestratorConstraint<F>;
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Quick shorthand for creating simple constraints.
|
|
779
|
+
* The returned object is a valid `OrchestratorConstraint<F>` — use directly
|
|
780
|
+
* or chain `.withPriority(n)` to set priority.
|
|
781
|
+
*
|
|
782
|
+
* @example
|
|
783
|
+
* ```typescript
|
|
784
|
+
* const myConstraint = when<MyFacts>(f => f.errors > 3)
|
|
785
|
+
* .require({ type: 'PAUSE' });
|
|
786
|
+
*
|
|
787
|
+
* // With priority
|
|
788
|
+
* const urgent = when<MyFacts>(f => f.critical)
|
|
789
|
+
* .require({ type: 'HALT' })
|
|
790
|
+
* .withPriority(100);
|
|
791
|
+
* ```
|
|
792
|
+
*/
|
|
793
|
+
declare function when<F extends Record<string, unknown> = Record<string, never>>(condition: (facts: F & OrchestratorState) => boolean | Promise<boolean>): WhenResult<F>;
|
|
794
|
+
|
|
795
|
+
/**
|
|
796
|
+
* Multi-Agent Orchestration Patterns
|
|
797
|
+
*
|
|
798
|
+
* Provides patterns for coordinating multiple AI agents:
|
|
799
|
+
* - Parallel execution with result merging
|
|
800
|
+
* - Sequential pipelines
|
|
801
|
+
* - Supervisor patterns with worker delegation
|
|
802
|
+
* - Constraint-driven agent selection
|
|
803
|
+
*
|
|
804
|
+
* @example
|
|
805
|
+
* ```typescript
|
|
806
|
+
* import { createMultiAgentOrchestrator } from '@directive-run/ai';
|
|
807
|
+
*
|
|
808
|
+
* const orchestrator = createMultiAgentOrchestrator({
|
|
809
|
+
* agents: {
|
|
810
|
+
* researcher: { agent: researchAgent, maxConcurrent: 3 },
|
|
811
|
+
* writer: { agent: writerAgent, maxConcurrent: 1 },
|
|
812
|
+
* reviewer: { agent: reviewerAgent, maxConcurrent: 1 },
|
|
813
|
+
* },
|
|
814
|
+
* patterns: {
|
|
815
|
+
* parallelResearch: {
|
|
816
|
+
* type: 'parallel',
|
|
817
|
+
* agents: ['researcher', 'researcher', 'researcher'],
|
|
818
|
+
* merge: (results) => combineResearch(results),
|
|
819
|
+
* },
|
|
820
|
+
* },
|
|
821
|
+
* });
|
|
822
|
+
* ```
|
|
823
|
+
*/
|
|
824
|
+
|
|
825
|
+
/**
|
|
826
|
+
* Async semaphore for controlling concurrent access.
|
|
827
|
+
* Uses a queue-based approach instead of polling for efficiency.
|
|
828
|
+
*
|
|
829
|
+
* @example
|
|
830
|
+
* ```typescript
|
|
831
|
+
* import { Semaphore } from '@directive-run/ai';
|
|
832
|
+
*
|
|
833
|
+
* const sem = new Semaphore(3); // Allow 3 concurrent operations
|
|
834
|
+
*
|
|
835
|
+
* async function doWork() {
|
|
836
|
+
* const release = await sem.acquire();
|
|
837
|
+
* try {
|
|
838
|
+
* await performWork();
|
|
839
|
+
* } finally {
|
|
840
|
+
* release();
|
|
841
|
+
* }
|
|
842
|
+
* }
|
|
843
|
+
* ```
|
|
844
|
+
*/
|
|
845
|
+
declare class Semaphore {
|
|
846
|
+
private count;
|
|
847
|
+
private readonly maxPermits;
|
|
848
|
+
private readonly queue;
|
|
849
|
+
constructor(max: number);
|
|
850
|
+
acquire(): Promise<() => void>;
|
|
851
|
+
private release;
|
|
852
|
+
/** Get current available permits */
|
|
853
|
+
get available(): number;
|
|
854
|
+
/** Get number of waiters in queue */
|
|
855
|
+
get waiting(): number;
|
|
856
|
+
/** Get maximum permits */
|
|
857
|
+
get max(): number;
|
|
858
|
+
/** Reject all pending waiters with an error and reset permits */
|
|
859
|
+
drain(): void;
|
|
860
|
+
}
|
|
861
|
+
/** Configuration for a registered agent */
|
|
862
|
+
interface AgentRegistration {
|
|
863
|
+
/** The agent instance */
|
|
864
|
+
agent: AgentLike;
|
|
865
|
+
/** Maximum concurrent runs for this agent (default: 1) */
|
|
866
|
+
maxConcurrent?: number;
|
|
867
|
+
/** Timeout for agent runs (ms) */
|
|
868
|
+
timeout?: number;
|
|
869
|
+
/** Custom run options */
|
|
870
|
+
runOptions?: Omit<RunOptions, "signal">;
|
|
871
|
+
/** Description for constraint-based selection */
|
|
872
|
+
description?: string;
|
|
873
|
+
/** Capabilities this agent has */
|
|
874
|
+
capabilities?: string[];
|
|
875
|
+
/** Per-agent output guardrails (applied in addition to stack-level guardrails) */
|
|
876
|
+
guardrails?: {
|
|
877
|
+
output?: Array<GuardrailFn<OutputGuardrailData> | NamedGuardrail<OutputGuardrailData>>;
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
/** Agent registry configuration */
|
|
881
|
+
interface AgentRegistry {
|
|
882
|
+
[agentId: string]: AgentRegistration;
|
|
883
|
+
}
|
|
884
|
+
/** State of a running agent */
|
|
885
|
+
interface AgentRunState {
|
|
886
|
+
agentId: string;
|
|
887
|
+
runId: string;
|
|
888
|
+
status: "pending" | "running" | "completed" | "error" | "cancelled";
|
|
889
|
+
input: string;
|
|
890
|
+
output?: unknown;
|
|
891
|
+
error?: Error;
|
|
892
|
+
startedAt?: number;
|
|
893
|
+
completedAt?: number;
|
|
894
|
+
tokens: number;
|
|
895
|
+
}
|
|
896
|
+
/** Parallel execution pattern - run agents concurrently and merge results */
|
|
897
|
+
interface ParallelPattern<T = unknown> {
|
|
898
|
+
type: "parallel";
|
|
899
|
+
/** Agent IDs to run in parallel (can repeat for multiple instances) */
|
|
900
|
+
agents: string[];
|
|
901
|
+
/** Function to merge results from all agents */
|
|
902
|
+
merge: (results: RunResult<unknown>[]) => T | Promise<T>;
|
|
903
|
+
/** Minimum successful results required (default: all) */
|
|
904
|
+
minSuccess?: number;
|
|
905
|
+
/** Overall timeout (ms) */
|
|
906
|
+
timeout?: number;
|
|
907
|
+
}
|
|
908
|
+
/** Sequential execution pattern - pipeline of agents */
|
|
909
|
+
interface SequentialPattern<T = unknown> {
|
|
910
|
+
type: "sequential";
|
|
911
|
+
/** Agent IDs in execution order */
|
|
912
|
+
agents: string[];
|
|
913
|
+
/** Transform output to next input (default: stringify) */
|
|
914
|
+
transform?: (output: unknown, agentId: string, index: number) => string;
|
|
915
|
+
/** Final result extractor */
|
|
916
|
+
extract?: (output: unknown) => T;
|
|
917
|
+
/** Continue on error (default: false) */
|
|
918
|
+
continueOnError?: boolean;
|
|
919
|
+
}
|
|
920
|
+
/** Supervisor pattern - one agent directs others */
|
|
921
|
+
interface SupervisorPattern<T = unknown> {
|
|
922
|
+
type: "supervisor";
|
|
923
|
+
/** Supervisor agent ID */
|
|
924
|
+
supervisor: string;
|
|
925
|
+
/** Worker agent IDs */
|
|
926
|
+
workers: string[];
|
|
927
|
+
/** Maximum delegation rounds */
|
|
928
|
+
maxRounds?: number;
|
|
929
|
+
/** Extract final result */
|
|
930
|
+
extract?: (supervisorOutput: unknown, workerResults: RunResult<unknown>[]) => T;
|
|
931
|
+
}
|
|
932
|
+
/** Union of all patterns */
|
|
933
|
+
type ExecutionPattern<T = unknown> = ParallelPattern<T> | SequentialPattern<T> | SupervisorPattern<T>;
|
|
934
|
+
/** Handoff request between agents */
|
|
935
|
+
interface HandoffRequest {
|
|
936
|
+
id: string;
|
|
937
|
+
fromAgent: string;
|
|
938
|
+
toAgent: string;
|
|
939
|
+
input: string;
|
|
940
|
+
context?: Record<string, unknown>;
|
|
941
|
+
requestedAt: number;
|
|
942
|
+
}
|
|
943
|
+
/** Handoff result */
|
|
944
|
+
interface HandoffResult {
|
|
945
|
+
request: HandoffRequest;
|
|
946
|
+
result: RunResult<unknown>;
|
|
947
|
+
completedAt: number;
|
|
948
|
+
}
|
|
949
|
+
/** Constraint for agent selection */
|
|
950
|
+
interface AgentSelectionConstraint {
|
|
951
|
+
when: (facts: Record<string, unknown>) => boolean | Promise<boolean>;
|
|
952
|
+
select: string | ((facts: Record<string, unknown>) => string);
|
|
953
|
+
input: string | ((facts: Record<string, unknown>) => string);
|
|
954
|
+
priority?: number;
|
|
955
|
+
}
|
|
956
|
+
/** Run agent requirement */
|
|
957
|
+
interface RunAgentRequirement extends Requirement {
|
|
958
|
+
type: "RUN_AGENT";
|
|
959
|
+
agent: string;
|
|
960
|
+
input: string;
|
|
961
|
+
context?: Record<string, unknown>;
|
|
962
|
+
}
|
|
963
|
+
/** Multi-agent orchestrator options */
|
|
964
|
+
interface MultiAgentOrchestratorOptions {
|
|
965
|
+
/** Base run function */
|
|
966
|
+
runner: AgentRunner;
|
|
967
|
+
/** Registered agents */
|
|
968
|
+
agents: AgentRegistry;
|
|
969
|
+
/** Execution patterns */
|
|
970
|
+
patterns?: Record<string, ExecutionPattern>;
|
|
971
|
+
/** Handoff callbacks */
|
|
972
|
+
onHandoff?: (request: HandoffRequest) => void;
|
|
973
|
+
/** Handoff completion callbacks */
|
|
974
|
+
onHandoffComplete?: (result: HandoffResult) => void;
|
|
975
|
+
/** Maximum number of handoff results to retain (default: 1000) */
|
|
976
|
+
maxHandoffHistory?: number;
|
|
977
|
+
/** Debug mode */
|
|
978
|
+
debug?: boolean;
|
|
979
|
+
}
|
|
980
|
+
/** Multi-agent state in facts */
|
|
981
|
+
interface MultiAgentState {
|
|
982
|
+
/** Namespace for each agent's state */
|
|
983
|
+
__agents: Record<string, {
|
|
984
|
+
status: "idle" | "running" | "completed" | "error";
|
|
985
|
+
lastInput?: string;
|
|
986
|
+
lastOutput?: unknown;
|
|
987
|
+
lastError?: string;
|
|
988
|
+
runCount: number;
|
|
989
|
+
totalTokens: number;
|
|
990
|
+
}>;
|
|
991
|
+
/** Pending handoffs */
|
|
992
|
+
__handoffs: HandoffRequest[];
|
|
993
|
+
/** Completed handoffs */
|
|
994
|
+
__handoffResults: HandoffResult[];
|
|
995
|
+
}
|
|
996
|
+
/** Multi-agent orchestrator instance */
|
|
997
|
+
interface MultiAgentOrchestrator {
|
|
998
|
+
/** Run a single agent */
|
|
999
|
+
runAgent<T>(agentId: string, input: string, options?: RunOptions): Promise<RunResult<T>>;
|
|
1000
|
+
/** Run an execution pattern */
|
|
1001
|
+
runPattern<T>(patternId: string, input: string): Promise<T>;
|
|
1002
|
+
/** Run agents in parallel */
|
|
1003
|
+
runParallel<T>(agentIds: string[], inputs: string | string[], merge: (results: RunResult<unknown>[]) => T | Promise<T>): Promise<T>;
|
|
1004
|
+
/** Run agents sequentially */
|
|
1005
|
+
runSequential<T>(agentIds: string[], initialInput: string, options?: {
|
|
1006
|
+
transform?: (output: unknown, agentId: string, index: number) => string;
|
|
1007
|
+
}): Promise<RunResult<T>[]>;
|
|
1008
|
+
/** Request a handoff between agents */
|
|
1009
|
+
handoff(fromAgent: string, toAgent: string, input: string, context?: Record<string, unknown>): Promise<RunResult<unknown>>;
|
|
1010
|
+
/** Get agent state */
|
|
1011
|
+
getAgentState(agentId: string): MultiAgentState["__agents"][string] | undefined;
|
|
1012
|
+
/** Get all agent states */
|
|
1013
|
+
getAllAgentStates(): Record<string, MultiAgentState["__agents"][string]>;
|
|
1014
|
+
/** Get pending handoffs */
|
|
1015
|
+
getPendingHandoffs(): HandoffRequest[];
|
|
1016
|
+
/** Reset all agent states */
|
|
1017
|
+
reset(): void;
|
|
1018
|
+
/** Dispose of the orchestrator, resetting all state */
|
|
1019
|
+
dispose(): void;
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* Create a multi-agent orchestrator.
|
|
1023
|
+
*
|
|
1024
|
+
* @example
|
|
1025
|
+
* ```typescript
|
|
1026
|
+
* const orchestrator = createMultiAgentOrchestrator({
|
|
1027
|
+
* runner,
|
|
1028
|
+
* agents: {
|
|
1029
|
+
* researcher: { agent: researchAgent, maxConcurrent: 3 },
|
|
1030
|
+
* writer: { agent: writerAgent },
|
|
1031
|
+
* reviewer: { agent: reviewerAgent },
|
|
1032
|
+
* },
|
|
1033
|
+
* patterns: {
|
|
1034
|
+
* research: {
|
|
1035
|
+
* type: 'parallel',
|
|
1036
|
+
* agents: ['researcher', 'researcher'],
|
|
1037
|
+
* merge: (results) => results.map(r => r.output).join('\n\n'),
|
|
1038
|
+
* },
|
|
1039
|
+
* write: {
|
|
1040
|
+
* type: 'sequential',
|
|
1041
|
+
* agents: ['writer', 'reviewer'],
|
|
1042
|
+
* },
|
|
1043
|
+
* },
|
|
1044
|
+
* });
|
|
1045
|
+
*
|
|
1046
|
+
* // Run pattern
|
|
1047
|
+
* const research = await orchestrator.runPattern('research', 'What is AI?');
|
|
1048
|
+
*
|
|
1049
|
+
* // Run parallel
|
|
1050
|
+
* const results = await orchestrator.runParallel(
|
|
1051
|
+
* ['researcher', 'researcher'],
|
|
1052
|
+
* ['Question 1', 'Question 2'],
|
|
1053
|
+
* (results) => results.map(r => r.output)
|
|
1054
|
+
* );
|
|
1055
|
+
*
|
|
1056
|
+
* // Handoff
|
|
1057
|
+
* const reviewed = await orchestrator.handoff('writer', 'reviewer', draft);
|
|
1058
|
+
* ```
|
|
1059
|
+
*
|
|
1060
|
+
* @throws {Error} If a pattern references an agent that is not in the registry
|
|
1061
|
+
*/
|
|
1062
|
+
declare function createMultiAgentOrchestrator(options: MultiAgentOrchestratorOptions): MultiAgentOrchestrator;
|
|
1063
|
+
/**
|
|
1064
|
+
* Create a parallel pattern configuration.
|
|
1065
|
+
*
|
|
1066
|
+
* @example
|
|
1067
|
+
* ```typescript
|
|
1068
|
+
* const researchPattern = parallel(
|
|
1069
|
+
* ['researcher', 'researcher', 'researcher'],
|
|
1070
|
+
* (results) => results.map(r => r.output).join('\n')
|
|
1071
|
+
* );
|
|
1072
|
+
* ```
|
|
1073
|
+
*/
|
|
1074
|
+
declare function parallel<T>(agents: string[], merge: (results: RunResult<unknown>[]) => T | Promise<T>, options?: {
|
|
1075
|
+
minSuccess?: number;
|
|
1076
|
+
timeout?: number;
|
|
1077
|
+
}): ParallelPattern<T>;
|
|
1078
|
+
/**
|
|
1079
|
+
* Create a sequential pattern configuration.
|
|
1080
|
+
*
|
|
1081
|
+
* @example
|
|
1082
|
+
* ```typescript
|
|
1083
|
+
* const writeReviewPattern = sequential(
|
|
1084
|
+
* ['writer', 'reviewer'],
|
|
1085
|
+
* { transform: (output) => `Review this: ${output}` }
|
|
1086
|
+
* );
|
|
1087
|
+
* ```
|
|
1088
|
+
*/
|
|
1089
|
+
declare function sequential<T>(agents: string[], options?: {
|
|
1090
|
+
transform?: (output: unknown, agentId: string, index: number) => string;
|
|
1091
|
+
extract?: (output: unknown) => T;
|
|
1092
|
+
continueOnError?: boolean;
|
|
1093
|
+
}): SequentialPattern<T>;
|
|
1094
|
+
/**
|
|
1095
|
+
* Create a supervisor pattern configuration.
|
|
1096
|
+
*
|
|
1097
|
+
* @example
|
|
1098
|
+
* ```typescript
|
|
1099
|
+
* const managedPattern = supervisor(
|
|
1100
|
+
* 'manager',
|
|
1101
|
+
* ['worker1', 'worker2'],
|
|
1102
|
+
* { maxRounds: 3 }
|
|
1103
|
+
* );
|
|
1104
|
+
* ```
|
|
1105
|
+
*/
|
|
1106
|
+
declare function supervisor<T>(supervisorAgent: string, workers: string[], options?: {
|
|
1107
|
+
maxRounds?: number;
|
|
1108
|
+
extract?: (supervisorOutput: unknown, workerResults: RunResult<unknown>[]) => T;
|
|
1109
|
+
}): SupervisorPattern<T>;
|
|
1110
|
+
/**
|
|
1111
|
+
* Create an agent selection constraint.
|
|
1112
|
+
*
|
|
1113
|
+
* @example
|
|
1114
|
+
* ```typescript
|
|
1115
|
+
* const constraints = {
|
|
1116
|
+
* routeToExpert: selectAgent(
|
|
1117
|
+
* (facts) => facts.complexity > 0.8,
|
|
1118
|
+
* 'expert',
|
|
1119
|
+
* (facts) => facts.query
|
|
1120
|
+
* ),
|
|
1121
|
+
* };
|
|
1122
|
+
* ```
|
|
1123
|
+
*/
|
|
1124
|
+
declare function selectAgent(when: (facts: Record<string, unknown>) => boolean | Promise<boolean>, agent: string | ((facts: Record<string, unknown>) => string), input: string | ((facts: Record<string, unknown>) => string), priority?: number): AgentSelectionConstraint;
|
|
1125
|
+
/**
|
|
1126
|
+
* Create a RUN_AGENT requirement.
|
|
1127
|
+
*
|
|
1128
|
+
* @example
|
|
1129
|
+
* ```typescript
|
|
1130
|
+
* constraints: {
|
|
1131
|
+
* needsResearch: {
|
|
1132
|
+
* when: (facts) => facts.hasUnknowns,
|
|
1133
|
+
* require: runAgentRequirement('researcher', facts.query),
|
|
1134
|
+
* },
|
|
1135
|
+
* }
|
|
1136
|
+
* ```
|
|
1137
|
+
*/
|
|
1138
|
+
declare function runAgentRequirement(agent: string, input: string, context?: Record<string, unknown>): RunAgentRequirement;
|
|
1139
|
+
/**
|
|
1140
|
+
* Merge results by concatenating outputs.
|
|
1141
|
+
*/
|
|
1142
|
+
declare function concatResults(results: RunResult<unknown>[], separator?: string): string;
|
|
1143
|
+
/**
|
|
1144
|
+
* Merge results by picking the best one based on a scoring function.
|
|
1145
|
+
*/
|
|
1146
|
+
declare function pickBestResult<T>(results: RunResult<T>[], score: (result: RunResult<T>) => number): RunResult<T>;
|
|
1147
|
+
/**
|
|
1148
|
+
* Merge results into an array of outputs.
|
|
1149
|
+
*/
|
|
1150
|
+
declare function collectOutputs<T>(results: RunResult<T>[]): T[];
|
|
1151
|
+
/**
|
|
1152
|
+
* Aggregate token counts from results.
|
|
1153
|
+
*/
|
|
1154
|
+
declare function aggregateTokens(results: RunResult<unknown>[]): number;
|
|
1155
|
+
|
|
1156
|
+
/**
|
|
1157
|
+
* Agent-to-Agent Communication Protocol
|
|
1158
|
+
*
|
|
1159
|
+
* Provides structured communication channels between agents for coordination,
|
|
1160
|
+
* delegation, and knowledge sharing without central orchestration.
|
|
1161
|
+
*
|
|
1162
|
+
* @example
|
|
1163
|
+
* ```typescript
|
|
1164
|
+
* import { createAgentNetwork, createMessageBus } from '@directive-run/ai';
|
|
1165
|
+
*
|
|
1166
|
+
* const messageBus = createMessageBus();
|
|
1167
|
+
*
|
|
1168
|
+
* const network = createAgentNetwork({
|
|
1169
|
+
* bus: messageBus,
|
|
1170
|
+
* agents: {
|
|
1171
|
+
* researcher: { capabilities: ['search', 'analyze'] },
|
|
1172
|
+
* writer: { capabilities: ['draft', 'edit'] },
|
|
1173
|
+
* reviewer: { capabilities: ['review', 'approve'] },
|
|
1174
|
+
* },
|
|
1175
|
+
* });
|
|
1176
|
+
*
|
|
1177
|
+
* // Agents can send messages to each other
|
|
1178
|
+
* await network.send('researcher', 'writer', {
|
|
1179
|
+
* type: 'DELEGATION',
|
|
1180
|
+
* task: 'Draft an article based on this research',
|
|
1181
|
+
* context: { findings: [...] },
|
|
1182
|
+
* });
|
|
1183
|
+
* ```
|
|
1184
|
+
*/
|
|
1185
|
+
/** Base message structure */
|
|
1186
|
+
interface AgentMessage {
|
|
1187
|
+
id: string;
|
|
1188
|
+
type: AgentMessageType;
|
|
1189
|
+
from: string;
|
|
1190
|
+
to: string | string[] | "*";
|
|
1191
|
+
timestamp: number;
|
|
1192
|
+
correlationId?: string;
|
|
1193
|
+
replyTo?: string;
|
|
1194
|
+
priority?: "low" | "normal" | "high" | "urgent";
|
|
1195
|
+
ttlMs?: number;
|
|
1196
|
+
metadata?: Record<string, unknown>;
|
|
1197
|
+
}
|
|
1198
|
+
/** Message types for agent communication */
|
|
1199
|
+
type AgentMessageType = "REQUEST" | "RESPONSE" | "DELEGATION" | "DELEGATION_RESULT" | "QUERY" | "INFORM" | "SUBSCRIBE" | "UNSUBSCRIBE" | "UPDATE" | "ACK" | "NACK" | "PING" | "PONG" | "CUSTOM";
|
|
1200
|
+
/** Request message */
|
|
1201
|
+
interface RequestMessage extends AgentMessage {
|
|
1202
|
+
type: "REQUEST";
|
|
1203
|
+
action: string;
|
|
1204
|
+
payload: Record<string, unknown>;
|
|
1205
|
+
timeout?: number;
|
|
1206
|
+
}
|
|
1207
|
+
/** Response message */
|
|
1208
|
+
interface ResponseMessage extends AgentMessage {
|
|
1209
|
+
type: "RESPONSE";
|
|
1210
|
+
success: boolean;
|
|
1211
|
+
result?: unknown;
|
|
1212
|
+
error?: string;
|
|
1213
|
+
}
|
|
1214
|
+
/** Delegation message */
|
|
1215
|
+
interface DelegationMessage extends AgentMessage {
|
|
1216
|
+
type: "DELEGATION";
|
|
1217
|
+
task: string;
|
|
1218
|
+
context: Record<string, unknown>;
|
|
1219
|
+
constraints?: {
|
|
1220
|
+
deadline?: number;
|
|
1221
|
+
maxCost?: number;
|
|
1222
|
+
requiredCapabilities?: string[];
|
|
1223
|
+
};
|
|
1224
|
+
}
|
|
1225
|
+
/** Delegation result message */
|
|
1226
|
+
interface DelegationResultMessage extends AgentMessage {
|
|
1227
|
+
type: "DELEGATION_RESULT";
|
|
1228
|
+
success: boolean;
|
|
1229
|
+
result?: unknown;
|
|
1230
|
+
error?: string;
|
|
1231
|
+
metrics?: {
|
|
1232
|
+
durationMs: number;
|
|
1233
|
+
tokensUsed?: number;
|
|
1234
|
+
cost?: number;
|
|
1235
|
+
};
|
|
1236
|
+
}
|
|
1237
|
+
/** Query message */
|
|
1238
|
+
interface QueryMessage extends AgentMessage {
|
|
1239
|
+
type: "QUERY";
|
|
1240
|
+
question: string;
|
|
1241
|
+
context?: Record<string, unknown>;
|
|
1242
|
+
}
|
|
1243
|
+
/** Inform message */
|
|
1244
|
+
interface InformMessage extends AgentMessage {
|
|
1245
|
+
type: "INFORM";
|
|
1246
|
+
topic: string;
|
|
1247
|
+
content: unknown;
|
|
1248
|
+
}
|
|
1249
|
+
/** Subscribe message */
|
|
1250
|
+
interface SubscribeMessage extends AgentMessage {
|
|
1251
|
+
type: "SUBSCRIBE";
|
|
1252
|
+
topics: string[];
|
|
1253
|
+
}
|
|
1254
|
+
/** Update message */
|
|
1255
|
+
interface UpdateMessage extends AgentMessage {
|
|
1256
|
+
type: "UPDATE";
|
|
1257
|
+
topic: string;
|
|
1258
|
+
content: unknown;
|
|
1259
|
+
}
|
|
1260
|
+
/** Union of all message types */
|
|
1261
|
+
type TypedAgentMessage = RequestMessage | ResponseMessage | DelegationMessage | DelegationResultMessage | QueryMessage | InformMessage | SubscribeMessage | UpdateMessage | (AgentMessage & {
|
|
1262
|
+
type: "UNSUBSCRIBE" | "ACK" | "NACK" | "PING" | "PONG" | "CUSTOM";
|
|
1263
|
+
});
|
|
1264
|
+
/** Message handler function */
|
|
1265
|
+
type MessageHandler = (message: TypedAgentMessage) => void | Promise<void>;
|
|
1266
|
+
/** Subscription to messages */
|
|
1267
|
+
interface Subscription {
|
|
1268
|
+
id: string;
|
|
1269
|
+
agentId: string;
|
|
1270
|
+
handler: MessageHandler;
|
|
1271
|
+
filter?: MessageFilter;
|
|
1272
|
+
unsubscribe: () => void;
|
|
1273
|
+
}
|
|
1274
|
+
/** Message filter criteria */
|
|
1275
|
+
interface MessageFilter {
|
|
1276
|
+
types?: AgentMessageType[];
|
|
1277
|
+
from?: string | string[];
|
|
1278
|
+
topics?: string[];
|
|
1279
|
+
priority?: ("low" | "normal" | "high" | "urgent")[];
|
|
1280
|
+
custom?: (message: TypedAgentMessage) => boolean;
|
|
1281
|
+
}
|
|
1282
|
+
/** Message bus configuration */
|
|
1283
|
+
interface MessageBusConfig {
|
|
1284
|
+
/** Maximum messages to retain in history */
|
|
1285
|
+
maxHistory?: number;
|
|
1286
|
+
/** Default TTL for messages */
|
|
1287
|
+
defaultTtlMs?: number;
|
|
1288
|
+
/** Maximum pending messages per offline agent (prevents unbounded queue growth) */
|
|
1289
|
+
maxPendingPerAgent?: number;
|
|
1290
|
+
/** Enable message persistence */
|
|
1291
|
+
persistence?: MessagePersistence;
|
|
1292
|
+
/** Callback when message is delivered */
|
|
1293
|
+
onDelivery?: (message: TypedAgentMessage, recipients: string[]) => void;
|
|
1294
|
+
/** Callback when message delivery fails */
|
|
1295
|
+
onDeliveryError?: (message: TypedAgentMessage, error: Error) => void;
|
|
1296
|
+
}
|
|
1297
|
+
/** Message persistence interface */
|
|
1298
|
+
interface MessagePersistence {
|
|
1299
|
+
save(message: TypedAgentMessage): Promise<void>;
|
|
1300
|
+
load(agentId: string, since?: number): Promise<TypedAgentMessage[]>;
|
|
1301
|
+
delete(messageId: string): Promise<void>;
|
|
1302
|
+
clear(agentId?: string): Promise<void>;
|
|
1303
|
+
}
|
|
1304
|
+
/** Message bus instance */
|
|
1305
|
+
interface MessageBus {
|
|
1306
|
+
/** Publish a message */
|
|
1307
|
+
publish(message: Omit<TypedAgentMessage, "id" | "timestamp">): string;
|
|
1308
|
+
/** Subscribe to messages */
|
|
1309
|
+
subscribe(agentId: string, handler: MessageHandler, filter?: MessageFilter): Subscription;
|
|
1310
|
+
/** Get message history */
|
|
1311
|
+
getHistory(filter?: MessageFilter, limit?: number): TypedAgentMessage[];
|
|
1312
|
+
/** Get a specific message by ID */
|
|
1313
|
+
getMessage(id: string): TypedAgentMessage | undefined;
|
|
1314
|
+
/** Get pending messages for an agent */
|
|
1315
|
+
getPending(agentId: string): TypedAgentMessage[];
|
|
1316
|
+
/** Clear all messages and data */
|
|
1317
|
+
clear(): void;
|
|
1318
|
+
/** Dispose of the message bus, clearing all data and subscriptions */
|
|
1319
|
+
dispose(): void;
|
|
1320
|
+
}
|
|
1321
|
+
/**
|
|
1322
|
+
* Create a message bus for agent communication.
|
|
1323
|
+
*
|
|
1324
|
+
* @example
|
|
1325
|
+
* ```typescript
|
|
1326
|
+
* const bus = createMessageBus({ maxHistory: 1000 });
|
|
1327
|
+
*
|
|
1328
|
+
* // Subscribe to messages
|
|
1329
|
+
* bus.subscribe('writer', (msg) => {
|
|
1330
|
+
* console.log(`Writer received: ${msg.type}`);
|
|
1331
|
+
* });
|
|
1332
|
+
*
|
|
1333
|
+
* // Publish a message
|
|
1334
|
+
* bus.publish({
|
|
1335
|
+
* type: 'DELEGATION',
|
|
1336
|
+
* from: 'researcher',
|
|
1337
|
+
* to: 'writer',
|
|
1338
|
+
* task: 'Write summary',
|
|
1339
|
+
* context: { data: '...' },
|
|
1340
|
+
* });
|
|
1341
|
+
* ```
|
|
1342
|
+
*/
|
|
1343
|
+
/**
|
|
1344
|
+
* Note: `publish()` is fire-and-forget -- it returns the message ID synchronously
|
|
1345
|
+
* before delivery completes. Use `onDelivery` / `onDeliveryError` callbacks in
|
|
1346
|
+
* config to track delivery status if needed.
|
|
1347
|
+
*/
|
|
1348
|
+
declare function createMessageBus(config?: MessageBusConfig): MessageBus;
|
|
1349
|
+
/** Agent registration info */
|
|
1350
|
+
interface AgentInfo {
|
|
1351
|
+
id: string;
|
|
1352
|
+
capabilities: string[];
|
|
1353
|
+
status: "online" | "offline" | "busy";
|
|
1354
|
+
lastSeen: number;
|
|
1355
|
+
metadata?: Record<string, unknown>;
|
|
1356
|
+
}
|
|
1357
|
+
/** Agent network configuration */
|
|
1358
|
+
interface AgentNetworkConfig {
|
|
1359
|
+
/** Message bus to use */
|
|
1360
|
+
bus: MessageBus;
|
|
1361
|
+
/** Registered agents */
|
|
1362
|
+
agents?: Record<string, Omit<AgentInfo, "id" | "lastSeen" | "status">>;
|
|
1363
|
+
/** Timeout for request-response patterns */
|
|
1364
|
+
defaultTimeout?: number;
|
|
1365
|
+
/** Callback when agent comes online */
|
|
1366
|
+
onAgentOnline?: (agentId: string) => void;
|
|
1367
|
+
/** Callback when agent goes offline */
|
|
1368
|
+
onAgentOffline?: (agentId: string) => void;
|
|
1369
|
+
}
|
|
1370
|
+
/** Agent network instance */
|
|
1371
|
+
interface AgentNetwork {
|
|
1372
|
+
/** Register an agent */
|
|
1373
|
+
register(id: string, info: Omit<AgentInfo, "id" | "lastSeen" | "status">): void;
|
|
1374
|
+
/** Unregister an agent */
|
|
1375
|
+
unregister(id: string): void;
|
|
1376
|
+
/** Get agent info */
|
|
1377
|
+
getAgent(id: string): AgentInfo | undefined;
|
|
1378
|
+
/** Get all agents */
|
|
1379
|
+
getAgents(): AgentInfo[];
|
|
1380
|
+
/** Find agents by capability */
|
|
1381
|
+
findByCapability(capability: string): AgentInfo[];
|
|
1382
|
+
/** Send a message */
|
|
1383
|
+
send(from: string, to: string | string[], message: Partial<TypedAgentMessage>): string;
|
|
1384
|
+
/** Send a request and wait for response */
|
|
1385
|
+
request(from: string, to: string, action: string, payload: Record<string, unknown>, timeout?: number): Promise<ResponseMessage>;
|
|
1386
|
+
/** Delegate a task */
|
|
1387
|
+
delegate(from: string, to: string, task: string, context: Record<string, unknown>): Promise<DelegationResultMessage>;
|
|
1388
|
+
/** Query an agent */
|
|
1389
|
+
query(from: string, to: string, question: string, context?: Record<string, unknown>): Promise<ResponseMessage>;
|
|
1390
|
+
/** Broadcast to all agents */
|
|
1391
|
+
broadcast(from: string, message: Partial<TypedAgentMessage>): string;
|
|
1392
|
+
/** Subscribe an agent to messages */
|
|
1393
|
+
listen(agentId: string, handler: MessageHandler, filter?: MessageFilter): Subscription;
|
|
1394
|
+
/** Get the message bus */
|
|
1395
|
+
getBus(): MessageBus;
|
|
1396
|
+
/** Dispose of the network, clearing pending waiters and timers */
|
|
1397
|
+
dispose(): void;
|
|
1398
|
+
}
|
|
1399
|
+
/**
|
|
1400
|
+
* Create an agent network for coordinated communication.
|
|
1401
|
+
*
|
|
1402
|
+
* @example
|
|
1403
|
+
* ```typescript
|
|
1404
|
+
* const network = createAgentNetwork({
|
|
1405
|
+
* bus: createMessageBus(),
|
|
1406
|
+
* agents: {
|
|
1407
|
+
* researcher: { capabilities: ['search', 'summarize'] },
|
|
1408
|
+
* writer: { capabilities: ['draft', 'edit'] },
|
|
1409
|
+
* reviewer: { capabilities: ['review', 'approve'] },
|
|
1410
|
+
* },
|
|
1411
|
+
* });
|
|
1412
|
+
*
|
|
1413
|
+
* // Delegate a task
|
|
1414
|
+
* const result = await network.delegate(
|
|
1415
|
+
* 'researcher',
|
|
1416
|
+
* 'writer',
|
|
1417
|
+
* 'Write an article about AI safety',
|
|
1418
|
+
* { research: findingsData }
|
|
1419
|
+
* );
|
|
1420
|
+
*
|
|
1421
|
+
* // Query for information
|
|
1422
|
+
* const answer = await network.query(
|
|
1423
|
+
* 'writer',
|
|
1424
|
+
* 'reviewer',
|
|
1425
|
+
* 'Is this paragraph technically accurate?',
|
|
1426
|
+
* { text: '...' }
|
|
1427
|
+
* );
|
|
1428
|
+
* ```
|
|
1429
|
+
*/
|
|
1430
|
+
declare function createAgentNetwork(config: AgentNetworkConfig): AgentNetwork;
|
|
1431
|
+
/**
|
|
1432
|
+
* Create a request-response helper for handling incoming requests.
|
|
1433
|
+
*
|
|
1434
|
+
* @example
|
|
1435
|
+
* ```typescript
|
|
1436
|
+
* const responder = createResponder(network, 'writer');
|
|
1437
|
+
*
|
|
1438
|
+
* responder.onRequest('draft', async (payload) => {
|
|
1439
|
+
* const draft = await generateDraft(payload.topic);
|
|
1440
|
+
* return { success: true, result: draft };
|
|
1441
|
+
* });
|
|
1442
|
+
* ```
|
|
1443
|
+
*/
|
|
1444
|
+
declare function createResponder(network: AgentNetwork, agentId: string): {
|
|
1445
|
+
onRequest(action: string, handler: (payload: Record<string, unknown>) => Promise<{
|
|
1446
|
+
success: boolean;
|
|
1447
|
+
result?: unknown;
|
|
1448
|
+
error?: string;
|
|
1449
|
+
}>): void;
|
|
1450
|
+
/** Remove a request handler */
|
|
1451
|
+
offRequest(action: string): void;
|
|
1452
|
+
/** Dispose of this responder, unsubscribing from network */
|
|
1453
|
+
dispose(): void;
|
|
1454
|
+
};
|
|
1455
|
+
/**
|
|
1456
|
+
* Create a task delegator for handling incoming delegations.
|
|
1457
|
+
*
|
|
1458
|
+
* @example
|
|
1459
|
+
* ```typescript
|
|
1460
|
+
* const delegator = createDelegator(network, 'writer');
|
|
1461
|
+
*
|
|
1462
|
+
* delegator.onDelegation(async (task, context) => {
|
|
1463
|
+
* const result = await executeTask(task, context);
|
|
1464
|
+
* return {
|
|
1465
|
+
* success: true,
|
|
1466
|
+
* result,
|
|
1467
|
+
* metrics: { durationMs: 1500, tokensUsed: 500 },
|
|
1468
|
+
* };
|
|
1469
|
+
* });
|
|
1470
|
+
* ```
|
|
1471
|
+
*/
|
|
1472
|
+
declare function createDelegator(network: AgentNetwork, agentId: string): {
|
|
1473
|
+
onDelegation(handler: (task: string, context: Record<string, unknown>) => Promise<{
|
|
1474
|
+
success: boolean;
|
|
1475
|
+
result?: unknown;
|
|
1476
|
+
error?: string;
|
|
1477
|
+
metrics?: {
|
|
1478
|
+
durationMs: number;
|
|
1479
|
+
tokensUsed?: number;
|
|
1480
|
+
cost?: number;
|
|
1481
|
+
};
|
|
1482
|
+
}>): void;
|
|
1483
|
+
/** Remove the delegation handler */
|
|
1484
|
+
offDelegation(): void;
|
|
1485
|
+
/** Dispose of this delegator, unsubscribing from network */
|
|
1486
|
+
dispose(): void;
|
|
1487
|
+
};
|
|
1488
|
+
/**
|
|
1489
|
+
* Create a pub/sub helper for topic-based communication.
|
|
1490
|
+
*
|
|
1491
|
+
* @example
|
|
1492
|
+
* ```typescript
|
|
1493
|
+
* const pubsub = createPubSub(network, 'analyst');
|
|
1494
|
+
*
|
|
1495
|
+
* // Subscribe to topics
|
|
1496
|
+
* pubsub.subscribe(['market-updates', 'alerts'], (topic, content) => {
|
|
1497
|
+
* console.log(`Received ${topic}:`, content);
|
|
1498
|
+
* });
|
|
1499
|
+
*
|
|
1500
|
+
* // Publish to topics
|
|
1501
|
+
* pubsub.publish('market-updates', { price: 100, change: 5 });
|
|
1502
|
+
* ```
|
|
1503
|
+
*/
|
|
1504
|
+
declare function createPubSub(network: AgentNetwork, agentId: string): {
|
|
1505
|
+
subscribe(topics: string[], handler: (topic: string, content: unknown) => void): () => void;
|
|
1506
|
+
publish(topic: string, content: unknown): void;
|
|
1507
|
+
/** Dispose of this pub/sub, unsubscribing from network and clearing handlers */
|
|
1508
|
+
dispose(): void;
|
|
1509
|
+
};
|
|
1510
|
+
|
|
1511
|
+
/**
|
|
1512
|
+
* Enhanced PII Detection Guardrail
|
|
1513
|
+
*
|
|
1514
|
+
* Provides comprehensive PII detection beyond basic regex patterns:
|
|
1515
|
+
* - Multiple PII types (SSN, credit cards, emails, phones, addresses, names)
|
|
1516
|
+
* - Pluggable detection backends (regex, custom, or external services)
|
|
1517
|
+
* - Context-aware detection (reduces false positives)
|
|
1518
|
+
* - Redaction with reversible or irreversible options
|
|
1519
|
+
*
|
|
1520
|
+
* @example
|
|
1521
|
+
* ```typescript
|
|
1522
|
+
* import { createEnhancedPIIGuardrail } from '@directive-run/ai';
|
|
1523
|
+
*
|
|
1524
|
+
* const guardrail = createEnhancedPIIGuardrail({
|
|
1525
|
+
* types: ['ssn', 'credit_card', 'email'],
|
|
1526
|
+
* redact: true,
|
|
1527
|
+
* detector: 'regex', // or 'custom' with custom detector
|
|
1528
|
+
* });
|
|
1529
|
+
* ```
|
|
1530
|
+
*/
|
|
1531
|
+
|
|
1532
|
+
/** Supported PII types */
|
|
1533
|
+
type PIIType = "ssn" | "credit_card" | "email" | "phone" | "address" | "name" | "date_of_birth" | "passport" | "driver_license" | "ip_address" | "bank_account" | "medical_id" | "national_id";
|
|
1534
|
+
/** Detected PII instance */
|
|
1535
|
+
interface DetectedPII {
|
|
1536
|
+
type: PIIType;
|
|
1537
|
+
value: string;
|
|
1538
|
+
position: {
|
|
1539
|
+
start: number;
|
|
1540
|
+
end: number;
|
|
1541
|
+
};
|
|
1542
|
+
confidence: number;
|
|
1543
|
+
context?: string;
|
|
1544
|
+
}
|
|
1545
|
+
/** PII detection result */
|
|
1546
|
+
interface PIIDetectionResult {
|
|
1547
|
+
detected: boolean;
|
|
1548
|
+
items: DetectedPII[];
|
|
1549
|
+
typeCounts: Partial<Record<PIIType, number>>;
|
|
1550
|
+
/** Text with PII redacted (if requested) */
|
|
1551
|
+
redactedText?: string;
|
|
1552
|
+
}
|
|
1553
|
+
/** Custom PII detector interface */
|
|
1554
|
+
interface PIIDetector {
|
|
1555
|
+
detect(text: string, types: PIIType[]): Promise<DetectedPII[]>;
|
|
1556
|
+
name: string;
|
|
1557
|
+
}
|
|
1558
|
+
/** Redaction style */
|
|
1559
|
+
type RedactionStyle =
|
|
1560
|
+
/** Replace with [REDACTED] */
|
|
1561
|
+
"placeholder"
|
|
1562
|
+
/** Replace with type-specific placeholder like [EMAIL] */
|
|
1563
|
+
| "typed"
|
|
1564
|
+
/** Replace with asterisks preserving length */
|
|
1565
|
+
| "masked"
|
|
1566
|
+
/** Replace with hash for reversible redaction */
|
|
1567
|
+
| "hashed";
|
|
1568
|
+
/** Redact detected PII from text */
|
|
1569
|
+
declare function redactPII(text: string, items: DetectedPII[], style?: RedactionStyle): string;
|
|
1570
|
+
/** Options for enhanced PII guardrail */
|
|
1571
|
+
interface EnhancedPIIGuardrailOptions {
|
|
1572
|
+
/** PII types to detect (default: all) */
|
|
1573
|
+
types?: PIIType[];
|
|
1574
|
+
/** Detection backend (default: 'regex') */
|
|
1575
|
+
detector?: "regex" | PIIDetector;
|
|
1576
|
+
/** Redact instead of blocking */
|
|
1577
|
+
redact?: boolean;
|
|
1578
|
+
/** Redaction style (default: 'typed') */
|
|
1579
|
+
redactionStyle?: RedactionStyle;
|
|
1580
|
+
/** Minimum confidence to flag (0-1, default: 0.7) */
|
|
1581
|
+
minConfidence?: number;
|
|
1582
|
+
/** Callback when PII is detected */
|
|
1583
|
+
onDetected?: (items: DetectedPII[]) => void;
|
|
1584
|
+
/** Allow specific values (whitelist) */
|
|
1585
|
+
allowlist?: string[];
|
|
1586
|
+
/** Block only if count exceeds threshold */
|
|
1587
|
+
minItemsToBlock?: number;
|
|
1588
|
+
/** Timeout for custom detector in milliseconds (default: 5000) */
|
|
1589
|
+
detectorTimeout?: number;
|
|
1590
|
+
}
|
|
1591
|
+
/**
|
|
1592
|
+
* Create an enhanced PII detection guardrail.
|
|
1593
|
+
*
|
|
1594
|
+
* @example
|
|
1595
|
+
* ```typescript
|
|
1596
|
+
* // Basic usage
|
|
1597
|
+
* const guardrail = createEnhancedPIIGuardrail();
|
|
1598
|
+
*
|
|
1599
|
+
* // Redact instead of blocking
|
|
1600
|
+
* const redactGuardrail = createEnhancedPIIGuardrail({
|
|
1601
|
+
* redact: true,
|
|
1602
|
+
* redactionStyle: 'masked',
|
|
1603
|
+
* });
|
|
1604
|
+
*
|
|
1605
|
+
* // Custom detection with external service
|
|
1606
|
+
* const customGuardrail = createEnhancedPIIGuardrail({
|
|
1607
|
+
* detector: myPresidioDetector,
|
|
1608
|
+
* types: ['ssn', 'credit_card', 'medical_id'],
|
|
1609
|
+
* });
|
|
1610
|
+
* ```
|
|
1611
|
+
*/
|
|
1612
|
+
declare function createEnhancedPIIGuardrail(options?: EnhancedPIIGuardrailOptions): GuardrailFn<InputGuardrailData>;
|
|
1613
|
+
/**
|
|
1614
|
+
* Create an output PII guardrail (for checking agent responses).
|
|
1615
|
+
*
|
|
1616
|
+
* @example
|
|
1617
|
+
* ```typescript
|
|
1618
|
+
* const outputGuardrail = createOutputPIIGuardrail({
|
|
1619
|
+
* types: ['ssn', 'credit_card'],
|
|
1620
|
+
* redact: true,
|
|
1621
|
+
* });
|
|
1622
|
+
* ```
|
|
1623
|
+
*/
|
|
1624
|
+
declare function createOutputPIIGuardrail(options?: EnhancedPIIGuardrailOptions): GuardrailFn<OutputGuardrailData>;
|
|
1625
|
+
/**
|
|
1626
|
+
* Detect PII in text without using as a guardrail.
|
|
1627
|
+
* Useful for analysis and logging.
|
|
1628
|
+
*
|
|
1629
|
+
* @example
|
|
1630
|
+
* ```typescript
|
|
1631
|
+
* const result = await detectPII('My SSN is 123-45-6789');
|
|
1632
|
+
* console.log(result.items); // [{ type: 'ssn', value: '123-45-6789', ... }]
|
|
1633
|
+
*
|
|
1634
|
+
* // With custom detector and timeout
|
|
1635
|
+
* const result = await detectPII(text, {
|
|
1636
|
+
* detector: myPresidioDetector,
|
|
1637
|
+
* timeout: 10000, // 10 seconds
|
|
1638
|
+
* });
|
|
1639
|
+
* ```
|
|
1640
|
+
*/
|
|
1641
|
+
declare function detectPII(text: string, options?: {
|
|
1642
|
+
types?: PIIType[];
|
|
1643
|
+
detector?: "regex" | PIIDetector;
|
|
1644
|
+
minConfidence?: number;
|
|
1645
|
+
/** Timeout for custom detectors in milliseconds (default: 5000) */
|
|
1646
|
+
timeout?: number;
|
|
1647
|
+
}): Promise<PIIDetectionResult>;
|
|
1648
|
+
|
|
1649
|
+
/**
|
|
1650
|
+
* Audit Plugin - Immutable Audit Trail with Hash Chain
|
|
1651
|
+
*
|
|
1652
|
+
* Provides enterprise-grade audit logging with:
|
|
1653
|
+
* - Cryptographic hash chain for tamper detection
|
|
1654
|
+
* - Bounded storage with FIFO eviction
|
|
1655
|
+
* - PII masking with configurable redaction
|
|
1656
|
+
* - Optional signing for non-repudiation
|
|
1657
|
+
* - Async export to external systems
|
|
1658
|
+
*
|
|
1659
|
+
* @example
|
|
1660
|
+
* ```typescript
|
|
1661
|
+
* import { createAuditTrail } from '@directive-run/ai';
|
|
1662
|
+
*
|
|
1663
|
+
* const audit = createAuditTrail({
|
|
1664
|
+
* maxEntries: 10000,
|
|
1665
|
+
* retentionMs: 7 * 24 * 60 * 60 * 1000, // 7 days
|
|
1666
|
+
* piiMasking: {
|
|
1667
|
+
* enabled: true,
|
|
1668
|
+
* types: ['ssn', 'credit_card', 'email'],
|
|
1669
|
+
* redactionStyle: 'typed',
|
|
1670
|
+
* },
|
|
1671
|
+
* exporter: async (entries) => {
|
|
1672
|
+
* await sendToSIEM(entries);
|
|
1673
|
+
* },
|
|
1674
|
+
* });
|
|
1675
|
+
*
|
|
1676
|
+
* const system = createSystem({
|
|
1677
|
+
* module: myModule,
|
|
1678
|
+
* plugins: [audit.createPlugin()],
|
|
1679
|
+
* });
|
|
1680
|
+
* ```
|
|
1681
|
+
*/
|
|
1682
|
+
|
|
1683
|
+
/** Audit event types - 17 total covering all system operations */
|
|
1684
|
+
type AuditEventType = "agent.run.start" | "agent.run.complete" | "agent.run.error" | "tool.call.start" | "tool.call.complete" | "tool.call.error" | "approval.requested" | "approval.granted" | "approval.denied" | "requirement.created" | "requirement.met" | "resolver.start" | "resolver.complete" | "resolver.error" | "fact.set" | "fact.batch" | "error.occurred" | "error.recovery";
|
|
1685
|
+
/** Single audit entry with hash chain linking */
|
|
1686
|
+
interface AuditEntry {
|
|
1687
|
+
/** Unique identifier for this entry */
|
|
1688
|
+
id: string;
|
|
1689
|
+
/** Unix timestamp in milliseconds */
|
|
1690
|
+
timestamp: number;
|
|
1691
|
+
/** Type of event */
|
|
1692
|
+
eventType: AuditEventType;
|
|
1693
|
+
/** SHA-256 hash of previous entry (empty string for genesis) */
|
|
1694
|
+
previousHash: string;
|
|
1695
|
+
/** SHA-256 hash of this entry's content */
|
|
1696
|
+
hash: string;
|
|
1697
|
+
/** Event payload data */
|
|
1698
|
+
payload: Record<string, unknown>;
|
|
1699
|
+
/** PII-redacted version of payload (if masking enabled) */
|
|
1700
|
+
maskedPayload?: Record<string, unknown>;
|
|
1701
|
+
/** Actor identifier (user, agent, or system) */
|
|
1702
|
+
actorId?: string;
|
|
1703
|
+
/** Session identifier for correlation */
|
|
1704
|
+
sessionId?: string;
|
|
1705
|
+
/** Cryptographic signature (if signing enabled) */
|
|
1706
|
+
signature?: string;
|
|
1707
|
+
}
|
|
1708
|
+
/** Filter options for querying audit entries */
|
|
1709
|
+
interface AuditEntryFilter {
|
|
1710
|
+
/** Filter by event types */
|
|
1711
|
+
eventTypes?: AuditEventType[];
|
|
1712
|
+
/** Filter by actor ID */
|
|
1713
|
+
actorId?: string;
|
|
1714
|
+
/** Filter by session ID */
|
|
1715
|
+
sessionId?: string;
|
|
1716
|
+
/** Start of time range */
|
|
1717
|
+
since?: number;
|
|
1718
|
+
/** End of time range */
|
|
1719
|
+
until?: number;
|
|
1720
|
+
/** Maximum entries to return */
|
|
1721
|
+
limit?: number;
|
|
1722
|
+
/** Offset for pagination */
|
|
1723
|
+
offset?: number;
|
|
1724
|
+
}
|
|
1725
|
+
/** Result of chain verification */
|
|
1726
|
+
interface AuditVerificationResult {
|
|
1727
|
+
/** Whether the chain is valid */
|
|
1728
|
+
valid: boolean;
|
|
1729
|
+
/** Number of entries verified */
|
|
1730
|
+
entriesVerified: number;
|
|
1731
|
+
/** First broken link (if any) */
|
|
1732
|
+
brokenAt?: {
|
|
1733
|
+
index: number;
|
|
1734
|
+
entryId: string;
|
|
1735
|
+
expectedHash: string;
|
|
1736
|
+
actualHash: string;
|
|
1737
|
+
};
|
|
1738
|
+
/** Verification timestamp */
|
|
1739
|
+
verifiedAt: number;
|
|
1740
|
+
}
|
|
1741
|
+
/** Audit statistics */
|
|
1742
|
+
interface AuditStats {
|
|
1743
|
+
/** Total entries in trail */
|
|
1744
|
+
totalEntries: number;
|
|
1745
|
+
/** Entries by event type */
|
|
1746
|
+
byEventType: Partial<Record<AuditEventType, number>>;
|
|
1747
|
+
/** Oldest entry timestamp */
|
|
1748
|
+
oldestEntry?: number;
|
|
1749
|
+
/** Newest entry timestamp */
|
|
1750
|
+
newestEntry?: number;
|
|
1751
|
+
/** Number of entries pruned */
|
|
1752
|
+
entriesPruned: number;
|
|
1753
|
+
/** Number of entries exported */
|
|
1754
|
+
entriesExported: number;
|
|
1755
|
+
/** Chain integrity (true if verified valid) */
|
|
1756
|
+
chainIntegrity: boolean;
|
|
1757
|
+
}
|
|
1758
|
+
/** PII masking configuration */
|
|
1759
|
+
interface PIIMaskingConfig {
|
|
1760
|
+
/** Enable PII masking */
|
|
1761
|
+
enabled: boolean;
|
|
1762
|
+
/** PII types to detect and mask */
|
|
1763
|
+
types: PIIType[];
|
|
1764
|
+
/** Redaction style */
|
|
1765
|
+
redactionStyle: RedactionStyle;
|
|
1766
|
+
/** Custom allowlist (values to skip) */
|
|
1767
|
+
allowlist?: string[];
|
|
1768
|
+
/** Minimum confidence threshold */
|
|
1769
|
+
minConfidence?: number;
|
|
1770
|
+
}
|
|
1771
|
+
/** Signing configuration for non-repudiation */
|
|
1772
|
+
interface SigningConfig {
|
|
1773
|
+
/** Function to sign a hash value */
|
|
1774
|
+
signFn: (hash: string) => Promise<string>;
|
|
1775
|
+
/** Function to verify a signature */
|
|
1776
|
+
verifyFn?: (hash: string, signature: string) => Promise<boolean>;
|
|
1777
|
+
}
|
|
1778
|
+
/** Audit plugin configuration */
|
|
1779
|
+
interface AuditPluginConfig {
|
|
1780
|
+
/** Maximum entries to retain (default: 10000) */
|
|
1781
|
+
maxEntries?: number;
|
|
1782
|
+
/** Retention period in milliseconds (default: 7 days) */
|
|
1783
|
+
retentionMs?: number;
|
|
1784
|
+
/** Export interval in milliseconds (default: 60000) */
|
|
1785
|
+
exportInterval?: number;
|
|
1786
|
+
/** Async exporter function */
|
|
1787
|
+
exporter?: (entries: AuditEntry[]) => Promise<void>;
|
|
1788
|
+
/** PII masking configuration */
|
|
1789
|
+
piiMasking?: PIIMaskingConfig;
|
|
1790
|
+
/** Signing configuration for non-repudiation */
|
|
1791
|
+
signing?: SigningConfig;
|
|
1792
|
+
/** Session ID for all entries */
|
|
1793
|
+
sessionId?: string;
|
|
1794
|
+
/** Actor ID for all entries */
|
|
1795
|
+
actorId?: string;
|
|
1796
|
+
/** Event callbacks */
|
|
1797
|
+
events?: {
|
|
1798
|
+
onEntryAdded?: (entry: AuditEntry) => void;
|
|
1799
|
+
onChainBroken?: (result: AuditVerificationResult) => void;
|
|
1800
|
+
onExportError?: (error: Error, entries: AuditEntry[]) => void;
|
|
1801
|
+
};
|
|
1802
|
+
}
|
|
1803
|
+
/** Audit trail instance */
|
|
1804
|
+
interface AuditInstance {
|
|
1805
|
+
/** Get entries with optional filtering */
|
|
1806
|
+
getEntries(filter?: AuditEntryFilter): AuditEntry[];
|
|
1807
|
+
/** Verify the integrity of the hash chain */
|
|
1808
|
+
verifyChain(): Promise<AuditVerificationResult>;
|
|
1809
|
+
/** Export entries since timestamp */
|
|
1810
|
+
export(since?: number): Promise<AuditEntry[]>;
|
|
1811
|
+
/** Prune old entries based on retention policy */
|
|
1812
|
+
prune(): number;
|
|
1813
|
+
/** Get audit statistics */
|
|
1814
|
+
getStats(): AuditStats;
|
|
1815
|
+
/** Dispose of the instance (clears timers, flushes exports) */
|
|
1816
|
+
dispose(): Promise<void>;
|
|
1817
|
+
/** Create a plugin for a directive system */
|
|
1818
|
+
createPlugin<M extends ModuleSchema>(): Plugin<M>;
|
|
1819
|
+
/** Add a custom audit entry */
|
|
1820
|
+
addEntry(eventType: AuditEventType, payload: Record<string, unknown>): Promise<AuditEntry>;
|
|
1821
|
+
}
|
|
1822
|
+
/**
|
|
1823
|
+
* Create an audit trail instance for enterprise-grade audit logging.
|
|
1824
|
+
*
|
|
1825
|
+
* Features:
|
|
1826
|
+
* - Immutable hash chain for tamper detection
|
|
1827
|
+
* - Bounded storage with automatic FIFO eviction
|
|
1828
|
+
* - PII masking with configurable redaction styles
|
|
1829
|
+
* - Optional cryptographic signing for non-repudiation
|
|
1830
|
+
* - Async export to external SIEM/logging systems
|
|
1831
|
+
*
|
|
1832
|
+
* @example
|
|
1833
|
+
* ```typescript
|
|
1834
|
+
* const audit = createAuditTrail({
|
|
1835
|
+
* maxEntries: 10000,
|
|
1836
|
+
* piiMasking: {
|
|
1837
|
+
* enabled: true,
|
|
1838
|
+
* types: ['ssn', 'credit_card'],
|
|
1839
|
+
* redactionStyle: 'typed',
|
|
1840
|
+
* },
|
|
1841
|
+
* exporter: async (entries) => {
|
|
1842
|
+
* await fetch('/api/audit', {
|
|
1843
|
+
* method: 'POST',
|
|
1844
|
+
* body: JSON.stringify(entries),
|
|
1845
|
+
* });
|
|
1846
|
+
* },
|
|
1847
|
+
* });
|
|
1848
|
+
*
|
|
1849
|
+
* // Use with directive system
|
|
1850
|
+
* const system = createSystem({
|
|
1851
|
+
* module: myModule,
|
|
1852
|
+
* plugins: [audit.createPlugin()],
|
|
1853
|
+
* });
|
|
1854
|
+
*
|
|
1855
|
+
* // Query audit entries
|
|
1856
|
+
* const recentErrors = audit.getEntries({
|
|
1857
|
+
* eventTypes: ['error.occurred', 'error.recovery'],
|
|
1858
|
+
* since: Date.now() - 3600000, // Last hour
|
|
1859
|
+
* });
|
|
1860
|
+
*
|
|
1861
|
+
* // Verify chain integrity
|
|
1862
|
+
* const verification = await audit.verifyChain();
|
|
1863
|
+
* if (!verification.valid) {
|
|
1864
|
+
* console.error('Audit chain tampered!', verification.brokenAt);
|
|
1865
|
+
* }
|
|
1866
|
+
* ```
|
|
1867
|
+
*/
|
|
1868
|
+
declare function createAuditTrail(config?: AuditPluginConfig): AuditInstance;
|
|
1869
|
+
/**
|
|
1870
|
+
* Create audit event handlers for agent orchestrator integration.
|
|
1871
|
+
* Use this to audit agent operations when using the agent orchestrator.
|
|
1872
|
+
*
|
|
1873
|
+
* @example
|
|
1874
|
+
* ```typescript
|
|
1875
|
+
* const audit = createAuditTrail({ ... });
|
|
1876
|
+
* const handlers = createAgentAuditHandlers(audit);
|
|
1877
|
+
*
|
|
1878
|
+
* // Use in orchestrator callbacks
|
|
1879
|
+
* orchestrator.run(agent, input, {
|
|
1880
|
+
* onStart: handlers.onAgentStart,
|
|
1881
|
+
* onComplete: handlers.onAgentComplete,
|
|
1882
|
+
* // ...
|
|
1883
|
+
* });
|
|
1884
|
+
* ```
|
|
1885
|
+
*/
|
|
1886
|
+
declare function createAgentAuditHandlers(audit: AuditInstance): {
|
|
1887
|
+
onAgentStart: (agentName: string, input: string) => void;
|
|
1888
|
+
onAgentComplete: (agentName: string, output: unknown, tokens: number, cost: number) => void;
|
|
1889
|
+
onAgentError: (agentName: string, error: Error) => void;
|
|
1890
|
+
onToolStart: (toolName: string, toolCallId: string, args: unknown) => void;
|
|
1891
|
+
onToolComplete: (toolName: string, toolCallId: string, result: unknown) => void;
|
|
1892
|
+
onToolError: (toolName: string, toolCallId: string, error: Error) => void;
|
|
1893
|
+
onApprovalRequested: (toolName: string, toolCallId: string, args: unknown) => void;
|
|
1894
|
+
onApprovalGranted: (toolName: string, toolCallId: string) => void;
|
|
1895
|
+
onApprovalDenied: (toolName: string, toolCallId: string, reason?: string) => void;
|
|
1896
|
+
};
|
|
1897
|
+
|
|
1898
|
+
/**
|
|
1899
|
+
* Prompt Injection Detection Guardrail
|
|
1900
|
+
*
|
|
1901
|
+
* Detects and blocks prompt injection attacks including:
|
|
1902
|
+
* - Direct injection attempts ("ignore previous instructions")
|
|
1903
|
+
* - Jailbreak patterns ("DAN mode", "pretend you can")
|
|
1904
|
+
* - Indirect injection via external content
|
|
1905
|
+
* - Encoding-based evasion attempts
|
|
1906
|
+
*
|
|
1907
|
+
* @example
|
|
1908
|
+
* ```typescript
|
|
1909
|
+
* import { createPromptInjectionGuardrail } from '@directive-run/ai';
|
|
1910
|
+
*
|
|
1911
|
+
* const guardrail = createPromptInjectionGuardrail({
|
|
1912
|
+
* strictMode: true,
|
|
1913
|
+
* onBlocked: (input, patterns) => logSecurityEvent(input, patterns),
|
|
1914
|
+
* });
|
|
1915
|
+
* ```
|
|
1916
|
+
*/
|
|
1917
|
+
|
|
1918
|
+
/** Pattern with metadata for better debugging */
|
|
1919
|
+
interface InjectionPattern {
|
|
1920
|
+
pattern: RegExp;
|
|
1921
|
+
name: string;
|
|
1922
|
+
severity: "low" | "medium" | "high" | "critical";
|
|
1923
|
+
category: InjectionCategory;
|
|
1924
|
+
}
|
|
1925
|
+
/** Categories of injection attacks */
|
|
1926
|
+
type InjectionCategory = "instruction_override" | "jailbreak" | "role_manipulation" | "encoding_evasion" | "delimiter_injection" | "context_manipulation" | "indirect_injection";
|
|
1927
|
+
/** Default injection patterns - well-tested and low false-positive rate */
|
|
1928
|
+
declare const DEFAULT_INJECTION_PATTERNS: InjectionPattern[];
|
|
1929
|
+
/** Strict patterns - more aggressive, may have higher false positives */
|
|
1930
|
+
declare const STRICT_INJECTION_PATTERNS: InjectionPattern[];
|
|
1931
|
+
/** Detailed detection result */
|
|
1932
|
+
interface InjectionDetectionResult {
|
|
1933
|
+
detected: boolean;
|
|
1934
|
+
patterns: Array<{
|
|
1935
|
+
name: string;
|
|
1936
|
+
category: InjectionCategory;
|
|
1937
|
+
severity: InjectionPattern["severity"];
|
|
1938
|
+
match: string;
|
|
1939
|
+
position: number;
|
|
1940
|
+
}>;
|
|
1941
|
+
riskScore: number;
|
|
1942
|
+
sanitizedInput?: string;
|
|
1943
|
+
}
|
|
1944
|
+
/**
|
|
1945
|
+
* Detect prompt injection patterns in text.
|
|
1946
|
+
* Returns detailed results about what was detected.
|
|
1947
|
+
*
|
|
1948
|
+
* @throws Error if input exceeds MAX_INJECTION_INPUT_LENGTH (100KB)
|
|
1949
|
+
*/
|
|
1950
|
+
declare function detectPromptInjection(text: string, patterns?: InjectionPattern[]): InjectionDetectionResult;
|
|
1951
|
+
/**
|
|
1952
|
+
* Sanitize text by removing detected injection patterns.
|
|
1953
|
+
* Warning: This is a best-effort sanitization, not a security guarantee.
|
|
1954
|
+
*
|
|
1955
|
+
* Uses a single-pass approach to prevent infinite loops where a replacement
|
|
1956
|
+
* could create a new pattern match.
|
|
1957
|
+
*/
|
|
1958
|
+
declare function sanitizeInjection(text: string, patterns?: InjectionPattern[]): string;
|
|
1959
|
+
/** Options for prompt injection guardrail */
|
|
1960
|
+
interface PromptInjectionGuardrailOptions {
|
|
1961
|
+
/** Additional patterns to check (added to defaults) */
|
|
1962
|
+
additionalPatterns?: InjectionPattern[];
|
|
1963
|
+
/** Replace default patterns entirely */
|
|
1964
|
+
replacePatterns?: InjectionPattern[];
|
|
1965
|
+
/** Use strict mode with more aggressive detection */
|
|
1966
|
+
strictMode?: boolean;
|
|
1967
|
+
/** Minimum risk score to block (0-100, default: 50) */
|
|
1968
|
+
blockThreshold?: number;
|
|
1969
|
+
/** Attempt to sanitize instead of blocking */
|
|
1970
|
+
sanitize?: boolean;
|
|
1971
|
+
/** Callback when injection is detected */
|
|
1972
|
+
onBlocked?: (input: string, result: InjectionDetectionResult) => void;
|
|
1973
|
+
/** Categories to ignore (e.g., allow 'role_manipulation' for roleplay apps) */
|
|
1974
|
+
ignoreCategories?: InjectionCategory[];
|
|
1975
|
+
}
|
|
1976
|
+
/**
|
|
1977
|
+
* Create a prompt injection detection guardrail.
|
|
1978
|
+
*
|
|
1979
|
+
* @example
|
|
1980
|
+
* ```typescript
|
|
1981
|
+
* // Basic usage
|
|
1982
|
+
* const guardrail = createPromptInjectionGuardrail();
|
|
1983
|
+
*
|
|
1984
|
+
* // Strict mode for high-security applications
|
|
1985
|
+
* const strictGuardrail = createPromptInjectionGuardrail({
|
|
1986
|
+
* strictMode: true,
|
|
1987
|
+
* blockThreshold: 25,
|
|
1988
|
+
* });
|
|
1989
|
+
*
|
|
1990
|
+
* // Allow role manipulation for roleplay apps
|
|
1991
|
+
* const roleplayGuardrail = createPromptInjectionGuardrail({
|
|
1992
|
+
* ignoreCategories: ['role_manipulation'],
|
|
1993
|
+
* });
|
|
1994
|
+
* ```
|
|
1995
|
+
*/
|
|
1996
|
+
declare function createPromptInjectionGuardrail(options?: PromptInjectionGuardrailOptions): GuardrailFn<InputGuardrailData>;
|
|
1997
|
+
/**
|
|
1998
|
+
* Mark content as potentially untrusted (from external sources).
|
|
1999
|
+
* This wraps the content with markers that injection detection will scrutinize more closely.
|
|
2000
|
+
*
|
|
2001
|
+
* @example
|
|
2002
|
+
* ```typescript
|
|
2003
|
+
* const userUpload = await readFile(path);
|
|
2004
|
+
* const markedContent = markUntrustedContent(userUpload, 'user-upload');
|
|
2005
|
+
* const prompt = `Summarize this document: ${markedContent}`;
|
|
2006
|
+
* ```
|
|
2007
|
+
*/
|
|
2008
|
+
declare function markUntrustedContent(content: string, source: string): string;
|
|
2009
|
+
/**
|
|
2010
|
+
* Create a guardrail that applies stricter checks to marked untrusted content.
|
|
2011
|
+
*
|
|
2012
|
+
* @example
|
|
2013
|
+
* ```typescript
|
|
2014
|
+
* const guardrail = createUntrustedContentGuardrail({
|
|
2015
|
+
* baseGuardrail: createPromptInjectionGuardrail({ strictMode: true }),
|
|
2016
|
+
* });
|
|
2017
|
+
* ```
|
|
2018
|
+
*/
|
|
2019
|
+
declare function createUntrustedContentGuardrail(options: {
|
|
2020
|
+
/** Guardrail to apply to untrusted sections */
|
|
2021
|
+
baseGuardrail?: GuardrailFn<InputGuardrailData>;
|
|
2022
|
+
/** Block if untrusted content contains these patterns */
|
|
2023
|
+
additionalPatterns?: InjectionPattern[];
|
|
2024
|
+
}): GuardrailFn<InputGuardrailData>;
|
|
2025
|
+
|
|
2026
|
+
/**
|
|
2027
|
+
* Compliance Plugin - GDPR/CCPA Data Subject Rights
|
|
2028
|
+
*
|
|
2029
|
+
* Provides enterprise-grade compliance features:
|
|
2030
|
+
* - Data Export (DSR) - Export all data for a subject
|
|
2031
|
+
* - Data Deletion - Soft/hard delete with certificates
|
|
2032
|
+
* - Consent Tracking - Track and enforce consent
|
|
2033
|
+
* - Retention Policies - Automatic data retention enforcement
|
|
2034
|
+
*
|
|
2035
|
+
* @example
|
|
2036
|
+
* ```typescript
|
|
2037
|
+
* import { createCompliance } from '@directive-run/ai';
|
|
2038
|
+
*
|
|
2039
|
+
* const compliance = createCompliance({
|
|
2040
|
+
* storage: myStorageAdapter,
|
|
2041
|
+
* retention: {
|
|
2042
|
+
* defaultRetentionMs: 365 * 24 * 60 * 60 * 1000, // 1 year
|
|
2043
|
+
* categoryRetention: {
|
|
2044
|
+
* audit: 7 * 365 * 24 * 60 * 60 * 1000, // 7 years for audit
|
|
2045
|
+
* sessions: 30 * 24 * 60 * 60 * 1000, // 30 days for sessions
|
|
2046
|
+
* },
|
|
2047
|
+
* },
|
|
2048
|
+
* });
|
|
2049
|
+
*
|
|
2050
|
+
* // Handle data subject request
|
|
2051
|
+
* const exportResult = await compliance.exportData({
|
|
2052
|
+
* subjectId: 'user-123',
|
|
2053
|
+
* format: 'json',
|
|
2054
|
+
* includeAudit: true,
|
|
2055
|
+
* });
|
|
2056
|
+
*
|
|
2057
|
+
* // Delete user data
|
|
2058
|
+
* const deleteResult = await compliance.deleteData({
|
|
2059
|
+
* subjectId: 'user-123',
|
|
2060
|
+
* scope: 'all',
|
|
2061
|
+
* });
|
|
2062
|
+
* ```
|
|
2063
|
+
*/
|
|
2064
|
+
|
|
2065
|
+
/** Data export request (GDPR Article 20) */
|
|
2066
|
+
interface DataExportRequest {
|
|
2067
|
+
/** Subject identifier (user ID) */
|
|
2068
|
+
subjectId: string;
|
|
2069
|
+
/** Export format */
|
|
2070
|
+
format: "json" | "csv";
|
|
2071
|
+
/** Include audit trail entries */
|
|
2072
|
+
includeAudit?: boolean;
|
|
2073
|
+
/** Include derived data */
|
|
2074
|
+
includeDerived?: boolean;
|
|
2075
|
+
/** Specific data categories to export */
|
|
2076
|
+
categories?: string[];
|
|
2077
|
+
/** Request metadata */
|
|
2078
|
+
metadata?: Record<string, unknown>;
|
|
2079
|
+
}
|
|
2080
|
+
/** Result of data export */
|
|
2081
|
+
interface DataExportResult {
|
|
2082
|
+
/** Whether export was successful */
|
|
2083
|
+
success: boolean;
|
|
2084
|
+
/** Subject identifier */
|
|
2085
|
+
subjectId: string;
|
|
2086
|
+
/** Export format used */
|
|
2087
|
+
format: "json" | "csv";
|
|
2088
|
+
/** Exported data */
|
|
2089
|
+
data: string;
|
|
2090
|
+
/** Data categories included */
|
|
2091
|
+
categories: string[];
|
|
2092
|
+
/** Number of records exported */
|
|
2093
|
+
recordCount: number;
|
|
2094
|
+
/** SHA-256 checksum of exported data */
|
|
2095
|
+
checksum: string;
|
|
2096
|
+
/** Export timestamp */
|
|
2097
|
+
exportedAt: number;
|
|
2098
|
+
/** Expiration timestamp for download link (if applicable) */
|
|
2099
|
+
expiresAt?: number;
|
|
2100
|
+
/** Error message if failed */
|
|
2101
|
+
error?: string;
|
|
2102
|
+
}
|
|
2103
|
+
/** Data deletion scope */
|
|
2104
|
+
type DeletionScope = "all" | "facts" | "audit" | "specific";
|
|
2105
|
+
/** Data deletion request (GDPR Article 17) */
|
|
2106
|
+
interface DataDeletionRequest {
|
|
2107
|
+
/** Subject identifier (user ID) */
|
|
2108
|
+
subjectId: string;
|
|
2109
|
+
/** Scope of deletion */
|
|
2110
|
+
scope: DeletionScope;
|
|
2111
|
+
/** Anonymize instead of delete (retain structure, remove PII) */
|
|
2112
|
+
anonymize?: boolean;
|
|
2113
|
+
/** Specific data categories to delete (when scope is 'specific') */
|
|
2114
|
+
categories?: string[];
|
|
2115
|
+
/** Reason for deletion */
|
|
2116
|
+
reason?: string;
|
|
2117
|
+
/** Request metadata */
|
|
2118
|
+
metadata?: Record<string, unknown>;
|
|
2119
|
+
}
|
|
2120
|
+
/** Result of data deletion */
|
|
2121
|
+
interface DataDeletionResult {
|
|
2122
|
+
/** Whether deletion was successful */
|
|
2123
|
+
success: boolean;
|
|
2124
|
+
/** Subject identifier */
|
|
2125
|
+
subjectId: string;
|
|
2126
|
+
/** Scope of deletion performed */
|
|
2127
|
+
scope: DeletionScope;
|
|
2128
|
+
/** Whether data was anonymized vs deleted */
|
|
2129
|
+
anonymized: boolean;
|
|
2130
|
+
/** Number of records deleted/anonymized */
|
|
2131
|
+
recordsAffected: number;
|
|
2132
|
+
/** Data categories affected */
|
|
2133
|
+
categoriesAffected: string[];
|
|
2134
|
+
/** Deletion certificate (for compliance records) */
|
|
2135
|
+
certificate: DeletionCertificate;
|
|
2136
|
+
/** Deletion timestamp */
|
|
2137
|
+
deletedAt: number;
|
|
2138
|
+
/** Error message if failed */
|
|
2139
|
+
error?: string;
|
|
2140
|
+
}
|
|
2141
|
+
/** Deletion certificate for compliance records */
|
|
2142
|
+
interface DeletionCertificate {
|
|
2143
|
+
/** Certificate ID */
|
|
2144
|
+
id: string;
|
|
2145
|
+
/** Subject identifier */
|
|
2146
|
+
subjectId: string;
|
|
2147
|
+
/** Type of deletion */
|
|
2148
|
+
type: "soft" | "hard" | "anonymization";
|
|
2149
|
+
/** Scope of deletion */
|
|
2150
|
+
scope: DeletionScope;
|
|
2151
|
+
/** Categories deleted */
|
|
2152
|
+
categories: string[];
|
|
2153
|
+
/** Record count */
|
|
2154
|
+
recordCount: number;
|
|
2155
|
+
/** Deletion timestamp */
|
|
2156
|
+
deletedAt: number;
|
|
2157
|
+
/** Reason for deletion */
|
|
2158
|
+
reason?: string;
|
|
2159
|
+
/** SHA-256 hash of certificate content */
|
|
2160
|
+
hash: string;
|
|
2161
|
+
}
|
|
2162
|
+
/** Consent record for a subject */
|
|
2163
|
+
interface ConsentRecord {
|
|
2164
|
+
/** Subject identifier */
|
|
2165
|
+
subjectId: string;
|
|
2166
|
+
/** Consent purpose (e.g., 'marketing', 'analytics', 'personalization') */
|
|
2167
|
+
purpose: string;
|
|
2168
|
+
/** Whether consent is granted */
|
|
2169
|
+
granted: boolean;
|
|
2170
|
+
/** When consent was granted (if granted) */
|
|
2171
|
+
grantedAt?: number;
|
|
2172
|
+
/** When consent expires (if applicable) */
|
|
2173
|
+
expiresAt?: number;
|
|
2174
|
+
/** When consent was revoked (if revoked) */
|
|
2175
|
+
revokedAt?: number;
|
|
2176
|
+
/** Source of consent (e.g., 'signup_form', 'settings_page') */
|
|
2177
|
+
source?: string;
|
|
2178
|
+
/** Consent version/hash (for tracking which T&C version was accepted) */
|
|
2179
|
+
version?: string;
|
|
2180
|
+
}
|
|
2181
|
+
/** Consent tracker interface */
|
|
2182
|
+
interface ConsentTracker {
|
|
2183
|
+
/** Record consent grant */
|
|
2184
|
+
grant(subjectId: string, purpose: string, options?: {
|
|
2185
|
+
expiresAt?: number;
|
|
2186
|
+
source?: string;
|
|
2187
|
+
version?: string;
|
|
2188
|
+
}): Promise<ConsentRecord>;
|
|
2189
|
+
/** Revoke consent */
|
|
2190
|
+
revoke(subjectId: string, purpose: string): Promise<ConsentRecord | null>;
|
|
2191
|
+
/** Check if consent is granted */
|
|
2192
|
+
check(subjectId: string, purpose: string): Promise<boolean>;
|
|
2193
|
+
/** Get all consents for a subject */
|
|
2194
|
+
getForSubject(subjectId: string): Promise<ConsentRecord[]>;
|
|
2195
|
+
/** Get all subjects with consent for a purpose */
|
|
2196
|
+
getForPurpose(purpose: string): Promise<ConsentRecord[]>;
|
|
2197
|
+
}
|
|
2198
|
+
/** Retention policy */
|
|
2199
|
+
interface RetentionPolicy {
|
|
2200
|
+
/** Policy name */
|
|
2201
|
+
name: string;
|
|
2202
|
+
/** Default retention period in milliseconds */
|
|
2203
|
+
defaultRetentionMs: number;
|
|
2204
|
+
/** Category-specific retention periods */
|
|
2205
|
+
categoryRetention?: Record<string, number>;
|
|
2206
|
+
/** Callback before data is deleted */
|
|
2207
|
+
onBeforeDelete?: (data: {
|
|
2208
|
+
category: string;
|
|
2209
|
+
count: number;
|
|
2210
|
+
}) => Promise<void>;
|
|
2211
|
+
/** Callback after data is deleted */
|
|
2212
|
+
onAfterDelete?: (data: {
|
|
2213
|
+
category: string;
|
|
2214
|
+
count: number;
|
|
2215
|
+
}) => void;
|
|
2216
|
+
}
|
|
2217
|
+
/** Storage adapter for compliance data */
|
|
2218
|
+
interface ComplianceStorage {
|
|
2219
|
+
/** Get all data for a subject */
|
|
2220
|
+
getSubjectData(subjectId: string, categories?: string[]): Promise<{
|
|
2221
|
+
category: string;
|
|
2222
|
+
records: Array<{
|
|
2223
|
+
id: string;
|
|
2224
|
+
data: Record<string, unknown>;
|
|
2225
|
+
createdAt: number;
|
|
2226
|
+
}>;
|
|
2227
|
+
}[]>;
|
|
2228
|
+
/** Delete data for a subject */
|
|
2229
|
+
deleteSubjectData(subjectId: string, categories?: string[]): Promise<number>;
|
|
2230
|
+
/** Anonymize data for a subject */
|
|
2231
|
+
anonymizeSubjectData(subjectId: string, categories?: string[]): Promise<number>;
|
|
2232
|
+
/** Get audit entries for a subject */
|
|
2233
|
+
getAuditEntries?(subjectId: string): Promise<Array<{
|
|
2234
|
+
id: string;
|
|
2235
|
+
timestamp: number;
|
|
2236
|
+
eventType: string;
|
|
2237
|
+
payload: Record<string, unknown>;
|
|
2238
|
+
}>>;
|
|
2239
|
+
/** Get data older than timestamp by category */
|
|
2240
|
+
getExpiredData(category: string, olderThan: number): Promise<Array<{
|
|
2241
|
+
id: string;
|
|
2242
|
+
createdAt: number;
|
|
2243
|
+
}>>;
|
|
2244
|
+
/** Delete records by IDs */
|
|
2245
|
+
deleteByIds(ids: string[]): Promise<number>;
|
|
2246
|
+
/** Store consent record */
|
|
2247
|
+
storeConsent(record: ConsentRecord): Promise<void>;
|
|
2248
|
+
/** Get consent record */
|
|
2249
|
+
getConsent(subjectId: string, purpose: string): Promise<ConsentRecord | null>;
|
|
2250
|
+
/** Get consents by subject */
|
|
2251
|
+
getConsentsBySubject(subjectId: string): Promise<ConsentRecord[]>;
|
|
2252
|
+
/** Get consents by purpose */
|
|
2253
|
+
getConsentsByPurpose(purpose: string): Promise<ConsentRecord[]>;
|
|
2254
|
+
/** Store deletion certificate */
|
|
2255
|
+
storeDeletionCertificate(certificate: DeletionCertificate): Promise<void>;
|
|
2256
|
+
}
|
|
2257
|
+
/** Compliance configuration */
|
|
2258
|
+
interface ComplianceConfig {
|
|
2259
|
+
/** Storage adapter */
|
|
2260
|
+
storage: ComplianceStorage;
|
|
2261
|
+
/** Retention policy */
|
|
2262
|
+
retention?: RetentionPolicy;
|
|
2263
|
+
/** Consent purposes to track */
|
|
2264
|
+
consentPurposes?: string[];
|
|
2265
|
+
/** Export link expiration (default: 24 hours) */
|
|
2266
|
+
exportExpirationMs?: number;
|
|
2267
|
+
/** Audit all compliance operations */
|
|
2268
|
+
auditOperations?: boolean;
|
|
2269
|
+
/** Event callbacks */
|
|
2270
|
+
events?: {
|
|
2271
|
+
onExport?: (result: DataExportResult) => void;
|
|
2272
|
+
onDelete?: (result: DataDeletionResult) => void;
|
|
2273
|
+
onConsentChange?: (record: ConsentRecord) => void;
|
|
2274
|
+
onRetentionEnforced?: (category: string, count: number) => void;
|
|
2275
|
+
};
|
|
2276
|
+
}
|
|
2277
|
+
/** Compliance instance */
|
|
2278
|
+
interface ComplianceInstance {
|
|
2279
|
+
/** Export data for a subject (GDPR Article 20) */
|
|
2280
|
+
exportData(request: DataExportRequest): Promise<DataExportResult>;
|
|
2281
|
+
/** Delete data for a subject (GDPR Article 17) */
|
|
2282
|
+
deleteData(request: DataDeletionRequest): Promise<DataDeletionResult>;
|
|
2283
|
+
/** Consent tracker */
|
|
2284
|
+
consent: ConsentTracker;
|
|
2285
|
+
/** Enforce retention policy */
|
|
2286
|
+
enforceRetention(): Promise<number>;
|
|
2287
|
+
/** Create a guardrail that checks consent before processing */
|
|
2288
|
+
createConsentGuardrail(purpose: string): GuardrailFn<InputGuardrailData>;
|
|
2289
|
+
/** Get deletion certificate by ID */
|
|
2290
|
+
getDeletionCertificate(subjectId: string): Promise<DeletionCertificate | null>;
|
|
2291
|
+
}
|
|
2292
|
+
/** Create an in-memory compliance storage adapter */
|
|
2293
|
+
declare function createInMemoryComplianceStorage(): ComplianceStorage;
|
|
2294
|
+
/**
|
|
2295
|
+
* Create a compliance instance for GDPR/CCPA data subject rights.
|
|
2296
|
+
*
|
|
2297
|
+
* Features:
|
|
2298
|
+
* - Data Export (GDPR Article 20) - Portable data export
|
|
2299
|
+
* - Data Deletion (GDPR Article 17) - Right to erasure
|
|
2300
|
+
* - Consent Tracking - Track and verify consent
|
|
2301
|
+
* - Retention Policies - Automatic data retention
|
|
2302
|
+
*
|
|
2303
|
+
* @example
|
|
2304
|
+
* ```typescript
|
|
2305
|
+
* const compliance = createCompliance({
|
|
2306
|
+
* storage: myStorageAdapter,
|
|
2307
|
+
* retention: {
|
|
2308
|
+
* name: 'default',
|
|
2309
|
+
* defaultRetentionMs: 365 * 24 * 60 * 60 * 1000, // 1 year
|
|
2310
|
+
* categoryRetention: {
|
|
2311
|
+
* audit: 7 * 365 * 24 * 60 * 60 * 1000, // 7 years
|
|
2312
|
+
* },
|
|
2313
|
+
* },
|
|
2314
|
+
* consentPurposes: ['marketing', 'analytics', 'personalization'],
|
|
2315
|
+
* });
|
|
2316
|
+
*
|
|
2317
|
+
* // Export user data
|
|
2318
|
+
* const exportResult = await compliance.exportData({
|
|
2319
|
+
* subjectId: 'user-123',
|
|
2320
|
+
* format: 'json',
|
|
2321
|
+
* });
|
|
2322
|
+
*
|
|
2323
|
+
* // Check consent
|
|
2324
|
+
* const hasConsent = await compliance.consent.check('user-123', 'marketing');
|
|
2325
|
+
* ```
|
|
2326
|
+
*/
|
|
2327
|
+
declare function createCompliance(config: ComplianceConfig): ComplianceInstance;
|
|
2328
|
+
|
|
2329
|
+
/**
|
|
2330
|
+
* Semantic Caching Guardrail
|
|
2331
|
+
*
|
|
2332
|
+
* Caches agent responses based on semantic similarity to reduce redundant LLM calls.
|
|
2333
|
+
* Uses vector embeddings to find semantically similar previous queries.
|
|
2334
|
+
*
|
|
2335
|
+
* @example
|
|
2336
|
+
* ```typescript
|
|
2337
|
+
* import { createSemanticCacheGuardrail } from '@directive-run/ai';
|
|
2338
|
+
*
|
|
2339
|
+
* const cacheGuardrail = createSemanticCacheGuardrail({
|
|
2340
|
+
* embedder: async (text) => {
|
|
2341
|
+
* // Use your embedding model (OpenAI, local model, etc.)
|
|
2342
|
+
* return await getEmbedding(text);
|
|
2343
|
+
* },
|
|
2344
|
+
* similarityThreshold: 0.95,
|
|
2345
|
+
* maxCacheSize: 1000,
|
|
2346
|
+
* ttlMs: 3600000, // 1 hour
|
|
2347
|
+
* });
|
|
2348
|
+
*
|
|
2349
|
+
* const orchestrator = createAgentOrchestrator({
|
|
2350
|
+
* guardrails: {
|
|
2351
|
+
* input: [cacheGuardrail],
|
|
2352
|
+
* },
|
|
2353
|
+
* runner: run,
|
|
2354
|
+
* });
|
|
2355
|
+
* ```
|
|
2356
|
+
*/
|
|
2357
|
+
/** Vector embedding (array of numbers) */
|
|
2358
|
+
type Embedding = number[];
|
|
2359
|
+
/** Function to generate embeddings for text */
|
|
2360
|
+
type EmbedderFn = (text: string) => Promise<Embedding>;
|
|
2361
|
+
/** Cached response entry */
|
|
2362
|
+
interface CacheEntry {
|
|
2363
|
+
id: string;
|
|
2364
|
+
query: string;
|
|
2365
|
+
queryEmbedding: Embedding;
|
|
2366
|
+
response: string;
|
|
2367
|
+
metadata: Record<string, unknown>;
|
|
2368
|
+
createdAt: number;
|
|
2369
|
+
accessedAt: number;
|
|
2370
|
+
accessCount: number;
|
|
2371
|
+
agentName?: string;
|
|
2372
|
+
}
|
|
2373
|
+
/** Cache lookup result */
|
|
2374
|
+
interface CacheLookupResult {
|
|
2375
|
+
hit: boolean;
|
|
2376
|
+
entry?: CacheEntry;
|
|
2377
|
+
similarity?: number;
|
|
2378
|
+
latencyMs: number;
|
|
2379
|
+
}
|
|
2380
|
+
/** Semantic cache configuration */
|
|
2381
|
+
interface SemanticCacheConfig {
|
|
2382
|
+
/** Function to generate embeddings */
|
|
2383
|
+
embedder: EmbedderFn;
|
|
2384
|
+
/** Similarity threshold (0.0 to 1.0) for cache hits */
|
|
2385
|
+
similarityThreshold?: number;
|
|
2386
|
+
/** Maximum number of entries to cache */
|
|
2387
|
+
maxCacheSize?: number;
|
|
2388
|
+
/** Time-to-live in milliseconds for cache entries */
|
|
2389
|
+
ttlMs?: number;
|
|
2390
|
+
/** Cache namespace for multi-tenant scenarios */
|
|
2391
|
+
namespace?: string;
|
|
2392
|
+
/** Custom storage backend (defaults to in-memory) */
|
|
2393
|
+
storage?: SemanticCacheStorage;
|
|
2394
|
+
/** Callback when cache hit occurs */
|
|
2395
|
+
onHit?: (entry: CacheEntry, similarity: number) => void;
|
|
2396
|
+
/** Callback when cache miss occurs */
|
|
2397
|
+
onMiss?: (query: string) => void;
|
|
2398
|
+
/** Callback when cache lookup encounters an error */
|
|
2399
|
+
onError?: (error: Error) => void;
|
|
2400
|
+
/** Whether to include agent name in cache key */
|
|
2401
|
+
perAgent?: boolean;
|
|
2402
|
+
}
|
|
2403
|
+
/** Storage interface for cache backends */
|
|
2404
|
+
interface SemanticCacheStorage {
|
|
2405
|
+
/** Get all entries for a namespace */
|
|
2406
|
+
getEntries(namespace: string): Promise<CacheEntry[]>;
|
|
2407
|
+
/** Add an entry to the cache */
|
|
2408
|
+
addEntry(namespace: string, entry: CacheEntry): Promise<void>;
|
|
2409
|
+
/** Update an entry (e.g., access count) */
|
|
2410
|
+
updateEntry(namespace: string, id: string, updates: Partial<CacheEntry>): Promise<void>;
|
|
2411
|
+
/** Remove an entry */
|
|
2412
|
+
removeEntry(namespace: string, id: string): Promise<void>;
|
|
2413
|
+
/** Clear all entries in a namespace */
|
|
2414
|
+
clear(namespace: string): Promise<void>;
|
|
2415
|
+
}
|
|
2416
|
+
/** Semantic cache instance */
|
|
2417
|
+
interface SemanticCache {
|
|
2418
|
+
/** Look up a query in the cache */
|
|
2419
|
+
lookup(query: string, agentName?: string): Promise<CacheLookupResult>;
|
|
2420
|
+
/** Store a response in the cache */
|
|
2421
|
+
store(query: string, response: string, agentName?: string, metadata?: Record<string, unknown>): Promise<void>;
|
|
2422
|
+
/** Invalidate cache entries matching a predicate */
|
|
2423
|
+
invalidate(predicate: (entry: CacheEntry) => boolean): Promise<number>;
|
|
2424
|
+
/** Clear all cache entries */
|
|
2425
|
+
clear(): Promise<void>;
|
|
2426
|
+
/** Get cache statistics */
|
|
2427
|
+
getStats(): CacheStats;
|
|
2428
|
+
/** Export cache entries (for persistence) */
|
|
2429
|
+
export(): Promise<CacheEntry[]>;
|
|
2430
|
+
/** Import cache entries (from persistence) */
|
|
2431
|
+
import(entries: CacheEntry[]): Promise<void>;
|
|
2432
|
+
}
|
|
2433
|
+
/** Cache statistics */
|
|
2434
|
+
interface CacheStats {
|
|
2435
|
+
totalEntries: number;
|
|
2436
|
+
totalHits: number;
|
|
2437
|
+
totalMisses: number;
|
|
2438
|
+
hitRate: number;
|
|
2439
|
+
avgSimilarityOnHit: number;
|
|
2440
|
+
oldestEntry: number | null;
|
|
2441
|
+
newestEntry: number | null;
|
|
2442
|
+
}
|
|
2443
|
+
/**
|
|
2444
|
+
* Create an in-memory cache storage backend.
|
|
2445
|
+
*/
|
|
2446
|
+
declare function createInMemoryStorage(): SemanticCacheStorage;
|
|
2447
|
+
/**
|
|
2448
|
+
* Create a semantic cache instance.
|
|
2449
|
+
*
|
|
2450
|
+
* @example
|
|
2451
|
+
* ```typescript
|
|
2452
|
+
* const cache = createSemanticCache({
|
|
2453
|
+
* embedder: async (text) => {
|
|
2454
|
+
* const response = await openai.embeddings.create({
|
|
2455
|
+
* model: 'text-embedding-3-small',
|
|
2456
|
+
* input: text,
|
|
2457
|
+
* });
|
|
2458
|
+
* return response.data[0].embedding;
|
|
2459
|
+
* },
|
|
2460
|
+
* similarityThreshold: 0.92,
|
|
2461
|
+
* maxCacheSize: 500,
|
|
2462
|
+
* ttlMs: 3600000, // 1 hour
|
|
2463
|
+
* });
|
|
2464
|
+
*
|
|
2465
|
+
* // Check cache before calling agent
|
|
2466
|
+
* const result = await cache.lookup(userQuery);
|
|
2467
|
+
* if (result.hit) {
|
|
2468
|
+
* return result.entry!.response;
|
|
2469
|
+
* }
|
|
2470
|
+
*
|
|
2471
|
+
* // Call agent and cache response
|
|
2472
|
+
* const response = await runAgent(userQuery);
|
|
2473
|
+
* await cache.store(userQuery, response);
|
|
2474
|
+
* ```
|
|
2475
|
+
*/
|
|
2476
|
+
declare function createSemanticCache(config: SemanticCacheConfig): SemanticCache;
|
|
2477
|
+
/** Input guardrail data for semantic cache */
|
|
2478
|
+
interface SemanticCacheGuardrailData {
|
|
2479
|
+
input: string;
|
|
2480
|
+
agentName?: string;
|
|
2481
|
+
}
|
|
2482
|
+
/**
|
|
2483
|
+
* Result of semantic cache guardrail.
|
|
2484
|
+
*
|
|
2485
|
+
* **Important semantics:**
|
|
2486
|
+
* - `passed: false` + `cacheHit: true` = Short-circuit with cached response (not an error!)
|
|
2487
|
+
* - `passed: true` + `cacheHit: false` = No cache hit, proceed with agent call
|
|
2488
|
+
*
|
|
2489
|
+
* The `passed: false` follows guardrail convention where "not passing" stops the flow,
|
|
2490
|
+
* but in this case stopping is desirable (returning cached data is good).
|
|
2491
|
+
*/
|
|
2492
|
+
interface SemanticCacheGuardrailResult {
|
|
2493
|
+
/**
|
|
2494
|
+
* Whether to proceed with the agent call.
|
|
2495
|
+
* `false` means short-circuit with cached response (this is good, not an error).
|
|
2496
|
+
* `true` means no cache hit, proceed with agent.
|
|
2497
|
+
*/
|
|
2498
|
+
passed: boolean;
|
|
2499
|
+
/** Indicates whether this was a cache hit */
|
|
2500
|
+
cacheHit: boolean;
|
|
2501
|
+
/** Reason for the result */
|
|
2502
|
+
reason?: string;
|
|
2503
|
+
/** The cached response (only present on cache hit) */
|
|
2504
|
+
cachedResponse?: string;
|
|
2505
|
+
/** Similarity score (0-1) of the cache hit */
|
|
2506
|
+
similarity?: number;
|
|
2507
|
+
}
|
|
2508
|
+
/**
|
|
2509
|
+
* Create a semantic caching input guardrail.
|
|
2510
|
+
*
|
|
2511
|
+
* **How it works:**
|
|
2512
|
+
* - On cache HIT: Returns `{ passed: false, cacheHit: true, cachedResponse: "..." }`
|
|
2513
|
+
* The orchestrator should detect `cacheHit: true` and return the cached response.
|
|
2514
|
+
* - On cache MISS: Returns `{ passed: true, cacheHit: false }`
|
|
2515
|
+
* Proceed with normal agent execution.
|
|
2516
|
+
*
|
|
2517
|
+
* **Important:** `passed: false` with `cacheHit: true` is SUCCESS, not failure.
|
|
2518
|
+
* The guardrail "short-circuits" the flow to return cached data efficiently.
|
|
2519
|
+
*
|
|
2520
|
+
* @example
|
|
2521
|
+
* ```typescript
|
|
2522
|
+
* const cacheGuardrail = createSemanticCacheGuardrail({
|
|
2523
|
+
* cache: mySemanticCache,
|
|
2524
|
+
* });
|
|
2525
|
+
*
|
|
2526
|
+
* const orchestrator = createAgentOrchestrator({
|
|
2527
|
+
* guardrails: {
|
|
2528
|
+
* input: [
|
|
2529
|
+
* {
|
|
2530
|
+
* name: 'semantic-cache',
|
|
2531
|
+
* fn: cacheGuardrail,
|
|
2532
|
+
* },
|
|
2533
|
+
* ],
|
|
2534
|
+
* },
|
|
2535
|
+
* runner: run,
|
|
2536
|
+
* });
|
|
2537
|
+
*
|
|
2538
|
+
* // In your orchestrator wrapper, check for cache hits:
|
|
2539
|
+
* const guardrailResult = await cacheGuardrail({ input: userQuery });
|
|
2540
|
+
* if (guardrailResult.cacheHit) {
|
|
2541
|
+
* return guardrailResult.cachedResponse; // Fast path!
|
|
2542
|
+
* }
|
|
2543
|
+
* // Otherwise proceed with agent call...
|
|
2544
|
+
* ```
|
|
2545
|
+
*/
|
|
2546
|
+
declare function createSemanticCacheGuardrail(config: {
|
|
2547
|
+
cache: SemanticCache;
|
|
2548
|
+
}): (data: SemanticCacheGuardrailData) => Promise<SemanticCacheGuardrailResult>;
|
|
2549
|
+
/**
|
|
2550
|
+
* Create a simple hash-based "embedder" for testing.
|
|
2551
|
+
* NOT suitable for production - use a real embedding model.
|
|
2552
|
+
*/
|
|
2553
|
+
declare function createTestEmbedder(dimensions?: number): EmbedderFn;
|
|
2554
|
+
/** Batched embedder instance with dispose capability */
|
|
2555
|
+
interface BatchedEmbedder {
|
|
2556
|
+
/** Embed a single text (batched internally) */
|
|
2557
|
+
embed: EmbedderFn;
|
|
2558
|
+
/** Flush any pending batch immediately */
|
|
2559
|
+
flush(): Promise<void>;
|
|
2560
|
+
/** Dispose of the embedder, clearing timers and rejecting pending requests */
|
|
2561
|
+
dispose(): void;
|
|
2562
|
+
}
|
|
2563
|
+
/**
|
|
2564
|
+
* Create a batched embedder that groups multiple texts into single API calls.
|
|
2565
|
+
*
|
|
2566
|
+
* **BREAKING CHANGE:** Previously returned `EmbedderFn` directly. Now returns
|
|
2567
|
+
* a `BatchedEmbedder` object with `embed`, `flush`, and `dispose` methods.
|
|
2568
|
+
*
|
|
2569
|
+
* To migrate: `const embed = createBatchedEmbedder(...)` becomes
|
|
2570
|
+
* `const { embed } = createBatchedEmbedder(...)`.
|
|
2571
|
+
*
|
|
2572
|
+
* @example
|
|
2573
|
+
* ```typescript
|
|
2574
|
+
* const batchedEmbedder = createBatchedEmbedder({
|
|
2575
|
+
* batchSize: 20,
|
|
2576
|
+
* embedBatch: async (texts) => {
|
|
2577
|
+
* const response = await openai.embeddings.create({
|
|
2578
|
+
* model: 'text-embedding-3-small',
|
|
2579
|
+
* input: texts,
|
|
2580
|
+
* });
|
|
2581
|
+
* return response.data.map(d => d.embedding);
|
|
2582
|
+
* },
|
|
2583
|
+
* maxWaitMs: 50,
|
|
2584
|
+
* });
|
|
2585
|
+
*
|
|
2586
|
+
* // Use the embedder
|
|
2587
|
+
* const embedding = await batchedEmbedder.embed("Hello world");
|
|
2588
|
+
*
|
|
2589
|
+
* // Clean up when done
|
|
2590
|
+
* batchedEmbedder.dispose();
|
|
2591
|
+
* ```
|
|
2592
|
+
*/
|
|
2593
|
+
declare function createBatchedEmbedder(config: {
|
|
2594
|
+
batchSize?: number;
|
|
2595
|
+
embedBatch: (texts: string[]) => Promise<Embedding[]>;
|
|
2596
|
+
maxWaitMs?: number;
|
|
2597
|
+
}): BatchedEmbedder;
|
|
2598
|
+
|
|
2599
|
+
/**
|
|
2600
|
+
* Approximate Nearest Neighbor (ANN) Index for Semantic Cache
|
|
2601
|
+
*
|
|
2602
|
+
* Provides pluggable vector search backends for efficient similarity lookups.
|
|
2603
|
+
* Includes a brute-force exact search and a VP-tree (Vantage Point Tree) for
|
|
2604
|
+
* fast approximate nearest neighbor queries.
|
|
2605
|
+
*
|
|
2606
|
+
* @example
|
|
2607
|
+
* ```typescript
|
|
2608
|
+
* import { createSemanticCache } from '@directive-run/ai';
|
|
2609
|
+
* import { createVPTreeIndex } from '@directive-run/ai';
|
|
2610
|
+
*
|
|
2611
|
+
* const index = createVPTreeIndex();
|
|
2612
|
+
*
|
|
2613
|
+
* const cache = createSemanticCache({
|
|
2614
|
+
* embedder: myEmbedder,
|
|
2615
|
+
* annIndex: index,
|
|
2616
|
+
* });
|
|
2617
|
+
* ```
|
|
2618
|
+
*/
|
|
2619
|
+
|
|
2620
|
+
/** Search result from an ANN index */
|
|
2621
|
+
interface ANNSearchResult {
|
|
2622
|
+
/** ID of the matched item */
|
|
2623
|
+
id: number;
|
|
2624
|
+
/** Similarity score (0-1, higher is more similar) */
|
|
2625
|
+
similarity: number;
|
|
2626
|
+
}
|
|
2627
|
+
/** ANN Index interface - pluggable vector search backend */
|
|
2628
|
+
interface ANNIndex {
|
|
2629
|
+
/** Add a vector to the index */
|
|
2630
|
+
add(id: number, embedding: Embedding): void;
|
|
2631
|
+
/** Remove a vector from the index */
|
|
2632
|
+
remove(id: number): void;
|
|
2633
|
+
/** Search for the k nearest neighbors */
|
|
2634
|
+
search(query: Embedding, k: number, threshold?: number): ANNSearchResult[];
|
|
2635
|
+
/** Rebuild the index (call after batch additions) */
|
|
2636
|
+
rebuild(): void;
|
|
2637
|
+
/** Get the number of indexed vectors */
|
|
2638
|
+
size(): number;
|
|
2639
|
+
/** Clear the index */
|
|
2640
|
+
clear(): void;
|
|
2641
|
+
/** Check if the index needs to be rebuilt (e.g., after additions/removals) */
|
|
2642
|
+
needsRebuild(): boolean;
|
|
2643
|
+
}
|
|
2644
|
+
/**
|
|
2645
|
+
* Create a brute-force exact search index.
|
|
2646
|
+
*
|
|
2647
|
+
* O(n) search, O(1) add/remove. Best for small collections (< 10,000 vectors).
|
|
2648
|
+
*
|
|
2649
|
+
* @example
|
|
2650
|
+
* ```typescript
|
|
2651
|
+
* const index = createBruteForceIndex();
|
|
2652
|
+
* index.add(0, [0.1, 0.2, 0.3]);
|
|
2653
|
+
* index.add(1, [0.4, 0.5, 0.6]);
|
|
2654
|
+
*
|
|
2655
|
+
* const results = index.search([0.1, 0.2, 0.3], 1);
|
|
2656
|
+
* // [{ id: 0, similarity: 1.0 }]
|
|
2657
|
+
* ```
|
|
2658
|
+
*/
|
|
2659
|
+
declare function createBruteForceIndex(): ANNIndex;
|
|
2660
|
+
/** VP-Tree index configuration */
|
|
2661
|
+
interface VPTreeIndexConfig {
|
|
2662
|
+
/** Optional random number generator for deterministic builds. Returns a number in [0, 1). */
|
|
2663
|
+
random?: () => number;
|
|
2664
|
+
}
|
|
2665
|
+
/**
|
|
2666
|
+
* Create a VP-Tree (Vantage Point Tree) index for efficient approximate nearest neighbor search.
|
|
2667
|
+
*
|
|
2668
|
+
* O(log n) search on average, requires rebuild after batch additions.
|
|
2669
|
+
* Best for medium collections (1,000 - 100,000 vectors).
|
|
2670
|
+
*
|
|
2671
|
+
* @example
|
|
2672
|
+
* ```typescript
|
|
2673
|
+
* const index = createVPTreeIndex();
|
|
2674
|
+
*
|
|
2675
|
+
* // Add vectors
|
|
2676
|
+
* for (let i = 0; i < embeddings.length; i++) {
|
|
2677
|
+
* index.add(i, embeddings[i]);
|
|
2678
|
+
* }
|
|
2679
|
+
*
|
|
2680
|
+
* // Build the tree
|
|
2681
|
+
* index.rebuild();
|
|
2682
|
+
*
|
|
2683
|
+
* // Search
|
|
2684
|
+
* const results = index.search(queryEmbedding, 5, 0.9);
|
|
2685
|
+
* ```
|
|
2686
|
+
*/
|
|
2687
|
+
declare function createVPTreeIndex(vpConfig?: VPTreeIndexConfig): ANNIndex;
|
|
2688
|
+
|
|
2689
|
+
/**
|
|
2690
|
+
* Streaming Channel for Agent-to-Agent Communication
|
|
2691
|
+
*
|
|
2692
|
+
* Provides AsyncIterable-based streaming for large data transfers between agents.
|
|
2693
|
+
* Supports backpressure, buffering, and graceful termination.
|
|
2694
|
+
*
|
|
2695
|
+
* @example
|
|
2696
|
+
* ```typescript
|
|
2697
|
+
* import { createStreamChannel } from '@directive-run/ai';
|
|
2698
|
+
*
|
|
2699
|
+
* // Producer side
|
|
2700
|
+
* const channel = createStreamChannel<string>({ bufferSize: 100 });
|
|
2701
|
+
*
|
|
2702
|
+
* // Send data
|
|
2703
|
+
* await channel.send('chunk 1');
|
|
2704
|
+
* await channel.send('chunk 2');
|
|
2705
|
+
* channel.end(); // Signal completion
|
|
2706
|
+
*
|
|
2707
|
+
* // Consumer side (async iteration)
|
|
2708
|
+
* for await (const chunk of channel) {
|
|
2709
|
+
* console.log(chunk);
|
|
2710
|
+
* }
|
|
2711
|
+
* ```
|
|
2712
|
+
*/
|
|
2713
|
+
/** Stream channel configuration */
|
|
2714
|
+
interface StreamChannelConfig {
|
|
2715
|
+
/** Maximum buffer size before backpressure is applied (default: 100) */
|
|
2716
|
+
bufferSize?: number;
|
|
2717
|
+
/** Channel name for debugging */
|
|
2718
|
+
name?: string;
|
|
2719
|
+
}
|
|
2720
|
+
/** Stream channel state */
|
|
2721
|
+
type StreamChannelState = "open" | "closed" | "error";
|
|
2722
|
+
/** Stream channel instance */
|
|
2723
|
+
interface StreamChannel<T> extends AsyncIterable<T> {
|
|
2724
|
+
/** Send a value to the channel. Resolves when consumed or buffered. */
|
|
2725
|
+
send(value: T): Promise<void>;
|
|
2726
|
+
/** Signal that no more values will be sent */
|
|
2727
|
+
end(): void;
|
|
2728
|
+
/** Signal an error and terminate the stream */
|
|
2729
|
+
error(err: Error): void;
|
|
2730
|
+
/** Get the current state */
|
|
2731
|
+
getState(): StreamChannelState;
|
|
2732
|
+
/** Get the number of buffered items */
|
|
2733
|
+
bufferedCount(): number;
|
|
2734
|
+
}
|
|
2735
|
+
/** Bidirectional stream between two agents */
|
|
2736
|
+
interface BidirectionalStream<TSend, TReceive> {
|
|
2737
|
+
/** Send a value to the remote end */
|
|
2738
|
+
send(value: TSend): Promise<void>;
|
|
2739
|
+
/** Iterate over received values */
|
|
2740
|
+
receive: AsyncIterable<TReceive>;
|
|
2741
|
+
/** Close both directions */
|
|
2742
|
+
close(): void;
|
|
2743
|
+
}
|
|
2744
|
+
/**
|
|
2745
|
+
* Create a stream channel for async data transfer.
|
|
2746
|
+
*
|
|
2747
|
+
* Implements proper backpressure: `send()` will pause when the buffer is full
|
|
2748
|
+
* and resume when the consumer catches up.
|
|
2749
|
+
*
|
|
2750
|
+
* @example
|
|
2751
|
+
* ```typescript
|
|
2752
|
+
* const channel = createStreamChannel<{ token: string }>();
|
|
2753
|
+
*
|
|
2754
|
+
* // Producer (e.g., LLM streaming response)
|
|
2755
|
+
* (async () => {
|
|
2756
|
+
* for (const token of tokens) {
|
|
2757
|
+
* await channel.send({ token });
|
|
2758
|
+
* }
|
|
2759
|
+
* channel.end();
|
|
2760
|
+
* })();
|
|
2761
|
+
*
|
|
2762
|
+
* // Consumer (e.g., downstream agent)
|
|
2763
|
+
* for await (const chunk of channel) {
|
|
2764
|
+
* process.stdout.write(chunk.token);
|
|
2765
|
+
* }
|
|
2766
|
+
* ```
|
|
2767
|
+
*/
|
|
2768
|
+
declare function createStreamChannel<T>(config?: StreamChannelConfig): StreamChannel<T>;
|
|
2769
|
+
/**
|
|
2770
|
+
* Create a bidirectional stream channel for two-way communication between agents.
|
|
2771
|
+
*
|
|
2772
|
+
* @example
|
|
2773
|
+
* ```typescript
|
|
2774
|
+
* const { sideA, sideB } = createBidirectionalStream<string, string>();
|
|
2775
|
+
*
|
|
2776
|
+
* // Agent A sends and receives
|
|
2777
|
+
* await sideA.send('question?');
|
|
2778
|
+
* for await (const reply of sideA.receive) {
|
|
2779
|
+
* console.log('A got:', reply);
|
|
2780
|
+
* }
|
|
2781
|
+
*
|
|
2782
|
+
* // Agent B receives and responds
|
|
2783
|
+
* for await (const msg of sideB.receive) {
|
|
2784
|
+
* await sideB.send(`reply to: ${msg}`);
|
|
2785
|
+
* }
|
|
2786
|
+
* ```
|
|
2787
|
+
*/
|
|
2788
|
+
declare function createBidirectionalStream<TA, TB>(config?: StreamChannelConfig): {
|
|
2789
|
+
sideA: BidirectionalStream<TA, TB>;
|
|
2790
|
+
sideB: BidirectionalStream<TB, TA>;
|
|
2791
|
+
};
|
|
2792
|
+
/**
|
|
2793
|
+
* Pipe one stream channel through a transform function into another.
|
|
2794
|
+
*
|
|
2795
|
+
* @example
|
|
2796
|
+
* ```typescript
|
|
2797
|
+
* const input = createStreamChannel<string>();
|
|
2798
|
+
* const output = createStreamChannel<number>();
|
|
2799
|
+
*
|
|
2800
|
+
* pipeThrough(input, output, (chunk) => chunk.length);
|
|
2801
|
+
* ```
|
|
2802
|
+
*/
|
|
2803
|
+
declare function pipeThrough<TIn, TOut>(source: AsyncIterable<TIn>, destination: StreamChannel<TOut>, transform: (value: TIn) => TOut | Promise<TOut>): Promise<void>;
|
|
2804
|
+
/**
|
|
2805
|
+
* Merge multiple async iterables into a single stream.
|
|
2806
|
+
* Values are emitted as soon as they arrive from any source.
|
|
2807
|
+
*
|
|
2808
|
+
* Note: The internal buffer is capped at 10,000 items. Values exceeding this
|
|
2809
|
+
* limit are dropped. For high-throughput scenarios, ensure the consumer keeps up
|
|
2810
|
+
* or use bounded `StreamChannel` sources.
|
|
2811
|
+
*
|
|
2812
|
+
* @example
|
|
2813
|
+
* ```typescript
|
|
2814
|
+
* const merged = mergeStreams(channel1, channel2, channel3);
|
|
2815
|
+
* for await (const value of merged) {
|
|
2816
|
+
* console.log(value);
|
|
2817
|
+
* }
|
|
2818
|
+
* ```
|
|
2819
|
+
*/
|
|
2820
|
+
declare function mergeStreams<T>(...sources: AsyncIterable<T>[]): AsyncIterable<T>;
|
|
2821
|
+
|
|
2822
|
+
/**
|
|
2823
|
+
* P2: Intelligent Retry — HTTP-status-aware retry wrapper for AgentRunner.
|
|
2824
|
+
*
|
|
2825
|
+
* Respects 429 Retry-After headers, uses exponential backoff with jitter for 503,
|
|
2826
|
+
* and never retries client errors (400/401/403/404/422).
|
|
2827
|
+
*
|
|
2828
|
+
* @module
|
|
2829
|
+
*
|
|
2830
|
+
* @example
|
|
2831
|
+
* ```typescript
|
|
2832
|
+
* import { withRetry, RetryExhaustedError } from '@directive-run/ai';
|
|
2833
|
+
*
|
|
2834
|
+
* const runner = withRetry(baseRunner, {
|
|
2835
|
+
* maxRetries: 3,
|
|
2836
|
+
* baseDelayMs: 1000,
|
|
2837
|
+
* maxDelayMs: 30000,
|
|
2838
|
+
* onRetry: (attempt, error, delayMs) => {
|
|
2839
|
+
* console.log(`Retry ${attempt} in ${delayMs}ms: ${error.message}`);
|
|
2840
|
+
* },
|
|
2841
|
+
* });
|
|
2842
|
+
*
|
|
2843
|
+
* try {
|
|
2844
|
+
* const result = await runner(agent, input);
|
|
2845
|
+
* } catch (err) {
|
|
2846
|
+
* if (err instanceof RetryExhaustedError) {
|
|
2847
|
+
* console.error(`All ${err.retryCount} retries failed:`, err.lastError);
|
|
2848
|
+
* }
|
|
2849
|
+
* }
|
|
2850
|
+
* ```
|
|
2851
|
+
*/
|
|
2852
|
+
|
|
2853
|
+
/**
|
|
2854
|
+
* Configuration for the intelligent retry wrapper.
|
|
2855
|
+
*
|
|
2856
|
+
* @example
|
|
2857
|
+
* ```typescript
|
|
2858
|
+
* const config: RetryConfig = {
|
|
2859
|
+
* maxRetries: 3,
|
|
2860
|
+
* baseDelayMs: 1000,
|
|
2861
|
+
* maxDelayMs: 30000,
|
|
2862
|
+
* isRetryable: (error) => !error.message.includes("invalid API key"),
|
|
2863
|
+
* onRetry: (attempt, error, delayMs) => {
|
|
2864
|
+
* console.log(`Retry ${attempt} after ${delayMs}ms: ${error.message}`);
|
|
2865
|
+
* },
|
|
2866
|
+
* };
|
|
2867
|
+
* ```
|
|
2868
|
+
*/
|
|
2869
|
+
interface RetryConfig {
|
|
2870
|
+
/** Maximum number of retry attempts (not counting the initial call). @default 3 */
|
|
2871
|
+
maxRetries?: number;
|
|
2872
|
+
/** Base delay in ms for exponential backoff. @default 1000 */
|
|
2873
|
+
baseDelayMs?: number;
|
|
2874
|
+
/** Maximum delay in ms (caps exponential growth). @default 30000 */
|
|
2875
|
+
maxDelayMs?: number;
|
|
2876
|
+
/** Custom predicate — return `false` to skip retry for specific errors. Called after the built-in HTTP status check. */
|
|
2877
|
+
isRetryable?: (error: Error) => boolean;
|
|
2878
|
+
/** Called before each retry wait. Useful for logging or metrics. */
|
|
2879
|
+
onRetry?: (attempt: number, error: Error, delayMs: number) => void;
|
|
2880
|
+
}
|
|
2881
|
+
/** Error enriched with retry metadata, thrown when all retries are exhausted. */
|
|
2882
|
+
declare class RetryExhaustedError extends Error {
|
|
2883
|
+
readonly retryCount: number;
|
|
2884
|
+
readonly lastError: Error;
|
|
2885
|
+
constructor(retryCount: number, lastError: Error);
|
|
2886
|
+
}
|
|
2887
|
+
/**
|
|
2888
|
+
* Extract HTTP status code from error message or error properties.
|
|
2889
|
+
*
|
|
2890
|
+
* Checks `error.status` / `error.statusCode` properties first, then falls back
|
|
2891
|
+
* to matching common error message patterns like "request failed: 429" or "HTTP 503".
|
|
2892
|
+
*/
|
|
2893
|
+
declare function parseHttpStatus(error: Error): number | null;
|
|
2894
|
+
/**
|
|
2895
|
+
* Extract Retry-After value (in ms) from error message.
|
|
2896
|
+
*
|
|
2897
|
+
* Per HTTP spec, `Retry-After` numeric values are always in seconds.
|
|
2898
|
+
* Returns the value converted to milliseconds.
|
|
2899
|
+
*/
|
|
2900
|
+
declare function parseRetryAfter(error: Error): number | null;
|
|
2901
|
+
/**
|
|
2902
|
+
* Wrap an AgentRunner with intelligent retry logic.
|
|
2903
|
+
*
|
|
2904
|
+
* @example
|
|
2905
|
+
* ```typescript
|
|
2906
|
+
* const runner = withRetry(baseRunner, {
|
|
2907
|
+
* maxRetries: 3,
|
|
2908
|
+
* baseDelayMs: 1000,
|
|
2909
|
+
* onRetry: (attempt, error, delayMs) => {
|
|
2910
|
+
* console.log(`Retry ${attempt} after ${delayMs}ms: ${error.message}`);
|
|
2911
|
+
* },
|
|
2912
|
+
* });
|
|
2913
|
+
* ```
|
|
2914
|
+
*/
|
|
2915
|
+
declare function withRetry(runner: AgentRunner, config?: RetryConfig): AgentRunner;
|
|
2916
|
+
|
|
2917
|
+
/**
|
|
2918
|
+
* P0: Provider Fallback Chains — Automatic failover across multiple AgentRunners.
|
|
2919
|
+
*
|
|
2920
|
+
* Tries runners in order, moving to the next on failure.
|
|
2921
|
+
* Composes naturally with {@link withRetry} (each runner can have its own retry policy).
|
|
2922
|
+
*
|
|
2923
|
+
* @module
|
|
2924
|
+
*
|
|
2925
|
+
* @example
|
|
2926
|
+
* ```typescript
|
|
2927
|
+
* import { withFallback, withRetry, AllProvidersFailedError } from '@directive-run/ai';
|
|
2928
|
+
*
|
|
2929
|
+
* const runner = withFallback([
|
|
2930
|
+
* withRetry(openaiRunner, { maxRetries: 2 }),
|
|
2931
|
+
* withRetry(anthropicRunner, { maxRetries: 2 }),
|
|
2932
|
+
* ollamaRunner,
|
|
2933
|
+
* ], {
|
|
2934
|
+
* onFallback: (from, to, error) => {
|
|
2935
|
+
* console.log(`Provider ${from} failed, trying ${to}: ${error.message}`);
|
|
2936
|
+
* },
|
|
2937
|
+
* });
|
|
2938
|
+
*
|
|
2939
|
+
* try {
|
|
2940
|
+
* const result = await runner(agent, input);
|
|
2941
|
+
* } catch (err) {
|
|
2942
|
+
* if (err instanceof AllProvidersFailedError) {
|
|
2943
|
+
* console.error(`All ${err.errors.length} providers failed`);
|
|
2944
|
+
* }
|
|
2945
|
+
* }
|
|
2946
|
+
* ```
|
|
2947
|
+
*/
|
|
2948
|
+
|
|
2949
|
+
interface FallbackConfig {
|
|
2950
|
+
/** Custom predicate to decide whether to fall back on a given error. Default: always fall back. */
|
|
2951
|
+
shouldFallback?: (error: Error) => boolean;
|
|
2952
|
+
/** Called when falling back from one provider to the next. */
|
|
2953
|
+
onFallback?: (fromIndex: number, toIndex: number, error: Error) => void;
|
|
2954
|
+
}
|
|
2955
|
+
/** Error thrown when all providers in the fallback chain have failed. */
|
|
2956
|
+
declare class AllProvidersFailedError extends Error {
|
|
2957
|
+
readonly errors: Error[];
|
|
2958
|
+
constructor(errors: Error[]);
|
|
2959
|
+
}
|
|
2960
|
+
/**
|
|
2961
|
+
* Wrap multiple AgentRunners into a fallback chain.
|
|
2962
|
+
*
|
|
2963
|
+
* @example
|
|
2964
|
+
* ```typescript
|
|
2965
|
+
* const runner = withFallback([
|
|
2966
|
+
* withRetry(openaiRunner, { maxRetries: 2 }),
|
|
2967
|
+
* withRetry(anthropicRunner, { maxRetries: 2 }),
|
|
2968
|
+
* ollamaRunner,
|
|
2969
|
+
* ], {
|
|
2970
|
+
* onFallback: (from, to, error) => {
|
|
2971
|
+
* console.log(`Falling back from provider ${from} to ${to}: ${error.message}`);
|
|
2972
|
+
* },
|
|
2973
|
+
* });
|
|
2974
|
+
* ```
|
|
2975
|
+
*/
|
|
2976
|
+
declare function withFallback(runners: AgentRunner[], config?: FallbackConfig): AgentRunner;
|
|
2977
|
+
|
|
2978
|
+
/**
|
|
2979
|
+
* P1: Cost Budget Guards — Pre-call estimation + rolling budget windows.
|
|
2980
|
+
*
|
|
2981
|
+
* Prevents runaway LLM costs by estimating costs before each call
|
|
2982
|
+
* and tracking actual costs after each call. Supports per-call limits
|
|
2983
|
+
* and rolling time-window budgets (hourly, daily).
|
|
2984
|
+
*
|
|
2985
|
+
* @module
|
|
2986
|
+
*
|
|
2987
|
+
* @example
|
|
2988
|
+
* ```typescript
|
|
2989
|
+
* import { withBudget, BudgetExceededError } from '@directive-run/ai';
|
|
2990
|
+
* import type { BudgetRunner } from '@directive-run/ai';
|
|
2991
|
+
*
|
|
2992
|
+
* const pricing = { inputPerMillion: 3, outputPerMillion: 15 };
|
|
2993
|
+
*
|
|
2994
|
+
* const runner = withBudget(baseRunner, {
|
|
2995
|
+
* maxCostPerCall: 0.10,
|
|
2996
|
+
* pricing,
|
|
2997
|
+
* budgets: [
|
|
2998
|
+
* { window: "hour", maxCost: 5.00, pricing },
|
|
2999
|
+
* { window: "day", maxCost: 50.00, pricing },
|
|
3000
|
+
* ],
|
|
3001
|
+
* });
|
|
3002
|
+
*
|
|
3003
|
+
* // Check spending via escape hatch
|
|
3004
|
+
* const spent = (runner as BudgetRunner).getSpent("hour");
|
|
3005
|
+
* if (spent > 4.00) {
|
|
3006
|
+
* console.warn("Approaching hourly budget limit!");
|
|
3007
|
+
* }
|
|
3008
|
+
* ```
|
|
3009
|
+
*/
|
|
3010
|
+
|
|
3011
|
+
/**
|
|
3012
|
+
* Token pricing for a specific model or provider.
|
|
3013
|
+
*
|
|
3014
|
+
* @example
|
|
3015
|
+
* ```typescript
|
|
3016
|
+
* // GPT-4o pricing (as of 2024)
|
|
3017
|
+
* const gpt4oPricing: TokenPricing = {
|
|
3018
|
+
* inputPerMillion: 5,
|
|
3019
|
+
* outputPerMillion: 15,
|
|
3020
|
+
* };
|
|
3021
|
+
* ```
|
|
3022
|
+
*/
|
|
3023
|
+
interface TokenPricing {
|
|
3024
|
+
/** Cost per million input tokens (in dollars). */
|
|
3025
|
+
inputPerMillion: number;
|
|
3026
|
+
/** Cost per million output tokens (in dollars). */
|
|
3027
|
+
outputPerMillion: number;
|
|
3028
|
+
}
|
|
3029
|
+
/**
|
|
3030
|
+
* Rolling budget window configuration.
|
|
3031
|
+
*
|
|
3032
|
+
* Each window tracks cost independently, preventing double-counting
|
|
3033
|
+
* when multiple windows are configured.
|
|
3034
|
+
*
|
|
3035
|
+
* @example
|
|
3036
|
+
* ```typescript
|
|
3037
|
+
* const hourlyBudget: BudgetWindow = {
|
|
3038
|
+
* window: "hour",
|
|
3039
|
+
* maxCost: 5.00,
|
|
3040
|
+
* pricing: { inputPerMillion: 3, outputPerMillion: 15 },
|
|
3041
|
+
* };
|
|
3042
|
+
* ```
|
|
3043
|
+
*/
|
|
3044
|
+
interface BudgetWindow {
|
|
3045
|
+
/** Time window for the budget. */
|
|
3046
|
+
window: "hour" | "day";
|
|
3047
|
+
/** Maximum cost in dollars for this window. */
|
|
3048
|
+
maxCost: number;
|
|
3049
|
+
/** Token pricing for cost calculation within this window. */
|
|
3050
|
+
pricing: TokenPricing;
|
|
3051
|
+
}
|
|
3052
|
+
interface BudgetConfig {
|
|
3053
|
+
/** Maximum estimated cost per individual call. */
|
|
3054
|
+
maxCostPerCall?: number;
|
|
3055
|
+
/** Rolling budget windows. */
|
|
3056
|
+
budgets?: BudgetWindow[];
|
|
3057
|
+
/** Pricing used for per-call estimation (when maxCostPerCall is set). */
|
|
3058
|
+
pricing?: TokenPricing;
|
|
3059
|
+
/** Approximate characters per token for input estimation. @default 4 */
|
|
3060
|
+
charsPerToken?: number;
|
|
3061
|
+
/**
|
|
3062
|
+
* Multiplier for estimated output tokens relative to input tokens.
|
|
3063
|
+
* For summarization tasks, use a value less than 1 (e.g., 0.3).
|
|
3064
|
+
* For generation tasks, use a value greater than 1 (e.g., 3.0).
|
|
3065
|
+
* @default 1.0
|
|
3066
|
+
*/
|
|
3067
|
+
estimatedOutputMultiplier?: number;
|
|
3068
|
+
/** Called when a budget check fails (before throwing). */
|
|
3069
|
+
onBudgetExceeded?: (details: BudgetExceededDetails) => void;
|
|
3070
|
+
}
|
|
3071
|
+
interface BudgetExceededDetails {
|
|
3072
|
+
estimated: number;
|
|
3073
|
+
remaining: number;
|
|
3074
|
+
window: "per-call" | "hour" | "day";
|
|
3075
|
+
}
|
|
3076
|
+
/** Error thrown when a budget limit is exceeded. */
|
|
3077
|
+
declare class BudgetExceededError extends Error {
|
|
3078
|
+
readonly estimated: number;
|
|
3079
|
+
readonly remaining: number;
|
|
3080
|
+
readonly window: "per-call" | "hour" | "day";
|
|
3081
|
+
constructor(details: BudgetExceededDetails);
|
|
3082
|
+
}
|
|
3083
|
+
/**
|
|
3084
|
+
* Wrap an AgentRunner with cost budget guards.
|
|
3085
|
+
*
|
|
3086
|
+
* @example
|
|
3087
|
+
* ```typescript
|
|
3088
|
+
* const runner = withBudget(baseRunner, {
|
|
3089
|
+
* maxCostPerCall: 0.10,
|
|
3090
|
+
* pricing: { inputPerMillion: 3, outputPerMillion: 15 },
|
|
3091
|
+
* budgets: [
|
|
3092
|
+
* { window: "hour", maxCost: 5.00, pricing: { inputPerMillion: 3, outputPerMillion: 15 } },
|
|
3093
|
+
* { window: "day", maxCost: 50.00, pricing: { inputPerMillion: 3, outputPerMillion: 15 } },
|
|
3094
|
+
* ],
|
|
3095
|
+
* });
|
|
3096
|
+
* ```
|
|
3097
|
+
*/
|
|
3098
|
+
declare function withBudget(runner: AgentRunner, config: BudgetConfig): BudgetRunner;
|
|
3099
|
+
/** Helper type for accessing budget runner's getSpent method. */
|
|
3100
|
+
type BudgetRunner = AgentRunner & {
|
|
3101
|
+
/** Get cost spent within a rolling window. */
|
|
3102
|
+
getSpent(window: "hour" | "day"): number;
|
|
3103
|
+
};
|
|
3104
|
+
|
|
3105
|
+
/**
|
|
3106
|
+
* P3: Smart Model Selection — Rule-based model routing for AgentRunner.
|
|
3107
|
+
*
|
|
3108
|
+
* Route simple tasks to cheaper models and complex tasks to expensive ones,
|
|
3109
|
+
* reducing cost without sacrificing quality where it matters.
|
|
3110
|
+
*
|
|
3111
|
+
* Rules are evaluated in order; the first match wins. If no rule matches,
|
|
3112
|
+
* the agent's original model is used unchanged.
|
|
3113
|
+
*
|
|
3114
|
+
* Accepts either a {@link ModelSelectionConfig} object (recommended) or
|
|
3115
|
+
* a bare `ModelRule[]` array for convenience.
|
|
3116
|
+
*
|
|
3117
|
+
* @module
|
|
3118
|
+
*
|
|
3119
|
+
* @example Config object (recommended)
|
|
3120
|
+
* ```typescript
|
|
3121
|
+
* import { withModelSelection, byInputLength, byAgentName, byPattern } from '@directive-run/ai';
|
|
3122
|
+
*
|
|
3123
|
+
* const runner = withModelSelection(baseRunner, {
|
|
3124
|
+
* rules: [
|
|
3125
|
+
* byInputLength(200, "gpt-4o-mini"),
|
|
3126
|
+
* byAgentName("summarizer", "gpt-4o-mini"),
|
|
3127
|
+
* byPattern(/classify|categorize/i, "gpt-4o-mini"),
|
|
3128
|
+
* ],
|
|
3129
|
+
* onModelSelected: (original, selected) => {
|
|
3130
|
+
* if (original !== selected) console.log(`Routed ${original} → ${selected}`);
|
|
3131
|
+
* },
|
|
3132
|
+
* });
|
|
3133
|
+
* ```
|
|
3134
|
+
*
|
|
3135
|
+
* @example Shorthand (rules array)
|
|
3136
|
+
* ```typescript
|
|
3137
|
+
* const runner = withModelSelection(baseRunner, [
|
|
3138
|
+
* byInputLength(200, "gpt-4o-mini"),
|
|
3139
|
+
* ]);
|
|
3140
|
+
* ```
|
|
3141
|
+
*/
|
|
3142
|
+
|
|
3143
|
+
/** A single model selection rule. First match wins. */
|
|
3144
|
+
interface ModelRule {
|
|
3145
|
+
/** Predicate that determines if this rule applies. */
|
|
3146
|
+
match: (agent: AgentLike, input: string) => boolean;
|
|
3147
|
+
/** The model to use when this rule matches. */
|
|
3148
|
+
model: string;
|
|
3149
|
+
}
|
|
3150
|
+
/** Configuration for model selection. */
|
|
3151
|
+
interface ModelSelectionConfig {
|
|
3152
|
+
/** Rules evaluated in order. First match wins. */
|
|
3153
|
+
rules: ModelRule[];
|
|
3154
|
+
/** Called when a model is selected (even if it matches the original). */
|
|
3155
|
+
onModelSelected?: (originalModel: string | undefined, selectedModel: string | undefined) => void;
|
|
3156
|
+
}
|
|
3157
|
+
/**
|
|
3158
|
+
* Match when input character length is at most `maxLength`.
|
|
3159
|
+
*
|
|
3160
|
+
* @example
|
|
3161
|
+
* ```typescript
|
|
3162
|
+
* byInputLength(500, "gpt-4o-mini") // inputs up to 500 chars use mini
|
|
3163
|
+
* ```
|
|
3164
|
+
*/
|
|
3165
|
+
declare function byInputLength(maxLength: number, model: string): ModelRule;
|
|
3166
|
+
/**
|
|
3167
|
+
* Match by agent name (exact string match).
|
|
3168
|
+
*
|
|
3169
|
+
* @example
|
|
3170
|
+
* ```typescript
|
|
3171
|
+
* byAgentName("classifier", "gpt-4o-mini")
|
|
3172
|
+
* ```
|
|
3173
|
+
*/
|
|
3174
|
+
declare function byAgentName(name: string, model: string): ModelRule;
|
|
3175
|
+
/**
|
|
3176
|
+
* Match by regex pattern on the input text.
|
|
3177
|
+
*
|
|
3178
|
+
* @example
|
|
3179
|
+
* ```typescript
|
|
3180
|
+
* byPattern(/classify|categorize/i, "gpt-4o-mini") // classification prompts use mini
|
|
3181
|
+
* ```
|
|
3182
|
+
*/
|
|
3183
|
+
declare function byPattern(pattern: RegExp, model: string): ModelRule;
|
|
3184
|
+
/**
|
|
3185
|
+
* Wrap an AgentRunner with rule-based model selection.
|
|
3186
|
+
*
|
|
3187
|
+
* Rules are evaluated in order. The first match wins and overrides `agent.model`.
|
|
3188
|
+
* If no rule matches, the agent's original model is used.
|
|
3189
|
+
*
|
|
3190
|
+
* Accepts either a config object or a bare `ModelRule[]` for convenience.
|
|
3191
|
+
*
|
|
3192
|
+
* @example
|
|
3193
|
+
* ```typescript
|
|
3194
|
+
* // Config object (recommended)
|
|
3195
|
+
* const runner = withModelSelection(baseRunner, {
|
|
3196
|
+
* rules: [
|
|
3197
|
+
* byInputLength(200, "gpt-4o-mini"),
|
|
3198
|
+
* byAgentName("summarizer", "gpt-4o-mini"),
|
|
3199
|
+
* byPattern(/classify|categorize/i, "gpt-4o-mini"),
|
|
3200
|
+
* ],
|
|
3201
|
+
* onModelSelected: (original, selected) => {
|
|
3202
|
+
* console.log(`Model: ${original} → ${selected}`);
|
|
3203
|
+
* },
|
|
3204
|
+
* });
|
|
3205
|
+
*
|
|
3206
|
+
* // Shorthand (rules array)
|
|
3207
|
+
* const runner = withModelSelection(baseRunner, [
|
|
3208
|
+
* byInputLength(200, "gpt-4o-mini"),
|
|
3209
|
+
* ]);
|
|
3210
|
+
* ```
|
|
3211
|
+
*/
|
|
3212
|
+
declare function withModelSelection(runner: AgentRunner, configOrRules: ModelSelectionConfig | ModelRule[]): AgentRunner;
|
|
3213
|
+
|
|
3214
|
+
/**
|
|
3215
|
+
* P6: Structured Outputs — Schema validation with auto-retry for LLM responses.
|
|
3216
|
+
*
|
|
3217
|
+
* Turns unreliable text output into typed, validated data. Appends JSON schema
|
|
3218
|
+
* instructions to the system prompt and retries with error feedback on parse failure.
|
|
3219
|
+
*
|
|
3220
|
+
* Works with any Zod-compatible schema (any object with a `safeParse` method).
|
|
3221
|
+
*
|
|
3222
|
+
* @module
|
|
3223
|
+
*
|
|
3224
|
+
* @example
|
|
3225
|
+
* ```typescript
|
|
3226
|
+
* import { z } from "zod";
|
|
3227
|
+
* import { withStructuredOutput, StructuredOutputError } from '@directive-run/ai';
|
|
3228
|
+
*
|
|
3229
|
+
* const SentimentSchema = z.object({
|
|
3230
|
+
* sentiment: z.enum(["positive", "negative", "neutral"]),
|
|
3231
|
+
* confidence: z.number().min(0).max(1),
|
|
3232
|
+
* });
|
|
3233
|
+
*
|
|
3234
|
+
* const runner = withStructuredOutput(baseRunner, {
|
|
3235
|
+
* schema: SentimentSchema,
|
|
3236
|
+
* maxRetries: 2,
|
|
3237
|
+
* });
|
|
3238
|
+
*
|
|
3239
|
+
* const result = await runner(agent, "Analyze: I love this product!");
|
|
3240
|
+
* // result.output is typed as { sentiment: string; confidence: number }
|
|
3241
|
+
* ```
|
|
3242
|
+
*/
|
|
3243
|
+
|
|
3244
|
+
/**
|
|
3245
|
+
* Zod-compatible schema duck type — any object with a `safeParse` method.
|
|
3246
|
+
*
|
|
3247
|
+
* This interface allows structured outputs to work with Zod, Valibot,
|
|
3248
|
+
* or any validation library that implements this pattern.
|
|
3249
|
+
*
|
|
3250
|
+
* @example
|
|
3251
|
+
* ```typescript
|
|
3252
|
+
* import { z } from "zod";
|
|
3253
|
+
*
|
|
3254
|
+
* // Zod schemas implement SafeParseable automatically
|
|
3255
|
+
* const schema = z.object({ name: z.string() });
|
|
3256
|
+
*
|
|
3257
|
+
* // Custom schema
|
|
3258
|
+
* const custom: SafeParseable<{ name: string }> = {
|
|
3259
|
+
* safeParse(value) {
|
|
3260
|
+
* if (typeof value === "object" && value && "name" in value) {
|
|
3261
|
+
* return { success: true, data: value as { name: string } };
|
|
3262
|
+
* }
|
|
3263
|
+
* return { success: false, error: { message: "Missing name field" } };
|
|
3264
|
+
* },
|
|
3265
|
+
* };
|
|
3266
|
+
* ```
|
|
3267
|
+
*/
|
|
3268
|
+
interface SafeParseable<T = unknown> {
|
|
3269
|
+
safeParse(value: unknown): SafeParseResult<T>;
|
|
3270
|
+
/** Optional: schema description injected into the system prompt. */
|
|
3271
|
+
description?: string;
|
|
3272
|
+
}
|
|
3273
|
+
interface SafeParseResult<T> {
|
|
3274
|
+
success: boolean;
|
|
3275
|
+
data?: T;
|
|
3276
|
+
error?: {
|
|
3277
|
+
message?: string;
|
|
3278
|
+
issues?: Array<{
|
|
3279
|
+
message: string;
|
|
3280
|
+
}>;
|
|
3281
|
+
};
|
|
3282
|
+
}
|
|
3283
|
+
interface StructuredOutputConfig<T = unknown> {
|
|
3284
|
+
/** Zod-compatible schema with safeParse. */
|
|
3285
|
+
schema: SafeParseable<T>;
|
|
3286
|
+
/** Max retries on parse/validation failure. @default 2 */
|
|
3287
|
+
maxRetries?: number;
|
|
3288
|
+
/** Custom JSON extractor. Default: finds first `{...}` or `[...]` in output. */
|
|
3289
|
+
extractJson?: (output: string) => unknown;
|
|
3290
|
+
/** Schema description to inject into system prompt. Auto-derived from schema.description if available. */
|
|
3291
|
+
schemaDescription?: string;
|
|
3292
|
+
}
|
|
3293
|
+
/** Default JSON extractor — finds the first `{...}` or `[...]` in output. */
|
|
3294
|
+
declare function extractJsonFromOutput(output: string): unknown;
|
|
3295
|
+
/**
|
|
3296
|
+
* Wrap an AgentRunner with structured output parsing and validation.
|
|
3297
|
+
*
|
|
3298
|
+
* @example
|
|
3299
|
+
* ```typescript
|
|
3300
|
+
* import { z } from "zod";
|
|
3301
|
+
*
|
|
3302
|
+
* const SentimentSchema = z.object({
|
|
3303
|
+
* sentiment: z.enum(["positive", "negative", "neutral"]),
|
|
3304
|
+
* confidence: z.number().min(0).max(1),
|
|
3305
|
+
* });
|
|
3306
|
+
*
|
|
3307
|
+
* const runner = withStructuredOutput(baseRunner, {
|
|
3308
|
+
* schema: SentimentSchema,
|
|
3309
|
+
* maxRetries: 2,
|
|
3310
|
+
* });
|
|
3311
|
+
*
|
|
3312
|
+
* const result = await runner(agent, "Analyze: I love this product!");
|
|
3313
|
+
* // result.output is typed as { sentiment: string; confidence: number }
|
|
3314
|
+
* ```
|
|
3315
|
+
*/
|
|
3316
|
+
declare function withStructuredOutput<T = unknown>(runner: AgentRunner, config: StructuredOutputConfig<T>): AgentRunner;
|
|
3317
|
+
/** Error thrown when structured output parsing fails after all retries. */
|
|
3318
|
+
declare class StructuredOutputError extends Error {
|
|
3319
|
+
readonly lastResult: RunResult<unknown> | undefined;
|
|
3320
|
+
constructor(message: string, lastResult?: RunResult<unknown>);
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3323
|
+
/**
|
|
3324
|
+
* Agent Stack — Composition API for AI Adapters
|
|
3325
|
+
*
|
|
3326
|
+
* One factory that wires orchestrator, memory, circuit breaker, rate limiter,
|
|
3327
|
+
* streaming, multi-agent patterns, semantic cache, observability, OTLP export,
|
|
3328
|
+
* and communication bus with sensible defaults.
|
|
3329
|
+
*
|
|
3330
|
+
* @example Basic usage
|
|
3331
|
+
* ```typescript
|
|
3332
|
+
* import { createAgentStack, parallel } from '@directive-run/ai';
|
|
3333
|
+
*
|
|
3334
|
+
* const stack = createAgentStack({
|
|
3335
|
+
* runner: myAgentRunner,
|
|
3336
|
+
* agents: { move: { agent: moveAgent, capabilities: ["move"] } },
|
|
3337
|
+
* memory: { maxMessages: 30 },
|
|
3338
|
+
* circuitBreaker: { failureThreshold: 3 },
|
|
3339
|
+
* cache: { threshold: 0.98, maxSize: 200, ttlMs: 600_000 },
|
|
3340
|
+
* observability: { serviceName: "my-app" },
|
|
3341
|
+
* });
|
|
3342
|
+
*
|
|
3343
|
+
* const result = await stack.run("move", input);
|
|
3344
|
+
* ```
|
|
3345
|
+
*
|
|
3346
|
+
* @example Streaming
|
|
3347
|
+
* ```typescript
|
|
3348
|
+
* const stack = createAgentStack({
|
|
3349
|
+
* runner: myAgentRunner,
|
|
3350
|
+
* streaming: { runner: myStreamingAgentRunner },
|
|
3351
|
+
* agents: { chat: { agent: chatAgent, capabilities: ["chat"] } },
|
|
3352
|
+
* });
|
|
3353
|
+
*
|
|
3354
|
+
* const tokenStream = stack.stream("chat", "Hello!");
|
|
3355
|
+
* for await (const token of tokenStream) { process.stdout.write(token); }
|
|
3356
|
+
* const finalResult = await tokenStream.result;
|
|
3357
|
+
* ```
|
|
3358
|
+
*/
|
|
3359
|
+
|
|
3360
|
+
/** Callback-based streaming run function (e.g. for SSE-based LLM APIs) */
|
|
3361
|
+
type StreamingCallbackRunner = (agent: AgentLike, input: string, callbacks: {
|
|
3362
|
+
onToken?: (token: string) => void;
|
|
3363
|
+
onToolStart?: (tool: string, id: string, args: string) => void;
|
|
3364
|
+
onToolEnd?: (tool: string, id: string, result: string) => void;
|
|
3365
|
+
onMessage?: (message: Message$1) => void;
|
|
3366
|
+
signal?: AbortSignal;
|
|
3367
|
+
}) => Promise<RunResult<unknown>>;
|
|
3368
|
+
interface AgentStackConfig {
|
|
3369
|
+
/** Required: base runner for agent execution */
|
|
3370
|
+
runner: AgentRunner;
|
|
3371
|
+
/** Enables stack.stream() when provided */
|
|
3372
|
+
streaming?: {
|
|
3373
|
+
runner: StreamingCallbackRunner;
|
|
3374
|
+
};
|
|
3375
|
+
/** Agent registry — required for multi-agent patterns */
|
|
3376
|
+
agents?: AgentRegistry;
|
|
3377
|
+
/** Named execution patterns (parallel, sequential, supervisor) */
|
|
3378
|
+
patterns?: Record<string, ExecutionPattern>;
|
|
3379
|
+
memory?: {
|
|
3380
|
+
maxMessages?: number;
|
|
3381
|
+
preserveRecentCount?: number;
|
|
3382
|
+
} | AgentMemory;
|
|
3383
|
+
circuitBreaker?: CircuitBreakerConfig | CircuitBreaker;
|
|
3384
|
+
rateLimit?: {
|
|
3385
|
+
maxPerMinute: number;
|
|
3386
|
+
};
|
|
3387
|
+
cache?: {
|
|
3388
|
+
threshold?: number;
|
|
3389
|
+
maxSize?: number;
|
|
3390
|
+
ttlMs?: number;
|
|
3391
|
+
embedder?: EmbedderFn;
|
|
3392
|
+
} | SemanticCache;
|
|
3393
|
+
observability?: {
|
|
3394
|
+
serviceName: string;
|
|
3395
|
+
alerts?: AlertConfig[];
|
|
3396
|
+
} | ObservabilityInstance;
|
|
3397
|
+
otlp?: {
|
|
3398
|
+
endpoint: string;
|
|
3399
|
+
intervalMs?: number;
|
|
3400
|
+
onError?: (err: Error, type: "metrics" | "traces") => void;
|
|
3401
|
+
};
|
|
3402
|
+
/** @deprecated Use `messageBus` instead */
|
|
3403
|
+
bus?: {
|
|
3404
|
+
maxHistory?: number;
|
|
3405
|
+
} | MessageBus;
|
|
3406
|
+
/** Message bus for agent communication */
|
|
3407
|
+
messageBus?: {
|
|
3408
|
+
maxHistory?: number;
|
|
3409
|
+
} | MessageBus;
|
|
3410
|
+
guardrails?: {
|
|
3411
|
+
input?: Array<GuardrailFn<InputGuardrailData> | NamedGuardrail<InputGuardrailData>>;
|
|
3412
|
+
output?: Array<GuardrailFn<OutputGuardrailData> | NamedGuardrail<OutputGuardrailData>>;
|
|
3413
|
+
streaming?: StreamingGuardrail[];
|
|
3414
|
+
};
|
|
3415
|
+
maxTokenBudget?: number;
|
|
3416
|
+
/** Cost per million tokens for cost estimation */
|
|
3417
|
+
costPerMillionTokens?: number;
|
|
3418
|
+
/** @deprecated Use `costPerMillionTokens` */
|
|
3419
|
+
costRatePerMillion?: number;
|
|
3420
|
+
debug?: boolean;
|
|
3421
|
+
constraints?: Record<string, OrchestratorConstraint<Record<string, unknown>>>;
|
|
3422
|
+
resolvers?: Record<string, OrchestratorResolver<Record<string, unknown>, Requirement>>;
|
|
3423
|
+
approvals?: {
|
|
3424
|
+
/** @default true */
|
|
3425
|
+
autoApproveToolCalls?: boolean;
|
|
3426
|
+
onRequest?: (request: ApprovalRequest) => void;
|
|
3427
|
+
/** @default 300_000 */
|
|
3428
|
+
timeoutMs?: number;
|
|
3429
|
+
};
|
|
3430
|
+
retry?: AgentRetryConfig;
|
|
3431
|
+
hooks?: OrchestratorLifecycleHooks;
|
|
3432
|
+
/** P2: Intelligent retry config for the base runner. */
|
|
3433
|
+
intelligentRetry?: RetryConfig;
|
|
3434
|
+
/** P0: Fallback runners (tried in order on failure). */
|
|
3435
|
+
fallback?: {
|
|
3436
|
+
runners: AgentRunner[];
|
|
3437
|
+
config?: FallbackConfig;
|
|
3438
|
+
};
|
|
3439
|
+
/** P1: Cost budget guards. */
|
|
3440
|
+
budget?: BudgetConfig;
|
|
3441
|
+
/** P3: Model selection rules (first match wins). */
|
|
3442
|
+
modelSelection?: ModelRule[];
|
|
3443
|
+
/** P6: Structured output config (applied per-agent via agents map, or globally here). */
|
|
3444
|
+
structuredOutput?: StructuredOutputConfig;
|
|
3445
|
+
}
|
|
3446
|
+
interface StackRunOptions {
|
|
3447
|
+
/** Override output guardrails for this call */
|
|
3448
|
+
guardrails?: {
|
|
3449
|
+
output?: Array<GuardrailFn<OutputGuardrailData> | NamedGuardrail<OutputGuardrailData>>;
|
|
3450
|
+
};
|
|
3451
|
+
/** Set to false to skip cache for this call */
|
|
3452
|
+
cache?: false;
|
|
3453
|
+
/** AbortSignal for cancellation */
|
|
3454
|
+
signal?: AbortSignal;
|
|
3455
|
+
}
|
|
3456
|
+
interface StackStreamOptions {
|
|
3457
|
+
signal?: AbortSignal;
|
|
3458
|
+
}
|
|
3459
|
+
interface TokenStream<T = string> extends AsyncIterable<string> {
|
|
3460
|
+
/** Resolves to the final run result after the stream completes */
|
|
3461
|
+
result: Promise<RunResult<T>>;
|
|
3462
|
+
/** Abort the stream */
|
|
3463
|
+
abort: () => void;
|
|
3464
|
+
}
|
|
3465
|
+
interface AgentStackState {
|
|
3466
|
+
totalTokens: number;
|
|
3467
|
+
estimatedCost: number;
|
|
3468
|
+
circuitState: CircuitState;
|
|
3469
|
+
cacheStats: CacheStats;
|
|
3470
|
+
memoryMessageCount: number;
|
|
3471
|
+
busMessageCount: number;
|
|
3472
|
+
rateLimitRemaining: number | null;
|
|
3473
|
+
}
|
|
3474
|
+
/** Options for runStructured() */
|
|
3475
|
+
interface StructuredRunOptions<_T = unknown> extends StackRunOptions {
|
|
3476
|
+
/** Validate the output. Return `true` or `{ valid: true }` on success. */
|
|
3477
|
+
validate: (value: unknown) => boolean | {
|
|
3478
|
+
valid: boolean;
|
|
3479
|
+
errors?: string[];
|
|
3480
|
+
};
|
|
3481
|
+
/** Number of retry attempts on validation failure @default 1 */
|
|
3482
|
+
retries?: number;
|
|
3483
|
+
}
|
|
3484
|
+
interface AgentStack {
|
|
3485
|
+
/** Run a single registered agent by ID */
|
|
3486
|
+
run<T = unknown>(agentId: string, input: string, options?: StackRunOptions): Promise<RunResult<T>>;
|
|
3487
|
+
/** Run and validate output against a schema, retrying on failure */
|
|
3488
|
+
runStructured<T>(agentId: string, input: string, options: StructuredRunOptions<T>): Promise<RunResult<T>>;
|
|
3489
|
+
/** Run a named execution pattern */
|
|
3490
|
+
runPattern<T = unknown>(patternId: string, input: string, options?: StackRunOptions): Promise<T>;
|
|
3491
|
+
/** Stream tokens from a single agent */
|
|
3492
|
+
stream<T = string>(agentId: string, input: string, options?: StackStreamOptions): TokenStream<T>;
|
|
3493
|
+
/** Stream full rich chunks (token, tool_start, tool_end, etc.) from a single agent */
|
|
3494
|
+
streamChunks<T = unknown>(agentId: string, input: string, options?: StackStreamOptions): StreamingRunResult<T>;
|
|
3495
|
+
/** Approve a pending approval request */
|
|
3496
|
+
approve(requestId: string): void;
|
|
3497
|
+
/** Reject a pending approval request */
|
|
3498
|
+
reject(requestId: string, reason?: string): void;
|
|
3499
|
+
/** Aggregate state across all features */
|
|
3500
|
+
getState(): AgentStackState;
|
|
3501
|
+
/** Reset all feature state */
|
|
3502
|
+
reset(): void;
|
|
3503
|
+
/** Dispose all resources */
|
|
3504
|
+
dispose(): Promise<void>;
|
|
3505
|
+
readonly orchestrator: AgentOrchestrator<Record<string, unknown>>;
|
|
3506
|
+
readonly observability: ObservabilityInstance | null;
|
|
3507
|
+
readonly messageBus: MessageBus | null;
|
|
3508
|
+
readonly coordinator: MultiAgentOrchestrator | null;
|
|
3509
|
+
readonly cache: SemanticCache | null;
|
|
3510
|
+
readonly memory: AgentMemory | null;
|
|
3511
|
+
/** Get observability timeline (spans + metrics) for debugging */
|
|
3512
|
+
getTimeline(limit?: number): {
|
|
3513
|
+
spans: readonly TraceSpan[];
|
|
3514
|
+
metrics: Record<string, AggregatedMetric>;
|
|
3515
|
+
};
|
|
3516
|
+
/** @deprecated Use `observability` */
|
|
3517
|
+
readonly obs: ObservabilityInstance | null;
|
|
3518
|
+
/** @deprecated Use `messageBus` */
|
|
3519
|
+
readonly bus: MessageBus | null;
|
|
3520
|
+
/** @deprecated Use `coordinator` */
|
|
3521
|
+
readonly multi: MultiAgentOrchestrator | null;
|
|
3522
|
+
}
|
|
3523
|
+
/**
|
|
3524
|
+
* Create an agent stack that composes all AI adapter features.
|
|
3525
|
+
*
|
|
3526
|
+
* Only `runner` is required. Every other feature activates when its config key
|
|
3527
|
+
* is present. Pass a pre-built instance to reuse existing objects, or pass
|
|
3528
|
+
* shorthand config to let the stack create them.
|
|
3529
|
+
*/
|
|
3530
|
+
declare function createAgentStack(config: AgentStackConfig): AgentStack;
|
|
3531
|
+
|
|
3532
|
+
/**
|
|
3533
|
+
* AI Bridge — Syncs AI adapter state into a Directive system.
|
|
3534
|
+
*
|
|
3535
|
+
* Eliminates manual state sync boilerplate when using createAgentStack()
|
|
3536
|
+
* alongside createSystem().
|
|
3537
|
+
*
|
|
3538
|
+
* @example Using with AgentStack directly
|
|
3539
|
+
* ```typescript
|
|
3540
|
+
* const syncAI = createAISyncer(stack, (state) => {
|
|
3541
|
+
* system.events.chat.updateAIState({
|
|
3542
|
+
* totalTokens: state.totalTokens,
|
|
3543
|
+
* estimatedCost: state.estimatedCost,
|
|
3544
|
+
* circuitState: state.circuitState,
|
|
3545
|
+
* });
|
|
3546
|
+
* });
|
|
3547
|
+
* syncAI();
|
|
3548
|
+
* ```
|
|
3549
|
+
*
|
|
3550
|
+
* @example Using with a wrapper that has getState()
|
|
3551
|
+
* ```typescript
|
|
3552
|
+
* const syncAI = createAISyncer(myAIWrapper, (state) => {
|
|
3553
|
+
* system.events.chat.updateAIState({ ... });
|
|
3554
|
+
* });
|
|
3555
|
+
* syncAI();
|
|
3556
|
+
* ```
|
|
3557
|
+
*/
|
|
3558
|
+
/**
|
|
3559
|
+
* Any object with a getState() method.
|
|
3560
|
+
* Works with AgentStack, or any wrapper that exposes getState().
|
|
3561
|
+
*/
|
|
3562
|
+
interface Syncable<S> {
|
|
3563
|
+
getState(): S;
|
|
3564
|
+
}
|
|
3565
|
+
/**
|
|
3566
|
+
* Create a sync function that reads the latest state from a source and
|
|
3567
|
+
* passes it to a callback (typically dispatching events into a Directive system).
|
|
3568
|
+
*
|
|
3569
|
+
* Call the returned function after any AI operation to push state updates.
|
|
3570
|
+
*/
|
|
3571
|
+
declare function createAISyncer<S>(source: Syncable<S>, syncFn: (state: S) => void): () => void;
|
|
3572
|
+
|
|
3573
|
+
/**
|
|
3574
|
+
* RAG Enricher — Composable retrieval-augmented generation pipeline.
|
|
3575
|
+
*
|
|
3576
|
+
* Embeds a query, searches a chunk store by cosine similarity, and assembles
|
|
3577
|
+
* an enriched input string (context + history + query) for any agent.
|
|
3578
|
+
*
|
|
3579
|
+
* @example
|
|
3580
|
+
* ```typescript
|
|
3581
|
+
* import {
|
|
3582
|
+
* createRAGEnricher,
|
|
3583
|
+
* createJSONFileStore,
|
|
3584
|
+
* } from '@directive-run/ai';
|
|
3585
|
+
*
|
|
3586
|
+
* const enricher = createRAGEnricher({
|
|
3587
|
+
* embedder: myEmbedder, // Provide your own EmbedderFn
|
|
3588
|
+
* storage: createJSONFileStore({ filePath: './embeddings.json' }),
|
|
3589
|
+
* });
|
|
3590
|
+
*
|
|
3591
|
+
* const enrichedInput = await enricher.enrich('How do constraints work?', {
|
|
3592
|
+
* prefix: 'User is viewing: /docs/constraints',
|
|
3593
|
+
* history: [{ role: 'user', content: 'Hello' }],
|
|
3594
|
+
* });
|
|
3595
|
+
* ```
|
|
3596
|
+
*/
|
|
3597
|
+
|
|
3598
|
+
/** A document chunk with embedding and metadata */
|
|
3599
|
+
interface RAGChunk {
|
|
3600
|
+
id: string;
|
|
3601
|
+
content: string;
|
|
3602
|
+
embedding: Embedding;
|
|
3603
|
+
metadata: Record<string, unknown>;
|
|
3604
|
+
}
|
|
3605
|
+
/** Pluggable storage backend */
|
|
3606
|
+
interface RAGStorage {
|
|
3607
|
+
getChunks(): Promise<RAGChunk[]>;
|
|
3608
|
+
size(): Promise<number>;
|
|
3609
|
+
/** Optional: optimized vector search (bypasses full getChunks scan) */
|
|
3610
|
+
search?(query: Embedding, topK: number, minSimilarity: number): Promise<Array<RAGChunk & {
|
|
3611
|
+
similarity: number;
|
|
3612
|
+
}>>;
|
|
3613
|
+
/** Reload storage (clear cache, re-read from source) */
|
|
3614
|
+
reload?(): Promise<void>;
|
|
3615
|
+
/** Dispose of resources */
|
|
3616
|
+
dispose?(): void;
|
|
3617
|
+
}
|
|
3618
|
+
interface RAGEnricherConfig {
|
|
3619
|
+
/** Function to generate query embeddings */
|
|
3620
|
+
embedder: EmbedderFn;
|
|
3621
|
+
/** Storage backend for document chunks */
|
|
3622
|
+
storage: RAGStorage;
|
|
3623
|
+
/** Number of top results to return (default: 5) */
|
|
3624
|
+
topK?: number;
|
|
3625
|
+
/** Minimum similarity score to include, clamped to [0, 1] (default: 0.3) */
|
|
3626
|
+
minSimilarity?: number;
|
|
3627
|
+
/** Custom chunk formatter */
|
|
3628
|
+
formatChunk?: (chunk: RAGChunk, similarity: number) => string;
|
|
3629
|
+
/** Custom context block formatter */
|
|
3630
|
+
formatContext?: (formattedChunks: string[], query: string) => string;
|
|
3631
|
+
/** Error callback — embedder/storage errors are non-fatal by default */
|
|
3632
|
+
onError?: (error: Error) => void;
|
|
3633
|
+
}
|
|
3634
|
+
interface RAGEnrichOptions {
|
|
3635
|
+
/** Prefix line (e.g. "User is viewing: /docs/constraints") */
|
|
3636
|
+
prefix?: string;
|
|
3637
|
+
/** Conversation history */
|
|
3638
|
+
history?: Array<{
|
|
3639
|
+
role: string;
|
|
3640
|
+
content: string;
|
|
3641
|
+
}>;
|
|
3642
|
+
/** Per-call topK override */
|
|
3643
|
+
topK?: number;
|
|
3644
|
+
/** Filter chunks before ranking (e.g. by metadata tag or section) */
|
|
3645
|
+
filter?: (chunk: RAGChunk) => boolean;
|
|
3646
|
+
}
|
|
3647
|
+
interface RAGEnricher {
|
|
3648
|
+
/** Retrieve relevant chunks for a query */
|
|
3649
|
+
retrieve(query: string, topK?: number): Promise<Array<RAGChunk & {
|
|
3650
|
+
similarity: number;
|
|
3651
|
+
}>>;
|
|
3652
|
+
/** Retrieve + format into an enriched input string */
|
|
3653
|
+
enrich(input: string, options?: RAGEnrichOptions): Promise<string>;
|
|
3654
|
+
}
|
|
3655
|
+
/**
|
|
3656
|
+
* Create a RAG enricher that retrieves relevant document chunks and
|
|
3657
|
+
* assembles enriched input for an agent.
|
|
3658
|
+
*/
|
|
3659
|
+
declare function createRAGEnricher(config: RAGEnricherConfig): RAGEnricher;
|
|
3660
|
+
interface JSONFileStoreOptions {
|
|
3661
|
+
/** Absolute or relative path to the JSON embeddings file */
|
|
3662
|
+
filePath: string;
|
|
3663
|
+
/** Optional transform from raw JSON entries to RAGChunk */
|
|
3664
|
+
mapEntry?: (entry: Record<string, unknown>) => RAGChunk;
|
|
3665
|
+
/** Cache TTL in ms. 0 = cache forever (default) */
|
|
3666
|
+
ttlMs?: number;
|
|
3667
|
+
}
|
|
3668
|
+
/**
|
|
3669
|
+
* Create a RAGStorage backed by a JSON file (lazy-loaded, cached in memory).
|
|
3670
|
+
* Uses dynamic `import('node:fs')` for isomorphic safety.
|
|
3671
|
+
*/
|
|
3672
|
+
declare function createJSONFileStore(options: JSONFileStoreOptions): RAGStorage;
|
|
3673
|
+
|
|
3674
|
+
/**
|
|
3675
|
+
* SSE Transport — Wrap a Directive AgentStack token stream into an HTTP
|
|
3676
|
+
* Server-Sent Events response.
|
|
3677
|
+
*
|
|
3678
|
+
* Framework-agnostic: uses the WinterCG `Response` constructor (Node 18+,
|
|
3679
|
+
* Deno, Bun, Cloudflare Workers, Next.js).
|
|
3680
|
+
*
|
|
3681
|
+
* @example
|
|
3682
|
+
* ```typescript
|
|
3683
|
+
* import { createSSETransport, createAgentStack } from '@directive-run/ai';
|
|
3684
|
+
*
|
|
3685
|
+
* const transport = createSSETransport({
|
|
3686
|
+
* maxResponseChars: 10_000,
|
|
3687
|
+
* errorMessages: {
|
|
3688
|
+
* INPUT_GUARDRAIL_FAILED: 'Your message was flagged by our safety filter.',
|
|
3689
|
+
* },
|
|
3690
|
+
* });
|
|
3691
|
+
*
|
|
3692
|
+
* // Next.js route handler
|
|
3693
|
+
* export async function POST(req: Request) {
|
|
3694
|
+
* const { message } = await req.json();
|
|
3695
|
+
* return transport.toResponse(stack, 'docs-qa', message);
|
|
3696
|
+
* }
|
|
3697
|
+
* ```
|
|
3698
|
+
*/
|
|
3699
|
+
|
|
3700
|
+
type SSEEvent = {
|
|
3701
|
+
type: "text";
|
|
3702
|
+
text: string;
|
|
3703
|
+
} | {
|
|
3704
|
+
type: "truncated";
|
|
3705
|
+
text: string;
|
|
3706
|
+
} | {
|
|
3707
|
+
type: "done";
|
|
3708
|
+
} | {
|
|
3709
|
+
type: "error";
|
|
3710
|
+
message: string;
|
|
3711
|
+
} | {
|
|
3712
|
+
type: "heartbeat";
|
|
3713
|
+
timestamp: number;
|
|
3714
|
+
};
|
|
3715
|
+
interface SSETransportConfig {
|
|
3716
|
+
/** Truncate response after this many characters (default: Infinity) */
|
|
3717
|
+
maxResponseChars?: number;
|
|
3718
|
+
/** Message shown when response is truncated */
|
|
3719
|
+
truncationMessage?: string;
|
|
3720
|
+
/** Heartbeat interval in ms (default: 0 = disabled) */
|
|
3721
|
+
heartbeatIntervalMs?: number;
|
|
3722
|
+
/** Map error codes/types to user-facing messages */
|
|
3723
|
+
errorMessages?: Record<string, string> | ((error: unknown) => string);
|
|
3724
|
+
/** Extra headers merged into the SSE response */
|
|
3725
|
+
headers?: Record<string, string>;
|
|
3726
|
+
}
|
|
3727
|
+
interface SSETransport {
|
|
3728
|
+
/** Create a full HTTP Response with SSE headers */
|
|
3729
|
+
toResponse(stack: AgentStack, agentId: string, input: string, opts?: {
|
|
3730
|
+
signal?: AbortSignal;
|
|
3731
|
+
}): Response;
|
|
3732
|
+
/** Return just the ReadableStream (for Express/Koa `res.write()`) */
|
|
3733
|
+
toStream(stack: AgentStack, agentId: string, input: string, opts?: {
|
|
3734
|
+
signal?: AbortSignal;
|
|
3735
|
+
}): ReadableStream<Uint8Array>;
|
|
3736
|
+
}
|
|
3737
|
+
/**
|
|
3738
|
+
* Create an SSE transport that converts a Directive AgentStack token stream
|
|
3739
|
+
* into Server-Sent Events.
|
|
3740
|
+
*/
|
|
3741
|
+
declare function createSSETransport(config?: SSETransportConfig): SSETransport;
|
|
3742
|
+
|
|
3743
|
+
/**
|
|
3744
|
+
* P5: Batch Queue — Application-level batching for agent calls.
|
|
3745
|
+
*
|
|
3746
|
+
* Accumulates calls and flushes them in batches to reduce overhead.
|
|
3747
|
+
* Each `submit()` returns a promise that resolves when its individual call completes.
|
|
3748
|
+
* Batches execute calls in parallel up to a configurable concurrency limit.
|
|
3749
|
+
*
|
|
3750
|
+
* @module
|
|
3751
|
+
*
|
|
3752
|
+
* @example
|
|
3753
|
+
* ```typescript
|
|
3754
|
+
* import { createBatchQueue } from '@directive-run/ai';
|
|
3755
|
+
*
|
|
3756
|
+
* const queue = createBatchQueue(runner, {
|
|
3757
|
+
* maxBatchSize: 20,
|
|
3758
|
+
* maxWaitMs: 5000,
|
|
3759
|
+
* concurrency: 5,
|
|
3760
|
+
* });
|
|
3761
|
+
*
|
|
3762
|
+
* // Submit calls — they batch automatically
|
|
3763
|
+
* const [r1, r2, r3] = await Promise.all([
|
|
3764
|
+
* queue.submit(agent, "input 1"),
|
|
3765
|
+
* queue.submit(agent, "input 2"),
|
|
3766
|
+
* queue.submit(agent, "input 3"),
|
|
3767
|
+
* ]);
|
|
3768
|
+
*
|
|
3769
|
+
* // Force immediate flush
|
|
3770
|
+
* await queue.flush();
|
|
3771
|
+
*
|
|
3772
|
+
* // Clean up (flushes remaining calls)
|
|
3773
|
+
* await queue.dispose();
|
|
3774
|
+
* ```
|
|
3775
|
+
*/
|
|
3776
|
+
|
|
3777
|
+
interface BatchQueueConfig {
|
|
3778
|
+
/** Maximum number of calls per batch. @default 20 */
|
|
3779
|
+
maxBatchSize?: number;
|
|
3780
|
+
/** Maximum time to wait before flushing (ms). @default 5000 */
|
|
3781
|
+
maxWaitMs?: number;
|
|
3782
|
+
/** Number of calls to run in parallel within a batch. @default 5 */
|
|
3783
|
+
concurrency?: number;
|
|
3784
|
+
}
|
|
3785
|
+
interface BatchQueue {
|
|
3786
|
+
/** Submit a call to the queue. Returns a promise that resolves when the call completes. */
|
|
3787
|
+
submit<T = unknown>(agent: AgentLike, input: string, options?: RunOptions): Promise<RunResult<T>>;
|
|
3788
|
+
/** Flush all pending calls immediately. */
|
|
3789
|
+
flush(): Promise<void>;
|
|
3790
|
+
/** Get the number of pending calls. */
|
|
3791
|
+
readonly pending: number;
|
|
3792
|
+
/** Dispose the queue, flushing remaining calls. */
|
|
3793
|
+
dispose(): Promise<void>;
|
|
3794
|
+
}
|
|
3795
|
+
/**
|
|
3796
|
+
* Create a batch queue for grouping agent calls.
|
|
3797
|
+
*
|
|
3798
|
+
* @example
|
|
3799
|
+
* ```typescript
|
|
3800
|
+
* const queue = createBatchQueue(runner, {
|
|
3801
|
+
* maxBatchSize: 20,
|
|
3802
|
+
* maxWaitMs: 5000,
|
|
3803
|
+
* concurrency: 5,
|
|
3804
|
+
* });
|
|
3805
|
+
*
|
|
3806
|
+
* // Submit multiple calls — they batch automatically
|
|
3807
|
+
* const [result1, result2, result3] = await Promise.all([
|
|
3808
|
+
* queue.submit(agent, "input 1"),
|
|
3809
|
+
* queue.submit(agent, "input 2"),
|
|
3810
|
+
* queue.submit(agent, "input 3"),
|
|
3811
|
+
* ]);
|
|
3812
|
+
*
|
|
3813
|
+
* // Clean up
|
|
3814
|
+
* await queue.dispose();
|
|
3815
|
+
* ```
|
|
3816
|
+
*/
|
|
3817
|
+
declare function createBatchQueue(runner: AgentRunner, config?: BatchQueueConfig): BatchQueue;
|
|
3818
|
+
|
|
3819
|
+
/**
|
|
3820
|
+
* P4: Constraint-Driven Provider Routing — Directive's unique differentiator.
|
|
3821
|
+
*
|
|
3822
|
+
* Uses user-supplied constraints to select providers based on runtime state:
|
|
3823
|
+
* cost, latency, error rates, and compliance regions.
|
|
3824
|
+
*
|
|
3825
|
+
* Tracks per-provider stats (call count, error count, cost, latency) and
|
|
3826
|
+
* exposes them as {@link RoutingFacts} for constraint evaluation.
|
|
3827
|
+
*
|
|
3828
|
+
* @module
|
|
3829
|
+
*
|
|
3830
|
+
* @example
|
|
3831
|
+
* ```typescript
|
|
3832
|
+
* import { createConstraintRouter } from '@directive-run/ai';
|
|
3833
|
+
* import type { ConstraintRouterRunner } from '@directive-run/ai';
|
|
3834
|
+
*
|
|
3835
|
+
* const router = createConstraintRouter({
|
|
3836
|
+
* providers: [
|
|
3837
|
+
* { name: "openai", runner: openaiRunner, pricing: { inputPerMillion: 5, outputPerMillion: 15 } },
|
|
3838
|
+
* { name: "anthropic", runner: anthropicRunner, pricing: { inputPerMillion: 3, outputPerMillion: 15 } },
|
|
3839
|
+
* { name: "ollama", runner: ollamaRunner },
|
|
3840
|
+
* ],
|
|
3841
|
+
* defaultProvider: "openai",
|
|
3842
|
+
* constraints: [
|
|
3843
|
+
* { when: (facts) => facts.totalCost > 100, provider: "ollama", priority: 10 },
|
|
3844
|
+
* { when: (facts) => facts.providers["openai"]?.errorCount > 5, provider: "anthropic" },
|
|
3845
|
+
* ],
|
|
3846
|
+
* preferCheapest: true, // opt-in to cheapest-provider heuristic
|
|
3847
|
+
* onProviderSelected: (name, reason) => console.log(`Using ${name} (${reason})`),
|
|
3848
|
+
* });
|
|
3849
|
+
*
|
|
3850
|
+
* // Access runtime stats
|
|
3851
|
+
* console.log(router.facts.totalCost, router.facts.callCount);
|
|
3852
|
+
* ```
|
|
3853
|
+
*/
|
|
3854
|
+
|
|
3855
|
+
/**
|
|
3856
|
+
* Provider definition for the constraint router.
|
|
3857
|
+
*
|
|
3858
|
+
* Each provider has its own runner, optional pricing (for cost tracking
|
|
3859
|
+
* and cheapest-provider heuristic), and optional region tag.
|
|
3860
|
+
*/
|
|
3861
|
+
interface RoutingProvider {
|
|
3862
|
+
/** Unique name for this provider. */
|
|
3863
|
+
name: string;
|
|
3864
|
+
/** The runner to use for this provider. */
|
|
3865
|
+
runner: AgentRunner;
|
|
3866
|
+
/** Token pricing (cost per million tokens). */
|
|
3867
|
+
pricing?: {
|
|
3868
|
+
inputPerMillion: number;
|
|
3869
|
+
outputPerMillion: number;
|
|
3870
|
+
};
|
|
3871
|
+
/** Geographic region (for compliance routing). */
|
|
3872
|
+
region?: string;
|
|
3873
|
+
}
|
|
3874
|
+
/**
|
|
3875
|
+
* Runtime facts tracked by the router — exposed for user constraints.
|
|
3876
|
+
*
|
|
3877
|
+
* Access via the `facts` property on the returned {@link ConstraintRouterRunner}.
|
|
3878
|
+
*/
|
|
3879
|
+
interface RoutingFacts {
|
|
3880
|
+
totalCost: number;
|
|
3881
|
+
callCount: number;
|
|
3882
|
+
errorCount: number;
|
|
3883
|
+
lastProvider: string | null;
|
|
3884
|
+
avgLatencyMs: number;
|
|
3885
|
+
/** Per-provider stats. */
|
|
3886
|
+
providers: Record<string, ProviderStats>;
|
|
3887
|
+
}
|
|
3888
|
+
interface ProviderStats {
|
|
3889
|
+
callCount: number;
|
|
3890
|
+
errorCount: number;
|
|
3891
|
+
totalCost: number;
|
|
3892
|
+
avgLatencyMs: number;
|
|
3893
|
+
lastErrorAt: number | null;
|
|
3894
|
+
}
|
|
3895
|
+
/** User-supplied routing constraint. */
|
|
3896
|
+
interface RoutingConstraint {
|
|
3897
|
+
/** When this constraint is active. */
|
|
3898
|
+
when: (facts: RoutingFacts) => boolean;
|
|
3899
|
+
/** The provider to route to. */
|
|
3900
|
+
provider: string;
|
|
3901
|
+
/** Priority — higher wins when multiple constraints match. @default 0 */
|
|
3902
|
+
priority?: number;
|
|
3903
|
+
}
|
|
3904
|
+
interface ConstraintRouterConfig {
|
|
3905
|
+
/** Available providers. */
|
|
3906
|
+
providers: RoutingProvider[];
|
|
3907
|
+
/** Default provider name. */
|
|
3908
|
+
defaultProvider: string;
|
|
3909
|
+
/** User-supplied routing constraints. */
|
|
3910
|
+
constraints?: RoutingConstraint[];
|
|
3911
|
+
/** Called when a provider is selected. */
|
|
3912
|
+
onProviderSelected?: (providerName: string, reason: "constraint" | "cheapest" | "default") => void;
|
|
3913
|
+
/** Error cooldown — skip a provider for this many ms after an error. @default 30000 */
|
|
3914
|
+
errorCooldownMs?: number;
|
|
3915
|
+
/**
|
|
3916
|
+
* When true, automatically prefer the cheapest available provider
|
|
3917
|
+
* (based on pricing) when no user constraint matches.
|
|
3918
|
+
* When false, the default provider is used unless a constraint overrides it.
|
|
3919
|
+
* @default false
|
|
3920
|
+
*/
|
|
3921
|
+
preferCheapest?: boolean;
|
|
3922
|
+
}
|
|
3923
|
+
/**
|
|
3924
|
+
* Create a constraint-driven provider router.
|
|
3925
|
+
*
|
|
3926
|
+
* @example
|
|
3927
|
+
* ```typescript
|
|
3928
|
+
* const runner = createConstraintRouter({
|
|
3929
|
+
* providers: [
|
|
3930
|
+
* { name: "openai", runner: openaiRunner, pricing: { inputPerMillion: 5, outputPerMillion: 15 } },
|
|
3931
|
+
* { name: "anthropic", runner: anthropicRunner, pricing: { inputPerMillion: 3, outputPerMillion: 15 } },
|
|
3932
|
+
* { name: "ollama", runner: ollamaRunner },
|
|
3933
|
+
* ],
|
|
3934
|
+
* defaultProvider: "openai",
|
|
3935
|
+
* constraints: [
|
|
3936
|
+
* { when: (facts) => facts.totalCost > 100, provider: "ollama", priority: 10 },
|
|
3937
|
+
* { when: (facts) => facts.providers["openai"]?.errorCount > 5, provider: "anthropic" },
|
|
3938
|
+
* ],
|
|
3939
|
+
* });
|
|
3940
|
+
* ```
|
|
3941
|
+
*/
|
|
3942
|
+
declare function createConstraintRouter(config: ConstraintRouterConfig): ConstraintRouterRunner;
|
|
3943
|
+
/** Helper type for accessing router facts. */
|
|
3944
|
+
type ConstraintRouterRunner = AgentRunner & {
|
|
3945
|
+
readonly facts: RoutingFacts;
|
|
3946
|
+
};
|
|
3947
|
+
|
|
3948
|
+
/**
|
|
3949
|
+
* MCP Type Definitions
|
|
3950
|
+
*
|
|
3951
|
+
* Model Context Protocol types for Directive integration.
|
|
3952
|
+
* These types are compatible with the MCP specification but don't require
|
|
3953
|
+
* the MCP SDK as a dependency.
|
|
3954
|
+
*
|
|
3955
|
+
* @see https://modelcontextprotocol.io/
|
|
3956
|
+
*/
|
|
3957
|
+
/** MCP Transport type */
|
|
3958
|
+
type MCPTransport = "stdio" | "sse" | "websocket";
|
|
3959
|
+
/** MCP Server connection configuration */
|
|
3960
|
+
interface MCPServerConfig {
|
|
3961
|
+
/** Unique name for this server */
|
|
3962
|
+
name: string;
|
|
3963
|
+
/** Transport protocol */
|
|
3964
|
+
transport: MCPTransport;
|
|
3965
|
+
/** For stdio: command to run */
|
|
3966
|
+
command?: string;
|
|
3967
|
+
/** For stdio: command arguments */
|
|
3968
|
+
args?: string[];
|
|
3969
|
+
/** For stdio: environment variables */
|
|
3970
|
+
env?: Record<string, string>;
|
|
3971
|
+
/** For sse/websocket: URL to connect to */
|
|
3972
|
+
url?: string;
|
|
3973
|
+
/** Optional authentication */
|
|
3974
|
+
auth?: {
|
|
3975
|
+
type: "bearer" | "api-key" | "oauth";
|
|
3976
|
+
token?: string;
|
|
3977
|
+
apiKey?: string;
|
|
3978
|
+
};
|
|
3979
|
+
/** Connection timeout (ms) */
|
|
3980
|
+
timeout?: number;
|
|
3981
|
+
/** Retry configuration */
|
|
3982
|
+
retry?: {
|
|
3983
|
+
maxAttempts?: number;
|
|
3984
|
+
backoffMs?: number;
|
|
3985
|
+
};
|
|
3986
|
+
}
|
|
3987
|
+
/** MCP Tool definition */
|
|
3988
|
+
interface MCPTool {
|
|
3989
|
+
/** Tool name (must be unique within server) */
|
|
3990
|
+
name: string;
|
|
3991
|
+
/** Human-readable description */
|
|
3992
|
+
description?: string;
|
|
3993
|
+
/** JSON Schema for tool input */
|
|
3994
|
+
inputSchema: MCPJsonSchema;
|
|
3995
|
+
}
|
|
3996
|
+
/** MCP Resource definition */
|
|
3997
|
+
interface MCPResource {
|
|
3998
|
+
/** Resource URI */
|
|
3999
|
+
uri: string;
|
|
4000
|
+
/** Human-readable name */
|
|
4001
|
+
name: string;
|
|
4002
|
+
/** Resource description */
|
|
4003
|
+
description?: string;
|
|
4004
|
+
/** MIME type */
|
|
4005
|
+
mimeType?: string;
|
|
4006
|
+
}
|
|
4007
|
+
/** MCP Prompt definition */
|
|
4008
|
+
interface MCPPrompt {
|
|
4009
|
+
/** Prompt name */
|
|
4010
|
+
name: string;
|
|
4011
|
+
/** Prompt description */
|
|
4012
|
+
description?: string;
|
|
4013
|
+
/** Arguments the prompt accepts */
|
|
4014
|
+
arguments?: MCPPromptArgument[];
|
|
4015
|
+
}
|
|
4016
|
+
/** MCP Prompt argument */
|
|
4017
|
+
interface MCPPromptArgument {
|
|
4018
|
+
name: string;
|
|
4019
|
+
description?: string;
|
|
4020
|
+
required?: boolean;
|
|
4021
|
+
}
|
|
4022
|
+
/** JSON Schema type (subset used by MCP) */
|
|
4023
|
+
interface MCPJsonSchema {
|
|
4024
|
+
type?: string;
|
|
4025
|
+
description?: string;
|
|
4026
|
+
properties?: Record<string, MCPJsonSchema>;
|
|
4027
|
+
required?: string[];
|
|
4028
|
+
items?: MCPJsonSchema;
|
|
4029
|
+
enum?: unknown[];
|
|
4030
|
+
default?: unknown;
|
|
4031
|
+
[key: string]: unknown;
|
|
4032
|
+
}
|
|
4033
|
+
/** Result from calling an MCP tool */
|
|
4034
|
+
interface MCPToolResult {
|
|
4035
|
+
/** Tool output content */
|
|
4036
|
+
content: MCPContent[];
|
|
4037
|
+
/** Whether the tool call failed */
|
|
4038
|
+
isError?: boolean;
|
|
4039
|
+
}
|
|
4040
|
+
/** MCP Content types */
|
|
4041
|
+
type MCPContent = MCPTextContent | MCPImageContent | MCPResourceContent;
|
|
4042
|
+
/** Text content */
|
|
4043
|
+
interface MCPTextContent {
|
|
4044
|
+
type: "text";
|
|
4045
|
+
text: string;
|
|
4046
|
+
}
|
|
4047
|
+
/** Image content */
|
|
4048
|
+
interface MCPImageContent {
|
|
4049
|
+
type: "image";
|
|
4050
|
+
data: string;
|
|
4051
|
+
mimeType: string;
|
|
4052
|
+
}
|
|
4053
|
+
/** Resource reference content */
|
|
4054
|
+
interface MCPResourceContent {
|
|
4055
|
+
type: "resource";
|
|
4056
|
+
resource: {
|
|
4057
|
+
uri: string;
|
|
4058
|
+
mimeType?: string;
|
|
4059
|
+
text?: string;
|
|
4060
|
+
blob?: string;
|
|
4061
|
+
};
|
|
4062
|
+
}
|
|
4063
|
+
/** Result from reading an MCP resource */
|
|
4064
|
+
interface MCPResourceResult {
|
|
4065
|
+
contents: Array<{
|
|
4066
|
+
uri: string;
|
|
4067
|
+
mimeType?: string;
|
|
4068
|
+
text?: string;
|
|
4069
|
+
blob?: string;
|
|
4070
|
+
}>;
|
|
4071
|
+
}
|
|
4072
|
+
/** Result from getting an MCP prompt */
|
|
4073
|
+
interface MCPPromptResult {
|
|
4074
|
+
description?: string;
|
|
4075
|
+
messages: Array<{
|
|
4076
|
+
role: "user" | "assistant";
|
|
4077
|
+
content: MCPContent;
|
|
4078
|
+
}>;
|
|
4079
|
+
}
|
|
4080
|
+
/** MCP Client capabilities */
|
|
4081
|
+
interface MCPCapabilities {
|
|
4082
|
+
tools?: boolean;
|
|
4083
|
+
resources?: boolean;
|
|
4084
|
+
prompts?: boolean;
|
|
4085
|
+
sampling?: boolean;
|
|
4086
|
+
logging?: boolean;
|
|
4087
|
+
}
|
|
4088
|
+
/** MCP Client interface (for custom implementations) */
|
|
4089
|
+
interface MCPClient {
|
|
4090
|
+
/** Connect to the server */
|
|
4091
|
+
connect(): Promise<void>;
|
|
4092
|
+
/** Disconnect from the server */
|
|
4093
|
+
disconnect(): Promise<void>;
|
|
4094
|
+
/** Check if connected */
|
|
4095
|
+
isConnected(): boolean;
|
|
4096
|
+
/** Get server capabilities */
|
|
4097
|
+
getCapabilities(): MCPCapabilities;
|
|
4098
|
+
/** List available tools */
|
|
4099
|
+
listTools(): Promise<MCPTool[]>;
|
|
4100
|
+
/** Call a tool */
|
|
4101
|
+
callTool(name: string, args: Record<string, unknown>): Promise<MCPToolResult>;
|
|
4102
|
+
/** List available resources */
|
|
4103
|
+
listResources(): Promise<MCPResource[]>;
|
|
4104
|
+
/** Read a resource */
|
|
4105
|
+
readResource(uri: string): Promise<MCPResourceResult>;
|
|
4106
|
+
/** Subscribe to resource changes */
|
|
4107
|
+
subscribeResource?(uri: string, callback: (resource: MCPResource) => void): () => void;
|
|
4108
|
+
/** List available prompts */
|
|
4109
|
+
listPrompts(): Promise<MCPPrompt[]>;
|
|
4110
|
+
/** Get a prompt with arguments */
|
|
4111
|
+
getPrompt(name: string, args?: Record<string, string>): Promise<MCPPromptResult>;
|
|
4112
|
+
}
|
|
4113
|
+
/** Constraint configuration for an MCP tool */
|
|
4114
|
+
interface MCPToolConstraint {
|
|
4115
|
+
/** Require human approval before calling */
|
|
4116
|
+
requireApproval?: boolean;
|
|
4117
|
+
/** Maximum argument size (bytes) */
|
|
4118
|
+
maxArgSize?: number;
|
|
4119
|
+
/** Constraint that must be true to allow the tool */
|
|
4120
|
+
when?: (facts: Record<string, unknown>, args: Record<string, unknown>) => boolean | Promise<boolean>;
|
|
4121
|
+
/** Requirement to emit when constraint is violated */
|
|
4122
|
+
require?: {
|
|
4123
|
+
type: string;
|
|
4124
|
+
[key: string]: unknown;
|
|
4125
|
+
};
|
|
4126
|
+
/** Rate limit (calls per minute) */
|
|
4127
|
+
rateLimit?: number;
|
|
4128
|
+
/** Timeout for tool execution (ms) */
|
|
4129
|
+
timeout?: number;
|
|
4130
|
+
}
|
|
4131
|
+
/** Mapping of MCP resources to Directive facts */
|
|
4132
|
+
interface MCPResourceMapping {
|
|
4133
|
+
/** Resource URI pattern (glob or regex) */
|
|
4134
|
+
pattern: string | RegExp;
|
|
4135
|
+
/** Fact key to sync to */
|
|
4136
|
+
factKey: string;
|
|
4137
|
+
/** Transform resource content before setting fact */
|
|
4138
|
+
transform?: (content: string) => unknown;
|
|
4139
|
+
/** Sync mode */
|
|
4140
|
+
mode: "poll" | "subscribe" | "manual";
|
|
4141
|
+
/** Poll interval (ms) for 'poll' mode */
|
|
4142
|
+
pollInterval?: number;
|
|
4143
|
+
}
|
|
4144
|
+
/** MCP Approval request */
|
|
4145
|
+
interface MCPApprovalRequest {
|
|
4146
|
+
id: string;
|
|
4147
|
+
server: string;
|
|
4148
|
+
tool: string;
|
|
4149
|
+
args: Record<string, unknown>;
|
|
4150
|
+
requestedAt: number;
|
|
4151
|
+
}
|
|
4152
|
+
/** MCP Adapter events */
|
|
4153
|
+
interface MCPAdapterEvents {
|
|
4154
|
+
/** Server connected */
|
|
4155
|
+
onConnect?: (server: string) => void;
|
|
4156
|
+
/** Server disconnected */
|
|
4157
|
+
onDisconnect?: (server: string, reason?: string) => void;
|
|
4158
|
+
/** Tool called */
|
|
4159
|
+
onToolCall?: (server: string, tool: string, args: Record<string, unknown>) => void;
|
|
4160
|
+
/** Tool result received */
|
|
4161
|
+
onToolResult?: (server: string, tool: string, result: MCPToolResult) => void;
|
|
4162
|
+
/** Resource updated */
|
|
4163
|
+
onResourceUpdate?: (server: string, uri: string, content: MCPResourceResult) => void;
|
|
4164
|
+
/** Error occurred */
|
|
4165
|
+
onError?: (server: string, error: Error) => void;
|
|
4166
|
+
/** Approval required for tool call */
|
|
4167
|
+
onApprovalRequest?: (request: MCPApprovalRequest) => void;
|
|
4168
|
+
/** Approval resolved */
|
|
4169
|
+
onApprovalResolved?: (requestId: string, approved: boolean) => void;
|
|
4170
|
+
}
|
|
4171
|
+
/** MCP Adapter configuration */
|
|
4172
|
+
interface MCPAdapterConfig {
|
|
4173
|
+
/** MCP servers to connect to */
|
|
4174
|
+
servers: MCPServerConfig[];
|
|
4175
|
+
/** Tool-specific constraints */
|
|
4176
|
+
toolConstraints?: Record<string, MCPToolConstraint>;
|
|
4177
|
+
/** Resource to fact mappings */
|
|
4178
|
+
resourceMappings?: MCPResourceMapping[];
|
|
4179
|
+
/** Event handlers */
|
|
4180
|
+
events?: MCPAdapterEvents;
|
|
4181
|
+
/** Auto-connect on adapter creation */
|
|
4182
|
+
autoConnect?: boolean;
|
|
4183
|
+
/** Reconnect on disconnect */
|
|
4184
|
+
autoReconnect?: boolean;
|
|
4185
|
+
/** Custom MCP client factory (for testing or custom implementations) */
|
|
4186
|
+
clientFactory?: (config: MCPServerConfig) => MCPClient;
|
|
4187
|
+
/** Enable debug logging for stub client (default: false) */
|
|
4188
|
+
debug?: boolean;
|
|
4189
|
+
/** Approval timeout in milliseconds (default: 300000 = 5 minutes) */
|
|
4190
|
+
approvalTimeoutMs?: number;
|
|
4191
|
+
}
|
|
4192
|
+
/** Requirement to call an MCP tool */
|
|
4193
|
+
interface MCPCallToolRequirement {
|
|
4194
|
+
type: "MCP_CALL_TOOL";
|
|
4195
|
+
server: string;
|
|
4196
|
+
tool: string;
|
|
4197
|
+
args: Record<string, unknown>;
|
|
4198
|
+
[key: string]: unknown;
|
|
4199
|
+
}
|
|
4200
|
+
/** Requirement to read an MCP resource */
|
|
4201
|
+
interface MCPReadResourceRequirement {
|
|
4202
|
+
type: "MCP_READ_RESOURCE";
|
|
4203
|
+
server: string;
|
|
4204
|
+
uri: string;
|
|
4205
|
+
[key: string]: unknown;
|
|
4206
|
+
}
|
|
4207
|
+
/** Requirement to get an MCP prompt */
|
|
4208
|
+
interface MCPGetPromptRequirement {
|
|
4209
|
+
type: "MCP_GET_PROMPT";
|
|
4210
|
+
server: string;
|
|
4211
|
+
prompt: string;
|
|
4212
|
+
args?: Record<string, string>;
|
|
4213
|
+
[key: string]: unknown;
|
|
4214
|
+
}
|
|
4215
|
+
/** Requirement to sync MCP resources */
|
|
4216
|
+
interface MCPSyncResourcesRequirement {
|
|
4217
|
+
type: "MCP_SYNC_RESOURCES";
|
|
4218
|
+
server?: string;
|
|
4219
|
+
pattern?: string | RegExp;
|
|
4220
|
+
[key: string]: unknown;
|
|
4221
|
+
}
|
|
4222
|
+
/** Union of all MCP requirements */
|
|
4223
|
+
type MCPRequirement = MCPCallToolRequirement | MCPReadResourceRequirement | MCPGetPromptRequirement | MCPSyncResourcesRequirement;
|
|
4224
|
+
|
|
4225
|
+
/**
|
|
4226
|
+
* MCP Adapter - Model Context Protocol Integration for Directive
|
|
4227
|
+
*
|
|
4228
|
+
* Provides seamless integration between Directive's constraint system and MCP servers:
|
|
4229
|
+
* - MCP tools become Directive resolvers with constraint-driven access control
|
|
4230
|
+
* - MCP resources sync to Directive facts
|
|
4231
|
+
* - MCP prompts available through requirements
|
|
4232
|
+
*
|
|
4233
|
+
* @example
|
|
4234
|
+
* ```typescript
|
|
4235
|
+
* import { createMCPAdapter } from '@directive-run/ai';
|
|
4236
|
+
*
|
|
4237
|
+
* const mcpAdapter = createMCPAdapter({
|
|
4238
|
+
* servers: [
|
|
4239
|
+
* { name: 'filesystem', transport: 'stdio', command: 'mcp-server-filesystem' },
|
|
4240
|
+
* { name: 'github', transport: 'sse', url: 'https://mcp.github.com' }
|
|
4241
|
+
* ],
|
|
4242
|
+
* toolConstraints: {
|
|
4243
|
+
* 'filesystem.write': { requireApproval: true },
|
|
4244
|
+
* 'github.create_pr': { when: (facts) => facts.reviewComplete }
|
|
4245
|
+
* }
|
|
4246
|
+
* });
|
|
4247
|
+
*
|
|
4248
|
+
* const system = createSystem({
|
|
4249
|
+
* module: myModule,
|
|
4250
|
+
* plugins: [mcpAdapter.plugin]
|
|
4251
|
+
* });
|
|
4252
|
+
* ```
|
|
4253
|
+
*/
|
|
4254
|
+
|
|
4255
|
+
/** State of an MCP server connection */
|
|
4256
|
+
interface MCPServerState {
|
|
4257
|
+
config: MCPServerConfig;
|
|
4258
|
+
client: MCPClient | null;
|
|
4259
|
+
tools: MCPTool[];
|
|
4260
|
+
resources: MCPResource[];
|
|
4261
|
+
status: "disconnected" | "connecting" | "connected" | "error";
|
|
4262
|
+
error?: Error;
|
|
4263
|
+
lastSync?: number;
|
|
4264
|
+
}
|
|
4265
|
+
/** MCP Adapter instance */
|
|
4266
|
+
interface MCPAdapter {
|
|
4267
|
+
/** Plugin to add to Directive system */
|
|
4268
|
+
plugin: Plugin;
|
|
4269
|
+
/** Connect to all configured servers */
|
|
4270
|
+
connect(): Promise<void>;
|
|
4271
|
+
/** Connect to a specific server */
|
|
4272
|
+
connectServer(name: string): Promise<void>;
|
|
4273
|
+
/** Disconnect from all servers */
|
|
4274
|
+
disconnect(): Promise<void>;
|
|
4275
|
+
/** Disconnect from a specific server */
|
|
4276
|
+
disconnectServer(name: string): Promise<void>;
|
|
4277
|
+
/** Get all available tools across all servers */
|
|
4278
|
+
getTools(): Map<string, MCPTool[]>;
|
|
4279
|
+
/** Get all available resources across all servers */
|
|
4280
|
+
getResources(): Map<string, MCPResource[]>;
|
|
4281
|
+
/**
|
|
4282
|
+
* Call a tool with constraint checking (recommended).
|
|
4283
|
+
* Applies rate limits, argument size limits, approval workflow, and custom constraints.
|
|
4284
|
+
* @param server - Server name
|
|
4285
|
+
* @param tool - Tool name
|
|
4286
|
+
* @param args - Tool arguments
|
|
4287
|
+
* @param facts - Current facts for constraint evaluation
|
|
4288
|
+
*/
|
|
4289
|
+
callTool(server: string, tool: string, args: Record<string, unknown>, facts: Record<string, unknown>): Promise<MCPToolResult>;
|
|
4290
|
+
/**
|
|
4291
|
+
* Call a tool directly, bypassing all constraints (rate limits, approvals, etc.).
|
|
4292
|
+
* Use only for trusted internal calls where constraint checking is not needed.
|
|
4293
|
+
*/
|
|
4294
|
+
callToolDirect(server: string, tool: string, args: Record<string, unknown>): Promise<MCPToolResult>;
|
|
4295
|
+
/** Read a resource directly */
|
|
4296
|
+
readResource(server: string, uri: string): Promise<MCPResourceResult>;
|
|
4297
|
+
/** Sync resources to facts */
|
|
4298
|
+
syncResources(facts: Record<string, unknown>): Promise<void>;
|
|
4299
|
+
/** Get server status */
|
|
4300
|
+
getServerStatus(name: string): MCPServerState | undefined;
|
|
4301
|
+
/** Get all server statuses */
|
|
4302
|
+
getAllServerStatuses(): Map<string, MCPServerState>;
|
|
4303
|
+
/** Approve a pending tool call request */
|
|
4304
|
+
approve(requestId: string): void;
|
|
4305
|
+
/** Reject a pending tool call request */
|
|
4306
|
+
reject(requestId: string, reason?: string): void;
|
|
4307
|
+
/** Get pending approval requests */
|
|
4308
|
+
getPendingApprovals(): MCPApprovalRequest[];
|
|
4309
|
+
/** Get the rejection reason for a request (if available) */
|
|
4310
|
+
getRejectionReason(requestId: string): string | undefined;
|
|
4311
|
+
}
|
|
4312
|
+
/**
|
|
4313
|
+
* Create an MCP adapter for Directive integration.
|
|
4314
|
+
*
|
|
4315
|
+
* @example
|
|
4316
|
+
* ```typescript
|
|
4317
|
+
* const adapter = createMCPAdapter({
|
|
4318
|
+
* servers: [
|
|
4319
|
+
* { name: 'fs', transport: 'stdio', command: 'mcp-server-filesystem' },
|
|
4320
|
+
* ],
|
|
4321
|
+
* toolConstraints: {
|
|
4322
|
+
* 'fs.write_file': {
|
|
4323
|
+
* requireApproval: true,
|
|
4324
|
+
* maxArgSize: 10000,
|
|
4325
|
+
* timeout: 30000,
|
|
4326
|
+
* },
|
|
4327
|
+
* },
|
|
4328
|
+
* resourceMappings: [
|
|
4329
|
+
* {
|
|
4330
|
+
* pattern: 'file://*.json',
|
|
4331
|
+
* factKey: 'jsonFiles',
|
|
4332
|
+
* mode: 'poll',
|
|
4333
|
+
* pollInterval: 5000,
|
|
4334
|
+
* },
|
|
4335
|
+
* ],
|
|
4336
|
+
* });
|
|
4337
|
+
*
|
|
4338
|
+
* // Add to system
|
|
4339
|
+
* const system = createSystem({
|
|
4340
|
+
* module: myModule,
|
|
4341
|
+
* plugins: [adapter.plugin],
|
|
4342
|
+
* });
|
|
4343
|
+
*
|
|
4344
|
+
* // Connect to servers
|
|
4345
|
+
* await adapter.connect();
|
|
4346
|
+
* ```
|
|
4347
|
+
*/
|
|
4348
|
+
declare function createMCPAdapter(config: MCPAdapterConfig): MCPAdapter;
|
|
4349
|
+
/**
|
|
4350
|
+
* Convert MCP tools to a format suitable for LLM tool calling.
|
|
4351
|
+
*
|
|
4352
|
+
* @example
|
|
4353
|
+
* ```typescript
|
|
4354
|
+
* const adapter = createMCPAdapter({ servers: [...] });
|
|
4355
|
+
* await adapter.connect();
|
|
4356
|
+
*
|
|
4357
|
+
* const tools = adapter.getTools();
|
|
4358
|
+
* const llmTools = convertToolsForLLM(tools);
|
|
4359
|
+
* // Use with OpenAI/Anthropic/etc.
|
|
4360
|
+
* ```
|
|
4361
|
+
*/
|
|
4362
|
+
declare function convertToolsForLLM(tools: Map<string, MCPTool[]>): Array<{
|
|
4363
|
+
type: "function";
|
|
4364
|
+
function: {
|
|
4365
|
+
name: string;
|
|
4366
|
+
description: string;
|
|
4367
|
+
parameters: Record<string, unknown>;
|
|
4368
|
+
};
|
|
4369
|
+
}>;
|
|
4370
|
+
/**
|
|
4371
|
+
* Create a requirement to call an MCP tool.
|
|
4372
|
+
*
|
|
4373
|
+
* @example
|
|
4374
|
+
* ```typescript
|
|
4375
|
+
* const req = mcpCallTool('filesystem', 'read_file', { path: '/etc/hosts' });
|
|
4376
|
+
* // { type: 'MCP_CALL_TOOL', server: 'filesystem', tool: 'read_file', args: { path: '/etc/hosts' } }
|
|
4377
|
+
* ```
|
|
4378
|
+
*/
|
|
4379
|
+
declare function mcpCallTool(server: string, tool: string, args: Record<string, unknown>): MCPCallToolRequirement;
|
|
4380
|
+
/**
|
|
4381
|
+
* Create a requirement to read an MCP resource.
|
|
4382
|
+
*/
|
|
4383
|
+
declare function mcpReadResource(server: string, uri: string): MCPReadResourceRequirement;
|
|
4384
|
+
/**
|
|
4385
|
+
* Create a requirement to get an MCP prompt.
|
|
4386
|
+
*/
|
|
4387
|
+
declare function mcpGetPrompt(server: string, prompt: string, args?: Record<string, string>): MCPGetPromptRequirement;
|
|
4388
|
+
/**
|
|
4389
|
+
* Create a requirement to sync MCP resources.
|
|
4390
|
+
*/
|
|
4391
|
+
declare function mcpSyncResources(server?: string, pattern?: string | RegExp): MCPSyncResourcesRequirement;
|
|
4392
|
+
|
|
4393
|
+
/**
|
|
4394
|
+
* AI Adapter – Constraint-driven agent orchestration with guardrails
|
|
4395
|
+
*
|
|
4396
|
+
* Philosophy: "Use Directive WITH any LLM agent framework"
|
|
4397
|
+
* - Your framework handles LLM tool execution
|
|
4398
|
+
* - Directive adds safety guardrails, approval workflows, state persistence
|
|
4399
|
+
*
|
|
4400
|
+
* Also available:
|
|
4401
|
+
* - `@directive-run/ai/testing` – Mock runners, test orchestrators, assertion helpers
|
|
4402
|
+
* - `@directive-run/ai/anthropic` – Anthropic Claude adapter
|
|
4403
|
+
* - `@directive-run/ai/openai` – OpenAI / Azure / Together adapter
|
|
4404
|
+
* - `@directive-run/ai/ollama` – Local Ollama inference adapter
|
|
4405
|
+
*
|
|
4406
|
+
* @example
|
|
4407
|
+
* ```typescript
|
|
4408
|
+
* import { createAgentOrchestrator } from '@directive-run/ai'
|
|
4409
|
+
*
|
|
4410
|
+
* const orchestrator = createAgentOrchestrator({
|
|
4411
|
+
* runner: myAgentRunner,
|
|
4412
|
+
* constraints: {
|
|
4413
|
+
* needsExpertReview: {
|
|
4414
|
+
* when: (facts) => facts.decision.confidence < 0.7,
|
|
4415
|
+
* require: { type: 'EXPERT_AGENT', query: facts.userQuery }
|
|
4416
|
+
* },
|
|
4417
|
+
* budgetLimit: {
|
|
4418
|
+
* when: (facts) => facts.tokenUsage > 10000,
|
|
4419
|
+
* require: { type: 'PAUSE_AGENTS' }
|
|
4420
|
+
* }
|
|
4421
|
+
* },
|
|
4422
|
+
* guardrails: {
|
|
4423
|
+
* input: [(data) => validatePII(data.input)],
|
|
4424
|
+
* output: [(data) => checkToxicity(data.output)]
|
|
4425
|
+
* }
|
|
4426
|
+
* })
|
|
4427
|
+
* ```
|
|
4428
|
+
*/
|
|
4429
|
+
|
|
4430
|
+
/** Orchestrator options */
|
|
4431
|
+
interface OrchestratorOptions<F extends Record<string, unknown>> {
|
|
4432
|
+
/** Function to run an agent */
|
|
4433
|
+
runner: AgentRunner;
|
|
4434
|
+
/** Additional facts schema */
|
|
4435
|
+
factsSchema?: Record<string, {
|
|
4436
|
+
_type: unknown;
|
|
4437
|
+
_validators: [];
|
|
4438
|
+
}>;
|
|
4439
|
+
/** Initialize additional facts */
|
|
4440
|
+
init?: (facts: F & OrchestratorState) => void;
|
|
4441
|
+
/** Constraints for orchestration */
|
|
4442
|
+
constraints?: Record<string, OrchestratorConstraint<F>>;
|
|
4443
|
+
/** Resolvers for orchestration */
|
|
4444
|
+
resolvers?: Record<string, OrchestratorResolver<F, Requirement>>;
|
|
4445
|
+
/** Guardrails */
|
|
4446
|
+
guardrails?: GuardrailsConfig;
|
|
4447
|
+
/** Callback for approval requests */
|
|
4448
|
+
onApprovalRequest?: (request: ApprovalRequest) => void;
|
|
4449
|
+
/**
|
|
4450
|
+
* Auto-approve tool calls
|
|
4451
|
+
* @default true
|
|
4452
|
+
*/
|
|
4453
|
+
autoApproveToolCalls?: boolean;
|
|
4454
|
+
/**
|
|
4455
|
+
* Maximum token budget across all agent runs.
|
|
4456
|
+
*
|
|
4457
|
+
* When exceeded, agents are automatically paused with status "paused".
|
|
4458
|
+
* Check `facts.agent.tokenUsage` to see current usage.
|
|
4459
|
+
*
|
|
4460
|
+
* For more sophisticated cost management (per-user budgets, tiered pricing,
|
|
4461
|
+
* cost alerts), see the Cost Management section in the documentation.
|
|
4462
|
+
*
|
|
4463
|
+
* @example
|
|
4464
|
+
* ```typescript
|
|
4465
|
+
* const orchestrator = createAgentOrchestrator({
|
|
4466
|
+
* maxTokenBudget: 10000, // Pause after 10K tokens
|
|
4467
|
+
* });
|
|
4468
|
+
*
|
|
4469
|
+
* // Check if paused due to budget
|
|
4470
|
+
* if (orchestrator.facts.agent.status === 'paused') {
|
|
4471
|
+
* console.log('Budget exceeded:', orchestrator.facts.agent.tokenUsage);
|
|
4472
|
+
* }
|
|
4473
|
+
* ```
|
|
4474
|
+
*/
|
|
4475
|
+
maxTokenBudget?: number;
|
|
4476
|
+
/** Plugins */
|
|
4477
|
+
plugins?: Plugin[];
|
|
4478
|
+
/**
|
|
4479
|
+
* Enable debugging
|
|
4480
|
+
* @default false
|
|
4481
|
+
*/
|
|
4482
|
+
debug?: boolean;
|
|
4483
|
+
/**
|
|
4484
|
+
* Approval timeout in milliseconds
|
|
4485
|
+
* @default 300000 (5 minutes)
|
|
4486
|
+
*/
|
|
4487
|
+
approvalTimeoutMs?: number;
|
|
4488
|
+
/** Retry configuration for agent runs (no retries if not specified) */
|
|
4489
|
+
agentRetry?: AgentRetryConfig;
|
|
4490
|
+
/** Lifecycle hooks for observability */
|
|
4491
|
+
hooks?: OrchestratorLifecycleHooks;
|
|
4492
|
+
/**
|
|
4493
|
+
* Optional memory instance. When provided, context messages are auto-injected
|
|
4494
|
+
* into agent instructions before each run, and result messages are auto-stored.
|
|
4495
|
+
*/
|
|
4496
|
+
memory?: AgentMemory;
|
|
4497
|
+
/**
|
|
4498
|
+
* Optional circuit breaker. Wraps every run() call.
|
|
4499
|
+
* When OPEN, throws CircuitBreakerOpenError instead of calling the agent.
|
|
4500
|
+
*/
|
|
4501
|
+
circuitBreaker?: CircuitBreaker;
|
|
4502
|
+
}
|
|
4503
|
+
/** Streaming run result from orchestrator */
|
|
4504
|
+
interface OrchestratorStreamResult<T = unknown> {
|
|
4505
|
+
/** Async iterator for streaming chunks */
|
|
4506
|
+
stream: AsyncIterable<OrchestratorStreamChunk>;
|
|
4507
|
+
/** Promise that resolves to the final result */
|
|
4508
|
+
result: Promise<RunResult<T>>;
|
|
4509
|
+
/** Abort the stream */
|
|
4510
|
+
abort: () => void;
|
|
4511
|
+
}
|
|
4512
|
+
/** Stream chunk types for orchestrator — extends StreamChunk with approval events */
|
|
4513
|
+
type OrchestratorStreamChunk = StreamChunk | {
|
|
4514
|
+
type: "approval_required";
|
|
4515
|
+
requestId: string;
|
|
4516
|
+
toolName: string;
|
|
4517
|
+
} | {
|
|
4518
|
+
type: "approval_resolved";
|
|
4519
|
+
requestId: string;
|
|
4520
|
+
approved: boolean;
|
|
4521
|
+
};
|
|
4522
|
+
/** Per-call options for run() */
|
|
4523
|
+
interface RunCallOptions {
|
|
4524
|
+
/** Override output guardrails for this call only. Set to [] to skip. */
|
|
4525
|
+
outputGuardrails?: Array<GuardrailFn<OutputGuardrailData> | NamedGuardrail<OutputGuardrailData>>;
|
|
4526
|
+
/** Override input guardrails for this call only. Set to [] to skip. */
|
|
4527
|
+
inputGuardrails?: Array<GuardrailFn<InputGuardrailData> | NamedGuardrail<InputGuardrailData>>;
|
|
4528
|
+
/** Signal for abort */
|
|
4529
|
+
signal?: AbortSignal;
|
|
4530
|
+
}
|
|
4531
|
+
/** Orchestrator instance */
|
|
4532
|
+
interface AgentOrchestrator<F extends Record<string, unknown>> {
|
|
4533
|
+
system: System<any>;
|
|
4534
|
+
facts: F & OrchestratorState;
|
|
4535
|
+
/** Run an agent with guardrails. Pass options to override guardrails per-call. */
|
|
4536
|
+
run<T>(agent: AgentLike, input: string, options?: RunCallOptions): Promise<RunResult<T>>;
|
|
4537
|
+
/**
|
|
4538
|
+
* Run an agent with streaming support.
|
|
4539
|
+
* Returns an async iterator for chunks and a promise for the final result.
|
|
4540
|
+
*
|
|
4541
|
+
* @example
|
|
4542
|
+
* ```typescript
|
|
4543
|
+
* const { stream, result, abort } = orchestrator.runStream(agent, input);
|
|
4544
|
+
*
|
|
4545
|
+
* for await (const chunk of stream) {
|
|
4546
|
+
* if (chunk.type === 'token') process.stdout.write(chunk.data);
|
|
4547
|
+
* if (chunk.type === 'approval_required') showApprovalDialog(chunk);
|
|
4548
|
+
* if (chunk.type === 'guardrail_triggered') handleGuardrail(chunk);
|
|
4549
|
+
* }
|
|
4550
|
+
*
|
|
4551
|
+
* const finalResult = await result;
|
|
4552
|
+
* ```
|
|
4553
|
+
*/
|
|
4554
|
+
runStream<T>(agent: AgentLike, input: string, options?: {
|
|
4555
|
+
signal?: AbortSignal;
|
|
4556
|
+
}): OrchestratorStreamResult<T>;
|
|
4557
|
+
/** Approve a pending request */
|
|
4558
|
+
approve(requestId: string): void;
|
|
4559
|
+
/** Reject a pending request */
|
|
4560
|
+
reject(requestId: string, reason?: string): void;
|
|
4561
|
+
/** Pause all agents */
|
|
4562
|
+
pause(): void;
|
|
4563
|
+
/** Resume agents */
|
|
4564
|
+
resume(): void;
|
|
4565
|
+
/** Reset conversation state */
|
|
4566
|
+
reset(): void;
|
|
4567
|
+
/** Dispose of the orchestrator */
|
|
4568
|
+
dispose(): void;
|
|
4569
|
+
}
|
|
4570
|
+
/**
|
|
4571
|
+
* Create an orchestrator for OpenAI agents with Directive constraints.
|
|
4572
|
+
*
|
|
4573
|
+
* @example
|
|
4574
|
+
* ```typescript
|
|
4575
|
+
* import { run as runner } from '@openai/agents'
|
|
4576
|
+
*
|
|
4577
|
+
* const orchestrator = createAgentOrchestrator({
|
|
4578
|
+
* runner,
|
|
4579
|
+
* constraints: {
|
|
4580
|
+
* escalateToExpert: {
|
|
4581
|
+
* when: (facts) => facts.agent.output?.confidence < 0.7,
|
|
4582
|
+
* require: (facts) => ({
|
|
4583
|
+
* type: 'RUN_EXPERT_AGENT',
|
|
4584
|
+
* query: facts.agent.input,
|
|
4585
|
+
* }),
|
|
4586
|
+
* },
|
|
4587
|
+
* budgetExceeded: {
|
|
4588
|
+
* when: (facts) => facts.agent.tokenUsage > 10000,
|
|
4589
|
+
* require: { type: 'PAUSE_AGENTS' },
|
|
4590
|
+
* },
|
|
4591
|
+
* },
|
|
4592
|
+
* guardrails: {
|
|
4593
|
+
* input: [
|
|
4594
|
+
* async (data) => {
|
|
4595
|
+
* const hasPII = await detectPII(data.input);
|
|
4596
|
+
* return { passed: !hasPII, reason: hasPII ? 'Contains PII' : undefined };
|
|
4597
|
+
* },
|
|
4598
|
+
* ],
|
|
4599
|
+
* output: [
|
|
4600
|
+
* async (data) => {
|
|
4601
|
+
* const isToxic = await checkToxicity(data.output);
|
|
4602
|
+
* return { passed: !isToxic, reason: isToxic ? 'Toxic content' : undefined };
|
|
4603
|
+
* },
|
|
4604
|
+
* ],
|
|
4605
|
+
* },
|
|
4606
|
+
* });
|
|
4607
|
+
*
|
|
4608
|
+
* // Run with guardrails and constraint-driven orchestration
|
|
4609
|
+
* const result = await orchestrator.run(myAgent, 'Hello, can you help me?');
|
|
4610
|
+
* ```
|
|
4611
|
+
*
|
|
4612
|
+
* @throws {Error} If autoApproveToolCalls is false but no onApprovalRequest callback is provided
|
|
4613
|
+
*/
|
|
4614
|
+
declare function createAgentOrchestrator<F extends Record<string, unknown> = Record<string, never>>(options: OrchestratorOptions<F>): AgentOrchestrator<F>;
|
|
4615
|
+
/** Builder for type-safe orchestrator configuration */
|
|
4616
|
+
interface OrchestratorBuilder<F extends Record<string, unknown>> {
|
|
4617
|
+
/** Add a constraint */
|
|
4618
|
+
withConstraint<K extends string>(id: K, constraint: OrchestratorConstraint<F>): OrchestratorBuilder<F>;
|
|
4619
|
+
/** Add a resolver */
|
|
4620
|
+
withResolver<R extends Requirement>(id: string, resolver: OrchestratorResolver<F, R>): OrchestratorBuilder<F>;
|
|
4621
|
+
/** Add an input guardrail */
|
|
4622
|
+
withInputGuardrail(nameOrGuardrail: string | NamedGuardrail<InputGuardrailData>, fn?: GuardrailFn<InputGuardrailData>): OrchestratorBuilder<F>;
|
|
4623
|
+
/** Add an output guardrail */
|
|
4624
|
+
withOutputGuardrail(nameOrGuardrail: string | NamedGuardrail<OutputGuardrailData>, fn?: GuardrailFn<OutputGuardrailData>): OrchestratorBuilder<F>;
|
|
4625
|
+
/** Add a tool call guardrail */
|
|
4626
|
+
withToolCallGuardrail(nameOrGuardrail: string | NamedGuardrail<ToolCallGuardrailData>, fn?: GuardrailFn<ToolCallGuardrailData>): OrchestratorBuilder<F>;
|
|
4627
|
+
/** Add a plugin */
|
|
4628
|
+
withPlugin(plugin: Plugin): OrchestratorBuilder<F>;
|
|
4629
|
+
/** Set memory instance for auto context injection and message storage */
|
|
4630
|
+
withMemory(memory: AgentMemory): OrchestratorBuilder<F>;
|
|
4631
|
+
/** Set circuit breaker to wrap all run() calls */
|
|
4632
|
+
withCircuitBreaker(cb: CircuitBreaker): OrchestratorBuilder<F>;
|
|
4633
|
+
/** Set max token budget */
|
|
4634
|
+
withBudget(maxTokens: number): OrchestratorBuilder<F>;
|
|
4635
|
+
/** Enable debug mode */
|
|
4636
|
+
withDebug(enabled?: boolean): OrchestratorBuilder<F>;
|
|
4637
|
+
/** Build the orchestrator */
|
|
4638
|
+
build(options: {
|
|
4639
|
+
runner: AgentRunner;
|
|
4640
|
+
autoApproveToolCalls?: boolean;
|
|
4641
|
+
onApprovalRequest?: (request: ApprovalRequest) => void;
|
|
4642
|
+
}): AgentOrchestrator<F>;
|
|
4643
|
+
}
|
|
4644
|
+
/**
|
|
4645
|
+
* Create a type-safe orchestrator builder.
|
|
4646
|
+
*
|
|
4647
|
+
* @example
|
|
4648
|
+
* ```typescript
|
|
4649
|
+
* const orchestrator = createOrchestratorBuilder<MyFacts>()
|
|
4650
|
+
* .withConstraint('budget', {
|
|
4651
|
+
* when: (facts) => facts.cost > 100,
|
|
4652
|
+
* require: { type: 'PAUSE' },
|
|
4653
|
+
* })
|
|
4654
|
+
* .withInputGuardrail('pii', createPIIGuardrail())
|
|
4655
|
+
* .withOutputGuardrail('toxicity', createModerationGuardrail({ ... }))
|
|
4656
|
+
* .withBudget(10000)
|
|
4657
|
+
* .withDebug()
|
|
4658
|
+
* .build({ runner });
|
|
4659
|
+
* ```
|
|
4660
|
+
*/
|
|
4661
|
+
declare function createOrchestratorBuilder<F extends Record<string, unknown> = Record<string, never>>(): OrchestratorBuilder<F>;
|
|
4662
|
+
|
|
4663
|
+
export { type ANNIndex, type ANNSearchResult, AdapterHooks, type AgentInfo, AgentLike, type AgentMemory, type AgentMemoryConfig, type AgentMessage, type AgentMessageType, type AgentNetwork, type AgentNetworkConfig, type AgentOrchestrator, type AgentRegistration, type AgentRegistry, AgentRetryConfig, type AgentRunState, AgentRunner, type AgentSelectionConstraint, type AgentStack, type AgentStackConfig, type AgentStackState, AgentState, AllProvidersFailedError, ApprovalRequest, ApprovalState, type AuditInstance, type AuditPluginConfig, type BackpressureStrategy, type BatchQueue, type BatchQueueConfig, type BatchedEmbedder, type BidirectionalStream, type BudgetConfig, type BudgetExceededDetails, BudgetExceededError, type BudgetRunner, type BudgetWindow, type CacheEntry, type CacheLookupResult, type CacheStats, type ComplianceConfig, type ComplianceInstance, type ComplianceStorage, type ConstraintBuilder, type ConstraintRouterConfig, type ConstraintRouterRunner, type CreateRunnerOptions, DEFAULT_INJECTION_PATTERNS, type DelegationMessage, type DelegationResultMessage, type DoneChunk, type EmbedderFn, type Embedding, type EnhancedPIIGuardrailOptions, type ErrorChunk, type ExecutionPattern, type FallbackConfig, GuardrailFn, type GuardrailTriggeredChunk, GuardrailsConfig, type HandoffRequest, type HandoffResult, type InformMessage, InputGuardrailData, type JSONFileStoreOptions, type MCPAdapter, type MCPAdapterConfig, type MCPApprovalRequest, type MCPCallToolRequirement, type MCPGetPromptRequirement, type MCPReadResourceRequirement, type MCPRequirement, type MCPResource, type MCPServerConfig, type MCPSyncResourcesRequirement, type MCPTool, type MCPToolConstraint, type MCPToolResult, type MemoryManageResult, type MemoryState, type MemoryStrategy, type MemoryStrategyConfig, type MemoryStrategyResult, Message$1 as Message, type MessageBus, type MessageBusConfig, type MessageChunk, type MessageFilter, type MessageHandler, type MessageSummarizer, type ModelRule, type ModelSelectionConfig, type MultiAgentOrchestrator, type MultiAgentOrchestratorOptions, type MultiAgentState, NamedGuardrail, type OrchestratorBuilder, OrchestratorConstraint, OrchestratorLifecycleHooks, type OrchestratorOptions, OrchestratorResolver, OrchestratorState, type OrchestratorStreamChunk, type OrchestratorStreamResult, OutputGuardrailData, type ParallelPattern, type ParsedResponse, type ProgressChunk, type PromptInjectionGuardrailOptions, type ProviderStats, type QueryMessage, type RAGChunk, type RAGEnrichOptions, type RAGEnricher, type RAGEnricherConfig, type RAGStorage, type RateLimitGuardrail, type RequestMessage, type ResponseMessage, type RetryConfig, RetryExhaustedError, type RoutingConstraint, type RoutingFacts, type RoutingProvider, type RunAgentRequirement, type RunCallOptions, RunOptions, RunResult, type SSEEvent, type SSETransport, type SSETransportConfig, STRICT_INJECTION_PATTERNS, type SafeParseResult, type SafeParseable, SchemaValidator, type SemanticCache, type SemanticCacheConfig, type SemanticCacheStorage, Semaphore, type SequentialPattern, type StackRunOptions, type StackStreamOptions, type StreamChannel, type StreamChannelConfig, type StreamChannelState, type StreamChunk, type StreamRunOptions, type StreamRunner, type StreamingCallbackRunner, type StreamingGuardrail, type StreamingGuardrailResult, type StreamingRunResult, type StructuredOutputConfig, StructuredOutputError, type StructuredRunOptions, type Subscription, type SupervisorPattern, type TokenChunk, type TokenPricing, type TokenStream, ToolCallGuardrailData, type ToolEndChunk, type ToolStartChunk, type TypedAgentMessage, type UpdateMessage, type VPTreeIndexConfig, type WhenWithRequire, adaptOutputGuardrail, aggregateTokens, byAgentName, byInputLength, byPattern, collectOutputs, collectTokens, combineStreamingGuardrails, concatResults, constraint, convertToolsForLLM, createAISyncer, createAgentAuditHandlers, createAgentMemory, createAgentNetwork, createAgentOrchestrator, createAgentStack, createAuditTrail, createBatchQueue, createBatchedEmbedder, createBidirectionalStream, createBruteForceIndex, createCompliance, createConstraintRouter, createContentFilterGuardrail, createDelegator, createEnhancedPIIGuardrail, createHybridStrategy, createInMemoryComplianceStorage, createInMemoryStorage, createJSONFileStore, createKeyPointsSummarizer, createLLMSummarizer, createLengthGuardrail, createLengthStreamingGuardrail, createMCPAdapter, createMessageBus, createModerationGuardrail, createMultiAgentOrchestrator, createOrchestratorBuilder, createOutputPIIGuardrail, createOutputSchemaGuardrail, createOutputTypeGuardrail, createPIIGuardrail, createPatternStreamingGuardrail, createPromptInjectionGuardrail, createPubSub, createRAGEnricher, createRateLimitGuardrail, createResponder, createRunner, createSSETransport, createSemanticCache, createSemanticCacheGuardrail, createSlidingWindowStrategy, createStreamChannel, createStreamingRunner, createTestEmbedder, createTokenBasedStrategy, createToolGuardrail, createToxicityStreamingGuardrail, createTruncationSummarizer, createUntrustedContentGuardrail, createVPTreeIndex, detectPII, detectPromptInjection, estimateCost, extractJsonFromOutput, filterStream, hasPendingApprovals, isAgentRunning, mapStream, markUntrustedContent, mcpCallTool, mcpGetPrompt, mcpReadResource, mcpSyncResources, mergeStreams, parallel, parseHttpStatus, parseRetryAfter, pickBestResult, pipeThrough, redactPII, runAgentRequirement, sanitizeInjection, selectAgent, sequential, supervisor, tapStream, validateBaseURL, when, withBudget, withFallback, withModelSelection, withRetry, withStructuredOutput };
|