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.
Files changed (114) hide show
  1. package/FEATURES.md +169 -0
  2. package/package.json +119 -0
  3. package/src/agent.ts +187 -0
  4. package/src/agentic/index.ts +87 -0
  5. package/src/agentic/runner.ts +386 -0
  6. package/src/agentic/types.ts +91 -0
  7. package/src/artifacts/artifact.ts +417 -0
  8. package/src/artifacts/index.ts +42 -0
  9. package/src/artifacts/media.ts +304 -0
  10. package/src/cli/index.ts +122 -0
  11. package/src/core/base-agent.ts +151 -0
  12. package/src/core/context-builder.ts +106 -0
  13. package/src/core/index.ts +8 -0
  14. package/src/core/schemas.ts +17 -0
  15. package/src/core/types.ts +158 -0
  16. package/src/create-agent.ts +309 -0
  17. package/src/debug-logger.ts +188 -0
  18. package/src/dx/agent.ts +88 -0
  19. package/src/dx/define-agent.ts +183 -0
  20. package/src/dx/dev-logger.ts +57 -0
  21. package/src/dx/index.ts +11 -0
  22. package/src/errors.ts +175 -0
  23. package/src/execution/engine.ts +522 -0
  24. package/src/execution/graph-builder.ts +362 -0
  25. package/src/execution/index.ts +8 -0
  26. package/src/execution/types.ts +257 -0
  27. package/src/execution/worker-pool.ts +308 -0
  28. package/src/extensions/index.ts +123 -0
  29. package/src/guardrails/allowlist.ts +155 -0
  30. package/src/guardrails/index.ts +17 -0
  31. package/src/guardrails/types.ts +159 -0
  32. package/src/guardrails/validator.ts +265 -0
  33. package/src/index.ts +74 -0
  34. package/src/knowledge/index.ts +5 -0
  35. package/src/knowledge/types.ts +52 -0
  36. package/src/learning/in-memory-store.ts +72 -0
  37. package/src/learning/index.ts +6 -0
  38. package/src/learning/types.ts +42 -0
  39. package/src/llm/cache.ts +300 -0
  40. package/src/llm/index.ts +22 -0
  41. package/src/llm/model-resolver.ts +81 -0
  42. package/src/llm/openai-provider.ts +313 -0
  43. package/src/llm/openrouter-provider.ts +29 -0
  44. package/src/llm/types.ts +131 -0
  45. package/src/memory/in-memory-store.ts +255 -0
  46. package/src/memory/index.ts +7 -0
  47. package/src/memory/types.ts +193 -0
  48. package/src/memory/vector-store.ts +251 -0
  49. package/src/observability/console-logger.ts +123 -0
  50. package/src/observability/index.ts +12 -0
  51. package/src/observability/metrics.ts +85 -0
  52. package/src/observability/otlp-exporter.ts +417 -0
  53. package/src/observability/tracer.ts +105 -0
  54. package/src/observability/types.ts +341 -0
  55. package/src/orchestration/agent-adapter.ts +33 -0
  56. package/src/orchestration/index.ts +34 -0
  57. package/src/orchestration/load-balancer.ts +151 -0
  58. package/src/orchestration/mcp-types.ts +59 -0
  59. package/src/orchestration/message-bus.ts +192 -0
  60. package/src/orchestration/orchestrator.ts +349 -0
  61. package/src/orchestration/pipeline.ts +66 -0
  62. package/src/orchestration/supervisor.ts +107 -0
  63. package/src/orchestration/swarm.ts +1099 -0
  64. package/src/orchestration/toolkit.ts +47 -0
  65. package/src/orchestration/types.ts +339 -0
  66. package/src/planner/classical-planner.ts +383 -0
  67. package/src/planner/index.ts +8 -0
  68. package/src/planner/llm-planner.ts +353 -0
  69. package/src/planner/types.ts +227 -0
  70. package/src/planner/validator.ts +297 -0
  71. package/src/production/circuit-breaker.ts +290 -0
  72. package/src/production/graceful-shutdown.ts +251 -0
  73. package/src/production/health.ts +333 -0
  74. package/src/production/index.ts +57 -0
  75. package/src/production/latency-eval.ts +62 -0
  76. package/src/production/rate-limiter.ts +287 -0
  77. package/src/production/resumable-stream.ts +289 -0
  78. package/src/production/types.ts +81 -0
  79. package/src/sdk/index.ts +374 -0
  80. package/src/session/db-driver.ts +50 -0
  81. package/src/session/in-memory-store.ts +235 -0
  82. package/src/session/index.ts +12 -0
  83. package/src/session/sql-store.ts +315 -0
  84. package/src/session/sqlite-store.ts +61 -0
  85. package/src/session/types.ts +153 -0
  86. package/src/tools/base-tool.ts +223 -0
  87. package/src/tools/browser-tool.ts +123 -0
  88. package/src/tools/calculator-tool.ts +265 -0
  89. package/src/tools/file-tools.ts +394 -0
  90. package/src/tools/github-tool.ts +432 -0
  91. package/src/tools/hackernews-tool.ts +187 -0
  92. package/src/tools/http-tool.ts +118 -0
  93. package/src/tools/index.ts +99 -0
  94. package/src/tools/jira-tool.ts +373 -0
  95. package/src/tools/notion-tool.ts +322 -0
  96. package/src/tools/openai-tool.ts +236 -0
  97. package/src/tools/registry.ts +131 -0
  98. package/src/tools/serpapi-tool.ts +234 -0
  99. package/src/tools/shell-tool.ts +118 -0
  100. package/src/tools/slack-tool.ts +327 -0
  101. package/src/tools/telegram-tool.ts +127 -0
  102. package/src/tools/types.ts +229 -0
  103. package/src/tools/websearch-tool.ts +335 -0
  104. package/src/tools/wikipedia-tool.ts +177 -0
  105. package/src/tools/yfinance-tool.ts +33 -0
  106. package/src/voice/index.ts +17 -0
  107. package/src/voice/voice-provider.ts +228 -0
  108. package/tests/artifact.test.ts +241 -0
  109. package/tests/circuit-breaker.test.ts +171 -0
  110. package/tests/health.test.ts +192 -0
  111. package/tests/llm-cache.test.ts +186 -0
  112. package/tests/rate-limiter.test.ts +161 -0
  113. package/tsconfig.json +29 -0
  114. 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';