@librechat/agents 3.1.86 → 3.1.88

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 (160) hide show
  1. package/README.md +69 -0
  2. package/dist/cjs/events.cjs +23 -0
  3. package/dist/cjs/events.cjs.map +1 -1
  4. package/dist/cjs/graphs/Graph.cjs +133 -18
  5. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  6. package/dist/cjs/graphs/MultiAgentGraph.cjs +1 -1
  7. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  8. package/dist/cjs/llm/anthropic/index.cjs +251 -53
  9. package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
  10. package/dist/cjs/llm/init.cjs +1 -5
  11. package/dist/cjs/llm/init.cjs.map +1 -1
  12. package/dist/cjs/llm/openai/index.cjs +113 -24
  13. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  14. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
  15. package/dist/cjs/llm/openrouter/index.cjs +3 -1
  16. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  17. package/dist/cjs/main.cjs +18 -5
  18. package/dist/cjs/main.cjs.map +1 -1
  19. package/dist/cjs/openai/index.cjs +253 -0
  20. package/dist/cjs/openai/index.cjs.map +1 -0
  21. package/dist/cjs/responses/index.cjs +448 -0
  22. package/dist/cjs/responses/index.cjs.map +1 -0
  23. package/dist/cjs/run.cjs +108 -7
  24. package/dist/cjs/run.cjs.map +1 -1
  25. package/dist/cjs/session/AgentSession.cjs +1057 -0
  26. package/dist/cjs/session/AgentSession.cjs.map +1 -0
  27. package/dist/cjs/session/JsonlSessionStore.cjs +425 -0
  28. package/dist/cjs/session/JsonlSessionStore.cjs.map +1 -0
  29. package/dist/cjs/session/handlers.cjs +221 -0
  30. package/dist/cjs/session/handlers.cjs.map +1 -0
  31. package/dist/cjs/session/ids.cjs +22 -0
  32. package/dist/cjs/session/ids.cjs.map +1 -0
  33. package/dist/cjs/session/messageSerialization.cjs +179 -0
  34. package/dist/cjs/session/messageSerialization.cjs.map +1 -0
  35. package/dist/cjs/stream.cjs +475 -11
  36. package/dist/cjs/stream.cjs.map +1 -1
  37. package/dist/cjs/summarization/node.cjs +1 -1
  38. package/dist/cjs/summarization/node.cjs.map +1 -1
  39. package/dist/cjs/tools/ToolNode.cjs +177 -59
  40. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  41. package/dist/cjs/tools/eagerEventExecution.cjs +113 -0
  42. package/dist/cjs/tools/eagerEventExecution.cjs.map +1 -0
  43. package/dist/cjs/tools/handlers.cjs +1 -1
  44. package/dist/cjs/tools/handlers.cjs.map +1 -1
  45. package/dist/cjs/tools/streamedToolCallSeals.cjs +42 -0
  46. package/dist/cjs/tools/streamedToolCallSeals.cjs.map +1 -0
  47. package/dist/esm/events.mjs +23 -1
  48. package/dist/esm/events.mjs.map +1 -1
  49. package/dist/esm/graphs/Graph.mjs +133 -18
  50. package/dist/esm/graphs/Graph.mjs.map +1 -1
  51. package/dist/esm/graphs/MultiAgentGraph.mjs +1 -1
  52. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  53. package/dist/esm/llm/anthropic/index.mjs +251 -53
  54. package/dist/esm/llm/anthropic/index.mjs.map +1 -1
  55. package/dist/esm/llm/init.mjs +1 -5
  56. package/dist/esm/llm/init.mjs.map +1 -1
  57. package/dist/esm/llm/openai/index.mjs +113 -25
  58. package/dist/esm/llm/openai/index.mjs.map +1 -1
  59. package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
  60. package/dist/esm/llm/openrouter/index.mjs +4 -2
  61. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  62. package/dist/esm/main.mjs +5 -1
  63. package/dist/esm/main.mjs.map +1 -1
  64. package/dist/esm/openai/index.mjs +246 -0
  65. package/dist/esm/openai/index.mjs.map +1 -0
  66. package/dist/esm/responses/index.mjs +440 -0
  67. package/dist/esm/responses/index.mjs.map +1 -0
  68. package/dist/esm/run.mjs +108 -7
  69. package/dist/esm/run.mjs.map +1 -1
  70. package/dist/esm/session/AgentSession.mjs +1054 -0
  71. package/dist/esm/session/AgentSession.mjs.map +1 -0
  72. package/dist/esm/session/JsonlSessionStore.mjs +422 -0
  73. package/dist/esm/session/JsonlSessionStore.mjs.map +1 -0
  74. package/dist/esm/session/handlers.mjs +219 -0
  75. package/dist/esm/session/handlers.mjs.map +1 -0
  76. package/dist/esm/session/ids.mjs +17 -0
  77. package/dist/esm/session/ids.mjs.map +1 -0
  78. package/dist/esm/session/messageSerialization.mjs +173 -0
  79. package/dist/esm/session/messageSerialization.mjs.map +1 -0
  80. package/dist/esm/stream.mjs +476 -12
  81. package/dist/esm/stream.mjs.map +1 -1
  82. package/dist/esm/summarization/node.mjs +1 -1
  83. package/dist/esm/summarization/node.mjs.map +1 -1
  84. package/dist/esm/tools/ToolNode.mjs +177 -59
  85. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  86. package/dist/esm/tools/eagerEventExecution.mjs +107 -0
  87. package/dist/esm/tools/eagerEventExecution.mjs.map +1 -0
  88. package/dist/esm/tools/handlers.mjs +1 -1
  89. package/dist/esm/tools/handlers.mjs.map +1 -1
  90. package/dist/esm/tools/streamedToolCallSeals.mjs +36 -0
  91. package/dist/esm/tools/streamedToolCallSeals.mjs.map +1 -0
  92. package/dist/types/events.d.ts +1 -0
  93. package/dist/types/graphs/Graph.d.ts +24 -9
  94. package/dist/types/index.d.ts +1 -0
  95. package/dist/types/llm/openai/index.d.ts +1 -0
  96. package/dist/types/openai/index.d.ts +75 -0
  97. package/dist/types/responses/index.d.ts +97 -0
  98. package/dist/types/run.d.ts +2 -0
  99. package/dist/types/session/AgentSession.d.ts +32 -0
  100. package/dist/types/session/JsonlSessionStore.d.ts +67 -0
  101. package/dist/types/session/handlers.d.ts +8 -0
  102. package/dist/types/session/ids.d.ts +4 -0
  103. package/dist/types/session/index.d.ts +5 -0
  104. package/dist/types/session/messageSerialization.d.ts +7 -0
  105. package/dist/types/session/types.d.ts +191 -0
  106. package/dist/types/tools/ToolNode.d.ts +12 -1
  107. package/dist/types/tools/eagerEventExecution.d.ts +23 -0
  108. package/dist/types/tools/streamedToolCallSeals.d.ts +13 -0
  109. package/dist/types/types/hitl.d.ts +4 -0
  110. package/dist/types/types/run.d.ts +11 -1
  111. package/dist/types/types/tools.d.ts +36 -0
  112. package/package.json +19 -2
  113. package/src/__tests__/stream.eagerEventExecution.test.ts +2571 -0
  114. package/src/events.ts +29 -0
  115. package/src/graphs/Graph.ts +224 -50
  116. package/src/graphs/MultiAgentGraph.ts +1 -1
  117. package/src/graphs/__tests__/composition.smoke.test.ts +30 -0
  118. package/src/index.ts +3 -0
  119. package/src/llm/anthropic/index.ts +356 -84
  120. package/src/llm/anthropic/llm.spec.ts +64 -0
  121. package/src/llm/custom-chat-models.smoke.test.ts +175 -4
  122. package/src/llm/openai/contentBlocks.test.ts +35 -0
  123. package/src/llm/openai/deepseek.test.ts +201 -2
  124. package/src/llm/openai/index.ts +171 -26
  125. package/src/llm/openai/utils/index.ts +22 -0
  126. package/src/llm/openrouter/index.ts +4 -2
  127. package/src/openai/__tests__/openai.test.ts +337 -0
  128. package/src/openai/index.ts +404 -0
  129. package/src/responses/__tests__/responses.test.ts +652 -0
  130. package/src/responses/index.ts +677 -0
  131. package/src/run.ts +158 -8
  132. package/src/scripts/compare_pi_vs_ours.ts +592 -173
  133. package/src/scripts/session_live.ts +548 -0
  134. package/src/session/AgentSession.ts +1432 -0
  135. package/src/session/JsonlSessionStore.ts +572 -0
  136. package/src/session/__tests__/JsonlSessionStore.test.ts +1410 -0
  137. package/src/session/__tests__/handlers.test.ts +161 -0
  138. package/src/session/handlers.ts +272 -0
  139. package/src/session/ids.ts +17 -0
  140. package/src/session/index.ts +44 -0
  141. package/src/session/messageSerialization.ts +207 -0
  142. package/src/session/types.ts +275 -0
  143. package/src/specs/custom-event-await.test.ts +89 -0
  144. package/src/specs/summarization.test.ts +1 -1
  145. package/src/stream.ts +756 -48
  146. package/src/summarization/node.ts +1 -1
  147. package/src/tools/ToolNode.ts +299 -126
  148. package/src/tools/__tests__/ToolNode.eagerEventExecution.test.ts +373 -0
  149. package/src/tools/__tests__/handlers.test.ts +2 -1
  150. package/src/tools/__tests__/hitl.test.ts +206 -110
  151. package/src/tools/eagerEventExecution.ts +153 -0
  152. package/src/tools/handlers.ts +8 -4
  153. package/src/tools/streamedToolCallSeals.ts +57 -0
  154. package/src/types/hitl.ts +4 -0
  155. package/src/types/run.ts +11 -0
  156. package/src/types/tools.ts +36 -0
  157. package/dist/cjs/llm/text.cjs +0 -69
  158. package/dist/cjs/llm/text.cjs.map +0 -1
  159. package/dist/esm/llm/text.mjs +0 -67
  160. package/dist/esm/llm/text.mjs.map +0 -1
@@ -5,9 +5,12 @@ var handlers = require('./tools/handlers.cjs');
5
5
  require('./messages/core.cjs');
6
6
  var ids = require('./messages/ids.cjs');
7
7
  require('@langchain/core/messages');
8
- require('@langchain/core/callbacks/dispatch');
8
+ var events = require('./utils/events.cjs');
9
9
  require('uuid');
10
+ var eagerEventExecution = require('./tools/eagerEventExecution.cjs');
11
+ var streamedToolCallSeals = require('./tools/streamedToolCallSeals.cjs');
10
12
 
13
+ const LOCAL_CODING_BUNDLE_NAME_SET = new Set(_enum.LOCAL_CODING_BUNDLE_NAMES);
11
14
  /**
12
15
  * Parses content to extract thinking sections enclosed in <think> tags using string operations
13
16
  * @param content The content to parse
@@ -55,6 +58,431 @@ function getNonEmptyValue(possibleValues) {
55
58
  }
56
59
  return undefined;
57
60
  }
61
+ function isBatchSensitiveToolExecution(graph) {
62
+ return (graph.hookRegistry != null ||
63
+ graph.humanInTheLoop?.enabled === true ||
64
+ graph.toolOutputReferences?.enabled === true);
65
+ }
66
+ function isDirectGraphTool(name, agentContext) {
67
+ if (name.startsWith(_enum.Constants.LC_TRANSFER_TO_)) {
68
+ return true;
69
+ }
70
+ return (agentContext?.graphTools?.some((tool) => 'name' in tool && tool.name === name) === true);
71
+ }
72
+ function isDirectLocalTool(name, graph) {
73
+ if (graph.toolExecution?.engine !== 'local') {
74
+ return false;
75
+ }
76
+ if (graph.toolExecution.local?.includeCodingTools === false) {
77
+ return _enum.CODE_EXECUTION_TOOLS.has(name);
78
+ }
79
+ return LOCAL_CODING_BUNDLE_NAME_SET.has(name);
80
+ }
81
+ function toCodeEnvFile(file, execSessionId) {
82
+ const base = {
83
+ id: file.id,
84
+ resource_id: file.resource_id ?? file.id,
85
+ name: file.name,
86
+ storage_session_id: file.storage_session_id ?? execSessionId,
87
+ };
88
+ const kind = file.kind ?? 'user';
89
+ if (kind === 'skill' && file.version != null) {
90
+ return { ...base, kind: 'skill', version: file.version };
91
+ }
92
+ if (kind === 'agent') {
93
+ return { ...base, kind: 'agent' };
94
+ }
95
+ return { ...base, kind: 'user' };
96
+ }
97
+ function getCodeSessionContext(graph, name) {
98
+ if (!_enum.CODE_EXECUTION_TOOLS.has(name) &&
99
+ name !== _enum.Constants.SKILL_TOOL &&
100
+ name !== _enum.Constants.READ_FILE) {
101
+ return undefined;
102
+ }
103
+ const codeSession = graph.sessions.get(_enum.Constants.EXECUTE_CODE);
104
+ if (codeSession?.session_id == null || codeSession.session_id === '') {
105
+ return undefined;
106
+ }
107
+ return {
108
+ session_id: codeSession.session_id,
109
+ files: codeSession.files?.map((file) => toCodeEnvFile(file, codeSession.session_id)),
110
+ };
111
+ }
112
+ function isEagerToolExecutionEnabledForBatch(args) {
113
+ const { graph, metadata, agentContext } = args;
114
+ if (graph.eagerEventToolExecution?.enabled !== true) {
115
+ return false;
116
+ }
117
+ if ((agentContext?.toolDefinitions?.length ?? 0) === 0) {
118
+ return false;
119
+ }
120
+ if (isBatchSensitiveToolExecution(graph)) {
121
+ return false;
122
+ }
123
+ if (metadata?.[_enum.Constants.PROGRAMMATIC_TOOL_CALLING] === true ||
124
+ metadata?.[_enum.Constants.BASH_PROGRAMMATIC_TOOL_CALLING] === true) {
125
+ return false;
126
+ }
127
+ if (graph.handlerRegistry?.getHandler(_enum.GraphEvents.ON_TOOL_EXECUTE) == null) {
128
+ return false;
129
+ }
130
+ return true;
131
+ }
132
+ function hasFinalToolCallSignal(chunk) {
133
+ const metadata = chunk.response_metadata;
134
+ const finishReason = metadata?.finish_reason ??
135
+ metadata?.finishReason ??
136
+ metadata?.stop_reason ??
137
+ metadata?.stopReason;
138
+ return finishReason === 'tool_calls' || finishReason === 'tool_use';
139
+ }
140
+ function canPrestartSequentialStreamedToolChunks(agentContext) {
141
+ // Anthropic seals each prior streamed tool-use block when the next indexed
142
+ // tool-use block begins. Live Kimi/Moonshot streams can still revise prior
143
+ // args after advancing to the next index, so keep those on the final
144
+ // tool-call path unless they grow an explicit adapter seal.
145
+ return agentContext?.provider === _enum.Providers.ANTHROPIC;
146
+ }
147
+ function hasExplicitStreamedToolCallSeals(chunk) {
148
+ return (streamedToolCallSeals.getStreamedToolCallAdapter(chunk.response_metadata) != null);
149
+ }
150
+ function hasDirectToolCallInBatch(args) {
151
+ const { graph, agentContext, toolCalls } = args;
152
+ return toolCalls.some((toolCall) => toolCall.name !== '' &&
153
+ (isDirectGraphTool(toolCall.name, agentContext) ||
154
+ isDirectLocalTool(toolCall.name, graph)));
155
+ }
156
+ function hasPotentialDirectToolInStreamContext(args) {
157
+ const { graph, agentContext } = args;
158
+ if (graph.toolExecution?.engine === 'local') {
159
+ return true;
160
+ }
161
+ if ((agentContext?.graphTools?.length ?? 0) > 0) {
162
+ return true;
163
+ }
164
+ return (agentContext?.toolDefinitions?.some((toolDefinition) => toolDefinition.name.startsWith(_enum.Constants.LC_TRANSFER_TO_)) === true);
165
+ }
166
+ function createEagerToolExecutionPlan(args) {
167
+ const { graph, metadata, agentContext, toolCalls, skipExisting = false, } = args;
168
+ if (!isEagerToolExecutionEnabledForBatch({
169
+ graph,
170
+ metadata,
171
+ agentContext,
172
+ })) {
173
+ return undefined;
174
+ }
175
+ if (hasDirectToolCallInBatch({ graph, agentContext, toolCalls })) {
176
+ return undefined;
177
+ }
178
+ const candidateToolCalls = skipExisting
179
+ ? toolCalls.filter((toolCall) => {
180
+ if (toolCall.id == null || toolCall.id === '') {
181
+ return true;
182
+ }
183
+ return !graph.eagerEventToolExecutions.has(toolCall.id);
184
+ })
185
+ : toolCalls;
186
+ if (candidateToolCalls.length === 0) {
187
+ return [];
188
+ }
189
+ // Eager execution must preserve ToolNode batch semantics exactly for every
190
+ // unstarted call. If any candidate cannot be planned, fall back for that
191
+ // candidate set.
192
+ if (candidateToolCalls.some((toolCall) => toolCall.id == null ||
193
+ toolCall.id === '' ||
194
+ toolCall.name === '' ||
195
+ (!skipExisting && graph.eagerEventToolExecutions.has(toolCall.id)))) {
196
+ return undefined;
197
+ }
198
+ const plan = eagerEventExecution.buildToolExecutionRequestPlan({
199
+ toolCalls: candidateToolCalls.map((toolCall) => ({
200
+ id: toolCall.id,
201
+ name: toolCall.name,
202
+ args: toolCall.args,
203
+ stepId: graph.toolCallStepIds.get(toolCall.id) ?? '',
204
+ codeSessionContext: getCodeSessionContext(graph, toolCall.name),
205
+ })),
206
+ usageCount: graph.getEagerEventToolUsageCount(agentContext?.agentId),
207
+ });
208
+ if (plan == null) {
209
+ return undefined;
210
+ }
211
+ return plan.requests.map((request) => ({
212
+ id: request.id,
213
+ toolName: request.name,
214
+ coercedArgs: request.args,
215
+ request,
216
+ }));
217
+ }
218
+ function startEagerToolExecutions(args) {
219
+ const { graph, metadata, agentContext, toolCalls, skipExisting } = args;
220
+ const entries = createEagerToolExecutionPlan({
221
+ graph,
222
+ metadata,
223
+ agentContext,
224
+ toolCalls,
225
+ skipExisting,
226
+ });
227
+ if (entries == null || entries.length === 0) {
228
+ return;
229
+ }
230
+ const promise = new Promise((resolve, reject) => {
231
+ let dispatchSettled = false;
232
+ let resultSettled = false;
233
+ let settledResults;
234
+ const maybeResolve = () => {
235
+ if (dispatchSettled && resultSettled) {
236
+ resolve(settledResults ?? []);
237
+ }
238
+ };
239
+ const batchRequest = {
240
+ toolCalls: entries.map((entry) => entry.request),
241
+ userId: graph.config?.configurable?.user_id,
242
+ agentId: agentContext?.agentId,
243
+ configurable: graph.config?.configurable,
244
+ metadata,
245
+ resolve: (results) => {
246
+ resultSettled = true;
247
+ settledResults = results;
248
+ maybeResolve();
249
+ },
250
+ reject,
251
+ };
252
+ void events.safeDispatchCustomEvent(_enum.GraphEvents.ON_TOOL_EXECUTE, batchRequest, graph.config)
253
+ .then(() => {
254
+ dispatchSettled = true;
255
+ maybeResolve();
256
+ })
257
+ .catch(reject);
258
+ }).then((results) => ({ results }), (error) => ({
259
+ error: eagerEventExecution.normalizeError(error),
260
+ }));
261
+ for (const entry of entries) {
262
+ graph.eagerEventToolExecutions.set(entry.id, {
263
+ toolCallId: entry.id,
264
+ toolName: entry.toolName,
265
+ args: entry.coercedArgs,
266
+ request: entry.request,
267
+ promise,
268
+ });
269
+ }
270
+ }
271
+ function getEagerToolChunkKey(stepKey, toolCallChunk) {
272
+ let chunkKey;
273
+ if (typeof toolCallChunk.index === 'number') {
274
+ chunkKey = String(toolCallChunk.index);
275
+ }
276
+ else if (toolCallChunk.id != null && toolCallChunk.id !== '') {
277
+ chunkKey = toolCallChunk.id;
278
+ }
279
+ if (chunkKey == null) {
280
+ return undefined;
281
+ }
282
+ return `${stepKey}\u0000${chunkKey}`;
283
+ }
284
+ function getEagerToolChunkIndex(toolCallChunk) {
285
+ return typeof toolCallChunk.index === 'number'
286
+ ? toolCallChunk.index
287
+ : undefined;
288
+ }
289
+ function pruneEagerToolCallChunkStates(args) {
290
+ const { graph, stepKey, toolCallIds, clearStep = false } = args;
291
+ const prefix = `${stepKey}\u0000`;
292
+ for (const [key, state] of graph.eagerEventToolCallChunks) {
293
+ if (!key.startsWith(prefix)) {
294
+ continue;
295
+ }
296
+ if (clearStep ||
297
+ (state.id != null && toolCallIds?.has(state.id) === true)) {
298
+ graph.eagerEventToolCallChunks.delete(key);
299
+ }
300
+ }
301
+ }
302
+ function isEagerToolChunkStateComplete(state) {
303
+ return (state.id != null &&
304
+ state.id !== '' &&
305
+ state.name != null &&
306
+ state.name !== '' &&
307
+ eagerEventExecution.coerceRecordArgs(state.argsText) != null);
308
+ }
309
+ function mergeToolCallArgsText(existing, incoming) {
310
+ if (incoming === '') {
311
+ return existing;
312
+ }
313
+ if (existing === '') {
314
+ return incoming;
315
+ }
316
+ if (incoming === existing) {
317
+ try {
318
+ JSON.parse(incoming);
319
+ return incoming;
320
+ }
321
+ catch {
322
+ return `${existing}${incoming}`;
323
+ }
324
+ }
325
+ if (incoming.startsWith(existing)) {
326
+ return incoming;
327
+ }
328
+ if (existing.startsWith(incoming)) {
329
+ return existing;
330
+ }
331
+ try {
332
+ JSON.parse(existing);
333
+ JSON.parse(incoming);
334
+ return incoming;
335
+ }
336
+ catch {
337
+ // Fall through to delta concatenation.
338
+ }
339
+ for (let overlap = Math.min(existing.length, incoming.length); overlap >= 8; overlap -= 1) {
340
+ if (existing.endsWith(incoming.slice(0, overlap))) {
341
+ return `${existing}${incoming.slice(overlap)}`;
342
+ }
343
+ }
344
+ return `${existing}${incoming}`;
345
+ }
346
+ function recordEagerToolCallChunks(args) {
347
+ const { graph, stepKey, toolCallChunks } = args;
348
+ if (toolCallChunks == null || toolCallChunks.length === 0) {
349
+ return;
350
+ }
351
+ // Streamed args can be cumulative and parseable before the provider has
352
+ // sealed the call. Recording stays separate from dispatch so the boundary
353
+ // logic can wait for either a later tool index or the final tool-call signal.
354
+ for (const toolCallChunk of toolCallChunks) {
355
+ const key = getEagerToolChunkKey(stepKey, toolCallChunk);
356
+ if (key == null) {
357
+ continue;
358
+ }
359
+ const incomingId = toolCallChunk.id != null && toolCallChunk.id !== ''
360
+ ? toolCallChunk.id
361
+ : undefined;
362
+ const incomingName = toolCallChunk.name != null && toolCallChunk.name !== ''
363
+ ? toolCallChunk.name
364
+ : undefined;
365
+ const previous = graph.eagerEventToolCallChunks.get(key);
366
+ const shouldReset = previous != null &&
367
+ ((incomingId != null &&
368
+ previous.id != null &&
369
+ incomingId !== previous.id) ||
370
+ (incomingName != null &&
371
+ previous.name != null &&
372
+ incomingName !== previous.name));
373
+ const existing = previous == null || shouldReset
374
+ ? {
375
+ argsText: '',
376
+ }
377
+ : previous;
378
+ const id = incomingId ?? existing.id;
379
+ const name = incomingName ?? existing.name;
380
+ const incomingArgs = toolCallChunk.args ?? '';
381
+ const isRepeatedObservedFragment = incomingArgs !== '' &&
382
+ incomingArgs.length > 1 &&
383
+ incomingArgs === existing.lastArgsFragment;
384
+ const argsText = isRepeatedObservedFragment
385
+ ? existing.argsText
386
+ : mergeToolCallArgsText(existing.argsText, incomingArgs);
387
+ const next = {
388
+ id,
389
+ name,
390
+ argsText,
391
+ index: getEagerToolChunkIndex(toolCallChunk) ?? existing.index,
392
+ lastArgsFragment: incomingArgs !== '' ? incomingArgs : existing.lastArgsFragment,
393
+ };
394
+ graph.eagerEventToolCallChunks.set(key, next);
395
+ }
396
+ }
397
+ function getStreamedReadyToolCalls(args) {
398
+ const { graph, stepKey, toolCallChunks, seal, allowSequentialSeal = false, sealAll = false, } = args;
399
+ const currentIndices = new Set();
400
+ for (const toolCallChunk of toolCallChunks ?? []) {
401
+ const index = getEagerToolChunkIndex(toolCallChunk);
402
+ if (index != null) {
403
+ currentIndices.add(index);
404
+ }
405
+ }
406
+ const highestCurrentIndex = currentIndices.size > 0 ? Math.max(...currentIndices) : undefined;
407
+ const prefix = `${stepKey}\u0000`;
408
+ const readyEntries = [];
409
+ for (const [key, state] of graph.eagerEventToolCallChunks) {
410
+ if (!key.startsWith(prefix)) {
411
+ continue;
412
+ }
413
+ if (state.id != null && graph.eagerEventToolExecutions.has(state.id)) {
414
+ graph.eagerEventToolCallChunks.delete(key);
415
+ continue;
416
+ }
417
+ if (!isEagerToolChunkStateComplete(state)) {
418
+ continue;
419
+ }
420
+ const isSealedByLaterChunk = allowSequentialSeal &&
421
+ highestCurrentIndex != null &&
422
+ state.index != null &&
423
+ state.index < highestCurrentIndex &&
424
+ !currentIndices.has(state.index);
425
+ const isSealedExplicitly = seal?.kind === 'single' &&
426
+ ((seal.id != null && state.id === seal.id) ||
427
+ (seal.index != null && state.index === seal.index));
428
+ if (sealAll ||
429
+ seal?.kind === 'all' ||
430
+ isSealedByLaterChunk ||
431
+ isSealedExplicitly) {
432
+ readyEntries.push({ key, state });
433
+ }
434
+ }
435
+ pruneEagerToolCallChunkStates({
436
+ graph,
437
+ stepKey,
438
+ toolCallIds: new Set(readyEntries
439
+ .map(({ state }) => state.id)
440
+ .filter((id) => id != null && id !== '')),
441
+ });
442
+ if (sealAll) {
443
+ pruneEagerToolCallChunkStates({ graph, stepKey, clearStep: true });
444
+ }
445
+ return readyEntries
446
+ .sort((left, right) => (left.state.index ?? 0) - (right.state.index ?? 0))
447
+ .flatMap(({ state }) => {
448
+ const args = eagerEventExecution.coerceRecordArgs(state.argsText);
449
+ if (args == null) {
450
+ return [];
451
+ }
452
+ return [
453
+ {
454
+ id: state.id,
455
+ name: state.name ?? '',
456
+ args,
457
+ },
458
+ ];
459
+ });
460
+ }
461
+ function startReadyStreamedEagerToolExecutions(args) {
462
+ const { graph, metadata, agentContext, stepKey, toolCallChunks, seal, allowSequentialSeal, sealAll, } = args;
463
+ if (hasPotentialDirectToolInStreamContext({ graph, agentContext }) ||
464
+ !isEagerToolExecutionEnabledForBatch({ graph, metadata, agentContext })) {
465
+ return;
466
+ }
467
+ const toolCalls = getStreamedReadyToolCalls({
468
+ graph,
469
+ stepKey,
470
+ toolCallChunks,
471
+ seal,
472
+ allowSequentialSeal,
473
+ sealAll,
474
+ });
475
+ if (toolCalls.length === 0) {
476
+ return;
477
+ }
478
+ startEagerToolExecutions({
479
+ graph,
480
+ metadata,
481
+ agentContext,
482
+ toolCalls,
483
+ skipExisting: true,
484
+ });
485
+ }
58
486
  function getChunkContent({ chunk, provider, reasoningKey, }) {
59
487
  if ((provider === _enum.Providers.OPENAI || provider === _enum.Providers.AZURE) &&
60
488
  chunk?.additional_kwargs?.reasoning?.summary?.[0]?.text != null &&
@@ -115,7 +543,9 @@ class ChatModelStreamHandler {
115
543
  return;
116
544
  }
117
545
  this.handleReasoning(chunk, agentContext);
546
+ const stepKey = graph.getStepKey(metadata);
118
547
  let hasToolCalls = false;
548
+ const hasToolCallChunks = (chunk.tool_call_chunks && chunk.tool_call_chunks.length > 0) ?? false;
119
549
  if (chunk.tool_calls &&
120
550
  chunk.tool_calls.length > 0 &&
121
551
  chunk.tool_calls.every((tc) => tc.id != null &&
@@ -124,8 +554,19 @@ class ChatModelStreamHandler {
124
554
  tc.name !== '')) {
125
555
  hasToolCalls = true;
126
556
  await handlers.handleToolCalls(chunk.tool_calls, metadata, graph);
557
+ if (hasFinalToolCallSignal(chunk)) {
558
+ startEagerToolExecutions({
559
+ graph,
560
+ metadata,
561
+ agentContext,
562
+ toolCalls: chunk.tool_calls,
563
+ skipExisting: true,
564
+ });
565
+ if (!hasToolCallChunks) {
566
+ pruneEagerToolCallChunkStates({ graph, stepKey, clearStep: true });
567
+ }
568
+ }
127
569
  }
128
- const hasToolCallChunks = (chunk.tool_call_chunks && chunk.tool_call_chunks.length > 0) ?? false;
129
570
  const isEmptyContent = typeof content === 'undefined' ||
130
571
  !content.length ||
131
572
  (typeof content === 'string' && !content);
@@ -134,23 +575,45 @@ class ChatModelStreamHandler {
134
575
  if (isEmptyChunk &&
135
576
  (chunk.id ?? '') !== '' &&
136
577
  !graph.prelimMessageIdsByStepKey.has(chunk.id ?? '')) {
137
- const stepKey = graph.getStepKey(metadata);
138
578
  graph.prelimMessageIdsByStepKey.set(stepKey, chunk.id ?? '');
139
579
  }
140
580
  else if (isEmptyChunk) {
141
581
  return;
142
582
  }
143
- const stepKey = graph.getStepKey(metadata);
144
583
  if (hasToolCallChunks &&
145
584
  chunk.tool_call_chunks &&
146
585
  chunk.tool_call_chunks.length &&
147
586
  typeof chunk.tool_call_chunks[0]?.index === 'number') {
587
+ const streamedToolCallSeal = streamedToolCallSeals.getStreamedToolCallSeal(chunk.response_metadata);
588
+ const allowSequentialSeal = canPrestartSequentialStreamedToolChunks(agentContext);
589
+ const canStreamEager = (allowSequentialSeal || hasExplicitStreamedToolCallSeals(chunk)) &&
590
+ !hasPotentialDirectToolInStreamContext({ graph, agentContext }) &&
591
+ isEagerToolExecutionEnabledForBatch({ graph, metadata, agentContext });
592
+ if (canStreamEager) {
593
+ recordEagerToolCallChunks({
594
+ graph,
595
+ stepKey,
596
+ toolCallChunks: chunk.tool_call_chunks,
597
+ });
598
+ }
148
599
  await handlers.handleToolCallChunks({
149
600
  graph,
150
601
  stepKey,
151
602
  toolCallChunks: chunk.tool_call_chunks,
152
603
  metadata,
153
604
  });
605
+ if (canStreamEager) {
606
+ startReadyStreamedEagerToolExecutions({
607
+ graph,
608
+ metadata,
609
+ agentContext,
610
+ stepKey,
611
+ toolCallChunks: chunk.tool_call_chunks,
612
+ seal: streamedToolCallSeal,
613
+ allowSequentialSeal,
614
+ sealAll: hasFinalToolCallSignal(chunk),
615
+ });
616
+ }
154
617
  }
155
618
  if (isEmptyContent) {
156
619
  return;
@@ -201,7 +664,7 @@ hasToolCallChunks: ${hasToolCallChunks}
201
664
  text: content,
202
665
  },
203
666
  ],
204
- });
667
+ }, metadata);
205
668
  }
206
669
  else if (agentContext.currentTokenType === 'think_and_text') {
207
670
  const { text, thinking } = parseThinkingContent(content);
@@ -213,7 +676,7 @@ hasToolCallChunks: ${hasToolCallChunks}
213
676
  think: thinking,
214
677
  },
215
678
  ],
216
- });
679
+ }, metadata);
217
680
  }
218
681
  if (text) {
219
682
  agentContext.currentTokenType = _enum.ContentTypes.TEXT;
@@ -234,7 +697,7 @@ hasToolCallChunks: ${hasToolCallChunks}
234
697
  text: text,
235
698
  },
236
699
  ],
237
- });
700
+ }, metadata);
238
701
  }
239
702
  }
240
703
  else {
@@ -245,13 +708,13 @@ hasToolCallChunks: ${hasToolCallChunks}
245
708
  think: content,
246
709
  },
247
710
  ],
248
- });
711
+ }, metadata);
249
712
  }
250
713
  }
251
714
  else if (content.every((c) => c.type?.startsWith(_enum.ContentTypes.TEXT) ?? false)) {
252
715
  await graph.dispatchMessageDelta(stepId, {
253
716
  content,
254
- });
717
+ }, metadata);
255
718
  }
256
719
  else if (content.every((c) => (c.type?.startsWith(_enum.ContentTypes.THINKING) ?? false) ||
257
720
  (c.type?.startsWith(_enum.ContentTypes.REASONING) ?? false) ||
@@ -262,10 +725,11 @@ hasToolCallChunks: ${hasToolCallChunks}
262
725
  type: _enum.ContentTypes.THINK,
263
726
  think: c.thinking ??
264
727
  c.reasoning ??
265
- c.reasoningText?.text ??
728
+ c.reasoningText
729
+ ?.text ??
266
730
  '',
267
731
  })),
268
- });
732
+ }, metadata);
269
733
  }
270
734
  }
271
735
  handleReasoning(chunk, agentContext) {