@strands-agents/sdk 1.4.0 → 1.6.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 (435) hide show
  1. package/README.md +11 -11
  2. package/dist/src/__fixtures__/agent-helpers.d.ts.map +1 -1
  3. package/dist/src/__fixtures__/agent-helpers.js +9 -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.map +1 -1
  10. package/dist/src/__fixtures__/test-sandbox.node.js +4 -3
  11. package/dist/src/__fixtures__/test-sandbox.node.js.map +1 -1
  12. package/dist/src/__tests__/default-slot.test.d.ts +2 -0
  13. package/dist/src/__tests__/default-slot.test.d.ts.map +1 -0
  14. package/dist/src/__tests__/default-slot.test.js +33 -0
  15. package/dist/src/__tests__/default-slot.test.js.map +1 -0
  16. package/dist/src/a2a/__tests__/async-lock.test.d.ts +2 -0
  17. package/dist/src/a2a/__tests__/async-lock.test.d.ts.map +1 -0
  18. package/dist/src/a2a/__tests__/async-lock.test.js +137 -0
  19. package/dist/src/a2a/__tests__/async-lock.test.js.map +1 -0
  20. package/dist/src/a2a/__tests__/executor.test.js +146 -8
  21. package/dist/src/a2a/__tests__/executor.test.js.map +1 -1
  22. package/dist/src/a2a/__tests__/server.test.js +20 -0
  23. package/dist/src/a2a/__tests__/server.test.js.map +1 -1
  24. package/dist/src/a2a/async-lock.d.ts +22 -0
  25. package/dist/src/a2a/async-lock.d.ts.map +1 -0
  26. package/dist/src/a2a/async-lock.js +38 -0
  27. package/dist/src/a2a/async-lock.js.map +1 -0
  28. package/dist/src/a2a/executor.d.ts +59 -24
  29. package/dist/src/a2a/executor.d.ts.map +1 -1
  30. package/dist/src/a2a/executor.js +209 -32
  31. package/dist/src/a2a/executor.js.map +1 -1
  32. package/dist/src/a2a/index.d.ts +1 -1
  33. package/dist/src/a2a/index.d.ts.map +1 -1
  34. package/dist/src/a2a/index.js +1 -1
  35. package/dist/src/a2a/index.js.map +1 -1
  36. package/dist/src/a2a/server.d.ts +18 -2
  37. package/dist/src/a2a/server.d.ts.map +1 -1
  38. package/dist/src/a2a/server.js +13 -2
  39. package/dist/src/a2a/server.js.map +1 -1
  40. package/dist/src/agent/__tests__/agent.context-manager.test.d.ts +2 -0
  41. package/dist/src/agent/__tests__/agent.context-manager.test.d.ts.map +1 -0
  42. package/dist/src/agent/__tests__/agent.context-manager.test.js +107 -0
  43. package/dist/src/agent/__tests__/agent.context-manager.test.js.map +1 -0
  44. package/dist/src/agent/__tests__/agent.stateful-model.test.js +2 -2
  45. package/dist/src/agent/__tests__/agent.stateful-model.test.js.map +1 -1
  46. package/dist/src/agent/__tests__/agent.tracer.test.node.js +14 -0
  47. package/dist/src/agent/__tests__/agent.tracer.test.node.js.map +1 -1
  48. package/dist/src/agent/agent.d.ts +135 -2
  49. package/dist/src/agent/agent.d.ts.map +1 -1
  50. package/dist/src/agent/agent.js +506 -189
  51. package/dist/src/agent/agent.js.map +1 -1
  52. package/dist/src/context-manager/modes/agentic/agentic-context.d.ts +19 -0
  53. package/dist/src/context-manager/modes/agentic/agentic-context.d.ts.map +1 -0
  54. package/dist/src/context-manager/modes/agentic/agentic-context.js +245 -0
  55. package/dist/src/context-manager/modes/agentic/agentic-context.js.map +1 -0
  56. package/dist/src/conversation-manager/__tests__/agentic-context.test.d.ts +2 -0
  57. package/dist/src/conversation-manager/__tests__/agentic-context.test.d.ts.map +1 -0
  58. package/dist/src/conversation-manager/__tests__/agentic-context.test.js +332 -0
  59. package/dist/src/conversation-manager/__tests__/agentic-context.test.js.map +1 -0
  60. package/dist/src/conversation-manager/__tests__/context-compression.test.d.ts +2 -0
  61. package/dist/src/conversation-manager/__tests__/context-compression.test.d.ts.map +1 -0
  62. package/dist/src/conversation-manager/__tests__/context-compression.test.js +176 -0
  63. package/dist/src/conversation-manager/__tests__/context-compression.test.js.map +1 -0
  64. package/dist/src/conversation-manager/__tests__/pin.test.d.ts +2 -0
  65. package/dist/src/conversation-manager/__tests__/pin.test.d.ts.map +1 -0
  66. package/dist/src/conversation-manager/__tests__/pin.test.js +119 -0
  67. package/dist/src/conversation-manager/__tests__/pin.test.js.map +1 -0
  68. package/dist/src/conversation-manager/__tests__/sliding-window-conversation-manager.test.js +49 -0
  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 +58 -0
  71. package/dist/src/conversation-manager/__tests__/summarizing-conversation-manager.test.js.map +1 -1
  72. package/dist/src/conversation-manager/__tests__/token-usage-middleware.test.d.ts +2 -0
  73. package/dist/src/conversation-manager/__tests__/token-usage-middleware.test.d.ts.map +1 -0
  74. package/dist/src/conversation-manager/__tests__/token-usage-middleware.test.js +138 -0
  75. package/dist/src/conversation-manager/__tests__/token-usage-middleware.test.js.map +1 -0
  76. package/dist/src/conversation-manager/compression/context-compression.d.ts +39 -0
  77. package/dist/src/conversation-manager/compression/context-compression.d.ts.map +1 -0
  78. package/dist/src/conversation-manager/compression/context-compression.js +150 -0
  79. package/dist/src/conversation-manager/compression/context-compression.js.map +1 -0
  80. package/dist/src/conversation-manager/compression/pin-message.d.ts +45 -0
  81. package/dist/src/conversation-manager/compression/pin-message.d.ts.map +1 -0
  82. package/dist/src/conversation-manager/compression/pin-message.js +106 -0
  83. package/dist/src/conversation-manager/compression/pin-message.js.map +1 -0
  84. package/dist/src/conversation-manager/conversation-manager.d.ts +2 -0
  85. package/dist/src/conversation-manager/conversation-manager.d.ts.map +1 -1
  86. package/dist/src/conversation-manager/conversation-manager.js +2 -2
  87. package/dist/src/conversation-manager/conversation-manager.js.map +1 -1
  88. package/dist/src/conversation-manager/sliding-window-conversation-manager.d.ts +7 -0
  89. package/dist/src/conversation-manager/sliding-window-conversation-manager.d.ts.map +1 -1
  90. package/dist/src/conversation-manager/sliding-window-conversation-manager.js +30 -38
  91. package/dist/src/conversation-manager/sliding-window-conversation-manager.js.map +1 -1
  92. package/dist/src/conversation-manager/summarizing-conversation-manager.d.ts +7 -19
  93. package/dist/src/conversation-manager/summarizing-conversation-manager.d.ts.map +1 -1
  94. package/dist/src/conversation-manager/summarizing-conversation-manager.js +20 -109
  95. package/dist/src/conversation-manager/summarizing-conversation-manager.js.map +1 -1
  96. package/dist/src/default-slot.d.ts +6 -0
  97. package/dist/src/default-slot.d.ts.map +1 -0
  98. package/dist/src/default-slot.js +18 -0
  99. package/dist/src/default-slot.js.map +1 -0
  100. package/dist/src/errors.d.ts +8 -0
  101. package/dist/src/errors.d.ts.map +1 -1
  102. package/dist/src/errors.js +11 -0
  103. package/dist/src/errors.js.map +1 -1
  104. package/dist/src/index.d.ts +8 -1
  105. package/dist/src/index.d.ts.map +1 -1
  106. package/dist/src/index.js +7 -1
  107. package/dist/src/index.js.map +1 -1
  108. package/dist/src/index.node.d.ts +2 -0
  109. package/dist/src/index.node.d.ts.map +1 -0
  110. package/dist/src/index.node.js +9 -0
  111. package/dist/src/index.node.js.map +1 -0
  112. package/dist/src/injection/__tests__/message-injection.test.d.ts +2 -0
  113. package/dist/src/injection/__tests__/message-injection.test.d.ts.map +1 -0
  114. package/dist/src/injection/__tests__/message-injection.test.js +200 -0
  115. package/dist/src/injection/__tests__/message-injection.test.js.map +1 -0
  116. package/dist/src/injection/index.d.ts +6 -0
  117. package/dist/src/injection/index.d.ts.map +1 -0
  118. package/dist/src/injection/index.js +2 -0
  119. package/dist/src/injection/index.js.map +1 -0
  120. package/dist/src/injection/message-injection.d.ts +65 -0
  121. package/dist/src/injection/message-injection.d.ts.map +1 -0
  122. package/dist/src/injection/message-injection.js +134 -0
  123. package/dist/src/injection/message-injection.js.map +1 -0
  124. package/dist/src/injection/types.d.ts +63 -0
  125. package/dist/src/injection/types.d.ts.map +1 -0
  126. package/dist/src/injection/types.js +2 -0
  127. package/dist/src/injection/types.js.map +1 -0
  128. package/dist/src/injection/xml.d.ts +27 -0
  129. package/dist/src/injection/xml.d.ts.map +1 -0
  130. package/dist/src/injection/xml.js +31 -0
  131. package/dist/src/injection/xml.js.map +1 -0
  132. package/dist/src/interrupt.d.ts +5 -1
  133. package/dist/src/interrupt.d.ts.map +1 -1
  134. package/dist/src/interrupt.js +6 -0
  135. package/dist/src/interrupt.js.map +1 -1
  136. package/dist/src/memory/__tests__/memory-manager.test.d.ts +2 -0
  137. package/dist/src/memory/__tests__/memory-manager.test.d.ts.map +1 -0
  138. package/dist/src/memory/__tests__/memory-manager.test.js +679 -0
  139. package/dist/src/memory/__tests__/memory-manager.test.js.map +1 -0
  140. package/dist/src/memory/extraction/__tests__/extraction.test.d.ts +2 -0
  141. package/dist/src/memory/extraction/__tests__/extraction.test.d.ts.map +1 -0
  142. package/dist/src/memory/extraction/__tests__/extraction.test.js +637 -0
  143. package/dist/src/memory/extraction/__tests__/extraction.test.js.map +1 -0
  144. package/dist/src/memory/extraction/__tests__/model-extractor.test.d.ts +2 -0
  145. package/dist/src/memory/extraction/__tests__/model-extractor.test.d.ts.map +1 -0
  146. package/dist/src/memory/extraction/__tests__/model-extractor.test.js +68 -0
  147. package/dist/src/memory/extraction/__tests__/model-extractor.test.js.map +1 -0
  148. package/dist/src/memory/extraction/__tests__/resolve-extraction-config.test.d.ts +2 -0
  149. package/dist/src/memory/extraction/__tests__/resolve-extraction-config.test.d.ts.map +1 -0
  150. package/dist/src/memory/extraction/__tests__/resolve-extraction-config.test.js +81 -0
  151. package/dist/src/memory/extraction/__tests__/resolve-extraction-config.test.js.map +1 -0
  152. package/dist/src/memory/extraction/coordinator.d.ts +128 -0
  153. package/dist/src/memory/extraction/coordinator.d.ts.map +1 -0
  154. package/dist/src/memory/extraction/coordinator.js +245 -0
  155. package/dist/src/memory/extraction/coordinator.js.map +1 -0
  156. package/dist/src/memory/extraction/model-extractor.d.ts +32 -0
  157. package/dist/src/memory/extraction/model-extractor.d.ts.map +1 -0
  158. package/dist/src/memory/extraction/model-extractor.js +118 -0
  159. package/dist/src/memory/extraction/model-extractor.js.map +1 -0
  160. package/dist/src/memory/extraction/resolve-extraction-config.d.ts +46 -0
  161. package/dist/src/memory/extraction/resolve-extraction-config.d.ts.map +1 -0
  162. package/dist/src/memory/extraction/resolve-extraction-config.js +59 -0
  163. package/dist/src/memory/extraction/resolve-extraction-config.js.map +1 -0
  164. package/dist/src/memory/extraction/triggers.d.ts +41 -0
  165. package/dist/src/memory/extraction/triggers.d.ts.map +1 -0
  166. package/dist/src/memory/extraction/triggers.js +59 -0
  167. package/dist/src/memory/extraction/triggers.js.map +1 -0
  168. package/dist/src/memory/extraction/types.d.ts +133 -0
  169. package/dist/src/memory/extraction/types.d.ts.map +1 -0
  170. package/dist/src/memory/extraction/types.js +19 -0
  171. package/dist/src/memory/extraction/types.js.map +1 -0
  172. package/dist/src/memory/index.d.ts +10 -0
  173. package/dist/src/memory/index.d.ts.map +1 -0
  174. package/dist/src/memory/index.js +5 -0
  175. package/dist/src/memory/index.js.map +1 -0
  176. package/dist/src/memory/memory-manager.d.ts +178 -0
  177. package/dist/src/memory/memory-manager.d.ts.map +1 -0
  178. package/dist/src/memory/memory-manager.js +526 -0
  179. package/dist/src/memory/memory-manager.js.map +1 -0
  180. package/dist/src/memory/types.d.ts +278 -0
  181. package/dist/src/memory/types.d.ts.map +1 -0
  182. package/dist/src/memory/types.js +2 -0
  183. package/dist/src/memory/types.js.map +1 -0
  184. package/dist/src/middleware/__tests__/agent-middleware.test.d.ts +2 -0
  185. package/dist/src/middleware/__tests__/agent-middleware.test.d.ts.map +1 -0
  186. package/dist/src/middleware/__tests__/agent-middleware.test.js +1206 -0
  187. package/dist/src/middleware/__tests__/agent-middleware.test.js.map +1 -0
  188. package/dist/src/middleware/__tests__/copy-on-input.test.d.ts +2 -0
  189. package/dist/src/middleware/__tests__/copy-on-input.test.d.ts.map +1 -0
  190. package/dist/src/middleware/__tests__/copy-on-input.test.js +379 -0
  191. package/dist/src/middleware/__tests__/copy-on-input.test.js.map +1 -0
  192. package/dist/src/middleware/__tests__/custom-stages.test.d.ts +2 -0
  193. package/dist/src/middleware/__tests__/custom-stages.test.d.ts.map +1 -0
  194. package/dist/src/middleware/__tests__/custom-stages.test.js +97 -0
  195. package/dist/src/middleware/__tests__/custom-stages.test.js.map +1 -0
  196. package/dist/src/middleware/__tests__/middleware-interrupts.test.d.ts +2 -0
  197. package/dist/src/middleware/__tests__/middleware-interrupts.test.d.ts.map +1 -0
  198. package/dist/src/middleware/__tests__/middleware-interrupts.test.js +267 -0
  199. package/dist/src/middleware/__tests__/middleware-interrupts.test.js.map +1 -0
  200. package/dist/src/middleware/__tests__/registry.test.d.ts +2 -0
  201. package/dist/src/middleware/__tests__/registry.test.d.ts.map +1 -0
  202. package/dist/src/middleware/__tests__/registry.test.js +525 -0
  203. package/dist/src/middleware/__tests__/registry.test.js.map +1 -0
  204. package/dist/src/middleware/index.d.ts +5 -0
  205. package/dist/src/middleware/index.d.ts.map +1 -0
  206. package/dist/src/middleware/index.js +3 -0
  207. package/dist/src/middleware/index.js.map +1 -0
  208. package/dist/src/middleware/registry.d.ts +58 -0
  209. package/dist/src/middleware/registry.d.ts.map +1 -0
  210. package/dist/src/middleware/registry.js +107 -0
  211. package/dist/src/middleware/registry.js.map +1 -0
  212. package/dist/src/middleware/stages.d.ts +145 -0
  213. package/dist/src/middleware/stages.d.ts.map +1 -0
  214. package/dist/src/middleware/stages.js +34 -0
  215. package/dist/src/middleware/stages.js.map +1 -0
  216. package/dist/src/middleware/types.d.ts +88 -0
  217. package/dist/src/middleware/types.d.ts.map +1 -0
  218. package/dist/src/middleware/types.js +2 -0
  219. package/dist/src/middleware/types.js.map +1 -0
  220. package/dist/src/models/__tests__/anthropic.test.js +16 -1
  221. package/dist/src/models/__tests__/anthropic.test.js.map +1 -1
  222. package/dist/src/models/__tests__/bedrock.test.js +39 -0
  223. package/dist/src/models/__tests__/bedrock.test.js.map +1 -1
  224. package/dist/src/models/__tests__/model.test.js +46 -3
  225. package/dist/src/models/__tests__/model.test.js.map +1 -1
  226. package/dist/src/models/anthropic.js +2 -2
  227. package/dist/src/models/anthropic.js.map +1 -1
  228. package/dist/src/models/bedrock.d.ts.map +1 -1
  229. package/dist/src/models/bedrock.js +12 -5
  230. package/dist/src/models/bedrock.js.map +1 -1
  231. package/dist/src/models/defaults.d.ts.map +1 -1
  232. package/dist/src/models/defaults.js +2 -0
  233. package/dist/src/models/defaults.js.map +1 -1
  234. package/dist/src/models/model.d.ts.map +1 -1
  235. package/dist/src/models/model.js +7 -3
  236. package/dist/src/models/model.js.map +1 -1
  237. package/dist/src/models/openai/__tests__/responses.test.js +8 -14
  238. package/dist/src/models/openai/__tests__/responses.test.js.map +1 -1
  239. package/dist/src/sandbox/__tests__/default.test.browser.d.ts +2 -0
  240. package/dist/src/sandbox/__tests__/default.test.browser.d.ts.map +1 -0
  241. package/dist/src/sandbox/__tests__/default.test.browser.js +11 -0
  242. package/dist/src/sandbox/__tests__/default.test.browser.js.map +1 -0
  243. package/dist/src/sandbox/__tests__/default.test.node.d.ts +2 -0
  244. package/dist/src/sandbox/__tests__/default.test.node.d.ts.map +1 -0
  245. package/dist/src/sandbox/__tests__/default.test.node.js +23 -0
  246. package/dist/src/sandbox/__tests__/default.test.node.js.map +1 -0
  247. package/dist/src/sandbox/__tests__/docker.test.node.d.ts +2 -0
  248. package/dist/src/sandbox/__tests__/docker.test.node.d.ts.map +1 -0
  249. package/dist/src/sandbox/__tests__/docker.test.node.js +89 -0
  250. package/dist/src/sandbox/__tests__/docker.test.node.js.map +1 -0
  251. package/dist/src/sandbox/__tests__/errors.test.node.d.ts +2 -0
  252. package/dist/src/sandbox/__tests__/errors.test.node.d.ts.map +1 -0
  253. package/dist/src/sandbox/__tests__/errors.test.node.js +33 -0
  254. package/dist/src/sandbox/__tests__/errors.test.node.js.map +1 -0
  255. package/dist/src/sandbox/__tests__/not-a-sandbox-local-environment.test.node.d.ts +2 -0
  256. package/dist/src/sandbox/__tests__/not-a-sandbox-local-environment.test.node.d.ts.map +1 -0
  257. package/dist/src/sandbox/__tests__/not-a-sandbox-local-environment.test.node.js +124 -0
  258. package/dist/src/sandbox/__tests__/not-a-sandbox-local-environment.test.node.js.map +1 -0
  259. package/dist/src/sandbox/__tests__/posix-shell.test.node.js +50 -4
  260. package/dist/src/sandbox/__tests__/posix-shell.test.node.js.map +1 -1
  261. package/dist/src/sandbox/__tests__/ssh.test.node.d.ts +2 -0
  262. package/dist/src/sandbox/__tests__/ssh.test.node.d.ts.map +1 -0
  263. package/dist/src/sandbox/__tests__/ssh.test.node.js +262 -0
  264. package/dist/src/sandbox/__tests__/ssh.test.node.js.map +1 -0
  265. package/dist/src/sandbox/base.d.ts +17 -4
  266. package/dist/src/sandbox/base.d.ts.map +1 -1
  267. package/dist/src/sandbox/base.js +10 -2
  268. package/dist/src/sandbox/base.js.map +1 -1
  269. package/dist/src/sandbox/constants.d.ts +18 -0
  270. package/dist/src/sandbox/constants.d.ts.map +1 -1
  271. package/dist/src/sandbox/constants.js +20 -0
  272. package/dist/src/sandbox/constants.js.map +1 -1
  273. package/dist/src/sandbox/default.d.ts +3 -0
  274. package/dist/src/sandbox/default.d.ts.map +1 -0
  275. package/dist/src/sandbox/default.js +3 -0
  276. package/dist/src/sandbox/default.js.map +1 -0
  277. package/dist/src/sandbox/docker.d.ts +38 -0
  278. package/dist/src/sandbox/docker.d.ts.map +1 -0
  279. package/dist/src/sandbox/docker.js +61 -0
  280. package/dist/src/sandbox/docker.js.map +1 -0
  281. package/dist/src/sandbox/errors.d.ts +26 -0
  282. package/dist/src/sandbox/errors.d.ts.map +1 -0
  283. package/dist/src/sandbox/errors.js +35 -0
  284. package/dist/src/sandbox/errors.js.map +1 -0
  285. package/dist/src/sandbox/index.d.ts +5 -0
  286. package/dist/src/sandbox/index.d.ts.map +1 -0
  287. package/dist/src/sandbox/index.js +4 -0
  288. package/dist/src/sandbox/index.js.map +1 -0
  289. package/dist/src/sandbox/not-a-sandbox-local-environment.d.ts +16 -0
  290. package/dist/src/sandbox/not-a-sandbox-local-environment.d.ts.map +1 -0
  291. package/dist/src/sandbox/not-a-sandbox-local-environment.js +83 -0
  292. package/dist/src/sandbox/not-a-sandbox-local-environment.js.map +1 -0
  293. package/dist/src/sandbox/posix-shell.d.ts +21 -0
  294. package/dist/src/sandbox/posix-shell.d.ts.map +1 -1
  295. package/dist/src/sandbox/posix-shell.js +41 -3
  296. package/dist/src/sandbox/posix-shell.js.map +1 -1
  297. package/dist/src/sandbox/ssh.d.ts +56 -0
  298. package/dist/src/sandbox/ssh.d.ts.map +1 -0
  299. package/dist/src/sandbox/ssh.js +121 -0
  300. package/dist/src/sandbox/ssh.js.map +1 -0
  301. package/dist/src/sandbox/stream-process.d.ts.map +1 -1
  302. package/dist/src/sandbox/stream-process.js +3 -2
  303. package/dist/src/sandbox/stream-process.js.map +1 -1
  304. package/dist/src/tsconfig.tsbuildinfo +1 -1
  305. package/dist/src/types/agent.d.ts +21 -0
  306. package/dist/src/types/agent.d.ts.map +1 -1
  307. package/dist/src/types/agent.js.map +1 -1
  308. package/dist/src/types/messages.d.ts +9 -1
  309. package/dist/src/types/messages.d.ts.map +1 -1
  310. package/dist/src/types/messages.js +13 -1
  311. package/dist/src/types/messages.js.map +1 -1
  312. package/dist/src/vended-interventions/cedar/__tests__/cedar.test.node.d.ts +2 -0
  313. package/dist/src/vended-interventions/cedar/__tests__/cedar.test.node.d.ts.map +1 -0
  314. package/dist/src/vended-interventions/cedar/__tests__/cedar.test.node.js +675 -0
  315. package/dist/src/vended-interventions/cedar/__tests__/cedar.test.node.js.map +1 -0
  316. package/dist/src/vended-interventions/cedar/cedar.d.ts +102 -0
  317. package/dist/src/vended-interventions/cedar/cedar.d.ts.map +1 -0
  318. package/dist/src/vended-interventions/cedar/cedar.js +228 -0
  319. package/dist/src/vended-interventions/cedar/cedar.js.map +1 -0
  320. package/dist/src/vended-interventions/cedar/index.d.ts +3 -0
  321. package/dist/src/vended-interventions/cedar/index.d.ts.map +1 -0
  322. package/dist/src/vended-interventions/cedar/index.js +2 -0
  323. package/dist/src/vended-interventions/cedar/index.js.map +1 -0
  324. package/dist/src/vended-interventions/cedar/schema-generator.d.ts +10 -0
  325. package/dist/src/vended-interventions/cedar/schema-generator.d.ts.map +1 -0
  326. package/dist/src/vended-interventions/cedar/schema-generator.js +33 -0
  327. package/dist/src/vended-interventions/cedar/schema-generator.js.map +1 -0
  328. package/dist/src/vended-memory-stores/bedrock-knowledge-base/__tests__/bedrock-knowledge-base-store.test.d.ts +2 -0
  329. package/dist/src/vended-memory-stores/bedrock-knowledge-base/__tests__/bedrock-knowledge-base-store.test.d.ts.map +1 -0
  330. package/dist/src/vended-memory-stores/bedrock-knowledge-base/__tests__/bedrock-knowledge-base-store.test.js +611 -0
  331. package/dist/src/vended-memory-stores/bedrock-knowledge-base/__tests__/bedrock-knowledge-base-store.test.js.map +1 -0
  332. package/dist/src/vended-memory-stores/bedrock-knowledge-base/index.d.ts +2 -0
  333. package/dist/src/vended-memory-stores/bedrock-knowledge-base/index.d.ts.map +1 -0
  334. package/dist/src/vended-memory-stores/bedrock-knowledge-base/index.js +2 -0
  335. package/dist/src/vended-memory-stores/bedrock-knowledge-base/index.js.map +1 -0
  336. package/dist/src/vended-memory-stores/bedrock-knowledge-base/store.d.ts +230 -0
  337. package/dist/src/vended-memory-stores/bedrock-knowledge-base/store.d.ts.map +1 -0
  338. package/dist/src/vended-memory-stores/bedrock-knowledge-base/store.js +370 -0
  339. package/dist/src/vended-memory-stores/bedrock-knowledge-base/store.js.map +1 -0
  340. package/dist/src/vended-plugins/context-injector/__tests__/plugin.test.d.ts +2 -0
  341. package/dist/src/vended-plugins/context-injector/__tests__/plugin.test.d.ts.map +1 -0
  342. package/dist/src/vended-plugins/context-injector/__tests__/plugin.test.js +96 -0
  343. package/dist/src/vended-plugins/context-injector/__tests__/plugin.test.js.map +1 -0
  344. package/dist/src/vended-plugins/context-injector/index.d.ts +25 -0
  345. package/dist/src/vended-plugins/context-injector/index.d.ts.map +1 -0
  346. package/dist/src/vended-plugins/context-injector/index.js +23 -0
  347. package/dist/src/vended-plugins/context-injector/index.js.map +1 -0
  348. package/dist/src/vended-plugins/context-injector/plugin.d.ts +55 -0
  349. package/dist/src/vended-plugins/context-injector/plugin.d.ts.map +1 -0
  350. package/dist/src/vended-plugins/context-injector/plugin.js +41 -0
  351. package/dist/src/vended-plugins/context-injector/plugin.js.map +1 -0
  352. package/dist/src/vended-plugins/context-offloader/__tests__/file-storage-sandbox.test.node.d.ts +2 -0
  353. package/dist/src/vended-plugins/context-offloader/__tests__/file-storage-sandbox.test.node.d.ts.map +1 -0
  354. package/dist/src/vended-plugins/context-offloader/__tests__/file-storage-sandbox.test.node.js +68 -0
  355. package/dist/src/vended-plugins/context-offloader/__tests__/file-storage-sandbox.test.node.js.map +1 -0
  356. package/dist/src/vended-plugins/context-offloader/__tests__/plugin.test.js +43 -4
  357. package/dist/src/vended-plugins/context-offloader/__tests__/plugin.test.js.map +1 -1
  358. package/dist/src/vended-plugins/context-offloader/__tests__/plugin.test.node.d.ts +2 -0
  359. package/dist/src/vended-plugins/context-offloader/__tests__/plugin.test.node.d.ts.map +1 -0
  360. package/dist/src/vended-plugins/context-offloader/__tests__/plugin.test.node.js +93 -0
  361. package/dist/src/vended-plugins/context-offloader/__tests__/plugin.test.node.js.map +1 -0
  362. package/dist/src/vended-plugins/context-offloader/__tests__/storage.test.js +68 -0
  363. package/dist/src/vended-plugins/context-offloader/__tests__/storage.test.js.map +1 -1
  364. package/dist/src/vended-plugins/context-offloader/index.d.ts +1 -1
  365. package/dist/src/vended-plugins/context-offloader/index.d.ts.map +1 -1
  366. package/dist/src/vended-plugins/context-offloader/plugin.d.ts +4 -1
  367. package/dist/src/vended-plugins/context-offloader/plugin.d.ts.map +1 -1
  368. package/dist/src/vended-plugins/context-offloader/plugin.js +40 -8
  369. package/dist/src/vended-plugins/context-offloader/plugin.js.map +1 -1
  370. package/dist/src/vended-plugins/context-offloader/search.d.ts.map +1 -1
  371. package/dist/src/vended-plugins/context-offloader/search.js +3 -5
  372. package/dist/src/vended-plugins/context-offloader/search.js.map +1 -1
  373. package/dist/src/vended-plugins/context-offloader/storage.d.ts +58 -6
  374. package/dist/src/vended-plugins/context-offloader/storage.d.ts.map +1 -1
  375. package/dist/src/vended-plugins/context-offloader/storage.js +136 -14
  376. package/dist/src/vended-plugins/context-offloader/storage.js.map +1 -1
  377. package/dist/src/vended-plugins/goal/__tests__/plugin.test.d.ts +2 -0
  378. package/dist/src/vended-plugins/goal/__tests__/plugin.test.d.ts.map +1 -0
  379. package/dist/src/vended-plugins/goal/__tests__/plugin.test.js +736 -0
  380. package/dist/src/vended-plugins/goal/__tests__/plugin.test.js.map +1 -0
  381. package/dist/src/vended-plugins/goal/index.d.ts +21 -0
  382. package/dist/src/vended-plugins/goal/index.d.ts.map +1 -0
  383. package/dist/src/vended-plugins/goal/index.js +20 -0
  384. package/dist/src/vended-plugins/goal/index.js.map +1 -0
  385. package/dist/src/vended-plugins/goal/judge.d.ts +41 -0
  386. package/dist/src/vended-plugins/goal/judge.d.ts.map +1 -0
  387. package/dist/src/vended-plugins/goal/judge.js +92 -0
  388. package/dist/src/vended-plugins/goal/judge.js.map +1 -0
  389. package/dist/src/vended-plugins/goal/plugin.d.ts +214 -0
  390. package/dist/src/vended-plugins/goal/plugin.d.ts.map +1 -0
  391. package/dist/src/vended-plugins/goal/plugin.js +287 -0
  392. package/dist/src/vended-plugins/goal/plugin.js.map +1 -0
  393. package/dist/src/vended-plugins/index.d.ts +3 -1
  394. package/dist/src/vended-plugins/index.d.ts.map +1 -1
  395. package/dist/src/vended-plugins/index.js +3 -1
  396. package/dist/src/vended-plugins/index.js.map +1 -1
  397. package/dist/src/vended-plugins/skills/__tests__/agent-skills.test.node.js +17 -7
  398. package/dist/src/vended-plugins/skills/__tests__/agent-skills.test.node.js.map +1 -1
  399. package/dist/src/vended-plugins/skills/agent-skills.d.ts +21 -7
  400. package/dist/src/vended-plugins/skills/agent-skills.d.ts.map +1 -1
  401. package/dist/src/vended-plugins/skills/agent-skills.js +144 -77
  402. package/dist/src/vended-plugins/skills/agent-skills.js.map +1 -1
  403. package/dist/src/vended-tools/bash/__tests__/bash.test.node.js +44 -4
  404. package/dist/src/vended-tools/bash/__tests__/bash.test.node.js.map +1 -1
  405. package/dist/src/vended-tools/bash/bash.d.ts +3 -24
  406. package/dist/src/vended-tools/bash/bash.d.ts.map +1 -1
  407. package/dist/src/vended-tools/bash/bash.js +9 -9
  408. package/dist/src/vended-tools/bash/bash.js.map +1 -1
  409. package/dist/src/vended-tools/bash/index.d.ts +3 -1
  410. package/dist/src/vended-tools/bash/index.d.ts.map +1 -1
  411. package/dist/src/vended-tools/bash/index.js +2 -1
  412. package/dist/src/vended-tools/bash/index.js.map +1 -1
  413. package/dist/src/vended-tools/bash/make-bash.d.ts +22 -0
  414. package/dist/src/vended-tools/bash/make-bash.d.ts.map +1 -0
  415. package/dist/src/vended-tools/bash/make-bash.js +40 -0
  416. package/dist/src/vended-tools/bash/make-bash.js.map +1 -0
  417. package/dist/src/vended-tools/bash/types.d.ts +1 -0
  418. package/dist/src/vended-tools/bash/types.d.ts.map +1 -1
  419. package/dist/src/vended-tools/bash/types.js +2 -0
  420. package/dist/src/vended-tools/bash/types.js.map +1 -1
  421. package/dist/src/vended-tools/file-editor/__tests__/file-editor.test.node.js +83 -1
  422. package/dist/src/vended-tools/file-editor/__tests__/file-editor.test.node.js.map +1 -1
  423. package/dist/src/vended-tools/file-editor/file-editor.d.ts +19 -10
  424. package/dist/src/vended-tools/file-editor/file-editor.d.ts.map +1 -1
  425. package/dist/src/vended-tools/file-editor/file-editor.js +188 -218
  426. package/dist/src/vended-tools/file-editor/file-editor.js.map +1 -1
  427. package/dist/src/vended-tools/file-editor/index.d.ts +2 -1
  428. package/dist/src/vended-tools/file-editor/index.d.ts.map +1 -1
  429. package/dist/src/vended-tools/file-editor/index.js +1 -1
  430. package/dist/src/vended-tools/file-editor/index.js.map +1 -1
  431. package/package.json +59 -6
  432. package/dist/src/utils/shell-quote.d.ts +0 -12
  433. package/dist/src/utils/shell-quote.d.ts.map +0 -1
  434. package/dist/src/utils/shell-quote.js +0 -14
  435. package/dist/src/utils/shell-quote.js.map +0 -1
@@ -0,0 +1,736 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { GoalLoop } from '../plugin.js';
3
+ import { buildJudgePrompt, JUDGE_OUTCOME_SCHEMA } from '../judge.js';
4
+ import { Agent } from '../../../agent/agent.js';
5
+ import { AfterInvocationEvent } from '../../../hooks/events.js';
6
+ import { MockMessageModel } from '../../../__fixtures__/mock-message-model.js';
7
+ import { JsonBlock, Message, TextBlock, ToolResultBlock, ToolUseBlock } from '../../../types/messages.js';
8
+ import { logger } from '../../../logging/logger.js';
9
+ describe('GoalLoop', () => {
10
+ describe('constructor', () => {
11
+ it('uses default name and unbounded budgets', () => {
12
+ const warnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => { });
13
+ const p = new GoalLoop({ goal: () => true, name: 'unique-defaults-test' });
14
+ expect(p.name).toBe('unique-defaults-test');
15
+ expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('execution is unbounded'));
16
+ warnSpy.mockRestore();
17
+ });
18
+ it('accepts custom name', () => {
19
+ const p = new GoalLoop({ goal: () => true, name: 'my:goal' });
20
+ expect(p.name).toBe('my:goal');
21
+ });
22
+ it('throws when goal is not provided', () => {
23
+ expect(() => new GoalLoop({})).toThrow(/`goal` is required/);
24
+ });
25
+ it('throws when maxAttempts < 1', () => {
26
+ expect(() => new GoalLoop({ goal: () => true, maxAttempts: 0 })).toThrow(/maxAttempts/);
27
+ });
28
+ it('throws when timeout < 1', () => {
29
+ expect(() => new GoalLoop({ goal: () => true, timeout: 0 })).toThrow(/timeout/);
30
+ });
31
+ it('does not warn when maxAttempts is set', () => {
32
+ const warnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => { });
33
+ new GoalLoop({ goal: () => true, maxAttempts: 5, name: 'bounded-by-attempts' });
34
+ expect(warnSpy).not.toHaveBeenCalled();
35
+ warnSpy.mockRestore();
36
+ });
37
+ });
38
+ describe('function validator', () => {
39
+ it('passes on first attempt with no resume', async () => {
40
+ const model = new MockMessageModel().addTurn({ type: 'textBlock', text: 'first' });
41
+ const validator = vi.fn(() => true);
42
+ const plugin = new GoalLoop({ goal: validator, maxAttempts: 5, name: 'fn-pass-first' });
43
+ const agent = new Agent({ model, plugins: [plugin], printer: false });
44
+ const result = await agent.invoke('do it');
45
+ expect(validator).toHaveBeenCalledTimes(1);
46
+ expect(result.lastMessage.content[0]).toEqual({ type: 'textBlock', text: 'first' });
47
+ expect(plugin.lastResult(agent)).toEqual({
48
+ passed: true,
49
+ stopReason: 'satisfied',
50
+ attempts: [{ attempt: 1, passed: true }],
51
+ });
52
+ });
53
+ it('feeds feedback as user message and re-invokes until passing', async () => {
54
+ const model = new MockMessageModel()
55
+ .addTurn({ type: 'textBlock', text: 'too long' })
56
+ .addTurn({ type: 'textBlock', text: 'still too long' })
57
+ .addTurn({ type: 'textBlock', text: 'ok' });
58
+ let n = 0;
59
+ const plugin = new GoalLoop({
60
+ name: 'fn-feedback-loop',
61
+ goal: () => {
62
+ n++;
63
+ return n === 3 || { passed: false, feedback: `attempt ${n} too long` };
64
+ },
65
+ maxAttempts: 5,
66
+ });
67
+ const agent = new Agent({ model, plugins: [plugin], printer: false });
68
+ await agent.invoke('summarise');
69
+ expect(model.callCount).toBe(3);
70
+ expect(plugin.lastResult(agent)).toEqual({
71
+ passed: true,
72
+ stopReason: 'satisfied',
73
+ attempts: [
74
+ { attempt: 1, passed: false, feedback: 'attempt 1 too long' },
75
+ { attempt: 2, passed: false, feedback: 'attempt 2 too long' },
76
+ { attempt: 3, passed: true },
77
+ ],
78
+ });
79
+ const userTexts = agent.messages
80
+ .filter((m) => m.role === 'user')
81
+ .flatMap((m) => m.content)
82
+ .flatMap((b) => (b.type === 'textBlock' ? [b.text] : []));
83
+ expect(userTexts.some((t) => t.includes('attempt 1 too long'))).toBe(true);
84
+ expect(userTexts.some((t) => t.includes('attempt 2 too long'))).toBe(true);
85
+ });
86
+ it('hits maxAttempts and surfaces lastResult without throwing', async () => {
87
+ const model = new MockMessageModel()
88
+ .addTurn({ type: 'textBlock', text: 'a' })
89
+ .addTurn({ type: 'textBlock', text: 'b' })
90
+ .addTurn({ type: 'textBlock', text: 'c' });
91
+ const plugin = new GoalLoop({
92
+ name: 'fn-maxattempts',
93
+ goal: () => ({ passed: false, feedback: 'nope' }),
94
+ maxAttempts: 3,
95
+ });
96
+ const agent = new Agent({ model, plugins: [plugin], printer: false });
97
+ await agent.invoke('go');
98
+ expect(model.callCount).toBe(3);
99
+ expect(plugin.lastResult(agent)).toEqual({
100
+ passed: false,
101
+ stopReason: 'maxAttempts',
102
+ attempts: [
103
+ { attempt: 1, passed: false, feedback: 'nope' },
104
+ { attempt: 2, passed: false, feedback: 'nope' },
105
+ { attempt: 3, passed: false, feedback: 'nope' },
106
+ ],
107
+ });
108
+ });
109
+ it('treats validator throws as a failed attempt and warns to surface buggy validators', async () => {
110
+ const model = new MockMessageModel()
111
+ .addTurn({ type: 'textBlock', text: 'a' })
112
+ .addTurn({ type: 'textBlock', text: 'b' });
113
+ const warnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => { });
114
+ let n = 0;
115
+ const plugin = new GoalLoop({
116
+ name: 'fn-throws',
117
+ goal: () => {
118
+ n++;
119
+ if (n === 1)
120
+ throw new Error('boom');
121
+ return true;
122
+ },
123
+ maxAttempts: 3,
124
+ });
125
+ const agent = new Agent({ model, plugins: [plugin], printer: false });
126
+ await agent.invoke('go');
127
+ expect(plugin.lastResult(agent)).toEqual({
128
+ passed: true,
129
+ stopReason: 'satisfied',
130
+ attempts: [
131
+ { attempt: 1, passed: false, feedback: 'Validator error: boom' },
132
+ { attempt: 2, passed: true },
133
+ ],
134
+ });
135
+ expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('fn-throws: validator threw: boom'));
136
+ warnSpy.mockRestore();
137
+ });
138
+ it('awaits async validator', async () => {
139
+ const model = new MockMessageModel()
140
+ .addTurn({ type: 'textBlock', text: 'a' })
141
+ .addTurn({ type: 'textBlock', text: 'b' });
142
+ let n = 0;
143
+ const plugin = new GoalLoop({
144
+ name: 'fn-async',
145
+ goal: async () => {
146
+ n++;
147
+ const localN = n;
148
+ await new Promise((r) => setTimeout(r, 1));
149
+ return localN === 2;
150
+ },
151
+ maxAttempts: 5,
152
+ });
153
+ const agent = new Agent({ model, plugins: [plugin], printer: false });
154
+ await agent.invoke('go');
155
+ expect(plugin.lastResult(agent)).toEqual({
156
+ passed: true,
157
+ stopReason: 'satisfied',
158
+ attempts: [
159
+ { attempt: 1, passed: false },
160
+ { attempt: 2, passed: true },
161
+ ],
162
+ });
163
+ });
164
+ it('exposes the assistant message to the validator', async () => {
165
+ const model = new MockMessageModel().addTurn({ type: 'textBlock', text: 'hello world' });
166
+ const seen = [];
167
+ const plugin = new GoalLoop({
168
+ name: 'fn-receives-message',
169
+ goal: (response) => {
170
+ seen.push(response);
171
+ return true;
172
+ },
173
+ maxAttempts: 3,
174
+ });
175
+ const agent = new Agent({ model, plugins: [plugin], printer: false });
176
+ await agent.invoke('go');
177
+ expect(seen).toHaveLength(1);
178
+ expect(seen[0].role).toBe('assistant');
179
+ expect(seen[0].content[0].text).toBe('hello world');
180
+ });
181
+ it('honours timeout by terminating before next validate', async () => {
182
+ vi.useFakeTimers();
183
+ try {
184
+ const model = new MockMessageModel()
185
+ .addTurn({ type: 'textBlock', text: 'a' })
186
+ .addTurn({ type: 'textBlock', text: 'b' });
187
+ let attemptCount = 0;
188
+ const plugin = new GoalLoop({
189
+ name: 'fn-timeout',
190
+ goal: async () => {
191
+ attemptCount++;
192
+ if (attemptCount === 1) {
193
+ await new Promise((resolve) => setTimeout(resolve, 30));
194
+ return { passed: false, feedback: 'try again' };
195
+ }
196
+ return true;
197
+ },
198
+ timeout: 10,
199
+ maxAttempts: 5,
200
+ });
201
+ const agent = new Agent({ model, plugins: [plugin], printer: false });
202
+ const invokePromise = agent.invoke('go');
203
+ await vi.runAllTimersAsync();
204
+ await invokePromise;
205
+ expect(plugin.lastResult(agent)).toEqual({
206
+ passed: false,
207
+ stopReason: 'timeout',
208
+ attempts: [{ attempt: 1, passed: false, feedback: 'try again' }],
209
+ });
210
+ expect(model.callCount).toBe(2);
211
+ }
212
+ finally {
213
+ vi.useRealTimers();
214
+ }
215
+ });
216
+ });
217
+ describe('lastResult lifecycle', () => {
218
+ it('is undefined before first invoke', () => {
219
+ const plugin = new GoalLoop({ goal: () => true, maxAttempts: 1, name: 'lr-untouched' });
220
+ const model = new MockMessageModel();
221
+ const agent = new Agent({ model, plugins: [plugin], printer: false });
222
+ expect(plugin.lastResult(agent)).toBeUndefined();
223
+ });
224
+ it('is replaced on each completed run', async () => {
225
+ const plugin = new GoalLoop({
226
+ name: 'lr-replaced',
227
+ goal: () => true,
228
+ maxAttempts: 1,
229
+ });
230
+ const m1 = new MockMessageModel().addTurn({ type: 'textBlock', text: 'one' });
231
+ const a1 = new Agent({ model: m1, plugins: [plugin], printer: false });
232
+ await a1.invoke('first');
233
+ const after1 = plugin.lastResult(a1);
234
+ expect(after1).toEqual({
235
+ passed: true,
236
+ stopReason: 'satisfied',
237
+ attempts: [{ attempt: 1, passed: true }],
238
+ });
239
+ m1.addTurn({ type: 'textBlock', text: 'two' });
240
+ await a1.invoke('second');
241
+ const after2 = plugin.lastResult(a1);
242
+ expect(after2).not.toBe(after1);
243
+ expect(after2).toEqual({
244
+ passed: true,
245
+ stopReason: 'satisfied',
246
+ attempts: [{ attempt: 1, passed: true }],
247
+ });
248
+ });
249
+ it('is undefined after a host throw mid-run', async () => {
250
+ const model = new MockMessageModel().addTurn(new Error('boom'));
251
+ const plugin = new GoalLoop({
252
+ name: 'lr-throw',
253
+ goal: () => true,
254
+ maxAttempts: 3,
255
+ });
256
+ const agent = new Agent({ model, plugins: [plugin], printer: false });
257
+ await expect(agent.invoke('go')).rejects.toThrow('boom');
258
+ expect(plugin.lastResult(agent)).toBeUndefined();
259
+ });
260
+ it('is undefined while a run is mid-flight (read between attempts via hook)', async () => {
261
+ const model = new MockMessageModel()
262
+ .addTurn({ type: 'textBlock', text: 'a' })
263
+ .addTurn({ type: 'textBlock', text: 'b' });
264
+ const observed = [];
265
+ let plugin;
266
+ plugin = new GoalLoop({
267
+ name: 'lr-midflight',
268
+ goal: (response, hostAgent) => {
269
+ observed.push(plugin.lastResult(hostAgent));
270
+ const text = response.content[0].text;
271
+ return text === 'b';
272
+ },
273
+ maxAttempts: 5,
274
+ });
275
+ const agent = new Agent({ model, plugins: [plugin], printer: false });
276
+ await agent.invoke('go');
277
+ expect(observed).toEqual([undefined, undefined]);
278
+ expect(plugin.lastResult(agent)).toEqual({
279
+ passed: true,
280
+ stopReason: 'satisfied',
281
+ attempts: [
282
+ { attempt: 1, passed: false },
283
+ { attempt: 2, passed: true },
284
+ ],
285
+ });
286
+ });
287
+ });
288
+ describe('conversation history', () => {
289
+ it('feedback messages accumulate as user-role messages between attempts', async () => {
290
+ const model = new MockMessageModel()
291
+ .addTurn({ type: 'textBlock', text: 'a1' })
292
+ .addTurn({ type: 'textBlock', text: 'a2' })
293
+ .addTurn({ type: 'textBlock', text: 'a3' });
294
+ let n = 0;
295
+ const plugin = new GoalLoop({
296
+ name: 'history-accumulates',
297
+ goal: () => {
298
+ n++;
299
+ return n === 3 ? true : { passed: false, feedback: `fb${n}` };
300
+ },
301
+ maxAttempts: 5,
302
+ });
303
+ const agent = new Agent({ model, plugins: [plugin], printer: false });
304
+ await agent.invoke('initial input');
305
+ const roles = agent.messages.map((m) => m.role);
306
+ expect(roles).toEqual(['user', 'assistant', 'user', 'assistant', 'user', 'assistant']);
307
+ });
308
+ });
309
+ describe('resumePromptTemplate override', () => {
310
+ it('replaces the default canned English with the user-supplied template', async () => {
311
+ const model = new MockMessageModel()
312
+ .addTurn({ type: 'textBlock', text: 'a' })
313
+ .addTurn({ type: 'textBlock', text: 'b' });
314
+ let n = 0;
315
+ const plugin = new GoalLoop({
316
+ name: 'custom-resume-template',
317
+ goal: () => {
318
+ n++;
319
+ return n === 2 || { passed: false, feedback: 'too long' };
320
+ },
321
+ maxAttempts: 3,
322
+ resumePromptTemplate: (fb) => `RETRY_TOKEN feedback=<${fb ?? 'none'}>`,
323
+ });
324
+ const agent = new Agent({ model, plugins: [plugin], printer: false });
325
+ await agent.invoke('go');
326
+ // The template's output must show up verbatim as a user message; the
327
+ // default English must NOT.
328
+ const userTexts = agent.messages
329
+ .filter((m) => m.role === 'user')
330
+ .flatMap((m) => m.content)
331
+ .flatMap((b) => (b.type === 'textBlock' ? [b.text] : []));
332
+ expect(userTexts.some((t) => t.includes('RETRY_TOKEN feedback=<too long>'))).toBe(true);
333
+ expect(userTexts.some((t) => t.includes('Refine your response'))).toBe(false);
334
+ });
335
+ });
336
+ describe('multiple plugin instances', () => {
337
+ it('shares one plugin across multiple agents with independent run state', async () => {
338
+ // Per-agent run state is keyed off the agent (WeakMap), so concurrent or
339
+ // sequential runs on different agents don't conflate results.
340
+ const m1 = new MockMessageModel()
341
+ .addTurn({ type: 'textBlock', text: 'a1-attempt-1' })
342
+ .addTurn({ type: 'textBlock', text: 'a1-attempt-2' });
343
+ const m2 = new MockMessageModel().addTurn({ type: 'textBlock', text: 'a2-only' });
344
+ let n1 = 0;
345
+ const plugin = new GoalLoop({
346
+ name: 'shared',
347
+ goal: (response) => {
348
+ const text = response.content[0].text;
349
+ if (text.startsWith('a2'))
350
+ return true;
351
+ n1++;
352
+ return n1 === 2;
353
+ },
354
+ maxAttempts: 5,
355
+ });
356
+ const a1 = new Agent({ model: m1, plugins: [plugin], printer: false });
357
+ const a2 = new Agent({ model: m2, plugins: [plugin], printer: false });
358
+ await a1.invoke('first');
359
+ await a2.invoke('second');
360
+ expect(plugin.lastResult(a1)).toEqual({
361
+ passed: true,
362
+ stopReason: 'satisfied',
363
+ attempts: [
364
+ { attempt: 1, passed: false },
365
+ { attempt: 2, passed: true },
366
+ ],
367
+ });
368
+ expect(plugin.lastResult(a2)).toEqual({
369
+ passed: true,
370
+ stopReason: 'satisfied',
371
+ attempts: [{ attempt: 1, passed: true }],
372
+ });
373
+ });
374
+ it('throws when two GoalLoops are attached to the same agent', async () => {
375
+ // Two GoalLoops both write `event.resume` in AfterInvocationEvent — last
376
+ // writer wins, silently dropping one's feedback. Compose constraints in a
377
+ // single validator function instead.
378
+ const model = new MockMessageModel().addTurn({ type: 'textBlock', text: 'x' });
379
+ const a = new GoalLoop({ goal: () => true, maxAttempts: 1, name: 'first' });
380
+ const b = new GoalLoop({ goal: () => true, maxAttempts: 1, name: 'second' });
381
+ const agent = new Agent({ model, plugins: [a, b], printer: false });
382
+ await expect(agent.invoke('go')).rejects.toThrow(/another GoalLoop is already attached/);
383
+ });
384
+ });
385
+ describe('preserveContext: false', () => {
386
+ it('restores the post-input transcript between attempts; agent never sees prior attempts', async () => {
387
+ const model = new MockMessageModel()
388
+ .addTurn({ type: 'textBlock', text: 'attempt-1' })
389
+ .addTurn({ type: 'textBlock', text: 'attempt-2' })
390
+ .addTurn({ type: 'textBlock', text: 'attempt-3' });
391
+ const messageSnapshots = [];
392
+ let n = 0;
393
+ const plugin = new GoalLoop({
394
+ name: 'fresh-context',
395
+ goal: () => {
396
+ n++;
397
+ return n === 3 ? true : { passed: false, feedback: `fb${n}` };
398
+ },
399
+ maxAttempts: 5,
400
+ preserveContext: false,
401
+ });
402
+ const agent = new Agent({ model, plugins: [plugin], printer: false });
403
+ // Spy on stream to capture the messages array sent to the model on each
404
+ // attempt. With fresh-context on, the model should never see prior
405
+ // assistant attempts in the transcript.
406
+ const originalStream = model.stream.bind(model);
407
+ vi.spyOn(model, 'stream').mockImplementation(async function* (messages, options) {
408
+ messageSnapshots.push(messages.map((m) => ({
409
+ role: m.role,
410
+ text: m.content.flatMap((b) => (b.type === 'textBlock' ? [b.text] : [])).join(''),
411
+ })));
412
+ yield* originalStream(messages, options);
413
+ });
414
+ await agent.invoke('do the thing');
415
+ expect(plugin.lastResult(agent)).toEqual({
416
+ passed: true,
417
+ stopReason: 'satisfied',
418
+ attempts: [
419
+ { attempt: 1, passed: false, feedback: 'fb1' },
420
+ { attempt: 2, passed: false, feedback: 'fb2' },
421
+ { attempt: 3, passed: true },
422
+ ],
423
+ });
424
+ // Each attempt's transcript: clean input + accumulated feedback only.
425
+ // The model must never see prior `attempt-N` assistant turns.
426
+ expect(messageSnapshots).toEqual([
427
+ [{ role: 'user', text: 'do the thing' }],
428
+ [
429
+ { role: 'user', text: 'do the thing' },
430
+ { role: 'user', text: expect.stringContaining('fb1') },
431
+ ],
432
+ [
433
+ { role: 'user', text: 'do the thing' },
434
+ { role: 'user', text: expect.stringContaining('fb2') },
435
+ ],
436
+ ]);
437
+ });
438
+ it('does not roll back appState mutations made during attempts', async () => {
439
+ // Locks in the contract: Ralph mode rewinds *only* messages. Other
440
+ // session-scope state (appState, modelState, systemPrompt, interrupts)
441
+ // accumulates across attempts so other plugins are unaffected.
442
+ const model = new MockMessageModel()
443
+ .addTurn({ type: 'textBlock', text: 'attempt-1' })
444
+ .addTurn({ type: 'textBlock', text: 'attempt-2' });
445
+ let n = 0;
446
+ const plugin = new GoalLoop({
447
+ name: 'fresh-context-appstate',
448
+ goal: () => {
449
+ n++;
450
+ return n === 2 || { passed: false, feedback: 'retry' };
451
+ },
452
+ maxAttempts: 3,
453
+ preserveContext: false,
454
+ });
455
+ const agent = new Agent({ model, plugins: [plugin], printer: false });
456
+ // Mutate appState from a hook that runs between attempts to confirm
457
+ // the mutation survives the rewind.
458
+ agent.appState.set('counter', 0);
459
+ agent.addHook(AfterInvocationEvent, () => {
460
+ const current = agent.appState.get('counter') ?? 0;
461
+ agent.appState.set('counter', current + 1);
462
+ });
463
+ await agent.invoke('go');
464
+ // counter incremented once per AfterInvocationEvent call (one per attempt).
465
+ expect(agent.appState.get('counter')).toBe(2);
466
+ });
467
+ it('preserves context by default (agent sees prior assistant attempts)', async () => {
468
+ const model = new MockMessageModel()
469
+ .addTurn({ type: 'textBlock', text: 'attempt-1' })
470
+ .addTurn({ type: 'textBlock', text: 'attempt-2' });
471
+ let n = 0;
472
+ const plugin = new GoalLoop({
473
+ name: 'fresh-context-off',
474
+ goal: () => {
475
+ n++;
476
+ return n === 2 || { passed: false, feedback: 'fb' };
477
+ },
478
+ maxAttempts: 3,
479
+ });
480
+ const agent = new Agent({ model, plugins: [plugin], printer: false });
481
+ await agent.invoke('go');
482
+ // Default behavior: attempt 2's transcript includes attempt-1's assistant turn.
483
+ const roles = agent.messages.map((m) => m.role);
484
+ expect(roles).toEqual(['user', 'assistant', 'user', 'assistant']);
485
+ });
486
+ });
487
+ describe('cross-invocation lastResult lifecycle', () => {
488
+ it('clears stale lastResult after a thrown invoke when a fresh invoke starts', async () => {
489
+ const model = new MockMessageModel().addTurn(new Error('boom')).addTurn({ type: 'textBlock', text: 'ok' });
490
+ const plugin = new GoalLoop({
491
+ name: 'throw-then-clean',
492
+ goal: () => true,
493
+ maxAttempts: 3,
494
+ });
495
+ const agent = new Agent({ model, plugins: [plugin], printer: false });
496
+ await expect(agent.invoke('first')).rejects.toThrow('boom');
497
+ expect(plugin.lastResult(agent)).toBeUndefined();
498
+ await agent.invoke('second');
499
+ expect(plugin.lastResult(agent)).toEqual({
500
+ passed: true,
501
+ stopReason: 'satisfied',
502
+ attempts: [{ attempt: 1, passed: true }],
503
+ });
504
+ });
505
+ });
506
+ });
507
+ describe('judge helpers', () => {
508
+ it('JUDGE_OUTCOME_SCHEMA validates pass/fail outcome shapes', () => {
509
+ expect(JUDGE_OUTCOME_SCHEMA.parse({ passed: true })).toEqual({ passed: true });
510
+ expect(JUDGE_OUTCOME_SCHEMA.parse({ passed: false, feedback: 'x' })).toEqual({
511
+ passed: false,
512
+ feedback: 'x',
513
+ });
514
+ expect(() => JUDGE_OUTCOME_SCHEMA.parse({ passed: 'yes' })).toThrow();
515
+ });
516
+ it('buildJudgePrompt embeds the goal description and a role-tagged transcript', () => {
517
+ const prompt = buildJudgePrompt('be concise', [
518
+ new Message({ role: 'user', content: [new TextBlock('hello?')] }),
519
+ new Message({ role: 'assistant', content: [new TextBlock('hi there')] }),
520
+ ]);
521
+ expect(prompt).toContain('Goal:\nbe concise');
522
+ expect(prompt).toContain('[user]\nhello?');
523
+ expect(prompt).toContain('[assistant]\nhi there');
524
+ });
525
+ it('buildJudgePrompt summarises tool calls and results so the judge can grade tool-using agents', () => {
526
+ const prompt = buildJudgePrompt('ran the tests', [
527
+ new Message({ role: 'user', content: [new TextBlock('run the tests')] }),
528
+ new Message({
529
+ role: 'assistant',
530
+ content: [new ToolUseBlock({ name: 'shell', toolUseId: 't1', input: { cmd: 'npm test' } })],
531
+ }),
532
+ new Message({
533
+ role: 'user',
534
+ content: [
535
+ new ToolResultBlock({
536
+ toolUseId: 't1',
537
+ status: 'success',
538
+ content: [new TextBlock('all green')],
539
+ }),
540
+ ],
541
+ }),
542
+ new Message({
543
+ role: 'assistant',
544
+ content: [new ToolUseBlock({ name: 'shell', toolUseId: 't2', input: { cmd: 'echo done' } })],
545
+ }),
546
+ new Message({
547
+ role: 'user',
548
+ content: [
549
+ new ToolResultBlock({
550
+ toolUseId: 't2',
551
+ status: 'error',
552
+ content: [new JsonBlock({ json: { code: 1 } })],
553
+ }),
554
+ ],
555
+ }),
556
+ ]);
557
+ expect(prompt).toContain('[tool-call: shell] input={"cmd":"npm test"}');
558
+ expect(prompt).toContain('[tool-result: success] all green');
559
+ expect(prompt).toContain('[tool-call: shell] input={"cmd":"echo done"}');
560
+ expect(prompt).toContain('[tool-result: error] {"code":1}');
561
+ });
562
+ it('buildJudgePrompt truncates very long tool inputs and outputs', () => {
563
+ const longInput = 'x'.repeat(2000);
564
+ const prompt = buildJudgePrompt('ok', [
565
+ new Message({
566
+ role: 'assistant',
567
+ content: [new ToolUseBlock({ name: 'shell', toolUseId: 't1', input: { data: longInput } })],
568
+ }),
569
+ ]);
570
+ expect(prompt).toContain('… [');
571
+ expect(prompt).toContain('more chars]');
572
+ expect(prompt.length).toBeLessThan(longInput.length);
573
+ });
574
+ });
575
+ // String-validator (NL judge) integration tests use the same MockMessageModel
576
+ // pattern: the host agent's model is mocked, and the judge agent — which the
577
+ // plugin builds internally — shares that mock model. Each `judge.invoke` consumes
578
+ // a turn from the model, returning a `strands_structured_output` tool use that the
579
+ // agent loop validates against JUDGE_OUTCOME_SCHEMA.
580
+ describe('GoalLoop natural-language judge', () => {
581
+ let judgeCallCount = 0;
582
+ function buildJudgeTurn(passed, feedback) {
583
+ const input = feedback !== undefined ? { passed, feedback } : { passed };
584
+ return [
585
+ {
586
+ type: 'toolUseBlock',
587
+ name: 'strands_structured_output',
588
+ toolUseId: `judge-${++judgeCallCount}`,
589
+ input,
590
+ },
591
+ ];
592
+ }
593
+ it('judges via the host agent model by default; passing on first attempt', async () => {
594
+ const model = new MockMessageModel()
595
+ .addTurn({ type: 'textBlock', text: 'rainbows are pretty' })
596
+ .addTurn(...buildJudgeTurn(true));
597
+ const plugin = new GoalLoop({
598
+ name: 'nl-default-pass',
599
+ goal: 'be concise',
600
+ maxAttempts: 3,
601
+ });
602
+ const agent = new Agent({ model, plugins: [plugin], printer: false });
603
+ await agent.invoke('explain rainbows');
604
+ expect(plugin.lastResult(agent)).toEqual({
605
+ passed: true,
606
+ stopReason: 'satisfied',
607
+ attempts: [{ attempt: 1, passed: true }],
608
+ });
609
+ });
610
+ it('feeds judge feedback back to the host agent', async () => {
611
+ const model = new MockMessageModel()
612
+ .addTurn({ type: 'textBlock', text: 'first try (long)' })
613
+ .addTurn(...buildJudgeTurn(false, 'too long'))
614
+ .addTurn({ type: 'textBlock', text: 'second try (short)' })
615
+ .addTurn(...buildJudgeTurn(true));
616
+ const plugin = new GoalLoop({
617
+ name: 'nl-feedback',
618
+ goal: 'be concise',
619
+ maxAttempts: 3,
620
+ });
621
+ const agent = new Agent({ model, plugins: [plugin], printer: false });
622
+ await agent.invoke('explain something');
623
+ expect(plugin.lastResult(agent)).toEqual({
624
+ passed: true,
625
+ stopReason: 'satisfied',
626
+ attempts: [
627
+ { attempt: 1, passed: false, feedback: 'too long' },
628
+ { attempt: 2, passed: true },
629
+ ],
630
+ });
631
+ const userTexts = agent.messages
632
+ .filter((m) => m.role === 'user')
633
+ .flatMap((m) => m.content)
634
+ .flatMap((b) => (b.type === 'textBlock' ? [b.text] : []));
635
+ expect(userTexts.some((t) => t.includes('too long'))).toBe(true);
636
+ });
637
+ it('passes when the judge replies with plain text first and the structured-output retry yields a tool call', async () => {
638
+ const model = new MockMessageModel()
639
+ .addTurn({ type: 'textBlock', text: 'response' })
640
+ .addTurn({ type: 'textBlock', text: 'judge says hi (no tool)' })
641
+ .addTurn(...buildJudgeTurn(true));
642
+ const plugin = new GoalLoop({
643
+ name: 'nl-judge-passes-on-retry',
644
+ goal: 'be concise',
645
+ maxAttempts: 3,
646
+ });
647
+ const agent = new Agent({ model, plugins: [plugin], printer: false });
648
+ await agent.invoke('go');
649
+ expect(plugin.lastResult(agent)).toEqual({
650
+ passed: true,
651
+ stopReason: 'satisfied',
652
+ attempts: [{ attempt: 1, passed: true }],
653
+ });
654
+ });
655
+ it('builds a fresh judge agent per validation, not leaking prior prompts', async () => {
656
+ const model = new MockMessageModel()
657
+ .addTurn({ type: 'textBlock', text: 'attempt-1-host' })
658
+ .addTurn(...buildJudgeTurn(false, 'fb-1'))
659
+ .addTurn({ type: 'textBlock', text: 'attempt-2-host' })
660
+ .addTurn(...buildJudgeTurn(true));
661
+ const messageLengths = [];
662
+ const originalStream = model.stream.bind(model);
663
+ vi.spyOn(model, 'stream').mockImplementation(async function* (messages, options) {
664
+ messageLengths.push(messages.length);
665
+ yield* originalStream(messages, options);
666
+ });
667
+ const plugin = new GoalLoop({
668
+ name: 'nl-fresh-judge-per-call',
669
+ goal: 'be concise',
670
+ maxAttempts: 3,
671
+ });
672
+ const agent = new Agent({ model, plugins: [plugin], printer: false });
673
+ await agent.invoke('initial');
674
+ expect(plugin.lastResult(agent)).toEqual({
675
+ passed: true,
676
+ stopReason: 'satisfied',
677
+ attempts: [
678
+ { attempt: 1, passed: false, feedback: 'fb-1' },
679
+ { attempt: 2, passed: true },
680
+ ],
681
+ });
682
+ // Stream calls in order: host-1 (1 msg), judge-1 (1 msg), host-2 (3 msgs:
683
+ // user+assistant+user-feedback), judge-2 (1 msg). The 1s on the judge calls
684
+ // confirm a fresh Agent — accumulated state would push the second to >1.
685
+ expect(messageLengths).toEqual([1, 1, 3, 1]);
686
+ });
687
+ it('uses judge.model override when provided (different model instance)', async () => {
688
+ const hostModel = new MockMessageModel().addTurn({ type: 'textBlock', text: 'response' });
689
+ const judgeModel = new MockMessageModel().addTurn(...buildJudgeTurn(true));
690
+ const hostSpy = vi.spyOn(hostModel, 'stream');
691
+ const judgeSpy = vi.spyOn(judgeModel, 'stream');
692
+ const plugin = new GoalLoop({
693
+ name: 'nl-override-model',
694
+ goal: 'be concise',
695
+ judge: { model: judgeModel },
696
+ maxAttempts: 1,
697
+ });
698
+ const agent = new Agent({ model: hostModel, plugins: [plugin], printer: false });
699
+ await agent.invoke('go');
700
+ expect(plugin.lastResult(agent)).toEqual({
701
+ passed: true,
702
+ stopReason: 'satisfied',
703
+ attempts: [{ attempt: 1, passed: true }],
704
+ });
705
+ expect(hostSpy).toHaveBeenCalledTimes(1);
706
+ expect(judgeSpy).toHaveBeenCalledTimes(1);
707
+ });
708
+ it('uses judge.systemPrompt override as the judge agent system prompt', async () => {
709
+ const model = new MockMessageModel()
710
+ .addTurn({ type: 'textBlock', text: 'response' })
711
+ .addTurn(...buildJudgeTurn(true));
712
+ // The judge agent is built internally; the only way it reaches the model is
713
+ // via the system prompt threaded into the stream call. Assert the override
714
+ // shows up there.
715
+ const systemPrompts = [];
716
+ const originalStream = model.stream.bind(model);
717
+ vi.spyOn(model, 'stream').mockImplementation(async function* (messages, options) {
718
+ systemPrompts.push(options?.systemPrompt);
719
+ yield* originalStream(messages, options);
720
+ });
721
+ const plugin = new GoalLoop({
722
+ name: 'nl-override-system-prompt',
723
+ goal: 'be concise',
724
+ judge: { systemPrompt: 'CUSTOM_JUDGE_RUBRIC_MARKER' },
725
+ maxAttempts: 1,
726
+ });
727
+ const agent = new Agent({ model, plugins: [plugin], printer: false });
728
+ await agent.invoke('go');
729
+ // Stream call order: host turn (no judge system prompt), then judge turn
730
+ // (carrying the override).
731
+ expect(systemPrompts).toContain('CUSTOM_JUDGE_RUBRIC_MARKER');
732
+ });
733
+ });
734
+ const _typeCheck = { attempt: 1, passed: true };
735
+ void _typeCheck;
736
+ //# sourceMappingURL=plugin.test.js.map