@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,1395 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced Agent Executor Implementation
|
|
3
|
+
*
|
|
4
|
+
* Full-featured executor with:
|
|
5
|
+
* - Checkpoint support (INV-CP-001, INV-CP-002)
|
|
6
|
+
* - Parallel execution (INV-PE-001, INV-PE-002, INV-PE-003)
|
|
7
|
+
* - Delegation tracking (INV-DT-001, INV-DT-002)
|
|
8
|
+
* - Resumable workflows
|
|
9
|
+
* - Event emission
|
|
10
|
+
*
|
|
11
|
+
* All external dependencies are injected via config (dependency inversion).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
type AgentResult,
|
|
16
|
+
type AgentRunOptions,
|
|
17
|
+
type AgentError,
|
|
18
|
+
type AgentWorkflowStep,
|
|
19
|
+
type AgentProfile,
|
|
20
|
+
AgentErrorCode,
|
|
21
|
+
AgentRunOptionsSchema,
|
|
22
|
+
createDefaultCheckpointConfig,
|
|
23
|
+
createDefaultParallelExecutionConfig,
|
|
24
|
+
type CheckpointConfig,
|
|
25
|
+
type ParallelExecutionConfig,
|
|
26
|
+
LIMIT_ABILITY_TOKENS_AGENT,
|
|
27
|
+
} from '@defai.digital/contracts';
|
|
28
|
+
import type {
|
|
29
|
+
AgentExecutor,
|
|
30
|
+
AgentRegistry,
|
|
31
|
+
ExecutionStatus,
|
|
32
|
+
StepExecutionContext,
|
|
33
|
+
StepExecutionResult,
|
|
34
|
+
StepExecutorFn,
|
|
35
|
+
AgentDomainConfig,
|
|
36
|
+
PromptExecutor,
|
|
37
|
+
PromptStepConfig,
|
|
38
|
+
AbilityManagerLike,
|
|
39
|
+
ToolExecutor,
|
|
40
|
+
DelegationTrackerFactory,
|
|
41
|
+
CheckpointStoragePort,
|
|
42
|
+
CheckpointManagerPort,
|
|
43
|
+
CheckpointStorageFactory,
|
|
44
|
+
CheckpointManagerFactory,
|
|
45
|
+
ParallelExecutorPort,
|
|
46
|
+
ParallelExecutorFactory,
|
|
47
|
+
} from './types.js';
|
|
48
|
+
import { createStubPromptExecutor } from './prompt-executor.js';
|
|
49
|
+
import { stubDelegationTrackerFactory } from './stub-delegation-tracker.js';
|
|
50
|
+
import { stubCheckpointStorageFactory, stubCheckpointManagerFactory } from './stub-checkpoint.js';
|
|
51
|
+
import { stubParallelExecutorFactory } from './stub-parallel-executor.js';
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Enhanced configuration with checkpoint and parallel settings
|
|
55
|
+
*
|
|
56
|
+
* All external dependencies are injected via factory functions.
|
|
57
|
+
* If not provided, stub implementations are used (with warnings).
|
|
58
|
+
*/
|
|
59
|
+
export interface EnhancedAgentDomainConfig extends AgentDomainConfig {
|
|
60
|
+
checkpointConfig?: Partial<CheckpointConfig>;
|
|
61
|
+
parallelConfig?: Partial<ParallelExecutionConfig>;
|
|
62
|
+
/** Factory for creating checkpoint storage (default: in-memory stub) */
|
|
63
|
+
checkpointStorageFactory?: CheckpointStorageFactory;
|
|
64
|
+
/** Factory for creating checkpoint managers */
|
|
65
|
+
checkpointManagerFactory?: CheckpointManagerFactory;
|
|
66
|
+
/** Factory for creating parallel executors (default: sequential stub) */
|
|
67
|
+
parallelExecutorFactory?: ParallelExecutorFactory;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Execution state with checkpoint support
|
|
72
|
+
*/
|
|
73
|
+
interface ExecutionState {
|
|
74
|
+
executionId: string;
|
|
75
|
+
agentId: string;
|
|
76
|
+
status: 'running' | 'completed' | 'failed' | 'cancelled';
|
|
77
|
+
startedAt: string;
|
|
78
|
+
completedAt?: string;
|
|
79
|
+
currentStep?: string;
|
|
80
|
+
stepResults: StepResult[];
|
|
81
|
+
previousOutputs: Record<string, unknown>;
|
|
82
|
+
checkpointId?: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Step result
|
|
87
|
+
*/
|
|
88
|
+
interface StepResult {
|
|
89
|
+
stepId: string;
|
|
90
|
+
success: boolean;
|
|
91
|
+
output?: unknown;
|
|
92
|
+
durationMs: number;
|
|
93
|
+
retryCount: number;
|
|
94
|
+
skipped: boolean;
|
|
95
|
+
error: AgentError | undefined;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Enhanced agent executor with full feature support
|
|
100
|
+
*
|
|
101
|
+
* Now includes ability injection (INV-AGT-ABL-001, INV-AGT-ABL-002, INV-AGT-ABL-003)
|
|
102
|
+
* All external dependencies are injected via config factories.
|
|
103
|
+
*/
|
|
104
|
+
export class EnhancedAgentExecutor implements AgentExecutor {
|
|
105
|
+
private readonly executions = new Map<string, ExecutionState>();
|
|
106
|
+
private readonly stepExecutors = new Map<string, StepExecutorFn>();
|
|
107
|
+
private readonly promptExecutor: PromptExecutor;
|
|
108
|
+
private readonly toolExecutor: ToolExecutor | undefined;
|
|
109
|
+
private readonly checkpointStorage: CheckpointStoragePort;
|
|
110
|
+
private readonly parallelExecutor: ParallelExecutorPort;
|
|
111
|
+
private readonly checkpointConfig: CheckpointConfig;
|
|
112
|
+
private readonly parallelConfig: ParallelExecutionConfig;
|
|
113
|
+
private readonly abilityManager: AbilityManagerLike | undefined;
|
|
114
|
+
private readonly enableAbilityInjection: boolean;
|
|
115
|
+
private readonly maxAbilityTokens: number;
|
|
116
|
+
private readonly delegationTrackerFactory: DelegationTrackerFactory;
|
|
117
|
+
private readonly checkpointManagerFactory: CheckpointManagerFactory;
|
|
118
|
+
|
|
119
|
+
constructor(
|
|
120
|
+
private readonly registry: AgentRegistry,
|
|
121
|
+
private readonly config: EnhancedAgentDomainConfig
|
|
122
|
+
) {
|
|
123
|
+
// Initialize prompt executor
|
|
124
|
+
this.promptExecutor = config.promptExecutor ?? createStubPromptExecutor(
|
|
125
|
+
config.defaultProvider ?? 'claude'
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Initialize tool executor (INV-TOOL-001, INV-TOOL-002, INV-TOOL-003)
|
|
129
|
+
this.toolExecutor = config.toolExecutor;
|
|
130
|
+
|
|
131
|
+
// Initialize ability manager (INV-AGT-ABL-001)
|
|
132
|
+
this.abilityManager = config.abilityManager;
|
|
133
|
+
this.enableAbilityInjection = config.enableAbilityInjection ?? (config.abilityManager !== undefined);
|
|
134
|
+
this.maxAbilityTokens = config.maxAbilityTokens ?? LIMIT_ABILITY_TOKENS_AGENT;
|
|
135
|
+
|
|
136
|
+
// Initialize delegation tracker factory (use stub if not provided)
|
|
137
|
+
this.delegationTrackerFactory = config.delegationTrackerFactory ?? stubDelegationTrackerFactory;
|
|
138
|
+
|
|
139
|
+
// Initialize checkpoint storage using injected factory (or stub)
|
|
140
|
+
this.checkpointConfig = {
|
|
141
|
+
...createDefaultCheckpointConfig(),
|
|
142
|
+
...config.checkpointConfig,
|
|
143
|
+
};
|
|
144
|
+
const checkpointStorageFactory = config.checkpointStorageFactory ?? stubCheckpointStorageFactory;
|
|
145
|
+
this.checkpointStorage = checkpointStorageFactory();
|
|
146
|
+
|
|
147
|
+
// Initialize checkpoint manager factory
|
|
148
|
+
this.checkpointManagerFactory = config.checkpointManagerFactory ?? stubCheckpointManagerFactory;
|
|
149
|
+
|
|
150
|
+
// Initialize parallel executor using injected factory (or stub)
|
|
151
|
+
this.parallelConfig = {
|
|
152
|
+
...createDefaultParallelExecutionConfig(),
|
|
153
|
+
...config.parallelConfig,
|
|
154
|
+
};
|
|
155
|
+
const parallelExecutorFactory = config.parallelExecutorFactory ?? stubParallelExecutorFactory;
|
|
156
|
+
this.parallelExecutor = parallelExecutorFactory(this.parallelConfig);
|
|
157
|
+
|
|
158
|
+
// Register step executors
|
|
159
|
+
this.registerDefaultExecutors();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Create a checkpoint manager for a specific execution
|
|
164
|
+
*/
|
|
165
|
+
private createCheckpointManagerForExecution(
|
|
166
|
+
agentId: string,
|
|
167
|
+
sessionId: string | undefined
|
|
168
|
+
): CheckpointManagerPort {
|
|
169
|
+
return this.checkpointManagerFactory(
|
|
170
|
+
agentId,
|
|
171
|
+
sessionId,
|
|
172
|
+
this.checkpointStorage,
|
|
173
|
+
this.checkpointConfig
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Execute an agent with full feature support
|
|
179
|
+
*/
|
|
180
|
+
async execute(
|
|
181
|
+
agentId: string,
|
|
182
|
+
input: unknown,
|
|
183
|
+
options?: AgentRunOptions
|
|
184
|
+
): Promise<AgentResult> {
|
|
185
|
+
const startTime = Date.now();
|
|
186
|
+
const executionId = crypto.randomUUID();
|
|
187
|
+
|
|
188
|
+
// Validate options
|
|
189
|
+
let validatedOptions: AgentRunOptions | undefined;
|
|
190
|
+
if (options !== undefined) {
|
|
191
|
+
try {
|
|
192
|
+
validatedOptions = AgentRunOptionsSchema.parse(options);
|
|
193
|
+
} catch (error) {
|
|
194
|
+
return this.createErrorResult(
|
|
195
|
+
agentId,
|
|
196
|
+
startTime,
|
|
197
|
+
AgentErrorCode.AGENT_VALIDATION_ERROR,
|
|
198
|
+
`Invalid run options: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Get agent profile
|
|
204
|
+
const agent = await this.registry.get(agentId);
|
|
205
|
+
if (agent === undefined) {
|
|
206
|
+
return this.createErrorResult(
|
|
207
|
+
agentId,
|
|
208
|
+
startTime,
|
|
209
|
+
AgentErrorCode.AGENT_NOT_FOUND,
|
|
210
|
+
`Agent "${agentId}" not found`
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Check if agent is enabled
|
|
215
|
+
if (!agent.enabled) {
|
|
216
|
+
return this.createErrorResult(
|
|
217
|
+
agentId,
|
|
218
|
+
startTime,
|
|
219
|
+
AgentErrorCode.AGENT_PERMISSION_DENIED,
|
|
220
|
+
`Agent "${agentId}" is disabled`
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Create checkpoint manager for this execution
|
|
225
|
+
const checkpointManager = this.createCheckpointManagerForExecution(
|
|
226
|
+
agentId,
|
|
227
|
+
validatedOptions?.sessionId
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
// Check for resume from checkpoint
|
|
231
|
+
let startFromStep = 0;
|
|
232
|
+
let previousOutputs: Record<string, unknown> = {};
|
|
233
|
+
|
|
234
|
+
if (validatedOptions?.resumable && validatedOptions.checkpoint) {
|
|
235
|
+
const latestCheckpoint = await checkpointManager.getLatestCheckpoint();
|
|
236
|
+
if (latestCheckpoint !== null) {
|
|
237
|
+
const resumeContext = await checkpointManager.getResumeContext(latestCheckpoint.checkpointId);
|
|
238
|
+
if (resumeContext !== null) {
|
|
239
|
+
// INV-CP-002: Resume from step after checkpoint
|
|
240
|
+
startFromStep = resumeContext.startFromStep;
|
|
241
|
+
previousOutputs = resumeContext.previousOutputs;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Initialize execution state
|
|
247
|
+
const state: ExecutionState = {
|
|
248
|
+
executionId,
|
|
249
|
+
agentId,
|
|
250
|
+
status: 'running',
|
|
251
|
+
startedAt: new Date().toISOString(),
|
|
252
|
+
stepResults: [],
|
|
253
|
+
previousOutputs,
|
|
254
|
+
};
|
|
255
|
+
this.executions.set(executionId, state);
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
const workflow = agent.workflow ?? [];
|
|
259
|
+
const stepResults: StepResult[] = [];
|
|
260
|
+
|
|
261
|
+
// Handle agents with no workflow
|
|
262
|
+
if (workflow.length === 0) {
|
|
263
|
+
return this.handleNoWorkflow(agent, input, validatedOptions, startTime);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Check if parallel execution is enabled
|
|
267
|
+
const useParallel = validatedOptions?.parallel ?? this.parallelConfig.enabled;
|
|
268
|
+
|
|
269
|
+
if (useParallel) {
|
|
270
|
+
// Execute with parallel executor
|
|
271
|
+
return await this.executeParallel(
|
|
272
|
+
agent,
|
|
273
|
+
workflow,
|
|
274
|
+
input,
|
|
275
|
+
state,
|
|
276
|
+
validatedOptions,
|
|
277
|
+
startTime,
|
|
278
|
+
startFromStep
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Sequential execution with checkpoint support
|
|
283
|
+
for (let i = startFromStep; i < workflow.length; i++) {
|
|
284
|
+
const step = workflow[i];
|
|
285
|
+
if (step === undefined) continue;
|
|
286
|
+
|
|
287
|
+
if (state.status === 'cancelled') break;
|
|
288
|
+
|
|
289
|
+
state.currentStep = step.stepId;
|
|
290
|
+
|
|
291
|
+
// Check dependencies
|
|
292
|
+
if (!this.checkDependencies(step, stepResults)) {
|
|
293
|
+
stepResults.push(this.createSkippedResult(step.stepId, 'Dependencies not met'));
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Check condition
|
|
298
|
+
if (step.condition !== undefined && !this.evaluateCondition(step.condition, state.previousOutputs)) {
|
|
299
|
+
stepResults.push(this.createSkippedResult(step.stepId));
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Execute step
|
|
304
|
+
const result = await this.executeStep(
|
|
305
|
+
step,
|
|
306
|
+
this.createStepContext(agentId, executionId, input, state.previousOutputs, validatedOptions),
|
|
307
|
+
step.retryPolicy?.maxAttempts ?? 1
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
stepResults.push({
|
|
311
|
+
stepId: step.stepId,
|
|
312
|
+
success: result.success,
|
|
313
|
+
output: result.output,
|
|
314
|
+
durationMs: result.durationMs,
|
|
315
|
+
retryCount: result.retryCount,
|
|
316
|
+
skipped: false,
|
|
317
|
+
error: result.error,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// Store output
|
|
321
|
+
if (result.success && result.output !== undefined) {
|
|
322
|
+
state.previousOutputs[step.stepId] = result.output;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Create checkpoint if enabled
|
|
326
|
+
// INV-CP-001: Checkpoint contains all data needed to resume
|
|
327
|
+
if (this.checkpointConfig.enabled && checkpointManager.shouldCheckpoint(i)) {
|
|
328
|
+
await checkpointManager.createCheckpoint(
|
|
329
|
+
i,
|
|
330
|
+
step.stepId,
|
|
331
|
+
state.previousOutputs,
|
|
332
|
+
{ input }
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Stop on failure (unless marked as parallel)
|
|
337
|
+
if (!result.success && !step.parallel) {
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return this.buildResult(agentId, stepResults, state, validatedOptions, startTime);
|
|
343
|
+
} catch (error) {
|
|
344
|
+
state.status = 'failed';
|
|
345
|
+
state.completedAt = new Date().toISOString();
|
|
346
|
+
return this.createErrorResult(
|
|
347
|
+
agentId,
|
|
348
|
+
startTime,
|
|
349
|
+
AgentErrorCode.AGENT_STAGE_FAILED,
|
|
350
|
+
error instanceof Error ? error.message : 'Unknown error'
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Execute workflow with parallel executor
|
|
357
|
+
* INV-PE-001: Independent steps execute concurrently
|
|
358
|
+
* INV-PE-002: Dependencies honored (DAG ordering)
|
|
359
|
+
* INV-PE-003: Concurrency limit respected
|
|
360
|
+
*/
|
|
361
|
+
private async executeParallel(
|
|
362
|
+
agent: AgentProfile,
|
|
363
|
+
workflow: AgentWorkflowStep[],
|
|
364
|
+
input: unknown,
|
|
365
|
+
state: ExecutionState,
|
|
366
|
+
options: AgentRunOptions | undefined,
|
|
367
|
+
startTime: number,
|
|
368
|
+
_startFromStep: number
|
|
369
|
+
): Promise<AgentResult> {
|
|
370
|
+
const context = this.createStepContext(
|
|
371
|
+
agent.agentId,
|
|
372
|
+
state.executionId,
|
|
373
|
+
input,
|
|
374
|
+
state.previousOutputs,
|
|
375
|
+
options
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
// Build executor function for parallel executor
|
|
379
|
+
const stepExecutor = async (
|
|
380
|
+
step: AgentWorkflowStep,
|
|
381
|
+
outputs: Record<string, unknown>
|
|
382
|
+
): Promise<unknown> => {
|
|
383
|
+
const stepContext = {
|
|
384
|
+
...context,
|
|
385
|
+
previousOutputs: outputs,
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
const result = await this.executeStep(
|
|
389
|
+
step,
|
|
390
|
+
stepContext,
|
|
391
|
+
step.retryPolicy?.maxAttempts ?? 1
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
if (!result.success) {
|
|
395
|
+
throw new Error(result.error?.message ?? 'Step failed');
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return result.output;
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
// Execute with parallel executor
|
|
402
|
+
const groupResult = await this.parallelExecutor.executeGroup(
|
|
403
|
+
workflow,
|
|
404
|
+
stepExecutor,
|
|
405
|
+
state.previousOutputs
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
// Convert parallel results to step results
|
|
409
|
+
const stepResults: StepResult[] = groupResult.stepResults.map((r) => ({
|
|
410
|
+
stepId: r.stepId,
|
|
411
|
+
success: r.success,
|
|
412
|
+
output: r.output,
|
|
413
|
+
durationMs: r.durationMs,
|
|
414
|
+
retryCount: 0,
|
|
415
|
+
skipped: r.cancelled ?? false,
|
|
416
|
+
error: r.error ? { code: AgentErrorCode.AGENT_STAGE_FAILED, message: r.error } : undefined,
|
|
417
|
+
}));
|
|
418
|
+
|
|
419
|
+
// Update state
|
|
420
|
+
state.stepResults = stepResults;
|
|
421
|
+
state.status = groupResult.allSucceeded ? 'completed' : 'failed';
|
|
422
|
+
state.completedAt = new Date().toISOString();
|
|
423
|
+
|
|
424
|
+
return {
|
|
425
|
+
success: groupResult.allSucceeded,
|
|
426
|
+
agentId: agent.agentId,
|
|
427
|
+
sessionId: options?.sessionId,
|
|
428
|
+
output: this.collectOutputs(stepResults, state.previousOutputs),
|
|
429
|
+
stepResults,
|
|
430
|
+
totalDurationMs: Date.now() - startTime,
|
|
431
|
+
error: groupResult.allSucceeded
|
|
432
|
+
? undefined
|
|
433
|
+
: stepResults.find((r) => r.error !== undefined)?.error,
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Handle agents with no workflow defined
|
|
439
|
+
*/
|
|
440
|
+
private handleNoWorkflow(
|
|
441
|
+
agent: AgentProfile,
|
|
442
|
+
input: unknown,
|
|
443
|
+
options: AgentRunOptions | undefined,
|
|
444
|
+
startTime: number
|
|
445
|
+
): AgentResult {
|
|
446
|
+
// If agent has systemPrompt, create a single prompt step on the fly
|
|
447
|
+
if (agent.systemPrompt) {
|
|
448
|
+
return {
|
|
449
|
+
success: true,
|
|
450
|
+
agentId: agent.agentId,
|
|
451
|
+
sessionId: options?.sessionId,
|
|
452
|
+
output: {
|
|
453
|
+
message: 'Agent has no workflow but has a system prompt. Use agent_run to execute prompts directly.',
|
|
454
|
+
agent: {
|
|
455
|
+
agentId: agent.agentId,
|
|
456
|
+
description: agent.description,
|
|
457
|
+
hasSystemPrompt: true,
|
|
458
|
+
},
|
|
459
|
+
suggestion: 'Register the agent with workflow steps for structured execution.',
|
|
460
|
+
},
|
|
461
|
+
stepResults: [],
|
|
462
|
+
totalDurationMs: Date.now() - startTime,
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return {
|
|
467
|
+
success: true,
|
|
468
|
+
agentId: agent.agentId,
|
|
469
|
+
sessionId: options?.sessionId,
|
|
470
|
+
output: {
|
|
471
|
+
message: `Agent "${agent.agentId}" has no workflow steps. Register with workflow to enable execution.`,
|
|
472
|
+
input,
|
|
473
|
+
},
|
|
474
|
+
stepResults: [],
|
|
475
|
+
totalDurationMs: Date.now() - startTime,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Cancel execution
|
|
481
|
+
*/
|
|
482
|
+
async cancel(executionId: string): Promise<void> {
|
|
483
|
+
const state = this.executions.get(executionId);
|
|
484
|
+
if (state?.status === 'running') {
|
|
485
|
+
state.status = 'cancelled';
|
|
486
|
+
state.completedAt = new Date().toISOString();
|
|
487
|
+
this.parallelExecutor.cancel();
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Get execution status
|
|
493
|
+
*/
|
|
494
|
+
async getStatus(executionId: string): Promise<ExecutionStatus | undefined> {
|
|
495
|
+
const state = this.executions.get(executionId);
|
|
496
|
+
if (state === undefined) return undefined;
|
|
497
|
+
|
|
498
|
+
const completedSteps = state.stepResults.filter((r) => r.success || r.skipped).length;
|
|
499
|
+
const totalSteps = state.stepResults.length;
|
|
500
|
+
|
|
501
|
+
return {
|
|
502
|
+
executionId: state.executionId,
|
|
503
|
+
agentId: state.agentId,
|
|
504
|
+
status: state.status,
|
|
505
|
+
currentStep: state.currentStep,
|
|
506
|
+
startedAt: state.startedAt,
|
|
507
|
+
completedAt: state.completedAt,
|
|
508
|
+
progress: {
|
|
509
|
+
totalSteps,
|
|
510
|
+
completedSteps,
|
|
511
|
+
currentStepName: state.currentStep,
|
|
512
|
+
percentComplete: totalSteps > 0 ? Math.round((completedSteps / totalSteps) * 100) : 0,
|
|
513
|
+
},
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Register custom step executor
|
|
519
|
+
*/
|
|
520
|
+
registerStepExecutor(type: string, executor: StepExecutorFn): void {
|
|
521
|
+
this.stepExecutors.set(type, executor);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Create step execution context
|
|
526
|
+
*/
|
|
527
|
+
private createStepContext(
|
|
528
|
+
agentId: string,
|
|
529
|
+
executionId: string,
|
|
530
|
+
input: unknown,
|
|
531
|
+
previousOutputs: Record<string, unknown>,
|
|
532
|
+
options?: AgentRunOptions
|
|
533
|
+
): StepExecutionContext {
|
|
534
|
+
return {
|
|
535
|
+
agentId,
|
|
536
|
+
executionId,
|
|
537
|
+
sessionId: options?.sessionId,
|
|
538
|
+
input,
|
|
539
|
+
previousOutputs,
|
|
540
|
+
memory: undefined,
|
|
541
|
+
provider: options?.provider,
|
|
542
|
+
model: options?.model,
|
|
543
|
+
delegationContext: options?.delegationContext,
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Execute a single step with retry
|
|
549
|
+
*/
|
|
550
|
+
private async executeStep(
|
|
551
|
+
step: AgentWorkflowStep,
|
|
552
|
+
context: StepExecutionContext,
|
|
553
|
+
maxAttempts: number
|
|
554
|
+
): Promise<StepExecutionResult> {
|
|
555
|
+
const executor = this.stepExecutors.get(step.type);
|
|
556
|
+
if (executor === undefined) {
|
|
557
|
+
return {
|
|
558
|
+
success: false,
|
|
559
|
+
error: {
|
|
560
|
+
code: AgentErrorCode.AGENT_STAGE_FAILED,
|
|
561
|
+
message: `No executor for step type: ${step.type}`,
|
|
562
|
+
stepId: step.stepId,
|
|
563
|
+
},
|
|
564
|
+
durationMs: 0,
|
|
565
|
+
retryCount: 0,
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
let lastError: AgentError | undefined;
|
|
570
|
+
let retryCount = 0;
|
|
571
|
+
|
|
572
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
573
|
+
const timeout = step.timeoutMs ?? this.config.defaultTimeout;
|
|
574
|
+
const { promise: timeoutPromise, cancel: cancelTimeout } = this.createTimeout(timeout, step.stepId);
|
|
575
|
+
|
|
576
|
+
try {
|
|
577
|
+
const result = await Promise.race([
|
|
578
|
+
executor(step, context),
|
|
579
|
+
timeoutPromise,
|
|
580
|
+
]);
|
|
581
|
+
cancelTimeout(); // Clean up the timer
|
|
582
|
+
|
|
583
|
+
if (result.success) {
|
|
584
|
+
return { ...result, retryCount };
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
lastError = result.error;
|
|
588
|
+
retryCount++;
|
|
589
|
+
|
|
590
|
+
// Backoff
|
|
591
|
+
if (attempt < maxAttempts - 1 && step.retryPolicy !== undefined) {
|
|
592
|
+
const backoff = step.retryPolicy.backoffMs *
|
|
593
|
+
Math.pow(step.retryPolicy.backoffMultiplier, attempt);
|
|
594
|
+
await this.sleep(backoff);
|
|
595
|
+
}
|
|
596
|
+
} catch (error) {
|
|
597
|
+
cancelTimeout(); // Clean up the timer on error too
|
|
598
|
+
lastError = {
|
|
599
|
+
code: AgentErrorCode.AGENT_STAGE_FAILED,
|
|
600
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
601
|
+
stepId: step.stepId,
|
|
602
|
+
};
|
|
603
|
+
retryCount++;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
return { success: false, error: lastError, durationMs: 0, retryCount };
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Check step dependencies
|
|
612
|
+
*/
|
|
613
|
+
private checkDependencies(step: AgentWorkflowStep, results: StepResult[]): boolean {
|
|
614
|
+
if (step.dependencies === undefined || step.dependencies.length === 0) {
|
|
615
|
+
return true;
|
|
616
|
+
}
|
|
617
|
+
return step.dependencies.every((depId) => {
|
|
618
|
+
const depResult = results.find((r) => r.stepId === depId);
|
|
619
|
+
return depResult !== undefined && depResult.success;
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Evaluate a condition expression safely without using eval/Function
|
|
625
|
+
*
|
|
626
|
+
* Supports patterns:
|
|
627
|
+
* - ${variable} === value
|
|
628
|
+
* - ${variable} !== value
|
|
629
|
+
* - ${variable} > value
|
|
630
|
+
* - ${variable} < value
|
|
631
|
+
* - ${variable} >= value
|
|
632
|
+
* - ${variable} <= value
|
|
633
|
+
* - ${variable} (truthy check)
|
|
634
|
+
* - !${variable} (falsy check)
|
|
635
|
+
* - condition && condition
|
|
636
|
+
* - condition || condition
|
|
637
|
+
*/
|
|
638
|
+
private evaluateCondition(condition: string, outputs: Record<string, unknown>): boolean {
|
|
639
|
+
try {
|
|
640
|
+
// Handle logical operators by recursively evaluating sub-conditions
|
|
641
|
+
// Check for || first (lower precedence)
|
|
642
|
+
const orParts = this.splitByOperator(condition, '||');
|
|
643
|
+
if (orParts.length > 1) {
|
|
644
|
+
return orParts.some((part) => this.evaluateCondition(part.trim(), outputs));
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// Check for && (higher precedence than ||)
|
|
648
|
+
const andParts = this.splitByOperator(condition, '&&');
|
|
649
|
+
if (andParts.length > 1) {
|
|
650
|
+
return andParts.every((part) => this.evaluateCondition(part.trim(), outputs));
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// Handle negation
|
|
654
|
+
const trimmed = condition.trim();
|
|
655
|
+
if (trimmed.startsWith('!')) {
|
|
656
|
+
return !this.evaluateCondition(trimmed.slice(1).trim(), outputs);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// Handle parentheses
|
|
660
|
+
if (trimmed.startsWith('(') && trimmed.endsWith(')')) {
|
|
661
|
+
return this.evaluateCondition(trimmed.slice(1, -1), outputs);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// Parse comparison expression: ${variable} op value
|
|
665
|
+
const comparisonMatch = /^\$\{(\w+(?:\.\w+)*)\}\s*(===|!==|==|!=|>=|<=|>|<)\s*(.+)$/.exec(trimmed);
|
|
666
|
+
if (comparisonMatch) {
|
|
667
|
+
const [, keyPath, op, rawValue] = comparisonMatch;
|
|
668
|
+
const actualValue = this.getConditionValue(outputs, keyPath!);
|
|
669
|
+
const expectedValue = this.parseConditionValue(rawValue!.trim());
|
|
670
|
+
return this.compareConditionValues(actualValue, expectedValue, op!);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// Handle simple variable reference (truthy check): ${variable}
|
|
674
|
+
const varMatch = /^\$\{(\w+(?:\.\w+)*)\}$/.exec(trimmed);
|
|
675
|
+
if (varMatch) {
|
|
676
|
+
const value = this.getConditionValue(outputs, varMatch[1]!);
|
|
677
|
+
return Boolean(value);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Handle literal boolean
|
|
681
|
+
if (trimmed === 'true') return true;
|
|
682
|
+
if (trimmed === 'false') return false;
|
|
683
|
+
|
|
684
|
+
// Unknown pattern - fail safely
|
|
685
|
+
console.warn(`[evaluateCondition] Unknown condition pattern: ${condition}`);
|
|
686
|
+
return false;
|
|
687
|
+
} catch (error) {
|
|
688
|
+
console.warn(`[evaluateCondition] Error evaluating condition: ${condition}`, error);
|
|
689
|
+
return false;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Split condition by operator, respecting parentheses
|
|
695
|
+
*/
|
|
696
|
+
private splitByOperator(condition: string, operator: string): string[] {
|
|
697
|
+
const parts: string[] = [];
|
|
698
|
+
let current = '';
|
|
699
|
+
let depth = 0;
|
|
700
|
+
let i = 0;
|
|
701
|
+
|
|
702
|
+
while (i < condition.length) {
|
|
703
|
+
const char = condition[i];
|
|
704
|
+
|
|
705
|
+
if (char === '(') {
|
|
706
|
+
depth++;
|
|
707
|
+
current += char;
|
|
708
|
+
} else if (char === ')') {
|
|
709
|
+
depth--;
|
|
710
|
+
current += char;
|
|
711
|
+
} else if (depth === 0 && condition.slice(i, i + operator.length) === operator) {
|
|
712
|
+
parts.push(current);
|
|
713
|
+
current = '';
|
|
714
|
+
i += operator.length;
|
|
715
|
+
continue;
|
|
716
|
+
} else {
|
|
717
|
+
current += char;
|
|
718
|
+
}
|
|
719
|
+
i++;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
if (current) {
|
|
723
|
+
parts.push(current);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
return parts;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* Get nested value from object using dot notation (for condition evaluation)
|
|
731
|
+
*/
|
|
732
|
+
private getConditionValue(obj: Record<string, unknown>, path: string): unknown {
|
|
733
|
+
const parts = path.split('.');
|
|
734
|
+
let current: unknown = obj;
|
|
735
|
+
|
|
736
|
+
for (const part of parts) {
|
|
737
|
+
if (current === null || current === undefined) {
|
|
738
|
+
return undefined;
|
|
739
|
+
}
|
|
740
|
+
current = (current as Record<string, unknown>)[part];
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
return current;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* Parse a value string into its actual type for condition evaluation
|
|
748
|
+
*/
|
|
749
|
+
private parseConditionValue(value: string): unknown {
|
|
750
|
+
// String literal
|
|
751
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
752
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
753
|
+
return value.slice(1, -1);
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// Null/undefined
|
|
757
|
+
if (value === 'null') return null;
|
|
758
|
+
if (value === 'undefined') return undefined;
|
|
759
|
+
|
|
760
|
+
// Boolean
|
|
761
|
+
if (value === 'true') return true;
|
|
762
|
+
if (value === 'false') return false;
|
|
763
|
+
|
|
764
|
+
// Number
|
|
765
|
+
const num = Number(value);
|
|
766
|
+
if (!isNaN(num)) return num;
|
|
767
|
+
|
|
768
|
+
// Default to string
|
|
769
|
+
return value;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* Compare two values using the specified operator
|
|
774
|
+
*/
|
|
775
|
+
private compareConditionValues(actual: unknown, expected: unknown, op: string): boolean {
|
|
776
|
+
switch (op) {
|
|
777
|
+
case '===':
|
|
778
|
+
return actual === expected;
|
|
779
|
+
case '!==':
|
|
780
|
+
return actual !== expected;
|
|
781
|
+
case '==':
|
|
782
|
+
return actual == expected;
|
|
783
|
+
case '!=':
|
|
784
|
+
return actual != expected;
|
|
785
|
+
case '>':
|
|
786
|
+
return (actual as number) > (expected as number);
|
|
787
|
+
case '<':
|
|
788
|
+
return (actual as number) < (expected as number);
|
|
789
|
+
case '>=':
|
|
790
|
+
return (actual as number) >= (expected as number);
|
|
791
|
+
case '<=':
|
|
792
|
+
return (actual as number) <= (expected as number);
|
|
793
|
+
default:
|
|
794
|
+
return false;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* Collect outputs from results
|
|
800
|
+
*/
|
|
801
|
+
private collectOutputs(
|
|
802
|
+
results: StepResult[],
|
|
803
|
+
_previousOutputs: Record<string, unknown>
|
|
804
|
+
): Record<string, unknown> {
|
|
805
|
+
const output: Record<string, unknown> = {};
|
|
806
|
+
for (const result of results) {
|
|
807
|
+
if (result.success && result.output !== undefined) {
|
|
808
|
+
output[result.stepId] = result.output;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
return output;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
* Build final result
|
|
816
|
+
*/
|
|
817
|
+
private buildResult(
|
|
818
|
+
agentId: string,
|
|
819
|
+
stepResults: StepResult[],
|
|
820
|
+
state: ExecutionState,
|
|
821
|
+
options: AgentRunOptions | undefined,
|
|
822
|
+
startTime: number
|
|
823
|
+
): AgentResult {
|
|
824
|
+
const allSuccess = stepResults.every((r) => r.success || r.skipped);
|
|
825
|
+
state.status = allSuccess ? 'completed' : 'failed';
|
|
826
|
+
state.completedAt = new Date().toISOString();
|
|
827
|
+
state.stepResults = stepResults;
|
|
828
|
+
|
|
829
|
+
return {
|
|
830
|
+
success: allSuccess,
|
|
831
|
+
agentId,
|
|
832
|
+
sessionId: options?.sessionId,
|
|
833
|
+
output: this.collectOutputs(stepResults, state.previousOutputs),
|
|
834
|
+
stepResults,
|
|
835
|
+
totalDurationMs: Date.now() - startTime,
|
|
836
|
+
error: allSuccess ? undefined : stepResults.find((r) => r.error !== undefined)?.error,
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* Create error result
|
|
842
|
+
*/
|
|
843
|
+
private createErrorResult(
|
|
844
|
+
agentId: string,
|
|
845
|
+
startTime: number,
|
|
846
|
+
code: string,
|
|
847
|
+
message: string
|
|
848
|
+
): AgentResult {
|
|
849
|
+
return {
|
|
850
|
+
success: false,
|
|
851
|
+
agentId,
|
|
852
|
+
error: { code, message },
|
|
853
|
+
totalDurationMs: Date.now() - startTime,
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
/**
|
|
858
|
+
* Create skipped result
|
|
859
|
+
*/
|
|
860
|
+
private createSkippedResult(stepId: string, reason?: string): StepResult {
|
|
861
|
+
return {
|
|
862
|
+
stepId,
|
|
863
|
+
success: reason === undefined,
|
|
864
|
+
durationMs: 0,
|
|
865
|
+
retryCount: 0,
|
|
866
|
+
skipped: true,
|
|
867
|
+
error: reason ? { code: AgentErrorCode.AGENT_DEPENDENCY_FAILED, message: reason } : undefined,
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Create timeout promise
|
|
873
|
+
*/
|
|
874
|
+
private createTimeout(ms: number, stepId: string): {
|
|
875
|
+
promise: Promise<StepExecutionResult>;
|
|
876
|
+
cancel: () => void;
|
|
877
|
+
} {
|
|
878
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
879
|
+
|
|
880
|
+
const promise = new Promise<StepExecutionResult>((_, reject) => {
|
|
881
|
+
timeoutId = setTimeout(() => { reject({
|
|
882
|
+
success: false,
|
|
883
|
+
error: {
|
|
884
|
+
code: AgentErrorCode.AGENT_STAGE_FAILED,
|
|
885
|
+
message: `Step "${stepId}" timed out after ${ms}ms`,
|
|
886
|
+
stepId,
|
|
887
|
+
retryable: true,
|
|
888
|
+
},
|
|
889
|
+
durationMs: ms,
|
|
890
|
+
retryCount: 0,
|
|
891
|
+
}); }, ms);
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
const cancel = (): void => {
|
|
895
|
+
if (timeoutId !== undefined) {
|
|
896
|
+
clearTimeout(timeoutId);
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
|
|
900
|
+
return { promise, cancel };
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
/**
|
|
904
|
+
* Sleep utility
|
|
905
|
+
*/
|
|
906
|
+
private sleep(ms: number): Promise<void> {
|
|
907
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* Substitute variables in template
|
|
912
|
+
*/
|
|
913
|
+
private substituteVariables(
|
|
914
|
+
template: string,
|
|
915
|
+
context: StepExecutionContext,
|
|
916
|
+
agentProfile: { description?: string; systemPrompt?: string; agentId: string }
|
|
917
|
+
): string {
|
|
918
|
+
return template.replace(/\$\{([^}]+)\}/g, (match, path) => {
|
|
919
|
+
const parts = path.split('.');
|
|
920
|
+
let value: unknown;
|
|
921
|
+
|
|
922
|
+
switch (parts[0]) {
|
|
923
|
+
case 'input':
|
|
924
|
+
if (parts.length === 1) {
|
|
925
|
+
value = typeof context.input === 'string'
|
|
926
|
+
? context.input
|
|
927
|
+
: JSON.stringify(context.input, null, 2);
|
|
928
|
+
} else {
|
|
929
|
+
value = this.getNestedValue(context.input, parts.slice(1));
|
|
930
|
+
}
|
|
931
|
+
break;
|
|
932
|
+
case 'previousOutputs':
|
|
933
|
+
if (parts.length > 1) {
|
|
934
|
+
value = this.getNestedValue(context.previousOutputs, parts.slice(1));
|
|
935
|
+
}
|
|
936
|
+
break;
|
|
937
|
+
case 'agent':
|
|
938
|
+
if (parts.length > 1) {
|
|
939
|
+
value = this.getNestedValue(agentProfile, parts.slice(1));
|
|
940
|
+
}
|
|
941
|
+
break;
|
|
942
|
+
default:
|
|
943
|
+
value = this.getNestedValue(context.input, parts);
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
if (value === undefined) return match;
|
|
947
|
+
return typeof value === 'string' ? value : JSON.stringify(value);
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
/**
|
|
952
|
+
* Get nested value from object
|
|
953
|
+
*/
|
|
954
|
+
private getNestedValue(obj: unknown, path: string[]): unknown {
|
|
955
|
+
let current: unknown = obj;
|
|
956
|
+
for (const key of path) {
|
|
957
|
+
if (current === null || current === undefined) return undefined;
|
|
958
|
+
if (typeof current !== 'object') return undefined;
|
|
959
|
+
current = (current as Record<string, unknown>)[key];
|
|
960
|
+
}
|
|
961
|
+
return current;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
/**
|
|
965
|
+
* Register all default step executors
|
|
966
|
+
*/
|
|
967
|
+
private registerDefaultExecutors(): void {
|
|
968
|
+
// Prompt step executor with ability injection
|
|
969
|
+
// INV-AGT-ABL-001: Abilities injected before prompt execution
|
|
970
|
+
// INV-AGT-ABL-002: Core abilities from agent profile included
|
|
971
|
+
// INV-AGT-ABL-003: Token limits respected
|
|
972
|
+
this.stepExecutors.set('prompt', async (step, context) => {
|
|
973
|
+
const startTime = Date.now();
|
|
974
|
+
const config = (step.config ?? {}) as PromptStepConfig;
|
|
975
|
+
|
|
976
|
+
const agent = await this.registry.get(context.agentId);
|
|
977
|
+
const agentProfile: { description?: string; systemPrompt?: string; agentId: string } = {
|
|
978
|
+
agentId: context.agentId,
|
|
979
|
+
};
|
|
980
|
+
if (agent?.description !== undefined) agentProfile.description = agent.description;
|
|
981
|
+
if (agent?.systemPrompt !== undefined) agentProfile.systemPrompt = agent.systemPrompt;
|
|
982
|
+
|
|
983
|
+
// INV-AGT-ABL-001: Inject abilities if ability manager is available
|
|
984
|
+
let abilityContent = '';
|
|
985
|
+
if (this.enableAbilityInjection && this.abilityManager !== undefined) {
|
|
986
|
+
try {
|
|
987
|
+
// INV-AGT-ABL-002: Core abilities from agent profile
|
|
988
|
+
const coreAbilities = agent?.abilities?.core ?? [];
|
|
989
|
+
|
|
990
|
+
// Determine task from input for relevance matching
|
|
991
|
+
const task = typeof context.input === 'string'
|
|
992
|
+
? context.input
|
|
993
|
+
: typeof context.input === 'object' && context.input !== null && 'task' in context.input
|
|
994
|
+
? String((context.input as { task: unknown }).task)
|
|
995
|
+
: typeof context.input === 'object' && context.input !== null && 'prompt' in context.input
|
|
996
|
+
? String((context.input as { prompt: unknown }).prompt)
|
|
997
|
+
: agent?.description ?? '';
|
|
998
|
+
|
|
999
|
+
// INV-AGT-ABL-003: Token limits respected
|
|
1000
|
+
const injectionResult = await this.abilityManager.injectAbilities(
|
|
1001
|
+
context.agentId,
|
|
1002
|
+
task,
|
|
1003
|
+
coreAbilities,
|
|
1004
|
+
{
|
|
1005
|
+
maxTokens: this.maxAbilityTokens,
|
|
1006
|
+
includeMetadata: true,
|
|
1007
|
+
}
|
|
1008
|
+
);
|
|
1009
|
+
|
|
1010
|
+
if (injectionResult.combinedContent.length > 0) {
|
|
1011
|
+
abilityContent = `\n\n## Relevant Knowledge & Abilities\n\n${injectionResult.combinedContent}\n\n---\n\n`;
|
|
1012
|
+
}
|
|
1013
|
+
} catch {
|
|
1014
|
+
// Ability injection failure should not block execution
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
// Build prompt with keyQuestions support
|
|
1019
|
+
let promptText: string;
|
|
1020
|
+
if (config.prompt) {
|
|
1021
|
+
promptText = this.substituteVariables(config.prompt, context, agentProfile);
|
|
1022
|
+
} else if (typeof context.input === 'string') {
|
|
1023
|
+
promptText = context.input;
|
|
1024
|
+
} else if (context.input && typeof context.input === 'object' && 'prompt' in context.input) {
|
|
1025
|
+
promptText = (context.input as { prompt: string }).prompt;
|
|
1026
|
+
} else {
|
|
1027
|
+
promptText = JSON.stringify(context.input, null, 2);
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
// Append keyQuestions if defined
|
|
1031
|
+
if (step.keyQuestions && step.keyQuestions.length > 0) {
|
|
1032
|
+
promptText += '\n\nKey Questions to Address:\n' +
|
|
1033
|
+
step.keyQuestions.map((q, i) => `${i + 1}. ${q}`).join('\n');
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
// Build system prompt with injected abilities
|
|
1037
|
+
const baseSystemPrompt = config.systemPrompt ?? agent?.systemPrompt ?? '';
|
|
1038
|
+
const systemPrompt = abilityContent
|
|
1039
|
+
? `${baseSystemPrompt}${abilityContent}`
|
|
1040
|
+
: baseSystemPrompt || undefined;
|
|
1041
|
+
|
|
1042
|
+
const response = await this.promptExecutor.execute({
|
|
1043
|
+
prompt: promptText,
|
|
1044
|
+
systemPrompt: systemPrompt || undefined,
|
|
1045
|
+
provider: config.provider ?? context.provider,
|
|
1046
|
+
model: config.model ?? context.model,
|
|
1047
|
+
maxTokens: config.maxTokens,
|
|
1048
|
+
temperature: config.temperature,
|
|
1049
|
+
timeout: config.timeout ?? step.timeoutMs,
|
|
1050
|
+
});
|
|
1051
|
+
|
|
1052
|
+
if (response.success) {
|
|
1053
|
+
return {
|
|
1054
|
+
success: true,
|
|
1055
|
+
output: {
|
|
1056
|
+
content: response.content,
|
|
1057
|
+
provider: response.provider,
|
|
1058
|
+
model: response.model,
|
|
1059
|
+
usage: response.usage,
|
|
1060
|
+
},
|
|
1061
|
+
error: undefined,
|
|
1062
|
+
durationMs: Date.now() - startTime,
|
|
1063
|
+
retryCount: 0,
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
return {
|
|
1068
|
+
success: false,
|
|
1069
|
+
error: {
|
|
1070
|
+
code: response.errorCode ?? AgentErrorCode.AGENT_STAGE_FAILED,
|
|
1071
|
+
message: response.error ?? 'Prompt execution failed',
|
|
1072
|
+
stepId: step.stepId,
|
|
1073
|
+
retryable: true,
|
|
1074
|
+
},
|
|
1075
|
+
durationMs: Date.now() - startTime,
|
|
1076
|
+
retryCount: 0,
|
|
1077
|
+
};
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
// Tool step executor (INV-TOOL-001, INV-TOOL-002, INV-TOOL-003)
|
|
1081
|
+
this.stepExecutors.set('tool', async (step, context) => {
|
|
1082
|
+
const startTime = Date.now();
|
|
1083
|
+
const config = step.config as {
|
|
1084
|
+
toolName?: string;
|
|
1085
|
+
toolInput?: Record<string, unknown>;
|
|
1086
|
+
} | undefined;
|
|
1087
|
+
|
|
1088
|
+
const toolName = config?.toolName;
|
|
1089
|
+
const toolInput = (config?.toolInput ?? context.input ?? {}) as Record<string, unknown>;
|
|
1090
|
+
|
|
1091
|
+
// If no tool executor is configured, return placeholder result
|
|
1092
|
+
if (this.toolExecutor === undefined) {
|
|
1093
|
+
return {
|
|
1094
|
+
success: true,
|
|
1095
|
+
output: {
|
|
1096
|
+
step: step.stepId,
|
|
1097
|
+
type: 'tool',
|
|
1098
|
+
toolName: toolName ?? 'unknown',
|
|
1099
|
+
toolInput,
|
|
1100
|
+
status: 'no_executor',
|
|
1101
|
+
message: 'Tool execution requires a ToolExecutor. Configure AgentDomainConfig.toolExecutor.',
|
|
1102
|
+
},
|
|
1103
|
+
error: undefined,
|
|
1104
|
+
durationMs: Date.now() - startTime,
|
|
1105
|
+
retryCount: 0,
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
// Check if tool name is provided
|
|
1110
|
+
if (toolName === undefined || toolName.trim() === '') {
|
|
1111
|
+
return {
|
|
1112
|
+
success: false,
|
|
1113
|
+
output: undefined,
|
|
1114
|
+
error: {
|
|
1115
|
+
code: 'TOOL_CONFIG_ERROR',
|
|
1116
|
+
message: `Tool step "${step.stepId}" requires toolName in config`,
|
|
1117
|
+
stepId: step.stepId,
|
|
1118
|
+
},
|
|
1119
|
+
durationMs: Date.now() - startTime,
|
|
1120
|
+
retryCount: 0,
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
// INV-TOOL-001: Check if tool is available
|
|
1125
|
+
if (!this.toolExecutor.isToolAvailable(toolName)) {
|
|
1126
|
+
return {
|
|
1127
|
+
success: false,
|
|
1128
|
+
output: undefined,
|
|
1129
|
+
error: {
|
|
1130
|
+
code: 'TOOL_NOT_FOUND',
|
|
1131
|
+
message: `Tool "${toolName}" is not available`,
|
|
1132
|
+
stepId: step.stepId,
|
|
1133
|
+
details: {
|
|
1134
|
+
availableTools: this.toolExecutor.getAvailableTools().slice(0, 10),
|
|
1135
|
+
},
|
|
1136
|
+
},
|
|
1137
|
+
durationMs: Date.now() - startTime,
|
|
1138
|
+
retryCount: 0,
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
// Execute the tool
|
|
1143
|
+
try {
|
|
1144
|
+
const result = await this.toolExecutor.execute(toolName, toolInput);
|
|
1145
|
+
|
|
1146
|
+
// INV-TOOL-002: Result is already frozen by executor
|
|
1147
|
+
return {
|
|
1148
|
+
success: result.success,
|
|
1149
|
+
output: {
|
|
1150
|
+
step: step.stepId,
|
|
1151
|
+
type: 'tool',
|
|
1152
|
+
toolName,
|
|
1153
|
+
toolOutput: result.output,
|
|
1154
|
+
durationMs: result.durationMs,
|
|
1155
|
+
},
|
|
1156
|
+
error: result.success ? undefined : {
|
|
1157
|
+
code: result.errorCode ?? 'TOOL_EXECUTION_ERROR',
|
|
1158
|
+
message: result.error ?? 'Tool execution failed',
|
|
1159
|
+
stepId: step.stepId,
|
|
1160
|
+
retryable: result.retryable ?? false,
|
|
1161
|
+
},
|
|
1162
|
+
durationMs: Date.now() - startTime,
|
|
1163
|
+
retryCount: 0,
|
|
1164
|
+
};
|
|
1165
|
+
} catch (error) {
|
|
1166
|
+
// INV-TOOL-003: Handle unexpected errors gracefully
|
|
1167
|
+
return {
|
|
1168
|
+
success: false,
|
|
1169
|
+
output: undefined,
|
|
1170
|
+
error: {
|
|
1171
|
+
code: 'TOOL_EXECUTION_ERROR',
|
|
1172
|
+
message: error instanceof Error ? error.message : 'Unknown tool execution error',
|
|
1173
|
+
stepId: step.stepId,
|
|
1174
|
+
retryable: true,
|
|
1175
|
+
},
|
|
1176
|
+
durationMs: Date.now() - startTime,
|
|
1177
|
+
retryCount: 0,
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
});
|
|
1181
|
+
|
|
1182
|
+
// Conditional step executor
|
|
1183
|
+
this.stepExecutors.set('conditional', async (step, context) => {
|
|
1184
|
+
const startTime = Date.now();
|
|
1185
|
+
const config = step.config as {
|
|
1186
|
+
condition?: string;
|
|
1187
|
+
thenSteps?: string[];
|
|
1188
|
+
elseSteps?: string[];
|
|
1189
|
+
} | undefined;
|
|
1190
|
+
|
|
1191
|
+
const conditionResult = config?.condition
|
|
1192
|
+
? this.evaluateCondition(config.condition, context.previousOutputs)
|
|
1193
|
+
: true;
|
|
1194
|
+
|
|
1195
|
+
return {
|
|
1196
|
+
success: true,
|
|
1197
|
+
output: {
|
|
1198
|
+
step: step.stepId,
|
|
1199
|
+
type: 'conditional',
|
|
1200
|
+
conditionMet: conditionResult,
|
|
1201
|
+
branch: conditionResult ? 'then' : 'else',
|
|
1202
|
+
nextSteps: conditionResult ? config?.thenSteps : config?.elseSteps,
|
|
1203
|
+
},
|
|
1204
|
+
error: undefined,
|
|
1205
|
+
durationMs: Date.now() - startTime,
|
|
1206
|
+
retryCount: 0,
|
|
1207
|
+
};
|
|
1208
|
+
});
|
|
1209
|
+
|
|
1210
|
+
// Loop step executor
|
|
1211
|
+
this.stepExecutors.set('loop', async (step, context) => {
|
|
1212
|
+
const startTime = Date.now();
|
|
1213
|
+
const config = step.config as {
|
|
1214
|
+
items?: unknown[];
|
|
1215
|
+
itemsPath?: string;
|
|
1216
|
+
maxIterations?: number;
|
|
1217
|
+
bodySteps?: string[];
|
|
1218
|
+
} | undefined;
|
|
1219
|
+
|
|
1220
|
+
// Get items to iterate over
|
|
1221
|
+
let items: unknown[] = config?.items ?? [];
|
|
1222
|
+
if (config?.itemsPath) {
|
|
1223
|
+
const pathParts = config.itemsPath.split('.');
|
|
1224
|
+
let value: unknown = context.previousOutputs;
|
|
1225
|
+
for (const part of pathParts) {
|
|
1226
|
+
if (value === null || value === undefined) break;
|
|
1227
|
+
value = (value as Record<string, unknown>)[part];
|
|
1228
|
+
}
|
|
1229
|
+
if (Array.isArray(value)) items = value;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
// Apply max iterations limit
|
|
1233
|
+
const maxIterations = config?.maxIterations ?? 100;
|
|
1234
|
+
const limitedItems = items.slice(0, maxIterations);
|
|
1235
|
+
|
|
1236
|
+
return {
|
|
1237
|
+
success: true,
|
|
1238
|
+
output: {
|
|
1239
|
+
step: step.stepId,
|
|
1240
|
+
type: 'loop',
|
|
1241
|
+
itemCount: limitedItems.length,
|
|
1242
|
+
items: limitedItems,
|
|
1243
|
+
bodySteps: config?.bodySteps,
|
|
1244
|
+
message: 'Loop step - items available for iteration',
|
|
1245
|
+
},
|
|
1246
|
+
error: undefined,
|
|
1247
|
+
durationMs: Date.now() - startTime,
|
|
1248
|
+
retryCount: 0,
|
|
1249
|
+
};
|
|
1250
|
+
});
|
|
1251
|
+
|
|
1252
|
+
// Parallel step executor (marker for parallel group)
|
|
1253
|
+
this.stepExecutors.set('parallel', async (step, _context) => {
|
|
1254
|
+
const startTime = Date.now();
|
|
1255
|
+
return {
|
|
1256
|
+
success: true,
|
|
1257
|
+
output: {
|
|
1258
|
+
step: step.stepId,
|
|
1259
|
+
type: 'parallel',
|
|
1260
|
+
message: 'Parallel step marker - child steps executed concurrently',
|
|
1261
|
+
},
|
|
1262
|
+
error: undefined,
|
|
1263
|
+
durationMs: Date.now() - startTime,
|
|
1264
|
+
retryCount: 0,
|
|
1265
|
+
};
|
|
1266
|
+
});
|
|
1267
|
+
|
|
1268
|
+
// Delegate step executor with full delegation tracking
|
|
1269
|
+
this.stepExecutors.set('delegate', async (step, context) => {
|
|
1270
|
+
const startTime = Date.now();
|
|
1271
|
+
const config = step.config as {
|
|
1272
|
+
targetAgentId?: string;
|
|
1273
|
+
task?: string;
|
|
1274
|
+
input?: unknown;
|
|
1275
|
+
} | undefined;
|
|
1276
|
+
|
|
1277
|
+
if (!config?.targetAgentId) {
|
|
1278
|
+
return {
|
|
1279
|
+
success: false,
|
|
1280
|
+
error: {
|
|
1281
|
+
code: AgentErrorCode.AGENT_STAGE_FAILED,
|
|
1282
|
+
message: 'Delegate step requires targetAgentId in config',
|
|
1283
|
+
stepId: step.stepId,
|
|
1284
|
+
},
|
|
1285
|
+
durationMs: Date.now() - startTime,
|
|
1286
|
+
retryCount: 0,
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
// Create delegation tracker using injected factory
|
|
1291
|
+
const delegationTracker = this.delegationTrackerFactory(
|
|
1292
|
+
context.agentId,
|
|
1293
|
+
context.delegationContext,
|
|
1294
|
+
this.config.maxDelegationDepth
|
|
1295
|
+
);
|
|
1296
|
+
|
|
1297
|
+
// Check delegation allowed (INV-DT-001, INV-DT-002)
|
|
1298
|
+
const checkResult = delegationTracker.canDelegate(config.targetAgentId);
|
|
1299
|
+
if (!checkResult.allowed) {
|
|
1300
|
+
return {
|
|
1301
|
+
success: false,
|
|
1302
|
+
error: {
|
|
1303
|
+
code: checkResult.reason === 'MAX_DEPTH_EXCEEDED'
|
|
1304
|
+
? AgentErrorCode.AGENT_DELEGATION_DEPTH_EXCEEDED
|
|
1305
|
+
: AgentErrorCode.AGENT_STAGE_FAILED,
|
|
1306
|
+
message: checkResult.message ?? `Cannot delegate to ${config.targetAgentId}`,
|
|
1307
|
+
stepId: step.stepId,
|
|
1308
|
+
retryable: false,
|
|
1309
|
+
},
|
|
1310
|
+
durationMs: Date.now() - startTime,
|
|
1311
|
+
retryCount: 0,
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
// Check target exists
|
|
1316
|
+
const targetAgent = await this.registry.get(config.targetAgentId);
|
|
1317
|
+
if (targetAgent === undefined) {
|
|
1318
|
+
return {
|
|
1319
|
+
success: false,
|
|
1320
|
+
error: {
|
|
1321
|
+
code: AgentErrorCode.AGENT_NOT_FOUND,
|
|
1322
|
+
message: `Delegation target "${config.targetAgentId}" not found`,
|
|
1323
|
+
stepId: step.stepId,
|
|
1324
|
+
},
|
|
1325
|
+
durationMs: Date.now() - startTime,
|
|
1326
|
+
retryCount: 0,
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
// Create child context and execute
|
|
1331
|
+
const childContext = delegationTracker.createChildContext(config.targetAgentId);
|
|
1332
|
+
|
|
1333
|
+
try {
|
|
1334
|
+
const delegateResult = await this.execute(
|
|
1335
|
+
config.targetAgentId,
|
|
1336
|
+
config.input ?? context.input,
|
|
1337
|
+
{
|
|
1338
|
+
sessionId: context.sessionId,
|
|
1339
|
+
provider: context.provider,
|
|
1340
|
+
model: context.model,
|
|
1341
|
+
delegationContext: childContext,
|
|
1342
|
+
}
|
|
1343
|
+
);
|
|
1344
|
+
|
|
1345
|
+
delegationTracker.recordResult({
|
|
1346
|
+
success: delegateResult.success,
|
|
1347
|
+
handledBy: config.targetAgentId,
|
|
1348
|
+
result: delegateResult.output,
|
|
1349
|
+
durationMs: delegateResult.totalDurationMs,
|
|
1350
|
+
finalDepth: childContext.currentDepth,
|
|
1351
|
+
error: delegateResult.error ? {
|
|
1352
|
+
code: delegateResult.error.code,
|
|
1353
|
+
message: delegateResult.error.message,
|
|
1354
|
+
retryable: false,
|
|
1355
|
+
} : undefined,
|
|
1356
|
+
});
|
|
1357
|
+
|
|
1358
|
+
return {
|
|
1359
|
+
success: delegateResult.success,
|
|
1360
|
+
output: {
|
|
1361
|
+
step: step.stepId,
|
|
1362
|
+
type: 'delegate',
|
|
1363
|
+
delegatedTo: config.targetAgentId,
|
|
1364
|
+
result: delegateResult.output,
|
|
1365
|
+
delegationDepth: childContext.currentDepth,
|
|
1366
|
+
},
|
|
1367
|
+
error: delegateResult.error,
|
|
1368
|
+
durationMs: Date.now() - startTime,
|
|
1369
|
+
retryCount: 0,
|
|
1370
|
+
};
|
|
1371
|
+
} catch (error) {
|
|
1372
|
+
return {
|
|
1373
|
+
success: false,
|
|
1374
|
+
error: {
|
|
1375
|
+
code: AgentErrorCode.AGENT_STAGE_FAILED,
|
|
1376
|
+
message: error instanceof Error ? error.message : 'Delegation failed',
|
|
1377
|
+
stepId: step.stepId,
|
|
1378
|
+
},
|
|
1379
|
+
durationMs: Date.now() - startTime,
|
|
1380
|
+
retryCount: 0,
|
|
1381
|
+
};
|
|
1382
|
+
}
|
|
1383
|
+
});
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
/**
|
|
1388
|
+
* Creates an enhanced agent executor with full feature support
|
|
1389
|
+
*/
|
|
1390
|
+
export function createEnhancedAgentExecutor(
|
|
1391
|
+
registry: AgentRegistry,
|
|
1392
|
+
config: EnhancedAgentDomainConfig
|
|
1393
|
+
): AgentExecutor {
|
|
1394
|
+
return new EnhancedAgentExecutor(registry, config);
|
|
1395
|
+
}
|