@illuma-ai/agents 1.1.25 → 1.3.0

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 +20 -3
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/common/spawnPath.cjs +104 -0
  4. package/dist/cjs/common/spawnPath.cjs.map +1 -0
  5. package/dist/cjs/graphs/Graph.cjs +87 -31
  6. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  7. package/dist/cjs/graphs/HandoffRegistry.cjs +143 -0
  8. package/dist/cjs/graphs/HandoffRegistry.cjs.map +1 -0
  9. package/dist/cjs/graphs/MultiAgentGraph.cjs +587 -184
  10. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  11. package/dist/cjs/graphs/phases/flushLoop.cjs +214 -0
  12. package/dist/cjs/graphs/phases/flushLoop.cjs.map +1 -0
  13. package/dist/cjs/graphs/phases/memoryFlushPhase.cjs +102 -0
  14. package/dist/cjs/graphs/phases/memoryFlushPhase.cjs.map +1 -0
  15. package/dist/cjs/llm/bedrock/index.cjs +4 -3
  16. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  17. package/dist/cjs/main.cjs +115 -0
  18. package/dist/cjs/main.cjs.map +1 -1
  19. package/dist/cjs/memory/citations.cjs +69 -0
  20. package/dist/cjs/memory/citations.cjs.map +1 -0
  21. package/dist/cjs/memory/compositeBackend.cjs +60 -0
  22. package/dist/cjs/memory/compositeBackend.cjs.map +1 -0
  23. package/dist/cjs/memory/constants.cjs +232 -0
  24. package/dist/cjs/memory/constants.cjs.map +1 -0
  25. package/dist/cjs/memory/embeddings.cjs +151 -0
  26. package/dist/cjs/memory/embeddings.cjs.map +1 -0
  27. package/dist/cjs/memory/factory.cjs +95 -0
  28. package/dist/cjs/memory/factory.cjs.map +1 -0
  29. package/dist/cjs/memory/migrate.cjs +81 -0
  30. package/dist/cjs/memory/migrate.cjs.map +1 -0
  31. package/dist/cjs/memory/mmr.cjs +138 -0
  32. package/dist/cjs/memory/mmr.cjs.map +1 -0
  33. package/dist/cjs/memory/paths.cjs +217 -0
  34. package/dist/cjs/memory/paths.cjs.map +1 -0
  35. package/dist/cjs/memory/pgvectorStore.cjs +225 -0
  36. package/dist/cjs/memory/pgvectorStore.cjs.map +1 -0
  37. package/dist/cjs/memory/recallTracking.cjs +98 -0
  38. package/dist/cjs/memory/recallTracking.cjs.map +1 -0
  39. package/dist/cjs/memory/schema.sql +51 -0
  40. package/dist/cjs/memory/temporalDecay.cjs +118 -0
  41. package/dist/cjs/memory/temporalDecay.cjs.map +1 -0
  42. package/dist/cjs/nodes/ApprovalGateNode.cjs +1 -1
  43. package/dist/cjs/nodes/ApprovalGateNode.cjs.map +1 -1
  44. package/dist/cjs/prompts/memoryFlushPrompt.cjs +49 -0
  45. package/dist/cjs/prompts/memoryFlushPrompt.cjs.map +1 -0
  46. package/dist/cjs/run.cjs +16 -3
  47. package/dist/cjs/run.cjs.map +1 -1
  48. package/dist/cjs/stream.cjs +4 -4
  49. package/dist/cjs/stream.cjs.map +1 -1
  50. package/dist/cjs/tools/AskUser.cjs +6 -1
  51. package/dist/cjs/tools/AskUser.cjs.map +1 -1
  52. package/dist/cjs/tools/BrowserTools.cjs +1 -1
  53. package/dist/cjs/tools/BrowserTools.cjs.map +1 -1
  54. package/dist/cjs/tools/ToolNode.cjs +127 -10
  55. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  56. package/dist/cjs/tools/approval/constants.cjs +2 -2
  57. package/dist/cjs/tools/approval/constants.cjs.map +1 -1
  58. package/dist/cjs/tools/memory/index.cjs +58 -0
  59. package/dist/cjs/tools/memory/index.cjs.map +1 -0
  60. package/dist/cjs/tools/memory/memoryAppendTool.cjs +69 -0
  61. package/dist/cjs/tools/memory/memoryAppendTool.cjs.map +1 -0
  62. package/dist/cjs/tools/memory/memoryGetTool.cjs +49 -0
  63. package/dist/cjs/tools/memory/memoryGetTool.cjs.map +1 -0
  64. package/dist/cjs/tools/memory/memorySearchTool.cjs +65 -0
  65. package/dist/cjs/tools/memory/memorySearchTool.cjs.map +1 -0
  66. package/dist/cjs/tools/memory/shared.cjs +106 -0
  67. package/dist/cjs/tools/memory/shared.cjs.map +1 -0
  68. package/dist/cjs/types/graph.cjs.map +1 -1
  69. package/dist/cjs/utils/childAgentContext.cjs +242 -0
  70. package/dist/cjs/utils/childAgentContext.cjs.map +1 -0
  71. package/dist/cjs/utils/events.cjs +36 -4
  72. package/dist/cjs/utils/events.cjs.map +1 -1
  73. package/dist/cjs/utils/finishReasons.cjs +44 -0
  74. package/dist/cjs/utils/finishReasons.cjs.map +1 -0
  75. package/dist/cjs/utils/llm.cjs.map +1 -1
  76. package/dist/cjs/utils/logging.cjs +34 -0
  77. package/dist/cjs/utils/logging.cjs.map +1 -0
  78. package/dist/cjs/utils/toolCallNormalization.cjs +250 -0
  79. package/dist/cjs/utils/toolCallNormalization.cjs.map +1 -0
  80. package/dist/esm/agents/AgentContext.mjs +20 -3
  81. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  82. package/dist/esm/common/spawnPath.mjs +95 -0
  83. package/dist/esm/common/spawnPath.mjs.map +1 -0
  84. package/dist/esm/graphs/Graph.mjs +87 -31
  85. package/dist/esm/graphs/Graph.mjs.map +1 -1
  86. package/dist/esm/graphs/HandoffRegistry.mjs +141 -0
  87. package/dist/esm/graphs/HandoffRegistry.mjs.map +1 -0
  88. package/dist/esm/graphs/MultiAgentGraph.mjs +587 -184
  89. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  90. package/dist/esm/graphs/phases/flushLoop.mjs +209 -0
  91. package/dist/esm/graphs/phases/flushLoop.mjs.map +1 -0
  92. package/dist/esm/graphs/phases/memoryFlushPhase.mjs +99 -0
  93. package/dist/esm/graphs/phases/memoryFlushPhase.mjs.map +1 -0
  94. package/dist/esm/llm/bedrock/index.mjs +4 -3
  95. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  96. package/dist/esm/main.mjs +21 -0
  97. package/dist/esm/main.mjs.map +1 -1
  98. package/dist/esm/memory/citations.mjs +64 -0
  99. package/dist/esm/memory/citations.mjs.map +1 -0
  100. package/dist/esm/memory/compositeBackend.mjs +58 -0
  101. package/dist/esm/memory/compositeBackend.mjs.map +1 -0
  102. package/dist/esm/memory/constants.mjs +198 -0
  103. package/dist/esm/memory/constants.mjs.map +1 -0
  104. package/dist/esm/memory/embeddings.mjs +148 -0
  105. package/dist/esm/memory/embeddings.mjs.map +1 -0
  106. package/dist/esm/memory/factory.mjs +93 -0
  107. package/dist/esm/memory/factory.mjs.map +1 -0
  108. package/dist/esm/memory/migrate.mjs +78 -0
  109. package/dist/esm/memory/migrate.mjs.map +1 -0
  110. package/dist/esm/memory/mmr.mjs +130 -0
  111. package/dist/esm/memory/mmr.mjs.map +1 -0
  112. package/dist/esm/memory/paths.mjs +207 -0
  113. package/dist/esm/memory/paths.mjs.map +1 -0
  114. package/dist/esm/memory/pgvectorStore.mjs +223 -0
  115. package/dist/esm/memory/pgvectorStore.mjs.map +1 -0
  116. package/dist/esm/memory/recallTracking.mjs +94 -0
  117. package/dist/esm/memory/recallTracking.mjs.map +1 -0
  118. package/dist/esm/memory/schema.sql +51 -0
  119. package/dist/esm/memory/temporalDecay.mjs +110 -0
  120. package/dist/esm/memory/temporalDecay.mjs.map +1 -0
  121. package/dist/esm/nodes/ApprovalGateNode.mjs +1 -1
  122. package/dist/esm/nodes/ApprovalGateNode.mjs.map +1 -1
  123. package/dist/esm/prompts/memoryFlushPrompt.mjs +44 -0
  124. package/dist/esm/prompts/memoryFlushPrompt.mjs.map +1 -0
  125. package/dist/esm/run.mjs +16 -3
  126. package/dist/esm/run.mjs.map +1 -1
  127. package/dist/esm/stream.mjs +4 -4
  128. package/dist/esm/stream.mjs.map +1 -1
  129. package/dist/esm/tools/AskUser.mjs +6 -1
  130. package/dist/esm/tools/AskUser.mjs.map +1 -1
  131. package/dist/esm/tools/BrowserTools.mjs +1 -1
  132. package/dist/esm/tools/BrowserTools.mjs.map +1 -1
  133. package/dist/esm/tools/ToolNode.mjs +128 -11
  134. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  135. package/dist/esm/tools/approval/constants.mjs +2 -2
  136. package/dist/esm/tools/approval/constants.mjs.map +1 -1
  137. package/dist/esm/tools/memory/index.mjs +46 -0
  138. package/dist/esm/tools/memory/index.mjs.map +1 -0
  139. package/dist/esm/tools/memory/memoryAppendTool.mjs +67 -0
  140. package/dist/esm/tools/memory/memoryAppendTool.mjs.map +1 -0
  141. package/dist/esm/tools/memory/memoryGetTool.mjs +47 -0
  142. package/dist/esm/tools/memory/memoryGetTool.mjs.map +1 -0
  143. package/dist/esm/tools/memory/memorySearchTool.mjs +63 -0
  144. package/dist/esm/tools/memory/memorySearchTool.mjs.map +1 -0
  145. package/dist/esm/tools/memory/shared.mjs +98 -0
  146. package/dist/esm/tools/memory/shared.mjs.map +1 -0
  147. package/dist/esm/types/graph.mjs.map +1 -1
  148. package/dist/esm/utils/childAgentContext.mjs +237 -0
  149. package/dist/esm/utils/childAgentContext.mjs.map +1 -0
  150. package/dist/esm/utils/events.mjs +36 -5
  151. package/dist/esm/utils/events.mjs.map +1 -1
  152. package/dist/esm/utils/finishReasons.mjs +41 -0
  153. package/dist/esm/utils/finishReasons.mjs.map +1 -0
  154. package/dist/esm/utils/llm.mjs.map +1 -1
  155. package/dist/esm/utils/logging.mjs +31 -0
  156. package/dist/esm/utils/logging.mjs.map +1 -0
  157. package/dist/esm/utils/toolCallNormalization.mjs +247 -0
  158. package/dist/esm/utils/toolCallNormalization.mjs.map +1 -0
  159. package/dist/types/common/index.d.ts +1 -0
  160. package/dist/types/common/spawnPath.d.ts +59 -0
  161. package/dist/types/graphs/HandoffRegistry.d.ts +97 -0
  162. package/dist/types/graphs/MultiAgentGraph.d.ts +58 -18
  163. package/dist/types/graphs/index.d.ts +1 -0
  164. package/dist/types/graphs/phases/flushLoop.d.ts +106 -0
  165. package/dist/types/graphs/phases/memoryFlushPhase.d.ts +100 -0
  166. package/dist/types/index.d.ts +7 -0
  167. package/dist/types/memory/__tests__/mockBackend.d.ts +40 -0
  168. package/dist/types/memory/citations.d.ts +39 -0
  169. package/dist/types/memory/compositeBackend.d.ts +30 -0
  170. package/dist/types/memory/constants.d.ts +121 -0
  171. package/dist/types/memory/embeddings.d.ts +15 -0
  172. package/dist/types/memory/factory.d.ts +23 -0
  173. package/dist/types/memory/index.d.ts +21 -0
  174. package/dist/types/memory/migrate.d.ts +14 -0
  175. package/dist/types/memory/mmr.d.ts +50 -0
  176. package/dist/types/memory/paths.d.ts +107 -0
  177. package/dist/types/memory/pgvectorStore.d.ts +56 -0
  178. package/dist/types/memory/recallTracking.d.ts +30 -0
  179. package/dist/types/memory/temporalDecay.d.ts +53 -0
  180. package/dist/types/memory/types.d.ts +182 -0
  181. package/dist/types/prompts/memoryFlushPrompt.d.ts +54 -0
  182. package/dist/types/run.d.ts +1 -0
  183. package/dist/types/tools/AskUser.d.ts +1 -1
  184. package/dist/types/tools/BrowserTools.d.ts +2 -2
  185. package/dist/types/tools/approval/constants.d.ts +2 -2
  186. package/dist/types/tools/memory/index.d.ts +39 -0
  187. package/dist/types/tools/memory/memoryAppendTool.d.ts +27 -0
  188. package/dist/types/tools/memory/memoryGetTool.d.ts +22 -0
  189. package/dist/types/tools/memory/memorySearchTool.d.ts +22 -0
  190. package/dist/types/tools/memory/shared.d.ts +106 -0
  191. package/dist/types/types/graph.d.ts +16 -3
  192. package/dist/types/utils/childAgentContext.d.ts +99 -0
  193. package/dist/types/utils/events.d.ts +21 -0
  194. package/dist/types/utils/finishReasons.d.ts +32 -0
  195. package/dist/types/utils/logging.d.ts +2 -0
  196. package/dist/types/utils/toolCallNormalization.d.ts +44 -0
  197. package/package.json +6 -4
  198. package/src/agents/AgentContext.ts +26 -3
  199. package/src/common/__tests__/enum.test.ts +4 -2
  200. package/src/common/__tests__/spawnPath.test.ts +110 -0
  201. package/src/common/index.ts +1 -0
  202. package/src/common/spawnPath.ts +101 -0
  203. package/src/graphs/Graph.ts +94 -43
  204. package/src/graphs/HandoffRegistry.ts +199 -0
  205. package/src/graphs/MultiAgentGraph.ts +694 -226
  206. package/src/graphs/__tests__/HandoffRegistry.test.ts +410 -0
  207. package/src/graphs/__tests__/multi-agent-delegate.test.ts +61 -16
  208. package/src/graphs/__tests__/multi-agent-edges.test.ts +4 -2
  209. package/src/graphs/__tests__/multi-agent-nested-subgraph.test.ts +221 -0
  210. package/src/graphs/__tests__/structured-output.integration.test.ts +212 -118
  211. package/src/graphs/contextManagement.e2e.test.ts +1 -1
  212. package/src/graphs/index.ts +1 -0
  213. package/src/graphs/phases/__tests__/flushLoop.test.ts +264 -0
  214. package/src/graphs/phases/__tests__/memoryFlushPhase.test.ts +37 -0
  215. package/src/graphs/phases/__tests__/runMemoryFlush.test.ts +150 -0
  216. package/src/graphs/phases/flushLoop.ts +303 -0
  217. package/src/graphs/phases/memoryFlushPhase.ts +209 -0
  218. package/src/index.ts +30 -1
  219. package/src/llm/bedrock/index.ts +4 -5
  220. package/src/memory/__tests__/citations.test.ts +61 -0
  221. package/src/memory/__tests__/compositeBackend.test.ts +79 -0
  222. package/src/memory/__tests__/isolation.test.ts +206 -0
  223. package/src/memory/__tests__/mmr.test.ts +148 -0
  224. package/src/memory/__tests__/mockBackend.ts +161 -0
  225. package/src/memory/__tests__/paths.test.ts +168 -0
  226. package/src/memory/__tests__/recallTracking.test.ts +96 -0
  227. package/src/memory/__tests__/temporalDecay.test.ts +151 -0
  228. package/src/memory/citations.ts +80 -0
  229. package/src/memory/compositeBackend.ts +99 -0
  230. package/src/memory/constants.ts +229 -0
  231. package/src/memory/embeddings.ts +188 -0
  232. package/src/memory/factory.ts +111 -0
  233. package/src/memory/index.ts +46 -0
  234. package/src/memory/migrate.ts +116 -0
  235. package/src/memory/mmr.ts +161 -0
  236. package/src/memory/paths.ts +258 -0
  237. package/src/memory/pgvectorStore.ts +324 -0
  238. package/src/memory/recallTracking.ts +127 -0
  239. package/src/memory/schema.sql +51 -0
  240. package/src/memory/temporalDecay.ts +134 -0
  241. package/src/memory/types.ts +185 -0
  242. package/src/nodes/ApprovalGateNode.ts +4 -10
  243. package/src/nodes/__tests__/ApprovalGateNode.test.ts +11 -20
  244. package/src/prompts/memoryFlushPrompt.ts +78 -0
  245. package/src/run.ts +17 -6
  246. package/src/scripts/test-bedrock-handoff-autonomous.ts +56 -20
  247. package/src/specs/agent-handoffs-bedrock.integration.test.ts +8 -5
  248. package/src/specs/agent-handoffs.test.ts +8 -2
  249. package/src/stream.ts +4 -6
  250. package/src/tools/AskUser.ts +7 -2
  251. package/src/tools/BrowserTools.ts +3 -5
  252. package/src/tools/ToolNode.ts +150 -13
  253. package/src/tools/__tests__/ToolApproval.test.ts +22 -9
  254. package/src/tools/approval/__tests__/constants.test.ts +4 -4
  255. package/src/tools/approval/constants.ts +2 -2
  256. package/src/tools/memory/__tests__/memoryTools.test.ts +205 -0
  257. package/src/tools/memory/index.ts +96 -0
  258. package/src/tools/memory/memoryAppendTool.ts +101 -0
  259. package/src/tools/memory/memoryGetTool.ts +53 -0
  260. package/src/tools/memory/memorySearchTool.ts +80 -0
  261. package/src/tools/memory/shared.ts +169 -0
  262. package/src/tools/search/search.test.ts +6 -1
  263. package/src/types/graph.ts +16 -3
  264. package/src/utils/__tests__/childAgentContext.test.ts +217 -0
  265. package/src/utils/__tests__/finishReasons.test.ts +55 -0
  266. package/src/utils/__tests__/toolCallNormalization.test.ts +181 -0
  267. package/src/utils/childAgentContext.ts +259 -0
  268. package/src/utils/events.ts +37 -4
  269. package/src/utils/finishReasons.ts +40 -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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"childAgentContext.cjs","sources":["../../../src/utils/childAgentContext.ts"],"sourcesContent":["/**\n * Child-agent context preparation utilities.\n *\n * When a parent agent invokes a child agent — via handoff, sequence edge,\n * or scoped subgraph — the child cannot just receive `state.messages`\n * verbatim. The parent's conversation contains tool_use/tool_result blocks\n * that are (a) often incompatible with the child's tool registry, and\n * (b) actively harmful to the child's ability to reason cleanly about its\n * own task (noise → schema confusion → malformed tool_use).\n *\n * This module provides the two canonical strategies, extracted from\n * `MultiAgentGraph` so they can be unit-tested in isolation and reused by\n * future sub-agent orchestrators:\n *\n * 1. `prepareHandoffMessages` — \"cleaned parent history\"\n * Used when the child still needs the orchestrator's context (it's\n * the handoff target). Drops orphaned tool_use, compacts paired\n * tool_use/tool_result into text summaries, and guarantees the tail\n * is a HumanMessage so Bedrock/VertexAI won't reject the conversation\n * with \"assistant message prefill\" errors.\n *\n * 2. `prepareIsolatedChildMessages` — \"fresh session\"\n * Used for downstream sequence-node children inside a scoped subgraph.\n * The child sees only the original user request plus a synthetic\n * HumanMessage summarizing the upstream agent's final text output and\n * directing the child to act. Raw upstream tool_use/tool_result blocks\n * are discarded.\n *\n * Both helpers are pure functions over message arrays — no I/O, no\n * LangGraph coupling — so they can be exercised by unit tests with\n * synthetic message fixtures.\n */\n\nimport { AIMessage, HumanMessage, ToolMessage } from '@langchain/core/messages';\nimport type { AIMessageChunk, BaseMessage } from '@langchain/core/messages';\n\n/* -------------------------------------------------------------------------- */\n/* Prompt template constants (kept outside the functions for reuse/tuning) */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Prefix injected in front of a trailing AIMessage when we flip it to a\n * HumanMessage to satisfy provider \"last message must be user\" rules.\n */\nexport const HANDOFF_TAIL_CONTEXT_PREFIX = '[Context from orchestrator]: ';\n\n/**\n * Directive task-framing wrapper for downstream scoped-subgraph children.\n *\n * Design notes — each line is load-bearing:\n * - \"Prior step output\" names the upstream role without leaking the\n * agent's internal id.\n * - \"You MUST now perform...\" replaces ambiguity with obligation.\n * - \"system instructions\" references the agent's stored system prompt\n * as the source of task definition — so operators can tune behavior\n * via data, not code.\n * - The tool-first clause prevents small/fast models from stalling on a\n * text-only acknowledgement when a tool action is expected.\n */\nexport function buildIsolatedChildPrompt(upstreamText: string): string {\n return (\n '## Prior step output\\n\\n' +\n upstreamText +\n '\\n\\n---\\n\\n' +\n '## Your task\\n\\n' +\n 'The previous step in this workflow has completed. You MUST now ' +\n 'perform your own task as defined in your system instructions, ' +\n \"using the prior step's output as input where relevant.\\n\\n\" +\n 'If your task requires calling a tool, call it directly — do not ' +\n 'ask for clarification and do not produce a text-only response when ' +\n 'a tool action is expected.'\n );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Internal helpers */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Extract concatenated text content from an AI message's content field.\n * Handles both the string shape (OpenAI/plain) and the array-of-blocks\n * shape (Anthropic/Bedrock).\n */\nfunction extractAIText(msg: AIMessage | AIMessageChunk): string {\n const content = msg.content;\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n return (content as Array<{ type?: string; text?: string }>)\n .filter((b) => b.type === 'text' && typeof b.text === 'string')\n .map((b) => b.text ?? '')\n .join('\\n');\n}\n\n/* -------------------------------------------------------------------------- */\n/* Strategy 1: cleaned parent history (handoff target / root subgraph) */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Prepare messages for a handoff child agent.\n *\n * Handles two problems that break Bedrock/Anthropic conversations:\n *\n * 1. **Orphaned tool_use**: The parent's AI message contains a `tool_use`\n * block for the handoff tool itself, with no matching `tool_result`.\n * Providers (Bedrock/Anthropic) reject this.\n *\n * 2. **Paired tool_use/tool_result in history**: The child may not have\n * the same tools as the parent. Bedrock requires `toolConfig` when any\n * tool_use/tool_result blocks exist in the history. Compacting these\n * into text summaries avoids the requirement and reduces context bloat.\n *\n * Also ensures the tail is a HumanMessage — some providers reject a\n * conversation that ends with an assistant message.\n *\n * @param messages - Current state messages from the parent\n * @returns A sanitized copy, safe to pass to any provider as the child's\n * input regardless of which tools the child has registered.\n */\nexport function prepareHandoffMessages(messages: BaseMessage[]): BaseMessage[] {\n if (messages.length === 0) return messages;\n\n /** Collect tool_result IDs so we know which tool_use blocks are paired */\n const pairedToolCallIds = new Set<string>();\n for (const msg of messages) {\n if (msg.getType() === 'tool') {\n const tm = msg as ToolMessage;\n if (tm.tool_call_id) pairedToolCallIds.add(tm.tool_call_id);\n }\n }\n\n /**\n * Pass 1: Drop all ToolMessages (paired ones are compacted in pass 2),\n * rewrite AI messages with tool_calls into plain-text summaries, leave\n * other messages untouched.\n */\n const cleaned: BaseMessage[] = [];\n for (const msg of messages) {\n if (msg.getType() === 'tool') continue;\n\n if (msg.getType() !== 'ai') {\n cleaned.push(msg);\n continue;\n }\n\n const aiMsg = msg as AIMessage | AIMessageChunk;\n const toolCalls = aiMsg.tool_calls ?? [];\n if (toolCalls.length === 0) {\n cleaned.push(msg);\n continue;\n }\n\n const textContent = extractAIText(aiMsg);\n\n const toolSummaries: string[] = [];\n for (const tc of toolCalls) {\n if (tc.id != null && pairedToolCallIds.has(tc.id)) {\n const toolResult = messages.find(\n (m) =>\n m.getType() === 'tool' && (m as ToolMessage).tool_call_id === tc.id\n ) as ToolMessage | undefined;\n const resultContent = toolResult\n ? typeof toolResult.content === 'string'\n ? toolResult.content.slice(0, 500)\n : '[complex result]'\n : '[no result]';\n toolSummaries.push(`[Tool \"${tc.name}\": ${resultContent}]`);\n }\n // Orphaned tool_use blocks (no matching result) are silently dropped.\n }\n\n const parts = [textContent, ...toolSummaries].filter(Boolean);\n if (parts.length > 0) {\n cleaned.push(\n new AIMessage({ content: parts.join('\\n\\n'), id: aiMsg.id })\n );\n }\n }\n\n /**\n * Ensure messages end with a HumanMessage. After stripping tool artifacts\n * the tail may be an AIMessage, which Bedrock/VertexAI reject. Convert it\n * to a HumanMessage preserving whatever text content was present, or drop\n * it entirely if empty.\n */\n if (cleaned.length > 0 && cleaned[cleaned.length - 1].getType() === 'ai') {\n const lastAI = cleaned[cleaned.length - 1];\n const content = typeof lastAI.content === 'string' ? lastAI.content : '';\n if (content.trim()) {\n cleaned[cleaned.length - 1] = new HumanMessage(\n `${HANDOFF_TAIL_CONTEXT_PREFIX}${content}`\n );\n } else {\n cleaned.pop();\n }\n }\n\n return cleaned;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Strategy 2: isolated fresh session (downstream scoped-subgraph child) */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Build an ISOLATED message context for a downstream scoped-subgraph node.\n *\n * Unlike `prepareHandoffMessages` (which cleans up tool_use artifacts but\n * preserves most of the parent history), this helper produces a fresh\n * minimal context containing only:\n *\n * 1. The original user request (first HumanMessage in the history)\n * 2. A synthetic HumanMessage summarizing the upstream agent's final\n * text output and directing the downstream agent to act on it\n *\n * Tool_use / tool_result blocks from the upstream agent are discarded —\n * the downstream agent shouldn't reason about how the upstream agent did\n * its work, only about the result.\n *\n * This \"fresh subagent session\" pattern is the primary defense against\n * schema confusion / malformed tool_use JSON that occurs when downstream\n * models see a noisy upstream conversation.\n *\n * Defensive fallback: if the messages array contains neither a user\n * message nor a non-empty upstream AI message, return the input unchanged\n * so the caller still has something to invoke on. This only matters for\n * malformed state fixtures in tests.\n */\nexport function prepareIsolatedChildMessages(\n messages: BaseMessage[]\n): BaseMessage[] {\n if (messages.length === 0) return messages;\n\n /** First HumanMessage is the original user request */\n const originalUser = messages.find((m) => m.getType() === 'human');\n\n /** Most recent AIMessage with non-empty text content */\n let upstreamText = '';\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i];\n if (msg.getType() !== 'ai') continue;\n const text = extractAIText(msg as AIMessage | AIMessageChunk);\n if (text.trim()) {\n upstreamText = text;\n break;\n }\n }\n\n const result: BaseMessage[] = [];\n if (originalUser) result.push(originalUser);\n\n if (upstreamText.trim()) {\n result.push(new HumanMessage(buildIsolatedChildPrompt(upstreamText)));\n } else if (result.length === 0) {\n /** Defensive: nothing to isolate — fall back to raw messages */\n return messages;\n }\n\n return result;\n}\n"],"names":["messages","AIMessage","HumanMessage"],"mappings":"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"}
@@ -1,25 +1,56 @@
1
1
  'use strict';
2
2
 
3
3
  var dispatch = require('@langchain/core/callbacks/dispatch');
4
+ var singletons = require('@langchain/core/singletons');
4
5
 
5
6
  /* eslint-disable no-console */
6
7
  // src/utils/events.ts
8
+ /**
9
+ * Returns the RunnableConfig currently active in LangChain's AsyncLocalStorage,
10
+ * or undefined if none is installed. This is the per-async-branch config that
11
+ * LangGraph installs when entering a node — it carries the correct
12
+ * `metadata.spawnKey` for child subgraph invocations inside `Promise.all`
13
+ * parallel handoffs.
14
+ *
15
+ * Prefer this over any Graph-instance-cached config (e.g. `this.config`)
16
+ * when dispatching events from code that may run concurrently across multiple
17
+ * child subgraphs. An instance-level cache is shared state and races between
18
+ * siblings — the last child to enter wins, so events fire with the wrong
19
+ * child's metadata and the backend routes them to the wrong spawnKey.
20
+ */
21
+ function getCurrentRunnableConfig() {
22
+ try {
23
+ return singletons.AsyncLocalStorageProviderSingleton.getInstance().getStore();
24
+ }
25
+ catch {
26
+ return undefined;
27
+ }
28
+ }
7
29
  /**
8
30
  * Safely dispatches a custom event and properly awaits it to avoid
9
31
  * race conditions where events are dispatched after run cleanup.
32
+ *
33
+ * **Parallel-handoff correctness:** callers should prefer passing
34
+ * `undefined` (or the per-node runtime config). When `config` is omitted,
35
+ * LangChain's `ensureConfig` reads the current RunnableConfig from
36
+ * AsyncLocalStorage, which is correctly isolated per async branch under
37
+ * `Promise.all`. Passing a stale instance-cached config overrides that
38
+ * implicit config's metadata and cross-contaminates parallel children.
10
39
  */
11
40
  async function safeDispatchCustomEvent(event, payload, config) {
12
41
  try {
13
- await dispatch.dispatchCustomEvent(event, payload, config);
42
+ // If the caller did not pass a config, fall back to the current
43
+ // AsyncLocalStorage-resident runnable config so nested Promise.all
44
+ // branches each use their own metadata.
45
+ const effectiveConfig = config ?? getCurrentRunnableConfig();
46
+ await dispatch.dispatchCustomEvent(event, payload, effectiveConfig);
14
47
  }
15
48
  catch (e) {
16
49
  // Check if this is the known EventStreamCallbackHandler error
17
50
  if (e instanceof Error &&
18
51
  e.message.includes('handleCustomEvent: Run ID') &&
19
52
  e.message.includes('not found in run map')) {
20
- // Suppress this specific error - it's expected during parallel execution
21
- // when EventStreamCallbackHandler loses track of run IDs
22
- // console.debug('Suppressed error dispatching custom event:', e);
53
+ // Suppress expected during parallel/async execution
23
54
  return;
24
55
  }
25
56
  // Log other errors
@@ -27,5 +58,6 @@ async function safeDispatchCustomEvent(event, payload, config) {
27
58
  }
28
59
  }
29
60
 
61
+ exports.getCurrentRunnableConfig = getCurrentRunnableConfig;
30
62
  exports.safeDispatchCustomEvent = safeDispatchCustomEvent;
31
63
  //# sourceMappingURL=events.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"events.cjs","sources":["../../../src/utils/events.ts"],"sourcesContent":["/* eslint-disable no-console */\n// src/utils/events.ts\nimport { dispatchCustomEvent } from '@langchain/core/callbacks/dispatch';\nimport type { RunnableConfig } from '@langchain/core/runnables';\n\n/**\n * Safely dispatches a custom event and properly awaits it to avoid\n * race conditions where events are dispatched after run cleanup.\n */\nexport async function safeDispatchCustomEvent(\n event: string,\n payload: unknown,\n config?: RunnableConfig\n): Promise<void> {\n try {\n await dispatchCustomEvent(event, payload, config);\n } catch (e) {\n // Check if this is the known EventStreamCallbackHandler error\n if (\n e instanceof Error &&\n e.message.includes('handleCustomEvent: Run ID') &&\n e.message.includes('not found in run map')\n ) {\n // Suppress this specific error - it's expected during parallel execution\n // when EventStreamCallbackHandler loses track of run IDs\n // console.debug('Suppressed error dispatching custom event:', e);\n return;\n }\n // Log other errors\n console.error('Error dispatching custom event:', e);\n }\n}\n"],"names":["dispatchCustomEvent"],"mappings":";;;;AAAA;AACA;AAIA;;;AAGG;AACI,eAAe,uBAAuB,CAC3C,KAAa,EACb,OAAgB,EAChB,MAAuB,EAAA;AAEvB,IAAA,IAAI;QACF,MAAMA,4BAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC;IACnD;IAAE,OAAO,CAAC,EAAE;;QAEV,IACE,CAAC,YAAY,KAAK;AAClB,YAAA,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,2BAA2B,CAAC;YAC/C,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAC,EAC1C;;;;YAIA;QACF;;AAEA,QAAA,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,CAAC,CAAC;IACrD;AACF;;;;"}
1
+ {"version":3,"file":"events.cjs","sources":["../../../src/utils/events.ts"],"sourcesContent":["/* eslint-disable no-console */\n// src/utils/events.ts\nimport { dispatchCustomEvent } from '@langchain/core/callbacks/dispatch';\nimport { AsyncLocalStorageProviderSingleton } from '@langchain/core/singletons';\nimport type { RunnableConfig } from '@langchain/core/runnables';\n\n/**\n * Returns the RunnableConfig currently active in LangChain's AsyncLocalStorage,\n * or undefined if none is installed. This is the per-async-branch config that\n * LangGraph installs when entering a node — it carries the correct\n * `metadata.spawnKey` for child subgraph invocations inside `Promise.all`\n * parallel handoffs.\n *\n * Prefer this over any Graph-instance-cached config (e.g. `this.config`)\n * when dispatching events from code that may run concurrently across multiple\n * child subgraphs. An instance-level cache is shared state and races between\n * siblings — the last child to enter wins, so events fire with the wrong\n * child's metadata and the backend routes them to the wrong spawnKey.\n */\nexport function getCurrentRunnableConfig(): RunnableConfig | undefined {\n try {\n return AsyncLocalStorageProviderSingleton.getInstance().getStore() as\n | RunnableConfig\n | undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Safely dispatches a custom event and properly awaits it to avoid\n * race conditions where events are dispatched after run cleanup.\n *\n * **Parallel-handoff correctness:** callers should prefer passing\n * `undefined` (or the per-node runtime config). When `config` is omitted,\n * LangChain's `ensureConfig` reads the current RunnableConfig from\n * AsyncLocalStorage, which is correctly isolated per async branch under\n * `Promise.all`. Passing a stale instance-cached config overrides that\n * implicit config's metadata and cross-contaminates parallel children.\n */\nexport async function safeDispatchCustomEvent(\n event: string,\n payload: unknown,\n config?: RunnableConfig\n): Promise<void> {\n try {\n // If the caller did not pass a config, fall back to the current\n // AsyncLocalStorage-resident runnable config so nested Promise.all\n // branches each use their own metadata.\n const effectiveConfig = config ?? getCurrentRunnableConfig();\n await dispatchCustomEvent(event, payload, effectiveConfig);\n } catch (e) {\n // Check if this is the known EventStreamCallbackHandler error\n if (\n e instanceof Error &&\n e.message.includes('handleCustomEvent: Run ID') &&\n e.message.includes('not found in run map')\n ) {\n // Suppress expected during parallel/async execution\n return;\n }\n // Log other errors\n console.error('Error dispatching custom event:', e);\n }\n}\n"],"names":["AsyncLocalStorageProviderSingleton","dispatchCustomEvent"],"mappings":";;;;;AAAA;AACA;AAKA;;;;;;;;;;;;AAYG;SACa,wBAAwB,GAAA;AACtC,IAAA,IAAI;AACF,QAAA,OAAOA,6CAAkC,CAAC,WAAW,EAAE,CAAC,QAAQ,EAEnD;IACf;AAAE,IAAA,MAAM;AACN,QAAA,OAAO,SAAS;IAClB;AACF;AAEA;;;;;;;;;;AAUG;AACI,eAAe,uBAAuB,CAC3C,KAAa,EACb,OAAgB,EAChB,MAAuB,EAAA;AAEvB,IAAA,IAAI;;;;AAIF,QAAA,MAAM,eAAe,GAAG,MAAM,IAAI,wBAAwB,EAAE;QAC5D,MAAMC,4BAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,eAAe,CAAC;IAC5D;IAAE,OAAO,CAAC,EAAE;;QAEV,IACE,CAAC,YAAY,KAAK;AAClB,YAAA,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,2BAA2B,CAAC;YAC/C,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAC,EAC1C;;YAEA;QACF;;AAEA,QAAA,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,CAAC,CAAC;IACrD;AACF;;;;;"}
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Finish-reason constants and helpers.
5
+ *
6
+ * LLM providers emit different keys (`finish_reason`, `stop_reason`,
7
+ * `stopReason`, `finishReason`) and different values for the same concept.
8
+ * This module is the single source of truth for detecting *truncation* —
9
+ * i.e., the model hit its output budget and the response is incomplete.
10
+ *
11
+ * Used by:
12
+ * - `Graph.ts` — sticky `lastFinishReason` across inner subgraph invokes,
13
+ * so host's continuation retry can detect truncation that happens
14
+ * inside a scoped-subgraph child node.
15
+ * - Any future continuation / auto-retry logic added to agents.
16
+ *
17
+ * Kept as a small utils module (instead of inline in Graph.ts) so that
18
+ * additional callers — e.g., structured output recovery, sub-agent
19
+ * orchestrators — can reuse the exact same detection logic without drift.
20
+ */
21
+ /**
22
+ * Canonical set of finish-reason strings that mean "output was truncated".
23
+ *
24
+ * Covers:
25
+ * - `max_tokens` — Anthropic direct API, Bedrock
26
+ * - `length` — OpenAI/Azure
27
+ * - `MAX_TOKENS` — VertexAI/Google (uppercased enum)
28
+ */
29
+ const TRUNCATION_FINISH_REASONS = new Set([
30
+ 'max_tokens',
31
+ 'length',
32
+ 'MAX_TOKENS',
33
+ ]);
34
+ /**
35
+ * @returns true when the given finish/stop reason indicates the response
36
+ * was cut short by the output token budget.
37
+ */
38
+ function isTruncationReason(reason) {
39
+ return reason != null && TRUNCATION_FINISH_REASONS.has(reason);
40
+ }
41
+
42
+ exports.TRUNCATION_FINISH_REASONS = TRUNCATION_FINISH_REASONS;
43
+ exports.isTruncationReason = isTruncationReason;
44
+ //# sourceMappingURL=finishReasons.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finishReasons.cjs","sources":["../../../src/utils/finishReasons.ts"],"sourcesContent":["/**\n * Finish-reason constants and helpers.\n *\n * LLM providers emit different keys (`finish_reason`, `stop_reason`,\n * `stopReason`, `finishReason`) and different values for the same concept.\n * This module is the single source of truth for detecting *truncation* —\n * i.e., the model hit its output budget and the response is incomplete.\n *\n * Used by:\n * - `Graph.ts` — sticky `lastFinishReason` across inner subgraph invokes,\n * so host's continuation retry can detect truncation that happens\n * inside a scoped-subgraph child node.\n * - Any future continuation / auto-retry logic added to agents.\n *\n * Kept as a small utils module (instead of inline in Graph.ts) so that\n * additional callers — e.g., structured output recovery, sub-agent\n * orchestrators — can reuse the exact same detection logic without drift.\n */\n\n/**\n * Canonical set of finish-reason strings that mean \"output was truncated\".\n *\n * Covers:\n * - `max_tokens` — Anthropic direct API, Bedrock\n * - `length` — OpenAI/Azure\n * - `MAX_TOKENS` — VertexAI/Google (uppercased enum)\n */\nexport const TRUNCATION_FINISH_REASONS: ReadonlySet<string> = new Set([\n 'max_tokens',\n 'length',\n 'MAX_TOKENS',\n]);\n\n/**\n * @returns true when the given finish/stop reason indicates the response\n * was cut short by the output token budget.\n */\nexport function isTruncationReason(reason: string | undefined | null): boolean {\n return reason != null && TRUNCATION_FINISH_REASONS.has(reason);\n}\n"],"names":[],"mappings":";;AAAA;;;;;;;;;;;;;;;;;AAiBG;AAEH;;;;;;;AAOG;AACI,MAAM,yBAAyB,GAAwB,IAAI,GAAG,CAAC;IACpE,YAAY;IACZ,QAAQ;IACR,YAAY;AACb,CAAA;AAED;;;AAGG;AACG,SAAU,kBAAkB,CAAC,MAAiC,EAAA;IAClE,OAAO,MAAM,IAAI,IAAI,IAAI,yBAAyB,CAAC,GAAG,CAAC,MAAM,CAAC;AAChE;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"llm.cjs","sources":["../../../src/utils/llm.ts"],"sourcesContent":["// src/utils/llm.ts\nimport { Providers } from '@/common';\n\nexport function isOpenAILike(provider?: string | Providers): boolean {\n if (provider == null) {\n return false;\n }\n return (\n [\n Providers.OPENAI,\n Providers.AZURE,\n Providers.OPENROUTER,\n Providers.XAI,\n Providers.DEEPSEEK,\n ] as string[]\n ).includes(provider);\n}\n\nexport function isGoogleLike(provider?: string | Providers): boolean {\n if (provider == null) {\n return false;\n }\n return ([Providers.GOOGLE, Providers.VERTEXAI] as string[]).includes(\n provider\n );\n}\n\n"],"names":["Providers"],"mappings":";;;;;AAAA;AAGM,SAAU,YAAY,CAAC,QAA6B,EAAA;AACxD,IAAA,IAAI,QAAQ,IAAI,IAAI,EAAE;AACpB,QAAA,OAAO,KAAK;IACd;IACA,OACE;AACE,QAAAA,eAAS,CAAC,MAAM;AAChB,QAAAA,eAAS,CAAC,KAAK;AACf,QAAAA,eAAS,CAAC,UAAU;AACpB,QAAAA,eAAS,CAAC,GAAG;AACb,QAAAA,eAAS,CAAC,QAAQ;AAErB,KAAA,CAAC,QAAQ,CAAC,QAAQ,CAAC;AACtB;AAEM,SAAU,YAAY,CAAC,QAA6B,EAAA;AACxD,IAAA,IAAI,QAAQ,IAAI,IAAI,EAAE;AACpB,QAAA,OAAO,KAAK;IACd;AACA,IAAA,OAAQ,CAACA,eAAS,CAAC,MAAM,EAAEA,eAAS,CAAC,QAAQ,CAAc,CAAC,QAAQ,CAClE,QAAQ,CACT;AACH;;;;;"}
1
+ {"version":3,"file":"llm.cjs","sources":["../../../src/utils/llm.ts"],"sourcesContent":["// src/utils/llm.ts\nimport { Providers } from '@/common';\n\nexport function isOpenAILike(provider?: string | Providers): boolean {\n if (provider == null) {\n return false;\n }\n return (\n [\n Providers.OPENAI,\n Providers.AZURE,\n Providers.OPENROUTER,\n Providers.XAI,\n Providers.DEEPSEEK,\n ] as string[]\n ).includes(provider);\n}\n\nexport function isGoogleLike(provider?: string | Providers): boolean {\n if (provider == null) {\n return false;\n }\n return ([Providers.GOOGLE, Providers.VERTEXAI] as string[]).includes(\n provider\n );\n}\n"],"names":["Providers"],"mappings":";;;;;AAAA;AAGM,SAAU,YAAY,CAAC,QAA6B,EAAA;AACxD,IAAA,IAAI,QAAQ,IAAI,IAAI,EAAE;AACpB,QAAA,OAAO,KAAK;IACd;IACA,OACE;AACE,QAAAA,eAAS,CAAC,MAAM;AAChB,QAAAA,eAAS,CAAC,KAAK;AACf,QAAAA,eAAS,CAAC,UAAU;AACpB,QAAAA,eAAS,CAAC,GAAG;AACb,QAAAA,eAAS,CAAC,QAAQ;AAErB,KAAA,CAAC,QAAQ,CAAC,QAAQ,CAAC;AACtB;AAEM,SAAU,YAAY,CAAC,QAA6B,EAAA;AACxD,IAAA,IAAI,QAAQ,IAAI,IAAI,EAAE;AACpB,QAAA,OAAO,KAAK;IACd;AACA,IAAA,OAAQ,CAACA,eAAS,CAAC,MAAM,EAAEA,eAAS,CAAC,QAAQ,CAAc,CAAC,QAAQ,CAClE,QAAQ,CACT;AACH;;;;;"}
@@ -0,0 +1,34 @@
1
+ 'use strict';
2
+
3
+ require('fs');
4
+ require('util');
5
+
6
+ // src/utils/logging.ts
7
+ /**
8
+ * Multi-agent debug gate. Controls chatty graph/handoff/transfer/sequence
9
+ * trace logs emitted from `MultiAgentGraph` and `Graph`. Off by default so the
10
+ * package stays quiet when embedded in host apps. Flip to
11
+ * `DEBUG_MULTIAGENT=true` in the host env to re-enable for testing.
12
+ *
13
+ * Exported as functions so runtime toggling via env reload works; the check
14
+ * is cheap (string equality) and only runs when a log site fires.
15
+ */
16
+ function isMultiAgentDebugEnabled() {
17
+ return process.env.DEBUG_MULTIAGENT === 'true';
18
+ }
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ const mlog = (...args) => {
21
+ if (isMultiAgentDebugEnabled()) {
22
+ console.debug(...args);
23
+ }
24
+ };
25
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
+ const mwarn = (...args) => {
27
+ if (isMultiAgentDebugEnabled()) {
28
+ console.warn(...args);
29
+ }
30
+ };
31
+
32
+ exports.mlog = mlog;
33
+ exports.mwarn = mwarn;
34
+ //# sourceMappingURL=logging.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logging.cjs","sources":["../../../src/utils/logging.ts"],"sourcesContent":["// src/utils/logging.ts\nimport fs from 'fs';\nimport util from 'util';\n\n/**\n * Multi-agent debug gate. Controls chatty graph/handoff/transfer/sequence\n * trace logs emitted from `MultiAgentGraph` and `Graph`. Off by default so the\n * package stays quiet when embedded in host apps. Flip to\n * `DEBUG_MULTIAGENT=true` in the host env to re-enable for testing.\n *\n * Exported as functions so runtime toggling via env reload works; the check\n * is cheap (string equality) and only runs when a log site fires.\n */\nfunction isMultiAgentDebugEnabled(): boolean {\n return process.env.DEBUG_MULTIAGENT === 'true';\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const mlog = (...args: any[]): void => {\n if (isMultiAgentDebugEnabled()) {\n console.debug(...args);\n }\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const mwarn = (...args: any[]): void => {\n if (isMultiAgentDebugEnabled()) {\n console.warn(...args);\n }\n};\n\nexport function setupLogging(logFileName: string): void {\n const logFile = fs.createWriteStream(logFileName, { flags: 'a' });\n\n const originalConsoleLog = console.log;\n const originalConsoleError = console.error;\n const originalStdoutWrite = process.stdout.write;\n const originalStderrWrite = process.stderr.write;\n\n console.log = function (...args): void {\n logFile.write(util.format.apply(null, args) + ' ');\n originalConsoleLog.apply(console, args);\n };\n\n console.error = function (...args): void {\n logFile.write(util.format.apply(null, args) + ' ');\n originalConsoleError.apply(console, args);\n };\n\n process.stdout.write = function (\n buffer: Uint8Array | string,\n cb?: ((err?: Error) => void) | string,\n fd?: (err?: Error) => void\n ): boolean {\n if (typeof buffer === 'string') {\n logFile.write(buffer);\n } else {\n logFile.write(buffer.toString());\n }\n return originalStdoutWrite.call(\n process.stdout,\n buffer,\n cb as BufferEncoding | undefined,\n fd\n );\n };\n\n process.stderr.write = function (\n buffer: Uint8Array | string,\n cb?: ((err?: Error) => void) | string,\n fd?: (err?: Error) => void\n ): boolean {\n if (typeof buffer === 'string') {\n logFile.write(buffer);\n } else {\n logFile.write(buffer.toString());\n }\n return originalStderrWrite.call(\n process.stderr,\n buffer,\n cb as BufferEncoding | undefined,\n fd\n );\n };\n}\n"],"names":[],"mappings":";;;;;AAAA;AAIA;;;;;;;;AAQG;AACH,SAAS,wBAAwB,GAAA;AAC/B,IAAA,OAAO,OAAO,CAAC,GAAG,CAAC,gBAAgB,KAAK,MAAM;AAChD;AAEA;MACa,IAAI,GAAG,CAAC,GAAG,IAAW,KAAU;IAC3C,IAAI,wBAAwB,EAAE,EAAE;AAC9B,QAAA,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IACxB;AACF;AAEA;MACa,KAAK,GAAG,CAAC,GAAG,IAAW,KAAU;IAC5C,IAAI,wBAAwB,EAAE,EAAE;AAC9B,QAAA,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACvB;AACF;;;;;"}
@@ -0,0 +1,250 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Tool-call name normalization for malformed LLM tool_use outputs.
5
+ *
6
+ * LLMs — especially smaller / faster models — frequently emit tool_use
7
+ * blocks with names that don't exactly match the registered tool name:
8
+ *
9
+ * - Wrong delimiter: `outlook/operations` vs `outlook_operations`
10
+ * - Function-prefix style: `functions.outlook_operations`
11
+ * - Case drift: `Outlook_Operations`
12
+ * - Counter suffix: `outlook_operations_1`
13
+ * - Missing name, only id: `{name: "", id: "tool_outlook_operations_42"}`
14
+ *
15
+ * Before this normalization layer, any of the above caused the LangGraph
16
+ * ToolNode to throw "Tool X not found" and the whole turn to fail. With
17
+ * normalization, we resolve to the correct registered name case-insensitively
18
+ * and transparently fix the tool_call in place.
19
+ *
20
+ * Resolution order:
21
+ * 1. Exact match
22
+ * 2. Normalized delimiter + exact
23
+ * 3. Case-insensitive match
24
+ * 4. Structured candidates (strip prefixes, take suffix segments)
25
+ * 5. Infer from tool_call id (strip trailing digits, function/tool prefix)
26
+ *
27
+ * Any unresolvable tool_call is left as-is — the downstream ToolNode will
28
+ * fail with its usual error and the auto-recovery path kicks in.
29
+ */
30
+ function lowercaseOrEmpty(value) {
31
+ return typeof value === 'string' ? value.toLowerCase() : '';
32
+ }
33
+ /** Collapse common delimiters (space, dot, slash, dash) to `_` and fold case. */
34
+ function normalizeDelimiters(name) {
35
+ return name
36
+ .trim()
37
+ .replace(/[\s./-]+/g, '_')
38
+ .replace(/_{2,}/g, '_')
39
+ .replace(/^_+|_+$/g, '');
40
+ }
41
+ function resolveCaseInsensitive(rawName, allowed) {
42
+ if (allowed.size === 0)
43
+ return null;
44
+ const folded = lowercaseOrEmpty(rawName);
45
+ let match = null;
46
+ for (const name of allowed) {
47
+ if (lowercaseOrEmpty(name) !== folded)
48
+ continue;
49
+ // Ambiguous → fail closed
50
+ if (match && match !== name)
51
+ return null;
52
+ match = name;
53
+ }
54
+ return match;
55
+ }
56
+ function resolveExact(rawName, allowed) {
57
+ if (allowed.size === 0)
58
+ return null;
59
+ if (allowed.has(rawName))
60
+ return rawName;
61
+ const normalized = normalizeDelimiters(rawName);
62
+ if (allowed.has(normalized))
63
+ return normalized;
64
+ return (resolveCaseInsensitive(rawName, allowed) ??
65
+ resolveCaseInsensitive(normalized, allowed));
66
+ }
67
+ function buildStructuredCandidates(rawName) {
68
+ const trimmed = rawName.trim();
69
+ if (!trimmed)
70
+ return [];
71
+ const seen = new Set();
72
+ const out = [];
73
+ const add = (value) => {
74
+ const c = value.trim();
75
+ if (!c || seen.has(c))
76
+ return;
77
+ seen.add(c);
78
+ out.push(c);
79
+ };
80
+ add(trimmed);
81
+ add(normalizeDelimiters(trimmed));
82
+ // Also try dot-normalized, then split
83
+ const dotForm = trimmed.replace(/\//g, '.');
84
+ add(dotForm);
85
+ add(normalizeDelimiters(dotForm));
86
+ const segments = dotForm
87
+ .split('.')
88
+ .map((s) => s.trim())
89
+ .filter(Boolean);
90
+ if (segments.length > 1) {
91
+ for (let i = 1; i < segments.length; i++) {
92
+ const suffix = segments.slice(i).join('.');
93
+ add(suffix);
94
+ add(normalizeDelimiters(suffix));
95
+ }
96
+ }
97
+ return out;
98
+ }
99
+ function resolveStructured(rawName, allowed) {
100
+ if (allowed.size === 0)
101
+ return null;
102
+ const candidates = buildStructuredCandidates(rawName);
103
+ for (const c of candidates) {
104
+ if (allowed.has(c))
105
+ return c;
106
+ }
107
+ for (const c of candidates) {
108
+ const ci = resolveCaseInsensitive(c, allowed);
109
+ if (ci)
110
+ return ci;
111
+ }
112
+ return null;
113
+ }
114
+ function inferFromToolCallId(rawId, allowed) {
115
+ if (!rawId || allowed.size === 0)
116
+ return null;
117
+ const id = rawId.trim();
118
+ if (!id)
119
+ return null;
120
+ const tokens = new Set();
121
+ const addTokens = (value) => {
122
+ const t = value.trim();
123
+ if (!t)
124
+ return;
125
+ tokens.add(t);
126
+ tokens.add(t.replace(/[:._/-]\d+$/, ''));
127
+ tokens.add(t.replace(/\d+$/, ''));
128
+ const dotForm = t.replace(/\//g, '.');
129
+ tokens.add(dotForm);
130
+ tokens.add(dotForm.replace(/[:._-]\d+$/, ''));
131
+ tokens.add(dotForm.replace(/\d+$/, ''));
132
+ for (const prefix of [/^functions?[._-]?/i, /^tools?[._-]?/i]) {
133
+ const stripped = dotForm.replace(prefix, '');
134
+ if (stripped !== dotForm) {
135
+ tokens.add(stripped);
136
+ tokens.add(stripped.replace(/[:._-]\d+$/, ''));
137
+ tokens.add(stripped.replace(/\d+$/, ''));
138
+ }
139
+ }
140
+ };
141
+ const preColon = id.split(':')[0] ?? id;
142
+ addTokens(id);
143
+ addTokens(preColon);
144
+ let singleMatch = null;
145
+ for (const token of tokens) {
146
+ const matched = resolveStructured(token, allowed);
147
+ if (!matched)
148
+ continue;
149
+ // Ambiguous → fail closed
150
+ if (singleMatch && singleMatch !== matched)
151
+ return null;
152
+ singleMatch = matched;
153
+ }
154
+ if (singleMatch)
155
+ return singleMatch;
156
+ // Substring fallback: ids frequently look like `call_<toolname>_<n>` or
157
+ // `toolu_01...<toolname>...`. Scan for any allowed name embedded in the
158
+ // delimiter-normalized id (case-insensitive) and fail closed if more than
159
+ // one distinct allowed name matches.
160
+ const haystack = lowercaseOrEmpty(normalizeDelimiters(id));
161
+ let substrMatch = null;
162
+ for (const allowedName of allowed) {
163
+ const needle = lowercaseOrEmpty(allowedName);
164
+ if (!needle)
165
+ continue;
166
+ if (!haystack.includes(needle))
167
+ continue;
168
+ if (substrMatch && substrMatch !== allowedName)
169
+ return null;
170
+ substrMatch = allowedName;
171
+ }
172
+ return substrMatch;
173
+ }
174
+ /**
175
+ * Produce the best allowed tool name for a raw LLM tool_use name.
176
+ * Returns the original name unchanged if no resolution is possible —
177
+ * the downstream executor will then fail with its normal error path.
178
+ */
179
+ function normalizeToolCallName(rawName, allowedToolNames, rawToolCallId) {
180
+ const trimmed = rawName.trim() ?? '';
181
+ if (!trimmed) {
182
+ return (inferFromToolCallId(rawToolCallId, allowedToolNames) ?? rawName ?? '');
183
+ }
184
+ if (allowedToolNames.size === 0)
185
+ return trimmed;
186
+ const exact = resolveExact(trimmed, allowedToolNames);
187
+ if (exact)
188
+ return exact;
189
+ const structured = resolveStructured(trimmed, allowedToolNames);
190
+ if (structured)
191
+ return structured;
192
+ // Last-resort: try the raw id (if supplied). Substring matching lives
193
+ // inside inferFromToolCallId — only safe to invoke when the caller
194
+ // passed a real provider id, not when the raw name was used above.
195
+ const inferred = inferFromToolCallId(rawToolCallId, allowedToolNames);
196
+ if (inferred)
197
+ return inferred;
198
+ return trimmed;
199
+ }
200
+ /**
201
+ * In-place normalization of all tool_calls on an AIMessage-like object.
202
+ *
203
+ * Applies `normalizeToolCallName` to each tool_call's `name` field using the
204
+ * provided allowed-tool set (derived from the agent's toolMap keys). Also
205
+ * rewrites the `name` field inside any content blocks of type `tool_use` so
206
+ * that downstream providers see the corrected name.
207
+ *
208
+ * Returns `true` if any name was rewritten.
209
+ */
210
+ function normalizeMessageToolCalls(message, allowedToolNames) {
211
+ if (!message || typeof message !== 'object')
212
+ return false;
213
+ let changed = false;
214
+ // LangChain AIMessage.tool_calls (normalized form)
215
+ const msg = message;
216
+ if (Array.isArray(msg.tool_calls)) {
217
+ for (const tc of msg.tool_calls) {
218
+ if (!tc || typeof tc !== 'object')
219
+ continue;
220
+ const rawName = typeof tc.name === 'string' ? tc.name : '';
221
+ const rawId = typeof tc.id === 'string' ? tc.id : undefined;
222
+ const normalized = normalizeToolCallName(rawName, allowedToolNames, rawId);
223
+ if (normalized && normalized !== rawName) {
224
+ tc.name = normalized;
225
+ changed = true;
226
+ }
227
+ }
228
+ }
229
+ // Anthropic-style content blocks with type: 'tool_use'
230
+ if (Array.isArray(msg.content)) {
231
+ for (const block of msg.content) {
232
+ if (!block || typeof block !== 'object')
233
+ continue;
234
+ if (block.type !== 'tool_use')
235
+ continue;
236
+ const rawName = typeof block.name === 'string' ? block.name : '';
237
+ const rawId = typeof block.id === 'string' ? block.id : undefined;
238
+ const normalized = normalizeToolCallName(rawName, allowedToolNames, rawId);
239
+ if (normalized && normalized !== rawName) {
240
+ block.name = normalized;
241
+ changed = true;
242
+ }
243
+ }
244
+ }
245
+ return changed;
246
+ }
247
+
248
+ exports.normalizeMessageToolCalls = normalizeMessageToolCalls;
249
+ exports.normalizeToolCallName = normalizeToolCallName;
250
+ //# sourceMappingURL=toolCallNormalization.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolCallNormalization.cjs","sources":["../../../src/utils/toolCallNormalization.ts"],"sourcesContent":["/**\n * Tool-call name normalization for malformed LLM tool_use outputs.\n *\n * LLMs — especially smaller / faster models — frequently emit tool_use\n * blocks with names that don't exactly match the registered tool name:\n *\n * - Wrong delimiter: `outlook/operations` vs `outlook_operations`\n * - Function-prefix style: `functions.outlook_operations`\n * - Case drift: `Outlook_Operations`\n * - Counter suffix: `outlook_operations_1`\n * - Missing name, only id: `{name: \"\", id: \"tool_outlook_operations_42\"}`\n *\n * Before this normalization layer, any of the above caused the LangGraph\n * ToolNode to throw \"Tool X not found\" and the whole turn to fail. With\n * normalization, we resolve to the correct registered name case-insensitively\n * and transparently fix the tool_call in place.\n *\n * Resolution order:\n * 1. Exact match\n * 2. Normalized delimiter + exact\n * 3. Case-insensitive match\n * 4. Structured candidates (strip prefixes, take suffix segments)\n * 5. Infer from tool_call id (strip trailing digits, function/tool prefix)\n *\n * Any unresolvable tool_call is left as-is — the downstream ToolNode will\n * fail with its usual error and the auto-recovery path kicks in.\n */\n\nfunction lowercaseOrEmpty(value: string): string {\n return typeof value === 'string' ? value.toLowerCase() : '';\n}\n\n/** Collapse common delimiters (space, dot, slash, dash) to `_` and fold case. */\nfunction normalizeDelimiters(name: string): string {\n return name\n .trim()\n .replace(/[\\s./-]+/g, '_')\n .replace(/_{2,}/g, '_')\n .replace(/^_+|_+$/g, '');\n}\n\nfunction resolveCaseInsensitive(\n rawName: string,\n allowed: Set<string>\n): string | null {\n if (allowed.size === 0) return null;\n const folded = lowercaseOrEmpty(rawName);\n let match: string | null = null;\n for (const name of allowed) {\n if (lowercaseOrEmpty(name) !== folded) continue;\n // Ambiguous → fail closed\n if (match && match !== name) return null;\n match = name;\n }\n return match;\n}\n\nfunction resolveExact(rawName: string, allowed: Set<string>): string | null {\n if (allowed.size === 0) return null;\n if (allowed.has(rawName)) return rawName;\n const normalized = normalizeDelimiters(rawName);\n if (allowed.has(normalized)) return normalized;\n return (\n resolveCaseInsensitive(rawName, allowed) ??\n resolveCaseInsensitive(normalized, allowed)\n );\n}\n\nfunction buildStructuredCandidates(rawName: string): string[] {\n const trimmed = rawName.trim();\n if (!trimmed) return [];\n const seen = new Set<string>();\n const out: string[] = [];\n const add = (value: string): void => {\n const c = value.trim();\n if (!c || seen.has(c)) return;\n seen.add(c);\n out.push(c);\n };\n add(trimmed);\n add(normalizeDelimiters(trimmed));\n // Also try dot-normalized, then split\n const dotForm = trimmed.replace(/\\//g, '.');\n add(dotForm);\n add(normalizeDelimiters(dotForm));\n const segments = dotForm\n .split('.')\n .map((s) => s.trim())\n .filter(Boolean);\n if (segments.length > 1) {\n for (let i = 1; i < segments.length; i++) {\n const suffix = segments.slice(i).join('.');\n add(suffix);\n add(normalizeDelimiters(suffix));\n }\n }\n return out;\n}\n\nfunction resolveStructured(\n rawName: string,\n allowed: Set<string>\n): string | null {\n if (allowed.size === 0) return null;\n const candidates = buildStructuredCandidates(rawName);\n for (const c of candidates) {\n if (allowed.has(c)) return c;\n }\n for (const c of candidates) {\n const ci = resolveCaseInsensitive(c, allowed);\n if (ci) return ci;\n }\n return null;\n}\n\nfunction inferFromToolCallId(\n rawId: string | undefined,\n allowed: Set<string>\n): string | null {\n if (!rawId || allowed.size === 0) return null;\n const id = rawId.trim();\n if (!id) return null;\n\n const tokens = new Set<string>();\n const addTokens = (value: string): void => {\n const t = value.trim();\n if (!t) return;\n tokens.add(t);\n tokens.add(t.replace(/[:._/-]\\d+$/, ''));\n tokens.add(t.replace(/\\d+$/, ''));\n const dotForm = t.replace(/\\//g, '.');\n tokens.add(dotForm);\n tokens.add(dotForm.replace(/[:._-]\\d+$/, ''));\n tokens.add(dotForm.replace(/\\d+$/, ''));\n for (const prefix of [/^functions?[._-]?/i, /^tools?[._-]?/i]) {\n const stripped = dotForm.replace(prefix, '');\n if (stripped !== dotForm) {\n tokens.add(stripped);\n tokens.add(stripped.replace(/[:._-]\\d+$/, ''));\n tokens.add(stripped.replace(/\\d+$/, ''));\n }\n }\n };\n\n const preColon = id.split(':')[0] ?? id;\n addTokens(id);\n addTokens(preColon);\n\n let singleMatch: string | null = null;\n for (const token of tokens) {\n const matched = resolveStructured(token, allowed);\n if (!matched) continue;\n // Ambiguous → fail closed\n if (singleMatch && singleMatch !== matched) return null;\n singleMatch = matched;\n }\n if (singleMatch) return singleMatch;\n\n // Substring fallback: ids frequently look like `call_<toolname>_<n>` or\n // `toolu_01...<toolname>...`. Scan for any allowed name embedded in the\n // delimiter-normalized id (case-insensitive) and fail closed if more than\n // one distinct allowed name matches.\n const haystack = lowercaseOrEmpty(normalizeDelimiters(id));\n let substrMatch: string | null = null;\n for (const allowedName of allowed) {\n const needle = lowercaseOrEmpty(allowedName);\n if (!needle) continue;\n if (!haystack.includes(needle)) continue;\n if (substrMatch && substrMatch !== allowedName) return null;\n substrMatch = allowedName;\n }\n return substrMatch;\n}\n\n/**\n * Produce the best allowed tool name for a raw LLM tool_use name.\n * Returns the original name unchanged if no resolution is possible —\n * the downstream executor will then fail with its normal error path.\n */\nexport function normalizeToolCallName(\n rawName: string,\n allowedToolNames: Set<string>,\n rawToolCallId?: string\n): string {\n const trimmed = rawName.trim() ?? '';\n if (!trimmed) {\n return (\n inferFromToolCallId(rawToolCallId, allowedToolNames) ?? rawName ?? ''\n );\n }\n if (allowedToolNames.size === 0) return trimmed;\n\n const exact = resolveExact(trimmed, allowedToolNames);\n if (exact) return exact;\n\n const structured = resolveStructured(trimmed, allowedToolNames);\n if (structured) return structured;\n\n // Last-resort: try the raw id (if supplied). Substring matching lives\n // inside inferFromToolCallId — only safe to invoke when the caller\n // passed a real provider id, not when the raw name was used above.\n const inferred = inferFromToolCallId(rawToolCallId, allowedToolNames);\n if (inferred) return inferred;\n\n return trimmed;\n}\n\n/**\n * In-place normalization of all tool_calls on an AIMessage-like object.\n *\n * Applies `normalizeToolCallName` to each tool_call's `name` field using the\n * provided allowed-tool set (derived from the agent's toolMap keys). Also\n * rewrites the `name` field inside any content blocks of type `tool_use` so\n * that downstream providers see the corrected name.\n *\n * Returns `true` if any name was rewritten.\n */\nexport function normalizeMessageToolCalls(\n message: unknown,\n allowedToolNames: Set<string>\n): boolean {\n if (!message || typeof message !== 'object') return false;\n let changed = false;\n\n // LangChain AIMessage.tool_calls (normalized form)\n const msg = message as {\n tool_calls?: Array<{ name?: string; id?: string }>;\n content?: unknown;\n };\n if (Array.isArray(msg.tool_calls)) {\n for (const tc of msg.tool_calls) {\n if (!tc || typeof tc !== 'object') continue;\n const rawName = typeof tc.name === 'string' ? tc.name : '';\n const rawId = typeof tc.id === 'string' ? tc.id : undefined;\n const normalized = normalizeToolCallName(\n rawName,\n allowedToolNames,\n rawId\n );\n if (normalized && normalized !== rawName) {\n tc.name = normalized;\n changed = true;\n }\n }\n }\n\n // Anthropic-style content blocks with type: 'tool_use'\n if (Array.isArray(msg.content)) {\n for (const block of msg.content as Array<{\n type?: unknown;\n name?: unknown;\n id?: unknown;\n }>) {\n if (!block || typeof block !== 'object') continue;\n if (block.type !== 'tool_use') continue;\n const rawName = typeof block.name === 'string' ? block.name : '';\n const rawId = typeof block.id === 'string' ? block.id : undefined;\n const normalized = normalizeToolCallName(\n rawName,\n allowedToolNames,\n rawId\n );\n if (normalized && normalized !== rawName) {\n block.name = normalized;\n changed = true;\n }\n }\n }\n\n return changed;\n}\n"],"names":[],"mappings":"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"}
@@ -452,12 +452,29 @@ class AgentContext {
452
452
  const { sourceAgentName, parallelSiblings } = this.handoffContext;
453
453
  const isParallel = parallelSiblings.length > 0;
454
454
  const lines = [];
455
- lines.push('## Multi-Agent Workflow');
456
- lines.push(`You are "${displayName}", transferred from "${sourceAgentName}".`);
455
+ lines.push('## Subagent Context');
456
+ lines.push('');
457
+ lines.push(`You are "${displayName}", a subagent spawned by "${sourceAgentName}" for a specific task.`);
457
458
  if (isParallel) {
458
459
  lines.push(`Running in parallel with: ${parallelSiblings.join(', ')}.`);
459
460
  }
460
- lines.push('Execute only tasks relevant to your role. Routing is already handled if requested, unless you can route further.');
461
+ lines.push('');
462
+ lines.push('### Your Role');
463
+ lines.push('- Complete your assigned task. That is your entire purpose.');
464
+ lines.push(`- You are NOT "${sourceAgentName}". Do not try to be.`);
465
+ lines.push('');
466
+ lines.push('### Rules');
467
+ lines.push('1. **Stay focused** — Do your assigned task, nothing else.');
468
+ lines.push('2. **Complete the task** — Your final message will be automatically reported back.');
469
+ lines.push('3. **Be autonomous** — Execute directly without asking for user confirmation. Use your tools and best judgment.');
470
+ lines.push('4. **No placeholders** — Never generate fake or placeholder data. If you cannot retrieve real data, say so.');
471
+ lines.push('5. **No side effects** — Do not send messages, emails, or notifications unless explicitly tasked to do so.');
472
+ lines.push('');
473
+ lines.push('### Output');
474
+ lines.push('When complete, your final response should include:');
475
+ lines.push('- What you accomplished or found');
476
+ lines.push('- Any relevant details the orchestrator should know');
477
+ lines.push('- Keep it concise but informative');
461
478
  return lines.join('\n');
462
479
  }
463
480
  /**