@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
@@ -60,6 +60,7 @@ import type {
60
60
  SubagentTaskConfig,
61
61
  SubagentTaskRuntime,
62
62
  SubagentResolveConfigurable,
63
+ SubagentHostArgs,
63
64
  SubagentResolveRequestContext,
64
65
  SubagentResolveUserContext,
65
66
  StepCompleted,
@@ -80,6 +81,12 @@ import type {
80
81
  SubagentExecutionPreparationLease,
81
82
  SubagentExecutionRecord,
82
83
  } from './SubagentExecutionRegistry';
84
+ import type {
85
+ SubagentResolutionPhase,
86
+ SubagentResolutionCause,
87
+ SubagentHostArgumentFailure,
88
+ SubagentResolutionFailureHandler,
89
+ } from './diagnostics';
83
90
  import type {
84
91
  SubagentResumeExecution,
85
92
  SubagentResumeManifest,
@@ -92,6 +99,7 @@ import type {
92
99
  PreemptBoundaryHookOutput,
93
100
  ToolApprovalReplaySnapshot,
94
101
  } from '@/hooks';
102
+ import type { RunBreakerScope } from '@/llm/streamLimits';
95
103
  import type { GraphFactory } from '@/graphs/graphFactory';
96
104
  import type { StandardGraph } from '@/graphs/Graph';
97
105
  import {
@@ -108,6 +116,18 @@ import {
108
116
  SUBAGENT_RESUME_ATTEMPT_CONFIG_KEY,
109
117
  SUBAGENT_RESUME_MANIFEST_CONFIG_KEY,
110
118
  } from './SubagentReplay';
119
+ import {
120
+ logSubagentResolutionFailure,
121
+ SubagentResolutionError,
122
+ getSubagentHostArgumentFailure,
123
+ getSubagentHostArgumentFailureMessage,
124
+ } from './diagnostics';
125
+ import {
126
+ getSubagentHostArgSpecs,
127
+ resolveSubagentHostArgs,
128
+ pickSubagentHostArgInput,
129
+ getSubagentHostArgsDigest,
130
+ } from './hostArgs';
111
131
  import {
112
132
  rebindToolBatchReplayScope,
113
133
  rebindToolBatchReplayPayload,
@@ -137,11 +157,11 @@ import {
137
157
  createChildGraphPlan,
138
158
  isGraphSubagentConfig,
139
159
  } from './childGraphConfig';
160
+ import { ProviderTextProtectionError } from '@/protection/providerText';
140
161
  import { stripRunStepResumeState } from '@/tools/runStepResume';
141
162
  import { seedAgentInitialSessions } from '@/utils/toolSessions';
142
163
  import { stableStringify } from '@/tools/eagerEventExecution';
143
164
  import { convertInjectedMessages } from '@/messages/injected';
144
- import { logSubagentResolutionFailure } from './diagnostics';
145
165
  import { resolveClientOptionsModel } from '@/llm/request';
146
166
  import { isBackgroundDenyMode } from '@/types/hitl';
147
167
  import { composeAbortSignals } from '@/utils/misc';
@@ -162,13 +182,23 @@ const MAX_QUEUED_SUBAGENT_UPDATES = 64;
162
182
  const MAX_BACKGROUND_SUBAGENT_SEALS = 32;
163
183
  const SUBAGENT_UPDATE_HANDLER_TIMEOUT_MS = 5_000;
164
184
  const TEXT_DELTA_CONTENT_TYPE = `${ContentTypes.TEXT}_delta`;
165
- const SUBAGENT_RESOLUTION_ERROR_MESSAGE =
166
- 'Subagent error: Unable to initialize the selected subagent.';
167
185
  const SUBAGENT_CONFIG_CHANGED_MESSAGE =
168
186
  'Subagent error: Subagent configuration changed since this execution was paused.';
169
187
  const SUBAGENT_INVOCATION_CHANGED_MESSAGE =
170
188
  'Subagent error: Subagent invocation changed for this execution.';
171
189
 
190
+ function isSubagentResolutionControlFlow(error: unknown): boolean {
191
+ try {
192
+ return (
193
+ error instanceof StreamLimitExceededError ||
194
+ error instanceof ProviderTextProtectionError ||
195
+ isGraphInterrupt(error)
196
+ );
197
+ } catch {
198
+ return false;
199
+ }
200
+ }
201
+
172
202
  function seedChildGraphSessions(
173
203
  childGraph: StandardGraph,
174
204
  agents: AgentInputs[]
@@ -663,7 +693,8 @@ function getSettlementFingerprint(output: PersistedToolOutput): string {
663
693
  function getBackgroundTaskFingerprint(
664
694
  description: string,
665
695
  subagentType: string,
666
- threadId?: string
696
+ threadId?: string,
697
+ hostArgsDigest?: string
667
698
  ): string {
668
699
  return createHash('sha256')
669
700
  .update(
@@ -671,11 +702,29 @@ function getBackgroundTaskFingerprint(
671
702
  description,
672
703
  subagentType,
673
704
  ...(threadId == null || threadId === '' ? {} : { threadId }),
705
+ ...(hostArgsDigest == null ? {} : { hostArgsDigest }),
674
706
  })
675
707
  )
676
708
  .digest('hex');
677
709
  }
678
710
 
711
+ /** Host-argument digest of persisted lifecycle args, when they are well formed. */
712
+ function getHostArgsDigestFromArgs(
713
+ config: ExecutableSubagentConfigEntry | undefined,
714
+ args: unknown
715
+ ): string | undefined {
716
+ if (config == null || !isObjectLike(args)) {
717
+ return undefined;
718
+ }
719
+ const names = new Set(getSubagentHostArgSpecs(config).map(([name]) => name));
720
+ const picked = pickSubagentHostArgInput(args, names);
721
+ if (!picked.ok) {
722
+ return undefined;
723
+ }
724
+ const resolved = resolveSubagentHostArgs(config, picked.hostArgs);
725
+ return resolved.ok ? getSubagentHostArgsDigest(resolved.hostArgs) : undefined;
726
+ }
727
+
679
728
  function getBackgroundTaskHookSessionId(
680
729
  sourceHookSessionId: string,
681
730
  taskId: string
@@ -862,11 +911,22 @@ export type SubagentExecuteParams = {
862
911
  taskRuntime?: SubagentTaskRuntime;
863
912
  /** Host-restored child transcript for a fresh continuation run. @internal */
864
913
  initialMessages?: BaseMessage[];
914
+ /**
915
+ * Values for the selected subagent's declared `hostArgs`. Checked against
916
+ * the declaration before an execution opens, then bound to it.
917
+ */
918
+ hostArgs?: SubagentHostArgs;
865
919
  };
866
920
 
867
921
  export type SubagentExecuteResult = SubagentContextResult & {
868
922
  /** Tagged internal failure; foreground callers retain the legacy content. */
869
923
  error?: string;
924
+ /** Safe startup classification retained for detached host delivery. */
925
+ resolutionFailure?: {
926
+ phase: SubagentResolutionPhase;
927
+ cause: SubagentResolutionCause;
928
+ hostArgument?: SubagentHostArgumentFailure;
929
+ };
870
930
  /** Completed child work whose host projection must be retried without re-execution. */
871
931
  retryableDelivery?: true;
872
932
  };
@@ -937,6 +997,7 @@ export type SubagentExecutorOptions = {
937
997
  * graph's own resolved limits; without this the child would silently
938
998
  * revert to the defaults.
939
999
  */
1000
+ providerTextProtection?: StandardGraphInput['providerTextProtection'];
940
1001
  streamLimits?: StandardGraphInput['streamLimits'];
941
1002
  humanInTheLoop?: HumanInTheLoopConfig;
942
1003
  /** Shared durable saver used to recover outer tool lifecycle results before
@@ -986,6 +1047,8 @@ export type SubagentExecutorOptions = {
986
1047
  /** Host-owned process-local task namespace for detached execution. */
987
1048
  taskConfig?: SubagentTaskConfig;
988
1049
  subagentContext?: SubagentContextAdapter;
1050
+ /** Host diagnostic sink and safe cause classifier for startup failures. */
1051
+ onResolutionFailure?: SubagentResolutionFailureHandler;
989
1052
  };
990
1053
 
991
1054
  type DurableExecutionRecord = SubagentExecutionRecord<
@@ -1012,6 +1075,7 @@ export class SubagentExecutor {
1012
1075
  private readonly executionContext: SubagentExecutionContext;
1013
1076
  private readonly langfuse?: StandardGraphInput['langfuse'];
1014
1077
  private readonly tokenCounter?: TokenCounter;
1078
+ private readonly providerTextProtection?: StandardGraphInput['providerTextProtection'];
1015
1079
  private readonly streamLimits?: StandardGraphInput['streamLimits'];
1016
1080
  private readonly humanInTheLoop?: HumanInTheLoopConfig;
1017
1081
  private readonly checkpointer?: BaseCheckpointSaver;
@@ -1024,6 +1088,7 @@ export class SubagentExecutor {
1024
1088
  private readonly usageSink?: SubagentUsageSink;
1025
1089
  private readonly taskConfig?: SubagentTaskConfig;
1026
1090
  private readonly subagentContext?: SubagentContextAdapter;
1091
+ private readonly onResolutionFailure?: SubagentResolutionFailureHandler;
1027
1092
  private readonly executions: SubagentExecutionRegistry<
1028
1093
  SubagentExecuteResult,
1029
1094
  ResolvedSubagentConfig,
@@ -1051,6 +1116,7 @@ export class SubagentExecutor {
1051
1116
  this.langfuse = options.langfuse;
1052
1117
  this.tokenCounter = options.tokenCounter;
1053
1118
  this.streamLimits = options.streamLimits;
1119
+ this.providerTextProtection = options.providerTextProtection;
1054
1120
  this.humanInTheLoop = options.humanInTheLoop;
1055
1121
  this.checkpointer = isCheckpointSaver(options.checkpointer)
1056
1122
  ? options.checkpointer
@@ -1069,6 +1135,7 @@ export class SubagentExecutor {
1069
1135
  this.usageSink = options.usageSink;
1070
1136
  this.taskConfig = options.taskConfig;
1071
1137
  this.subagentContext = options.subagentContext;
1138
+ this.onResolutionFailure = options.onResolutionFailure;
1072
1139
  const rawRegistry = options.parentHandlerRegistry;
1073
1140
  if (typeof rawRegistry === 'function') {
1074
1141
  this.resolveParentHandlerRegistry = rawRegistry;
@@ -1124,6 +1191,18 @@ export class SubagentExecutor {
1124
1191
  message: `Unknown subagent type "${params.subagentType}".`,
1125
1192
  });
1126
1193
  }
1194
+ const hostArgResult = resolveSubagentHostArgs(
1195
+ executableConfig,
1196
+ params.hostArgs
1197
+ );
1198
+ if (!hostArgResult.ok) {
1199
+ return JSON.stringify({
1200
+ status: 'rejected',
1201
+ tool: Constants.SUBAGENT,
1202
+ message: hostArgResult.message,
1203
+ });
1204
+ }
1205
+ const { hostArgs } = hostArgResult;
1127
1206
  if (this.maxDepth <= 0) {
1128
1207
  return JSON.stringify({
1129
1208
  status: 'rejected',
@@ -1189,7 +1268,8 @@ export class SubagentExecutor {
1189
1268
  requestFingerprint: getBackgroundTaskFingerprint(
1190
1269
  params.description,
1191
1270
  params.subagentType,
1192
- subagentThreadId
1271
+ subagentThreadId,
1272
+ getSubagentHostArgsDigest(hostArgs)
1193
1273
  ),
1194
1274
  ...(subagentThreadId == null || subagentThreadId === ''
1195
1275
  ? {}
@@ -1204,6 +1284,7 @@ export class SubagentExecutor {
1204
1284
  this.executeDetached(
1205
1285
  {
1206
1286
  ...params,
1287
+ hostArgs,
1207
1288
  ...(initialMessages == null ? {} : { initialMessages }),
1208
1289
  },
1209
1290
  runtime,
@@ -1318,7 +1399,9 @@ export class SubagentExecutor {
1318
1399
  tokenCounter: this.tokenCounter,
1319
1400
  usageSink: this.usageSink,
1320
1401
  subagentContext: this.subagentContext,
1402
+ onResolutionFailure: this.onResolutionFailure,
1321
1403
  streamLimits: this.streamLimits,
1404
+ providerTextProtection: this.providerTextProtection,
1322
1405
  humanInTheLoop:
1323
1406
  this.humanInTheLoop?.enabled === true ||
1324
1407
  this.humanInTheLoop?.backgroundPausePolicy === 'deny'
@@ -1369,6 +1452,13 @@ export class SubagentExecutor {
1369
1452
  result = await executeAttempt();
1370
1453
  }
1371
1454
  if (result.error != null) {
1455
+ if (result.resolutionFailure != null) {
1456
+ throw new SubagentResolutionError(
1457
+ result.resolutionFailure.phase,
1458
+ result.resolutionFailure.cause,
1459
+ result.resolutionFailure.hostArgument
1460
+ );
1461
+ }
1372
1462
  throw new Error(result.error);
1373
1463
  }
1374
1464
  if (this.humanInTheLoop?.enabled === true) {
@@ -1430,6 +1520,7 @@ export class SubagentExecutor {
1430
1520
  threadId?: string;
1431
1521
  parentToolCallId?: string;
1432
1522
  parentConfigurable?: Record<string, unknown>;
1523
+ hostArgs?: SubagentHostArgs;
1433
1524
  }
1434
1525
  ): Promise<ResolvedSubagentConfigEntry> {
1435
1526
  if (isGraphSubagentConfig(config) || config.agentInputs != null) {
@@ -1458,6 +1549,9 @@ export class SubagentExecutor {
1458
1549
  context.parentConfigurable == null
1459
1550
  ? undefined
1460
1551
  : sanitizeResolverConfigurable(context.parentConfigurable),
1552
+ ...(context.hostArgs == null
1553
+ ? {}
1554
+ : { hostArgs: context.hostArgs }),
1461
1555
  }),
1462
1556
  context.childSignal
1463
1557
  ).then(
@@ -1662,13 +1756,6 @@ export class SubagentExecutor {
1662
1756
  ),
1663
1757
  });
1664
1758
  }
1665
- const sourceCheckpoints =
1666
- await this.getLatestCheckpointSnapshot(baseChildThreadId);
1667
- if (sourceCheckpoints.length === 0) {
1668
- throw new Error(
1669
- `Cannot fork subagent checkpoint thread "${baseChildThreadId}" without a checkpoint ID.`
1670
- );
1671
- }
1672
1759
  const childRunId = persistedChildRunId ?? currentChildRunId;
1673
1760
  const identity = {
1674
1761
  childRunId,
@@ -1678,6 +1765,14 @@ export class SubagentExecutor {
1678
1765
  resumeAttemptId
1679
1766
  ),
1680
1767
  };
1768
+ execution.recordIdentityAttempt(identity);
1769
+ const sourceCheckpoints =
1770
+ await this.getLatestCheckpointSnapshot(baseChildThreadId);
1771
+ if (sourceCheckpoints.length === 0) {
1772
+ throw new Error(
1773
+ `Cannot fork subagent checkpoint thread "${baseChildThreadId}" without a checkpoint ID.`
1774
+ );
1775
+ }
1681
1776
  const lease = this.executions.beginIdentityPreparation(execution, identity);
1682
1777
  await this.prepareCheckpointFork(
1683
1778
  sourceCheckpoints,
@@ -1905,7 +2000,7 @@ export class SubagentExecutor {
1905
2000
  }
1906
2001
  const executions: SubagentResumeExecution[] = [];
1907
2002
  for (const record of records) {
1908
- const { identity, binding, activeRun } = record.snapshot;
2003
+ const { identity, binding, invocation, activeRun } = record.snapshot;
1909
2004
  if (identity == null) {
1910
2005
  continue;
1911
2006
  }
@@ -1949,6 +2044,9 @@ export class SubagentExecutor {
1949
2044
  ? {}
1950
2045
  : { subagentType: binding.subagentType }),
1951
2046
  ...(binding?.configId == null ? {} : { configId: binding.configId }),
2047
+ ...(invocation?.hostArgsDigest == null
2048
+ ? {}
2049
+ : { hostArgsDigest: invocation.hostArgsDigest }),
1952
2050
  approvalExecutionScope: identity.approvalExecutionScope,
1953
2051
  checkpoints,
1954
2052
  graphState,
@@ -2083,9 +2181,10 @@ export class SubagentExecutor {
2083
2181
  config: RunnableConfig
2084
2182
  ): Promise<SettledSubagentToolOutput | undefined> {
2085
2183
  const parentToolCallId = call.id;
2184
+ const checkpointer = this.checkpointer;
2086
2185
  if (
2087
2186
  this.humanInTheLoop?.enabled !== true ||
2088
- this.checkpointer == null ||
2187
+ checkpointer == null ||
2089
2188
  parentToolCallId == null ||
2090
2189
  parentToolCallId === ''
2091
2190
  ) {
@@ -2100,6 +2199,44 @@ export class SubagentExecutor {
2100
2199
  parentToolCallId,
2101
2200
  parentConfigurable,
2102
2201
  });
2202
+ const signal = this.getReplaySignal(config);
2203
+ try {
2204
+ return await this.restoreSettledToolOutput(
2205
+ call,
2206
+ parentConfigurable,
2207
+ parentToolCallId,
2208
+ execution,
2209
+ checkpointer
2210
+ );
2211
+ } catch (error) {
2212
+ if (isSubagentResolutionControlFlow(error)) throw error;
2213
+ const failure = this.createReplayResolutionError(
2214
+ call,
2215
+ config,
2216
+ execution,
2217
+ signal,
2218
+ error
2219
+ );
2220
+ if (signal.aborted) throw failure;
2221
+ return {
2222
+ output: new ToolMessage({
2223
+ content: failure.message,
2224
+ name: call.name,
2225
+ tool_call_id: parentToolCallId,
2226
+ status: 'error',
2227
+ }),
2228
+ additionalContexts: [],
2229
+ };
2230
+ }
2231
+ }
2232
+
2233
+ private async restoreSettledToolOutput(
2234
+ call: ToolCall,
2235
+ parentConfigurable: Record<string, unknown> | undefined,
2236
+ parentToolCallId: string,
2237
+ execution: DurableExecutionRecord,
2238
+ checkpointer: BaseCheckpointSaver
2239
+ ): Promise<SettledSubagentToolOutput | undefined> {
2103
2240
  const { resumeExecution } = execution;
2104
2241
  const inProcessSettledOutput = execution.settledOutput;
2105
2242
  if (inProcessSettledOutput != null) {
@@ -2158,7 +2295,7 @@ export class SubagentExecutor {
2158
2295
  const { childThreadId } =
2159
2296
  await this.resolveChildExecutionIdentity(execution);
2160
2297
  if (marker == null) {
2161
- const checkpoint = await this.checkpointer.getTuple({
2298
+ const checkpoint = await checkpointer.getTuple({
2162
2299
  configurable: { thread_id: childThreadId },
2163
2300
  });
2164
2301
  messages = getCheckpointMessages(
@@ -2262,6 +2399,22 @@ export class SubagentExecutor {
2262
2399
  ) {
2263
2400
  return;
2264
2401
  }
2402
+ const resolvedHostArgsDigest =
2403
+ settled.resolvedArgs == null
2404
+ ? undefined
2405
+ : getHostArgsDigestFromArgs(
2406
+ this.configs.get(
2407
+ resolvedSubagentType ?? execution.binding?.subagentType ?? ''
2408
+ ),
2409
+ settled.resolvedArgs
2410
+ );
2411
+ if (
2412
+ boundInvocation != null &&
2413
+ settled.resolvedArgs != null &&
2414
+ resolvedHostArgsDigest !== boundInvocation.hostArgsDigest
2415
+ ) {
2416
+ return;
2417
+ }
2265
2418
  const description =
2266
2419
  boundInvocation?.description ??
2267
2420
  resolvedDescription ??
@@ -2293,10 +2446,15 @@ export class SubagentExecutor {
2293
2446
  return;
2294
2447
  }
2295
2448
  const persistedOutput = serializeToolOutput(settled);
2449
+ const hostArgsDigest =
2450
+ settled.resolvedArgs == null
2451
+ ? getHostArgsDigestFromArgs(executableConfig, call.args)
2452
+ : resolvedHostArgsDigest;
2296
2453
  const invocation = boundInvocation ?? {
2297
2454
  description,
2298
2455
  subagentType,
2299
2456
  ...(configId == null ? {} : { configId }),
2457
+ ...(hostArgsDigest == null ? {} : { hostArgsDigest }),
2300
2458
  };
2301
2459
  try {
2302
2460
  await execution.settle(
@@ -2310,8 +2468,20 @@ export class SubagentExecutor {
2310
2468
  },
2311
2469
  persistedOutput,
2312
2470
  async (): Promise<void> => {
2313
- const { childRunId, childThreadId } =
2314
- await this.resolveChildExecutionIdentity(execution);
2471
+ let identity: SubagentExecutionIdentity;
2472
+ try {
2473
+ identity = await this.resolveChildExecutionIdentity(execution);
2474
+ } catch (error) {
2475
+ if (isSubagentResolutionControlFlow(error)) throw error;
2476
+ throw this.createReplayResolutionError(
2477
+ call,
2478
+ config,
2479
+ execution,
2480
+ this.getReplaySignal(config),
2481
+ error
2482
+ );
2483
+ }
2484
+ const { childRunId, childThreadId } = identity;
2315
2485
  const activeChildRun = execution.activeRun;
2316
2486
  if (activeChildRun != null) {
2317
2487
  await this.persistChildCheckpointMarker(
@@ -2401,6 +2571,15 @@ export class SubagentExecutor {
2401
2571
  )
2402
2572
  );
2403
2573
  }
2574
+ const hostArgResult = resolveSubagentHostArgs(
2575
+ executableConfig,
2576
+ params.hostArgs
2577
+ );
2578
+ if (!hostArgResult.ok) {
2579
+ return Promise.resolve(createSubagentFailure(hostArgResult.message));
2580
+ }
2581
+ const { hostArgs } = hostArgResult;
2582
+ const hostArgsDigest = getSubagentHostArgsDigest(hostArgs);
2404
2583
  if (this.maxDepth <= 0) {
2405
2584
  return Promise.resolve(
2406
2585
  createSubagentFailure('Error: Maximum subagent nesting depth exceeded.')
@@ -2439,8 +2618,10 @@ export class SubagentExecutor {
2439
2618
  ...(executableConfig.configId == null
2440
2619
  ? {}
2441
2620
  : { configId: executableConfig.configId }),
2621
+ ...(hostArgsDigest == null ? {} : { hostArgsDigest }),
2442
2622
  },
2443
- () => this.executeOnce(params, execution, executableConfig)
2623
+ () =>
2624
+ this.executeOnce({ ...params, hostArgs }, execution, executableConfig)
2444
2625
  );
2445
2626
  } catch (error) {
2446
2627
  if (error instanceof SubagentDefinitionBindingError) {
@@ -2457,6 +2638,103 @@ export class SubagentExecutor {
2457
2638
  }
2458
2639
  }
2459
2640
 
2641
+ private getReplaySignal(config: RunnableConfig): AbortSignal {
2642
+ const scope = config.configurable?.[RUN_BREAKER_SCOPE_CONFIG_KEY] as
2643
+ | RunBreakerScope
2644
+ | undefined;
2645
+ return this.composeChildSignal(
2646
+ scope?.controller ?? this.resolveBreakerController(),
2647
+ config.signal
2648
+ );
2649
+ }
2650
+
2651
+ private createReplayResolutionError(
2652
+ call: ToolCall,
2653
+ config: RunnableConfig,
2654
+ execution: DurableExecutionRecord,
2655
+ signal: AbortSignal,
2656
+ error: unknown
2657
+ ): SubagentResolutionError {
2658
+ const selectedType =
2659
+ execution.binding?.subagentType ??
2660
+ execution.resumeExecution?.subagentType ??
2661
+ getSubagentType(call);
2662
+ const threadId = config.configurable?.thread_id;
2663
+ const failure = this.createResolutionFailure(
2664
+ 'identity',
2665
+ {
2666
+ subagentType:
2667
+ selectedType == null
2668
+ ? 'unknown'
2669
+ : (this.configs.get(selectedType)?.type ?? 'unknown'),
2670
+ description: DEFAULT_SUBAGENT_DESCRIPTION,
2671
+ parentToolCallId: call.id,
2672
+ threadId: typeof threadId === 'string' ? threadId : undefined,
2673
+ },
2674
+ execution,
2675
+ signal,
2676
+ error
2677
+ );
2678
+ return new SubagentResolutionError(
2679
+ 'identity',
2680
+ failure.resolutionFailure?.cause ?? 'unknown'
2681
+ );
2682
+ }
2683
+
2684
+ private createResolutionFailure(
2685
+ phase: SubagentResolutionPhase,
2686
+ params: SubagentExecuteParams,
2687
+ execution: DurableExecutionRecord,
2688
+ signal: AbortSignal,
2689
+ error: unknown
2690
+ ): SubagentExecuteResult {
2691
+ const identity = execution.identity ?? execution.attemptedIdentity;
2692
+ const resumeExecution =
2693
+ this.humanInTheLoop?.enabled === true && this.checkpointer != null
2694
+ ? execution.resumeExecution
2695
+ : undefined;
2696
+ const detail = logSubagentResolutionFailure(
2697
+ phase,
2698
+ params.subagentType,
2699
+ signal,
2700
+ error,
2701
+ {
2702
+ parentRunId: this.parentRunId,
2703
+ parentAgentId: this.parentAgentId,
2704
+ parentToolCallId: params.parentToolCallId,
2705
+ threadId: params.threadId,
2706
+ childRunId:
2707
+ identity?.childRunId ??
2708
+ resumeExecution?.childRunId ??
2709
+ execution.address.currentChildRunId,
2710
+ childThreadId:
2711
+ identity?.childThreadId ??
2712
+ (resumeExecution == null
2713
+ ? execution.address.baseChildThreadId
2714
+ : execution.address.branchChildThreadId),
2715
+ taskId: params.taskRuntime?.taskId,
2716
+ },
2717
+ this.onResolutionFailure,
2718
+ params.hostArgs
2719
+ );
2720
+ const hostArgument =
2721
+ phase === 'config'
2722
+ ? getSubagentHostArgumentFailure(error, params.hostArgs)
2723
+ : undefined;
2724
+ if (hostArgument != null) {
2725
+ return {
2726
+ ...createSubagentFailure(
2727
+ getSubagentHostArgumentFailureMessage(hostArgument)
2728
+ ),
2729
+ resolutionFailure: { phase, cause: detail.cause, hostArgument },
2730
+ };
2731
+ }
2732
+ return {
2733
+ ...createSubagentFailure(detail.message),
2734
+ resolutionFailure: { phase, cause: detail.cause },
2735
+ };
2736
+ }
2737
+
2460
2738
  private async executeOnce(
2461
2739
  params: SubagentExecuteParams,
2462
2740
  execution: DurableExecutionRecord,
@@ -2485,21 +2763,29 @@ export class SubagentExecutor {
2485
2763
  this.executions.remove(execution);
2486
2764
  return createSubagentFailure(SUBAGENT_CONFIG_CHANGED_MESSAGE);
2487
2765
  }
2766
+ if (
2767
+ resumeExecution != null &&
2768
+ resumeExecution.hostArgsDigest !==
2769
+ getSubagentHostArgsDigest(params.hostArgs)
2770
+ ) {
2771
+ this.executions.remove(execution);
2772
+ return createSubagentFailure(SUBAGENT_INVOCATION_CHANGED_MESSAGE);
2773
+ }
2488
2774
  let identity: SubagentExecutionIdentity;
2489
2775
  try {
2490
2776
  identity = await this.resolveChildExecutionIdentity(execution);
2491
2777
  execution.assertUsable(childSignal);
2492
2778
  } catch (error) {
2493
- if (error instanceof StreamLimitExceededError) {
2779
+ if (isSubagentResolutionControlFlow(error)) {
2494
2780
  throw error;
2495
2781
  }
2496
- logSubagentResolutionFailure(
2782
+ return this.createResolutionFailure(
2497
2783
  'identity',
2498
- executableConfig.type,
2784
+ params,
2785
+ execution,
2499
2786
  childSignal,
2500
2787
  error
2501
2788
  );
2502
- return createSubagentFailure(SUBAGENT_RESOLUTION_ERROR_MESSAGE);
2503
2789
  }
2504
2790
  const { childRunId, childThreadId, approvalExecutionScope } = identity;
2505
2791
  const bound = this.bindExecutionDefinition(
@@ -2523,19 +2809,20 @@ export class SubagentExecutor {
2523
2809
  threadId,
2524
2810
  parentToolCallId,
2525
2811
  parentConfigurable: params.parentConfigurable,
2812
+ hostArgs: params.hostArgs,
2526
2813
  });
2527
2814
  execution.assertUsable(childSignal);
2528
2815
  } catch (error) {
2529
- if (error instanceof StreamLimitExceededError) {
2816
+ if (isSubagentResolutionControlFlow(error)) {
2530
2817
  throw error;
2531
2818
  }
2532
- logSubagentResolutionFailure(
2819
+ return this.createResolutionFailure(
2533
2820
  'config',
2534
- executableConfig.type,
2821
+ params,
2822
+ execution,
2535
2823
  childSignal,
2536
2824
  error
2537
2825
  );
2538
- return createSubagentFailure(SUBAGENT_RESOLUTION_ERROR_MESSAGE);
2539
2826
  }
2540
2827
 
2541
2828
  const parentRegistry = this.getParentHandlerRegistry();
@@ -2664,9 +2951,11 @@ export class SubagentExecutor {
2664
2951
  langfuse: this.langfuse,
2665
2952
  tokenCounter: this.tokenCounter,
2666
2953
  streamLimits: this.streamLimits,
2954
+ providerTextProtection: this.providerTextProtection,
2667
2955
  subagentScope: true,
2668
2956
  subagentExecutionContext: childExecutionContext,
2669
2957
  subagentContext: this.subagentContext,
2958
+ onSubagentResolutionFailure: this.onResolutionFailure,
2670
2959
  ...(resumeExecution?.graphState.fadingTier == null
2671
2960
  ? {}
2672
2961
  : { fadingTier: resumeExecution.graphState.fadingTier }),
@@ -3109,7 +3398,10 @@ export class SubagentExecutor {
3109
3398
  * quota for that entire interval. Trips the ENTRY-captured controller:
3110
3399
  * after a reset, a straggler must break its own dead run, not the
3111
3400
  * current one. */
3112
- if (error instanceof StreamLimitExceededError) {
3401
+ if (
3402
+ error instanceof StreamLimitExceededError ||
3403
+ error instanceof ProviderTextProtectionError
3404
+ ) {
3113
3405
  childBreaker.abort(error);
3114
3406
  }
3115
3407
  const errorMessage = truncateErrorMessage(error);
@@ -3148,7 +3440,10 @@ export class SubagentExecutor {
3148
3440
  * fired. Rethrown here and passed through ToolNode's error conversion,
3149
3441
  * so the parent run rejects with the child's limit error.
3150
3442
  */
3151
- if (error instanceof StreamLimitExceededError) {
3443
+ if (
3444
+ error instanceof StreamLimitExceededError ||
3445
+ error instanceof ProviderTextProtectionError
3446
+ ) {
3152
3447
  throw error;
3153
3448
  }
3154
3449
  return createSubagentFailure(