@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.
Files changed (77) hide show
  1. package/LICENSE +214 -0
  2. package/dist/enhanced-executor.d.ts +170 -0
  3. package/dist/enhanced-executor.d.ts.map +1 -0
  4. package/dist/enhanced-executor.js +1072 -0
  5. package/dist/enhanced-executor.js.map +1 -0
  6. package/dist/executor.d.ts +120 -0
  7. package/dist/executor.d.ts.map +1 -0
  8. package/dist/executor.js +929 -0
  9. package/dist/executor.js.map +1 -0
  10. package/dist/index.d.ts +25 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +34 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/loader.d.ts +50 -0
  15. package/dist/loader.d.ts.map +1 -0
  16. package/dist/loader.js +160 -0
  17. package/dist/loader.js.map +1 -0
  18. package/dist/persistent-registry.d.ts +105 -0
  19. package/dist/persistent-registry.d.ts.map +1 -0
  20. package/dist/persistent-registry.js +183 -0
  21. package/dist/persistent-registry.js.map +1 -0
  22. package/dist/production-factories.d.ts +70 -0
  23. package/dist/production-factories.d.ts.map +1 -0
  24. package/dist/production-factories.js +434 -0
  25. package/dist/production-factories.js.map +1 -0
  26. package/dist/prompt-executor.d.ts +119 -0
  27. package/dist/prompt-executor.d.ts.map +1 -0
  28. package/dist/prompt-executor.js +211 -0
  29. package/dist/prompt-executor.js.map +1 -0
  30. package/dist/registry.d.ts +57 -0
  31. package/dist/registry.d.ts.map +1 -0
  32. package/dist/registry.js +123 -0
  33. package/dist/registry.js.map +1 -0
  34. package/dist/selection-service.d.ts +74 -0
  35. package/dist/selection-service.d.ts.map +1 -0
  36. package/dist/selection-service.js +322 -0
  37. package/dist/selection-service.js.map +1 -0
  38. package/dist/selector.d.ts +51 -0
  39. package/dist/selector.d.ts.map +1 -0
  40. package/dist/selector.js +249 -0
  41. package/dist/selector.js.map +1 -0
  42. package/dist/stub-checkpoint.d.ts +23 -0
  43. package/dist/stub-checkpoint.d.ts.map +1 -0
  44. package/dist/stub-checkpoint.js +137 -0
  45. package/dist/stub-checkpoint.js.map +1 -0
  46. package/dist/stub-delegation-tracker.d.ts +25 -0
  47. package/dist/stub-delegation-tracker.d.ts.map +1 -0
  48. package/dist/stub-delegation-tracker.js +118 -0
  49. package/dist/stub-delegation-tracker.js.map +1 -0
  50. package/dist/stub-parallel-executor.d.ts +19 -0
  51. package/dist/stub-parallel-executor.d.ts.map +1 -0
  52. package/dist/stub-parallel-executor.js +176 -0
  53. package/dist/stub-parallel-executor.js.map +1 -0
  54. package/dist/types.d.ts +614 -0
  55. package/dist/types.d.ts.map +1 -0
  56. package/dist/types.js +15 -0
  57. package/dist/types.js.map +1 -0
  58. package/dist/workflow-templates.d.ts +117 -0
  59. package/dist/workflow-templates.d.ts.map +1 -0
  60. package/dist/workflow-templates.js +342 -0
  61. package/dist/workflow-templates.js.map +1 -0
  62. package/package.json +51 -0
  63. package/src/enhanced-executor.ts +1395 -0
  64. package/src/executor.ts +1153 -0
  65. package/src/index.ts +172 -0
  66. package/src/loader.ts +191 -0
  67. package/src/persistent-registry.ts +235 -0
  68. package/src/production-factories.ts +613 -0
  69. package/src/prompt-executor.ts +310 -0
  70. package/src/registry.ts +167 -0
  71. package/src/selection-service.ts +411 -0
  72. package/src/selector.ts +299 -0
  73. package/src/stub-checkpoint.ts +187 -0
  74. package/src/stub-delegation-tracker.ts +161 -0
  75. package/src/stub-parallel-executor.ts +224 -0
  76. package/src/types.ts +784 -0
  77. package/src/workflow-templates.ts +393 -0
@@ -0,0 +1,929 @@
1
+ /**
2
+ * Agent Executor Implementation
3
+ *
4
+ * Executes agent workflows with step-by-step processing.
5
+ * Integrates with DelegationTracker for INV-DT-001 (depth limits) and INV-DT-002 (no circular).
6
+ */
7
+ import { AgentErrorCode, AgentRunOptionsSchema, LIMIT_ABILITY_TOKENS_AGENT, } from '@defai.digital/contracts';
8
+ import { createStubPromptExecutor } from './prompt-executor.js';
9
+ import { stubDelegationTrackerFactory } from './stub-delegation-tracker.js';
10
+ /**
11
+ * Default agent executor implementation
12
+ *
13
+ * Supports optional ability injection via AbilityManager.
14
+ * INV-AGT-ABL-001: When abilityManager is provided, abilities are injected before prompt execution
15
+ * INV-AGT-ABL-002: Core abilities from agent profile are always included
16
+ * INV-AGT-ABL-003: Token limits are respected during injection
17
+ */
18
+ export class DefaultAgentExecutor {
19
+ registry;
20
+ config;
21
+ executions = new Map();
22
+ stepExecutors = new Map();
23
+ promptExecutor;
24
+ toolExecutor;
25
+ abilityManager;
26
+ enableAbilityInjection;
27
+ maxAbilityTokens;
28
+ delegationTrackerFactory;
29
+ constructor(registry, config) {
30
+ this.registry = registry;
31
+ this.config = config;
32
+ // Use provided prompt executor or create a stub for testing
33
+ this.promptExecutor = config.promptExecutor ?? createStubPromptExecutor(config.defaultProvider ?? 'claude');
34
+ // Tool executor for executing MCP tools (optional)
35
+ this.toolExecutor = config.toolExecutor;
36
+ // Ability injection configuration
37
+ this.abilityManager = config.abilityManager;
38
+ this.enableAbilityInjection = config.enableAbilityInjection ?? (config.abilityManager !== undefined);
39
+ this.maxAbilityTokens = config.maxAbilityTokens ?? LIMIT_ABILITY_TOKENS_AGENT;
40
+ // Delegation tracker factory (use stub if not provided)
41
+ this.delegationTrackerFactory = config.delegationTrackerFactory ?? stubDelegationTrackerFactory;
42
+ // Register default step executors
43
+ this.registerDefaultExecutors();
44
+ }
45
+ /**
46
+ * Execute an agent with the given input
47
+ * Validates AgentRunOptions at entry point per contract
48
+ */
49
+ async execute(agentId, input, options) {
50
+ const startTime = Date.now();
51
+ const executionId = crypto.randomUUID();
52
+ // Validate options at entry point
53
+ let validatedOptions;
54
+ if (options !== undefined) {
55
+ try {
56
+ validatedOptions = AgentRunOptionsSchema.parse(options);
57
+ }
58
+ catch (error) {
59
+ return this.createErrorResult(agentId, startTime, AgentErrorCode.AGENT_VALIDATION_ERROR, `Invalid run options: ${error instanceof Error ? error.message : 'Unknown error'}`);
60
+ }
61
+ }
62
+ // Get agent profile
63
+ const agent = await this.registry.get(agentId);
64
+ if (agent === undefined) {
65
+ return this.createErrorResult(agentId, startTime, AgentErrorCode.AGENT_NOT_FOUND, `Agent "${agentId}" not found`);
66
+ }
67
+ // Check if agent is enabled
68
+ if (!agent.enabled) {
69
+ return this.createErrorResult(agentId, startTime, AgentErrorCode.AGENT_PERMISSION_DENIED, `Agent "${agentId}" is disabled`);
70
+ }
71
+ // Initialize execution state
72
+ const state = {
73
+ executionId,
74
+ agentId,
75
+ status: 'running',
76
+ startedAt: new Date().toISOString(),
77
+ stepResults: [],
78
+ previousOutputs: {},
79
+ };
80
+ this.executions.set(executionId, state);
81
+ try {
82
+ // Execute workflow steps
83
+ const workflow = agent.workflow ?? [];
84
+ const stepResults = [];
85
+ // Handle agents with no workflow defined
86
+ if (workflow.length === 0) {
87
+ state.status = 'completed';
88
+ state.completedAt = new Date().toISOString();
89
+ return {
90
+ success: true,
91
+ agentId,
92
+ sessionId: validatedOptions?.sessionId,
93
+ output: {
94
+ message: `Agent "${agentId}" has no workflow steps defined. Register the agent with a workflow to enable execution.`,
95
+ agent: {
96
+ agentId: agent.agentId,
97
+ description: agent.description,
98
+ capabilities: agent.capabilities,
99
+ systemPrompt: agent.systemPrompt ? '(defined)' : undefined,
100
+ },
101
+ input,
102
+ },
103
+ stepResults: [],
104
+ totalDurationMs: Date.now() - startTime,
105
+ };
106
+ }
107
+ for (const step of workflow) {
108
+ // Check for cancellation
109
+ if (state.status === 'cancelled') {
110
+ break;
111
+ }
112
+ state.currentStep = step.stepId;
113
+ // Check dependencies
114
+ const dependenciesMet = this.checkDependencies(step, stepResults);
115
+ if (!dependenciesMet) {
116
+ stepResults.push({
117
+ stepId: step.stepId,
118
+ success: false,
119
+ durationMs: 0,
120
+ retryCount: 0,
121
+ skipped: true,
122
+ error: {
123
+ code: AgentErrorCode.AGENT_DEPENDENCY_FAILED,
124
+ message: 'Dependencies not met',
125
+ },
126
+ });
127
+ continue;
128
+ }
129
+ // Check condition
130
+ if (step.condition !== undefined) {
131
+ const conditionMet = this.evaluateCondition(step.condition, state.previousOutputs);
132
+ if (!conditionMet) {
133
+ stepResults.push({
134
+ stepId: step.stepId,
135
+ success: true,
136
+ durationMs: 0,
137
+ retryCount: 0,
138
+ skipped: true,
139
+ error: undefined,
140
+ });
141
+ continue;
142
+ }
143
+ }
144
+ // Execute step with retries
145
+ const result = await this.executeStep(step, {
146
+ agentId,
147
+ executionId,
148
+ sessionId: validatedOptions?.sessionId,
149
+ input,
150
+ previousOutputs: state.previousOutputs,
151
+ memory: undefined,
152
+ provider: validatedOptions?.provider,
153
+ model: validatedOptions?.model,
154
+ delegationContext: validatedOptions?.delegationContext,
155
+ }, step.retryPolicy?.maxAttempts ?? 1);
156
+ stepResults.push({
157
+ stepId: step.stepId,
158
+ success: result.success,
159
+ output: result.output,
160
+ durationMs: result.durationMs,
161
+ retryCount: result.retryCount,
162
+ skipped: false,
163
+ error: result.error,
164
+ });
165
+ // Store output for dependencies
166
+ if (result.success && result.output !== undefined) {
167
+ state.previousOutputs[step.stepId] = result.output;
168
+ }
169
+ // Stop on failure (unless parallel execution)
170
+ if (!result.success && !step.parallel) {
171
+ break;
172
+ }
173
+ }
174
+ // Determine final status
175
+ const allSuccess = stepResults.every((r) => r.success || r.skipped);
176
+ const finalOutput = this.collectOutputs(stepResults, state.previousOutputs);
177
+ state.status = allSuccess ? 'completed' : 'failed';
178
+ state.completedAt = new Date().toISOString();
179
+ state.stepResults = stepResults;
180
+ return {
181
+ success: allSuccess,
182
+ agentId,
183
+ sessionId: validatedOptions?.sessionId,
184
+ output: finalOutput,
185
+ stepResults,
186
+ totalDurationMs: Date.now() - startTime,
187
+ error: allSuccess
188
+ ? undefined
189
+ : stepResults.find((r) => r.error !== undefined)?.error,
190
+ };
191
+ }
192
+ catch (error) {
193
+ state.status = 'failed';
194
+ state.completedAt = new Date().toISOString();
195
+ return this.createErrorResult(agentId, startTime, AgentErrorCode.AGENT_STAGE_FAILED, error instanceof Error ? error.message : 'Unknown error');
196
+ }
197
+ }
198
+ /**
199
+ * Cancel a running agent execution
200
+ */
201
+ async cancel(executionId) {
202
+ const state = this.executions.get(executionId);
203
+ if (state?.status === 'running') {
204
+ state.status = 'cancelled';
205
+ state.completedAt = new Date().toISOString();
206
+ }
207
+ }
208
+ /**
209
+ * Get execution status
210
+ */
211
+ async getStatus(executionId) {
212
+ const state = this.executions.get(executionId);
213
+ if (state === undefined) {
214
+ return undefined;
215
+ }
216
+ const completedSteps = state.stepResults.filter((r) => r.success || r.skipped).length;
217
+ const totalSteps = state.stepResults.length;
218
+ return {
219
+ executionId: state.executionId,
220
+ agentId: state.agentId,
221
+ status: state.status,
222
+ currentStep: state.currentStep,
223
+ startedAt: state.startedAt,
224
+ completedAt: state.completedAt,
225
+ progress: {
226
+ totalSteps,
227
+ completedSteps,
228
+ currentStepName: state.currentStep,
229
+ percentComplete: totalSteps > 0 ? Math.round((completedSteps / totalSteps) * 100) : 0,
230
+ },
231
+ };
232
+ }
233
+ /**
234
+ * Register a step executor for a specific step type
235
+ */
236
+ registerStepExecutor(type, executor) {
237
+ this.stepExecutors.set(type, executor);
238
+ }
239
+ /**
240
+ * Execute a single step with retry logic
241
+ */
242
+ async executeStep(step, context, maxAttempts) {
243
+ const executor = this.stepExecutors.get(step.type);
244
+ if (executor === undefined) {
245
+ return {
246
+ success: false,
247
+ error: {
248
+ code: AgentErrorCode.AGENT_STAGE_FAILED,
249
+ message: `No executor for step type: ${step.type}`,
250
+ stepId: step.stepId,
251
+ },
252
+ durationMs: 0,
253
+ retryCount: 0,
254
+ };
255
+ }
256
+ let lastError;
257
+ let retryCount = 0;
258
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
259
+ // Apply timeout with proper cleanup
260
+ const timeout = step.timeoutMs ?? this.config.defaultTimeout;
261
+ const { promise: timeoutPromise, cancel: cancelTimeout } = this.createTimeout(timeout, step.stepId);
262
+ try {
263
+ const result = await Promise.race([
264
+ executor(step, context),
265
+ timeoutPromise,
266
+ ]);
267
+ cancelTimeout(); // Clean up the timer
268
+ if (result.success) {
269
+ return {
270
+ ...result,
271
+ retryCount,
272
+ };
273
+ }
274
+ lastError = result.error;
275
+ retryCount++;
276
+ // Apply backoff
277
+ if (attempt < maxAttempts - 1 && step.retryPolicy !== undefined) {
278
+ const backoff = step.retryPolicy.backoffMs *
279
+ Math.pow(step.retryPolicy.backoffMultiplier, attempt);
280
+ await this.sleep(backoff);
281
+ }
282
+ }
283
+ catch (error) {
284
+ cancelTimeout(); // Clean up the timer on error too
285
+ lastError = {
286
+ code: AgentErrorCode.AGENT_STAGE_FAILED,
287
+ message: error instanceof Error ? error.message : 'Unknown error',
288
+ stepId: step.stepId,
289
+ };
290
+ retryCount++;
291
+ }
292
+ }
293
+ return {
294
+ success: false,
295
+ error: lastError,
296
+ durationMs: 0,
297
+ retryCount,
298
+ };
299
+ }
300
+ /**
301
+ * Check if step dependencies are met
302
+ */
303
+ checkDependencies(step, results) {
304
+ if (step.dependencies === undefined || step.dependencies.length === 0) {
305
+ return true;
306
+ }
307
+ return step.dependencies.every((depId) => {
308
+ const depResult = results.find((r) => r.stepId === depId);
309
+ return depResult !== undefined && depResult.success;
310
+ });
311
+ }
312
+ /**
313
+ * Evaluate a condition expression safely without using eval/Function
314
+ *
315
+ * Supports patterns:
316
+ * - ${variable} === value
317
+ * - ${variable} !== value
318
+ * - ${variable} > value
319
+ * - ${variable} < value
320
+ * - ${variable} >= value
321
+ * - ${variable} <= value
322
+ * - ${variable} (truthy check)
323
+ * - !${variable} (falsy check)
324
+ * - condition && condition
325
+ * - condition || condition
326
+ */
327
+ evaluateCondition(condition, outputs) {
328
+ try {
329
+ // Handle logical operators by recursively evaluating sub-conditions
330
+ // Check for || first (lower precedence)
331
+ const orParts = this.splitByOperator(condition, '||');
332
+ if (orParts.length > 1) {
333
+ return orParts.some((part) => this.evaluateCondition(part.trim(), outputs));
334
+ }
335
+ // Check for && (higher precedence than ||)
336
+ const andParts = this.splitByOperator(condition, '&&');
337
+ if (andParts.length > 1) {
338
+ return andParts.every((part) => this.evaluateCondition(part.trim(), outputs));
339
+ }
340
+ // Handle negation
341
+ const trimmed = condition.trim();
342
+ if (trimmed.startsWith('!')) {
343
+ return !this.evaluateCondition(trimmed.slice(1).trim(), outputs);
344
+ }
345
+ // Handle parentheses
346
+ if (trimmed.startsWith('(') && trimmed.endsWith(')')) {
347
+ return this.evaluateCondition(trimmed.slice(1, -1), outputs);
348
+ }
349
+ // Parse comparison expression: ${variable} op value
350
+ const comparisonMatch = /^\$\{(\w+(?:\.\w+)*)\}\s*(===|!==|==|!=|>=|<=|>|<)\s*(.+)$/.exec(trimmed);
351
+ if (comparisonMatch) {
352
+ const [, keyPath, op, rawValue] = comparisonMatch;
353
+ const actualValue = this.getConditionValue(outputs, keyPath);
354
+ const expectedValue = this.parseValue(rawValue.trim());
355
+ return this.compareValues(actualValue, expectedValue, op);
356
+ }
357
+ // Handle simple variable reference (truthy check): ${variable}
358
+ const varMatch = /^\$\{(\w+(?:\.\w+)*)\}$/.exec(trimmed);
359
+ if (varMatch) {
360
+ const value = this.getConditionValue(outputs, varMatch[1]);
361
+ return Boolean(value);
362
+ }
363
+ // Handle literal boolean
364
+ if (trimmed === 'true')
365
+ return true;
366
+ if (trimmed === 'false')
367
+ return false;
368
+ // Unknown pattern - fail safely
369
+ console.warn(`[evaluateCondition] Unknown condition pattern: ${condition}`);
370
+ return false;
371
+ }
372
+ catch (error) {
373
+ console.warn(`[evaluateCondition] Error evaluating condition: ${condition}`, error);
374
+ return false;
375
+ }
376
+ }
377
+ /**
378
+ * Split condition by operator, respecting parentheses
379
+ */
380
+ splitByOperator(condition, operator) {
381
+ const parts = [];
382
+ let current = '';
383
+ let depth = 0;
384
+ let i = 0;
385
+ while (i < condition.length) {
386
+ const char = condition[i];
387
+ if (char === '(') {
388
+ depth++;
389
+ current += char;
390
+ }
391
+ else if (char === ')') {
392
+ depth--;
393
+ current += char;
394
+ }
395
+ else if (depth === 0 && condition.slice(i, i + operator.length) === operator) {
396
+ parts.push(current);
397
+ current = '';
398
+ i += operator.length;
399
+ continue;
400
+ }
401
+ else {
402
+ current += char;
403
+ }
404
+ i++;
405
+ }
406
+ if (current) {
407
+ parts.push(current);
408
+ }
409
+ return parts;
410
+ }
411
+ /**
412
+ * Get nested value from object using dot notation (for condition evaluation)
413
+ */
414
+ getConditionValue(obj, path) {
415
+ const parts = path.split('.');
416
+ let current = obj;
417
+ for (const part of parts) {
418
+ if (current === null || current === undefined) {
419
+ return undefined;
420
+ }
421
+ current = current[part];
422
+ }
423
+ return current;
424
+ }
425
+ /**
426
+ * Parse a value string into its actual type
427
+ */
428
+ parseValue(value) {
429
+ // String literal
430
+ if ((value.startsWith('"') && value.endsWith('"')) ||
431
+ (value.startsWith("'") && value.endsWith("'"))) {
432
+ return value.slice(1, -1);
433
+ }
434
+ // Null/undefined
435
+ if (value === 'null')
436
+ return null;
437
+ if (value === 'undefined')
438
+ return undefined;
439
+ // Boolean
440
+ if (value === 'true')
441
+ return true;
442
+ if (value === 'false')
443
+ return false;
444
+ // Number
445
+ const num = Number(value);
446
+ if (!isNaN(num))
447
+ return num;
448
+ // Default to string
449
+ return value;
450
+ }
451
+ /**
452
+ * Compare two values using the specified operator
453
+ */
454
+ compareValues(actual, expected, op) {
455
+ switch (op) {
456
+ case '===':
457
+ return actual === expected;
458
+ case '!==':
459
+ return actual !== expected;
460
+ case '==':
461
+ return actual == expected;
462
+ case '!=':
463
+ return actual != expected;
464
+ case '>':
465
+ return actual > expected;
466
+ case '<':
467
+ return actual < expected;
468
+ case '>=':
469
+ return actual >= expected;
470
+ case '<=':
471
+ return actual <= expected;
472
+ default:
473
+ return false;
474
+ }
475
+ }
476
+ /**
477
+ * Collect final outputs from step results
478
+ */
479
+ collectOutputs(results, _previousOutputs) {
480
+ const output = {};
481
+ for (const result of results) {
482
+ if (result.success && result.output !== undefined) {
483
+ output[result.stepId] = result.output;
484
+ }
485
+ }
486
+ return output;
487
+ }
488
+ /**
489
+ * Create error result
490
+ */
491
+ createErrorResult(agentId, startTime, code, message) {
492
+ return {
493
+ success: false,
494
+ agentId,
495
+ error: { code, message },
496
+ totalDurationMs: Date.now() - startTime,
497
+ };
498
+ }
499
+ /**
500
+ * Create timeout promise with cancellation support
501
+ */
502
+ createTimeout(ms, stepId) {
503
+ let timeoutId;
504
+ const promise = new Promise((_, reject) => {
505
+ timeoutId = setTimeout(() => {
506
+ reject({
507
+ success: false,
508
+ error: {
509
+ code: AgentErrorCode.AGENT_STAGE_FAILED,
510
+ message: `Step "${stepId}" timed out after ${ms}ms`,
511
+ stepId,
512
+ retryable: true,
513
+ },
514
+ durationMs: ms,
515
+ retryCount: 0,
516
+ });
517
+ }, ms);
518
+ });
519
+ const cancel = () => {
520
+ if (timeoutId !== undefined) {
521
+ clearTimeout(timeoutId);
522
+ }
523
+ };
524
+ return { promise, cancel };
525
+ }
526
+ /**
527
+ * Sleep for specified duration
528
+ */
529
+ sleep(ms) {
530
+ return new Promise((resolve) => setTimeout(resolve, ms));
531
+ }
532
+ /**
533
+ * Substitute template variables in a string
534
+ * Supports: ${input}, ${input.field}, ${previousOutputs.stepId}, ${agent.field}
535
+ */
536
+ substituteVariables(template, context, agentProfile) {
537
+ return template.replace(/\$\{([^}]+)\}/g, (match, path) => {
538
+ const parts = path.split('.');
539
+ let value;
540
+ switch (parts[0]) {
541
+ case 'input':
542
+ if (parts.length === 1) {
543
+ value = typeof context.input === 'string'
544
+ ? context.input
545
+ : JSON.stringify(context.input, null, 2);
546
+ }
547
+ else {
548
+ value = this.getNestedValue(context.input, parts.slice(1));
549
+ }
550
+ break;
551
+ case 'previousOutputs':
552
+ if (parts.length > 1) {
553
+ value = this.getNestedValue(context.previousOutputs, parts.slice(1));
554
+ }
555
+ break;
556
+ case 'agent':
557
+ if (parts.length > 1) {
558
+ value = this.getNestedValue(agentProfile, parts.slice(1));
559
+ }
560
+ break;
561
+ default:
562
+ // Try to get from input directly
563
+ value = this.getNestedValue(context.input, parts);
564
+ }
565
+ if (value === undefined) {
566
+ return match; // Keep original if not found
567
+ }
568
+ return typeof value === 'string' ? value : JSON.stringify(value);
569
+ });
570
+ }
571
+ /**
572
+ * Get nested value from object using path array
573
+ */
574
+ getNestedValue(obj, path) {
575
+ let current = obj;
576
+ for (const key of path) {
577
+ if (current === null || current === undefined) {
578
+ return undefined;
579
+ }
580
+ if (typeof current !== 'object') {
581
+ return undefined;
582
+ }
583
+ current = current[key];
584
+ }
585
+ return current;
586
+ }
587
+ /**
588
+ * Register default step executors
589
+ */
590
+ registerDefaultExecutors() {
591
+ // Prompt step executor - executes real LLM calls via promptExecutor
592
+ // INV-AGT-ABL-001: Abilities injected before prompt execution
593
+ this.stepExecutors.set('prompt', async (step, context) => {
594
+ const startTime = Date.now();
595
+ const config = (step.config ?? {});
596
+ // Get agent profile for system prompt and variable substitution
597
+ const agent = await this.registry.get(context.agentId);
598
+ const agentProfile = {
599
+ agentId: context.agentId,
600
+ };
601
+ if (agent?.description !== undefined) {
602
+ agentProfile.description = agent.description;
603
+ }
604
+ if (agent?.systemPrompt !== undefined) {
605
+ agentProfile.systemPrompt = agent.systemPrompt;
606
+ }
607
+ // INV-AGT-ABL-001: Inject abilities if ability manager is available
608
+ // INV-AGT-ABL-002: Core abilities from agent profile are always included
609
+ // INV-AGT-ABL-003: Token limits are respected
610
+ let abilityContent = '';
611
+ if (this.enableAbilityInjection && this.abilityManager !== undefined) {
612
+ try {
613
+ // Get core abilities from agent profile
614
+ const coreAbilities = agent?.abilities?.core ?? [];
615
+ // Determine task from input for relevance matching
616
+ const task = typeof context.input === 'string'
617
+ ? context.input
618
+ : typeof context.input === 'object' && context.input !== null && 'task' in context.input
619
+ ? String(context.input.task)
620
+ : typeof context.input === 'object' && context.input !== null && 'prompt' in context.input
621
+ ? String(context.input.prompt)
622
+ : agent?.description ?? '';
623
+ // Inject abilities
624
+ const injectionResult = await this.abilityManager.injectAbilities(context.agentId, task, coreAbilities, {
625
+ maxTokens: this.maxAbilityTokens,
626
+ includeMetadata: true,
627
+ });
628
+ if (injectionResult.combinedContent.length > 0) {
629
+ abilityContent = `\n\n## Relevant Knowledge & Abilities\n\n${injectionResult.combinedContent}\n\n---\n\n`;
630
+ }
631
+ }
632
+ catch {
633
+ // Ability injection failure should not block execution
634
+ // Just log and continue without abilities
635
+ }
636
+ }
637
+ // Build the prompt from config or input
638
+ let promptText;
639
+ if (config.prompt) {
640
+ // Use template from config with variable substitution
641
+ promptText = this.substituteVariables(config.prompt, context, agentProfile);
642
+ }
643
+ else if (typeof context.input === 'string') {
644
+ // Use input directly as prompt
645
+ promptText = context.input;
646
+ }
647
+ else if (context.input !== null &&
648
+ typeof context.input === 'object' &&
649
+ 'prompt' in context.input &&
650
+ typeof context.input.prompt === 'string') {
651
+ // Use input.prompt
652
+ promptText = context.input.prompt;
653
+ }
654
+ else {
655
+ // Default: stringify input as the prompt
656
+ promptText = JSON.stringify(context.input, null, 2);
657
+ }
658
+ // Determine system prompt with injected abilities
659
+ const baseSystemPrompt = config.systemPrompt ?? agent?.systemPrompt ?? '';
660
+ const systemPrompt = abilityContent
661
+ ? `${baseSystemPrompt}${abilityContent}`
662
+ : baseSystemPrompt || undefined;
663
+ // Execute the prompt
664
+ const response = await this.promptExecutor.execute({
665
+ prompt: promptText,
666
+ systemPrompt: systemPrompt || undefined,
667
+ provider: config.provider ?? context.provider,
668
+ model: config.model ?? context.model,
669
+ maxTokens: config.maxTokens,
670
+ temperature: config.temperature,
671
+ timeout: config.timeout ?? step.timeoutMs,
672
+ });
673
+ if (response.success) {
674
+ return {
675
+ success: true,
676
+ output: {
677
+ content: response.content,
678
+ provider: response.provider,
679
+ model: response.model,
680
+ usage: response.usage,
681
+ },
682
+ error: undefined,
683
+ durationMs: Date.now() - startTime,
684
+ retryCount: 0,
685
+ };
686
+ }
687
+ else {
688
+ return {
689
+ success: false,
690
+ error: {
691
+ code: response.errorCode ?? AgentErrorCode.AGENT_STAGE_FAILED,
692
+ message: response.error ?? 'Prompt execution failed',
693
+ stepId: step.stepId,
694
+ retryable: true,
695
+ },
696
+ durationMs: Date.now() - startTime,
697
+ retryCount: 0,
698
+ };
699
+ }
700
+ });
701
+ // Tool step executor (INV-TOOL-001, INV-TOOL-002, INV-TOOL-003)
702
+ this.stepExecutors.set('tool', async (step, context) => {
703
+ const startTime = Date.now();
704
+ const config = step.config;
705
+ const toolName = config?.toolName;
706
+ const toolInput = (config?.toolInput ?? context.input ?? {});
707
+ // If no tool executor is configured, return placeholder result
708
+ if (this.toolExecutor === undefined) {
709
+ return {
710
+ success: true,
711
+ output: {
712
+ step: step.stepId,
713
+ type: 'tool',
714
+ toolName: toolName ?? 'unknown',
715
+ status: 'no_executor',
716
+ message: 'Tool execution requires a ToolExecutor. Configure AgentDomainConfig.toolExecutor.',
717
+ },
718
+ error: undefined,
719
+ durationMs: Date.now() - startTime,
720
+ retryCount: 0,
721
+ };
722
+ }
723
+ // Check if tool name is provided
724
+ if (toolName === undefined || toolName.trim() === '') {
725
+ return {
726
+ success: false,
727
+ output: undefined,
728
+ error: {
729
+ code: 'TOOL_CONFIG_ERROR',
730
+ message: `Tool step "${step.stepId}" requires toolName in config`,
731
+ stepId: step.stepId,
732
+ },
733
+ durationMs: Date.now() - startTime,
734
+ retryCount: 0,
735
+ };
736
+ }
737
+ // INV-TOOL-001: Check if tool is available
738
+ if (!this.toolExecutor.isToolAvailable(toolName)) {
739
+ return {
740
+ success: false,
741
+ output: undefined,
742
+ error: {
743
+ code: 'TOOL_NOT_FOUND',
744
+ message: `Tool "${toolName}" is not available`,
745
+ stepId: step.stepId,
746
+ },
747
+ durationMs: Date.now() - startTime,
748
+ retryCount: 0,
749
+ };
750
+ }
751
+ // Execute the tool
752
+ try {
753
+ const result = await this.toolExecutor.execute(toolName, toolInput);
754
+ return {
755
+ success: result.success,
756
+ output: {
757
+ step: step.stepId,
758
+ type: 'tool',
759
+ toolName,
760
+ toolOutput: result.output,
761
+ },
762
+ error: result.success ? undefined : {
763
+ code: result.errorCode ?? 'TOOL_EXECUTION_ERROR',
764
+ message: result.error ?? 'Tool execution failed',
765
+ stepId: step.stepId,
766
+ retryable: result.retryable ?? false,
767
+ },
768
+ durationMs: Date.now() - startTime,
769
+ retryCount: 0,
770
+ };
771
+ }
772
+ catch (error) {
773
+ return {
774
+ success: false,
775
+ output: undefined,
776
+ error: {
777
+ code: 'TOOL_EXECUTION_ERROR',
778
+ message: error instanceof Error ? error.message : 'Unknown tool execution error',
779
+ stepId: step.stepId,
780
+ retryable: true,
781
+ },
782
+ durationMs: Date.now() - startTime,
783
+ retryCount: 0,
784
+ };
785
+ }
786
+ });
787
+ // Conditional step executor
788
+ this.stepExecutors.set('conditional', async (step, _context) => {
789
+ const startTime = Date.now();
790
+ return {
791
+ success: true,
792
+ output: { step: step.stepId, type: 'conditional' },
793
+ error: undefined,
794
+ durationMs: Date.now() - startTime,
795
+ retryCount: 0,
796
+ };
797
+ });
798
+ // Loop step executor
799
+ this.stepExecutors.set('loop', async (step, _context) => {
800
+ const startTime = Date.now();
801
+ return {
802
+ success: true,
803
+ output: { step: step.stepId, type: 'loop' },
804
+ error: undefined,
805
+ durationMs: Date.now() - startTime,
806
+ retryCount: 0,
807
+ };
808
+ });
809
+ // Parallel step executor
810
+ this.stepExecutors.set('parallel', async (step, _context) => {
811
+ const startTime = Date.now();
812
+ return {
813
+ success: true,
814
+ output: { step: step.stepId, type: 'parallel' },
815
+ error: undefined,
816
+ durationMs: Date.now() - startTime,
817
+ retryCount: 0,
818
+ };
819
+ });
820
+ // Delegate step executor - integrates with DelegationTracker
821
+ // INV-DT-001: Depth never exceeds maxDepth
822
+ // INV-DT-002: No circular delegations
823
+ this.stepExecutors.set('delegate', async (step, context) => {
824
+ const startTime = Date.now();
825
+ const config = step.config;
826
+ if (!config?.targetAgentId) {
827
+ return {
828
+ success: false,
829
+ error: {
830
+ code: AgentErrorCode.AGENT_STAGE_FAILED,
831
+ message: 'Delegate step requires targetAgentId in config',
832
+ stepId: step.stepId,
833
+ },
834
+ durationMs: Date.now() - startTime,
835
+ retryCount: 0,
836
+ };
837
+ }
838
+ // Create delegation tracker using injected factory
839
+ const delegationTracker = this.delegationTrackerFactory(context.agentId, context.delegationContext, this.config.maxDelegationDepth);
840
+ // Check if delegation is allowed (INV-DT-001, INV-DT-002)
841
+ const checkResult = delegationTracker.canDelegate(config.targetAgentId);
842
+ if (!checkResult.allowed) {
843
+ return {
844
+ success: false,
845
+ error: {
846
+ code: checkResult.reason === 'MAX_DEPTH_EXCEEDED'
847
+ ? AgentErrorCode.AGENT_DELEGATION_DEPTH_EXCEEDED
848
+ : AgentErrorCode.AGENT_STAGE_FAILED,
849
+ message: checkResult.message ?? `Cannot delegate to ${config.targetAgentId}`,
850
+ stepId: step.stepId,
851
+ retryable: false,
852
+ },
853
+ durationMs: Date.now() - startTime,
854
+ retryCount: 0,
855
+ };
856
+ }
857
+ // Get target agent profile
858
+ const targetAgent = await this.registry.get(config.targetAgentId);
859
+ if (targetAgent === undefined) {
860
+ return {
861
+ success: false,
862
+ error: {
863
+ code: AgentErrorCode.AGENT_NOT_FOUND,
864
+ message: `Delegation target "${config.targetAgentId}" not found`,
865
+ stepId: step.stepId,
866
+ },
867
+ durationMs: Date.now() - startTime,
868
+ retryCount: 0,
869
+ };
870
+ }
871
+ // Create child context for the delegated agent
872
+ const childContext = delegationTracker.createChildContext(config.targetAgentId);
873
+ // Execute the delegated agent recursively
874
+ try {
875
+ const delegateResult = await this.execute(config.targetAgentId, config.input ?? context.input, {
876
+ sessionId: context.sessionId,
877
+ provider: context.provider,
878
+ model: context.model,
879
+ delegationContext: childContext,
880
+ });
881
+ // Record the delegation result
882
+ delegationTracker.recordResult({
883
+ success: delegateResult.success,
884
+ handledBy: config.targetAgentId,
885
+ result: delegateResult.output,
886
+ durationMs: delegateResult.totalDurationMs,
887
+ finalDepth: childContext.currentDepth,
888
+ error: delegateResult.error ? {
889
+ code: delegateResult.error.code,
890
+ message: delegateResult.error.message,
891
+ retryable: false,
892
+ } : undefined,
893
+ });
894
+ return {
895
+ success: delegateResult.success,
896
+ output: {
897
+ step: step.stepId,
898
+ type: 'delegate',
899
+ delegatedTo: config.targetAgentId,
900
+ result: delegateResult.output,
901
+ delegationDepth: childContext.currentDepth,
902
+ },
903
+ error: delegateResult.error,
904
+ durationMs: Date.now() - startTime,
905
+ retryCount: 0,
906
+ };
907
+ }
908
+ catch (error) {
909
+ return {
910
+ success: false,
911
+ error: {
912
+ code: AgentErrorCode.AGENT_STAGE_FAILED,
913
+ message: error instanceof Error ? error.message : 'Delegation failed',
914
+ stepId: step.stepId,
915
+ },
916
+ durationMs: Date.now() - startTime,
917
+ retryCount: 0,
918
+ };
919
+ }
920
+ });
921
+ }
922
+ }
923
+ /**
924
+ * Creates a new agent executor
925
+ */
926
+ export function createAgentExecutor(registry, config) {
927
+ return new DefaultAgentExecutor(registry, config);
928
+ }
929
+ //# sourceMappingURL=executor.js.map