@minded-ai/mindedjs 3.1.43-beta.3 → 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 (32) hide show
  1. package/dist/nodes/addAppToolNode.d.ts.map +1 -1
  2. package/dist/nodes/addAppToolNode.js +66 -14
  3. package/dist/nodes/addAppToolNode.js.map +1 -1
  4. package/dist/nodes/addBrowserTaskNode.d.ts.map +1 -1
  5. package/dist/nodes/addBrowserTaskNode.js +13 -3
  6. package/dist/nodes/addBrowserTaskNode.js.map +1 -1
  7. package/dist/nodes/addToolNode.d.ts.map +1 -1
  8. package/dist/nodes/addToolNode.js +29 -41
  9. package/dist/nodes/addToolNode.js.map +1 -1
  10. package/dist/nodes/addToolRunNode.d.ts.map +1 -1
  11. package/dist/nodes/addToolRunNode.js +93 -82
  12. package/dist/nodes/addToolRunNode.js.map +1 -1
  13. package/dist/nodes/compilePrompt.d.ts +0 -5
  14. package/dist/nodes/compilePrompt.d.ts.map +1 -1
  15. package/dist/nodes/compilePrompt.js +21 -46
  16. package/dist/nodes/compilePrompt.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/package.json +2 -2
  26. package/src/nodes/addAppToolNode.ts +68 -17
  27. package/src/nodes/addBrowserTaskNode.ts +13 -3
  28. package/src/nodes/addToolNode.ts +31 -48
  29. package/src/nodes/addToolRunNode.ts +114 -97
  30. package/src/nodes/compilePrompt.ts +24 -43
  31. package/src/nodes/utils/getSchemaForToolInference.ts +48 -0
  32. package/src/nodes/utils/inferToolCallWithRetry.ts +89 -0
@@ -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);
@@ -6,13 +6,6 @@ import { HistoryStep, TriggerHistoryStep } from '../types/Agent.types';
6
6
  import { State } from '../types/LangGraph.types';
7
7
  import { NodeType } from '../types/Flows.types';
8
8
 
9
- // Matches dotted path identifiers — allows letters, digits, `_`, `$`, `.`, `[`, `]`, spaces, and hyphens
10
- // (spaces/hyphens support node display names like "Greeting Prompt" and trigger keys like "some-key").
11
- // Implicitly rejects JSON braces (which contain `"` and `:`) since those chars are not in the set.
12
- const PLACEHOLDER_PATH_CHARS = String.raw`[A-Za-z0-9_.$\[\] -]`;
13
- const PLACEHOLDER_REGEX = new RegExp(String.raw`\{\s*([A-Za-z_$]${PLACEHOLDER_PATH_CHARS}*)\s*\}`, 'g');
14
- const SINGLE_PLACEHOLDER_REGEX = new RegExp(String.raw`^\{\s*([A-Za-z_$]${PLACEHOLDER_PATH_CHARS}*)\s*\}$`);
15
-
16
9
  /**
17
10
  * Extract node outputs from state.history and state.messages
18
11
  * Returns a map of nodeId/nodeDisplayName -> output object
@@ -136,45 +129,29 @@ export type ExtendedContextData = ContextData & {
136
129
  */
137
130
  export function compileParameter(parameter: string, context: ContextData = {}, inputKey?: string, inputSchema?: ZodSchema): any {
138
131
  try {
139
- return compileParameterWithContext(parameter, extendContextData(context), inputKey, inputSchema);
140
- } catch (err) {
141
- logger.error({ message: 'Error compiling parameter', err, parameter });
142
- return parameter;
143
- }
144
- }
132
+ context = extendContextData(context);
145
133
 
146
- /**
147
- * Recursively compile placeholders inside nested objects/arrays.
148
- * Top-level strings get full schema-based coercion; nested string leaves are compiled without schema coercion.
149
- */
150
- export function compileValueDeep(value: unknown, context: ContextData = {}, inputKey?: string, inputSchema?: ZodSchema): any {
151
- if (typeof value === 'string') return compileParameter(value, context, inputKey, inputSchema);
152
- if (!value || typeof value !== 'object') return value;
153
- const extCtx = extendContextData(context);
154
- return compileValueDeepInternal(value, extCtx);
155
- }
134
+ // First, render with EJS
135
+ let compiledParameter = ejs.render(parameter, context).trim();
156
136
 
157
- function compileValueDeepInternal(value: unknown, context: ExtendedContextData): any {
158
- if (typeof value === 'string') return compileParameterWithContext(value, context);
159
- if (!value || typeof value !== 'object') return value;
160
- if (Array.isArray(value)) return value.map((v) => compileValueDeepInternal(v, context));
161
- return Object.fromEntries(Object.entries(value as Record<string, unknown>).map(([k, v]) => [k, compileValueDeepInternal(v, context)]));
162
- }
137
+ // Then, replace placeholders in {} format
138
+ // If the result contains only one set of curly braces, get the native value at that key path, e.g., {state.memory.arrayOfStrings} -> state.memory.arrayOfStrings
139
+ // Otherwise, compile it into a string.
140
+ const keyPathMatch = compiledParameter.match(/^\{([^}]+)}$/);
141
+ if (keyPathMatch) {
142
+ const keyPath = keyPathMatch[1];
143
+ compiledParameter = getContextValueFromPath(keyPath, context, compiledParameter);
144
+ } else {
145
+ compiledParameter = replacePlaceholders(compiledParameter, context);
146
+ }
163
147
 
164
- /** Shared implementation expects already-extended context. */
165
- function compileParameterWithContext(parameter: string, context: ExtendedContextData, inputKey?: string, inputSchema?: ZodSchema): any {
166
- let compiledParameter = ejs.render(parameter, context).trim();
148
+ compiledParameter = coerceValueWithZodType(compiledParameter, inputKey, inputSchema);
167
149
 
168
- // Single placeholder → return native value; otherwise replace all placeholders as strings.
169
- // Regex matches path identifiers (allows spaces/hyphens for node display names), rejects JSON braces.
170
- const keyPathMatch = compiledParameter.match(SINGLE_PLACEHOLDER_REGEX);
171
- if (keyPathMatch) {
172
- compiledParameter = getContextValueFromPath(keyPathMatch[1].trim(), context, compiledParameter);
173
- } else {
174
- compiledParameter = replacePlaceholders(compiledParameter, context);
150
+ return compiledParameter;
151
+ } catch (err) {
152
+ logger.error({ message: 'Error compiling parameter', err, parameter });
153
+ return parameter; // Return uncompiled if there's an error
175
154
  }
176
-
177
- return coerceValueWithZodType(compiledParameter, inputKey, inputSchema);
178
155
  }
179
156
 
180
157
  /**
@@ -184,8 +161,12 @@ export function compilePrompt(prompt: string, context: ContextData): string {
184
161
  try {
185
162
  context = extendContextData(context);
186
163
 
164
+ // First, render with EJS
187
165
  let compiledPrompt = ejs.render(prompt, context);
166
+
167
+ // Then, replace placeholders in {} format
188
168
  compiledPrompt = replacePlaceholders(compiledPrompt, context);
169
+
189
170
  return compiledPrompt;
190
171
  } catch (err) {
191
172
  logger.error({ message: 'Error compiling prompt', err, prompt });
@@ -234,10 +215,10 @@ function extendContextData(context: ContextData = {}): ExtendedContextData {
234
215
  * - {tools.NodeName.field} - node reference with 'tools' prefix (alias)
235
216
  */
236
217
  function replacePlaceholders(text: string, context: Record<string, any>): string {
237
- return text.replace(PLACEHOLDER_REGEX, (match, keyPathRaw) => {
238
- const keyPath = String(keyPathRaw).trim();
218
+ return text.replace(/\{([^}]+)}/g, (match, keyPath) => {
239
219
  const value = getContextValueFromPath(keyPath, context, match);
240
220
 
221
+ // In the case of complex values (e.g., arrays or objects), JSON stringify them to ensure all data is preserved.
241
222
  if (Array.isArray(value) || (typeof value === 'object' && value !== null)) {
242
223
  try {
243
224
  return JSON.stringify(value);
@@ -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
+ };
@@ -0,0 +1,89 @@
1
+ import { AIMessage, BaseMessage, SystemMessage } from '@langchain/core/messages';
2
+ import { z } from 'zod';
3
+ import { logger } from '../../utils/logger';
4
+
5
+ interface InferToolCallWithRetryParams {
6
+ llm: any;
7
+ tool: { name: string };
8
+ inputSchema: z.ZodTypeAny;
9
+ messages: BaseMessage[];
10
+ sessionId: string;
11
+ mergeArgs?: (args: Record<string, any>) => Record<string, any>;
12
+ onAfterAttempt?: () => Promise<void>;
13
+ maxAttempts?: number;
14
+ toolNameForLogs: string;
15
+ }
16
+
17
+ export const inferToolCallWithRetry = async ({
18
+ llm,
19
+ tool,
20
+ inputSchema,
21
+ messages,
22
+ sessionId,
23
+ mergeArgs,
24
+ onAfterAttempt,
25
+ maxAttempts = 2,
26
+ toolNameForLogs,
27
+ }: InferToolCallWithRetryParams): Promise<AIMessage> => {
28
+ let inferenceMessages = [...messages];
29
+ let aiToolCallMessage: AIMessage = new AIMessage({ content: '', tool_calls: [] });
30
+
31
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
32
+ aiToolCallMessage = await llm.bindTools([tool], { tool_choice: tool.name }).invoke(inferenceMessages);
33
+ if (onAfterAttempt) {
34
+ await onAfterAttempt();
35
+ }
36
+
37
+ if (!aiToolCallMessage.tool_calls || aiToolCallMessage.tool_calls.length === 0) {
38
+ throw new Error('No tool calls generated by LLM');
39
+ }
40
+
41
+ const inferredArgs = (aiToolCallMessage.tool_calls[0].args || {}) as Record<string, any>;
42
+ const mergedArgs = mergeArgs ? mergeArgs(inferredArgs) : inferredArgs;
43
+ const parseResult = inputSchema.safeParse(mergedArgs);
44
+
45
+ if (parseResult.success) {
46
+ aiToolCallMessage.tool_calls[0].args = parseResult.data;
47
+ return aiToolCallMessage;
48
+ }
49
+
50
+ if (attempt < maxAttempts) {
51
+ const validationIssues = parseResult.error.issues
52
+ .map((issue: z.ZodIssue) => `${issue.path.join('.') || '<root>'}: ${issue.message}`)
53
+ .join('; ');
54
+
55
+ logger.error({
56
+ msg: '[Tool] Retrying parameter inference due to schema mismatch',
57
+ tool: toolNameForLogs,
58
+ sessionId,
59
+ attempt,
60
+ validationIssues,
61
+ });
62
+
63
+ inferenceMessages = [
64
+ ...inferenceMessages,
65
+ new SystemMessage(
66
+ `Your previous tool arguments were invalid for tool "${tool.name}".
67
+ Validation issues: ${validationIssues}
68
+ Return arguments that strictly match the tool schema types. Do not wrap scalar values in arrays.`,
69
+ ),
70
+ ];
71
+ continue;
72
+ }
73
+
74
+ logger.error({
75
+ msg: '[Tool] Inferred parameters remain schema-invalid after retries',
76
+ tool: toolNameForLogs,
77
+ sessionId,
78
+ validationIssues: parseResult.error.issues.map((issue: z.ZodIssue) => ({
79
+ path: issue.path.join('.') || '<root>',
80
+ message: issue.message,
81
+ })),
82
+ });
83
+
84
+ aiToolCallMessage.tool_calls[0].args = mergedArgs;
85
+ return aiToolCallMessage;
86
+ }
87
+
88
+ return aiToolCallMessage;
89
+ };