@illuma-ai/agents 1.1.28 → 1.3.1

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 (272) hide show
  1. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  2. package/dist/cjs/common/spawnPath.cjs +104 -0
  3. package/dist/cjs/common/spawnPath.cjs.map +1 -0
  4. package/dist/cjs/graphs/Graph.cjs +89 -45
  5. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  6. package/dist/cjs/graphs/HandoffRegistry.cjs +47 -8
  7. package/dist/cjs/graphs/HandoffRegistry.cjs.map +1 -1
  8. package/dist/cjs/graphs/MultiAgentGraph.cjs +493 -267
  9. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  10. package/dist/cjs/graphs/phases/flushLoop.cjs +214 -0
  11. package/dist/cjs/graphs/phases/flushLoop.cjs.map +1 -0
  12. package/dist/cjs/graphs/phases/memoryFlushPhase.cjs +102 -0
  13. package/dist/cjs/graphs/phases/memoryFlushPhase.cjs.map +1 -0
  14. package/dist/cjs/llm/bedrock/index.cjs +4 -3
  15. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  16. package/dist/cjs/main.cjs +117 -0
  17. package/dist/cjs/main.cjs.map +1 -1
  18. package/dist/cjs/memory/citations.cjs +69 -0
  19. package/dist/cjs/memory/citations.cjs.map +1 -0
  20. package/dist/cjs/memory/compositeBackend.cjs +60 -0
  21. package/dist/cjs/memory/compositeBackend.cjs.map +1 -0
  22. package/dist/cjs/memory/constants.cjs +232 -0
  23. package/dist/cjs/memory/constants.cjs.map +1 -0
  24. package/dist/cjs/memory/embeddings.cjs +151 -0
  25. package/dist/cjs/memory/embeddings.cjs.map +1 -0
  26. package/dist/cjs/memory/factory.cjs +95 -0
  27. package/dist/cjs/memory/factory.cjs.map +1 -0
  28. package/dist/cjs/memory/migrate.cjs +81 -0
  29. package/dist/cjs/memory/migrate.cjs.map +1 -0
  30. package/dist/cjs/memory/mmr.cjs +138 -0
  31. package/dist/cjs/memory/mmr.cjs.map +1 -0
  32. package/dist/cjs/memory/paths.cjs +217 -0
  33. package/dist/cjs/memory/paths.cjs.map +1 -0
  34. package/dist/cjs/memory/pgvectorStore.cjs +225 -0
  35. package/dist/cjs/memory/pgvectorStore.cjs.map +1 -0
  36. package/dist/cjs/memory/recallTracking.cjs +98 -0
  37. package/dist/cjs/memory/recallTracking.cjs.map +1 -0
  38. package/dist/cjs/memory/schema.sql +51 -0
  39. package/dist/cjs/memory/temporalDecay.cjs +118 -0
  40. package/dist/cjs/memory/temporalDecay.cjs.map +1 -0
  41. package/dist/cjs/nodes/ApprovalGateNode.cjs +1 -1
  42. package/dist/cjs/nodes/ApprovalGateNode.cjs.map +1 -1
  43. package/dist/cjs/prompts/memoryFlushPrompt.cjs +49 -0
  44. package/dist/cjs/prompts/memoryFlushPrompt.cjs.map +1 -0
  45. package/dist/cjs/run.cjs +16 -3
  46. package/dist/cjs/run.cjs.map +1 -1
  47. package/dist/cjs/tools/AskUser.cjs +6 -1
  48. package/dist/cjs/tools/AskUser.cjs.map +1 -1
  49. package/dist/cjs/tools/BrowserTools.cjs +1 -1
  50. package/dist/cjs/tools/BrowserTools.cjs.map +1 -1
  51. package/dist/cjs/tools/ToolNode.cjs +127 -10
  52. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  53. package/dist/cjs/tools/approval/constants.cjs +2 -2
  54. package/dist/cjs/tools/approval/constants.cjs.map +1 -1
  55. package/dist/cjs/tools/memory/index.cjs +58 -0
  56. package/dist/cjs/tools/memory/index.cjs.map +1 -0
  57. package/dist/cjs/tools/memory/memoryAppendTool.cjs +69 -0
  58. package/dist/cjs/tools/memory/memoryAppendTool.cjs.map +1 -0
  59. package/dist/cjs/tools/memory/memoryGetTool.cjs +49 -0
  60. package/dist/cjs/tools/memory/memoryGetTool.cjs.map +1 -0
  61. package/dist/cjs/tools/memory/memorySearchTool.cjs +65 -0
  62. package/dist/cjs/tools/memory/memorySearchTool.cjs.map +1 -0
  63. package/dist/cjs/tools/memory/shared.cjs +106 -0
  64. package/dist/cjs/tools/memory/shared.cjs.map +1 -0
  65. package/dist/cjs/types/graph.cjs.map +1 -1
  66. package/dist/cjs/utils/childAgentContext.cjs +242 -0
  67. package/dist/cjs/utils/childAgentContext.cjs.map +1 -0
  68. package/dist/cjs/utils/errors.cjs +113 -0
  69. package/dist/cjs/utils/errors.cjs.map +1 -0
  70. package/dist/cjs/utils/events.cjs +36 -7
  71. package/dist/cjs/utils/events.cjs.map +1 -1
  72. package/dist/cjs/utils/finishReasons.cjs +44 -0
  73. package/dist/cjs/utils/finishReasons.cjs.map +1 -0
  74. package/dist/cjs/utils/llm.cjs.map +1 -1
  75. package/dist/cjs/utils/logging.cjs +34 -0
  76. package/dist/cjs/utils/logging.cjs.map +1 -0
  77. package/dist/cjs/utils/toolCallNormalization.cjs +250 -0
  78. package/dist/cjs/utils/toolCallNormalization.cjs.map +1 -0
  79. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  80. package/dist/esm/common/spawnPath.mjs +95 -0
  81. package/dist/esm/common/spawnPath.mjs.map +1 -0
  82. package/dist/esm/graphs/Graph.mjs +89 -45
  83. package/dist/esm/graphs/Graph.mjs.map +1 -1
  84. package/dist/esm/graphs/HandoffRegistry.mjs +47 -8
  85. package/dist/esm/graphs/HandoffRegistry.mjs.map +1 -1
  86. package/dist/esm/graphs/MultiAgentGraph.mjs +493 -267
  87. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  88. package/dist/esm/graphs/phases/flushLoop.mjs +209 -0
  89. package/dist/esm/graphs/phases/flushLoop.mjs.map +1 -0
  90. package/dist/esm/graphs/phases/memoryFlushPhase.mjs +99 -0
  91. package/dist/esm/graphs/phases/memoryFlushPhase.mjs.map +1 -0
  92. package/dist/esm/llm/bedrock/index.mjs +4 -3
  93. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  94. package/dist/esm/main.mjs +21 -0
  95. package/dist/esm/main.mjs.map +1 -1
  96. package/dist/esm/memory/citations.mjs +64 -0
  97. package/dist/esm/memory/citations.mjs.map +1 -0
  98. package/dist/esm/memory/compositeBackend.mjs +58 -0
  99. package/dist/esm/memory/compositeBackend.mjs.map +1 -0
  100. package/dist/esm/memory/constants.mjs +198 -0
  101. package/dist/esm/memory/constants.mjs.map +1 -0
  102. package/dist/esm/memory/embeddings.mjs +148 -0
  103. package/dist/esm/memory/embeddings.mjs.map +1 -0
  104. package/dist/esm/memory/factory.mjs +93 -0
  105. package/dist/esm/memory/factory.mjs.map +1 -0
  106. package/dist/esm/memory/migrate.mjs +78 -0
  107. package/dist/esm/memory/migrate.mjs.map +1 -0
  108. package/dist/esm/memory/mmr.mjs +130 -0
  109. package/dist/esm/memory/mmr.mjs.map +1 -0
  110. package/dist/esm/memory/paths.mjs +207 -0
  111. package/dist/esm/memory/paths.mjs.map +1 -0
  112. package/dist/esm/memory/pgvectorStore.mjs +223 -0
  113. package/dist/esm/memory/pgvectorStore.mjs.map +1 -0
  114. package/dist/esm/memory/recallTracking.mjs +94 -0
  115. package/dist/esm/memory/recallTracking.mjs.map +1 -0
  116. package/dist/esm/memory/schema.sql +51 -0
  117. package/dist/esm/memory/temporalDecay.mjs +110 -0
  118. package/dist/esm/memory/temporalDecay.mjs.map +1 -0
  119. package/dist/esm/nodes/ApprovalGateNode.mjs +1 -1
  120. package/dist/esm/nodes/ApprovalGateNode.mjs.map +1 -1
  121. package/dist/esm/prompts/memoryFlushPrompt.mjs +44 -0
  122. package/dist/esm/prompts/memoryFlushPrompt.mjs.map +1 -0
  123. package/dist/esm/run.mjs +16 -3
  124. package/dist/esm/run.mjs.map +1 -1
  125. package/dist/esm/tools/AskUser.mjs +6 -1
  126. package/dist/esm/tools/AskUser.mjs.map +1 -1
  127. package/dist/esm/tools/BrowserTools.mjs +1 -1
  128. package/dist/esm/tools/BrowserTools.mjs.map +1 -1
  129. package/dist/esm/tools/ToolNode.mjs +128 -11
  130. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  131. package/dist/esm/tools/approval/constants.mjs +2 -2
  132. package/dist/esm/tools/approval/constants.mjs.map +1 -1
  133. package/dist/esm/tools/memory/index.mjs +46 -0
  134. package/dist/esm/tools/memory/index.mjs.map +1 -0
  135. package/dist/esm/tools/memory/memoryAppendTool.mjs +67 -0
  136. package/dist/esm/tools/memory/memoryAppendTool.mjs.map +1 -0
  137. package/dist/esm/tools/memory/memoryGetTool.mjs +47 -0
  138. package/dist/esm/tools/memory/memoryGetTool.mjs.map +1 -0
  139. package/dist/esm/tools/memory/memorySearchTool.mjs +63 -0
  140. package/dist/esm/tools/memory/memorySearchTool.mjs.map +1 -0
  141. package/dist/esm/tools/memory/shared.mjs +98 -0
  142. package/dist/esm/tools/memory/shared.mjs.map +1 -0
  143. package/dist/esm/types/graph.mjs.map +1 -1
  144. package/dist/esm/utils/childAgentContext.mjs +237 -0
  145. package/dist/esm/utils/childAgentContext.mjs.map +1 -0
  146. package/dist/esm/utils/errors.mjs +109 -0
  147. package/dist/esm/utils/errors.mjs.map +1 -0
  148. package/dist/esm/utils/events.mjs +36 -8
  149. package/dist/esm/utils/events.mjs.map +1 -1
  150. package/dist/esm/utils/finishReasons.mjs +41 -0
  151. package/dist/esm/utils/finishReasons.mjs.map +1 -0
  152. package/dist/esm/utils/llm.mjs.map +1 -1
  153. package/dist/esm/utils/logging.mjs +31 -0
  154. package/dist/esm/utils/logging.mjs.map +1 -0
  155. package/dist/esm/utils/toolCallNormalization.mjs +247 -0
  156. package/dist/esm/utils/toolCallNormalization.mjs.map +1 -0
  157. package/dist/types/common/index.d.ts +1 -0
  158. package/dist/types/common/spawnPath.d.ts +59 -0
  159. package/dist/types/graphs/HandoffRegistry.d.ts +24 -7
  160. package/dist/types/graphs/MultiAgentGraph.d.ts +43 -23
  161. package/dist/types/graphs/phases/flushLoop.d.ts +106 -0
  162. package/dist/types/graphs/phases/memoryFlushPhase.d.ts +100 -0
  163. package/dist/types/index.d.ts +7 -0
  164. package/dist/types/memory/__tests__/mockBackend.d.ts +40 -0
  165. package/dist/types/memory/citations.d.ts +39 -0
  166. package/dist/types/memory/compositeBackend.d.ts +30 -0
  167. package/dist/types/memory/constants.d.ts +121 -0
  168. package/dist/types/memory/embeddings.d.ts +15 -0
  169. package/dist/types/memory/factory.d.ts +23 -0
  170. package/dist/types/memory/index.d.ts +21 -0
  171. package/dist/types/memory/migrate.d.ts +14 -0
  172. package/dist/types/memory/mmr.d.ts +50 -0
  173. package/dist/types/memory/paths.d.ts +107 -0
  174. package/dist/types/memory/pgvectorStore.d.ts +56 -0
  175. package/dist/types/memory/recallTracking.d.ts +30 -0
  176. package/dist/types/memory/temporalDecay.d.ts +53 -0
  177. package/dist/types/memory/types.d.ts +182 -0
  178. package/dist/types/prompts/memoryFlushPrompt.d.ts +54 -0
  179. package/dist/types/run.d.ts +1 -0
  180. package/dist/types/tools/AskUser.d.ts +1 -1
  181. package/dist/types/tools/BrowserTools.d.ts +2 -2
  182. package/dist/types/tools/approval/constants.d.ts +2 -2
  183. package/dist/types/tools/memory/index.d.ts +39 -0
  184. package/dist/types/tools/memory/memoryAppendTool.d.ts +27 -0
  185. package/dist/types/tools/memory/memoryGetTool.d.ts +22 -0
  186. package/dist/types/tools/memory/memorySearchTool.d.ts +22 -0
  187. package/dist/types/tools/memory/shared.d.ts +106 -0
  188. package/dist/types/types/graph.d.ts +10 -3
  189. package/dist/types/utils/childAgentContext.d.ts +99 -0
  190. package/dist/types/utils/errors.d.ts +37 -0
  191. package/dist/types/utils/events.d.ts +21 -0
  192. package/dist/types/utils/finishReasons.d.ts +32 -0
  193. package/dist/types/utils/index.d.ts +1 -0
  194. package/dist/types/utils/logging.d.ts +2 -0
  195. package/dist/types/utils/toolCallNormalization.d.ts +44 -0
  196. package/package.json +6 -4
  197. package/src/agents/AgentContext.ts +12 -4
  198. package/src/common/__tests__/enum.test.ts +4 -2
  199. package/src/common/__tests__/spawnPath.test.ts +110 -0
  200. package/src/common/index.ts +1 -0
  201. package/src/common/spawnPath.ts +101 -0
  202. package/src/graphs/Graph.ts +95 -61
  203. package/src/graphs/HandoffRegistry.ts +48 -17
  204. package/src/graphs/MultiAgentGraph.ts +588 -327
  205. package/src/graphs/__tests__/HandoffRegistry.test.ts +4 -1
  206. package/src/graphs/__tests__/multi-agent-delegate.test.ts +61 -16
  207. package/src/graphs/__tests__/multi-agent-edges.test.ts +4 -2
  208. package/src/graphs/__tests__/multi-agent-nested-subgraph.test.ts +221 -0
  209. package/src/graphs/__tests__/structured-output.integration.test.ts +212 -118
  210. package/src/graphs/contextManagement.e2e.test.ts +1 -1
  211. package/src/graphs/phases/__tests__/flushLoop.test.ts +264 -0
  212. package/src/graphs/phases/__tests__/memoryFlushPhase.test.ts +37 -0
  213. package/src/graphs/phases/__tests__/runMemoryFlush.test.ts +150 -0
  214. package/src/graphs/phases/flushLoop.ts +303 -0
  215. package/src/graphs/phases/memoryFlushPhase.ts +209 -0
  216. package/src/index.ts +30 -1
  217. package/src/llm/bedrock/index.ts +4 -5
  218. package/src/memory/__tests__/citations.test.ts +61 -0
  219. package/src/memory/__tests__/compositeBackend.test.ts +79 -0
  220. package/src/memory/__tests__/isolation.test.ts +206 -0
  221. package/src/memory/__tests__/mmr.test.ts +148 -0
  222. package/src/memory/__tests__/mockBackend.ts +161 -0
  223. package/src/memory/__tests__/paths.test.ts +168 -0
  224. package/src/memory/__tests__/recallTracking.test.ts +96 -0
  225. package/src/memory/__tests__/temporalDecay.test.ts +151 -0
  226. package/src/memory/citations.ts +80 -0
  227. package/src/memory/compositeBackend.ts +99 -0
  228. package/src/memory/constants.ts +229 -0
  229. package/src/memory/embeddings.ts +188 -0
  230. package/src/memory/factory.ts +111 -0
  231. package/src/memory/index.ts +46 -0
  232. package/src/memory/migrate.ts +116 -0
  233. package/src/memory/mmr.ts +161 -0
  234. package/src/memory/paths.ts +258 -0
  235. package/src/memory/pgvectorStore.ts +324 -0
  236. package/src/memory/recallTracking.ts +127 -0
  237. package/src/memory/schema.sql +51 -0
  238. package/src/memory/temporalDecay.ts +134 -0
  239. package/src/memory/types.ts +185 -0
  240. package/src/nodes/ApprovalGateNode.ts +4 -10
  241. package/src/nodes/__tests__/ApprovalGateNode.test.ts +11 -20
  242. package/src/prompts/memoryFlushPrompt.ts +78 -0
  243. package/src/run.ts +17 -6
  244. package/src/scripts/test-bedrock-handoff-autonomous.ts +56 -20
  245. package/src/specs/agent-handoffs-bedrock.integration.test.ts +8 -5
  246. package/src/specs/agent-handoffs.test.ts +8 -2
  247. package/src/tools/AskUser.ts +7 -2
  248. package/src/tools/BrowserTools.ts +3 -5
  249. package/src/tools/ToolNode.ts +150 -13
  250. package/src/tools/__tests__/ToolApproval.test.ts +22 -9
  251. package/src/tools/approval/__tests__/constants.test.ts +1 -1
  252. package/src/tools/approval/constants.ts +2 -2
  253. package/src/tools/memory/__tests__/memoryTools.test.ts +205 -0
  254. package/src/tools/memory/index.ts +96 -0
  255. package/src/tools/memory/memoryAppendTool.ts +101 -0
  256. package/src/tools/memory/memoryGetTool.ts +53 -0
  257. package/src/tools/memory/memorySearchTool.ts +80 -0
  258. package/src/tools/memory/shared.ts +169 -0
  259. package/src/tools/search/search.test.ts +6 -1
  260. package/src/types/graph.ts +10 -3
  261. package/src/utils/__tests__/childAgentContext.test.ts +217 -0
  262. package/src/utils/__tests__/errors.test.ts +136 -0
  263. package/src/utils/__tests__/finishReasons.test.ts +55 -0
  264. package/src/utils/__tests__/toolCallNormalization.test.ts +181 -0
  265. package/src/utils/childAgentContext.ts +259 -0
  266. package/src/utils/errors.ts +115 -0
  267. package/src/utils/events.ts +37 -7
  268. package/src/utils/finishReasons.ts +40 -0
  269. package/src/utils/index.ts +1 -0
  270. package/src/utils/llm.ts +0 -1
  271. package/src/utils/logging.ts +45 -8
  272. package/src/utils/toolCallNormalization.ts +271 -0
@@ -1 +1 @@
1
- {"version":3,"file":"BrowserTools.cjs","sources":["../../../src/tools/BrowserTools.ts"],"sourcesContent":["import { z } from 'zod';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as _t from '@/types';\n\n/**\n * Browser tool names - keep in sync with ranger-browser extension\n * These tools execute locally in the browser extension, NOT on the server\n */\nexport const EBrowserTools = {\n CLICK: 'browser_click',\n TYPE: 'browser_type',\n NAVIGATE: 'browser_navigate',\n SCROLL: 'browser_scroll',\n EXTRACT: 'browser_extract',\n HOVER: 'browser_hover',\n WAIT: 'browser_wait',\n BACK: 'browser_back',\n SCREENSHOT: 'browser_screenshot',\n GET_PAGE_STATE: 'browser_get_page_state',\n KEYPRESS: 'browser_keypress',\n SWITCH_TAB: 'browser_switch_tab',\n} as const;\n\nexport type BrowserToolName =\n (typeof EBrowserTools)[keyof typeof EBrowserTools];\n\n/**\n * Callback function type for waiting on browser action results\n * This allows the server (Ranger) to provide a callback that waits for the extension\n * to POST results back to the server before returning to the LLM.\n *\n * @param action - The browser action (click, type, navigate, etc.)\n * @param args - Arguments for the action\n * @param toolCallId - Unique ID for this tool call (from config.toolCall.id)\n * @returns Promise that resolves with the actual browser result (page state, etc.)\n */\nexport type BrowserToolCallback = (\n action: string,\n args: Record<string, unknown>,\n toolCallId: string\n) => Promise<BrowserActionResult>;\n\n/**\n * Result returned from browser action execution\n */\nexport interface BrowserActionResult {\n success: boolean;\n url?: string;\n title?: string;\n elementList?: string; // Text-based element list\n error?: string;\n screenshot?: string; // Base64 screenshot (if requested)\n}\n\n/**\n * Check if browser capability is available based on request headers or context\n * The browser extension sets these headers when connected:\n * - X-Illuma-Browser-Extension: true\n * - X-Illuma-Browser-Capable: true\n */\nexport function hasBrowserCapability(req?: {\n headers?: Record<string, string | string[] | undefined>;\n}): boolean {\n if (!req?.headers) {\n return false;\n }\n\n const browserExtension = req.headers['x-illuma-browser-extension'];\n const browserCapable = req.headers['x-illuma-browser-capable'];\n\n return browserExtension === 'true' || browserCapable === 'true';\n}\n\n// Tool schemas\nconst BrowserClickSchema = z.object({\n index: z\n .number()\n .describe(\n '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.'\n ),\n text: z\n .string()\n .optional()\n .describe(\n 'The visible text of the element being clicked (e.g., \"Send\", \"Delete\", \"Buy Now\"). Always include this for buttons and links.'\n ),\n label: z\n .string()\n .optional()\n .describe(\n 'The fieldLabel or ariaLabel of the element, if available.'\n ),\n});\n\nconst BrowserTypeSchema = z.object({\n index: z\n .number()\n .describe(\n 'The [index] of the INPUT element to type into. Target <input> or <textarea> elements. Check fieldLabel to identify the correct field.'\n ),\n text: z.string().describe('The text to type into the element'),\n pressEnter: z\n .boolean()\n .optional()\n .describe(\n 'Whether to press Enter after typing (useful for search forms and submitting)'\n ),\n});\n\nconst BrowserNavigateSchema = z.object({\n url: z\n .string()\n .describe('The full URL to navigate to (must include https://)'),\n});\n\nconst BrowserScrollSchema = z.object({\n direction: z\n .enum(['up', 'down', 'left', 'right'])\n .describe('Direction to scroll'),\n amount: z\n .number()\n .optional()\n .describe('Pixels to scroll (default: one viewport height)'),\n});\n\nconst BrowserExtractSchema = z.object({\n query: z\n .string()\n .optional()\n .describe('Optional: specific content to extract from the page'),\n});\n\nconst BrowserHoverSchema = z.object({\n index: z.number().describe('The index number of the element to hover over'),\n});\n\nconst BrowserWaitSchema = z.object({\n duration: z\n .number()\n .optional()\n .describe('Milliseconds to wait (default: 1000)'),\n});\n\nconst BrowserBackSchema = z.object({});\n\nconst BrowserScreenshotSchema = z.object({});\n\nconst BrowserGetPageStateSchema = z.object({});\n\nconst BrowserKeypressSchema = z.object({\n keys: z\n .string()\n .describe(\n '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).'\n ),\n});\n\nconst BrowserSwitchTabSchema = z.object({\n tabId: z\n .number()\n .describe(\n 'The tab ID to switch to. Use the tab IDs shown in the tabs list from page state.'\n ),\n});\n\n/**\n * Browser tool response interface\n * This is what the extension returns after executing the action\n */\nexport interface BrowserToolResponse {\n requiresBrowserExecution: true;\n action: string;\n args: Record<string, unknown>;\n toolCallId?: string; // Added to help extension correlate with callback\n}\n\n/**\n * Options for creating browser tools\n */\nexport interface CreateBrowserToolsOptions {\n /**\n * Optional callback that waits for browser action results.\n * When provided, tools will await this callback to get actual results from the extension.\n * When not provided, tools return markers immediately (for non-server contexts).\n */\n waitForResult?: BrowserToolCallback;\n}\n\n/**\n * Format browser action result for LLM consumption\n */\nfunction formatResultForLLM(\n result: BrowserActionResult,\n action: string\n): string {\n const parts: string[] = [];\n\n // Include error info but DON'T discard page state — the extension often sends\n // success=false with valid page state (URL, elementList) that the LLM needs\n if (!result.success && result.error) {\n parts.push(`**Note:** Action \"${action}\" reported: ${result.error}`);\n }\n\n if (result.url != null && result.url !== '') {\n parts.push(`**Current URL:** ${result.url}`);\n }\n if (result.title != null && result.title !== '') {\n parts.push(`**Page Title:** ${result.title}`);\n }\n if (result.elementList != null && result.elementList !== '') {\n parts.push(\n `\\n**Interactive Elements** (for typing: target <input> elements with fieldLabel, NOT parent <div> containers):\\n${result.elementList}`\n );\n }\n if (result.screenshot != null && result.screenshot !== '') {\n parts.push('\\n[Screenshot captured and displayed to user]');\n }\n\n if (parts.length === 0) {\n if (!result.success) {\n return `Browser action \"${action}\" failed: ${result.error || 'Unknown error'}`;\n }\n return `Browser action \"${action}\" completed successfully.`;\n }\n\n return parts.join('\\n');\n}\n\n/**\n * Create browser tools with optional callback for waiting on results\n *\n * When waitForResult callback is provided:\n * 1. Tool returns marker that triggers extension\n * 2. Tool then awaits callback to get actual results\n * 3. Returns real page state to LLM\n *\n * When no callback:\n * 1. Tool returns marker only (for non-server contexts)\n *\n * NOTE: These tools use TEXT-BASED element lists, NOT screenshots\n * Screenshots would be 100K+ tokens each - element lists are ~100 tokens\n */\nexport function createBrowserTools(\n options?: CreateBrowserToolsOptions\n): DynamicStructuredTool[] {\n const { waitForResult } = options || {};\n const tools: DynamicStructuredTool[] = [];\n\n /**\n * Helper to create tool function that optionally waits for results\n * The toolCallId is extracted from the RunnableConfig passed by LangChain\n */\n const createToolFunction = (action: string) => {\n return async (\n args: Record<string, unknown>,\n config?: { toolCall?: { id?: string } }\n ): Promise<string> => {\n const toolCallId =\n config?.toolCall?.id ??\n `tool_${Date.now()}_${Math.random().toString(36).slice(2)}`;\n\n // Create marker for extension\n const marker: BrowserToolResponse = {\n requiresBrowserExecution: true,\n action,\n args,\n toolCallId,\n };\n\n // If no callback, return marker immediately (extension handles via SSE interception)\n if (!waitForResult) {\n return JSON.stringify(marker);\n }\n\n // With callback: wait for actual results from extension\n // The marker is still returned initially via SSE, but we wait for the callback\n try {\n const result = await waitForResult(action, args, toolCallId);\n return formatResultForLLM(result, action);\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n return `Browser action \"${action}\" failed: ${errorMessage}`;\n }\n };\n };\n\n // browser_click\n tools.push(\n tool(createToolFunction('click'), {\n name: EBrowserTools.CLICK,\n description:\n 'Click element by [index]. ALWAYS include the element text and label in your call for safety review. Use fieldLabel attribute to identify correct element. For form fields, target <input> elements NOT parent <div> containers.',\n schema: BrowserClickSchema,\n })\n );\n\n // browser_type\n tools.push(\n tool(createToolFunction('type'), {\n name: EBrowserTools.TYPE,\n description:\n '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).',\n schema: BrowserTypeSchema,\n })\n );\n\n // browser_navigate\n tools.push(\n tool(createToolFunction('navigate'), {\n name: EBrowserTools.NAVIGATE,\n description:\n 'Navigate to URL (include https://). Returns new page element list.',\n schema: BrowserNavigateSchema,\n })\n );\n\n // browser_scroll\n tools.push(\n tool(createToolFunction('scroll'), {\n name: EBrowserTools.SCROLL,\n description:\n 'Scroll page (up/down/left/right). Returns updated element list.',\n schema: BrowserScrollSchema,\n })\n );\n\n // browser_extract\n tools.push(\n tool(createToolFunction('extract'), {\n name: EBrowserTools.EXTRACT,\n description:\n 'Extract page content. Returns URL, title, and element list.',\n schema: BrowserExtractSchema,\n })\n );\n\n // browser_hover\n tools.push(\n tool(createToolFunction('hover'), {\n name: EBrowserTools.HOVER,\n description:\n 'Hover element by [index] to reveal menus/tooltips. Returns updated element list.',\n schema: BrowserHoverSchema,\n })\n );\n\n // browser_wait\n tools.push(\n tool(createToolFunction('wait'), {\n name: EBrowserTools.WAIT,\n description:\n 'Wait for async content to load. Returns updated element list.',\n schema: BrowserWaitSchema,\n })\n );\n\n // browser_back\n tools.push(\n tool(createToolFunction('back'), {\n name: EBrowserTools.BACK,\n description:\n 'Go back in browser history. Returns previous page element list.',\n schema: BrowserBackSchema,\n })\n );\n\n // browser_screenshot\n tools.push(\n tool(createToolFunction('screenshot'), {\n name: EBrowserTools.SCREENSHOT,\n description:\n 'Capture screenshot. Displayed to user. Use get_page_state for automation.',\n schema: BrowserScreenshotSchema,\n })\n );\n\n // browser_get_page_state\n tools.push(\n tool(createToolFunction('get_page_state'), {\n name: EBrowserTools.GET_PAGE_STATE,\n description:\n 'Get page URL, title, and interactive elements with [index] for actions. Start here.',\n schema: BrowserGetPageStateSchema,\n })\n );\n\n // browser_keypress - for keyboard shortcuts\n tools.push(\n tool(createToolFunction('keypress'), {\n name: EBrowserTools.KEYPRESS,\n description:\n '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.',\n schema: BrowserKeypressSchema,\n })\n );\n\n // browser_switch_tab - for switching between tabs\n tools.push(\n tool(createToolFunction('switch_tab'), {\n name: EBrowserTools.SWITCH_TAB,\n description:\n '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).',\n schema: BrowserSwitchTabSchema,\n })\n );\n\n return tools;\n}\n"],"names":["z","tools","tool"],"mappings":"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"}
1
+ {"version":3,"file":"BrowserTools.cjs","sources":["../../../src/tools/BrowserTools.ts"],"sourcesContent":["import { z } from 'zod';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as _t from '@/types';\n\n/**\n * Browser tool names - keep in sync with the browser extension\n * These tools execute locally in the browser extension, NOT on the server\n */\nexport const EBrowserTools = {\n CLICK: 'browser_click',\n TYPE: 'browser_type',\n NAVIGATE: 'browser_navigate',\n SCROLL: 'browser_scroll',\n EXTRACT: 'browser_extract',\n HOVER: 'browser_hover',\n WAIT: 'browser_wait',\n BACK: 'browser_back',\n SCREENSHOT: 'browser_screenshot',\n GET_PAGE_STATE: 'browser_get_page_state',\n KEYPRESS: 'browser_keypress',\n SWITCH_TAB: 'browser_switch_tab',\n} as const;\n\nexport type BrowserToolName =\n (typeof EBrowserTools)[keyof typeof EBrowserTools];\n\n/**\n * Callback function type for waiting on browser action results\n * This allows the host server to provide a callback that waits for the extension\n * to POST results back to the server before returning to the LLM.\n *\n * @param action - The browser action (click, type, navigate, etc.)\n * @param args - Arguments for the action\n * @param toolCallId - Unique ID for this tool call (from config.toolCall.id)\n * @returns Promise that resolves with the actual browser result (page state, etc.)\n */\nexport type BrowserToolCallback = (\n action: string,\n args: Record<string, unknown>,\n toolCallId: string\n) => Promise<BrowserActionResult>;\n\n/**\n * Result returned from browser action execution\n */\nexport interface BrowserActionResult {\n success: boolean;\n url?: string;\n title?: string;\n elementList?: string; // Text-based element list\n error?: string;\n screenshot?: string; // Base64 screenshot (if requested)\n}\n\n/**\n * Check if browser capability is available based on request headers or context\n * The browser extension sets these headers when connected:\n * - X-Illuma-Browser-Extension: true\n * - X-Illuma-Browser-Capable: true\n */\nexport function hasBrowserCapability(req?: {\n headers?: Record<string, string | string[] | undefined>;\n}): boolean {\n if (!req?.headers) {\n return false;\n }\n\n const browserExtension = req.headers['x-illuma-browser-extension'];\n const browserCapable = req.headers['x-illuma-browser-capable'];\n\n return browserExtension === 'true' || browserCapable === 'true';\n}\n\n// Tool schemas\nconst BrowserClickSchema = z.object({\n index: z\n .number()\n .describe(\n '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.'\n ),\n text: z\n .string()\n .optional()\n .describe(\n 'The visible text of the element being clicked (e.g., \"Send\", \"Delete\", \"Buy Now\"). Always include this for buttons and links.'\n ),\n label: z\n .string()\n .optional()\n .describe('The fieldLabel or ariaLabel of the element, if available.'),\n});\n\nconst BrowserTypeSchema = z.object({\n index: z\n .number()\n .describe(\n 'The [index] of the INPUT element to type into. Target <input> or <textarea> elements. Check fieldLabel to identify the correct field.'\n ),\n text: z.string().describe('The text to type into the element'),\n pressEnter: z\n .boolean()\n .optional()\n .describe(\n 'Whether to press Enter after typing (useful for search forms and submitting)'\n ),\n});\n\nconst BrowserNavigateSchema = z.object({\n url: z\n .string()\n .describe('The full URL to navigate to (must include https://)'),\n});\n\nconst BrowserScrollSchema = z.object({\n direction: z\n .enum(['up', 'down', 'left', 'right'])\n .describe('Direction to scroll'),\n amount: z\n .number()\n .optional()\n .describe('Pixels to scroll (default: one viewport height)'),\n});\n\nconst BrowserExtractSchema = z.object({\n query: z\n .string()\n .optional()\n .describe('Optional: specific content to extract from the page'),\n});\n\nconst BrowserHoverSchema = z.object({\n index: z.number().describe('The index number of the element to hover over'),\n});\n\nconst BrowserWaitSchema = z.object({\n duration: z\n .number()\n .optional()\n .describe('Milliseconds to wait (default: 1000)'),\n});\n\nconst BrowserBackSchema = z.object({});\n\nconst BrowserScreenshotSchema = z.object({});\n\nconst BrowserGetPageStateSchema = z.object({});\n\nconst BrowserKeypressSchema = z.object({\n keys: z\n .string()\n .describe(\n '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).'\n ),\n});\n\nconst BrowserSwitchTabSchema = z.object({\n tabId: z\n .number()\n .describe(\n 'The tab ID to switch to. Use the tab IDs shown in the tabs list from page state.'\n ),\n});\n\n/**\n * Browser tool response interface\n * This is what the extension returns after executing the action\n */\nexport interface BrowserToolResponse {\n requiresBrowserExecution: true;\n action: string;\n args: Record<string, unknown>;\n toolCallId?: string; // Added to help extension correlate with callback\n}\n\n/**\n * Options for creating browser tools\n */\nexport interface CreateBrowserToolsOptions {\n /**\n * Optional callback that waits for browser action results.\n * When provided, tools will await this callback to get actual results from the extension.\n * When not provided, tools return markers immediately (for non-server contexts).\n */\n waitForResult?: BrowserToolCallback;\n}\n\n/**\n * Format browser action result for LLM consumption\n */\nfunction formatResultForLLM(\n result: BrowserActionResult,\n action: string\n): string {\n const parts: string[] = [];\n\n // Include error info but DON'T discard page state — the extension often sends\n // success=false with valid page state (URL, elementList) that the LLM needs\n if (!result.success && result.error) {\n parts.push(`**Note:** Action \"${action}\" reported: ${result.error}`);\n }\n\n if (result.url != null && result.url !== '') {\n parts.push(`**Current URL:** ${result.url}`);\n }\n if (result.title != null && result.title !== '') {\n parts.push(`**Page Title:** ${result.title}`);\n }\n if (result.elementList != null && result.elementList !== '') {\n parts.push(\n `\\n**Interactive Elements** (for typing: target <input> elements with fieldLabel, NOT parent <div> containers):\\n${result.elementList}`\n );\n }\n if (result.screenshot != null && result.screenshot !== '') {\n parts.push('\\n[Screenshot captured and displayed to user]');\n }\n\n if (parts.length === 0) {\n if (!result.success) {\n return `Browser action \"${action}\" failed: ${result.error || 'Unknown error'}`;\n }\n return `Browser action \"${action}\" completed successfully.`;\n }\n\n return parts.join('\\n');\n}\n\n/**\n * Create browser tools with optional callback for waiting on results\n *\n * When waitForResult callback is provided:\n * 1. Tool returns marker that triggers extension\n * 2. Tool then awaits callback to get actual results\n * 3. Returns real page state to LLM\n *\n * When no callback:\n * 1. Tool returns marker only (for non-server contexts)\n *\n * NOTE: These tools use TEXT-BASED element lists, NOT screenshots\n * Screenshots would be 100K+ tokens each - element lists are ~100 tokens\n */\nexport function createBrowserTools(\n options?: CreateBrowserToolsOptions\n): DynamicStructuredTool[] {\n const { waitForResult } = options || {};\n const tools: DynamicStructuredTool[] = [];\n\n /**\n * Helper to create tool function that optionally waits for results\n * The toolCallId is extracted from the RunnableConfig passed by LangChain\n */\n const createToolFunction = (action: string) => {\n return async (\n args: Record<string, unknown>,\n config?: { toolCall?: { id?: string } }\n ): Promise<string> => {\n const toolCallId =\n config?.toolCall?.id ??\n `tool_${Date.now()}_${Math.random().toString(36).slice(2)}`;\n\n // Create marker for extension\n const marker: BrowserToolResponse = {\n requiresBrowserExecution: true,\n action,\n args,\n toolCallId,\n };\n\n // If no callback, return marker immediately (extension handles via SSE interception)\n if (!waitForResult) {\n return JSON.stringify(marker);\n }\n\n // With callback: wait for actual results from extension\n // The marker is still returned initially via SSE, but we wait for the callback\n try {\n const result = await waitForResult(action, args, toolCallId);\n return formatResultForLLM(result, action);\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n return `Browser action \"${action}\" failed: ${errorMessage}`;\n }\n };\n };\n\n // browser_click\n tools.push(\n tool(createToolFunction('click'), {\n name: EBrowserTools.CLICK,\n description:\n 'Click element by [index]. ALWAYS include the element text and label in your call for safety review. Use fieldLabel attribute to identify correct element. For form fields, target <input> elements NOT parent <div> containers.',\n schema: BrowserClickSchema,\n })\n );\n\n // browser_type\n tools.push(\n tool(createToolFunction('type'), {\n name: EBrowserTools.TYPE,\n description:\n '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).',\n schema: BrowserTypeSchema,\n })\n );\n\n // browser_navigate\n tools.push(\n tool(createToolFunction('navigate'), {\n name: EBrowserTools.NAVIGATE,\n description:\n 'Navigate to URL (include https://). Returns new page element list.',\n schema: BrowserNavigateSchema,\n })\n );\n\n // browser_scroll\n tools.push(\n tool(createToolFunction('scroll'), {\n name: EBrowserTools.SCROLL,\n description:\n 'Scroll page (up/down/left/right). Returns updated element list.',\n schema: BrowserScrollSchema,\n })\n );\n\n // browser_extract\n tools.push(\n tool(createToolFunction('extract'), {\n name: EBrowserTools.EXTRACT,\n description:\n 'Extract page content. Returns URL, title, and element list.',\n schema: BrowserExtractSchema,\n })\n );\n\n // browser_hover\n tools.push(\n tool(createToolFunction('hover'), {\n name: EBrowserTools.HOVER,\n description:\n 'Hover element by [index] to reveal menus/tooltips. Returns updated element list.',\n schema: BrowserHoverSchema,\n })\n );\n\n // browser_wait\n tools.push(\n tool(createToolFunction('wait'), {\n name: EBrowserTools.WAIT,\n description:\n 'Wait for async content to load. Returns updated element list.',\n schema: BrowserWaitSchema,\n })\n );\n\n // browser_back\n tools.push(\n tool(createToolFunction('back'), {\n name: EBrowserTools.BACK,\n description:\n 'Go back in browser history. Returns previous page element list.',\n schema: BrowserBackSchema,\n })\n );\n\n // browser_screenshot\n tools.push(\n tool(createToolFunction('screenshot'), {\n name: EBrowserTools.SCREENSHOT,\n description:\n 'Capture screenshot. Displayed to user. Use get_page_state for automation.',\n schema: BrowserScreenshotSchema,\n })\n );\n\n // browser_get_page_state\n tools.push(\n tool(createToolFunction('get_page_state'), {\n name: EBrowserTools.GET_PAGE_STATE,\n description:\n 'Get page URL, title, and interactive elements with [index] for actions. Start here.',\n schema: BrowserGetPageStateSchema,\n })\n );\n\n // browser_keypress - for keyboard shortcuts\n tools.push(\n tool(createToolFunction('keypress'), {\n name: EBrowserTools.KEYPRESS,\n description:\n '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.',\n schema: BrowserKeypressSchema,\n })\n );\n\n // browser_switch_tab - for switching between tabs\n tools.push(\n tool(createToolFunction('switch_tab'), {\n name: EBrowserTools.SWITCH_TAB,\n description:\n '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).',\n schema: BrowserSwitchTabSchema,\n })\n );\n\n return tools;\n}\n"],"names":["z","tools","tool"],"mappings":"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"}
@@ -139,7 +139,8 @@ class ToolNode extends run.RunnableCallable {
139
139
  // Resolve the effective execution context for this agent.
140
140
  // Per-agent overrides take precedence — allows handoff agents to bypass HITL
141
141
  // while the primary agent retains interactive approval.
142
- const effectiveContext = (this.agentId && agentExecutionContextOverrides?.[this.agentId]) ?? executionContext;
142
+ const effectiveContext = (this.agentId && agentExecutionContextOverrides?.[this.agentId]) ??
143
+ executionContext;
143
144
  // Scheduled executions bypass all approval checks — no user is present
144
145
  if (effectiveContext === constants.ExecutionContext.SCHEDULED) {
145
146
  return false;
@@ -151,9 +152,19 @@ class ToolNode extends run.RunnableCallable {
151
152
  if (effectiveContext === constants.ExecutionContext.HANDOFF) {
152
153
  return false;
153
154
  }
154
- // Graph-managed tools (handoff/transfer tools) bypass HITL approval —
155
- // these are internal routing mechanisms, not user-facing tool executions
156
- if (this.directToolNames?.has(toolName)) {
155
+ // Graph-managed routing tools (handoff/transfer) bypass HITL approval —
156
+ // these are internal routing mechanisms, not user-facing tool executions.
157
+ //
158
+ // NOTE: `directToolNames` is used for two purposes — (1) marking tools that
159
+ // are loaded as full instances and don't need on-demand ON_TOOL_EXECUTE loading,
160
+ // and (2) bypassing HITL. In event-driven mode ALL built-in tools (including
161
+ // `ask_user`) end up in directToolNames for reason (1), so we cannot use
162
+ // `directToolNames.has(toolName)` as the HITL-bypass test — it would let
163
+ // `ask_user` execute without ever firing interrupt(), defeating the whole tool.
164
+ // Instead, gate the bypass on the actual routing-tool name prefix.
165
+ if (this.directToolNames?.has(toolName) &&
166
+ (toolName.startsWith(_enum.Constants.LC_TRANSFER_TO_) ||
167
+ toolName.startsWith(_enum.Constants.LC_HANDOFF_TO_))) {
157
168
  return false;
158
169
  }
159
170
  // Determine the effective policy for this tool
@@ -185,7 +196,7 @@ class ToolNode extends run.RunnableCallable {
185
196
  * @param config - The runnable config for event dispatch
186
197
  * @returns The approval response from the human
187
198
  */
188
- requestApproval(call, config) {
199
+ async requestApproval(call, config) {
189
200
  const approvalRequest = {
190
201
  type: 'tool_approval_required',
191
202
  toolCallId: call.id ?? '',
@@ -194,9 +205,13 @@ class ToolNode extends run.RunnableCallable {
194
205
  agentId: this.agentId,
195
206
  description: `Tool "${call.name}" wants to execute with the provided arguments.`,
196
207
  };
197
- // Notify host before interrupting allows SSE event emission and DB persistence.
198
- // This is a fire-and-forget notification, NOT the approval mechanism.
199
- events.safeDispatchCustomEvent(_enum.GraphEvents.ON_TOOL_APPROVAL_REQUIRED, approvalRequest, config);
208
+ // MUST awaitinterrupt() throws GraphInterrupt synchronously which unwinds
209
+ // the call stack. Any un-awaited dispatch Promise is abandoned before the
210
+ // host's ON_TOOL_APPROVAL_REQUIRED handler runs, so the MongoDB row never
211
+ // gets written and the subsequent approve-tool POST 404s with
212
+ // "No pending approval found". Awaiting guarantees the handler has persisted
213
+ // the request before we suspend the graph.
214
+ await events.safeDispatchCustomEvent(_enum.GraphEvents.ON_TOOL_APPROVAL_REQUIRED, approvalRequest, config);
200
215
  // interrupt() throws GraphInterrupt on first call (checkpoints state),
201
216
  // returns the resume value on re-execution after Command({ resume }).
202
217
  const response = langgraph.interrupt(approvalRequest);
@@ -294,7 +309,7 @@ class ToolNode extends run.RunnableCallable {
294
309
  // Resumes when host sends Command({ resume: ToolApprovalResponse }).
295
310
  // ========================================================================
296
311
  if (this.requiresApproval(call.name, call.args)) {
297
- const approvalResponse = this.requestApproval(call, config);
312
+ const approvalResponse = await this.requestApproval(call, config);
298
313
  if (!approvalResponse.approved) {
299
314
  // Human denied the tool call - return a denial message
300
315
  return new messages.ToolMessage({
@@ -659,7 +674,7 @@ class ToolNode extends run.RunnableCallable {
659
674
  const denialMessages = [];
660
675
  for (const call of toolCalls) {
661
676
  if (this.requiresApproval(call.name, call.args)) {
662
- const approvalResponse = this.requestApproval(call, config);
677
+ const approvalResponse = await this.requestApproval(call, config);
663
678
  if (!approvalResponse.approved) {
664
679
  denialMessages.push(new messages.ToolMessage({
665
680
  status: 'error',
@@ -913,6 +928,108 @@ class ToolNode extends run.RunnableCallable {
913
928
  combinedOutputs.push(Array.isArray(input) ? [output] : { messages: [output] });
914
929
  }
915
930
  }
931
+ /**
932
+ * Dedupe handoffs targeting the same destination.
933
+ *
934
+ * Parent LLMs sometimes emit multiple parallel transfer_to_<agent> tool
935
+ * calls for the same child agent in one model tick (e.g. "send email 1"
936
+ * and "send email 2" both routed to the Productivity Assistant). LangGraph
937
+ * would otherwise create two parallel Sends into the same subgraph, which
938
+ * produces duplicate handoff entries in the UI and leaves extra
939
+ * tool_call_ids without a clean result when the single child run finishes.
940
+ *
941
+ * Merge same-destination handoffs into one Command whose update.messages
942
+ * carries every original ToolMessage — the primary one has its
943
+ * Instructions field rebuilt from the combined set, and the rest are kept
944
+ * verbatim so the parent message history has a ToolMessage for every
945
+ * tool_call_id (LangChain requires every AI tool_call to be resolved).
946
+ * The child's _extractTransferContext filters all transfer ToolMessages
947
+ * out of the child's view, so it only ever sees the merged instructions.
948
+ */
949
+ if (handoffCommands.length > 1) {
950
+ const byDestination = new Map();
951
+ for (const cmd of handoffCommands) {
952
+ const goto = cmd.goto;
953
+ const dest = typeof goto === 'string' ? goto : goto[0];
954
+ const arr = byDestination.get(dest) ?? [];
955
+ arr.push(cmd);
956
+ byDestination.set(dest, arr);
957
+ }
958
+ const hasDuplicates = Array.from(byDestination.values()).some((arr) => arr.length > 1);
959
+ if (hasDuplicates) {
960
+ const TRANSFER_INSTRUCTIONS_PATTERN = /(?:Instructions?|Context):\s*(.+)/is;
961
+ const mergedHandoffs = [];
962
+ for (const [dest, cmds] of byDestination) {
963
+ if (cmds.length === 1) {
964
+ mergedHandoffs.push(cmds[0]);
965
+ continue;
966
+ }
967
+ const allToolMessages = [];
968
+ const allOtherMessages = [];
969
+ for (const cmd of cmds) {
970
+ const upd = cmd.update;
971
+ for (const m of upd?.messages ?? []) {
972
+ if (m.getType() === 'tool') {
973
+ allToolMessages.push(m);
974
+ }
975
+ else {
976
+ allOtherMessages.push(m);
977
+ }
978
+ }
979
+ }
980
+ if (allToolMessages.length === 0) {
981
+ mergedHandoffs.push(cmds[0]);
982
+ continue;
983
+ }
984
+ const primary = allToolMessages[0];
985
+ const primaryContent = typeof primary.content === 'string'
986
+ ? primary.content
987
+ : JSON.stringify(primary.content);
988
+ const mergedInstructions = [];
989
+ for (const tm of allToolMessages) {
990
+ const c = typeof tm.content === 'string'
991
+ ? tm.content
992
+ : JSON.stringify(tm.content);
993
+ const match = c.match(TRANSFER_INSTRUCTIONS_PATTERN);
994
+ if (match?.[1]) {
995
+ const instr = match[1].trim();
996
+ if (instr && !mergedInstructions.includes(instr)) {
997
+ mergedInstructions.push(instr);
998
+ }
999
+ }
1000
+ }
1001
+ let mergedPrimaryContent = primaryContent;
1002
+ if (mergedInstructions.length > 1) {
1003
+ const header = primaryContent
1004
+ .replace(TRANSFER_INSTRUCTIONS_PATTERN, '')
1005
+ .trimEnd();
1006
+ const combinedInstr = mergedInstructions
1007
+ .map((s, i) => `${i + 1}. ${s}`)
1008
+ .join('\n');
1009
+ mergedPrimaryContent =
1010
+ `${header}\nInstructions: ${combinedInstr}`.trim();
1011
+ }
1012
+ const mergedPrimary = new messages.ToolMessage({
1013
+ content: mergedPrimaryContent,
1014
+ tool_call_id: primary.tool_call_id,
1015
+ name: primary.name,
1016
+ additional_kwargs: { ...primary.additional_kwargs },
1017
+ });
1018
+ const mergedMessages = [
1019
+ mergedPrimary,
1020
+ ...allToolMessages.slice(1),
1021
+ ...allOtherMessages,
1022
+ ];
1023
+ mergedHandoffs.push(new langgraph.Command({
1024
+ graph: langgraph.Command.PARENT,
1025
+ goto: dest,
1026
+ update: { messages: mergedMessages },
1027
+ }));
1028
+ }
1029
+ handoffCommands.length = 0;
1030
+ handoffCommands.push(...mergedHandoffs);
1031
+ }
1032
+ }
916
1033
  /**
917
1034
  * Handle handoff commands - convert to Send objects for parallel execution
918
1035
  * when multiple handoffs are requested