@memberjunction/ai-agents 5.34.1 → 5.35.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 (53) hide show
  1. package/dist/AgentRunner.d.ts +1 -21
  2. package/dist/AgentRunner.d.ts.map +1 -1
  3. package/dist/AgentRunner.js +69 -99
  4. package/dist/AgentRunner.js.map +1 -1
  5. package/dist/ArtifactToolManager.d.ts +57 -46
  6. package/dist/ArtifactToolManager.d.ts.map +1 -1
  7. package/dist/ArtifactToolManager.js +161 -132
  8. package/dist/ArtifactToolManager.js.map +1 -1
  9. package/dist/artifact-tools/CSVToolLibrary.d.ts +8 -0
  10. package/dist/artifact-tools/CSVToolLibrary.d.ts.map +1 -0
  11. package/dist/artifact-tools/CSVToolLibrary.js +161 -0
  12. package/dist/artifact-tools/CSVToolLibrary.js.map +1 -0
  13. package/dist/artifact-tools/DataSnapshotToolLibrary.d.ts +2 -3
  14. package/dist/artifact-tools/DataSnapshotToolLibrary.d.ts.map +1 -1
  15. package/dist/artifact-tools/DataSnapshotToolLibrary.js +4 -29
  16. package/dist/artifact-tools/DataSnapshotToolLibrary.js.map +1 -1
  17. package/dist/artifact-tools/DocxToolLibrary.d.ts +2 -2
  18. package/dist/artifact-tools/DocxToolLibrary.d.ts.map +1 -1
  19. package/dist/artifact-tools/DocxToolLibrary.js +2 -2
  20. package/dist/artifact-tools/DocxToolLibrary.js.map +1 -1
  21. package/dist/artifact-tools/ExcelToolLibrary.d.ts +2 -2
  22. package/dist/artifact-tools/ExcelToolLibrary.d.ts.map +1 -1
  23. package/dist/artifact-tools/ExcelToolLibrary.js +4 -4
  24. package/dist/artifact-tools/ExcelToolLibrary.js.map +1 -1
  25. package/dist/artifact-tools/GenericBinaryToolLibrary.d.ts +6 -0
  26. package/dist/artifact-tools/GenericBinaryToolLibrary.d.ts.map +1 -0
  27. package/dist/artifact-tools/GenericBinaryToolLibrary.js +49 -0
  28. package/dist/artifact-tools/GenericBinaryToolLibrary.js.map +1 -0
  29. package/dist/artifact-tools/JSONToolLibrary.d.ts +2 -2
  30. package/dist/artifact-tools/JSONToolLibrary.d.ts.map +1 -1
  31. package/dist/artifact-tools/JSONToolLibrary.js +2 -2
  32. package/dist/artifact-tools/JSONToolLibrary.js.map +1 -1
  33. package/dist/artifact-tools/PDFToolLibrary.d.ts +6 -3
  34. package/dist/artifact-tools/PDFToolLibrary.d.ts.map +1 -1
  35. package/dist/artifact-tools/PDFToolLibrary.js +41 -20
  36. package/dist/artifact-tools/PDFToolLibrary.js.map +1 -1
  37. package/dist/artifact-tools/SearchResultSetToolLibrary.d.ts +2 -2
  38. package/dist/artifact-tools/SearchResultSetToolLibrary.d.ts.map +1 -1
  39. package/dist/artifact-tools/SearchResultSetToolLibrary.js +2 -2
  40. package/dist/artifact-tools/SearchResultSetToolLibrary.js.map +1 -1
  41. package/dist/artifact-tools/TextToolLibrary.d.ts +2 -2
  42. package/dist/artifact-tools/TextToolLibrary.d.ts.map +1 -1
  43. package/dist/artifact-tools/TextToolLibrary.js +2 -2
  44. package/dist/artifact-tools/TextToolLibrary.js.map +1 -1
  45. package/dist/base-agent.d.ts +27 -12
  46. package/dist/base-agent.d.ts.map +1 -1
  47. package/dist/base-agent.js +124 -53
  48. package/dist/base-agent.js.map +1 -1
  49. package/dist/index.d.ts +2 -0
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +2 -0
  52. package/dist/index.js.map +1 -1
  53. package/package.json +17 -17
@@ -991,8 +991,12 @@ export class BaseAgent {
991
991
  // Reset scratchpad and artifact tools for each new execution (ephemeral per run)
992
992
  this._scratchpadManager.Clear();
993
993
  this._artifactToolManager.Clear();
994
- // Initialize artifact tools with any input artifacts from the conversation
995
- const inputArtifacts = wrappedParams.data?.__inputArtifacts;
994
+ // Initialize artifact tools with any input artifacts attached to the run.
995
+ // Artifacts arrive as a typed first-class field on ExecuteAgentParams —
996
+ // they are NOT routed through `data` because prompt-template rendering
997
+ // would otherwise serialize artifact bodies into the LLM payload. Only
998
+ // the manifest (injected via _ARTIFACT_MANIFEST below) reaches the LLM.
999
+ const inputArtifacts = wrappedParams.inputArtifacts;
996
1000
  if (inputArtifacts?.length) {
997
1001
  this._artifactToolManager.Initialize(inputArtifacts);
998
1002
  this.logStatus(`[ArtifactTools] Initialized with ${inputArtifacts.length} artifact(s): ${inputArtifacts.map(a => `${a.typeName}:"${a.name}"`).join(', ')}`, true, params);
@@ -1802,12 +1806,15 @@ export class BaseAgent {
1802
1806
  promptParams.data['_SCRATCHPAD_TASKS'] = this._scratchpadManager.ToPromptString();
1803
1807
  promptParams.data['_SCRATCHPAD_TASK_SUMMARY'] = this._scratchpadManager.GetTaskSummary();
1804
1808
  }
1805
- // Inject artifact tools template variables if enabled and artifacts are present
1809
+ // Inject artifact tools template variables if enabled and artifacts are present.
1810
+ // Note: prior tool results are NO LONGER injected via a per-turn template var.
1811
+ // They are pushed into conversationMessages as a one-shot 'tool-result'
1812
+ // message at execution time (see injectArtifactToolResultsMessage) and decay
1813
+ // via pruneAndCompactExpiredMessages — same lifecycle as action results.
1806
1814
  const artifactToolsEnabled = agentTypePromptParams?.includeArtifactToolsDocs !== false;
1807
1815
  if (artifactToolsEnabled && this._artifactToolManager.HasArtifacts()) {
1808
1816
  promptParams.data['_ARTIFACT_MANIFEST'] = this._artifactToolManager.ToManifestString();
1809
1817
  promptParams.data['_ARTIFACT_TOOLS'] = this._artifactToolManager.GetToolDocumentation();
1810
- promptParams.data['_ARTIFACT_TOOL_RESULTS'] = this._artifactToolManager.GetPendingResults();
1811
1818
  promptParams.data['_ARTIFACT_TOOL_SUMMARY'] = this._artifactToolManager.GetSummary();
1812
1819
  this.logStatus(`[ArtifactTools] Injected manifest into prompt: ${this._artifactToolManager.GetSummary()}`, true, params);
1813
1820
  }
@@ -2875,7 +2882,7 @@ export class BaseAgent {
2875
2882
  return payload ? JSON.stringify(payload) : null;
2876
2883
  }
2877
2884
  /**
2878
- * Recovery Strategy 1: Remove oldest action-result messages.
2885
+ * Recovery Strategy 1: Remove oldest tool-result messages.
2879
2886
  * Targets messages older than minAge turns for removal.
2880
2887
  *
2881
2888
  * @param params - Agent execution parameters
@@ -2885,10 +2892,10 @@ export class BaseAgent {
2885
2892
  * @returns Result with tokens saved and strategy description
2886
2893
  * @protected
2887
2894
  */
2888
- recoveryStrategy_RemoveOldestActionResults(params, tokensToSave, currentStepCount, minAge = 5) {
2895
+ recoveryStrategy_RemoveOldestToolResults(params, tokensToSave, currentStepCount, minAge = 5) {
2889
2896
  let tokensSaved = 0;
2890
2897
  const removedIndices = [];
2891
- // Find action-result messages older than minAge turns
2898
+ // Find tool-result messages older than minAge turns
2892
2899
  const candidates = params.conversationMessages
2893
2900
  .map((msg, index) => ({
2894
2901
  message: msg,
@@ -2907,7 +2914,7 @@ export class BaseAgent {
2907
2914
  break;
2908
2915
  removedIndices.push(candidate.index);
2909
2916
  tokensSaved += candidate.tokens;
2910
- this.logStatus(`Removing action-result from ${candidate.age} turns ago (${candidate.tokens} tokens)`, true, params);
2917
+ this.logStatus(`Removing tool-result from ${candidate.age} turns ago (${candidate.tokens} tokens)`, true, params);
2911
2918
  }
2912
2919
  // Remove in reverse order to maintain indices
2913
2920
  removedIndices.sort((a, b) => b - a).forEach(index => {
@@ -2918,17 +2925,17 @@ export class BaseAgent {
2918
2925
  turn: currentStepCount,
2919
2926
  messageIndex: index,
2920
2927
  message: removed,
2921
- reason: 'Context recovery - oldest action results',
2928
+ reason: 'Context recovery - oldest tool results',
2922
2929
  tokensSaved: this.estimateTokens(removed.content)
2923
2930
  });
2924
2931
  });
2925
2932
  return {
2926
2933
  tokensSaved,
2927
- strategyName: `Removed ${removedIndices.length} old action-results (${minAge}+ turns)`
2934
+ strategyName: `Removed ${removedIndices.length} old tool-results (${minAge}+ turns)`
2928
2935
  };
2929
2936
  }
2930
2937
  /**
2931
- * Recovery Strategy 2: Compact old action-result messages.
2938
+ * Recovery Strategy 2: Compact old tool-result messages.
2932
2939
  * Uses smart trimming to reduce size while preserving some content.
2933
2940
  *
2934
2941
  * @param params - Agent execution parameters
@@ -2938,10 +2945,10 @@ export class BaseAgent {
2938
2945
  * @returns Result with tokens saved and strategy description
2939
2946
  * @protected
2940
2947
  */
2941
- async recoveryStrategy_CompactOldActionResults(params, tokensToSave, currentStepCount, minAge = 3) {
2948
+ async recoveryStrategy_CompactOldToolResults(params, tokensToSave, currentStepCount, minAge = 3) {
2942
2949
  let tokensSaved = 0;
2943
2950
  let compactedCount = 0;
2944
- // Find action-result messages to compact
2951
+ // Find tool-result messages to compact
2945
2952
  const candidates = params.conversationMessages
2946
2953
  .map((msg, index) => ({
2947
2954
  message: msg,
@@ -2986,16 +2993,16 @@ export class BaseAgent {
2986
2993
  };
2987
2994
  tokensSaved += saved;
2988
2995
  compactedCount++;
2989
- this.logStatus(`Compacted action-result from ${candidate.age} turns ago (saved ${saved} tokens)`, true, params);
2996
+ this.logStatus(`Compacted tool-result from ${candidate.age} turns ago (saved ${saved} tokens)`, true, params);
2990
2997
  }
2991
2998
  }
2992
2999
  return {
2993
3000
  tokensSaved,
2994
- strategyName: `Compacted ${compactedCount} old action-results (${minAge}+ turns)`
3001
+ strategyName: `Compacted ${compactedCount} old tool-results (${minAge}+ turns)`
2995
3002
  };
2996
3003
  }
2997
3004
  /**
2998
- * Recovery Strategy 3: Aggressively compact ALL action-result messages.
3005
+ * Recovery Strategy 3: Aggressively compact ALL tool-result messages.
2999
3006
  * Used when gentler strategies haven't freed enough space.
3000
3007
  *
3001
3008
  * @param params - Agent execution parameters
@@ -3003,10 +3010,10 @@ export class BaseAgent {
3003
3010
  * @returns Result with tokens saved and strategy description
3004
3011
  * @protected
3005
3012
  */
3006
- async recoveryStrategy_CompactAllActionResults(params, tokensToSave) {
3013
+ async recoveryStrategy_CompactAllToolResults(params, tokensToSave) {
3007
3014
  let tokensSaved = 0;
3008
3015
  let compactedCount = 0;
3009
- // Find ALL action-result messages that aren't already compacted
3016
+ // Find ALL tool-result messages that aren't already compacted
3010
3017
  const candidates = params.conversationMessages
3011
3018
  .map((msg, index) => ({
3012
3019
  message: msg,
@@ -3052,7 +3059,7 @@ export class BaseAgent {
3052
3059
  }
3053
3060
  return {
3054
3061
  tokensSaved,
3055
- strategyName: `Aggressively compacted ${compactedCount} action-results`
3062
+ strategyName: `Aggressively compacted ${compactedCount} tool-results`
3056
3063
  };
3057
3064
  }
3058
3065
  /**
@@ -3111,7 +3118,7 @@ export class BaseAgent {
3111
3118
  /**
3112
3119
  * Attempts to recover from a context length exceeded error using multiple strategies.
3113
3120
  * Uses escalating strategies: remove old results → compact old results → compact all → trim user message.
3114
- * This approach preserves the user's original request while removing stale action results.
3121
+ * This approach preserves the user's original request while removing stale tool results.
3115
3122
  *
3116
3123
  * @param params - Agent execution parameters (conversationMessages will be modified)
3117
3124
  * @param payload - Current payload to carry forward
@@ -3143,10 +3150,10 @@ export class BaseAgent {
3143
3150
  const currentPromptTurn = this._promptTurnCount;
3144
3151
  // Try multiple recovery strategies in order
3145
3152
  const strategies = [
3146
- () => this.recoveryStrategy_RemoveOldestActionResults(params, tokensToSave, currentPromptTurn, 5),
3147
- () => this.recoveryStrategy_CompactOldActionResults(params, tokensToSave, currentPromptTurn, 3),
3148
- () => this.recoveryStrategy_RemoveOldestActionResults(params, tokensToSave, currentPromptTurn, 2),
3149
- () => this.recoveryStrategy_CompactAllActionResults(params, tokensToSave),
3153
+ () => this.recoveryStrategy_RemoveOldestToolResults(params, tokensToSave, currentPromptTurn, 5),
3154
+ () => this.recoveryStrategy_CompactOldToolResults(params, tokensToSave, currentPromptTurn, 3),
3155
+ () => this.recoveryStrategy_RemoveOldestToolResults(params, tokensToSave, currentPromptTurn, 2),
3156
+ () => this.recoveryStrategy_CompactAllToolResults(params, tokensToSave),
3150
3157
  () => Promise.resolve(this.recoveryStrategy_TrimLastUserMessage(params, tokensToSave))
3151
3158
  ];
3152
3159
  let tokensSaved = 0;
@@ -3215,30 +3222,88 @@ The context is now within limits. Please retry your request with the recovered c
3215
3222
  return guardrailCheckedStep;
3216
3223
  }
3217
3224
  /**
3218
- * Creates a chat message containing action execution results.
3225
+ * Executes a batch of artifact tool calls, recording each as its own
3226
+ * `Tool` AIAgentRunStep (a sibling of the Prompt step that requested them)
3227
+ * with full inputs/outputs captured in InputData/OutputData. Returns the
3228
+ * stored results so the caller can render them into a single recall-friendly
3229
+ * message for the next prompt turn.
3230
+ *
3231
+ * Step naming convention: `Artifact Tool: {toolName}` for log/UI clarity.
3219
3232
  *
3220
- * @param {AgentAction[]} actions - The actions that were executed
3221
- * @param {any[]} results - The results from action execution
3222
- * @returns {ChatMessage} A formatted message with action results
3223
3233
  * @protected
3224
3234
  */
3225
- createActionResultMessage(actions, results) {
3226
- const actionSummaries = actions.map((action, index) => {
3227
- const result = results[index];
3228
- const outputParams = result.Params?.filter(p => p.Type === 'Output' || p.Type === 'Both') || [];
3229
- return {
3230
- actionName: action.name,
3231
- success: result.Success,
3232
- params: outputParams,
3233
- resultCode: result.Result?.ResultCode || 'N/A',
3234
- message: result.Message || '(no message)',
3235
- aiDirectives: result.AIDirectives,
3236
- };
3237
- });
3238
- return {
3235
+ async executeArtifactToolCallsAsSteps(calls, params) {
3236
+ // No parentId: artifact tool steps are siblings of the prompt step that
3237
+ // requested them, matching how action steps render. ParentID is reserved
3238
+ // for genuine control-flow nesting (ForEach/While loops, sub-agents), which
3239
+ // artifact tools are never dispatched from.
3240
+ const results = await Promise.all(calls.map(async (call) => {
3241
+ const toolStep = await this.createStepEntity({
3242
+ stepType: 'Tool',
3243
+ stepName: `Artifact Tool: ${call.tool}`,
3244
+ contextUser: params.contextUser,
3245
+ inputData: {
3246
+ artifactId: call.artifactId,
3247
+ tool: call.tool,
3248
+ input: call.input,
3249
+ },
3250
+ });
3251
+ const stored = await this._artifactToolManager.ExecuteSingleToolCall(call);
3252
+ await this.finalizeStepEntity(toolStep, stored.result.success, stored.result.success ? undefined : stored.result.errorMessage, {
3253
+ artifactId: stored.artifactId,
3254
+ tool: stored.tool,
3255
+ input: stored.input,
3256
+ result: stored.result,
3257
+ durationMs: stored.durationMs,
3258
+ });
3259
+ return stored;
3260
+ }));
3261
+ return results;
3262
+ }
3263
+ /**
3264
+ * Pushes a single user-role message containing rendered artifact-tool
3265
+ * results into the conversation. This mirrors the action-result
3266
+ * "inject once, then expire" pattern — the LLM sees the results on its
3267
+ * next turn, and older messages are pruned/compacted by
3268
+ * `pruneAndCompactExpiredMessages` instead of being re-rendered into
3269
+ * every system-prompt turn.
3270
+ *
3271
+ * @protected
3272
+ */
3273
+ injectArtifactToolResultsMessage(params, toolResults) {
3274
+ if (toolResults.length === 0)
3275
+ return;
3276
+ const header = toolResults.length === 1
3277
+ ? 'Artifact tool result:'
3278
+ : `Artifact tool results (${toolResults.length} calls):`;
3279
+ const body = toolResults.map((r, i) => {
3280
+ const heading = `### ${i + 1}. ${r.artifactId}.${r.tool}(${JSON.stringify(r.input)})`;
3281
+ if (r.result.success) {
3282
+ const data = typeof r.result.data === 'string'
3283
+ ? r.result.data
3284
+ : JSON.stringify(r.result.data, null, 2);
3285
+ return `${heading}\n\`\`\`json\n${data}\n\`\`\``;
3286
+ }
3287
+ return `${heading}\n**Error:** ${r.result.errorMessage}`;
3288
+ }).join('\n\n');
3289
+ const message = {
3239
3290
  role: 'user',
3240
- content: `Action results:\n${this.formatActionResultsAsMarkdown(actionSummaries)}`
3291
+ content: `${header}\n${body}`,
3292
+ metadata: {
3293
+ turnAdded: this._promptTurnCount,
3294
+ messageType: 'tool-result',
3295
+ // Default: keep results visible for a few turns then compact to a
3296
+ // first-N-chars preview. The LLM is taught (via the loop-agent
3297
+ // system prompt) that older tool results are summarised and that
3298
+ // it can re-call the tool if it needs the full result back.
3299
+ expirationTurns: 3,
3300
+ expirationMode: 'Compact',
3301
+ compactMode: 'First N Chars',
3302
+ compactLength: 500,
3303
+ compactPromptId: '',
3304
+ },
3241
3305
  };
3306
+ params.conversationMessages.push(message);
3242
3307
  }
3243
3308
  /**
3244
3309
  * Creates a chat message containing sub-agent execution results.
@@ -4685,8 +4750,8 @@ The context is now within limits. Please retry your request with the recovered c
4685
4750
  return await this.processSubAgentStep(params, previousDecision, undefined, undefined, stepCount);
4686
4751
  case 'Actions':
4687
4752
  return await this.executeActionsStep(params, previousDecision, undefined, true, stepCount);
4688
- // Type assertion required because 'ClientTools' is not yet in the DB StepType value list.
4689
- // The LoopAgentType.DetermineNextStep() emits this value when the LLM chooses client tools.
4753
+ // Type assertion required because 'ClientTools' is not part of the BaseAgentNextStep
4754
+ // step union — LoopAgentType.DetermineNextStep() emits it when the LLM chooses client tools.
4690
4755
  case 'ClientTools':
4691
4756
  return await this.executeClientToolsStep(params, config, previousDecision, stepCount);
4692
4757
  case 'Chat':
@@ -5001,7 +5066,16 @@ The context is now within limits. Please retry your request with the recovered c
5001
5066
  const artifactToolsExecutedThisTurn = !!(artifactToolCalls?.length);
5002
5067
  if (artifactToolsExecutedThisTurn) {
5003
5068
  this.logStatus(`[ArtifactTools] LLM requested ${artifactToolCalls.length} tool call(s): ${artifactToolCalls.map(c => `${c.artifactId}.${c.tool}`).join(', ')}`, true, params);
5004
- await this._artifactToolManager.ExecuteToolCalls(artifactToolCalls);
5069
+ // Per-call observability: wrap each invocation in its own AIAgentRunStep
5070
+ // (StepType='Tool') so the run tree shows the calls + their inputs/outputs
5071
+ // at full fidelity instead of a summary buried inside the parent Prompt
5072
+ // step's OutputData. Step naming convention is "Artifact Tool: {tool}".
5073
+ // The result is also surfaced to the agent on the NEXT turn via a
5074
+ // one-shot ChatMessage push (see injectArtifactToolResultsMessage below),
5075
+ // mirroring the action-result inject-once-then-expire pattern instead of
5076
+ // the previous re-render-every-turn `_ARTIFACT_TOOL_RESULTS` template var.
5077
+ const toolResults = await this.executeArtifactToolCallsAsSteps(artifactToolCalls, params);
5078
+ this.injectArtifactToolResultsMessage(params, toolResults);
5005
5079
  }
5006
5080
  else if (this._artifactToolManager.HasArtifacts()) {
5007
5081
  this.logStatus(`[ArtifactTools] LLM did not use artifact tools this turn (artifacts available but not accessed)`, true, params);
@@ -6362,12 +6436,7 @@ The context is now within limits. Please retry your request with the recovered c
6362
6436
  // Execute tools sequentially (client may not support parallel UI operations)
6363
6437
  for (const tool of clientTools) {
6364
6438
  const stepEntity = await this.createStepEntity({
6365
- // INTENTIONAL: We use 'Actions' as the DB step type because the MJ: AI Agent Run Steps
6366
- // entity's StepType value list does not yet include 'ClientTools'. A future database
6367
- // migration will add 'ClientTools' to the allowed values in the StepType CHECK constraint
6368
- // and CodeGen will regenerate the types. Until then, client tool steps are recorded under
6369
- // 'Actions' in the run history. The step name ("Client Tool: {name}") distinguishes them.
6370
- stepType: 'Actions',
6439
+ stepType: 'Tool',
6371
6440
  stepName: `Client Tool: ${tool.Name}`,
6372
6441
  inputData: { toolName: tool.Name, params: tool.Params },
6373
6442
  contextUser: params.contextUser,
@@ -7860,7 +7929,9 @@ The context is now within limits. Please retry your request with the recovered c
7860
7929
  */
7861
7930
  IsToolResultMessage(msg) {
7862
7931
  const messageType = msg.metadata?.messageType;
7863
- return messageType === 'action-result' || messageType === 'client-tool-result';
7932
+ return messageType === 'action-result'
7933
+ || messageType === 'client-tool-result'
7934
+ || messageType === 'tool-result';
7864
7935
  }
7865
7936
  estimateTokens(content, modelName) {
7866
7937
  const text = typeof content === 'string'