@memberjunction/ai-agents 2.68.0 → 2.69.1

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.
@@ -38,12 +38,18 @@ class BaseAgent {
38
38
  constructor() {
39
39
  this._promptRunner = new ai_prompts_1.AIPromptRunner();
40
40
  this._metadata = new core_1.Metadata();
41
+ this._executionCounts = new Map();
42
+ this._generalValidationRetryCount = 0;
41
43
  this._agentRun = null;
42
44
  this._agentHierarchy = [];
43
45
  this._depth = 0;
44
46
  this._allProgressSteps = [];
45
47
  this._subAgentRuns = [];
46
48
  this._payloadManager = new PayloadManager_1.PayloadManager();
49
+ this._validationRetryCount = 0;
50
+ }
51
+ get ValidationRetryCount() {
52
+ return this._validationRetryCount;
47
53
  }
48
54
  logStatus(message, verboseOnly = false, params) {
49
55
  if (verboseOnly) {
@@ -128,6 +134,8 @@ class BaseAgent {
128
134
  message: this.formatHierarchicalMessage(`Initializing ${params.agent.Name} agent and preparing execution environment`)
129
135
  });
130
136
  await this.initializeAgentRun(wrappedParams);
137
+ this._validationRetryCount = 0;
138
+ this._generalValidationRetryCount = 0;
131
139
  await this.initializeEngines(params.contextUser);
132
140
  if (params.cancellationToken?.aborted) {
133
141
  return await this.createCancelledResult('Cancelled during initialization', params.contextUser);
@@ -349,20 +357,20 @@ class BaseAgent {
349
357
  const nextStep = await agentTypeInstance.DetermineNextStep(promptResult, currentPayload);
350
358
  return nextStep;
351
359
  }
352
- async validateNextStep(params, nextStep, currentPayload) {
360
+ async validateNextStep(params, nextStep, currentPayload, agentRun, currentStep) {
353
361
  switch (nextStep.step) {
354
362
  case 'Sub-Agent':
355
- return this.validateSubAgentNextStep(params, nextStep, currentPayload);
363
+ return this.validateSubAgentNextStep(params, nextStep, currentPayload, agentRun, currentStep);
356
364
  case 'Actions':
357
- return this.validateActionsNextStep(params, nextStep, currentPayload);
365
+ return this.validateActionsNextStep(params, nextStep, currentPayload, agentRun, currentStep);
358
366
  case 'Success':
359
- return this.validateSuccessNextStep(params, nextStep, currentPayload);
367
+ return this.validateSuccessNextStep(params, nextStep, currentPayload, agentRun, currentStep);
360
368
  case 'Chat':
361
- return this.validateChatNextStep(params, nextStep, currentPayload);
369
+ return this.validateChatNextStep(params, nextStep, currentPayload, agentRun, currentStep);
362
370
  case 'Retry':
363
- return this.validateRetryNextStep(params, nextStep, currentPayload);
371
+ return this.validateRetryNextStep(params, nextStep, currentPayload, agentRun, currentStep);
364
372
  case 'Failed':
365
- return this.validateFailedNextStep(params, nextStep, currentPayload);
373
+ return this.validateFailedNextStep(params, nextStep, currentPayload, agentRun, currentStep);
366
374
  default:
367
375
  this.logError(`Invalid next step '${nextStep.step}' for agent '${params.agent.Name}'`, {
368
376
  agent: params.agent,
@@ -375,23 +383,49 @@ class BaseAgent {
375
383
  };
376
384
  }
377
385
  }
378
- async validateSubAgentNextStep(params, nextStep, currentPayload) {
386
+ async validateSubAgentNextStep(params, nextStep, currentPayload, agentRun, currentStep) {
379
387
  const name = nextStep.subAgent?.name;
380
- const curAgentSubAgents = aiengine_1.AIEngine.Instance.Agents.filter(a => a.ParentID === params.agent.ID && a.Status === 'Active');
381
- if (!name || !curAgentSubAgents.some(a => a.Name.trim().toLowerCase() === name?.trim().toLowerCase())) {
388
+ const curAgentSubAgents = aiengine_1.AIEngine.Instance.GetSubAgents(params.agent.ID, 'Active');
389
+ const subAgent = curAgentSubAgents.find(a => a.Name.trim().toLowerCase() === name?.trim().toLowerCase());
390
+ if (!name || !subAgent) {
382
391
  this.logError(`Sub-agent '${name}' not found or not active for agent '${params.agent.Name}'`, {
383
392
  agent: params.agent,
384
393
  category: 'SubAgentExecution'
385
394
  });
395
+ if (nextStep.step !== 'Retry') {
396
+ this._generalValidationRetryCount++;
397
+ }
386
398
  return {
387
399
  step: 'Retry',
388
400
  terminate: false,
389
401
  errorMessage: `Sub-agent '${name}' not found or not active`
390
402
  };
391
403
  }
404
+ if (subAgent.MaxExecutionsPerRun != null) {
405
+ const executionCount = await this.getSubAgentExecutionCount(agentRun.ID, subAgent.ID);
406
+ if (executionCount >= subAgent.MaxExecutionsPerRun) {
407
+ this.logError(`Sub-agent '${name}' has reached its maximum execution limit of ${subAgent.MaxExecutionsPerRun}`, {
408
+ agent: params.agent,
409
+ category: 'SubAgentExecution',
410
+ metadata: {
411
+ subAgentName: name,
412
+ executionCount,
413
+ maxExecutions: subAgent.MaxExecutionsPerRun
414
+ }
415
+ });
416
+ if (nextStep.step !== 'Retry') {
417
+ this._generalValidationRetryCount++;
418
+ }
419
+ return {
420
+ step: 'Retry',
421
+ terminate: false,
422
+ errorMessage: `Sub-agent '${name}' has reached its maximum execution limit of ${subAgent.MaxExecutionsPerRun}`
423
+ };
424
+ }
425
+ }
392
426
  return nextStep;
393
427
  }
394
- async validateActionsNextStep(params, nextStep, currentPayload) {
428
+ async validateActionsNextStep(params, nextStep, currentPayload, agentRun, currentStep) {
395
429
  const curAgentActions = aiengine_1.AIEngine.Instance.AgentActions.filter(aa => aa.AgentID === params.agent.ID && aa.Status === 'Active');
396
430
  const missingActions = nextStep.actions?.filter(action => !curAgentActions.some(aa => aa.Action.trim().toLowerCase() === action.name.trim().toLowerCase()));
397
431
  if (missingActions && missingActions.length > 0) {
@@ -400,31 +434,312 @@ class BaseAgent {
400
434
  agent: params.agent,
401
435
  category: 'ActionExecution'
402
436
  });
437
+ if (nextStep.step !== 'Retry') {
438
+ this._generalValidationRetryCount++;
439
+ }
403
440
  return {
404
441
  step: 'Retry',
405
442
  terminate: false,
406
443
  errorMessage: `Actions '${missingActionNames}' not found or not active`
407
444
  };
408
445
  }
446
+ if (nextStep.actions) {
447
+ const violatedActions = [];
448
+ for (const action of nextStep.actions) {
449
+ const agentAction = curAgentActions.find(aa => aa.Action.trim().toLowerCase() === action.name.trim().toLowerCase());
450
+ if (agentAction && agentAction.MaxExecutionsPerRun != null) {
451
+ const executionCount = await this.getActionExecutionCount(agentRun.ID, agentAction.ActionID);
452
+ if (executionCount >= agentAction.MaxExecutionsPerRun) {
453
+ violatedActions.push(`${action.name} (limit: ${agentAction.MaxExecutionsPerRun}, current: ${executionCount})`);
454
+ }
455
+ }
456
+ }
457
+ if (violatedActions.length > 0) {
458
+ const violationMessage = `Actions have reached execution limits: ${violatedActions.join(', ')}`;
459
+ this.logError(violationMessage, {
460
+ agent: params.agent,
461
+ category: 'ActionExecution',
462
+ metadata: {
463
+ violatedActions
464
+ }
465
+ });
466
+ if (nextStep.step !== 'Retry') {
467
+ this._generalValidationRetryCount++;
468
+ }
469
+ return {
470
+ step: 'Retry',
471
+ terminate: false,
472
+ errorMessage: violationMessage
473
+ };
474
+ }
475
+ }
409
476
  return nextStep;
410
477
  }
411
- async validateSuccessNextStep(params, nextStep, currentPayload) {
478
+ async validateSuccessNextStep(params, nextStep, currentPayload, agentRun, currentStep) {
479
+ const minViolations = await this.checkMinimumExecutionRequirements(params.agent, agentRun);
480
+ if (minViolations.length > 0) {
481
+ const violationMessage = `Minimum execution requirements not met:\n${minViolations.join('\n')}`;
482
+ this.logError(violationMessage, {
483
+ agent: params.agent,
484
+ category: 'MinimumExecutionValidation'
485
+ });
486
+ if (nextStep.step !== 'Retry') {
487
+ this._generalValidationRetryCount++;
488
+ }
489
+ return {
490
+ step: 'Retry',
491
+ terminate: false,
492
+ errorMessage: violationMessage
493
+ };
494
+ }
495
+ const agent = params.agent;
496
+ if (!agent.FinalPayloadValidation || !currentPayload) {
497
+ return nextStep;
498
+ }
499
+ try {
500
+ let validationSchema;
501
+ try {
502
+ validationSchema = JSON.parse(agent.FinalPayloadValidation);
503
+ }
504
+ catch (parseError) {
505
+ this.logError(`Invalid FinalPayloadValidation JSON for agent ${agent.Name}: ${parseError.message}`, {
506
+ category: 'PayloadValidation',
507
+ metadata: {
508
+ agentName: agent.Name,
509
+ agentId: agent.ID,
510
+ validationSchema: agent.FinalPayloadValidation
511
+ }
512
+ });
513
+ return nextStep;
514
+ }
515
+ let payloadToValidate = currentPayload;
516
+ if (agent.PayloadScope) {
517
+ payloadToValidate = this._payloadManager.applyPayloadScope(currentPayload, agent.PayloadScope);
518
+ if (payloadToValidate === null) {
519
+ const errorMessage = `PayloadScope '${agent.PayloadScope}' not found in payload`;
520
+ return this.handleFinalPayloadValidationFailure(params, nextStep, currentPayload, agent.FinalPayloadValidationMode || 'Retry', [errorMessage], agentRun, currentStep);
521
+ }
522
+ }
523
+ const jsonValidator = new global_1.JSONValidator();
524
+ const validationResult = jsonValidator.validate(payloadToValidate, validationSchema);
525
+ if (!validationResult.Success) {
526
+ const mode = agent.FinalPayloadValidationMode || 'Retry';
527
+ const errorMessages = validationResult.Errors.map(e => e.Message);
528
+ this.logStatus(`⚠️ Final payload validation failed for agent ${agent.Name} (mode: ${mode}):`, true, params);
529
+ errorMessages.forEach((error, index) => {
530
+ this.logStatus(` ${index + 1}. ${error}`, true, params);
531
+ });
532
+ return this.handleFinalPayloadValidationFailure(params, nextStep, currentPayload, mode, errorMessages, agentRun, currentStep);
533
+ }
534
+ this.logStatus(`✅ Final payload validation passed for agent ${agent.Name}`, true, params);
535
+ try {
536
+ currentStep.FinalPayloadValidationResult = 'Pass';
537
+ currentStep.FinalPayloadValidationMessages = null;
538
+ await currentStep.Save();
539
+ }
540
+ catch (error) {
541
+ this.logError(`Failed to save validation success result: ${error.message}`, {
542
+ category: 'PayloadValidation',
543
+ metadata: { stepId: currentStep.ID }
544
+ });
545
+ }
546
+ return nextStep;
547
+ }
548
+ catch (error) {
549
+ this.logError(`Unexpected error during final payload validation: ${error.message}`, {
550
+ category: 'PayloadValidation',
551
+ metadata: {
552
+ agentName: agent.Name,
553
+ agentId: agent.ID,
554
+ error: error.message
555
+ }
556
+ });
557
+ return nextStep;
558
+ }
559
+ }
560
+ async handleFinalPayloadValidationFailure(params, nextStep, currentPayload, mode, errorMessages, agentRun, currentStep) {
561
+ const validationFeedback = `Final payload validation failed:\n${errorMessages.map((e, i) => `${i + 1}. ${e}`).join('\n')}`;
562
+ try {
563
+ currentStep.FinalPayloadValidationMessages = errorMessages.join('; ');
564
+ switch (mode) {
565
+ case 'Retry':
566
+ this._validationRetryCount++;
567
+ const maxRetries = params.agent.FinalPayloadValidationMaxRetries || 3;
568
+ if (this._validationRetryCount >= maxRetries) {
569
+ this.logStatus(`❌ Max validation retries (${maxRetries}) exceeded, forcing failure`, false, params);
570
+ currentStep.FinalPayloadValidationResult = 'Fail';
571
+ await currentStep.Save();
572
+ return {
573
+ ...nextStep,
574
+ step: 'Failed',
575
+ message: `${validationFeedback}\n\nMax validation retries (${maxRetries}) exceeded.`,
576
+ terminate: true
577
+ };
578
+ }
579
+ currentStep.FinalPayloadValidationResult = 'Retry';
580
+ await currentStep.Save();
581
+ this.logStatus(`🔄 Validation retry ${this._validationRetryCount}/${maxRetries}`, true, params);
582
+ return {
583
+ ...nextStep,
584
+ step: 'Retry',
585
+ message: `${validationFeedback}\n\nRetry attempt ${this._validationRetryCount} of ${maxRetries}`,
586
+ terminate: false
587
+ };
588
+ case 'Fail':
589
+ currentStep.FinalPayloadValidationResult = 'Fail';
590
+ await currentStep.Save();
591
+ return {
592
+ ...nextStep,
593
+ step: 'Failed',
594
+ message: validationFeedback,
595
+ terminate: true
596
+ };
597
+ case 'Warn':
598
+ this.logStatus(`⚠️ WARNING: ${validationFeedback}`, false, params);
599
+ currentStep.FinalPayloadValidationResult = 'Warn';
600
+ await currentStep.Save();
601
+ return nextStep;
602
+ default:
603
+ this._validationRetryCount++;
604
+ currentStep.FinalPayloadValidationResult = 'Retry';
605
+ await currentStep.Save();
606
+ return {
607
+ ...nextStep,
608
+ step: 'Retry',
609
+ message: validationFeedback,
610
+ terminate: false
611
+ };
612
+ }
613
+ }
614
+ catch (error) {
615
+ this.logError(`Failed to save validation results: ${error.message}`, {
616
+ category: 'PayloadValidation',
617
+ metadata: { stepId: currentStep.ID }
618
+ });
619
+ return mode === 'Warn' ? nextStep : {
620
+ ...nextStep,
621
+ step: mode === 'Fail' ? 'Failed' : 'Retry',
622
+ message: validationFeedback,
623
+ terminate: mode === 'Fail'
624
+ };
625
+ }
626
+ }
627
+ async validateFailedNextStep(params, nextStep, currentPayload, agentRun, currentStep) {
412
628
  return nextStep;
413
629
  }
414
- async validateFailedNextStep(params, nextStep, currentPayload) {
630
+ async validateRetryNextStep(params, nextStep, currentPayload, agentRun, currentStep) {
415
631
  return nextStep;
416
632
  }
417
- async validateRetryNextStep(params, nextStep, currentPayload) {
633
+ async validateChatNextStep(params, nextStep, currentPayload, agentRun, currentStep) {
418
634
  return nextStep;
419
635
  }
420
- async validateChatNextStep(params, nextStep, currentPayload) {
636
+ async checkExecutionGuardrails(params, nextStep, currentPayload, agentRun, currentStep) {
637
+ if (nextStep.step === 'Success' || nextStep.step === 'Failed' || nextStep.step === 'Chat') {
638
+ return nextStep;
639
+ }
640
+ const guardrailResult = await this.hasExceededAgentRunGuardrails(params, agentRun);
641
+ if (guardrailResult.exceeded) {
642
+ this.logStatus(`⛔ Execution guardrail exceeded: ${guardrailResult.reason}`, false, params);
643
+ try {
644
+ const outputData = currentStep.OutputData ? JSON.parse(currentStep.OutputData) : {};
645
+ currentStep.OutputData = JSON.stringify({
646
+ ...outputData,
647
+ guardrailExceeded: {
648
+ type: guardrailResult.type,
649
+ limit: guardrailResult.limit,
650
+ current: guardrailResult.current,
651
+ reason: guardrailResult.reason,
652
+ timestamp: new Date().toISOString()
653
+ }
654
+ });
655
+ await currentStep.Save();
656
+ }
657
+ catch (error) {
658
+ this.logError(`Failed to save guardrail violation to step: ${error.message}`, {
659
+ category: 'Guardrails',
660
+ metadata: { stepId: currentStep.ID }
661
+ });
662
+ }
663
+ return {
664
+ ...nextStep,
665
+ step: 'Failed',
666
+ terminate: true,
667
+ message: guardrailResult.reason,
668
+ errorMessage: guardrailResult.reason
669
+ };
670
+ }
421
671
  return nextStep;
422
672
  }
423
- async processNextStep(params, agentType, promptResult, currentPayload) {
673
+ async hasExceededAgentRunGuardrails(params, agentRun) {
674
+ const agent = params.agent;
675
+ if (agent.MaxCostPerRun && agentRun.TotalCost) {
676
+ if (agentRun.TotalCost >= agent.MaxCostPerRun) {
677
+ return {
678
+ exceeded: true,
679
+ type: 'cost',
680
+ limit: agent.MaxCostPerRun,
681
+ current: agentRun.TotalCost,
682
+ reason: `Maximum cost limit of $${agent.MaxCostPerRun} exceeded. Current cost: $${agentRun.TotalCost.toFixed(4)}`
683
+ };
684
+ }
685
+ }
686
+ if (agent.MaxTokensPerRun && agentRun.TotalTokensUsed) {
687
+ if (agentRun.TotalTokensUsed >= agent.MaxTokensPerRun) {
688
+ return {
689
+ exceeded: true,
690
+ type: 'tokens',
691
+ limit: agent.MaxTokensPerRun,
692
+ current: agentRun.TotalTokensUsed,
693
+ reason: `Maximum token limit of ${agent.MaxTokensPerRun} exceeded. Current tokens: ${agentRun.TotalTokensUsed}`
694
+ };
695
+ }
696
+ }
697
+ if (agent.MaxIterationsPerRun && agentRun.TotalPromptIterations) {
698
+ if (agentRun.TotalPromptIterations >= agent.MaxIterationsPerRun) {
699
+ return {
700
+ exceeded: true,
701
+ type: 'iterations',
702
+ limit: agent.MaxIterationsPerRun,
703
+ current: agentRun.TotalPromptIterations,
704
+ reason: `Maximum iteration limit of ${agent.MaxIterationsPerRun} exceeded. Current iterations: ${agentRun.TotalPromptIterations}`
705
+ };
706
+ }
707
+ }
708
+ if (agent.MaxTimePerRun && agentRun.StartedAt) {
709
+ const elapsedSeconds = Math.floor((Date.now() - new Date(agentRun.StartedAt).getTime()) / 1000);
710
+ if (elapsedSeconds >= agent.MaxTimePerRun) {
711
+ return {
712
+ exceeded: true,
713
+ type: 'time',
714
+ limit: agent.MaxTimePerRun,
715
+ current: elapsedSeconds,
716
+ reason: `Maximum time limit of ${agent.MaxTimePerRun} seconds exceeded. Elapsed time: ${elapsedSeconds} seconds`
717
+ };
718
+ }
719
+ }
720
+ if (this._generalValidationRetryCount >= BaseAgent.MAX_VALIDATION_RETRIES) {
721
+ return {
722
+ exceeded: true,
723
+ type: 'iterations',
724
+ limit: BaseAgent.MAX_VALIDATION_RETRIES,
725
+ current: this._generalValidationRetryCount,
726
+ reason: `Maximum validation retries of ${BaseAgent.MAX_VALIDATION_RETRIES} exceeded. The agent is unable to produce valid output after ${this._generalValidationRetryCount} validation failures.`
727
+ };
728
+ }
729
+ return { exceeded: false };
730
+ }
731
+ async processNextStep(params, agentType, promptResult, currentPayload, currentStep) {
424
732
  const nextStep = await this.determineNextStep(params, agentType, promptResult, currentPayload);
425
- const validatedNextStep = await this.validateNextStep(params, nextStep, currentPayload);
426
- this.logStatus(`📌 Next step determined: ${validatedNextStep.step}${validatedNextStep.terminate ? ' (terminating)' : ''}`, true, params);
427
- return validatedNextStep;
733
+ const validatedNextStep = await this.validateNextStep(params, nextStep, currentPayload, this._agentRun, currentStep);
734
+ const guardrailCheckedStep = await this.checkExecutionGuardrails(params, validatedNextStep, currentPayload, this._agentRun, currentStep);
735
+ this.logStatus(`📌 Next step determined: ${guardrailCheckedStep.step}${guardrailCheckedStep.terminate ? ' (terminating)' : ''}`, true, params);
736
+ if (guardrailCheckedStep.step === 'Retry' && (guardrailCheckedStep.message || guardrailCheckedStep.errorMessage || guardrailCheckedStep.retryInstructions)) {
737
+ params.conversationMessages.push({
738
+ role: 'user',
739
+ content: `Retrying due to: ${guardrailCheckedStep.retryInstructions || guardrailCheckedStep.message || guardrailCheckedStep.errorMessage}`
740
+ });
741
+ }
742
+ return guardrailCheckedStep;
428
743
  }
429
744
  createActionResultMessage(actions, results) {
430
745
  const resultSummary = actions.map((action, index) => {
@@ -913,6 +1228,9 @@ class BaseAgent {
913
1228
  stepEntity.TargetLogID = promptResult.promptRun.ID;
914
1229
  stepEntity.PromptRun = promptResult.promptRun;
915
1230
  }
1231
+ if (this._agentRun) {
1232
+ this._agentRun.TotalPromptIterations = (this._agentRun.TotalPromptIterations || 0) + 1;
1233
+ }
916
1234
  if (params.cancellationToken?.aborted) {
917
1235
  await this.finalizeStepEntity(stepEntity, false, 'Cancelled during prompt execution');
918
1236
  const cancelledResult = await this.createCancelledResult('Cancelled during prompt execution', params.contextUser);
@@ -930,7 +1248,7 @@ class BaseAgent {
930
1248
  message: this.formatHierarchicalMessage('Analyzing response and determining next steps'),
931
1249
  displayMode: 'both'
932
1250
  });
933
- const nextStep = await this.processNextStep(params, config.agentType, promptResult, payload);
1251
+ const nextStep = await this.processNextStep(params, config.agentType, promptResult, payload, stepEntity);
934
1252
  let finalPayload = payload;
935
1253
  let currentStepPayloadChangeResult = undefined;
936
1254
  if (nextStep.payloadChangeRequest) {
@@ -1023,6 +1341,7 @@ class BaseAgent {
1023
1341
  throw new Error(`Sub-agent '${subAgentRequest.name}' not found`);
1024
1342
  }
1025
1343
  const stepEntity = await this.createStepEntity('Sub-Agent', `Execute Sub-Agent: ${subAgentRequest.name}`, params.contextUser, subAgentEntity.ID, inputData);
1344
+ this.incrementExecutionCount(subAgentEntity.ID);
1026
1345
  try {
1027
1346
  let downstreamPaths = ['*'];
1028
1347
  let upstreamPaths = ['*'];
@@ -1046,10 +1365,31 @@ class BaseAgent {
1046
1365
  }
1047
1366
  });
1048
1367
  }
1049
- const downstreamPayload = this._payloadManager.extractDownstreamPayload(subAgentRequest.name, previousDecision.newPayload, downstreamPaths);
1368
+ let downstreamPayload = this._payloadManager.extractDownstreamPayload(subAgentRequest.name, previousDecision.newPayload, downstreamPaths);
1369
+ let scopedPayload = downstreamPayload;
1370
+ if (subAgentEntity.PayloadScope) {
1371
+ scopedPayload = this._payloadManager.applyPayloadScope(downstreamPayload, subAgentEntity.PayloadScope);
1372
+ if (scopedPayload === null) {
1373
+ const errorMessage = `Critical: Failed to extract payload scope '${subAgentEntity.PayloadScope}' for sub-agent '${subAgentRequest.name}'. The specified path does not exist in the payload.`;
1374
+ this.logError(errorMessage, {
1375
+ category: 'SubAgentExecution',
1376
+ metadata: {
1377
+ agentName: params.agent.Name,
1378
+ subAgentName: subAgentRequest.name,
1379
+ payloadScope: subAgentEntity.PayloadScope,
1380
+ availableKeys: Object.keys(downstreamPayload || {})
1381
+ }
1382
+ });
1383
+ throw new Error(errorMessage);
1384
+ }
1385
+ }
1050
1386
  stepEntity.PayloadAtStart = JSON.stringify(previousDecision.newPayload);
1051
- const subAgentResult = await this.ExecuteSubAgent(params, subAgentRequest, subAgentEntity, stepEntity, downstreamPayload);
1052
- const mergedPayload = this._payloadManager.mergeUpstreamPayload(subAgentRequest.name, previousDecision.newPayload, subAgentResult.payload, upstreamPaths);
1387
+ const subAgentResult = await this.ExecuteSubAgent(params, subAgentRequest, subAgentEntity, stepEntity, scopedPayload);
1388
+ let resultPayloadForMerge = subAgentResult.payload;
1389
+ if (subAgentEntity.PayloadScope) {
1390
+ resultPayloadForMerge = this._payloadManager.reversePayloadScope(subAgentResult.payload, subAgentEntity.PayloadScope);
1391
+ }
1392
+ const mergedPayload = this._payloadManager.mergeUpstreamPayload(subAgentRequest.name, previousDecision.newPayload, resultPayloadForMerge, upstreamPaths);
1053
1393
  const mergeChangeRequest = {
1054
1394
  newElements: {},
1055
1395
  updateElements: {},
@@ -1216,6 +1556,7 @@ class BaseAgent {
1216
1556
  }
1217
1557
  const stepEntity = await this.createStepEntity('Actions', `Execute Action: ${aa.name}`, params.contextUser, actionEntity.ID);
1218
1558
  stepEntity.StepNumber = baseStepNumber + numActionsProcessed++;
1559
+ this.incrementExecutionCount(actionEntity.ID);
1219
1560
  let actionResult;
1220
1561
  try {
1221
1562
  actionResult = await this.ExecuteSingleAction(params, aa, actionEntity, params.contextUser);
@@ -1377,6 +1718,42 @@ class BaseAgent {
1377
1718
  }
1378
1719
  return { totalTokens, promptTokens, completionTokens, totalCost };
1379
1720
  }
1721
+ async getActionExecutionCount(agentRunId, actionId) {
1722
+ return this.getExecutionCount(actionId);
1723
+ }
1724
+ async getSubAgentExecutionCount(agentRunId, subAgentId) {
1725
+ return this.getExecutionCount(subAgentId);
1726
+ }
1727
+ incrementExecutionCount(itemId) {
1728
+ const currentCount = this._executionCounts.get(itemId) || 0;
1729
+ this._executionCounts.set(itemId, currentCount + 1);
1730
+ }
1731
+ getExecutionCount(itemId) {
1732
+ return this._executionCounts.get(itemId) || 0;
1733
+ }
1734
+ async checkMinimumExecutionRequirements(agent, agentRun) {
1735
+ const violations = [];
1736
+ const agentActions = aiengine_1.AIEngine.Instance.AgentActions.filter(aa => aa.AgentID === agent.ID &&
1737
+ aa.Status === 'Active' &&
1738
+ aa.MinExecutionsPerRun != null &&
1739
+ aa.MinExecutionsPerRun > 0);
1740
+ for (const agentAction of agentActions) {
1741
+ const executionCount = await this.getActionExecutionCount(agentRun.ID, agentAction.ActionID);
1742
+ if (executionCount < agentAction.MinExecutionsPerRun) {
1743
+ violations.push(`Action '${agentAction.Action}' requires ${agentAction.MinExecutionsPerRun} execution(s) but was executed ${executionCount} time(s)`);
1744
+ }
1745
+ }
1746
+ const subAgents = aiengine_1.AIEngine.Instance.GetSubAgents(agent.ID, "Active").filter(a => a.MinExecutionsPerRun != null &&
1747
+ a.MinExecutionsPerRun > 0);
1748
+ for (const subAgent of subAgents) {
1749
+ const executionCount = await this.getSubAgentExecutionCount(agentRun.ID, subAgent.ID);
1750
+ if (executionCount < subAgent.MinExecutionsPerRun) {
1751
+ violations.push(`Sub-agent '${subAgent.Name}' requires ${subAgent.MinExecutionsPerRun} execution(s) but was executed ${executionCount} time(s)`);
1752
+ }
1753
+ }
1754
+ return violations;
1755
+ }
1380
1756
  }
1381
1757
  exports.BaseAgent = BaseAgent;
1758
+ BaseAgent.MAX_VALIDATION_RETRIES = 10;
1382
1759
  //# sourceMappingURL=base-agent.js.map