@librechat/agents 3.0.63 → 3.0.65
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.
- package/dist/cjs/agents/AgentContext.cjs +66 -10
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +85 -8
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +51 -7
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +66 -10
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +85 -8
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +52 -8
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +29 -1
- package/dist/types/graphs/MultiAgentGraph.d.ts +5 -2
- package/dist/types/types/graph.d.ts +2 -0
- package/package.json +1 -1
- package/src/agents/AgentContext.ts +79 -8
- package/src/graphs/MultiAgentGraph.ts +111 -9
- package/src/scripts/test-handoff-preamble.ts +2 -0
- package/src/scripts/test-parallel-handoffs.ts +291 -0
- package/src/tools/ToolNode.ts +56 -7
- package/src/types/graph.ts +2 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isBaseMessage, ToolMessage, isAIMessage } from '@langchain/core/messages';
|
|
2
|
-
import { isCommand, isGraphInterrupt, Command,
|
|
2
|
+
import { isCommand, isGraphInterrupt, Command, Send, END } from '@langchain/langgraph';
|
|
3
3
|
import { Constants } from '../common/enum.mjs';
|
|
4
4
|
import 'nanoid';
|
|
5
5
|
import '../messages/core.mjs';
|
|
@@ -218,6 +218,11 @@ class ToolNode extends RunnableCallable {
|
|
|
218
218
|
}
|
|
219
219
|
const combinedOutputs = [];
|
|
220
220
|
let parentCommand = null;
|
|
221
|
+
/**
|
|
222
|
+
* Collect handoff commands (Commands with string goto and Command.PARENT)
|
|
223
|
+
* for potential parallel handoff aggregation
|
|
224
|
+
*/
|
|
225
|
+
const handoffCommands = [];
|
|
221
226
|
for (const output of outputs) {
|
|
222
227
|
if (isCommand(output)) {
|
|
223
228
|
if (output.graph === Command.PARENT &&
|
|
@@ -234,15 +239,27 @@ class ToolNode extends RunnableCallable {
|
|
|
234
239
|
});
|
|
235
240
|
}
|
|
236
241
|
}
|
|
237
|
-
else {
|
|
242
|
+
else if (output.graph === Command.PARENT) {
|
|
238
243
|
/**
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
* Note: Parallel handoffs (LLM calling multiple transfer tools simultaneously)
|
|
243
|
-
* are not yet fully supported. For parallel agent execution, use direct edges
|
|
244
|
-
* with edgeType: 'direct' instead of handoff edges.
|
|
244
|
+
* Handoff Command with destination.
|
|
245
|
+
* Handle both string ('agent') and array (['agent']) formats.
|
|
246
|
+
* Collect for potential parallel aggregation.
|
|
245
247
|
*/
|
|
248
|
+
const goto = output.goto;
|
|
249
|
+
const isSingleStringDest = typeof goto === 'string';
|
|
250
|
+
const isSingleArrayDest = Array.isArray(goto) &&
|
|
251
|
+
goto.length === 1 &&
|
|
252
|
+
typeof goto[0] === 'string';
|
|
253
|
+
if (isSingleStringDest || isSingleArrayDest) {
|
|
254
|
+
handoffCommands.push(output);
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
/** Multi-destination or other command - pass through */
|
|
258
|
+
combinedOutputs.push(output);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
/** Other commands - pass through */
|
|
246
263
|
combinedOutputs.push(output);
|
|
247
264
|
}
|
|
248
265
|
}
|
|
@@ -250,6 +267,33 @@ class ToolNode extends RunnableCallable {
|
|
|
250
267
|
combinedOutputs.push(Array.isArray(input) ? [output] : { messages: [output] });
|
|
251
268
|
}
|
|
252
269
|
}
|
|
270
|
+
/**
|
|
271
|
+
* Handle handoff commands - convert to Send objects for parallel execution
|
|
272
|
+
* when multiple handoffs are requested
|
|
273
|
+
*/
|
|
274
|
+
if (handoffCommands.length > 1) {
|
|
275
|
+
/**
|
|
276
|
+
* Multiple parallel handoffs - convert to Send objects.
|
|
277
|
+
* Each Send carries its own state with the appropriate messages.
|
|
278
|
+
* This enables LLM-initiated parallel execution when calling multiple
|
|
279
|
+
* transfer tools simultaneously.
|
|
280
|
+
*/
|
|
281
|
+
const sends = handoffCommands.map((cmd) => {
|
|
282
|
+
/** Extract destination - handle both string and array formats */
|
|
283
|
+
const goto = cmd.goto;
|
|
284
|
+
const destination = typeof goto === 'string' ? goto : goto[0];
|
|
285
|
+
return new Send(destination, cmd.update);
|
|
286
|
+
});
|
|
287
|
+
const parallelCommand = new Command({
|
|
288
|
+
graph: Command.PARENT,
|
|
289
|
+
goto: sends,
|
|
290
|
+
});
|
|
291
|
+
combinedOutputs.push(parallelCommand);
|
|
292
|
+
}
|
|
293
|
+
else if (handoffCommands.length === 1) {
|
|
294
|
+
/** Single handoff - pass through as-is */
|
|
295
|
+
combinedOutputs.push(handoffCommands[0]);
|
|
296
|
+
}
|
|
253
297
|
if (parentCommand) {
|
|
254
298
|
combinedOutputs.push(parentCommand);
|
|
255
299
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolNode.mjs","sources":["../../../src/tools/ToolNode.ts"],"sourcesContent":["import { ToolCall } from '@langchain/core/messages/tool';\nimport {\n ToolMessage,\n isAIMessage,\n isBaseMessage,\n} from '@langchain/core/messages';\nimport {\n END,\n Send,\n Command,\n isCommand,\n isGraphInterrupt,\n MessagesAnnotation,\n} from '@langchain/langgraph';\nimport type {\n RunnableConfig,\n RunnableToolLike,\n} from '@langchain/core/runnables';\nimport type { BaseMessage, AIMessage } from '@langchain/core/messages';\nimport type { StructuredToolInterface } from '@langchain/core/tools';\nimport type * as t from '@/types';\nimport { RunnableCallable } from '@/utils';\nimport { Constants } from '@/common';\n\n/**\n * Helper to check if a value is a Send object\n */\nfunction isSend(value: unknown): value is Send {\n return value instanceof Send;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport class ToolNode<T = any> extends RunnableCallable<T, T> {\n private toolMap: Map<string, StructuredToolInterface | RunnableToolLike>;\n private loadRuntimeTools?: t.ToolRefGenerator;\n handleToolErrors = true;\n trace = false;\n toolCallStepIds?: Map<string, string>;\n errorHandler?: t.ToolNodeConstructorParams['errorHandler'];\n private toolUsageCount: Map<string, number>;\n /** Tool registry for filtering (lazy computation of programmatic maps) */\n private toolRegistry?: t.LCToolRegistry;\n /** Cached programmatic tools (computed once on first PTC call) */\n private programmaticCache?: t.ProgrammaticCache;\n\n constructor({\n tools,\n toolMap,\n name,\n tags,\n errorHandler,\n toolCallStepIds,\n handleToolErrors,\n loadRuntimeTools,\n toolRegistry,\n }: t.ToolNodeConstructorParams) {\n super({ name, tags, func: (input, config) => this.run(input, config) });\n this.toolMap = toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));\n this.toolCallStepIds = toolCallStepIds;\n this.handleToolErrors = handleToolErrors ?? this.handleToolErrors;\n this.loadRuntimeTools = loadRuntimeTools;\n this.errorHandler = errorHandler;\n this.toolUsageCount = new Map<string, number>();\n this.toolRegistry = toolRegistry;\n }\n\n /**\n * Returns cached programmatic tools, computing once on first access.\n * Single iteration builds both toolMap and toolDefs simultaneously.\n */\n private getProgrammaticTools(): { toolMap: t.ToolMap; toolDefs: t.LCTool[] } {\n if (this.programmaticCache) return this.programmaticCache;\n\n const toolMap: t.ToolMap = new Map();\n const toolDefs: t.LCTool[] = [];\n\n if (this.toolRegistry) {\n for (const [name, toolDef] of this.toolRegistry) {\n if (\n (toolDef.allowed_callers ?? ['direct']).includes('code_execution')\n ) {\n toolDefs.push(toolDef);\n const tool = this.toolMap.get(name);\n if (tool) toolMap.set(name, tool);\n }\n }\n }\n\n this.programmaticCache = { toolMap, toolDefs };\n return this.programmaticCache;\n }\n\n /**\n * Returns a snapshot of the current tool usage counts.\n * @returns A ReadonlyMap where keys are tool names and values are their usage counts.\n */\n public getToolUsageCounts(): ReadonlyMap<string, number> {\n return new Map(this.toolUsageCount); // Return a copy\n }\n\n /**\n * Runs a single tool call with error handling\n */\n protected async runTool(\n call: ToolCall,\n config: RunnableConfig\n ): Promise<BaseMessage | Command> {\n const tool = this.toolMap.get(call.name);\n try {\n if (tool === undefined) {\n throw new Error(`Tool \"${call.name}\" not found.`);\n }\n const turn = this.toolUsageCount.get(call.name) ?? 0;\n this.toolUsageCount.set(call.name, turn + 1);\n const args = call.args;\n const stepId = this.toolCallStepIds?.get(call.id!);\n\n // Build invoke params - LangChain extracts non-schema fields to config.toolCall\n let invokeParams: Record<string, unknown> = {\n ...call,\n args,\n type: 'tool_call',\n stepId,\n turn,\n };\n\n // Inject runtime data for special tools (becomes available at config.toolCall)\n if (call.name === Constants.PROGRAMMATIC_TOOL_CALLING) {\n const { toolMap, toolDefs } = this.getProgrammaticTools();\n invokeParams = {\n ...invokeParams,\n toolMap,\n toolDefs,\n };\n } else if (call.name === Constants.TOOL_SEARCH_REGEX) {\n invokeParams = {\n ...invokeParams,\n toolRegistry: this.toolRegistry,\n };\n }\n\n const output = await tool.invoke(invokeParams, config);\n if (\n (isBaseMessage(output) && output._getType() === 'tool') ||\n isCommand(output)\n ) {\n return output;\n } else {\n return new ToolMessage({\n status: 'success',\n name: tool.name,\n content: typeof output === 'string' ? output : JSON.stringify(output),\n tool_call_id: call.id!,\n });\n }\n } catch (_e: unknown) {\n const e = _e as Error;\n if (!this.handleToolErrors) {\n throw e;\n }\n if (isGraphInterrupt(e)) {\n throw e;\n }\n if (this.errorHandler) {\n try {\n await this.errorHandler(\n {\n error: e,\n id: call.id!,\n name: call.name,\n input: call.args,\n },\n config.metadata\n );\n } catch (handlerError) {\n // eslint-disable-next-line no-console\n console.error('Error in errorHandler:', {\n toolName: call.name,\n toolCallId: call.id,\n toolArgs: call.args,\n stepId: this.toolCallStepIds?.get(call.id!),\n turn: this.toolUsageCount.get(call.name),\n originalError: {\n message: e.message,\n stack: e.stack ?? undefined,\n },\n handlerError:\n handlerError instanceof Error\n ? {\n message: handlerError.message,\n stack: handlerError.stack ?? undefined,\n }\n : {\n message: String(handlerError),\n stack: undefined,\n },\n });\n }\n }\n return new ToolMessage({\n status: 'error',\n content: `Error: ${e.message}\\n Please fix your mistakes.`,\n name: call.name,\n tool_call_id: call.id ?? '',\n });\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n protected async run(input: any, config: RunnableConfig): Promise<T> {\n let outputs: (BaseMessage | Command)[];\n\n if (this.isSendInput(input)) {\n outputs = [await this.runTool(input.lg_tool_call, config)];\n } else {\n let messages: BaseMessage[];\n if (Array.isArray(input)) {\n messages = input;\n } else if (this.isMessagesState(input)) {\n messages = input.messages;\n } else {\n throw new Error(\n 'ToolNode only accepts BaseMessage[] or { messages: BaseMessage[] } as input.'\n );\n }\n\n const toolMessageIds: Set<string> = new Set(\n messages\n .filter((msg) => msg._getType() === 'tool')\n .map((msg) => (msg as ToolMessage).tool_call_id)\n );\n\n let aiMessage: AIMessage | undefined;\n for (let i = messages.length - 1; i >= 0; i--) {\n const message = messages[i];\n if (isAIMessage(message)) {\n aiMessage = message;\n break;\n }\n }\n\n if (aiMessage == null || !isAIMessage(aiMessage)) {\n throw new Error('ToolNode only accepts AIMessages as input.');\n }\n\n if (this.loadRuntimeTools) {\n const { tools, toolMap } = this.loadRuntimeTools(\n aiMessage.tool_calls ?? []\n );\n this.toolMap =\n toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));\n this.programmaticCache = undefined; // Invalidate cache on toolMap change\n }\n\n outputs = await Promise.all(\n aiMessage.tool_calls\n ?.filter((call) => {\n /**\n * Filter out:\n * 1. Already processed tool calls (present in toolMessageIds)\n * 2. Server tool calls (e.g., web_search with IDs starting with 'srvtoolu_')\n * which are executed by the provider's API and don't require invocation\n */\n return (\n (call.id == null || !toolMessageIds.has(call.id)) &&\n !(call.id?.startsWith('srvtoolu_') ?? false)\n );\n })\n .map((call) => this.runTool(call, config)) ?? []\n );\n }\n\n if (!outputs.some(isCommand)) {\n return (Array.isArray(input) ? outputs : { messages: outputs }) as T;\n }\n\n const combinedOutputs: (\n | { messages: BaseMessage[] }\n | BaseMessage[]\n | Command\n )[] = [];\n let parentCommand: Command | null = null;\n\n for (const output of outputs) {\n if (isCommand(output)) {\n if (\n output.graph === Command.PARENT &&\n Array.isArray(output.goto) &&\n output.goto.every((send): send is Send => isSend(send))\n ) {\n /** Aggregate Send-based commands */\n if (parentCommand) {\n (parentCommand.goto as Send[]).push(...(output.goto as Send[]));\n } else {\n parentCommand = new Command({\n graph: Command.PARENT,\n goto: output.goto,\n });\n }\n } else {\n /**\n * Non-Send Commands (including handoff Commands with string goto)\n * are passed through as-is. For single handoffs, this works correctly.\n *\n * Note: Parallel handoffs (LLM calling multiple transfer tools simultaneously)\n * are not yet fully supported. For parallel agent execution, use direct edges\n * with edgeType: 'direct' instead of handoff edges.\n */\n combinedOutputs.push(output);\n }\n } else {\n combinedOutputs.push(\n Array.isArray(input) ? [output] : { messages: [output] }\n );\n }\n }\n\n if (parentCommand) {\n combinedOutputs.push(parentCommand);\n }\n\n return combinedOutputs as T;\n }\n\n private isSendInput(input: unknown): input is { lg_tool_call: ToolCall } {\n return (\n typeof input === 'object' && input != null && 'lg_tool_call' in input\n );\n }\n\n private isMessagesState(\n input: unknown\n ): input is { messages: BaseMessage[] } {\n return (\n typeof input === 'object' &&\n input != null &&\n 'messages' in input &&\n Array.isArray((input as { messages: unknown }).messages) &&\n (input as { messages: unknown[] }).messages.every(isBaseMessage)\n );\n }\n}\n\nfunction areToolCallsInvoked(\n message: AIMessage,\n invokedToolIds?: Set<string>\n): boolean {\n if (!invokedToolIds || invokedToolIds.size === 0) return false;\n return (\n message.tool_calls?.every(\n (toolCall) => toolCall.id != null && invokedToolIds.has(toolCall.id)\n ) ?? false\n );\n}\n\nexport function toolsCondition<T extends string>(\n state: BaseMessage[] | typeof MessagesAnnotation.State,\n toolNode: T,\n invokedToolIds?: Set<string>\n): T | typeof END {\n const message: AIMessage = Array.isArray(state)\n ? state[state.length - 1]\n : state.messages[state.messages.length - 1];\n\n if (\n 'tool_calls' in message &&\n (message.tool_calls?.length ?? 0) > 0 &&\n !areToolCallsInvoked(message, invokedToolIds)\n ) {\n return toolNode;\n } else {\n return END;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ToolNode.mjs","sources":["../../../src/tools/ToolNode.ts"],"sourcesContent":["import { ToolCall } from '@langchain/core/messages/tool';\nimport {\n ToolMessage,\n isAIMessage,\n isBaseMessage,\n} from '@langchain/core/messages';\nimport {\n END,\n Send,\n Command,\n isCommand,\n isGraphInterrupt,\n MessagesAnnotation,\n} from '@langchain/langgraph';\nimport type {\n RunnableConfig,\n RunnableToolLike,\n} from '@langchain/core/runnables';\nimport type { BaseMessage, AIMessage } from '@langchain/core/messages';\nimport type { StructuredToolInterface } from '@langchain/core/tools';\nimport type * as t from '@/types';\nimport { RunnableCallable } from '@/utils';\nimport { Constants } from '@/common';\n\n/**\n * Helper to check if a value is a Send object\n */\nfunction isSend(value: unknown): value is Send {\n return value instanceof Send;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport class ToolNode<T = any> extends RunnableCallable<T, T> {\n private toolMap: Map<string, StructuredToolInterface | RunnableToolLike>;\n private loadRuntimeTools?: t.ToolRefGenerator;\n handleToolErrors = true;\n trace = false;\n toolCallStepIds?: Map<string, string>;\n errorHandler?: t.ToolNodeConstructorParams['errorHandler'];\n private toolUsageCount: Map<string, number>;\n /** Tool registry for filtering (lazy computation of programmatic maps) */\n private toolRegistry?: t.LCToolRegistry;\n /** Cached programmatic tools (computed once on first PTC call) */\n private programmaticCache?: t.ProgrammaticCache;\n\n constructor({\n tools,\n toolMap,\n name,\n tags,\n errorHandler,\n toolCallStepIds,\n handleToolErrors,\n loadRuntimeTools,\n toolRegistry,\n }: t.ToolNodeConstructorParams) {\n super({ name, tags, func: (input, config) => this.run(input, config) });\n this.toolMap = toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));\n this.toolCallStepIds = toolCallStepIds;\n this.handleToolErrors = handleToolErrors ?? this.handleToolErrors;\n this.loadRuntimeTools = loadRuntimeTools;\n this.errorHandler = errorHandler;\n this.toolUsageCount = new Map<string, number>();\n this.toolRegistry = toolRegistry;\n }\n\n /**\n * Returns cached programmatic tools, computing once on first access.\n * Single iteration builds both toolMap and toolDefs simultaneously.\n */\n private getProgrammaticTools(): { toolMap: t.ToolMap; toolDefs: t.LCTool[] } {\n if (this.programmaticCache) return this.programmaticCache;\n\n const toolMap: t.ToolMap = new Map();\n const toolDefs: t.LCTool[] = [];\n\n if (this.toolRegistry) {\n for (const [name, toolDef] of this.toolRegistry) {\n if (\n (toolDef.allowed_callers ?? ['direct']).includes('code_execution')\n ) {\n toolDefs.push(toolDef);\n const tool = this.toolMap.get(name);\n if (tool) toolMap.set(name, tool);\n }\n }\n }\n\n this.programmaticCache = { toolMap, toolDefs };\n return this.programmaticCache;\n }\n\n /**\n * Returns a snapshot of the current tool usage counts.\n * @returns A ReadonlyMap where keys are tool names and values are their usage counts.\n */\n public getToolUsageCounts(): ReadonlyMap<string, number> {\n return new Map(this.toolUsageCount); // Return a copy\n }\n\n /**\n * Runs a single tool call with error handling\n */\n protected async runTool(\n call: ToolCall,\n config: RunnableConfig\n ): Promise<BaseMessage | Command> {\n const tool = this.toolMap.get(call.name);\n try {\n if (tool === undefined) {\n throw new Error(`Tool \"${call.name}\" not found.`);\n }\n const turn = this.toolUsageCount.get(call.name) ?? 0;\n this.toolUsageCount.set(call.name, turn + 1);\n const args = call.args;\n const stepId = this.toolCallStepIds?.get(call.id!);\n\n // Build invoke params - LangChain extracts non-schema fields to config.toolCall\n let invokeParams: Record<string, unknown> = {\n ...call,\n args,\n type: 'tool_call',\n stepId,\n turn,\n };\n\n // Inject runtime data for special tools (becomes available at config.toolCall)\n if (call.name === Constants.PROGRAMMATIC_TOOL_CALLING) {\n const { toolMap, toolDefs } = this.getProgrammaticTools();\n invokeParams = {\n ...invokeParams,\n toolMap,\n toolDefs,\n };\n } else if (call.name === Constants.TOOL_SEARCH_REGEX) {\n invokeParams = {\n ...invokeParams,\n toolRegistry: this.toolRegistry,\n };\n }\n\n const output = await tool.invoke(invokeParams, config);\n if (\n (isBaseMessage(output) && output._getType() === 'tool') ||\n isCommand(output)\n ) {\n return output;\n } else {\n return new ToolMessage({\n status: 'success',\n name: tool.name,\n content: typeof output === 'string' ? output : JSON.stringify(output),\n tool_call_id: call.id!,\n });\n }\n } catch (_e: unknown) {\n const e = _e as Error;\n if (!this.handleToolErrors) {\n throw e;\n }\n if (isGraphInterrupt(e)) {\n throw e;\n }\n if (this.errorHandler) {\n try {\n await this.errorHandler(\n {\n error: e,\n id: call.id!,\n name: call.name,\n input: call.args,\n },\n config.metadata\n );\n } catch (handlerError) {\n // eslint-disable-next-line no-console\n console.error('Error in errorHandler:', {\n toolName: call.name,\n toolCallId: call.id,\n toolArgs: call.args,\n stepId: this.toolCallStepIds?.get(call.id!),\n turn: this.toolUsageCount.get(call.name),\n originalError: {\n message: e.message,\n stack: e.stack ?? undefined,\n },\n handlerError:\n handlerError instanceof Error\n ? {\n message: handlerError.message,\n stack: handlerError.stack ?? undefined,\n }\n : {\n message: String(handlerError),\n stack: undefined,\n },\n });\n }\n }\n return new ToolMessage({\n status: 'error',\n content: `Error: ${e.message}\\n Please fix your mistakes.`,\n name: call.name,\n tool_call_id: call.id ?? '',\n });\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n protected async run(input: any, config: RunnableConfig): Promise<T> {\n let outputs: (BaseMessage | Command)[];\n\n if (this.isSendInput(input)) {\n outputs = [await this.runTool(input.lg_tool_call, config)];\n } else {\n let messages: BaseMessage[];\n if (Array.isArray(input)) {\n messages = input;\n } else if (this.isMessagesState(input)) {\n messages = input.messages;\n } else {\n throw new Error(\n 'ToolNode only accepts BaseMessage[] or { messages: BaseMessage[] } as input.'\n );\n }\n\n const toolMessageIds: Set<string> = new Set(\n messages\n .filter((msg) => msg._getType() === 'tool')\n .map((msg) => (msg as ToolMessage).tool_call_id)\n );\n\n let aiMessage: AIMessage | undefined;\n for (let i = messages.length - 1; i >= 0; i--) {\n const message = messages[i];\n if (isAIMessage(message)) {\n aiMessage = message;\n break;\n }\n }\n\n if (aiMessage == null || !isAIMessage(aiMessage)) {\n throw new Error('ToolNode only accepts AIMessages as input.');\n }\n\n if (this.loadRuntimeTools) {\n const { tools, toolMap } = this.loadRuntimeTools(\n aiMessage.tool_calls ?? []\n );\n this.toolMap =\n toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));\n this.programmaticCache = undefined; // Invalidate cache on toolMap change\n }\n\n outputs = await Promise.all(\n aiMessage.tool_calls\n ?.filter((call) => {\n /**\n * Filter out:\n * 1. Already processed tool calls (present in toolMessageIds)\n * 2. Server tool calls (e.g., web_search with IDs starting with 'srvtoolu_')\n * which are executed by the provider's API and don't require invocation\n */\n return (\n (call.id == null || !toolMessageIds.has(call.id)) &&\n !(call.id?.startsWith('srvtoolu_') ?? false)\n );\n })\n .map((call) => this.runTool(call, config)) ?? []\n );\n }\n\n if (!outputs.some(isCommand)) {\n return (Array.isArray(input) ? outputs : { messages: outputs }) as T;\n }\n\n const combinedOutputs: (\n | { messages: BaseMessage[] }\n | BaseMessage[]\n | Command\n )[] = [];\n let parentCommand: Command | null = null;\n\n /**\n * Collect handoff commands (Commands with string goto and Command.PARENT)\n * for potential parallel handoff aggregation\n */\n const handoffCommands: Command[] = [];\n const nonCommandOutputs: BaseMessage[] = [];\n\n for (const output of outputs) {\n if (isCommand(output)) {\n if (\n output.graph === Command.PARENT &&\n Array.isArray(output.goto) &&\n output.goto.every((send): send is Send => isSend(send))\n ) {\n /** Aggregate Send-based commands */\n if (parentCommand) {\n (parentCommand.goto as Send[]).push(...(output.goto as Send[]));\n } else {\n parentCommand = new Command({\n graph: Command.PARENT,\n goto: output.goto,\n });\n }\n } else if (output.graph === Command.PARENT) {\n /**\n * Handoff Command with destination.\n * Handle both string ('agent') and array (['agent']) formats.\n * Collect for potential parallel aggregation.\n */\n const goto = output.goto;\n const isSingleStringDest = typeof goto === 'string';\n const isSingleArrayDest =\n Array.isArray(goto) &&\n goto.length === 1 &&\n typeof goto[0] === 'string';\n\n if (isSingleStringDest || isSingleArrayDest) {\n handoffCommands.push(output);\n } else {\n /** Multi-destination or other command - pass through */\n combinedOutputs.push(output);\n }\n } else {\n /** Other commands - pass through */\n combinedOutputs.push(output);\n }\n } else {\n nonCommandOutputs.push(output);\n combinedOutputs.push(\n Array.isArray(input) ? [output] : { messages: [output] }\n );\n }\n }\n\n /**\n * Handle handoff commands - convert to Send objects for parallel execution\n * when multiple handoffs are requested\n */\n if (handoffCommands.length > 1) {\n /**\n * Multiple parallel handoffs - convert to Send objects.\n * Each Send carries its own state with the appropriate messages.\n * This enables LLM-initiated parallel execution when calling multiple\n * transfer tools simultaneously.\n */\n const sends = handoffCommands.map((cmd) => {\n /** Extract destination - handle both string and array formats */\n const goto = cmd.goto;\n const destination =\n typeof goto === 'string' ? goto : (goto as string[])[0];\n return new Send(destination, cmd.update);\n });\n\n const parallelCommand = new Command({\n graph: Command.PARENT,\n goto: sends,\n });\n combinedOutputs.push(parallelCommand);\n } else if (handoffCommands.length === 1) {\n /** Single handoff - pass through as-is */\n combinedOutputs.push(handoffCommands[0]);\n }\n\n if (parentCommand) {\n combinedOutputs.push(parentCommand);\n }\n\n return combinedOutputs as T;\n }\n\n private isSendInput(input: unknown): input is { lg_tool_call: ToolCall } {\n return (\n typeof input === 'object' && input != null && 'lg_tool_call' in input\n );\n }\n\n private isMessagesState(\n input: unknown\n ): input is { messages: BaseMessage[] } {\n return (\n typeof input === 'object' &&\n input != null &&\n 'messages' in input &&\n Array.isArray((input as { messages: unknown }).messages) &&\n (input as { messages: unknown[] }).messages.every(isBaseMessage)\n );\n }\n}\n\nfunction areToolCallsInvoked(\n message: AIMessage,\n invokedToolIds?: Set<string>\n): boolean {\n if (!invokedToolIds || invokedToolIds.size === 0) return false;\n return (\n message.tool_calls?.every(\n (toolCall) => toolCall.id != null && invokedToolIds.has(toolCall.id)\n ) ?? false\n );\n}\n\nexport function toolsCondition<T extends string>(\n state: BaseMessage[] | typeof MessagesAnnotation.State,\n toolNode: T,\n invokedToolIds?: Set<string>\n): T | typeof END {\n const message: AIMessage = Array.isArray(state)\n ? state[state.length - 1]\n : state.messages[state.messages.length - 1];\n\n if (\n 'tool_calls' in message &&\n (message.tool_calls?.length ?? 0) > 0 &&\n !areToolCallsInvoked(message, invokedToolIds)\n ) {\n return toolNode;\n } else {\n return END;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -14,6 +14,8 @@ export declare class AgentContext {
|
|
|
14
14
|
static fromConfig(agentConfig: t.AgentInputs, tokenCounter?: t.TokenCounter, indexTokenCountMap?: Record<string, number>): AgentContext;
|
|
15
15
|
/** Agent identifier */
|
|
16
16
|
agentId: string;
|
|
17
|
+
/** Human-readable name for this agent (used in handoff context). Falls back to agentId if not provided. */
|
|
18
|
+
name?: string;
|
|
17
19
|
/** Provider for this specific agent */
|
|
18
20
|
provider: Providers;
|
|
19
21
|
/** Client options for this agent */
|
|
@@ -69,8 +71,16 @@ export declare class AgentContext {
|
|
|
69
71
|
tokenCalculationPromise?: Promise<void>;
|
|
70
72
|
/** Format content blocks as strings (for legacy compatibility) */
|
|
71
73
|
useLegacyContent: boolean;
|
|
72
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Handoff context when this agent receives control via handoff.
|
|
76
|
+
* Contains source agent name for system message identity context.
|
|
77
|
+
*/
|
|
78
|
+
handoffContext?: {
|
|
79
|
+
sourceAgentName: string;
|
|
80
|
+
};
|
|
81
|
+
constructor({ agentId, name, provider, clientOptions, maxContextTokens, streamBuffer, tokenCounter, tools, toolMap, toolRegistry, instructions, additionalInstructions, reasoningKey, toolEnd, instructionTokens, useLegacyContent, }: {
|
|
73
82
|
agentId: string;
|
|
83
|
+
name?: string;
|
|
74
84
|
provider: Providers;
|
|
75
85
|
clientOptions?: t.ClientOptions;
|
|
76
86
|
maxContextTokens?: number;
|
|
@@ -109,8 +119,14 @@ export declare class AgentContext {
|
|
|
109
119
|
initializeSystemRunnable(): void;
|
|
110
120
|
/**
|
|
111
121
|
* Builds the raw instructions string (without creating SystemMessage).
|
|
122
|
+
* Includes agent identity preamble and handoff context when available.
|
|
112
123
|
*/
|
|
113
124
|
private buildInstructionsString;
|
|
125
|
+
/**
|
|
126
|
+
* Builds the agent identity preamble including handoff context if present.
|
|
127
|
+
* This helps the agent understand its role in the multi-agent workflow.
|
|
128
|
+
*/
|
|
129
|
+
private buildIdentityPreamble;
|
|
114
130
|
/**
|
|
115
131
|
* Build system runnable from pre-built instructions string.
|
|
116
132
|
* Only called when content has actually changed.
|
|
@@ -135,6 +151,18 @@ export declare class AgentContext {
|
|
|
135
151
|
* @returns LCToolRegistry with tool definitions
|
|
136
152
|
*/
|
|
137
153
|
getDeferredToolRegistry(onlyDeferred?: boolean): t.LCToolRegistry;
|
|
154
|
+
/**
|
|
155
|
+
* Sets the handoff context for this agent.
|
|
156
|
+
* Call this when the agent receives control via handoff from another agent.
|
|
157
|
+
* Marks system runnable as stale to include handoff context in system message.
|
|
158
|
+
* @param sourceAgentName - The name of the agent that handed off to this agent
|
|
159
|
+
*/
|
|
160
|
+
setHandoffContext(sourceAgentName: string): void;
|
|
161
|
+
/**
|
|
162
|
+
* Clears any handoff context.
|
|
163
|
+
* Call this when resetting the agent or when handoff context is no longer relevant.
|
|
164
|
+
*/
|
|
165
|
+
clearHandoffContext(): void;
|
|
138
166
|
/**
|
|
139
167
|
* Marks tools as discovered via tool search.
|
|
140
168
|
* Discovered tools will be included in the next model binding.
|
|
@@ -75,6 +75,9 @@ export declare class MultiAgentGraph extends StandardGraph {
|
|
|
75
75
|
private createHandoffTools;
|
|
76
76
|
/**
|
|
77
77
|
* Create handoff tools for an edge (handles multiple destinations)
|
|
78
|
+
* @param edge - The graph edge defining the handoff
|
|
79
|
+
* @param sourceAgentId - The ID of the agent that will perform the handoff
|
|
80
|
+
* @param sourceAgentName - The human-readable name of the source agent
|
|
78
81
|
*/
|
|
79
82
|
private createHandoffToolsForEdge;
|
|
80
83
|
/**
|
|
@@ -84,14 +87,14 @@ export declare class MultiAgentGraph extends StandardGraph {
|
|
|
84
87
|
/**
|
|
85
88
|
* Detects if the current agent is receiving a handoff and processes the messages accordingly.
|
|
86
89
|
* Returns filtered messages with the transfer tool call/message removed, plus any instructions
|
|
87
|
-
* extracted from the transfer
|
|
90
|
+
* and source agent information extracted from the transfer.
|
|
88
91
|
*
|
|
89
92
|
* Supports both single handoffs (last message is the transfer) and parallel handoffs
|
|
90
93
|
* (multiple transfer ToolMessages, need to find the one targeting this agent).
|
|
91
94
|
*
|
|
92
95
|
* @param messages - Current state messages
|
|
93
96
|
* @param agentId - The agent ID to check for handoff reception
|
|
94
|
-
* @returns Object with filtered messages
|
|
97
|
+
* @returns Object with filtered messages, extracted instructions, and source agent, or null if not a handoff
|
|
95
98
|
*/
|
|
96
99
|
private processHandoffReception;
|
|
97
100
|
/**
|
|
@@ -236,6 +236,8 @@ export type MultiAgentGraphInput = StandardGraphInput & {
|
|
|
236
236
|
};
|
|
237
237
|
export interface AgentInputs {
|
|
238
238
|
agentId: string;
|
|
239
|
+
/** Human-readable name for the agent (used in handoff context). Defaults to agentId if not provided. */
|
|
240
|
+
name?: string;
|
|
239
241
|
toolEnd?: boolean;
|
|
240
242
|
toolMap?: ToolMap;
|
|
241
243
|
tools?: GraphTools;
|
package/package.json
CHANGED
|
@@ -27,6 +27,7 @@ export class AgentContext {
|
|
|
27
27
|
): AgentContext {
|
|
28
28
|
const {
|
|
29
29
|
agentId,
|
|
30
|
+
name,
|
|
30
31
|
provider,
|
|
31
32
|
clientOptions,
|
|
32
33
|
tools,
|
|
@@ -43,6 +44,7 @@ export class AgentContext {
|
|
|
43
44
|
|
|
44
45
|
const agentContext = new AgentContext({
|
|
45
46
|
agentId,
|
|
47
|
+
name: name ?? agentId,
|
|
46
48
|
provider,
|
|
47
49
|
clientOptions,
|
|
48
50
|
maxContextTokens,
|
|
@@ -85,6 +87,8 @@ export class AgentContext {
|
|
|
85
87
|
|
|
86
88
|
/** Agent identifier */
|
|
87
89
|
agentId: string;
|
|
90
|
+
/** Human-readable name for this agent (used in handoff context). Falls back to agentId if not provided. */
|
|
91
|
+
name?: string;
|
|
88
92
|
/** Provider for this specific agent */
|
|
89
93
|
provider: Providers;
|
|
90
94
|
/** Client options for this agent */
|
|
@@ -145,9 +149,17 @@ export class AgentContext {
|
|
|
145
149
|
tokenCalculationPromise?: Promise<void>;
|
|
146
150
|
/** Format content blocks as strings (for legacy compatibility) */
|
|
147
151
|
useLegacyContent: boolean = false;
|
|
152
|
+
/**
|
|
153
|
+
* Handoff context when this agent receives control via handoff.
|
|
154
|
+
* Contains source agent name for system message identity context.
|
|
155
|
+
*/
|
|
156
|
+
handoffContext?: {
|
|
157
|
+
sourceAgentName: string;
|
|
158
|
+
};
|
|
148
159
|
|
|
149
160
|
constructor({
|
|
150
161
|
agentId,
|
|
162
|
+
name,
|
|
151
163
|
provider,
|
|
152
164
|
clientOptions,
|
|
153
165
|
maxContextTokens,
|
|
@@ -164,6 +176,7 @@ export class AgentContext {
|
|
|
164
176
|
useLegacyContent,
|
|
165
177
|
}: {
|
|
166
178
|
agentId: string;
|
|
179
|
+
name?: string;
|
|
167
180
|
provider: Providers;
|
|
168
181
|
clientOptions?: t.ClientOptions;
|
|
169
182
|
maxContextTokens?: number;
|
|
@@ -180,6 +193,7 @@ export class AgentContext {
|
|
|
180
193
|
useLegacyContent?: boolean;
|
|
181
194
|
}) {
|
|
182
195
|
this.agentId = agentId;
|
|
196
|
+
this.name = name;
|
|
183
197
|
this.provider = provider;
|
|
184
198
|
this.clientOptions = clientOptions;
|
|
185
199
|
this.maxContextTokens = maxContextTokens;
|
|
@@ -293,27 +307,61 @@ export class AgentContext {
|
|
|
293
307
|
|
|
294
308
|
/**
|
|
295
309
|
* Builds the raw instructions string (without creating SystemMessage).
|
|
310
|
+
* Includes agent identity preamble and handoff context when available.
|
|
296
311
|
*/
|
|
297
312
|
private buildInstructionsString(): string {
|
|
298
|
-
|
|
313
|
+
const parts: string[] = [];
|
|
314
|
+
|
|
315
|
+
/** Build agent identity and handoff context preamble */
|
|
316
|
+
const identityPreamble = this.buildIdentityPreamble();
|
|
317
|
+
if (identityPreamble) {
|
|
318
|
+
parts.push(identityPreamble);
|
|
319
|
+
}
|
|
299
320
|
|
|
321
|
+
/** Add main instructions */
|
|
322
|
+
if (this.instructions != null && this.instructions !== '') {
|
|
323
|
+
parts.push(this.instructions);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/** Add additional instructions */
|
|
300
327
|
if (
|
|
301
328
|
this.additionalInstructions != null &&
|
|
302
329
|
this.additionalInstructions !== ''
|
|
303
330
|
) {
|
|
304
|
-
|
|
305
|
-
? `${result}\n\n${this.additionalInstructions}`
|
|
306
|
-
: this.additionalInstructions;
|
|
331
|
+
parts.push(this.additionalInstructions);
|
|
307
332
|
}
|
|
308
333
|
|
|
334
|
+
/** Add programmatic tools documentation */
|
|
309
335
|
const programmaticToolsDoc = this.buildProgrammaticOnlyToolsInstructions();
|
|
310
336
|
if (programmaticToolsDoc) {
|
|
311
|
-
|
|
312
|
-
? `${result}${programmaticToolsDoc}`
|
|
313
|
-
: programmaticToolsDoc;
|
|
337
|
+
parts.push(programmaticToolsDoc);
|
|
314
338
|
}
|
|
315
339
|
|
|
316
|
-
return
|
|
340
|
+
return parts.join('\n\n');
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Builds the agent identity preamble including handoff context if present.
|
|
345
|
+
* This helps the agent understand its role in the multi-agent workflow.
|
|
346
|
+
*/
|
|
347
|
+
private buildIdentityPreamble(): string {
|
|
348
|
+
/** Only include preamble if we have handoff context (indicates multi-agent workflow) */
|
|
349
|
+
if (!this.handoffContext) return '';
|
|
350
|
+
|
|
351
|
+
/** Use name (falls back to agentId if not provided) */
|
|
352
|
+
const displayName = this.name ?? this.agentId;
|
|
353
|
+
|
|
354
|
+
const lines: string[] = [];
|
|
355
|
+
lines.push('## Agent Context');
|
|
356
|
+
lines.push(`You are the "${displayName}" agent.`);
|
|
357
|
+
|
|
358
|
+
if (this.handoffContext.sourceAgentName) {
|
|
359
|
+
lines.push(
|
|
360
|
+
`Control was transferred to you from the "${this.handoffContext.sourceAgentName}" agent.`
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return lines.join('\n');
|
|
317
365
|
}
|
|
318
366
|
|
|
319
367
|
/**
|
|
@@ -393,6 +441,7 @@ export class AgentContext {
|
|
|
393
441
|
this.tokenTypeSwitch = undefined;
|
|
394
442
|
this.currentTokenType = ContentTypes.TEXT;
|
|
395
443
|
this.discoveredToolNames.clear();
|
|
444
|
+
this.handoffContext = undefined;
|
|
396
445
|
}
|
|
397
446
|
|
|
398
447
|
/**
|
|
@@ -472,6 +521,28 @@ export class AgentContext {
|
|
|
472
521
|
return registry;
|
|
473
522
|
}
|
|
474
523
|
|
|
524
|
+
/**
|
|
525
|
+
* Sets the handoff context for this agent.
|
|
526
|
+
* Call this when the agent receives control via handoff from another agent.
|
|
527
|
+
* Marks system runnable as stale to include handoff context in system message.
|
|
528
|
+
* @param sourceAgentName - The name of the agent that handed off to this agent
|
|
529
|
+
*/
|
|
530
|
+
setHandoffContext(sourceAgentName: string): void {
|
|
531
|
+
this.handoffContext = { sourceAgentName };
|
|
532
|
+
this.systemRunnableStale = true;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Clears any handoff context.
|
|
537
|
+
* Call this when resetting the agent or when handoff context is no longer relevant.
|
|
538
|
+
*/
|
|
539
|
+
clearHandoffContext(): void {
|
|
540
|
+
if (this.handoffContext) {
|
|
541
|
+
this.handoffContext = undefined;
|
|
542
|
+
this.systemRunnableStale = true;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
475
546
|
/**
|
|
476
547
|
* Marks tools as discovered via tool search.
|
|
477
548
|
* Discovered tools will be included in the next model binding.
|
|
@@ -249,8 +249,11 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
249
249
|
|
|
250
250
|
// Create handoff tools for this agent's outgoing edges
|
|
251
251
|
const handoffTools: t.GenericTool[] = [];
|
|
252
|
+
const sourceAgentName = agentContext.name ?? agentId;
|
|
252
253
|
for (const edge of edges) {
|
|
253
|
-
handoffTools.push(
|
|
254
|
+
handoffTools.push(
|
|
255
|
+
...this.createHandoffToolsForEdge(edge, agentId, sourceAgentName)
|
|
256
|
+
);
|
|
254
257
|
}
|
|
255
258
|
|
|
256
259
|
// Add handoff tools to the agent's existing tools
|
|
@@ -263,8 +266,15 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
263
266
|
|
|
264
267
|
/**
|
|
265
268
|
* Create handoff tools for an edge (handles multiple destinations)
|
|
269
|
+
* @param edge - The graph edge defining the handoff
|
|
270
|
+
* @param sourceAgentId - The ID of the agent that will perform the handoff
|
|
271
|
+
* @param sourceAgentName - The human-readable name of the source agent
|
|
266
272
|
*/
|
|
267
|
-
private createHandoffToolsForEdge(
|
|
273
|
+
private createHandoffToolsForEdge(
|
|
274
|
+
edge: t.GraphEdge,
|
|
275
|
+
sourceAgentId: string,
|
|
276
|
+
sourceAgentName: string
|
|
277
|
+
): t.GenericTool[] {
|
|
268
278
|
const tools: t.GenericTool[] = [];
|
|
269
279
|
const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];
|
|
270
280
|
|
|
@@ -319,6 +329,8 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
319
329
|
additional_kwargs: {
|
|
320
330
|
/** Store destination for programmatic access in handoff detection */
|
|
321
331
|
handoff_destination: destination,
|
|
332
|
+
/** Store source agent name for receiving agent to know who handed off */
|
|
333
|
+
handoff_source_name: sourceAgentName,
|
|
322
334
|
},
|
|
323
335
|
});
|
|
324
336
|
|
|
@@ -377,13 +389,81 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
377
389
|
content,
|
|
378
390
|
name: toolName,
|
|
379
391
|
tool_call_id: toolCallId,
|
|
392
|
+
additional_kwargs: {
|
|
393
|
+
/** Store source agent name for receiving agent to know who handed off */
|
|
394
|
+
handoff_source_name: sourceAgentName,
|
|
395
|
+
},
|
|
380
396
|
});
|
|
381
397
|
|
|
382
398
|
const state = getCurrentTaskInput() as t.BaseGraphState;
|
|
383
399
|
|
|
400
|
+
/**
|
|
401
|
+
* For parallel handoff support:
|
|
402
|
+
* Build messages that include ONLY this tool call's context.
|
|
403
|
+
* This prevents errors when LLM calls multiple transfers simultaneously -
|
|
404
|
+
* each destination gets a valid AIMessage with matching tool_call and tool_result.
|
|
405
|
+
*
|
|
406
|
+
* Strategy:
|
|
407
|
+
* 1. Find the AIMessage containing this tool call
|
|
408
|
+
* 2. Create a filtered AIMessage with ONLY this tool_call
|
|
409
|
+
* 3. Include all messages before the AIMessage plus the filtered pair
|
|
410
|
+
*/
|
|
411
|
+
const messages = state.messages;
|
|
412
|
+
let filteredMessages = messages;
|
|
413
|
+
let aiMessageIndex = -1;
|
|
414
|
+
|
|
415
|
+
/** Find the AIMessage containing this tool call */
|
|
416
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
417
|
+
const msg = messages[i];
|
|
418
|
+
if (msg.getType() === 'ai') {
|
|
419
|
+
const aiMsg = msg as AIMessage;
|
|
420
|
+
const hasThisCall = aiMsg.tool_calls?.some(
|
|
421
|
+
(tc) => tc.id === toolCallId
|
|
422
|
+
);
|
|
423
|
+
if (hasThisCall === true) {
|
|
424
|
+
aiMessageIndex = i;
|
|
425
|
+
break;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (aiMessageIndex >= 0) {
|
|
431
|
+
const originalAiMsg = messages[aiMessageIndex] as AIMessage;
|
|
432
|
+
const thisToolCall = originalAiMsg.tool_calls?.find(
|
|
433
|
+
(tc) => tc.id === toolCallId
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
if (
|
|
437
|
+
thisToolCall != null &&
|
|
438
|
+
(originalAiMsg.tool_calls?.length ?? 0) > 1
|
|
439
|
+
) {
|
|
440
|
+
/**
|
|
441
|
+
* Multiple tool calls - create filtered AIMessage with ONLY this call.
|
|
442
|
+
* This ensures valid message structure for parallel handoffs.
|
|
443
|
+
*/
|
|
444
|
+
const filteredAiMsg = new AIMessage({
|
|
445
|
+
content: originalAiMsg.content,
|
|
446
|
+
tool_calls: [thisToolCall],
|
|
447
|
+
id: originalAiMsg.id,
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
filteredMessages = [
|
|
451
|
+
...messages.slice(0, aiMessageIndex),
|
|
452
|
+
filteredAiMsg,
|
|
453
|
+
toolMessage,
|
|
454
|
+
];
|
|
455
|
+
} else {
|
|
456
|
+
/** Single tool call - use messages as-is */
|
|
457
|
+
filteredMessages = messages.concat(toolMessage);
|
|
458
|
+
}
|
|
459
|
+
} else {
|
|
460
|
+
/** Fallback - append tool message */
|
|
461
|
+
filteredMessages = messages.concat(toolMessage);
|
|
462
|
+
}
|
|
463
|
+
|
|
384
464
|
return new Command({
|
|
385
465
|
goto: destination,
|
|
386
|
-
update: { messages:
|
|
466
|
+
update: { messages: filteredMessages },
|
|
387
467
|
graph: Command.PARENT,
|
|
388
468
|
});
|
|
389
469
|
},
|
|
@@ -418,19 +498,23 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
418
498
|
/**
|
|
419
499
|
* Detects if the current agent is receiving a handoff and processes the messages accordingly.
|
|
420
500
|
* Returns filtered messages with the transfer tool call/message removed, plus any instructions
|
|
421
|
-
* extracted from the transfer
|
|
501
|
+
* and source agent information extracted from the transfer.
|
|
422
502
|
*
|
|
423
503
|
* Supports both single handoffs (last message is the transfer) and parallel handoffs
|
|
424
504
|
* (multiple transfer ToolMessages, need to find the one targeting this agent).
|
|
425
505
|
*
|
|
426
506
|
* @param messages - Current state messages
|
|
427
507
|
* @param agentId - The agent ID to check for handoff reception
|
|
428
|
-
* @returns Object with filtered messages
|
|
508
|
+
* @returns Object with filtered messages, extracted instructions, and source agent, or null if not a handoff
|
|
429
509
|
*/
|
|
430
510
|
private processHandoffReception(
|
|
431
511
|
messages: BaseMessage[],
|
|
432
512
|
agentId: string
|
|
433
|
-
): {
|
|
513
|
+
): {
|
|
514
|
+
filteredMessages: BaseMessage[];
|
|
515
|
+
instructions: string | null;
|
|
516
|
+
sourceAgentName: string | null;
|
|
517
|
+
} | null {
|
|
434
518
|
if (messages.length === 0) return null;
|
|
435
519
|
|
|
436
520
|
/**
|
|
@@ -486,6 +570,11 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
486
570
|
const instructionsMatch = contentStr.match(HANDOFF_INSTRUCTIONS_PATTERN);
|
|
487
571
|
const instructions = instructionsMatch?.[1]?.trim() ?? null;
|
|
488
572
|
|
|
573
|
+
/** Extract source agent name from additional_kwargs */
|
|
574
|
+
const handoffSourceName = toolMessage.additional_kwargs.handoff_source_name;
|
|
575
|
+
const sourceAgentName =
|
|
576
|
+
typeof handoffSourceName === 'string' ? handoffSourceName : null;
|
|
577
|
+
|
|
489
578
|
/** Get the tool_call_id to find and filter the AI message's tool call */
|
|
490
579
|
const toolCallId = toolMessage.tool_call_id;
|
|
491
580
|
|
|
@@ -559,7 +648,7 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
559
648
|
filteredMessages.push(msg);
|
|
560
649
|
}
|
|
561
650
|
|
|
562
|
-
return { filteredMessages, instructions };
|
|
651
|
+
return { filteredMessages, instructions, sourceAgentName };
|
|
563
652
|
}
|
|
564
653
|
|
|
565
654
|
/**
|
|
@@ -647,7 +736,21 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
647
736
|
);
|
|
648
737
|
|
|
649
738
|
if (handoffContext !== null) {
|
|
650
|
-
const { filteredMessages, instructions } =
|
|
739
|
+
const { filteredMessages, instructions, sourceAgentName } =
|
|
740
|
+
handoffContext;
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* Set handoff context on the receiving agent.
|
|
744
|
+
* This updates the system message to include agent identity info.
|
|
745
|
+
*/
|
|
746
|
+
const agentContext = this.agentContexts.get(agentId);
|
|
747
|
+
if (
|
|
748
|
+
agentContext &&
|
|
749
|
+
sourceAgentName != null &&
|
|
750
|
+
sourceAgentName !== ''
|
|
751
|
+
) {
|
|
752
|
+
agentContext.setHandoffContext(sourceAgentName);
|
|
753
|
+
}
|
|
651
754
|
|
|
652
755
|
/** Build messages for the receiving agent */
|
|
653
756
|
let messagesForAgent = filteredMessages;
|
|
@@ -662,7 +765,6 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
662
765
|
}
|
|
663
766
|
|
|
664
767
|
/** Update token map if we have a token counter */
|
|
665
|
-
const agentContext = this.agentContexts.get(agentId);
|
|
666
768
|
if (agentContext?.tokenCounter && hasInstructions) {
|
|
667
769
|
const freshTokenMap: Record<string, number> = {};
|
|
668
770
|
for (
|