@strands-agents/sdk 1.3.0 → 1.5.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 (462) hide show
  1. package/README.md +16 -16
  2. package/dist/src/__fixtures__/agent-helpers.d.ts.map +1 -1
  3. package/dist/src/__fixtures__/agent-helpers.js +6 -0
  4. package/dist/src/__fixtures__/agent-helpers.js.map +1 -1
  5. package/dist/src/__fixtures__/register-node-defaults.d.ts +2 -0
  6. package/dist/src/__fixtures__/register-node-defaults.d.ts.map +1 -0
  7. package/dist/src/__fixtures__/register-node-defaults.js +6 -0
  8. package/dist/src/__fixtures__/register-node-defaults.js.map +1 -0
  9. package/dist/src/__fixtures__/test-sandbox.node.d.ts +15 -0
  10. package/dist/src/__fixtures__/test-sandbox.node.d.ts.map +1 -0
  11. package/dist/src/__fixtures__/test-sandbox.node.js +23 -0
  12. package/dist/src/__fixtures__/test-sandbox.node.js.map +1 -0
  13. package/dist/src/__tests__/default-slot.test.d.ts +2 -0
  14. package/dist/src/__tests__/default-slot.test.d.ts.map +1 -0
  15. package/dist/src/__tests__/default-slot.test.js +33 -0
  16. package/dist/src/__tests__/default-slot.test.js.map +1 -0
  17. package/dist/src/__tests__/mcp.test.js +14 -14
  18. package/dist/src/__tests__/mcp.test.js.map +1 -1
  19. package/dist/src/a2a/__tests__/async-lock.test.d.ts +2 -0
  20. package/dist/src/a2a/__tests__/async-lock.test.d.ts.map +1 -0
  21. package/dist/src/a2a/__tests__/async-lock.test.js +137 -0
  22. package/dist/src/a2a/__tests__/async-lock.test.js.map +1 -0
  23. package/dist/src/a2a/__tests__/executor.test.js +146 -8
  24. package/dist/src/a2a/__tests__/executor.test.js.map +1 -1
  25. package/dist/src/a2a/__tests__/server.test.js +20 -0
  26. package/dist/src/a2a/__tests__/server.test.js.map +1 -1
  27. package/dist/src/a2a/async-lock.d.ts +22 -0
  28. package/dist/src/a2a/async-lock.d.ts.map +1 -0
  29. package/dist/src/a2a/async-lock.js +38 -0
  30. package/dist/src/a2a/async-lock.js.map +1 -0
  31. package/dist/src/a2a/executor.d.ts +59 -24
  32. package/dist/src/a2a/executor.d.ts.map +1 -1
  33. package/dist/src/a2a/executor.js +209 -32
  34. package/dist/src/a2a/executor.js.map +1 -1
  35. package/dist/src/a2a/index.d.ts +1 -1
  36. package/dist/src/a2a/index.d.ts.map +1 -1
  37. package/dist/src/a2a/index.js +1 -1
  38. package/dist/src/a2a/index.js.map +1 -1
  39. package/dist/src/a2a/server.d.ts +18 -2
  40. package/dist/src/a2a/server.d.ts.map +1 -1
  41. package/dist/src/a2a/server.js +13 -2
  42. package/dist/src/a2a/server.js.map +1 -1
  43. package/dist/src/agent/__tests__/agent.context-manager.test.d.ts +2 -0
  44. package/dist/src/agent/__tests__/agent.context-manager.test.d.ts.map +1 -0
  45. package/dist/src/agent/__tests__/agent.context-manager.test.js +107 -0
  46. package/dist/src/agent/__tests__/agent.context-manager.test.js.map +1 -0
  47. package/dist/src/agent/__tests__/agent.test.js +195 -0
  48. package/dist/src/agent/__tests__/agent.test.js.map +1 -1
  49. package/dist/src/agent/__tests__/agent.tracer.test.node.js +14 -0
  50. package/dist/src/agent/__tests__/agent.tracer.test.node.js.map +1 -1
  51. package/dist/src/agent/__tests__/tool-caller.test.d.ts +2 -0
  52. package/dist/src/agent/__tests__/tool-caller.test.d.ts.map +1 -0
  53. package/dist/src/agent/__tests__/tool-caller.test.js +459 -0
  54. package/dist/src/agent/__tests__/tool-caller.test.js.map +1 -0
  55. package/dist/src/agent/agent.d.ts +194 -4
  56. package/dist/src/agent/agent.d.ts.map +1 -1
  57. package/dist/src/agent/agent.js +599 -253
  58. package/dist/src/agent/agent.js.map +1 -1
  59. package/dist/src/agent/tool-caller.d.ts +149 -0
  60. package/dist/src/agent/tool-caller.d.ts.map +1 -0
  61. package/dist/src/agent/tool-caller.js +198 -0
  62. package/dist/src/agent/tool-caller.js.map +1 -0
  63. package/dist/src/conversation-manager/__tests__/pin.test.d.ts +2 -0
  64. package/dist/src/conversation-manager/__tests__/pin.test.d.ts.map +1 -0
  65. package/dist/src/conversation-manager/__tests__/pin.test.js +119 -0
  66. package/dist/src/conversation-manager/__tests__/pin.test.js.map +1 -0
  67. package/dist/src/conversation-manager/__tests__/sliding-window-conversation-manager.test.js +49 -0
  68. package/dist/src/conversation-manager/__tests__/sliding-window-conversation-manager.test.js.map +1 -1
  69. package/dist/src/conversation-manager/__tests__/summarizing-conversation-manager.test.js +58 -0
  70. package/dist/src/conversation-manager/__tests__/summarizing-conversation-manager.test.js.map +1 -1
  71. package/dist/src/conversation-manager/pin-message.d.ts +45 -0
  72. package/dist/src/conversation-manager/pin-message.d.ts.map +1 -0
  73. package/dist/src/conversation-manager/pin-message.js +106 -0
  74. package/dist/src/conversation-manager/pin-message.js.map +1 -0
  75. package/dist/src/conversation-manager/sliding-window-conversation-manager.d.ts +7 -0
  76. package/dist/src/conversation-manager/sliding-window-conversation-manager.d.ts.map +1 -1
  77. package/dist/src/conversation-manager/sliding-window-conversation-manager.js +27 -4
  78. package/dist/src/conversation-manager/sliding-window-conversation-manager.js.map +1 -1
  79. package/dist/src/conversation-manager/summarizing-conversation-manager.d.ts +7 -0
  80. package/dist/src/conversation-manager/summarizing-conversation-manager.d.ts.map +1 -1
  81. package/dist/src/conversation-manager/summarizing-conversation-manager.js +18 -4
  82. package/dist/src/conversation-manager/summarizing-conversation-manager.js.map +1 -1
  83. package/dist/src/default-slot.d.ts +6 -0
  84. package/dist/src/default-slot.d.ts.map +1 -0
  85. package/dist/src/default-slot.js +18 -0
  86. package/dist/src/default-slot.js.map +1 -0
  87. package/dist/src/errors.d.ts +25 -0
  88. package/dist/src/errors.d.ts.map +1 -1
  89. package/dist/src/errors.js +32 -0
  90. package/dist/src/errors.js.map +1 -1
  91. package/dist/src/index.d.ts +15 -3
  92. package/dist/src/index.d.ts.map +1 -1
  93. package/dist/src/index.js +10 -1
  94. package/dist/src/index.js.map +1 -1
  95. package/dist/src/index.node.d.ts +2 -0
  96. package/dist/src/index.node.d.ts.map +1 -0
  97. package/dist/src/index.node.js +9 -0
  98. package/dist/src/index.node.js.map +1 -0
  99. package/dist/src/interrupt.d.ts +5 -1
  100. package/dist/src/interrupt.d.ts.map +1 -1
  101. package/dist/src/interrupt.js +6 -0
  102. package/dist/src/interrupt.js.map +1 -1
  103. package/dist/src/interventions/handler.d.ts +1 -2
  104. package/dist/src/interventions/handler.d.ts.map +1 -1
  105. package/dist/src/interventions/registry.d.ts +2 -0
  106. package/dist/src/interventions/registry.d.ts.map +1 -1
  107. package/dist/src/interventions/registry.js +4 -0
  108. package/dist/src/interventions/registry.js.map +1 -1
  109. package/dist/src/mcp.d.ts +20 -15
  110. package/dist/src/mcp.d.ts.map +1 -1
  111. package/dist/src/mcp.js +15 -8
  112. package/dist/src/mcp.js.map +1 -1
  113. package/dist/src/memory/__tests__/memory-manager.test.d.ts +2 -0
  114. package/dist/src/memory/__tests__/memory-manager.test.d.ts.map +1 -0
  115. package/dist/src/memory/__tests__/memory-manager.test.js +493 -0
  116. package/dist/src/memory/__tests__/memory-manager.test.js.map +1 -0
  117. package/dist/src/memory/extraction/__tests__/extraction.test.d.ts +2 -0
  118. package/dist/src/memory/extraction/__tests__/extraction.test.d.ts.map +1 -0
  119. package/dist/src/memory/extraction/__tests__/extraction.test.js +637 -0
  120. package/dist/src/memory/extraction/__tests__/extraction.test.js.map +1 -0
  121. package/dist/src/memory/extraction/__tests__/model-extractor.test.d.ts +2 -0
  122. package/dist/src/memory/extraction/__tests__/model-extractor.test.d.ts.map +1 -0
  123. package/dist/src/memory/extraction/__tests__/model-extractor.test.js +68 -0
  124. package/dist/src/memory/extraction/__tests__/model-extractor.test.js.map +1 -0
  125. package/dist/src/memory/extraction/__tests__/resolve-extraction-config.test.d.ts +2 -0
  126. package/dist/src/memory/extraction/__tests__/resolve-extraction-config.test.d.ts.map +1 -0
  127. package/dist/src/memory/extraction/__tests__/resolve-extraction-config.test.js +81 -0
  128. package/dist/src/memory/extraction/__tests__/resolve-extraction-config.test.js.map +1 -0
  129. package/dist/src/memory/extraction/coordinator.d.ts +128 -0
  130. package/dist/src/memory/extraction/coordinator.d.ts.map +1 -0
  131. package/dist/src/memory/extraction/coordinator.js +245 -0
  132. package/dist/src/memory/extraction/coordinator.js.map +1 -0
  133. package/dist/src/memory/extraction/model-extractor.d.ts +32 -0
  134. package/dist/src/memory/extraction/model-extractor.d.ts.map +1 -0
  135. package/dist/src/memory/extraction/model-extractor.js +118 -0
  136. package/dist/src/memory/extraction/model-extractor.js.map +1 -0
  137. package/dist/src/memory/extraction/resolve-extraction-config.d.ts +46 -0
  138. package/dist/src/memory/extraction/resolve-extraction-config.d.ts.map +1 -0
  139. package/dist/src/memory/extraction/resolve-extraction-config.js +59 -0
  140. package/dist/src/memory/extraction/resolve-extraction-config.js.map +1 -0
  141. package/dist/src/memory/extraction/triggers.d.ts +41 -0
  142. package/dist/src/memory/extraction/triggers.d.ts.map +1 -0
  143. package/dist/src/memory/extraction/triggers.js +59 -0
  144. package/dist/src/memory/extraction/triggers.js.map +1 -0
  145. package/dist/src/memory/extraction/types.d.ts +133 -0
  146. package/dist/src/memory/extraction/types.d.ts.map +1 -0
  147. package/dist/src/memory/extraction/types.js +19 -0
  148. package/dist/src/memory/extraction/types.js.map +1 -0
  149. package/dist/src/memory/index.d.ts +9 -0
  150. package/dist/src/memory/index.d.ts.map +1 -0
  151. package/dist/src/memory/index.js +5 -0
  152. package/dist/src/memory/index.js.map +1 -0
  153. package/dist/src/memory/memory-manager.d.ts +131 -0
  154. package/dist/src/memory/memory-manager.d.ts.map +1 -0
  155. package/dist/src/memory/memory-manager.js +404 -0
  156. package/dist/src/memory/memory-manager.js.map +1 -0
  157. package/dist/src/memory/types.d.ts +213 -0
  158. package/dist/src/memory/types.d.ts.map +1 -0
  159. package/dist/src/memory/types.js +2 -0
  160. package/dist/src/memory/types.js.map +1 -0
  161. package/dist/src/middleware/__tests__/agent-middleware.test.d.ts +2 -0
  162. package/dist/src/middleware/__tests__/agent-middleware.test.d.ts.map +1 -0
  163. package/dist/src/middleware/__tests__/agent-middleware.test.js +1207 -0
  164. package/dist/src/middleware/__tests__/agent-middleware.test.js.map +1 -0
  165. package/dist/src/middleware/__tests__/custom-stages.test.d.ts +2 -0
  166. package/dist/src/middleware/__tests__/custom-stages.test.d.ts.map +1 -0
  167. package/dist/src/middleware/__tests__/custom-stages.test.js +97 -0
  168. package/dist/src/middleware/__tests__/custom-stages.test.js.map +1 -0
  169. package/dist/src/middleware/__tests__/middleware-interrupts.test.d.ts +2 -0
  170. package/dist/src/middleware/__tests__/middleware-interrupts.test.d.ts.map +1 -0
  171. package/dist/src/middleware/__tests__/middleware-interrupts.test.js +267 -0
  172. package/dist/src/middleware/__tests__/middleware-interrupts.test.js.map +1 -0
  173. package/dist/src/middleware/__tests__/registry.test.d.ts +2 -0
  174. package/dist/src/middleware/__tests__/registry.test.d.ts.map +1 -0
  175. package/dist/src/middleware/__tests__/registry.test.js +525 -0
  176. package/dist/src/middleware/__tests__/registry.test.js.map +1 -0
  177. package/dist/src/middleware/index.d.ts +5 -0
  178. package/dist/src/middleware/index.d.ts.map +1 -0
  179. package/dist/src/middleware/index.js +3 -0
  180. package/dist/src/middleware/index.js.map +1 -0
  181. package/dist/src/middleware/registry.d.ts +58 -0
  182. package/dist/src/middleware/registry.d.ts.map +1 -0
  183. package/dist/src/middleware/registry.js +107 -0
  184. package/dist/src/middleware/registry.js.map +1 -0
  185. package/dist/src/middleware/stages.d.ts +138 -0
  186. package/dist/src/middleware/stages.d.ts.map +1 -0
  187. package/dist/src/middleware/stages.js +31 -0
  188. package/dist/src/middleware/stages.js.map +1 -0
  189. package/dist/src/middleware/types.d.ts +88 -0
  190. package/dist/src/middleware/types.d.ts.map +1 -0
  191. package/dist/src/middleware/types.js +2 -0
  192. package/dist/src/middleware/types.js.map +1 -0
  193. package/dist/src/models/__tests__/anthropic.test.js +37 -7
  194. package/dist/src/models/__tests__/anthropic.test.js.map +1 -1
  195. package/dist/src/models/__tests__/bedrock.test.js +181 -0
  196. package/dist/src/models/__tests__/bedrock.test.js.map +1 -1
  197. package/dist/src/models/__tests__/model.test.js +46 -3
  198. package/dist/src/models/__tests__/model.test.js.map +1 -1
  199. package/dist/src/models/anthropic.d.ts.map +1 -1
  200. package/dist/src/models/anthropic.js +7 -4
  201. package/dist/src/models/anthropic.js.map +1 -1
  202. package/dist/src/models/bedrock.d.ts +26 -1
  203. package/dist/src/models/bedrock.d.ts.map +1 -1
  204. package/dist/src/models/bedrock.js +34 -9
  205. package/dist/src/models/bedrock.js.map +1 -1
  206. package/dist/src/models/defaults.d.ts.map +1 -1
  207. package/dist/src/models/defaults.js +2 -0
  208. package/dist/src/models/defaults.js.map +1 -1
  209. package/dist/src/models/model.d.ts.map +1 -1
  210. package/dist/src/models/model.js +7 -3
  211. package/dist/src/models/model.js.map +1 -1
  212. package/dist/src/models/openai/__tests__/chat.test.js +2 -10
  213. package/dist/src/models/openai/__tests__/chat.test.js.map +1 -1
  214. package/dist/src/models/openai/__tests__/errors.test.d.ts +2 -0
  215. package/dist/src/models/openai/__tests__/errors.test.d.ts.map +1 -0
  216. package/dist/src/models/openai/__tests__/errors.test.js +30 -0
  217. package/dist/src/models/openai/__tests__/errors.test.js.map +1 -0
  218. package/dist/src/models/openai/__tests__/responses.test.js +8 -33
  219. package/dist/src/models/openai/__tests__/responses.test.js.map +1 -1
  220. package/dist/src/models/openai/errors.d.ts.map +1 -1
  221. package/dist/src/models/openai/errors.js +5 -3
  222. package/dist/src/models/openai/errors.js.map +1 -1
  223. package/dist/src/multiagent/__tests__/nodes.test.js +50 -0
  224. package/dist/src/multiagent/__tests__/nodes.test.js.map +1 -1
  225. package/dist/src/multiagent/nodes.d.ts +23 -2
  226. package/dist/src/multiagent/nodes.d.ts.map +1 -1
  227. package/dist/src/multiagent/nodes.js +18 -4
  228. package/dist/src/multiagent/nodes.js.map +1 -1
  229. package/dist/src/registry/__tests__/tool-registry.test.js +50 -1
  230. package/dist/src/registry/__tests__/tool-registry.test.js.map +1 -1
  231. package/dist/src/registry/tool-registry.d.ts +13 -0
  232. package/dist/src/registry/tool-registry.d.ts.map +1 -1
  233. package/dist/src/registry/tool-registry.js +35 -1
  234. package/dist/src/registry/tool-registry.js.map +1 -1
  235. package/dist/src/sandbox/__tests__/default.test.browser.d.ts +2 -0
  236. package/dist/src/sandbox/__tests__/default.test.browser.d.ts.map +1 -0
  237. package/dist/src/sandbox/__tests__/default.test.browser.js +11 -0
  238. package/dist/src/sandbox/__tests__/default.test.browser.js.map +1 -0
  239. package/dist/src/sandbox/__tests__/default.test.node.d.ts +2 -0
  240. package/dist/src/sandbox/__tests__/default.test.node.d.ts.map +1 -0
  241. package/dist/src/sandbox/__tests__/default.test.node.js +23 -0
  242. package/dist/src/sandbox/__tests__/default.test.node.js.map +1 -0
  243. package/dist/src/sandbox/__tests__/docker.test.node.d.ts +2 -0
  244. package/dist/src/sandbox/__tests__/docker.test.node.d.ts.map +1 -0
  245. package/dist/src/sandbox/__tests__/docker.test.node.js +89 -0
  246. package/dist/src/sandbox/__tests__/docker.test.node.js.map +1 -0
  247. package/dist/src/sandbox/__tests__/errors.test.node.d.ts +2 -0
  248. package/dist/src/sandbox/__tests__/errors.test.node.d.ts.map +1 -0
  249. package/dist/src/sandbox/__tests__/errors.test.node.js +33 -0
  250. package/dist/src/sandbox/__tests__/errors.test.node.js.map +1 -0
  251. package/dist/src/sandbox/__tests__/not-a-sandbox-local-environment.test.node.d.ts +2 -0
  252. package/dist/src/sandbox/__tests__/not-a-sandbox-local-environment.test.node.d.ts.map +1 -0
  253. package/dist/src/sandbox/__tests__/not-a-sandbox-local-environment.test.node.js +124 -0
  254. package/dist/src/sandbox/__tests__/not-a-sandbox-local-environment.test.node.js.map +1 -0
  255. package/dist/src/sandbox/__tests__/posix-shell.test.node.d.ts +2 -0
  256. package/dist/src/sandbox/__tests__/posix-shell.test.node.d.ts.map +1 -0
  257. package/dist/src/sandbox/__tests__/posix-shell.test.node.js +298 -0
  258. package/dist/src/sandbox/__tests__/posix-shell.test.node.js.map +1 -0
  259. package/dist/src/sandbox/__tests__/ssh.test.node.d.ts +2 -0
  260. package/dist/src/sandbox/__tests__/ssh.test.node.d.ts.map +1 -0
  261. package/dist/src/sandbox/__tests__/ssh.test.node.js +262 -0
  262. package/dist/src/sandbox/__tests__/ssh.test.node.js.map +1 -0
  263. package/dist/src/sandbox/base.d.ts +156 -0
  264. package/dist/src/sandbox/base.d.ts.map +1 -0
  265. package/dist/src/sandbox/base.js +97 -0
  266. package/dist/src/sandbox/base.js.map +1 -0
  267. package/dist/src/sandbox/constants.d.ts +25 -0
  268. package/dist/src/sandbox/constants.d.ts.map +1 -0
  269. package/dist/src/sandbox/constants.js +27 -0
  270. package/dist/src/sandbox/constants.js.map +1 -0
  271. package/dist/src/sandbox/default.d.ts +3 -0
  272. package/dist/src/sandbox/default.d.ts.map +1 -0
  273. package/dist/src/sandbox/default.js +3 -0
  274. package/dist/src/sandbox/default.js.map +1 -0
  275. package/dist/src/sandbox/docker.d.ts +38 -0
  276. package/dist/src/sandbox/docker.d.ts.map +1 -0
  277. package/dist/src/sandbox/docker.js +59 -0
  278. package/dist/src/sandbox/docker.js.map +1 -0
  279. package/dist/src/sandbox/errors.d.ts +26 -0
  280. package/dist/src/sandbox/errors.d.ts.map +1 -0
  281. package/dist/src/sandbox/errors.js +35 -0
  282. package/dist/src/sandbox/errors.js.map +1 -0
  283. package/dist/src/sandbox/index.d.ts +5 -0
  284. package/dist/src/sandbox/index.d.ts.map +1 -0
  285. package/dist/src/sandbox/index.js +4 -0
  286. package/dist/src/sandbox/index.js.map +1 -0
  287. package/dist/src/sandbox/not-a-sandbox-local-environment.d.ts +16 -0
  288. package/dist/src/sandbox/not-a-sandbox-local-environment.d.ts.map +1 -0
  289. package/dist/src/sandbox/not-a-sandbox-local-environment.js +83 -0
  290. package/dist/src/sandbox/not-a-sandbox-local-environment.js.map +1 -0
  291. package/dist/src/sandbox/posix-shell.d.ts +53 -0
  292. package/dist/src/sandbox/posix-shell.d.ts.map +1 -0
  293. package/dist/src/sandbox/posix-shell.js +116 -0
  294. package/dist/src/sandbox/posix-shell.js.map +1 -0
  295. package/dist/src/sandbox/ssh.d.ts +56 -0
  296. package/dist/src/sandbox/ssh.d.ts.map +1 -0
  297. package/dist/src/sandbox/ssh.js +119 -0
  298. package/dist/src/sandbox/ssh.js.map +1 -0
  299. package/dist/src/sandbox/stream-process.d.ts +32 -0
  300. package/dist/src/sandbox/stream-process.d.ts.map +1 -0
  301. package/dist/src/sandbox/stream-process.js +161 -0
  302. package/dist/src/sandbox/stream-process.js.map +1 -0
  303. package/dist/src/sandbox/types.d.ts +57 -0
  304. package/dist/src/sandbox/types.d.ts.map +1 -0
  305. package/dist/src/sandbox/types.js +8 -0
  306. package/dist/src/sandbox/types.js.map +1 -0
  307. package/dist/src/telemetry/__tests__/meter.test.js +11 -0
  308. package/dist/src/telemetry/__tests__/meter.test.js.map +1 -1
  309. package/dist/src/telemetry/meter.d.ts +10 -6
  310. package/dist/src/telemetry/meter.d.ts.map +1 -1
  311. package/dist/src/telemetry/meter.js +16 -3
  312. package/dist/src/telemetry/meter.js.map +1 -1
  313. package/dist/src/tsconfig.tsbuildinfo +1 -1
  314. package/dist/src/types/__tests__/messages.test.js +28 -0
  315. package/dist/src/types/__tests__/messages.test.js.map +1 -1
  316. package/dist/src/types/agent.d.ts +72 -0
  317. package/dist/src/types/agent.d.ts.map +1 -1
  318. package/dist/src/types/agent.js.map +1 -1
  319. package/dist/src/types/lifecycle-observer.d.ts +18 -0
  320. package/dist/src/types/lifecycle-observer.d.ts.map +1 -0
  321. package/dist/src/types/lifecycle-observer.js +2 -0
  322. package/dist/src/types/lifecycle-observer.js.map +1 -0
  323. package/dist/src/types/messages.d.ts +19 -3
  324. package/dist/src/types/messages.d.ts.map +1 -1
  325. package/dist/src/types/messages.js +9 -0
  326. package/dist/src/types/messages.js.map +1 -1
  327. package/dist/src/vended-interventions/hitl/__tests__/hitl.test.d.ts +2 -0
  328. package/dist/src/vended-interventions/hitl/__tests__/hitl.test.d.ts.map +1 -0
  329. package/dist/src/vended-interventions/hitl/__tests__/hitl.test.js +358 -0
  330. package/dist/src/vended-interventions/hitl/__tests__/hitl.test.js.map +1 -0
  331. package/dist/src/vended-interventions/hitl/hitl.d.ts +115 -0
  332. package/dist/src/vended-interventions/hitl/hitl.d.ts.map +1 -0
  333. package/dist/src/vended-interventions/hitl/hitl.js +138 -0
  334. package/dist/src/vended-interventions/hitl/hitl.js.map +1 -0
  335. package/dist/src/vended-interventions/hitl/index.d.ts +24 -0
  336. package/dist/src/vended-interventions/hitl/index.d.ts.map +1 -0
  337. package/dist/src/vended-interventions/hitl/index.js +23 -0
  338. package/dist/src/vended-interventions/hitl/index.js.map +1 -0
  339. package/dist/src/vended-interventions/steering/__tests__/handler.test.d.ts +2 -0
  340. package/dist/src/vended-interventions/steering/__tests__/handler.test.d.ts.map +1 -0
  341. package/dist/src/vended-interventions/steering/__tests__/handler.test.js +163 -0
  342. package/dist/src/vended-interventions/steering/__tests__/handler.test.js.map +1 -0
  343. package/dist/src/vended-interventions/steering/__tests__/llm.test.d.ts +2 -0
  344. package/dist/src/vended-interventions/steering/__tests__/llm.test.d.ts.map +1 -0
  345. package/dist/src/vended-interventions/steering/__tests__/llm.test.js +60 -0
  346. package/dist/src/vended-interventions/steering/__tests__/llm.test.js.map +1 -0
  347. package/dist/src/vended-interventions/steering/__tests__/tool-ledger.test.d.ts +2 -0
  348. package/dist/src/vended-interventions/steering/__tests__/tool-ledger.test.d.ts.map +1 -0
  349. package/dist/src/vended-interventions/steering/__tests__/tool-ledger.test.js +94 -0
  350. package/dist/src/vended-interventions/steering/__tests__/tool-ledger.test.js.map +1 -0
  351. package/dist/src/vended-interventions/steering/handlers/handler.d.ts +64 -0
  352. package/dist/src/vended-interventions/steering/handlers/handler.d.ts.map +1 -0
  353. package/dist/src/vended-interventions/steering/handlers/handler.js +71 -0
  354. package/dist/src/vended-interventions/steering/handlers/handler.js.map +1 -0
  355. package/dist/src/vended-interventions/steering/handlers/llm.d.ts +72 -0
  356. package/dist/src/vended-interventions/steering/handlers/llm.d.ts.map +1 -0
  357. package/dist/src/vended-interventions/steering/handlers/llm.js +177 -0
  358. package/dist/src/vended-interventions/steering/handlers/llm.js.map +1 -0
  359. package/dist/src/vended-interventions/steering/index.d.ts +31 -0
  360. package/dist/src/vended-interventions/steering/index.d.ts.map +1 -0
  361. package/dist/src/vended-interventions/steering/index.js +32 -0
  362. package/dist/src/vended-interventions/steering/index.js.map +1 -0
  363. package/dist/src/vended-interventions/steering/providers/context-provider.d.ts +55 -0
  364. package/dist/src/vended-interventions/steering/providers/context-provider.d.ts.map +1 -0
  365. package/dist/src/vended-interventions/steering/providers/context-provider.js +8 -0
  366. package/dist/src/vended-interventions/steering/providers/context-provider.js.map +1 -0
  367. package/dist/src/vended-interventions/steering/providers/tool-ledger.d.ts +49 -0
  368. package/dist/src/vended-interventions/steering/providers/tool-ledger.d.ts.map +1 -0
  369. package/dist/src/vended-interventions/steering/providers/tool-ledger.js +75 -0
  370. package/dist/src/vended-interventions/steering/providers/tool-ledger.js.map +1 -0
  371. package/dist/src/vended-memory-stores/bedrock-knowledge-base/__tests__/bedrock-knowledge-base-store.test.d.ts +2 -0
  372. package/dist/src/vended-memory-stores/bedrock-knowledge-base/__tests__/bedrock-knowledge-base-store.test.d.ts.map +1 -0
  373. package/dist/src/vended-memory-stores/bedrock-knowledge-base/__tests__/bedrock-knowledge-base-store.test.js +611 -0
  374. package/dist/src/vended-memory-stores/bedrock-knowledge-base/__tests__/bedrock-knowledge-base-store.test.js.map +1 -0
  375. package/dist/src/vended-memory-stores/bedrock-knowledge-base/index.d.ts +2 -0
  376. package/dist/src/vended-memory-stores/bedrock-knowledge-base/index.d.ts.map +1 -0
  377. package/dist/src/vended-memory-stores/bedrock-knowledge-base/index.js +2 -0
  378. package/dist/src/vended-memory-stores/bedrock-knowledge-base/index.js.map +1 -0
  379. package/dist/src/vended-memory-stores/bedrock-knowledge-base/store.d.ts +230 -0
  380. package/dist/src/vended-memory-stores/bedrock-knowledge-base/store.d.ts.map +1 -0
  381. package/dist/src/vended-memory-stores/bedrock-knowledge-base/store.js +370 -0
  382. package/dist/src/vended-memory-stores/bedrock-knowledge-base/store.js.map +1 -0
  383. package/dist/src/vended-plugins/context-offloader/__tests__/file-storage-sandbox.test.node.d.ts +2 -0
  384. package/dist/src/vended-plugins/context-offloader/__tests__/file-storage-sandbox.test.node.d.ts.map +1 -0
  385. package/dist/src/vended-plugins/context-offloader/__tests__/file-storage-sandbox.test.node.js +68 -0
  386. package/dist/src/vended-plugins/context-offloader/__tests__/file-storage-sandbox.test.node.js.map +1 -0
  387. package/dist/src/vended-plugins/context-offloader/__tests__/plugin.test.node.d.ts +2 -0
  388. package/dist/src/vended-plugins/context-offloader/__tests__/plugin.test.node.d.ts.map +1 -0
  389. package/dist/src/vended-plugins/context-offloader/__tests__/plugin.test.node.js +93 -0
  390. package/dist/src/vended-plugins/context-offloader/__tests__/plugin.test.node.js.map +1 -0
  391. package/dist/src/vended-plugins/context-offloader/index.d.ts +1 -1
  392. package/dist/src/vended-plugins/context-offloader/index.d.ts.map +1 -1
  393. package/dist/src/vended-plugins/context-offloader/plugin.d.ts +4 -1
  394. package/dist/src/vended-plugins/context-offloader/plugin.d.ts.map +1 -1
  395. package/dist/src/vended-plugins/context-offloader/plugin.js +29 -7
  396. package/dist/src/vended-plugins/context-offloader/plugin.js.map +1 -1
  397. package/dist/src/vended-plugins/context-offloader/search.d.ts.map +1 -1
  398. package/dist/src/vended-plugins/context-offloader/search.js +3 -5
  399. package/dist/src/vended-plugins/context-offloader/search.js.map +1 -1
  400. package/dist/src/vended-plugins/context-offloader/storage.d.ts +29 -5
  401. package/dist/src/vended-plugins/context-offloader/storage.d.ts.map +1 -1
  402. package/dist/src/vended-plugins/context-offloader/storage.js +75 -11
  403. package/dist/src/vended-plugins/context-offloader/storage.js.map +1 -1
  404. package/dist/src/vended-plugins/goal/__tests__/plugin.test.d.ts +2 -0
  405. package/dist/src/vended-plugins/goal/__tests__/plugin.test.d.ts.map +1 -0
  406. package/dist/src/vended-plugins/goal/__tests__/plugin.test.js +736 -0
  407. package/dist/src/vended-plugins/goal/__tests__/plugin.test.js.map +1 -0
  408. package/dist/src/vended-plugins/goal/index.d.ts +21 -0
  409. package/dist/src/vended-plugins/goal/index.d.ts.map +1 -0
  410. package/dist/src/vended-plugins/goal/index.js +20 -0
  411. package/dist/src/vended-plugins/goal/index.js.map +1 -0
  412. package/dist/src/vended-plugins/goal/judge.d.ts +41 -0
  413. package/dist/src/vended-plugins/goal/judge.d.ts.map +1 -0
  414. package/dist/src/vended-plugins/goal/judge.js +92 -0
  415. package/dist/src/vended-plugins/goal/judge.js.map +1 -0
  416. package/dist/src/vended-plugins/goal/plugin.d.ts +214 -0
  417. package/dist/src/vended-plugins/goal/plugin.d.ts.map +1 -0
  418. package/dist/src/vended-plugins/goal/plugin.js +287 -0
  419. package/dist/src/vended-plugins/goal/plugin.js.map +1 -0
  420. package/dist/src/vended-plugins/index.d.ts +12 -0
  421. package/dist/src/vended-plugins/index.d.ts.map +1 -0
  422. package/dist/src/vended-plugins/index.js +12 -0
  423. package/dist/src/vended-plugins/index.js.map +1 -0
  424. package/dist/src/vended-plugins/skills/__tests__/agent-skills.test.node.js +17 -7
  425. package/dist/src/vended-plugins/skills/__tests__/agent-skills.test.node.js.map +1 -1
  426. package/dist/src/vended-plugins/skills/agent-skills.d.ts +21 -7
  427. package/dist/src/vended-plugins/skills/agent-skills.d.ts.map +1 -1
  428. package/dist/src/vended-plugins/skills/agent-skills.js +144 -77
  429. package/dist/src/vended-plugins/skills/agent-skills.js.map +1 -1
  430. package/dist/src/vended-tools/bash/__tests__/bash.test.node.js +44 -4
  431. package/dist/src/vended-tools/bash/__tests__/bash.test.node.js.map +1 -1
  432. package/dist/src/vended-tools/bash/bash.d.ts +3 -24
  433. package/dist/src/vended-tools/bash/bash.d.ts.map +1 -1
  434. package/dist/src/vended-tools/bash/bash.js +9 -9
  435. package/dist/src/vended-tools/bash/bash.js.map +1 -1
  436. package/dist/src/vended-tools/bash/index.d.ts +3 -1
  437. package/dist/src/vended-tools/bash/index.d.ts.map +1 -1
  438. package/dist/src/vended-tools/bash/index.js +2 -1
  439. package/dist/src/vended-tools/bash/index.js.map +1 -1
  440. package/dist/src/vended-tools/bash/make-bash.d.ts +22 -0
  441. package/dist/src/vended-tools/bash/make-bash.d.ts.map +1 -0
  442. package/dist/src/vended-tools/bash/make-bash.js +40 -0
  443. package/dist/src/vended-tools/bash/make-bash.js.map +1 -0
  444. package/dist/src/vended-tools/bash/types.d.ts +1 -0
  445. package/dist/src/vended-tools/bash/types.d.ts.map +1 -1
  446. package/dist/src/vended-tools/bash/types.js +2 -0
  447. package/dist/src/vended-tools/bash/types.js.map +1 -1
  448. package/dist/src/vended-tools/file-editor/__tests__/file-editor.test.node.js +83 -1
  449. package/dist/src/vended-tools/file-editor/__tests__/file-editor.test.node.js.map +1 -1
  450. package/dist/src/vended-tools/file-editor/file-editor.d.ts +19 -10
  451. package/dist/src/vended-tools/file-editor/file-editor.d.ts.map +1 -1
  452. package/dist/src/vended-tools/file-editor/file-editor.js +188 -218
  453. package/dist/src/vended-tools/file-editor/file-editor.js.map +1 -1
  454. package/dist/src/vended-tools/file-editor/index.d.ts +2 -1
  455. package/dist/src/vended-tools/file-editor/index.d.ts.map +1 -1
  456. package/dist/src/vended-tools/file-editor/index.js +1 -1
  457. package/dist/src/vended-tools/file-editor/index.js.map +1 -1
  458. package/dist/src/vended-tools/index.d.ts +17 -0
  459. package/dist/src/vended-tools/index.d.ts.map +1 -0
  460. package/dist/src/vended-tools/index.js +17 -0
  461. package/dist/src/vended-tools/index.js.map +1 -0
  462. package/package.json +66 -15
@@ -1,55 +1,3 @@
1
- var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
2
- if (value !== null && value !== void 0) {
3
- if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
4
- var dispose, inner;
5
- if (async) {
6
- if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
7
- dispose = value[Symbol.asyncDispose];
8
- }
9
- if (dispose === void 0) {
10
- if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
11
- dispose = value[Symbol.dispose];
12
- if (async) inner = dispose;
13
- }
14
- if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
- if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
16
- env.stack.push({ value: value, dispose: dispose, async: async });
17
- }
18
- else if (async) {
19
- env.stack.push({ async: true });
20
- }
21
- return value;
22
- };
23
- var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
24
- return function (env) {
25
- function fail(e) {
26
- env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
27
- env.hasError = true;
28
- }
29
- var r, s = 0;
30
- function next() {
31
- while (r = env.stack.pop()) {
32
- try {
33
- if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
34
- if (r.dispose) {
35
- var result = r.dispose.call(r.value);
36
- if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
37
- }
38
- else s |= 1;
39
- }
40
- catch (e) {
41
- fail(e);
42
- }
43
- }
44
- if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
45
- if (env.hasError) throw env.error;
46
- }
47
- return next();
48
- };
49
- })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
- var e = new Error(message);
51
- return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
- });
53
1
  import { AgentResult, } from '../types/agent.js';
54
2
  import { BedrockModel } from '../models/bedrock.js';
55
3
  import { contentBlockFromData, Message, TextBlock, ToolResultBlock, ToolUseBlock, } from '../types/messages.js';
@@ -66,12 +14,18 @@ import { AgentPrinter, getDefaultAppender } from './printer.js';
66
14
  import { InterventionRegistry } from '../interventions/registry.js';
67
15
  import { PluginRegistry } from '../plugins/registry.js';
68
16
  import { SlidingWindowConversationManager } from '../conversation-manager/sliding-window-conversation-manager.js';
17
+ import { SummarizingConversationManager } from '../conversation-manager/summarizing-conversation-manager.js';
69
18
  import { NullConversationManager } from '../conversation-manager/null-conversation-manager.js';
70
19
  import { ConversationManager } from '../conversation-manager/conversation-manager.js';
20
+ import { ContextOffloader } from '../vended-plugins/context-offloader/plugin.js';
21
+ import { InMemoryStorage } from '../vended-plugins/context-offloader/storage.js';
71
22
  import { HookRegistryImplementation } from '../hooks/registry.js';
23
+ import { MiddlewareRegistry, InvokeModelStage, ExecuteToolStage, AgentStreamStage } from '../middleware/index.js';
72
24
  import { InitializedEvent, AfterInvocationEvent, AfterModelCallEvent, AfterToolCallEvent, AfterToolsEvent, BeforeInvocationEvent, BeforeModelCallEvent, BeforeToolCallEvent, BeforeToolsEvent, HookableEvent, MessageAddedEvent, ModelStreamUpdateEvent, ContentBlockEvent, ModelMessageEvent, ToolResultEvent, AgentResultEvent, ToolStreamUpdateEvent, InterruptEvent, } from '../hooks/events.js';
73
25
  import { StructuredOutputTool, STRUCTURED_OUTPUT_TOOL_NAME } from '../tools/structured-output-tool.js';
74
26
  import { AgentAsTool } from './agent-as-tool.js';
27
+ import { ToolCaller } from './tool-caller.js';
28
+ import { MemoryManager } from '../memory/memory-manager.js';
75
29
  import { SessionManager } from '../session/session-manager.js';
76
30
  import { Tracer } from '../telemetry/tracer.js';
77
31
  import { Meter } from '../telemetry/meter.js';
@@ -79,13 +33,69 @@ import { logger } from '../logging/logger.js';
79
33
  import { CancelledError } from '../errors.js';
80
34
  import { DefaultModelRetryStrategy } from '../retry/default-model-retry-strategy.js';
81
35
  import { warnOnDuplicateRetryStrategyTypes } from '../retry/retry-strategy.js';
82
- import { InterruptError, InterruptState, interruptFromAgent } from '../interrupt.js';
36
+ import { Interrupt, InterruptError, InterruptState, interruptFromAgent } from '../interrupt.js';
83
37
  import { isInterruptResponseContent } from '../types/interrupt.js';
84
38
  import { takeSnapshot as takeSnapshotInternal, loadSnapshot as loadSnapshotInternal } from './snapshot.js';
39
+ import { defaultSandbox } from '../sandbox/default.js';
40
+ /** Benchmark-validated token threshold for offloading tool results. */
41
+ const CONTEXT_MANAGER_MAX_RESULT_TOKENS = 1_500;
42
+ /** Benchmark-validated preview token count for offloaded results. */
43
+ const CONTEXT_MANAGER_PREVIEW_TOKENS = 750;
44
+ /** Benchmark-validated ratio of messages to summarize on overflow. */
45
+ const CONTEXT_MANAGER_SUMMARY_RATIO = 0.3;
46
+ /** Benchmark-validated context window ratio that triggers proactive compression. */
47
+ const CONTEXT_MANAGER_COMPRESSION_THRESHOLD = 0.85;
48
+ /**
49
+ * Resolve the contextManager facade into a concrete ConversationManager.
50
+ *
51
+ * When contextManager is undefined, falls back to the default SlidingWindowConversationManager.
52
+ * When "auto", uses SummarizingConversationManager with benchmark-validated defaults,
53
+ * unless the user already provided a conversationManager.
54
+ */
55
+ function resolveConversationManager(contextManager, conversationManager) {
56
+ if (contextManager !== undefined && contextManager !== 'auto') {
57
+ throw new Error(`Unsupported contextManager value: "${contextManager}". Supported values: "auto"`);
58
+ }
59
+ if (contextManager === 'auto') {
60
+ return (conversationManager ??
61
+ new SummarizingConversationManager({
62
+ summaryRatio: CONTEXT_MANAGER_SUMMARY_RATIO,
63
+ proactiveCompression: { compressionThreshold: CONTEXT_MANAGER_COMPRESSION_THRESHOLD },
64
+ }));
65
+ }
66
+ return conversationManager ?? new SlidingWindowConversationManager({ windowSize: 40 });
67
+ }
85
68
  /** Default name assigned to agents when none is provided. */
86
69
  const DEFAULT_AGENT_NAME = 'Strands Agent';
87
70
  /** Default identifier assigned to agents when none is provided. */
88
71
  const DEFAULT_AGENT_ID = 'agent';
72
+ /**
73
+ * Creates a non-mutating interrupt function for middleware contexts.
74
+ * Reads existing responses from state (resume case) but never writes to it.
75
+ * On first run (no response), throws InterruptError with a locally-created Interrupt.
76
+ *
77
+ * @param interruptState - The agent's interrupt state (read-only access)
78
+ * @param idPrefix - Prefix for the interrupt ID (e.g., 'middleware:agentStream')
79
+ */
80
+ function createMiddlewareInterrupt(interruptState, idPrefix) {
81
+ return (params) => {
82
+ const interruptId = `${idPrefix}:${params.name}`;
83
+ const existing = interruptState.interrupts[interruptId];
84
+ if (existing?.response !== undefined) {
85
+ return { response: existing.response };
86
+ }
87
+ if (params.response !== undefined) {
88
+ return { response: params.response };
89
+ }
90
+ const interrupt = new Interrupt({
91
+ id: interruptId,
92
+ name: params.name,
93
+ ...(params.reason !== undefined && { reason: params.reason }),
94
+ source: 'middleware',
95
+ });
96
+ throw new InterruptError(interrupt);
97
+ };
98
+ }
89
99
  /**
90
100
  * Orchestrates the interaction between a model, a set of tools, and MCP clients.
91
101
  * The Agent is responsible for managing the lifecycle of tools and clients
@@ -132,7 +142,22 @@ export class Agent {
132
142
  * The session manager for saving and restoring agent sessions, if configured.
133
143
  */
134
144
  sessionManager;
145
+ /**
146
+ * The memory manager for cross-session memory retrieval and storage, if configured.
147
+ */
148
+ memoryManager;
149
+ _sandbox;
150
+ /**
151
+ * Execution environment for running commands, code, and file operations.
152
+ *
153
+ * @throws DefaultNotConfiguredError if no sandbox is configured for this
154
+ * environment (e.g. browsers, where no host default is registered).
155
+ */
156
+ get sandbox() {
157
+ return this._sandbox || defaultSandbox.get();
158
+ }
135
159
  _hooksRegistry;
160
+ _middlewareRegistry;
136
161
  _pluginRegistry;
137
162
  _interventionRegistry;
138
163
  _toolRegistry;
@@ -151,6 +176,8 @@ export class Agent {
151
176
  _interruptState;
152
177
  /** Strategy for executing tool calls from a single assistant turn. */
153
178
  _toolExecutor;
179
+ /** Direct tool caller — created via {@link ToolCaller.create} factory. */
180
+ _toolCaller;
154
181
  /**
155
182
  * Creates an instance of the Agent.
156
183
  * @param config - The configuration for the agent.
@@ -165,6 +192,13 @@ export class Agent {
165
192
  if (config?.description !== undefined)
166
193
  this.description = config.description;
167
194
  this.sessionManager = config?.sessionManager;
195
+ this.memoryManager =
196
+ config?.memoryManager instanceof MemoryManager
197
+ ? config.memoryManager
198
+ : config?.memoryManager
199
+ ? new MemoryManager(config.memoryManager)
200
+ : undefined;
201
+ this._sandbox = config?.sandbox;
168
202
  if (typeof config?.model === 'string') {
169
203
  this.model = new BedrockModel({ modelId: config.model });
170
204
  }
@@ -173,14 +207,13 @@ export class Agent {
173
207
  }
174
208
  // Validate and assign conversation manager
175
209
  if (this.model.stateful) {
176
- if (config?.conversationManager) {
177
- throw new Error('Cannot use a conversationManager with a stateful model. The model manages conversation state server-side.');
210
+ if (config?.conversationManager || config?.contextManager) {
211
+ throw new Error('contextManager and conversationManager cannot be used with a stateful model. The model manages conversation state server-side.');
178
212
  }
179
213
  this._conversationManager = new NullConversationManager();
180
214
  }
181
215
  else {
182
- this._conversationManager =
183
- config?.conversationManager ?? new SlidingWindowConversationManager({ windowSize: 40 });
216
+ this._conversationManager = resolveConversationManager(config?.contextManager, config?.conversationManager);
184
217
  }
185
218
  const { tools, mcpClients } = flattenTools(config?.tools ?? []);
186
219
  this._toolRegistry = new ToolRegistry(tools);
@@ -188,6 +221,8 @@ export class Agent {
188
221
  // Initialize hooks registry
189
222
  this._hooksRegistry = new HookRegistryImplementation();
190
223
  this._interventionRegistry = new InterventionRegistry(config?.interventions ?? [], this._hooksRegistry);
224
+ // Initialize middleware registry
225
+ this._middlewareRegistry = new MiddlewareRegistry();
191
226
  // `undefined` (omitted) → install the default; `null`/`[]` → explicit opt-out.
192
227
  const retryStrategies = config?.retryStrategy === null
193
228
  ? []
@@ -205,10 +240,21 @@ export class Agent {
205
240
  // - Retry-strategy ordering is not load-bearing for correctness: `DefaultModelRetryStrategy`
206
241
  // guards on `event.retry`, so a user hook that already set it short-circuits
207
242
  // the strategy regardless of registration order.
243
+ const hasOffloader = (config?.plugins ?? []).some((p) => p.name === 'strands:context-offloader');
208
244
  this._pluginRegistry = new PluginRegistry([
209
245
  this._conversationManager,
210
246
  ...retryStrategies,
211
247
  ...(config?.plugins ?? []),
248
+ ...(config?.contextManager === 'auto' && !hasOffloader
249
+ ? [
250
+ new ContextOffloader({
251
+ storage: new InMemoryStorage(),
252
+ maxResultTokens: CONTEXT_MANAGER_MAX_RESULT_TOKENS,
253
+ previewTokens: CONTEXT_MANAGER_PREVIEW_TOKENS,
254
+ }),
255
+ ]
256
+ : []),
257
+ ...(this.memoryManager ? [this.memoryManager] : []),
212
258
  ...(config?.sessionManager ? [config.sessionManager] : []),
213
259
  new ModelPlugin(this.model),
214
260
  ]);
@@ -229,6 +275,9 @@ export class Agent {
229
275
  // Initialize interrupt state for human-in-the-loop workflows
230
276
  this._interruptState = new InterruptState();
231
277
  this._toolExecutor = config?.toolExecutor ?? 'concurrent';
278
+ // Pass a private helper into ToolCaller so message append + hook firing
279
+ // remains an internal concern of Agent (not exposed as a public method).
280
+ this._toolCaller = ToolCaller.create(this, (message, invocationState) => this._appendMessageAndFireHooks(message, invocationState));
232
281
  this._initialized = false;
233
282
  }
234
283
  /**
@@ -254,6 +303,33 @@ export class Agent {
254
303
  addHook(eventType, callback, options) {
255
304
  return this._hooksRegistry.addCallback(eventType, callback, options);
256
305
  }
306
+ addMiddleware(stageOrPhase, handler) {
307
+ if ('_phase' in stageOrPhase) {
308
+ const phase = stageOrPhase;
309
+ const stage = phase._stage;
310
+ switch (phase._phase) {
311
+ case 'input': {
312
+ const adapted = this._middlewareRegistry.addInput(stageOrPhase, handler);
313
+ return () => this._middlewareRegistry.remove(stage, adapted);
314
+ }
315
+ case 'output': {
316
+ const adapted = this._middlewareRegistry.addOutput(stageOrPhase, handler);
317
+ return () => this._middlewareRegistry.remove(stage, adapted);
318
+ }
319
+ case 'wrap': {
320
+ const wrapHandler = handler;
321
+ this._middlewareRegistry.add(stage, wrapHandler);
322
+ return () => this._middlewareRegistry.remove(stage, wrapHandler);
323
+ }
324
+ default:
325
+ throw new Error(`Unknown middleware phase: ${phase._phase}`);
326
+ }
327
+ }
328
+ const stage = stageOrPhase;
329
+ const wrapHandler = handler;
330
+ this._middlewareRegistry.add(stage, wrapHandler);
331
+ return () => this._middlewareRegistry.remove(stage, wrapHandler);
332
+ }
257
333
  async initialize() {
258
334
  if (this._initialized) {
259
335
  return;
@@ -267,24 +343,44 @@ export class Agent {
267
343
  this._toolRegistry.addOrReplace(newTools);
268
344
  };
269
345
  }));
346
+ // Register tools vended by an explicitly-configured sandbox, applying the sandbox's
347
+ // toolPrefix to names (like MCP's prefix for server-vended tools).
348
+ if (this._sandbox) {
349
+ const prefix = this._sandbox.toolPrefix;
350
+ for (const tool of this._sandbox.getTools()) {
351
+ const prefixed = prefix
352
+ ? Object.create(tool, {
353
+ name: { value: `${prefix}_${tool.name}` },
354
+ toolSpec: { value: { ...tool.toolSpec, name: `${prefix}_${tool.name}` } },
355
+ })
356
+ : tool;
357
+ if (this._toolRegistry.get(prefixed.name)) {
358
+ logger.debug(`tool_name=<${prefixed.name}> | sandbox-vended tool skipped, user has already registered tool with this name`);
359
+ }
360
+ else {
361
+ this._toolRegistry.add(prefixed);
362
+ }
363
+ }
364
+ }
270
365
  await this._pluginRegistry.initialize(this);
366
+ for (const handler of this._interventionRegistry.handlers) {
367
+ const observer = handler;
368
+ if (typeof observer.observeAgent === 'function') {
369
+ await observer.observeAgent(this);
370
+ }
371
+ }
271
372
  await this._hooksRegistry.invokeCallbacks(new InitializedEvent({ agent: this }));
272
373
  this._initialized = true;
273
374
  }
274
375
  /**
275
- * Acquires a lock to prevent concurrent invocations.
276
- * Returns a Disposable that releases the lock when disposed.
376
+ * Acquires the invocation lock. Throws if an invocation is already in progress.
377
+ * Callers must release via try/finally with `this._isInvoking = false`.
277
378
  */
278
379
  acquireLock() {
279
380
  if (this._isInvoking) {
280
381
  throw new ConcurrentInvocationError('Agent is already processing an invocation. Wait for the current invoke() or stream() call to complete before invoking again.');
281
382
  }
282
383
  this._isInvoking = true;
283
- return {
284
- [Symbol.dispose]: () => {
285
- this._isInvoking = false;
286
- },
287
- };
288
384
  }
289
385
  /**
290
386
  * Throws {@link CancelledError} if cancellation has been requested.
@@ -295,6 +391,63 @@ export class Agent {
295
391
  throw new CancelledError();
296
392
  }
297
393
  }
394
+ /**
395
+ * Validates the per-invocation budget caps in {@link InvokeOptions.limits}.
396
+ * Called once at the top of `_stream` so bad inputs fail fast with a clear
397
+ * error instead of silently no-op'ing (`NaN`, `Infinity`) or tripping
398
+ * pathologically (zero swallows the user input; negative trips immediately).
399
+ *
400
+ * Each cap, when set, must be a positive finite number. Fractional values
401
+ * are accepted — harmless, and useful for token budgets derived from
402
+ * arithmetic.
403
+ */
404
+ _validateLimits(options) {
405
+ if (!options?.limits)
406
+ return;
407
+ const assertPositive = (name, value) => {
408
+ if (value !== undefined && (!Number.isFinite(value) || value <= 0)) {
409
+ throw new TypeError(`${name} must be a positive finite number, got ${value}`);
410
+ }
411
+ };
412
+ assertPositive('limits.turns', options.limits.turns);
413
+ assertPositive('limits.outputTokens', options.limits.outputTokens);
414
+ assertPositive('limits.totalTokens', options.limits.totalTokens);
415
+ }
416
+ /**
417
+ * Evaluates the per-invocation budget caps in {@link InvokeOptions.limits}
418
+ * against the current invocation's metrics. Called at the top of each
419
+ * agent-loop iteration, after `_throwIfCancelled` and before `startCycle`.
420
+ *
421
+ * Reads from {@link AgentMetrics.latestAgentInvocation} (scoped to the
422
+ * current invocation) — not `cycleCount` / `accumulatedUsage`, which are
423
+ * lifetime accumulators that would cause caps to fire prematurely on the
424
+ * second `invoke()` call against a reused agent.
425
+ *
426
+ * Priority on simultaneous trip: turns → totalTokens → outputTokens.
427
+ *
428
+ * Returns the {@link StopReason} the loop should terminate with, or
429
+ * `undefined` if every configured cap is still within budget.
430
+ */
431
+ _checkLimits(options) {
432
+ const limits = options?.limits;
433
+ if (!limits)
434
+ return undefined;
435
+ const invocation = this._meter.metrics.latestAgentInvocation;
436
+ if (!invocation)
437
+ return undefined;
438
+ const cycleCount = invocation.cycles.length;
439
+ const { outputTokens, totalTokens } = invocation.usage;
440
+ if (limits.turns !== undefined && cycleCount >= limits.turns) {
441
+ return 'limitTurns';
442
+ }
443
+ if (limits.totalTokens !== undefined && totalTokens >= limits.totalTokens) {
444
+ return 'limitTotalTokens';
445
+ }
446
+ if (limits.outputTokens !== undefined && outputTokens >= limits.outputTokens) {
447
+ return 'limitOutputTokens';
448
+ }
449
+ return undefined;
450
+ }
298
451
  /**
299
452
  * The tools this agent can use.
300
453
  */
@@ -307,6 +460,32 @@ export class Agent {
307
460
  get toolRegistry() {
308
461
  return this._toolRegistry;
309
462
  }
463
+ /**
464
+ * Whether the agent is currently processing an invocation.
465
+ */
466
+ get isInvoking() {
467
+ return this._isInvoking;
468
+ }
469
+ /**
470
+ * Direct tool calling accessor.
471
+ *
472
+ * Returns a proxy where each property is a {@link ToolHandle} with
473
+ * `.invoke()` and `.stream()` methods:
474
+ * ```typescript
475
+ * const result = await agent.tool.calculator!.invoke({ a: 5, b: 3 })
476
+ *
477
+ * for await (const event of agent.tool.calculator!.stream({ a: 5, b: 3 })) {
478
+ * console.log('progress:', event)
479
+ * }
480
+ * ```
481
+ *
482
+ * Supports underscore-to-hyphen and case-insensitive name resolution.
483
+ * Results are recorded in message history by default (pass
484
+ * `{ recordDirectToolCall: false }` to skip).
485
+ */
486
+ get tool() {
487
+ return this._toolCaller;
488
+ }
310
489
  /**
311
490
  * The cancellation signal for the current invocation.
312
491
  *
@@ -413,97 +592,185 @@ export class Agent {
413
592
  * ```
414
593
  */
415
594
  async *stream(args, options) {
416
- const env_1 = { stack: [], error: void 0, hasError: false };
595
+ this.acquireLock();
417
596
  try {
418
- const _lock = __addDisposableResource(env_1, this.acquireLock(), false);
419
597
  await this.initialize();
598
+ // Thread the resolved invocationState so all layers share the same reference.
599
+ const invocationState = options?.invocationState ?? {};
600
+ const resolvedOptions = options?.invocationState ? options : { ...options, invocationState };
420
601
  let currentArgs = args;
421
- // Outer loop: re-enters _stream when a hook sets AfterInvocationEvent.resume.
422
- // One invocation lock spans the whole resume chain.
423
602
  while (true) {
424
- // Fresh AbortController per invocation iteration, composed with any external signal.
603
+ // Fresh AbortController per iteration, composed with any external signal.
425
604
  this._abortController = new AbortController();
426
- this._abortSignal = options?.cancelSignal
427
- ? AbortSignal.any([this._abortController.signal, options.cancelSignal])
605
+ this._abortSignal = resolvedOptions?.cancelSignal
606
+ ? AbortSignal.any([this._abortController.signal, resolvedOptions.cancelSignal])
428
607
  : this._abortController.signal;
429
- const streamGenerator = this._stream(currentArgs, options);
608
+ // Process interrupt responses before middleware runs so context.interrupt() can find them
609
+ const interruptResponses = this._extractInterruptResponses(currentArgs);
610
+ if (interruptResponses.length > 0) {
611
+ this._interruptState.resume(interruptResponses);
612
+ }
613
+ // Hooks fire outside middleware — always, even on short-circuit.
614
+ const beforeInvocationEvent = new BeforeInvocationEvent({ agent: this, invocationState });
615
+ yield await this._invokeCallbacks(beforeInvocationEvent);
616
+ if (beforeInvocationEvent.cancel) {
617
+ const cancelText = typeof beforeInvocationEvent.cancel === 'string'
618
+ ? beforeInvocationEvent.cancel
619
+ : 'invocation denied by hook';
620
+ const message = new Message({ role: 'assistant', content: [new TextBlock(cancelText)] });
621
+ yield this._appendMessage(message, invocationState);
622
+ const afterEvent = new AfterInvocationEvent({ agent: this, invocationState });
623
+ await this._invokeCallbacks(afterEvent);
624
+ yield afterEvent;
625
+ return new AgentResult({
626
+ stopReason: 'endTurn',
627
+ lastMessage: message,
628
+ traces: this._tracer.localTraces,
629
+ metrics: this._meter.metrics,
630
+ invocationState,
631
+ });
632
+ }
633
+ let result;
430
634
  let caughtError;
431
- let lastAfterInvocation;
432
- let iterationResult;
635
+ const afterInvocationEvent = new AfterInvocationEvent({ agent: this, invocationState });
433
636
  try {
434
- iterationResult = await streamGenerator.next();
435
- while (!iterationResult.done) {
436
- try {
437
- const processed = await this._invokeCallbacks(iterationResult.value);
438
- if (processed instanceof AfterInvocationEvent) {
439
- lastAfterInvocation = processed;
440
- }
441
- yield processed;
442
- iterationResult = await streamGenerator.next();
443
- }
444
- catch (error) {
445
- // Throw interrupt errors back into _stream so executeTools can store the
446
- // assistant message as pending execution state for resume.
447
- if (error instanceof InterruptError) {
448
- iterationResult = await streamGenerator.throw(error);
449
- }
450
- else {
451
- throw error;
452
- }
453
- }
454
- }
455
- // Suppress AgentResultEvent for resumed iterations — only the final
456
- // invocation in a resume chain reports an agent result.
457
- if (lastAfterInvocation?.resume === undefined) {
458
- yield await this._invokeCallbacks(new AgentResultEvent({
459
- agent: this,
460
- result: iterationResult.value,
461
- invocationState: iterationResult.value.invocationState,
462
- }));
463
- }
637
+ result = yield* this._streamWithMiddleware(currentArgs, resolvedOptions, invocationState);
464
638
  }
465
639
  catch (error) {
466
640
  caughtError = error;
467
- throw error;
468
641
  }
469
642
  finally {
470
- // Drain _stream() so cleanup hooks and printer still fire.
471
- // Yield only on error (consumer may still be iterating); on a consumer
472
- // break, yielding would suspend the generator and leak the lock.
473
- let drainResult = await streamGenerator.return(undefined);
474
- while (!drainResult.done) {
475
- try {
476
- if (caughtError) {
477
- yield await this._invokeCallbacks(drainResult.value);
478
- }
479
- else {
480
- await this._invokeCallbacks(drainResult.value);
481
- }
482
- }
483
- catch (error) {
484
- logger.warn(`event_type=<${drainResult.value.type}>, error=<${error}> | error invoking callbacks during cleanup`);
485
- }
486
- drainResult = await streamGenerator.next();
487
- }
488
- // Reset controller and signal for next iteration / invocation
489
- this._abortController = new AbortController();
490
- this._abortSignal = this._abortController.signal;
643
+ // AfterInvocationEvent always fires even on error or consumer break. Outside middleware.
644
+ // Invoke hooks (so .resume can be set) but don't yield in finally (yields in finally
645
+ // suspend the generator on consumer break instead of completing cleanup).
646
+ await this._invokeCallbacks(afterInvocationEvent);
647
+ }
648
+ // Yield outside finally — in JS, a `yield` inside `finally` suspends the generator
649
+ // mid-cleanup when the consumer breaks, preventing subsequent cleanup code from running.
650
+ // This line is only reached on normal completion or caught error, never on consumer break.
651
+ yield afterInvocationEvent;
652
+ // Re-throw after hooks have fired
653
+ if (caughtError) {
654
+ throw caughtError;
491
655
  }
492
656
  // Resume only on a clean invocation — errors propagate above.
493
- if (lastAfterInvocation?.resume !== undefined) {
494
- currentArgs = lastAfterInvocation.resume;
657
+ if (afterInvocationEvent.resume !== undefined) {
658
+ currentArgs = afterInvocationEvent.resume;
495
659
  continue;
496
660
  }
497
- return iterationResult.value;
661
+ // Only emit AgentResultEvent on the final iteration (not on resumed ones).
662
+ yield await this._invokeCallbacks(new AgentResultEvent({
663
+ agent: this,
664
+ result: result,
665
+ invocationState,
666
+ }));
667
+ return result;
668
+ }
669
+ }
670
+ finally {
671
+ this._isInvoking = false;
672
+ }
673
+ }
674
+ /**
675
+ * Invokes the AgentStreamStage middleware chain.
676
+ * Hooks fire outside this method (in stream()'s resume loop).
677
+ */
678
+ async *_streamWithMiddleware(args, options, invocationState) {
679
+ const context = {
680
+ agent: this,
681
+ args,
682
+ ...(options !== undefined && { options }),
683
+ interrupt: createMiddlewareInterrupt(this._interruptState, 'middleware:agentStream'),
684
+ };
685
+ // async function* doesn't bind lexical `this`; capture for the terminal callback.
686
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
687
+ const self = this;
688
+ try {
689
+ const { result } = yield* this._middlewareRegistry.invoke(AgentStreamStage, context, async function* (ctx) {
690
+ const result = yield* self._streamCore(ctx.args, ctx.options);
691
+ return { result };
692
+ });
693
+ return result;
694
+ }
695
+ catch (error) {
696
+ if (error instanceof InterruptError) {
697
+ for (const interrupt of error.interrupts) {
698
+ this._interruptState.registerInterrupt(interrupt);
699
+ }
700
+ this._interruptState.activate();
701
+ for (const interrupt of error.interrupts) {
702
+ yield new InterruptEvent({ agent: this, interrupt, invocationState });
703
+ }
704
+ return new AgentResult({
705
+ stopReason: 'interrupt',
706
+ lastMessage: this.messages.length > 0
707
+ ? this.messages[this.messages.length - 1]
708
+ : new Message({ role: 'assistant', content: [new TextBlock('Interrupted')] }),
709
+ traces: this._tracer.localTraces,
710
+ metrics: this._meter.metrics,
711
+ interrupts: this._interruptState.getUnansweredInterrupts(),
712
+ invocationState,
713
+ });
714
+ }
715
+ throw error;
716
+ }
717
+ }
718
+ /**
719
+ * Single-pass stream through _stream() with event processing.
720
+ * No resume loop, no lifecycle events — those are handled by stream()'s resume loop.
721
+ */
722
+ async *_streamCore(args, options) {
723
+ const streamGenerator = this._stream(args, options);
724
+ let caughtError;
725
+ let iterationResult;
726
+ try {
727
+ iterationResult = await streamGenerator.next();
728
+ while (!iterationResult.done) {
729
+ try {
730
+ const processed = await this._invokeCallbacks(iterationResult.value);
731
+ yield processed;
732
+ iterationResult = await streamGenerator.next();
733
+ }
734
+ catch (error) {
735
+ // Throw interrupt errors back into _stream so executeTools can store the
736
+ // assistant message as pending execution state for resume.
737
+ if (error instanceof InterruptError) {
738
+ iterationResult = await streamGenerator.throw(error);
739
+ }
740
+ else {
741
+ throw error;
742
+ }
743
+ }
498
744
  }
499
745
  }
500
- catch (e_1) {
501
- env_1.error = e_1;
502
- env_1.hasError = true;
746
+ catch (error) {
747
+ caughtError = error;
748
+ throw error;
503
749
  }
504
750
  finally {
505
- __disposeResources(env_1);
751
+ // Drain _stream() so cleanup hooks and printer still fire.
752
+ // Yield only on error (consumer may still be iterating); on a consumer
753
+ // break, yielding would suspend the generator and leak the lock.
754
+ let drainResult = await streamGenerator.return(undefined);
755
+ while (!drainResult.done) {
756
+ try {
757
+ if (caughtError) {
758
+ yield await this._invokeCallbacks(drainResult.value);
759
+ }
760
+ else {
761
+ await this._invokeCallbacks(drainResult.value);
762
+ }
763
+ }
764
+ catch (error) {
765
+ logger.warn(`event_type=<${drainResult.value.type}>, error=<${error}> | error invoking callbacks during cleanup`);
766
+ }
767
+ drainResult = await streamGenerator.next();
768
+ }
769
+ // Reset controller and signal for next iteration / invocation
770
+ this._abortController = new AbortController();
771
+ this._abortSignal = this._abortController.signal;
506
772
  }
773
+ return iterationResult.value;
507
774
  }
508
775
  /**
509
776
  * Returns a {@link Tool} that wraps this agent, allowing it to be used
@@ -618,6 +885,7 @@ export class Agent {
618
885
  async *_stream(args, options) {
619
886
  let currentArgs = args;
620
887
  let result;
888
+ this._validateLimits(options);
621
889
  // Resolve structured output schema from per-invocation options or constructor config
622
890
  const structuredOutputSchema = options?.structuredOutputSchema ?? this._structuredOutputSchema;
623
891
  const structuredOutputTool = structuredOutputSchema ? new StructuredOutputTool(structuredOutputSchema) : undefined;
@@ -627,30 +895,14 @@ export class Agent {
627
895
  // AgentResult. Mutations by hooks/tools are visible across all recursive
628
896
  // agent loop cycles within this invocation.
629
897
  const invocationState = options?.invocationState ?? {};
630
- // Handle interrupt responses if present in input
898
+ // Interrupt responses are already consumed in stream() before middleware
899
+ // runs (so middleware-level interrupt() can find them). Re-extract here
900
+ // to gate the "non-interrupt input while interrupted" check below.
631
901
  const interruptResponses = this._extractInterruptResponses(args);
632
- if (interruptResponses.length > 0) {
633
- this._interruptState.resume(interruptResponses);
634
- }
635
902
  // Reject non-interrupt input while in interrupted state
636
903
  if (this._interruptState.activated && interruptResponses.length === 0) {
637
904
  throw new TypeError('Agent is in an interrupted state. Resume by invoking with interruptResponse content blocks.');
638
905
  }
639
- const beforeInvocationEvent = new BeforeInvocationEvent({ agent: this, invocationState });
640
- yield beforeInvocationEvent;
641
- if (beforeInvocationEvent.cancel) {
642
- const cancelText = typeof beforeInvocationEvent.cancel === 'string' ? beforeInvocationEvent.cancel : 'invocation denied by hook';
643
- const message = new Message({ role: 'assistant', content: [new TextBlock(cancelText)] });
644
- yield this._appendMessage(message, invocationState);
645
- yield new AfterInvocationEvent({ agent: this, invocationState });
646
- return new AgentResult({
647
- stopReason: 'endTurn',
648
- lastMessage: message,
649
- traces: this._tracer.localTraces,
650
- metrics: this._meter.metrics,
651
- invocationState,
652
- });
653
- }
654
906
  // Normalize input to get the user messages for telemetry
655
907
  const inputMessages = this._normalizeInput(args);
656
908
  // Start agent trace span
@@ -676,6 +928,17 @@ export class Agent {
676
928
  // Main agent loop - continues until model stops without requesting tools
677
929
  while (true) {
678
930
  this._throwIfCancelled();
931
+ const limitStopReason = this._checkLimits(options);
932
+ if (limitStopReason) {
933
+ result = new AgentResult({
934
+ stopReason: limitStopReason,
935
+ lastMessage: this.messages.at(-1),
936
+ traces: this._tracer.localTraces,
937
+ metrics: this._meter.metrics,
938
+ invocationState,
939
+ });
940
+ return result;
941
+ }
679
942
  // Start metrics cycle tracking
680
943
  const { cycleId, startTime: cycleStartTime } = this._meter.startCycle();
681
944
  // Create agent loop cycle span within agent span context
@@ -839,6 +1102,11 @@ export class Agent {
839
1102
  return result;
840
1103
  }
841
1104
  if (error instanceof InterruptError) {
1105
+ // Handles interrupts from tools/hooks that propagated up through the agent loop.
1106
+ // AgentStreamStage middleware interrupts are caught separately in _streamWithMiddleware().
1107
+ for (const interrupt of error.interrupts) {
1108
+ this._interruptState.registerInterrupt(interrupt);
1109
+ }
842
1110
  // Fan out one event per interrupt. Each event exposes `interrupt.source` so
843
1111
  // consumers can filter by origin (tool callback vs hook callback) without
844
1112
  // subscribing to separate event types.
@@ -872,8 +1140,6 @@ export class Agent {
872
1140
  if (structuredOutputTool) {
873
1141
  this._toolRegistry.remove(STRUCTURED_OUTPUT_TOOL_NAME);
874
1142
  }
875
- // Always emit final event
876
- yield new AfterInvocationEvent({ agent: this, invocationState });
877
1143
  }
878
1144
  }
879
1145
  /**
@@ -1050,26 +1316,10 @@ export class Agent {
1050
1316
  }
1051
1317
  return { message, stopReason: 'endTurn' };
1052
1318
  }
1053
- // Start model span within loop span context
1054
- const modelId = this.model.modelId;
1055
- const modelSpan = this._tracer.startModelInvokeSpan({
1056
- messages: this.messages,
1057
- ...(modelId && { modelId }),
1058
- ...(this.systemPrompt !== undefined && { systemPrompt: this.systemPrompt }),
1059
- });
1060
1319
  try {
1061
- const result = yield* this._streamFromModel(this.messages, streamOptions, invocationState);
1320
+ const result = yield* this._invokeModelWithMiddleware(invocationState, toolChoice);
1062
1321
  // Accumulate token usage and model latency metrics
1063
1322
  this._meter.updateCycle(result.metadata);
1064
- // End model span with usage
1065
- const usage = result.metadata?.usage;
1066
- const metrics = result.metadata?.metrics;
1067
- this._tracer.endModelInvokeSpan(modelSpan, {
1068
- output: result.message,
1069
- stopReason: result.stopReason,
1070
- ...(usage && { usage }),
1071
- ...(metrics && { metrics }),
1072
- });
1073
1323
  yield new ModelMessageEvent({
1074
1324
  agent: this,
1075
1325
  message: result.message,
@@ -1101,8 +1351,6 @@ export class Agent {
1101
1351
  }
1102
1352
  catch (error) {
1103
1353
  const modelError = normalizeError(error);
1104
- // End model span with error
1105
- this._tracer.endModelInvokeSpan(modelSpan, { error: modelError });
1106
1354
  // Create error event
1107
1355
  const errorEvent = new AfterModelCallEvent({
1108
1356
  agent: this,
@@ -1128,6 +1376,66 @@ export class Agent {
1128
1376
  }
1129
1377
  }
1130
1378
  }
1379
+ /**
1380
+ * Invokes the model through the InvokeModelStage middleware chain.
1381
+ * Builds an InvokeModelContext from current agent state and composes the
1382
+ * middleware chain with a terminal function that calls _streamFromModel
1383
+ * using context fields directly (not re-derived from the agent).
1384
+ *
1385
+ * @param invocationState - Per-invocation state shared across hooks and tools
1386
+ * @param toolChoice - Optional tool choice to force specific tool usage
1387
+ * @returns StreamAggregatedResult from the model (or middleware short-circuit)
1388
+ */
1389
+ async *_invokeModelWithMiddleware(invocationState, toolChoice) {
1390
+ const context = {
1391
+ agent: this,
1392
+ messages: this.messages,
1393
+ ...(this.systemPrompt !== undefined && { systemPrompt: this.systemPrompt }),
1394
+ toolSpecs: this._toolRegistry.list().map((tool) => tool.toolSpec),
1395
+ ...(toolChoice !== undefined && { toolChoice }),
1396
+ modelState: this.modelState,
1397
+ invocationState,
1398
+ };
1399
+ // async function* doesn't bind lexical `this`; capture for the terminal callback.
1400
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
1401
+ const self = this;
1402
+ const middlewareResult = yield* this._middlewareRegistry.invoke(InvokeModelStage, context, async function* (ctx) {
1403
+ const modelId = self.model.modelId;
1404
+ const modelSpan = self._tracer.startModelInvokeSpan({
1405
+ messages: ctx.messages,
1406
+ ...(modelId && { modelId }),
1407
+ ...(ctx.systemPrompt !== undefined && { systemPrompt: ctx.systemPrompt }),
1408
+ });
1409
+ try {
1410
+ const streamOptions = {
1411
+ toolSpecs: ctx.toolSpecs,
1412
+ modelState: ctx.modelState,
1413
+ ...(ctx.systemPrompt !== undefined && { systemPrompt: ctx.systemPrompt }),
1414
+ ...(ctx.toolChoice && { toolChoice: ctx.toolChoice }),
1415
+ };
1416
+ const gen = self._streamFromModel(ctx.messages, streamOptions, ctx.invocationState);
1417
+ let iterResult = await gen.next();
1418
+ while (!iterResult.done) {
1419
+ yield iterResult.value;
1420
+ iterResult = await gen.next();
1421
+ }
1422
+ const usage = iterResult.value.metadata?.usage;
1423
+ const metrics = iterResult.value.metadata?.metrics;
1424
+ self._tracer.endModelInvokeSpan(modelSpan, {
1425
+ output: iterResult.value.message,
1426
+ stopReason: iterResult.value.stopReason,
1427
+ ...(usage && { usage }),
1428
+ ...(metrics && { metrics }),
1429
+ });
1430
+ return { result: iterResult.value };
1431
+ }
1432
+ catch (error) {
1433
+ self._tracer.endModelInvokeSpan(modelSpan, { error: normalizeError(error) });
1434
+ throw error;
1435
+ }
1436
+ });
1437
+ return middlewareResult.result;
1438
+ }
1131
1439
  /**
1132
1440
  * Streams events from the model and dispatches appropriate events for each.
1133
1441
  *
@@ -1484,86 +1792,9 @@ export class Agent {
1484
1792
  }
1485
1793
  return afterToolCallEvent.result;
1486
1794
  }
1487
- // Start tool span within loop span context
1488
- const toolSpan = this._tracer.startToolCallSpan({
1489
- tool: toolUse,
1490
- });
1491
- // Track tool execution time for metrics
1492
- const toolStartTime = Date.now();
1493
- let toolResult;
1494
- let error;
1495
- if (!effectiveTool) {
1496
- // Tool not found
1497
- toolResult = new ToolResultBlock({
1498
- toolUseId: toolUse.toolUseId,
1499
- status: 'error',
1500
- content: [new TextBlock(`Tool '${toolUse.name}' not found in registry`)],
1501
- });
1502
- }
1503
- else {
1504
- // Execute tool within the tool span context
1505
- const toolContext = {
1506
- toolUse: {
1507
- name: toolUse.name,
1508
- toolUseId: toolUse.toolUseId,
1509
- input: toolUse.input,
1510
- },
1511
- agent: this,
1512
- invocationState,
1513
- interrupt: (params) => {
1514
- return interruptFromAgent(this, `tool:${toolUseBlock.toolUseId}:${params.name}`, params, 'tool');
1515
- },
1516
- };
1517
- try {
1518
- // Manually iterate tool stream to wrap each ToolStreamEvent in ToolStreamUpdateEvent.
1519
- // This keeps the tool authoring interface unchanged — tools construct ToolStreamEvent
1520
- // without knowledge of agents or hooks, and we wrap at the boundary.
1521
- // Tool execution is ran within the tool span's context so that
1522
- // downstream calls (e.g., MCP clients) can propagate trace context
1523
- const toolGenerator = this._tracer.withSpanContext(toolSpan, () => effectiveTool.stream(toolContext));
1524
- let toolNext = await this._tracer.withSpanContext(toolSpan, () => toolGenerator.next());
1525
- while (!toolNext.done) {
1526
- yield new ToolStreamUpdateEvent({ agent: this, event: toolNext.value, invocationState });
1527
- toolNext = await this._tracer.withSpanContext(toolSpan, () => toolGenerator.next());
1528
- }
1529
- const result = toolNext.value;
1530
- if (!result) {
1531
- // Tool didn't return a result
1532
- toolResult = new ToolResultBlock({
1533
- toolUseId: toolUse.toolUseId,
1534
- status: 'error',
1535
- content: [new TextBlock(`Tool '${toolUse.name}' did not return a result`)],
1536
- });
1537
- }
1538
- else {
1539
- toolResult = result;
1540
- error = result.error;
1541
- }
1542
- }
1543
- catch (e) {
1544
- // Re-throw InterruptError to allow interrupt handling
1545
- if (e instanceof InterruptError) {
1546
- throw e;
1547
- }
1548
- // Tool execution failed with error
1549
- error = normalizeError(e);
1550
- toolResult = new ToolResultBlock({
1551
- toolUseId: toolUse.toolUseId,
1552
- status: 'error',
1553
- content: [new TextBlock(error.message)],
1554
- error,
1555
- });
1556
- }
1557
- }
1558
- // End tool span with the raw tool result — telemetry reflects what the
1559
- // tool actually returned, independent of AfterToolCallEvent mutations.
1560
- this._tracer.endToolCallSpan(toolSpan, { toolResult, ...(error && { error }) });
1561
- // End tool metrics tracking
1562
- this._meter.endToolCall({
1563
- tool: toolUse,
1564
- duration: Date.now() - toolStartTime,
1565
- success: toolResult.status === 'success',
1566
- });
1795
+ // Execute tool core logic through middleware chain
1796
+ const toolResult = yield* this._executeToolWithMiddleware(effectiveTool, toolUse, invocationState);
1797
+ const error = toolResult.error;
1567
1798
  // Single point for AfterToolCallEvent
1568
1799
  const afterToolCallEvent = new AfterToolCallEvent({
1569
1800
  agent: this,
@@ -1582,6 +1813,110 @@ export class Agent {
1582
1813
  return afterToolCallEvent.result;
1583
1814
  }
1584
1815
  }
1816
+ async *_executeToolWithMiddleware(tool, toolUse, invocationState) {
1817
+ const context = {
1818
+ agent: this,
1819
+ tool,
1820
+ toolUse: {
1821
+ name: toolUse.name,
1822
+ toolUseId: toolUse.toolUseId,
1823
+ input: toolUse.input,
1824
+ },
1825
+ invocationState,
1826
+ interrupt: createMiddlewareInterrupt(this._interruptState, `middleware:executeTool:${toolUse.toolUseId}`),
1827
+ };
1828
+ // async function* doesn't bind lexical `this`; capture for the terminal callback.
1829
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
1830
+ const self = this;
1831
+ const middlewareResult = yield* this._middlewareRegistry.invoke(ExecuteToolStage, context, async function* (ctx) {
1832
+ const result = yield* self._executeToolCore(ctx.tool, ctx.toolUse, ctx.invocationState);
1833
+ return { result };
1834
+ });
1835
+ return middlewareResult.result;
1836
+ }
1837
+ async *_executeToolCore(effectiveTool, toolUse, invocationState) {
1838
+ // Start tool span within loop span context
1839
+ const toolSpan = this._tracer.startToolCallSpan({
1840
+ tool: toolUse,
1841
+ });
1842
+ // Track tool execution time for metrics
1843
+ const toolStartTime = Date.now();
1844
+ let toolResult;
1845
+ let error;
1846
+ if (!effectiveTool) {
1847
+ // Tool not found
1848
+ toolResult = new ToolResultBlock({
1849
+ toolUseId: toolUse.toolUseId,
1850
+ status: 'error',
1851
+ content: [new TextBlock(`Tool '${toolUse.name}' not found in registry`)],
1852
+ });
1853
+ }
1854
+ else {
1855
+ // Execute tool within the tool span context
1856
+ const toolContext = {
1857
+ toolUse: {
1858
+ name: toolUse.name,
1859
+ toolUseId: toolUse.toolUseId,
1860
+ input: toolUse.input,
1861
+ },
1862
+ agent: this,
1863
+ invocationState,
1864
+ interrupt: (params) => {
1865
+ return interruptFromAgent(this, `tool:${toolUse.toolUseId}:${params.name}`, params, 'tool');
1866
+ },
1867
+ };
1868
+ try {
1869
+ // Manually iterate tool stream to wrap each ToolStreamEvent in ToolStreamUpdateEvent.
1870
+ // This keeps the tool authoring interface unchanged — tools construct ToolStreamEvent
1871
+ // without knowledge of agents or hooks, and we wrap at the boundary.
1872
+ // Tool execution is ran within the tool span's context so that
1873
+ // downstream calls (e.g., MCP clients) can propagate trace context
1874
+ const toolGenerator = this._tracer.withSpanContext(toolSpan, () => effectiveTool.stream(toolContext));
1875
+ let toolNext = await this._tracer.withSpanContext(toolSpan, () => toolGenerator.next());
1876
+ while (!toolNext.done) {
1877
+ yield new ToolStreamUpdateEvent({ agent: this, event: toolNext.value, invocationState });
1878
+ toolNext = await this._tracer.withSpanContext(toolSpan, () => toolGenerator.next());
1879
+ }
1880
+ const result = toolNext.value;
1881
+ if (!result) {
1882
+ // Tool didn't return a result
1883
+ toolResult = new ToolResultBlock({
1884
+ toolUseId: toolUse.toolUseId,
1885
+ status: 'error',
1886
+ content: [new TextBlock(`Tool '${toolUse.name}' did not return a result`)],
1887
+ });
1888
+ }
1889
+ else {
1890
+ toolResult = result;
1891
+ error = result.error;
1892
+ }
1893
+ }
1894
+ catch (e) {
1895
+ // Re-throw InterruptError to allow interrupt handling
1896
+ if (e instanceof InterruptError) {
1897
+ throw e;
1898
+ }
1899
+ // Tool execution failed with error
1900
+ error = normalizeError(e);
1901
+ toolResult = new ToolResultBlock({
1902
+ toolUseId: toolUse.toolUseId,
1903
+ status: 'error',
1904
+ content: [new TextBlock(error.message)],
1905
+ error,
1906
+ });
1907
+ }
1908
+ }
1909
+ // End tool span with the raw tool result — telemetry reflects what the
1910
+ // tool actually returned, independent of AfterToolCallEvent mutations.
1911
+ this._tracer.endToolCallSpan(toolSpan, { toolResult, ...(error && { error }) });
1912
+ // End tool metrics tracking
1913
+ this._meter.endToolCall({
1914
+ tool: toolUse,
1915
+ duration: Date.now() - toolStartTime,
1916
+ success: toolResult.status === 'success',
1917
+ });
1918
+ return toolResult;
1919
+ }
1585
1920
  /**
1586
1921
  * Redacts the last message in the conversation history.
1587
1922
  * Called when guardrails block user input and redaction is enabled.
@@ -1668,6 +2003,17 @@ export class Agent {
1668
2003
  }
1669
2004
  return estimate;
1670
2005
  }
2006
+ /**
2007
+ * Appends a message to the conversation history and fires MessageAddedEvent hooks.
2008
+ *
2009
+ * Used by {@link ToolCaller} (via the helper passed to `ToolCaller.create`) for
2010
+ * direct tool calls that cannot yield events into the agent stream. This stays
2011
+ * private — callers outside the agent should never directly mutate messages.
2012
+ */
2013
+ async _appendMessageAndFireHooks(message, invocationState = {}) {
2014
+ this.messages.push(message);
2015
+ await this._hooksRegistry.invokeCallbacks(new MessageAddedEvent({ agent: this, message, invocationState }));
2016
+ }
1671
2017
  /**
1672
2018
  * Appends a message to the conversation history and returns the event for yielding.
1673
2019
  *