@namzu/sdk 0.6.0 → 1.1.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 (429) hide show
  1. package/CHANGELOG.md +529 -0
  2. package/dist/advisory/executor.d.ts.map +1 -1
  3. package/dist/advisory/executor.js +9 -2
  4. package/dist/advisory/executor.js.map +1 -1
  5. package/dist/advisory/executor.test.d.ts +2 -1
  6. package/dist/advisory/executor.test.d.ts.map +1 -1
  7. package/dist/advisory/executor.test.js +7 -4
  8. package/dist/advisory/executor.test.js.map +1 -1
  9. package/dist/agents/ReactiveAgent.d.ts.map +1 -1
  10. package/dist/agents/ReactiveAgent.js +7 -0
  11. package/dist/agents/ReactiveAgent.js.map +1 -1
  12. package/dist/agents/SupervisorAgent.d.ts +22 -1
  13. package/dist/agents/SupervisorAgent.d.ts.map +1 -1
  14. package/dist/agents/SupervisorAgent.js +54 -14
  15. package/dist/agents/SupervisorAgent.js.map +1 -1
  16. package/dist/agents/__tests__/supervisor-ledger.test.d.ts +2 -0
  17. package/dist/agents/__tests__/supervisor-ledger.test.d.ts.map +1 -0
  18. package/dist/agents/__tests__/supervisor-ledger.test.js +131 -0
  19. package/dist/agents/__tests__/supervisor-ledger.test.js.map +1 -0
  20. package/dist/bridge/sse/mapper.test.js +2 -2
  21. package/dist/config/runtime.d.ts +24 -0
  22. package/dist/config/runtime.d.ts.map +1 -1
  23. package/dist/config/runtime.js +9 -0
  24. package/dist/config/runtime.js.map +1 -1
  25. package/dist/connector/builtins/webhook.test.js +17 -0
  26. package/dist/connector/builtins/webhook.test.js.map +1 -1
  27. package/dist/connector/index.d.ts +1 -0
  28. package/dist/connector/index.d.ts.map +1 -1
  29. package/dist/connector/index.js +1 -0
  30. package/dist/connector/index.js.map +1 -1
  31. package/dist/connector/mcp/client.d.ts.map +1 -1
  32. package/dist/connector/mcp/client.js +4 -0
  33. package/dist/connector/mcp/client.js.map +1 -1
  34. package/dist/connector/mcp/index.d.ts +1 -0
  35. package/dist/connector/mcp/index.d.ts.map +1 -1
  36. package/dist/connector/mcp/index.js +1 -0
  37. package/dist/connector/mcp/index.js.map +1 -1
  38. package/dist/connector/mcp/streamable-http.d.ts +22 -0
  39. package/dist/connector/mcp/streamable-http.d.ts.map +1 -0
  40. package/dist/connector/mcp/streamable-http.js +128 -0
  41. package/dist/connector/mcp/streamable-http.js.map +1 -0
  42. package/dist/connector/mcp/streamable-http.test.d.ts +2 -0
  43. package/dist/connector/mcp/streamable-http.test.d.ts.map +1 -0
  44. package/dist/connector/mcp/streamable-http.test.js +132 -0
  45. package/dist/connector/mcp/streamable-http.test.js.map +1 -0
  46. package/dist/constants/compaction/index.d.ts.map +1 -1
  47. package/dist/constants/compaction/index.js +8 -3
  48. package/dist/constants/compaction/index.js.map +1 -1
  49. package/dist/constants/sandbox/index.d.ts +21 -0
  50. package/dist/constants/sandbox/index.d.ts.map +1 -1
  51. package/dist/constants/sandbox/index.js +30 -0
  52. package/dist/constants/sandbox/index.js.map +1 -1
  53. package/dist/constants/tools/index.d.ts.map +1 -1
  54. package/dist/constants/tools/index.js +33 -2
  55. package/dist/constants/tools/index.js.map +1 -1
  56. package/dist/manager/run/persistence.d.ts.map +1 -1
  57. package/dist/manager/run/persistence.js +35 -5
  58. package/dist/manager/run/persistence.js.map +1 -1
  59. package/dist/persona/assembler.d.ts +1 -0
  60. package/dist/persona/assembler.d.ts.map +1 -1
  61. package/dist/persona/assembler.js +37 -6
  62. package/dist/persona/assembler.js.map +1 -1
  63. package/dist/provider/collect.d.ts.map +1 -1
  64. package/dist/provider/collect.js +4 -1
  65. package/dist/provider/collect.js.map +1 -1
  66. package/dist/provider/collect.test.js +2 -2
  67. package/dist/public-runtime.d.ts +6 -5
  68. package/dist/public-runtime.d.ts.map +1 -1
  69. package/dist/public-runtime.js +6 -5
  70. package/dist/public-runtime.js.map +1 -1
  71. package/dist/public-tools.d.ts +2 -0
  72. package/dist/public-tools.d.ts.map +1 -1
  73. package/dist/public-tools.js +2 -0
  74. package/dist/public-tools.js.map +1 -1
  75. package/dist/public-types.d.ts +3 -0
  76. package/dist/public-types.d.ts.map +1 -1
  77. package/dist/registry/index.d.ts +2 -0
  78. package/dist/registry/index.d.ts.map +1 -1
  79. package/dist/registry/index.js +1 -0
  80. package/dist/registry/index.js.map +1 -1
  81. package/dist/registry/tool/execute.d.ts +27 -0
  82. package/dist/registry/tool/execute.d.ts.map +1 -1
  83. package/dist/registry/tool/execute.js +192 -7
  84. package/dist/registry/tool/execute.js.map +1 -1
  85. package/dist/registry/tool/execute.test.d.ts +9 -4
  86. package/dist/registry/tool/execute.test.d.ts.map +1 -1
  87. package/dist/registry/tool/execute.test.js +178 -7
  88. package/dist/registry/tool/execute.test.js.map +1 -1
  89. package/dist/registry/toolset/catalog.d.ts +42 -0
  90. package/dist/registry/toolset/catalog.d.ts.map +1 -0
  91. package/dist/registry/toolset/catalog.js +217 -0
  92. package/dist/registry/toolset/catalog.js.map +1 -0
  93. package/dist/registry/toolset/catalog.test.d.ts +2 -0
  94. package/dist/registry/toolset/catalog.test.d.ts.map +1 -0
  95. package/dist/registry/toolset/catalog.test.js +147 -0
  96. package/dist/registry/toolset/catalog.test.js.map +1 -0
  97. package/dist/runtime/query/__tests__/deferred-tools.test.d.ts +2 -0
  98. package/dist/runtime/query/__tests__/deferred-tools.test.d.ts.map +1 -0
  99. package/dist/runtime/query/__tests__/deferred-tools.test.js +185 -0
  100. package/dist/runtime/query/__tests__/deferred-tools.test.js.map +1 -0
  101. package/dist/runtime/query/__tests__/executor-concurrency.test.d.ts +2 -0
  102. package/dist/runtime/query/__tests__/executor-concurrency.test.d.ts.map +1 -0
  103. package/dist/runtime/query/__tests__/executor-concurrency.test.js +98 -0
  104. package/dist/runtime/query/__tests__/executor-concurrency.test.js.map +1 -0
  105. package/dist/runtime/query/__tests__/executor-plugin-hooks.test.js +38 -3
  106. package/dist/runtime/query/__tests__/executor-plugin-hooks.test.js.map +1 -1
  107. package/dist/runtime/query/__tests__/long-document-flow.test.d.ts +2 -0
  108. package/dist/runtime/query/__tests__/long-document-flow.test.d.ts.map +1 -0
  109. package/dist/runtime/query/__tests__/long-document-flow.test.js +154 -0
  110. package/dist/runtime/query/__tests__/long-document-flow.test.js.map +1 -0
  111. package/dist/runtime/query/__tests__/prompt.test.js +51 -2
  112. package/dist/runtime/query/__tests__/prompt.test.js.map +1 -1
  113. package/dist/runtime/query/__tests__/stream-recovery.test.d.ts +2 -0
  114. package/dist/runtime/query/__tests__/stream-recovery.test.d.ts.map +1 -0
  115. package/dist/runtime/query/__tests__/stream-recovery.test.js +126 -0
  116. package/dist/runtime/query/__tests__/stream-recovery.test.js.map +1 -0
  117. package/dist/runtime/query/continuation.d.ts +16 -0
  118. package/dist/runtime/query/continuation.d.ts.map +1 -0
  119. package/dist/runtime/query/continuation.js +16 -0
  120. package/dist/runtime/query/continuation.js.map +1 -0
  121. package/dist/runtime/query/executor.d.ts +3 -0
  122. package/dist/runtime/query/executor.d.ts.map +1 -1
  123. package/dist/runtime/query/executor.js +71 -3
  124. package/dist/runtime/query/executor.js.map +1 -1
  125. package/dist/runtime/query/index.d.ts +9 -0
  126. package/dist/runtime/query/index.d.ts.map +1 -1
  127. package/dist/runtime/query/index.js +42 -5
  128. package/dist/runtime/query/index.js.map +1 -1
  129. package/dist/runtime/query/iteration/index.d.ts +24 -0
  130. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  131. package/dist/runtime/query/iteration/index.js +326 -66
  132. package/dist/runtime/query/iteration/index.js.map +1 -1
  133. package/dist/runtime/query/iteration/phases/checkpoint.d.ts.map +1 -1
  134. package/dist/runtime/query/iteration/phases/checkpoint.js +2 -2
  135. package/dist/runtime/query/iteration/phases/checkpoint.js.map +1 -1
  136. package/dist/runtime/query/iteration/phases/compaction.d.ts +8 -0
  137. package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
  138. package/dist/runtime/query/iteration/phases/compaction.js +84 -3
  139. package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
  140. package/dist/runtime/query/iteration/phases/context.d.ts +22 -0
  141. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  142. package/dist/runtime/query/iteration/phases/context.js +53 -0
  143. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  144. package/dist/runtime/query/iteration/phases/hitl-abort-race.test.d.ts +2 -0
  145. package/dist/runtime/query/iteration/phases/hitl-abort-race.test.d.ts.map +1 -0
  146. package/dist/runtime/query/iteration/phases/hitl-abort-race.test.js +73 -0
  147. package/dist/runtime/query/iteration/phases/hitl-abort-race.test.js.map +1 -0
  148. package/dist/runtime/query/iteration/phases/hitl-answer-question.test.d.ts +2 -0
  149. package/dist/runtime/query/iteration/phases/hitl-answer-question.test.d.ts.map +1 -0
  150. package/dist/runtime/query/iteration/phases/hitl-answer-question.test.js +120 -0
  151. package/dist/runtime/query/iteration/phases/hitl-answer-question.test.js.map +1 -0
  152. package/dist/runtime/query/iteration/phases/tool-review.d.ts +1 -1
  153. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  154. package/dist/runtime/query/iteration/phases/tool-review.js +7 -2
  155. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  156. package/dist/runtime/query/iteration/phases/working-memory-compaction.test.d.ts +19 -0
  157. package/dist/runtime/query/iteration/phases/working-memory-compaction.test.d.ts.map +1 -0
  158. package/dist/runtime/query/iteration/phases/working-memory-compaction.test.js +177 -0
  159. package/dist/runtime/query/iteration/phases/working-memory-compaction.test.js.map +1 -0
  160. package/dist/runtime/query/iteration/phases/working-memory.d.ts +31 -0
  161. package/dist/runtime/query/iteration/phases/working-memory.d.ts.map +1 -0
  162. package/dist/runtime/query/iteration/phases/working-memory.js +81 -0
  163. package/dist/runtime/query/iteration/phases/working-memory.js.map +1 -0
  164. package/dist/runtime/query/prompt.d.ts.map +1 -1
  165. package/dist/runtime/query/prompt.js +21 -1
  166. package/dist/runtime/query/prompt.js.map +1 -1
  167. package/dist/runtime/query/tooling.d.ts +1 -0
  168. package/dist/runtime/query/tooling.d.ts.map +1 -1
  169. package/dist/runtime/query/tooling.js +1 -0
  170. package/dist/runtime/query/tooling.js.map +1 -1
  171. package/dist/sandbox/provider/local.d.ts.map +1 -1
  172. package/dist/sandbox/provider/local.js +32 -1
  173. package/dist/sandbox/provider/local.js.map +1 -1
  174. package/dist/session/workspace/__tests__/shared-run.test.d.ts +2 -0
  175. package/dist/session/workspace/__tests__/shared-run.test.d.ts.map +1 -0
  176. package/dist/session/workspace/__tests__/shared-run.test.js +147 -0
  177. package/dist/session/workspace/__tests__/shared-run.test.js.map +1 -0
  178. package/dist/session/workspace/index.d.ts +2 -0
  179. package/dist/session/workspace/index.d.ts.map +1 -1
  180. package/dist/session/workspace/index.js +1 -0
  181. package/dist/session/workspace/index.js.map +1 -1
  182. package/dist/session/workspace/shared-run.d.ts +81 -0
  183. package/dist/session/workspace/shared-run.d.ts.map +1 -0
  184. package/dist/session/workspace/shared-run.js +252 -0
  185. package/dist/session/workspace/shared-run.js.map +1 -0
  186. package/dist/skills/loader.d.ts.map +1 -1
  187. package/dist/skills/loader.js +36 -6
  188. package/dist/skills/loader.js.map +1 -1
  189. package/dist/skills/loader.test.d.ts +2 -0
  190. package/dist/skills/loader.test.d.ts.map +1 -0
  191. package/dist/skills/loader.test.js +65 -0
  192. package/dist/skills/loader.test.js.map +1 -0
  193. package/dist/streaming/coalesce.test.js +1 -1
  194. package/dist/streaming/coalesce.test.js.map +1 -1
  195. package/dist/tools/builtins/__tests__/edit.test.d.ts +2 -0
  196. package/dist/tools/builtins/__tests__/edit.test.d.ts.map +1 -0
  197. package/dist/tools/builtins/__tests__/edit.test.js +38 -0
  198. package/dist/tools/builtins/__tests__/edit.test.js.map +1 -0
  199. package/dist/tools/builtins/__tests__/payload-budget.test.d.ts +2 -0
  200. package/dist/tools/builtins/__tests__/payload-budget.test.d.ts.map +1 -0
  201. package/dist/tools/builtins/__tests__/payload-budget.test.js +70 -0
  202. package/dist/tools/builtins/__tests__/payload-budget.test.js.map +1 -0
  203. package/dist/tools/builtins/__tests__/read-file.test.d.ts +2 -0
  204. package/dist/tools/builtins/__tests__/read-file.test.d.ts.map +1 -0
  205. package/dist/tools/builtins/__tests__/read-file.test.js +33 -0
  206. package/dist/tools/builtins/__tests__/read-file.test.js.map +1 -0
  207. package/dist/tools/builtins/__tests__/verify-outputs.test.d.ts +2 -0
  208. package/dist/tools/builtins/__tests__/verify-outputs.test.d.ts.map +1 -0
  209. package/dist/tools/builtins/__tests__/verify-outputs.test.js +52 -0
  210. package/dist/tools/builtins/__tests__/verify-outputs.test.js.map +1 -0
  211. package/dist/tools/builtins/__tests__/write-file.test.d.ts +2 -0
  212. package/dist/tools/builtins/__tests__/write-file.test.d.ts.map +1 -0
  213. package/dist/tools/builtins/__tests__/write-file.test.js +74 -0
  214. package/dist/tools/builtins/__tests__/write-file.test.js.map +1 -0
  215. package/dist/tools/builtins/bash.d.ts.map +1 -1
  216. package/dist/tools/builtins/bash.js +40 -7
  217. package/dist/tools/builtins/bash.js.map +1 -1
  218. package/dist/tools/builtins/edit.d.ts +5 -2
  219. package/dist/tools/builtins/edit.d.ts.map +1 -1
  220. package/dist/tools/builtins/edit.js +114 -18
  221. package/dist/tools/builtins/edit.js.map +1 -1
  222. package/dist/tools/builtins/index.d.ts +1 -0
  223. package/dist/tools/builtins/index.d.ts.map +1 -1
  224. package/dist/tools/builtins/index.js +13 -13
  225. package/dist/tools/builtins/index.js.map +1 -1
  226. package/dist/tools/builtins/read-file.d.ts +1 -0
  227. package/dist/tools/builtins/read-file.d.ts.map +1 -1
  228. package/dist/tools/builtins/read-file.js +73 -10
  229. package/dist/tools/builtins/read-file.js.map +1 -1
  230. package/dist/tools/builtins/search-tools.d.ts.map +1 -1
  231. package/dist/tools/builtins/search-tools.js +35 -7
  232. package/dist/tools/builtins/search-tools.js.map +1 -1
  233. package/dist/tools/builtins/verify-outputs.d.ts +5 -0
  234. package/dist/tools/builtins/verify-outputs.d.ts.map +1 -0
  235. package/dist/tools/builtins/verify-outputs.js +103 -0
  236. package/dist/tools/builtins/verify-outputs.js.map +1 -0
  237. package/dist/tools/builtins/write-file.d.ts +3 -2
  238. package/dist/tools/builtins/write-file.d.ts.map +1 -1
  239. package/dist/tools/builtins/write-file.js +72 -12
  240. package/dist/tools/builtins/write-file.js.map +1 -1
  241. package/dist/tools/coordinator/__tests__/agent.test.d.ts +15 -0
  242. package/dist/tools/coordinator/__tests__/agent.test.d.ts.map +1 -0
  243. package/dist/tools/coordinator/__tests__/agent.test.js +142 -0
  244. package/dist/tools/coordinator/__tests__/agent.test.js.map +1 -0
  245. package/dist/tools/coordinator/__tests__/approve-plan.test.d.ts +2 -0
  246. package/dist/tools/coordinator/__tests__/approve-plan.test.d.ts.map +1 -0
  247. package/dist/tools/coordinator/__tests__/approve-plan.test.js +114 -0
  248. package/dist/tools/coordinator/__tests__/approve-plan.test.js.map +1 -0
  249. package/dist/tools/coordinator/__tests__/ask-user-question.test.d.ts +2 -0
  250. package/dist/tools/coordinator/__tests__/ask-user-question.test.d.ts.map +1 -0
  251. package/dist/tools/coordinator/__tests__/ask-user-question.test.js +309 -0
  252. package/dist/tools/coordinator/__tests__/ask-user-question.test.js.map +1 -0
  253. package/dist/tools/coordinator/__tests__/task-list.test.d.ts +13 -0
  254. package/dist/tools/coordinator/__tests__/task-list.test.d.ts.map +1 -0
  255. package/dist/tools/coordinator/__tests__/task-list.test.js +162 -0
  256. package/dist/tools/coordinator/__tests__/task-list.test.js.map +1 -0
  257. package/dist/tools/coordinator/agent.d.ts +34 -0
  258. package/dist/tools/coordinator/agent.d.ts.map +1 -0
  259. package/dist/tools/coordinator/agent.js +107 -0
  260. package/dist/tools/coordinator/agent.js.map +1 -0
  261. package/dist/tools/coordinator/index.d.ts +15 -0
  262. package/dist/tools/coordinator/index.d.ts.map +1 -1
  263. package/dist/tools/coordinator/index.js +305 -35
  264. package/dist/tools/coordinator/index.js.map +1 -1
  265. package/dist/types/agent/base.d.ts +8 -0
  266. package/dist/types/agent/base.d.ts.map +1 -1
  267. package/dist/types/agent/index.d.ts +1 -0
  268. package/dist/types/agent/index.d.ts.map +1 -1
  269. package/dist/types/agent/index.js +1 -0
  270. package/dist/types/agent/index.js.map +1 -1
  271. package/dist/types/agent/reactive.d.ts +38 -0
  272. package/dist/types/agent/reactive.d.ts.map +1 -1
  273. package/dist/types/agent/supervisor.d.ts +32 -0
  274. package/dist/types/agent/supervisor.d.ts.map +1 -1
  275. package/dist/types/agent/working-memory.d.ts +29 -0
  276. package/dist/types/agent/working-memory.d.ts.map +1 -0
  277. package/dist/types/agent/working-memory.js +2 -0
  278. package/dist/types/agent/working-memory.js.map +1 -0
  279. package/dist/types/common/index.d.ts +10 -0
  280. package/dist/types/common/index.d.ts.map +1 -1
  281. package/dist/types/common/index.js +18 -0
  282. package/dist/types/common/index.js.map +1 -1
  283. package/dist/types/common/usage-merge.test.d.ts +2 -0
  284. package/dist/types/common/usage-merge.test.d.ts.map +1 -0
  285. package/dist/types/common/usage-merge.test.js +48 -0
  286. package/dist/types/common/usage-merge.test.js.map +1 -0
  287. package/dist/types/connector/mcp.d.ts +9 -2
  288. package/dist/types/connector/mcp.d.ts.map +1 -1
  289. package/dist/types/hitl/index.d.ts +42 -0
  290. package/dist/types/hitl/index.d.ts.map +1 -1
  291. package/dist/types/hitl/index.js +11 -0
  292. package/dist/types/hitl/index.js.map +1 -1
  293. package/dist/types/message/index.d.ts +22 -1
  294. package/dist/types/message/index.d.ts.map +1 -1
  295. package/dist/types/message/index.js +7 -2
  296. package/dist/types/message/index.js.map +1 -1
  297. package/dist/types/provider/chat.d.ts +11 -9
  298. package/dist/types/provider/chat.d.ts.map +1 -1
  299. package/dist/types/run/events.d.ts +6 -0
  300. package/dist/types/run/events.d.ts.map +1 -1
  301. package/dist/types/run/events.js.map +1 -1
  302. package/dist/types/sandbox/index.d.ts +193 -0
  303. package/dist/types/sandbox/index.d.ts.map +1 -1
  304. package/dist/types/sandbox/index.js.map +1 -1
  305. package/dist/types/skills/index.d.ts +2 -0
  306. package/dist/types/skills/index.d.ts.map +1 -1
  307. package/dist/types/tool/index.d.ts +22 -0
  308. package/dist/types/tool/index.d.ts.map +1 -1
  309. package/dist/types/toolset/index.d.ts +71 -0
  310. package/dist/types/toolset/index.d.ts.map +1 -0
  311. package/dist/types/toolset/index.js +2 -0
  312. package/dist/types/toolset/index.js.map +1 -0
  313. package/dist/types/workspace/index.d.ts +1 -0
  314. package/dist/types/workspace/index.d.ts.map +1 -1
  315. package/dist/types/workspace/shared-run.d.ts +61 -0
  316. package/dist/types/workspace/shared-run.d.ts.map +1 -0
  317. package/dist/types/workspace/shared-run.js +2 -0
  318. package/dist/types/workspace/shared-run.js.map +1 -0
  319. package/dist/verification/index.d.ts +1 -0
  320. package/dist/verification/index.d.ts.map +1 -1
  321. package/dist/verification/index.js +1 -0
  322. package/dist/verification/index.js.map +1 -1
  323. package/dist/verification/presets.d.ts +53 -0
  324. package/dist/verification/presets.d.ts.map +1 -0
  325. package/dist/verification/presets.js +70 -0
  326. package/dist/verification/presets.js.map +1 -0
  327. package/dist/verification/presets.test.d.ts +16 -0
  328. package/dist/verification/presets.test.d.ts.map +1 -0
  329. package/dist/verification/presets.test.js +79 -0
  330. package/dist/verification/presets.test.js.map +1 -0
  331. package/dist/version.d.ts.map +1 -1
  332. package/dist/version.js +25 -4
  333. package/dist/version.js.map +1 -1
  334. package/package.json +3 -2
  335. package/src/advisory/executor.test.ts +7 -4
  336. package/src/advisory/executor.ts +11 -2
  337. package/src/agents/ReactiveAgent.ts +7 -0
  338. package/src/agents/SupervisorAgent.ts +63 -16
  339. package/src/agents/__tests__/supervisor-ledger.test.ts +185 -0
  340. package/src/bridge/sse/mapper.test.ts +2 -2
  341. package/src/config/runtime.ts +9 -0
  342. package/src/connector/builtins/webhook.test.ts +21 -0
  343. package/src/connector/index.ts +1 -0
  344. package/src/connector/mcp/client.ts +4 -0
  345. package/src/connector/mcp/index.ts +1 -0
  346. package/src/connector/mcp/streamable-http.test.ts +169 -0
  347. package/src/connector/mcp/streamable-http.ts +152 -0
  348. package/src/constants/compaction/index.ts +8 -3
  349. package/src/constants/sandbox/index.ts +37 -0
  350. package/src/constants/tools/index.ts +33 -2
  351. package/src/manager/run/persistence.ts +34 -6
  352. package/src/persona/assembler.ts +43 -8
  353. package/src/provider/collect.test.ts +2 -2
  354. package/src/provider/collect.ts +4 -1
  355. package/src/public-runtime.ts +15 -1
  356. package/src/public-tools.ts +2 -0
  357. package/src/public-types.ts +7 -0
  358. package/src/registry/index.ts +7 -0
  359. package/src/registry/tool/execute.test.ts +211 -7
  360. package/src/registry/tool/execute.ts +199 -11
  361. package/src/registry/toolset/catalog.test.ts +165 -0
  362. package/src/registry/toolset/catalog.ts +283 -0
  363. package/src/runtime/query/__tests__/deferred-tools.test.ts +230 -0
  364. package/src/runtime/query/__tests__/executor-concurrency.test.ts +122 -0
  365. package/src/runtime/query/__tests__/executor-plugin-hooks.test.ts +48 -3
  366. package/src/runtime/query/__tests__/long-document-flow.test.ts +180 -0
  367. package/src/runtime/query/__tests__/prompt.test.ts +55 -2
  368. package/src/runtime/query/__tests__/stream-recovery.test.ts +156 -0
  369. package/src/runtime/query/continuation.ts +16 -0
  370. package/src/runtime/query/executor.ts +82 -13
  371. package/src/runtime/query/index.ts +58 -5
  372. package/src/runtime/query/iteration/index.ts +361 -74
  373. package/src/runtime/query/iteration/phases/checkpoint.ts +7 -2
  374. package/src/runtime/query/iteration/phases/compaction.ts +89 -3
  375. package/src/runtime/query/iteration/phases/context.ts +68 -0
  376. package/src/runtime/query/iteration/phases/hitl-abort-race.test.ts +86 -0
  377. package/src/runtime/query/iteration/phases/hitl-answer-question.test.ts +139 -0
  378. package/src/runtime/query/iteration/phases/tool-review.ts +7 -3
  379. package/src/runtime/query/iteration/phases/working-memory-compaction.test.ts +215 -0
  380. package/src/runtime/query/iteration/phases/working-memory.ts +86 -0
  381. package/src/runtime/query/prompt.ts +17 -1
  382. package/src/runtime/query/tooling.ts +2 -0
  383. package/src/sandbox/provider/local.ts +33 -0
  384. package/src/session/workspace/__tests__/shared-run.test.ts +181 -0
  385. package/src/session/workspace/index.ts +6 -0
  386. package/src/session/workspace/shared-run.ts +317 -0
  387. package/src/skills/loader.test.ts +89 -0
  388. package/src/skills/loader.ts +37 -6
  389. package/src/streaming/coalesce.test.ts +4 -4
  390. package/src/tools/builtins/__tests__/edit.test.ts +57 -0
  391. package/src/tools/builtins/__tests__/payload-budget.test.ts +92 -0
  392. package/src/tools/builtins/__tests__/read-file.test.ts +43 -0
  393. package/src/tools/builtins/__tests__/verify-outputs.test.ts +71 -0
  394. package/src/tools/builtins/__tests__/write-file.test.ts +97 -0
  395. package/src/tools/builtins/bash.ts +48 -7
  396. package/src/tools/builtins/edit.ts +162 -27
  397. package/src/tools/builtins/index.ts +13 -13
  398. package/src/tools/builtins/read-file.ts +81 -10
  399. package/src/tools/builtins/search-tools.ts +42 -7
  400. package/src/tools/builtins/verify-outputs.ts +126 -0
  401. package/src/tools/builtins/write-file.ts +83 -14
  402. package/src/tools/coordinator/__tests__/agent.test.ts +172 -0
  403. package/src/tools/coordinator/__tests__/approve-plan.test.ts +139 -0
  404. package/src/tools/coordinator/__tests__/ask-user-question.test.ts +370 -0
  405. package/src/tools/coordinator/__tests__/task-list.test.ts +182 -0
  406. package/src/tools/coordinator/agent.ts +157 -0
  407. package/src/tools/coordinator/index.ts +355 -39
  408. package/src/types/agent/base.ts +8 -0
  409. package/src/types/agent/index.ts +1 -0
  410. package/src/types/agent/reactive.ts +42 -0
  411. package/src/types/agent/supervisor.ts +36 -0
  412. package/src/types/agent/working-memory.ts +29 -0
  413. package/src/types/common/index.ts +19 -0
  414. package/src/types/common/usage-merge.test.ts +53 -0
  415. package/src/types/connector/mcp.ts +14 -2
  416. package/src/types/hitl/index.ts +52 -1
  417. package/src/types/message/index.ts +32 -2
  418. package/src/types/provider/chat.ts +12 -9
  419. package/src/types/run/events.ts +6 -0
  420. package/src/types/sandbox/index.ts +219 -0
  421. package/src/types/skills/index.ts +4 -0
  422. package/src/types/tool/index.ts +24 -0
  423. package/src/types/toolset/index.ts +86 -0
  424. package/src/types/workspace/index.ts +9 -0
  425. package/src/types/workspace/shared-run.ts +65 -0
  426. package/src/verification/index.ts +1 -0
  427. package/src/verification/presets.test.ts +112 -0
  428. package/src/verification/presets.ts +72 -0
  429. package/src/version.ts +26 -4
@@ -9,6 +9,8 @@ import { collect } from '../../../provider/collect.js'
9
9
  import type { ActivityStore } from '../../../store/activity/memory.js'
10
10
  import { GENAI, NAMZU, agentIterationSpanName } from '../../../telemetry/attributes.js'
11
11
  import { getTracer } from '../../../telemetry/runtime-accessors.js'
12
+ import type { WorkingMemoryProvider } from '../../../types/agent/working-memory.js'
13
+ import { mergeTokenUsage } from '../../../types/common/index.js'
12
14
  import type { ResumeHandler } from '../../../types/hitl/index.js'
13
15
  import type { ToolUseId } from '../../../types/ids/index.js'
14
16
  import { createAssistantMessage, createUserMessage } from '../../../types/message/index.js'
@@ -24,6 +26,7 @@ import { toErrorMessage } from '../../../utils/error.js'
24
26
  import { generateMessageId } from '../../../utils/id.js'
25
27
  import type { Logger } from '../../../utils/logger.js'
26
28
  import type { CheckpointManager } from '../checkpoint.js'
29
+ import { AUTO_CONTINUATION_USER_MESSAGE } from '../continuation.js'
27
30
  import type { EmitEvent } from '../events.js'
28
31
  import type { ToolExecutor } from '../executor.js'
29
32
  import type { GuardCoordinator } from '../guard.js'
@@ -34,6 +37,7 @@ import { runCompactionCheck } from './phases/compaction.js'
34
37
  import type { IterationContext } from './phases/index.js'
35
38
  import { runPlanGate } from './phases/plan.js'
36
39
  import { runToolReview } from './phases/tool-review.js'
40
+ import { refreshWorkingMemory } from './phases/working-memory.js'
37
41
 
38
42
  export type { IterationContext } from './phases/index.js'
39
43
  export type { PhaseSignal } from './phases/index.js'
@@ -52,12 +56,42 @@ export interface IterationConfig {
52
56
  >
53
57
  compactionConfig?: CompactionConfig
54
58
  workingStateManager?: WorkingStateManager
59
+ workingMemoryProvider?: WorkingMemoryProvider
55
60
  advisoryCtx?: AdvisoryContext
56
61
  agentBus?: import('../../../bus/index.js').AgentBus
57
62
  verificationGate?: import('../../../verification/gate.js').VerificationGate
58
63
  pluginManager?: import('../../../plugin/lifecycle.js').PluginLifecycleManager
59
64
  }
60
65
 
66
+ /**
67
+ * Escape the five XML metacharacters so an interpolated value cannot
68
+ * break out of a tag. Used for the simple identifier fields in the
69
+ * `<task-notification>` envelope (taskId, agentId, status) — values
70
+ * here are controlled enums / opaque ids in practice, but escaping
71
+ * keeps the envelope robust against any future producer that lets a
72
+ * `<` or `&` leak in.
73
+ */
74
+ function xmlEscape(value: string): string {
75
+ return value
76
+ .replace(/&/g, '&amp;')
77
+ .replace(/</g, '&lt;')
78
+ .replace(/>/g, '&gt;')
79
+ .replace(/"/g, '&quot;')
80
+ .replace(/'/g, '&apos;')
81
+ }
82
+
83
+ /**
84
+ * Wrap free-form worker output in a CDATA section. CDATA preserves
85
+ * the raw text — code, markdown angle brackets, ampersands — so the
86
+ * supervisor sees what the worker actually produced instead of an
87
+ * escape-encoded approximation. The only termination CDATA forbids
88
+ * is the literal `]]>` sequence; we split-and-rejoin around it to
89
+ * keep the section well-formed regardless of payload.
90
+ */
91
+ function cdataWrap(value: string): string {
92
+ return `<![CDATA[${value.replace(/]]>/g, ']]]]><![CDATA[>')}]]>`
93
+ }
94
+
61
95
  /**
62
96
  * Map a provider's coarse `finishReason` plus the orchestrator's
63
97
  * `forceFinalize` flag onto the per-message {@link MessageStopReason}
@@ -104,8 +138,11 @@ interface StreamingTurnResult {
104
138
  * finally-style fall-through path with `stopReason: 'refusal'`.
105
139
  * - `tool_input_delta` with no `toolUseId` registered yet: we drop
106
140
  * the fragment and log a warning (proxies seen to misorder events).
107
- * - `chunk.error`: we surface as a thrown error after emitting the
108
- * message_completed terminator so consumer cards still close.
141
+ * - `chunk.error`: when no tool input is recoverable, we surface as
142
+ * a thrown error after emitting the message_completed terminator so
143
+ * consumer cards still close. If a tool-use block was already open,
144
+ * we instead synthesize a tool call with runtime truncation metadata
145
+ * so the executor can return a model-readable retry hint.
109
146
  */
110
147
  async function* streamProviderTurn(
111
148
  provider: LLMProvider,
@@ -134,14 +171,60 @@ async function* streamProviderTurn(
134
171
  }
135
172
  const toolBuckets = new Map<
136
173
  number,
137
- { id: string; name: string; argsBuf: string; started: boolean; completed: boolean }
174
+ {
175
+ id: string
176
+ name: string
177
+ argsBuf: string
178
+ started: boolean
179
+ completed: boolean
180
+ /**
181
+ * Parsed input. `null` while the bucket is still streaming.
182
+ * The synthesized
183
+ * `ChatCompletionResponse.toolCalls[].function.arguments` is
184
+ * derived from this — never from the raw buffer — so the
185
+ * downstream executor (`runtime/query/executor.ts`) never has
186
+ * to re-parse a truncated string. A truncated tool call is
187
+ * surfaced as `arguments: "{}"` plus `metadata.inputTruncated`
188
+ * so tool args remain clean while the executor can still
189
+ * return a specific retry hint.
190
+ */
191
+ parsed: unknown | null
192
+ inputTruncated: boolean
193
+ }
138
194
  >()
139
195
  let streamError: string | undefined
140
196
 
141
197
  const stream = provider.chatStream({ ...params, stream: true }) as AsyncIterable<StreamChunk>
142
198
 
199
+ // Drive the stream manually so each `.next()` can be RACED against the run
200
+ // abort: a Stop tears the in-flight model request down (the provider got
201
+ // `params.signal`), and we ALSO stop pulling within a tick even if a
202
+ // transport buffers or ignores the signal. The abort rejection propagates
203
+ // out of this generator so the run loop settles the turn as cancelled.
204
+ // `{ once: true }` keeps a multi-iteration run from leaking a listener/turn.
205
+ const it = stream[Symbol.asyncIterator]()
206
+ const signal = params.signal
207
+ let onAbort: (() => void) | undefined
208
+ const aborted: Promise<never> | undefined = signal
209
+ ? new Promise<never>((_resolve, reject) => {
210
+ if (signal.aborted) {
211
+ reject(signal.reason)
212
+ return
213
+ }
214
+ onAbort = () => reject(signal.reason)
215
+ signal.addEventListener('abort', onAbort, { once: true })
216
+ })
217
+ : undefined
218
+
143
219
  try {
144
- for await (const chunk of stream) {
220
+ for (;;) {
221
+ const next = it.next()
222
+ // Neutralize the dangling loser so an eventual rejection of the
223
+ // un-awaited `next` is never an unhandled rejection.
224
+ if (aborted) next.catch(() => {})
225
+ const res = await (aborted ? Promise.race([next, aborted]) : next)
226
+ if (res.done) break
227
+ const chunk = res.value
145
228
  if (chunk.error) {
146
229
  streamError = chunk.error
147
230
  break
@@ -169,6 +252,8 @@ async function* streamProviderTurn(
169
252
  argsBuf: '',
170
253
  started: false,
171
254
  completed: false,
255
+ parsed: null,
256
+ inputTruncated: false,
172
257
  }
173
258
  toolBuckets.set(tc.index, bucket)
174
259
  }
@@ -218,53 +303,149 @@ async function* streamProviderTurn(
218
303
  try {
219
304
  parsed = bucket.argsBuf ? JSON.parse(bucket.argsBuf) : {}
220
305
  } catch (err) {
306
+ bucket.inputTruncated = true
221
307
  log.warn('tool input JSON parse failed at content_block_stop', {
222
308
  runId,
223
309
  toolUseId: endId,
224
310
  error: err instanceof Error ? err.message : String(err),
225
311
  })
226
312
  }
313
+ bucket.parsed = parsed
227
314
  await emitEvent({
228
315
  type: 'tool_input_completed',
229
316
  runId,
230
317
  toolUseId: endId as ToolUseId,
231
318
  input: parsed,
319
+ ...(bucket.inputTruncated ? { inputTruncated: true } : {}),
232
320
  })
233
321
  yield* drainPending()
234
322
  }
235
323
  }
236
324
 
237
325
  if (chunk.finishReason) finishReason = chunk.finishReason
238
- if (chunk.usage) usage = chunk.usage
326
+ // Merge (per-field max), not last-write-wins: a late usage frame that
327
+ // omits input/cache tokens must not zero the counts seen earlier in the
328
+ // stream, which would under-report this turn's accumulated usage.
329
+ if (chunk.usage) usage = mergeTokenUsage(usage, chunk.usage)
239
330
  }
240
331
  } catch (err) {
332
+ // An abort tears the turn down: propagate it so the run loop settles the
333
+ // run as cancelled rather than recording a normal (errored) turn. Any
334
+ // other stream error is captured into the synthesized response as before.
335
+ if (signal?.aborted) throw err
241
336
  streamError = err instanceof Error ? err.message : String(err)
337
+ } finally {
338
+ if (onAbort) signal?.removeEventListener('abort', onAbort)
339
+ // Release the underlying connection on every exit (natural end, error,
340
+ // or abort). `for await` did this implicitly on natural completion; the
341
+ // manual drive must do it explicitly. `.return()` on an already-finished
342
+ // provider generator is a no-op.
343
+ await it.return?.().catch(() => {})
242
344
  }
243
345
 
244
346
  // Flush any tool buckets the provider failed to close (no toolCallEnd
245
- // arrived — defensive against providers that don't yet emit it).
347
+ // arrived — defensive against providers that don't yet emit it, and
348
+ // the load-bearing path when the provider stream ends with
349
+ // `stop_reason: "max_tokens"` mid-`input_json_delta`. In that case
350
+ // Anthropic's SSE never sends `content_block_stop` for the open
351
+ // tool_use block: the upstream model ran out of completion tokens
352
+ // before it could close the JSON literal, so the buffered
353
+ // `argsBuf` ends with something like `"content":"…some prefix` —
354
+ // not parseable.
355
+ //
356
+ // Two cases coalesce here:
357
+ // 1. The buffer parses cleanly (the provider just forgot to emit
358
+ // `content_block_stop` but the args are intact) — keep parsed.
359
+ // 2. The buffer is truncated mid-literal — `parsed = {}` is the
360
+ // safe fallback so the executor's `JSON.parse(arguments)`
361
+ // succeeds and downstream consumers don't crash. The PRICE
362
+ // we used to pay was the model getting back a generic
363
+ // "<field> is required" Zod error and not realising its
364
+ // previous tool call was truncated server-side, so it would
365
+ // retry with the SAME long input and hit the same cutoff in
366
+ // a loop. Detect the truncation case and mark the tool call
367
+ // with runtime metadata; the executor surfaces a specific
368
+ // "your tool call was cut off by max_tokens — retry with
369
+ // shorter input or split into smaller calls" message that the
370
+ // model can act on.
246
371
  for (const bucket of toolBuckets.values()) {
247
372
  if (bucket.started && !bucket.completed) {
248
373
  bucket.completed = true
249
374
  let parsed: unknown = {}
250
- try {
251
- parsed = bucket.argsBuf ? JSON.parse(bucket.argsBuf) : {}
252
- } catch {
253
- // leave parsed = {}
375
+ let truncated = false
376
+ if (bucket.argsBuf) {
377
+ try {
378
+ parsed = JSON.parse(bucket.argsBuf)
379
+ } catch {
380
+ // argsBuf had content but didn't parse — almost
381
+ // certainly the max_tokens-mid-literal cutoff. Mark
382
+ // the bucket so the executor can return a model-
383
+ // readable hint instead of a generic Zod error.
384
+ truncated = true
385
+ parsed = {}
386
+ }
387
+ }
388
+ bucket.parsed = parsed
389
+ bucket.inputTruncated = truncated
390
+ if (truncated) {
391
+ log.warn('tool input truncated by upstream cutoff (no toolCallEnd, argsBuf unparsable)', {
392
+ runId,
393
+ toolUseId: bucket.id,
394
+ toolName: bucket.name,
395
+ bufferLength: bucket.argsBuf.length,
396
+ })
254
397
  }
255
398
  await emitEvent({
256
399
  type: 'tool_input_completed',
257
400
  runId,
258
401
  toolUseId: bucket.id as ToolUseId,
259
402
  input: parsed,
403
+ ...(truncated ? { inputTruncated: true } : {}),
260
404
  })
261
405
  yield* drainPending()
262
406
  }
263
407
  }
264
408
 
409
+ // `arguments` MUST be valid JSON for the executor's `JSON.parse`
410
+ // (`runtime/query/executor.ts:executeSingle`) to succeed. We
411
+ // always serialise from the bucket's `parsed` object (filled by
412
+ // either the `toolCallEnd` branch above or the post-stream flush
413
+ // loop) instead of re-emitting `argsBuf`. When the provider
414
+ // stream truncated mid-input, `metadata.inputTruncated` carries that
415
+ // state; the executor parses cleanly and returns a specific
416
+ // model-readable retry hint instead of the generic "Invalid JSON in
417
+ // tool arguments" intercept.
418
+ const toolCalls = [...toolBuckets.entries()]
419
+ .sort(([a], [b]) => a - b)
420
+ .map(([, b]) => ({
421
+ id: b.id,
422
+ type: 'function' as const,
423
+ function: {
424
+ name: b.name,
425
+ arguments: JSON.stringify(b.parsed ?? {}),
426
+ },
427
+ ...(b.inputTruncated ? { metadata: { inputTruncated: true } } : {}),
428
+ }))
429
+
430
+ const recoveredToolInputFromStreamError =
431
+ streamError !== undefined && toolCalls.some((tc) => tc.id && tc.function.name)
432
+ const effectiveFinishReason: ChatCompletionResponse['finishReason'] =
433
+ recoveredToolInputFromStreamError ? 'tool_calls' : finishReason
434
+
435
+ if (recoveredToolInputFromStreamError) {
436
+ log.warn('provider stream failed after tool input; surfacing tool call to executor', {
437
+ runId,
438
+ iteration,
439
+ error: streamError,
440
+ toolCallCount: toolCalls.length,
441
+ })
442
+ }
443
+
265
444
  const stopReason: MessageStopReason = streamError
266
- ? 'refusal'
267
- : synthesizeMessageStopReason(finishReason, forceFinalize)
445
+ ? recoveredToolInputFromStreamError
446
+ ? 'tool_use'
447
+ : 'refusal'
448
+ : synthesizeMessageStopReason(effectiveFinishReason, forceFinalize)
268
449
 
269
450
  await emitEvent({
270
451
  type: 'message_completed',
@@ -277,18 +458,10 @@ async function* streamProviderTurn(
277
458
  })
278
459
  yield* drainPending()
279
460
 
280
- if (streamError) {
461
+ if (streamError && !recoveredToolInputFromStreamError) {
281
462
  throw new Error(`Provider stream error: ${streamError}`)
282
463
  }
283
464
 
284
- const toolCalls = [...toolBuckets.entries()]
285
- .sort(([a], [b]) => a - b)
286
- .map(([, b]) => ({
287
- id: b.id,
288
- type: 'function' as const,
289
- function: { name: b.name, arguments: b.argsBuf },
290
- }))
291
-
292
465
  const response: ChatCompletionResponse = {
293
466
  id: id || messageId,
294
467
  model: model || params.model,
@@ -297,7 +470,7 @@ async function* streamProviderTurn(
297
470
  content: textBuf.length > 0 ? textBuf : null,
298
471
  toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
299
472
  },
300
- finishReason,
473
+ finishReason: effectiveFinishReason,
301
474
  usage,
302
475
  }
303
476
  return { response, messageId }
@@ -342,6 +515,7 @@ export class IterationOrchestrator {
342
515
  launchedTasks: config.launchedTasks ?? new Map(),
343
516
  compactionConfig: config.compactionConfig,
344
517
  workingStateManager: config.workingStateManager,
518
+ workingMemoryProvider: config.workingMemoryProvider,
345
519
  advisoryCtx: config.advisoryCtx,
346
520
  agentBus: config.agentBus,
347
521
  verificationGate: config.verificationGate,
@@ -354,17 +528,19 @@ export class IterationOrchestrator {
354
528
  const { model } = runConfig
355
529
  const tracer = getTracer()
356
530
 
531
+ // Worker-completion delivery used to fan out through a global
532
+ // onTaskCompleted listener that pushed handles onto
533
+ // `pendingNotifications`; the iteration loop then drained
534
+ // them as <task-notification> envelopes. Both `create_task`
535
+ // and the `Agent` tool are now blocking and return their
536
+ // worker output as the dispatching tool_use's canonical
537
+ // tool_result, so the listener path would only DUPLICATE
538
+ // every completion (once as tool_result, once as injected
539
+ // envelope user-message). Leaving the binding out closes
540
+ // the duplicate notification surface entirely; the dormant
541
+ // drain stays as a no-op until a follow-up tears it out.
357
542
  let unsubscribeTaskListener: (() => void) | undefined
358
- if (this.ctx.taskGateway) {
359
- unsubscribeTaskListener = this.ctx.taskGateway.onTaskCompleted((handle) => {
360
- this.ctx.pendingNotifications.push(handle)
361
- this.ctx.log.debug('Task completion queued for notification', {
362
- taskId: handle.taskId,
363
- agentId: handle.agentId,
364
- state: handle.state,
365
- })
366
- })
367
- }
543
+ void unsubscribeTaskListener
368
544
 
369
545
  try {
370
546
  const planSignal = yield* runPlanGate(this.ctx)
@@ -442,11 +618,19 @@ export class IterationOrchestrator {
442
618
  await this.injectOneTaskNotification()
443
619
  }
444
620
 
621
+ // Re-pin the working-memory block from ground truth at the primacy
622
+ // edge BEFORE compaction runs (so the refreshed slot is what
623
+ // compaction preserves). No-op when no provider is configured.
624
+ await refreshWorkingMemory(this.ctx)
445
625
  await runCompactionCheck(this.ctx)
446
626
 
447
- const openAITools = forceFinalize
448
- ? undefined
449
- : this.ctx.tools.toLLMTools(this.ctx.allowedTools)
627
+ // Cache discipline: keep the tools param byte-stable even on the
628
+ // forced-final iteration and forbid tool use via tool_choice
629
+ // 'none' instead. Dropping the tools array would invalidate the
630
+ // entire prompt-cache prefix (tools render at position 0) and
631
+ // risks a 400 because the history still carries
632
+ // tool_use/tool_result blocks.
633
+ const openAITools = this.ctx.tools.toLLMTools(this.ctx.allowedTools)
450
634
 
451
635
  const messages = forceFinalize
452
636
  ? [
@@ -480,10 +664,15 @@ export class IterationOrchestrator {
480
664
  {
481
665
  model,
482
666
  messages,
483
- tools: openAITools && openAITools.length > 0 ? openAITools : undefined,
667
+ tools: openAITools.length > 0 ? openAITools : undefined,
668
+ toolChoice: forceFinalize && openAITools.length > 0 ? 'none' : undefined,
484
669
  temperature: runConfig.temperature,
485
670
  maxTokens: runConfig.maxResponseTokens,
486
671
  cacheControl: { type: 'auto' },
672
+ // Thread the run abort into the model call so a Stop tears the
673
+ // in-flight turn down (provider passes it to fetch; the consumer
674
+ // also races it). Inert when never aborted.
675
+ signal: this.ctx.abortController.signal,
487
676
  },
488
677
  this.ctx.emitEvent,
489
678
  this.ctx.drainPending,
@@ -588,6 +777,43 @@ export class IterationOrchestrator {
588
777
  const hasContent =
589
778
  response.message.content !== null && response.message.content.length > 0
590
779
 
780
+ // Auto-continuation on `stop_reason: max_tokens`. The
781
+ // model hit its per-call output cap mid-text (NOT
782
+ // mid-tool-use — that path is handled separately
783
+ // below via `inputTruncated`). Push a synthetic
784
+ // "continue" user message and let the loop fire
785
+ // another turn. The provider receives the partial
786
+ // assistant content + the continue prompt and
787
+ // resumes from where it left off, mirroring the
788
+ // Claude.ai "Continue" affordance.
789
+ //
790
+ // Guards:
791
+ // - `hasContent` so we don't loop forever on an
792
+ // empty cutoff (Anthropic occasionally emits
793
+ // `stop_reason: max_tokens` with no content
794
+ // when an injected pre-fill blocks the model).
795
+ // - `!forceFinalize` so the forced-finalize path
796
+ // never auto-continues — that path is invoked
797
+ // specifically to extract a closing summary.
798
+ // - max_iterations bounds the loop in any case.
799
+ if (!forceFinalize && response.finishReason === 'length' && hasContent) {
800
+ this.ctx.log.info('LLM hit max_tokens mid-text — auto-continuing', {
801
+ runId: runMgr.id,
802
+ iteration: iterationNum,
803
+ completionTokens: response.usage.completionTokens,
804
+ })
805
+ runMgr.pushMessage(createUserMessage(AUTO_CONTINUATION_USER_MESSAGE))
806
+ await this.ctx.emitEvent({
807
+ type: 'iteration_completed',
808
+ runId: runMgr.id,
809
+ iteration: iterationNum,
810
+ hasToolCalls: false,
811
+ })
812
+ yield* this.ctx.drainPending()
813
+ iterSpan.end()
814
+ continue
815
+ }
816
+
591
817
  if (!hasContent && !forceFinalize) {
592
818
  this.ctx.log.warn('Empty completion detected — requesting final summary', {
593
819
  iteration: iterationNum,
@@ -604,6 +830,16 @@ export class IterationOrchestrator {
604
830
  hasToolCalls: false,
605
831
  })
606
832
  yield* this.ctx.drainPending()
833
+ // A Stop that lands AFTER the final turn streamed but before
834
+ // this break must settle the run as cancelled, not end_turn —
835
+ // otherwise the just-produced answer is recorded as a clean
836
+ // completion. Mirrors the between-iteration cancel at :511.
837
+ if (this.ctx.abortController.signal.aborted) {
838
+ runMgr.setStopReason('cancelled')
839
+ runMgr.markCancelled()
840
+ iterSpan.end()
841
+ break
842
+ }
607
843
  runMgr.setStopReason('end_turn')
608
844
  iterSpan.end()
609
845
  break
@@ -648,6 +884,20 @@ export class IterationOrchestrator {
648
884
  yield* this.ctx.drainPending()
649
885
  iterSpan.end()
650
886
  } catch (err) {
887
+ // A Stop that aborted the in-flight turn surfaces here as a
888
+ // thrown abort (the provider stream was raced against the run
889
+ // signal). Settle it as a CANCELLATION — mirroring the
890
+ // between-iteration cancel at the top of the loop — rather than
891
+ // recording it as an SDK failure (error span + failed activity)
892
+ // and re-throwing. The run then returns cleanly with a
893
+ // 'cancelled' stop reason instead of propagating an error.
894
+ if (this.ctx.abortController.signal.aborted) {
895
+ runMgr.setStopReason('cancelled')
896
+ runMgr.markCancelled()
897
+ iterSpan.end()
898
+ break
899
+ }
900
+
651
901
  if (iterationActivity) {
652
902
  this.ctx.activityStore.fail(iterationActivity.id, toErrorMessage(err))
653
903
  }
@@ -686,47 +936,75 @@ export class IterationOrchestrator {
686
936
  await this.injectOneTaskNotification()
687
937
  }
688
938
 
939
+ /**
940
+ * Canonical async completion delivery (ses_009-task-notification-envelope).
941
+ *
942
+ * Drains every pending task completion in one pass and emits each as
943
+ * a plain USER text message wrapped in the `<task-notification>`
944
+ * envelope the supervisor prompt expects.
945
+ *
946
+ * Why not a `tool_result` block bound to the dispatching tool_use_id:
947
+ * `create_task` is documented as NON-BLOCKING and returns
948
+ * "Task launched: …" immediately. That immediate return is already
949
+ * recorded as the canonical tool_result for that tool_use, so a
950
+ * second tool_result for the SAME tool_use_id — emitted later, after
951
+ * intervening assistant turns — is rejected by Anthropic with
952
+ * `messages.<n>.content.0: unexpected tool_use_id found in
953
+ * tool_result blocks` because the immediately-prior assistant
954
+ * message no longer carries the matching tool_use. Wrapping as a
955
+ * user text envelope sidesteps the pairing rule entirely.
956
+ *
957
+ * Coalescing N drops into one drain replaces the previous
958
+ * one-at-a-time pattern which forced a separate orchestrator
959
+ * iteration per completed task on wide fan-outs.
960
+ */
689
961
  private async injectOneTaskNotification(): Promise<void> {
690
- const handle = this.ctx.pendingNotifications.shift()
691
- if (!handle) return
692
- const meta = this.ctx.launchedTasks.get(handle.taskId)
693
- const resultText =
694
- handle.result?.result ??
695
- handle.result?.lastError ??
696
- `Task finished with state: ${handle.state}`
697
-
698
- if (meta?.planTaskId && this.ctx.taskStore) {
699
- const success = handle.state === 'completed'
700
- await this.ctx.taskStore.update(meta.planTaskId as `task_${string}`, {
701
- status: 'completed',
702
- description: success ? undefined : `Failed: ${resultText.substring(0, 200)}`,
962
+ if (this.ctx.pendingNotifications.length === 0) return
963
+ const handles = this.ctx.pendingNotifications.splice(0)
964
+
965
+ for (const handle of handles) {
966
+ const meta = this.ctx.launchedTasks.get(handle.taskId)
967
+ const resultText =
968
+ handle.result?.result ??
969
+ handle.result?.lastError ??
970
+ `Task finished with state: ${handle.state}`
971
+
972
+ if (meta?.planTaskId && this.ctx.taskStore) {
973
+ const success = handle.state === 'completed'
974
+ await this.ctx.taskStore.update(meta.planTaskId as `task_${string}`, {
975
+ status: 'completed',
976
+ description: success ? undefined : `Failed: ${resultText.substring(0, 200)}`,
977
+ })
978
+ }
979
+
980
+ this.ctx.launchedTasks.delete(handle.taskId)
981
+
982
+ // `remaining-tasks` = inflight workers still pending after this
983
+ // one drains. `launchedTasks` is the single source of truth:
984
+ // it holds every dispatched worker that has NOT yet been
985
+ // drained + delete()'d. The drain batch entries are still
986
+ // inside launchedTasks until each iteration's delete() above
987
+ // removes them, so reading the size right after that delete
988
+ // gives the honest count. Adding `handles.length - 1 - i`
989
+ // here used to double-count this same queue.
990
+ const remainingTasks = this.ctx.launchedTasks.size
991
+ const envelope =
992
+ `<task-notification>\n<task-id>${xmlEscape(handle.taskId)}</task-id>\n` +
993
+ `<agent-id>${xmlEscape(handle.agentId)}</agent-id>\n` +
994
+ `<status>${xmlEscape(handle.state)}</status>\n` +
995
+ `<result>${cdataWrap(resultText)}</result>\n` +
996
+ `<remaining-tasks>${remainingTasks}</remaining-tasks>\n</task-notification>`
997
+
998
+ this.ctx.runMgr.pushMessage(createUserMessage(envelope))
999
+
1000
+ this.ctx.log.info('Task notification injected', {
1001
+ taskId: handle.taskId,
1002
+ agentId: handle.agentId,
1003
+ state: handle.state,
1004
+ planTaskId: meta?.planTaskId,
1005
+ remainingNotifications: remainingTasks,
703
1006
  })
704
1007
  }
705
-
706
- this.ctx.launchedTasks.delete(handle.taskId)
707
- const remainingTasks = this.ctx.launchedTasks.size
708
-
709
- const notification = [
710
- '<task-notification>',
711
- ` <task-id>${handle.taskId}</task-id>`,
712
- ` <agent-id>${handle.agentId}</agent-id>`,
713
- ` <status>${handle.state}</status>`,
714
- ` <description>${meta?.description ?? 'agent task'}</description>`,
715
- ` <result>${resultText}</result>`,
716
- ` <remaining-tasks>${remainingTasks}</remaining-tasks>`,
717
- '</task-notification>',
718
- ].join('\n')
719
-
720
- this.ctx.runMgr.pushMessage(createUserMessage(notification))
721
-
722
- this.ctx.log.info('Task notification injected', {
723
- taskId: handle.taskId,
724
- agentId: handle.agentId,
725
- state: handle.state,
726
- planTaskId: meta?.planTaskId,
727
- remainingTasks,
728
- remainingNotifications: this.ctx.pendingNotifications.length,
729
- })
730
1008
  }
731
1009
 
732
1010
  private async requestFinalResponse(model: string, reason: StopReason): Promise<void> {
@@ -753,13 +1031,22 @@ export class IterationOrchestrator {
753
1031
  ),
754
1032
  ]
755
1033
 
1034
+ // Same cache discipline as the forced-final iteration: keep the
1035
+ // tools param identical to prior iterations (cache prefix intact,
1036
+ // no 400 on tool blocks in history) and forbid use via tool_choice.
1037
+ const finalTools = this.ctx.tools.toLLMTools(this.ctx.allowedTools)
756
1038
  const response = await collect(
757
1039
  this.ctx.provider.chatStream({
758
1040
  model,
759
1041
  messages: finalMessages,
1042
+ tools: finalTools.length > 0 ? finalTools : undefined,
1043
+ toolChoice: finalTools.length > 0 ? 'none' : undefined,
760
1044
  temperature: this.ctx.runConfig.temperature,
761
1045
  maxTokens: this.ctx.runConfig.maxResponseTokens,
762
1046
  cacheControl: { type: 'auto' },
1047
+ // Cancellable too: a Stop during the closing summary must not
1048
+ // stream to completion.
1049
+ signal: this.ctx.abortController.signal,
763
1050
  }),
764
1051
  )
765
1052
 
@@ -1,6 +1,11 @@
1
1
  import type { RunEvent } from '../../../../types/run/index.js'
2
2
  import { CheckpointManager } from '../../checkpoint.js'
3
- import { type IterationContext, type PhaseSignal, handleHITLDecision } from './context.js'
3
+ import {
4
+ type IterationContext,
5
+ type PhaseSignal,
6
+ awaitDecisionOrAbort,
7
+ handleHITLDecision,
8
+ } from './context.js'
4
9
 
5
10
  export async function* runIterationCheckpoint(
6
11
  ctx: IterationContext,
@@ -17,7 +22,7 @@ export async function* runIterationCheckpoint(
17
22
  yield* ctx.drainPending()
18
23
 
19
24
  const summary = CheckpointManager.buildSummary(ctx.runMgr, iterationNum)
20
- const iterDecision = await ctx.resumeHandler({
25
+ const iterDecision = await awaitDecisionOrAbort(ctx, {
21
26
  type: 'iteration_checkpoint',
22
27
  runId: ctx.runMgr.id,
23
28
  checkpointId: iterCheckpoint.id,