illuma-agents 1.0.37 → 1.0.38

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 (125) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +69 -14
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/common/enum.cjs +3 -1
  4. package/dist/cjs/common/enum.cjs.map +1 -1
  5. package/dist/cjs/graphs/Graph.cjs +50 -8
  6. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  7. package/dist/cjs/graphs/MultiAgentGraph.cjs +277 -11
  8. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  9. package/dist/cjs/llm/bedrock/index.cjs +128 -61
  10. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  11. package/dist/cjs/main.cjs +16 -7
  12. package/dist/cjs/main.cjs.map +1 -1
  13. package/dist/cjs/messages/cache.cjs +1 -0
  14. package/dist/cjs/messages/cache.cjs.map +1 -1
  15. package/dist/cjs/messages/core.cjs +1 -1
  16. package/dist/cjs/messages/core.cjs.map +1 -1
  17. package/dist/cjs/messages/tools.cjs +2 -2
  18. package/dist/cjs/messages/tools.cjs.map +1 -1
  19. package/dist/cjs/stream.cjs +4 -2
  20. package/dist/cjs/stream.cjs.map +1 -1
  21. package/dist/cjs/tools/BrowserTools.cjs.map +1 -1
  22. package/dist/cjs/tools/CodeExecutor.cjs +22 -21
  23. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  24. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +14 -11
  25. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  26. package/dist/cjs/tools/ToolNode.cjs +101 -2
  27. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  28. package/dist/cjs/tools/ToolSearch.cjs +862 -0
  29. package/dist/cjs/tools/ToolSearch.cjs.map +1 -0
  30. package/dist/esm/agents/AgentContext.mjs +69 -14
  31. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  32. package/dist/esm/common/enum.mjs +3 -1
  33. package/dist/esm/common/enum.mjs.map +1 -1
  34. package/dist/esm/graphs/Graph.mjs +51 -9
  35. package/dist/esm/graphs/Graph.mjs.map +1 -1
  36. package/dist/esm/graphs/MultiAgentGraph.mjs +278 -12
  37. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  38. package/dist/esm/llm/bedrock/index.mjs +127 -60
  39. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  40. package/dist/esm/main.mjs +1 -1
  41. package/dist/esm/messages/cache.mjs +1 -0
  42. package/dist/esm/messages/cache.mjs.map +1 -1
  43. package/dist/esm/messages/core.mjs +1 -1
  44. package/dist/esm/messages/core.mjs.map +1 -1
  45. package/dist/esm/messages/tools.mjs +2 -2
  46. package/dist/esm/messages/tools.mjs.map +1 -1
  47. package/dist/esm/stream.mjs +4 -2
  48. package/dist/esm/stream.mjs.map +1 -1
  49. package/dist/esm/tools/BrowserTools.mjs.map +1 -1
  50. package/dist/esm/tools/CodeExecutor.mjs +22 -21
  51. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  52. package/dist/esm/tools/ProgrammaticToolCalling.mjs +14 -11
  53. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  54. package/dist/esm/tools/ToolNode.mjs +102 -3
  55. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  56. package/dist/esm/tools/ToolSearch.mjs +827 -0
  57. package/dist/esm/tools/ToolSearch.mjs.map +1 -0
  58. package/dist/types/agents/AgentContext.d.ts +33 -1
  59. package/dist/types/common/enum.d.ts +4 -2
  60. package/dist/types/graphs/Graph.d.ts +6 -0
  61. package/dist/types/graphs/MultiAgentGraph.d.ts +16 -0
  62. package/dist/types/index.d.ts +1 -1
  63. package/dist/types/llm/bedrock/index.d.ts +89 -11
  64. package/dist/types/llm/bedrock/types.d.ts +27 -0
  65. package/dist/types/llm/bedrock/utils/index.d.ts +5 -0
  66. package/dist/types/llm/bedrock/utils/message_inputs.d.ts +31 -0
  67. package/dist/types/llm/bedrock/utils/message_outputs.d.ts +33 -0
  68. package/dist/types/tools/CodeExecutor.d.ts +0 -3
  69. package/dist/types/tools/ProgrammaticToolCalling.d.ts +0 -3
  70. package/dist/types/tools/ToolNode.d.ts +3 -1
  71. package/dist/types/tools/ToolSearch.d.ts +148 -0
  72. package/dist/types/types/graph.d.ts +2 -0
  73. package/dist/types/types/llm.d.ts +3 -1
  74. package/dist/types/types/tools.d.ts +42 -2
  75. package/package.json +12 -5
  76. package/src/agents/AgentContext.ts +88 -16
  77. package/src/common/enum.ts +3 -1
  78. package/src/graphs/Graph.ts +64 -13
  79. package/src/graphs/MultiAgentGraph.ts +350 -13
  80. package/src/index.ts +1 -1
  81. package/src/llm/bedrock/index.ts +221 -99
  82. package/src/llm/bedrock/llm.spec.ts +616 -0
  83. package/src/llm/bedrock/types.ts +51 -0
  84. package/src/llm/bedrock/utils/index.ts +18 -0
  85. package/src/llm/bedrock/utils/message_inputs.ts +563 -0
  86. package/src/llm/bedrock/utils/message_outputs.ts +310 -0
  87. package/src/messages/__tests__/tools.test.ts +21 -21
  88. package/src/messages/cache.test.ts +259 -0
  89. package/src/messages/cache.ts +104 -1
  90. package/src/messages/core.ts +1 -1
  91. package/src/messages/tools.ts +2 -2
  92. package/src/scripts/caching.ts +27 -19
  93. package/src/scripts/code_exec_files.ts +58 -15
  94. package/src/scripts/code_exec_multi_session.ts +241 -0
  95. package/src/scripts/code_exec_session.ts +282 -0
  96. package/src/scripts/multi-agent-conditional.ts +1 -0
  97. package/src/scripts/multi-agent-supervisor.ts +1 -0
  98. package/src/scripts/programmatic_exec_agent.ts +4 -4
  99. package/src/scripts/test-handoff-preamble.ts +277 -0
  100. package/src/scripts/test-parallel-handoffs.ts +291 -0
  101. package/src/scripts/test-tools-before-handoff.ts +8 -4
  102. package/src/scripts/test_code_api.ts +361 -0
  103. package/src/scripts/thinking-bedrock.ts +159 -0
  104. package/src/scripts/thinking.ts +39 -18
  105. package/src/scripts/{tool_search_regex.ts → tool_search.ts} +5 -5
  106. package/src/scripts/tools.ts +7 -3
  107. package/src/stream.ts +4 -2
  108. package/src/tools/BrowserTools.ts +39 -17
  109. package/src/tools/CodeExecutor.ts +26 -23
  110. package/src/tools/ProgrammaticToolCalling.ts +18 -14
  111. package/src/tools/ToolNode.ts +114 -1
  112. package/src/tools/ToolSearch.ts +1041 -0
  113. package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +0 -2
  114. package/src/tools/__tests__/{ToolSearchRegex.integration.test.ts → ToolSearch.integration.test.ts} +6 -6
  115. package/src/tools/__tests__/ToolSearch.test.ts +1003 -0
  116. package/src/types/graph.ts +2 -0
  117. package/src/types/llm.ts +3 -1
  118. package/src/types/tools.ts +51 -2
  119. package/dist/cjs/tools/ToolSearchRegex.cjs +0 -455
  120. package/dist/cjs/tools/ToolSearchRegex.cjs.map +0 -1
  121. package/dist/esm/tools/ToolSearchRegex.mjs +0 -448
  122. package/dist/esm/tools/ToolSearchRegex.mjs.map +0 -1
  123. package/dist/types/tools/ToolSearchRegex.d.ts +0 -80
  124. package/src/tools/ToolSearchRegex.ts +0 -535
  125. package/src/tools/__tests__/ToolSearchRegex.test.ts +0 -232
@@ -18,9 +18,13 @@ async function testStandardStreaming(): Promise<void> {
18
18
  const { userName, location, provider, currentDate } = await getArgs();
19
19
  const { contentParts, aggregateContent } = createContentAggregator();
20
20
  const customHandlers = {
21
- [GraphEvents.TOOL_END]: new ToolEndHandler(undefined, (name?: string) => {
22
- return true;
23
- }),
21
+ [GraphEvents.TOOL_END]: new ToolEndHandler(
22
+ undefined,
23
+ undefined,
24
+ (name?: string) => {
25
+ return true;
26
+ }
27
+ ),
24
28
  [GraphEvents.CHAT_MODEL_END]: {
25
29
  handle: (
26
30
  _event: string,
package/src/stream.ts CHANGED
@@ -339,7 +339,8 @@ hasToolCallChunks: ${hasToolCallChunks}
339
339
  (c) =>
340
340
  (c.type?.startsWith(ContentTypes.THINKING) ?? false) ||
341
341
  (c.type?.startsWith(ContentTypes.REASONING) ?? false) ||
342
- (c.type?.startsWith(ContentTypes.REASONING_CONTENT) ?? false)
342
+ (c.type?.startsWith(ContentTypes.REASONING_CONTENT) ?? false) ||
343
+ c.type === 'redacted_thinking'
343
344
  )
344
345
  ) {
345
346
  await graph.dispatchReasoningDelta(stepId, {
@@ -365,7 +366,8 @@ hasToolCallChunks: ${hasToolCallChunks}
365
366
  Array.isArray(chunk.content) &&
366
367
  (chunk.content[0]?.type === ContentTypes.THINKING ||
367
368
  chunk.content[0]?.type === ContentTypes.REASONING ||
368
- chunk.content[0]?.type === ContentTypes.REASONING_CONTENT)
369
+ chunk.content[0]?.type === ContentTypes.REASONING_CONTENT ||
370
+ chunk.content[0]?.type === 'redacted_thinking')
369
371
  ) {
370
372
  reasoning_content = 'valid';
371
373
  } else if (
@@ -83,12 +83,16 @@ const BrowserClickSchema = z.object({
83
83
  const BrowserTypeSchema = z.object({
84
84
  index: z
85
85
  .number()
86
- .describe('The [index] of the INPUT element to type into. Target <input> or <textarea> elements. Check fieldLabel to identify the correct field.'),
86
+ .describe(
87
+ 'The [index] of the INPUT element to type into. Target <input> or <textarea> elements. Check fieldLabel to identify the correct field.'
88
+ ),
87
89
  text: z.string().describe('The text to type into the element'),
88
90
  pressEnter: z
89
91
  .boolean()
90
92
  .optional()
91
- .describe('Whether to press Enter after typing (useful for search forms and submitting)'),
93
+ .describe(
94
+ 'Whether to press Enter after typing (useful for search forms and submitting)'
95
+ ),
92
96
  });
93
97
 
94
98
  const BrowserNavigateSchema = z.object({
@@ -134,13 +138,17 @@ const BrowserGetPageStateSchema = z.object({});
134
138
  const BrowserKeypressSchema = z.object({
135
139
  keys: z
136
140
  .string()
137
- .describe('Keyboard keys to press. Use "+" to combine modifiers (e.g., "Control+Enter", "Control+a", "Escape", "Tab", "Enter"). Common shortcuts: Control+Enter (submit forms/send), Escape (close dialogs), Tab (next field).'),
141
+ .describe(
142
+ 'Keyboard keys to press. Use "+" to combine modifiers (e.g., "Control+Enter", "Control+a", "Escape", "Tab", "Enter"). Common shortcuts: Control+Enter (submit forms/send), Escape (close dialogs), Tab (next field).'
143
+ ),
138
144
  });
139
145
 
140
146
  const BrowserSwitchTabSchema = z.object({
141
147
  tabId: z
142
148
  .number()
143
- .describe('The tab ID to switch to. Use the tab IDs shown in the tabs list from page state.'),
149
+ .describe(
150
+ 'The tab ID to switch to. Use the tab IDs shown in the tabs list from page state.'
151
+ ),
144
152
  });
145
153
 
146
154
  /**
@@ -187,7 +195,9 @@ function formatResultForLLM(
187
195
  }
188
196
  if (result.elementList != null && result.elementList !== '') {
189
197
  // Add hint about fieldLabel and targeting inputs for form interactions
190
- parts.push(`\n**Interactive Elements** (for typing: target <input> elements with fieldLabel, NOT parent <div> containers):\n${result.elementList}`);
198
+ parts.push(
199
+ `\n**Interactive Elements** (for typing: target <input> elements with fieldLabel, NOT parent <div> containers):\n${result.elementList}`
200
+ );
191
201
  }
192
202
  if (result.screenshot != null && result.screenshot !== '') {
193
203
  parts.push('\n[Screenshot captured and displayed to user]');
@@ -263,7 +273,8 @@ export function createBrowserTools(
263
273
  tools.push(
264
274
  tool(createToolFunction('click'), {
265
275
  name: EBrowserTools.CLICK,
266
- description: 'Click element by [index]. Use fieldLabel attribute to identify correct element. For form fields, target <input> elements NOT parent <div> containers.',
276
+ description:
277
+ 'Click element by [index]. Use fieldLabel attribute to identify correct element. For form fields, target <input> elements NOT parent <div> containers.',
267
278
  schema: BrowserClickSchema,
268
279
  })
269
280
  );
@@ -272,7 +283,8 @@ export function createBrowserTools(
272
283
  tools.push(
273
284
  tool(createToolFunction('type'), {
274
285
  name: EBrowserTools.TYPE,
275
- description: 'Type text into <input> element by [index]. CRITICAL: Always target <input> or <textarea> tags (NOT parent <div> containers). Use fieldLabel to identify correct field (e.g., fieldLabel="To recipients" for To field).',
286
+ description:
287
+ 'Type text into <input> element by [index]. CRITICAL: Always target <input> or <textarea> tags (NOT parent <div> containers). Use fieldLabel to identify correct field (e.g., fieldLabel="To recipients" for To field).',
276
288
  schema: BrowserTypeSchema,
277
289
  })
278
290
  );
@@ -281,7 +293,8 @@ export function createBrowserTools(
281
293
  tools.push(
282
294
  tool(createToolFunction('navigate'), {
283
295
  name: EBrowserTools.NAVIGATE,
284
- description: 'Navigate to URL (include https://). Returns new page element list.',
296
+ description:
297
+ 'Navigate to URL (include https://). Returns new page element list.',
285
298
  schema: BrowserNavigateSchema,
286
299
  })
287
300
  );
@@ -290,7 +303,8 @@ export function createBrowserTools(
290
303
  tools.push(
291
304
  tool(createToolFunction('scroll'), {
292
305
  name: EBrowserTools.SCROLL,
293
- description: 'Scroll page (up/down/left/right). Returns updated element list.',
306
+ description:
307
+ 'Scroll page (up/down/left/right). Returns updated element list.',
294
308
  schema: BrowserScrollSchema,
295
309
  })
296
310
  );
@@ -299,7 +313,8 @@ export function createBrowserTools(
299
313
  tools.push(
300
314
  tool(createToolFunction('extract'), {
301
315
  name: EBrowserTools.EXTRACT,
302
- description: 'Extract page content. Returns URL, title, and element list.',
316
+ description:
317
+ 'Extract page content. Returns URL, title, and element list.',
303
318
  schema: BrowserExtractSchema,
304
319
  })
305
320
  );
@@ -308,7 +323,8 @@ export function createBrowserTools(
308
323
  tools.push(
309
324
  tool(createToolFunction('hover'), {
310
325
  name: EBrowserTools.HOVER,
311
- description: 'Hover element by [index] to reveal menus/tooltips. Returns updated element list.',
326
+ description:
327
+ 'Hover element by [index] to reveal menus/tooltips. Returns updated element list.',
312
328
  schema: BrowserHoverSchema,
313
329
  })
314
330
  );
@@ -317,7 +333,8 @@ export function createBrowserTools(
317
333
  tools.push(
318
334
  tool(createToolFunction('wait'), {
319
335
  name: EBrowserTools.WAIT,
320
- description: 'Wait for async content to load. Returns updated element list.',
336
+ description:
337
+ 'Wait for async content to load. Returns updated element list.',
321
338
  schema: BrowserWaitSchema,
322
339
  })
323
340
  );
@@ -326,7 +343,8 @@ export function createBrowserTools(
326
343
  tools.push(
327
344
  tool(createToolFunction('back'), {
328
345
  name: EBrowserTools.BACK,
329
- description: 'Go back in browser history. Returns previous page element list.',
346
+ description:
347
+ 'Go back in browser history. Returns previous page element list.',
330
348
  schema: BrowserBackSchema,
331
349
  })
332
350
  );
@@ -335,7 +353,8 @@ export function createBrowserTools(
335
353
  tools.push(
336
354
  tool(createToolFunction('screenshot'), {
337
355
  name: EBrowserTools.SCREENSHOT,
338
- description: 'Capture screenshot. Displayed to user. Use get_page_state for automation.',
356
+ description:
357
+ 'Capture screenshot. Displayed to user. Use get_page_state for automation.',
339
358
  schema: BrowserScreenshotSchema,
340
359
  })
341
360
  );
@@ -344,7 +363,8 @@ export function createBrowserTools(
344
363
  tools.push(
345
364
  tool(createToolFunction('get_page_state'), {
346
365
  name: EBrowserTools.GET_PAGE_STATE,
347
- description: 'Get page URL, title, and interactive elements with [index] for actions. Start here.',
366
+ description:
367
+ 'Get page URL, title, and interactive elements with [index] for actions. Start here.',
348
368
  schema: BrowserGetPageStateSchema,
349
369
  })
350
370
  );
@@ -353,7 +373,8 @@ export function createBrowserTools(
353
373
  tools.push(
354
374
  tool(createToolFunction('keypress'), {
355
375
  name: EBrowserTools.KEYPRESS,
356
- description: 'Send keyboard shortcut or key press. Use for: Control+Enter (send email/submit), Escape (close dialog/cancel), Tab (next field), Enter (confirm). The keys are sent to the currently focused element.',
376
+ description:
377
+ 'Send keyboard shortcut or key press. Use for: Control+Enter (send email/submit), Escape (close dialog/cancel), Tab (next field), Enter (confirm). The keys are sent to the currently focused element.',
357
378
  schema: BrowserKeypressSchema,
358
379
  })
359
380
  );
@@ -362,7 +383,8 @@ export function createBrowserTools(
362
383
  tools.push(
363
384
  tool(createToolFunction('switch_tab'), {
364
385
  name: EBrowserTools.SWITCH_TAB,
365
- description: 'Switch to a different browser tab by its ID. Tab IDs are shown in the page state. Use this to work with existing open tabs (e.g., use existing Gmail tab instead of opening a new one).',
386
+ description:
387
+ 'Switch to a different browser tab by its ID. Tab IDs are shown in the page state. Use this to work with existing open tabs (e.g., use existing Gmail tab instead of opening a new one).',
366
388
  schema: BrowserSwitchTabSchema,
367
389
  })
368
390
  );
@@ -17,7 +17,7 @@ export const getCodeBaseURL = (): string =>
17
17
  const imageMessage = 'Image is already displayed to the user';
18
18
  const otherMessage = 'File is already downloaded by the user';
19
19
  const accessMessage =
20
- 'Note: Files are READ-ONLY. Save changes to NEW filenames. To access these files in future executions, provide the `session_id` as a parameter (not in your code).';
20
+ 'Note: Files from previous executions are automatically available and can be modified.';
21
21
  const emptyOutputMessage =
22
22
  'stdout: Empty. Ensure you\'re writing output explicitly.\n';
23
23
 
@@ -41,7 +41,8 @@ const CodeExecutionToolSchema = z.object({
41
41
  code: z.string()
42
42
  .describe(`The complete, self-contained code to execute, without any truncation or minimization.
43
43
  - The environment is stateless; variables and imports don't persist between executions.
44
- - When using \`session_id\`: Don't hardcode it in \`code\`, and write file modifications to NEW filenames (files are READ-ONLY).
44
+ - Generated files from previous executions are automatically available in "/mnt/data/".
45
+ - Files from previous executions are automatically available and can be modified in place.
45
46
  - Input code **IS ALREADY** displayed to the user, so **DO NOT** repeat it in your response unless asked.
46
47
  - Output code **IS NOT** displayed to the user, so **DO** write all desired output explicitly.
47
48
  - IMPORTANT: You MUST explicitly print/output ALL results you want the user to see.
@@ -50,17 +51,6 @@ const CodeExecutionToolSchema = z.object({
50
51
  - js: use the \`console\` or \`process\` methods for all outputs.
51
52
  - r: IMPORTANT: No X11 display available. ALL graphics MUST use Cairo library (library(Cairo)).
52
53
  - Other languages: use appropriate output functions.`),
53
- session_id: z
54
- .string()
55
- .optional()
56
- .describe(
57
- `Session ID from a previous response to access generated files.
58
- - Files load into the current working directory ("/mnt/data/")
59
- - Use relative paths ONLY
60
- - Files are READ-ONLY and cannot be modified in-place
61
- - To modify: read original file, write to NEW filename
62
- `.trim()
63
- ),
64
54
  args: z
65
55
  .array(z.string())
66
56
  .optional()
@@ -107,15 +97,33 @@ Rules:
107
97
  `.trim();
108
98
 
109
99
  return tool<typeof CodeExecutionToolSchema>(
110
- async ({ lang, code, session_id, ...rest }) => {
111
- const postData = {
100
+ async ({ lang, code, ...rest }, config) => {
101
+ /**
102
+ * Extract session context from config.toolCall (injected by ToolNode).
103
+ * - session_id: For API to associate with previous session
104
+ * - _injected_files: File refs to pass directly (avoids /files endpoint race condition)
105
+ */
106
+ const { session_id, _injected_files } = (config.toolCall ?? {}) as {
107
+ session_id?: string;
108
+ _injected_files?: t.CodeEnvFile[];
109
+ };
110
+
111
+ const postData: Record<string, unknown> = {
112
112
  lang,
113
113
  code,
114
114
  ...rest,
115
115
  ...params,
116
116
  };
117
117
 
118
- if (session_id != null && session_id.length > 0) {
118
+ /**
119
+ * File injection priority:
120
+ * 1. Use _injected_files from ToolNode (avoids /files endpoint race condition)
121
+ * 2. Fall back to fetching from /files endpoint if session_id provided but no injected files
122
+ */
123
+ if (_injected_files && _injected_files.length > 0) {
124
+ postData.files = _injected_files;
125
+ } else if (session_id != null && session_id.length > 0) {
126
+ /** Fallback: fetch from /files endpoint (may have race condition issues) */
119
127
  try {
120
128
  const filesEndpoint = `${baseEndpoint}/files/${session_id}?detail=full`;
121
129
  const fetchOptions: RequestInit = {
@@ -140,7 +148,6 @@ Rules:
140
148
  const files = await response.json();
141
149
  if (Array.isArray(files) && files.length > 0) {
142
150
  const fileReferences: t.CodeEnvFile[] = files.map((file) => {
143
- // Extract the ID from the file name (part after session ID prefix and before extension)
144
151
  const nameParts = file.name.split('/');
145
152
  const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';
146
153
 
@@ -151,11 +158,7 @@ Rules:
151
158
  };
152
159
  });
153
160
 
154
- if (!postData.files) {
155
- postData.files = fileReferences;
156
- } else if (Array.isArray(postData.files)) {
157
- postData.files = [...postData.files, ...fileReferences];
158
- }
161
+ postData.files = fileReferences;
159
162
  }
160
163
  } catch {
161
164
  // eslint-disable-next-line no-console
@@ -204,7 +207,7 @@ Rules:
204
207
  }
205
208
  }
206
209
 
207
- formattedOutput += `\nsession_id: ${result.session_id}\n\n${accessMessage}`;
210
+ formattedOutput += `\n\n${accessMessage}`;
208
211
  return [
209
212
  formattedOutput.trim(),
210
213
  {
@@ -19,7 +19,7 @@ config();
19
19
  const imageMessage = 'Image is already displayed to the user';
20
20
  const otherMessage = 'File is already downloaded by the user';
21
21
  const accessMessage =
22
- 'Note: Files are READ-ONLY. Save changes to NEW filenames. To access these files in future executions, provide the `session_id` as a parameter (not in your code).';
22
+ 'Note: Files from previous executions are automatically available and can be modified.';
23
23
  const emptyOutputMessage =
24
24
  'stdout: Empty. Ensure you\'re writing output explicitly.\n';
25
25
 
@@ -68,12 +68,6 @@ Rules:
68
68
  - Tools are pre-defined—DO NOT write function definitions
69
69
  - Only print() output returns to the model`
70
70
  ),
71
- session_id: z
72
- .string()
73
- .optional()
74
- .describe(
75
- 'Session ID for file access (same as regular code execution). Files load into /mnt/data/ and are READ-ONLY.'
76
- ),
77
71
  timeout: z
78
72
  .number()
79
73
  .int()
@@ -542,7 +536,7 @@ export function formatCompletedResponse(
542
536
  }
543
537
  }
544
538
 
545
- formatted += `\nsession_id: ${response.session_id}\n\n${accessMessage}`;
539
+ formatted += `\n\n${accessMessage}`;
546
540
  }
547
541
 
548
542
  return [
@@ -613,7 +607,7 @@ Rules:
613
607
  - Do NOT define \`async def main()\` or call \`asyncio.run()\`—just write code with await
614
608
  - Tools are pre-defined—DO NOT write function definitions
615
609
  - Only \`print()\` output returns; tool results are raw dicts/lists/strings
616
- - Use \`session_id\` param for file persistence across calls
610
+ - Generated files are automatically available in /mnt/data/ for subsequent executions
617
611
  - Tool names normalized: hyphens→underscores, keywords get \`_tool\` suffix
618
612
 
619
613
  When to use: loops, conditionals, parallel (\`asyncio.gather\`), multi-step pipelines.
@@ -624,11 +618,15 @@ Example (complete pipeline):
624
618
 
625
619
  return tool<typeof ProgrammaticToolCallingSchema>(
626
620
  async (params, config) => {
627
- const { code, session_id, timeout = DEFAULT_TIMEOUT } = params;
621
+ const { code, timeout = DEFAULT_TIMEOUT } = params;
628
622
 
629
623
  // Extra params injected by ToolNode (follows web_search pattern)
630
- const { toolMap, toolDefs } = (config.toolCall ?? {}) as ToolCall &
631
- Partial<t.ProgrammaticCache>;
624
+ const { toolMap, toolDefs, session_id, _injected_files } =
625
+ (config.toolCall ?? {}) as ToolCall &
626
+ Partial<t.ProgrammaticCache> & {
627
+ session_id?: string;
628
+ _injected_files?: t.CodeEnvFile[];
629
+ };
632
630
 
633
631
  if (toolMap == null || toolMap.size === 0) {
634
632
  throw new Error(
@@ -661,9 +659,15 @@ Example (complete pipeline):
661
659
  );
662
660
  }
663
661
 
664
- // Fetch files from previous session if session_id is provided
662
+ /**
663
+ * File injection priority:
664
+ * 1. Use _injected_files from ToolNode (avoids /files endpoint race condition)
665
+ * 2. Fall back to fetching from /files endpoint if session_id provided but no injected files
666
+ */
665
667
  let files: t.CodeEnvFile[] | undefined;
666
- if (session_id != null && session_id.length > 0) {
668
+ if (_injected_files && _injected_files.length > 0) {
669
+ files = _injected_files;
670
+ } else if (session_id != null && session_id.length > 0) {
667
671
  files = await fetchSessionFiles(baseUrl, apiKey, session_id, proxy);
668
672
  }
669
673
 
@@ -80,6 +80,8 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
80
80
  private toolRegistry?: t.LCToolRegistry;
81
81
  /** Cached programmatic tools (computed once on first PTC call) */
82
82
  private programmaticCache?: t.ProgrammaticCache;
83
+ /** Reference to Graph's sessions map for automatic session injection */
84
+ private sessions?: t.ToolSessionMap;
83
85
 
84
86
  constructor({
85
87
  tools,
@@ -91,6 +93,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
91
93
  handleToolErrors,
92
94
  loadRuntimeTools,
93
95
  toolRegistry,
96
+ sessions,
94
97
  }: t.ToolNodeConstructorParams) {
95
98
  super({ name, tags, func: (input, config) => this.run(input, config) });
96
99
  this.toolMap = toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));
@@ -100,6 +103,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
100
103
  this.errorHandler = errorHandler;
101
104
  this.toolUsageCount = new Map<string, number>();
102
105
  this.toolRegistry = toolRegistry;
106
+ this.sessions = sessions;
103
107
  }
104
108
 
105
109
  /**
@@ -170,13 +174,46 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
170
174
  toolMap,
171
175
  toolDefs,
172
176
  };
173
- } else if (call.name === Constants.TOOL_SEARCH_REGEX) {
177
+ } else if (call.name === Constants.TOOL_SEARCH) {
174
178
  invokeParams = {
175
179
  ...invokeParams,
176
180
  toolRegistry: this.toolRegistry,
177
181
  };
178
182
  }
179
183
 
184
+ /**
185
+ * Inject session context for code execution tools when available.
186
+ * Each file uses its own session_id (supporting multi-session file tracking).
187
+ * Both session_id and _injected_files are injected directly to invokeParams
188
+ * (not inside args) so they bypass Zod schema validation and reach config.toolCall.
189
+ */
190
+ if (
191
+ call.name === Constants.EXECUTE_CODE ||
192
+ call.name === Constants.PROGRAMMATIC_TOOL_CALLING
193
+ ) {
194
+ const codeSession = this.sessions?.get(Constants.EXECUTE_CODE) as
195
+ | t.CodeSessionContext
196
+ | undefined;
197
+ if (codeSession?.files != null && codeSession.files.length > 0) {
198
+ /**
199
+ * Convert tracked files to CodeEnvFile format for the API.
200
+ * Each file uses its own session_id (set when file was created).
201
+ * This supports files from multiple parallel/sequential executions.
202
+ */
203
+ const fileRefs: t.CodeEnvFile[] = codeSession.files.map((file) => ({
204
+ session_id: file.session_id ?? codeSession.session_id,
205
+ id: file.id,
206
+ name: file.name,
207
+ }));
208
+ /** Inject latest session_id and files - bypasses Zod, reaches config.toolCall */
209
+ invokeParams = {
210
+ ...invokeParams,
211
+ session_id: codeSession.session_id,
212
+ _injected_files: fileRefs,
213
+ };
214
+ }
215
+ }
216
+
180
217
  const output = await tool.invoke(invokeParams, config);
181
218
 
182
219
  // Handle Command outputs directly
@@ -354,6 +391,13 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
354
391
  )[] = [];
355
392
  let parentCommand: Command | null = null;
356
393
 
394
+ /**
395
+ * Collect handoff commands (Commands with string goto and Command.PARENT)
396
+ * for potential parallel handoff aggregation
397
+ */
398
+ const handoffCommands: Command[] = [];
399
+ const nonCommandOutputs: BaseMessage[] = [];
400
+
357
401
  for (const output of outputs) {
358
402
  if (isCommand(output)) {
359
403
  if (
@@ -361,6 +405,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
361
405
  Array.isArray(output.goto) &&
362
406
  output.goto.every((send): send is Send => isSend(send))
363
407
  ) {
408
+ /** Aggregate Send-based commands */
364
409
  if (parentCommand) {
365
410
  (parentCommand.goto as Send[]).push(...(output.goto as Send[]));
366
411
  } else {
@@ -369,16 +414,84 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
369
414
  goto: output.goto,
370
415
  });
371
416
  }
417
+ } else if (output.graph === Command.PARENT) {
418
+ /**
419
+ * Handoff Command with destination.
420
+ * Handle both string ('agent') and array (['agent']) formats.
421
+ * Collect for potential parallel aggregation.
422
+ */
423
+ const goto = output.goto;
424
+ const isSingleStringDest = typeof goto === 'string';
425
+ const isSingleArrayDest =
426
+ Array.isArray(goto) &&
427
+ goto.length === 1 &&
428
+ typeof goto[0] === 'string';
429
+
430
+ if (isSingleStringDest || isSingleArrayDest) {
431
+ handoffCommands.push(output);
432
+ } else {
433
+ /** Multi-destination or other command - pass through */
434
+ combinedOutputs.push(output);
435
+ }
372
436
  } else {
437
+ /** Other commands - pass through */
373
438
  combinedOutputs.push(output);
374
439
  }
375
440
  } else {
441
+ nonCommandOutputs.push(output);
376
442
  combinedOutputs.push(
377
443
  Array.isArray(input) ? [output] : { messages: [output] }
378
444
  );
379
445
  }
380
446
  }
381
447
 
448
+ /**
449
+ * Handle handoff commands - convert to Send objects for parallel execution
450
+ * when multiple handoffs are requested
451
+ */
452
+ if (handoffCommands.length > 1) {
453
+ /**
454
+ * Multiple parallel handoffs - convert to Send objects.
455
+ * Each Send carries its own state with the appropriate messages.
456
+ * This enables LLM-initiated parallel execution when calling multiple
457
+ * transfer tools simultaneously.
458
+ */
459
+
460
+ /** Collect all destinations for sibling tracking */
461
+ const allDestinations = handoffCommands.map((cmd) => {
462
+ const goto = cmd.goto;
463
+ return typeof goto === 'string' ? goto : (goto as string[])[0];
464
+ });
465
+
466
+ const sends = handoffCommands.map((cmd, idx) => {
467
+ const destination = allDestinations[idx];
468
+ /** Get siblings (other destinations, not this one) */
469
+ const siblings = allDestinations.filter((d) => d !== destination);
470
+
471
+ /** Add siblings to ToolMessage additional_kwargs */
472
+ const update = cmd.update as { messages?: BaseMessage[] } | undefined;
473
+ if (update && update.messages) {
474
+ for (const msg of update.messages) {
475
+ if (msg.getType() === 'tool') {
476
+ (msg as ToolMessage).additional_kwargs.handoff_parallel_siblings =
477
+ siblings;
478
+ }
479
+ }
480
+ }
481
+
482
+ return new Send(destination, cmd.update);
483
+ });
484
+
485
+ const parallelCommand = new Command({
486
+ graph: Command.PARENT,
487
+ goto: sends,
488
+ });
489
+ combinedOutputs.push(parallelCommand);
490
+ } else if (handoffCommands.length === 1) {
491
+ /** Single handoff - pass through as-is */
492
+ combinedOutputs.push(handoffCommands[0]);
493
+ }
494
+
382
495
  if (parentCommand) {
383
496
  combinedOutputs.push(parentCommand);
384
497
  }