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,59 @@
1
+ /**
2
+ * MCP (Model Context Protocol) and A2A (Agent-to-Agent) support.
3
+ * First-class integration for external tools and cross-agent communication.
4
+ */
5
+
6
+ import type { Tool } from '../tools/types.js';
7
+
8
+ /** MCP tool descriptor (from MCP server) */
9
+ export interface MCPToolDescriptor {
10
+ readonly name: string;
11
+ readonly description?: string;
12
+ readonly inputSchema?: Record<string, unknown>;
13
+ }
14
+
15
+ /** MCP client: connect to an MCP server and expose tools to the agent */
16
+ export interface MCPClient {
17
+ /** List tools offered by the MCP server */
18
+ listTools(): Promise<MCPToolDescriptor[]>;
19
+
20
+ /** Call a tool by name with arguments */
21
+ callTool(name: string, args: Record<string, unknown>): Promise<{ content: Array<{ type: string; text?: string }> }>;
22
+
23
+ /** Optional: get framework Tool adapters for registry */
24
+ getTools?(): Promise<Tool[]>;
25
+
26
+ /** Disconnect / cleanup */
27
+ disconnect?(): Promise<void>;
28
+ }
29
+
30
+ /** MCP server adapter: expose framework tools via MCP (for external clients) */
31
+ export interface MCPServerAdapter {
32
+ /** Start serving (e.g. stdio or HTTP) */
33
+ start(): Promise<void>;
34
+
35
+ /** Stop serving */
36
+ stop(): Promise<void>;
37
+
38
+ /** Register a tool to expose */
39
+ registerTool(tool: Tool): void;
40
+ }
41
+
42
+ /** A2A (Agent-to-Agent) message for cross-agent communication */
43
+ export interface A2AMessage {
44
+ readonly id: string;
45
+ readonly from: string;
46
+ readonly to: string | string[];
47
+ readonly type: 'request' | 'response' | 'event';
48
+ readonly payload: unknown;
49
+ readonly timestamp: Date;
50
+ readonly correlationId?: string;
51
+ }
52
+
53
+ /** A2A client: send/receive messages to other agents */
54
+ export interface A2AClient {
55
+ send(message: Omit<A2AMessage, 'id' | 'timestamp'>): Promise<A2AMessage>;
56
+
57
+ /** Subscribe to incoming messages (e.g. for this agent id) */
58
+ subscribe(agentId: string, handler: (msg: A2AMessage) => void | Promise<void>): () => void;
59
+ }
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Message Bus Implementation
3
+ *
4
+ * Handles inter-agent communication with pub/sub pattern
5
+ */
6
+
7
+ import {
8
+ MessageBus,
9
+ AgentMessage,
10
+ MessageType,
11
+ MessageFilter,
12
+ MessageHandler,
13
+ Subscription,
14
+ MessagePriority,
15
+ } from './types.js';
16
+ import type { EntityId } from '../core/types.js';
17
+
18
+ /**
19
+ * Message bus implementation
20
+ */
21
+ export class MessageBusImpl implements MessageBus {
22
+ private messages: AgentMessage[] = [];
23
+ private subscriptions: Map<EntityId, Set<SubscriptionImpl>> = new Map();
24
+ private pendingRequests: Map<string, { resolve: (value: unknown) => void; reject: (error: Error) => void; timeout: ReturnType<typeof setTimeout> }> = new Map();
25
+ private messageCounter = 0;
26
+
27
+ async send(message: Omit<AgentMessage, 'id' | 'timestamp'>): Promise<AgentMessage> {
28
+ const fullMessage: AgentMessage = {
29
+ ...message,
30
+ id: this.generateId(),
31
+ timestamp: new Date(),
32
+ };
33
+
34
+ this.messages.push(fullMessage);
35
+
36
+ // Deliver to subscribers
37
+ await this.deliverMessage(fullMessage);
38
+
39
+ return fullMessage;
40
+ }
41
+
42
+ subscribe(
43
+ subscriberId: EntityId,
44
+ filter: MessageFilter,
45
+ handler: MessageHandler
46
+ ): Subscription {
47
+ const subscription: SubscriptionImpl = {
48
+ id: this.generateId(),
49
+ subscriberId,
50
+ filter,
51
+ handler,
52
+ unsubscribe: () => {
53
+ const subs = this.subscriptions.get(subscriberId);
54
+ if (subs) {
55
+ subs.delete(subscription);
56
+ }
57
+ },
58
+ };
59
+
60
+ if (!this.subscriptions.has(subscriberId)) {
61
+ this.subscriptions.set(subscriberId, new Set());
62
+ }
63
+
64
+ this.subscriptions.get(subscriberId)!.add(subscription);
65
+
66
+ return subscription;
67
+ }
68
+
69
+ unsubscribe(subscription: Subscription): void {
70
+ subscription.unsubscribe();
71
+ }
72
+
73
+ async request<T>(to: EntityId, payload: unknown, timeoutMs = 30000): Promise<T> {
74
+ const correlationId = this.generateId();
75
+
76
+ return new Promise((resolve, reject) => {
77
+ const timeout = setTimeout(() => {
78
+ this.pendingRequests.delete(correlationId);
79
+ reject(new Error(`Request timeout after ${timeoutMs}ms`));
80
+ }, timeoutMs);
81
+
82
+ this.pendingRequests.set(correlationId, {
83
+ resolve: resolve as (value: unknown) => void,
84
+ reject,
85
+ timeout,
86
+ });
87
+
88
+ // Send request message
89
+ this.send({
90
+ from: 'orchestrator',
91
+ to,
92
+ type: MessageType.QUERY,
93
+ payload,
94
+ priority: MessagePriority.HIGH,
95
+ correlationId,
96
+ }).catch(reject);
97
+ });
98
+ }
99
+
100
+ /**
101
+ * Respond to a request
102
+ */
103
+ async respond(correlationId: string, payload: unknown): Promise<void> {
104
+ const pending = this.pendingRequests.get(correlationId);
105
+ if (pending) {
106
+ clearTimeout(pending.timeout);
107
+ this.pendingRequests.delete(correlationId);
108
+ pending.resolve(payload);
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Get message history
114
+ */
115
+ getMessages(filter?: MessageFilter): AgentMessage[] {
116
+ if (!filter) {
117
+ return [...this.messages];
118
+ }
119
+
120
+ return this.messages.filter(msg => this.matchesFilter(msg, filter));
121
+ }
122
+
123
+ private async deliverMessage(message: AgentMessage): Promise<void> {
124
+ const targetId = message.to;
125
+
126
+ if (targetId === 'broadcast') {
127
+ // Deliver to all subscribers
128
+ for (const [_, subs] of this.subscriptions) {
129
+ for (const sub of subs) {
130
+ if (this.matchesFilter(message, sub.filter)) {
131
+ try {
132
+ await sub.handler(message);
133
+ } catch (error) {
134
+ console.error('Error handling message:', error);
135
+ }
136
+ }
137
+ }
138
+ }
139
+ } else {
140
+ // Deliver to specific subscriber
141
+ const subs = this.subscriptions.get(targetId);
142
+ if (subs) {
143
+ for (const sub of subs) {
144
+ if (this.matchesFilter(message, sub.filter)) {
145
+ try {
146
+ await sub.handler(message);
147
+ } catch (error) {
148
+ console.error('Error handling message:', error);
149
+ }
150
+ }
151
+ }
152
+ }
153
+ }
154
+
155
+ // Handle response messages
156
+ if (message.correlationId && message.type === MessageType.TASK_RESPONSE) {
157
+ await this.respond(message.correlationId, message.payload);
158
+ }
159
+ }
160
+
161
+ private matchesFilter(message: AgentMessage, filter: MessageFilter): boolean {
162
+ if (filter.from && message.from !== filter.from) {
163
+ return false;
164
+ }
165
+
166
+ if (filter.types && !filter.types.includes(message.type)) {
167
+ return false;
168
+ }
169
+
170
+ if (filter.correlationId && message.correlationId !== filter.correlationId) {
171
+ return false;
172
+ }
173
+
174
+ if (filter.minPriority !== undefined && message.priority > filter.minPriority) {
175
+ return false;
176
+ }
177
+
178
+ return true;
179
+ }
180
+
181
+ private generateId(): string {
182
+ return `msg-${Date.now()}-${++this.messageCounter}`;
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Subscription implementation
188
+ */
189
+ interface SubscriptionImpl extends Subscription {
190
+ filter: MessageFilter;
191
+ handler: MessageHandler;
192
+ }
@@ -0,0 +1,349 @@
1
+ /**
2
+ * Multi-Agent Orchestrator Implementation
3
+ *
4
+ * Coordinates multiple agents with message passing and load balancing
5
+ */
6
+
7
+ import {
8
+ Orchestrator,
9
+ AgentRegistration,
10
+ AgentRole,
11
+ MessageType,
12
+ MessagePriority,
13
+ DelegationTask,
14
+ DelegationResult,
15
+ DelegationStatus,
16
+ DelegationPriority,
17
+ CoordinationTask,
18
+ CoordinationResult,
19
+ CoordinationType,
20
+ CoordinationStatus,
21
+ } from './types.js';
22
+ import type { MessageBus, Subscription } from './types.js';
23
+ import { MessageBusImpl } from './message-bus';
24
+ import { RoundRobinLoadBalancer } from './load-balancer';
25
+ import type { Agent, AgentInput, AgentContext } from '../core/types.js';
26
+ import type { AgentOutput } from '../core/types.js';
27
+ import { AgentState } from '../core/types.js';
28
+ import type { EntityId } from '../core/types.js';
29
+ import type { LoadBalancer } from './types.js';
30
+ import { AgentContextBuilder } from '../core/context-builder.js';
31
+ import { InMemoryStore } from '../memory/in-memory-store.js';
32
+ import { ToolRegistryImpl } from '../tools/registry.js';
33
+ import { ClassicalPlanner } from '../planner/classical-planner.js';
34
+ import { PlanningAlgorithm } from '../planner/types.js';
35
+
36
+ const ORCHESTRATOR_ID = 'orchestrator' as EntityId;
37
+
38
+ /**
39
+ * Orchestrator implementation
40
+ */
41
+ export class OrchestratorImpl implements Orchestrator {
42
+ private agents: Map<EntityId, AgentRegistration> = new Map();
43
+ private subscriptions: Map<EntityId, Subscription> = new Map();
44
+ private messageBus: MessageBus;
45
+ private loadBalancer: LoadBalancer;
46
+ private _isRunning = false;
47
+
48
+ constructor(
49
+ messageBus?: MessageBus,
50
+ loadBalancer?: LoadBalancer
51
+ ) {
52
+ this.messageBus = messageBus ?? new MessageBusImpl();
53
+ this.loadBalancer = loadBalancer ?? new RoundRobinLoadBalancer();
54
+ }
55
+
56
+ async registerAgent(agent: Agent, role: AgentRole): Promise<void> {
57
+ const registration: AgentRegistration = {
58
+ agent,
59
+ role,
60
+ capabilities: role.responsibilities,
61
+ metadata: {
62
+ registeredAt: new Date(),
63
+ totalTasksCompleted: 0,
64
+ totalTasksFailed: 0,
65
+ averageExecutionTimeMs: 0,
66
+ currentLoad: 0,
67
+ maxConcurrentTasks: role.permissions.canExecuteTools ? 5 : 1,
68
+ },
69
+ };
70
+
71
+ this.agents.set(agent.id, registration);
72
+
73
+ const subscription = this.messageBus.subscribe(
74
+ agent.id,
75
+ { types: [MessageType.QUERY] },
76
+ async (msg) => {
77
+ const payload = msg.payload as { type?: string; task?: AgentInput };
78
+ const taskInput = (payload?.task ?? payload) as AgentInput;
79
+ const ctx = createMinimalContext(agent);
80
+ try {
81
+ const output = await agent.run(taskInput, ctx);
82
+ await this.messageBus.send({
83
+ from: agent.id,
84
+ to: ORCHESTRATOR_ID,
85
+ type: MessageType.TASK_RESPONSE,
86
+ payload: output,
87
+ correlationId: msg.correlationId,
88
+ priority: MessagePriority.NORMAL,
89
+ });
90
+ } catch (err) {
91
+ const errorOutput: AgentOutput = {
92
+ result: err instanceof Error ? err.message : String(err),
93
+ state: AgentState.FAILED,
94
+ metadata: { startTime: new Date(), iterations: 0 },
95
+ };
96
+ await this.messageBus.send({
97
+ from: agent.id,
98
+ to: ORCHESTRATOR_ID,
99
+ type: MessageType.TASK_RESPONSE,
100
+ payload: errorOutput,
101
+ correlationId: msg.correlationId,
102
+ priority: MessagePriority.NORMAL,
103
+ });
104
+ }
105
+ }
106
+ );
107
+ this.subscriptions.set(agent.id, subscription);
108
+
109
+ // Notify other agents
110
+ await this.broadcast({
111
+ type: 'agent_registered',
112
+ agentId: agent.id,
113
+ role: role.name,
114
+ }, MessageType.EVENT);
115
+ }
116
+
117
+ async unregisterAgent(agentId: EntityId): Promise<void> {
118
+ const registration = this.agents.get(agentId);
119
+ if (!registration) {
120
+ throw new Error(`Agent ${agentId} not found`);
121
+ }
122
+
123
+ const subscription = this.subscriptions.get(agentId);
124
+ if (subscription) {
125
+ this.messageBus.unsubscribe(subscription);
126
+ this.subscriptions.delete(agentId);
127
+ }
128
+ this.agents.delete(agentId);
129
+
130
+ await this.broadcast({
131
+ type: 'agent_unregistered',
132
+ agentId,
133
+ }, MessageType.EVENT);
134
+ }
135
+
136
+ getAgent(agentId: EntityId): AgentRegistration | undefined {
137
+ return this.agents.get(agentId);
138
+ }
139
+
140
+ listAgents(): AgentRegistration[] {
141
+ return Array.from(this.agents.values());
142
+ }
143
+
144
+ findAgentsByRole(roleName: string): AgentRegistration[] {
145
+ return Array.from(this.agents.values()).filter(
146
+ reg => reg.role.name === roleName
147
+ );
148
+ }
149
+
150
+ findAgentsByCapability(capability: string): AgentRegistration[] {
151
+ return Array.from(this.agents.values()).filter(
152
+ reg => reg.capabilities.includes(capability)
153
+ );
154
+ }
155
+
156
+ async delegateTask(task: DelegationTask, options?: { timeoutMs?: number; retryCount?: number }): Promise<DelegationResult> {
157
+ const startTime = Date.now();
158
+
159
+ // Find candidates with required capabilities
160
+ const candidates = this.findAgentsByCapabilities(task.requiredCapabilities);
161
+
162
+ if (candidates.length === 0) {
163
+ return {
164
+ taskId: task.id,
165
+ assignedAgentId: '',
166
+ status: DelegationStatus.FAILED,
167
+ error: 'No agents found with required capabilities',
168
+ executionTimeMs: Date.now() - startTime,
169
+ };
170
+ }
171
+
172
+ // Select best agent using load balancer
173
+ const selected = this.loadBalancer.selectAgent(candidates, task);
174
+
175
+ if (!selected) {
176
+ return {
177
+ taskId: task.id,
178
+ assignedAgentId: '',
179
+ status: DelegationStatus.REJECTED,
180
+ error: 'No agent available to handle task',
181
+ executionTimeMs: Date.now() - startTime,
182
+ };
183
+ }
184
+
185
+ try {
186
+ // Send task to selected agent
187
+ const response = await this.messageBus.request(
188
+ selected.agent.id,
189
+ {
190
+ type: 'task',
191
+ task: task.input,
192
+ },
193
+ options?.timeoutMs ?? 30000
194
+ );
195
+
196
+ // Update metrics
197
+ this.loadBalancer.updateMetrics(
198
+ selected.agent.id,
199
+ Date.now() - startTime,
200
+ true
201
+ );
202
+
203
+ return {
204
+ taskId: task.id,
205
+ assignedAgentId: selected.agent.id,
206
+ status: DelegationStatus.COMPLETED,
207
+ output: response as AgentOutput,
208
+ executionTimeMs: Date.now() - startTime,
209
+ };
210
+ } catch (error) {
211
+ // Update metrics
212
+ this.loadBalancer.updateMetrics(
213
+ selected.agent.id,
214
+ Date.now() - startTime,
215
+ false
216
+ );
217
+
218
+ return {
219
+ taskId: task.id,
220
+ assignedAgentId: selected.agent.id,
221
+ status: DelegationStatus.FAILED,
222
+ error: error instanceof Error ? error.message : String(error),
223
+ executionTimeMs: Date.now() - startTime,
224
+ };
225
+ }
226
+ }
227
+
228
+ async broadcast(payload: unknown, type: MessageType = MessageType.NOTIFICATION): Promise<void> {
229
+ for (const [agentId] of this.agents) {
230
+ await this.messageBus.send({
231
+ from: ORCHESTRATOR_ID,
232
+ to: agentId,
233
+ type,
234
+ payload,
235
+ priority: MessagePriority.NORMAL,
236
+ });
237
+ }
238
+ }
239
+
240
+ getMessageBus(): MessageBus {
241
+ return this.messageBus;
242
+ }
243
+
244
+ async start(): Promise<void> {
245
+ this._isRunning = true;
246
+ }
247
+
248
+ async stop(): Promise<void> {
249
+ this._isRunning = false;
250
+ }
251
+
252
+ /**
253
+ * Check if orchestrator is running
254
+ */
255
+ isRunning(): boolean {
256
+ return this._isRunning;
257
+ }
258
+
259
+ /**
260
+ * Coordinate multiple agents for a complex task
261
+ */
262
+ async coordinate(agents: AgentRegistration[], task: CoordinationTask): Promise<CoordinationResult> {
263
+ const startTime = Date.now();
264
+ const results = new Map<EntityId, AgentOutput>();
265
+
266
+ switch (task.coordinationType) {
267
+ case CoordinationType.SEQUENTIAL:
268
+ for (const agentReg of agents) {
269
+ const result = await this.delegateTask({
270
+ id: `${task.id}-${agentReg.agent.id}`,
271
+ description: task.description,
272
+ requiredCapabilities: agentReg.capabilities,
273
+ input: { prompt: task.description },
274
+ priority: DelegationPriority.NORMAL,
275
+ });
276
+
277
+ if (result.output) {
278
+ results.set(agentReg.agent.id, result.output);
279
+ }
280
+ }
281
+ break;
282
+
283
+ case CoordinationType.PARALLEL:
284
+ const promises = agents.map(agentReg =>
285
+ this.delegateTask({
286
+ id: `${task.id}-${agentReg.agent.id}`,
287
+ description: task.description,
288
+ requiredCapabilities: agentReg.capabilities,
289
+ input: { prompt: task.description },
290
+ priority: DelegationPriority.NORMAL,
291
+ })
292
+ );
293
+
294
+ const parallelResults = await Promise.all(promises);
295
+ for (let i = 0; i < agents.length; i++) {
296
+ const output = parallelResults[i].output;
297
+ if (output) {
298
+ results.set(agents[i].agent.id, output);
299
+ }
300
+ }
301
+ break;
302
+
303
+ default:
304
+ // Default to sequential
305
+ for (const agentReg of agents) {
306
+ const result = await this.delegateTask({
307
+ id: `${task.id}-${agentReg.agent.id}`,
308
+ description: task.description,
309
+ requiredCapabilities: agentReg.capabilities,
310
+ input: { prompt: task.description },
311
+ priority: DelegationPriority.NORMAL,
312
+ });
313
+
314
+ if (result.output) {
315
+ results.set(agentReg.agent.id, result.output);
316
+ }
317
+ }
318
+ }
319
+
320
+ return {
321
+ taskId: task.id,
322
+ status: results.size === agents.length
323
+ ? CoordinationStatus.SUCCESS
324
+ : results.size > 0
325
+ ? CoordinationStatus.PARTIAL
326
+ : CoordinationStatus.FAILED,
327
+ results,
328
+ executionTimeMs: Date.now() - startTime,
329
+ };
330
+ }
331
+
332
+ private findAgentsByCapabilities(requiredCapabilities: string[]): AgentRegistration[] {
333
+ return Array.from(this.agents.values()).filter(reg =>
334
+ requiredCapabilities.every(cap => reg.capabilities.includes(cap))
335
+ );
336
+ }
337
+ }
338
+
339
+ /**
340
+ * Create a minimal AgentContext for in-process agent execution
341
+ */
342
+ function createMinimalContext(agent: Agent): AgentContext {
343
+ return new AgentContextBuilder()
344
+ .withAgentId(agent.id)
345
+ .withMemory(new InMemoryStore())
346
+ .withTools(new ToolRegistryImpl())
347
+ .withPlanner(new ClassicalPlanner({ algorithm: PlanningAlgorithm.HIERARCHICAL }))
348
+ .build();
349
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Pipeline pattern: run agents in sequence, passing output of one as input to the next.
3
+ *
4
+ * Inspired by VoltAgent's pipeline pattern:
5
+ * https://github.com/VoltAgent/voltagent/blob/main/website/blog/2025-07-16-ai-agent-orchestration/index.md
6
+ */
7
+
8
+ import type { Agent, AgentInput, AgentOutput, AgentContext } from '../core/types.js';
9
+ import { AgentState } from '../core/types.js';
10
+ import { createRunnableAgent } from './agent-adapter.js';
11
+ import { AgentContextBuilder } from '../core/context-builder.js';
12
+ import { InMemoryStore } from '../memory/in-memory-store.js';
13
+ import { ToolRegistryImpl } from '../tools/registry.js';
14
+ import { ClassicalPlanner } from '../planner/classical-planner.js';
15
+ import { PlanningAlgorithm } from '../planner/index.js';
16
+
17
+ export interface PipelineConfig {
18
+ readonly name: string;
19
+ readonly description?: string;
20
+ /** Agents in execution order; output of step N is passed as input to step N+1 */
21
+ readonly agents: Agent[];
22
+ }
23
+
24
+ /**
25
+ * Creates a pipeline agent that runs the given agents in sequence.
26
+ * Each agent receives the previous agent's output as its input (as JSON in the prompt).
27
+ */
28
+ export function createPipeline(config: PipelineConfig): Agent {
29
+ const run = async (input: AgentInput, _ctx: AgentContext): Promise<AgentOutput> => {
30
+ const results: unknown[] = [];
31
+ let currentPrompt = input.prompt;
32
+ const sharedContext = new AgentContextBuilder()
33
+ .withAgentId(`pipeline-${config.name}`)
34
+ .withMemory(new InMemoryStore())
35
+ .withTools(new ToolRegistryImpl())
36
+ .withPlanner(new ClassicalPlanner({ algorithm: PlanningAlgorithm.HIERARCHICAL }))
37
+ .build();
38
+
39
+ for (const agent of config.agents) {
40
+ const agentInput: AgentInput = {
41
+ prompt: currentPrompt,
42
+ context: input.context,
43
+ };
44
+ const output = await agent.run(agentInput, sharedContext);
45
+ const result = output.result;
46
+ results.push(result);
47
+ currentPrompt = typeof result === 'string' ? result : JSON.stringify(result);
48
+ }
49
+
50
+ return {
51
+ result: results.length === 1 ? results[0] : { steps: results, final: results[results.length - 1] },
52
+ state: AgentState.COMPLETED,
53
+ metadata: {
54
+ startTime: new Date(),
55
+ durationMs: 0,
56
+ iterations: config.agents.length,
57
+ },
58
+ };
59
+ };
60
+
61
+ return createRunnableAgent({
62
+ name: config.name,
63
+ description: config.description ?? `Pipeline of ${config.agents.length} agents`,
64
+ run,
65
+ });
66
+ }