@minded-ai/mindedjs 3.1.43 → 3.1.44

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 (45) hide show
  1. package/dist/cli/cliUtils.d.ts +4 -2
  2. package/dist/cli/cliUtils.d.ts.map +1 -1
  3. package/dist/cli/cliUtils.js +12 -5
  4. package/dist/cli/cliUtils.js.map +1 -1
  5. package/dist/nodes/addAppToolNode.d.ts.map +1 -1
  6. package/dist/nodes/addAppToolNode.js +59 -13
  7. package/dist/nodes/addAppToolNode.js.map +1 -1
  8. package/dist/nodes/addBrowserTaskNode.d.ts.map +1 -1
  9. package/dist/nodes/addBrowserTaskNode.js +13 -3
  10. package/dist/nodes/addBrowserTaskNode.js.map +1 -1
  11. package/dist/nodes/addToolNode.d.ts.map +1 -1
  12. package/dist/nodes/addToolNode.js +22 -40
  13. package/dist/nodes/addToolNode.js.map +1 -1
  14. package/dist/nodes/addToolRunNode.d.ts.map +1 -1
  15. package/dist/nodes/addToolRunNode.js +93 -82
  16. package/dist/nodes/addToolRunNode.js.map +1 -1
  17. package/dist/nodes/utils/getSchemaForToolInference.d.ts +13 -0
  18. package/dist/nodes/utils/getSchemaForToolInference.d.ts.map +1 -0
  19. package/dist/nodes/utils/getSchemaForToolInference.js +34 -0
  20. package/dist/nodes/utils/getSchemaForToolInference.js.map +1 -0
  21. package/dist/nodes/utils/inferToolCallWithRetry.d.ts +18 -0
  22. package/dist/nodes/utils/inferToolCallWithRetry.d.ts.map +1 -0
  23. package/dist/nodes/utils/inferToolCallWithRetry.js +58 -0
  24. package/dist/nodes/utils/inferToolCallWithRetry.js.map +1 -0
  25. package/dist/types/Flows.types.d.ts +1 -0
  26. package/dist/types/Flows.types.d.ts.map +1 -1
  27. package/dist/types/Flows.types.js +1 -0
  28. package/dist/types/Flows.types.js.map +1 -1
  29. package/dist/utils/flowSchema.d.ts.map +1 -1
  30. package/dist/utils/flowSchema.js +20 -4
  31. package/dist/utils/flowSchema.js.map +1 -1
  32. package/dist/utils/schemaUtils.d.ts.map +1 -1
  33. package/dist/utils/schemaUtils.js +2 -0
  34. package/dist/utils/schemaUtils.js.map +1 -1
  35. package/package.json +1 -1
  36. package/src/cli/cliUtils.ts +12 -5
  37. package/src/nodes/addAppToolNode.ts +61 -15
  38. package/src/nodes/addBrowserTaskNode.ts +13 -3
  39. package/src/nodes/addToolNode.ts +24 -46
  40. package/src/nodes/addToolRunNode.ts +114 -97
  41. package/src/nodes/utils/getSchemaForToolInference.ts +48 -0
  42. package/src/nodes/utils/inferToolCallWithRetry.ts +89 -0
  43. package/src/types/Flows.types.ts +1 -0
  44. package/src/utils/flowSchema.ts +21 -4
  45. package/src/utils/schemaUtils.ts +2 -0
@@ -1 +1 @@
1
- {"version":3,"file":"schemaUtils.js","sourceRoot":"","sources":["../../src/utils/schemaUtils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schemaUtils.js","sourceRoot":"","sources":["../../src/utils/schemaUtils.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minded-ai/mindedjs",
3
- "version": "3.1.43",
3
+ "version": "3.1.44",
4
4
  "description": "MindedJS is a TypeScript library for building agents.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -4,12 +4,19 @@ import * as path from 'path';
4
4
  import { Agent } from '../agent';
5
5
 
6
6
  /**
7
- * Registers TypeScript loader (tsx or ts-node) to enable requiring .ts files
7
+ * Registers TypeScript loader (tsx or ts-node) to enable requiring .ts files.
8
+ * Resolves loaders from the agent's project directory so that devDependencies
9
+ * installed there (not in mindedjs itself) are found correctly.
8
10
  */
9
- export function registerTypeScriptLoader(): void {
10
- // Try tsx first (faster), then fall back to ts-node
11
+ export function registerTypeScriptLoader(projectDir: string): void {
11
12
  const loaders = [
12
- { name: 'tsx', register: () => require('tsx/cjs') },
13
+ {
14
+ name: 'tsx',
15
+ register: () => {
16
+ const tsxPath = require.resolve('tsx/cjs', { paths: [projectDir] });
17
+ require(tsxPath);
18
+ },
19
+ },
13
20
  ];
14
21
 
15
22
  for (const loader of loaders) {
@@ -44,7 +51,7 @@ export function loadAgentFromConfig(projectDir: string, agentFilePath: string):
44
51
  }
45
52
 
46
53
  // Register TypeScript loader before requiring .ts files
47
- registerTypeScriptLoader();
54
+ registerTypeScriptLoader(projectDir);
48
55
 
49
56
  // Clear require cache to ensure fresh load
50
57
  const resolvedPath = require.resolve(modulePath);
@@ -1,7 +1,7 @@
1
1
  import { AppToolNode, AppToolNodeMindedMetadata, NodeType } from '../types/Flows.types';
2
2
  import { tool as langchainTool } from '@langchain/core/tools';
3
3
  import { PreCompiledGraph, stateAnnotation } from '../types/LangGraph.types';
4
- import { SystemMessage } from '@langchain/core/messages';
4
+ import { AIMessage, SystemMessage } from '@langchain/core/messages';
5
5
  import { RunnableLike } from '@langchain/core/runnables';
6
6
  import { z } from 'zod';
7
7
  import { LLMProviders } from '../types/LLM.types';
@@ -15,6 +15,8 @@ import { compileParameter, compilePrompt } from './compilePrompt';
15
15
  import { ToolMessage } from '@langchain/core/messages';
16
16
  import { AnalyticsEventName } from '../types/Analytics.types';
17
17
  import { trackAnalyticsEvent } from '../internalTools/analytics';
18
+ import { inferToolCallWithRetry } from './utils/inferToolCallWithRetry';
19
+ import { getSchemaForToolInference } from './utils/getSchemaForToolInference';
18
20
 
19
21
  export const addAppToolNode = async ({
20
22
  graph,
@@ -82,6 +84,12 @@ export const addAppToolNode = async ({
82
84
  }
83
85
  }
84
86
  }
87
+ const overriddenKeys = Object.keys(compiledParameters);
88
+ const { schemaForLLM, skipLLMCall } = getSchemaForToolInference({
89
+ inputSchema: appRunnerTool.input,
90
+ overriddenKeys,
91
+ toolName: appRunnerTool.name,
92
+ });
85
93
 
86
94
  const executeWrapper = async (input: z.infer<typeof appRunnerTool.input>) => {
87
95
  try {
@@ -92,10 +100,15 @@ export const addAppToolNode = async ({
92
100
  throw err;
93
101
  }
94
102
  };
95
- const tool = langchainTool(executeWrapper, {
103
+ const inferenceTool = langchainTool(() => ({}), {
104
+ name: appRunnerTool.name,
105
+ description: appRunnerTool.description,
106
+ schema: schemaForLLM as any,
107
+ });
108
+ const executionTool = langchainTool(executeWrapper, {
96
109
  name: appRunnerTool.name,
97
110
  description: appRunnerTool.description,
98
- schema: appRunnerTool.input,
111
+ schema: appRunnerTool.input as any,
99
112
  });
100
113
 
101
114
  const combinedPlaybooks = combinePlaybooks(agent.playbooks) || '';
@@ -143,19 +156,52 @@ export const addAppToolNode = async ({
143
156
  ${compiledNodePrompt ? compiledNodePrompt : 'no instructions set by the user'}`;
144
157
 
145
158
  // const compiledPrompt = compilePrompt(message, { state: state, memory: state.memory, env: process.env });
146
- const systemMessage = new SystemMessage(message);
147
- if (state.messages.length === 0 || state.messages[0].getType() === 'system') {
148
- state.messages[0] = systemMessage;
159
+ let AIToolCallMessage: AIMessage;
160
+ if (skipLLMCall) {
161
+ AIToolCallMessage = new AIMessage({
162
+ content: '',
163
+ tool_calls: [
164
+ {
165
+ name: appRunnerTool.name,
166
+ args: compiledParameters,
167
+ id: `call_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
168
+ type: 'tool_call',
169
+ },
170
+ ],
171
+ });
149
172
  } else {
150
- state.messages.unshift(systemMessage);
173
+ const systemMessage = new SystemMessage(message);
174
+ if (state.messages.length === 0 || state.messages[0].getType() === 'system') {
175
+ state.messages[0] = systemMessage;
176
+ } else {
177
+ state.messages.unshift(systemMessage);
178
+ }
179
+
180
+ AIToolCallMessage = await inferToolCallWithRetry({
181
+ llm,
182
+ tool: inferenceTool,
183
+ inputSchema: appRunnerTool.input,
184
+ messages: state.messages,
185
+ sessionId: state.sessionId,
186
+ mergeArgs: (args) => ({
187
+ ...args,
188
+ ...compiledParameters, // user-set parameters have priority over AI-generated parameters
189
+ }),
190
+ onAfterAttempt: async () => {
191
+ await agent.interruptSessionManager.checkQueueAndInterrupt(state.sessionId);
192
+ },
193
+ toolNameForLogs: appRunnerTool.name,
194
+ });
195
+ if (!AIToolCallMessage.tool_calls || AIToolCallMessage.tool_calls.length === 0) {
196
+ throw new Error('No tool calls generated by LLM');
197
+ }
151
198
  }
152
199
 
153
- const AIToolCallMessage = await llm.bindTools([tool], { tool_choice: tool.name }).invoke(state.messages);
154
- AIToolCallMessage.tool_calls[0].args = {
155
- ...AIToolCallMessage.tool_calls[0].args,
156
- ...compiledParameters, //user set parameters have priority over ai generated parameters
157
- };
158
- const toolCallMessage = await tool.invoke(AIToolCallMessage.tool_calls[0]);
200
+ if (!AIToolCallMessage.tool_calls || AIToolCallMessage.tool_calls.length === 0) {
201
+ throw new Error('No tool calls generated by LLM');
202
+ }
203
+ const firstToolCall = AIToolCallMessage.tool_calls[0];
204
+ const toolCallMessage = await executionTool.invoke(firstToolCall);
159
205
  AIToolCallMessage.additional_kwargs = {
160
206
  mindedMetadata: {
161
207
  nodeType: NodeType.APP_TOOL,
@@ -190,11 +236,11 @@ export const addAppToolNode = async ({
190
236
  nodeId: node.name,
191
237
  nodeDisplayName: node.displayName!,
192
238
  raw: {
193
- ...AIToolCallMessage.tool_calls[0],
239
+ ...firstToolCall,
194
240
  result: parsedContent,
195
241
  },
196
242
  appName: node.appName,
197
- messageIds: [AIToolCallMessage.tool_calls[0].id],
243
+ messageIds: [firstToolCall.id!],
198
244
  }),
199
245
  );
200
246
  return state;
@@ -17,6 +17,7 @@ import { getConfig } from '../platform/config';
17
17
  import { createZodSchemaFromFields } from '../utils/schemaUtils';
18
18
  import { AnalyticsEventName } from '../types/Analytics.types';
19
19
  import { trackAnalyticsEvent } from '../internalTools/analytics';
20
+ import { inferToolCallWithRetry } from './utils/inferToolCallWithRetry';
20
21
 
21
22
  type AddBrowserTaskNodeParams = {
22
23
  graph: PreCompiledGraph;
@@ -61,9 +62,18 @@ export const addBrowserTaskNode = async ({ graph, node, agent, llm }: AddBrowser
61
62
  }
62
63
 
63
64
  try {
64
- // Use LLM to generate tool call
65
- const AIToolCallMessage: AIMessage = await llm.bindTools([tool], { tool_choice: tool.name }).invoke(state.messages);
66
- await agent.interruptSessionManager.checkQueueAndInterrupt(state.sessionId);
65
+ // Use LLM to generate tool call, with retry when inferred args are schema-invalid
66
+ const AIToolCallMessage: AIMessage = await inferToolCallWithRetry({
67
+ llm,
68
+ tool,
69
+ inputSchema: zodSchema as any,
70
+ messages: state.messages,
71
+ sessionId: state.sessionId,
72
+ onAfterAttempt: async () => {
73
+ await agent.interruptSessionManager.checkQueueAndInterrupt(state.sessionId);
74
+ },
75
+ toolNameForLogs: 'browser-task',
76
+ });
67
77
  if (!AIToolCallMessage.tool_calls || AIToolCallMessage.tool_calls.length === 0) {
68
78
  throw new Error('No tool calls generated by LLM');
69
79
  }
@@ -13,7 +13,8 @@ import { combinePlaybooks } from '../playbooks/playbooks';
13
13
  import { compileParameter, compilePrompt } from './compilePrompt';
14
14
  import { AnalyticsEventName } from '../types/Analytics.types';
15
15
  import { trackAnalyticsEvent } from '../internalTools/analytics';
16
- import { z } from 'zod';
16
+ import { inferToolCallWithRetry } from './utils/inferToolCallWithRetry';
17
+ import { getSchemaForToolInference } from './utils/getSchemaForToolInference';
17
18
 
18
19
  export const addToolNode = async ({
19
20
  graph,
@@ -65,38 +66,14 @@ export const addToolNode = async ({
65
66
  }
66
67
  }
67
68
 
68
- // Create a filtered schema for the LLM (exclude overridden parameters)
69
- let schemaForLLM = matchedTool.input;
70
69
  const overriddenKeys = Object.keys(compiledParameters);
71
- let skipLLMCall = false;
72
-
73
- if (overriddenKeys.length > 0) {
74
- try {
75
- // Only filter if it's a ZodObject
76
- if (matchedTool.input instanceof z.ZodObject) {
77
- // Create omit map: { key1: true, key2: true, ... }
78
- const omitMap = overriddenKeys.reduce((acc, key) => {
79
- acc[key] = true;
80
- return acc;
81
- }, {} as Record<string, true>);
82
-
83
- schemaForLLM = matchedTool.input.omit(omitMap);
84
-
85
- // Check if all parameters are overridden
86
- const remainingKeys = Object.keys(schemaForLLM.shape);
87
- skipLLMCall = remainingKeys.length === 0;
88
- }
89
- } catch (err) {
90
- logger.warn({
91
- msg: '[Tool] Failed to filter schema, using original',
92
- tool: matchedTool.name,
93
- err,
94
- });
95
- // Fall back to original schema on error
96
- }
97
- }
70
+ const { schemaForLLM, skipLLMCall } = getSchemaForToolInference({
71
+ inputSchema: matchedTool.input,
72
+ overriddenKeys,
73
+ toolName: matchedTool.name,
74
+ });
98
75
 
99
- let AIToolCallMessage: AIMessage;
76
+ let AIToolCallMessage: AIMessage = new AIMessage({ content: '', tool_calls: [] });
100
77
 
101
78
  if (skipLLMCall) {
102
79
  // All parameters are overridden - create synthetic tool call without LLM
@@ -112,10 +89,10 @@ export const addToolNode = async ({
112
89
  });
113
90
  } else {
114
91
  // Need LLM to infer remaining parameters
115
- const tool = langchainTool(() => {}, {
92
+ const tool = langchainTool(() => { }, {
116
93
  name: matchedTool.name,
117
94
  description: matchedTool.description,
118
- schema: schemaForLLM, // Use filtered schema
95
+ schema: schemaForLLM as any, // Use filtered schema
119
96
  });
120
97
 
121
98
  const combinedPlaybooks = combinePlaybooks(agent.playbooks) || '';
@@ -172,20 +149,21 @@ export const addToolNode = async ({
172
149
  state.messages.unshift(systemMessage);
173
150
  }
174
151
  }
175
-
176
- const startTime = Date.now();
177
- AIToolCallMessage = await llm.bindTools([tool], { tool_choice: tool.name }).invoke(state.messages);
178
- const endTime = Date.now();
179
- logger.debug({ msg: '[Tool] Model execution time', tool: matchedTool.name, executionTimeMs: endTime - startTime });
180
- await agent.interruptSessionManager.checkQueueAndInterrupt(state.sessionId);
181
-
182
- // Merge AI-generated parameters with user-set parameters
183
- if (AIToolCallMessage.tool_calls && AIToolCallMessage.tool_calls.length > 0) {
184
- AIToolCallMessage.tool_calls[0].args = {
185
- ...AIToolCallMessage.tool_calls[0].args,
152
+ AIToolCallMessage = await inferToolCallWithRetry({
153
+ llm,
154
+ tool,
155
+ inputSchema: matchedTool.input,
156
+ messages: state.messages,
157
+ sessionId: state.sessionId,
158
+ mergeArgs: (args) => ({
159
+ ...args,
186
160
  ...compiledParameters, // User-set parameters have priority over AI-generated parameters
187
- };
188
- }
161
+ }),
162
+ onAfterAttempt: async () => {
163
+ await agent.interruptSessionManager.checkQueueAndInterrupt(state.sessionId);
164
+ },
165
+ toolNameForLogs: matchedTool.name,
166
+ });
189
167
  }
190
168
 
191
169
  state.goto = null;
@@ -35,55 +35,50 @@ export const addToolRunNode = async ({ graph, tools, toolNode, attachedToNodeNam
35
35
  const { env, browserTaskMode } = getConfig();
36
36
 
37
37
  const executeWrapper = async (input: z.infer<typeof matchedTool.input>) => {
38
- try {
39
- const startTime = Date.now();
40
-
41
- // Check if this is an RPA tool
42
- if (matchedTool.type === 'rpa') {
43
- // Wrap with browser session and screenshot capture
44
- const { result, breakpointHit, breakpointInfo } = await withBrowserSession(
45
- {
46
- sessionId: state.sessionId,
47
- browserTaskMode: matchedTool.browserTaskMode ?? browserTaskMode,
48
- toolCallId: toolCallObj.tool_calls[0].id,
49
- proxyConfig: matchedTool.proxyConfig,
50
- toolName: matchedTool.name,
51
- persistSession: matchedTool.persistSession,
52
- checkBreakpoints: env === Environment.DEVELOPMENT,
53
- },
54
- async (page) => {
55
- // Pass page directly to RPA tool execution
56
- const toolResult = await matchedTool.execute({
57
- input,
58
- state,
59
- agent,
60
- page,
61
- });
62
-
63
- return toolResult;
64
- },
65
- );
66
-
67
- const endTime = Date.now();
68
- logger.debug({ msg: '[Tool] RPA tool execution time', tool: matchedTool.name, executionTimeMs: endTime - startTime });
69
-
70
- // Check if breakpoint was hit - return special result
71
- if (breakpointHit && breakpointInfo) {
72
- return { breakpointHit: true, breakpointInfo } as unknown as ToolMessage;
73
- }
74
-
75
- return result as ToolMessage;
76
- } else {
77
- // Non-RPA tool - execute normally
78
- const response = await matchedTool.execute({ input, state, agent });
79
- const endTime = Date.now();
80
- logger.debug({ msg: '[Tool] Tool execution time', tool: matchedTool.name, executionTimeMs: endTime - startTime });
81
- return response as ToolMessage;
38
+ const startTime = Date.now();
39
+
40
+ // Check if this is an RPA tool
41
+ if (matchedTool.type === 'rpa') {
42
+ // Wrap with browser session and screenshot capture
43
+ const { result, breakpointHit, breakpointInfo } = await withBrowserSession(
44
+ {
45
+ sessionId: state.sessionId,
46
+ browserTaskMode: matchedTool.browserTaskMode ?? browserTaskMode,
47
+ toolCallId: toolCallObj.tool_calls[0].id,
48
+ proxyConfig: matchedTool.proxyConfig,
49
+ toolName: matchedTool.name,
50
+ persistSession: matchedTool.persistSession,
51
+ checkBreakpoints: env === Environment.DEVELOPMENT,
52
+ },
53
+ async (page) => {
54
+ // Pass page directly to RPA tool execution
55
+ const toolResult = await matchedTool.execute({
56
+ input,
57
+ state,
58
+ agent,
59
+ page,
60
+ });
61
+
62
+ return toolResult;
63
+ },
64
+ );
65
+
66
+ const endTime = Date.now();
67
+ logger.debug({ msg: '[Tool] RPA tool execution time', tool: matchedTool.name, executionTimeMs: endTime - startTime });
68
+
69
+ // Check if breakpoint was hit - return special result
70
+ if (breakpointHit && breakpointInfo) {
71
+ return { breakpointHit: true, breakpointInfo } as unknown as ToolMessage;
82
72
  }
83
- } catch (err) {
84
- logger.error({ message: '[Tool] Error executing tool', err, node: toolNode.displayName });
85
- throw err;
73
+
74
+ return result as ToolMessage;
86
75
  }
76
+
77
+ // Non-RPA tool - execute normally
78
+ const response = await matchedTool.execute({ input, state, agent });
79
+ const endTime = Date.now();
80
+ logger.debug({ msg: '[Tool] Tool execution time', tool: matchedTool.name, executionTimeMs: endTime - startTime });
81
+ return response as ToolMessage;
87
82
  };
88
83
 
89
84
  const toolCallObj = state.messages[state.messages.length - 1] as any;
@@ -95,72 +90,94 @@ export const addToolRunNode = async ({ graph, tools, toolNode, attachedToNodeNam
95
90
  const toolCall = toolCallObj.tool_calls[0];
96
91
  const parsedArgs = matchedTool.input.parse(toolCall.args);
97
92
 
98
- // Execute the tool with validated args
99
- const result = await executeWrapper(parsedArgs);
100
-
101
- // Check if breakpoint was hit - handle gracefully by ending the flow
102
- const resultObj = result as unknown as { breakpointHit?: boolean; breakpointInfo?: BreakpointHitResult };
103
- if (resultObj?.breakpointHit && resultObj?.breakpointInfo) {
104
- logger.info({
105
- msg: '[Tool] RPA breakpoint hit - ending flow gracefully',
106
- toolName: matchedTool.name,
107
- stepNumber: resultObj.breakpointInfo.stepNumber,
108
- });
93
+ try {
94
+ // Execute the tool with validated args
95
+ const result = await executeWrapper(parsedArgs);
109
96
 
110
- // Create a ToolMessage indicating breakpoint was hit
111
- const breakpointMessage = new ToolMessage({
112
- content: JSON.stringify({
113
- breakpointHit: true,
114
- message: `RPA breakpoint reached at step ${resultObj.breakpointInfo.stepNumber + 1}`,
115
- toolName: resultObj.breakpointInfo.toolName,
97
+ // Check if breakpoint was hit - handle gracefully by ending the flow
98
+ const resultObj = result as unknown as { breakpointHit?: boolean; breakpointInfo?: BreakpointHitResult };
99
+ if (resultObj?.breakpointHit && resultObj?.breakpointInfo) {
100
+ logger.info({
101
+ msg: '[Tool] RPA breakpoint hit - ending flow gracefully',
102
+ toolName: matchedTool.name,
116
103
  stepNumber: resultObj.breakpointInfo.stepNumber,
117
- }),
104
+ });
105
+
106
+ // Create a ToolMessage indicating breakpoint was hit
107
+ const breakpointMessage = new ToolMessage({
108
+ content: JSON.stringify({
109
+ breakpointHit: true,
110
+ message: `RPA breakpoint reached at step ${resultObj.breakpointInfo.stepNumber + 1}`,
111
+ toolName: resultObj.breakpointInfo.toolName,
112
+ stepNumber: resultObj.breakpointInfo.stepNumber,
113
+ }),
114
+ tool_call_id: toolCall.id,
115
+ name: matchedTool.name,
116
+ });
117
+
118
+ state.messages.push(breakpointMessage);
119
+ state.history.push(
120
+ createHistoryStep<HistoryStep>(state.history, {
121
+ type: NodeType.TOOL,
122
+ nodeId: toolNode.name,
123
+ nodeDisplayName: toolNode.displayName,
124
+ raw: breakpointMessage,
125
+ messageIds: [toolCall.id],
126
+ }),
127
+ );
128
+
129
+ // Set goto to __end__ to stop the flow gracefully
130
+ state.goto = '__end__';
131
+ return state;
132
+ }
133
+
134
+ // Create a ToolMessage with the result
135
+ const toolCallMessage = new ToolMessage({
136
+ content: typeof result === 'string' ? result : JSON.stringify(result),
118
137
  tool_call_id: toolCall.id,
119
138
  name: matchedTool.name,
120
139
  });
121
140
 
122
- state.messages.push(breakpointMessage);
141
+ // Add the tool message to the state
142
+ state.messages.push(toolCallMessage);
143
+
144
+ // Add history step
123
145
  state.history.push(
124
146
  createHistoryStep<HistoryStep>(state.history, {
125
147
  type: NodeType.TOOL,
126
148
  nodeId: toolNode.name,
127
149
  nodeDisplayName: toolNode.displayName,
128
- raw: breakpointMessage,
129
- messageIds: [toolCallObj.tool_calls[0].id],
150
+ raw: toolCallMessage,
151
+ messageIds: [toolCall.id],
130
152
  }),
131
153
  );
132
154
 
133
- // Set goto to __end__ to stop the flow gracefully
134
- state.goto = '__end__';
155
+ // Clear goto if it was set
156
+ state.goto = null;
157
+
158
+ // Return the entire modified state
135
159
  return state;
136
- }
160
+ } catch (err) {
161
+ logger.error({ message: '[Tool] Error executing tool', err, node: toolNode.displayName });
162
+
163
+ const errorMessage = err instanceof Error ? err.message : JSON.stringify(err);
164
+ const toolErrorMessage = new ToolMessage({
165
+ content: JSON.stringify({ error: errorMessage }),
166
+ tool_call_id: toolCall.id,
167
+ name: matchedTool.name,
168
+ });
137
169
 
138
- // Create a ToolMessage with the result
139
- const toolCallMessage = new ToolMessage({
140
- content: typeof result === 'string' ? result : JSON.stringify(result),
141
- tool_call_id: toolCall.id,
142
- name: matchedTool.name,
143
- });
144
-
145
- // Add the tool message to the state
146
- state.messages.push(toolCallMessage);
147
-
148
- // Add history step
149
- state.history.push(
150
- createHistoryStep<HistoryStep>(state.history, {
151
- type: NodeType.TOOL,
152
- nodeId: toolNode.name,
153
- nodeDisplayName: toolNode.displayName,
154
- raw: toolCallMessage,
155
- messageIds: [toolCallObj.tool_calls[0].id],
156
- }),
157
- );
158
-
159
- // Clear goto if it was set
160
- state.goto = null;
161
-
162
- // Return the entire modified state
163
- return state;
170
+ state.messages.push(toolErrorMessage);
171
+ state.history.push(
172
+ createHistoryStep<HistoryStep>(state.history, {
173
+ type: NodeType.TOOL,
174
+ nodeId: toolNode.name,
175
+ nodeDisplayName: toolNode.displayName,
176
+ raw: toolErrorMessage,
177
+ messageIds: [toolCall.id],
178
+ }),
179
+ );
180
+ }
164
181
  };
165
182
 
166
183
  graph.addNode(buildToolRunNodeName(attachedToNodeName), callback);
@@ -0,0 +1,48 @@
1
+ import { z } from 'zod';
2
+ import { logger } from '../../utils/logger';
3
+
4
+ interface GetSchemaForToolInferenceParams {
5
+ inputSchema: z.ZodTypeAny;
6
+ overriddenKeys: string[];
7
+ toolName: string;
8
+ }
9
+
10
+ interface GetSchemaForToolInferenceResult {
11
+ schemaForLLM: z.ZodTypeAny;
12
+ skipLLMCall: boolean;
13
+ }
14
+
15
+ export const getSchemaForToolInference = ({
16
+ inputSchema,
17
+ overriddenKeys,
18
+ toolName,
19
+ }: GetSchemaForToolInferenceParams): GetSchemaForToolInferenceResult => {
20
+ let schemaForLLM = inputSchema;
21
+ let skipLLMCall = false;
22
+
23
+ if (overriddenKeys.length === 0) {
24
+ return { schemaForLLM, skipLLMCall };
25
+ }
26
+
27
+ try {
28
+ if (inputSchema instanceof z.ZodObject) {
29
+ const omitMap = overriddenKeys.reduce((acc, key) => {
30
+ acc[key] = true;
31
+ return acc;
32
+ }, {} as Record<string, true>);
33
+
34
+ const filteredSchema = inputSchema.omit(omitMap);
35
+ schemaForLLM = filteredSchema;
36
+ const remainingKeys = Object.keys(filteredSchema.shape);
37
+ skipLLMCall = remainingKeys.length === 0;
38
+ }
39
+ } catch (err) {
40
+ logger.warn({
41
+ msg: '[Tool] Failed to filter schema, using original',
42
+ tool: toolName,
43
+ err,
44
+ });
45
+ }
46
+
47
+ return { schemaForLLM, skipLLMCall };
48
+ };