@illuma-ai/agents 1.1.21 → 1.1.23

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 (244) hide show
  1. package/dist/cjs/graphs/Graph.cjs +12 -1
  2. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  3. package/dist/cjs/graphs/MultiAgentGraph.cjs +105 -1
  4. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  5. package/dist/cjs/run.cjs +20 -9
  6. package/dist/cjs/run.cjs.map +1 -1
  7. package/dist/cjs/utils/llm.cjs.map +1 -1
  8. package/dist/esm/graphs/Graph.mjs +12 -1
  9. package/dist/esm/graphs/Graph.mjs.map +1 -1
  10. package/dist/esm/graphs/MultiAgentGraph.mjs +105 -1
  11. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  12. package/dist/esm/run.mjs +20 -9
  13. package/dist/esm/run.mjs.map +1 -1
  14. package/dist/esm/utils/llm.mjs.map +1 -1
  15. package/dist/types/graphs/MultiAgentGraph.d.ts +17 -0
  16. package/package.json +1 -1
  17. package/src/graphs/Graph.ts +13 -1
  18. package/src/graphs/MultiAgentGraph.ts +128 -1
  19. package/src/graphs/__tests__/multi-agent-delegate.test.ts +205 -0
  20. package/src/run.ts +20 -11
  21. package/src/scripts/test-bedrock-handoff-autonomous.ts +231 -0
  22. package/src/utils/llm.ts +1 -0
  23. package/src/agents/AgentContext.js +0 -782
  24. package/src/agents/AgentContext.test.js +0 -421
  25. package/src/agents/__tests__/AgentContext.test.js +0 -678
  26. package/src/agents/__tests__/resolveStructuredOutputMode.test.js +0 -117
  27. package/src/common/enum.js +0 -192
  28. package/src/common/index.js +0 -3
  29. package/src/events.js +0 -166
  30. package/src/graphs/Graph.js +0 -1857
  31. package/src/graphs/MultiAgentGraph.js +0 -1092
  32. package/src/graphs/__tests__/structured-output.integration.test.js +0 -624
  33. package/src/graphs/__tests__/structured-output.test.js +0 -144
  34. package/src/graphs/contextManagement.e2e.test.js +0 -718
  35. package/src/graphs/contextManagement.test.js +0 -485
  36. package/src/graphs/handoffValidation.test.js +0 -276
  37. package/src/graphs/index.js +0 -3
  38. package/src/index.js +0 -28
  39. package/src/instrumentation.js +0 -21
  40. package/src/llm/anthropic/index.js +0 -319
  41. package/src/llm/anthropic/types.js +0 -46
  42. package/src/llm/anthropic/utils/message_inputs.js +0 -627
  43. package/src/llm/anthropic/utils/message_outputs.js +0 -290
  44. package/src/llm/anthropic/utils/output_parsers.js +0 -89
  45. package/src/llm/anthropic/utils/tools.js +0 -25
  46. package/src/llm/bedrock/__tests__/bedrock-caching.test.js +0 -392
  47. package/src/llm/bedrock/index.js +0 -303
  48. package/src/llm/bedrock/types.js +0 -2
  49. package/src/llm/bedrock/utils/index.js +0 -6
  50. package/src/llm/bedrock/utils/message_inputs.js +0 -463
  51. package/src/llm/bedrock/utils/message_outputs.js +0 -269
  52. package/src/llm/fake.js +0 -92
  53. package/src/llm/google/index.js +0 -215
  54. package/src/llm/google/types.js +0 -12
  55. package/src/llm/google/utils/common.js +0 -670
  56. package/src/llm/google/utils/tools.js +0 -111
  57. package/src/llm/google/utils/zod_to_genai_parameters.js +0 -47
  58. package/src/llm/openai/index.js +0 -1033
  59. package/src/llm/openai/types.js +0 -2
  60. package/src/llm/openai/utils/index.js +0 -756
  61. package/src/llm/openai/utils/isReasoningModel.test.js +0 -79
  62. package/src/llm/openrouter/index.js +0 -261
  63. package/src/llm/openrouter/reasoning.test.js +0 -181
  64. package/src/llm/providers.js +0 -36
  65. package/src/llm/text.js +0 -65
  66. package/src/llm/vertexai/index.js +0 -402
  67. package/src/messages/__tests__/tools.test.js +0 -392
  68. package/src/messages/cache.js +0 -404
  69. package/src/messages/cache.test.js +0 -1167
  70. package/src/messages/content.js +0 -48
  71. package/src/messages/content.test.js +0 -314
  72. package/src/messages/core.js +0 -359
  73. package/src/messages/ensureThinkingBlock.test.js +0 -997
  74. package/src/messages/format.js +0 -973
  75. package/src/messages/formatAgentMessages.test.js +0 -2278
  76. package/src/messages/formatAgentMessages.tools.test.js +0 -362
  77. package/src/messages/formatMessage.test.js +0 -608
  78. package/src/messages/ids.js +0 -18
  79. package/src/messages/index.js +0 -9
  80. package/src/messages/labelContentByAgent.test.js +0 -725
  81. package/src/messages/prune.js +0 -438
  82. package/src/messages/reducer.js +0 -60
  83. package/src/messages/shiftIndexTokenCountMap.test.js +0 -63
  84. package/src/messages/summarize.js +0 -146
  85. package/src/messages/summarize.test.js +0 -332
  86. package/src/messages/tools.js +0 -90
  87. package/src/mockStream.js +0 -81
  88. package/src/prompts/collab.js +0 -7
  89. package/src/prompts/index.js +0 -3
  90. package/src/prompts/taskmanager.js +0 -58
  91. package/src/run.js +0 -427
  92. package/src/schemas/index.js +0 -3
  93. package/src/schemas/schema-preparation.test.js +0 -370
  94. package/src/schemas/validate.js +0 -314
  95. package/src/schemas/validate.test.js +0 -264
  96. package/src/scripts/abort.js +0 -127
  97. package/src/scripts/ant_web_search.js +0 -130
  98. package/src/scripts/ant_web_search_edge_case.js +0 -133
  99. package/src/scripts/ant_web_search_error_edge_case.js +0 -119
  100. package/src/scripts/args.js +0 -41
  101. package/src/scripts/bedrock-cache-debug.js +0 -186
  102. package/src/scripts/bedrock-content-aggregation-test.js +0 -195
  103. package/src/scripts/bedrock-merge-test.js +0 -80
  104. package/src/scripts/bedrock-parallel-tools-test.js +0 -150
  105. package/src/scripts/caching.js +0 -106
  106. package/src/scripts/cli.js +0 -152
  107. package/src/scripts/cli2.js +0 -119
  108. package/src/scripts/cli3.js +0 -163
  109. package/src/scripts/cli4.js +0 -165
  110. package/src/scripts/cli5.js +0 -165
  111. package/src/scripts/code_exec.js +0 -171
  112. package/src/scripts/code_exec_files.js +0 -180
  113. package/src/scripts/code_exec_multi_session.js +0 -185
  114. package/src/scripts/code_exec_ptc.js +0 -265
  115. package/src/scripts/code_exec_session.js +0 -217
  116. package/src/scripts/code_exec_simple.js +0 -120
  117. package/src/scripts/content.js +0 -111
  118. package/src/scripts/empty_input.js +0 -125
  119. package/src/scripts/handoff-test.js +0 -96
  120. package/src/scripts/image.js +0 -138
  121. package/src/scripts/memory.js +0 -83
  122. package/src/scripts/multi-agent-chain.js +0 -271
  123. package/src/scripts/multi-agent-conditional.js +0 -185
  124. package/src/scripts/multi-agent-document-review-chain.js +0 -171
  125. package/src/scripts/multi-agent-hybrid-flow.js +0 -264
  126. package/src/scripts/multi-agent-parallel-start.js +0 -214
  127. package/src/scripts/multi-agent-parallel.js +0 -346
  128. package/src/scripts/multi-agent-sequence.js +0 -184
  129. package/src/scripts/multi-agent-supervisor.js +0 -324
  130. package/src/scripts/multi-agent-test.js +0 -147
  131. package/src/scripts/parallel-asymmetric-tools-test.js +0 -202
  132. package/src/scripts/parallel-full-metadata-test.js +0 -176
  133. package/src/scripts/parallel-tools-test.js +0 -256
  134. package/src/scripts/programmatic_exec.js +0 -277
  135. package/src/scripts/programmatic_exec_agent.js +0 -168
  136. package/src/scripts/search.js +0 -118
  137. package/src/scripts/sequential-full-metadata-test.js +0 -143
  138. package/src/scripts/simple.js +0 -174
  139. package/src/scripts/single-agent-metadata-test.js +0 -152
  140. package/src/scripts/stream.js +0 -113
  141. package/src/scripts/test-custom-prompt-key.js +0 -132
  142. package/src/scripts/test-handoff-input.js +0 -143
  143. package/src/scripts/test-handoff-preamble.js +0 -227
  144. package/src/scripts/test-handoff-steering.js +0 -353
  145. package/src/scripts/test-multi-agent-list-handoff.js +0 -318
  146. package/src/scripts/test-parallel-agent-labeling.js +0 -253
  147. package/src/scripts/test-parallel-handoffs.js +0 -229
  148. package/src/scripts/test-thinking-handoff-bedrock.js +0 -132
  149. package/src/scripts/test-thinking-handoff.js +0 -132
  150. package/src/scripts/test-thinking-to-thinking-handoff-bedrock.js +0 -140
  151. package/src/scripts/test-tool-before-handoff-role-order.js +0 -223
  152. package/src/scripts/test-tools-before-handoff.js +0 -187
  153. package/src/scripts/test_code_api.js +0 -263
  154. package/src/scripts/thinking-bedrock.js +0 -128
  155. package/src/scripts/thinking-vertexai.js +0 -130
  156. package/src/scripts/thinking.js +0 -134
  157. package/src/scripts/tool_search.js +0 -114
  158. package/src/scripts/tools.js +0 -125
  159. package/src/specs/agent-handoffs-bedrock.integration.test.js +0 -280
  160. package/src/specs/agent-handoffs.test.js +0 -924
  161. package/src/specs/anthropic.simple.test.js +0 -287
  162. package/src/specs/azure.simple.test.js +0 -381
  163. package/src/specs/cache.simple.test.js +0 -282
  164. package/src/specs/custom-event-await.test.js +0 -148
  165. package/src/specs/deepseek.simple.test.js +0 -189
  166. package/src/specs/emergency-prune.test.js +0 -308
  167. package/src/specs/moonshot.simple.test.js +0 -237
  168. package/src/specs/observability.integration.test.js +0 -1337
  169. package/src/specs/openai.simple.test.js +0 -233
  170. package/src/specs/openrouter.simple.test.js +0 -202
  171. package/src/specs/prune.test.js +0 -733
  172. package/src/specs/reasoning.test.js +0 -144
  173. package/src/specs/spec.utils.js +0 -4
  174. package/src/specs/thinking-handoff.test.js +0 -486
  175. package/src/specs/thinking-prune.test.js +0 -600
  176. package/src/specs/token-distribution-edge-case.test.js +0 -246
  177. package/src/specs/token-memoization.test.js +0 -32
  178. package/src/specs/tokens.test.js +0 -49
  179. package/src/specs/tool-error.test.js +0 -139
  180. package/src/splitStream.js +0 -204
  181. package/src/splitStream.test.js +0 -504
  182. package/src/stream.js +0 -650
  183. package/src/stream.test.js +0 -225
  184. package/src/test/mockTools.js +0 -340
  185. package/src/tools/BrowserTools.js +0 -245
  186. package/src/tools/Calculator.js +0 -38
  187. package/src/tools/Calculator.test.js +0 -225
  188. package/src/tools/CodeExecutor.js +0 -233
  189. package/src/tools/ProgrammaticToolCalling.js +0 -602
  190. package/src/tools/StreamingToolCallBuffer.js +0 -179
  191. package/src/tools/ToolNode.js +0 -930
  192. package/src/tools/ToolSearch.js +0 -904
  193. package/src/tools/__tests__/BrowserTools.test.js +0 -306
  194. package/src/tools/__tests__/ProgrammaticToolCalling.integration.test.js +0 -276
  195. package/src/tools/__tests__/ProgrammaticToolCalling.test.js +0 -807
  196. package/src/tools/__tests__/StreamingToolCallBuffer.test.js +0 -175
  197. package/src/tools/__tests__/ToolApproval.test.js +0 -675
  198. package/src/tools/__tests__/ToolNode.recovery.test.js +0 -200
  199. package/src/tools/__tests__/ToolNode.session.test.js +0 -319
  200. package/src/tools/__tests__/ToolSearch.integration.test.js +0 -125
  201. package/src/tools/__tests__/ToolSearch.test.js +0 -812
  202. package/src/tools/__tests__/handlers.test.js +0 -799
  203. package/src/tools/__tests__/truncation-recovery.integration.test.js +0 -362
  204. package/src/tools/handlers.js +0 -306
  205. package/src/tools/schema.js +0 -25
  206. package/src/tools/search/anthropic.js +0 -34
  207. package/src/tools/search/content.js +0 -116
  208. package/src/tools/search/content.test.js +0 -133
  209. package/src/tools/search/firecrawl.js +0 -173
  210. package/src/tools/search/format.js +0 -198
  211. package/src/tools/search/highlights.js +0 -241
  212. package/src/tools/search/index.js +0 -3
  213. package/src/tools/search/jina-reranker.test.js +0 -106
  214. package/src/tools/search/rerankers.js +0 -165
  215. package/src/tools/search/schema.js +0 -102
  216. package/src/tools/search/search.js +0 -561
  217. package/src/tools/search/serper-scraper.js +0 -126
  218. package/src/tools/search/test.js +0 -129
  219. package/src/tools/search/tool.js +0 -453
  220. package/src/tools/search/types.js +0 -2
  221. package/src/tools/search/utils.js +0 -59
  222. package/src/types/graph.js +0 -24
  223. package/src/types/graph.test.js +0 -192
  224. package/src/types/index.js +0 -7
  225. package/src/types/llm.js +0 -2
  226. package/src/types/messages.js +0 -2
  227. package/src/types/run.js +0 -2
  228. package/src/types/stream.js +0 -2
  229. package/src/types/tools.js +0 -2
  230. package/src/utils/contextAnalytics.js +0 -79
  231. package/src/utils/contextAnalytics.test.js +0 -166
  232. package/src/utils/events.js +0 -26
  233. package/src/utils/graph.js +0 -11
  234. package/src/utils/handlers.js +0 -65
  235. package/src/utils/index.js +0 -10
  236. package/src/utils/llm.js +0 -21
  237. package/src/utils/llmConfig.js +0 -205
  238. package/src/utils/logging.js +0 -37
  239. package/src/utils/misc.js +0 -51
  240. package/src/utils/run.js +0 -69
  241. package/src/utils/schema.js +0 -21
  242. package/src/utils/title.js +0 -119
  243. package/src/utils/tokens.js +0 -92
  244. package/src/utils/toonFormat.js +0 -379
@@ -1,678 +0,0 @@
1
- // src/agents/__tests__/AgentContext.test.ts
2
- import { AgentContext } from '../AgentContext';
3
- import { Providers } from '@/common';
4
- describe('AgentContext', () => {
5
- const createBasicContext = (options = {}) => {
6
- const { agentConfig = {}, tokenCounter, indexTokenCountMap } = options;
7
- return AgentContext.fromConfig({
8
- agentId: 'test-agent',
9
- provider: Providers.OPENAI,
10
- instructions: 'Test instructions',
11
- ...agentConfig,
12
- }, tokenCounter, indexTokenCountMap);
13
- };
14
- const createMockTool = (name) => ({
15
- name,
16
- description: `Mock ${name} tool`,
17
- invoke: jest.fn(),
18
- schema: { type: 'object', properties: {} },
19
- });
20
- describe('System Runnable - Lazy Creation', () => {
21
- it('creates system runnable on first access', () => {
22
- const ctx = createBasicContext({
23
- agentConfig: { instructions: 'Hello world' },
24
- });
25
- expect(ctx.systemRunnable).toBeDefined();
26
- });
27
- it('returns cached system runnable on subsequent access', () => {
28
- const ctx = createBasicContext({
29
- agentConfig: { instructions: 'Hello world' },
30
- });
31
- const first = ctx.systemRunnable;
32
- const second = ctx.systemRunnable;
33
- expect(first).toBe(second);
34
- });
35
- it('returns undefined when no instructions provided', () => {
36
- const ctx = createBasicContext({
37
- agentConfig: {
38
- instructions: undefined,
39
- additional_instructions: undefined,
40
- },
41
- });
42
- expect(ctx.systemRunnable).toBeUndefined();
43
- });
44
- it('includes additional_instructions in system message', () => {
45
- const ctx = createBasicContext({
46
- agentConfig: {
47
- instructions: 'Base instructions',
48
- additional_instructions: 'Additional instructions',
49
- },
50
- });
51
- expect(ctx.systemRunnable).toBeDefined();
52
- });
53
- });
54
- describe('System Runnable - Stale Flag', () => {
55
- it('rebuilds when marked stale via markToolsAsDiscovered', () => {
56
- const toolRegistry = new Map([
57
- [
58
- 'deferred_tool',
59
- {
60
- name: 'deferred_tool',
61
- description: 'A deferred code-only tool',
62
- allowed_callers: ['code_execution'],
63
- defer_loading: true,
64
- },
65
- ],
66
- ]);
67
- const ctx = createBasicContext({
68
- agentConfig: { instructions: 'Test', toolRegistry },
69
- });
70
- const firstRunnable = ctx.systemRunnable;
71
- const hasNew = ctx.markToolsAsDiscovered(['deferred_tool']);
72
- expect(hasNew).toBe(true);
73
- const secondRunnable = ctx.systemRunnable;
74
- expect(secondRunnable).not.toBe(firstRunnable);
75
- });
76
- it('does not rebuild when discovering already-known tools', () => {
77
- const toolRegistry = new Map([
78
- [
79
- 'tool1',
80
- {
81
- name: 'tool1',
82
- description: 'Tool 1',
83
- allowed_callers: ['code_execution'],
84
- defer_loading: true,
85
- },
86
- ],
87
- ]);
88
- const ctx = createBasicContext({
89
- agentConfig: { instructions: 'Test', toolRegistry },
90
- });
91
- ctx.markToolsAsDiscovered(['tool1']);
92
- const firstRunnable = ctx.systemRunnable;
93
- const hasNew = ctx.markToolsAsDiscovered(['tool1']);
94
- expect(hasNew).toBe(false);
95
- const secondRunnable = ctx.systemRunnable;
96
- expect(secondRunnable).toBe(firstRunnable);
97
- });
98
- });
99
- describe('markToolsAsDiscovered', () => {
100
- it('returns true when new tools are discovered', () => {
101
- const ctx = createBasicContext();
102
- const result = ctx.markToolsAsDiscovered(['tool1', 'tool2']);
103
- expect(result).toBe(true);
104
- expect(ctx.discoveredToolNames.has('tool1')).toBe(true);
105
- expect(ctx.discoveredToolNames.has('tool2')).toBe(true);
106
- });
107
- it('returns false when all tools already discovered', () => {
108
- const ctx = createBasicContext();
109
- ctx.markToolsAsDiscovered(['tool1']);
110
- const result = ctx.markToolsAsDiscovered(['tool1']);
111
- expect(result).toBe(false);
112
- });
113
- it('returns true if at least one tool is new', () => {
114
- const ctx = createBasicContext();
115
- ctx.markToolsAsDiscovered(['tool1']);
116
- const result = ctx.markToolsAsDiscovered(['tool1', 'tool2']);
117
- expect(result).toBe(true);
118
- expect(ctx.discoveredToolNames.size).toBe(2);
119
- });
120
- it('handles empty array gracefully', () => {
121
- const ctx = createBasicContext();
122
- const result = ctx.markToolsAsDiscovered([]);
123
- expect(result).toBe(false);
124
- });
125
- });
126
- describe('buildProgrammaticOnlyToolsInstructions', () => {
127
- it('includes code_execution-only tools in system message', () => {
128
- const toolRegistry = new Map([
129
- [
130
- 'programmatic_tool',
131
- {
132
- name: 'programmatic_tool',
133
- description: 'Only callable via code execution',
134
- allowed_callers: ['code_execution'],
135
- },
136
- ],
137
- ]);
138
- const ctx = createBasicContext({
139
- agentConfig: { instructions: 'Base', toolRegistry },
140
- });
141
- const runnable = ctx.systemRunnable;
142
- expect(runnable).toBeDefined();
143
- });
144
- it('excludes direct-callable tools from programmatic section', () => {
145
- const toolRegistry = new Map([
146
- [
147
- 'direct_tool',
148
- {
149
- name: 'direct_tool',
150
- description: 'Direct callable',
151
- allowed_callers: ['direct'],
152
- },
153
- ],
154
- [
155
- 'both_tool',
156
- {
157
- name: 'both_tool',
158
- description: 'Both direct and code',
159
- allowed_callers: ['direct', 'code_execution'],
160
- },
161
- ],
162
- ]);
163
- const ctx = createBasicContext({
164
- agentConfig: { instructions: 'Base', toolRegistry },
165
- });
166
- expect(ctx.systemRunnable).toBeDefined();
167
- });
168
- it('excludes deferred code_execution-only tools until discovered', () => {
169
- const toolRegistry = new Map([
170
- [
171
- 'deferred_code_tool',
172
- {
173
- name: 'deferred_code_tool',
174
- description: 'Deferred and code-only',
175
- allowed_callers: ['code_execution'],
176
- defer_loading: true,
177
- },
178
- ],
179
- [
180
- 'immediate_code_tool',
181
- {
182
- name: 'immediate_code_tool',
183
- description: 'Immediate and code-only',
184
- allowed_callers: ['code_execution'],
185
- defer_loading: false,
186
- },
187
- ],
188
- ]);
189
- const ctx = createBasicContext({
190
- agentConfig: { instructions: 'Base', toolRegistry },
191
- });
192
- const firstRunnable = ctx.systemRunnable;
193
- expect(firstRunnable).toBeDefined();
194
- ctx.markToolsAsDiscovered(['deferred_code_tool']);
195
- const secondRunnable = ctx.systemRunnable;
196
- expect(secondRunnable).not.toBe(firstRunnable);
197
- });
198
- });
199
- describe('getToolsForBinding', () => {
200
- it('returns all tools when no toolRegistry', () => {
201
- const tools = [createMockTool('tool1'), createMockTool('tool2')];
202
- const ctx = createBasicContext({ agentConfig: { tools } });
203
- const result = ctx.getToolsForBinding();
204
- expect(result).toEqual(tools);
205
- });
206
- it('excludes code_execution-only tools', () => {
207
- const tools = [
208
- createMockTool('direct_tool'),
209
- createMockTool('code_only_tool'),
210
- ];
211
- const toolRegistry = new Map([
212
- ['direct_tool', { name: 'direct_tool', allowed_callers: ['direct'] }],
213
- [
214
- 'code_only_tool',
215
- { name: 'code_only_tool', allowed_callers: ['code_execution'] },
216
- ],
217
- ]);
218
- const ctx = createBasicContext({ agentConfig: { tools, toolRegistry } });
219
- const result = ctx.getToolsForBinding();
220
- expect(result?.length).toBe(1);
221
- expect((result?.[0]).name).toBe('direct_tool');
222
- });
223
- it('excludes deferred tools until discovered', () => {
224
- const tools = [
225
- createMockTool('immediate_tool'),
226
- createMockTool('deferred_tool'),
227
- ];
228
- const toolRegistry = new Map([
229
- [
230
- 'immediate_tool',
231
- {
232
- name: 'immediate_tool',
233
- allowed_callers: ['direct'],
234
- defer_loading: false,
235
- },
236
- ],
237
- [
238
- 'deferred_tool',
239
- {
240
- name: 'deferred_tool',
241
- allowed_callers: ['direct'],
242
- defer_loading: true,
243
- },
244
- ],
245
- ]);
246
- const ctx = createBasicContext({ agentConfig: { tools, toolRegistry } });
247
- let result = ctx.getToolsForBinding();
248
- expect(result?.length).toBe(1);
249
- expect((result?.[0]).name).toBe('immediate_tool');
250
- ctx.markToolsAsDiscovered(['deferred_tool']);
251
- result = ctx.getToolsForBinding();
252
- expect(result?.length).toBe(2);
253
- });
254
- it('includes tools with both direct and code_execution callers', () => {
255
- const tools = [createMockTool('hybrid_tool')];
256
- const toolRegistry = new Map([
257
- [
258
- 'hybrid_tool',
259
- {
260
- name: 'hybrid_tool',
261
- allowed_callers: ['direct', 'code_execution'],
262
- },
263
- ],
264
- ]);
265
- const ctx = createBasicContext({ agentConfig: { tools, toolRegistry } });
266
- const result = ctx.getToolsForBinding();
267
- expect(result?.length).toBe(1);
268
- });
269
- it('defaults to direct when allowed_callers not specified', () => {
270
- const tools = [createMockTool('default_tool')];
271
- const toolRegistry = new Map([
272
- ['default_tool', { name: 'default_tool' }],
273
- ]);
274
- const ctx = createBasicContext({ agentConfig: { tools, toolRegistry } });
275
- const result = ctx.getToolsForBinding();
276
- expect(result?.length).toBe(1);
277
- });
278
- });
279
- describe('Token Accounting', () => {
280
- const mockTokenCounter = (msg) => {
281
- const content = typeof msg.content === 'string'
282
- ? msg.content
283
- : JSON.stringify(msg.content);
284
- return content.length;
285
- };
286
- it('counts system message tokens on first access', () => {
287
- const ctx = createBasicContext({
288
- agentConfig: { instructions: 'Hello' },
289
- tokenCounter: mockTokenCounter,
290
- });
291
- ctx.initializeSystemRunnable();
292
- expect(ctx.instructionTokens).toBeGreaterThan(0);
293
- });
294
- it('updates token count when system message changes', () => {
295
- const toolRegistry = new Map([
296
- [
297
- 'code_tool',
298
- {
299
- name: 'code_tool',
300
- description: 'A tool with a long description that adds tokens',
301
- allowed_callers: ['code_execution'],
302
- defer_loading: true,
303
- },
304
- ],
305
- ]);
306
- const ctx = createBasicContext({
307
- agentConfig: { instructions: 'Short', toolRegistry },
308
- tokenCounter: mockTokenCounter,
309
- });
310
- ctx.initializeSystemRunnable();
311
- const initialTokens = ctx.instructionTokens;
312
- ctx.markToolsAsDiscovered(['code_tool']);
313
- void ctx.systemRunnable;
314
- expect(ctx.instructionTokens).toBeGreaterThan(initialTokens);
315
- });
316
- });
317
- describe('reset()', () => {
318
- it('clears all cached state', () => {
319
- const ctx = createBasicContext({ agentConfig: { instructions: 'Test' } });
320
- ctx.markToolsAsDiscovered(['tool1']);
321
- void ctx.systemRunnable;
322
- ctx.instructionTokens = 100;
323
- ctx.indexTokenCountMap = { '0': 50 };
324
- ctx.currentUsage = { input_tokens: 100 };
325
- ctx.reset();
326
- expect(ctx.discoveredToolNames.size).toBe(0);
327
- expect(ctx.instructionTokens).toBe(0);
328
- expect(ctx.indexTokenCountMap).toEqual({});
329
- expect(ctx.currentUsage).toBeUndefined();
330
- });
331
- it('rebuilds indexTokenCountMap from base map after reset', async () => {
332
- const tokenCounter = jest.fn(() => 5);
333
- const ctx = createBasicContext({
334
- tokenCounter,
335
- indexTokenCountMap: { '0': 10, '1': 20 },
336
- });
337
- await ctx.tokenCalculationPromise;
338
- ctx.indexTokenCountMap = {};
339
- ctx.reset();
340
- await ctx.tokenCalculationPromise;
341
- expect(ctx.indexTokenCountMap['1']).toBe(20);
342
- expect(ctx.indexTokenCountMap['0'] ?? 0).toBeGreaterThanOrEqual(10);
343
- });
344
- it('forces rebuild on next systemRunnable access', () => {
345
- const ctx = createBasicContext({ agentConfig: { instructions: 'Test' } });
346
- const firstRunnable = ctx.systemRunnable;
347
- ctx.reset();
348
- ctx.instructions = 'Test';
349
- const secondRunnable = ctx.systemRunnable;
350
- expect(secondRunnable).not.toBe(firstRunnable);
351
- });
352
- });
353
- describe('initializeSystemRunnable()', () => {
354
- it('explicitly initializes system runnable', () => {
355
- const ctx = createBasicContext({ agentConfig: { instructions: 'Test' } });
356
- expect(ctx['cachedSystemRunnable']).toBeUndefined();
357
- ctx.initializeSystemRunnable();
358
- expect(ctx['cachedSystemRunnable']).toBeDefined();
359
- });
360
- it('is idempotent when not stale', () => {
361
- const ctx = createBasicContext({ agentConfig: { instructions: 'Test' } });
362
- ctx.initializeSystemRunnable();
363
- const first = ctx['cachedSystemRunnable'];
364
- ctx.initializeSystemRunnable();
365
- const second = ctx['cachedSystemRunnable'];
366
- expect(first).toBe(second);
367
- });
368
- });
369
- describe('Edge Cases', () => {
370
- it('handles empty toolRegistry gracefully', () => {
371
- const ctx = createBasicContext({
372
- agentConfig: {
373
- instructions: 'Test',
374
- toolRegistry: new Map(),
375
- tools: [],
376
- },
377
- });
378
- expect(ctx.systemRunnable).toBeDefined();
379
- expect(ctx.getToolsForBinding()).toEqual([]);
380
- });
381
- it('handles undefined tools array', () => {
382
- const ctx = createBasicContext({
383
- agentConfig: { instructions: 'Test', tools: undefined },
384
- });
385
- expect(ctx.getToolsForBinding()).toBeUndefined();
386
- });
387
- it('handles tool not in registry', () => {
388
- const tools = [createMockTool('unknown_tool')];
389
- const toolRegistry = new Map();
390
- const ctx = createBasicContext({ agentConfig: { tools, toolRegistry } });
391
- const result = ctx.getToolsForBinding();
392
- expect(result?.length).toBe(1);
393
- });
394
- it('handles tool without name property', () => {
395
- const toolWithoutName = { invoke: jest.fn() };
396
- const toolRegistry = new Map();
397
- const ctx = createBasicContext({
398
- agentConfig: { tools: [toolWithoutName], toolRegistry },
399
- });
400
- const result = ctx.getToolsForBinding();
401
- expect(result?.length).toBe(1);
402
- });
403
- it('handles discovery of non-existent tool', () => {
404
- const toolRegistry = new Map([
405
- [
406
- 'real_tool',
407
- { name: 'real_tool', allowed_callers: ['code_execution'] },
408
- ],
409
- ]);
410
- const ctx = createBasicContext({
411
- agentConfig: { instructions: 'Test', toolRegistry },
412
- });
413
- const result = ctx.markToolsAsDiscovered(['fake_tool']);
414
- expect(result).toBe(true);
415
- expect(ctx.discoveredToolNames.has('fake_tool')).toBe(true);
416
- });
417
- });
418
- describe('Multi-Step Run Flow (emulating createCallModel)', () => {
419
- /**
420
- * This test emulates the flow in Graph.createCallModel across multiple turns:
421
- *
422
- * Turn 1: User asks a question
423
- * - No tool search results yet
424
- * - System runnable built with initial instructions
425
- * - Token map initialized
426
- *
427
- * Turn 2: Tool results come back (including tool search)
428
- * - extractToolDiscoveries() finds new tools
429
- * - markToolsAsDiscovered() called → sets stale flag
430
- * - systemRunnable getter rebuilds with discovered tools
431
- * - Token counts updated
432
- *
433
- * Turn 3: Another turn with same discovered tools
434
- * - No new discoveries
435
- * - systemRunnable returns cached (not rebuilt)
436
- * - Token counts unchanged
437
- */
438
- const mockTokenCounter = (msg) => {
439
- const content = typeof msg.content === 'string'
440
- ? msg.content
441
- : JSON.stringify(msg.content);
442
- return Math.ceil(content.length / 4); // ~4 chars per token (realistic)
443
- };
444
- it('handles complete multi-step run with tool discovery', () => {
445
- // Setup: Tools with different configurations
446
- const tools = [
447
- createMockTool('always_available'),
448
- createMockTool('deferred_direct_tool'),
449
- createMockTool('deferred_code_tool'),
450
- ];
451
- const toolRegistry = new Map([
452
- [
453
- 'always_available',
454
- {
455
- name: 'always_available',
456
- description: 'Always available tool',
457
- allowed_callers: ['direct'],
458
- defer_loading: false,
459
- },
460
- ],
461
- [
462
- 'deferred_direct_tool',
463
- {
464
- name: 'deferred_direct_tool',
465
- description: 'Deferred but direct-callable',
466
- allowed_callers: ['direct'],
467
- defer_loading: true,
468
- },
469
- ],
470
- [
471
- 'deferred_code_tool',
472
- {
473
- name: 'deferred_code_tool',
474
- description: 'Deferred and code-execution only - hidden until discovered',
475
- allowed_callers: ['code_execution'],
476
- defer_loading: true,
477
- },
478
- ],
479
- ]);
480
- const ctx = createBasicContext({
481
- agentConfig: {
482
- instructions: 'You are a helpful assistant.',
483
- tools,
484
- toolRegistry,
485
- },
486
- tokenCounter: mockTokenCounter,
487
- });
488
- // ========== TURN 1: Initial call (like first createCallModel) ==========
489
- // In createCallModel, we first check for tool discoveries (none yet)
490
- const turn1Discoveries = []; // No tool search results
491
- if (turn1Discoveries.length > 0) {
492
- ctx.markToolsAsDiscovered(turn1Discoveries);
493
- }
494
- // Get tools for binding
495
- const turn1Tools = ctx.getToolsForBinding();
496
- expect(turn1Tools?.length).toBe(1); // Only 'always_available'
497
- expect(turn1Tools?.map((t) => t.name)).toEqual([
498
- 'always_available',
499
- ]);
500
- // Access system runnable (triggers lazy build)
501
- const turn1Runnable = ctx.systemRunnable;
502
- expect(turn1Runnable).toBeDefined();
503
- // Store initial token count
504
- const turn1Tokens = ctx.instructionTokens;
505
- expect(turn1Tokens).toBeGreaterThan(0);
506
- // Simulate token map update (as done in fromConfig flow)
507
- ctx.updateTokenMapWithInstructions({ '0': 10, '1': 20 });
508
- expect(ctx.indexTokenCountMap['0']).toBe(10 + turn1Tokens);
509
- expect(ctx.indexTokenCountMap['1']).toBe(20);
510
- // ========== TURN 2: Tool search results come back ==========
511
- // Simulate tool search discovering tools
512
- const turn2Discoveries = ['deferred_direct_tool', 'deferred_code_tool'];
513
- const hasNewDiscoveries = ctx.markToolsAsDiscovered(turn2Discoveries);
514
- expect(hasNewDiscoveries).toBe(true);
515
- // Get tools for binding - now includes discovered direct tool
516
- const turn2Tools = ctx.getToolsForBinding();
517
- expect(turn2Tools?.length).toBe(2); // 'always_available' + 'deferred_direct_tool'
518
- expect(turn2Tools?.map((t) => t.name)).toContain('always_available');
519
- expect(turn2Tools?.map((t) => t.name)).toContain('deferred_direct_tool');
520
- // Note: 'deferred_code_tool' is NOT in binding (code_execution only)
521
- // Access system runnable - should rebuild due to stale flag
522
- const turn2Runnable = ctx.systemRunnable;
523
- expect(turn2Runnable).not.toBe(turn1Runnable); // Different instance = rebuilt
524
- // Token count should increase (now includes deferred_code_tool in system message)
525
- const turn2Tokens = ctx.instructionTokens;
526
- expect(turn2Tokens).toBeGreaterThan(turn1Tokens);
527
- // ========== TURN 3: Another turn, same discoveries ==========
528
- // Same discoveries (duplicates)
529
- const turn3Discoveries = ['deferred_direct_tool'];
530
- const hasNewDiscoveriesTurn3 = ctx.markToolsAsDiscovered(turn3Discoveries);
531
- expect(hasNewDiscoveriesTurn3).toBe(false); // No NEW discoveries
532
- // Tools should be same as turn 2
533
- const turn3Tools = ctx.getToolsForBinding();
534
- expect(turn3Tools?.length).toBe(2);
535
- // System runnable should be CACHED (same reference)
536
- const turn3Runnable = ctx.systemRunnable;
537
- expect(turn3Runnable).toBe(turn2Runnable); // Same instance = cached
538
- // Token count unchanged
539
- expect(ctx.instructionTokens).toBe(turn2Tokens);
540
- });
541
- it('maintains consistent indexTokenCountMap across turns', () => {
542
- const ctx = createBasicContext({
543
- agentConfig: { instructions: 'Base instructions' },
544
- tokenCounter: mockTokenCounter,
545
- });
546
- // Initial setup (simulating fromConfig flow)
547
- ctx.initializeSystemRunnable();
548
- const initialSystemTokens = ctx.instructionTokens;
549
- // Simulate message token counts from conversation
550
- const messageTokenCounts = { '0': 50, '1': 100, '2': 75 };
551
- ctx.updateTokenMapWithInstructions(messageTokenCounts);
552
- // Verify token map: first message gets instruction tokens added
553
- expect(ctx.indexTokenCountMap['0']).toBe(50 + initialSystemTokens);
554
- expect(ctx.indexTokenCountMap['1']).toBe(100);
555
- expect(ctx.indexTokenCountMap['2']).toBe(75);
556
- // Simulate turn where system message changes
557
- const toolRegistry = new Map([
558
- [
559
- 'new_code_tool',
560
- {
561
- name: 'new_code_tool',
562
- description: 'A newly discovered code-only tool with detailed documentation',
563
- allowed_callers: ['code_execution'],
564
- defer_loading: true,
565
- },
566
- ],
567
- ]);
568
- ctx.toolRegistry = toolRegistry;
569
- // Discover the tool
570
- ctx.markToolsAsDiscovered(['new_code_tool']);
571
- // Access system runnable to trigger rebuild
572
- void ctx.systemRunnable;
573
- // Token count should have increased
574
- const newSystemTokens = ctx.instructionTokens;
575
- expect(newSystemTokens).toBeGreaterThan(initialSystemTokens);
576
- // If we update token map again, it should use NEW instruction tokens
577
- const newMessageTokenCounts = { '0': 60, '1': 110 };
578
- ctx.updateTokenMapWithInstructions(newMessageTokenCounts);
579
- expect(ctx.indexTokenCountMap['0']).toBe(60 + newSystemTokens);
580
- expect(ctx.indexTokenCountMap['1']).toBe(110);
581
- });
582
- it('correctly tracks token delta when system message content changes', () => {
583
- const toolRegistry = new Map([
584
- [
585
- 'tool_a',
586
- {
587
- name: 'tool_a',
588
- description: 'Short description',
589
- allowed_callers: ['code_execution'],
590
- defer_loading: true,
591
- },
592
- ],
593
- [
594
- 'tool_b',
595
- {
596
- name: 'tool_b',
597
- description: 'Another tool that adds more content',
598
- allowed_callers: ['code_execution'],
599
- defer_loading: true,
600
- },
601
- ],
602
- ]);
603
- const ctx = createBasicContext({
604
- agentConfig: {
605
- instructions: 'You are helpful.',
606
- toolRegistry,
607
- },
608
- tokenCounter: mockTokenCounter,
609
- });
610
- ctx.initializeSystemRunnable();
611
- const baseTokens = ctx.instructionTokens;
612
- // Discover tool_a
613
- ctx.markToolsAsDiscovered(['tool_a']);
614
- void ctx.systemRunnable;
615
- const tokensAfterA = ctx.instructionTokens;
616
- expect(tokensAfterA).toBeGreaterThan(baseTokens);
617
- // Discover tool_b - adds more content
618
- ctx.markToolsAsDiscovered(['tool_b']);
619
- void ctx.systemRunnable;
620
- const tokensAfterB = ctx.instructionTokens;
621
- expect(tokensAfterB).toBeGreaterThan(tokensAfterA);
622
- // Both deltas should be positive (each discovery adds tokens)
623
- const deltaBaseToA = tokensAfterA - baseTokens;
624
- const deltaAToB = tokensAfterB - tokensAfterA;
625
- expect(deltaBaseToA).toBeGreaterThan(0);
626
- expect(deltaAToB).toBeGreaterThan(0);
627
- });
628
- it('handles reset between runs correctly', () => {
629
- const toolRegistry = new Map([
630
- [
631
- 'discovered_tool',
632
- {
633
- name: 'discovered_tool',
634
- description: 'Will be discovered',
635
- allowed_callers: ['code_execution'],
636
- defer_loading: true,
637
- },
638
- ],
639
- ]);
640
- const ctx = createBasicContext({
641
- agentConfig: {
642
- instructions: 'Assistant instructions',
643
- toolRegistry,
644
- },
645
- tokenCounter: mockTokenCounter,
646
- });
647
- // ========== RUN 1 ==========
648
- ctx.initializeSystemRunnable();
649
- ctx.markToolsAsDiscovered(['discovered_tool']);
650
- void ctx.systemRunnable;
651
- expect(ctx.discoveredToolNames.has('discovered_tool')).toBe(true);
652
- const run1Tokens = ctx.instructionTokens;
653
- expect(run1Tokens).toBeGreaterThan(0);
654
- // ========== RESET (new run) ==========
655
- ctx.reset();
656
- // Verify state is cleared
657
- expect(ctx.discoveredToolNames.size).toBe(0);
658
- const resetTokens = ctx.instructionTokens;
659
- expect(resetTokens).toBeGreaterThan(0);
660
- expect(resetTokens).toBeLessThan(run1Tokens);
661
- // ========== RUN 2 ==========
662
- // Re-initialize (as fromConfig would do)
663
- ctx.initializeSystemRunnable();
664
- // System runnable should NOT include the previously discovered tool
665
- // (because discoveredToolNames was cleared)
666
- const run2Tokens = ctx.instructionTokens;
667
- expect(run2Tokens).toBe(resetTokens);
668
- // Token count should be lower than run 1 (no discovered tool in system message)
669
- expect(run2Tokens).toBeLessThan(run1Tokens);
670
- // Discover again
671
- ctx.markToolsAsDiscovered(['discovered_tool']);
672
- void ctx.systemRunnable;
673
- // Now should match run 1
674
- expect(ctx.instructionTokens).toBe(run1Tokens);
675
- });
676
- });
677
- });
678
- //# sourceMappingURL=AgentContext.test.js.map