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,522 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execution engine implementation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
ExecutionEngine,
|
|
7
|
+
ExecutionEngineConfig,
|
|
8
|
+
ExecutionOptions,
|
|
9
|
+
ExecutionStatus,
|
|
10
|
+
ExecutionState,
|
|
11
|
+
ExecutionProgress,
|
|
12
|
+
ExecutionEvent,
|
|
13
|
+
ExecutionEventType,
|
|
14
|
+
ExecutionEventHandler,
|
|
15
|
+
ExecutionGraph,
|
|
16
|
+
ExecutionNode,
|
|
17
|
+
ExecutionNodeStatus,
|
|
18
|
+
TaskExecutor,
|
|
19
|
+
ExecutionContext,
|
|
20
|
+
BackoffStrategy,
|
|
21
|
+
} from './types.js';
|
|
22
|
+
import {
|
|
23
|
+
Plan,
|
|
24
|
+
Task,
|
|
25
|
+
TaskResult,
|
|
26
|
+
TaskStatus,
|
|
27
|
+
PlanExecutionResult,
|
|
28
|
+
PlanExecutionStatus,
|
|
29
|
+
} from '../planner/types.js';
|
|
30
|
+
import type { EntityId } from '../core/types.js';
|
|
31
|
+
import { EventEmitter } from 'events';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Default execution configuration
|
|
35
|
+
*/
|
|
36
|
+
const DEFAULT_CONFIG: Required<ExecutionEngineConfig> = {
|
|
37
|
+
maxConcurrency: 4,
|
|
38
|
+
defaultTimeoutMs: 30000,
|
|
39
|
+
enableParallelExecution: true,
|
|
40
|
+
workerPoolSize: 4,
|
|
41
|
+
retryPolicy: {
|
|
42
|
+
maxRetries: 3,
|
|
43
|
+
backoffStrategy: BackoffStrategy.EXPONENTIAL,
|
|
44
|
+
initialDelayMs: 1000,
|
|
45
|
+
maxDelayMs: 30000,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Execution engine implementation
|
|
51
|
+
*/
|
|
52
|
+
export class ExecutionEngineImpl extends EventEmitter implements ExecutionEngine {
|
|
53
|
+
private config: Required<ExecutionEngineConfig>;
|
|
54
|
+
private executors: Map<string, TaskExecutor> = new Map();
|
|
55
|
+
private executions: Map<EntityId, ExecutionStatus> = new Map();
|
|
56
|
+
private runningExecutions: Map<EntityId, AbortController> = new Map();
|
|
57
|
+
|
|
58
|
+
constructor(config: Partial<ExecutionEngineConfig> = {}) {
|
|
59
|
+
super();
|
|
60
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async execute(plan: Plan, options: ExecutionOptions = {}): Promise<PlanExecutionResult> {
|
|
64
|
+
const executionId = options.executionId ?? this.generateId();
|
|
65
|
+
// timeoutMs is used for potential future timeout implementation
|
|
66
|
+
void (options.timeoutMs ?? this.config.defaultTimeoutMs);
|
|
67
|
+
|
|
68
|
+
// Create execution graph
|
|
69
|
+
const graph = this.buildExecutionGraph(plan);
|
|
70
|
+
|
|
71
|
+
// Initialize execution status
|
|
72
|
+
const status: ExecutionStatus = {
|
|
73
|
+
executionId,
|
|
74
|
+
planId: plan.id,
|
|
75
|
+
state: ExecutionState.PENDING,
|
|
76
|
+
progress: this.calculateProgress(graph),
|
|
77
|
+
currentTasks: [],
|
|
78
|
+
startedAt: new Date(),
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
this.executions.set(executionId, status);
|
|
82
|
+
|
|
83
|
+
// Create abort controller for cancellation
|
|
84
|
+
const abortController = new AbortController();
|
|
85
|
+
this.runningExecutions.set(executionId, abortController);
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
// Update state to running
|
|
89
|
+
this.updateExecutionState(executionId, ExecutionState.RUNNING);
|
|
90
|
+
this.emitEvent('execution:start', executionId, { planId: plan.id });
|
|
91
|
+
|
|
92
|
+
// Execute the plan
|
|
93
|
+
const results = await this.executeGraph(
|
|
94
|
+
graph,
|
|
95
|
+
executionId,
|
|
96
|
+
abortController.signal,
|
|
97
|
+
options
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
// Determine final status
|
|
101
|
+
const failedTasks = Array.from(results.values()).filter(
|
|
102
|
+
r => r.status === TaskStatus.FAILED
|
|
103
|
+
);
|
|
104
|
+
const cancelledTasks = Array.from(results.values()).filter(
|
|
105
|
+
r => r.status === TaskStatus.CANCELLED
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
let finalStatus: PlanExecutionStatus;
|
|
109
|
+
if (cancelledTasks.length > 0) {
|
|
110
|
+
finalStatus = PlanExecutionStatus.CANCELLED;
|
|
111
|
+
} else if (failedTasks.length === 0) {
|
|
112
|
+
finalStatus = PlanExecutionStatus.COMPLETED;
|
|
113
|
+
} else if (failedTasks.length < results.size) {
|
|
114
|
+
finalStatus = PlanExecutionStatus.PARTIAL;
|
|
115
|
+
} else {
|
|
116
|
+
finalStatus = PlanExecutionStatus.FAILED;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const endTime = new Date();
|
|
120
|
+
const result: PlanExecutionResult = {
|
|
121
|
+
planId: plan.id,
|
|
122
|
+
status: finalStatus,
|
|
123
|
+
taskResults: results,
|
|
124
|
+
startedAt: status.startedAt,
|
|
125
|
+
completedAt: endTime,
|
|
126
|
+
totalExecutionTimeMs: endTime.getTime() - status.startedAt.getTime(),
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Update execution status
|
|
130
|
+
this.updateExecutionState(
|
|
131
|
+
executionId,
|
|
132
|
+
finalStatus === PlanExecutionStatus.COMPLETED
|
|
133
|
+
? ExecutionState.COMPLETED
|
|
134
|
+
: ExecutionState.FAILED
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
this.emitEvent('execution:complete', executionId, { result });
|
|
138
|
+
|
|
139
|
+
return result;
|
|
140
|
+
} catch (error) {
|
|
141
|
+
this.updateExecutionState(executionId, ExecutionState.FAILED);
|
|
142
|
+
this.emitEvent('execution:fail', executionId, { error });
|
|
143
|
+
|
|
144
|
+
throw error;
|
|
145
|
+
} finally {
|
|
146
|
+
this.runningExecutions.delete(executionId);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async cancel(executionId: EntityId): Promise<boolean> {
|
|
151
|
+
const controller = this.runningExecutions.get(executionId);
|
|
152
|
+
if (!controller) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
controller.abort();
|
|
157
|
+
this.updateExecutionState(executionId, ExecutionState.CANCELLED);
|
|
158
|
+
this.emitEvent('execution:cancel', executionId, {});
|
|
159
|
+
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
getStatus(executionId: EntityId): ExecutionStatus | undefined {
|
|
164
|
+
return this.executions.get(executionId);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
registerExecutor(executor: TaskExecutor): void {
|
|
168
|
+
const type = executor.constructor.name;
|
|
169
|
+
this.executors.set(type, executor);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
on(event: ExecutionEventType, handler: ExecutionEventHandler): this {
|
|
173
|
+
super.on(event, handler);
|
|
174
|
+
return this;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
off(event: ExecutionEventType, handler: ExecutionEventHandler): this {
|
|
178
|
+
super.off(event, handler);
|
|
179
|
+
return this;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Build execution graph from plan
|
|
184
|
+
*/
|
|
185
|
+
private buildExecutionGraph(plan: Plan): ExecutionGraph {
|
|
186
|
+
const nodes = new Map<EntityId, ExecutionNode>();
|
|
187
|
+
const readyQueue: EntityId[] = [];
|
|
188
|
+
|
|
189
|
+
// Create nodes for all tasks
|
|
190
|
+
for (const task of plan.tasks) {
|
|
191
|
+
const node: ExecutionNode = {
|
|
192
|
+
task,
|
|
193
|
+
status: ExecutionNodeStatus.PENDING,
|
|
194
|
+
dependencies: new Set(task.dependencies),
|
|
195
|
+
dependents: new Set(),
|
|
196
|
+
};
|
|
197
|
+
nodes.set(task.id, node);
|
|
198
|
+
|
|
199
|
+
// Add to ready queue if no dependencies
|
|
200
|
+
if (task.dependencies.length === 0) {
|
|
201
|
+
readyQueue.push(task.id);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Build reverse dependency map (dependents)
|
|
206
|
+
for (const [id, node] of nodes) {
|
|
207
|
+
for (const depId of node.dependencies) {
|
|
208
|
+
const depNode = nodes.get(depId);
|
|
209
|
+
if (depNode) {
|
|
210
|
+
depNode.dependents.add(id);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
planId: plan.id,
|
|
217
|
+
nodes,
|
|
218
|
+
readyQueue,
|
|
219
|
+
completedCount: 0,
|
|
220
|
+
failedCount: 0,
|
|
221
|
+
totalCount: plan.tasks.length,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Execute the execution graph
|
|
227
|
+
*/
|
|
228
|
+
private async executeGraph(
|
|
229
|
+
graph: ExecutionGraph,
|
|
230
|
+
executionId: EntityId,
|
|
231
|
+
abortSignal: AbortSignal,
|
|
232
|
+
options: ExecutionOptions
|
|
233
|
+
): Promise<Map<EntityId, TaskResult>> {
|
|
234
|
+
const results = new Map<EntityId, TaskResult>();
|
|
235
|
+
const executing = new Set<Promise<void>>();
|
|
236
|
+
|
|
237
|
+
const processNode = async (nodeId: EntityId): Promise<void> => {
|
|
238
|
+
const node = graph.nodes.get(nodeId);
|
|
239
|
+
if (!node || abortSignal.aborted) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Check if all dependencies are satisfied
|
|
244
|
+
for (const depId of node.dependencies) {
|
|
245
|
+
const depResult = results.get(depId);
|
|
246
|
+
if (!depResult || depResult.status === TaskStatus.FAILED) {
|
|
247
|
+
// Skip this task if dependency failed
|
|
248
|
+
const skipResult: TaskResult = {
|
|
249
|
+
taskId: nodeId,
|
|
250
|
+
status: TaskStatus.SKIPPED,
|
|
251
|
+
executionTimeMs: 0,
|
|
252
|
+
startedAt: new Date(),
|
|
253
|
+
};
|
|
254
|
+
results.set(nodeId, skipResult);
|
|
255
|
+
this.updateNodeStatus(graph, nodeId, ExecutionNodeStatus.CANCELLED);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Update status
|
|
261
|
+
this.updateNodeStatus(graph, nodeId, ExecutionNodeStatus.RUNNING);
|
|
262
|
+
this.emitEvent('task:start', executionId, { taskId: nodeId });
|
|
263
|
+
|
|
264
|
+
if (options.onTaskStart) {
|
|
265
|
+
options.onTaskStart(nodeId);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const startTime = new Date();
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
// Find appropriate executor
|
|
272
|
+
const executor = this.findExecutor(node.task);
|
|
273
|
+
if (!executor) {
|
|
274
|
+
throw new Error(`No executor found for task: ${node.task.name}`);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Create execution context
|
|
278
|
+
const context: ExecutionContext = {
|
|
279
|
+
executionId,
|
|
280
|
+
taskId: nodeId,
|
|
281
|
+
planId: graph.planId,
|
|
282
|
+
inputs: this.collectInputs(node.task, results),
|
|
283
|
+
sharedState: new Map(),
|
|
284
|
+
metadata: {
|
|
285
|
+
startedAt: startTime,
|
|
286
|
+
attemptNumber: 1,
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
// Execute task
|
|
291
|
+
const result = await executor.execute(node.task, context);
|
|
292
|
+
|
|
293
|
+
results.set(nodeId, result);
|
|
294
|
+
|
|
295
|
+
// Update status based on result
|
|
296
|
+
if (result.status === TaskStatus.COMPLETED) {
|
|
297
|
+
this.updateNodeStatus(graph, nodeId, ExecutionNodeStatus.COMPLETED);
|
|
298
|
+
this.emitEvent('task:complete', executionId, { taskId: nodeId, result });
|
|
299
|
+
|
|
300
|
+
if (options.onTaskComplete) {
|
|
301
|
+
options.onTaskComplete(nodeId, result);
|
|
302
|
+
}
|
|
303
|
+
} else {
|
|
304
|
+
this.updateNodeStatus(graph, nodeId, ExecutionNodeStatus.FAILED);
|
|
305
|
+
this.emitEvent('task:fail', executionId, { taskId: nodeId, result });
|
|
306
|
+
|
|
307
|
+
if (options.onTaskError) {
|
|
308
|
+
options.onTaskError(nodeId, new Error(result.error?.message ?? 'Task failed'));
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
} catch (error) {
|
|
312
|
+
const failedResult: TaskResult = {
|
|
313
|
+
taskId: nodeId,
|
|
314
|
+
status: TaskStatus.FAILED,
|
|
315
|
+
error: {
|
|
316
|
+
code: 'EXECUTION_ERROR',
|
|
317
|
+
message: error instanceof Error ? error.message : String(error),
|
|
318
|
+
retryable: true,
|
|
319
|
+
},
|
|
320
|
+
executionTimeMs: Date.now() - startTime.getTime(),
|
|
321
|
+
startedAt: startTime,
|
|
322
|
+
completedAt: new Date(),
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
results.set(nodeId, failedResult);
|
|
326
|
+
this.updateNodeStatus(graph, nodeId, ExecutionNodeStatus.FAILED);
|
|
327
|
+
this.emitEvent('task:fail', executionId, { taskId: nodeId, error });
|
|
328
|
+
|
|
329
|
+
if (options.onTaskError) {
|
|
330
|
+
options.onTaskError(nodeId, error instanceof Error ? error : new Error(String(error)));
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
// Process ready queue
|
|
336
|
+
while (graph.readyQueue.length > 0 || executing.size > 0) {
|
|
337
|
+
if (abortSignal.aborted) {
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Start new tasks up to concurrency limit
|
|
342
|
+
while (
|
|
343
|
+
graph.readyQueue.length > 0 &&
|
|
344
|
+
executing.size < this.config.maxConcurrency
|
|
345
|
+
) {
|
|
346
|
+
const nodeId = graph.readyQueue.shift()!;
|
|
347
|
+
const promise = processNode(nodeId).then(() => {
|
|
348
|
+
executing.delete(promise);
|
|
349
|
+
|
|
350
|
+
// Add dependents to ready queue
|
|
351
|
+
const node = graph.nodes.get(nodeId);
|
|
352
|
+
if (node) {
|
|
353
|
+
for (const dependentId of node.dependents) {
|
|
354
|
+
const dependent = graph.nodes.get(dependentId);
|
|
355
|
+
if (dependent && dependent.status === ExecutionNodeStatus.PENDING) {
|
|
356
|
+
// Check if all dependencies are completed
|
|
357
|
+
const allDepsCompleted = Array.from(dependent.dependencies).every(
|
|
358
|
+
depId => {
|
|
359
|
+
const depNode = graph.nodes.get(depId);
|
|
360
|
+
return depNode?.status === ExecutionNodeStatus.COMPLETED;
|
|
361
|
+
}
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
if (allDepsCompleted) {
|
|
365
|
+
graph.readyQueue.push(dependentId);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
executing.add(promise);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Wait for at least one task to complete
|
|
376
|
+
if (executing.size > 0) {
|
|
377
|
+
await Promise.race(executing);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return results;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Find an executor for a task
|
|
386
|
+
*/
|
|
387
|
+
private findExecutor(task: Task): TaskExecutor | undefined {
|
|
388
|
+
for (const executor of this.executors.values()) {
|
|
389
|
+
if (executor.canExecute(task)) {
|
|
390
|
+
return executor;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return undefined;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Collect inputs from completed dependencies
|
|
398
|
+
*/
|
|
399
|
+
private collectInputs(
|
|
400
|
+
task: Task,
|
|
401
|
+
results: Map<EntityId, TaskResult>
|
|
402
|
+
): Map<EntityId, unknown> {
|
|
403
|
+
const inputs = new Map<EntityId, unknown>();
|
|
404
|
+
|
|
405
|
+
for (const depId of task.dependencies) {
|
|
406
|
+
const result = results.get(depId);
|
|
407
|
+
if (result?.output !== undefined) {
|
|
408
|
+
inputs.set(depId, result.output);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return inputs;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Update node status and execution progress
|
|
417
|
+
*/
|
|
418
|
+
private updateNodeStatus(
|
|
419
|
+
graph: ExecutionGraph,
|
|
420
|
+
nodeId: EntityId,
|
|
421
|
+
status: ExecutionNodeStatus
|
|
422
|
+
): void {
|
|
423
|
+
const node = graph.nodes.get(nodeId) as MutableExecutionNode | undefined;
|
|
424
|
+
if (!node) {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
node.status = status;
|
|
429
|
+
|
|
430
|
+
if (status === ExecutionNodeStatus.COMPLETED) {
|
|
431
|
+
(graph as MutableExecutionGraph).completedCount++;
|
|
432
|
+
} else if (status === ExecutionNodeStatus.FAILED) {
|
|
433
|
+
(graph as MutableExecutionGraph).failedCount++;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Update execution status
|
|
437
|
+
for (const execStatus of this.executions.values()) {
|
|
438
|
+
if (execStatus.planId === graph.planId) {
|
|
439
|
+
(execStatus as MutableExecutionStatus).progress = this.calculateProgress(graph);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Calculate execution progress
|
|
446
|
+
*/
|
|
447
|
+
private calculateProgress(graph: ExecutionGraph): ExecutionProgress {
|
|
448
|
+
const total = graph.totalCount;
|
|
449
|
+
const completed = graph.completedCount;
|
|
450
|
+
const failed = graph.failedCount;
|
|
451
|
+
const running = Array.from(graph.nodes.values()).filter(
|
|
452
|
+
n => n.status === ExecutionNodeStatus.RUNNING
|
|
453
|
+
).length;
|
|
454
|
+
const pending = total - completed - failed - running;
|
|
455
|
+
|
|
456
|
+
return {
|
|
457
|
+
total,
|
|
458
|
+
completed,
|
|
459
|
+
failed,
|
|
460
|
+
pending,
|
|
461
|
+
running,
|
|
462
|
+
percentage: total > 0 ? Math.round(((completed + failed) / total) * 100) : 0,
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Update execution state
|
|
468
|
+
*/
|
|
469
|
+
private updateExecutionState(executionId: EntityId, state: ExecutionState): void {
|
|
470
|
+
const status = this.executions.get(executionId);
|
|
471
|
+
if (status) {
|
|
472
|
+
this.executions.set(executionId, { ...status, state });
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Emit execution event
|
|
478
|
+
*/
|
|
479
|
+
private emitEvent(
|
|
480
|
+
type: ExecutionEventType,
|
|
481
|
+
executionId: EntityId,
|
|
482
|
+
data: unknown
|
|
483
|
+
): void {
|
|
484
|
+
const event: ExecutionEvent = {
|
|
485
|
+
type,
|
|
486
|
+
executionId,
|
|
487
|
+
timestamp: new Date(),
|
|
488
|
+
data,
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
this.emit(type, event);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Generate a unique ID
|
|
496
|
+
*/
|
|
497
|
+
private generateId(): EntityId {
|
|
498
|
+
return `exec-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Mutable execution node for internal use
|
|
504
|
+
*/
|
|
505
|
+
interface MutableExecutionNode extends ExecutionNode {
|
|
506
|
+
status: ExecutionNodeStatus;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Mutable execution graph for internal use
|
|
511
|
+
*/
|
|
512
|
+
interface MutableExecutionGraph extends ExecutionGraph {
|
|
513
|
+
completedCount: number;
|
|
514
|
+
failedCount: number;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Mutable execution status for internal use
|
|
519
|
+
*/
|
|
520
|
+
interface MutableExecutionStatus extends ExecutionStatus {
|
|
521
|
+
progress: ExecutionProgress;
|
|
522
|
+
}
|