@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
@@ -3,15 +3,45 @@ import { extractFromAssistantMessage } from '../../../compaction/extractor.js';
3
3
  import { collect } from '../../../provider/collect.js';
4
4
  import { GENAI, NAMZU, agentIterationSpanName } from '../../../telemetry/attributes.js';
5
5
  import { getTracer } from '../../../telemetry/runtime-accessors.js';
6
+ import { mergeTokenUsage } from '../../../types/common/index.js';
6
7
  import { createAssistantMessage, createUserMessage } from '../../../types/message/index.js';
7
8
  import { toErrorMessage } from '../../../utils/error.js';
8
9
  import { generateMessageId } from '../../../utils/id.js';
10
+ import { AUTO_CONTINUATION_USER_MESSAGE } from '../continuation.js';
9
11
  import { applyLifecycleHookResults } from '../plugin-hooks.js';
10
12
  import { runAdvisoryPhase } from './phases/advisory.js';
11
13
  import { runIterationCheckpoint } from './phases/checkpoint.js';
12
14
  import { runCompactionCheck } from './phases/compaction.js';
13
15
  import { runPlanGate } from './phases/plan.js';
14
16
  import { runToolReview } from './phases/tool-review.js';
17
+ import { refreshWorkingMemory } from './phases/working-memory.js';
18
+ /**
19
+ * Escape the five XML metacharacters so an interpolated value cannot
20
+ * break out of a tag. Used for the simple identifier fields in the
21
+ * `<task-notification>` envelope (taskId, agentId, status) — values
22
+ * here are controlled enums / opaque ids in practice, but escaping
23
+ * keeps the envelope robust against any future producer that lets a
24
+ * `<` or `&` leak in.
25
+ */
26
+ function xmlEscape(value) {
27
+ return value
28
+ .replace(/&/g, '&amp;')
29
+ .replace(/</g, '&lt;')
30
+ .replace(/>/g, '&gt;')
31
+ .replace(/"/g, '&quot;')
32
+ .replace(/'/g, '&apos;');
33
+ }
34
+ /**
35
+ * Wrap free-form worker output in a CDATA section. CDATA preserves
36
+ * the raw text — code, markdown angle brackets, ampersands — so the
37
+ * supervisor sees what the worker actually produced instead of an
38
+ * escape-encoded approximation. The only termination CDATA forbids
39
+ * is the literal `]]>` sequence; we split-and-rejoin around it to
40
+ * keep the section well-formed regardless of payload.
41
+ */
42
+ function cdataWrap(value) {
43
+ return `<![CDATA[${value.replace(/]]>/g, ']]]]><![CDATA[>')}]]>`;
44
+ }
15
45
  /**
16
46
  * Map a provider's coarse `finishReason` plus the orchestrator's
17
47
  * `forceFinalize` flag onto the per-message {@link MessageStopReason}
@@ -50,8 +80,11 @@ function synthesizeMessageStopReason(finishReason, forceFinalize) {
50
80
  * finally-style fall-through path with `stopReason: 'refusal'`.
51
81
  * - `tool_input_delta` with no `toolUseId` registered yet: we drop
52
82
  * the fragment and log a warning (proxies seen to misorder events).
53
- * - `chunk.error`: we surface as a thrown error after emitting the
54
- * message_completed terminator so consumer cards still close.
83
+ * - `chunk.error`: when no tool input is recoverable, we surface as
84
+ * a thrown error after emitting the message_completed terminator so
85
+ * consumer cards still close. If a tool-use block was already open,
86
+ * we instead synthesize a tool call with runtime truncation metadata
87
+ * so the executor can return a model-readable retry hint.
55
88
  */
56
89
  async function* streamProviderTurn(provider, params, emitEvent, drainPending, runId, iteration, forceFinalize, log) {
57
90
  const messageId = generateMessageId();
@@ -71,8 +104,36 @@ async function* streamProviderTurn(provider, params, emitEvent, drainPending, ru
71
104
  const toolBuckets = new Map();
72
105
  let streamError;
73
106
  const stream = provider.chatStream({ ...params, stream: true });
107
+ // Drive the stream manually so each `.next()` can be RACED against the run
108
+ // abort: a Stop tears the in-flight model request down (the provider got
109
+ // `params.signal`), and we ALSO stop pulling within a tick even if a
110
+ // transport buffers or ignores the signal. The abort rejection propagates
111
+ // out of this generator so the run loop settles the turn as cancelled.
112
+ // `{ once: true }` keeps a multi-iteration run from leaking a listener/turn.
113
+ const it = stream[Symbol.asyncIterator]();
114
+ const signal = params.signal;
115
+ let onAbort;
116
+ const aborted = signal
117
+ ? new Promise((_resolve, reject) => {
118
+ if (signal.aborted) {
119
+ reject(signal.reason);
120
+ return;
121
+ }
122
+ onAbort = () => reject(signal.reason);
123
+ signal.addEventListener('abort', onAbort, { once: true });
124
+ })
125
+ : undefined;
74
126
  try {
75
- for await (const chunk of stream) {
127
+ for (;;) {
128
+ const next = it.next();
129
+ // Neutralize the dangling loser so an eventual rejection of the
130
+ // un-awaited `next` is never an unhandled rejection.
131
+ if (aborted)
132
+ next.catch(() => { });
133
+ const res = await (aborted ? Promise.race([next, aborted]) : next);
134
+ if (res.done)
135
+ break;
136
+ const chunk = res.value;
76
137
  if (chunk.error) {
77
138
  streamError = chunk.error;
78
139
  break;
@@ -99,6 +160,8 @@ async function* streamProviderTurn(provider, params, emitEvent, drainPending, ru
99
160
  argsBuf: '',
100
161
  started: false,
101
162
  completed: false,
163
+ parsed: null,
164
+ inputTruncated: false,
102
165
  };
103
166
  toolBuckets.set(tc.index, bucket);
104
167
  }
@@ -149,54 +212,148 @@ async function* streamProviderTurn(provider, params, emitEvent, drainPending, ru
149
212
  parsed = bucket.argsBuf ? JSON.parse(bucket.argsBuf) : {};
150
213
  }
151
214
  catch (err) {
215
+ bucket.inputTruncated = true;
152
216
  log.warn('tool input JSON parse failed at content_block_stop', {
153
217
  runId,
154
218
  toolUseId: endId,
155
219
  error: err instanceof Error ? err.message : String(err),
156
220
  });
157
221
  }
222
+ bucket.parsed = parsed;
158
223
  await emitEvent({
159
224
  type: 'tool_input_completed',
160
225
  runId,
161
226
  toolUseId: endId,
162
227
  input: parsed,
228
+ ...(bucket.inputTruncated ? { inputTruncated: true } : {}),
163
229
  });
164
230
  yield* drainPending();
165
231
  }
166
232
  }
167
233
  if (chunk.finishReason)
168
234
  finishReason = chunk.finishReason;
235
+ // Merge (per-field max), not last-write-wins: a late usage frame that
236
+ // omits input/cache tokens must not zero the counts seen earlier in the
237
+ // stream, which would under-report this turn's accumulated usage.
169
238
  if (chunk.usage)
170
- usage = chunk.usage;
239
+ usage = mergeTokenUsage(usage, chunk.usage);
171
240
  }
172
241
  }
173
242
  catch (err) {
243
+ // An abort tears the turn down: propagate it so the run loop settles the
244
+ // run as cancelled rather than recording a normal (errored) turn. Any
245
+ // other stream error is captured into the synthesized response as before.
246
+ if (signal?.aborted)
247
+ throw err;
174
248
  streamError = err instanceof Error ? err.message : String(err);
175
249
  }
250
+ finally {
251
+ if (onAbort)
252
+ signal?.removeEventListener('abort', onAbort);
253
+ // Release the underlying connection on every exit (natural end, error,
254
+ // or abort). `for await` did this implicitly on natural completion; the
255
+ // manual drive must do it explicitly. `.return()` on an already-finished
256
+ // provider generator is a no-op.
257
+ await it.return?.().catch(() => { });
258
+ }
176
259
  // Flush any tool buckets the provider failed to close (no toolCallEnd
177
- // arrived — defensive against providers that don't yet emit it).
260
+ // arrived — defensive against providers that don't yet emit it, and
261
+ // the load-bearing path when the provider stream ends with
262
+ // `stop_reason: "max_tokens"` mid-`input_json_delta`. In that case
263
+ // Anthropic's SSE never sends `content_block_stop` for the open
264
+ // tool_use block: the upstream model ran out of completion tokens
265
+ // before it could close the JSON literal, so the buffered
266
+ // `argsBuf` ends with something like `"content":"…some prefix` —
267
+ // not parseable.
268
+ //
269
+ // Two cases coalesce here:
270
+ // 1. The buffer parses cleanly (the provider just forgot to emit
271
+ // `content_block_stop` but the args are intact) — keep parsed.
272
+ // 2. The buffer is truncated mid-literal — `parsed = {}` is the
273
+ // safe fallback so the executor's `JSON.parse(arguments)`
274
+ // succeeds and downstream consumers don't crash. The PRICE
275
+ // we used to pay was the model getting back a generic
276
+ // "<field> is required" Zod error and not realising its
277
+ // previous tool call was truncated server-side, so it would
278
+ // retry with the SAME long input and hit the same cutoff in
279
+ // a loop. Detect the truncation case and mark the tool call
280
+ // with runtime metadata; the executor surfaces a specific
281
+ // "your tool call was cut off by max_tokens — retry with
282
+ // shorter input or split into smaller calls" message that the
283
+ // model can act on.
178
284
  for (const bucket of toolBuckets.values()) {
179
285
  if (bucket.started && !bucket.completed) {
180
286
  bucket.completed = true;
181
287
  let parsed = {};
182
- try {
183
- parsed = bucket.argsBuf ? JSON.parse(bucket.argsBuf) : {};
288
+ let truncated = false;
289
+ if (bucket.argsBuf) {
290
+ try {
291
+ parsed = JSON.parse(bucket.argsBuf);
292
+ }
293
+ catch {
294
+ // argsBuf had content but didn't parse — almost
295
+ // certainly the max_tokens-mid-literal cutoff. Mark
296
+ // the bucket so the executor can return a model-
297
+ // readable hint instead of a generic Zod error.
298
+ truncated = true;
299
+ parsed = {};
300
+ }
184
301
  }
185
- catch {
186
- // leave parsed = {}
302
+ bucket.parsed = parsed;
303
+ bucket.inputTruncated = truncated;
304
+ if (truncated) {
305
+ log.warn('tool input truncated by upstream cutoff (no toolCallEnd, argsBuf unparsable)', {
306
+ runId,
307
+ toolUseId: bucket.id,
308
+ toolName: bucket.name,
309
+ bufferLength: bucket.argsBuf.length,
310
+ });
187
311
  }
188
312
  await emitEvent({
189
313
  type: 'tool_input_completed',
190
314
  runId,
191
315
  toolUseId: bucket.id,
192
316
  input: parsed,
317
+ ...(truncated ? { inputTruncated: true } : {}),
193
318
  });
194
319
  yield* drainPending();
195
320
  }
196
321
  }
322
+ // `arguments` MUST be valid JSON for the executor's `JSON.parse`
323
+ // (`runtime/query/executor.ts:executeSingle`) to succeed. We
324
+ // always serialise from the bucket's `parsed` object (filled by
325
+ // either the `toolCallEnd` branch above or the post-stream flush
326
+ // loop) instead of re-emitting `argsBuf`. When the provider
327
+ // stream truncated mid-input, `metadata.inputTruncated` carries that
328
+ // state; the executor parses cleanly and returns a specific
329
+ // model-readable retry hint instead of the generic "Invalid JSON in
330
+ // tool arguments" intercept.
331
+ const toolCalls = [...toolBuckets.entries()]
332
+ .sort(([a], [b]) => a - b)
333
+ .map(([, b]) => ({
334
+ id: b.id,
335
+ type: 'function',
336
+ function: {
337
+ name: b.name,
338
+ arguments: JSON.stringify(b.parsed ?? {}),
339
+ },
340
+ ...(b.inputTruncated ? { metadata: { inputTruncated: true } } : {}),
341
+ }));
342
+ const recoveredToolInputFromStreamError = streamError !== undefined && toolCalls.some((tc) => tc.id && tc.function.name);
343
+ const effectiveFinishReason = recoveredToolInputFromStreamError ? 'tool_calls' : finishReason;
344
+ if (recoveredToolInputFromStreamError) {
345
+ log.warn('provider stream failed after tool input; surfacing tool call to executor', {
346
+ runId,
347
+ iteration,
348
+ error: streamError,
349
+ toolCallCount: toolCalls.length,
350
+ });
351
+ }
197
352
  const stopReason = streamError
198
- ? 'refusal'
199
- : synthesizeMessageStopReason(finishReason, forceFinalize);
353
+ ? recoveredToolInputFromStreamError
354
+ ? 'tool_use'
355
+ : 'refusal'
356
+ : synthesizeMessageStopReason(effectiveFinishReason, forceFinalize);
200
357
  await emitEvent({
201
358
  type: 'message_completed',
202
359
  runId,
@@ -207,16 +364,9 @@ async function* streamProviderTurn(provider, params, emitEvent, drainPending, ru
207
364
  content: textBuf || undefined,
208
365
  });
209
366
  yield* drainPending();
210
- if (streamError) {
367
+ if (streamError && !recoveredToolInputFromStreamError) {
211
368
  throw new Error(`Provider stream error: ${streamError}`);
212
369
  }
213
- const toolCalls = [...toolBuckets.entries()]
214
- .sort(([a], [b]) => a - b)
215
- .map(([, b]) => ({
216
- id: b.id,
217
- type: 'function',
218
- function: { name: b.name, arguments: b.argsBuf },
219
- }));
220
370
  const response = {
221
371
  id: id || messageId,
222
372
  model: model || params.model,
@@ -225,7 +375,7 @@ async function* streamProviderTurn(provider, params, emitEvent, drainPending, ru
225
375
  content: textBuf.length > 0 ? textBuf : null,
226
376
  toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
227
377
  },
228
- finishReason,
378
+ finishReason: effectiveFinishReason,
229
379
  usage,
230
380
  };
231
381
  return { response, messageId };
@@ -255,6 +405,7 @@ export class IterationOrchestrator {
255
405
  launchedTasks: config.launchedTasks ?? new Map(),
256
406
  compactionConfig: config.compactionConfig,
257
407
  workingStateManager: config.workingStateManager,
408
+ workingMemoryProvider: config.workingMemoryProvider,
258
409
  advisoryCtx: config.advisoryCtx,
259
410
  agentBus: config.agentBus,
260
411
  verificationGate: config.verificationGate,
@@ -265,17 +416,19 @@ export class IterationOrchestrator {
265
416
  const { runConfig, runMgr } = this.ctx;
266
417
  const { model } = runConfig;
267
418
  const tracer = getTracer();
419
+ // Worker-completion delivery used to fan out through a global
420
+ // onTaskCompleted listener that pushed handles onto
421
+ // `pendingNotifications`; the iteration loop then drained
422
+ // them as <task-notification> envelopes. Both `create_task`
423
+ // and the `Agent` tool are now blocking and return their
424
+ // worker output as the dispatching tool_use's canonical
425
+ // tool_result, so the listener path would only DUPLICATE
426
+ // every completion (once as tool_result, once as injected
427
+ // envelope user-message). Leaving the binding out closes
428
+ // the duplicate notification surface entirely; the dormant
429
+ // drain stays as a no-op until a follow-up tears it out.
268
430
  let unsubscribeTaskListener;
269
- if (this.ctx.taskGateway) {
270
- unsubscribeTaskListener = this.ctx.taskGateway.onTaskCompleted((handle) => {
271
- this.ctx.pendingNotifications.push(handle);
272
- this.ctx.log.debug('Task completion queued for notification', {
273
- taskId: handle.taskId,
274
- agentId: handle.agentId,
275
- state: handle.state,
276
- });
277
- });
278
- }
431
+ void unsubscribeTaskListener;
279
432
  try {
280
433
  const planSignal = yield* runPlanGate(this.ctx);
281
434
  if (planSignal === 'stop')
@@ -339,10 +492,18 @@ export class IterationOrchestrator {
339
492
  if (this.ctx.pendingNotifications.length > 0) {
340
493
  await this.injectOneTaskNotification();
341
494
  }
495
+ // Re-pin the working-memory block from ground truth at the primacy
496
+ // edge BEFORE compaction runs (so the refreshed slot is what
497
+ // compaction preserves). No-op when no provider is configured.
498
+ await refreshWorkingMemory(this.ctx);
342
499
  await runCompactionCheck(this.ctx);
343
- const openAITools = forceFinalize
344
- ? undefined
345
- : this.ctx.tools.toLLMTools(this.ctx.allowedTools);
500
+ // Cache discipline: keep the tools param byte-stable even on the
501
+ // forced-final iteration and forbid tool use via tool_choice
502
+ // 'none' instead. Dropping the tools array would invalidate the
503
+ // entire prompt-cache prefix (tools render at position 0) and
504
+ // risks a 400 because the history still carries
505
+ // tool_use/tool_result blocks.
506
+ const openAITools = this.ctx.tools.toLLMTools(this.ctx.allowedTools);
346
507
  const messages = forceFinalize
347
508
  ? [
348
509
  ...runMgr.messages,
@@ -365,10 +526,15 @@ export class IterationOrchestrator {
365
526
  const { response } = yield* streamProviderTurn(this.ctx.provider, {
366
527
  model,
367
528
  messages,
368
- tools: openAITools && openAITools.length > 0 ? openAITools : undefined,
529
+ tools: openAITools.length > 0 ? openAITools : undefined,
530
+ toolChoice: forceFinalize && openAITools.length > 0 ? 'none' : undefined,
369
531
  temperature: runConfig.temperature,
370
532
  maxTokens: runConfig.maxResponseTokens,
371
533
  cacheControl: { type: 'auto' },
534
+ // Thread the run abort into the model call so a Stop tears the
535
+ // in-flight turn down (provider passes it to fetch; the consumer
536
+ // also races it). Inert when never aborted.
537
+ signal: this.ctx.abortController.signal,
372
538
  }, this.ctx.emitEvent, this.ctx.drainPending, runMgr.id, iterationNum, forceFinalize, this.ctx.log);
373
539
  runMgr.accumulateUsage(response.usage);
374
540
  if (this.ctx.pluginManager) {
@@ -434,6 +600,42 @@ export class IterationOrchestrator {
434
600
  continue;
435
601
  }
436
602
  const hasContent = response.message.content !== null && response.message.content.length > 0;
603
+ // Auto-continuation on `stop_reason: max_tokens`. The
604
+ // model hit its per-call output cap mid-text (NOT
605
+ // mid-tool-use — that path is handled separately
606
+ // below via `inputTruncated`). Push a synthetic
607
+ // "continue" user message and let the loop fire
608
+ // another turn. The provider receives the partial
609
+ // assistant content + the continue prompt and
610
+ // resumes from where it left off, mirroring the
611
+ // Claude.ai "Continue" affordance.
612
+ //
613
+ // Guards:
614
+ // - `hasContent` so we don't loop forever on an
615
+ // empty cutoff (Anthropic occasionally emits
616
+ // `stop_reason: max_tokens` with no content
617
+ // when an injected pre-fill blocks the model).
618
+ // - `!forceFinalize` so the forced-finalize path
619
+ // never auto-continues — that path is invoked
620
+ // specifically to extract a closing summary.
621
+ // - max_iterations bounds the loop in any case.
622
+ if (!forceFinalize && response.finishReason === 'length' && hasContent) {
623
+ this.ctx.log.info('LLM hit max_tokens mid-text — auto-continuing', {
624
+ runId: runMgr.id,
625
+ iteration: iterationNum,
626
+ completionTokens: response.usage.completionTokens,
627
+ });
628
+ runMgr.pushMessage(createUserMessage(AUTO_CONTINUATION_USER_MESSAGE));
629
+ await this.ctx.emitEvent({
630
+ type: 'iteration_completed',
631
+ runId: runMgr.id,
632
+ iteration: iterationNum,
633
+ hasToolCalls: false,
634
+ });
635
+ yield* this.ctx.drainPending();
636
+ iterSpan.end();
637
+ continue;
638
+ }
437
639
  if (!hasContent && !forceFinalize) {
438
640
  this.ctx.log.warn('Empty completion detected — requesting final summary', {
439
641
  iteration: iterationNum,
@@ -449,6 +651,16 @@ export class IterationOrchestrator {
449
651
  hasToolCalls: false,
450
652
  });
451
653
  yield* this.ctx.drainPending();
654
+ // A Stop that lands AFTER the final turn streamed but before
655
+ // this break must settle the run as cancelled, not end_turn —
656
+ // otherwise the just-produced answer is recorded as a clean
657
+ // completion. Mirrors the between-iteration cancel at :511.
658
+ if (this.ctx.abortController.signal.aborted) {
659
+ runMgr.setStopReason('cancelled');
660
+ runMgr.markCancelled();
661
+ iterSpan.end();
662
+ break;
663
+ }
452
664
  runMgr.setStopReason('end_turn');
453
665
  iterSpan.end();
454
666
  break;
@@ -483,6 +695,19 @@ export class IterationOrchestrator {
483
695
  iterSpan.end();
484
696
  }
485
697
  catch (err) {
698
+ // A Stop that aborted the in-flight turn surfaces here as a
699
+ // thrown abort (the provider stream was raced against the run
700
+ // signal). Settle it as a CANCELLATION — mirroring the
701
+ // between-iteration cancel at the top of the loop — rather than
702
+ // recording it as an SDK failure (error span + failed activity)
703
+ // and re-throwing. The run then returns cleanly with a
704
+ // 'cancelled' stop reason instead of propagating an error.
705
+ if (this.ctx.abortController.signal.aborted) {
706
+ runMgr.setStopReason('cancelled');
707
+ runMgr.markCancelled();
708
+ iterSpan.end();
709
+ break;
710
+ }
486
711
  if (iterationActivity) {
487
712
  this.ctx.activityStore.fail(iterationActivity.id, toErrorMessage(err));
488
713
  }
@@ -516,42 +741,68 @@ export class IterationOrchestrator {
516
741
  }
517
742
  await this.injectOneTaskNotification();
518
743
  }
744
+ /**
745
+ * Canonical async completion delivery (ses_009-task-notification-envelope).
746
+ *
747
+ * Drains every pending task completion in one pass and emits each as
748
+ * a plain USER text message wrapped in the `<task-notification>`
749
+ * envelope the supervisor prompt expects.
750
+ *
751
+ * Why not a `tool_result` block bound to the dispatching tool_use_id:
752
+ * `create_task` is documented as NON-BLOCKING and returns
753
+ * "Task launched: …" immediately. That immediate return is already
754
+ * recorded as the canonical tool_result for that tool_use, so a
755
+ * second tool_result for the SAME tool_use_id — emitted later, after
756
+ * intervening assistant turns — is rejected by Anthropic with
757
+ * `messages.<n>.content.0: unexpected tool_use_id found in
758
+ * tool_result blocks` because the immediately-prior assistant
759
+ * message no longer carries the matching tool_use. Wrapping as a
760
+ * user text envelope sidesteps the pairing rule entirely.
761
+ *
762
+ * Coalescing N drops into one drain replaces the previous
763
+ * one-at-a-time pattern which forced a separate orchestrator
764
+ * iteration per completed task on wide fan-outs.
765
+ */
519
766
  async injectOneTaskNotification() {
520
- const handle = this.ctx.pendingNotifications.shift();
521
- if (!handle)
767
+ if (this.ctx.pendingNotifications.length === 0)
522
768
  return;
523
- const meta = this.ctx.launchedTasks.get(handle.taskId);
524
- const resultText = handle.result?.result ??
525
- handle.result?.lastError ??
526
- `Task finished with state: ${handle.state}`;
527
- if (meta?.planTaskId && this.ctx.taskStore) {
528
- const success = handle.state === 'completed';
529
- await this.ctx.taskStore.update(meta.planTaskId, {
530
- status: 'completed',
531
- description: success ? undefined : `Failed: ${resultText.substring(0, 200)}`,
769
+ const handles = this.ctx.pendingNotifications.splice(0);
770
+ for (const handle of handles) {
771
+ const meta = this.ctx.launchedTasks.get(handle.taskId);
772
+ const resultText = handle.result?.result ??
773
+ handle.result?.lastError ??
774
+ `Task finished with state: ${handle.state}`;
775
+ if (meta?.planTaskId && this.ctx.taskStore) {
776
+ const success = handle.state === 'completed';
777
+ await this.ctx.taskStore.update(meta.planTaskId, {
778
+ status: 'completed',
779
+ description: success ? undefined : `Failed: ${resultText.substring(0, 200)}`,
780
+ });
781
+ }
782
+ this.ctx.launchedTasks.delete(handle.taskId);
783
+ // `remaining-tasks` = inflight workers still pending after this
784
+ // one drains. `launchedTasks` is the single source of truth:
785
+ // it holds every dispatched worker that has NOT yet been
786
+ // drained + delete()'d. The drain batch entries are still
787
+ // inside launchedTasks until each iteration's delete() above
788
+ // removes them, so reading the size right after that delete
789
+ // gives the honest count. Adding `handles.length - 1 - i`
790
+ // here used to double-count this same queue.
791
+ const remainingTasks = this.ctx.launchedTasks.size;
792
+ const envelope = `<task-notification>\n<task-id>${xmlEscape(handle.taskId)}</task-id>\n` +
793
+ `<agent-id>${xmlEscape(handle.agentId)}</agent-id>\n` +
794
+ `<status>${xmlEscape(handle.state)}</status>\n` +
795
+ `<result>${cdataWrap(resultText)}</result>\n` +
796
+ `<remaining-tasks>${remainingTasks}</remaining-tasks>\n</task-notification>`;
797
+ this.ctx.runMgr.pushMessage(createUserMessage(envelope));
798
+ this.ctx.log.info('Task notification injected', {
799
+ taskId: handle.taskId,
800
+ agentId: handle.agentId,
801
+ state: handle.state,
802
+ planTaskId: meta?.planTaskId,
803
+ remainingNotifications: remainingTasks,
532
804
  });
533
805
  }
534
- this.ctx.launchedTasks.delete(handle.taskId);
535
- const remainingTasks = this.ctx.launchedTasks.size;
536
- const notification = [
537
- '<task-notification>',
538
- ` <task-id>${handle.taskId}</task-id>`,
539
- ` <agent-id>${handle.agentId}</agent-id>`,
540
- ` <status>${handle.state}</status>`,
541
- ` <description>${meta?.description ?? 'agent task'}</description>`,
542
- ` <result>${resultText}</result>`,
543
- ` <remaining-tasks>${remainingTasks}</remaining-tasks>`,
544
- '</task-notification>',
545
- ].join('\n');
546
- this.ctx.runMgr.pushMessage(createUserMessage(notification));
547
- this.ctx.log.info('Task notification injected', {
548
- taskId: handle.taskId,
549
- agentId: handle.agentId,
550
- state: handle.state,
551
- planTaskId: meta?.planTaskId,
552
- remainingTasks,
553
- remainingNotifications: this.ctx.pendingNotifications.length,
554
- });
555
806
  }
556
807
  async requestFinalResponse(model, reason) {
557
808
  const lastAssistant = [...this.ctx.runMgr.messages]
@@ -570,12 +821,21 @@ export class IterationOrchestrator {
570
821
  ...this.ctx.runMgr.messages,
571
822
  createUserMessage(`[SYSTEM] Run is ending due to ${reason}. You MUST provide a final response now summarizing all your findings and work so far. Do not use any tools.`),
572
823
  ];
824
+ // Same cache discipline as the forced-final iteration: keep the
825
+ // tools param identical to prior iterations (cache prefix intact,
826
+ // no 400 on tool blocks in history) and forbid use via tool_choice.
827
+ const finalTools = this.ctx.tools.toLLMTools(this.ctx.allowedTools);
573
828
  const response = await collect(this.ctx.provider.chatStream({
574
829
  model,
575
830
  messages: finalMessages,
831
+ tools: finalTools.length > 0 ? finalTools : undefined,
832
+ toolChoice: finalTools.length > 0 ? 'none' : undefined,
576
833
  temperature: this.ctx.runConfig.temperature,
577
834
  maxTokens: this.ctx.runConfig.maxResponseTokens,
578
835
  cacheControl: { type: 'auto' },
836
+ // Cancellable too: a Stop during the closing summary must not
837
+ // stream to completion.
838
+ signal: this.ctx.abortController.signal,
579
839
  }));
580
840
  this.ctx.runMgr.accumulateUsage(response.usage);
581
841
  const assistantMsg = createAssistantMessage(response.message.content);