@librechat/agents 3.3.4 → 3.3.6

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 (263) hide show
  1. package/dist/cjs/common/constants.cjs +21 -0
  2. package/dist/cjs/common/constants.cjs.map +1 -1
  3. package/dist/cjs/events.cjs +10 -1
  4. package/dist/cjs/events.cjs.map +1 -1
  5. package/dist/cjs/graphs/Graph.cjs +456 -7
  6. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  7. package/dist/cjs/hooks/HookRegistry.cjs +25 -0
  8. package/dist/cjs/hooks/HookRegistry.cjs.map +1 -1
  9. package/dist/cjs/hooks/createWorkspacePolicyHook.cjs +1 -1
  10. package/dist/cjs/hooks/index.cjs +12 -0
  11. package/dist/cjs/hooks/index.cjs.map +1 -1
  12. package/dist/cjs/hooks/types.cjs +1 -0
  13. package/dist/cjs/hooks/types.cjs.map +1 -1
  14. package/dist/cjs/langfuse.cjs +8 -0
  15. package/dist/cjs/langfuse.cjs.map +1 -1
  16. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +1 -3
  17. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
  18. package/dist/cjs/llm/invoke.cjs +268 -3
  19. package/dist/cjs/llm/invoke.cjs.map +1 -1
  20. package/dist/cjs/llm/openai/index.cjs +51 -6
  21. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  22. package/dist/cjs/llm/preempt.cjs +132 -0
  23. package/dist/cjs/llm/preempt.cjs.map +1 -0
  24. package/dist/cjs/main.cjs +31 -3
  25. package/dist/cjs/messages/alternation.cjs +92 -0
  26. package/dist/cjs/messages/alternation.cjs.map +1 -0
  27. package/dist/cjs/messages/format.cjs +72 -0
  28. package/dist/cjs/messages/format.cjs.map +1 -1
  29. package/dist/cjs/messages/handoffCue.cjs +63 -0
  30. package/dist/cjs/messages/handoffCue.cjs.map +1 -0
  31. package/dist/cjs/messages/index.cjs +3 -0
  32. package/dist/cjs/messages/injected.cjs +60 -0
  33. package/dist/cjs/messages/injected.cjs.map +1 -0
  34. package/dist/cjs/run.cjs +80 -7
  35. package/dist/cjs/run.cjs.map +1 -1
  36. package/dist/cjs/session/JsonlSessionStore.cjs +1 -1
  37. package/dist/cjs/session/handlers.cjs +18 -9
  38. package/dist/cjs/session/handlers.cjs.map +1 -1
  39. package/dist/cjs/stream.cjs +34 -8
  40. package/dist/cjs/stream.cjs.map +1 -1
  41. package/dist/cjs/tools/BashExecutor.cjs +3 -1
  42. package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
  43. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +2 -0
  44. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
  45. package/dist/cjs/tools/CodeExecutor.cjs +19 -16
  46. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  47. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +2 -0
  48. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  49. package/dist/cjs/tools/ReadFile.cjs +8 -4
  50. package/dist/cjs/tools/ReadFile.cjs.map +1 -1
  51. package/dist/cjs/tools/SkillTool.cjs +2 -0
  52. package/dist/cjs/tools/SkillTool.cjs.map +1 -1
  53. package/dist/cjs/tools/SubagentTool.cjs +3 -0
  54. package/dist/cjs/tools/SubagentTool.cjs.map +1 -1
  55. package/dist/cjs/tools/ToolNode.cjs +91 -50
  56. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  57. package/dist/cjs/tools/ToolSearch.cjs +5 -1
  58. package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
  59. package/dist/cjs/tools/intentArg.cjs +230 -0
  60. package/dist/cjs/tools/intentArg.cjs.map +1 -0
  61. package/dist/cjs/tools/local/CompileCheckTool.cjs +3 -2
  62. package/dist/cjs/tools/local/CompileCheckTool.cjs.map +1 -1
  63. package/dist/cjs/tools/local/LocalCodingTools.cjs +13 -12
  64. package/dist/cjs/tools/local/LocalCodingTools.cjs.map +1 -1
  65. package/dist/cjs/tools/local/LocalExecutionEngine.cjs +2 -2
  66. package/dist/cjs/tools/ptcTimeout.cjs.map +1 -1
  67. package/dist/cjs/tools/search/keenable-scraper.cjs +90 -0
  68. package/dist/cjs/tools/search/keenable-scraper.cjs.map +1 -0
  69. package/dist/cjs/tools/search/schema.cjs +2 -0
  70. package/dist/cjs/tools/search/schema.cjs.map +1 -1
  71. package/dist/cjs/tools/search/tool.cjs +40 -2
  72. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  73. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +1 -0
  74. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  75. package/dist/cjs/tools/toolOutputReferences.cjs +41 -7
  76. package/dist/cjs/tools/toolOutputReferences.cjs.map +1 -1
  77. package/dist/esm/common/constants.mjs +19 -1
  78. package/dist/esm/common/constants.mjs.map +1 -1
  79. package/dist/esm/events.mjs +10 -1
  80. package/dist/esm/events.mjs.map +1 -1
  81. package/dist/esm/graphs/Graph.mjs +455 -6
  82. package/dist/esm/graphs/Graph.mjs.map +1 -1
  83. package/dist/esm/hooks/HookRegistry.mjs +25 -0
  84. package/dist/esm/hooks/HookRegistry.mjs.map +1 -1
  85. package/dist/esm/hooks/createWorkspacePolicyHook.mjs +1 -1
  86. package/dist/esm/hooks/index.mjs +12 -1
  87. package/dist/esm/hooks/index.mjs.map +1 -1
  88. package/dist/esm/hooks/types.mjs +1 -0
  89. package/dist/esm/hooks/types.mjs.map +1 -1
  90. package/dist/esm/langfuse.mjs +9 -1
  91. package/dist/esm/langfuse.mjs.map +1 -1
  92. package/dist/esm/llm/bedrock/utils/message_inputs.mjs +1 -3
  93. package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
  94. package/dist/esm/llm/invoke.mjs +270 -6
  95. package/dist/esm/llm/invoke.mjs.map +1 -1
  96. package/dist/esm/llm/openai/index.mjs +51 -6
  97. package/dist/esm/llm/openai/index.mjs.map +1 -1
  98. package/dist/esm/llm/preempt.mjs +131 -0
  99. package/dist/esm/llm/preempt.mjs.map +1 -0
  100. package/dist/esm/main.mjs +11 -7
  101. package/dist/esm/messages/alternation.mjs +91 -0
  102. package/dist/esm/messages/alternation.mjs.map +1 -0
  103. package/dist/esm/messages/format.mjs +72 -0
  104. package/dist/esm/messages/format.mjs.map +1 -1
  105. package/dist/esm/messages/handoffCue.mjs +61 -0
  106. package/dist/esm/messages/handoffCue.mjs.map +1 -0
  107. package/dist/esm/messages/index.mjs +3 -0
  108. package/dist/esm/messages/injected.mjs +60 -0
  109. package/dist/esm/messages/injected.mjs.map +1 -0
  110. package/dist/esm/run.mjs +80 -7
  111. package/dist/esm/run.mjs.map +1 -1
  112. package/dist/esm/session/JsonlSessionStore.mjs +1 -1
  113. package/dist/esm/session/handlers.mjs +19 -10
  114. package/dist/esm/session/handlers.mjs.map +1 -1
  115. package/dist/esm/stream.mjs +33 -9
  116. package/dist/esm/stream.mjs.map +1 -1
  117. package/dist/esm/tools/BashExecutor.mjs +3 -1
  118. package/dist/esm/tools/BashExecutor.mjs.map +1 -1
  119. package/dist/esm/tools/BashProgrammaticToolCalling.mjs +2 -0
  120. package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
  121. package/dist/esm/tools/CodeExecutor.mjs +19 -16
  122. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  123. package/dist/esm/tools/ProgrammaticToolCalling.mjs +2 -0
  124. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  125. package/dist/esm/tools/ReadFile.mjs +8 -4
  126. package/dist/esm/tools/ReadFile.mjs.map +1 -1
  127. package/dist/esm/tools/SkillTool.mjs +2 -0
  128. package/dist/esm/tools/SkillTool.mjs.map +1 -1
  129. package/dist/esm/tools/SubagentTool.mjs +3 -0
  130. package/dist/esm/tools/SubagentTool.mjs.map +1 -1
  131. package/dist/esm/tools/ToolNode.mjs +91 -51
  132. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  133. package/dist/esm/tools/ToolSearch.mjs +5 -1
  134. package/dist/esm/tools/ToolSearch.mjs.map +1 -1
  135. package/dist/esm/tools/intentArg.mjs +220 -0
  136. package/dist/esm/tools/intentArg.mjs.map +1 -0
  137. package/dist/esm/tools/local/CompileCheckTool.mjs +3 -2
  138. package/dist/esm/tools/local/CompileCheckTool.mjs.map +1 -1
  139. package/dist/esm/tools/local/LocalCodingTools.mjs +13 -12
  140. package/dist/esm/tools/local/LocalCodingTools.mjs.map +1 -1
  141. package/dist/esm/tools/local/LocalExecutionEngine.mjs +2 -2
  142. package/dist/esm/tools/ptcTimeout.mjs.map +1 -1
  143. package/dist/esm/tools/search/keenable-scraper.mjs +88 -0
  144. package/dist/esm/tools/search/keenable-scraper.mjs.map +1 -0
  145. package/dist/esm/tools/search/schema.mjs +2 -0
  146. package/dist/esm/tools/search/schema.mjs.map +1 -1
  147. package/dist/esm/tools/search/tool.mjs +40 -3
  148. package/dist/esm/tools/search/tool.mjs.map +1 -1
  149. package/dist/esm/tools/subagent/SubagentExecutor.mjs +1 -0
  150. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  151. package/dist/esm/tools/toolOutputReferences.mjs +41 -7
  152. package/dist/esm/tools/toolOutputReferences.mjs.map +1 -1
  153. package/dist/types/common/constants.d.ts +18 -0
  154. package/dist/types/graphs/Graph.d.ts +138 -1
  155. package/dist/types/hooks/HookRegistry.d.ts +15 -0
  156. package/dist/types/hooks/index.d.ts +12 -1
  157. package/dist/types/hooks/types.d.ts +45 -6
  158. package/dist/types/index.d.ts +2 -1
  159. package/dist/types/llm/invoke.d.ts +7 -0
  160. package/dist/types/llm/openai/index.d.ts +0 -11
  161. package/dist/types/llm/preempt.d.ts +40 -0
  162. package/dist/types/messages/alternation.d.ts +23 -0
  163. package/dist/types/messages/handoffCue.d.ts +40 -0
  164. package/dist/types/messages/index.d.ts +3 -0
  165. package/dist/types/messages/injected.d.ts +3 -0
  166. package/dist/types/run.d.ts +7 -0
  167. package/dist/types/stream.d.ts +14 -0
  168. package/dist/types/tools/BashExecutor.d.ts +18 -0
  169. package/dist/types/tools/CodeExecutor.d.ts +18 -0
  170. package/dist/types/tools/ReadFile.d.ts +18 -0
  171. package/dist/types/tools/SkillTool.d.ts +18 -0
  172. package/dist/types/tools/SubagentTool.d.ts +9 -0
  173. package/dist/types/tools/ToolNode.d.ts +37 -12
  174. package/dist/types/tools/ToolSearch.d.ts +18 -0
  175. package/dist/types/tools/intentArg.d.ts +119 -0
  176. package/dist/types/tools/ptcTimeout.d.ts +2 -0
  177. package/dist/types/tools/search/keenable-scraper.d.ts +15 -0
  178. package/dist/types/tools/search/schema.d.ts +18 -0
  179. package/dist/types/tools/search/tool.d.ts +18 -0
  180. package/dist/types/tools/search/types.d.ts +31 -2
  181. package/dist/types/tools/toolOutputReferences.d.ts +12 -2
  182. package/dist/types/types/graph.d.ts +8 -1
  183. package/dist/types/types/run.d.ts +65 -0
  184. package/dist/types/types/stream.d.ts +12 -25
  185. package/dist/types/types/tools.d.ts +27 -2
  186. package/package.json +1 -1
  187. package/src/{splitStream.test.ts → aggregator.test.ts} +59 -666
  188. package/src/common/constants.ts +21 -0
  189. package/src/events.ts +15 -1
  190. package/src/graphs/Graph.ts +568 -3
  191. package/src/graphs/__tests__/Graph.contextOverflow.test.ts +18 -7
  192. package/src/graphs/__tests__/Graph.preemptSignal.test.ts +126 -0
  193. package/src/hooks/HookRegistry.ts +40 -0
  194. package/src/hooks/__tests__/preemptBoundary.test.ts +152 -0
  195. package/src/hooks/index.ts +16 -2
  196. package/src/hooks/types.ts +47 -3
  197. package/src/index.ts +2 -1
  198. package/src/langfuse.ts +26 -1
  199. package/src/llm/bedrock/utils/message_inputs.test.ts +82 -0
  200. package/src/llm/bedrock/utils/message_inputs.ts +14 -11
  201. package/src/llm/bedrock/utils/toolResultCachePoint.test.ts +15 -0
  202. package/src/llm/invoke.alternation.test.ts +87 -0
  203. package/src/llm/invoke.handoffCue.test.ts +112 -0
  204. package/src/llm/invoke.ts +417 -8
  205. package/src/llm/openai/index.ts +74 -10
  206. package/src/llm/openai/llm.spec.ts +105 -0
  207. package/src/llm/preempt.test.ts +323 -0
  208. package/src/llm/preempt.ts +178 -0
  209. package/src/messages/alternation.test.ts +212 -0
  210. package/src/messages/alternation.ts +112 -0
  211. package/src/messages/format.ts +91 -0
  212. package/src/messages/formatAgentMessages.steer.test.ts +267 -0
  213. package/src/messages/handoffCue.test.ts +96 -0
  214. package/src/messages/handoffCue.ts +78 -0
  215. package/src/messages/index.ts +3 -0
  216. package/src/messages/injected.test.ts +90 -0
  217. package/src/messages/injected.ts +74 -0
  218. package/src/run.ts +91 -6
  219. package/src/scripts/preempt-probe.ts +330 -0
  220. package/src/scripts/preempt-scenarios.ts +388 -0
  221. package/src/session/handlers.ts +32 -12
  222. package/src/specs/handoffCue.test.ts +165 -0
  223. package/src/specs/langfuse-callbacks.test.ts +352 -2
  224. package/src/specs/preemptSeal.test.ts +309 -0
  225. package/src/stream.dispatch.test.ts +63 -0
  226. package/src/stream.ts +48 -9
  227. package/src/tools/BashExecutor.ts +6 -0
  228. package/src/tools/BashProgrammaticToolCalling.ts +2 -0
  229. package/src/tools/CodeExecutor.ts +6 -0
  230. package/src/tools/ProgrammaticToolCalling.ts +2 -0
  231. package/src/tools/ReadFile.ts +2 -0
  232. package/src/tools/SkillTool.ts +2 -0
  233. package/src/tools/SubagentTool.ts +3 -0
  234. package/src/tools/ToolNode.ts +193 -74
  235. package/src/tools/ToolSearch.ts +3 -0
  236. package/src/tools/__tests__/ToolNode.onResultCompletion.test.ts +174 -0
  237. package/src/tools/__tests__/intentArg.test.ts +281 -0
  238. package/src/tools/__tests__/intentCoverage.test.ts +139 -0
  239. package/src/tools/__tests__/toolOutputReferences.test.ts +83 -0
  240. package/src/tools/intentArg.ts +323 -0
  241. package/src/tools/local/CompileCheckTool.ts +4 -3
  242. package/src/tools/local/LocalCodingTools.ts +14 -13
  243. package/src/tools/ptcTimeout.ts +2 -0
  244. package/src/tools/search/keenable-scraper.test.ts +153 -0
  245. package/src/tools/search/keenable-scraper.ts +137 -0
  246. package/src/tools/search/outcome.test.ts +90 -0
  247. package/src/tools/search/schema.ts +3 -0
  248. package/src/tools/search/tool.ts +60 -3
  249. package/src/tools/search/types.ts +50 -3
  250. package/src/tools/subagent/SubagentExecutor.ts +2 -1
  251. package/src/tools/toolOutputReferences.ts +77 -9
  252. package/src/types/graph.ts +12 -1
  253. package/src/types/run.ts +67 -0
  254. package/src/types/stream.ts +12 -41
  255. package/src/types/tools.ts +24 -2
  256. package/dist/cjs/splitStream.cjs +0 -151
  257. package/dist/cjs/splitStream.cjs.map +0 -1
  258. package/dist/esm/splitStream.mjs +0 -150
  259. package/dist/esm/splitStream.mjs.map +0 -1
  260. package/dist/types/mockStream.d.ts +0 -32
  261. package/dist/types/splitStream.d.ts +0 -37
  262. package/src/mockStream.ts +0 -99
  263. package/src/splitStream.ts +0 -234
@@ -3,9 +3,10 @@ import { CODE_EXECUTION_TOOLS } from "../common/enum.mjs";
3
3
  import "../common/index.mjs";
4
4
  import { calculateMaxToolResultChars, truncateToolResultContent } from "../utils/truncation.mjs";
5
5
  import { cloneToolMessageWithContent, compactToolContent, hasComputerCallOutputMarker, isComputerCallOutputContent, isComputerCallOutputMessage, serializeStructuredValueBounded, serializeToolContentBounded } from "../utils/toolContent.mjs";
6
- import { toLangChainContent } from "../messages/langchain.mjs";
7
6
  import { safeDispatchCustomEvent } from "../utils/events.mjs";
7
+ import { convertInjectedMessages } from "../messages/injected.mjs";
8
8
  import { buildToolExecutionRequestPlan, recordArgsEqual, resolveRuntimeSessionHint } from "./eagerEventExecution.mjs";
9
+ import { INTENT_ARG, isIntentLabelProperty, outcomeFieldsFromResult, readOutcomeFields, resolveToolOutcome } from "./intentArg.mjs";
9
10
  import { ToolOutputReferenceRegistry, buildReferenceKey } from "./toolOutputReferences.mjs";
10
11
  import { RunnableCallable } from "../utils/run.mjs";
11
12
  import "../utils/index.mjs";
@@ -19,6 +20,12 @@ import { Command, END, Send, interrupt, isCommand, isGraphInterrupt } from "@lan
19
20
  import { nanoid } from "nanoid";
20
21
  import { AsyncLocalStorageProviderSingleton } from "@langchain/core/singletons";
21
22
  //#region src/tools/ToolNode.ts
23
+ function createToolErrorOwnership() {
24
+ return {
25
+ handlerOwned: /* @__PURE__ */ new Set(),
26
+ undispatched: /* @__PURE__ */ new Set()
27
+ };
28
+ }
22
29
  /**
23
30
  * Produces the provider preview and, when requested, the exact registry prefix
24
31
  * in one bounded traversal. String outputs already exist in memory when a tool
@@ -251,12 +258,12 @@ var ToolNode = class extends RunnableCallable {
251
258
  toolCallStepIds;
252
259
  errorHandler;
253
260
  /**
254
- * Tool call ids whose `errorHandler` did NOT dispatch the error completion
255
- * event (it returned `false` or threw). The output loop must dispatch the
256
- * completion for these itself skipping them there would strand the
257
- * client's tool-call part without a terminal event.
261
+ * Fallback error-completion ownership for calls that reach `runTool`
262
+ * outside a batch context (direct `runTool` use in tests / embedders).
263
+ * Batch-scoped ownership is threaded via `RunToolBatchContext` instead
264
+ * see {@link ToolErrorOwnership} for why per-invocation scoping matters.
258
265
  */
259
- undispatchedToolErrors = /* @__PURE__ */ new Set();
266
+ looseErrorOwnership = createToolErrorOwnership();
260
267
  toolUsageCount;
261
268
  /** Maps toolCallId → turn captured in runTool, used by handleRunToolCompletions */
262
269
  toolCallTurns = /* @__PURE__ */ new Map();
@@ -543,8 +550,9 @@ var ToolNode = class extends RunnableCallable {
543
550
  const tool = this.toolMap.get(call.name);
544
551
  const registry = this.toolOutputRegistry;
545
552
  let resolveFn;
546
- if (preBatchSnapshot != null) resolveFn = (_runId, args) => preBatchSnapshot.resolve(args);
547
- else if (registry != null) resolveFn = (runIdArg, args) => registry.resolve(runIdArg, args);
553
+ const resolveOptions = { substituteIntentKey: this.toolDeclaresBusinessIntent(call.name) };
554
+ if (preBatchSnapshot != null) resolveFn = (_runId, args) => preBatchSnapshot.resolve(args, resolveOptions);
555
+ else if (registry != null) resolveFn = (runIdArg, args) => registry.resolve(runIdArg, args, resolveOptions);
548
556
  /**
549
557
  * Precompute the reference key once per call — captured locally
550
558
  * so concurrent `invoke()` calls on the same ToolNode cannot race
@@ -737,12 +745,14 @@ var ToolNode = class extends RunnableCallable {
737
745
  if (!this.handleToolErrors) throw e;
738
746
  if (isGraphInterrupt(e)) throw e;
739
747
  if (this.errorHandler) try {
740
- if (await this.errorHandler({
748
+ const dispatched = await this.errorHandler({
741
749
  error: e,
742
750
  id: call.id,
743
751
  name: call.name,
744
752
  input: call.args
745
- }, config.metadata) === false && call.id != null && call.id !== "")
753
+ }, config.metadata);
754
+ const ownership = batchContext.errorOwnership ?? this.looseErrorOwnership;
755
+ if (call.id != null && call.id !== "") if (dispatched === false)
746
756
  /**
747
757
  * The handler could not dispatch the error completion (typically
748
758
  * a resume pass, where a fast-failing tool errors before the
@@ -750,8 +760,10 @@ var ToolNode = class extends RunnableCallable {
750
760
  * output loop dispatches the completion itself instead of
751
761
  * assuming the handler covered it.
752
762
  */
753
- this.undispatchedToolErrors.add(call.id);
763
+ ownership.undispatched.add(call.id);
764
+ else ownership.handlerOwned.add(call.id);
754
765
  } catch (handlerError) {
766
+ if (call.id != null && call.id !== "") (batchContext.errorOwnership ?? this.looseErrorOwnership).handlerOwned.add(call.id);
755
767
  console.error("Error in errorHandler:", {
756
768
  toolName: call.name,
757
769
  toolCallId: call.id,
@@ -852,11 +864,12 @@ var ToolNode = class extends RunnableCallable {
852
864
  const turn = usageCount;
853
865
  const stepId = this.toolCallStepIds?.get(call.id ?? "") ?? "";
854
866
  let resolvedArgs = call.args;
867
+ const hookResolveOptions = { substituteIntentKey: this.toolDeclaresBusinessIntent(call.name) };
855
868
  if (batchContext.preBatchSnapshot != null) {
856
- const { resolved } = batchContext.preBatchSnapshot.resolve(call.args);
869
+ const { resolved } = batchContext.preBatchSnapshot.resolve(call.args, hookResolveOptions);
857
870
  resolvedArgs = resolved;
858
871
  } else if (this.toolOutputRegistry != null) {
859
- const { resolved } = this.toolOutputRegistry.resolve(registryRunId, call.args);
872
+ const { resolved } = this.toolOutputRegistry.resolve(registryRunId, call.args, hookResolveOptions);
860
873
  resolvedArgs = resolved;
861
874
  }
862
875
  let effectiveCall = call;
@@ -968,6 +981,16 @@ var ToolNode = class extends RunnableCallable {
968
981
  }
969
982
  }
970
983
  }
984
+ /**
985
+ * A hook (`PreToolUse.updatedInput`) or HITL `edit` decision rewrote the
986
+ * args: expose the EFFECTIVE args to downstream completion handling
987
+ * (`handleRunToolCompletions` reads this sink), so the emitted
988
+ * `tool_call.args` — and any intent/outcome label resolved from them —
989
+ * reflect what the tool actually ran with. `runTool`'s own placeholder
990
+ * substitution may overwrite this entry with the post-substitution args,
991
+ * which is strictly more accurate.
992
+ */
993
+ if (effectiveCall !== call && batchContext.resolvedArgsByCallId != null && call.id != null && call.id !== "") batchContext.resolvedArgsByCallId.set(call.id, effectiveCall.args);
971
994
  const output = await this.runTool(effectiveCall, config, {
972
995
  ...batchContext,
973
996
  usageCount
@@ -1174,7 +1197,8 @@ var ToolNode = class extends RunnableCallable {
1174
1197
  * by `runTool`. Threaded as a local map (instead of instance state)
1175
1198
  * so concurrent batches cannot read each other's entries.
1176
1199
  */
1177
- async handleRunToolCompletions(calls, outputs, config, resolvedArgsByCallId) {
1200
+ async handleRunToolCompletions(calls, outputs, config, resolvedArgsByCallId, errorOwnership) {
1201
+ const ownership = errorOwnership ?? this.looseErrorOwnership;
1178
1202
  for (let i = 0; i < calls.length; i++) {
1179
1203
  const call = calls[i];
1180
1204
  const output = outputs[i];
@@ -1182,8 +1206,13 @@ var ToolNode = class extends RunnableCallable {
1182
1206
  if (isCommand(output)) continue;
1183
1207
  const toolMessage = output;
1184
1208
  const toolCallId = call.id ?? "";
1185
- if (toolMessage.status === "error" && this.errorHandler != null) if (this.undispatchedToolErrors.has(toolCallId)) this.undispatchedToolErrors.delete(toolCallId);
1186
- else continue;
1209
+ if (toolMessage.status === "error" && this.errorHandler != null) {
1210
+ if (ownership.handlerOwned.has(toolCallId)) {
1211
+ ownership.handlerOwned.delete(toolCallId);
1212
+ continue;
1213
+ }
1214
+ ownership.undispatched.delete(toolCallId);
1215
+ }
1187
1216
  if (this.sessions && this.participatesInCodeSession(call.name)) {
1188
1217
  const artifact = toolMessage.artifact;
1189
1218
  const execSessionId = artifact?.session_id;
@@ -1195,16 +1224,27 @@ var ToolNode = class extends RunnableCallable {
1195
1224
  if (isComputerCallOutputMessage(toolMessage)) contentString = truncateToolResultContent("[Computer screenshot omitted from completion event]", this.maxToolResultChars);
1196
1225
  else if (typeof toolMessage.content === "string") contentString = toolMessage.content;
1197
1226
  else contentString = serializeStructuredValueBounded(toolMessage.content, this.maxToolResultChars).content;
1227
+ /**
1228
+ * Prefer the post-substitution args when a `{{…}}` placeholder
1229
+ * was resolved in `runTool`. This keeps
1230
+ * `ON_RUN_STEP_COMPLETED.tool_call.args` consistent with what
1231
+ * the tool actually received rather than leaking the template.
1232
+ */
1233
+ const effectiveArgs = resolvedArgsByCallId?.get(toolCallId) ?? call.args;
1234
+ /** Authored outcomes apply to failed calls too (“Search failed for…”);
1235
+ * without one, an error call simply stays unlabeled. */
1236
+ const outcome = resolveToolOutcome(effectiveArgs, readOutcomeFields(toolMessage.artifact), { isError: toolMessage.status === "error" });
1198
1237
  await safeDispatchCustomEvent("on_run_step_completed", { result: {
1199
1238
  id: stepId,
1200
1239
  index: turn,
1201
1240
  type: "tool_call",
1202
1241
  tool_call: {
1203
- args: serializeToolContentBounded(resolvedArgsByCallId?.get(toolCallId) ?? call.args ?? {}, this.maxToolResultChars),
1242
+ args: serializeToolContentBounded(effectiveArgs ?? {}, this.maxToolResultChars),
1204
1243
  name: call.name,
1205
1244
  id: toolCallId,
1206
1245
  output: contentString,
1207
- progress: 1
1246
+ progress: 1,
1247
+ ...outcome != null && { outcome }
1208
1248
  }
1209
1249
  } }, config);
1210
1250
  }
@@ -1253,7 +1293,7 @@ var ToolNode = class extends RunnableCallable {
1253
1293
  resolvedArgs = pre.resolved;
1254
1294
  if (pre.unresolved.length > 0 && call.id != null) unresolvedByCallId.set(call.id, pre.unresolved);
1255
1295
  } else if (registry != null) {
1256
- const { resolved, unresolved } = registry.resolve(registryRunId, originalArgs);
1296
+ const { resolved, unresolved } = registry.resolve(registryRunId, originalArgs, { substituteIntentKey: this.toolDeclaresBusinessIntent(call.name) });
1257
1297
  resolvedArgs = resolved;
1258
1298
  if (unresolved.length > 0 && call.id != null) unresolvedByCallId.set(call.id, unresolved);
1259
1299
  }
@@ -1429,7 +1469,7 @@ var ToolNode = class extends RunnableCallable {
1429
1469
  */
1430
1470
  const applyInputOverride = (entry, nextArgs) => {
1431
1471
  if (registry != null) {
1432
- const { resolved, unresolved } = (preBatchSnapshot ?? { resolve: (args) => registry.resolve(registryRunId, args) }).resolve(nextArgs);
1472
+ const { resolved, unresolved } = (preBatchSnapshot ?? { resolve: (args, options) => registry.resolve(registryRunId, args, options) }).resolve(nextArgs, { substituteIntentKey: this.toolDeclaresBusinessIntent(entry.call.name) });
1433
1473
  entry.args = resolved;
1434
1474
  if (entry.call.id != null) if (unresolved.length > 0) unresolvedByCallId.set(entry.call.id, unresolved);
1435
1475
  else unresolvedByCallId.delete(entry.call.id);
@@ -1772,7 +1812,7 @@ var ToolNode = class extends RunnableCallable {
1772
1812
  this.storeCodeSessionFromResults(results, requestMap);
1773
1813
  for (const result of results) {
1774
1814
  if (result.injectedMessages && result.injectedMessages.length > 0) try {
1775
- injected.push(...this.convertInjectedMessages(result.injectedMessages));
1815
+ injected.push(...convertInjectedMessages(result.injectedMessages));
1776
1816
  } catch (e) {
1777
1817
  console.warn(`[ToolNode] Failed to convert injectedMessages for toolCallId=${result.toolCallId}:`, e instanceof Error ? e.message : e);
1778
1818
  }
@@ -1890,7 +1930,7 @@ var ToolNode = class extends RunnableCallable {
1890
1930
  ...successRefMeta != null && { additional_kwargs: successRefMeta }
1891
1931
  });
1892
1932
  }
1893
- if (!eagerCompletionDispatchedIds.has(result.toolCallId) && !earlyCompletionDispatchedIds.has(result.toolCallId)) await this.dispatchStepCompleted(result.toolCallId, toolName, request?.args ?? {}, contentString, config, request?.turn);
1933
+ if (!eagerCompletionDispatchedIds.has(result.toolCallId) && !earlyCompletionDispatchedIds.has(result.toolCallId)) await this.dispatchStepCompleted(result.toolCallId, toolName, request?.args ?? {}, contentString, config, request?.turn, resolveToolOutcome(request?.args, outcomeFieldsFromResult(result), { isError: result.status === "error" }));
1894
1934
  postToolBatchEntryByCallId.set(result.toolCallId, {
1895
1935
  toolName,
1896
1936
  toolInput: request?.args ?? {},
@@ -2021,12 +2061,26 @@ var ToolNode = class extends RunnableCallable {
2021
2061
  * user speech (e.g. steering) sits closest to the next model call.
2022
2062
  */
2023
2063
  if (hookInjectedMessages.length > 0) try {
2024
- injected.push(...this.convertInjectedMessages(hookInjectedMessages));
2064
+ injected.push(...convertInjectedMessages(hookInjectedMessages));
2025
2065
  } catch (e) {
2026
2066
  console.warn("[ToolNode] Failed to convert PostToolBatch injectedMessages:", e instanceof Error ? e.message : e);
2027
2067
  }
2028
2068
  }
2029
- async dispatchStepCompleted(toolCallId, toolName, args, output, config, turn) {
2069
+ /**
2070
+ * Whether `name`'s own schema declares a BUSINESS parameter called
2071
+ * `intent` (declared, and not the injected label contract — see
2072
+ * `isIntentLabelProperty`). Such a parameter must keep participating in
2073
+ * `{{tool…}}` placeholder substitution; only the display label is exempt.
2074
+ */
2075
+ toolDeclaresBusinessIntent(name) {
2076
+ const instance = this.toolMap.get(name);
2077
+ const instanceProp = instance?.schema?.properties?.["intent"] ?? instance?.schema?.shape?.["intent"];
2078
+ if (instanceProp != null) return !isIntentLabelProperty(instanceProp);
2079
+ const defProp = this.toolRegistry?.get(name)?.parameters?.properties?.[INTENT_ARG];
2080
+ if (defProp != null) return !isIntentLabelProperty(defProp);
2081
+ return false;
2082
+ }
2083
+ async dispatchStepCompleted(toolCallId, toolName, args, output, config, turn, outcome) {
2030
2084
  const stepId = this.toolCallStepIds?.get(toolCallId) ?? "";
2031
2085
  if (!stepId) console.warn(`[ToolNode] toolCallStepIds missing entry for toolCallId=${toolCallId} (tool=${toolName}). This indicates a race between the stream consumer and graph execution. Map size: ${this.toolCallStepIds?.size ?? 0}`);
2032
2086
  return await safeDispatchCustomEvent("on_run_step_completed", { result: {
@@ -2038,7 +2092,8 @@ var ToolNode = class extends RunnableCallable {
2038
2092
  name: toolName,
2039
2093
  id: toolCallId,
2040
2094
  output,
2041
- progress: 1
2095
+ progress: 1,
2096
+ ...outcome != null && { outcome }
2042
2097
  }
2043
2098
  } }, config) !== false;
2044
2099
  }
@@ -2051,27 +2106,7 @@ var ToolNode = class extends RunnableCallable {
2051
2106
  */
2052
2107
  async dispatchEarlyToolCompletion(result, request, config) {
2053
2108
  const output = result.status === "error" ? truncateToolResultContent(`Error: ${result.errorMessage ?? "Unknown error"}\n Please fix your mistakes.`, this.maxToolResultChars) : serializeToolOutputWithinLimits(result.content, this.maxToolResultChars).content;
2054
- return this.dispatchStepCompleted(result.toolCallId, request.name, request.args, output, config, request.turn);
2055
- }
2056
- /**
2057
- * Converts InjectedMessage instances to LangChain HumanMessage objects.
2058
- * Both 'user' and 'system' roles become HumanMessage to avoid provider
2059
- * rejections (Anthropic/Google reject non-leading SystemMessages).
2060
- * The original role is preserved in additional_kwargs for downstream consumers.
2061
- */
2062
- convertInjectedMessages(messages) {
2063
- const converted = [];
2064
- for (const msg of messages) {
2065
- const additional_kwargs = { role: msg.role };
2066
- if (msg.isMeta != null) additional_kwargs.isMeta = msg.isMeta;
2067
- if (msg.source != null) additional_kwargs.source = msg.source;
2068
- if (msg.skillName != null) additional_kwargs.skillName = msg.skillName;
2069
- converted.push(new HumanMessage({
2070
- content: toLangChainContent(msg.content),
2071
- additional_kwargs
2072
- }));
2073
- }
2074
- return converted;
2109
+ return this.dispatchStepCompleted(result.toolCallId, request.name, request.args, output, config, request.turn, resolveToolOutcome(request.args, outcomeFieldsFromResult(result), { isError: result.status === "error" }));
2075
2110
  }
2076
2111
  /**
2077
2112
  * Execute a group of direct (in-process) tool calls with interrupt-safe
@@ -2152,6 +2187,8 @@ var ToolNode = class extends RunnableCallable {
2152
2187
  * ToolNode cannot read or wipe each other's entries.
2153
2188
  */
2154
2189
  const resolvedArgsByCallId = /* @__PURE__ */ new Map();
2190
+ /** Per-invocation error-completion ownership — see `ToolErrorOwnership`. */
2191
+ const errorOwnership = createToolErrorOwnership();
2155
2192
  const batchScopeId = config.configurable?.run_id ?? `\0anon-${this.anonBatchCounter++}`;
2156
2193
  const turn = this.toolOutputRegistry?.nextTurn(batchScopeId) ?? 0;
2157
2194
  let outputs;
@@ -2169,6 +2206,7 @@ var ToolNode = class extends RunnableCallable {
2169
2206
  turn,
2170
2207
  batchScopeId,
2171
2208
  resolvedArgsByCallId,
2209
+ errorOwnership,
2172
2210
  additionalContextsSink: directAdditionalContexts,
2173
2211
  runInput: sendState
2174
2212
  });
@@ -2179,7 +2217,7 @@ var ToolNode = class extends RunnableCallable {
2179
2217
  source: "hook"
2180
2218
  }
2181
2219
  })] : [sendOutput];
2182
- await this.handleRunToolCompletions([input.lg_tool_call], [sendOutput], config, resolvedArgsByCallId);
2220
+ await this.handleRunToolCompletions([input.lg_tool_call], [sendOutput], config, resolvedArgsByCallId, errorOwnership);
2183
2221
  } else {
2184
2222
  let messages;
2185
2223
  if (Array.isArray(input)) messages = input;
@@ -2257,7 +2295,7 @@ var ToolNode = class extends RunnableCallable {
2257
2295
  const preBatchSnapshot = this.toolOutputRegistry?.snapshot(batchScopeId);
2258
2296
  if (preBatchSnapshot != null) {
2259
2297
  for (const entry of eventEntries) if (entry.call.id != null) {
2260
- const { resolved, unresolved } = preBatchSnapshot.resolve(entry.call.args);
2298
+ const { resolved, unresolved } = preBatchSnapshot.resolve(entry.call.args, { substituteIntentKey: this.toolDeclaresBusinessIntent(entry.call.name) });
2261
2299
  preResolvedEventArgs.set(entry.call.id, {
2262
2300
  resolved,
2263
2301
  unresolved
@@ -2269,11 +2307,12 @@ var ToolNode = class extends RunnableCallable {
2269
2307
  turn,
2270
2308
  batchScopeId,
2271
2309
  resolvedArgsByCallId,
2310
+ errorOwnership,
2272
2311
  preBatchSnapshot,
2273
2312
  additionalContextsSink: directAdditionalContexts,
2274
2313
  runInput: input
2275
2314
  }) : [];
2276
- if (directCalls.length > 0 && directOutputs.length > 0) await this.handleRunToolCompletions(directCalls, directOutputs, config, resolvedArgsByCallId);
2315
+ if (directCalls.length > 0 && directOutputs.length > 0) await this.handleRunToolCompletions(directCalls, directOutputs, config, resolvedArgsByCallId, errorOwnership);
2277
2316
  const eventResult = eventCalls.length > 0 ? await this.dispatchToolEvents(eventCalls, config, {
2278
2317
  batchIndices: eventIndices,
2279
2318
  turn,
@@ -2304,11 +2343,12 @@ var ToolNode = class extends RunnableCallable {
2304
2343
  turn,
2305
2344
  batchScopeId,
2306
2345
  resolvedArgsByCallId,
2346
+ errorOwnership,
2307
2347
  preBatchSnapshot,
2308
2348
  additionalContextsSink: directAdditionalContexts,
2309
2349
  runInput: input
2310
2350
  });
2311
- await this.handleRunToolCompletions(filteredCalls, toolOutputs, config, resolvedArgsByCallId);
2351
+ await this.handleRunToolCompletions(filteredCalls, toolOutputs, config, resolvedArgsByCallId, errorOwnership);
2312
2352
  outputs = directAdditionalContexts.length > 0 ? [...toolOutputs, new HumanMessage({
2313
2353
  content: directAdditionalContexts.join("\n\n"),
2314
2354
  additional_kwargs: {
@@ -2414,6 +2454,6 @@ function toolsCondition(state, toolNode, invokedToolIds) {
2414
2454
  return END;
2415
2455
  }
2416
2456
  //#endregion
2417
- export { ToolNode, toolsCondition };
2457
+ export { ToolNode, createToolErrorOwnership, toolsCondition };
2418
2458
 
2419
2459
  //# sourceMappingURL=ToolNode.mjs.map