@illuma-ai/agents 1.1.20 → 1.1.22

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 (246) 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 +85 -1
  4. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  5. package/dist/cjs/llm/bedrock/index.cjs +14 -0
  6. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  7. package/dist/cjs/run.cjs +20 -9
  8. package/dist/cjs/run.cjs.map +1 -1
  9. package/dist/esm/graphs/Graph.mjs +12 -1
  10. package/dist/esm/graphs/Graph.mjs.map +1 -1
  11. package/dist/esm/graphs/MultiAgentGraph.mjs +85 -1
  12. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  13. package/dist/esm/llm/bedrock/index.mjs +14 -0
  14. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  15. package/dist/esm/run.mjs +20 -9
  16. package/dist/esm/run.mjs.map +1 -1
  17. package/dist/types/graphs/MultiAgentGraph.d.ts +17 -0
  18. package/package.json +1 -1
  19. package/src/graphs/Graph.ts +12 -1
  20. package/src/graphs/MultiAgentGraph.ts +105 -1
  21. package/src/graphs/__tests__/multi-agent-delegate.test.ts +191 -0
  22. package/src/llm/bedrock/index.ts +17 -0
  23. package/src/run.ts +20 -11
  24. package/src/scripts/test-bedrock-handoff-autonomous.ts +231 -0
  25. package/src/agents/AgentContext.js +0 -782
  26. package/src/agents/AgentContext.test.js +0 -421
  27. package/src/agents/__tests__/AgentContext.test.js +0 -678
  28. package/src/agents/__tests__/resolveStructuredOutputMode.test.js +0 -117
  29. package/src/common/enum.js +0 -192
  30. package/src/common/index.js +0 -3
  31. package/src/events.js +0 -166
  32. package/src/graphs/Graph.js +0 -1857
  33. package/src/graphs/MultiAgentGraph.js +0 -1092
  34. package/src/graphs/__tests__/structured-output.integration.test.js +0 -624
  35. package/src/graphs/__tests__/structured-output.test.js +0 -144
  36. package/src/graphs/contextManagement.e2e.test.js +0 -718
  37. package/src/graphs/contextManagement.test.js +0 -485
  38. package/src/graphs/handoffValidation.test.js +0 -276
  39. package/src/graphs/index.js +0 -3
  40. package/src/index.js +0 -28
  41. package/src/instrumentation.js +0 -21
  42. package/src/llm/anthropic/index.js +0 -319
  43. package/src/llm/anthropic/types.js +0 -46
  44. package/src/llm/anthropic/utils/message_inputs.js +0 -627
  45. package/src/llm/anthropic/utils/message_outputs.js +0 -290
  46. package/src/llm/anthropic/utils/output_parsers.js +0 -89
  47. package/src/llm/anthropic/utils/tools.js +0 -25
  48. package/src/llm/bedrock/__tests__/bedrock-caching.test.js +0 -392
  49. package/src/llm/bedrock/index.js +0 -303
  50. package/src/llm/bedrock/types.js +0 -2
  51. package/src/llm/bedrock/utils/index.js +0 -6
  52. package/src/llm/bedrock/utils/message_inputs.js +0 -463
  53. package/src/llm/bedrock/utils/message_outputs.js +0 -269
  54. package/src/llm/fake.js +0 -92
  55. package/src/llm/google/index.js +0 -215
  56. package/src/llm/google/types.js +0 -12
  57. package/src/llm/google/utils/common.js +0 -670
  58. package/src/llm/google/utils/tools.js +0 -111
  59. package/src/llm/google/utils/zod_to_genai_parameters.js +0 -47
  60. package/src/llm/openai/index.js +0 -1033
  61. package/src/llm/openai/types.js +0 -2
  62. package/src/llm/openai/utils/index.js +0 -756
  63. package/src/llm/openai/utils/isReasoningModel.test.js +0 -79
  64. package/src/llm/openrouter/index.js +0 -261
  65. package/src/llm/openrouter/reasoning.test.js +0 -181
  66. package/src/llm/providers.js +0 -36
  67. package/src/llm/text.js +0 -65
  68. package/src/llm/vertexai/index.js +0 -402
  69. package/src/messages/__tests__/tools.test.js +0 -392
  70. package/src/messages/cache.js +0 -404
  71. package/src/messages/cache.test.js +0 -1167
  72. package/src/messages/content.js +0 -48
  73. package/src/messages/content.test.js +0 -314
  74. package/src/messages/core.js +0 -359
  75. package/src/messages/ensureThinkingBlock.test.js +0 -997
  76. package/src/messages/format.js +0 -973
  77. package/src/messages/formatAgentMessages.test.js +0 -2278
  78. package/src/messages/formatAgentMessages.tools.test.js +0 -362
  79. package/src/messages/formatMessage.test.js +0 -608
  80. package/src/messages/ids.js +0 -18
  81. package/src/messages/index.js +0 -9
  82. package/src/messages/labelContentByAgent.test.js +0 -725
  83. package/src/messages/prune.js +0 -438
  84. package/src/messages/reducer.js +0 -60
  85. package/src/messages/shiftIndexTokenCountMap.test.js +0 -63
  86. package/src/messages/summarize.js +0 -146
  87. package/src/messages/summarize.test.js +0 -332
  88. package/src/messages/tools.js +0 -90
  89. package/src/mockStream.js +0 -81
  90. package/src/prompts/collab.js +0 -7
  91. package/src/prompts/index.js +0 -3
  92. package/src/prompts/taskmanager.js +0 -58
  93. package/src/run.js +0 -427
  94. package/src/schemas/index.js +0 -3
  95. package/src/schemas/schema-preparation.test.js +0 -370
  96. package/src/schemas/validate.js +0 -314
  97. package/src/schemas/validate.test.js +0 -264
  98. package/src/scripts/abort.js +0 -127
  99. package/src/scripts/ant_web_search.js +0 -130
  100. package/src/scripts/ant_web_search_edge_case.js +0 -133
  101. package/src/scripts/ant_web_search_error_edge_case.js +0 -119
  102. package/src/scripts/args.js +0 -41
  103. package/src/scripts/bedrock-cache-debug.js +0 -186
  104. package/src/scripts/bedrock-content-aggregation-test.js +0 -195
  105. package/src/scripts/bedrock-merge-test.js +0 -80
  106. package/src/scripts/bedrock-parallel-tools-test.js +0 -150
  107. package/src/scripts/caching.js +0 -106
  108. package/src/scripts/cli.js +0 -152
  109. package/src/scripts/cli2.js +0 -119
  110. package/src/scripts/cli3.js +0 -163
  111. package/src/scripts/cli4.js +0 -165
  112. package/src/scripts/cli5.js +0 -165
  113. package/src/scripts/code_exec.js +0 -171
  114. package/src/scripts/code_exec_files.js +0 -180
  115. package/src/scripts/code_exec_multi_session.js +0 -185
  116. package/src/scripts/code_exec_ptc.js +0 -265
  117. package/src/scripts/code_exec_session.js +0 -217
  118. package/src/scripts/code_exec_simple.js +0 -120
  119. package/src/scripts/content.js +0 -111
  120. package/src/scripts/empty_input.js +0 -125
  121. package/src/scripts/handoff-test.js +0 -96
  122. package/src/scripts/image.js +0 -138
  123. package/src/scripts/memory.js +0 -83
  124. package/src/scripts/multi-agent-chain.js +0 -271
  125. package/src/scripts/multi-agent-conditional.js +0 -185
  126. package/src/scripts/multi-agent-document-review-chain.js +0 -171
  127. package/src/scripts/multi-agent-hybrid-flow.js +0 -264
  128. package/src/scripts/multi-agent-parallel-start.js +0 -214
  129. package/src/scripts/multi-agent-parallel.js +0 -346
  130. package/src/scripts/multi-agent-sequence.js +0 -184
  131. package/src/scripts/multi-agent-supervisor.js +0 -324
  132. package/src/scripts/multi-agent-test.js +0 -147
  133. package/src/scripts/parallel-asymmetric-tools-test.js +0 -202
  134. package/src/scripts/parallel-full-metadata-test.js +0 -176
  135. package/src/scripts/parallel-tools-test.js +0 -256
  136. package/src/scripts/programmatic_exec.js +0 -277
  137. package/src/scripts/programmatic_exec_agent.js +0 -168
  138. package/src/scripts/search.js +0 -118
  139. package/src/scripts/sequential-full-metadata-test.js +0 -143
  140. package/src/scripts/simple.js +0 -174
  141. package/src/scripts/single-agent-metadata-test.js +0 -152
  142. package/src/scripts/stream.js +0 -113
  143. package/src/scripts/test-custom-prompt-key.js +0 -132
  144. package/src/scripts/test-handoff-input.js +0 -143
  145. package/src/scripts/test-handoff-preamble.js +0 -227
  146. package/src/scripts/test-handoff-steering.js +0 -353
  147. package/src/scripts/test-multi-agent-list-handoff.js +0 -318
  148. package/src/scripts/test-parallel-agent-labeling.js +0 -253
  149. package/src/scripts/test-parallel-handoffs.js +0 -229
  150. package/src/scripts/test-thinking-handoff-bedrock.js +0 -132
  151. package/src/scripts/test-thinking-handoff.js +0 -132
  152. package/src/scripts/test-thinking-to-thinking-handoff-bedrock.js +0 -140
  153. package/src/scripts/test-tool-before-handoff-role-order.js +0 -223
  154. package/src/scripts/test-tools-before-handoff.js +0 -187
  155. package/src/scripts/test_code_api.js +0 -263
  156. package/src/scripts/thinking-bedrock.js +0 -128
  157. package/src/scripts/thinking-vertexai.js +0 -130
  158. package/src/scripts/thinking.js +0 -134
  159. package/src/scripts/tool_search.js +0 -114
  160. package/src/scripts/tools.js +0 -125
  161. package/src/specs/agent-handoffs-bedrock.integration.test.js +0 -280
  162. package/src/specs/agent-handoffs.test.js +0 -924
  163. package/src/specs/anthropic.simple.test.js +0 -287
  164. package/src/specs/azure.simple.test.js +0 -381
  165. package/src/specs/cache.simple.test.js +0 -282
  166. package/src/specs/custom-event-await.test.js +0 -148
  167. package/src/specs/deepseek.simple.test.js +0 -189
  168. package/src/specs/emergency-prune.test.js +0 -308
  169. package/src/specs/moonshot.simple.test.js +0 -237
  170. package/src/specs/observability.integration.test.js +0 -1337
  171. package/src/specs/openai.simple.test.js +0 -233
  172. package/src/specs/openrouter.simple.test.js +0 -202
  173. package/src/specs/prune.test.js +0 -733
  174. package/src/specs/reasoning.test.js +0 -144
  175. package/src/specs/spec.utils.js +0 -4
  176. package/src/specs/thinking-handoff.test.js +0 -486
  177. package/src/specs/thinking-prune.test.js +0 -600
  178. package/src/specs/token-distribution-edge-case.test.js +0 -246
  179. package/src/specs/token-memoization.test.js +0 -32
  180. package/src/specs/tokens.test.js +0 -49
  181. package/src/specs/tool-error.test.js +0 -139
  182. package/src/splitStream.js +0 -204
  183. package/src/splitStream.test.js +0 -504
  184. package/src/stream.js +0 -650
  185. package/src/stream.test.js +0 -225
  186. package/src/test/mockTools.js +0 -340
  187. package/src/tools/BrowserTools.js +0 -245
  188. package/src/tools/Calculator.js +0 -38
  189. package/src/tools/Calculator.test.js +0 -225
  190. package/src/tools/CodeExecutor.js +0 -233
  191. package/src/tools/ProgrammaticToolCalling.js +0 -602
  192. package/src/tools/StreamingToolCallBuffer.js +0 -179
  193. package/src/tools/ToolNode.js +0 -930
  194. package/src/tools/ToolSearch.js +0 -904
  195. package/src/tools/__tests__/BrowserTools.test.js +0 -306
  196. package/src/tools/__tests__/ProgrammaticToolCalling.integration.test.js +0 -276
  197. package/src/tools/__tests__/ProgrammaticToolCalling.test.js +0 -807
  198. package/src/tools/__tests__/StreamingToolCallBuffer.test.js +0 -175
  199. package/src/tools/__tests__/ToolApproval.test.js +0 -675
  200. package/src/tools/__tests__/ToolNode.recovery.test.js +0 -200
  201. package/src/tools/__tests__/ToolNode.session.test.js +0 -319
  202. package/src/tools/__tests__/ToolSearch.integration.test.js +0 -125
  203. package/src/tools/__tests__/ToolSearch.test.js +0 -812
  204. package/src/tools/__tests__/handlers.test.js +0 -799
  205. package/src/tools/__tests__/truncation-recovery.integration.test.js +0 -362
  206. package/src/tools/handlers.js +0 -306
  207. package/src/tools/schema.js +0 -25
  208. package/src/tools/search/anthropic.js +0 -34
  209. package/src/tools/search/content.js +0 -116
  210. package/src/tools/search/content.test.js +0 -133
  211. package/src/tools/search/firecrawl.js +0 -173
  212. package/src/tools/search/format.js +0 -198
  213. package/src/tools/search/highlights.js +0 -241
  214. package/src/tools/search/index.js +0 -3
  215. package/src/tools/search/jina-reranker.test.js +0 -106
  216. package/src/tools/search/rerankers.js +0 -165
  217. package/src/tools/search/schema.js +0 -102
  218. package/src/tools/search/search.js +0 -561
  219. package/src/tools/search/serper-scraper.js +0 -126
  220. package/src/tools/search/test.js +0 -129
  221. package/src/tools/search/tool.js +0 -453
  222. package/src/tools/search/types.js +0 -2
  223. package/src/tools/search/utils.js +0 -59
  224. package/src/types/graph.js +0 -24
  225. package/src/types/graph.test.js +0 -192
  226. package/src/types/index.js +0 -7
  227. package/src/types/llm.js +0 -2
  228. package/src/types/messages.js +0 -2
  229. package/src/types/run.js +0 -2
  230. package/src/types/stream.js +0 -2
  231. package/src/types/tools.js +0 -2
  232. package/src/utils/contextAnalytics.js +0 -79
  233. package/src/utils/contextAnalytics.test.js +0 -166
  234. package/src/utils/events.js +0 -26
  235. package/src/utils/graph.js +0 -11
  236. package/src/utils/handlers.js +0 -65
  237. package/src/utils/index.js +0 -10
  238. package/src/utils/llm.js +0 -21
  239. package/src/utils/llmConfig.js +0 -205
  240. package/src/utils/logging.js +0 -37
  241. package/src/utils/misc.js +0 -51
  242. package/src/utils/run.js +0 -69
  243. package/src/utils/schema.js +0 -21
  244. package/src/utils/title.js +0 -119
  245. package/src/utils/tokens.js +0 -92
  246. 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