@namzu/sdk 51.1.0 → 52.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 (395) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/dist/agents/QueryAgent.d.ts.map +1 -1
  3. package/dist/agents/QueryAgent.js +2 -0
  4. package/dist/agents/QueryAgent.js.map +1 -1
  5. package/dist/agents/SupervisorAgent.d.ts +1 -1
  6. package/dist/agents/SupervisorAgent.d.ts.map +1 -1
  7. package/dist/agents/SupervisorAgent.js.map +1 -1
  8. package/dist/agents/examples/SupervisorAgent.d.ts +42 -0
  9. package/dist/agents/examples/SupervisorAgent.d.ts.map +1 -1
  10. package/dist/agents/examples/SupervisorAgent.js +80 -4
  11. package/dist/agents/examples/SupervisorAgent.js.map +1 -1
  12. package/dist/agents/index.d.ts +1 -1
  13. package/dist/agents/index.d.ts.map +1 -1
  14. package/dist/agents/index.js.map +1 -1
  15. package/dist/authorization/protected-paths.d.ts +36 -0
  16. package/dist/authorization/protected-paths.d.ts.map +1 -0
  17. package/dist/authorization/protected-paths.js +107 -0
  18. package/dist/authorization/protected-paths.js.map +1 -0
  19. package/dist/authorization/readonly-commands.d.ts +39 -0
  20. package/dist/authorization/readonly-commands.d.ts.map +1 -0
  21. package/dist/authorization/readonly-commands.js +797 -0
  22. package/dist/authorization/readonly-commands.js.map +1 -0
  23. package/dist/authorization/readonly-flags.d.ts +19 -0
  24. package/dist/authorization/readonly-flags.d.ts.map +1 -0
  25. package/dist/authorization/readonly-flags.js +178 -0
  26. package/dist/authorization/readonly-flags.js.map +1 -0
  27. package/dist/authorization/removal.d.ts +52 -0
  28. package/dist/authorization/removal.d.ts.map +1 -0
  29. package/dist/authorization/removal.js +283 -0
  30. package/dist/authorization/removal.js.map +1 -0
  31. package/dist/authorization/shell-paths.d.ts +66 -0
  32. package/dist/authorization/shell-paths.d.ts.map +1 -0
  33. package/dist/authorization/shell-paths.js +697 -0
  34. package/dist/authorization/shell-paths.js.map +1 -0
  35. package/dist/bridge/acp/update.d.ts.map +1 -1
  36. package/dist/bridge/acp/update.js +1 -0
  37. package/dist/bridge/acp/update.js.map +1 -1
  38. package/dist/compaction/context-window.d.ts +24 -0
  39. package/dist/compaction/context-window.d.ts.map +1 -1
  40. package/dist/compaction/context-window.js +34 -0
  41. package/dist/compaction/context-window.js.map +1 -1
  42. package/dist/compaction/manual.d.ts.map +1 -1
  43. package/dist/compaction/manual.js +10 -2
  44. package/dist/compaction/manual.js.map +1 -1
  45. package/dist/compaction/plan.d.ts +4 -0
  46. package/dist/compaction/plan.d.ts.map +1 -1
  47. package/dist/compaction/plan.js +6 -2
  48. package/dist/compaction/plan.js.map +1 -1
  49. package/dist/compaction/verifier.d.ts +11 -1
  50. package/dist/compaction/verifier.d.ts.map +1 -1
  51. package/dist/compaction/verifier.js +75 -23
  52. package/dist/compaction/verifier.js.map +1 -1
  53. package/dist/config/runtime.d.ts +55 -7
  54. package/dist/config/runtime.d.ts.map +1 -1
  55. package/dist/config/runtime.js +23 -2
  56. package/dist/config/runtime.js.map +1 -1
  57. package/dist/connector/web/guarded-fetch.d.ts.map +1 -1
  58. package/dist/connector/web/guarded-fetch.js +24 -0
  59. package/dist/connector/web/guarded-fetch.js.map +1 -1
  60. package/dist/connector/web/index.d.ts +1 -1
  61. package/dist/connector/web/index.d.ts.map +1 -1
  62. package/dist/connector/web/page.d.ts +59 -0
  63. package/dist/connector/web/page.d.ts.map +1 -0
  64. package/dist/connector/web/page.js +112 -0
  65. package/dist/connector/web/page.js.map +1 -0
  66. package/dist/connector/web/types.d.ts +47 -0
  67. package/dist/connector/web/types.d.ts.map +1 -1
  68. package/dist/connector/web/types.js.map +1 -1
  69. package/dist/constants/continuation.d.ts +8 -0
  70. package/dist/constants/continuation.d.ts.map +1 -1
  71. package/dist/constants/continuation.js +8 -0
  72. package/dist/constants/continuation.js.map +1 -1
  73. package/dist/constants/plugin/index.d.ts +1 -1
  74. package/dist/constants/plugin/index.d.ts.map +1 -1
  75. package/dist/constants/plugin/index.js +1 -1
  76. package/dist/constants/plugin/index.js.map +1 -1
  77. package/dist/contracts/a2a.d.ts +2 -2
  78. package/dist/manager/agent/lifecycle.d.ts +7 -0
  79. package/dist/manager/agent/lifecycle.d.ts.map +1 -1
  80. package/dist/manager/agent/lifecycle.js +11 -2
  81. package/dist/manager/agent/lifecycle.js.map +1 -1
  82. package/dist/manager/session/turn-recorder.d.ts.map +1 -1
  83. package/dist/manager/session/turn-recorder.js +4 -2
  84. package/dist/manager/session/turn-recorder.js.map +1 -1
  85. package/dist/plugin/index.d.ts +1 -1
  86. package/dist/plugin/index.d.ts.map +1 -1
  87. package/dist/plugin/index.js +1 -1
  88. package/dist/plugin/index.js.map +1 -1
  89. package/dist/plugin/lifecycle.d.ts +6 -0
  90. package/dist/plugin/lifecycle.d.ts.map +1 -1
  91. package/dist/plugin/lifecycle.js +98 -114
  92. package/dist/plugin/lifecycle.js.map +1 -1
  93. package/dist/plugin/shell-hook.d.ts +90 -22
  94. package/dist/plugin/shell-hook.d.ts.map +1 -1
  95. package/dist/plugin/shell-hook.js +434 -41
  96. package/dist/plugin/shell-hook.js.map +1 -1
  97. package/dist/provider/capabilities.d.ts +9 -0
  98. package/dist/provider/capabilities.d.ts.map +1 -1
  99. package/dist/provider/capabilities.js +15 -0
  100. package/dist/provider/capabilities.js.map +1 -1
  101. package/dist/provider/errors.d.ts.map +1 -1
  102. package/dist/provider/errors.js +13 -0
  103. package/dist/provider/errors.js.map +1 -1
  104. package/dist/provider/fallback.d.ts.map +1 -1
  105. package/dist/provider/fallback.js +10 -0
  106. package/dist/provider/fallback.js.map +1 -1
  107. package/dist/provider/idle-timeout.d.ts.map +1 -1
  108. package/dist/provider/idle-timeout.js +5 -0
  109. package/dist/provider/idle-timeout.js.map +1 -1
  110. package/dist/provider/image-input-refusal.d.ts +9 -0
  111. package/dist/provider/image-input-refusal.d.ts.map +1 -0
  112. package/dist/provider/image-input-refusal.js +54 -0
  113. package/dist/provider/image-input-refusal.js.map +1 -0
  114. package/dist/provider/index.d.ts +2 -1
  115. package/dist/provider/index.d.ts.map +1 -1
  116. package/dist/provider/index.js +2 -1
  117. package/dist/provider/index.js.map +1 -1
  118. package/dist/provider/retry.d.ts +7 -2
  119. package/dist/provider/retry.d.ts.map +1 -1
  120. package/dist/provider/retry.js +38 -4
  121. package/dist/provider/retry.js.map +1 -1
  122. package/dist/public-runtime.d.ts +5 -2
  123. package/dist/public-runtime.d.ts.map +1 -1
  124. package/dist/public-runtime.js +5 -2
  125. package/dist/public-runtime.js.map +1 -1
  126. package/dist/public-types.d.ts +2 -2
  127. package/dist/public-types.d.ts.map +1 -1
  128. package/dist/runtime/jobs/registry.d.ts +21 -0
  129. package/dist/runtime/jobs/registry.d.ts.map +1 -1
  130. package/dist/runtime/jobs/registry.js +15 -2
  131. package/dist/runtime/jobs/registry.js.map +1 -1
  132. package/dist/runtime/query/deferred-sandbox.d.ts +54 -0
  133. package/dist/runtime/query/deferred-sandbox.d.ts.map +1 -0
  134. package/dist/runtime/query/deferred-sandbox.js +75 -0
  135. package/dist/runtime/query/deferred-sandbox.js.map +1 -0
  136. package/dist/runtime/query/executor/tool-call-admission.d.ts +10 -0
  137. package/dist/runtime/query/executor/tool-call-admission.d.ts.map +1 -1
  138. package/dist/runtime/query/executor/tool-call-admission.js +74 -13
  139. package/dist/runtime/query/executor/tool-call-admission.js.map +1 -1
  140. package/dist/runtime/query/executor.d.ts +51 -2
  141. package/dist/runtime/query/executor.d.ts.map +1 -1
  142. package/dist/runtime/query/executor.js +214 -9
  143. package/dist/runtime/query/executor.js.map +1 -1
  144. package/dist/runtime/query/finalize-turn.d.ts +6 -0
  145. package/dist/runtime/query/finalize-turn.d.ts.map +1 -1
  146. package/dist/runtime/query/finalize-turn.js +1 -1
  147. package/dist/runtime/query/finalize-turn.js.map +1 -1
  148. package/dist/runtime/query/index.d.ts +29 -0
  149. package/dist/runtime/query/index.d.ts.map +1 -1
  150. package/dist/runtime/query/index.js +108 -28
  151. package/dist/runtime/query/index.js.map +1 -1
  152. package/dist/runtime/query/iteration/index.d.ts +57 -0
  153. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  154. package/dist/runtime/query/iteration/index.js +380 -28
  155. package/dist/runtime/query/iteration/index.js.map +1 -1
  156. package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts +61 -0
  157. package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts.map +1 -0
  158. package/dist/runtime/query/iteration/phases/compaction-breaker.js +94 -0
  159. package/dist/runtime/query/iteration/phases/compaction-breaker.js.map +1 -0
  160. package/dist/runtime/query/iteration/phases/compaction.d.ts +42 -1
  161. package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
  162. package/dist/runtime/query/iteration/phases/compaction.js +203 -23
  163. package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
  164. package/dist/runtime/query/iteration/phases/context.d.ts +10 -0
  165. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  166. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  167. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  168. package/dist/runtime/query/iteration/phases/tool-review.js +165 -2
  169. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  170. package/dist/runtime/query/iteration/provider-rejected-image.d.ts +21 -1
  171. package/dist/runtime/query/iteration/provider-rejected-image.d.ts.map +1 -1
  172. package/dist/runtime/query/iteration/provider-rejected-image.js +53 -7
  173. package/dist/runtime/query/iteration/provider-rejected-image.js.map +1 -1
  174. package/dist/runtime/query/iteration/request-repair.d.ts +108 -0
  175. package/dist/runtime/query/iteration/request-repair.d.ts.map +1 -0
  176. package/dist/runtime/query/iteration/request-repair.js +593 -0
  177. package/dist/runtime/query/iteration/request-repair.js.map +1 -0
  178. package/dist/runtime/query/iteration/stream-turn.d.ts +6 -0
  179. package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
  180. package/dist/runtime/query/iteration/stream-turn.js +2 -2
  181. package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
  182. package/dist/runtime/query/path-escalation.d.ts +36 -0
  183. package/dist/runtime/query/path-escalation.d.ts.map +1 -0
  184. package/dist/runtime/query/path-escalation.js +184 -0
  185. package/dist/runtime/query/path-escalation.js.map +1 -0
  186. package/dist/runtime/query/plugin-hooks.d.ts.map +1 -1
  187. package/dist/runtime/query/plugin-hooks.js +8 -0
  188. package/dist/runtime/query/plugin-hooks.js.map +1 -1
  189. package/dist/runtime/query/reminders.d.ts +35 -0
  190. package/dist/runtime/query/reminders.d.ts.map +1 -0
  191. package/dist/runtime/query/reminders.js +7 -0
  192. package/dist/runtime/query/reminders.js.map +1 -0
  193. package/dist/runtime/query/request-rich-content.d.ts +5 -1
  194. package/dist/runtime/query/request-rich-content.d.ts.map +1 -1
  195. package/dist/runtime/query/request-rich-content.js +67 -25
  196. package/dist/runtime/query/request-rich-content.js.map +1 -1
  197. package/dist/runtime/query/resume-pending.d.ts.map +1 -1
  198. package/dist/runtime/query/resume-pending.js +26 -0
  199. package/dist/runtime/query/resume-pending.js.map +1 -1
  200. package/dist/runtime/query/review-policy.d.ts +26 -0
  201. package/dist/runtime/query/review-policy.d.ts.map +1 -1
  202. package/dist/runtime/query/review-policy.js +79 -5
  203. package/dist/runtime/query/review-policy.js.map +1 -1
  204. package/dist/runtime/query/tool-output-budget.d.ts +46 -0
  205. package/dist/runtime/query/tool-output-budget.d.ts.map +1 -1
  206. package/dist/runtime/query/tool-output-budget.js +85 -1
  207. package/dist/runtime/query/tool-output-budget.js.map +1 -1
  208. package/dist/runtime/query/tooling.d.ts +1 -0
  209. package/dist/runtime/query/tooling.d.ts.map +1 -1
  210. package/dist/runtime/query/tooling.js +5 -0
  211. package/dist/runtime/query/tooling.js.map +1 -1
  212. package/dist/session/paths.d.ts +5 -1
  213. package/dist/session/paths.d.ts.map +1 -1
  214. package/dist/session/paths.js +8 -0
  215. package/dist/session/paths.js.map +1 -1
  216. package/dist/store/memory/disk.d.ts.map +1 -1
  217. package/dist/store/memory/disk.js +2 -2
  218. package/dist/store/memory/disk.js.map +1 -1
  219. package/dist/store/memory/markdown.d.ts.map +1 -1
  220. package/dist/store/memory/markdown.js +2 -2
  221. package/dist/store/memory/markdown.js.map +1 -1
  222. package/dist/store/memory/naming.d.ts +9 -0
  223. package/dist/store/memory/naming.d.ts.map +1 -1
  224. package/dist/store/memory/naming.js +12 -0
  225. package/dist/store/memory/naming.js.map +1 -1
  226. package/dist/tools/builtins/bash.d.ts +30 -1
  227. package/dist/tools/builtins/bash.d.ts.map +1 -1
  228. package/dist/tools/builtins/bash.js +170 -26
  229. package/dist/tools/builtins/bash.js.map +1 -1
  230. package/dist/tools/builtins/computer-use.d.ts +1 -1
  231. package/dist/tools/builtins/computer-use.d.ts.map +1 -1
  232. package/dist/tools/builtins/computer-use.js +12 -5
  233. package/dist/tools/builtins/computer-use.js.map +1 -1
  234. package/dist/tools/builtins/content-fingerprint.d.ts +2 -0
  235. package/dist/tools/builtins/content-fingerprint.d.ts.map +1 -1
  236. package/dist/tools/builtins/content-fingerprint.js +4 -0
  237. package/dist/tools/builtins/content-fingerprint.js.map +1 -1
  238. package/dist/tools/builtins/edit.d.ts.map +1 -1
  239. package/dist/tools/builtins/edit.js +60 -18
  240. package/dist/tools/builtins/edit.js.map +1 -1
  241. package/dist/tools/builtins/grep.d.ts +8 -1
  242. package/dist/tools/builtins/grep.d.ts.map +1 -1
  243. package/dist/tools/builtins/grep.js +152 -33
  244. package/dist/tools/builtins/grep.js.map +1 -1
  245. package/dist/tools/builtins/read-file.d.ts +1 -0
  246. package/dist/tools/builtins/read-file.d.ts.map +1 -1
  247. package/dist/tools/builtins/read-file.js +44 -5
  248. package/dist/tools/builtins/read-file.js.map +1 -1
  249. package/dist/tools/builtins/read-render.d.ts +52 -0
  250. package/dist/tools/builtins/read-render.d.ts.map +1 -1
  251. package/dist/tools/builtins/read-render.js +143 -0
  252. package/dist/tools/builtins/read-render.js.map +1 -1
  253. package/dist/tools/builtins/web.d.ts +13 -2
  254. package/dist/tools/builtins/web.d.ts.map +1 -1
  255. package/dist/tools/builtins/web.js +225 -32
  256. package/dist/tools/builtins/web.js.map +1 -1
  257. package/dist/tools/coordinator/ask-user-question.d.ts +2 -0
  258. package/dist/tools/coordinator/ask-user-question.d.ts.map +1 -1
  259. package/dist/tools/coordinator/ask-user-question.js +256 -92
  260. package/dist/tools/coordinator/ask-user-question.js.map +1 -1
  261. package/dist/tools/coordinator/question-options.d.ts +1 -0
  262. package/dist/tools/coordinator/question-options.d.ts.map +1 -1
  263. package/dist/tools/coordinator/question-options.js +6 -1
  264. package/dist/tools/coordinator/question-options.js.map +1 -1
  265. package/dist/tools/defineTool.d.ts +4 -1
  266. package/dist/tools/defineTool.d.ts.map +1 -1
  267. package/dist/tools/defineTool.js +6 -1
  268. package/dist/tools/defineTool.js.map +1 -1
  269. package/dist/tools/paths.d.ts +6 -0
  270. package/dist/tools/paths.d.ts.map +1 -1
  271. package/dist/tools/paths.js +1 -1
  272. package/dist/tools/paths.js.map +1 -1
  273. package/dist/types/agent/query.d.ts +3 -0
  274. package/dist/types/agent/query.d.ts.map +1 -1
  275. package/dist/types/errors/catalog.js +1 -1
  276. package/dist/types/errors/catalog.js.map +1 -1
  277. package/dist/types/hitl/index.d.ts +104 -0
  278. package/dist/types/hitl/index.d.ts.map +1 -1
  279. package/dist/types/hitl/index.js.map +1 -1
  280. package/dist/types/message/index.d.ts +1 -1
  281. package/dist/types/message/index.d.ts.map +1 -1
  282. package/dist/types/message/index.js +2 -0
  283. package/dist/types/message/index.js.map +1 -1
  284. package/dist/types/plugin/index.d.ts +93 -19
  285. package/dist/types/plugin/index.d.ts.map +1 -1
  286. package/dist/types/plugin/index.js +3 -0
  287. package/dist/types/plugin/index.js.map +1 -1
  288. package/dist/types/provider/errors.d.ts +19 -0
  289. package/dist/types/provider/errors.d.ts.map +1 -1
  290. package/dist/types/provider/errors.js +54 -5
  291. package/dist/types/provider/errors.js.map +1 -1
  292. package/dist/types/provider/interface.d.ts +12 -0
  293. package/dist/types/provider/interface.d.ts.map +1 -1
  294. package/dist/types/provider/stream.d.ts +1 -1
  295. package/dist/types/session/config.d.ts +14 -0
  296. package/dist/types/session/config.d.ts.map +1 -1
  297. package/dist/types/session/events.d.ts +27 -4
  298. package/dist/types/session/events.d.ts.map +1 -1
  299. package/dist/types/session/events.js.map +1 -1
  300. package/dist/types/session/records.d.ts +9 -9
  301. package/dist/types/session/records.d.ts.map +1 -1
  302. package/dist/types/session/records.js +1 -0
  303. package/dist/types/session/records.js.map +1 -1
  304. package/dist/types/session/stop-reason.d.ts +5 -0
  305. package/dist/types/session/stop-reason.d.ts.map +1 -1
  306. package/dist/types/tool/index.d.ts +52 -0
  307. package/dist/types/tool/index.d.ts.map +1 -1
  308. package/dist/types/tool/index.js.map +1 -1
  309. package/package.json +4 -2
  310. package/src/agents/QueryAgent.ts +5 -1
  311. package/src/agents/SupervisorAgent.ts +1 -1
  312. package/src/agents/examples/SupervisorAgent.ts +217 -106
  313. package/src/agents/index.ts +1 -1
  314. package/src/authorization/protected-paths.ts +110 -0
  315. package/src/authorization/readonly-commands.ts +771 -0
  316. package/src/authorization/readonly-flags.ts +196 -0
  317. package/src/authorization/removal.ts +326 -0
  318. package/src/authorization/shell-paths.ts +927 -0
  319. package/src/bridge/acp/update.ts +1 -0
  320. package/src/compaction/context-window.ts +41 -0
  321. package/src/compaction/manual.ts +10 -2
  322. package/src/compaction/plan.ts +11 -2
  323. package/src/compaction/verifier.ts +84 -22
  324. package/src/config/runtime.ts +23 -2
  325. package/src/connector/web/guarded-fetch.ts +26 -0
  326. package/src/connector/web/index.ts +3 -0
  327. package/src/connector/web/page.ts +130 -0
  328. package/src/connector/web/types.ts +47 -0
  329. package/src/constants/continuation.ts +10 -0
  330. package/src/constants/plugin/index.ts +1 -1
  331. package/src/manager/agent/lifecycle.ts +12 -4
  332. package/src/manager/session/turn-recorder.ts +10 -2
  333. package/src/plugin/index.ts +1 -0
  334. package/src/plugin/lifecycle.ts +107 -119
  335. package/src/plugin/shell-hook.ts +477 -43
  336. package/src/provider/capabilities.ts +17 -0
  337. package/src/provider/errors.ts +13 -0
  338. package/src/provider/fallback.ts +10 -0
  339. package/src/provider/idle-timeout.ts +5 -0
  340. package/src/provider/image-input-refusal.ts +54 -0
  341. package/src/provider/index.ts +2 -0
  342. package/src/provider/retry.ts +42 -4
  343. package/src/public-runtime.ts +11 -0
  344. package/src/public-types.ts +4 -0
  345. package/src/runtime/jobs/registry.ts +39 -2
  346. package/src/runtime/query/deferred-sandbox.ts +110 -0
  347. package/src/runtime/query/executor/tool-call-admission.ts +79 -13
  348. package/src/runtime/query/executor.ts +267 -11
  349. package/src/runtime/query/finalize-turn.ts +7 -1
  350. package/src/runtime/query/index.ts +169 -52
  351. package/src/runtime/query/iteration/index.ts +439 -35
  352. package/src/runtime/query/iteration/phases/compaction-breaker.ts +108 -0
  353. package/src/runtime/query/iteration/phases/compaction.ts +241 -38
  354. package/src/runtime/query/iteration/phases/context.ts +11 -0
  355. package/src/runtime/query/iteration/phases/tool-review.ts +177 -2
  356. package/src/runtime/query/iteration/provider-rejected-image.ts +71 -5
  357. package/src/runtime/query/iteration/request-repair.ts +743 -0
  358. package/src/runtime/query/iteration/stream-turn.ts +11 -1
  359. package/src/runtime/query/path-escalation.ts +204 -0
  360. package/src/runtime/query/plugin-hooks.ts +8 -0
  361. package/src/runtime/query/reminders.ts +45 -0
  362. package/src/runtime/query/request-rich-content.ts +77 -27
  363. package/src/runtime/query/resume-pending.ts +27 -0
  364. package/src/runtime/query/review-policy.ts +106 -5
  365. package/src/runtime/query/tool-output-budget.ts +115 -1
  366. package/src/runtime/query/tooling.ts +6 -0
  367. package/src/session/paths.ts +11 -1
  368. package/src/store/memory/disk.ts +2 -1
  369. package/src/store/memory/markdown.ts +2 -1
  370. package/src/store/memory/naming.ts +13 -0
  371. package/src/tools/builtins/bash.ts +203 -34
  372. package/src/tools/builtins/computer-use.ts +13 -4
  373. package/src/tools/builtins/content-fingerprint.ts +5 -0
  374. package/src/tools/builtins/edit.ts +69 -19
  375. package/src/tools/builtins/grep.ts +159 -38
  376. package/src/tools/builtins/read-file.ts +48 -5
  377. package/src/tools/builtins/read-render.ts +193 -0
  378. package/src/tools/builtins/web.ts +258 -33
  379. package/src/tools/coordinator/ask-user-question.ts +309 -103
  380. package/src/tools/coordinator/question-options.ts +8 -1
  381. package/src/tools/defineTool.ts +11 -2
  382. package/src/tools/paths.ts +1 -1
  383. package/src/types/agent/query.ts +3 -0
  384. package/src/types/errors/catalog.ts +1 -1
  385. package/src/types/hitl/index.ts +104 -0
  386. package/src/types/message/index.ts +2 -0
  387. package/src/types/plugin/index.ts +101 -18
  388. package/src/types/provider/errors.ts +66 -5
  389. package/src/types/provider/interface.ts +13 -0
  390. package/src/types/provider/stream.ts +1 -1
  391. package/src/types/session/config.ts +14 -0
  392. package/src/types/session/events.ts +32 -4
  393. package/src/types/session/records.ts +1 -0
  394. package/src/types/session/stop-reason.ts +5 -0
  395. package/src/types/tool/index.ts +53 -2
@@ -1,12 +1,12 @@
1
1
  import { SpanStatusCode } from '@opentelemetry/api';
2
2
  import { extractFromAssistantMessage, extractFromUserMessage, } from '../../../compaction/extractor.js';
3
3
  import { estimateMessageTokens } from '../../../compaction/token-estimate.js';
4
- import { AUTO_CONTINUATION_USER_MESSAGE } from '../../../constants/continuation.js';
4
+ import { AUTO_CONTINUATION_USER_MESSAGE, REASONING_CUT_USER_MESSAGE, } from '../../../constants/continuation.js';
5
5
  import { CHARS_PER_TOKEN } from '../../../constants/limits.js';
6
6
  import { DEFAULT_STRUCTURED_OUTPUT_RETRIES, STRUCTURED_OUTPUT_REPROMPT, } from '../../../constants/tools/index.js';
7
7
  import { withStreamedMessageIdentity } from '../../../manager/session/streamed-message-identity.js';
8
8
  import { renderSkillsSection } from '../../../persona/assembler.js';
9
- import { resolveProviderCapabilities } from '../../../provider/capabilities.js';
9
+ import { modelReadsImages, resolveProviderCapabilities } from '../../../provider/capabilities.js';
10
10
  import { collectChatCompletion } from '../../../provider/collect-chat-completion.js';
11
11
  import { renderToolSchema } from '../../../registry/tool/schema.js';
12
12
  import { formatCompletionNotification } from '../../../scheduler/completion-inbox.js';
@@ -15,12 +15,13 @@ import { getTracer } from '../../../telemetry/runtime-accessors.js';
15
15
  import { STRUCTURED_OUTPUT_TOOL_NAME } from '../../../tools/builtins/structuredOutput.js';
16
16
  import { NamzuError } from '../../../types/errors/index.js';
17
17
  import { createAssistantMessage, createRuntimeContextMessage, createSystemMessage, } from '../../../types/message/index.js';
18
- import { classifyProviderError } from '../../../types/provider/errors.js';
18
+ import { classifyProviderError, readOutputOverflow } from '../../../types/provider/errors.js';
19
19
  import { toErrorMessage } from '../../../utils/error.js';
20
20
  import { stableDigest } from '../../../utils/hash.js';
21
21
  import { generateMessageId } from '../../../utils/id.js';
22
22
  import { parseStructuredResultJson } from '../../../utils/structured-result-json.js';
23
23
  import { createCallbackInference } from '../callback-inference.js';
24
+ import { SandboxAcquisitionStopped } from '../deferred-sandbox.js';
24
25
  import { deliverDurableInbound } from '../durable-inbound.js';
25
26
  import { projectObservationContext } from '../observation-context.js';
26
27
  import { applyLifecycleHookResults } from '../plugin-hooks.js';
@@ -31,12 +32,14 @@ import { parseNativeCandidate } from './native-output.js';
31
32
  import { drainArrivedTaskCompletions, holdForOutstandingWork, settleOutstandingWork, } from './outstanding-work.js';
32
33
  import { runAdvisoryPhase } from './phases/advisory.js';
33
34
  import { runIterationCheckpoint } from './phases/checkpoint.js';
34
- import { activeContextWindow, measureContext, relieveOverflow, runCompactionCheck, } from './phases/compaction.js';
35
+ import { compactionBreaker } from './phases/compaction-breaker.js';
36
+ import { AUTOMATIC_COMPACTION_FAILED, OVERFLOW_RELIEF_LIMIT, activeContextWindow, clearCompactionErrorNote, compactionErrorNote, measureContext, relieveOverflow, runCompactionCheck, stopOnRefill, } from './phases/compaction.js';
35
37
  import { runHandoffPause } from './phases/handoff.js';
36
38
  import { runPlanGate } from './phases/plan.js';
37
39
  import { runToolReview } from './phases/tool-review.js';
38
40
  import { refreshWorkingMemory, splitWorkingMemoryForRequest } from './phases/working-memory.js';
39
- import { streamWithProviderRejectedImageRecovery } from './provider-rejected-image.js';
41
+ import { streamWithProviderRejectedImageRecovery, } from './provider-rejected-image.js';
42
+ import { RequestRepair } from './request-repair.js';
40
43
  import { appendWorkContext, beforeStep, prepareStep, selectContextModel, stepContextMessage, } from './step-shaping.js';
41
44
  import { streamProviderTurn } from './stream-turn.js';
42
45
  /** A host reviewer is not the model transport, even when its cause is an HTTP failure. */
@@ -60,10 +63,18 @@ class AnswerReviewFailure extends NamzuError {
60
63
  * on the thing that actually went wrong.
61
64
  */
62
65
  const DEFAULT_ANSWER_REVIEW_LIMIT = 3;
66
+ /** Left unused when an output cap is shrunk to fit the window (reference: 1000). */
67
+ const OUTPUT_CAP_MARGIN_TOKENS = 1000;
68
+ /** A shrunk output cap below this is not worth a call (reference floor: 3000). */
69
+ const MIN_OUTPUT_CAP_TOKENS = 3000;
63
70
  // Ending a turn changes the available actions, not the strength of its evidence.
64
71
  // Use the same standard for warning closure and empty-completion recovery.
65
72
  const CLOSING_RESPONSE_GUIDANCE = 'Give a concise response using only what the available evidence supports. Attribute unverified statements to their source instead of presenting them as observed facts. If evidence is missing or conflicting, state what cannot be established. Do not claim unfinished work is complete. Do not request any more tool calls.';
66
73
  export { awaitedJobGraceMs, settleGraceMs } from './outstanding-work.js';
74
+ /** One provider's model, as {@link IterationOrchestrator} remembers it. */
75
+ function textOnlyKey(providerId, model) {
76
+ return `${providerId}\u0000${model}`;
77
+ }
67
78
  export class IterationOrchestrator {
68
79
  ctx;
69
80
  advisoryTurn;
@@ -98,6 +109,68 @@ export class IterationOrchestrator {
98
109
  }
99
110
  /** Rich tool blocks already reported; durable history is scanned every turn. */
100
111
  warnedRichToolResults = new Set();
112
+ /** What this run has learned about requests its provider rejects as invalid, and how to repair them. */
113
+ requestRepair = new RequestRepair({
114
+ debug: (message) => this.ctx.log.debug(message),
115
+ onMediaRemoved: (removal) => this.noteMediaRemoved(removal),
116
+ servedBy: () => this.ctx.servingMember?.(),
117
+ });
118
+ /**
119
+ * Provider and model pairs that refused a request for holding an image; they
120
+ * get none for the rest of the run. Keyed by both: one model id can be served
121
+ * by two members of a fallback chain, and only the one that refused is known.
122
+ */
123
+ textOnlyModels = new Set();
124
+ /**
125
+ * The member a request for `model` goes to first. A fallback chain keeps
126
+ * serving from the member it last moved to, so that member, not the head,
127
+ * is the one whose reading of images counts.
128
+ */
129
+ servingModel(model) {
130
+ const serving = this.ctx.servingMember?.();
131
+ return {
132
+ providerId: serving?.providerId ?? this.ctx.provider.id,
133
+ model: serving?.model ?? model,
134
+ };
135
+ }
136
+ /** Whether a request for `model` may carry images: the driver's catalogue and this run's own evidence agree. */
137
+ modelReadsImages(model) {
138
+ const serving = this.servingModel(model);
139
+ return (!this.textOnlyModels.has(textOnlyKey(serving.providerId, serving.model)) &&
140
+ modelReadsImages(this.ctx.provider, model));
141
+ }
142
+ /** What a request for `model` does when its model turns out to read text only. */
143
+ textOnlyRecovery(model) {
144
+ return {
145
+ servedBy: () => this.servingModel(model),
146
+ onAccepted: (refused) => this.noteTextOnlyModel(refused),
147
+ };
148
+ }
149
+ /**
150
+ * A provider refused a request for holding an image its model cannot read, and
151
+ * the retry without images was accepted. Stop sending images to it, and say so
152
+ * once, so the host can remember the model beyond this run.
153
+ */
154
+ async noteTextOnlyModel(refused) {
155
+ const key = textOnlyKey(refused.providerId, refused.model ?? '');
156
+ if (this.textOnlyModels.has(key))
157
+ return;
158
+ this.textOnlyModels.add(key);
159
+ const { providerId, model } = refused;
160
+ this.ctx.log.warn('Capability mismatch: the model cannot read images', {
161
+ [GENAI.SYSTEM]: providerId,
162
+ [GENAI.REQUEST_MODEL]: model ?? '',
163
+ });
164
+ await this.ctx.emitEvent?.({
165
+ type: 'capability_warning',
166
+ turnId: this.ctx.recorder.turnId,
167
+ capability: 'vision',
168
+ contentSource: 'history',
169
+ providerId,
170
+ ...(model !== undefined ? { model } : {}),
171
+ message: `Model '${model ?? 'unknown'}' on provider '${providerId}' refused a request that held an image, so images are left out of its requests. They stay in the conversation and are sent again to a model that reads them.`,
172
+ });
173
+ }
101
174
  /**
102
175
  * The previous iteration held a `stopWhen` decision open for a worker.
103
176
  *
@@ -113,6 +186,67 @@ export class IterationOrchestrator {
113
186
  * sets it.
114
187
  */
115
188
  stopDeferredForOutstandingWork = false;
189
+ /** Nudges sent since the model last finished a call without hitting its output cap. */
190
+ outputContinuations = 0;
191
+ /**
192
+ * `turn_end` (and, for a child, `subagent_stop`) fired when the model
193
+ * finished and no hook asked for more. `finalizeTurn` then does not fire
194
+ * them a second time.
195
+ */
196
+ turnEndFired = false;
197
+ /** A hook already kept this turn going once: it is asked again with `stop_hook_active`. */
198
+ stopHookActive = false;
199
+ get turnEndHooksFired() {
200
+ return this.turnEndFired;
201
+ }
202
+ /**
203
+ * Fire `turn_end` (and `subagent_stop` for a child) at the point the model
204
+ * has finished, and read the answer.
205
+ *
206
+ * A hook that answers `skip` is refusing to let the turn end: its reasons
207
+ * are returned, joined, as feedback for the model. A hook that asks for the
208
+ * turn to halt wins over any such feedback (`takeHalt`). With nothing asked,
209
+ * the events have fired for this turn and the finalizer does not repeat them.
210
+ */
211
+ async *askStopHooks() {
212
+ const manager = this.ctx.pluginManager;
213
+ if (!manager)
214
+ return undefined;
215
+ const recorder = this.ctx.recorder;
216
+ const feedback = [];
217
+ const ask = async (event, extra) => {
218
+ const results = await manager.executeHooks(event, {
219
+ sessionId: recorder.sessionId,
220
+ turnId: recorder.turnId,
221
+ ...extra,
222
+ ...(this.stopHookActive ? { stopHookActive: true } : {}),
223
+ signal: this.ctx.abortController.signal,
224
+ }, this.ctx.emitEvent);
225
+ applyLifecycleHookResults(event, results);
226
+ for (const result of results) {
227
+ if (result.action === 'skip')
228
+ feedback.push(result.reason);
229
+ if ((result.action === 'continue' || result.action === 'skip') && result.halt) {
230
+ this.ctx.toolExecutor.requestHalt?.(result.halt.reason);
231
+ }
232
+ }
233
+ };
234
+ await ask('turn_end', {});
235
+ yield* this.ctx.drainPending();
236
+ if (this.ctx.parentSessionId !== undefined) {
237
+ await ask('subagent_stop', {
238
+ parentSessionId: this.ctx.parentSessionId,
239
+ ...(this.ctx.parentTurnId !== undefined ? { parentTurnId: this.ctx.parentTurnId } : {}),
240
+ });
241
+ yield* this.ctx.drainPending();
242
+ }
243
+ if (feedback.length === 0) {
244
+ this.turnEndFired = true;
245
+ return undefined;
246
+ }
247
+ this.stopHookActive = true;
248
+ return feedback.join('\n');
249
+ }
116
250
  /** One current input, independent of the compactable history array. */
117
251
  latestUserMessage;
118
252
  initialDurableDelivered = false;
@@ -264,16 +398,22 @@ export class IterationOrchestrator {
264
398
  // paused it. Taken after the intent above is known, so the
265
399
  // checkpoint names the message the turn is answering.
266
400
  await this.ctx.checkpointMgr.markLoopStart?.(recorder);
267
- // One context-overflow relief per *stuck point*, not per turn.
401
+ // Context-overflow relief is a two-rung ladder per *stuck point*, not per
402
+ // turn, as in the reference terminal (at most two reactive compactions per
403
+ // overflow): the first relief, then one deeper pass if the retry still
404
+ // overflows.
268
405
  //
269
- // The latch exists so that a second overflow immediately after a
270
- // successful compaction — meaning the prompt is irreducible — stops
271
- // instead of looping. It was never meant to disarm the mechanism for
272
- // the rest of the turn, which is what a turn-scoped flag did: one
273
- // relief at iteration 3 left iteration 40 to die on an overflow with
274
- // obvious moves left. It is cleared by a turn that actually
275
- // succeeded, which is the evidence that the turn is no longer stuck.
276
- let overflowRelieved = false;
406
+ // The limit exists so that an overflow that survives both rungs — an
407
+ // irreducible prompt — stops instead of looping. It was never meant to
408
+ // disarm the mechanism for the rest of the turn, which is what a
409
+ // turn-scoped flag did: one relief at iteration 3 left iteration 40 to die
410
+ // on an overflow with obvious moves left. The count is cleared by a turn
411
+ // that actually succeeded, which is the evidence that the turn is no
412
+ // longer stuck.
413
+ let overflowReliefs = 0;
414
+ // The output cap a provider told us fits beside this prompt. One request's
415
+ // worth: it is cleared by a call that went through.
416
+ let outputCapOverride;
277
417
  // A `finally` rather than a line at each exit, for the reason written
278
418
  // beside `iterSpan.end()` below: this loop leaves by eight `break`s,
279
419
  // two `return`s and a `throw`, and a rule every future edit has to
@@ -446,12 +586,16 @@ export class IterationOrchestrator {
446
586
  applyLifecycleHookResults('iteration_start', hookResults);
447
587
  yield* this.ctx.drainPending();
448
588
  }
589
+ if (!forceFinalize)
590
+ await this.deliverReminders(iterationNum);
449
591
  // Re-pin the working-memory block from ground truth at the primacy
450
592
  // edge BEFORE compaction runs (so the refreshed slot is what
451
593
  // compaction preserves). No-op when no provider is configured.
452
594
  await refreshWorkingMemory(this.ctx);
453
595
  await runCompactionCheck(this.ctx);
454
596
  yield* this.ctx.drainPending();
597
+ if (this.stopForCompactionBreaker())
598
+ break;
455
599
  // Cache discipline: keep the tools param byte-stable even on the
456
600
  // forced-final iteration and forbid tool use via tool_choice
457
601
  // 'none' instead. Dropping the tools array would invalidate the
@@ -498,6 +642,8 @@ export class IterationOrchestrator {
498
642
  await runCompactionCheck(this.ctx);
499
643
  // Publish context edits before entering a possibly slow provider.
500
644
  yield* this.ctx.drainPending();
645
+ if (this.stopForCompactionBreaker())
646
+ break;
501
647
  const stepAllowedTools = step.allowedTools ?? this.ctx.allowedTools;
502
648
  const deferredMcpContext = deferredMcpHints?.forNames(stepAllowedTools) ?? '';
503
649
  const contextText = [
@@ -587,7 +733,7 @@ export class IterationOrchestrator {
587
733
  if (step.context)
588
734
  requestHistory.push(stepContextMessage(step.context));
589
735
  const messages = projectRequestRichContent(this.projectObservations(requestHistory), this.ctx.turnConfig.maxRequestRichContentBytes ??
590
- DEFAULT_MAX_REQUEST_RICH_CONTENT_BYTES);
736
+ DEFAULT_MAX_REQUEST_RICH_CONTENT_BYTES, { imagesReadable: this.modelReadsImages(stepModel) });
591
737
  appendWorkContext(shaping, messages, iterationNum, step);
592
738
  if (closingDirective)
593
739
  messages.push(closingDirective);
@@ -660,7 +806,7 @@ export class IterationOrchestrator {
660
806
  messages: Object.freeze(messages.map((m) => Object.freeze({ ...m }))),
661
807
  toolNames: Object.freeze(llmTools.map((t) => t.function.name)),
662
808
  temperature: step.temperature ?? turnConfig.temperature,
663
- maxTokens: step.maxResponseTokens ?? turnConfig.maxResponseTokens,
809
+ maxTokens: outputCapOverride ?? step.maxResponseTokens ?? turnConfig.maxResponseTokens,
664
810
  }),
665
811
  }, this.ctx.emitEvent);
666
812
  applyLifecycleHookResults('pre_llm_call', hookResults);
@@ -726,7 +872,7 @@ export class IterationOrchestrator {
726
872
  ? step.toolChoice
727
873
  : undefined,
728
874
  temperature: step.temperature ?? turnConfig.temperature,
729
- maxTokens: step.maxResponseTokens ?? turnConfig.maxResponseTokens,
875
+ maxTokens: outputCapOverride ?? step.maxResponseTokens ?? turnConfig.maxResponseTokens,
730
876
  cacheControl: { type: 'auto' },
731
877
  ...(turnConfig.thinking ? { thinking: turnConfig.thinking } : {}),
732
878
  ...(turnConfig.effort ? { effort: turnConfig.effort } : {}),
@@ -739,6 +885,8 @@ export class IterationOrchestrator {
739
885
  signal: this.ctx.abortController.signal,
740
886
  }, this.ctx.emitEvent, this.ctx.drainPending, recorder.turnId, iterationNum, forceFinalize, this.ctx.log, iterSpan, stepMessageId, {
741
887
  onAccepted: (identity) => this.acceptProviderRejectedImage(identity),
888
+ textOnly: this.textOnlyRecovery(stepModel),
889
+ repair: this.requestRepair,
742
890
  }, Boolean(this.ctx.reviewAnswer || this.ctx.structuredOutput?.review || this.ctx.advisoryCtx), recorder.sessionId);
743
891
  stepResponse = response;
744
892
  const reviewRequest = {
@@ -784,7 +932,11 @@ export class IterationOrchestrator {
784
932
  });
785
933
  // The turn went through, so the turn is not sitting on an
786
934
  // irreducible prompt any more. Re-arm relief for the next one.
787
- overflowRelieved = false;
935
+ overflowReliefs = 0;
936
+ clearCompactionErrorNote(this.ctx);
937
+ outputCapOverride = undefined;
938
+ if (response.finishReason !== 'length')
939
+ this.outputContinuations = 0;
788
940
  if (this.ctx.pluginManager) {
789
941
  const hookResults = await this.ctx.pluginManager.executeHooks('post_llm_call', {
790
942
  sessionId: recorder.sessionId,
@@ -1055,16 +1207,43 @@ export class IterationOrchestrator {
1055
1207
  // it has no room left to continue into, and the
1056
1208
  // continuation would only send a longer prompt into
1057
1209
  // the same full window. The reply stands as it is.
1210
+ const cutDuringReasoning = !hasContent &&
1211
+ (response.message.toolCalls?.length ?? 0) === 0 &&
1212
+ (response.message.reasoning?.length ?? 0) > 0;
1058
1213
  if (!forceFinalize &&
1059
1214
  response.finishReason === 'length' &&
1060
1215
  response.finishDetail !== 'context_window' &&
1061
- hasContent) {
1062
- this.ctx.log.info('LLM hit max_tokens mid-text — auto-continuing', {
1216
+ (hasContent || cutDuringReasoning)) {
1217
+ // A host that opted in gets a cap on the nudges: a model that
1218
+ // overruns its output limit every time would otherwise spend the
1219
+ // whole turn on it. Past the cap the turn stops, and says why.
1220
+ const cap = this.ctx.maxOutputContinuations;
1221
+ if (cap !== undefined && this.outputContinuations >= cap) {
1222
+ this.ctx.log.warn('LLM kept hitting max_tokens — stopping the turn', {
1223
+ [NAMZU.TURN_ID]: recorder.turnId,
1224
+ [NAMZU.ITERATION]: iterationNum,
1225
+ 'namzu.runtime.output_continuations': this.outputContinuations,
1226
+ });
1227
+ recorder.setLastError(`The reply was cut off at the output limit ${cap + 1} times in a row. Ask for a smaller piece of the work, or raise the output limit.`);
1228
+ recorder.setStopReason('output_limit');
1229
+ await this.ctx.emitEvent({
1230
+ type: 'iteration_completed',
1231
+ turnId: recorder.turnId,
1232
+ iteration: iterationNum,
1233
+ hasToolCalls: false,
1234
+ });
1235
+ yield* this.ctx.drainPending();
1236
+ break;
1237
+ }
1238
+ this.outputContinuations++;
1239
+ this.ctx.log.info(cutDuringReasoning
1240
+ ? 'LLM hit max_tokens while reasoning — nudging to answer briefly'
1241
+ : 'LLM hit max_tokens mid-text — auto-continuing', {
1063
1242
  [NAMZU.TURN_ID]: recorder.turnId,
1064
1243
  [NAMZU.ITERATION]: iterationNum,
1065
1244
  [GENAI.USAGE_OUTPUT_TOKENS]: response.usage.completionTokens,
1066
1245
  });
1067
- recorder.pushMessage(createRuntimeContextMessage(AUTO_CONTINUATION_USER_MESSAGE, 'auto-continuation'));
1246
+ recorder.pushMessage(createRuntimeContextMessage(cutDuringReasoning ? REASONING_CUT_USER_MESSAGE : AUTO_CONTINUATION_USER_MESSAGE, 'auto-continuation'));
1068
1247
  await this.ctx.emitEvent({
1069
1248
  type: 'iteration_completed',
1070
1249
  turnId: recorder.turnId,
@@ -1206,6 +1385,22 @@ export class IterationOrchestrator {
1206
1385
  // also settle as `end_turn`, and there the deferred predicate
1207
1386
  // is not why the turn ended: those decided the answer
1208
1387
  // themselves.
1388
+ // The model is done and has no tool calls. A `turn_end` (or
1389
+ // `subagent_stop`) hook may refuse to let it be: its reason goes
1390
+ // back to the model and the turn goes on.
1391
+ if (!forceFinalize && closingStopReason === undefined) {
1392
+ const feedback = yield* this.askStopHooks();
1393
+ const stoppedByHook = this.ctx.toolExecutor.takeHalt?.();
1394
+ if (stoppedByHook !== undefined) {
1395
+ recorder.setLastError(`Stopped by a hook: ${stoppedByHook}`);
1396
+ recorder.setStopReason('stop_condition');
1397
+ break;
1398
+ }
1399
+ if (feedback !== undefined) {
1400
+ recorder.pushMessage(createRuntimeContextMessage(`A hook asked for more before this turn ends:\n${feedback}`, 'hook-feedback'));
1401
+ continue;
1402
+ }
1403
+ }
1209
1404
  recorder.setStopReason(closingStopReason ??
1210
1405
  (stopWasDeferredForOutstandingWork ? 'stop_condition' : 'end_turn'));
1211
1406
  break;
@@ -1240,6 +1435,27 @@ export class IterationOrchestrator {
1240
1435
  if ((yield* runHandoffPause(this.ctx, iterationNum, reviewOutcome.results)) === 'stop') {
1241
1436
  return;
1242
1437
  }
1438
+ // A hook asked for the turn to stop once this step's calls were
1439
+ // answered. Every `tool_use` has its `tool_result` by now, so the
1440
+ // transcript is whole; what is left is to end the turn and say why.
1441
+ const hookHalt = this.ctx.toolExecutor.takeHalt?.();
1442
+ if (hookHalt !== undefined) {
1443
+ this.ctx.log.info('A hook stopped the turn', {
1444
+ [NAMZU.TURN_ID]: recorder.turnId,
1445
+ [NAMZU.ITERATION]: iterationNum,
1446
+ 'namzu.hook.reason': hookHalt,
1447
+ });
1448
+ recorder.setLastError(`Stopped by a hook: ${hookHalt}`);
1449
+ recorder.setStopReason('stop_condition');
1450
+ await this.ctx.emitEvent({
1451
+ type: 'iteration_completed',
1452
+ turnId: recorder.turnId,
1453
+ iteration: iterationNum,
1454
+ hasToolCalls: true,
1455
+ });
1456
+ yield* this.ctx.drainPending();
1457
+ break;
1458
+ }
1243
1459
  if (reviewOutcome.decision === 'rejected') {
1244
1460
  continue;
1245
1461
  }
@@ -1449,6 +1665,12 @@ export class IterationOrchestrator {
1449
1665
  }
1450
1666
  catch (err) {
1451
1667
  const cancelled = this.ctx.abortController.signal.aborted;
1668
+ // A deferred sandbox that was cancelled or ran out of time. The
1669
+ // turn settles it as `cancelled` / `timeout` in `query()`, which is
1670
+ // not a failure of this iteration: it is recorded the way a turn
1671
+ // stopped by its own abort or timeout is (a cancelled step, no
1672
+ // error span), so the ledger and the stop reason agree.
1673
+ const sandboxStopped = err instanceof SandboxAcquisitionStopped;
1452
1674
  // This iteration gets a step too, and it is the one the
1453
1675
  // argument three hundred lines above was actually about.
1454
1676
  //
@@ -1509,7 +1731,7 @@ export class IterationOrchestrator {
1509
1731
  startedAt: stepStartedAt,
1510
1732
  usageBefore,
1511
1733
  costBefore,
1512
- unfinished: cancelled
1734
+ unfinished: cancelled || sandboxStopped
1513
1735
  ? { finishReason: 'cancelled' }
1514
1736
  : {
1515
1737
  finishReason: 'error',
@@ -1541,21 +1763,82 @@ export class IterationOrchestrator {
1541
1763
  // actually shed something. A second overflow after a successful
1542
1764
  // compaction means the prompt is irreducible, and looping on it
1543
1765
  // would burn the budget to arrive at the same error.
1544
- if (!overflowRelieved &&
1545
- !(err instanceof AnswerReviewFailure) &&
1766
+ // The prompt fit and only the requested output did not: ask for what
1767
+ // room is left (less a margin) and send it again, once. A reply that
1768
+ // would be too small to be useful, or a cap that is not smaller than
1769
+ // the one already tried, is not retried: the same failure would follow.
1770
+ const outputOverflow = err instanceof AnswerReviewFailure ? undefined : readOutputOverflow(err);
1771
+ if (outputOverflow) {
1772
+ const room = Math.max(0, outputOverflow.contextLimit - outputOverflow.inputTokens - OUTPUT_CAP_MARGIN_TOKENS);
1773
+ if (room >= MIN_OUTPUT_CAP_TOKENS &&
1774
+ (outputCapOverride === undefined || room < outputCapOverride)) {
1775
+ outputCapOverride = room;
1776
+ this.ctx.log.info('Output cap does not fit beside the prompt — retrying with less', {
1777
+ [NAMZU.TURN_ID]: recorder.turnId,
1778
+ [NAMZU.ITERATION]: iterationNum,
1779
+ 'namzu.runtime.input_tokens': outputOverflow.inputTokens,
1780
+ 'namzu.runtime.context_limit': outputOverflow.contextLimit,
1781
+ 'namzu.runtime.adjusted_max_tokens': room,
1782
+ });
1783
+ if (iterationActivity) {
1784
+ this.ctx.activityStore.complete(iterationActivity.id);
1785
+ }
1786
+ continue;
1787
+ }
1788
+ }
1789
+ if (!(err instanceof AnswerReviewFailure) &&
1546
1790
  classifyProviderError(err, this.ctx.provider.id).code === 'context_length_exceeded') {
1547
- overflowRelieved = true;
1548
- const shed = await relieveOverflow(this.ctx);
1791
+ // The rapid-refill check comes first, before the first reactive relief
1792
+ // of an overflow, as in the reference terminal. A relief that follows
1793
+ // the previous compaction within the refill window counts toward the
1794
+ // limit, so a tool that re-fills the window every time it is called
1795
+ // stops the turn instead of being cleared and called again without end.
1796
+ // The deeper rung of the same overflow is not counted again.
1797
+ if (overflowReliefs === 0 && (await stopOnRefill(this.ctx))) {
1798
+ if (iterationActivity) {
1799
+ this.ctx.activityStore.fail(iterationActivity.id, toErrorMessage(err));
1800
+ }
1801
+ if (this.stopForCompactionBreaker())
1802
+ break;
1803
+ }
1804
+ // A rung that sheds too little to change the verdict is not worth a
1805
+ // provider call; climb to the next one at once.
1806
+ let shed = false;
1807
+ while (!shed && overflowReliefs < OVERFLOW_RELIEF_LIMIT) {
1808
+ overflowReliefs++;
1809
+ shed = await relieveOverflow(this.ctx, { deeper: overflowReliefs > 1 });
1810
+ }
1549
1811
  if (shed) {
1812
+ // Clearing results is a compaction too: only summaries and reducers
1813
+ // recorded themselves, so a clear-only relief was invisible to the
1814
+ // breaker.
1815
+ compactionBreaker(this.ctx)?.recordCompaction(iterationNum);
1550
1816
  this.ctx.log.info('Retrying the turn after relieving a context overflow', {
1551
1817
  [NAMZU.TURN_ID]: recorder.turnId,
1552
1818
  [NAMZU.ITERATION]: iterationNum,
1819
+ 'namzu.runtime.relief_rung': overflowReliefs,
1553
1820
  });
1554
1821
  if (iterationActivity) {
1555
1822
  this.ctx.activityStore.complete(iterationActivity.id);
1556
1823
  }
1557
1824
  continue;
1558
1825
  }
1826
+ // Out of rungs. The conversation is kept; say why compaction could not
1827
+ // make room when it errored, as the reference does.
1828
+ const cause = compactionErrorNote(this.ctx);
1829
+ if (cause !== undefined && err instanceof Error) {
1830
+ try {
1831
+ err.message = `${err.message}${AUTOMATIC_COMPACTION_FAILED}${cause}`;
1832
+ }
1833
+ catch {
1834
+ // A frozen error keeps its own message; the cause stays in the log.
1835
+ }
1836
+ }
1837
+ }
1838
+ if (sandboxStopped) {
1839
+ if (iterationActivity)
1840
+ this.ctx.activityStore.complete(iterationActivity.id);
1841
+ throw err;
1559
1842
  }
1560
1843
  if (iterationActivity) {
1561
1844
  this.ctx.activityStore.fail(iterationActivity.id, toErrorMessage(err));
@@ -1639,6 +1922,41 @@ export class IterationOrchestrator {
1639
1922
  extractFromUserMessage(manager, message.content, !manager.getState().task);
1640
1923
  }
1641
1924
  }
1925
+ /**
1926
+ * Ask the host's reminder source (`QueryParams.reminders`) what the model
1927
+ * should be told before this request, and append each text as a hidden
1928
+ * user-role message. The start of a request is a provider-valid boundary:
1929
+ * the history ends in the operator's message or in a finished tool batch.
1930
+ * A source that throws has nothing to say; it never fails the turn.
1931
+ */
1932
+ async deliverReminders(iteration) {
1933
+ const source = this.ctx.reminders;
1934
+ if (!source)
1935
+ return;
1936
+ const { recorder } = this.ctx;
1937
+ let texts;
1938
+ try {
1939
+ texts = await source({
1940
+ sessionId: recorder.sessionId,
1941
+ turnId: recorder.turnId,
1942
+ iteration,
1943
+ messages: recorder.messages,
1944
+ });
1945
+ }
1946
+ catch (error) {
1947
+ this.ctx.log.warn('A reminder source threw; the request goes without its reminders', {
1948
+ [NAMZU.TURN_ID]: recorder.turnId,
1949
+ [NAMZU.ITERATION]: iteration,
1950
+ 'exception.message': error instanceof Error ? error.message : String(error),
1951
+ });
1952
+ return;
1953
+ }
1954
+ for (const text of texts ?? []) {
1955
+ if (text.trim().length === 0)
1956
+ continue;
1957
+ recorder.pushMessage(createRuntimeContextMessage(text, 'reminder'));
1958
+ }
1959
+ }
1642
1960
  async deliverInbound() {
1643
1961
  const queued = this.ctx.inboundMessages?.() ?? [];
1644
1962
  for (const message of queued) {
@@ -1756,6 +2074,15 @@ export class IterationOrchestrator {
1756
2074
  recorder.setStopReason('structured_output_failed');
1757
2075
  return true;
1758
2076
  }
2077
+ /** The compaction circuit breaker asked for the turn to stop (opt-in; see `compactionConfig.circuitBreaker`). */
2078
+ stopForCompactionBreaker() {
2079
+ const reason = compactionBreaker(this.ctx)?.stop;
2080
+ if (reason === undefined)
2081
+ return false;
2082
+ this.ctx.recorder.setLastError(reason);
2083
+ this.ctx.recorder.setStopReason('error');
2084
+ return true;
2085
+ }
1759
2086
  async recordToolStructuredFailure(results, iteration) {
1760
2087
  if (!this.needsStructuredOutput() ||
1761
2088
  this.ctx.structuredOutput?.mode === 'native' ||
@@ -2048,6 +2375,31 @@ export class IterationOrchestrator {
2048
2375
  .listTasks()
2049
2376
  .some((t) => t.state !== 'completed' && t.state !== 'failed' && t.state !== 'canceled');
2050
2377
  }
2378
+ /** Media the provider could not process was left out of a request that then went through. */
2379
+ async noteMediaRemoved(removal) {
2380
+ this.ctx.log.warn('Media the provider rejected was left out of the request', {
2381
+ [GENAI.SYSTEM]: this.ctx.provider.id,
2382
+ 'namzu.request.media_removed': removal.count,
2383
+ });
2384
+ const noun = removal.count === 1 ? 'a file' : `${removal.count} files`;
2385
+ const it = removal.count === 1 ? 'it' : 'they';
2386
+ await this.ctx.emitEvent?.({
2387
+ type: 'capability_warning',
2388
+ turnId: this.ctx.recorder.turnId,
2389
+ capability: removal.kind === 'document' ? 'documents' : 'vision',
2390
+ // The person's own latest attachment is named as such; a count limit is
2391
+ // not a fault in any file.
2392
+ contentSource: removal.current
2393
+ ? 'media-removed-current'
2394
+ : removal.limit
2395
+ ? 'media-limit'
2396
+ : 'media-removed',
2397
+ providerId: this.ctx.provider.id,
2398
+ message: removal.limit
2399
+ ? `The conversation holds more files than the provider accepts at once, so ${noun} (the oldest) ${removal.count === 1 ? 'was' : 'were'} left out of the request. The conversation still holds ${it}.`
2400
+ : `The provider could not process ${noun}${removal.current ? ' attached to your latest message' : ' in this conversation'}, so ${it} ${removal.count === 1 ? 'was' : 'were'} left out of the request. The conversation still holds ${it}.`,
2401
+ });
2402
+ }
2051
2403
  async acceptProviderRejectedImage(identity) {
2052
2404
  const repaired = markProviderRejectedImage(this.ctx.recorder.messages, identity);
2053
2405
  if (repaired.count === 0) {
@@ -2092,7 +2444,7 @@ export class IterationOrchestrator {
2092
2444
  ...workingMemory.history,
2093
2445
  ...(workingMemory.context ? [workingMemory.context] : []),
2094
2446
  ];
2095
- const finalMessages = projectRequestRichContent(this.projectObservations(finalHistory), this.ctx.turnConfig.maxRequestRichContentBytes ?? DEFAULT_MAX_REQUEST_RICH_CONTENT_BYTES);
2447
+ const finalMessages = projectRequestRichContent(this.projectObservations(finalHistory), this.ctx.turnConfig.maxRequestRichContentBytes ?? DEFAULT_MAX_REQUEST_RICH_CONTENT_BYTES, { imagesReadable: this.modelReadsImages(model) });
2096
2448
  appendWorkContext(this.stepShaping(), finalMessages, this.steps.length + 1, { model });
2097
2449
  // The closing directive is the last thing the model reads: after
2098
2450
  // the working memory and any work context above.
@@ -2131,7 +2483,7 @@ export class IterationOrchestrator {
2131
2483
  // stream to completion.
2132
2484
  signal: this.ctx.abortController.signal,
2133
2485
  };
2134
- const response = await collectChatCompletion(streamWithProviderRejectedImageRecovery(this.ctx.provider, finalParams, (identity) => this.acceptProviderRejectedImage(identity)));
2486
+ const response = await collectChatCompletion(streamWithProviderRejectedImageRecovery(this.ctx.provider, finalParams, (identity) => this.acceptProviderRejectedImage(identity), undefined, this.textOnlyRecovery(model), this.requestRepair));
2135
2487
  const servingMember = this.ctx.servingMember?.() ?? requestedMember;
2136
2488
  const servedBy = {
2137
2489
  providerId: servingMember.providerId,