@librechat/agents 3.1.67-dev.4 → 3.1.68

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 (162) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +3 -23
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/common/enum.cjs +0 -16
  4. package/dist/cjs/common/enum.cjs.map +1 -1
  5. package/dist/cjs/graphs/Graph.cjs +0 -91
  6. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  7. package/dist/cjs/graphs/MultiAgentGraph.cjs +36 -0
  8. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  9. package/dist/cjs/main.cjs +1 -53
  10. package/dist/cjs/main.cjs.map +1 -1
  11. package/dist/cjs/messages/format.cjs +12 -74
  12. package/dist/cjs/messages/format.cjs.map +1 -1
  13. package/dist/cjs/run.cjs +0 -111
  14. package/dist/cjs/run.cjs.map +1 -1
  15. package/dist/cjs/summarization/index.cjs +41 -0
  16. package/dist/cjs/summarization/index.cjs.map +1 -1
  17. package/dist/cjs/summarization/node.cjs +121 -63
  18. package/dist/cjs/summarization/node.cjs.map +1 -1
  19. package/dist/cjs/tools/ToolNode.cjs +140 -304
  20. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  21. package/dist/esm/agents/AgentContext.mjs +3 -23
  22. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  23. package/dist/esm/common/enum.mjs +1 -15
  24. package/dist/esm/common/enum.mjs.map +1 -1
  25. package/dist/esm/graphs/Graph.mjs +0 -91
  26. package/dist/esm/graphs/Graph.mjs.map +1 -1
  27. package/dist/esm/graphs/MultiAgentGraph.mjs +36 -0
  28. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  29. package/dist/esm/main.mjs +2 -13
  30. package/dist/esm/main.mjs.map +1 -1
  31. package/dist/esm/messages/format.mjs +4 -66
  32. package/dist/esm/messages/format.mjs.map +1 -1
  33. package/dist/esm/run.mjs +0 -111
  34. package/dist/esm/run.mjs.map +1 -1
  35. package/dist/esm/summarization/index.mjs +41 -1
  36. package/dist/esm/summarization/index.mjs.map +1 -1
  37. package/dist/esm/summarization/node.mjs +121 -63
  38. package/dist/esm/summarization/node.mjs.map +1 -1
  39. package/dist/esm/tools/ToolNode.mjs +142 -306
  40. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  41. package/dist/types/agents/AgentContext.d.ts +0 -6
  42. package/dist/types/common/enum.d.ts +1 -10
  43. package/dist/types/graphs/Graph.d.ts +0 -2
  44. package/dist/types/graphs/MultiAgentGraph.d.ts +12 -0
  45. package/dist/types/index.d.ts +0 -8
  46. package/dist/types/messages/format.d.ts +1 -2
  47. package/dist/types/run.d.ts +0 -1
  48. package/dist/types/summarization/index.d.ts +2 -0
  49. package/dist/types/summarization/node.d.ts +0 -2
  50. package/dist/types/tools/ToolNode.d.ts +2 -24
  51. package/dist/types/types/graph.d.ts +2 -61
  52. package/dist/types/types/index.d.ts +0 -1
  53. package/dist/types/types/run.d.ts +0 -20
  54. package/dist/types/types/tools.d.ts +1 -38
  55. package/package.json +1 -5
  56. package/src/agents/AgentContext.ts +2 -26
  57. package/src/common/enum.ts +0 -15
  58. package/src/graphs/Graph.ts +0 -113
  59. package/src/graphs/MultiAgentGraph.ts +39 -0
  60. package/src/graphs/__tests__/MultiAgentGraph.test.ts +91 -0
  61. package/src/index.ts +0 -10
  62. package/src/messages/format.ts +4 -74
  63. package/src/run.ts +0 -126
  64. package/src/summarization/__tests__/node.test.ts +42 -0
  65. package/src/summarization/__tests__/trigger.test.ts +100 -1
  66. package/src/summarization/index.ts +47 -0
  67. package/src/summarization/node.ts +149 -77
  68. package/src/tools/ToolNode.ts +169 -391
  69. package/src/tools/__tests__/ToolNode.session.test.ts +12 -12
  70. package/src/types/graph.ts +1 -80
  71. package/src/types/index.ts +0 -1
  72. package/src/types/run.ts +0 -20
  73. package/src/types/tools.ts +1 -41
  74. package/dist/cjs/hooks/HookRegistry.cjs +0 -162
  75. package/dist/cjs/hooks/HookRegistry.cjs.map +0 -1
  76. package/dist/cjs/hooks/executeHooks.cjs +0 -276
  77. package/dist/cjs/hooks/executeHooks.cjs.map +0 -1
  78. package/dist/cjs/hooks/matchers.cjs +0 -256
  79. package/dist/cjs/hooks/matchers.cjs.map +0 -1
  80. package/dist/cjs/hooks/types.cjs +0 -27
  81. package/dist/cjs/hooks/types.cjs.map +0 -1
  82. package/dist/cjs/tools/BashExecutor.cjs +0 -175
  83. package/dist/cjs/tools/BashExecutor.cjs.map +0 -1
  84. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +0 -296
  85. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +0 -1
  86. package/dist/cjs/tools/ReadFile.cjs +0 -43
  87. package/dist/cjs/tools/ReadFile.cjs.map +0 -1
  88. package/dist/cjs/tools/SkillTool.cjs +0 -50
  89. package/dist/cjs/tools/SkillTool.cjs.map +0 -1
  90. package/dist/cjs/tools/SubagentTool.cjs +0 -92
  91. package/dist/cjs/tools/SubagentTool.cjs.map +0 -1
  92. package/dist/cjs/tools/skillCatalog.cjs +0 -84
  93. package/dist/cjs/tools/skillCatalog.cjs.map +0 -1
  94. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +0 -511
  95. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +0 -1
  96. package/dist/esm/hooks/HookRegistry.mjs +0 -160
  97. package/dist/esm/hooks/HookRegistry.mjs.map +0 -1
  98. package/dist/esm/hooks/executeHooks.mjs +0 -273
  99. package/dist/esm/hooks/executeHooks.mjs.map +0 -1
  100. package/dist/esm/hooks/matchers.mjs +0 -251
  101. package/dist/esm/hooks/matchers.mjs.map +0 -1
  102. package/dist/esm/hooks/types.mjs +0 -25
  103. package/dist/esm/hooks/types.mjs.map +0 -1
  104. package/dist/esm/tools/BashExecutor.mjs +0 -169
  105. package/dist/esm/tools/BashExecutor.mjs.map +0 -1
  106. package/dist/esm/tools/BashProgrammaticToolCalling.mjs +0 -287
  107. package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +0 -1
  108. package/dist/esm/tools/ReadFile.mjs +0 -38
  109. package/dist/esm/tools/ReadFile.mjs.map +0 -1
  110. package/dist/esm/tools/SkillTool.mjs +0 -45
  111. package/dist/esm/tools/SkillTool.mjs.map +0 -1
  112. package/dist/esm/tools/SubagentTool.mjs +0 -85
  113. package/dist/esm/tools/SubagentTool.mjs.map +0 -1
  114. package/dist/esm/tools/skillCatalog.mjs +0 -82
  115. package/dist/esm/tools/skillCatalog.mjs.map +0 -1
  116. package/dist/esm/tools/subagent/SubagentExecutor.mjs +0 -505
  117. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +0 -1
  118. package/dist/types/hooks/HookRegistry.d.ts +0 -56
  119. package/dist/types/hooks/executeHooks.d.ts +0 -79
  120. package/dist/types/hooks/index.d.ts +0 -6
  121. package/dist/types/hooks/matchers.d.ts +0 -95
  122. package/dist/types/hooks/types.d.ts +0 -320
  123. package/dist/types/tools/BashExecutor.d.ts +0 -45
  124. package/dist/types/tools/BashProgrammaticToolCalling.d.ts +0 -72
  125. package/dist/types/tools/ReadFile.d.ts +0 -28
  126. package/dist/types/tools/SkillTool.d.ts +0 -40
  127. package/dist/types/tools/SubagentTool.d.ts +0 -36
  128. package/dist/types/tools/skillCatalog.d.ts +0 -19
  129. package/dist/types/tools/subagent/SubagentExecutor.d.ts +0 -137
  130. package/dist/types/tools/subagent/index.d.ts +0 -2
  131. package/dist/types/types/skill.d.ts +0 -9
  132. package/src/hooks/HookRegistry.ts +0 -208
  133. package/src/hooks/__tests__/HookRegistry.test.ts +0 -190
  134. package/src/hooks/__tests__/compactHooks.test.ts +0 -214
  135. package/src/hooks/__tests__/executeHooks.test.ts +0 -1013
  136. package/src/hooks/__tests__/integration.test.ts +0 -337
  137. package/src/hooks/__tests__/matchers.test.ts +0 -238
  138. package/src/hooks/__tests__/toolHooks.test.ts +0 -669
  139. package/src/hooks/executeHooks.ts +0 -375
  140. package/src/hooks/index.ts +0 -57
  141. package/src/hooks/matchers.ts +0 -280
  142. package/src/hooks/types.ts +0 -404
  143. package/src/messages/formatAgentMessages.skills.test.ts +0 -334
  144. package/src/scripts/multi-agent-subagent.ts +0 -246
  145. package/src/scripts/subagent-event-driven-debug.ts +0 -190
  146. package/src/scripts/subagent-tools-debug.ts +0 -160
  147. package/src/specs/subagent.test.ts +0 -305
  148. package/src/tools/BashExecutor.ts +0 -205
  149. package/src/tools/BashProgrammaticToolCalling.ts +0 -397
  150. package/src/tools/ReadFile.ts +0 -39
  151. package/src/tools/SkillTool.ts +0 -46
  152. package/src/tools/SubagentTool.ts +0 -100
  153. package/src/tools/__tests__/ReadFile.test.ts +0 -44
  154. package/src/tools/__tests__/SkillTool.test.ts +0 -442
  155. package/src/tools/__tests__/SubagentExecutor.test.ts +0 -1148
  156. package/src/tools/__tests__/SubagentTool.test.ts +0 -149
  157. package/src/tools/__tests__/skillCatalog.test.ts +0 -161
  158. package/src/tools/__tests__/subagentHooks.test.ts +0 -215
  159. package/src/tools/skillCatalog.ts +0 -126
  160. package/src/tools/subagent/SubagentExecutor.ts +0 -676
  161. package/src/tools/subagent/index.ts +0 -13
  162. package/src/types/skill.ts +0 -11
@@ -1,190 +0,0 @@
1
- import { config } from 'dotenv';
2
- config();
3
-
4
- import { HumanMessage } from '@langchain/core/messages';
5
- import type { BaseMessage } from '@langchain/core/messages';
6
- import type * as t from '@/types';
7
- import { ChatModelStreamHandler } from '@/stream';
8
- import { ToolEndHandler, ModelEndHandler } from '@/events';
9
- import { Providers, GraphEvents, Constants } from '@/common';
10
- import { Run } from '@/run';
11
-
12
- /**
13
- * Repro for LibreChat's actual setup: event-driven tools via `toolDefinitions`
14
- * + an ON_TOOL_EXECUTE handler that runs the tool. Self-spawn subagent must
15
- * be able to drive the SAME tool pipeline.
16
- */
17
- const apiKey = process.env.OPENAI_API_KEY!;
18
- if (!apiKey) {
19
- console.error('Missing OPENAI_API_KEY');
20
- process.exit(1);
21
- }
22
-
23
- // Simulate LibreChat: tool definitions only, execution routed via event.
24
- const calculatorDef: t.LCTool = {
25
- name: 'calculator',
26
- description: 'Evaluate a math expression. Use for any arithmetic.',
27
- parameters: {
28
- type: 'object',
29
- properties: {
30
- expression: {
31
- type: 'string',
32
- description: "A JS math expression, e.g. '42 * 58'",
33
- },
34
- },
35
- required: ['expression'],
36
- },
37
- };
38
-
39
- async function main() {
40
- console.log('=== Subagent Event-Driven Tool Diagnostic ===\n');
41
-
42
- const parentAgent: t.AgentInputs = {
43
- agentId: 'supervisor',
44
- provider: Providers.OPENAI,
45
- clientOptions: { modelName: 'gpt-4o-mini', apiKey },
46
- instructions: `You have calculator AND can spawn a "self" subagent in an isolated context.
47
- For any arithmetic question, spawn the "self" subagent with the math task.
48
- The subagent MUST use the calculator tool — never estimate.`,
49
- maxContextTokens: 8000,
50
- toolDefinitions: [calculatorDef],
51
- subagentConfigs: [
52
- {
53
- type: 'self',
54
- self: true,
55
- name: 'supervisor',
56
- description:
57
- 'Spawn a copy of this agent in an isolated context for a focused math subtask.',
58
- },
59
- ],
60
- };
61
-
62
- let toolCallCount = 0;
63
- const customHandlers: Record<string, t.EventHandler> = {
64
- [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
65
- [GraphEvents.TOOL_END]: new ToolEndHandler(),
66
- [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),
67
- [GraphEvents.ON_TOOL_EXECUTE]: {
68
- handle: (_event, rawData): void => {
69
- const data = rawData as t.ToolExecuteBatchRequest;
70
- console.log(
71
- `[PARENT ON_TOOL_EXECUTE] agentId=${data.agentId} calls=${data.toolCalls
72
- .map((c) => c.name)
73
- .join(',')}`
74
- );
75
- const results: t.ToolExecuteResult[] = data.toolCalls.map((call) => {
76
- toolCallCount += 1;
77
- const args = call.args as { expression?: string };
78
- const expression = args.expression ?? '';
79
- let content: string;
80
- try {
81
- // eslint-disable-next-line no-eval
82
- const result = eval(expression);
83
- content = `${expression} = ${result}`;
84
- } catch (err) {
85
- content = `Error: ${String(err)}`;
86
- }
87
- return {
88
- toolCallId: call.id!,
89
- status: 'success',
90
- content,
91
- };
92
- });
93
- data.resolve(results);
94
- },
95
- },
96
- [GraphEvents.ON_RUN_STEP]: {
97
- handle: (event, data): void => {
98
- const d = data as { type?: string; runId?: string; agentId?: string };
99
- console.log(
100
- `[PARENT ${event}] type=${d.type} agentId=${d.agentId ?? '-'} runId=${d.runId ?? '-'}`
101
- );
102
- },
103
- },
104
- [GraphEvents.ON_RUN_STEP_COMPLETED]: {
105
- handle: (event, data): void => {
106
- const r = (
107
- data as { result: { type: string; tool_call?: { name?: string } } }
108
- ).result;
109
- console.log(
110
- `[PARENT ${event}] type=${r.type} tool=${r.tool_call?.name ?? '-'}`
111
- );
112
- },
113
- },
114
- [GraphEvents.ON_SUBAGENT_UPDATE]: {
115
- handle: (_event, rawData): void => {
116
- const d = rawData as t.SubagentUpdateEvent;
117
- console.log(
118
- `[SUBAGENT ${d.phase}] [${d.subagentType}] tool_call_id=${d.parentToolCallId ?? '-'} ${d.label ?? ''}`
119
- );
120
- },
121
- },
122
- };
123
-
124
- const run = await Run.create<t.IState>({
125
- runId: `sub-evt-${Date.now()}`,
126
- graphConfig: { type: 'standard', agents: [parentAgent] },
127
- customHandlers,
128
- });
129
-
130
- const question = new HumanMessage(
131
- 'Compute (42 * 58) + (13 ** 3). Use the self subagent, and have it use the calculator.'
132
- );
133
-
134
- console.log('User:', question.content, '\n');
135
-
136
- await run.processStream(
137
- { messages: [question] },
138
- {
139
- configurable: { thread_id: `sub-evt` },
140
- version: 'v2' as const,
141
- }
142
- );
143
-
144
- const msgs = (run.getRunMessages() ?? []) as BaseMessage[];
145
- console.log('\n--- Run messages ---\n');
146
- for (const msg of msgs) {
147
- const type = msg._getType();
148
- const name = 'name' in msg ? (msg as { name?: string }).name : undefined;
149
- const content =
150
- typeof msg.content === 'string'
151
- ? msg.content.slice(0, 400)
152
- : JSON.stringify(msg.content).slice(0, 400);
153
- const toolCalls =
154
- 'tool_calls' in msg
155
- ? (msg as { tool_calls?: Array<{ name: string; args: unknown }> })
156
- .tool_calls
157
- : undefined;
158
- console.log(`[${type}]${name ? ` name=${name}` : ''}`);
159
- if (toolCalls?.length) {
160
- for (const tc of toolCalls) {
161
- console.log(
162
- ` tool_call: ${tc.name}(${JSON.stringify(tc.args).slice(0, 150)})`
163
- );
164
- }
165
- }
166
- console.log(` content: ${content}\n`);
167
- }
168
-
169
- const subagentMsgs = msgs.filter(
170
- (m) =>
171
- m._getType() === 'tool' &&
172
- (m as { name?: string }).name === Constants.SUBAGENT
173
- );
174
-
175
- console.log('--- Verification ---');
176
- console.log(`subagent tool calls seen (parent): ${subagentMsgs.length}`);
177
- console.log(
178
- `ON_TOOL_EXECUTE dispatched (parent saw): ${toolCallCount} (expected >= 1 if subagent used calculator)`
179
- );
180
- if (subagentMsgs[0]) {
181
- console.log(
182
- `\nsubagent result:\n${(subagentMsgs[0].content as string).slice(0, 600)}`
183
- );
184
- }
185
- }
186
-
187
- main().catch((err) => {
188
- console.error('Script error:', err);
189
- process.exit(1);
190
- });
@@ -1,160 +0,0 @@
1
- import { config } from 'dotenv';
2
- config();
3
-
4
- import { HumanMessage } from '@langchain/core/messages';
5
- import type { BaseMessage } from '@langchain/core/messages';
6
- import { tool } from '@langchain/core/tools';
7
- import { z } from 'zod';
8
- import type * as t from '@/types';
9
- import { ChatModelStreamHandler } from '@/stream';
10
- import { ToolEndHandler, ModelEndHandler } from '@/events';
11
- import { Providers, GraphEvents, Constants } from '@/common';
12
- import { Run } from '@/run';
13
-
14
- /**
15
- * Diagnostic: verify a self-spawned subagent can call parent's real tools.
16
- * Expected before-fix: parent delegates to self; child cannot invoke calculator.
17
- */
18
- const apiKey = process.env.OPENAI_API_KEY!;
19
- if (!apiKey) {
20
- console.error('Missing OPENAI_API_KEY');
21
- process.exit(1);
22
- }
23
-
24
- const calculator = tool(
25
- async ({ expression }) => {
26
- const result = eval(expression); // don't do this in prod
27
- return `${expression} = ${result}`;
28
- },
29
- {
30
- name: 'calculator',
31
- description: 'Evaluate a math expression. Use for any arithmetic.',
32
- schema: z.object({
33
- expression: z.string().describe("A JS math expression, e.g. '42 * 58'"),
34
- }),
35
- }
36
- );
37
-
38
- async function main() {
39
- console.log('=== Subagent Tool-Access Diagnostic ===\n');
40
-
41
- const parentAgent: t.AgentInputs = {
42
- agentId: 'supervisor',
43
- provider: Providers.OPENAI,
44
- clientOptions: { modelName: 'gpt-4o-mini', apiKey },
45
- instructions: `You have calculator AND can spawn a "self" subagent in an isolated context.
46
- For any arithmetic question that would bloat your context, spawn the "self" subagent with the math task.
47
- The subagent must use the calculator tool — never estimate.`,
48
- maxContextTokens: 8000,
49
- tools: [calculator],
50
- subagentConfigs: [
51
- {
52
- type: 'self',
53
- self: true,
54
- name: 'supervisor',
55
- description:
56
- 'Spawn a copy of this agent in an isolated context for a focused math subtask.',
57
- },
58
- ],
59
- };
60
-
61
- const customHandlers: Record<string, t.EventHandler> = {
62
- [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
63
- [GraphEvents.TOOL_END]: new ToolEndHandler(),
64
- [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),
65
- [GraphEvents.ON_RUN_STEP]: {
66
- handle: (event, data): void => {
67
- console.log(
68
- `[PARENT EVENT] ${event}`,
69
- JSON.stringify(data).slice(0, 200)
70
- );
71
- },
72
- },
73
- [GraphEvents.ON_RUN_STEP_COMPLETED]: {
74
- handle: (event, data): void => {
75
- console.log(
76
- `[PARENT EVENT] ${event}`,
77
- JSON.stringify(data).slice(0, 200)
78
- );
79
- },
80
- },
81
- };
82
-
83
- const run = await Run.create<t.IState>({
84
- runId: `subagent-debug-${Date.now()}`,
85
- graphConfig: { type: 'standard', agents: [parentAgent] },
86
- customHandlers,
87
- });
88
-
89
- const question = new HumanMessage(
90
- 'Compute (42 * 58) + (13 ^ 3). Spawn the self subagent to do this, and have IT use calculator.'
91
- );
92
-
93
- console.log('User:', question.content, '\n');
94
-
95
- await run.processStream(
96
- { messages: [question] },
97
- {
98
- configurable: { thread_id: `subagent-debug` },
99
- version: 'v2' as const,
100
- }
101
- );
102
-
103
- const msgs = (run.getRunMessages() ?? []) as BaseMessage[];
104
- console.log('\n--- Run messages ---\n');
105
- for (const msg of msgs) {
106
- const type = msg._getType();
107
- const name = 'name' in msg ? (msg as { name?: string }).name : undefined;
108
- const content =
109
- typeof msg.content === 'string'
110
- ? msg.content.slice(0, 400)
111
- : JSON.stringify(msg.content).slice(0, 400);
112
- const toolCalls =
113
- 'tool_calls' in msg
114
- ? (msg as { tool_calls?: Array<{ name: string; args: unknown }> })
115
- .tool_calls
116
- : undefined;
117
- console.log(`[${type}]${name ? ` name=${name}` : ''}`);
118
- if (toolCalls?.length) {
119
- for (const tc of toolCalls) {
120
- console.log(
121
- ` tool_call: ${tc.name}(${JSON.stringify(tc.args).slice(0, 150)})`
122
- );
123
- }
124
- }
125
- console.log(` content: ${content}\n`);
126
- }
127
-
128
- const subagentCalls = msgs.filter(
129
- (m) =>
130
- m._getType() === 'tool' &&
131
- 'name' in m &&
132
- (m as { name?: string }).name === Constants.SUBAGENT
133
- );
134
- const calculatorCalls = msgs.filter(
135
- (m) =>
136
- m._getType() === 'tool' &&
137
- 'name' in m &&
138
- (m as { name?: string }).name === 'calculator'
139
- );
140
-
141
- console.log('--- Verification ---');
142
- console.log(`subagent tool calls seen (parent): ${subagentCalls.length}`);
143
- console.log(
144
- `calculator tool calls seen (parent): ${calculatorCalls.length} (expected: 0 if subagent did the math)`
145
- );
146
- if (subagentCalls.length > 0) {
147
- const subResult = subagentCalls[0].content as string;
148
- console.log(`\nsubagent result snippet:\n${subResult.slice(0, 600)}\n`);
149
- if (/\berror\b/i.test(subResult) && /tool/i.test(subResult)) {
150
- console.log('⚠️ BUG CONFIRMED: subagent result mentions tool error');
151
- } else if (!/\d/.test(subResult)) {
152
- console.log('⚠️ POSSIBLY BUGGY: subagent result has no numbers');
153
- }
154
- }
155
- }
156
-
157
- main().catch((err) => {
158
- console.error('Script error:', err);
159
- process.exit(1);
160
- });
@@ -1,305 +0,0 @@
1
- import { HumanMessage } from '@langchain/core/messages';
2
- import { FakeListChatModel } from '@langchain/core/utils/testing';
3
- import type { ToolCall } from '@langchain/core/messages/tool';
4
- import type { RunnableConfig } from '@langchain/core/runnables';
5
- import type * as t from '@/types';
6
- import { Run } from '@/run';
7
- import {
8
- Constants,
9
- GraphEvents,
10
- Providers,
11
- ToolEndHandler,
12
- ModelEndHandler,
13
- StandardGraph,
14
- } from '@/index';
15
- import * as providers from '@/llm/providers';
16
-
17
- const CHILD_RESPONSE = 'Research result: Paris is the capital of France.';
18
-
19
- const callerConfig: Partial<RunnableConfig> & {
20
- version: 'v1' | 'v2';
21
- streamMode: string;
22
- } = {
23
- configurable: { thread_id: 'subagent-test-thread' },
24
- streamMode: 'values',
25
- version: 'v2' as const,
26
- };
27
-
28
- const createParentAgent = (): t.AgentInputs => ({
29
- agentId: 'parent',
30
- provider: Providers.OPENAI,
31
- clientOptions: { modelName: 'gpt-4o-mini', apiKey: 'test-key' },
32
- instructions:
33
- 'You are a supervisor. Delegate research tasks using the subagent tool.',
34
- maxContextTokens: 8000,
35
- subagentConfigs: [
36
- {
37
- type: 'researcher',
38
- name: 'Research Agent',
39
- description: 'Researches and summarizes information',
40
- agentInputs: {
41
- agentId: 'researcher',
42
- provider: Providers.OPENAI,
43
- clientOptions: { modelName: 'gpt-4o-mini', apiKey: 'test-key' },
44
- instructions: 'You are a research agent. Answer concisely.',
45
- maxContextTokens: 8000,
46
- },
47
- },
48
- ],
49
- });
50
-
51
- describe('Subagent Integration', () => {
52
- jest.setTimeout(30000);
53
-
54
- let getChatModelClassSpy: jest.SpyInstance;
55
- const originalGetChatModelClass = providers.getChatModelClass;
56
-
57
- beforeEach(() => {
58
- getChatModelClassSpy = jest
59
- .spyOn(providers, 'getChatModelClass')
60
- .mockImplementation(((provider: Providers) => {
61
- if (provider === Providers.OPENAI) {
62
- return class extends FakeListChatModel {
63
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
64
- constructor(_options: any) {
65
- super({ responses: [CHILD_RESPONSE] });
66
- }
67
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
68
- } as any;
69
- }
70
- return originalGetChatModelClass(provider);
71
- }) as typeof providers.getChatModelClass);
72
- });
73
-
74
- afterEach(() => {
75
- getChatModelClassSpy.mockRestore();
76
- });
77
-
78
- it('should create subagent tool on agent context', async () => {
79
- const run = await Run.create<t.IState>({
80
- runId: `subagent-test-${Date.now()}`,
81
- graphConfig: {
82
- type: 'standard',
83
- agents: [createParentAgent()],
84
- },
85
- returnContent: true,
86
- skipCleanup: true,
87
- });
88
-
89
- expect(run.Graph).toBeDefined();
90
- const parentContext = (run.Graph as StandardGraph).agentContexts.get(
91
- 'parent'
92
- );
93
- expect(parentContext).toBeDefined();
94
- expect(parentContext?.graphTools).toBeDefined();
95
-
96
- const subagentTool = (parentContext?.graphTools as t.GenericTool[]).find(
97
- (t) => 'name' in t && t.name === Constants.SUBAGENT
98
- );
99
- expect(subagentTool).toBeDefined();
100
- });
101
-
102
- it('should execute subagent and return filtered result to parent', async () => {
103
- const customHandlers: Record<string, t.EventHandler> = {
104
- [GraphEvents.TOOL_END]: new ToolEndHandler(),
105
- [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),
106
- };
107
-
108
- const run = await Run.create<t.IState>({
109
- runId: `subagent-exec-${Date.now()}`,
110
- graphConfig: {
111
- type: 'standard',
112
- agents: [createParentAgent()],
113
- },
114
- returnContent: true,
115
- skipCleanup: true,
116
- customHandlers,
117
- });
118
-
119
- const subagentToolCall: ToolCall = {
120
- id: 'call_subagent_1',
121
- name: Constants.SUBAGENT,
122
- args: {
123
- description: 'What is the capital of France?',
124
- subagent_type: 'researcher',
125
- },
126
- type: 'tool_call',
127
- };
128
-
129
- run.Graph?.overrideTestModel(
130
- [
131
- 'Let me delegate this research task.',
132
- `Based on the research: ${CHILD_RESPONSE}`,
133
- ],
134
- 10,
135
- [subagentToolCall]
136
- );
137
-
138
- const result = await run.processStream(
139
- { messages: [new HumanMessage('What is the capital of France?')] },
140
- callerConfig
141
- );
142
-
143
- expect(result).toBeDefined();
144
-
145
- const runMessages = run.getRunMessages();
146
- expect(runMessages).toBeDefined();
147
- expect(runMessages!.length).toBeGreaterThan(0);
148
-
149
- const toolMessages = runMessages!.filter(
150
- (msg) => msg._getType() === 'tool'
151
- );
152
- const subagentResult = toolMessages.find(
153
- (msg) => 'name' in msg && msg.name === Constants.SUBAGENT
154
- );
155
- expect(subagentResult).toBeDefined();
156
- expect(String(subagentResult!.content)).toContain('Paris');
157
- });
158
-
159
- it('should not create subagent tool when no subagentConfigs', async () => {
160
- const agentWithoutSubagents: t.AgentInputs = {
161
- agentId: 'plain',
162
- provider: Providers.OPENAI,
163
- clientOptions: { modelName: 'gpt-4o-mini', apiKey: 'test-key' },
164
- instructions: 'Plain agent without subagents.',
165
- maxContextTokens: 8000,
166
- };
167
-
168
- const run = await Run.create<t.IState>({
169
- runId: `no-subagent-${Date.now()}`,
170
- graphConfig: {
171
- type: 'standard',
172
- agents: [agentWithoutSubagents],
173
- },
174
- returnContent: true,
175
- skipCleanup: true,
176
- });
177
-
178
- const context = (run.Graph as StandardGraph).agentContexts.get('plain');
179
- const tools = context?.graphTools as t.GenericTool[] | undefined;
180
- const subagentTool = tools?.find(
181
- (t) => 'name' in t && t.name === Constants.SUBAGENT
182
- );
183
- expect(subagentTool).toBeUndefined();
184
- });
185
-
186
- it('should handle self-spawn subagent config', async () => {
187
- const agentWithSelfSpawn: t.AgentInputs = {
188
- agentId: 'self-parent',
189
- provider: Providers.OPENAI,
190
- clientOptions: { modelName: 'gpt-4o-mini', apiKey: 'test-key' },
191
- instructions: 'Agent with self-spawn for context isolation.',
192
- maxContextTokens: 8000,
193
- subagentConfigs: [
194
- {
195
- type: 'isolated',
196
- name: 'Isolated Worker',
197
- description: 'Runs a task with isolated context',
198
- self: true,
199
- },
200
- ],
201
- };
202
-
203
- const run = await Run.create<t.IState>({
204
- runId: `self-spawn-${Date.now()}`,
205
- graphConfig: {
206
- type: 'standard',
207
- agents: [agentWithSelfSpawn],
208
- },
209
- returnContent: true,
210
- skipCleanup: true,
211
- });
212
-
213
- const context = (run.Graph as StandardGraph).agentContexts.get(
214
- 'self-parent'
215
- );
216
- const tools = context?.graphTools as t.GenericTool[] | undefined;
217
- const subagentTool = tools?.find(
218
- (t) => 'name' in t && t.name === Constants.SUBAGENT
219
- );
220
- expect(subagentTool).toBeDefined();
221
- });
222
-
223
- it('should not create subagent tool when maxSubagentDepth is 0', async () => {
224
- const agentWithZeroDepth: t.AgentInputs = {
225
- ...createParentAgent(),
226
- agentId: 'zero-depth',
227
- maxSubagentDepth: 0,
228
- };
229
-
230
- const run = await Run.create<t.IState>({
231
- runId: `zero-depth-${Date.now()}`,
232
- graphConfig: {
233
- type: 'standard',
234
- agents: [agentWithZeroDepth],
235
- },
236
- returnContent: true,
237
- skipCleanup: true,
238
- });
239
-
240
- const context = (run.Graph as StandardGraph).agentContexts.get(
241
- 'zero-depth'
242
- );
243
- const tools = context?.graphTools as t.GenericTool[] | undefined;
244
- const subagentTool = tools?.find(
245
- (t) => 'name' in t && t.name === Constants.SUBAGENT
246
- );
247
- expect(subagentTool).toBeUndefined();
248
- });
249
-
250
- it('should account for subagent tool schema in toolSchemaTokens', async () => {
251
- /** Simple char-count tokenizer — deterministic, lets us assert presence. */
252
- const tokenCounter: t.TokenCounter = (message) => {
253
- const content = message.content;
254
- if (typeof content === 'string') return content.length;
255
- if (Array.isArray(content)) return JSON.stringify(content).length;
256
- return JSON.stringify(content).length;
257
- };
258
-
259
- const agentWithSubagent = createParentAgent();
260
- const runWith = await Run.create<t.IState>({
261
- runId: `with-sub-${Date.now()}`,
262
- graphConfig: {
263
- type: 'standard',
264
- agents: [agentWithSubagent],
265
- },
266
- tokenCounter,
267
- returnContent: true,
268
- skipCleanup: true,
269
- });
270
-
271
- const agentWithoutSubagent: t.AgentInputs = {
272
- agentId: 'plain',
273
- provider: Providers.OPENAI,
274
- clientOptions: { modelName: 'gpt-4o-mini', apiKey: 'test-key' },
275
- instructions:
276
- 'You are a supervisor. Delegate research tasks using the subagent tool.',
277
- maxContextTokens: 8000,
278
- };
279
- const runWithout = await Run.create<t.IState>({
280
- runId: `without-sub-${Date.now()}`,
281
- graphConfig: {
282
- type: 'standard',
283
- agents: [agentWithoutSubagent],
284
- },
285
- tokenCounter,
286
- returnContent: true,
287
- skipCleanup: true,
288
- });
289
-
290
- const contextWith = (runWith.Graph as StandardGraph).agentContexts.get(
291
- 'parent'
292
- );
293
- const contextWithout = (
294
- runWithout.Graph as StandardGraph
295
- ).agentContexts.get('plain');
296
-
297
- await contextWith?.tokenCalculationPromise;
298
- await contextWithout?.tokenCalculationPromise;
299
-
300
- /** Subagent tool schema is ~600 chars; expect measurable difference. */
301
- expect(contextWith!.toolSchemaTokens).toBeGreaterThan(
302
- contextWithout!.toolSchemaTokens
303
- );
304
- });
305
- });