@librechat/agents 3.1.44 → 3.1.50

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 (44) hide show
  1. package/dist/cjs/events.cjs +9 -4
  2. package/dist/cjs/events.cjs.map +1 -1
  3. package/dist/cjs/graphs/Graph.cjs +142 -106
  4. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  5. package/dist/cjs/messages/format.cjs +12 -0
  6. package/dist/cjs/messages/format.cjs.map +1 -1
  7. package/dist/cjs/run.cjs +0 -4
  8. package/dist/cjs/run.cjs.map +1 -1
  9. package/dist/cjs/tools/ToolNode.cjs +100 -1
  10. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  11. package/dist/cjs/tools/handlers.cjs.map +1 -1
  12. package/dist/esm/events.mjs +9 -4
  13. package/dist/esm/events.mjs.map +1 -1
  14. package/dist/esm/graphs/Graph.mjs +138 -102
  15. package/dist/esm/graphs/Graph.mjs.map +1 -1
  16. package/dist/esm/messages/format.mjs +12 -0
  17. package/dist/esm/messages/format.mjs.map +1 -1
  18. package/dist/esm/run.mjs +1 -5
  19. package/dist/esm/run.mjs.map +1 -1
  20. package/dist/esm/tools/ToolNode.mjs +100 -1
  21. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  22. package/dist/esm/tools/handlers.mjs.map +1 -1
  23. package/dist/types/events.d.ts +10 -3
  24. package/dist/types/graphs/Graph.d.ts +0 -2
  25. package/dist/types/tools/ToolNode.d.ts +13 -1
  26. package/dist/types/tools/handlers.d.ts +2 -2
  27. package/package.json +1 -1
  28. package/src/events.ts +11 -14
  29. package/src/graphs/Graph.ts +181 -144
  30. package/src/messages/ensureThinkingBlock.test.ts +62 -0
  31. package/src/messages/format.ts +15 -0
  32. package/src/run.ts +0 -6
  33. package/src/specs/anthropic.simple.test.ts +1 -2
  34. package/src/specs/azure.simple.test.ts +1 -2
  35. package/src/specs/cache.simple.test.ts +1 -2
  36. package/src/specs/custom-event-await.test.ts +2 -4
  37. package/src/specs/deepseek.simple.test.ts +1 -2
  38. package/src/specs/moonshot.simple.test.ts +1 -2
  39. package/src/specs/openai.simple.test.ts +1 -2
  40. package/src/specs/openrouter.simple.test.ts +1 -2
  41. package/src/specs/reasoning.test.ts +1 -2
  42. package/src/specs/tool-error.test.ts +1 -2
  43. package/src/tools/ToolNode.ts +130 -1
  44. package/src/tools/handlers.ts +2 -2
@@ -47,12 +47,18 @@ class ModelEndHandler {
47
47
  class ToolEndHandler {
48
48
  callback;
49
49
  logger;
50
- omitOutput;
51
- constructor(callback, logger, omitOutput) {
50
+ constructor(callback, logger) {
52
51
  this.callback = callback;
53
52
  this.logger = logger;
54
- this.omitOutput = omitOutput;
55
53
  }
54
+ /**
55
+ * Handles on_tool_end events from the for-await stream consumer.
56
+ *
57
+ * This handler is now purely a consumer callback — tool completion
58
+ * (ON_RUN_STEP_COMPLETED dispatch + session context storage) is handled
59
+ * in graph context by ToolNode directly, eliminating the race between
60
+ * the stream consumer and graph execution.
61
+ */
56
62
  async handle(event, data, metadata, graph) {
57
63
  try {
58
64
  if (!graph || !metadata) {
@@ -80,7 +86,6 @@ class ToolEndHandler {
80
86
  if (this.callback) {
81
87
  await this.callback(toolEndData, metadata);
82
88
  }
83
- await graph.handleToolCallCompleted({ input: toolEndData.input, output: toolEndData.output }, metadata, this.omitOutput?.(toolEndData.output?.name));
84
89
  }
85
90
  catch (error) {
86
91
  if (this.logger) {
@@ -1 +1 @@
1
- {"version":3,"file":"events.cjs","sources":["../../src/events.ts"],"sourcesContent":["/* eslint-disable no-console */\n// src/events.ts\nimport type {\n ToolMessage,\n UsageMetadata,\n BaseMessageFields,\n} from '@langchain/core/messages';\nimport type { MultiAgentGraph, StandardGraph } from '@/graphs';\nimport type { Logger } from 'winston';\nimport type * as t from '@/types';\nimport { handleToolCalls } from '@/tools/handlers';\nimport { Constants, Providers } from '@/common';\n\nexport class HandlerRegistry {\n private handlers: Map<string, t.EventHandler> = new Map();\n\n register(eventType: string, handler: t.EventHandler): void {\n this.handlers.set(eventType, handler);\n }\n\n getHandler(eventType: string): t.EventHandler | undefined {\n return this.handlers.get(eventType);\n }\n}\n\nexport class ModelEndHandler implements t.EventHandler {\n collectedUsage?: UsageMetadata[];\n constructor(collectedUsage?: UsageMetadata[]) {\n if (collectedUsage && !Array.isArray(collectedUsage)) {\n throw new Error('collectedUsage must be an array');\n }\n this.collectedUsage = collectedUsage;\n }\n\n async handle(\n event: string,\n data: t.ModelEndData,\n metadata?: Record<string, unknown>,\n graph?: StandardGraph | MultiAgentGraph\n ): Promise<void> {\n if (!graph || !metadata) {\n console.warn(`Graph or metadata not found in ${event} event`);\n return;\n }\n\n const usage = data?.output?.usage_metadata;\n if (usage != null && this.collectedUsage != null) {\n this.collectedUsage.push(usage);\n }\n\n if (metadata.ls_provider === 'FakeListChatModel') {\n return handleToolCalls(data?.output?.tool_calls, metadata, graph);\n }\n\n console.log(`====== ${event.toUpperCase()} ======`);\n console.dir(\n {\n usage,\n },\n { depth: null }\n );\n\n const agentContext = graph.getAgentContext(metadata);\n\n if (\n agentContext.provider !== Providers.GOOGLE &&\n agentContext.provider !== Providers.BEDROCK\n ) {\n return;\n }\n\n await handleToolCalls(data?.output?.tool_calls, metadata, graph);\n }\n}\n\nexport class ToolEndHandler implements t.EventHandler {\n private callback?: t.ToolEndCallback;\n private logger?: Logger;\n private omitOutput?: (name?: string) => boolean;\n constructor(\n callback?: t.ToolEndCallback,\n logger?: Logger,\n omitOutput?: (name?: string) => boolean\n ) {\n this.callback = callback;\n this.logger = logger;\n this.omitOutput = omitOutput;\n }\n async handle(\n event: string,\n data: t.StreamEventData | undefined,\n metadata?: Record<string, unknown>,\n graph?: StandardGraph | MultiAgentGraph\n ): Promise<void> {\n try {\n if (!graph || !metadata) {\n if (this.logger) {\n this.logger.warn(`Graph or metadata not found in ${event} event`);\n } else {\n console.warn(`Graph or metadata not found in ${event} event`);\n }\n return;\n }\n\n const toolEndData = data as t.ToolEndData | undefined;\n if (!toolEndData?.output) {\n if (this.logger) {\n this.logger.warn('No output found in tool_end event');\n } else {\n console.warn('No output found in tool_end event');\n }\n return;\n }\n\n if (metadata[Constants.PROGRAMMATIC_TOOL_CALLING] === true) {\n return;\n }\n\n if (this.callback) {\n await this.callback(toolEndData, metadata);\n }\n await graph.handleToolCallCompleted(\n { input: toolEndData.input, output: toolEndData.output },\n metadata,\n this.omitOutput?.((toolEndData.output as ToolMessage | undefined)?.name)\n );\n } catch (error) {\n if (this.logger) {\n this.logger.error('Error handling tool_end event:', error);\n } else {\n console.error('Error handling tool_end event:', error);\n }\n }\n }\n}\n\nexport class TestLLMStreamHandler implements t.EventHandler {\n handle(event: string, data: t.StreamEventData | undefined): void {\n const chunk = data?.chunk;\n const isMessageChunk = !!(chunk && 'message' in chunk);\n const msg = isMessageChunk ? chunk.message : undefined;\n if (msg && msg.tool_call_chunks && msg.tool_call_chunks.length > 0) {\n console.log(msg.tool_call_chunks);\n } else if (msg && msg.content) {\n if (typeof msg.content === 'string') {\n process.stdout.write(msg.content);\n }\n }\n }\n}\n\nexport class TestChatStreamHandler implements t.EventHandler {\n handle(event: string, data: t.StreamEventData | undefined): void {\n const chunk = data?.chunk;\n const isContentChunk = !!(chunk && 'content' in chunk);\n const content = isContentChunk && chunk.content;\n\n if (!content || !isContentChunk) {\n return;\n }\n\n if (chunk.tool_call_chunks && chunk.tool_call_chunks.length > 0) {\n console.dir(chunk.tool_call_chunks, { depth: null });\n }\n\n if (typeof content === 'string') {\n process.stdout.write(content);\n } else {\n console.dir(content, { depth: null });\n }\n }\n}\n\nexport class LLMStreamHandler implements t.EventHandler {\n handle(\n event: string,\n data: t.StreamEventData | undefined,\n metadata?: Record<string, unknown>\n ): void {\n const chunk = data?.chunk;\n const isMessageChunk = !!(chunk && 'message' in chunk);\n const msg = isMessageChunk && chunk.message;\n if (metadata) {\n console.log(metadata);\n }\n if (msg && msg.tool_call_chunks && msg.tool_call_chunks.length > 0) {\n console.log(msg.tool_call_chunks);\n } else if (msg && msg.content) {\n if (typeof msg.content === 'string') {\n // const text_delta = msg.content;\n // dispatchCustomEvent(GraphEvents.CHAT_MODEL_STREAM, { chunk }, config);\n process.stdout.write(msg.content);\n }\n }\n }\n}\n\nexport const createMetadataAggregator = (\n _collected?: Record<\n string,\n NonNullable<BaseMessageFields['response_metadata']>\n >[]\n): t.MetadataAggregatorResult => {\n const collected = _collected || [];\n\n const handleLLMEnd: t.HandleLLMEnd = (output) => {\n const { generations } = output;\n const lastMessageOutput = (\n generations[generations.length - 1] as\n | (t.StreamGeneration | undefined)[]\n | undefined\n )?.[0];\n if (!lastMessageOutput) {\n return;\n }\n const { message } = lastMessageOutput;\n if (message?.response_metadata) {\n collected.push(message.response_metadata);\n }\n };\n\n return { handleLLMEnd, collected };\n};\n"],"names":["handleToolCalls","Providers","Constants"],"mappings":"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"}
1
+ {"version":3,"file":"events.cjs","sources":["../../src/events.ts"],"sourcesContent":["/* eslint-disable no-console */\n// src/events.ts\nimport type {\n BaseMessageFields,\n UsageMetadata,\n} from '@langchain/core/messages';\nimport type { MultiAgentGraph, StandardGraph } from '@/graphs';\nimport type { Logger } from 'winston';\nimport type * as t from '@/types';\nimport { handleToolCalls } from '@/tools/handlers';\nimport { Constants, Providers } from '@/common';\n\nexport class HandlerRegistry {\n private handlers: Map<string, t.EventHandler> = new Map();\n\n register(eventType: string, handler: t.EventHandler): void {\n this.handlers.set(eventType, handler);\n }\n\n getHandler(eventType: string): t.EventHandler | undefined {\n return this.handlers.get(eventType);\n }\n}\n\nexport class ModelEndHandler implements t.EventHandler {\n collectedUsage?: UsageMetadata[];\n constructor(collectedUsage?: UsageMetadata[]) {\n if (collectedUsage && !Array.isArray(collectedUsage)) {\n throw new Error('collectedUsage must be an array');\n }\n this.collectedUsage = collectedUsage;\n }\n\n async handle(\n event: string,\n data: t.ModelEndData,\n metadata?: Record<string, unknown>,\n graph?: StandardGraph | MultiAgentGraph\n ): Promise<void> {\n if (!graph || !metadata) {\n console.warn(`Graph or metadata not found in ${event} event`);\n return;\n }\n\n const usage = data?.output?.usage_metadata;\n if (usage != null && this.collectedUsage != null) {\n this.collectedUsage.push(usage);\n }\n\n if (metadata.ls_provider === 'FakeListChatModel') {\n return handleToolCalls(data?.output?.tool_calls, metadata, graph);\n }\n\n console.log(`====== ${event.toUpperCase()} ======`);\n console.dir(\n {\n usage,\n },\n { depth: null }\n );\n\n const agentContext = graph.getAgentContext(metadata);\n\n if (\n agentContext.provider !== Providers.GOOGLE &&\n agentContext.provider !== Providers.BEDROCK\n ) {\n return;\n }\n\n await handleToolCalls(data?.output?.tool_calls, metadata, graph);\n }\n}\n\nexport class ToolEndHandler implements t.EventHandler {\n private callback?: t.ToolEndCallback;\n private logger?: Logger;\n constructor(callback?: t.ToolEndCallback, logger?: Logger) {\n this.callback = callback;\n this.logger = logger;\n }\n\n /**\n * Handles on_tool_end events from the for-await stream consumer.\n *\n * This handler is now purely a consumer callback — tool completion\n * (ON_RUN_STEP_COMPLETED dispatch + session context storage) is handled\n * in graph context by ToolNode directly, eliminating the race between\n * the stream consumer and graph execution.\n */\n async handle(\n event: string,\n data: t.StreamEventData | undefined,\n metadata?: Record<string, unknown>,\n graph?: StandardGraph | MultiAgentGraph\n ): Promise<void> {\n try {\n if (!graph || !metadata) {\n if (this.logger) {\n this.logger.warn(`Graph or metadata not found in ${event} event`);\n } else {\n console.warn(`Graph or metadata not found in ${event} event`);\n }\n return;\n }\n\n const toolEndData = data as t.ToolEndData | undefined;\n if (!toolEndData?.output) {\n if (this.logger) {\n this.logger.warn('No output found in tool_end event');\n } else {\n console.warn('No output found in tool_end event');\n }\n return;\n }\n\n if (metadata[Constants.PROGRAMMATIC_TOOL_CALLING] === true) {\n return;\n }\n\n if (this.callback) {\n await this.callback(toolEndData, metadata);\n }\n } catch (error) {\n if (this.logger) {\n this.logger.error('Error handling tool_end event:', error);\n } else {\n console.error('Error handling tool_end event:', error);\n }\n }\n }\n}\n\nexport class TestLLMStreamHandler implements t.EventHandler {\n handle(event: string, data: t.StreamEventData | undefined): void {\n const chunk = data?.chunk;\n const isMessageChunk = !!(chunk && 'message' in chunk);\n const msg = isMessageChunk ? chunk.message : undefined;\n if (msg && msg.tool_call_chunks && msg.tool_call_chunks.length > 0) {\n console.log(msg.tool_call_chunks);\n } else if (msg && msg.content) {\n if (typeof msg.content === 'string') {\n process.stdout.write(msg.content);\n }\n }\n }\n}\n\nexport class TestChatStreamHandler implements t.EventHandler {\n handle(event: string, data: t.StreamEventData | undefined): void {\n const chunk = data?.chunk;\n const isContentChunk = !!(chunk && 'content' in chunk);\n const content = isContentChunk && chunk.content;\n\n if (!content || !isContentChunk) {\n return;\n }\n\n if (chunk.tool_call_chunks && chunk.tool_call_chunks.length > 0) {\n console.dir(chunk.tool_call_chunks, { depth: null });\n }\n\n if (typeof content === 'string') {\n process.stdout.write(content);\n } else {\n console.dir(content, { depth: null });\n }\n }\n}\n\nexport class LLMStreamHandler implements t.EventHandler {\n handle(\n event: string,\n data: t.StreamEventData | undefined,\n metadata?: Record<string, unknown>\n ): void {\n const chunk = data?.chunk;\n const isMessageChunk = !!(chunk && 'message' in chunk);\n const msg = isMessageChunk && chunk.message;\n if (metadata) {\n console.log(metadata);\n }\n if (msg && msg.tool_call_chunks && msg.tool_call_chunks.length > 0) {\n console.log(msg.tool_call_chunks);\n } else if (msg && msg.content) {\n if (typeof msg.content === 'string') {\n // const text_delta = msg.content;\n // dispatchCustomEvent(GraphEvents.CHAT_MODEL_STREAM, { chunk }, config);\n process.stdout.write(msg.content);\n }\n }\n }\n}\n\nexport const createMetadataAggregator = (\n _collected?: Record<\n string,\n NonNullable<BaseMessageFields['response_metadata']>\n >[]\n): t.MetadataAggregatorResult => {\n const collected = _collected || [];\n\n const handleLLMEnd: t.HandleLLMEnd = (output) => {\n const { generations } = output;\n const lastMessageOutput = (\n generations[generations.length - 1] as\n | (t.StreamGeneration | undefined)[]\n | undefined\n )?.[0];\n if (!lastMessageOutput) {\n return;\n }\n const { message } = lastMessageOutput;\n if (message?.response_metadata) {\n collected.push(message.response_metadata);\n }\n };\n\n return { handleLLMEnd, collected };\n};\n"],"names":["handleToolCalls","Providers","Constants"],"mappings":"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"}
@@ -1,20 +1,23 @@
1
1
  'use strict';
2
2
 
3
3
  var nanoid = require('nanoid');
4
- var stream = require('@langchain/core/utils/stream');
4
+ var stream$1 = require('@langchain/core/utils/stream');
5
5
  var googleVertexai = require('@langchain/google-vertexai');
6
6
  var langgraph = require('@langchain/langgraph');
7
7
  var runnables = require('@langchain/core/runnables');
8
8
  var messages = require('@langchain/core/messages');
9
- var _enum = require('../common/enum.cjs');
10
9
  var core = require('../messages/core.cjs');
10
+ var ids = require('../messages/ids.cjs');
11
11
  var prune = require('../messages/prune.cjs');
12
12
  var format = require('../messages/format.cjs');
13
13
  var cache = require('../messages/cache.cjs');
14
14
  var content = require('../messages/content.cjs');
15
15
  var tools = require('../messages/tools.cjs');
16
+ var _enum = require('../common/enum.cjs');
16
17
  var graph = require('../utils/graph.cjs');
17
18
  var llm = require('../utils/llm.cjs');
19
+ var stream = require('../stream.cjs');
20
+ var handlers = require('../tools/handlers.cjs');
18
21
  var run = require('../utils/run.cjs');
19
22
  require('js-tiktoken/lite');
20
23
  require('zod-to-json-schema');
@@ -85,7 +88,13 @@ class StandardGraph extends Graph {
85
88
  this.contentIndexMap = graph.resetIfNotEmpty(this.contentIndexMap, new Map());
86
89
  }
87
90
  this.stepKeyIds = graph.resetIfNotEmpty(this.stepKeyIds, new Map());
88
- this.toolCallStepIds = graph.resetIfNotEmpty(this.toolCallStepIds, new Map());
91
+ /**
92
+ * Clear in-place instead of replacing with a new Map to preserve the
93
+ * shared reference held by ToolNode (passed at construction time).
94
+ * Using resetIfNotEmpty would create a new Map, leaving ToolNode with
95
+ * a stale reference on 2nd+ processStream calls.
96
+ */
97
+ this.toolCallStepIds.clear();
89
98
  this.messageIdsByStepKey = graph.resetIfNotEmpty(this.messageIdsByStepKey, new Map());
90
99
  this.messageStepHasToolCalls = graph.resetIfNotEmpty(this.messageStepHasToolCalls, new Map());
91
100
  this.prelimMessageIdsByStepKey = graph.resetIfNotEmpty(this.prelimMessageIdsByStepKey, new Map());
@@ -378,25 +387,50 @@ class StandardGraph extends Graph {
378
387
  }
379
388
  }
380
389
  /** Execute model invocation with streaming support */
381
- async attemptInvoke({ currentModel, finalMessages, provider, tools, }, config) {
390
+ async attemptInvoke({ currentModel, finalMessages, provider, tools: _tools, }, config) {
382
391
  const model = this.overrideModel ?? currentModel;
383
392
  if (!model) {
384
393
  throw new Error('No model found');
385
394
  }
386
- if ((tools?.length ?? 0) > 0 && providers.manualToolStreamProviders.has(provider)) {
387
- if (!model.stream) {
388
- throw new Error('Model does not support stream');
389
- }
390
- const stream$1 = await model.stream(finalMessages, config);
395
+ if (model.stream) {
396
+ /**
397
+ * Process all model output through a local ChatModelStreamHandler in the
398
+ * graph execution context. Each chunk is awaited before the next one is
399
+ * consumed, so by the time the stream is exhausted every run step
400
+ * (MESSAGE_CREATION, TOOL_CALLS) has been created and toolCallStepIds is
401
+ * fully populated — the graph will not transition to ToolNode until this
402
+ * is done.
403
+ *
404
+ * This replaces the previous pattern where ChatModelStreamHandler lived
405
+ * in the for-await stream consumer (handler registry). That consumer
406
+ * runs concurrently with graph execution, so the graph could advance to
407
+ * ToolNode before the consumer had processed all events. By handling
408
+ * chunks here, inside the agent node, the race is eliminated.
409
+ *
410
+ * The for-await consumer no longer needs a ChatModelStreamHandler; its
411
+ * on_chat_model_stream events are simply ignored (no handler registered).
412
+ * The dispatched custom events (ON_RUN_STEP, ON_MESSAGE_DELTA, etc.)
413
+ * still reach the content aggregator and SSE handlers through the custom
414
+ * event callback in Run.createCustomEventCallback.
415
+ */
416
+ const metadata = config?.metadata;
417
+ const streamHandler = new stream.ChatModelStreamHandler();
418
+ const stream$2 = await model.stream(finalMessages, config);
391
419
  let finalChunk;
392
- for await (const chunk of stream$1) {
393
- await events.safeDispatchCustomEvent(_enum.GraphEvents.CHAT_MODEL_STREAM, { chunk, emitted: true }, config);
394
- finalChunk = finalChunk ? stream.concat(finalChunk, chunk) : chunk;
420
+ for await (const chunk of stream$2) {
421
+ await streamHandler.handle(_enum.GraphEvents.CHAT_MODEL_STREAM, { chunk }, metadata, this);
422
+ finalChunk = finalChunk ? stream$1.concat(finalChunk, chunk) : chunk;
423
+ }
424
+ if (providers.manualToolStreamProviders.has(provider)) {
425
+ finalChunk = core.modifyDeltaProperties(provider, finalChunk);
426
+ }
427
+ if ((finalChunk?.tool_calls?.length ?? 0) > 0) {
428
+ finalChunk.tool_calls = finalChunk.tool_calls?.filter((tool_call) => !!tool_call.name);
395
429
  }
396
- finalChunk = core.modifyDeltaProperties(provider, finalChunk);
397
430
  return { messages: [finalChunk] };
398
431
  }
399
432
  else {
433
+ /** Fallback for models without stream support. */
400
434
  const finalMessage = await model.invoke(finalMessages, config);
401
435
  if ((finalMessage.tool_calls?.length ?? 0) > 0) {
402
436
  finalMessage.tool_calls = finalMessage.tool_calls?.filter((tool_call) => !!tool_call.name);
@@ -599,6 +633,101 @@ class StandardGraph extends Graph {
599
633
  if (!result) {
600
634
  throw new Error('No result after model invocation');
601
635
  }
636
+ /**
637
+ * Fallback: populate toolCallStepIds in the graph execution context.
638
+ *
639
+ * When model.stream() is available (the common case), attemptInvoke
640
+ * processes all chunks through a local ChatModelStreamHandler which
641
+ * creates run steps and populates toolCallStepIds before returning.
642
+ * The code below is a fallback for the rare case where model.stream
643
+ * is unavailable and model.invoke() was used instead.
644
+ *
645
+ * Text content is dispatched FIRST so that MESSAGE_CREATION is the
646
+ * current step when handleToolCalls runs. handleToolCalls then creates
647
+ * TOOL_CALLS on top of it. The dedup in getMessageId and
648
+ * toolCallStepIds.has makes this safe when attemptInvoke already
649
+ * handled everything — both paths become no-ops.
650
+ */
651
+ const responseMessage = result.messages?.[0];
652
+ const toolCalls = responseMessage
653
+ ?.tool_calls;
654
+ const hasToolCalls = Array.isArray(toolCalls) && toolCalls.length > 0;
655
+ if (hasToolCalls) {
656
+ const metadata = config.metadata;
657
+ const stepKey = this.getStepKey(metadata);
658
+ const content = responseMessage?.content;
659
+ const hasTextContent = content != null &&
660
+ (typeof content === 'string'
661
+ ? content !== ''
662
+ : Array.isArray(content) && content.length > 0);
663
+ /**
664
+ * Dispatch text content BEFORE creating TOOL_CALLS steps.
665
+ * getMessageId returns a new ID only on the first call for a step key;
666
+ * if the for-await consumer already claimed it, this is a no-op.
667
+ */
668
+ if (hasTextContent) {
669
+ const messageId = ids.getMessageId(stepKey, this) ?? '';
670
+ if (messageId) {
671
+ await this.dispatchRunStep(stepKey, {
672
+ type: _enum.StepTypes.MESSAGE_CREATION,
673
+ message_creation: { message_id: messageId },
674
+ }, metadata);
675
+ const stepId = this.getStepIdByKey(stepKey);
676
+ if (typeof content === 'string') {
677
+ await this.dispatchMessageDelta(stepId, {
678
+ content: [{ type: _enum.ContentTypes.TEXT, text: content }],
679
+ });
680
+ }
681
+ else if (Array.isArray(content) &&
682
+ content.every((c) => typeof c === 'object' &&
683
+ 'type' in c &&
684
+ typeof c.type === 'string' &&
685
+ c.type.startsWith('text'))) {
686
+ await this.dispatchMessageDelta(stepId, {
687
+ content: content,
688
+ });
689
+ }
690
+ }
691
+ }
692
+ await handlers.handleToolCalls(toolCalls, metadata, this);
693
+ }
694
+ /**
695
+ * When streaming is disabled, on_chat_model_stream events are never
696
+ * emitted so ChatModelStreamHandler never fires. Dispatch the text
697
+ * content as MESSAGE_CREATION + MESSAGE_DELTA here.
698
+ */
699
+ const disableStreaming = agentContext.clientOptions
700
+ ?.disableStreaming === true;
701
+ if (disableStreaming &&
702
+ !hasToolCalls &&
703
+ responseMessage != null &&
704
+ responseMessage.content != null) {
705
+ const metadata = config.metadata;
706
+ const stepKey = this.getStepKey(metadata);
707
+ const messageId = ids.getMessageId(stepKey, this) ?? '';
708
+ if (messageId) {
709
+ await this.dispatchRunStep(stepKey, {
710
+ type: _enum.StepTypes.MESSAGE_CREATION,
711
+ message_creation: { message_id: messageId },
712
+ }, metadata);
713
+ }
714
+ const stepId = this.getStepIdByKey(stepKey);
715
+ const content = responseMessage.content;
716
+ if (typeof content === 'string') {
717
+ await this.dispatchMessageDelta(stepId, {
718
+ content: [{ type: _enum.ContentTypes.TEXT, text: content }],
719
+ });
720
+ }
721
+ else if (Array.isArray(content) &&
722
+ content.every((c) => typeof c === 'object' &&
723
+ 'type' in c &&
724
+ typeof c.type === 'string' &&
725
+ c.type.startsWith('text'))) {
726
+ await this.dispatchMessageDelta(stepId, {
727
+ content: content,
728
+ });
729
+ }
730
+ }
602
731
  agentContext.currentUsage = this.getUsageMetadata(result.messages?.[0]);
603
732
  this.cleanupSignalListener();
604
733
  return result;
@@ -731,99 +860,6 @@ class StandardGraph extends Graph {
731
860
  await events.safeDispatchCustomEvent(_enum.GraphEvents.ON_RUN_STEP, runStep, this.config);
732
861
  return stepId;
733
862
  }
734
- async handleToolCallCompleted(data, metadata, omitOutput) {
735
- if (!this.config) {
736
- throw new Error('No config provided');
737
- }
738
- if (!data.output) {
739
- return;
740
- }
741
- const { input, output: _output } = data;
742
- if (_output?.lg_name === 'Command') {
743
- return;
744
- }
745
- const output = _output;
746
- const { tool_call_id } = output;
747
- const stepId = this.toolCallStepIds.get(tool_call_id) ?? '';
748
- if (!stepId) {
749
- throw new Error(`No stepId found for tool_call_id ${tool_call_id}`);
750
- }
751
- const runStep = this.getRunStep(stepId);
752
- if (!runStep) {
753
- throw new Error(`No run step found for stepId ${stepId}`);
754
- }
755
- /**
756
- * Extract and store code execution session context from artifacts.
757
- * Each file is stamped with its source session_id to support multi-session file tracking.
758
- * When the same filename appears in a later execution, the newer version replaces the old.
759
- */
760
- const toolName = output.name;
761
- if (toolName === _enum.Constants.EXECUTE_CODE ||
762
- toolName === _enum.Constants.PROGRAMMATIC_TOOL_CALLING) {
763
- const artifact = output.artifact;
764
- if (artifact?.session_id != null && artifact.session_id !== '') {
765
- const newFiles = artifact.files ?? [];
766
- const existingSession = this.sessions.get(_enum.Constants.EXECUTE_CODE);
767
- const existingFiles = existingSession?.files ?? [];
768
- if (newFiles.length > 0) {
769
- /**
770
- * Stamp each new file with its source session_id.
771
- * This enables files from different executions (parallel or sequential)
772
- * to be tracked and passed to subsequent calls.
773
- */
774
- const filesWithSession = newFiles.map((file) => ({
775
- ...file,
776
- session_id: artifact.session_id,
777
- }));
778
- /**
779
- * Merge files, preferring latest versions by name.
780
- * If a file with the same name exists, replace it with the new version.
781
- * This handles cases where files are edited/recreated in subsequent executions.
782
- */
783
- const newFileNames = new Set(filesWithSession.map((f) => f.name));
784
- const filteredExisting = existingFiles.filter((f) => !newFileNames.has(f.name));
785
- this.sessions.set(_enum.Constants.EXECUTE_CODE, {
786
- session_id: artifact.session_id,
787
- files: [...filteredExisting, ...filesWithSession],
788
- lastUpdated: Date.now(),
789
- });
790
- }
791
- else {
792
- /**
793
- * Store session_id even without new files for session continuity.
794
- * The CodeExecutor can fall back to the /files endpoint to discover
795
- * session files not explicitly returned in the exec response.
796
- */
797
- this.sessions.set(_enum.Constants.EXECUTE_CODE, {
798
- session_id: artifact.session_id,
799
- files: existingFiles,
800
- lastUpdated: Date.now(),
801
- });
802
- }
803
- }
804
- }
805
- const dispatchedOutput = typeof output.content === 'string'
806
- ? output.content
807
- : JSON.stringify(output.content);
808
- const args = typeof input === 'string' ? input : input.input;
809
- const tool_call = {
810
- args: typeof args === 'string' ? args : JSON.stringify(args),
811
- name: output.name ?? '',
812
- id: output.tool_call_id,
813
- output: omitOutput === true ? '' : dispatchedOutput,
814
- progress: 1,
815
- };
816
- await this.handlerRegistry
817
- ?.getHandler(_enum.GraphEvents.ON_RUN_STEP_COMPLETED)
818
- ?.handle(_enum.GraphEvents.ON_RUN_STEP_COMPLETED, {
819
- result: {
820
- id: stepId,
821
- index: runStep.index,
822
- type: 'tool_call',
823
- tool_call,
824
- },
825
- }, metadata, this);
826
- }
827
863
  /**
828
864
  * Static version of handleToolCallError to avoid creating strong references
829
865
  * that prevent garbage collection