@memberjunction/ai-agents 5.28.0 → 5.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/AgentRunner.d.ts +39 -0
  2. package/dist/AgentRunner.d.ts.map +1 -1
  3. package/dist/AgentRunner.js +301 -6
  4. package/dist/AgentRunner.js.map +1 -1
  5. package/dist/ArtifactToolManager.d.ts +165 -0
  6. package/dist/ArtifactToolManager.d.ts.map +1 -0
  7. package/dist/ArtifactToolManager.js +534 -0
  8. package/dist/ArtifactToolManager.js.map +1 -0
  9. package/dist/agent-types/loop-agent-prompt-params.d.ts +14 -0
  10. package/dist/agent-types/loop-agent-prompt-params.d.ts.map +1 -1
  11. package/dist/agent-types/loop-agent-prompt-params.js +3 -1
  12. package/dist/agent-types/loop-agent-prompt-params.js.map +1 -1
  13. package/dist/agent-types/loop-agent-response-type.d.ts +9 -1
  14. package/dist/agent-types/loop-agent-response-type.d.ts.map +1 -1
  15. package/dist/agent-types/loop-agent-response-type.js.map +1 -1
  16. package/dist/agent-types/loop-agent-type.d.ts.map +1 -1
  17. package/dist/agent-types/loop-agent-type.js +3 -0
  18. package/dist/agent-types/loop-agent-type.js.map +1 -1
  19. package/dist/artifact-tools/DataSnapshotToolLibrary.d.ts +18 -0
  20. package/dist/artifact-tools/DataSnapshotToolLibrary.d.ts.map +1 -0
  21. package/dist/artifact-tools/DataSnapshotToolLibrary.js +365 -0
  22. package/dist/artifact-tools/DataSnapshotToolLibrary.js.map +1 -0
  23. package/dist/artifact-tools/DocxToolLibrary.d.ts +15 -0
  24. package/dist/artifact-tools/DocxToolLibrary.d.ts.map +1 -0
  25. package/dist/artifact-tools/DocxToolLibrary.js +160 -0
  26. package/dist/artifact-tools/DocxToolLibrary.js.map +1 -0
  27. package/dist/artifact-tools/ExcelToolLibrary.d.ts +16 -0
  28. package/dist/artifact-tools/ExcelToolLibrary.d.ts.map +1 -0
  29. package/dist/artifact-tools/ExcelToolLibrary.js +300 -0
  30. package/dist/artifact-tools/ExcelToolLibrary.js.map +1 -0
  31. package/dist/artifact-tools/JSONToolLibrary.d.ts +18 -0
  32. package/dist/artifact-tools/JSONToolLibrary.d.ts.map +1 -0
  33. package/dist/artifact-tools/JSONToolLibrary.js +178 -0
  34. package/dist/artifact-tools/JSONToolLibrary.js.map +1 -0
  35. package/dist/artifact-tools/PDFToolLibrary.d.ts +14 -0
  36. package/dist/artifact-tools/PDFToolLibrary.d.ts.map +1 -0
  37. package/dist/artifact-tools/PDFToolLibrary.js +172 -0
  38. package/dist/artifact-tools/PDFToolLibrary.js.map +1 -0
  39. package/dist/artifact-tools/TextToolLibrary.d.ts +12 -0
  40. package/dist/artifact-tools/TextToolLibrary.d.ts.map +1 -0
  41. package/dist/artifact-tools/TextToolLibrary.js +82 -0
  42. package/dist/artifact-tools/TextToolLibrary.js.map +1 -0
  43. package/dist/base-agent.d.ts +12 -0
  44. package/dist/base-agent.d.ts.map +1 -1
  45. package/dist/base-agent.js +155 -9
  46. package/dist/base-agent.js.map +1 -1
  47. package/dist/file-input-resolver.d.ts +2 -0
  48. package/dist/file-input-resolver.d.ts.map +1 -0
  49. package/dist/file-input-resolver.js +4 -0
  50. package/dist/file-input-resolver.js.map +1 -0
  51. package/dist/index.d.ts +9 -1
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +9 -1
  54. package/dist/index.js.map +1 -1
  55. package/dist/memory-cleanup-agent.d.ts +10 -3
  56. package/dist/memory-cleanup-agent.d.ts.map +1 -1
  57. package/dist/memory-cleanup-agent.js +10 -3
  58. package/dist/memory-cleanup-agent.js.map +1 -1
  59. package/dist/memory-manager-agent.d.ts +341 -7
  60. package/dist/memory-manager-agent.d.ts.map +1 -1
  61. package/dist/memory-manager-agent.js +1723 -373
  62. package/dist/memory-manager-agent.js.map +1 -1
  63. package/package.json +18 -15
@@ -24,6 +24,7 @@ import { AIPromptParams, ChildPromptParam, ConversationUtility, ParseFileOutputR
24
24
  import { AgentRunner } from './AgentRunner.js';
25
25
  import { PayloadManager } from './PayloadManager.js';
26
26
  import { ScratchpadManager } from './ScratchpadManager.js';
27
+ import { ArtifactToolManager } from './ArtifactToolManager.js';
27
28
  import { AgentDataPreloader } from './AgentDataPreloader.js';
28
29
  import { ClientToolRequestManager } from './ClientToolRequestManager.js';
29
30
  import { ConversationMessageResolver } from './utils/ConversationMessageResolver.js';
@@ -184,6 +185,11 @@ export class BaseAgent {
184
185
  * @since 2.46.0
185
186
  */
186
187
  this._scratchpadManager = new ScratchpadManager();
188
+ /**
189
+ * Manages artifact tools for the current agent run.
190
+ * Allows agents to explore input artifacts on demand.
191
+ */
192
+ this._artifactToolManager = new ArtifactToolManager();
187
193
  /**
188
194
  * Effective actions available to this agent after applying actionChanges.
189
195
  * Populated during gatherPromptTemplateData() and used for validation in executeActionsStep().
@@ -863,10 +869,59 @@ export class BaseAgent {
863
869
  * });
864
870
  * ```
865
871
  */
872
+ /**
873
+ * Engine-default wall-clock timeout applied to any agent run whose
874
+ * `ExecuteAgentParams.maxExecutionTimeMs` is not set. Sub-classes can
875
+ * override to globally change the default. Intentionally generous
876
+ * (2 hours) — tighten per-run for interactive scenarios.
877
+ */
878
+ get DefaultAgentTimeoutMS() {
879
+ return 2 * 60 * 60 * 1000;
880
+ }
866
881
  async Execute(params) {
867
882
  // Capture per-request provider for the duration of this execution so all entity
868
883
  // saves go through the isolated provider, never the global singleton's transaction.
869
884
  this._activeProvider = params.provider || Metadata.Provider;
885
+ // =====================================================================================
886
+ // UNIVERSAL WALL-CLOCK TIMEOUT
887
+ //
888
+ // We chain any caller-supplied `cancellationToken` with an internal
889
+ // AbortController that fires after `maxExecutionTimeMs` (falling back
890
+ // to `DefaultAgentTimeoutMS`). The chained signal replaces
891
+ // `params.cancellationToken` for the duration of the run, so every
892
+ // existing cancellation check in the body of Execute sees the merged
893
+ // abort condition — whether it came from the caller, the timeout, or
894
+ // both.
895
+ //
896
+ // Actions invoked from this agent carry their own AbortSignal on
897
+ // `RunActionParams.AbortSignal` (see ActionEngine.RunAction) and are
898
+ // unaffected by this wrapper — their timeout budget is independent.
899
+ // =====================================================================================
900
+ const agentTimeoutMS = params.maxExecutionTimeMs ?? this.DefaultAgentTimeoutMS;
901
+ const upstreamToken = params.cancellationToken;
902
+ const timeoutController = new AbortController();
903
+ const relayUpstreamAbort = () => {
904
+ if (!timeoutController.signal.aborted) {
905
+ timeoutController.abort(upstreamToken?.reason ?? 'upstream cancellation');
906
+ }
907
+ };
908
+ if (upstreamToken) {
909
+ if (upstreamToken.aborted) {
910
+ relayUpstreamAbort();
911
+ }
912
+ else {
913
+ upstreamToken.addEventListener('abort', relayUpstreamAbort, { once: true });
914
+ }
915
+ }
916
+ const timeoutId = setTimeout(() => {
917
+ if (!timeoutController.signal.aborted) {
918
+ timeoutController.abort(`Agent '${params.agent.Name}' exceeded maxExecutionTimeMs (${agentTimeoutMS}ms)`);
919
+ }
920
+ }, agentTimeoutMS);
921
+ // Route the merged signal back through `params` so the existing body of
922
+ // Execute (and downstream sub-agent invocations that propagate
923
+ // `cancellationToken`) observe it.
924
+ params.cancellationToken = timeoutController.signal;
870
925
  try {
871
926
  this.logStatus(`🤖 Starting execution of agent '${params.agent.Name}'`, true, params);
872
927
  // =====================================================================================
@@ -908,8 +963,18 @@ export class BaseAgent {
908
963
  if (params.convertUIMarkupToPlainText !== false) {
909
964
  this.convertUIMarkupInMessages(wrappedParams.conversationMessages);
910
965
  }
911
- // Reset scratchpad for each new execution (ephemeral per run)
966
+ // Reset scratchpad and artifact tools for each new execution (ephemeral per run)
912
967
  this._scratchpadManager.Clear();
968
+ this._artifactToolManager.Clear();
969
+ // Initialize artifact tools with any input artifacts from the conversation
970
+ const inputArtifacts = wrappedParams.data?.__inputArtifacts;
971
+ if (inputArtifacts?.length) {
972
+ this._artifactToolManager.Initialize(inputArtifacts);
973
+ this.logStatus(`[ArtifactTools] Initialized with ${inputArtifacts.length} artifact(s): ${inputArtifacts.map(a => `${a.typeName}:"${a.name}"`).join(', ')}`, true, params);
974
+ }
975
+ else {
976
+ this.logStatus(`[ArtifactTools] No input artifacts found for this run`, true, params);
977
+ }
913
978
  // Initialize starting payload — must complete before AgentRun creation since the
914
979
  // run record stores the starting payload snapshot.
915
980
  await this.initializeStartingPayload(wrappedParams);
@@ -1065,8 +1130,11 @@ export class BaseAgent {
1065
1130
  catch (error) {
1066
1131
  // Check if error is due to cancellation
1067
1132
  if (params.cancellationToken?.aborted || error.message === 'Cancelled during execution') {
1068
- this.logStatus(`⚠️ Agent '${params.agent.Name}' execution cancelled: ${error.message}`, true, params);
1069
- return await this.createCancelledResult(error.message || 'Cancelled due to error during execution', params.contextUser);
1133
+ const reason = typeof timeoutController.signal.reason === 'string'
1134
+ ? timeoutController.signal.reason
1135
+ : error.message;
1136
+ this.logStatus(`⚠️ Agent '${params.agent.Name}' execution cancelled: ${reason}`, true, params);
1137
+ return await this.createCancelledResult(reason || 'Cancelled due to error during execution', params.contextUser);
1070
1138
  }
1071
1139
  this.logError(error, {
1072
1140
  agent: params.agent,
@@ -1075,6 +1143,18 @@ export class BaseAgent {
1075
1143
  });
1076
1144
  return await this.createFailureResult(error.message, params.contextUser);
1077
1145
  }
1146
+ finally {
1147
+ // Release timeout / upstream-abort listeners so we don't leak
1148
+ // handles when the run completes (success, failure, or cancel).
1149
+ clearTimeout(timeoutId);
1150
+ if (upstreamToken) {
1151
+ upstreamToken.removeEventListener('abort', relayUpstreamAbort);
1152
+ }
1153
+ // Restore the caller's original cancellationToken on `params` so
1154
+ // consumers that re-read `params` after the call see what they
1155
+ // passed in, not our chained signal.
1156
+ params.cancellationToken = upstreamToken;
1157
+ }
1078
1158
  }
1079
1159
  /**
1080
1160
  * Sub-classes can override this method to perform any specialized initialization
@@ -1636,6 +1716,26 @@ export class BaseAgent {
1636
1716
  promptParams.data['_SCRATCHPAD_TASKS'] = this._scratchpadManager.ToPromptString();
1637
1717
  promptParams.data['_SCRATCHPAD_TASK_SUMMARY'] = this._scratchpadManager.GetTaskSummary();
1638
1718
  }
1719
+ // Inject artifact tools template variables if enabled and artifacts are present
1720
+ const artifactToolsEnabled = agentTypePromptParams?.includeArtifactToolsDocs !== false;
1721
+ if (artifactToolsEnabled && this._artifactToolManager.HasArtifacts()) {
1722
+ promptParams.data['_ARTIFACT_MANIFEST'] = this._artifactToolManager.ToManifestString();
1723
+ promptParams.data['_ARTIFACT_TOOLS'] = this._artifactToolManager.GetToolDocumentation();
1724
+ promptParams.data['_ARTIFACT_TOOL_RESULTS'] = this._artifactToolManager.GetPendingResults();
1725
+ promptParams.data['_ARTIFACT_TOOL_SUMMARY'] = this._artifactToolManager.GetSummary();
1726
+ this.logStatus(`[ArtifactTools] Injected manifest into prompt: ${this._artifactToolManager.GetSummary()}`, true, params);
1727
+ }
1728
+ else if (this._artifactToolManager.HasArtifacts()) {
1729
+ this.logStatus(`[ArtifactTools] Artifacts present but tools disabled by agent config (includeArtifactToolsDocs=false)`, true, params);
1730
+ }
1731
+ // Pass file artifacts as candidate native file inputs.
1732
+ // The AIPromptRunner will check these against the resolved driver's
1733
+ // FileCapabilities and attach qualifying files as native content blocks.
1734
+ // When the driver doesn't support a file type, the runner falls back to
1735
+ // the pre-extracted TextContent on each candidate.
1736
+ if (this._artifactToolManager.HasArtifacts()) {
1737
+ promptParams.nativeFileInputs = await this._artifactToolManager.GetNativeFileInputCandidates();
1738
+ }
1639
1739
  }
1640
1740
  // Only set up child prompts if we have a system prompt
1641
1741
  if (systemPrompt) {
@@ -2403,14 +2503,36 @@ export class BaseAgent {
2403
2503
  * @protected
2404
2504
  */
2405
2505
  isConfigurationError(errorMessage, config) {
2506
+ // Extract the property name from the error up front — used by the
2507
+ // narrowed classifier below to decide whether this is a genuine config
2508
+ // issue or a generic runtime exception that should bubble up normally.
2509
+ const propertyMatch = errorMessage.match(/reading '(\w+)'/i);
2510
+ const accessedProperty = propertyMatch ? propertyMatch[1].toLowerCase() : '';
2511
+ // Only `.map/.x on undefined` errors that reference config-related
2512
+ // properties are treated as configuration errors. Generic runtime
2513
+ // errors (e.g. a tool handler crashing on `rows.map`) should not
2514
+ // terminate the run as "unrecoverable config issue" — they should
2515
+ // fail the step and let the agent try to recover.
2516
+ const CONFIG_RELATED_PROPERTIES = new Set([
2517
+ 'prompt', 'childprompt', 'systemprompt', 'prompts',
2518
+ 'agent', 'agents', 'agenttype', 'agenttypes',
2519
+ 'model', 'models', 'vendor', 'vendors',
2520
+ 'template', 'templates',
2521
+ ]);
2522
+ const isConfigRelatedProperty = accessedProperty !== ''
2523
+ && CONFIG_RELATED_PROPERTIES.has(accessedProperty);
2406
2524
  // Check for common configuration error patterns
2407
2525
  const configErrorPatterns = [
2408
2526
  {
2409
- pattern: /cannot read propert(y|ies) of (undefined|null)/i,
2527
+ // Only match when the accessed property is config-related.
2528
+ // Without this guard, any runtime `.map on undefined` (e.g.
2529
+ // in an artifact tool handler) gets misclassified as a fatal
2530
+ // configuration error and the agent run terminates.
2531
+ pattern: isConfigRelatedProperty
2532
+ ? /cannot read propert(y|ies) of (undefined|null)/i
2533
+ : /__NEVER_MATCH_GENERIC_UNDEFINED_ACCESS__/,
2410
2534
  getMessage: () => {
2411
- // Try to extract what property was being accessed
2412
- const propertyMatch = errorMessage.match(/reading '(\w+)'/i);
2413
- const property = propertyMatch ? propertyMatch[1] : 'unknown property';
2535
+ const property = accessedProperty || 'unknown property';
2414
2536
  let details = `Attempted to access property '${property}' on an undefined or null object.`;
2415
2537
  // Provide specific guidance based on the property name
2416
2538
  if (property.toLowerCase().includes('prompt')) {
@@ -3223,7 +3345,8 @@ The context is now within limits. Please retry your request with the recovered c
3223
3345
  { docsFlag: 'includeCommandDocs', responseTypeKey: 'commands' },
3224
3346
  { docsFlag: 'includeForEachDocs', responseTypeKey: 'forEach' },
3225
3347
  { docsFlag: 'includeWhileDocs', responseTypeKey: 'while' },
3226
- { docsFlag: 'includeScratchpadDocs', responseTypeKey: 'scratchpad' }
3348
+ { docsFlag: 'includeScratchpadDocs', responseTypeKey: 'scratchpad' },
3349
+ { docsFlag: 'includeArtifactToolsDocs', responseTypeKey: 'artifactToolCalls' }
3227
3350
  ];
3228
3351
  for (const { docsFlag, responseTypeKey } of alignmentMappings) {
3229
3352
  // Check if the user explicitly set this response type property
@@ -4080,7 +4203,8 @@ The context is now within limits. Please retry your request with the recovered c
4080
4203
  }
4081
4204
  this._agentRun.Status = 'Running';
4082
4205
  this._agentRun.StartedAt = new Date();
4083
- this._agentRun.UserID = params.contextUser?.ID || null;
4206
+ this._agentRun.UserID = params.userId || params.contextUser?.ID || null;
4207
+ this._agentRun.CompanyID = params.companyId || null;
4084
4208
  // Resolve and save the effort level used (same precedence hierarchy as prompts)
4085
4209
  if (params.effortLevel !== undefined && params.effortLevel !== null) {
4086
4210
  this._agentRun.EffortLevel = params.effortLevel;
@@ -4780,6 +4904,16 @@ The context is now within limits. Please retry your request with the recovered c
4780
4904
  LogStatus(`Scratchpad: pruned ${pruned} completed tasks (limit: ${maxTasks})`);
4781
4905
  }
4782
4906
  }
4907
+ // Execute artifact tool calls if provided (zero turn cost — processed inline)
4908
+ const artifactToolCalls = initialNextStep.artifactToolCalls;
4909
+ const artifactToolsExecutedThisTurn = !!(artifactToolCalls?.length);
4910
+ if (artifactToolsExecutedThisTurn) {
4911
+ this.logStatus(`[ArtifactTools] LLM requested ${artifactToolCalls.length} tool call(s): ${artifactToolCalls.map(c => `${c.artifactId}.${c.tool}`).join(', ')}`, true, params);
4912
+ await this._artifactToolManager.ExecuteToolCalls(artifactToolCalls);
4913
+ }
4914
+ else if (this._artifactToolManager.HasArtifacts()) {
4915
+ this.logStatus(`[ArtifactTools] LLM did not use artifact tools this turn (artifacts available but not accessed)`, true, params);
4916
+ }
4783
4917
  // now that we have processed the payload, we can process the next step which does validation and changes the next step if
4784
4918
  // validation fails
4785
4919
  const updatedNextStep = await this.processNextStep(initialNextStep, params, config.agentType, promptResult, finalPayload, stepEntity);
@@ -4802,6 +4936,10 @@ The context is now within limits. Please retry your request with the recovered c
4802
4936
  ...(this._scratchpadManager.HasContent() && {
4803
4937
  scratchpad: this._scratchpadManager.ToJSON()
4804
4938
  }),
4939
+ // Include artifact tools snapshot for audit/training data
4940
+ ...(this._artifactToolManager.HasArtifacts() && {
4941
+ artifactTools: this._artifactToolManager.ToJSON()
4942
+ }),
4805
4943
  // Include memory attribution for observability
4806
4944
  // This tracks which notes/examples were injected and influenced this step
4807
4945
  ...(this._injectedMemory && (this._injectedMemory.notes.length > 0 || this._injectedMemory.examples.length > 0) && {
@@ -4834,6 +4972,14 @@ The context is now within limits. Please retry your request with the recovered c
4834
4972
  await this.finalizeStepEntity(stepEntity, promptResult.success, promptResult.success ? undefined : promptResult.errorMessage, outputData);
4835
4973
  // Return based on next step
4836
4974
  if (updatedNextStep.step === 'Chat') {
4975
+ // If artifact tools were called THIS turn, don't terminate yet — the LLM
4976
+ // needs one more turn to see the results and incorporate them into its response.
4977
+ // Without this, the tool results are wasted because the run exits before
4978
+ // the LLM ever sees them.
4979
+ if (artifactToolsExecutedThisTurn && this._artifactToolManager.HasArtifacts()) {
4980
+ this.logStatus(`[ArtifactTools] Chat step included tool calls — forcing one more turn so LLM can use results`, true, params);
4981
+ return { ...updatedNextStep, terminate: false, step: 'Retry' };
4982
+ }
4837
4983
  // For root agents, create a persistent AIAgentRequest so the request is
4838
4984
  // tracked in the dashboard and can be responded to outside a conversation.
4839
4985
  // This is done here because Chat decisions from executePromptStep terminate