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,1099 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Swarm Implementation
|
|
3
|
+
*
|
|
4
|
+
* Inspired by Kimi K2.5's Agent Swarm architecture with PARL (Parallel-Agent Reinforcement Learning)
|
|
5
|
+
*
|
|
6
|
+
* Key concepts:
|
|
7
|
+
* - Orchestrator decomposes tasks into parallelizable subtasks
|
|
8
|
+
* - Dynamic subagent instantiation (up to 100 sub-agents)
|
|
9
|
+
* - Parallel execution across coordinated steps
|
|
10
|
+
* - Critical path optimization for latency reduction
|
|
11
|
+
* - No predefined roles or hand-crafted workflows
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { Agent, AgentInput, AgentOutput, AgentContext, EntityId } from '../core/types.js';
|
|
15
|
+
import { AgentState } from '../core/types.js';
|
|
16
|
+
import type { AgentRole, MessageBus } from './types.js';
|
|
17
|
+
import { MessageBusImpl } from './message-bus.js';
|
|
18
|
+
import { createRunnableAgent, type RunnableAgentConfig } from './agent-adapter.js';
|
|
19
|
+
import { AgentContextBuilder } from '../core/context-builder.js';
|
|
20
|
+
import { InMemoryStore } from '../memory/in-memory-store.js';
|
|
21
|
+
import { ToolRegistryImpl } from '../tools/registry.js';
|
|
22
|
+
import { ClassicalPlanner } from '../planner/classical-planner.js';
|
|
23
|
+
import { PlanningAlgorithm } from '../planner/types.js';
|
|
24
|
+
import type { LLMProvider, Message, GenerateOptions } from '../llm/types.js';
|
|
25
|
+
import { OpenAIProvider } from '../llm/openai-provider.js';
|
|
26
|
+
import { createOpenRouterProvider } from '../llm/openrouter-provider.js';
|
|
27
|
+
import { resolveModelString, isModelString, LLAMABARN_BASE_URL } from '../llm/model-resolver.js';
|
|
28
|
+
import { DebugLogger, createDebugLogger } from '../debug-logger.js';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Subagent template for dynamic instantiation
|
|
32
|
+
*/
|
|
33
|
+
export interface SubagentTemplate {
|
|
34
|
+
readonly id: string;
|
|
35
|
+
readonly name: string;
|
|
36
|
+
readonly description: string;
|
|
37
|
+
readonly capabilities: string[];
|
|
38
|
+
readonly specialization: string;
|
|
39
|
+
readonly maxConcurrentTasks?: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* A decomposed subtask ready for parallel execution
|
|
44
|
+
*/
|
|
45
|
+
export interface Subtask {
|
|
46
|
+
readonly id: EntityId;
|
|
47
|
+
readonly description: string;
|
|
48
|
+
readonly specialization: string;
|
|
49
|
+
readonly dependencies: EntityId[];
|
|
50
|
+
readonly estimatedComplexity: number; // 1-10 scale
|
|
51
|
+
readonly input: AgentInput;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Execution stage for critical path optimization
|
|
56
|
+
*/
|
|
57
|
+
export interface ExecutionStage {
|
|
58
|
+
readonly id: EntityId;
|
|
59
|
+
readonly subtasks: Subtask[];
|
|
60
|
+
readonly stageNumber: number;
|
|
61
|
+
readonly canParallelize: boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Subagent instance created dynamically
|
|
66
|
+
*/
|
|
67
|
+
export interface SubagentInstance {
|
|
68
|
+
readonly id: EntityId;
|
|
69
|
+
readonly templateId: string;
|
|
70
|
+
readonly agent: Agent;
|
|
71
|
+
readonly role: AgentRole;
|
|
72
|
+
assignedSubtasks: EntityId[];
|
|
73
|
+
readonly createdAt: Date;
|
|
74
|
+
active: boolean;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Result from a subtask execution
|
|
79
|
+
*/
|
|
80
|
+
export interface SubtaskResult {
|
|
81
|
+
readonly subtaskId: EntityId;
|
|
82
|
+
readonly subagentId: EntityId;
|
|
83
|
+
readonly output: AgentOutput;
|
|
84
|
+
readonly executionTimeMs: number;
|
|
85
|
+
readonly completedAt: Date;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Critical path metrics for optimization
|
|
90
|
+
*/
|
|
91
|
+
export interface CriticalPathMetrics {
|
|
92
|
+
readonly totalStages: number;
|
|
93
|
+
readonly criticalSteps: number;
|
|
94
|
+
readonly parallelEfficiency: number; // 0-1 scale
|
|
95
|
+
readonly orchestrationOverheadMs: number;
|
|
96
|
+
readonly slowestSubagentTimeMs: number;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Swarm execution result
|
|
101
|
+
*/
|
|
102
|
+
export interface SwarmResult {
|
|
103
|
+
readonly taskId: EntityId;
|
|
104
|
+
readonly status: 'success' | 'partial' | 'failed';
|
|
105
|
+
readonly results: Map<EntityId, SubtaskResult>;
|
|
106
|
+
readonly aggregatedOutput: unknown;
|
|
107
|
+
readonly executionTimeMs: number;
|
|
108
|
+
readonly metrics: CriticalPathMetrics;
|
|
109
|
+
readonly subagentCount: number;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* LLM configuration for swarm subagents
|
|
114
|
+
*/
|
|
115
|
+
export interface SwarmLLMConfig {
|
|
116
|
+
/**
|
|
117
|
+
* LLM provider to use for subagents.
|
|
118
|
+
* Can be a provider instance, a model string (e.g., "openrouter:meta-llama/llama-3.3-70b-instruct"),
|
|
119
|
+
* or "default" to use placeholder logic.
|
|
120
|
+
*/
|
|
121
|
+
readonly provider?: LLMProvider | string;
|
|
122
|
+
/**
|
|
123
|
+
* Model identifier for OpenRouter or other providers.
|
|
124
|
+
* Examples: "meta-llama/llama-3.3-70b-instruct", "openai/gpt-oss-20b"
|
|
125
|
+
*/
|
|
126
|
+
readonly model?: string;
|
|
127
|
+
/** Temperature for LLM generation (default: 0.7) */
|
|
128
|
+
readonly temperature?: number;
|
|
129
|
+
/** Maximum tokens for LLM generation */
|
|
130
|
+
readonly maxTokens?: number;
|
|
131
|
+
/** OpenRouter API key (if using OpenRouter) */
|
|
132
|
+
readonly openRouterApiKey?: string;
|
|
133
|
+
/** OpenAI API key (if using OpenAI) */
|
|
134
|
+
readonly openAIApiKey?: string;
|
|
135
|
+
/** Base URL for custom endpoints */
|
|
136
|
+
readonly baseURL?: string;
|
|
137
|
+
/** LlamaBarn API key (if using LlamaBarn) */
|
|
138
|
+
readonly llamaBarnApiKey?: string;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Configuration for the swarm orchestrator
|
|
143
|
+
*/
|
|
144
|
+
export interface SwarmConfig {
|
|
145
|
+
/** Maximum number of subagents that can be instantiated (default: 100) */
|
|
146
|
+
readonly maxSubagents?: number;
|
|
147
|
+
/** Maximum number of parallel execution stages (default: 1500) */
|
|
148
|
+
readonly maxStages?: number;
|
|
149
|
+
/** Timeout for individual subtask execution (default: 30000ms) */
|
|
150
|
+
readonly subtaskTimeoutMs?: number;
|
|
151
|
+
/** Enable critical path optimization (default: true) */
|
|
152
|
+
readonly enableCriticalPathOptimization?: boolean;
|
|
153
|
+
/** Minimum parallelism threshold to avoid serial collapse (default: 2) */
|
|
154
|
+
readonly minParallelism?: number;
|
|
155
|
+
/** Templates for dynamic subagent creation */
|
|
156
|
+
readonly subagentTemplates?: SubagentTemplate[];
|
|
157
|
+
/** LLM configuration for subagents */
|
|
158
|
+
readonly llm?: SwarmLLMConfig;
|
|
159
|
+
/** Enable debug logging (default: false) */
|
|
160
|
+
readonly debug?: boolean;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Task decomposition result
|
|
165
|
+
*/
|
|
166
|
+
interface TaskDecomposition {
|
|
167
|
+
readonly subtasks: Subtask[];
|
|
168
|
+
readonly stages: ExecutionStage[];
|
|
169
|
+
readonly estimatedTotalComplexity: number;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Swarm Orchestrator - Inspired by Kimi K2.5's PARL architecture
|
|
174
|
+
*
|
|
175
|
+
* The orchestrator:
|
|
176
|
+
* 1. Decomposes complex tasks into parallelizable subtasks
|
|
177
|
+
* 2. Dynamically instantiates specialized subagents
|
|
178
|
+
* 3. Executes subtasks across stages optimizing for critical path
|
|
179
|
+
* 4. Aggregates results and manages subagent lifecycle
|
|
180
|
+
*/
|
|
181
|
+
export class SwarmOrchestrator {
|
|
182
|
+
private config: Required<SwarmConfig>;
|
|
183
|
+
/** @internal Message bus for inter-agent communication */
|
|
184
|
+
messageBus: MessageBus;
|
|
185
|
+
private subagents: Map<EntityId, SubagentInstance> = new Map();
|
|
186
|
+
private templates: Map<string, SubagentTemplate> = new Map();
|
|
187
|
+
private _isRunning = false;
|
|
188
|
+
private taskCounter = 0;
|
|
189
|
+
private logger: DebugLogger;
|
|
190
|
+
|
|
191
|
+
private llmProvider: LLMProvider | undefined;
|
|
192
|
+
|
|
193
|
+
constructor(config: SwarmConfig = {}, messageBus?: MessageBus) {
|
|
194
|
+
this.config = {
|
|
195
|
+
maxSubagents: config.maxSubagents ?? 100,
|
|
196
|
+
maxStages: config.maxStages ?? 1500,
|
|
197
|
+
subtaskTimeoutMs: config.subtaskTimeoutMs ?? 30000,
|
|
198
|
+
enableCriticalPathOptimization: config.enableCriticalPathOptimization ?? true,
|
|
199
|
+
minParallelism: config.minParallelism ?? 2,
|
|
200
|
+
subagentTemplates: config.subagentTemplates ?? defaultTemplates,
|
|
201
|
+
llm: config.llm ?? {},
|
|
202
|
+
debug: config.debug ?? false,
|
|
203
|
+
};
|
|
204
|
+
this.messageBus = messageBus ?? new MessageBusImpl();
|
|
205
|
+
this.llmProvider = this.initializeLLMProvider(config.llm);
|
|
206
|
+
this.logger = createDebugLogger('Swarm', this.config.debug);
|
|
207
|
+
|
|
208
|
+
// Register templates
|
|
209
|
+
for (const template of this.config.subagentTemplates) {
|
|
210
|
+
this.templates.set(template.id, template);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
this.logger.logStart('SwarmOrchestrator initialization', {
|
|
214
|
+
maxSubagents: this.config.maxSubagents,
|
|
215
|
+
maxStages: this.config.maxStages,
|
|
216
|
+
debug: this.config.debug,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Initialize LLM provider from configuration
|
|
222
|
+
*/
|
|
223
|
+
private initializeLLMProvider(llmConfig?: SwarmLLMConfig): LLMProvider | undefined {
|
|
224
|
+
if (!llmConfig) {
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// If a provider instance is passed directly, use it
|
|
229
|
+
if (llmConfig.provider && typeof llmConfig.provider === 'object') {
|
|
230
|
+
return llmConfig.provider;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// If a model string is passed (e.g., "openrouter:meta-llama/llama-3.3-70b-instruct")
|
|
234
|
+
if (typeof llmConfig.provider === 'string' && isModelString(llmConfig.provider)) {
|
|
235
|
+
const resolved = resolveModelString(llmConfig.provider);
|
|
236
|
+
if (resolved) {
|
|
237
|
+
return new OpenAIProvider({
|
|
238
|
+
apiKey: resolved.apiKey,
|
|
239
|
+
baseURL: resolved.baseURL,
|
|
240
|
+
model: resolved.model,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// If model is specified but no provider, try to infer from model string
|
|
246
|
+
if (llmConfig.model) {
|
|
247
|
+
// Check if model string is a provider:model format
|
|
248
|
+
if (isModelString(llmConfig.model)) {
|
|
249
|
+
const resolved = resolveModelString(llmConfig.model);
|
|
250
|
+
if (resolved) {
|
|
251
|
+
return new OpenAIProvider({
|
|
252
|
+
apiKey: resolved.apiKey ?? llmConfig.openRouterApiKey ?? llmConfig.openAIApiKey ?? llmConfig.llamaBarnApiKey,
|
|
253
|
+
baseURL: resolved.baseURL ?? llmConfig.baseURL,
|
|
254
|
+
model: resolved.model,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// If it looks like an OpenRouter model (contains "/")
|
|
260
|
+
if (llmConfig.model.includes('/')) {
|
|
261
|
+
return createOpenRouterProvider({
|
|
262
|
+
apiKey: llmConfig.openRouterApiKey ?? process.env.OPENROUTER_API_KEY ?? '',
|
|
263
|
+
model: llmConfig.model,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Check for LlamaBarn GPT-OSS models
|
|
268
|
+
if (llmConfig.model.toLowerCase().includes('gpt-oss')) {
|
|
269
|
+
return new OpenAIProvider({
|
|
270
|
+
apiKey: llmConfig.llamaBarnApiKey ?? process.env.LLAMABARN_API_KEY ?? 'not-needed',
|
|
271
|
+
baseURL: llmConfig.baseURL ?? LLAMABARN_BASE_URL,
|
|
272
|
+
model: llmConfig.model,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Default to OpenAI provider with the specified model
|
|
277
|
+
return new OpenAIProvider({
|
|
278
|
+
apiKey: llmConfig.openAIApiKey ?? process.env.OPENAI_API_KEY,
|
|
279
|
+
baseURL: llmConfig.baseURL,
|
|
280
|
+
model: llmConfig.model,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Execute a complex task using the swarm pattern
|
|
289
|
+
*
|
|
290
|
+
* This is the main entry point that:
|
|
291
|
+
* 1. Decomposes the task into subtasks
|
|
292
|
+
* 2. Creates execution stages based on dependencies
|
|
293
|
+
* 3. Dynamically instantiates subagents
|
|
294
|
+
* 4. Executes stages in parallel where possible
|
|
295
|
+
* 5. Aggregates results
|
|
296
|
+
*/
|
|
297
|
+
async execute(task: AgentInput, parentContext?: AgentContext): Promise<SwarmResult> {
|
|
298
|
+
const startTime = Date.now();
|
|
299
|
+
const taskId = this.generateTaskId();
|
|
300
|
+
|
|
301
|
+
this.logger.logStart(`Task execution: ${taskId}`, {
|
|
302
|
+
prompt: task.prompt.slice(0, 80),
|
|
303
|
+
hasLLM: !!this.llmProvider,
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
if (!this._isRunning) {
|
|
307
|
+
await this.start();
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
try {
|
|
311
|
+
// Step 1: Decompose task into parallelizable subtasks
|
|
312
|
+
this.logger.logStep('Task decomposition', 1, 4, { taskId });
|
|
313
|
+
const decomposition = await this.decomposeTask(task, parentContext);
|
|
314
|
+
this.logger.logComplete('Task decomposition', undefined, {
|
|
315
|
+
subtaskCount: decomposition.subtasks.length,
|
|
316
|
+
stageCount: decomposition.stages.length,
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// Step 2: Validate parallelism (avoid serial collapse)
|
|
320
|
+
if (this.config.enableCriticalPathOptimization) {
|
|
321
|
+
this.logger.logStep('Parallelism validation', 2, 4);
|
|
322
|
+
this.validateParallelism(decomposition);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Step 3: Execute stages
|
|
326
|
+
this.logger.logStep('Stage execution', 3, 4, { totalStages: decomposition.stages.length });
|
|
327
|
+
const results = new Map<EntityId, SubtaskResult>();
|
|
328
|
+
let orchestrationOverheadMs = 0;
|
|
329
|
+
let slowestSubagentTimeMs = 0;
|
|
330
|
+
|
|
331
|
+
for (const stage of decomposition.stages) {
|
|
332
|
+
const stageStart = Date.now();
|
|
333
|
+
this.logger.logStart(`Stage ${stage.stageNumber}/${decomposition.stages.length}`, {
|
|
334
|
+
subtaskCount: stage.subtasks.length,
|
|
335
|
+
canParallelize: stage.canParallelize,
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// Dynamically instantiate subagents for this stage
|
|
339
|
+
const stageSubagents = await this.instantiateSubagentsForStage(stage);
|
|
340
|
+
this.logger.debug(`Instantiated ${stageSubagents.length} subagents for stage ${stage.stageNumber}`);
|
|
341
|
+
|
|
342
|
+
// Execute subtasks in parallel
|
|
343
|
+
const stageResults = await this.executeStage(stage, stageSubagents);
|
|
344
|
+
const stageTime = Date.now() - stageStart;
|
|
345
|
+
this.logger.logComplete(`Stage ${stage.stageNumber}`, stageTime, {
|
|
346
|
+
resultsCount: stageResults.length,
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// Track metrics
|
|
350
|
+
orchestrationOverheadMs += stageTime;
|
|
351
|
+
slowestSubagentTimeMs = Math.max(
|
|
352
|
+
slowestSubagentTimeMs,
|
|
353
|
+
...stageResults.map(r => r.executionTimeMs)
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
// Store results
|
|
357
|
+
for (const result of stageResults) {
|
|
358
|
+
results.set(result.subtaskId, result);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Clean up subagents that are no longer needed
|
|
362
|
+
await this.cleanupSubagents(stage, decomposition.stages);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Step 4: Aggregate results
|
|
366
|
+
this.logger.logStep('Result aggregation', 4, 4);
|
|
367
|
+
const aggregatedOutput = await this.aggregateResults(results, task);
|
|
368
|
+
|
|
369
|
+
const executionTimeMs = Date.now() - startTime;
|
|
370
|
+
|
|
371
|
+
// Calculate critical path metrics
|
|
372
|
+
const metrics: CriticalPathMetrics = {
|
|
373
|
+
totalStages: decomposition.stages.length,
|
|
374
|
+
criticalSteps: this.calculateCriticalSteps(decomposition.stages),
|
|
375
|
+
parallelEfficiency: this.calculateParallelEfficiency(decomposition, results),
|
|
376
|
+
orchestrationOverheadMs,
|
|
377
|
+
slowestSubagentTimeMs,
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
const finalStatus = this.determineStatus(results, decomposition.subtasks.length);
|
|
381
|
+
this.logger.logComplete(`Task ${taskId}`, executionTimeMs, {
|
|
382
|
+
status: finalStatus,
|
|
383
|
+
stages: metrics.totalStages,
|
|
384
|
+
parallelEfficiency: `${(metrics.parallelEfficiency * 100).toFixed(1)}%`,
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
return {
|
|
388
|
+
taskId,
|
|
389
|
+
status: finalStatus,
|
|
390
|
+
results,
|
|
391
|
+
aggregatedOutput,
|
|
392
|
+
executionTimeMs,
|
|
393
|
+
metrics,
|
|
394
|
+
subagentCount: this.subagents.size,
|
|
395
|
+
};
|
|
396
|
+
} catch (error) {
|
|
397
|
+
this.logger.error(`Task ${taskId} failed`, undefined, {
|
|
398
|
+
error: error instanceof Error ? error.message : String(error),
|
|
399
|
+
});
|
|
400
|
+
throw error;
|
|
401
|
+
} finally {
|
|
402
|
+
// Cleanup all subagents
|
|
403
|
+
this.logger.debug('Cleaning up subagents...');
|
|
404
|
+
await this.cleanupAllSubagents();
|
|
405
|
+
this.logger.debug('Cleanup complete');
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Decompose a complex task into parallelizable subtasks
|
|
411
|
+
*
|
|
412
|
+
* This mimics the PARL orchestrator's task decomposition capability
|
|
413
|
+
*/
|
|
414
|
+
private async decomposeTask(
|
|
415
|
+
task: AgentInput,
|
|
416
|
+
_parentContext?: AgentContext
|
|
417
|
+
): Promise<TaskDecomposition> {
|
|
418
|
+
// For now, use a simple rule-based decomposition
|
|
419
|
+
// In production, this would use an LLM to intelligently decompose
|
|
420
|
+
const subtasks = this.ruleBasedDecomposition(task);
|
|
421
|
+
|
|
422
|
+
// Build execution stages based on dependencies
|
|
423
|
+
const stages = this.buildExecutionStages(subtasks);
|
|
424
|
+
|
|
425
|
+
const estimatedTotalComplexity = subtasks.reduce(
|
|
426
|
+
(sum, s) => sum + s.estimatedComplexity,
|
|
427
|
+
0
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
return { subtasks, stages, estimatedTotalComplexity };
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Simple rule-based decomposition for demonstration
|
|
435
|
+
* In production, this would use LLM-based decomposition
|
|
436
|
+
*/
|
|
437
|
+
private ruleBasedDecomposition(task: AgentInput): Subtask[] {
|
|
438
|
+
const subtasks: Subtask[] = [];
|
|
439
|
+
|
|
440
|
+
// Detect task type and decompose accordingly
|
|
441
|
+
const prompt = task.prompt.toLowerCase();
|
|
442
|
+
|
|
443
|
+
if (prompt.includes('research') || prompt.includes('analyze')) {
|
|
444
|
+
// Research task - parallelize by aspect
|
|
445
|
+
const aspects = ['background', 'current_state', 'future_trends', 'key_players'];
|
|
446
|
+
for (let i = 0; i < aspects.length; i++) {
|
|
447
|
+
subtasks.push({
|
|
448
|
+
id: `subtask-${i + 1}`,
|
|
449
|
+
description: `Research ${aspects[i]}`,
|
|
450
|
+
specialization: 'researcher',
|
|
451
|
+
dependencies: [],
|
|
452
|
+
estimatedComplexity: 5,
|
|
453
|
+
input: {
|
|
454
|
+
prompt: `Research the ${aspects[i]} of: ${task.prompt}`,
|
|
455
|
+
context: task.context,
|
|
456
|
+
},
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
} else if (prompt.includes('code') || prompt.includes('develop')) {
|
|
460
|
+
// Development task - parallelize by component
|
|
461
|
+
const components = ['architecture', 'implementation', 'testing', 'documentation'];
|
|
462
|
+
for (let i = 0; i < components.length; i++) {
|
|
463
|
+
const deps = i > 0 ? [`subtask-${i}`] : [];
|
|
464
|
+
subtasks.push({
|
|
465
|
+
id: `subtask-${i + 1}`,
|
|
466
|
+
description: `${components[i]} phase`,
|
|
467
|
+
specialization: 'developer',
|
|
468
|
+
dependencies: deps,
|
|
469
|
+
estimatedComplexity: 6,
|
|
470
|
+
input: {
|
|
471
|
+
prompt: `Handle the ${components[i]} for: ${task.prompt}`,
|
|
472
|
+
context: task.context,
|
|
473
|
+
},
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
} else {
|
|
477
|
+
// Generic task - create parallel subtasks
|
|
478
|
+
const subtaskCount = Math.min(3, this.config.maxSubagents);
|
|
479
|
+
for (let i = 0; i < subtaskCount; i++) {
|
|
480
|
+
subtasks.push({
|
|
481
|
+
id: `subtask-${i + 1}`,
|
|
482
|
+
description: `Subtask ${i + 1}`,
|
|
483
|
+
specialization: 'generalist',
|
|
484
|
+
dependencies: [],
|
|
485
|
+
estimatedComplexity: 4,
|
|
486
|
+
input: {
|
|
487
|
+
prompt: `Part ${i + 1} of: ${task.prompt}`,
|
|
488
|
+
context: task.context,
|
|
489
|
+
},
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
return subtasks;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Build execution stages from subtasks based on dependencies
|
|
499
|
+
*/
|
|
500
|
+
private buildExecutionStages(subtasks: Subtask[]): ExecutionStage[] {
|
|
501
|
+
const stages: ExecutionStage[] = [];
|
|
502
|
+
const completed = new Set<EntityId>();
|
|
503
|
+
const remaining = new Set(subtasks.map(s => s.id));
|
|
504
|
+
|
|
505
|
+
let stageNumber = 1;
|
|
506
|
+
|
|
507
|
+
while (remaining.size > 0) {
|
|
508
|
+
// Find subtasks with all dependencies satisfied
|
|
509
|
+
const readySubtasks = subtasks.filter(
|
|
510
|
+
s => remaining.has(s.id) && s.dependencies.every(dep => completed.has(dep))
|
|
511
|
+
);
|
|
512
|
+
|
|
513
|
+
if (readySubtasks.length === 0) {
|
|
514
|
+
// Circular dependency or invalid dependency
|
|
515
|
+
throw new Error('Circular or invalid dependency detected in subtasks');
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
stages.push({
|
|
519
|
+
id: `stage-${stageNumber}`,
|
|
520
|
+
subtasks: readySubtasks,
|
|
521
|
+
stageNumber,
|
|
522
|
+
canParallelize: readySubtasks.length > 1,
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
for (const subtask of readySubtasks) {
|
|
526
|
+
completed.add(subtask.id);
|
|
527
|
+
remaining.delete(subtask.id);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
stageNumber++;
|
|
531
|
+
|
|
532
|
+
// Safety check
|
|
533
|
+
if (stageNumber > this.config.maxStages) {
|
|
534
|
+
throw new Error(`Exceeded maximum stages (${this.config.maxStages})`);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
return stages;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Validate that we have sufficient parallelism to avoid serial collapse
|
|
543
|
+
*/
|
|
544
|
+
private validateParallelism(decomposition: TaskDecomposition): void {
|
|
545
|
+
const parallelStages = decomposition.stages.filter(s => s.canParallelize);
|
|
546
|
+
const avgParallelism =
|
|
547
|
+
parallelStages.reduce((sum, s) => sum + s.subtasks.length, 0) /
|
|
548
|
+
(parallelStages.length || 1);
|
|
549
|
+
|
|
550
|
+
if (avgParallelism < this.config.minParallelism && decomposition.stages.length > 1) {
|
|
551
|
+
console.warn(
|
|
552
|
+
`Warning: Low parallelism detected (${avgParallelism.toFixed(1)}). ` +
|
|
553
|
+
`Consider restructuring tasks for better parallelization.`
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Dynamically instantiate subagents for a stage
|
|
560
|
+
*/
|
|
561
|
+
private async instantiateSubagentsForStage(
|
|
562
|
+
stage: ExecutionStage
|
|
563
|
+
): Promise<SubagentInstance[]> {
|
|
564
|
+
const instances: SubagentInstance[] = [];
|
|
565
|
+
|
|
566
|
+
for (const subtask of stage.subtasks) {
|
|
567
|
+
const template = this.templates.get(subtask.specialization);
|
|
568
|
+
if (!template) {
|
|
569
|
+
throw new Error(`Unknown specialization: ${subtask.specialization}`);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Check if we can reuse an existing subagent
|
|
573
|
+
const existing = this.findReusableSubagent(template, subtask);
|
|
574
|
+
|
|
575
|
+
if (existing) {
|
|
576
|
+
this.logger.debug(`Reusing subagent ${existing.id} (${template.name}) for ${subtask.id}`);
|
|
577
|
+
existing.assignedSubtasks.push(subtask.id);
|
|
578
|
+
instances.push(existing);
|
|
579
|
+
} else {
|
|
580
|
+
// Create new subagent instance
|
|
581
|
+
this.logger.debug(`Creating new subagent (${template.name}) for ${subtask.id}`);
|
|
582
|
+
const instance = await this.createSubagent(template, subtask);
|
|
583
|
+
instances.push(instance);
|
|
584
|
+
this.subagents.set(instance.id, instance);
|
|
585
|
+
this.logger.debug(`Created subagent ${instance.id}`);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
return instances;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Find a reusable subagent that can handle additional tasks
|
|
594
|
+
*/
|
|
595
|
+
private findReusableSubagent(
|
|
596
|
+
template: SubagentTemplate,
|
|
597
|
+
_subtask: Subtask
|
|
598
|
+
): SubagentInstance | undefined {
|
|
599
|
+
for (const instance of this.subagents.values()) {
|
|
600
|
+
if (
|
|
601
|
+
instance.templateId === template.id &&
|
|
602
|
+
instance.active &&
|
|
603
|
+
instance.assignedSubtasks.length < (template.maxConcurrentTasks ?? 5)
|
|
604
|
+
) {
|
|
605
|
+
return instance;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
return undefined;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Create a new subagent instance from a template
|
|
613
|
+
*/
|
|
614
|
+
private async createSubagent(
|
|
615
|
+
template: SubagentTemplate,
|
|
616
|
+
subtask: Subtask
|
|
617
|
+
): Promise<SubagentInstance> {
|
|
618
|
+
const id = `subagent-${template.id}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
619
|
+
|
|
620
|
+
// Create the agent using the adapter
|
|
621
|
+
const agentConfig: RunnableAgentConfig = {
|
|
622
|
+
name: `${template.name} (${id})`,
|
|
623
|
+
description: template.description,
|
|
624
|
+
run: async (input: AgentInput, ctx: AgentContext): Promise<AgentOutput> => {
|
|
625
|
+
// Subagent execution logic
|
|
626
|
+
const startTime = Date.now();
|
|
627
|
+
try {
|
|
628
|
+
// In a real implementation, this would use an LLM
|
|
629
|
+
// For now, return a placeholder result
|
|
630
|
+
const result = await this.executeSubagentLogic(template, input, ctx);
|
|
631
|
+
|
|
632
|
+
return {
|
|
633
|
+
result,
|
|
634
|
+
state: AgentState.COMPLETED,
|
|
635
|
+
metadata: {
|
|
636
|
+
startTime: new Date(startTime),
|
|
637
|
+
iterations: 1,
|
|
638
|
+
durationMs: Date.now() - startTime,
|
|
639
|
+
},
|
|
640
|
+
};
|
|
641
|
+
} catch (error) {
|
|
642
|
+
return {
|
|
643
|
+
result: error instanceof Error ? error.message : String(error),
|
|
644
|
+
state: AgentState.FAILED,
|
|
645
|
+
metadata: {
|
|
646
|
+
startTime: new Date(startTime),
|
|
647
|
+
iterations: 1,
|
|
648
|
+
durationMs: Date.now() - startTime,
|
|
649
|
+
},
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
},
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
const agent = createRunnableAgent(agentConfig);
|
|
656
|
+
|
|
657
|
+
const role: AgentRole = {
|
|
658
|
+
id: `role-${template.id}`,
|
|
659
|
+
name: template.name,
|
|
660
|
+
description: template.description,
|
|
661
|
+
responsibilities: template.capabilities,
|
|
662
|
+
permissions: {
|
|
663
|
+
canExecuteTools: true,
|
|
664
|
+
canAccessMemory: true,
|
|
665
|
+
canCreateSubAgents: false,
|
|
666
|
+
canModifyPlan: false,
|
|
667
|
+
},
|
|
668
|
+
canDelegate: false,
|
|
669
|
+
canCommunicateWith: [],
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
return {
|
|
673
|
+
id,
|
|
674
|
+
templateId: template.id,
|
|
675
|
+
agent,
|
|
676
|
+
role,
|
|
677
|
+
assignedSubtasks: [subtask.id],
|
|
678
|
+
createdAt: new Date(),
|
|
679
|
+
active: true,
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* System prompts for different subagent specializations
|
|
685
|
+
*/
|
|
686
|
+
private getSystemPrompt(template: SubagentTemplate): string {
|
|
687
|
+
const basePrompt = `You are a specialized AI agent: ${template.name}.
|
|
688
|
+
${template.description}
|
|
689
|
+
|
|
690
|
+
Your capabilities include: ${template.capabilities.join(', ')}.
|
|
691
|
+
Your specialization: ${template.specialization}
|
|
692
|
+
|
|
693
|
+
Provide a detailed, helpful response to the user's request.`;
|
|
694
|
+
|
|
695
|
+
// Add specialization-specific instructions
|
|
696
|
+
switch (template.specialization) {
|
|
697
|
+
case 'research':
|
|
698
|
+
return `${basePrompt}\n\nWhen researching:\n- Gather comprehensive information\n- Cite sources when possible\n- Synthesize findings clearly\n- Identify key trends and insights`;
|
|
699
|
+
case 'development':
|
|
700
|
+
return `${basePrompt}\n\nWhen developing:\n- Write clean, well-documented code\n- Follow best practices\n- Consider edge cases\n- Provide explanations for complex logic`;
|
|
701
|
+
case 'analysis':
|
|
702
|
+
return `${basePrompt}\n\nWhen analyzing:\n- Be thorough and data-driven\n- Identify patterns and correlations\n- Provide actionable insights\n- Support conclusions with evidence`;
|
|
703
|
+
case 'writing':
|
|
704
|
+
return `${basePrompt}\n\nWhen writing:\n- Create engaging, clear content\n- Adapt tone to the audience\n- Structure information logically\n- Edit for clarity and impact`;
|
|
705
|
+
case 'verification':
|
|
706
|
+
return `${basePrompt}\n\nWhen fact-checking:\n- Verify claims against reliable sources\n- Cross-reference information\n- Identify potential biases\n- Report confidence levels`;
|
|
707
|
+
default:
|
|
708
|
+
return basePrompt;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Execute subagent logic using LLM if configured, otherwise use placeholder
|
|
714
|
+
*/
|
|
715
|
+
private async executeSubagentLogic(
|
|
716
|
+
template: SubagentTemplate,
|
|
717
|
+
input: AgentInput,
|
|
718
|
+
_ctx: AgentContext
|
|
719
|
+
): Promise<unknown> {
|
|
720
|
+
// If no LLM provider is configured, use placeholder logic
|
|
721
|
+
if (!this.llmProvider) {
|
|
722
|
+
this.logger.debug(`Using placeholder logic for ${template.name} (no LLM configured)`);
|
|
723
|
+
return {
|
|
724
|
+
specialization: template.specialization,
|
|
725
|
+
capabilities: template.capabilities,
|
|
726
|
+
result: `Executed by ${template.name} (placeholder - no LLM configured)`,
|
|
727
|
+
input: input.prompt,
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
this.logger.debug(`Calling LLM for ${template.name}`, undefined, {
|
|
732
|
+
promptPreview: input.prompt.slice(0, 60),
|
|
733
|
+
model: this.config.llm?.model ?? 'unknown',
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
// Build messages for LLM
|
|
737
|
+
const messages: Message[] = [
|
|
738
|
+
{ role: 'system', content: this.getSystemPrompt(template) },
|
|
739
|
+
{ role: 'user', content: input.prompt },
|
|
740
|
+
];
|
|
741
|
+
|
|
742
|
+
// Add context if available
|
|
743
|
+
if (input.context && Object.keys(input.context).length > 0) {
|
|
744
|
+
messages.push({
|
|
745
|
+
role: 'user',
|
|
746
|
+
content: `Context: ${JSON.stringify(input.context, null, 2)}`,
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// Call LLM
|
|
751
|
+
const options: GenerateOptions = {
|
|
752
|
+
temperature: this.config.llm?.temperature ?? 0.7,
|
|
753
|
+
maxTokens: this.config.llm?.maxTokens,
|
|
754
|
+
};
|
|
755
|
+
|
|
756
|
+
this.logger.debug(`Sending ${messages.length} messages to LLM`, undefined, {
|
|
757
|
+
model: this.config.llm?.model ?? 'unknown',
|
|
758
|
+
});
|
|
759
|
+
const llmStartTime = Date.now();
|
|
760
|
+
const result = await this.llmProvider.generateText(messages, options);
|
|
761
|
+
this.logger.logComplete(`LLM call for ${template.name}`, Date.now() - llmStartTime, {
|
|
762
|
+
tokens: result.usage?.totalTokens,
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
return {
|
|
766
|
+
specialization: template.specialization,
|
|
767
|
+
capabilities: template.capabilities,
|
|
768
|
+
result: result.text,
|
|
769
|
+
model: this.config.llm?.model ?? 'unknown',
|
|
770
|
+
usage: result.usage,
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/**
|
|
775
|
+
* Execute all subtasks in a stage in parallel
|
|
776
|
+
*/
|
|
777
|
+
private async executeStage(
|
|
778
|
+
stage: ExecutionStage,
|
|
779
|
+
subagents: SubagentInstance[]
|
|
780
|
+
): Promise<SubtaskResult[]> {
|
|
781
|
+
const promises = stage.subtasks.map(async subtask => {
|
|
782
|
+
const subagent = subagents.find(s => s.assignedSubtasks.includes(subtask.id));
|
|
783
|
+
if (!subagent) {
|
|
784
|
+
throw new Error(`No subagent found for subtask ${subtask.id}`);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
const startTime = Date.now();
|
|
788
|
+
this.logger.logStart(`Subtask ${subtask.id}`, {
|
|
789
|
+
subagentId: subagent.id,
|
|
790
|
+
templateId: subagent.templateId,
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
// Create minimal context for subagent
|
|
794
|
+
const ctx = this.createMinimalContext(subagent.agent);
|
|
795
|
+
|
|
796
|
+
// Execute with timeout
|
|
797
|
+
const timeoutPromise = new Promise<AgentOutput>((_, reject) => {
|
|
798
|
+
setTimeout(
|
|
799
|
+
() => reject(new Error(`Subtask ${subtask.id} timed out after ${this.config.subtaskTimeoutMs}ms`)),
|
|
800
|
+
this.config.subtaskTimeoutMs
|
|
801
|
+
);
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
try {
|
|
805
|
+
const output = await Promise.race([
|
|
806
|
+
subagent.agent.run(subtask.input, ctx),
|
|
807
|
+
timeoutPromise,
|
|
808
|
+
]);
|
|
809
|
+
this.logger.logComplete(`Subtask ${subtask.id}`, Date.now() - startTime, {
|
|
810
|
+
state: output.state,
|
|
811
|
+
});
|
|
812
|
+
return {
|
|
813
|
+
subtaskId: subtask.id,
|
|
814
|
+
subagentId: subagent.id,
|
|
815
|
+
output,
|
|
816
|
+
executionTimeMs: Date.now() - startTime,
|
|
817
|
+
completedAt: new Date(),
|
|
818
|
+
};
|
|
819
|
+
} catch (error) {
|
|
820
|
+
this.logger.error(`Subtask ${subtask.id} failed`, undefined, {
|
|
821
|
+
error: error instanceof Error ? error.message : String(error),
|
|
822
|
+
});
|
|
823
|
+
throw error;
|
|
824
|
+
}
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
return Promise.all(promises);
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
/**
|
|
831
|
+
* Cleanup subagents that are no longer needed
|
|
832
|
+
*/
|
|
833
|
+
private async cleanupSubagents(
|
|
834
|
+
currentStage: ExecutionStage,
|
|
835
|
+
allStages: ExecutionStage[]
|
|
836
|
+
): Promise<void> {
|
|
837
|
+
const currentSubtaskIds = new Set(currentStage.subtasks.map(s => s.id));
|
|
838
|
+
const futureSubtaskIds = new Set<EntityId>();
|
|
839
|
+
|
|
840
|
+
// Collect all future subtask IDs
|
|
841
|
+
let foundCurrent = false;
|
|
842
|
+
for (const stage of allStages) {
|
|
843
|
+
if (foundCurrent) {
|
|
844
|
+
for (const subtask of stage.subtasks) {
|
|
845
|
+
futureSubtaskIds.add(subtask.id);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
if (stage.id === currentStage.id) {
|
|
849
|
+
foundCurrent = true;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// Find subagents that can be cleaned up
|
|
854
|
+
for (const instance of this.subagents.values()) {
|
|
855
|
+
const hasCurrentTasks = instance.assignedSubtasks.some(id => currentSubtaskIds.has(id));
|
|
856
|
+
const hasFutureTasks = instance.assignedSubtasks.some(id => futureSubtaskIds.has(id));
|
|
857
|
+
|
|
858
|
+
if (!hasCurrentTasks && !hasFutureTasks) {
|
|
859
|
+
instance.active = false;
|
|
860
|
+
this.subagents.delete(instance.id);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
/**
|
|
866
|
+
* Cleanup all subagents
|
|
867
|
+
*/
|
|
868
|
+
private async cleanupAllSubagents(): Promise<void> {
|
|
869
|
+
this.subagents.clear();
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* Aggregate results from all subtasks
|
|
874
|
+
*/
|
|
875
|
+
private async aggregateResults(
|
|
876
|
+
results: Map<EntityId, SubtaskResult>,
|
|
877
|
+
originalTask: AgentInput
|
|
878
|
+
): Promise<unknown> {
|
|
879
|
+
// In production, this would use an LLM to synthesize results
|
|
880
|
+
const outputs: Record<string, unknown> = {};
|
|
881
|
+
|
|
882
|
+
results.forEach((result, subtaskId) => {
|
|
883
|
+
outputs[subtaskId] = {
|
|
884
|
+
result: result.output.result,
|
|
885
|
+
executionTimeMs: result.executionTimeMs,
|
|
886
|
+
};
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
return {
|
|
890
|
+
originalTask: originalTask.prompt,
|
|
891
|
+
subtaskResults: outputs,
|
|
892
|
+
summary: `Completed ${results.size} subtasks`,
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* Calculate critical steps metric (inspired by Kimi's Critical Steps formula)
|
|
898
|
+
*/
|
|
899
|
+
private calculateCriticalSteps(stages: ExecutionStage[]): number {
|
|
900
|
+
return stages.reduce((sum, stage) => {
|
|
901
|
+
// S_main: orchestration overhead per stage (constant)
|
|
902
|
+
const orchestrationOverhead = 1;
|
|
903
|
+
|
|
904
|
+
// max_i S_sub,i: slowest subagent in this stage
|
|
905
|
+
const maxSubagentSteps = stage.canParallelize ? Math.max(1, stage.subtasks.length * 0.5) : 1;
|
|
906
|
+
|
|
907
|
+
return sum + orchestrationOverhead + maxSubagentSteps;
|
|
908
|
+
}, 0);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
/**
|
|
912
|
+
* Calculate parallel efficiency metric
|
|
913
|
+
*/
|
|
914
|
+
private calculateParallelEfficiency(
|
|
915
|
+
decomposition: TaskDecomposition,
|
|
916
|
+
_results: Map<EntityId, SubtaskResult>
|
|
917
|
+
): number {
|
|
918
|
+
if (decomposition.subtasks.length <= 1) return 1;
|
|
919
|
+
|
|
920
|
+
const totalSubtasks = decomposition.subtasks.length;
|
|
921
|
+
const parallelSubtasks = decomposition.stages
|
|
922
|
+
.filter(s => s.canParallelize)
|
|
923
|
+
.reduce((sum, s) => sum + s.subtasks.length, 0);
|
|
924
|
+
|
|
925
|
+
return parallelSubtasks / totalSubtasks;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/**
|
|
929
|
+
* Determine overall execution status
|
|
930
|
+
*/
|
|
931
|
+
private determineStatus(
|
|
932
|
+
results: Map<EntityId, SubtaskResult>,
|
|
933
|
+
totalSubtasks: number
|
|
934
|
+
): 'success' | 'partial' | 'failed' {
|
|
935
|
+
const completed = Array.from(results.values()).filter(
|
|
936
|
+
r => r.output.state === AgentState.COMPLETED
|
|
937
|
+
).length;
|
|
938
|
+
|
|
939
|
+
if (completed === totalSubtasks) return 'success';
|
|
940
|
+
if (completed === 0) return 'failed';
|
|
941
|
+
return 'partial';
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
/**
|
|
945
|
+
* Generate unique task ID
|
|
946
|
+
*/
|
|
947
|
+
private generateTaskId(): EntityId {
|
|
948
|
+
return `swarm-task-${Date.now()}-${++this.taskCounter}`;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
/**
|
|
952
|
+
* Create minimal context for subagent execution
|
|
953
|
+
*/
|
|
954
|
+
private createMinimalContext(agent: Agent): AgentContext {
|
|
955
|
+
return new AgentContextBuilder()
|
|
956
|
+
.withAgentId(agent.id)
|
|
957
|
+
.withMemory(new InMemoryStore())
|
|
958
|
+
.withTools(new ToolRegistryImpl())
|
|
959
|
+
.withPlanner(new ClassicalPlanner({ algorithm: PlanningAlgorithm.HIERARCHICAL }))
|
|
960
|
+
.build();
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
/**
|
|
964
|
+
* Start the orchestrator
|
|
965
|
+
*/
|
|
966
|
+
async start(): Promise<void> {
|
|
967
|
+
this._isRunning = true;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
/**
|
|
971
|
+
* Stop the orchestrator
|
|
972
|
+
*/
|
|
973
|
+
async stop(): Promise<void> {
|
|
974
|
+
this._isRunning = false;
|
|
975
|
+
await this.cleanupAllSubagents();
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
/**
|
|
979
|
+
* Check if orchestrator is running
|
|
980
|
+
*/
|
|
981
|
+
isRunning(): boolean {
|
|
982
|
+
return this._isRunning;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
/**
|
|
986
|
+
* Get current subagent count
|
|
987
|
+
*/
|
|
988
|
+
getSubagentCount(): number {
|
|
989
|
+
return this.subagents.size;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
/**
|
|
993
|
+
* Register a custom subagent template
|
|
994
|
+
*/
|
|
995
|
+
registerTemplate(template: SubagentTemplate): void {
|
|
996
|
+
this.templates.set(template.id, template);
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
/**
|
|
1000
|
+
* Get registered templates
|
|
1001
|
+
*/
|
|
1002
|
+
getTemplates(): SubagentTemplate[] {
|
|
1003
|
+
return Array.from(this.templates.values());
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
/**
|
|
1008
|
+
* Default subagent templates
|
|
1009
|
+
*/
|
|
1010
|
+
const defaultTemplates: SubagentTemplate[] = [
|
|
1011
|
+
{
|
|
1012
|
+
id: 'researcher',
|
|
1013
|
+
name: 'Research Agent',
|
|
1014
|
+
description: 'Specialized in research and information gathering',
|
|
1015
|
+
capabilities: ['web_search', 'data_analysis', 'synthesis'],
|
|
1016
|
+
specialization: 'research',
|
|
1017
|
+
maxConcurrentTasks: 5,
|
|
1018
|
+
},
|
|
1019
|
+
{
|
|
1020
|
+
id: 'developer',
|
|
1021
|
+
name: 'Developer Agent',
|
|
1022
|
+
description: 'Specialized in code development and technical tasks',
|
|
1023
|
+
capabilities: ['code_generation', 'debugging', 'testing'],
|
|
1024
|
+
specialization: 'development',
|
|
1025
|
+
maxConcurrentTasks: 3,
|
|
1026
|
+
},
|
|
1027
|
+
{
|
|
1028
|
+
id: 'analyst',
|
|
1029
|
+
name: 'Analyst Agent',
|
|
1030
|
+
description: 'Specialized in data analysis and insights',
|
|
1031
|
+
capabilities: ['data_processing', 'statistical_analysis', 'visualization'],
|
|
1032
|
+
specialization: 'analysis',
|
|
1033
|
+
maxConcurrentTasks: 4,
|
|
1034
|
+
},
|
|
1035
|
+
{
|
|
1036
|
+
id: 'writer',
|
|
1037
|
+
name: 'Writer Agent',
|
|
1038
|
+
description: 'Specialized in content creation and writing',
|
|
1039
|
+
capabilities: ['content_generation', 'editing', 'formatting'],
|
|
1040
|
+
specialization: 'writing',
|
|
1041
|
+
maxConcurrentTasks: 5,
|
|
1042
|
+
},
|
|
1043
|
+
{
|
|
1044
|
+
id: 'fact-checker',
|
|
1045
|
+
name: 'Fact Checker Agent',
|
|
1046
|
+
description: 'Specialized in verifying facts and accuracy',
|
|
1047
|
+
capabilities: ['verification', 'cross_reference', 'validation'],
|
|
1048
|
+
specialization: 'verification',
|
|
1049
|
+
maxConcurrentTasks: 10,
|
|
1050
|
+
},
|
|
1051
|
+
{
|
|
1052
|
+
id: 'generalist',
|
|
1053
|
+
name: 'Generalist Agent',
|
|
1054
|
+
description: 'General-purpose agent for various tasks',
|
|
1055
|
+
capabilities: ['general_task_execution', 'coordination'],
|
|
1056
|
+
specialization: 'general',
|
|
1057
|
+
maxConcurrentTasks: 5,
|
|
1058
|
+
},
|
|
1059
|
+
];
|
|
1060
|
+
|
|
1061
|
+
/**
|
|
1062
|
+
* Create a swarm orchestrator with the given configuration
|
|
1063
|
+
*/
|
|
1064
|
+
export function createSwarm(config?: SwarmConfig): SwarmOrchestrator {
|
|
1065
|
+
return new SwarmOrchestrator(config);
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* Create a swarm-based agent that can be used in the framework
|
|
1070
|
+
*/
|
|
1071
|
+
export function createSwarmAgent(
|
|
1072
|
+
name: string,
|
|
1073
|
+
config?: SwarmConfig
|
|
1074
|
+
): Agent {
|
|
1075
|
+
const swarm = new SwarmOrchestrator(config);
|
|
1076
|
+
|
|
1077
|
+
const run = async (input: AgentInput, _ctx: AgentContext): Promise<AgentOutput> => {
|
|
1078
|
+
const result = await swarm.execute(input);
|
|
1079
|
+
|
|
1080
|
+
return {
|
|
1081
|
+
result: {
|
|
1082
|
+
swarmResult: result,
|
|
1083
|
+
aggregated: result.aggregatedOutput,
|
|
1084
|
+
},
|
|
1085
|
+
state: result.status === 'success' ? AgentState.COMPLETED : AgentState.FAILED,
|
|
1086
|
+
metadata: {
|
|
1087
|
+
startTime: new Date(Date.now() - result.executionTimeMs),
|
|
1088
|
+
durationMs: result.executionTimeMs,
|
|
1089
|
+
iterations: result.metrics.totalStages,
|
|
1090
|
+
},
|
|
1091
|
+
};
|
|
1092
|
+
};
|
|
1093
|
+
|
|
1094
|
+
return createRunnableAgent({
|
|
1095
|
+
name,
|
|
1096
|
+
description: `Swarm-based agent using ${config?.maxSubagents ?? 100} max subagents`,
|
|
1097
|
+
run,
|
|
1098
|
+
});
|
|
1099
|
+
}
|