@librechat/agents 4.0.1 → 4.0.3

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 (245) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +3 -2
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/decisions/deadline.cjs +52 -0
  4. package/dist/cjs/decisions/deadline.cjs.map +1 -0
  5. package/dist/cjs/decisions/dialect.cjs +88 -0
  6. package/dist/cjs/decisions/dialect.cjs.map +1 -0
  7. package/dist/cjs/decisions/http.cjs +77 -0
  8. package/dist/cjs/decisions/http.cjs.map +1 -0
  9. package/dist/cjs/decisions/index.cjs +7 -0
  10. package/dist/cjs/decisions/presets.cjs +82 -0
  11. package/dist/cjs/decisions/presets.cjs.map +1 -0
  12. package/dist/cjs/decisions/questions.cjs +70 -0
  13. package/dist/cjs/decisions/questions.cjs.map +1 -0
  14. package/dist/cjs/decisions/structuredChat.cjs +172 -0
  15. package/dist/cjs/decisions/structuredChat.cjs.map +1 -0
  16. package/dist/cjs/decisions/structuredOutput.cjs +18 -0
  17. package/dist/cjs/decisions/structuredOutput.cjs.map +1 -0
  18. package/dist/cjs/decisions/traceMarker.cjs +6 -0
  19. package/dist/cjs/decisions/traceMarker.cjs.map +1 -0
  20. package/dist/cjs/decisions/transport.cjs +166 -0
  21. package/dist/cjs/decisions/transport.cjs.map +1 -0
  22. package/dist/cjs/decisions/types.cjs +44 -0
  23. package/dist/cjs/decisions/types.cjs.map +1 -0
  24. package/dist/cjs/graphs/Graph.cjs +28 -9
  25. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  26. package/dist/cjs/langfuseToolOutputTracing.cjs +6 -1
  27. package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
  28. package/dist/cjs/llm/google/index.cjs +1 -1
  29. package/dist/cjs/llm/invoke.cjs +34 -12
  30. package/dist/cjs/llm/invoke.cjs.map +1 -1
  31. package/dist/cjs/llm/openai/index.cjs +7 -0
  32. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  33. package/dist/cjs/llm/providerTextBoundary.cjs +355 -0
  34. package/dist/cjs/llm/providerTextBoundary.cjs.map +1 -0
  35. package/dist/cjs/llm/providerTextChunk.cjs +14 -0
  36. package/dist/cjs/llm/providerTextChunk.cjs.map +1 -0
  37. package/dist/cjs/llm/providerTextControls.cjs +236 -0
  38. package/dist/cjs/llm/providerTextControls.cjs.map +1 -0
  39. package/dist/cjs/llm/stream/smoother.cjs +2 -1
  40. package/dist/cjs/llm/stream/smoother.cjs.map +1 -1
  41. package/dist/cjs/main.cjs +49 -1
  42. package/dist/cjs/protection/providerText.cjs +224 -0
  43. package/dist/cjs/protection/providerText.cjs.map +1 -0
  44. package/dist/cjs/protection/providerTextInput.cjs +25 -0
  45. package/dist/cjs/protection/providerTextInput.cjs.map +1 -0
  46. package/dist/cjs/rerank/index.cjs +2 -0
  47. package/dist/cjs/rerank/search.cjs +45 -0
  48. package/dist/cjs/rerank/search.cjs.map +1 -0
  49. package/dist/cjs/rerank/systemone.cjs +70 -0
  50. package/dist/cjs/rerank/systemone.cjs.map +1 -0
  51. package/dist/cjs/run.cjs +11 -1
  52. package/dist/cjs/run.cjs.map +1 -1
  53. package/dist/cjs/stream.cjs +42 -14
  54. package/dist/cjs/stream.cjs.map +1 -1
  55. package/dist/cjs/summarization/node.cjs +26 -8
  56. package/dist/cjs/summarization/node.cjs.map +1 -1
  57. package/dist/cjs/tools/SubagentTool.cjs +11 -3
  58. package/dist/cjs/tools/SubagentTool.cjs.map +1 -1
  59. package/dist/cjs/tools/ToolNode.cjs +6 -5
  60. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  61. package/dist/cjs/tools/search/rerankers.cjs +1 -0
  62. package/dist/cjs/tools/search/search.cjs +3 -3
  63. package/dist/cjs/tools/search/search.cjs.map +1 -1
  64. package/dist/cjs/tools/search/tool.cjs +4 -3
  65. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  66. package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs +13 -2
  67. package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs.map +1 -1
  68. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +149 -26
  69. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  70. package/dist/cjs/tools/subagent/SubagentReplay.cjs +4 -2
  71. package/dist/cjs/tools/subagent/SubagentReplay.cjs.map +1 -1
  72. package/dist/cjs/tools/subagent/childGraphConfig.cjs +3 -1
  73. package/dist/cjs/tools/subagent/childGraphConfig.cjs.map +1 -1
  74. package/dist/cjs/tools/subagent/diagnostics.cjs +88 -2
  75. package/dist/cjs/tools/subagent/diagnostics.cjs.map +1 -1
  76. package/dist/cjs/tools/subagent/hostArgs.cjs +214 -0
  77. package/dist/cjs/tools/subagent/hostArgs.cjs.map +1 -0
  78. package/dist/cjs/tools/subagent/index.cjs +2 -0
  79. package/dist/cjs/tools/toolErrorContent.cjs +1 -1
  80. package/dist/esm/agents/AgentContext.mjs +3 -2
  81. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  82. package/dist/esm/decisions/deadline.mjs +52 -0
  83. package/dist/esm/decisions/deadline.mjs.map +1 -0
  84. package/dist/esm/decisions/dialect.mjs +87 -0
  85. package/dist/esm/decisions/dialect.mjs.map +1 -0
  86. package/dist/esm/decisions/http.mjs +75 -0
  87. package/dist/esm/decisions/http.mjs.map +1 -0
  88. package/dist/esm/decisions/index.mjs +8 -0
  89. package/dist/esm/decisions/presets.mjs +77 -0
  90. package/dist/esm/decisions/presets.mjs.map +1 -0
  91. package/dist/esm/decisions/questions.mjs +67 -0
  92. package/dist/esm/decisions/questions.mjs.map +1 -0
  93. package/dist/esm/decisions/structuredChat.mjs +172 -0
  94. package/dist/esm/decisions/structuredChat.mjs.map +1 -0
  95. package/dist/esm/decisions/structuredOutput.mjs +18 -0
  96. package/dist/esm/decisions/structuredOutput.mjs.map +1 -0
  97. package/dist/esm/decisions/traceMarker.mjs +6 -0
  98. package/dist/esm/decisions/traceMarker.mjs.map +1 -0
  99. package/dist/esm/decisions/transport.mjs +164 -0
  100. package/dist/esm/decisions/transport.mjs.map +1 -0
  101. package/dist/esm/decisions/types.mjs +39 -0
  102. package/dist/esm/decisions/types.mjs.map +1 -0
  103. package/dist/esm/graphs/Graph.mjs +28 -9
  104. package/dist/esm/graphs/Graph.mjs.map +1 -1
  105. package/dist/esm/langfuseToolOutputTracing.mjs +6 -1
  106. package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
  107. package/dist/esm/llm/google/index.mjs +1 -1
  108. package/dist/esm/llm/invoke.mjs +34 -12
  109. package/dist/esm/llm/invoke.mjs.map +1 -1
  110. package/dist/esm/llm/openai/index.mjs +7 -0
  111. package/dist/esm/llm/openai/index.mjs.map +1 -1
  112. package/dist/esm/llm/providerTextBoundary.mjs +355 -0
  113. package/dist/esm/llm/providerTextBoundary.mjs.map +1 -0
  114. package/dist/esm/llm/providerTextChunk.mjs +13 -0
  115. package/dist/esm/llm/providerTextChunk.mjs.map +1 -0
  116. package/dist/esm/llm/providerTextControls.mjs +230 -0
  117. package/dist/esm/llm/providerTextControls.mjs.map +1 -0
  118. package/dist/esm/llm/stream/smoother.mjs +2 -1
  119. package/dist/esm/llm/stream/smoother.mjs.map +1 -1
  120. package/dist/esm/main.mjs +16 -2
  121. package/dist/esm/protection/providerText.mjs +218 -0
  122. package/dist/esm/protection/providerText.mjs.map +1 -0
  123. package/dist/esm/protection/providerTextInput.mjs +24 -0
  124. package/dist/esm/protection/providerTextInput.mjs.map +1 -0
  125. package/dist/esm/rerank/index.mjs +3 -0
  126. package/dist/esm/rerank/search.mjs +45 -0
  127. package/dist/esm/rerank/search.mjs.map +1 -0
  128. package/dist/esm/rerank/systemone.mjs +70 -0
  129. package/dist/esm/rerank/systemone.mjs.map +1 -0
  130. package/dist/esm/run.mjs +11 -1
  131. package/dist/esm/run.mjs.map +1 -1
  132. package/dist/esm/stream.mjs +42 -14
  133. package/dist/esm/stream.mjs.map +1 -1
  134. package/dist/esm/summarization/node.mjs +26 -8
  135. package/dist/esm/summarization/node.mjs.map +1 -1
  136. package/dist/esm/tools/SubagentTool.mjs +11 -3
  137. package/dist/esm/tools/SubagentTool.mjs.map +1 -1
  138. package/dist/esm/tools/ToolNode.mjs +6 -5
  139. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  140. package/dist/esm/tools/search/rerankers.mjs +1 -1
  141. package/dist/esm/tools/search/search.mjs +3 -3
  142. package/dist/esm/tools/search/search.mjs.map +1 -1
  143. package/dist/esm/tools/search/tool.mjs +4 -3
  144. package/dist/esm/tools/search/tool.mjs.map +1 -1
  145. package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs +13 -2
  146. package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs.map +1 -1
  147. package/dist/esm/tools/subagent/SubagentExecutor.mjs +149 -26
  148. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  149. package/dist/esm/tools/subagent/SubagentReplay.mjs +4 -2
  150. package/dist/esm/tools/subagent/SubagentReplay.mjs.map +1 -1
  151. package/dist/esm/tools/subagent/childGraphConfig.mjs +3 -1
  152. package/dist/esm/tools/subagent/childGraphConfig.mjs.map +1 -1
  153. package/dist/esm/tools/subagent/diagnostics.mjs +84 -3
  154. package/dist/esm/tools/subagent/diagnostics.mjs.map +1 -1
  155. package/dist/esm/tools/subagent/hostArgs.mjs +207 -0
  156. package/dist/esm/tools/subagent/hostArgs.mjs.map +1 -0
  157. package/dist/esm/tools/subagent/index.mjs +2 -0
  158. package/dist/esm/tools/toolErrorContent.mjs +1 -1
  159. package/dist/types/decisions/deadline.d.ts +3 -0
  160. package/dist/types/decisions/dialect.d.ts +11 -0
  161. package/dist/types/decisions/http.d.ts +22 -0
  162. package/dist/types/decisions/index.d.ts +10 -0
  163. package/dist/types/decisions/presets.d.ts +17 -0
  164. package/dist/types/decisions/questions.d.ts +8 -0
  165. package/dist/types/decisions/structuredChat.d.ts +17 -0
  166. package/dist/types/decisions/structuredOutput.d.ts +4 -0
  167. package/dist/types/decisions/traceMarker.d.ts +2 -0
  168. package/dist/types/decisions/transport.d.ts +29 -0
  169. package/dist/types/decisions/types.d.ts +119 -0
  170. package/dist/types/graphs/Graph.d.ts +7 -3
  171. package/dist/types/index.d.ts +4 -0
  172. package/dist/types/llm/invoke.d.ts +4 -1
  173. package/dist/types/llm/providerTextBoundary.d.ts +4 -0
  174. package/dist/types/llm/providerTextChunk.d.ts +7 -0
  175. package/dist/types/llm/providerTextControls.d.ts +18 -0
  176. package/dist/types/protection/providerText.d.ts +77 -0
  177. package/dist/types/protection/providerTextInput.d.ts +3 -0
  178. package/dist/types/rerank/index.d.ts +4 -0
  179. package/dist/types/rerank/search.d.ts +7 -0
  180. package/dist/types/rerank/systemone.d.ts +13 -0
  181. package/dist/types/rerank/types.d.ts +24 -0
  182. package/dist/types/run.d.ts +2 -0
  183. package/dist/types/tools/BashExecutor.d.ts +4 -0
  184. package/dist/types/tools/CodeExecutor.d.ts +4 -0
  185. package/dist/types/tools/ReadFile.d.ts +4 -0
  186. package/dist/types/tools/SkillTool.d.ts +4 -0
  187. package/dist/types/tools/SubagentTool.d.ts +2 -0
  188. package/dist/types/tools/ToolSearch.d.ts +4 -0
  189. package/dist/types/tools/search/schema.d.ts +4 -0
  190. package/dist/types/tools/search/types.d.ts +3 -1
  191. package/dist/types/tools/subagent/SubagentExecutionRegistry.d.ts +6 -0
  192. package/dist/types/tools/subagent/SubagentExecutor.d.ts +22 -1
  193. package/dist/types/tools/subagent/SubagentReplay.d.ts +3 -1
  194. package/dist/types/tools/subagent/diagnostics.d.ts +56 -8
  195. package/dist/types/tools/subagent/hostArgs.d.ts +57 -0
  196. package/dist/types/tools/subagent/index.d.ts +3 -0
  197. package/dist/types/types/graph.d.ts +39 -0
  198. package/dist/types/types/run.d.ts +6 -2
  199. package/dist/types/types/tools.d.ts +2 -0
  200. package/package.json +4 -4
  201. package/src/agents/AgentContext.ts +3 -2
  202. package/src/decisions/deadline.ts +92 -0
  203. package/src/decisions/dialect.ts +200 -0
  204. package/src/decisions/http.ts +175 -0
  205. package/src/decisions/index.ts +14 -0
  206. package/src/decisions/presets.ts +111 -0
  207. package/src/decisions/questions.ts +128 -0
  208. package/src/decisions/structuredChat.ts +358 -0
  209. package/src/decisions/structuredOutput.ts +25 -0
  210. package/src/decisions/traceMarker.ts +2 -0
  211. package/src/decisions/transport.ts +335 -0
  212. package/src/decisions/types.ts +193 -0
  213. package/src/graphs/Graph.ts +57 -9
  214. package/src/index.ts +9 -0
  215. package/src/langfuseToolOutputTracing.ts +8 -1
  216. package/src/llm/invoke.ts +48 -7
  217. package/src/llm/openai/index.ts +8 -0
  218. package/src/llm/providerTextBoundary.ts +446 -0
  219. package/src/llm/providerTextChunk.ts +15 -0
  220. package/src/llm/providerTextControls.ts +173 -0
  221. package/src/llm/stream/smoother.ts +4 -2
  222. package/src/protection/providerText.ts +325 -0
  223. package/src/protection/providerTextInput.ts +21 -0
  224. package/src/rerank/index.ts +4 -0
  225. package/src/rerank/search.ts +85 -0
  226. package/src/rerank/systemone.ts +140 -0
  227. package/src/rerank/types.ts +28 -0
  228. package/src/run.ts +13 -0
  229. package/src/stream.ts +33 -7
  230. package/src/summarization/node.ts +32 -14
  231. package/src/tools/SubagentTool.ts +14 -3
  232. package/src/tools/ToolNode.ts +7 -3
  233. package/src/tools/search/search.ts +4 -4
  234. package/src/tools/search/tool.ts +27 -25
  235. package/src/tools/search/types.ts +4 -1
  236. package/src/tools/subagent/SubagentExecutionRegistry.ts +27 -2
  237. package/src/tools/subagent/SubagentExecutor.ts +323 -28
  238. package/src/tools/subagent/SubagentReplay.ts +10 -3
  239. package/src/tools/subagent/childGraphConfig.ts +12 -1
  240. package/src/tools/subagent/diagnostics.ts +180 -9
  241. package/src/tools/subagent/hostArgs.ts +413 -0
  242. package/src/tools/subagent/index.ts +20 -0
  243. package/src/types/graph.ts +44 -0
  244. package/src/types/run.ts +6 -2
  245. package/src/types/tools.ts +6 -1
package/src/stream.ts CHANGED
@@ -58,6 +58,7 @@ import {
58
58
  import { createToolCallsDispatchedEvent, safeDispatchCustomEvent } from '@/utils/events';
59
59
  import { resolveToolOutcome, outcomeFieldsFromResult } from '@/tools/intentArg';
60
60
  import { snapshotValidatedModelChunk } from '@/graphs/acceptedModelResponse';
61
+ import { ProviderTextProtectionError } from '@/protection/providerText';
61
62
  import { TOOL_OUTPUT_REF_PATTERN } from '@/tools/toolOutputReferences';
62
63
  import { formatToolErrorContent } from '@/tools/toolErrorContent';
63
64
  import { PreparedSubagentError } from '@/tools/preparedSubagents';
@@ -1687,6 +1688,7 @@ export class ChatModelStreamHandler implements t.EventHandler {
1687
1688
  graph.breakerAbort instanceof AbortController
1688
1689
  ? graph.breakerAbort
1689
1690
  : undefined;
1691
+ const entrySignals = [eventBreaker?.signal, graph.signal, graph.config.signal];
1690
1692
  /** Immutable scope captured at handler entry. A reset while this
1691
1693
  * handler is suspended in an await replaces the object, so ONE
1692
1694
  * reference comparison proves the event still belongs to the live run
@@ -1695,13 +1697,16 @@ export class ChatModelStreamHandler implements t.EventHandler {
1695
1697
  const runScopeInvalidated = (): boolean =>
1696
1698
  entryRunScope != null && graph.runScope !== entryRunScope;
1697
1699
  const throwIfRunBreakerTripped = (): void => {
1698
- if (
1699
- eventBreaker != null &&
1700
- eventBreaker.signal.aborted &&
1701
- (eventBreaker.signal.reason instanceof StreamLimitExceededError ||
1702
- eventBreaker.signal.reason instanceof PreparedSubagentError)
1703
- ) {
1704
- throw eventBreaker.signal.reason;
1700
+ for (const signal of entrySignals) {
1701
+ if (signal?.aborted === true &&
1702
+ (signal.reason instanceof StreamLimitExceededError ||
1703
+ signal.reason instanceof PreparedSubagentError ||
1704
+ signal.reason instanceof ProviderTextProtectionError)) {
1705
+ throw signal.reason;
1706
+ }
1707
+ if (signal?.aborted === true && graph.providerTextProtection != null) {
1708
+ throw new ProviderTextProtectionError('cancelled');
1709
+ }
1705
1710
  }
1706
1711
  };
1707
1712
 
@@ -1797,6 +1802,8 @@ export class ChatModelStreamHandler implements t.EventHandler {
1797
1802
  metadata,
1798
1803
  agentContext,
1799
1804
  });
1805
+ if (runScopeInvalidated()) return;
1806
+ throwIfRunBreakerTripped();
1800
1807
  if (skipHandling) {
1801
1808
  return;
1802
1809
  }
@@ -1831,6 +1838,8 @@ export class ChatModelStreamHandler implements t.EventHandler {
1831
1838
  content,
1832
1839
  metadata,
1833
1840
  });
1841
+ if (runScopeInvalidated()) return;
1842
+ throwIfRunBreakerTripped();
1834
1843
  }
1835
1844
 
1836
1845
  if (
@@ -2033,6 +2042,23 @@ export class ChatModelStreamHandler implements t.EventHandler {
2033
2042
  });
2034
2043
  runStep = graph.getRunStep(stepId);
2035
2044
  }
2045
+ if (
2046
+ graph.providerTextProtection != null &&
2047
+ runStep?.type === StepTypes.TOOL_CALLS &&
2048
+ agentContext.currentTokenType === ContentTypes.TEXT &&
2049
+ (typeof content === 'string' || content.every(isTextContentPart))
2050
+ ) {
2051
+ stepId = await dispatchMessageCreationStep({
2052
+ graph,
2053
+ stepKey,
2054
+ content,
2055
+ contentType: ContentTypes.TEXT,
2056
+ metadata,
2057
+ });
2058
+ if (runScopeInvalidated()) return;
2059
+ throwIfRunBreakerTripped();
2060
+ runStep = graph.getRunStep(stepId);
2061
+ }
2036
2062
  if (!runStep) {
2037
2063
  console.warn(`\n
2038
2064
  ==============================================================
@@ -56,6 +56,7 @@ import {
56
56
  resolveClientOptionsModel,
57
57
  } from '@/llm/request';
58
58
  import { renderCompactionSemanticIndex } from '@/summarization/semanticIndex';
59
+ import { ProviderTextProtectionError } from '@/protection/providerText';
59
60
  import { safeDispatchCustomEvent, emitAgentLog } from '@/utils/events';
60
61
  import { prepareToolsForPromptCache } from '@/llm/promptCacheTools';
61
62
  import { attemptInvoke, tryFallbackProviders } from '@/llm/invoke';
@@ -739,16 +740,6 @@ async function executeSummarizationWithFallback(params: {
739
740
  summaryText = result.text;
740
741
  summaryUsage = result.usage;
741
742
  } catch (primaryError) {
742
- const primaryDescribed = describeProviderError(
743
- primaryError,
744
- clientConfig.provider,
745
- clientConfig.modelName
746
- );
747
- log('error', `Summarization LLM call failed ${primaryDescribed.suffix}`, {
748
- ...primaryDescribed.data,
749
- messagesToRefineCount: messages.length,
750
- });
751
-
752
743
  /**
753
744
  * A tripped stream circuit breaker is a safety abort, not a
754
745
  * summarization failure to recover from: retrying on fallback providers
@@ -758,7 +749,7 @@ async function executeSummarizationWithFallback(params: {
758
749
  * rejects the run. Rethrown so the summarize node fails the run with
759
750
  * the actionable limit error, consistent with agent turns and subagents.
760
751
  */
761
- if (primaryError instanceof StreamLimitExceededError) {
752
+ if (primaryError instanceof StreamLimitExceededError || primaryError instanceof ProviderTextProtectionError) {
762
753
  throw primaryError;
763
754
  }
764
755
  /** A parallel branch's trip aborts the composed summarization signal,
@@ -777,6 +768,16 @@ async function executeSummarizationWithFallback(params: {
777
768
  }
778
769
  }
779
770
 
771
+ const primaryDescribed = describeProviderError(
772
+ primaryError,
773
+ clientConfig.provider,
774
+ clientConfig.modelName
775
+ );
776
+ log('error', `Summarization LLM call failed ${primaryDescribed.suffix}`, {
777
+ ...primaryDescribed.data,
778
+ messagesToRefineCount: messages.length,
779
+ });
780
+
780
781
  const rawFallbacks = (
781
782
  clientConfig.clientOptions as unknown as t.LLMConfig | undefined
782
783
  )?.fallbacks;
@@ -819,7 +820,9 @@ async function executeSummarizationWithFallback(params: {
819
820
  );
820
821
  }
821
822
  } catch (fbErr) {
822
- if (fbErr instanceof StreamLimitExceededError) {
823
+ const trip = findStreamLimitAbortReason(graph?.getBreakerSignal?.(), summarizeConfig?.signal);
824
+ if (trip != null) throw trip;
825
+ if (fbErr instanceof StreamLimitExceededError || fbErr instanceof ProviderTextProtectionError) {
823
826
  throw fbErr;
824
827
  }
825
828
  const fbDescribed = describeFallbackError(fbErr, fallbacks);
@@ -842,6 +845,8 @@ async function executeSummarizationWithFallback(params: {
842
845
  }
843
846
  }
844
847
 
848
+ const trip = findStreamLimitAbortReason(graph?.getBreakerSignal?.(), summarizeConfig?.signal);
849
+ if (trip != null) throw trip;
845
850
  return { text: summaryText, usage: summaryUsage, usedMetadataStub };
846
851
  }
847
852
 
@@ -875,6 +880,11 @@ async function dispatchCompletionEvents(params: {
875
880
  messagesAfterCount,
876
881
  } = params;
877
882
 
883
+ const assertNotTripped = (): void => {
884
+ const trip = findStreamLimitAbortReason(graph.getBreakerSignal?.(), runnableConfig?.signal);
885
+ if (trip != null) throw trip;
886
+ };
887
+ assertNotTripped();
878
888
  runStep.summary = summaryBlock;
879
889
  if (summaryUsage) {
880
890
  runStep.usage = {
@@ -892,6 +902,7 @@ async function dispatchCompletionEvents(params: {
892
902
  runnableConfig
893
903
  );
894
904
 
905
+ assertNotTripped();
895
906
  if (runnableConfig) {
896
907
  await safeDispatchCustomEvent(
897
908
  GraphEvents.ON_SUMMARIZE_COMPLETE,
@@ -904,6 +915,7 @@ async function dispatchCompletionEvents(params: {
904
915
  );
905
916
  }
906
917
 
918
+ assertNotTripped();
907
919
  const sessionId = graph.runId ?? '';
908
920
  if (graph.hookRegistry?.hasHookFor('PostCompact', sessionId) === true) {
909
921
  const threadId = (
@@ -1017,11 +1029,11 @@ function withBreakerSignal<T extends { signal?: AbortSignal }>(
1017
1029
  * child's private breaker never fires for it. */
1018
1030
  function findStreamLimitAbortReason(
1019
1031
  ...signals: Array<AbortSignal | undefined>
1020
- ): StreamLimitExceededError | undefined {
1032
+ ): StreamLimitExceededError | ProviderTextProtectionError | undefined {
1021
1033
  for (const signal of signals) {
1022
1034
  if (
1023
1035
  signal?.aborted === true &&
1024
- signal.reason instanceof StreamLimitExceededError
1036
+ (signal.reason instanceof StreamLimitExceededError || signal.reason instanceof ProviderTextProtectionError)
1025
1037
  ) {
1026
1038
  return signal.reason;
1027
1039
  }
@@ -1526,6 +1538,8 @@ export function createSummarizeNode({
1526
1538
  return { summarizationRequest: undefined };
1527
1539
  }
1528
1540
 
1541
+ const postCallTrip = findStreamLimitAbortReason(entryBreakerSignal, config?.signal);
1542
+ if (postCallTrip != null) throw postCallTrip;
1529
1543
  const summaryText = enrichSummary(rawText, messagesToRefine);
1530
1544
 
1531
1545
  const tokenCount = await computeSummaryTokenCount(
@@ -1533,6 +1547,8 @@ export function createSummarizeNode({
1533
1547
  agentContext
1534
1548
  );
1535
1549
 
1550
+ const commitTrip = findStreamLimitAbortReason(entryBreakerSignal, config?.signal);
1551
+ if (commitTrip != null) throw commitTrip;
1536
1552
  if (usedIntraTurnFallback) {
1537
1553
  agentContext.setSummary(summaryText, tokenCount, {
1538
1554
  precedesMessages: true,
@@ -1712,6 +1728,8 @@ export function createSummarizationChunkHandler({
1712
1728
  throw error;
1713
1729
  }
1714
1730
  }
1731
+ const protectionTrip = findStreamLimitAbortReason(graph?.getBreakerController?.().signal, config.signal);
1732
+ if (protectionTrip != null) throw protectionTrip;
1715
1733
  const chunkAny = chunk as Parameters<typeof getChunkContent>[0]['chunk'];
1716
1734
  const raw = getChunkContent({ chunk: chunkAny, provider, reasoningKey });
1717
1735
  if (raw == null || (typeof raw === 'string' && !raw)) {
@@ -1,5 +1,6 @@
1
1
  import type { JsonSchemaType, LCTool } from '@/types/tools';
2
2
  import type { SubagentConfig } from '@/types';
3
+ import { buildSubagentHostArgProperties } from '@/tools/subagent/hostArgs';
3
4
  import { INTENT_PROPERTY } from '@/tools/intentArg';
4
5
  import { Constants } from '@/common';
5
6
 
@@ -33,6 +34,9 @@ const RUN_IN_BACKGROUND_PROP_DESCRIPTION =
33
34
  const SUBAGENT_THREAD_PROP_DESCRIPTION =
34
35
  'Continue a host-owned child thread using a fresh execution lease. The saved thread must belong to this scope and subagent type. Only available with run_in_background.';
35
36
 
37
+ const HOST_ARGS_DESCRIPTION =
38
+ '\n\nOPTIONAL ARGUMENTS:\n- Some types accept extra arguments, listed in brackets after the type. Each is optional: omit it to let the host choose. Where values are listed, pass one listed for the selected type; where it says any text, pass a short value of your own.';
39
+
36
40
  export const SubagentToolSchema = {
37
41
  type: 'object',
38
42
  properties: {
@@ -69,8 +73,13 @@ export function buildSubagentToolParams(
69
73
  description: string;
70
74
  } {
71
75
  const types = configs.map((c) => c.type);
76
+ const hostArgs = buildSubagentHostArgProperties(configs);
72
77
  const typeDescriptions = configs
73
- .map((c) => `- "${c.type}" (${c.name}): ${c.description}`)
78
+ .map((c) => {
79
+ const summary = hostArgs.summaries.get(c.type);
80
+ const line = `- "${c.type}" (${c.name}): ${c.description}`;
81
+ return summary == null ? line : `${line} [optional ${summary}]`;
82
+ })
74
83
  .join('\n');
75
84
 
76
85
  return {
@@ -96,8 +105,7 @@ export function buildSubagentToolParams(
96
105
  },
97
106
  }
98
107
  : {}),
99
- ...(options.background === true &&
100
- options.threadContinuation === true
108
+ ...(options.background === true && options.threadContinuation === true
101
109
  ? {
102
110
  subagent_thread_id: {
103
111
  type: 'string',
@@ -105,10 +113,13 @@ export function buildSubagentToolParams(
105
113
  },
106
114
  }
107
115
  : {}),
116
+ ...hostArgs.properties,
108
117
  },
109
118
  required: ['description', 'subagent_type'],
110
119
  },
111
120
  description: `${SubagentToolDescription}${
121
+ hostArgs.summaries.size > 0 ? HOST_ARGS_DESCRIPTION : ''
122
+ }${
112
123
  options.background === true
113
124
  ? '\n\nBACKGROUND EXECUTION:\n- Set run_in_background to true when you do not need the result immediately. The call returns a background_task_id; use the host background-task tools to poll, steer, queue, interrupt, or cancel it.'
114
125
  : ''
@@ -130,6 +130,7 @@ import {
130
130
  } from '@/tools/local';
131
131
  import { stripCodeSessionFileSummary } from '@/tools/CodeSessionFileSummary';
132
132
  import { Constants, GraphEvents, CODE_EXECUTION_TOOLS } from '@/common';
133
+ import { ProviderTextProtectionError } from '@/protection/providerText';
133
134
  import { formatToolErrorContent } from '@/tools/toolErrorContent';
134
135
  import { PreparedSubagentError } from '@/tools/preparedSubagents';
135
136
  import { attachRunStepResumeState } from '@/tools/runStepResume';
@@ -2146,7 +2147,8 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
2146
2147
  */
2147
2148
  if (
2148
2149
  e instanceof StreamLimitExceededError ||
2149
- e instanceof PreparedSubagentError
2150
+ e instanceof PreparedSubagentError ||
2151
+ e instanceof ProviderTextProtectionError
2150
2152
  ) {
2151
2153
  throw e;
2152
2154
  }
@@ -3486,7 +3488,8 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
3486
3488
  if (
3487
3489
  signal?.aborted === true &&
3488
3490
  (signal.reason instanceof StreamLimitExceededError ||
3489
- signal.reason instanceof PreparedSubagentError)
3491
+ signal.reason instanceof PreparedSubagentError ||
3492
+ signal.reason instanceof ProviderTextProtectionError)
3490
3493
  ) {
3491
3494
  throw signal.reason;
3492
3495
  }
@@ -5467,7 +5470,8 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
5467
5470
  if (
5468
5471
  composedSignal?.aborted === true &&
5469
5472
  (composedSignal.reason instanceof StreamLimitExceededError ||
5470
- composedSignal.reason instanceof PreparedSubagentError)
5473
+ composedSignal.reason instanceof PreparedSubagentError ||
5474
+ composedSignal.reason instanceof ProviderTextProtectionError)
5471
5475
  ) {
5472
5476
  throw composedSignal.reason;
5473
5477
  }
@@ -9,8 +9,8 @@ import {
9
9
  import { createKeenableAPI } from './keenable-search';
10
10
  import { createTavilyAPI } from './tavily-search';
11
11
  import { createSearchMetrics } from './metrics';
12
+ import { getDefaultRanking } from './rerankers';
12
13
  import { createCrwAPI } from './crw-search';
13
- import { BaseReranker, getDefaultRanking } from './rerankers';
14
14
 
15
15
  /** Engines queried when `searxngSearchOptions.engines` is not configured. */
16
16
  const DEFAULT_SEARXNG_ENGINES = 'google,bing,duckduckgo';
@@ -169,7 +169,7 @@ const getHighlights = async ({
169
169
  }: {
170
170
  content: string;
171
171
  query: string;
172
- reranker?: BaseReranker;
172
+ reranker?: t.SearchReranker;
173
173
  metrics: t.SearchMetrics;
174
174
  topResults?: number;
175
175
  maxContentLength?: number;
@@ -453,7 +453,7 @@ const createSearXNGAPI = (
453
453
  const organicResults = (data.results ?? [])
454
454
  .slice(0, numResults)
455
455
  .map((result: t.SearXNGResult, index: number) => {
456
- let attribution = '';
456
+ let attribution: string;
457
457
  try {
458
458
  attribution = new URL(result.url ?? '').hostname;
459
459
  } catch {
@@ -486,7 +486,7 @@ const createSearXNGAPI = (
486
486
  const newsResults = (data.results ?? [])
487
487
  .filter(isNewsResult)
488
488
  .map((result: t.SearXNGResult, index: number) => {
489
- let attribution = '';
489
+ let attribution: string;
490
490
  try {
491
491
  attribution = new URL(result.url ?? '').hostname;
492
492
  } catch {
@@ -12,6 +12,10 @@ import {
12
12
  newsSchema,
13
13
  DATE_RANGE,
14
14
  } from './schema';
15
+ import {
16
+ formatResultsForLLM,
17
+ MISSING_SEARCH_RESULT_DATA_ERROR,
18
+ } from './format';
15
19
  import { createSearchAPI, createSourceProcessor } from './search';
16
20
  import { createKeenableScraper } from './keenable-scraper';
17
21
  import { createSerperScraper } from './serper-scraper';
@@ -21,10 +25,6 @@ import { INTENT_PROPERTY } from '@/tools/intentArg';
21
25
  import { createCrwScraper } from './crw-scraper';
22
26
  import { expandHighlights } from './highlights';
23
27
  import { createSearchMetrics } from './metrics';
24
- import {
25
- formatResultsForLLM,
26
- MISSING_SEARCH_RESULT_DATA_ERROR,
27
- } from './format';
28
28
  import { createDefaultLogger } from './utils';
29
29
  import { createReranker } from './rerankers';
30
30
  import { Constants } from '@/common';
@@ -535,13 +535,13 @@ export const createSearchTool = (
535
535
  } = config;
536
536
 
537
537
  const logger = config.logger || createDefaultLogger();
538
- const effectiveTavilySearchOptions =
539
- searchProvider === 'tavily' && config.safeSearch != null
540
- ? {
541
- ...tavilySearchOptions,
542
- safeSearch: config.safeSearch !== 0,
543
- }
544
- : tavilySearchOptions;
538
+ let effectiveTavilySearchOptions = tavilySearchOptions;
539
+ if (searchProvider === 'tavily' && config.safeSearch != null) {
540
+ effectiveTavilySearchOptions = {
541
+ ...tavilySearchOptions,
542
+ safeSearch: config.safeSearch !== 0,
543
+ };
544
+ }
545
545
 
546
546
  const schemaProperties: Record<string, unknown> = {
547
547
  intent: { ...INTENT_PROPERTY },
@@ -643,20 +643,22 @@ export const createSearchTool = (
643
643
  });
644
644
  }
645
645
 
646
- const selectedReranker = createReranker({
647
- rerankerType,
648
- jinaApiKey,
649
- jinaApiUrl,
650
- cohereApiKey,
651
- cohereApiUrl,
652
- ragApiUrl,
653
- ragApiTokenSupplier,
654
- ragApiProfile,
655
- rerankerTimeout,
656
- httpAgent,
657
- httpsAgent,
658
- logger,
659
- });
646
+ const selectedReranker =
647
+ config.reranker ??
648
+ createReranker({
649
+ rerankerType,
650
+ jinaApiKey,
651
+ jinaApiUrl,
652
+ cohereApiKey,
653
+ cohereApiUrl,
654
+ ragApiUrl,
655
+ ragApiTokenSupplier,
656
+ ragApiProfile,
657
+ rerankerTimeout,
658
+ httpAgent,
659
+ httpsAgent,
660
+ logger,
661
+ });
660
662
 
661
663
  /** `none` is a deliberate opt-out that `createReranker` already reports;
662
664
  * only an unusable configuration warrants a warning here. */
@@ -287,6 +287,9 @@ export interface ScrapeResult {
287
287
  highlights?: Highlight[];
288
288
  }
289
289
 
290
+ /** Host implementations need only the public ranking seam, not BaseReranker's private state. */
291
+ export type SearchReranker = Pick<BaseReranker, 'provider' | 'rerank'>;
292
+
290
293
  export interface ProcessSourcesConfig {
291
294
  topResults?: number;
292
295
  /** Max chars of scraped content stored per source and passed to the
@@ -306,7 +309,7 @@ export interface ProcessSourcesConfig {
306
309
  separatorExpandBy?: number;
307
310
  strategies?: string[];
308
311
  filterContent?: boolean;
309
- reranker?: BaseReranker;
312
+ reranker?: SearchReranker;
310
313
  logger?: Logger;
311
314
  }
312
315
 
@@ -63,6 +63,8 @@ export type SubagentInvocationBinding = Readonly<{
63
63
  description: string;
64
64
  subagentType: string;
65
65
  configId?: string;
66
+ /** Value-free identity of the call's validated host arguments. */
67
+ hostArgsDigest?: string;
66
68
  }>;
67
69
 
68
70
  export type SubagentSettlementBinding = Readonly<{
@@ -340,7 +342,8 @@ function assertCompatibleResumeExecution(
340
342
  current.parentToolCallId === next.parentToolCallId &&
341
343
  current.childRunId === next.childRunId &&
342
344
  current.subagentType === next.subagentType &&
343
- current.configId === next.configId
345
+ current.configId === next.configId &&
346
+ current.hostArgsDigest === next.hostArgsDigest
344
347
  ) {
345
348
  return;
346
349
  }
@@ -354,7 +357,8 @@ function assertSameInvocation(
354
357
  if (
355
358
  current.description === next.description &&
356
359
  current.subagentType === next.subagentType &&
357
- current.configId === next.configId
360
+ current.configId === next.configId &&
361
+ current.hostArgsDigest === next.hostArgsDigest
358
362
  ) {
359
363
  return;
360
364
  }
@@ -425,6 +429,7 @@ export class SubagentExecutionRecord<
425
429
  TSettledOutput = never,
426
430
  > {
427
431
  private identityValue?: SubagentExecutionIdentity;
432
+ private attemptedIdentityValue?: SubagentExecutionIdentity;
428
433
  private bindingValue?: SubagentDefinitionBinding;
429
434
  private bindingAuthority?: 'provisional' | 'effective';
430
435
  private invocationValue?: SubagentInvocationBinding;
@@ -488,6 +493,24 @@ export class SubagentExecutionRecord<
488
493
  return this.identityValue;
489
494
  }
490
495
 
496
+ /** Correlation only; never committed identity or resume authority. */
497
+ get attemptedIdentity(): SubagentExecutionIdentity | undefined {
498
+ return this.attemptedIdentityValue;
499
+ }
500
+
501
+ recordIdentityAttempt(identity: SubagentExecutionIdentity): void {
502
+ const current = this.attemptedIdentityValue;
503
+ if (
504
+ current != null &&
505
+ current.childRunId === identity.childRunId &&
506
+ current.childThreadId === identity.childThreadId &&
507
+ current.approvalExecutionScope === identity.approvalExecutionScope
508
+ ) {
509
+ return;
510
+ }
511
+ this.attemptedIdentityValue = Object.freeze({ ...identity });
512
+ }
513
+
491
514
  get binding(): SubagentDefinitionBinding | undefined {
492
515
  return this.bindingValue;
493
516
  }
@@ -570,6 +593,7 @@ export class SubagentExecutionRecord<
570
593
  if (this.pendingIdentityResolution != null) {
571
594
  return this.pendingIdentityResolution;
572
595
  }
596
+ this.attemptedIdentityValue = undefined;
573
597
  const pending = Promise.resolve()
574
598
  .then(() => {
575
599
  this.assertCurrentIdentityResolution(pending);
@@ -929,6 +953,7 @@ export class SubagentExecutionRegistry<
929
953
  if (this.recordsByAddress.get(record.address.key) !== record) {
930
954
  throw new SubagentExecutionInvalidatedError();
931
955
  }
956
+ record.recordIdentityAttempt(identity);
932
957
  const checkpointKey = getPreparationResourceKey(
933
958
  'checkpoint',
934
959
  identity.childThreadId