@strands-agents/sdk 1.0.0-rc.4 → 1.0.0

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 (265) hide show
  1. package/README.md +8 -15
  2. package/dist/src/__fixtures__/agent-helpers.d.ts +6 -0
  3. package/dist/src/__fixtures__/agent-helpers.d.ts.map +1 -1
  4. package/dist/src/__fixtures__/agent-helpers.js +3 -1
  5. package/dist/src/__fixtures__/agent-helpers.js.map +1 -1
  6. package/dist/src/__fixtures__/mock-plugin.d.ts.map +1 -1
  7. package/dist/src/__fixtures__/mock-plugin.js +3 -1
  8. package/dist/src/__fixtures__/mock-plugin.js.map +1 -1
  9. package/dist/src/__fixtures__/tool-helpers.d.ts +3 -1
  10. package/dist/src/__fixtures__/tool-helpers.d.ts.map +1 -1
  11. package/dist/src/__fixtures__/tool-helpers.js +3 -1
  12. package/dist/src/__fixtures__/tool-helpers.js.map +1 -1
  13. package/dist/src/__tests__/mcp.test.js +274 -1
  14. package/dist/src/__tests__/mcp.test.js.map +1 -1
  15. package/dist/src/a2a/__tests__/events.test.js +2 -0
  16. package/dist/src/a2a/__tests__/events.test.js.map +1 -1
  17. package/dist/src/a2a/__tests__/executor.test.js +16 -5
  18. package/dist/src/a2a/__tests__/executor.test.js.map +1 -1
  19. package/dist/src/a2a/a2a-agent.d.ts +8 -3
  20. package/dist/src/a2a/a2a-agent.d.ts.map +1 -1
  21. package/dist/src/a2a/a2a-agent.js +12 -6
  22. package/dist/src/a2a/a2a-agent.js.map +1 -1
  23. package/dist/src/a2a/executor.d.ts +13 -0
  24. package/dist/src/a2a/executor.d.ts.map +1 -1
  25. package/dist/src/a2a/executor.js +19 -1
  26. package/dist/src/a2a/executor.js.map +1 -1
  27. package/dist/src/agent/__tests__/agent-as-tool.invocation-state.test.d.ts +2 -0
  28. package/dist/src/agent/__tests__/agent-as-tool.invocation-state.test.d.ts.map +1 -0
  29. package/dist/src/agent/__tests__/agent-as-tool.invocation-state.test.js +23 -0
  30. package/dist/src/agent/__tests__/agent-as-tool.invocation-state.test.js.map +1 -0
  31. package/dist/src/agent/__tests__/agent.cancel.test.js +1 -1
  32. package/dist/src/agent/__tests__/agent.cancel.test.js.map +1 -1
  33. package/dist/src/agent/__tests__/agent.concurrent.test.d.ts +2 -0
  34. package/dist/src/agent/__tests__/agent.concurrent.test.d.ts.map +1 -0
  35. package/dist/src/agent/__tests__/agent.concurrent.test.js +488 -0
  36. package/dist/src/agent/__tests__/agent.concurrent.test.js.map +1 -0
  37. package/dist/src/agent/__tests__/agent.hook.test.js +174 -12
  38. package/dist/src/agent/__tests__/agent.hook.test.js.map +1 -1
  39. package/dist/src/agent/__tests__/agent.invocation-state.test.d.ts +2 -0
  40. package/dist/src/agent/__tests__/agent.invocation-state.test.d.ts.map +1 -0
  41. package/dist/src/agent/__tests__/agent.invocation-state.test.js +219 -0
  42. package/dist/src/agent/__tests__/agent.invocation-state.test.js.map +1 -0
  43. package/dist/src/agent/__tests__/agent.stateful-model.test.d.ts +2 -0
  44. package/dist/src/agent/__tests__/agent.stateful-model.test.d.ts.map +1 -0
  45. package/dist/src/agent/__tests__/agent.stateful-model.test.js +169 -0
  46. package/dist/src/agent/__tests__/agent.stateful-model.test.js.map +1 -0
  47. package/dist/src/agent/__tests__/agent.test.js +99 -2
  48. package/dist/src/agent/__tests__/agent.test.js.map +1 -1
  49. package/dist/src/agent/__tests__/agent.tracer.test.node.js +39 -0
  50. package/dist/src/agent/__tests__/agent.tracer.test.node.js.map +1 -1
  51. package/dist/src/agent/__tests__/snapshot.test.js +5 -4
  52. package/dist/src/agent/__tests__/snapshot.test.js.map +1 -1
  53. package/dist/src/agent/agent-as-tool.d.ts.map +1 -1
  54. package/dist/src/agent/agent-as-tool.js +4 -2
  55. package/dist/src/agent/agent-as-tool.js.map +1 -1
  56. package/dist/src/agent/agent.d.ts +75 -1
  57. package/dist/src/agent/agent.d.ts.map +1 -1
  58. package/dist/src/agent/agent.js +323 -83
  59. package/dist/src/agent/agent.js.map +1 -1
  60. package/dist/src/agent/snapshot.d.ts +2 -2
  61. package/dist/src/agent/snapshot.d.ts.map +1 -1
  62. package/dist/src/agent/snapshot.js +8 -2
  63. package/dist/src/agent/snapshot.js.map +1 -1
  64. package/dist/src/conversation-manager/__tests__/conversation-manager.test.js +4 -4
  65. package/dist/src/conversation-manager/__tests__/conversation-manager.test.js.map +1 -1
  66. package/dist/src/conversation-manager/__tests__/null-conversation-manager.test.js +2 -2
  67. package/dist/src/conversation-manager/__tests__/null-conversation-manager.test.js.map +1 -1
  68. package/dist/src/conversation-manager/__tests__/sliding-window-conversation-manager.test.js +8 -3
  69. package/dist/src/conversation-manager/__tests__/sliding-window-conversation-manager.test.js.map +1 -1
  70. package/dist/src/conversation-manager/__tests__/summarizing-conversation-manager.test.js +1 -0
  71. package/dist/src/conversation-manager/__tests__/summarizing-conversation-manager.test.js.map +1 -1
  72. package/dist/src/errors.d.ts +11 -0
  73. package/dist/src/errors.d.ts.map +1 -1
  74. package/dist/src/errors.js +12 -0
  75. package/dist/src/errors.js.map +1 -1
  76. package/dist/src/hooks/__tests__/events.test.js +177 -70
  77. package/dist/src/hooks/__tests__/events.test.js.map +1 -1
  78. package/dist/src/hooks/__tests__/registry.test.js +16 -16
  79. package/dist/src/hooks/__tests__/registry.test.js.map +1 -1
  80. package/dist/src/hooks/events.d.ts +95 -25
  81. package/dist/src/hooks/events.d.ts.map +1 -1
  82. package/dist/src/hooks/events.js +98 -23
  83. package/dist/src/hooks/events.js.map +1 -1
  84. package/dist/src/index.d.ts +6 -5
  85. package/dist/src/index.d.ts.map +1 -1
  86. package/dist/src/index.js.map +1 -1
  87. package/dist/src/logging/__tests__/warn-once.test.d.ts +2 -0
  88. package/dist/src/logging/__tests__/warn-once.test.d.ts.map +1 -0
  89. package/dist/src/logging/__tests__/warn-once.test.js +30 -0
  90. package/dist/src/logging/__tests__/warn-once.test.js.map +1 -0
  91. package/dist/src/logging/warn-once.d.ts +13 -0
  92. package/dist/src/logging/warn-once.d.ts.map +1 -0
  93. package/dist/src/logging/warn-once.js +18 -0
  94. package/dist/src/logging/warn-once.js.map +1 -0
  95. package/dist/src/mcp.d.ts +20 -1
  96. package/dist/src/mcp.d.ts.map +1 -1
  97. package/dist/src/mcp.js +10 -1
  98. package/dist/src/mcp.js.map +1 -1
  99. package/dist/src/mime.d.ts +2 -1
  100. package/dist/src/mime.d.ts.map +1 -1
  101. package/dist/src/mime.js +1 -0
  102. package/dist/src/mime.js.map +1 -1
  103. package/dist/src/models/__tests__/anthropic.test.js +99 -1
  104. package/dist/src/models/__tests__/anthropic.test.js.map +1 -1
  105. package/dist/src/models/__tests__/bedrock.test.js +123 -2
  106. package/dist/src/models/__tests__/bedrock.test.js.map +1 -1
  107. package/dist/src/models/__tests__/google.test.js +88 -0
  108. package/dist/src/models/__tests__/google.test.js.map +1 -1
  109. package/dist/src/models/__tests__/model.test.js +149 -1
  110. package/dist/src/models/__tests__/model.test.js.map +1 -1
  111. package/dist/src/models/anthropic.d.ts +18 -1
  112. package/dist/src/models/anthropic.d.ts.map +1 -1
  113. package/dist/src/models/anthropic.js +40 -6
  114. package/dist/src/models/anthropic.js.map +1 -1
  115. package/dist/src/models/bedrock.d.ts +12 -1
  116. package/dist/src/models/bedrock.d.ts.map +1 -1
  117. package/dist/src/models/bedrock.js +45 -11
  118. package/dist/src/models/bedrock.js.map +1 -1
  119. package/dist/src/models/defaults.d.ts +37 -0
  120. package/dist/src/models/defaults.d.ts.map +1 -0
  121. package/dist/src/models/defaults.js +41 -0
  122. package/dist/src/models/defaults.js.map +1 -0
  123. package/dist/src/models/google/model.d.ts +14 -1
  124. package/dist/src/models/google/model.d.ts.map +1 -1
  125. package/dist/src/models/google/model.js +50 -6
  126. package/dist/src/models/google/model.js.map +1 -1
  127. package/dist/src/models/model.d.ts +56 -0
  128. package/dist/src/models/model.d.ts.map +1 -1
  129. package/dist/src/models/model.js +120 -0
  130. package/dist/src/models/model.js.map +1 -1
  131. package/dist/src/models/openai/__tests__/chat.test.d.ts +2 -0
  132. package/dist/src/models/openai/__tests__/chat.test.d.ts.map +1 -0
  133. package/dist/src/models/{__tests__/openai.test.js → openai/__tests__/chat.test.js} +84 -7
  134. package/dist/src/models/openai/__tests__/chat.test.js.map +1 -0
  135. package/dist/src/models/openai/__tests__/responses.test.d.ts +2 -0
  136. package/dist/src/models/openai/__tests__/responses.test.d.ts.map +1 -0
  137. package/dist/src/models/openai/__tests__/responses.test.js +668 -0
  138. package/dist/src/models/openai/__tests__/responses.test.js.map +1 -0
  139. package/dist/src/models/openai/chat-adapter.d.ts +33 -0
  140. package/dist/src/models/openai/chat-adapter.d.ts.map +1 -0
  141. package/dist/src/models/openai/chat-adapter.js +383 -0
  142. package/dist/src/models/openai/chat-adapter.js.map +1 -0
  143. package/dist/src/models/openai/errors.d.ts +16 -0
  144. package/dist/src/models/openai/errors.d.ts.map +1 -0
  145. package/dist/src/models/openai/errors.js +40 -0
  146. package/dist/src/models/openai/errors.js.map +1 -0
  147. package/dist/src/models/openai/formatting.d.ts +18 -0
  148. package/dist/src/models/openai/formatting.d.ts.map +1 -0
  149. package/dist/src/models/openai/formatting.js +38 -0
  150. package/dist/src/models/openai/formatting.js.map +1 -0
  151. package/dist/src/models/openai/index.d.ts +19 -0
  152. package/dist/src/models/openai/index.d.ts.map +1 -0
  153. package/dist/src/models/openai/index.js +18 -0
  154. package/dist/src/models/openai/index.js.map +1 -0
  155. package/dist/src/models/openai/model.d.ts +77 -0
  156. package/dist/src/models/openai/model.d.ts.map +1 -0
  157. package/dist/src/models/openai/model.js +211 -0
  158. package/dist/src/models/openai/model.js.map +1 -0
  159. package/dist/src/models/openai/responses-adapter.d.ts +78 -0
  160. package/dist/src/models/openai/responses-adapter.d.ts.map +1 -0
  161. package/dist/src/models/openai/responses-adapter.js +467 -0
  162. package/dist/src/models/openai/responses-adapter.js.map +1 -0
  163. package/dist/src/models/openai/types.d.ts +131 -0
  164. package/dist/src/models/openai/types.d.ts.map +1 -0
  165. package/dist/src/models/openai/types.js +5 -0
  166. package/dist/src/models/openai/types.js.map +1 -0
  167. package/dist/src/multiagent/__tests__/events.test.js +122 -28
  168. package/dist/src/multiagent/__tests__/events.test.js.map +1 -1
  169. package/dist/src/multiagent/__tests__/graph.invocation-state.test.d.ts +2 -0
  170. package/dist/src/multiagent/__tests__/graph.invocation-state.test.d.ts.map +1 -0
  171. package/dist/src/multiagent/__tests__/graph.invocation-state.test.js +95 -0
  172. package/dist/src/multiagent/__tests__/graph.invocation-state.test.js.map +1 -0
  173. package/dist/src/multiagent/__tests__/nodes.test.js +5 -2
  174. package/dist/src/multiagent/__tests__/nodes.test.js.map +1 -1
  175. package/dist/src/multiagent/__tests__/swarm.invocation-state.test.d.ts +2 -0
  176. package/dist/src/multiagent/__tests__/swarm.invocation-state.test.d.ts.map +1 -0
  177. package/dist/src/multiagent/__tests__/swarm.invocation-state.test.js +56 -0
  178. package/dist/src/multiagent/__tests__/swarm.invocation-state.test.js.map +1 -0
  179. package/dist/src/multiagent/events.d.ts +19 -1
  180. package/dist/src/multiagent/events.d.ts.map +1 -1
  181. package/dist/src/multiagent/events.js +18 -0
  182. package/dist/src/multiagent/events.js.map +1 -1
  183. package/dist/src/multiagent/graph.d.ts +5 -3
  184. package/dist/src/multiagent/graph.d.ts.map +1 -1
  185. package/dist/src/multiagent/graph.js +22 -15
  186. package/dist/src/multiagent/graph.js.map +1 -1
  187. package/dist/src/multiagent/index.d.ts +1 -1
  188. package/dist/src/multiagent/index.d.ts.map +1 -1
  189. package/dist/src/multiagent/multiagent.d.ts +16 -3
  190. package/dist/src/multiagent/multiagent.d.ts.map +1 -1
  191. package/dist/src/multiagent/nodes.d.ts +10 -3
  192. package/dist/src/multiagent/nodes.d.ts.map +1 -1
  193. package/dist/src/multiagent/nodes.js +28 -6
  194. package/dist/src/multiagent/nodes.js.map +1 -1
  195. package/dist/src/multiagent/swarm.d.ts +5 -3
  196. package/dist/src/multiagent/swarm.d.ts.map +1 -1
  197. package/dist/src/multiagent/swarm.js +22 -16
  198. package/dist/src/multiagent/swarm.js.map +1 -1
  199. package/dist/src/plugins/__tests__/registry.test.js +1 -1
  200. package/dist/src/plugins/__tests__/registry.test.js.map +1 -1
  201. package/dist/src/plugins/model-plugin.d.ts +20 -0
  202. package/dist/src/plugins/model-plugin.d.ts.map +1 -0
  203. package/dist/src/plugins/model-plugin.js +29 -0
  204. package/dist/src/plugins/model-plugin.js.map +1 -0
  205. package/dist/src/session/__tests__/session-manager.test.js +13 -11
  206. package/dist/src/session/__tests__/session-manager.test.js.map +1 -1
  207. package/dist/src/session/session-manager.d.ts.map +1 -1
  208. package/dist/src/session/session-manager.js +9 -0
  209. package/dist/src/session/session-manager.js.map +1 -1
  210. package/dist/src/telemetry/__tests__/config.test.js +6 -6
  211. package/dist/src/telemetry/__tests__/config.test.js.map +1 -1
  212. package/dist/src/telemetry/__tests__/config.test.node.js +16 -11
  213. package/dist/src/telemetry/__tests__/config.test.node.js.map +1 -1
  214. package/dist/src/telemetry/__tests__/meter.test.js +23 -0
  215. package/dist/src/telemetry/__tests__/meter.test.js.map +1 -1
  216. package/dist/src/telemetry/config.d.ts +9 -3
  217. package/dist/src/telemetry/config.d.ts.map +1 -1
  218. package/dist/src/telemetry/config.js +44 -69
  219. package/dist/src/telemetry/config.js.map +1 -1
  220. package/dist/src/telemetry/meter.d.ts +15 -0
  221. package/dist/src/telemetry/meter.d.ts.map +1 -1
  222. package/dist/src/telemetry/meter.js +14 -0
  223. package/dist/src/telemetry/meter.js.map +1 -1
  224. package/dist/src/tools/mcp-tool.d.ts +24 -3
  225. package/dist/src/tools/mcp-tool.d.ts.map +1 -1
  226. package/dist/src/tools/mcp-tool.js +105 -14
  227. package/dist/src/tools/mcp-tool.js.map +1 -1
  228. package/dist/src/tools/tool.d.ts +11 -1
  229. package/dist/src/tools/tool.d.ts.map +1 -1
  230. package/dist/src/tools/tool.js.map +1 -1
  231. package/dist/src/tsconfig.tsbuildinfo +1 -1
  232. package/dist/src/types/__tests__/agent.test.js +48 -0
  233. package/dist/src/types/__tests__/agent.test.js.map +1 -1
  234. package/dist/src/types/agent.d.ts +55 -6
  235. package/dist/src/types/agent.d.ts.map +1 -1
  236. package/dist/src/types/agent.js +22 -6
  237. package/dist/src/types/agent.js.map +1 -1
  238. package/dist/src/types/elicitation.d.ts +15 -0
  239. package/dist/src/types/elicitation.d.ts.map +1 -0
  240. package/dist/src/types/elicitation.js +2 -0
  241. package/dist/src/types/elicitation.js.map +1 -0
  242. package/dist/src/vended-plugins/skills/__tests__/agent-skills.test.node.js +37 -33
  243. package/dist/src/vended-plugins/skills/__tests__/agent-skills.test.node.js.map +1 -1
  244. package/dist/src/vended-plugins/skills/agent-skills.d.ts +8 -8
  245. package/dist/src/vended-plugins/skills/agent-skills.d.ts.map +1 -1
  246. package/dist/src/vended-plugins/skills/agent-skills.js +5 -5
  247. package/dist/src/vended-plugins/skills/agent-skills.js.map +1 -1
  248. package/dist/src/vended-plugins/skills/index.d.ts +5 -5
  249. package/dist/src/vended-plugins/skills/index.d.ts.map +1 -1
  250. package/dist/src/vended-plugins/skills/index.js +4 -4
  251. package/dist/src/vended-plugins/skills/index.js.map +1 -1
  252. package/dist/src/vended-tools/bash/__tests__/bash.test.node.js +1 -0
  253. package/dist/src/vended-tools/bash/__tests__/bash.test.node.js.map +1 -1
  254. package/dist/src/vended-tools/file-editor/__tests__/file-editor.test.node.js +1 -0
  255. package/dist/src/vended-tools/file-editor/__tests__/file-editor.test.node.js.map +1 -1
  256. package/dist/src/vended-tools/notebook/__tests__/notebook.test.js +1 -0
  257. package/dist/src/vended-tools/notebook/__tests__/notebook.test.js.map +1 -1
  258. package/package.json +28 -26
  259. package/dist/src/models/__tests__/openai.test.d.ts +0 -2
  260. package/dist/src/models/__tests__/openai.test.d.ts.map +0 -1
  261. package/dist/src/models/__tests__/openai.test.js.map +0 -1
  262. package/dist/src/models/openai.d.ts +0 -312
  263. package/dist/src/models/openai.d.ts.map +0 -1
  264. package/dist/src/models/openai.js +0 -789
  265. package/dist/src/models/openai.js.map +0 -1
@@ -0,0 +1,219 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { Agent } from '../agent.js';
3
+ import { AfterInvocationEvent, AfterModelCallEvent, AfterToolCallEvent, BeforeInvocationEvent, BeforeModelCallEvent, MessageAddedEvent, } from '../../hooks/events.js';
4
+ import { MockMessageModel } from '../../__fixtures__/mock-message-model.js';
5
+ import { createMockTool } from '../../__fixtures__/tool-helpers.js';
6
+ import { ToolResultBlock, TextBlock } from '../../types/messages.js';
7
+ describe('invocationState', () => {
8
+ describe('round-trip', () => {
9
+ it('returns an empty object on AgentResult when no invocationState is passed', async () => {
10
+ const model = new MockMessageModel().addTurn({ type: 'textBlock', text: 'Hello' });
11
+ const agent = new Agent({ model });
12
+ const result = await agent.invoke('Hi');
13
+ expect(result.invocationState).toEqual({});
14
+ });
15
+ it('returns the passed invocationState on AgentResult', async () => {
16
+ const model = new MockMessageModel().addTurn({ type: 'textBlock', text: 'Hello' });
17
+ const agent = new Agent({ model });
18
+ const result = await agent.invoke('Hi', { invocationState: { userId: 'u-1', traceId: 't-1' } });
19
+ expect(result.invocationState).toEqual({ userId: 'u-1', traceId: 't-1' });
20
+ });
21
+ it('preserves reference identity: caller keeps the same object they passed in', async () => {
22
+ const model = new MockMessageModel().addTurn({ type: 'textBlock', text: 'Hello' });
23
+ const agent = new Agent({ model });
24
+ const state = { userId: 'u-1' };
25
+ const result = await agent.invoke('Hi', { invocationState: state });
26
+ expect(result.invocationState).toBe(state);
27
+ });
28
+ });
29
+ describe('hook mutation', () => {
30
+ it('propagates mutations from BeforeModelCallEvent to AfterModelCallEvent and AgentResult', async () => {
31
+ const model = new MockMessageModel().addTurn({ type: 'textBlock', text: 'Hello' });
32
+ const agent = new Agent({ model });
33
+ let seenInAfter;
34
+ agent.addHook(BeforeModelCallEvent, (event) => {
35
+ event.invocationState.counter = event.invocationState.counter ?? 0;
36
+ event.invocationState.counter = event.invocationState.counter + 1;
37
+ });
38
+ agent.addHook(AfterModelCallEvent, (event) => {
39
+ seenInAfter = event.invocationState;
40
+ });
41
+ const result = await agent.invoke('Hi');
42
+ expect(seenInAfter).toEqual({ counter: 1 });
43
+ expect(result.invocationState).toEqual({ counter: 1 });
44
+ });
45
+ it('shares the same invocationState object across all lifecycle events in one invocation', async () => {
46
+ const model = new MockMessageModel().addTurn({ type: 'textBlock', text: 'Hello' });
47
+ const agent = new Agent({ model });
48
+ const seen = [];
49
+ const collect = (event) => {
50
+ seen.push(event.invocationState);
51
+ };
52
+ agent.addHook(BeforeInvocationEvent, collect);
53
+ agent.addHook(BeforeModelCallEvent, collect);
54
+ agent.addHook(AfterModelCallEvent, collect);
55
+ agent.addHook(MessageAddedEvent, collect);
56
+ agent.addHook(AfterInvocationEvent, collect);
57
+ const result = await agent.invoke('Hi');
58
+ // Every hook, plus the result, sees the same reference.
59
+ expect(seen.length).toBeGreaterThan(0);
60
+ for (const observed of seen) {
61
+ expect(observed).toBe(result.invocationState);
62
+ }
63
+ });
64
+ });
65
+ describe('multi-cycle persistence', () => {
66
+ it('persists mutations across recursive agent loop cycles (tool-use scenario)', async () => {
67
+ const tool = createMockTool('ping', () => new ToolResultBlock({
68
+ toolUseId: 'tool-1',
69
+ status: 'success',
70
+ content: [new TextBlock('pong')],
71
+ }));
72
+ const model = new MockMessageModel()
73
+ .addTurn([{ type: 'toolUseBlock', name: 'ping', toolUseId: 'tool-1', input: {} }])
74
+ .addTurn({ type: 'textBlock', text: 'Done' });
75
+ const agent = new Agent({ model, tools: [tool] });
76
+ // Write in AfterToolCallEvent during cycle 1; read in BeforeModelCallEvent during cycle 2.
77
+ let cycle2State;
78
+ let modelCalls = 0;
79
+ agent.addHook(AfterToolCallEvent, (event) => {
80
+ event.invocationState.toolCompleted = true;
81
+ });
82
+ agent.addHook(BeforeModelCallEvent, (event) => {
83
+ modelCalls++;
84
+ if (modelCalls === 2) {
85
+ cycle2State = event.invocationState;
86
+ }
87
+ });
88
+ const result = await agent.invoke('Run ping');
89
+ expect(modelCalls).toBe(2);
90
+ expect(cycle2State).toEqual({ toolCompleted: true });
91
+ expect(result.invocationState).toEqual({ toolCompleted: true });
92
+ });
93
+ });
94
+ describe('tool access', () => {
95
+ it('passes invocationState to tools via ToolContext and surfaces mutations on the result', async () => {
96
+ const tool = createMockTool('writer', () => {
97
+ throw new Error('unused');
98
+ });
99
+ // Override stream to read/write invocationState.
100
+ // eslint-disable-next-line require-yield
101
+ tool.stream = async function* (context) {
102
+ const prev = context.invocationState.callCount ?? 0;
103
+ context.invocationState.callCount = prev + 1;
104
+ context.invocationState.lastToolSeenUserId = context.invocationState.userId;
105
+ return new ToolResultBlock({
106
+ toolUseId: context.toolUse.toolUseId,
107
+ status: 'success',
108
+ content: [new TextBlock('ok')],
109
+ });
110
+ };
111
+ const model = new MockMessageModel()
112
+ .addTurn([{ type: 'toolUseBlock', name: 'writer', toolUseId: 'tu-1', input: {} }])
113
+ .addTurn({ type: 'textBlock', text: 'Done' });
114
+ const agent = new Agent({ model, tools: [tool] });
115
+ const result = await agent.invoke('Run writer', { invocationState: { userId: 'u-42' } });
116
+ expect(result.invocationState).toEqual({
117
+ userId: 'u-42',
118
+ callCount: 1,
119
+ lastToolSeenUserId: 'u-42',
120
+ });
121
+ });
122
+ });
123
+ describe('isolation from appState', () => {
124
+ it('does not touch agent.appState when invocationState is mutated', async () => {
125
+ const model = new MockMessageModel().addTurn({ type: 'textBlock', text: 'Hello' });
126
+ const agent = new Agent({ model, appState: { persistent: 'yes' } });
127
+ agent.addHook(BeforeModelCallEvent, (event) => {
128
+ event.invocationState.ephemeral = 'only-this-run';
129
+ });
130
+ const result = await agent.invoke('Hi', { invocationState: { requestId: 'r-1' } });
131
+ expect(result.invocationState).toEqual({ requestId: 'r-1', ephemeral: 'only-this-run' });
132
+ expect(agent.appState.get('persistent')).toBe('yes');
133
+ expect(agent.appState.get('ephemeral')).toBeUndefined();
134
+ expect(agent.appState.get('requestId')).toBeUndefined();
135
+ });
136
+ });
137
+ describe('across invocations', () => {
138
+ it('does not leak state between invocations on the same agent (default bag)', async () => {
139
+ const model = new MockMessageModel()
140
+ .addTurn({ type: 'textBlock', text: 'A' })
141
+ .addTurn({ type: 'textBlock', text: 'B' });
142
+ const agent = new Agent({ model });
143
+ agent.addHook(BeforeModelCallEvent, (event) => {
144
+ event.invocationState.seen = true;
145
+ });
146
+ const first = await agent.invoke('1');
147
+ const second = await agent.invoke('2');
148
+ expect(first.invocationState).toEqual({ seen: true });
149
+ expect(second.invocationState).toEqual({ seen: true });
150
+ expect(first.invocationState).not.toBe(second.invocationState);
151
+ });
152
+ });
153
+ describe('retry paths', () => {
154
+ it('preserves same invocationState reference across AfterModelCallEvent retry', async () => {
155
+ const model = new MockMessageModel()
156
+ .addTurn(new Error('transient failure'))
157
+ .addTurn({ type: 'textBlock', text: 'Success after retry' });
158
+ const agent = new Agent({ model, printer: false });
159
+ let retried = false;
160
+ const seen = [];
161
+ agent.addHook(BeforeModelCallEvent, (event) => {
162
+ seen.push(event.invocationState);
163
+ event.invocationState.modelCalls = event.invocationState.modelCalls ?? 0;
164
+ event.invocationState.modelCalls = event.invocationState.modelCalls + 1;
165
+ });
166
+ agent.addHook(AfterModelCallEvent, (event) => {
167
+ seen.push(event.invocationState);
168
+ if (!retried && event.error) {
169
+ retried = true;
170
+ event.retry = true;
171
+ }
172
+ });
173
+ const result = await agent.invoke('Test', { invocationState: { userId: 'u-1' } });
174
+ // Retry path was exercised: two Before + two After observations.
175
+ expect(seen.length).toBe(4);
176
+ // Every observation is the same object the caller passed in.
177
+ for (const observed of seen) {
178
+ expect(observed).toBe(result.invocationState);
179
+ }
180
+ // Mutations from the first attempt survive into the retry.
181
+ expect(result.invocationState).toEqual({ userId: 'u-1', modelCalls: 2 });
182
+ });
183
+ it('preserves same invocationState reference across AfterToolCallEvent retry', async () => {
184
+ let toolCalls = 0;
185
+ const tool = createMockTool('flaky', () => {
186
+ toolCalls++;
187
+ return new ToolResultBlock({
188
+ toolUseId: 'tu-1',
189
+ status: toolCalls === 1 ? 'error' : 'success',
190
+ content: [new TextBlock(toolCalls === 1 ? 'fail' : 'ok')],
191
+ });
192
+ });
193
+ const model = new MockMessageModel()
194
+ .addTurn([{ type: 'toolUseBlock', name: 'flaky', toolUseId: 'tu-1', input: {} }])
195
+ .addTurn({ type: 'textBlock', text: 'Done' });
196
+ const agent = new Agent({ model, tools: [tool], printer: false });
197
+ let retried = false;
198
+ const seen = [];
199
+ agent.addHook(AfterToolCallEvent, (event) => {
200
+ seen.push(event.invocationState);
201
+ event.invocationState.toolAttempts = event.invocationState.toolAttempts ?? 0;
202
+ event.invocationState.toolAttempts = event.invocationState.toolAttempts + 1;
203
+ if (!retried && event.result.status === 'error') {
204
+ retried = true;
205
+ event.retry = true;
206
+ }
207
+ });
208
+ const result = await agent.invoke('Run flaky', { invocationState: { requestId: 'r-1' } });
209
+ // Retry fired twice: failed attempt + successful attempt.
210
+ expect(toolCalls).toBe(2);
211
+ expect(seen.length).toBe(2);
212
+ for (const observed of seen) {
213
+ expect(observed).toBe(result.invocationState);
214
+ }
215
+ expect(result.invocationState).toEqual({ requestId: 'r-1', toolAttempts: 2 });
216
+ });
217
+ });
218
+ });
219
+ //# sourceMappingURL=agent.invocation-state.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.invocation-state.test.js","sourceRoot":"","sources":["../../../../src/agent/__tests__/agent.invocation-state.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=agent.stateful-model.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.stateful-model.test.d.ts","sourceRoot":"","sources":["../../../../src/agent/__tests__/agent.stateful-model.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,169 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { Agent } from '../agent.js';
3
+ import { MockMessageModel } from '../../__fixtures__/mock-message-model.js';
4
+ import { MockSnapshotStorage } from '../../__fixtures__/mock-storage-provider.js';
5
+ import { SlidingWindowConversationManager } from '../../conversation-manager/sliding-window-conversation-manager.js';
6
+ import { NullConversationManager } from '../../conversation-manager/null-conversation-manager.js';
7
+ import { SessionManager } from '../../session/session-manager.js';
8
+ import { SNAPSHOT_SCHEMA_VERSION } from '../../types/snapshot.js';
9
+ import { Message } from '../../types/messages.js';
10
+ /**
11
+ * Mock model that advertises itself as stateful and records the modelState
12
+ * object it receives, so tests can verify the agent's modelState flows through.
13
+ */
14
+ class StatefulMockModel extends MockMessageModel {
15
+ receivedOptions = [];
16
+ _responseIds;
17
+ constructor(responseIds = ['resp_1', 'resp_2', 'resp_3']) {
18
+ super();
19
+ this._responseIds = responseIds;
20
+ }
21
+ get stateful() {
22
+ return true;
23
+ }
24
+ async *stream(messages, options) {
25
+ this.receivedOptions.push(options ?? {});
26
+ // Simulate that the provider captured a fresh response id on the wire.
27
+ if (options?.modelState) {
28
+ const next = this._responseIds[this.receivedOptions.length - 1];
29
+ if (next !== undefined) {
30
+ options.modelState.set('responseId', next);
31
+ }
32
+ }
33
+ yield* super.stream(messages, options);
34
+ }
35
+ }
36
+ describe('Agent with stateful model', () => {
37
+ describe('constructor', () => {
38
+ it('throws when a conversationManager is supplied alongside a stateful model', () => {
39
+ const model = new StatefulMockModel();
40
+ expect(() => new Agent({ model, conversationManager: new SlidingWindowConversationManager({ windowSize: 5 }) })).toThrow(/stateful model/);
41
+ });
42
+ it('assigns NullConversationManager when the model is stateful', () => {
43
+ const model = new StatefulMockModel();
44
+ const agent = new Agent({ model, printer: false });
45
+ // Private field; access through bracket notation to avoid making it public.
46
+ expect(agent._conversationManager).toBeInstanceOf(NullConversationManager);
47
+ });
48
+ it('initializes modelState as an empty store', () => {
49
+ const model = new StatefulMockModel();
50
+ const agent = new Agent({ model, printer: false });
51
+ expect(agent.modelState.getAll()).toEqual({});
52
+ });
53
+ it('hydrates modelState from AgentConfig.modelState', () => {
54
+ const model = new StatefulMockModel();
55
+ const agent = new Agent({ model, printer: false, modelState: { responseId: 'resp_restored' } });
56
+ expect(agent.modelState.getAll()).toEqual({ responseId: 'resp_restored' });
57
+ });
58
+ });
59
+ describe('invocation', () => {
60
+ it('passes agent.modelState to the model via streamOptions.modelState', async () => {
61
+ const model = new StatefulMockModel(['resp_first']).addTurn({ type: 'textBlock', text: 'Hi' });
62
+ const agent = new Agent({ model, printer: false });
63
+ await agent.invoke('Hello');
64
+ expect(model.receivedOptions[0]?.modelState).toBe(agent.modelState);
65
+ expect(agent.modelState.getAll()).toEqual({ responseId: 'resp_first' });
66
+ });
67
+ it('clears messages after invocation since the server holds history', async () => {
68
+ const model = new StatefulMockModel().addTurn({ type: 'textBlock', text: 'Hi there' });
69
+ const agent = new Agent({ model, printer: false });
70
+ await agent.invoke('First turn');
71
+ expect(agent.messages).toEqual([]);
72
+ });
73
+ it('clears messages before SessionManager snapshots on AfterInvocationEvent', async () => {
74
+ // Guards the ordering of ModelPlugin vs SessionManager hooks on
75
+ // AfterInvocationEvent: ModelPlugin must clear messages *before*
76
+ // SessionManager persists the snapshot, otherwise the stored snapshot
77
+ // would duplicate history that the server already owns.
78
+ const storage = new MockSnapshotStorage();
79
+ const sessionManager = new SessionManager({
80
+ sessionId: 'test-session',
81
+ storage: { snapshot: storage },
82
+ });
83
+ const model = new StatefulMockModel().addTurn({ type: 'textBlock', text: 'reply' });
84
+ const agent = new Agent({ id: 'agent-1', model, sessionManager, printer: false });
85
+ await agent.invoke('hi');
86
+ const snapshot = await storage.loadSnapshot({
87
+ location: { sessionId: 'test-session', scope: 'agent', scopeId: 'agent-1' },
88
+ });
89
+ expect(snapshot).not.toBeNull();
90
+ expect(snapshot.data.messages).toEqual([]);
91
+ });
92
+ it('preserves modelState across invocations so previous_response_id chains', async () => {
93
+ const model = new StatefulMockModel(['resp_1', 'resp_2'])
94
+ .addTurn({ type: 'textBlock', text: 'one' })
95
+ .addTurn({ type: 'textBlock', text: 'two' });
96
+ const agent = new Agent({ model, printer: false });
97
+ await agent.invoke('turn 1');
98
+ expect(agent.modelState.getAll()).toEqual({ responseId: 'resp_1' });
99
+ await agent.invoke('turn 2');
100
+ expect(agent.modelState.getAll()).toEqual({ responseId: 'resp_2' });
101
+ // Both turns should have seen the state at invocation time.
102
+ expect(model.receivedOptions).toHaveLength(2);
103
+ });
104
+ });
105
+ describe('stateless model (default)', () => {
106
+ it('does not clear messages after invocation', async () => {
107
+ const model = new MockMessageModel().addTurn({ type: 'textBlock', text: 'Hello' });
108
+ const agent = new Agent({ model, printer: false });
109
+ await agent.invoke('Hi');
110
+ // user message + assistant reply
111
+ expect(agent.messages.length).toBe(2);
112
+ });
113
+ it('uses the caller-provided conversationManager', () => {
114
+ const model = new MockMessageModel();
115
+ const convo = new SlidingWindowConversationManager({ windowSize: 7 });
116
+ const agent = new Agent({ model, conversationManager: convo });
117
+ expect(agent._conversationManager).toBe(convo);
118
+ });
119
+ });
120
+ describe('SessionManager restore guard', () => {
121
+ // Pre-seeds a session snapshot with messages, then verifies that SessionManager
122
+ // discards those messages on restore when the model is stateful.
123
+ async function setupStorageWithMessages(agentId, sessionId) {
124
+ const storage = new MockSnapshotStorage();
125
+ await storage.saveSnapshot({
126
+ location: { sessionId, scope: 'agent', scopeId: agentId },
127
+ snapshotId: 'latest',
128
+ isLatest: true,
129
+ snapshot: {
130
+ scope: 'agent',
131
+ schemaVersion: SNAPSHOT_SCHEMA_VERSION,
132
+ createdAt: new Date().toISOString(),
133
+ data: {
134
+ messages: [{ role: 'user', content: [{ text: 'old turn' }] }],
135
+ state: {},
136
+ systemPrompt: null,
137
+ modelState: {},
138
+ },
139
+ appData: {},
140
+ },
141
+ });
142
+ return storage;
143
+ }
144
+ it('discards restored messages when the model is stateful', async () => {
145
+ const storage = await setupStorageWithMessages('agent-1', 'session-stateful');
146
+ const sessionManager = new SessionManager({
147
+ sessionId: 'session-stateful',
148
+ storage: { snapshot: storage },
149
+ });
150
+ const model = new StatefulMockModel();
151
+ const agent = new Agent({ id: 'agent-1', model, sessionManager, printer: false });
152
+ await agent.initialize();
153
+ expect(agent.messages).toEqual([]);
154
+ });
155
+ it('restores messages when the model is stateless', async () => {
156
+ const storage = await setupStorageWithMessages('agent-2', 'session-stateless');
157
+ const sessionManager = new SessionManager({
158
+ sessionId: 'session-stateless',
159
+ storage: { snapshot: storage },
160
+ });
161
+ const model = new MockMessageModel();
162
+ const agent = new Agent({ id: 'agent-2', model, sessionManager, printer: false });
163
+ await agent.initialize();
164
+ expect(agent.messages).toHaveLength(1);
165
+ expect(agent.messages[0].role).toBe('user');
166
+ });
167
+ });
168
+ });
169
+ //# sourceMappingURL=agent.stateful-model.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.stateful-model.test.js","sourceRoot":"","sources":["../../../../src/agent/__tests__/agent.stateful-model.test.ts"],"names":[],"mappings":"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"}
@@ -7,7 +7,7 @@ import { createMockTool, createRandomTool } from '../../__fixtures__/tool-helper
7
7
  import { ConcurrentInvocationError } from '../../errors.js';
8
8
  import { MaxTokensError, TextBlock, CachePointBlock, Message, ToolUseBlock, ToolResultBlock, ReasoningBlock, GuardContentBlock, ImageBlock, VideoBlock, DocumentBlock, } from '../../index.js';
9
9
  import { AgentPrinter } from '../printer.js';
10
- import { AfterInvocationEvent, AfterToolCallEvent, AfterToolsEvent, BeforeInvocationEvent, BeforeToolsEvent, } from '../../hooks/events.js';
10
+ import { AfterInvocationEvent, AfterToolCallEvent, AfterToolsEvent, BeforeInvocationEvent, BeforeModelCallEvent, BeforeToolsEvent, } from '../../hooks/events.js';
11
11
  import { BedrockModel } from '../../models/bedrock.js';
12
12
  import { StructuredOutputError } from '../../errors.js';
13
13
  import { expectLoopMetrics } from '../../__fixtures__/metrics-helpers.js';
@@ -36,7 +36,7 @@ describe('Agent', () => {
36
36
  const { items } = await collectGenerator(agent.stream('Test prompt'));
37
37
  expect(items.length).toBeGreaterThan(0);
38
38
  const firstItem = items[0];
39
- expect(firstItem).toEqual(new BeforeInvocationEvent({ agent: agent }));
39
+ expect(firstItem).toEqual(new BeforeInvocationEvent({ agent: agent, invocationState: {} }));
40
40
  });
41
41
  it('returns AgentResult as generator return value', async () => {
42
42
  const model = new MockMessageModel().addTurn({ type: 'textBlock', text: 'Hello' });
@@ -89,6 +89,7 @@ describe('Agent', () => {
89
89
  role: 'assistant',
90
90
  content: [new ToolUseBlock({ name: 'testTool', toolUseId: 'tool-1', input: {} })],
91
91
  }),
92
+ invocationState: {},
92
93
  }));
93
94
  expect(afterTools).toBeDefined();
94
95
  expect(afterTools?.type).toBe('afterToolsEvent');
@@ -1155,4 +1156,100 @@ describe('Agent._redactLastMessage', () => {
1155
1156
  expect(agent['messages']).toHaveLength(0);
1156
1157
  });
1157
1158
  });
1159
+ describe('_estimateInputTokens', () => {
1160
+ function captureProjectedTokens(agent) {
1161
+ return new Promise((resolve) => {
1162
+ agent.addHook(BeforeModelCallEvent, (event) => {
1163
+ resolve(event.projectedInputTokens);
1164
+ });
1165
+ });
1166
+ }
1167
+ it('uses full estimation on cold start (no prior usage metadata)', async () => {
1168
+ const model = new MockMessageModel();
1169
+ model.addTurn({ type: 'textBlock', text: 'Hello' });
1170
+ const countTokensSpy = vi.spyOn(model, 'countTokens');
1171
+ countTokensSpy.mockResolvedValue(42);
1172
+ const agent = new Agent({ model, printer: false });
1173
+ const tokenPromise = captureProjectedTokens(agent);
1174
+ await agent.invoke('Hi');
1175
+ expect(await tokenPromise).toBe(42);
1176
+ expect(countTokensSpy).toHaveBeenCalledWith(expect.any(Array), expect.any(Object));
1177
+ });
1178
+ it('uses known baseline when no new messages after last assistant', async () => {
1179
+ const model = new MockMessageModel();
1180
+ model.addTurn({ type: 'textBlock', text: 'Hello' });
1181
+ const agent = new Agent({
1182
+ model,
1183
+ printer: false,
1184
+ messages: [
1185
+ new Message({ role: 'user', content: [new TextBlock('Hi')] }),
1186
+ new Message({
1187
+ role: 'assistant',
1188
+ content: [new TextBlock('Hello')],
1189
+ metadata: { usage: { inputTokens: 100, outputTokens: 20, totalTokens: 120 } },
1190
+ }),
1191
+ ],
1192
+ });
1193
+ // Invoke with no args — no new user message appended, so the last assistant
1194
+ // message is still the final message and newMessages.length === 0
1195
+ const tokenPromise = captureProjectedTokens(agent);
1196
+ await agent.invoke([]);
1197
+ // baseline = inputTokens(100) + outputTokens(20) = 120
1198
+ expect(await tokenPromise).toBe(120);
1199
+ });
1200
+ it('returns undefined projectedInputTokens when estimation fails', async () => {
1201
+ const model = new MockMessageModel();
1202
+ model.addTurn({ type: 'textBlock', text: 'Hello' });
1203
+ vi.spyOn(model, 'countTokens').mockRejectedValue(new Error('API unavailable'));
1204
+ const agent = new Agent({ model, printer: false });
1205
+ const tokenPromise = captureProjectedTokens(agent);
1206
+ await agent.invoke('Hi');
1207
+ expect(await tokenPromise).toBeUndefined();
1208
+ });
1209
+ it('estimates delta for new messages after last assistant', async () => {
1210
+ const model = new MockMessageModel();
1211
+ model
1212
+ .addTurn([{ type: 'toolUseBlock', name: 'test', toolUseId: 'id-1', input: {} }], {
1213
+ usage: { inputTokens: 100, outputTokens: 30, totalTokens: 130 },
1214
+ })
1215
+ .addTurn({ type: 'textBlock', text: 'Done' });
1216
+ const countTokensSpy = vi.spyOn(model, 'countTokens');
1217
+ countTokensSpy.mockResolvedValue(50);
1218
+ const tool = createMockTool('test', () => new ToolResultBlock({
1219
+ toolUseId: 'id-1',
1220
+ status: 'success',
1221
+ content: [new TextBlock('result')],
1222
+ }));
1223
+ const agent = new Agent({ model, tools: [tool], printer: false });
1224
+ // Capture the second BeforeModelCallEvent (after tool execution)
1225
+ let callCount = 0;
1226
+ const tokenPromise = new Promise((resolve) => {
1227
+ agent.addHook(BeforeModelCallEvent, (event) => {
1228
+ callCount++;
1229
+ if (callCount === 2)
1230
+ resolve(event.projectedInputTokens);
1231
+ });
1232
+ });
1233
+ await agent.invoke('Use the tool');
1234
+ // baseline (100+30) + estimated delta (50) = 180
1235
+ expect(await tokenPromise).toBe(180);
1236
+ expect(countTokensSpy).toHaveBeenCalled();
1237
+ });
1238
+ it('uses baseline from prior invocation on second invoke', async () => {
1239
+ const model = new MockMessageModel();
1240
+ model
1241
+ .addTurn({ type: 'textBlock', text: 'First response' }, { usage: { inputTokens: 200, outputTokens: 50, totalTokens: 250 } })
1242
+ .addTurn({ type: 'textBlock', text: 'Second response' });
1243
+ const countTokensSpy = vi.spyOn(model, 'countTokens');
1244
+ countTokensSpy.mockResolvedValue(15);
1245
+ const agent = new Agent({ model, printer: false });
1246
+ await agent.invoke('First question');
1247
+ // Second invocation — the user message "Second question" is appended after
1248
+ // the assistant message with usage metadata, so it hits the baseline + delta path
1249
+ const tokenPromise = captureProjectedTokens(agent);
1250
+ await agent.invoke('Second question');
1251
+ // baseline (200+50) + estimated delta for new user message (15) = 265
1252
+ expect(await tokenPromise).toBe(265);
1253
+ });
1254
+ });
1158
1255
  //# sourceMappingURL=agent.test.js.map