@librechat/agents 3.1.66 → 3.1.67-dev.4
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 +23 -3
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/common/enum.cjs +16 -0
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +91 -0
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/hooks/HookRegistry.cjs +162 -0
- package/dist/cjs/hooks/HookRegistry.cjs.map +1 -0
- package/dist/cjs/hooks/executeHooks.cjs +276 -0
- package/dist/cjs/hooks/executeHooks.cjs.map +1 -0
- package/dist/cjs/hooks/matchers.cjs +256 -0
- package/dist/cjs/hooks/matchers.cjs.map +1 -0
- package/dist/cjs/hooks/types.cjs +27 -0
- package/dist/cjs/hooks/types.cjs.map +1 -0
- package/dist/cjs/main.cjs +53 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +74 -12
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/run.cjs +111 -0
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +44 -0
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/BashExecutor.cjs +175 -0
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -0
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +296 -0
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -0
- package/dist/cjs/tools/ReadFile.cjs +43 -0
- package/dist/cjs/tools/ReadFile.cjs.map +1 -0
- package/dist/cjs/tools/SkillTool.cjs +50 -0
- package/dist/cjs/tools/SkillTool.cjs.map +1 -0
- package/dist/cjs/tools/SubagentTool.cjs +92 -0
- package/dist/cjs/tools/SubagentTool.cjs.map +1 -0
- package/dist/cjs/tools/ToolNode.cjs +304 -140
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/skillCatalog.cjs +84 -0
- package/dist/cjs/tools/skillCatalog.cjs.map +1 -0
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +511 -0
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -0
- package/dist/esm/agents/AgentContext.mjs +23 -3
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/common/enum.mjs +15 -1
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +91 -0
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/hooks/HookRegistry.mjs +160 -0
- package/dist/esm/hooks/HookRegistry.mjs.map +1 -0
- package/dist/esm/hooks/executeHooks.mjs +273 -0
- package/dist/esm/hooks/executeHooks.mjs.map +1 -0
- package/dist/esm/hooks/matchers.mjs +251 -0
- package/dist/esm/hooks/matchers.mjs.map +1 -0
- package/dist/esm/hooks/types.mjs +25 -0
- package/dist/esm/hooks/types.mjs.map +1 -0
- package/dist/esm/main.mjs +12 -1
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +66 -4
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/run.mjs +111 -0
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +44 -0
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/BashExecutor.mjs +169 -0
- package/dist/esm/tools/BashExecutor.mjs.map +1 -0
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs +287 -0
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -0
- package/dist/esm/tools/ReadFile.mjs +38 -0
- package/dist/esm/tools/ReadFile.mjs.map +1 -0
- package/dist/esm/tools/SkillTool.mjs +45 -0
- package/dist/esm/tools/SkillTool.mjs.map +1 -0
- package/dist/esm/tools/SubagentTool.mjs +85 -0
- package/dist/esm/tools/SubagentTool.mjs.map +1 -0
- package/dist/esm/tools/ToolNode.mjs +306 -142
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/skillCatalog.mjs +82 -0
- package/dist/esm/tools/skillCatalog.mjs.map +1 -0
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +505 -0
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -0
- package/dist/types/agents/AgentContext.d.ts +6 -0
- package/dist/types/common/enum.d.ts +10 -1
- package/dist/types/graphs/Graph.d.ts +2 -0
- package/dist/types/hooks/HookRegistry.d.ts +56 -0
- package/dist/types/hooks/executeHooks.d.ts +79 -0
- package/dist/types/hooks/index.d.ts +6 -0
- package/dist/types/hooks/matchers.d.ts +95 -0
- package/dist/types/hooks/types.d.ts +320 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/messages/format.d.ts +2 -1
- package/dist/types/run.d.ts +1 -0
- package/dist/types/summarization/node.d.ts +2 -0
- package/dist/types/tools/BashExecutor.d.ts +45 -0
- package/dist/types/tools/BashProgrammaticToolCalling.d.ts +72 -0
- package/dist/types/tools/ReadFile.d.ts +28 -0
- package/dist/types/tools/SkillTool.d.ts +40 -0
- package/dist/types/tools/SubagentTool.d.ts +36 -0
- package/dist/types/tools/ToolNode.d.ts +24 -2
- package/dist/types/tools/skillCatalog.d.ts +19 -0
- package/dist/types/tools/subagent/SubagentExecutor.d.ts +137 -0
- package/dist/types/tools/subagent/index.d.ts +2 -0
- package/dist/types/types/graph.d.ts +61 -2
- package/dist/types/types/index.d.ts +1 -0
- package/dist/types/types/llm.d.ts +14 -2
- package/dist/types/types/run.d.ts +20 -0
- package/dist/types/types/skill.d.ts +9 -0
- package/dist/types/types/tools.d.ts +38 -1
- package/package.json +5 -1
- package/src/agents/AgentContext.ts +26 -2
- package/src/common/enum.ts +15 -0
- package/src/graphs/Graph.ts +113 -0
- package/src/hooks/HookRegistry.ts +208 -0
- package/src/hooks/__tests__/HookRegistry.test.ts +190 -0
- package/src/hooks/__tests__/compactHooks.test.ts +214 -0
- package/src/hooks/__tests__/executeHooks.test.ts +1013 -0
- package/src/hooks/__tests__/integration.test.ts +337 -0
- package/src/hooks/__tests__/matchers.test.ts +238 -0
- package/src/hooks/__tests__/toolHooks.test.ts +669 -0
- package/src/hooks/executeHooks.ts +375 -0
- package/src/hooks/index.ts +57 -0
- package/src/hooks/matchers.ts +280 -0
- package/src/hooks/types.ts +404 -0
- package/src/index.ts +10 -0
- package/src/messages/format.ts +74 -4
- package/src/messages/formatAgentMessages.skills.test.ts +334 -0
- package/src/run.ts +126 -0
- package/src/scripts/multi-agent-subagent.ts +246 -0
- package/src/scripts/subagent-event-driven-debug.ts +190 -0
- package/src/scripts/subagent-tools-debug.ts +160 -0
- package/src/specs/subagent.test.ts +305 -0
- package/src/summarization/node.ts +53 -0
- package/src/tools/BashExecutor.ts +205 -0
- package/src/tools/BashProgrammaticToolCalling.ts +397 -0
- package/src/tools/ReadFile.ts +39 -0
- package/src/tools/SkillTool.ts +46 -0
- package/src/tools/SubagentTool.ts +100 -0
- package/src/tools/ToolNode.ts +391 -169
- package/src/tools/__tests__/ReadFile.test.ts +44 -0
- package/src/tools/__tests__/SkillTool.test.ts +442 -0
- package/src/tools/__tests__/SubagentExecutor.test.ts +1148 -0
- package/src/tools/__tests__/SubagentTool.test.ts +149 -0
- package/src/tools/__tests__/ToolNode.session.test.ts +12 -12
- package/src/tools/__tests__/skillCatalog.test.ts +161 -0
- package/src/tools/__tests__/subagentHooks.test.ts +215 -0
- package/src/tools/skillCatalog.ts +126 -0
- package/src/tools/subagent/SubagentExecutor.ts +676 -0
- package/src/tools/subagent/index.ts +13 -0
- package/src/types/graph.ts +80 -1
- package/src/types/index.ts +1 -0
- package/src/types/llm.ts +16 -2
- package/src/types/run.ts +20 -0
- package/src/types/skill.ts +11 -0
- package/src/types/tools.ts +41 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var _enum = require('../common/enum.cjs');
|
|
4
|
+
|
|
5
|
+
const SubagentToolName = _enum.Constants.SUBAGENT;
|
|
6
|
+
const SubagentToolDescription = `Delegate a task to a specialized subagent that runs in an isolated context window. The subagent executes independently and returns only its final text result — all intermediate tool calls, reasoning, and context stay isolated.
|
|
7
|
+
|
|
8
|
+
WHEN TO USE:
|
|
9
|
+
- The task is self-contained and can be described in a single prompt.
|
|
10
|
+
- You want to offload verbose or exploratory work without bloating your own context.
|
|
11
|
+
- A specialized subagent is available for the task domain.
|
|
12
|
+
|
|
13
|
+
WHAT HAPPENS:
|
|
14
|
+
- A fresh agent is created with the task description as its only input.
|
|
15
|
+
- The subagent runs to completion using its own tools and context.
|
|
16
|
+
- Only the final text response is returned to you.
|
|
17
|
+
|
|
18
|
+
CONSTRAINTS:
|
|
19
|
+
- subagent_type must match one of the available types listed below.
|
|
20
|
+
- The subagent cannot see your conversation history.`;
|
|
21
|
+
const DESCRIPTION_PROP_DESCRIPTION = 'Complete task description for the subagent. This is the ONLY information it receives — include all necessary context, requirements, and constraints.';
|
|
22
|
+
const SUBAGENT_TYPE_PROP_DESCRIPTION = 'Which subagent type to delegate to. Must be one of the available types.';
|
|
23
|
+
const SubagentToolSchema = {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
description: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
description: DESCRIPTION_PROP_DESCRIPTION,
|
|
29
|
+
},
|
|
30
|
+
subagent_type: {
|
|
31
|
+
type: 'string',
|
|
32
|
+
description: SUBAGENT_TYPE_PROP_DESCRIPTION,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
required: ['description', 'subagent_type'],
|
|
36
|
+
};
|
|
37
|
+
const SubagentToolDefinition = {
|
|
38
|
+
name: SubagentToolName,
|
|
39
|
+
description: SubagentToolDescription,
|
|
40
|
+
parameters: SubagentToolSchema,
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Build the name, schema, and description params for `tool()` from available configs.
|
|
44
|
+
* Used by `Graph.createAgentNode()` when constructing the runtime tool instance.
|
|
45
|
+
* Extends `SubagentToolSchema` by populating `subagent_type.enum` dynamically.
|
|
46
|
+
*/
|
|
47
|
+
function buildSubagentToolParams(configs) {
|
|
48
|
+
const types = configs.map((c) => c.type);
|
|
49
|
+
const typeDescriptions = configs
|
|
50
|
+
.map((c) => `- "${c.type}" (${c.name}): ${c.description}`)
|
|
51
|
+
.join('\n');
|
|
52
|
+
return {
|
|
53
|
+
name: SubagentToolName,
|
|
54
|
+
schema: {
|
|
55
|
+
type: 'object',
|
|
56
|
+
properties: {
|
|
57
|
+
description: {
|
|
58
|
+
type: 'string',
|
|
59
|
+
description: DESCRIPTION_PROP_DESCRIPTION,
|
|
60
|
+
},
|
|
61
|
+
subagent_type: {
|
|
62
|
+
type: 'string',
|
|
63
|
+
enum: types,
|
|
64
|
+
description: `${SUBAGENT_TYPE_PROP_DESCRIPTION} Available: ${types.join(', ')}.`,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
required: ['description', 'subagent_type'],
|
|
68
|
+
},
|
|
69
|
+
description: `${SubagentToolDescription}\n\nAvailable types:\n${typeDescriptions}`,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Create a SubagentTool LCTool definition with dynamic enum and description
|
|
74
|
+
* populated from the available subagent configs.
|
|
75
|
+
* Used for the tool registry in event-driven mode.
|
|
76
|
+
*/
|
|
77
|
+
function createSubagentToolDefinition(configs) {
|
|
78
|
+
const params = buildSubagentToolParams(configs);
|
|
79
|
+
return {
|
|
80
|
+
name: params.name,
|
|
81
|
+
description: params.description,
|
|
82
|
+
parameters: params.schema,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
exports.SubagentToolDefinition = SubagentToolDefinition;
|
|
87
|
+
exports.SubagentToolDescription = SubagentToolDescription;
|
|
88
|
+
exports.SubagentToolName = SubagentToolName;
|
|
89
|
+
exports.SubagentToolSchema = SubagentToolSchema;
|
|
90
|
+
exports.buildSubagentToolParams = buildSubagentToolParams;
|
|
91
|
+
exports.createSubagentToolDefinition = createSubagentToolDefinition;
|
|
92
|
+
//# sourceMappingURL=SubagentTool.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SubagentTool.cjs","sources":["../../../src/tools/SubagentTool.ts"],"sourcesContent":["import { Constants } from '@/common';\nimport type { SubagentConfig } from '@/types';\nimport type { JsonSchemaType, LCTool } from '@/types/tools';\n\nexport const SubagentToolName = Constants.SUBAGENT;\n\nexport const SubagentToolDescription = `Delegate a task to a specialized subagent that runs in an isolated context window. The subagent executes independently and returns only its final text result — all intermediate tool calls, reasoning, and context stay isolated.\n\nWHEN TO USE:\n- The task is self-contained and can be described in a single prompt.\n- You want to offload verbose or exploratory work without bloating your own context.\n- A specialized subagent is available for the task domain.\n\nWHAT HAPPENS:\n- A fresh agent is created with the task description as its only input.\n- The subagent runs to completion using its own tools and context.\n- Only the final text response is returned to you.\n\nCONSTRAINTS:\n- subagent_type must match one of the available types listed below.\n- The subagent cannot see your conversation history.`;\n\nconst DESCRIPTION_PROP_DESCRIPTION =\n 'Complete task description for the subagent. This is the ONLY information it receives — include all necessary context, requirements, and constraints.';\n\nconst SUBAGENT_TYPE_PROP_DESCRIPTION =\n 'Which subagent type to delegate to. Must be one of the available types.';\n\nexport const SubagentToolSchema = {\n type: 'object',\n properties: {\n description: {\n type: 'string',\n description: DESCRIPTION_PROP_DESCRIPTION,\n },\n subagent_type: {\n type: 'string',\n description: SUBAGENT_TYPE_PROP_DESCRIPTION,\n },\n },\n required: ['description', 'subagent_type'] as string[],\n} as const;\n\nexport const SubagentToolDefinition: LCTool = {\n name: SubagentToolName,\n description: SubagentToolDescription,\n parameters: SubagentToolSchema,\n};\n\n/**\n * Build the name, schema, and description params for `tool()` from available configs.\n * Used by `Graph.createAgentNode()` when constructing the runtime tool instance.\n * Extends `SubagentToolSchema` by populating `subagent_type.enum` dynamically.\n */\nexport function buildSubagentToolParams(configs: SubagentConfig[]): {\n name: string;\n schema: JsonSchemaType;\n description: string;\n} {\n const types = configs.map((c) => c.type);\n const typeDescriptions = configs\n .map((c) => `- \"${c.type}\" (${c.name}): ${c.description}`)\n .join('\\n');\n\n return {\n name: SubagentToolName,\n schema: {\n type: 'object',\n properties: {\n description: {\n type: 'string',\n description: DESCRIPTION_PROP_DESCRIPTION,\n },\n subagent_type: {\n type: 'string',\n enum: types,\n description: `${SUBAGENT_TYPE_PROP_DESCRIPTION} Available: ${types.join(', ')}.`,\n },\n },\n required: ['description', 'subagent_type'],\n },\n description: `${SubagentToolDescription}\\n\\nAvailable types:\\n${typeDescriptions}`,\n };\n}\n\n/**\n * Create a SubagentTool LCTool definition with dynamic enum and description\n * populated from the available subagent configs.\n * Used for the tool registry in event-driven mode.\n */\nexport function createSubagentToolDefinition(\n configs: SubagentConfig[]\n): LCTool {\n const params = buildSubagentToolParams(configs);\n return {\n name: params.name,\n description: params.description,\n parameters: params.schema,\n };\n}\n"],"names":["Constants"],"mappings":";;;;AAIO,MAAM,gBAAgB,GAAGA,eAAS,CAAC;AAEnC,MAAM,uBAAuB,GAAG,CAAA;;;;;;;;;;;;;;;AAgBvC,MAAM,4BAA4B,GAChC,sJAAsJ;AAExJ,MAAM,8BAA8B,GAClC,yEAAyE;AAEpE,MAAM,kBAAkB,GAAG;AAChC,IAAA,IAAI,EAAE,QAAQ;AACd,IAAA,UAAU,EAAE;AACV,QAAA,WAAW,EAAE;AACX,YAAA,IAAI,EAAE,QAAQ;AACd,YAAA,WAAW,EAAE,4BAA4B;AAC1C,SAAA;AACD,QAAA,aAAa,EAAE;AACb,YAAA,IAAI,EAAE,QAAQ;AACd,YAAA,WAAW,EAAE,8BAA8B;AAC5C,SAAA;AACF,KAAA;AACD,IAAA,QAAQ,EAAE,CAAC,aAAa,EAAE,eAAe,CAAa;;AAGjD,MAAM,sBAAsB,GAAW;AAC5C,IAAA,IAAI,EAAE,gBAAgB;AACtB,IAAA,WAAW,EAAE,uBAAuB;AACpC,IAAA,UAAU,EAAE,kBAAkB;;AAGhC;;;;AAIG;AACG,SAAU,uBAAuB,CAAC,OAAyB,EAAA;AAK/D,IAAA,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC;IACxC,MAAM,gBAAgB,GAAG;AACtB,SAAA,GAAG,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,IAAI,CAAA,GAAA,EAAM,CAAC,CAAC,IAAI,CAAA,GAAA,EAAM,CAAC,CAAC,WAAW,EAAE;SACxD,IAAI,CAAC,IAAI,CAAC;IAEb,OAAO;AACL,QAAA,IAAI,EAAE,gBAAgB;AACtB,QAAA,MAAM,EAAE;AACN,YAAA,IAAI,EAAE,QAAQ;AACd,YAAA,UAAU,EAAE;AACV,gBAAA,WAAW,EAAE;AACX,oBAAA,IAAI,EAAE,QAAQ;AACd,oBAAA,WAAW,EAAE,4BAA4B;AAC1C,iBAAA;AACD,gBAAA,aAAa,EAAE;AACb,oBAAA,IAAI,EAAE,QAAQ;AACd,oBAAA,IAAI,EAAE,KAAK;oBACX,WAAW,EAAE,CAAA,EAAG,8BAA8B,CAAA,YAAA,EAAe,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA,CAAA,CAAG;AACjF,iBAAA;AACF,aAAA;AACD,YAAA,QAAQ,EAAE,CAAC,aAAa,EAAE,eAAe,CAAC;AAC3C,SAAA;AACD,QAAA,WAAW,EAAE,CAAA,EAAG,uBAAuB,CAAA,sBAAA,EAAyB,gBAAgB,CAAA,CAAE;KACnF;AACH;AAEA;;;;AAIG;AACG,SAAU,4BAA4B,CAC1C,OAAyB,EAAA;AAEzB,IAAA,MAAM,MAAM,GAAG,uBAAuB,CAAC,OAAO,CAAC;IAC/C,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,UAAU,EAAE,MAAM,CAAC,MAAM;KAC1B;AACH;;;;;;;;;"}
|
|
@@ -11,6 +11,7 @@ require('uuid');
|
|
|
11
11
|
var run = require('../utils/run.cjs');
|
|
12
12
|
require('ai-tokenizer');
|
|
13
13
|
require('zod-to-json-schema');
|
|
14
|
+
var executeHooks = require('../hooks/executeHooks.cjs');
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Helper to check if a value is a Send object
|
|
@@ -18,6 +19,32 @@ require('zod-to-json-schema');
|
|
|
18
19
|
function isSend(value) {
|
|
19
20
|
return value instanceof langgraph.Send;
|
|
20
21
|
}
|
|
22
|
+
/** Merges code execution session context into the sessions map. */
|
|
23
|
+
function updateCodeSession(sessions, sessionId, files) {
|
|
24
|
+
const newFiles = files ?? [];
|
|
25
|
+
const existingSession = sessions.get(_enum.Constants.EXECUTE_CODE);
|
|
26
|
+
const existingFiles = existingSession?.files ?? [];
|
|
27
|
+
if (newFiles.length > 0) {
|
|
28
|
+
const filesWithSession = newFiles.map((file) => ({
|
|
29
|
+
...file,
|
|
30
|
+
session_id: sessionId,
|
|
31
|
+
}));
|
|
32
|
+
const newFileNames = new Set(filesWithSession.map((f) => f.name));
|
|
33
|
+
const filteredExisting = existingFiles.filter((f) => !newFileNames.has(f.name));
|
|
34
|
+
sessions.set(_enum.Constants.EXECUTE_CODE, {
|
|
35
|
+
session_id: sessionId,
|
|
36
|
+
files: [...filteredExisting, ...filesWithSession],
|
|
37
|
+
lastUpdated: Date.now(),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
sessions.set(_enum.Constants.EXECUTE_CODE, {
|
|
42
|
+
session_id: sessionId,
|
|
43
|
+
files: existingFiles,
|
|
44
|
+
lastUpdated: Date.now(),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
21
48
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
49
|
class ToolNode extends run.RunnableCallable {
|
|
23
50
|
toolMap;
|
|
@@ -43,7 +70,9 @@ class ToolNode extends run.RunnableCallable {
|
|
|
43
70
|
directToolNames;
|
|
44
71
|
/** Maximum characters allowed in a single tool result before truncation. */
|
|
45
72
|
maxToolResultChars;
|
|
46
|
-
|
|
73
|
+
/** Hook registry for PreToolUse/PostToolUse lifecycle hooks */
|
|
74
|
+
hookRegistry;
|
|
75
|
+
constructor({ tools, toolMap, name, tags, errorHandler, toolCallStepIds, handleToolErrors, loadRuntimeTools, toolRegistry, sessions, eventDrivenMode, agentId, directToolNames, maxContextTokens, maxToolResultChars, hookRegistry, }) {
|
|
47
76
|
super({ name, tags, func: (input, config) => this.run(input, config) });
|
|
48
77
|
this.toolMap = toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));
|
|
49
78
|
this.toolCallStepIds = toolCallStepIds;
|
|
@@ -58,6 +87,7 @@ class ToolNode extends run.RunnableCallable {
|
|
|
58
87
|
this.directToolNames = directToolNames;
|
|
59
88
|
this.maxToolResultChars =
|
|
60
89
|
maxToolResultChars ?? truncation.calculateMaxToolResultChars(maxContextTokens);
|
|
90
|
+
this.hookRegistry = hookRegistry;
|
|
61
91
|
}
|
|
62
92
|
/**
|
|
63
93
|
* Returns cached programmatic tools, computing once on first access.
|
|
@@ -113,7 +143,8 @@ class ToolNode extends run.RunnableCallable {
|
|
|
113
143
|
turn,
|
|
114
144
|
};
|
|
115
145
|
// Inject runtime data for special tools (becomes available at config.toolCall)
|
|
116
|
-
if (call.name === _enum.Constants.PROGRAMMATIC_TOOL_CALLING
|
|
146
|
+
if (call.name === _enum.Constants.PROGRAMMATIC_TOOL_CALLING ||
|
|
147
|
+
call.name === _enum.Constants.BASH_PROGRAMMATIC_TOOL_CALLING) {
|
|
117
148
|
const { toolMap, toolDefs } = this.getProgrammaticTools();
|
|
118
149
|
invokeParams = {
|
|
119
150
|
...invokeParams,
|
|
@@ -136,8 +167,7 @@ class ToolNode extends run.RunnableCallable {
|
|
|
136
167
|
* session_id is always injected when available (even without tracked files)
|
|
137
168
|
* so the CodeExecutor can fall back to the /files endpoint for session continuity.
|
|
138
169
|
*/
|
|
139
|
-
if (call.name
|
|
140
|
-
call.name === _enum.Constants.PROGRAMMATIC_TOOL_CALLING) {
|
|
170
|
+
if (_enum.CODE_EXECUTION_TOOLS.has(call.name)) {
|
|
141
171
|
const codeSession = this.sessions?.get(_enum.Constants.EXECUTE_CODE);
|
|
142
172
|
if (codeSession?.session_id != null && codeSession.session_id !== '') {
|
|
143
173
|
invokeParams = {
|
|
@@ -246,7 +276,7 @@ class ToolNode extends run.RunnableCallable {
|
|
|
246
276
|
* Extracts code execution session context from tool results and stores in Graph.sessions.
|
|
247
277
|
* Mirrors the session storage logic in handleRunToolCompletions for direct execution.
|
|
248
278
|
*/
|
|
249
|
-
storeCodeSessionFromResults(results,
|
|
279
|
+
storeCodeSessionFromResults(results, requestMap) {
|
|
250
280
|
if (!this.sessions) {
|
|
251
281
|
return;
|
|
252
282
|
}
|
|
@@ -255,38 +285,17 @@ class ToolNode extends run.RunnableCallable {
|
|
|
255
285
|
if (result.status !== 'success' || result.artifact == null) {
|
|
256
286
|
continue;
|
|
257
287
|
}
|
|
258
|
-
const request =
|
|
259
|
-
if (request?.name
|
|
260
|
-
|
|
288
|
+
const request = requestMap.get(result.toolCallId);
|
|
289
|
+
if (!request?.name ||
|
|
290
|
+
(!_enum.CODE_EXECUTION_TOOLS.has(request.name) &&
|
|
291
|
+
request.name !== _enum.Constants.SKILL_TOOL)) {
|
|
261
292
|
continue;
|
|
262
293
|
}
|
|
263
294
|
const artifact = result.artifact;
|
|
264
295
|
if (artifact?.session_id == null || artifact.session_id === '') {
|
|
265
296
|
continue;
|
|
266
297
|
}
|
|
267
|
-
|
|
268
|
-
const existingSession = this.sessions.get(_enum.Constants.EXECUTE_CODE);
|
|
269
|
-
const existingFiles = existingSession?.files ?? [];
|
|
270
|
-
if (newFiles.length > 0) {
|
|
271
|
-
const filesWithSession = newFiles.map((file) => ({
|
|
272
|
-
...file,
|
|
273
|
-
session_id: artifact.session_id,
|
|
274
|
-
}));
|
|
275
|
-
const newFileNames = new Set(filesWithSession.map((f) => f.name));
|
|
276
|
-
const filteredExisting = existingFiles.filter((f) => !newFileNames.has(f.name));
|
|
277
|
-
this.sessions.set(_enum.Constants.EXECUTE_CODE, {
|
|
278
|
-
session_id: artifact.session_id,
|
|
279
|
-
files: [...filteredExisting, ...filesWithSession],
|
|
280
|
-
lastUpdated: Date.now(),
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
else {
|
|
284
|
-
this.sessions.set(_enum.Constants.EXECUTE_CODE, {
|
|
285
|
-
session_id: artifact.session_id,
|
|
286
|
-
files: existingFiles,
|
|
287
|
-
lastUpdated: Date.now(),
|
|
288
|
-
});
|
|
289
|
-
}
|
|
298
|
+
updateCodeSession(this.sessions, artifact.session_id, artifact.files);
|
|
290
299
|
}
|
|
291
300
|
}
|
|
292
301
|
/**
|
|
@@ -313,35 +322,10 @@ class ToolNode extends run.RunnableCallable {
|
|
|
313
322
|
if (toolMessage.status === 'error' && this.errorHandler != null) {
|
|
314
323
|
continue;
|
|
315
324
|
}
|
|
316
|
-
|
|
317
|
-
if (this.sessions &&
|
|
318
|
-
(call.name === _enum.Constants.EXECUTE_CODE ||
|
|
319
|
-
call.name === _enum.Constants.PROGRAMMATIC_TOOL_CALLING)) {
|
|
325
|
+
if (this.sessions && _enum.CODE_EXECUTION_TOOLS.has(call.name)) {
|
|
320
326
|
const artifact = toolMessage.artifact;
|
|
321
327
|
if (artifact?.session_id != null && artifact.session_id !== '') {
|
|
322
|
-
|
|
323
|
-
const existingSession = this.sessions.get(_enum.Constants.EXECUTE_CODE);
|
|
324
|
-
const existingFiles = existingSession?.files ?? [];
|
|
325
|
-
if (newFiles.length > 0) {
|
|
326
|
-
const filesWithSession = newFiles.map((file) => ({
|
|
327
|
-
...file,
|
|
328
|
-
session_id: artifact.session_id,
|
|
329
|
-
}));
|
|
330
|
-
const newFileNames = new Set(filesWithSession.map((f) => f.name));
|
|
331
|
-
const filteredExisting = existingFiles.filter((f) => !newFileNames.has(f.name));
|
|
332
|
-
this.sessions.set(_enum.Constants.EXECUTE_CODE, {
|
|
333
|
-
session_id: artifact.session_id,
|
|
334
|
-
files: [...filteredExisting, ...filesWithSession],
|
|
335
|
-
lastUpdated: Date.now(),
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
else {
|
|
339
|
-
this.sessions.set(_enum.Constants.EXECUTE_CODE, {
|
|
340
|
-
session_id: artifact.session_id,
|
|
341
|
-
files: existingFiles,
|
|
342
|
-
lastUpdated: Date.now(),
|
|
343
|
-
});
|
|
344
|
-
}
|
|
328
|
+
updateCodeSession(this.sessions, artifact.session_id, artifact.files);
|
|
345
329
|
}
|
|
346
330
|
}
|
|
347
331
|
// Dispatch ON_RUN_STEP_COMPLETED via custom event (same path as dispatchToolEvents)
|
|
@@ -374,100 +358,273 @@ class ToolNode extends run.RunnableCallable {
|
|
|
374
358
|
/**
|
|
375
359
|
* Dispatches tool calls to the host via ON_TOOL_EXECUTE event and returns raw ToolMessages.
|
|
376
360
|
* Core logic for event-driven execution, separated from output shaping.
|
|
361
|
+
*
|
|
362
|
+
* Hook lifecycle (when `hookRegistry` is set):
|
|
363
|
+
* 1. **PreToolUse** fires per call in parallel before dispatch. Denied
|
|
364
|
+
* calls produce error ToolMessages and fire **PermissionDenied**;
|
|
365
|
+
* surviving calls proceed with optional `updatedInput`.
|
|
366
|
+
* 2. Surviving calls are dispatched to the host via `ON_TOOL_EXECUTE`.
|
|
367
|
+
* 3. **PostToolUse** / **PostToolUseFailure** fire per result. Post hooks
|
|
368
|
+
* can replace tool output via `updatedOutput`.
|
|
369
|
+
* 4. Injected messages from results are collected and returned alongside
|
|
370
|
+
* ToolMessages (appended AFTER to respect provider ordering).
|
|
377
371
|
*/
|
|
378
372
|
async dispatchToolEvents(toolCalls, config) {
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
request.codeSessionContext = this.getCodeSessionContext();
|
|
392
|
-
}
|
|
393
|
-
return request;
|
|
394
|
-
});
|
|
395
|
-
const results = await new Promise((resolve, reject) => {
|
|
396
|
-
const request = {
|
|
397
|
-
toolCalls: requests,
|
|
398
|
-
userId: config.configurable?.user_id,
|
|
399
|
-
agentId: this.agentId,
|
|
400
|
-
configurable: config.configurable,
|
|
401
|
-
metadata: config.metadata,
|
|
402
|
-
resolve,
|
|
403
|
-
reject,
|
|
404
|
-
};
|
|
405
|
-
events.safeDispatchCustomEvent(_enum.GraphEvents.ON_TOOL_EXECUTE, request, config);
|
|
373
|
+
const runId = config.configurable?.run_id ?? '';
|
|
374
|
+
const threadId = config.configurable?.thread_id;
|
|
375
|
+
const preToolCalls = toolCalls.map((call) => ({
|
|
376
|
+
call,
|
|
377
|
+
stepId: this.toolCallStepIds?.get(call.id) ?? '',
|
|
378
|
+
args: call.args,
|
|
379
|
+
}));
|
|
380
|
+
const messageByCallId = new Map();
|
|
381
|
+
const approvedEntries = [];
|
|
382
|
+
const HOOK_FALLBACK = Object.freeze({
|
|
383
|
+
additionalContexts: [],
|
|
384
|
+
errors: [],
|
|
406
385
|
});
|
|
407
|
-
this.
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
386
|
+
if (this.hookRegistry?.hasHookFor('PreToolUse', runId) === true) {
|
|
387
|
+
const preResults = await Promise.all(preToolCalls.map((entry) => executeHooks.executeHooks({
|
|
388
|
+
registry: this.hookRegistry,
|
|
389
|
+
input: {
|
|
390
|
+
hook_event_name: 'PreToolUse',
|
|
391
|
+
runId,
|
|
392
|
+
threadId,
|
|
393
|
+
agentId: this.agentId,
|
|
394
|
+
toolName: entry.call.name,
|
|
395
|
+
toolInput: entry.args,
|
|
396
|
+
toolUseId: entry.call.id,
|
|
397
|
+
stepId: entry.stepId,
|
|
398
|
+
turn: this.toolUsageCount.get(entry.call.name) ?? 0,
|
|
399
|
+
},
|
|
400
|
+
sessionId: runId,
|
|
401
|
+
matchQuery: entry.call.name,
|
|
402
|
+
}).catch(() => HOOK_FALLBACK)));
|
|
403
|
+
for (let i = 0; i < preToolCalls.length; i++) {
|
|
404
|
+
const hookResult = preResults[i];
|
|
405
|
+
const entry = preToolCalls[i];
|
|
406
|
+
const isDenied = hookResult.decision === 'deny' || hookResult.decision === 'ask';
|
|
407
|
+
if (isDenied) {
|
|
408
|
+
const reason = hookResult.reason ?? 'Blocked by hook';
|
|
409
|
+
const contentString = `Blocked: ${reason}`;
|
|
410
|
+
messageByCallId.set(entry.call.id, new messages.ToolMessage({
|
|
411
|
+
status: 'error',
|
|
412
|
+
content: contentString,
|
|
413
|
+
name: entry.call.name,
|
|
414
|
+
tool_call_id: entry.call.id,
|
|
415
|
+
}));
|
|
416
|
+
this.dispatchStepCompleted(entry.call.id, entry.call.name, entry.args, contentString, config);
|
|
417
|
+
if (this.hookRegistry.hasHookFor('PermissionDenied', runId)) {
|
|
418
|
+
executeHooks.executeHooks({
|
|
419
|
+
registry: this.hookRegistry,
|
|
420
|
+
input: {
|
|
421
|
+
hook_event_name: 'PermissionDenied',
|
|
422
|
+
runId,
|
|
423
|
+
threadId,
|
|
424
|
+
agentId: this.agentId,
|
|
425
|
+
toolName: entry.call.name,
|
|
426
|
+
toolInput: entry.args,
|
|
427
|
+
toolUseId: entry.call.id,
|
|
428
|
+
reason,
|
|
429
|
+
},
|
|
430
|
+
sessionId: runId,
|
|
431
|
+
matchQuery: entry.call.name,
|
|
432
|
+
}).catch(() => {
|
|
433
|
+
/* PermissionDenied is observational — swallow errors */
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
if (hookResult.updatedInput != null) {
|
|
439
|
+
entry.args = hookResult.updatedInput;
|
|
440
|
+
}
|
|
441
|
+
approvedEntries.push(entry);
|
|
417
442
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
443
|
+
}
|
|
444
|
+
else {
|
|
445
|
+
approvedEntries.push(...preToolCalls);
|
|
446
|
+
}
|
|
447
|
+
const injected = [];
|
|
448
|
+
if (approvedEntries.length > 0) {
|
|
449
|
+
const requests = approvedEntries.map((entry) => {
|
|
450
|
+
const turn = this.toolUsageCount.get(entry.call.name) ?? 0;
|
|
451
|
+
this.toolUsageCount.set(entry.call.name, turn + 1);
|
|
452
|
+
const request = {
|
|
453
|
+
id: entry.call.id,
|
|
454
|
+
name: entry.call.name,
|
|
455
|
+
args: entry.args,
|
|
456
|
+
stepId: entry.stepId,
|
|
457
|
+
turn,
|
|
458
|
+
};
|
|
459
|
+
if (_enum.CODE_EXECUTION_TOOLS.has(entry.call.name) ||
|
|
460
|
+
entry.call.name === _enum.Constants.SKILL_TOOL) {
|
|
461
|
+
request.codeSessionContext = this.getCodeSessionContext();
|
|
462
|
+
}
|
|
463
|
+
return request;
|
|
464
|
+
});
|
|
465
|
+
const requestMap = new Map(requests.map((r) => [r.id, r]));
|
|
466
|
+
const results = await new Promise((resolve, reject) => {
|
|
467
|
+
const batchRequest = {
|
|
468
|
+
toolCalls: requests,
|
|
469
|
+
userId: config.configurable?.user_id,
|
|
470
|
+
agentId: this.agentId,
|
|
471
|
+
configurable: config.configurable,
|
|
472
|
+
metadata: config.metadata,
|
|
473
|
+
resolve,
|
|
474
|
+
reject,
|
|
475
|
+
};
|
|
476
|
+
events.safeDispatchCustomEvent(_enum.GraphEvents.ON_TOOL_EXECUTE, batchRequest, config);
|
|
477
|
+
});
|
|
478
|
+
this.storeCodeSessionFromResults(results, requestMap);
|
|
479
|
+
const hasPostHook = this.hookRegistry?.hasHookFor('PostToolUse', runId) === true;
|
|
480
|
+
const hasFailureHook = this.hookRegistry?.hasHookFor('PostToolUseFailure', runId) === true;
|
|
481
|
+
for (const result of results) {
|
|
482
|
+
if (result.injectedMessages && result.injectedMessages.length > 0) {
|
|
483
|
+
try {
|
|
484
|
+
injected.push(...this.convertInjectedMessages(result.injectedMessages));
|
|
485
|
+
}
|
|
486
|
+
catch (e) {
|
|
487
|
+
// eslint-disable-next-line no-console
|
|
488
|
+
console.warn(`[ToolNode] Failed to convert injectedMessages for toolCallId=${result.toolCallId}:`, e instanceof Error ? e.message : e);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
const request = requestMap.get(result.toolCallId);
|
|
492
|
+
const toolName = request?.name ?? 'unknown';
|
|
493
|
+
let contentString;
|
|
494
|
+
let toolMessage;
|
|
495
|
+
if (result.status === 'error') {
|
|
496
|
+
contentString = `Error: ${result.errorMessage ?? 'Unknown error'}\n Please fix your mistakes.`;
|
|
497
|
+
toolMessage = new messages.ToolMessage({
|
|
498
|
+
status: 'error',
|
|
499
|
+
content: contentString,
|
|
500
|
+
name: toolName,
|
|
501
|
+
tool_call_id: result.toolCallId,
|
|
502
|
+
});
|
|
503
|
+
if (hasFailureHook) {
|
|
504
|
+
await executeHooks.executeHooks({
|
|
505
|
+
registry: this.hookRegistry,
|
|
506
|
+
input: {
|
|
507
|
+
hook_event_name: 'PostToolUseFailure',
|
|
508
|
+
runId,
|
|
509
|
+
threadId,
|
|
510
|
+
agentId: this.agentId,
|
|
511
|
+
toolName,
|
|
512
|
+
toolInput: request?.args ?? {},
|
|
513
|
+
toolUseId: result.toolCallId,
|
|
514
|
+
error: result.errorMessage ?? 'Unknown error',
|
|
515
|
+
stepId: request?.stepId,
|
|
516
|
+
turn: request?.turn,
|
|
517
|
+
},
|
|
518
|
+
sessionId: runId,
|
|
519
|
+
matchQuery: toolName,
|
|
520
|
+
}).catch(() => {
|
|
521
|
+
/* PostToolUseFailure is observational — swallow errors */
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
else {
|
|
526
|
+
const rawContent = typeof result.content === 'string'
|
|
527
|
+
? result.content
|
|
528
|
+
: JSON.stringify(result.content);
|
|
529
|
+
contentString = truncation.truncateToolResultContent(rawContent, this.maxToolResultChars);
|
|
530
|
+
if (hasPostHook) {
|
|
531
|
+
const hookResult = await executeHooks.executeHooks({
|
|
532
|
+
registry: this.hookRegistry,
|
|
533
|
+
input: {
|
|
534
|
+
hook_event_name: 'PostToolUse',
|
|
535
|
+
runId,
|
|
536
|
+
threadId,
|
|
537
|
+
agentId: this.agentId,
|
|
538
|
+
toolName,
|
|
539
|
+
toolInput: request?.args ?? {},
|
|
540
|
+
toolOutput: result.content,
|
|
541
|
+
toolUseId: result.toolCallId,
|
|
542
|
+
stepId: request?.stepId,
|
|
543
|
+
turn: request?.turn,
|
|
544
|
+
},
|
|
545
|
+
sessionId: runId,
|
|
546
|
+
matchQuery: toolName,
|
|
547
|
+
}).catch(() => undefined);
|
|
548
|
+
if (hookResult?.updatedOutput != null) {
|
|
549
|
+
const replaced = typeof hookResult.updatedOutput === 'string'
|
|
550
|
+
? hookResult.updatedOutput
|
|
551
|
+
: JSON.stringify(hookResult.updatedOutput);
|
|
552
|
+
contentString = truncation.truncateToolResultContent(replaced, this.maxToolResultChars);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
toolMessage = new messages.ToolMessage({
|
|
556
|
+
status: 'success',
|
|
557
|
+
name: toolName,
|
|
558
|
+
content: contentString,
|
|
559
|
+
artifact: result.artifact,
|
|
560
|
+
tool_call_id: result.toolCallId,
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
this.dispatchStepCompleted(result.toolCallId, toolName, request?.args ?? {}, contentString, config, request?.turn);
|
|
564
|
+
messageByCallId.set(result.toolCallId, toolMessage);
|
|
428
565
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
566
|
+
}
|
|
567
|
+
const toolMessages = toolCalls
|
|
568
|
+
.map((call) => messageByCallId.get(call.id))
|
|
569
|
+
.filter((m) => m != null);
|
|
570
|
+
return { toolMessages, injected };
|
|
571
|
+
}
|
|
572
|
+
dispatchStepCompleted(toolCallId, toolName, args, output, config, turn) {
|
|
573
|
+
const stepId = this.toolCallStepIds?.get(toolCallId) ?? '';
|
|
574
|
+
if (!stepId) {
|
|
575
|
+
// eslint-disable-next-line no-console
|
|
576
|
+
console.warn(`[ToolNode] toolCallStepIds missing entry for toolCallId=${toolCallId} (tool=${toolName}). ` +
|
|
577
|
+
'This indicates a race between the stream consumer and graph execution. ' +
|
|
578
|
+
`Map size: ${this.toolCallStepIds?.size ?? 0}`);
|
|
579
|
+
}
|
|
580
|
+
events.safeDispatchCustomEvent(_enum.GraphEvents.ON_RUN_STEP_COMPLETED, {
|
|
581
|
+
result: {
|
|
582
|
+
id: stepId,
|
|
583
|
+
index: turn ?? this.toolUsageCount.get(toolName) ?? 0,
|
|
584
|
+
type: 'tool_call',
|
|
585
|
+
tool_call: {
|
|
586
|
+
args: JSON.stringify(args),
|
|
436
587
|
name: toolName,
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
const tool_call = {
|
|
443
|
-
args: typeof request?.args === 'string'
|
|
444
|
-
? request.args
|
|
445
|
-
: JSON.stringify(request?.args ?? {}),
|
|
446
|
-
name: toolName,
|
|
447
|
-
id: result.toolCallId,
|
|
448
|
-
output: contentString,
|
|
449
|
-
progress: 1,
|
|
450
|
-
};
|
|
451
|
-
const runStepCompletedData = {
|
|
452
|
-
result: {
|
|
453
|
-
id: stepId,
|
|
454
|
-
index: request?.turn ?? 0,
|
|
455
|
-
type: 'tool_call',
|
|
456
|
-
tool_call,
|
|
588
|
+
id: toolCallId,
|
|
589
|
+
output,
|
|
590
|
+
progress: 1,
|
|
457
591
|
},
|
|
592
|
+
},
|
|
593
|
+
}, config);
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Converts InjectedMessage instances to LangChain HumanMessage objects.
|
|
597
|
+
* Both 'user' and 'system' roles become HumanMessage to avoid provider
|
|
598
|
+
* rejections (Anthropic/Google reject non-leading SystemMessages).
|
|
599
|
+
* The original role is preserved in additional_kwargs for downstream consumers.
|
|
600
|
+
*/
|
|
601
|
+
convertInjectedMessages(messages$1) {
|
|
602
|
+
const converted = [];
|
|
603
|
+
for (const msg of messages$1) {
|
|
604
|
+
const additional_kwargs = {
|
|
605
|
+
role: msg.role,
|
|
458
606
|
};
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
607
|
+
if (msg.isMeta != null)
|
|
608
|
+
additional_kwargs.isMeta = msg.isMeta;
|
|
609
|
+
if (msg.source != null)
|
|
610
|
+
additional_kwargs.source = msg.source;
|
|
611
|
+
if (msg.skillName != null)
|
|
612
|
+
additional_kwargs.skillName = msg.skillName;
|
|
613
|
+
converted.push(new messages.HumanMessage({ content: msg.content, additional_kwargs }));
|
|
614
|
+
}
|
|
615
|
+
return converted;
|
|
462
616
|
}
|
|
463
617
|
/**
|
|
464
618
|
* Execute all tool calls via ON_TOOL_EXECUTE event dispatch.
|
|
465
|
-
*
|
|
619
|
+
* Injected messages are placed AFTER ToolMessages to respect provider
|
|
620
|
+
* message ordering (AIMessage tool_calls must be immediately followed
|
|
621
|
+
* by their ToolMessage results).
|
|
466
622
|
*/
|
|
467
623
|
async executeViaEvent(toolCalls, config,
|
|
468
624
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
469
625
|
input) {
|
|
470
|
-
const
|
|
626
|
+
const { toolMessages, injected } = await this.dispatchToolEvents(toolCalls, config);
|
|
627
|
+
const outputs = [...toolMessages, ...injected];
|
|
471
628
|
return (Array.isArray(input) ? outputs : { messages: outputs });
|
|
472
629
|
}
|
|
473
630
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -536,10 +693,17 @@ class ToolNode extends run.RunnableCallable {
|
|
|
536
693
|
if (directCalls.length > 0 && directOutputs.length > 0) {
|
|
537
694
|
this.handleRunToolCompletions(directCalls, directOutputs, config);
|
|
538
695
|
}
|
|
539
|
-
const
|
|
696
|
+
const eventResult = eventCalls.length > 0
|
|
540
697
|
? await this.dispatchToolEvents(eventCalls, config)
|
|
541
|
-
:
|
|
542
|
-
|
|
698
|
+
: {
|
|
699
|
+
toolMessages: [],
|
|
700
|
+
injected: [],
|
|
701
|
+
};
|
|
702
|
+
outputs = [
|
|
703
|
+
...directOutputs,
|
|
704
|
+
...eventResult.toolMessages,
|
|
705
|
+
...eventResult.injected,
|
|
706
|
+
];
|
|
543
707
|
}
|
|
544
708
|
else {
|
|
545
709
|
outputs = await Promise.all(filteredCalls.map((call) => this.runTool(call, config)));
|