confused-ai-core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/FEATURES.md +169 -0
- package/package.json +119 -0
- package/src/agent.ts +187 -0
- package/src/agentic/index.ts +87 -0
- package/src/agentic/runner.ts +386 -0
- package/src/agentic/types.ts +91 -0
- package/src/artifacts/artifact.ts +417 -0
- package/src/artifacts/index.ts +42 -0
- package/src/artifacts/media.ts +304 -0
- package/src/cli/index.ts +122 -0
- package/src/core/base-agent.ts +151 -0
- package/src/core/context-builder.ts +106 -0
- package/src/core/index.ts +8 -0
- package/src/core/schemas.ts +17 -0
- package/src/core/types.ts +158 -0
- package/src/create-agent.ts +309 -0
- package/src/debug-logger.ts +188 -0
- package/src/dx/agent.ts +88 -0
- package/src/dx/define-agent.ts +183 -0
- package/src/dx/dev-logger.ts +57 -0
- package/src/dx/index.ts +11 -0
- package/src/errors.ts +175 -0
- package/src/execution/engine.ts +522 -0
- package/src/execution/graph-builder.ts +362 -0
- package/src/execution/index.ts +8 -0
- package/src/execution/types.ts +257 -0
- package/src/execution/worker-pool.ts +308 -0
- package/src/extensions/index.ts +123 -0
- package/src/guardrails/allowlist.ts +155 -0
- package/src/guardrails/index.ts +17 -0
- package/src/guardrails/types.ts +159 -0
- package/src/guardrails/validator.ts +265 -0
- package/src/index.ts +74 -0
- package/src/knowledge/index.ts +5 -0
- package/src/knowledge/types.ts +52 -0
- package/src/learning/in-memory-store.ts +72 -0
- package/src/learning/index.ts +6 -0
- package/src/learning/types.ts +42 -0
- package/src/llm/cache.ts +300 -0
- package/src/llm/index.ts +22 -0
- package/src/llm/model-resolver.ts +81 -0
- package/src/llm/openai-provider.ts +313 -0
- package/src/llm/openrouter-provider.ts +29 -0
- package/src/llm/types.ts +131 -0
- package/src/memory/in-memory-store.ts +255 -0
- package/src/memory/index.ts +7 -0
- package/src/memory/types.ts +193 -0
- package/src/memory/vector-store.ts +251 -0
- package/src/observability/console-logger.ts +123 -0
- package/src/observability/index.ts +12 -0
- package/src/observability/metrics.ts +85 -0
- package/src/observability/otlp-exporter.ts +417 -0
- package/src/observability/tracer.ts +105 -0
- package/src/observability/types.ts +341 -0
- package/src/orchestration/agent-adapter.ts +33 -0
- package/src/orchestration/index.ts +34 -0
- package/src/orchestration/load-balancer.ts +151 -0
- package/src/orchestration/mcp-types.ts +59 -0
- package/src/orchestration/message-bus.ts +192 -0
- package/src/orchestration/orchestrator.ts +349 -0
- package/src/orchestration/pipeline.ts +66 -0
- package/src/orchestration/supervisor.ts +107 -0
- package/src/orchestration/swarm.ts +1099 -0
- package/src/orchestration/toolkit.ts +47 -0
- package/src/orchestration/types.ts +339 -0
- package/src/planner/classical-planner.ts +383 -0
- package/src/planner/index.ts +8 -0
- package/src/planner/llm-planner.ts +353 -0
- package/src/planner/types.ts +227 -0
- package/src/planner/validator.ts +297 -0
- package/src/production/circuit-breaker.ts +290 -0
- package/src/production/graceful-shutdown.ts +251 -0
- package/src/production/health.ts +333 -0
- package/src/production/index.ts +57 -0
- package/src/production/latency-eval.ts +62 -0
- package/src/production/rate-limiter.ts +287 -0
- package/src/production/resumable-stream.ts +289 -0
- package/src/production/types.ts +81 -0
- package/src/sdk/index.ts +374 -0
- package/src/session/db-driver.ts +50 -0
- package/src/session/in-memory-store.ts +235 -0
- package/src/session/index.ts +12 -0
- package/src/session/sql-store.ts +315 -0
- package/src/session/sqlite-store.ts +61 -0
- package/src/session/types.ts +153 -0
- package/src/tools/base-tool.ts +223 -0
- package/src/tools/browser-tool.ts +123 -0
- package/src/tools/calculator-tool.ts +265 -0
- package/src/tools/file-tools.ts +394 -0
- package/src/tools/github-tool.ts +432 -0
- package/src/tools/hackernews-tool.ts +187 -0
- package/src/tools/http-tool.ts +118 -0
- package/src/tools/index.ts +99 -0
- package/src/tools/jira-tool.ts +373 -0
- package/src/tools/notion-tool.ts +322 -0
- package/src/tools/openai-tool.ts +236 -0
- package/src/tools/registry.ts +131 -0
- package/src/tools/serpapi-tool.ts +234 -0
- package/src/tools/shell-tool.ts +118 -0
- package/src/tools/slack-tool.ts +327 -0
- package/src/tools/telegram-tool.ts +127 -0
- package/src/tools/types.ts +229 -0
- package/src/tools/websearch-tool.ts +335 -0
- package/src/tools/wikipedia-tool.ts +177 -0
- package/src/tools/yfinance-tool.ts +33 -0
- package/src/voice/index.ts +17 -0
- package/src/voice/voice-provider.ts +228 -0
- package/tests/artifact.test.ts +241 -0
- package/tests/circuit-breaker.test.ts +171 -0
- package/tests/health.test.ts +192 -0
- package/tests/llm-cache.test.ts +186 -0
- package/tests/rate-limiter.test.ts +161 -0
- package/tsconfig.json +29 -0
- package/vitest.config.ts +47 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker Pool for Parallel Task Execution
|
|
3
|
+
*
|
|
4
|
+
* Manages a pool of workers for executing tasks concurrently
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
WorkerPoolConfig,
|
|
9
|
+
WorkerPoolStatus,
|
|
10
|
+
ParallelExecutor,
|
|
11
|
+
ExecutionContext,
|
|
12
|
+
} from './types.js';
|
|
13
|
+
import {
|
|
14
|
+
Task,
|
|
15
|
+
TaskResult,
|
|
16
|
+
TaskStatus,
|
|
17
|
+
} from '../planner/types.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Worker task wrapper
|
|
21
|
+
*/
|
|
22
|
+
interface WorkerTask {
|
|
23
|
+
readonly task: Task;
|
|
24
|
+
readonly context: ExecutionContext;
|
|
25
|
+
readonly resolve: (result: TaskResult) => void;
|
|
26
|
+
readonly reject: (error: Error) => void;
|
|
27
|
+
readonly enqueuedAt: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Worker state
|
|
32
|
+
*/
|
|
33
|
+
interface Worker {
|
|
34
|
+
readonly id: number;
|
|
35
|
+
busy: boolean;
|
|
36
|
+
currentTask?: WorkerTask;
|
|
37
|
+
lastActiveAt: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Worker pool implementation
|
|
42
|
+
*/
|
|
43
|
+
export class WorkerPool implements ParallelExecutor {
|
|
44
|
+
private config: Required<WorkerPoolConfig>;
|
|
45
|
+
private workers: Worker[] = [];
|
|
46
|
+
private taskQueue: WorkerTask[] = [];
|
|
47
|
+
private completedTasks = 0;
|
|
48
|
+
private shutdown = false;
|
|
49
|
+
private idleTimeoutId?: ReturnType<typeof setTimeout>;
|
|
50
|
+
|
|
51
|
+
constructor(config: WorkerPoolConfig) {
|
|
52
|
+
this.config = {
|
|
53
|
+
minWorkers: config.minWorkers ?? 2,
|
|
54
|
+
maxWorkers: config.maxWorkers ?? 8,
|
|
55
|
+
idleTimeoutMs: config.idleTimeoutMs ?? 60000,
|
|
56
|
+
taskTimeoutMs: config.taskTimeoutMs ?? 30000,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Initialize minimum workers
|
|
60
|
+
this.ensureMinWorkers();
|
|
61
|
+
this.startIdleCleanup();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Execute multiple tasks in parallel
|
|
66
|
+
*/
|
|
67
|
+
async executeParallel(tasks: Task[], context: ExecutionContext): Promise<TaskResult[]> {
|
|
68
|
+
if (this.shutdown) {
|
|
69
|
+
throw new Error('Worker pool is shutting down');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const promises = tasks.map(task => this.enqueueTask(task, context));
|
|
73
|
+
return Promise.all(promises);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get current pool status
|
|
78
|
+
*/
|
|
79
|
+
getPoolStatus(): WorkerPoolStatus {
|
|
80
|
+
const activeWorkers = this.workers.filter(w => w.busy).length;
|
|
81
|
+
return {
|
|
82
|
+
totalWorkers: this.workers.length,
|
|
83
|
+
activeWorkers,
|
|
84
|
+
idleWorkers: this.workers.length - activeWorkers,
|
|
85
|
+
pendingTasks: this.taskQueue.length,
|
|
86
|
+
completedTasks: this.completedTasks,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Shutdown the worker pool
|
|
92
|
+
*/
|
|
93
|
+
async shutdownPool(waitForTasks = true): Promise<void> {
|
|
94
|
+
this.shutdown = true;
|
|
95
|
+
|
|
96
|
+
if (this.idleTimeoutId) {
|
|
97
|
+
clearTimeout(this.idleTimeoutId);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (waitForTasks) {
|
|
101
|
+
// Wait for queued tasks to complete
|
|
102
|
+
while (this.taskQueue.length > 0 || this.workers.some(w => w.busy)) {
|
|
103
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
106
|
+
// Reject all pending tasks
|
|
107
|
+
for (const task of this.taskQueue) {
|
|
108
|
+
task.reject(new Error('Worker pool is shutting down'));
|
|
109
|
+
}
|
|
110
|
+
this.taskQueue = [];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
this.workers = [];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Enqueue a task for execution
|
|
118
|
+
*/
|
|
119
|
+
private enqueueTask(task: Task, context: ExecutionContext): Promise<TaskResult> {
|
|
120
|
+
return new Promise((resolve, reject) => {
|
|
121
|
+
const workerTask: WorkerTask = {
|
|
122
|
+
task,
|
|
123
|
+
context,
|
|
124
|
+
resolve,
|
|
125
|
+
reject,
|
|
126
|
+
enqueuedAt: Date.now(),
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
this.taskQueue.push(workerTask);
|
|
130
|
+
this.processQueue();
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Process the task queue
|
|
136
|
+
*/
|
|
137
|
+
private processQueue(): void {
|
|
138
|
+
if (this.shutdown) return;
|
|
139
|
+
|
|
140
|
+
while (this.taskQueue.length > 0) {
|
|
141
|
+
const worker = this.getAvailableWorker();
|
|
142
|
+
if (!worker) break;
|
|
143
|
+
|
|
144
|
+
const task = this.taskQueue.shift()!;
|
|
145
|
+
this.executeTask(worker, task);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Scale up if needed
|
|
149
|
+
if (this.taskQueue.length > 0 && this.workers.length < this.config.maxWorkers) {
|
|
150
|
+
this.createWorker();
|
|
151
|
+
this.processQueue();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Get an available worker
|
|
157
|
+
*/
|
|
158
|
+
private getAvailableWorker(): Worker | undefined {
|
|
159
|
+
// Find idle worker
|
|
160
|
+
const idleWorker = this.workers.find(w => !w.busy);
|
|
161
|
+
if (idleWorker) return idleWorker;
|
|
162
|
+
|
|
163
|
+
// Create new worker if under max
|
|
164
|
+
if (this.workers.length < this.config.maxWorkers) {
|
|
165
|
+
return this.createWorker();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Create a new worker
|
|
173
|
+
*/
|
|
174
|
+
private createWorker(): Worker {
|
|
175
|
+
const worker: Worker = {
|
|
176
|
+
id: this.workers.length + 1,
|
|
177
|
+
busy: false,
|
|
178
|
+
lastActiveAt: Date.now(),
|
|
179
|
+
};
|
|
180
|
+
this.workers.push(worker);
|
|
181
|
+
return worker;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Execute a task on a worker
|
|
186
|
+
*/
|
|
187
|
+
private async executeTask(worker: Worker, workerTask: WorkerTask): Promise<void> {
|
|
188
|
+
worker.busy = true;
|
|
189
|
+
worker.currentTask = workerTask;
|
|
190
|
+
worker.lastActiveAt = Date.now();
|
|
191
|
+
|
|
192
|
+
const { task, context, resolve, reject } = workerTask;
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
// Create timeout promise
|
|
196
|
+
const timeoutPromise = new Promise<never>((_, timeoutReject) => {
|
|
197
|
+
setTimeout(() => {
|
|
198
|
+
timeoutReject(new Error(`Task ${task.id} timed out after ${this.config.taskTimeoutMs}ms`));
|
|
199
|
+
}, this.config.taskTimeoutMs);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// Execute task with timeout
|
|
203
|
+
const result = await Promise.race([
|
|
204
|
+
this.runTask(task, context),
|
|
205
|
+
timeoutPromise,
|
|
206
|
+
]);
|
|
207
|
+
|
|
208
|
+
this.completedTasks++;
|
|
209
|
+
resolve(result);
|
|
210
|
+
} catch (error) {
|
|
211
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
212
|
+
reject(err);
|
|
213
|
+
} finally {
|
|
214
|
+
worker.busy = false;
|
|
215
|
+
worker.currentTask = undefined;
|
|
216
|
+
worker.lastActiveAt = Date.now();
|
|
217
|
+
|
|
218
|
+
// Process more tasks
|
|
219
|
+
this.processQueue();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Run a task (to be overridden by actual implementation)
|
|
225
|
+
*/
|
|
226
|
+
private async runTask(task: Task, _context: ExecutionContext): Promise<TaskResult> {
|
|
227
|
+
// This is a placeholder - actual task execution would be provided
|
|
228
|
+
// by the execution engine or a task executor
|
|
229
|
+
const startTime = Date.now();
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
// Simulate task execution
|
|
233
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
taskId: task.id,
|
|
237
|
+
status: TaskStatus.COMPLETED,
|
|
238
|
+
output: { message: `Task ${task.name} completed` },
|
|
239
|
+
executionTimeMs: Date.now() - startTime,
|
|
240
|
+
startedAt: new Date(startTime),
|
|
241
|
+
completedAt: new Date(),
|
|
242
|
+
};
|
|
243
|
+
} catch (error) {
|
|
244
|
+
return {
|
|
245
|
+
taskId: task.id,
|
|
246
|
+
status: TaskStatus.FAILED,
|
|
247
|
+
error: {
|
|
248
|
+
code: 'EXECUTION_ERROR',
|
|
249
|
+
message: error instanceof Error ? error.message : String(error),
|
|
250
|
+
retryable: true,
|
|
251
|
+
},
|
|
252
|
+
executionTimeMs: Date.now() - startTime,
|
|
253
|
+
startedAt: new Date(startTime),
|
|
254
|
+
completedAt: new Date(),
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Ensure minimum number of workers
|
|
261
|
+
*/
|
|
262
|
+
private ensureMinWorkers(): void {
|
|
263
|
+
while (this.workers.length < this.config.minWorkers) {
|
|
264
|
+
this.createWorker();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Start idle worker cleanup
|
|
270
|
+
*/
|
|
271
|
+
private startIdleCleanup(): void {
|
|
272
|
+
const cleanup = () => {
|
|
273
|
+
if (this.shutdown) return;
|
|
274
|
+
|
|
275
|
+
const now = Date.now();
|
|
276
|
+
const toRemove: Worker[] = [];
|
|
277
|
+
|
|
278
|
+
// Find idle workers that have been idle too long
|
|
279
|
+
for (const worker of this.workers) {
|
|
280
|
+
if (!worker.busy &&
|
|
281
|
+
this.workers.length > this.config.minWorkers &&
|
|
282
|
+
now - worker.lastActiveAt > this.config.idleTimeoutMs) {
|
|
283
|
+
toRemove.push(worker);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Remove excess idle workers
|
|
288
|
+
for (const worker of toRemove) {
|
|
289
|
+
if (this.workers.length <= this.config.minWorkers) break;
|
|
290
|
+
const index = this.workers.indexOf(worker);
|
|
291
|
+
if (index > -1) {
|
|
292
|
+
this.workers.splice(index, 1);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
this.idleTimeoutId = setTimeout(cleanup, this.config.idleTimeoutMs);
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
this.idleTimeoutId = setTimeout(cleanup, this.config.idleTimeoutMs);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Create a new worker pool
|
|
305
|
+
*/
|
|
306
|
+
export function createWorkerPool(config: WorkerPoolConfig): WorkerPool {
|
|
307
|
+
return new WorkerPool(config);
|
|
308
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extension points for production-grade, multi-agent frameworks.
|
|
3
|
+
*
|
|
4
|
+
* Plug any DB (session/memory), any tools (or ToolRegistry), cross-tool middleware,
|
|
5
|
+
* and use high-level agents (createAgent / Agent) with Orchestrator, Supervisor, Pipeline.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { SessionStore } from '../session/types.js';
|
|
9
|
+
import type { MemoryStore } from '../memory/types.js';
|
|
10
|
+
import type { Tool, ToolRegistry, ToolMiddleware } from '../tools/types.js';
|
|
11
|
+
import type { LLMProvider } from '../llm/types.js';
|
|
12
|
+
import type { GuardrailEngine } from '../guardrails/types.js';
|
|
13
|
+
import { Agent, AgentState } from '../core/types.js';
|
|
14
|
+
import type { AgentInput, AgentOutput, AgentContext } from '../core/types.js';
|
|
15
|
+
import type { AgentRunOptions } from '../create-agent.js';
|
|
16
|
+
import type { AgenticRunResult } from '../agentic/types.js';
|
|
17
|
+
|
|
18
|
+
// Re-export extension types and tool provider for convenience
|
|
19
|
+
export type { SessionStore, MemoryStore, Tool, ToolRegistry, ToolMiddleware, LLMProvider, GuardrailEngine };
|
|
20
|
+
export { toToolRegistry, type ToolProvider } from '../tools/registry.js';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Create a tool middleware that logs tool calls and results (easy cross-tool integration).
|
|
24
|
+
* Pass to createAgent({ toolMiddleware: [createLoggingToolMiddleware(logger)] }) or Agent({ toolMiddleware: [...] }).
|
|
25
|
+
*/
|
|
26
|
+
export function createLoggingToolMiddleware(
|
|
27
|
+
log: (msg: string, meta?: Record<string, unknown>) => void = (msg, meta) => console.log(`[tool] ${msg}`, meta ?? {})
|
|
28
|
+
): ToolMiddleware {
|
|
29
|
+
return {
|
|
30
|
+
beforeExecute(tool, params) {
|
|
31
|
+
log('tool.before', { tool: tool.name, params });
|
|
32
|
+
},
|
|
33
|
+
afterExecute(tool, result, ctx) {
|
|
34
|
+
log('tool.after', {
|
|
35
|
+
tool: tool.name,
|
|
36
|
+
success: result.success,
|
|
37
|
+
executionTimeMs: result.executionTimeMs,
|
|
38
|
+
agentId: ctx.agentId,
|
|
39
|
+
sessionId: ctx.sessionId,
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
onError(tool, error, ctx) {
|
|
43
|
+
log('tool.error', {
|
|
44
|
+
tool: tool.name,
|
|
45
|
+
error: error.message,
|
|
46
|
+
agentId: ctx.agentId,
|
|
47
|
+
sessionId: ctx.sessionId,
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Session store provider: plug any DB (SQLite, Postgres, Redis, etc.).
|
|
55
|
+
* Implement SessionStore and pass to createAgent({ sessionStore }) or Agent({ db }).
|
|
56
|
+
*/
|
|
57
|
+
export type SessionStoreProvider = SessionStore;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Memory store provider: plug any vector/RAG store for long-term knowledge.
|
|
61
|
+
* Implement MemoryStore and pass via agent options when supported.
|
|
62
|
+
*/
|
|
63
|
+
export type MemoryStoreProvider = MemoryStore;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Minimal interface for high-level agents (createAgent return value or Agent class).
|
|
67
|
+
* Use with wrapAgentForOrchestration so both work in Orchestrator, Supervisor, Pipeline.
|
|
68
|
+
*/
|
|
69
|
+
export interface RunnableHighLevelAgent {
|
|
70
|
+
readonly name: string;
|
|
71
|
+
readonly instructions: string;
|
|
72
|
+
run(prompt: string, options?: AgentRunOptions): Promise<AgenticRunResult>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Wrap a high-level agent (createAgent / Agent) so it can be used with
|
|
77
|
+
* Orchestrator, Supervisor, and Pipeline (core.Agent with run(AgentInput, AgentContext) => AgentOutput).
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* const highLevelAgent = createAgent({ name: 'Researcher', instructions: '...' });
|
|
81
|
+
* const coreAgent = wrapAgentForOrchestration(highLevelAgent);
|
|
82
|
+
* const pipeline = createPipeline({ name: 'Research', agents: [coreAgent, writerAgent] });
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* const agent = new Agent({ instructions: '...' });
|
|
86
|
+
* const coreAgent = wrapAgentForOrchestration(agent);
|
|
87
|
+
*/
|
|
88
|
+
export function wrapAgentForOrchestration(agent: RunnableHighLevelAgent): Agent {
|
|
89
|
+
return new OrchestrationAgentAdapter(agent);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
class OrchestrationAgentAdapter extends Agent {
|
|
93
|
+
constructor(private readonly delegate: RunnableHighLevelAgent) {
|
|
94
|
+
super({
|
|
95
|
+
name: delegate.name,
|
|
96
|
+
description: delegate.instructions,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async run(input: AgentInput, _ctx: AgentContext): Promise<AgentOutput> {
|
|
101
|
+
const ctx = input.context ?? {};
|
|
102
|
+
const result = await this.delegate.run(input.prompt, {
|
|
103
|
+
sessionId: ctx.sessionId as string | undefined,
|
|
104
|
+
userId: ctx.userId as string | undefined,
|
|
105
|
+
});
|
|
106
|
+
const state =
|
|
107
|
+
result.finishReason === 'stop'
|
|
108
|
+
? AgentState.COMPLETED
|
|
109
|
+
: result.finishReason === 'error' || result.finishReason === 'timeout'
|
|
110
|
+
? AgentState.FAILED
|
|
111
|
+
: AgentState.COMPLETED;
|
|
112
|
+
return {
|
|
113
|
+
result: result.text,
|
|
114
|
+
state,
|
|
115
|
+
metadata: {
|
|
116
|
+
startTime: new Date(),
|
|
117
|
+
durationMs: 0,
|
|
118
|
+
iterations: result.steps,
|
|
119
|
+
tokensUsed: result.usage?.totalTokens,
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Allowlist implementation for guardrails
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { AllowlistConfig, GuardrailRule, GuardrailResult, GuardrailContext } from './types.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Create an allowlist-based guardrail rule
|
|
9
|
+
*/
|
|
10
|
+
export function createAllowlistRule(config: AllowlistConfig): GuardrailRule {
|
|
11
|
+
return {
|
|
12
|
+
name: 'allowlist',
|
|
13
|
+
description: 'Enforces allowlist restrictions on tools, hosts, paths, and outputs',
|
|
14
|
+
severity: 'error',
|
|
15
|
+
check(context: GuardrailContext): GuardrailResult {
|
|
16
|
+
// Check tool allowlist
|
|
17
|
+
if (config.allowedTools && context.toolName) {
|
|
18
|
+
if (!config.allowedTools.includes(context.toolName)) {
|
|
19
|
+
return {
|
|
20
|
+
passed: false,
|
|
21
|
+
rule: 'allowlist',
|
|
22
|
+
message: `Tool '${context.toolName}' is not in the allowed tools list`,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Check host allowlist for HTTP tools
|
|
28
|
+
if (config.allowedHosts && context.toolArgs?.url) {
|
|
29
|
+
const url = String(context.toolArgs.url);
|
|
30
|
+
const allowed = config.allowedHosts.some(host => url.includes(host));
|
|
31
|
+
if (!allowed) {
|
|
32
|
+
return {
|
|
33
|
+
passed: false,
|
|
34
|
+
rule: 'allowlist',
|
|
35
|
+
message: `Host in URL '${url}' is not in the allowed hosts list`,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Check blocked patterns
|
|
41
|
+
if (config.blockedPatterns) {
|
|
42
|
+
const raw = context.output;
|
|
43
|
+
const content =
|
|
44
|
+
typeof raw === 'string' ? raw : raw === undefined || raw === null ? '' : JSON.stringify(raw);
|
|
45
|
+
|
|
46
|
+
for (const pattern of config.blockedPatterns) {
|
|
47
|
+
if (pattern.test(content)) {
|
|
48
|
+
return {
|
|
49
|
+
passed: false,
|
|
50
|
+
rule: 'allowlist',
|
|
51
|
+
message: `Content matches blocked pattern: ${pattern}`,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return { passed: true, rule: 'allowlist' };
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Default sensitive data patterns to block
|
|
64
|
+
*/
|
|
65
|
+
export const SENSITIVE_DATA_PATTERNS: RegExp[] = [
|
|
66
|
+
// Credit card numbers
|
|
67
|
+
/\b(?:\d[ -]*?){13,16}\b/,
|
|
68
|
+
// Social Security Numbers (SSN)
|
|
69
|
+
/\b\d{3}[ -]?\d{2}[ -]?\d{4}\b/,
|
|
70
|
+
// API keys (common patterns)
|
|
71
|
+
// eslint-disable-next-line no-useless-escape
|
|
72
|
+
/['"\s](?:api[_-]?key|apikey|token|secret)[\s]*[:=][\s]*['"][a-zA-Z0-9_\-]{16,}['"\s]/i,
|
|
73
|
+
// Private keys
|
|
74
|
+
/-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,
|
|
75
|
+
// Passwords in URLs
|
|
76
|
+
/[?&](?:password|passwd|pwd)=[^&\s]+/i,
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Create a rule to block sensitive data
|
|
81
|
+
*/
|
|
82
|
+
export function createSensitiveDataRule(): GuardrailRule {
|
|
83
|
+
return {
|
|
84
|
+
name: 'sensitive_data',
|
|
85
|
+
description: 'Blocks common sensitive data patterns like credit cards, SSNs, API keys',
|
|
86
|
+
severity: 'error',
|
|
87
|
+
check(context: GuardrailContext): GuardrailResult {
|
|
88
|
+
const raw = context.output;
|
|
89
|
+
const content =
|
|
90
|
+
typeof raw === 'string' ? raw : raw === undefined || raw === null ? '' : JSON.stringify(raw);
|
|
91
|
+
|
|
92
|
+
for (const pattern of SENSITIVE_DATA_PATTERNS) {
|
|
93
|
+
if (pattern.test(content)) {
|
|
94
|
+
return {
|
|
95
|
+
passed: false,
|
|
96
|
+
rule: 'sensitive_data',
|
|
97
|
+
message: `Output may contain sensitive data matching pattern: ${pattern}`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return { passed: true, rule: 'sensitive_data' };
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Create a URL validation rule
|
|
109
|
+
*/
|
|
110
|
+
export function createUrlValidationRule(
|
|
111
|
+
allowedProtocols: string[] = ['https:'],
|
|
112
|
+
allowedHosts?: string[]
|
|
113
|
+
): GuardrailRule {
|
|
114
|
+
return {
|
|
115
|
+
name: 'url_validation',
|
|
116
|
+
description: `Validates URLs use allowed protocols (${allowedProtocols.join(', ')})`,
|
|
117
|
+
severity: 'error',
|
|
118
|
+
check(context: GuardrailContext): GuardrailResult {
|
|
119
|
+
const url = context.toolArgs?.url;
|
|
120
|
+
if (!url || typeof url !== 'string') {
|
|
121
|
+
return { passed: true, rule: 'url_validation' };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const parsed = new URL(url);
|
|
126
|
+
|
|
127
|
+
// Check protocol
|
|
128
|
+
if (!allowedProtocols.includes(parsed.protocol)) {
|
|
129
|
+
return {
|
|
130
|
+
passed: false,
|
|
131
|
+
rule: 'url_validation',
|
|
132
|
+
message: `Protocol '${parsed.protocol}' is not allowed. Allowed: ${allowedProtocols.join(', ')}`,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Check host if allowlist provided
|
|
137
|
+
if (allowedHosts && !allowedHosts.includes(parsed.hostname)) {
|
|
138
|
+
return {
|
|
139
|
+
passed: false,
|
|
140
|
+
rule: 'url_validation',
|
|
141
|
+
message: `Host '${parsed.hostname}' is not in the allowed hosts list`,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return { passed: true, rule: 'url_validation' };
|
|
146
|
+
} catch {
|
|
147
|
+
return {
|
|
148
|
+
passed: false,
|
|
149
|
+
rule: 'url_validation',
|
|
150
|
+
message: `Invalid URL: ${url}`,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guardrails module for output validation and safety controls
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export * from './types';
|
|
6
|
+
export {
|
|
7
|
+
GuardrailValidator,
|
|
8
|
+
createContentRule,
|
|
9
|
+
createToolAllowlistRule,
|
|
10
|
+
createMaxLengthRule,
|
|
11
|
+
} from './validator';
|
|
12
|
+
export {
|
|
13
|
+
createAllowlistRule,
|
|
14
|
+
createSensitiveDataRule,
|
|
15
|
+
createUrlValidationRule,
|
|
16
|
+
SENSITIVE_DATA_PATTERNS,
|
|
17
|
+
} from './allowlist';
|