illuma-agents 1.0.36 → 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.
- package/dist/cjs/agents/AgentContext.cjs +69 -14
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/common/enum.cjs +3 -1
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +50 -8
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +277 -11
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/index.cjs +128 -61
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +16 -7
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/cache.cjs +1 -0
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/messages/core.cjs +1 -1
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/messages/tools.cjs +2 -2
- package/dist/cjs/messages/tools.cjs.map +1 -1
- package/dist/cjs/stream.cjs +4 -2
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/BrowserTools.cjs +27 -3
- package/dist/cjs/tools/BrowserTools.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +22 -21
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +14 -11
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +101 -2
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/ToolSearch.cjs +862 -0
- package/dist/cjs/tools/ToolSearch.cjs.map +1 -0
- package/dist/esm/agents/AgentContext.mjs +69 -14
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/common/enum.mjs +3 -1
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +51 -9
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +278 -12
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/llm/bedrock/index.mjs +127 -60
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/esm/main.mjs +1 -1
- package/dist/esm/messages/cache.mjs +1 -0
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/messages/core.mjs +1 -1
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/messages/tools.mjs +2 -2
- package/dist/esm/messages/tools.mjs.map +1 -1
- package/dist/esm/stream.mjs +4 -2
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/BrowserTools.mjs +27 -3
- package/dist/esm/tools/BrowserTools.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +22 -21
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +14 -11
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +102 -3
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/ToolSearch.mjs +827 -0
- package/dist/esm/tools/ToolSearch.mjs.map +1 -0
- package/dist/types/agents/AgentContext.d.ts +33 -1
- package/dist/types/common/enum.d.ts +4 -2
- package/dist/types/graphs/Graph.d.ts +6 -0
- package/dist/types/graphs/MultiAgentGraph.d.ts +16 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/llm/bedrock/index.d.ts +89 -11
- package/dist/types/llm/bedrock/types.d.ts +27 -0
- package/dist/types/llm/bedrock/utils/index.d.ts +5 -0
- package/dist/types/llm/bedrock/utils/message_inputs.d.ts +31 -0
- package/dist/types/llm/bedrock/utils/message_outputs.d.ts +33 -0
- package/dist/types/tools/BrowserTools.d.ts +2 -0
- package/dist/types/tools/CodeExecutor.d.ts +0 -3
- package/dist/types/tools/ProgrammaticToolCalling.d.ts +0 -3
- package/dist/types/tools/ToolNode.d.ts +3 -1
- package/dist/types/tools/ToolSearch.d.ts +148 -0
- package/dist/types/types/graph.d.ts +2 -0
- package/dist/types/types/llm.d.ts +3 -1
- package/dist/types/types/tools.d.ts +42 -2
- package/package.json +12 -5
- package/src/agents/AgentContext.ts +88 -16
- package/src/common/enum.ts +3 -1
- package/src/graphs/Graph.ts +64 -13
- package/src/graphs/MultiAgentGraph.ts +350 -13
- package/src/index.ts +1 -1
- package/src/llm/bedrock/index.ts +221 -99
- package/src/llm/bedrock/llm.spec.ts +616 -0
- package/src/llm/bedrock/types.ts +51 -0
- package/src/llm/bedrock/utils/index.ts +18 -0
- package/src/llm/bedrock/utils/message_inputs.ts +563 -0
- package/src/llm/bedrock/utils/message_outputs.ts +310 -0
- package/src/messages/__tests__/tools.test.ts +21 -21
- package/src/messages/cache.test.ts +259 -0
- package/src/messages/cache.ts +104 -1
- package/src/messages/core.ts +1 -1
- package/src/messages/tools.ts +2 -2
- package/src/scripts/caching.ts +27 -19
- package/src/scripts/code_exec_files.ts +58 -15
- package/src/scripts/code_exec_multi_session.ts +241 -0
- package/src/scripts/code_exec_session.ts +282 -0
- package/src/scripts/multi-agent-conditional.ts +1 -0
- package/src/scripts/multi-agent-supervisor.ts +1 -0
- package/src/scripts/programmatic_exec_agent.ts +4 -4
- package/src/scripts/test-handoff-preamble.ts +277 -0
- package/src/scripts/test-parallel-handoffs.ts +291 -0
- package/src/scripts/test-tools-before-handoff.ts +8 -4
- package/src/scripts/test_code_api.ts +361 -0
- package/src/scripts/thinking-bedrock.ts +159 -0
- package/src/scripts/thinking.ts +39 -18
- package/src/scripts/{tool_search_regex.ts → tool_search.ts} +5 -5
- package/src/scripts/tools.ts +7 -3
- package/src/stream.ts +4 -2
- package/src/tools/BrowserTools.ts +68 -14
- package/src/tools/CodeExecutor.ts +26 -23
- package/src/tools/ProgrammaticToolCalling.ts +18 -14
- package/src/tools/ToolNode.ts +114 -1
- package/src/tools/ToolSearch.ts +1041 -0
- package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +0 -2
- package/src/tools/__tests__/{ToolSearchRegex.integration.test.ts → ToolSearch.integration.test.ts} +6 -6
- package/src/tools/__tests__/ToolSearch.test.ts +1003 -0
- package/src/types/graph.ts +2 -0
- package/src/types/llm.ts +3 -1
- package/src/types/tools.ts +51 -2
- package/dist/cjs/tools/ToolSearchRegex.cjs +0 -455
- package/dist/cjs/tools/ToolSearchRegex.cjs.map +0 -1
- package/dist/esm/tools/ToolSearchRegex.mjs +0 -448
- package/dist/esm/tools/ToolSearchRegex.mjs.map +0 -1
- package/dist/types/tools/ToolSearchRegex.d.ts +0 -80
- package/src/tools/ToolSearchRegex.ts +0 -535
- package/src/tools/__tests__/ToolSearchRegex.test.ts +0 -232
package/src/scripts/tools.ts
CHANGED
|
@@ -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(
|
|
22
|
-
|
|
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 (
|
|
@@ -17,6 +17,8 @@ export const EBrowserTools = {
|
|
|
17
17
|
BACK: 'browser_back',
|
|
18
18
|
SCREENSHOT: 'browser_screenshot',
|
|
19
19
|
GET_PAGE_STATE: 'browser_get_page_state',
|
|
20
|
+
KEYPRESS: 'browser_keypress',
|
|
21
|
+
SWITCH_TAB: 'browser_switch_tab',
|
|
20
22
|
} as const;
|
|
21
23
|
|
|
22
24
|
export type BrowserToolName =
|
|
@@ -74,19 +76,23 @@ const BrowserClickSchema = z.object({
|
|
|
74
76
|
index: z
|
|
75
77
|
.number()
|
|
76
78
|
.describe(
|
|
77
|
-
'The [index] of the element to click.
|
|
79
|
+
'The [index] of the element to click. Use fieldLabel to identify the correct element. For form fields, target <input> or <textarea> elements, NOT parent <div> containers.'
|
|
78
80
|
),
|
|
79
81
|
});
|
|
80
82
|
|
|
81
83
|
const BrowserTypeSchema = z.object({
|
|
82
84
|
index: z
|
|
83
85
|
.number()
|
|
84
|
-
.describe(
|
|
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
|
+
),
|
|
85
89
|
text: z.string().describe('The text to type into the element'),
|
|
86
90
|
pressEnter: z
|
|
87
91
|
.boolean()
|
|
88
92
|
.optional()
|
|
89
|
-
.describe(
|
|
93
|
+
.describe(
|
|
94
|
+
'Whether to press Enter after typing (useful for search forms and submitting)'
|
|
95
|
+
),
|
|
90
96
|
});
|
|
91
97
|
|
|
92
98
|
const BrowserNavigateSchema = z.object({
|
|
@@ -129,6 +135,22 @@ const BrowserScreenshotSchema = z.object({});
|
|
|
129
135
|
|
|
130
136
|
const BrowserGetPageStateSchema = z.object({});
|
|
131
137
|
|
|
138
|
+
const BrowserKeypressSchema = z.object({
|
|
139
|
+
keys: z
|
|
140
|
+
.string()
|
|
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
|
+
),
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const BrowserSwitchTabSchema = z.object({
|
|
147
|
+
tabId: z
|
|
148
|
+
.number()
|
|
149
|
+
.describe(
|
|
150
|
+
'The tab ID to switch to. Use the tab IDs shown in the tabs list from page state.'
|
|
151
|
+
),
|
|
152
|
+
});
|
|
153
|
+
|
|
132
154
|
/**
|
|
133
155
|
* Browser tool response interface
|
|
134
156
|
* This is what the extension returns after executing the action
|
|
@@ -173,7 +195,9 @@ function formatResultForLLM(
|
|
|
173
195
|
}
|
|
174
196
|
if (result.elementList != null && result.elementList !== '') {
|
|
175
197
|
// Add hint about fieldLabel and targeting inputs for form interactions
|
|
176
|
-
parts.push(
|
|
198
|
+
parts.push(
|
|
199
|
+
`\n**Interactive Elements** (for typing: target <input> elements with fieldLabel, NOT parent <div> containers):\n${result.elementList}`
|
|
200
|
+
);
|
|
177
201
|
}
|
|
178
202
|
if (result.screenshot != null && result.screenshot !== '') {
|
|
179
203
|
parts.push('\n[Screenshot captured and displayed to user]');
|
|
@@ -249,7 +273,8 @@ export function createBrowserTools(
|
|
|
249
273
|
tools.push(
|
|
250
274
|
tool(createToolFunction('click'), {
|
|
251
275
|
name: EBrowserTools.CLICK,
|
|
252
|
-
description:
|
|
276
|
+
description:
|
|
277
|
+
'Click element by [index]. Use fieldLabel attribute to identify correct element. For form fields, target <input> elements NOT parent <div> containers.',
|
|
253
278
|
schema: BrowserClickSchema,
|
|
254
279
|
})
|
|
255
280
|
);
|
|
@@ -258,7 +283,8 @@ export function createBrowserTools(
|
|
|
258
283
|
tools.push(
|
|
259
284
|
tool(createToolFunction('type'), {
|
|
260
285
|
name: EBrowserTools.TYPE,
|
|
261
|
-
description:
|
|
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).',
|
|
262
288
|
schema: BrowserTypeSchema,
|
|
263
289
|
})
|
|
264
290
|
);
|
|
@@ -267,7 +293,8 @@ export function createBrowserTools(
|
|
|
267
293
|
tools.push(
|
|
268
294
|
tool(createToolFunction('navigate'), {
|
|
269
295
|
name: EBrowserTools.NAVIGATE,
|
|
270
|
-
description:
|
|
296
|
+
description:
|
|
297
|
+
'Navigate to URL (include https://). Returns new page element list.',
|
|
271
298
|
schema: BrowserNavigateSchema,
|
|
272
299
|
})
|
|
273
300
|
);
|
|
@@ -276,7 +303,8 @@ export function createBrowserTools(
|
|
|
276
303
|
tools.push(
|
|
277
304
|
tool(createToolFunction('scroll'), {
|
|
278
305
|
name: EBrowserTools.SCROLL,
|
|
279
|
-
description:
|
|
306
|
+
description:
|
|
307
|
+
'Scroll page (up/down/left/right). Returns updated element list.',
|
|
280
308
|
schema: BrowserScrollSchema,
|
|
281
309
|
})
|
|
282
310
|
);
|
|
@@ -285,7 +313,8 @@ export function createBrowserTools(
|
|
|
285
313
|
tools.push(
|
|
286
314
|
tool(createToolFunction('extract'), {
|
|
287
315
|
name: EBrowserTools.EXTRACT,
|
|
288
|
-
description:
|
|
316
|
+
description:
|
|
317
|
+
'Extract page content. Returns URL, title, and element list.',
|
|
289
318
|
schema: BrowserExtractSchema,
|
|
290
319
|
})
|
|
291
320
|
);
|
|
@@ -294,7 +323,8 @@ export function createBrowserTools(
|
|
|
294
323
|
tools.push(
|
|
295
324
|
tool(createToolFunction('hover'), {
|
|
296
325
|
name: EBrowserTools.HOVER,
|
|
297
|
-
description:
|
|
326
|
+
description:
|
|
327
|
+
'Hover element by [index] to reveal menus/tooltips. Returns updated element list.',
|
|
298
328
|
schema: BrowserHoverSchema,
|
|
299
329
|
})
|
|
300
330
|
);
|
|
@@ -303,7 +333,8 @@ export function createBrowserTools(
|
|
|
303
333
|
tools.push(
|
|
304
334
|
tool(createToolFunction('wait'), {
|
|
305
335
|
name: EBrowserTools.WAIT,
|
|
306
|
-
description:
|
|
336
|
+
description:
|
|
337
|
+
'Wait for async content to load. Returns updated element list.',
|
|
307
338
|
schema: BrowserWaitSchema,
|
|
308
339
|
})
|
|
309
340
|
);
|
|
@@ -312,7 +343,8 @@ export function createBrowserTools(
|
|
|
312
343
|
tools.push(
|
|
313
344
|
tool(createToolFunction('back'), {
|
|
314
345
|
name: EBrowserTools.BACK,
|
|
315
|
-
description:
|
|
346
|
+
description:
|
|
347
|
+
'Go back in browser history. Returns previous page element list.',
|
|
316
348
|
schema: BrowserBackSchema,
|
|
317
349
|
})
|
|
318
350
|
);
|
|
@@ -321,7 +353,8 @@ export function createBrowserTools(
|
|
|
321
353
|
tools.push(
|
|
322
354
|
tool(createToolFunction('screenshot'), {
|
|
323
355
|
name: EBrowserTools.SCREENSHOT,
|
|
324
|
-
description:
|
|
356
|
+
description:
|
|
357
|
+
'Capture screenshot. Displayed to user. Use get_page_state for automation.',
|
|
325
358
|
schema: BrowserScreenshotSchema,
|
|
326
359
|
})
|
|
327
360
|
);
|
|
@@ -330,10 +363,31 @@ export function createBrowserTools(
|
|
|
330
363
|
tools.push(
|
|
331
364
|
tool(createToolFunction('get_page_state'), {
|
|
332
365
|
name: EBrowserTools.GET_PAGE_STATE,
|
|
333
|
-
description:
|
|
366
|
+
description:
|
|
367
|
+
'Get page URL, title, and interactive elements with [index] for actions. Start here.',
|
|
334
368
|
schema: BrowserGetPageStateSchema,
|
|
335
369
|
})
|
|
336
370
|
);
|
|
337
371
|
|
|
372
|
+
// browser_keypress - for keyboard shortcuts
|
|
373
|
+
tools.push(
|
|
374
|
+
tool(createToolFunction('keypress'), {
|
|
375
|
+
name: EBrowserTools.KEYPRESS,
|
|
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.',
|
|
378
|
+
schema: BrowserKeypressSchema,
|
|
379
|
+
})
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
// browser_switch_tab - for switching between tabs
|
|
383
|
+
tools.push(
|
|
384
|
+
tool(createToolFunction('switch_tab'), {
|
|
385
|
+
name: EBrowserTools.SWITCH_TAB,
|
|
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).',
|
|
388
|
+
schema: BrowserSwitchTabSchema,
|
|
389
|
+
})
|
|
390
|
+
);
|
|
391
|
+
|
|
338
392
|
return tools;
|
|
339
393
|
}
|
|
@@ -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
|
|
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
|
-
-
|
|
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,
|
|
111
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 += `\
|
|
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
|
|
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 += `\
|
|
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
|
-
-
|
|
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,
|
|
621
|
+
const { code, timeout = DEFAULT_TIMEOUT } = params;
|
|
628
622
|
|
|
629
623
|
// Extra params injected by ToolNode (follows web_search pattern)
|
|
630
|
-
const { toolMap, toolDefs
|
|
631
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
|
package/src/tools/ToolNode.ts
CHANGED
|
@@ -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.
|
|
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
|
}
|