@defai.digital/agent-domain 13.0.3
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/LICENSE +214 -0
- package/dist/enhanced-executor.d.ts +170 -0
- package/dist/enhanced-executor.d.ts.map +1 -0
- package/dist/enhanced-executor.js +1072 -0
- package/dist/enhanced-executor.js.map +1 -0
- package/dist/executor.d.ts +120 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +929 -0
- package/dist/executor.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.d.ts +50 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +160 -0
- package/dist/loader.js.map +1 -0
- package/dist/persistent-registry.d.ts +105 -0
- package/dist/persistent-registry.d.ts.map +1 -0
- package/dist/persistent-registry.js +183 -0
- package/dist/persistent-registry.js.map +1 -0
- package/dist/production-factories.d.ts +70 -0
- package/dist/production-factories.d.ts.map +1 -0
- package/dist/production-factories.js +434 -0
- package/dist/production-factories.js.map +1 -0
- package/dist/prompt-executor.d.ts +119 -0
- package/dist/prompt-executor.d.ts.map +1 -0
- package/dist/prompt-executor.js +211 -0
- package/dist/prompt-executor.js.map +1 -0
- package/dist/registry.d.ts +57 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +123 -0
- package/dist/registry.js.map +1 -0
- package/dist/selection-service.d.ts +74 -0
- package/dist/selection-service.d.ts.map +1 -0
- package/dist/selection-service.js +322 -0
- package/dist/selection-service.js.map +1 -0
- package/dist/selector.d.ts +51 -0
- package/dist/selector.d.ts.map +1 -0
- package/dist/selector.js +249 -0
- package/dist/selector.js.map +1 -0
- package/dist/stub-checkpoint.d.ts +23 -0
- package/dist/stub-checkpoint.d.ts.map +1 -0
- package/dist/stub-checkpoint.js +137 -0
- package/dist/stub-checkpoint.js.map +1 -0
- package/dist/stub-delegation-tracker.d.ts +25 -0
- package/dist/stub-delegation-tracker.d.ts.map +1 -0
- package/dist/stub-delegation-tracker.js +118 -0
- package/dist/stub-delegation-tracker.js.map +1 -0
- package/dist/stub-parallel-executor.d.ts +19 -0
- package/dist/stub-parallel-executor.d.ts.map +1 -0
- package/dist/stub-parallel-executor.js +176 -0
- package/dist/stub-parallel-executor.js.map +1 -0
- package/dist/types.d.ts +614 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +15 -0
- package/dist/types.js.map +1 -0
- package/dist/workflow-templates.d.ts +117 -0
- package/dist/workflow-templates.d.ts.map +1 -0
- package/dist/workflow-templates.js +342 -0
- package/dist/workflow-templates.js.map +1 -0
- package/package.json +51 -0
- package/src/enhanced-executor.ts +1395 -0
- package/src/executor.ts +1153 -0
- package/src/index.ts +172 -0
- package/src/loader.ts +191 -0
- package/src/persistent-registry.ts +235 -0
- package/src/production-factories.ts +613 -0
- package/src/prompt-executor.ts +310 -0
- package/src/registry.ts +167 -0
- package/src/selection-service.ts +411 -0
- package/src/selector.ts +299 -0
- package/src/stub-checkpoint.ts +187 -0
- package/src/stub-delegation-tracker.ts +161 -0
- package/src/stub-parallel-executor.ts +224 -0
- package/src/types.ts +784 -0
- package/src/workflow-templates.ts +393 -0
|
@@ -0,0 +1,613 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Production Factory Implementations
|
|
3
|
+
*
|
|
4
|
+
* Provides real implementations of checkpoint, delegation, and parallel execution
|
|
5
|
+
* factories for production use. These wire together the various domain packages.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { createProductionFactories } from '@defai.digital/agent-domain';
|
|
10
|
+
*
|
|
11
|
+
* const factories = createProductionFactories({
|
|
12
|
+
* checkpointStorage: sqliteCheckpointStorage,
|
|
13
|
+
* // ... other config
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* const executor = createEnhancedAgentExecutor(registry, {
|
|
17
|
+
* ...factories,
|
|
18
|
+
* // ... other config
|
|
19
|
+
* });
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import type { CheckpointConfig, ParallelExecutionConfig } from '@defai.digital/contracts';
|
|
24
|
+
import { createDefaultParallelExecutionConfig } from '@defai.digital/contracts';
|
|
25
|
+
import type {
|
|
26
|
+
CheckpointStoragePort,
|
|
27
|
+
CheckpointManagerPort,
|
|
28
|
+
CheckpointStorageFactory,
|
|
29
|
+
CheckpointManagerFactory,
|
|
30
|
+
DelegationTrackerFactory,
|
|
31
|
+
DelegationTrackerPort,
|
|
32
|
+
ParallelExecutorPort,
|
|
33
|
+
ParallelExecutorFactory,
|
|
34
|
+
Checkpoint,
|
|
35
|
+
} from './types.js';
|
|
36
|
+
import type {
|
|
37
|
+
DelegationContext,
|
|
38
|
+
DelegationCheckResult,
|
|
39
|
+
DelegationResult,
|
|
40
|
+
DelegationRequest,
|
|
41
|
+
} from '@defai.digital/contracts';
|
|
42
|
+
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// Production Checkpoint Manager
|
|
45
|
+
// ============================================================================
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Production checkpoint manager implementation
|
|
49
|
+
*
|
|
50
|
+
* INV-CP-001: Checkpoint contains all data needed to resume
|
|
51
|
+
* INV-CP-002: Resumed execution starts from step after checkpoint
|
|
52
|
+
*/
|
|
53
|
+
class ProductionCheckpointManager implements CheckpointManagerPort {
|
|
54
|
+
private readonly agentId: string;
|
|
55
|
+
private readonly sessionId: string | undefined;
|
|
56
|
+
private readonly storage: CheckpointStoragePort;
|
|
57
|
+
private readonly config: CheckpointConfig;
|
|
58
|
+
private checkpointCount = 0;
|
|
59
|
+
|
|
60
|
+
constructor(
|
|
61
|
+
agentId: string,
|
|
62
|
+
sessionId: string | undefined,
|
|
63
|
+
storage: CheckpointStoragePort,
|
|
64
|
+
config: CheckpointConfig
|
|
65
|
+
) {
|
|
66
|
+
this.agentId = agentId;
|
|
67
|
+
this.sessionId = sessionId;
|
|
68
|
+
this.storage = storage;
|
|
69
|
+
this.config = config;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
getConfig(): CheckpointConfig {
|
|
73
|
+
return { ...this.config };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
shouldCheckpoint(stepIndex: number): boolean {
|
|
77
|
+
if (!this.config.enabled) return false;
|
|
78
|
+
if (this.config.intervalSteps === 0) return true;
|
|
79
|
+
return stepIndex % this.config.intervalSteps === 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async createCheckpoint(
|
|
83
|
+
stepIndex: number,
|
|
84
|
+
stepId: string,
|
|
85
|
+
previousOutputs: Record<string, unknown>,
|
|
86
|
+
metadata?: Record<string, unknown>
|
|
87
|
+
): Promise<Checkpoint> {
|
|
88
|
+
const now = new Date();
|
|
89
|
+
const expiresAt = new Date(
|
|
90
|
+
now.getTime() + this.config.retentionHours * 60 * 60 * 1000
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// INV-CP-001: Checkpoint contains all data needed to resume
|
|
94
|
+
const checkpoint: Checkpoint = {
|
|
95
|
+
checkpointId: crypto.randomUUID(),
|
|
96
|
+
agentId: this.agentId,
|
|
97
|
+
sessionId: this.sessionId,
|
|
98
|
+
stepIndex,
|
|
99
|
+
stepId,
|
|
100
|
+
previousOutputs,
|
|
101
|
+
metadata,
|
|
102
|
+
createdAt: now.toISOString(),
|
|
103
|
+
expiresAt: expiresAt.toISOString(),
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
await this.storage.save(checkpoint);
|
|
107
|
+
this.checkpointCount++;
|
|
108
|
+
|
|
109
|
+
// Enforce max checkpoints
|
|
110
|
+
if (this.checkpointCount > this.config.maxCheckpoints) {
|
|
111
|
+
const allCheckpoints = await this.storage.list(this.agentId, this.sessionId);
|
|
112
|
+
const toDelete = allCheckpoints.slice(this.config.maxCheckpoints);
|
|
113
|
+
for (const cp of toDelete) {
|
|
114
|
+
await this.storage.delete(cp.checkpointId);
|
|
115
|
+
}
|
|
116
|
+
this.checkpointCount = this.config.maxCheckpoints;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return checkpoint;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async getLatestCheckpoint(): Promise<Checkpoint | null> {
|
|
123
|
+
return this.storage.loadLatest(this.agentId, this.sessionId);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async getResumeContext(checkpointId: string): Promise<{
|
|
127
|
+
startFromStep: number;
|
|
128
|
+
previousOutputs: Record<string, unknown>;
|
|
129
|
+
} | null> {
|
|
130
|
+
const checkpoint = await this.storage.load(checkpointId);
|
|
131
|
+
|
|
132
|
+
if (!checkpoint) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Check if expired
|
|
137
|
+
if (checkpoint.expiresAt) {
|
|
138
|
+
const expiresAt = new Date(checkpoint.expiresAt).getTime();
|
|
139
|
+
if (expiresAt < Date.now()) {
|
|
140
|
+
await this.storage.delete(checkpointId);
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// INV-CP-002: Resumed execution starts from step after checkpoint
|
|
146
|
+
return {
|
|
147
|
+
startFromStep: checkpoint.stepIndex + 1,
|
|
148
|
+
previousOutputs: checkpoint.previousOutputs,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async cleanup(): Promise<number> {
|
|
153
|
+
return this.storage.deleteExpired();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ============================================================================
|
|
158
|
+
// Production Delegation Tracker
|
|
159
|
+
// ============================================================================
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Production delegation tracker implementation
|
|
163
|
+
*
|
|
164
|
+
* INV-DT-001: Maximum delegation depth enforced
|
|
165
|
+
* INV-DT-002: Circular delegations prevented
|
|
166
|
+
*/
|
|
167
|
+
class ProductionDelegationTracker implements DelegationTrackerPort {
|
|
168
|
+
private readonly agentId: string;
|
|
169
|
+
private readonly context: DelegationContext;
|
|
170
|
+
private readonly maxDepth: number;
|
|
171
|
+
private readonly history: DelegationResult[] = [];
|
|
172
|
+
private readonly rootTaskId: string;
|
|
173
|
+
|
|
174
|
+
constructor(
|
|
175
|
+
agentId: string,
|
|
176
|
+
parentContext: DelegationContext | undefined,
|
|
177
|
+
maxDepth: number
|
|
178
|
+
) {
|
|
179
|
+
this.agentId = agentId;
|
|
180
|
+
this.maxDepth = maxDepth;
|
|
181
|
+
|
|
182
|
+
if (parentContext) {
|
|
183
|
+
this.rootTaskId = parentContext.rootTaskId;
|
|
184
|
+
this.context = {
|
|
185
|
+
initiatorAgentId: parentContext.initiatorAgentId,
|
|
186
|
+
rootTaskId: parentContext.rootTaskId,
|
|
187
|
+
currentDepth: parentContext.currentDepth + 1,
|
|
188
|
+
delegationChain: [...parentContext.delegationChain, agentId],
|
|
189
|
+
maxDepth: parentContext.maxDepth,
|
|
190
|
+
startedAt: parentContext.startedAt,
|
|
191
|
+
};
|
|
192
|
+
} else {
|
|
193
|
+
this.rootTaskId = crypto.randomUUID();
|
|
194
|
+
this.context = {
|
|
195
|
+
initiatorAgentId: agentId,
|
|
196
|
+
rootTaskId: this.rootTaskId,
|
|
197
|
+
currentDepth: 0,
|
|
198
|
+
delegationChain: [agentId],
|
|
199
|
+
maxDepth,
|
|
200
|
+
startedAt: new Date().toISOString(),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
getContext(): DelegationContext {
|
|
206
|
+
return { ...this.context };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
canDelegate(toAgentId: string): DelegationCheckResult {
|
|
210
|
+
// INV-DT-001: Check depth limit
|
|
211
|
+
if (this.context.currentDepth >= this.maxDepth) {
|
|
212
|
+
return {
|
|
213
|
+
allowed: false,
|
|
214
|
+
reason: 'MAX_DEPTH_EXCEEDED',
|
|
215
|
+
message: `Maximum delegation depth (${this.maxDepth}) exceeded`,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// INV-DT-002: Check for circular delegation
|
|
220
|
+
if (this.context.delegationChain.includes(toAgentId)) {
|
|
221
|
+
return {
|
|
222
|
+
allowed: false,
|
|
223
|
+
reason: 'CIRCULAR_DELEGATION',
|
|
224
|
+
message: `Circular delegation detected: ${this.context.delegationChain.join(' -> ')} -> ${toAgentId}`,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
allowed: true,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
createDelegationRequest(
|
|
234
|
+
toAgentId: string,
|
|
235
|
+
task: string,
|
|
236
|
+
input?: unknown,
|
|
237
|
+
timeout?: number
|
|
238
|
+
): DelegationRequest | null {
|
|
239
|
+
const check = this.canDelegate(toAgentId);
|
|
240
|
+
if (!check.allowed) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
fromAgentId: this.agentId,
|
|
246
|
+
toAgentId,
|
|
247
|
+
task,
|
|
248
|
+
input,
|
|
249
|
+
timeout,
|
|
250
|
+
context: this.context,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
createChildContext(toAgentId: string): DelegationContext {
|
|
255
|
+
return {
|
|
256
|
+
initiatorAgentId: this.context.initiatorAgentId,
|
|
257
|
+
rootTaskId: this.context.rootTaskId,
|
|
258
|
+
currentDepth: this.context.currentDepth + 1,
|
|
259
|
+
delegationChain: [...this.context.delegationChain, toAgentId],
|
|
260
|
+
maxDepth: this.context.maxDepth,
|
|
261
|
+
startedAt: this.context.startedAt,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
recordResult(result: DelegationResult): void {
|
|
266
|
+
this.history.push(result);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
getHistory(): DelegationResult[] {
|
|
270
|
+
return [...this.history];
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
isRoot(): boolean {
|
|
274
|
+
return this.context.currentDepth === 0;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
getRemainingDepth(): number {
|
|
278
|
+
return this.maxDepth - this.context.currentDepth;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// ============================================================================
|
|
283
|
+
// Production Parallel Executor
|
|
284
|
+
// ============================================================================
|
|
285
|
+
|
|
286
|
+
import type { AgentWorkflowStep } from '@defai.digital/contracts';
|
|
287
|
+
import type { ParallelStepExecutor, ParallelGroupResult, ParallelStepResult } from './types.js';
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Production parallel executor implementation
|
|
291
|
+
*
|
|
292
|
+
* INV-PE-001: Independent steps execute concurrently
|
|
293
|
+
* INV-PE-002: Dependencies honored (DAG ordering)
|
|
294
|
+
* INV-PE-003: Concurrency limit respected
|
|
295
|
+
*/
|
|
296
|
+
class ProductionParallelExecutor implements ParallelExecutorPort {
|
|
297
|
+
private readonly config: ParallelExecutionConfig;
|
|
298
|
+
private cancelled = false;
|
|
299
|
+
|
|
300
|
+
constructor(config: ParallelExecutionConfig) {
|
|
301
|
+
this.config = config;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
getConfig(): ParallelExecutionConfig {
|
|
305
|
+
return { ...this.config };
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async executeGroup(
|
|
309
|
+
steps: AgentWorkflowStep[],
|
|
310
|
+
executor: ParallelStepExecutor,
|
|
311
|
+
previousOutputs: Record<string, unknown> = {}
|
|
312
|
+
): Promise<ParallelGroupResult> {
|
|
313
|
+
this.cancelled = false;
|
|
314
|
+
const startTime = Date.now();
|
|
315
|
+
const results: ParallelStepResult[] = [];
|
|
316
|
+
const outputs = { ...previousOutputs };
|
|
317
|
+
|
|
318
|
+
if (!this.config.enabled) {
|
|
319
|
+
// Sequential execution
|
|
320
|
+
for (const step of steps) {
|
|
321
|
+
if (this.cancelled) {
|
|
322
|
+
results.push(this.createCancelledResult(step.stepId));
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const stepResult = await this.executeStep(step, executor, outputs);
|
|
327
|
+
results.push(stepResult);
|
|
328
|
+
|
|
329
|
+
if (stepResult.success && stepResult.output !== undefined) {
|
|
330
|
+
outputs[step.stepId] = stepResult.output;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Handle failure strategy
|
|
334
|
+
if (!stepResult.success && this.config.failureStrategy === 'failFast') {
|
|
335
|
+
break;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
} else {
|
|
339
|
+
// Parallel execution with DAG ordering
|
|
340
|
+
// INV-PE-002: Dependencies honored
|
|
341
|
+
const layers = this.buildExecutionLayers(steps);
|
|
342
|
+
|
|
343
|
+
for (const layer of layers) {
|
|
344
|
+
if (this.cancelled) {
|
|
345
|
+
for (const step of layer) {
|
|
346
|
+
results.push(this.createCancelledResult(step.stepId));
|
|
347
|
+
}
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// INV-PE-001: Independent steps execute concurrently
|
|
352
|
+
// INV-PE-003: Concurrency limit respected
|
|
353
|
+
const layerResults = await this.executeLayer(layer, executor, outputs);
|
|
354
|
+
results.push(...layerResults);
|
|
355
|
+
|
|
356
|
+
// Collect outputs
|
|
357
|
+
for (const result of layerResults) {
|
|
358
|
+
if (result.success && result.output !== undefined) {
|
|
359
|
+
outputs[result.stepId] = result.output;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Handle failure strategy
|
|
364
|
+
if (this.config.failureStrategy === 'failFast') {
|
|
365
|
+
if (layerResults.some((r) => !r.success)) {
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const failedCount = results.filter((r) => !r.success && !r.cancelled).length;
|
|
373
|
+
const cancelledCount = results.filter((r) => r.cancelled).length;
|
|
374
|
+
|
|
375
|
+
return {
|
|
376
|
+
stepResults: results,
|
|
377
|
+
totalDurationMs: Date.now() - startTime,
|
|
378
|
+
allSucceeded: failedCount === 0 && cancelledCount === 0,
|
|
379
|
+
failedCount,
|
|
380
|
+
cancelledCount,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
buildExecutionLayers(steps: AgentWorkflowStep[]): AgentWorkflowStep[][] {
|
|
385
|
+
const layers: AgentWorkflowStep[][] = [];
|
|
386
|
+
const completed = new Set<string>();
|
|
387
|
+
const remaining = [...steps];
|
|
388
|
+
|
|
389
|
+
// Guard against infinite loops with max iteration limit
|
|
390
|
+
const maxIterations = steps.length + 1;
|
|
391
|
+
let iterations = 0;
|
|
392
|
+
|
|
393
|
+
while (remaining.length > 0 && iterations < maxIterations) {
|
|
394
|
+
iterations++;
|
|
395
|
+
const layer: AgentWorkflowStep[] = [];
|
|
396
|
+
const toRemove: number[] = [];
|
|
397
|
+
|
|
398
|
+
for (let i = 0; i < remaining.length; i++) {
|
|
399
|
+
const step = remaining[i];
|
|
400
|
+
if (!step) continue;
|
|
401
|
+
const deps = step.dependencies ?? [];
|
|
402
|
+
|
|
403
|
+
// Check if all dependencies are completed
|
|
404
|
+
if (deps.every((d) => completed.has(d))) {
|
|
405
|
+
layer.push(step);
|
|
406
|
+
toRemove.push(i);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Remove processed steps
|
|
411
|
+
for (let i = toRemove.length - 1; i >= 0; i--) {
|
|
412
|
+
const idx = toRemove[i];
|
|
413
|
+
if (idx !== undefined) {
|
|
414
|
+
remaining.splice(idx, 1);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (layer.length === 0 && remaining.length > 0) {
|
|
419
|
+
// Circular dependency or unresolvable - add remaining as single layer
|
|
420
|
+
const unresolvableSteps = remaining.map((s) => s.stepId).join(', ');
|
|
421
|
+
console.warn(
|
|
422
|
+
`[buildExecutionLayers] Circular or unresolvable dependencies detected. ` +
|
|
423
|
+
`Unresolvable steps: ${unresolvableSteps}`
|
|
424
|
+
);
|
|
425
|
+
layers.push(remaining);
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (layer.length > 0) {
|
|
430
|
+
layers.push(layer);
|
|
431
|
+
for (const step of layer) {
|
|
432
|
+
completed.add(step.stepId);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (iterations >= maxIterations) {
|
|
438
|
+
console.error(
|
|
439
|
+
`[buildExecutionLayers] Max iterations (${maxIterations}) reached. ` +
|
|
440
|
+
`This indicates a bug in the algorithm or malformed input.`
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return layers;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
cancel(): void {
|
|
448
|
+
this.cancelled = true;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
private async executeLayer(
|
|
452
|
+
layer: AgentWorkflowStep[],
|
|
453
|
+
executor: ParallelStepExecutor,
|
|
454
|
+
outputs: Record<string, unknown>
|
|
455
|
+
): Promise<ParallelStepResult[]> {
|
|
456
|
+
// INV-PE-003: Respect concurrency limit
|
|
457
|
+
const concurrency = this.config.maxConcurrency;
|
|
458
|
+
const results: ParallelStepResult[] = [];
|
|
459
|
+
|
|
460
|
+
for (let i = 0; i < layer.length; i += concurrency) {
|
|
461
|
+
const batch = layer.slice(i, i + concurrency);
|
|
462
|
+
const batchPromises = batch.map((step) =>
|
|
463
|
+
this.executeStep(step, executor, outputs)
|
|
464
|
+
);
|
|
465
|
+
const batchResults = await Promise.all(batchPromises);
|
|
466
|
+
results.push(...batchResults);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
return results;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
private async executeStep(
|
|
473
|
+
step: AgentWorkflowStep,
|
|
474
|
+
executor: ParallelStepExecutor,
|
|
475
|
+
outputs: Record<string, unknown>
|
|
476
|
+
): Promise<ParallelStepResult> {
|
|
477
|
+
const startTime = Date.now();
|
|
478
|
+
|
|
479
|
+
try {
|
|
480
|
+
const output = await executor(step, outputs);
|
|
481
|
+
return {
|
|
482
|
+
stepId: step.stepId,
|
|
483
|
+
success: true,
|
|
484
|
+
output,
|
|
485
|
+
durationMs: Date.now() - startTime,
|
|
486
|
+
};
|
|
487
|
+
} catch (error) {
|
|
488
|
+
return {
|
|
489
|
+
stepId: step.stepId,
|
|
490
|
+
success: false,
|
|
491
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
492
|
+
durationMs: Date.now() - startTime,
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
private createCancelledResult(stepId: string): ParallelStepResult {
|
|
498
|
+
return {
|
|
499
|
+
stepId,
|
|
500
|
+
success: false,
|
|
501
|
+
cancelled: true,
|
|
502
|
+
durationMs: 0,
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// ============================================================================
|
|
508
|
+
// Factory Functions
|
|
509
|
+
// ============================================================================
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Create a production checkpoint storage factory
|
|
513
|
+
*/
|
|
514
|
+
export function createCheckpointStorageFactory(
|
|
515
|
+
storage: CheckpointStoragePort
|
|
516
|
+
): CheckpointStorageFactory {
|
|
517
|
+
return () => storage;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Create a production checkpoint manager factory
|
|
522
|
+
*/
|
|
523
|
+
export function createCheckpointManagerFactory(): CheckpointManagerFactory {
|
|
524
|
+
return (
|
|
525
|
+
agentId: string,
|
|
526
|
+
sessionId: string | undefined,
|
|
527
|
+
storage: CheckpointStoragePort,
|
|
528
|
+
config: CheckpointConfig
|
|
529
|
+
) => new ProductionCheckpointManager(agentId, sessionId, storage, config);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Create a production delegation tracker factory
|
|
534
|
+
*/
|
|
535
|
+
export function createDelegationTrackerFactory(): DelegationTrackerFactory {
|
|
536
|
+
return (
|
|
537
|
+
agentId: string,
|
|
538
|
+
parentContext: DelegationContext | undefined,
|
|
539
|
+
maxDepth: number
|
|
540
|
+
) => new ProductionDelegationTracker(agentId, parentContext, maxDepth);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Create a production parallel executor factory
|
|
545
|
+
*/
|
|
546
|
+
export function createParallelExecutorFactory(): ParallelExecutorFactory {
|
|
547
|
+
return (config: Partial<ParallelExecutionConfig>) =>
|
|
548
|
+
new ProductionParallelExecutor({
|
|
549
|
+
...createDefaultParallelExecutionConfig(),
|
|
550
|
+
...config,
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// ============================================================================
|
|
555
|
+
// Combined Factory Configuration
|
|
556
|
+
// ============================================================================
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Configuration for production factories
|
|
560
|
+
*/
|
|
561
|
+
export interface ProductionFactoriesConfig {
|
|
562
|
+
/** Checkpoint storage implementation */
|
|
563
|
+
checkpointStorage?: CheckpointStoragePort;
|
|
564
|
+
/** Checkpoint config overrides */
|
|
565
|
+
checkpointConfig?: Partial<CheckpointConfig>;
|
|
566
|
+
/** Parallel execution config overrides */
|
|
567
|
+
parallelConfig?: Partial<ParallelExecutionConfig>;
|
|
568
|
+
/** Maximum delegation depth */
|
|
569
|
+
maxDelegationDepth?: number;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Production factories result
|
|
574
|
+
*/
|
|
575
|
+
export interface ProductionFactories {
|
|
576
|
+
checkpointStorageFactory?: CheckpointStorageFactory;
|
|
577
|
+
checkpointManagerFactory: CheckpointManagerFactory;
|
|
578
|
+
delegationTrackerFactory: DelegationTrackerFactory;
|
|
579
|
+
parallelExecutorFactory: ParallelExecutorFactory;
|
|
580
|
+
checkpointConfig?: Partial<CheckpointConfig>;
|
|
581
|
+
parallelConfig?: Partial<ParallelExecutionConfig>;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Create all production factories
|
|
586
|
+
*
|
|
587
|
+
* This is the main entry point for wiring real implementations.
|
|
588
|
+
*/
|
|
589
|
+
export function createProductionFactories(
|
|
590
|
+
config: ProductionFactoriesConfig = {}
|
|
591
|
+
): ProductionFactories {
|
|
592
|
+
const factories: ProductionFactories = {
|
|
593
|
+
checkpointManagerFactory: createCheckpointManagerFactory(),
|
|
594
|
+
delegationTrackerFactory: createDelegationTrackerFactory(),
|
|
595
|
+
parallelExecutorFactory: createParallelExecutorFactory(),
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
if (config.checkpointStorage) {
|
|
599
|
+
factories.checkpointStorageFactory = createCheckpointStorageFactory(
|
|
600
|
+
config.checkpointStorage
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (config.checkpointConfig) {
|
|
605
|
+
factories.checkpointConfig = config.checkpointConfig;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
if (config.parallelConfig) {
|
|
609
|
+
factories.parallelConfig = config.parallelConfig;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
return factories;
|
|
613
|
+
}
|