@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
@@ -4,7 +4,10 @@ import {
4
4
  extractFromUserMessage,
5
5
  } from '../../../compaction/extractor.js'
6
6
  import { estimateMessageTokens } from '../../../compaction/token-estimate.js'
7
- import { AUTO_CONTINUATION_USER_MESSAGE } from '../../../constants/continuation.js'
7
+ import {
8
+ AUTO_CONTINUATION_USER_MESSAGE,
9
+ REASONING_CUT_USER_MESSAGE,
10
+ } from '../../../constants/continuation.js'
8
11
  import { CHARS_PER_TOKEN } from '../../../constants/limits.js'
9
12
  import {
10
13
  DEFAULT_STRUCTURED_OUTPUT_RETRIES,
@@ -12,7 +15,7 @@ import {
12
15
  } from '../../../constants/tools/index.js'
13
16
  import { withStreamedMessageIdentity } from '../../../manager/session/streamed-message-identity.js'
14
17
  import { renderSkillsSection } from '../../../persona/assembler.js'
15
- import { resolveProviderCapabilities } from '../../../provider/capabilities.js'
18
+ import { modelReadsImages, resolveProviderCapabilities } from '../../../provider/capabilities.js'
16
19
  import { collectChatCompletion } from '../../../provider/collect-chat-completion.js'
17
20
  import { renderToolSchema } from '../../../registry/tool/schema.js'
18
21
  import { formatCompletionNotification } from '../../../scheduler/completion-inbox.js'
@@ -27,7 +30,7 @@ import { STRUCTURED_OUTPUT_TOOL_NAME } from '../../../tools/builtins/structuredO
27
30
  import type { ToolManager } from '../../../toolsets/manager.js'
28
31
  import type { CostInfo, TokenUsage } from '../../../types/common/index.js'
29
32
  import { NamzuError } from '../../../types/errors/index.js'
30
- import type { MessageId } from '../../../types/ids/index.js'
33
+ import type { MessageId, SessionId, TurnId } from '../../../types/ids/index.js'
31
34
  import {
32
35
  type Message,
33
36
  type UserMessage,
@@ -35,7 +38,7 @@ import {
35
38
  createRuntimeContextMessage,
36
39
  createSystemMessage,
37
40
  } from '../../../types/message/index.js'
38
- import { classifyProviderError } from '../../../types/provider/errors.js'
41
+ import { classifyProviderError, readOutputOverflow } from '../../../types/provider/errors.js'
39
42
  import type { ChatCompletionResponse } from '../../../types/provider/index.js'
40
43
  import type { AnswerReview, AnswerReviewContext } from '../../../types/session/answer-review.js'
41
44
  import type {
@@ -51,6 +54,7 @@ import { stableDigest } from '../../../utils/hash.js'
51
54
  import { generateMessageId } from '../../../utils/id.js'
52
55
  import { parseStructuredResultJson } from '../../../utils/structured-result-json.js'
53
56
  import { createCallbackInference } from '../callback-inference.js'
57
+ import { SandboxAcquisitionStopped } from '../deferred-sandbox.js'
54
58
  import { deliverDurableInbound } from '../durable-inbound.js'
55
59
  import type { ToolCallOutcome } from '../executor.js'
56
60
  import { projectObservationContext } from '../observation-context.js'
@@ -75,18 +79,29 @@ import {
75
79
  } from './outstanding-work.js'
76
80
  import { runAdvisoryPhase } from './phases/advisory.js'
77
81
  import { runIterationCheckpoint } from './phases/checkpoint.js'
82
+ import { compactionBreaker } from './phases/compaction-breaker.js'
78
83
  import {
84
+ AUTOMATIC_COMPACTION_FAILED,
85
+ OVERFLOW_RELIEF_LIMIT,
79
86
  activeContextWindow,
87
+ clearCompactionErrorNote,
88
+ compactionErrorNote,
80
89
  measureContext,
81
90
  relieveOverflow,
82
91
  runCompactionCheck,
92
+ stopOnRefill,
83
93
  } from './phases/compaction.js'
84
94
  import { runHandoffPause } from './phases/handoff.js'
85
95
  import type { IterationContext } from './phases/index.js'
86
96
  import { runPlanGate } from './phases/plan.js'
87
97
  import { runToolReview } from './phases/tool-review.js'
88
98
  import { refreshWorkingMemory, splitWorkingMemoryForRequest } from './phases/working-memory.js'
89
- import { streamWithProviderRejectedImageRecovery } from './provider-rejected-image.js'
99
+ import {
100
+ type TextOnlyModelRecovery,
101
+ type TextOnlyModelRefusal,
102
+ streamWithProviderRejectedImageRecovery,
103
+ } from './provider-rejected-image.js'
104
+ import { type MediaRemoval, RequestRepair } from './request-repair.js'
90
105
  import {
91
106
  type StepShaping,
92
107
  appendWorkContext,
@@ -125,6 +140,11 @@ export type { ToolReviewOutcome } from './phases/index.js'
125
140
  */
126
141
  const DEFAULT_ANSWER_REVIEW_LIMIT = 3
127
142
 
143
+ /** Left unused when an output cap is shrunk to fit the window (reference: 1000). */
144
+ const OUTPUT_CAP_MARGIN_TOKENS = 1000
145
+ /** A shrunk output cap below this is not worth a call (reference floor: 3000). */
146
+ const MIN_OUTPUT_CAP_TOKENS = 3000
147
+
128
148
  // Ending a turn changes the available actions, not the strength of its evidence.
129
149
  // Use the same standard for warning closure and empty-completion recovery.
130
150
  const CLOSING_RESPONSE_GUIDANCE =
@@ -132,6 +152,11 @@ const CLOSING_RESPONSE_GUIDANCE =
132
152
 
133
153
  export { awaitedJobGraceMs, settleGraceMs } from './outstanding-work.js'
134
154
 
155
+ /** One provider's model, as {@link IterationOrchestrator} remembers it. */
156
+ function textOnlyKey(providerId: string, model: string): string {
157
+ return `${providerId}\u0000${model}`
158
+ }
159
+
135
160
  export class IterationOrchestrator {
136
161
  private ctx: IterationContext
137
162
  private advisoryTurn:
@@ -179,6 +204,73 @@ export class IterationOrchestrator {
179
204
  }
180
205
  /** Rich tool blocks already reported; durable history is scanned every turn. */
181
206
  private readonly warnedRichToolResults = new Set<string>()
207
+ /** What this run has learned about requests its provider rejects as invalid, and how to repair them. */
208
+ private readonly requestRepair = new RequestRepair({
209
+ debug: (message) => this.ctx.log.debug(message),
210
+ onMediaRemoved: (removal) => this.noteMediaRemoved(removal),
211
+ servedBy: () => this.ctx.servingMember?.(),
212
+ })
213
+ /**
214
+ * Provider and model pairs that refused a request for holding an image; they
215
+ * get none for the rest of the run. Keyed by both: one model id can be served
216
+ * by two members of a fallback chain, and only the one that refused is known.
217
+ */
218
+ private readonly textOnlyModels = new Set<string>()
219
+
220
+ /**
221
+ * The member a request for `model` goes to first. A fallback chain keeps
222
+ * serving from the member it last moved to, so that member, not the head,
223
+ * is the one whose reading of images counts.
224
+ */
225
+ private servingModel(model: string): { readonly providerId: string; readonly model: string } {
226
+ const serving = this.ctx.servingMember?.()
227
+ return {
228
+ providerId: serving?.providerId ?? this.ctx.provider.id,
229
+ model: serving?.model ?? model,
230
+ }
231
+ }
232
+
233
+ /** Whether a request for `model` may carry images: the driver's catalogue and this run's own evidence agree. */
234
+ private modelReadsImages(model: string): boolean {
235
+ const serving = this.servingModel(model)
236
+ return (
237
+ !this.textOnlyModels.has(textOnlyKey(serving.providerId, serving.model)) &&
238
+ modelReadsImages(this.ctx.provider, model)
239
+ )
240
+ }
241
+
242
+ /** What a request for `model` does when its model turns out to read text only. */
243
+ private textOnlyRecovery(model: string): TextOnlyModelRecovery {
244
+ return {
245
+ servedBy: () => this.servingModel(model),
246
+ onAccepted: (refused) => this.noteTextOnlyModel(refused),
247
+ }
248
+ }
249
+
250
+ /**
251
+ * A provider refused a request for holding an image its model cannot read, and
252
+ * the retry without images was accepted. Stop sending images to it, and say so
253
+ * once, so the host can remember the model beyond this run.
254
+ */
255
+ private async noteTextOnlyModel(refused: TextOnlyModelRefusal): Promise<void> {
256
+ const key = textOnlyKey(refused.providerId, refused.model ?? '')
257
+ if (this.textOnlyModels.has(key)) return
258
+ this.textOnlyModels.add(key)
259
+ const { providerId, model } = refused
260
+ this.ctx.log.warn('Capability mismatch: the model cannot read images', {
261
+ [GENAI.SYSTEM]: providerId,
262
+ [GENAI.REQUEST_MODEL]: model ?? '',
263
+ })
264
+ await this.ctx.emitEvent?.({
265
+ type: 'capability_warning',
266
+ turnId: this.ctx.recorder.turnId,
267
+ capability: 'vision',
268
+ contentSource: 'history',
269
+ providerId,
270
+ ...(model !== undefined ? { model } : {}),
271
+ 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.`,
272
+ })
273
+ }
182
274
  /**
183
275
  * The previous iteration held a `stopWhen` decision open for a worker.
184
276
  *
@@ -194,6 +286,74 @@ export class IterationOrchestrator {
194
286
  * sets it.
195
287
  */
196
288
  private stopDeferredForOutstandingWork = false
289
+ /** Nudges sent since the model last finished a call without hitting its output cap. */
290
+ private outputContinuations = 0
291
+ /**
292
+ * `turn_end` (and, for a child, `subagent_stop`) fired when the model
293
+ * finished and no hook asked for more. `finalizeTurn` then does not fire
294
+ * them a second time.
295
+ */
296
+ private turnEndFired = false
297
+ /** A hook already kept this turn going once: it is asked again with `stop_hook_active`. */
298
+ private stopHookActive = false
299
+ get turnEndHooksFired(): boolean {
300
+ return this.turnEndFired
301
+ }
302
+
303
+ /**
304
+ * Fire `turn_end` (and `subagent_stop` for a child) at the point the model
305
+ * has finished, and read the answer.
306
+ *
307
+ * A hook that answers `skip` is refusing to let the turn end: its reasons
308
+ * are returned, joined, as feedback for the model. A hook that asks for the
309
+ * turn to halt wins over any such feedback (`takeHalt`). With nothing asked,
310
+ * the events have fired for this turn and the finalizer does not repeat them.
311
+ */
312
+ private async *askStopHooks(): AsyncGenerator<SessionEvent, string | undefined> {
313
+ const manager = this.ctx.pluginManager
314
+ if (!manager) return undefined
315
+ const recorder = this.ctx.recorder
316
+ const feedback: string[] = []
317
+ const ask = async (
318
+ event: 'turn_end' | 'subagent_stop',
319
+ extra: { parentSessionId?: SessionId; parentTurnId?: TurnId },
320
+ ): Promise<void> => {
321
+ const results = await manager.executeHooks(
322
+ event,
323
+ {
324
+ sessionId: recorder.sessionId,
325
+ turnId: recorder.turnId,
326
+ ...extra,
327
+ ...(this.stopHookActive ? { stopHookActive: true } : {}),
328
+ signal: this.ctx.abortController.signal,
329
+ },
330
+ this.ctx.emitEvent,
331
+ )
332
+ applyLifecycleHookResults(event, results)
333
+ for (const result of results) {
334
+ if (result.action === 'skip') feedback.push(result.reason)
335
+ if ((result.action === 'continue' || result.action === 'skip') && result.halt) {
336
+ this.ctx.toolExecutor.requestHalt?.(result.halt.reason)
337
+ }
338
+ }
339
+ }
340
+ await ask('turn_end', {})
341
+ yield* this.ctx.drainPending()
342
+ if (this.ctx.parentSessionId !== undefined) {
343
+ await ask('subagent_stop', {
344
+ parentSessionId: this.ctx.parentSessionId,
345
+ ...(this.ctx.parentTurnId !== undefined ? { parentTurnId: this.ctx.parentTurnId } : {}),
346
+ })
347
+ yield* this.ctx.drainPending()
348
+ }
349
+ if (feedback.length === 0) {
350
+ this.turnEndFired = true
351
+ return undefined
352
+ }
353
+ this.stopHookActive = true
354
+ return feedback.join('\n')
355
+ }
356
+
197
357
  /** One current input, independent of the compactable history array. */
198
358
  private latestUserMessage: UserMessage | undefined
199
359
  private initialDurableDelivered = false
@@ -365,16 +525,22 @@ export class IterationOrchestrator {
365
525
  // checkpoint names the message the turn is answering.
366
526
  await this.ctx.checkpointMgr.markLoopStart?.(recorder)
367
527
 
368
- // One context-overflow relief per *stuck point*, not per turn.
528
+ // Context-overflow relief is a two-rung ladder per *stuck point*, not per
529
+ // turn, as in the reference terminal (at most two reactive compactions per
530
+ // overflow): the first relief, then one deeper pass if the retry still
531
+ // overflows.
369
532
  //
370
- // The latch exists so that a second overflow immediately after a
371
- // successful compaction — meaning the prompt is irreducible — stops
372
- // instead of looping. It was never meant to disarm the mechanism for
373
- // the rest of the turn, which is what a turn-scoped flag did: one
374
- // relief at iteration 3 left iteration 40 to die on an overflow with
375
- // obvious moves left. It is cleared by a turn that actually
376
- // succeeded, which is the evidence that the turn is no longer stuck.
377
- let overflowRelieved = false
533
+ // The limit exists so that an overflow that survives both rungs — an
534
+ // irreducible prompt — stops instead of looping. It was never meant to
535
+ // disarm the mechanism for the rest of the turn, which is what a
536
+ // turn-scoped flag did: one relief at iteration 3 left iteration 40 to die
537
+ // on an overflow with obvious moves left. The count is cleared by a turn
538
+ // that actually succeeded, which is the evidence that the turn is no
539
+ // longer stuck.
540
+ let overflowReliefs = 0
541
+ // The output cap a provider told us fits beside this prompt. One request's
542
+ // worth: it is cleared by a call that went through.
543
+ let outputCapOverride: number | undefined
378
544
 
379
545
  // A `finally` rather than a line at each exit, for the reason written
380
546
  // beside `iterSpan.end()` below: this loop leaves by eight `break`s,
@@ -573,12 +739,15 @@ export class IterationOrchestrator {
573
739
  yield* this.ctx.drainPending()
574
740
  }
575
741
 
742
+ if (!forceFinalize) await this.deliverReminders(iterationNum)
743
+
576
744
  // Re-pin the working-memory block from ground truth at the primacy
577
745
  // edge BEFORE compaction runs (so the refreshed slot is what
578
746
  // compaction preserves). No-op when no provider is configured.
579
747
  await refreshWorkingMemory(this.ctx)
580
748
  await runCompactionCheck(this.ctx)
581
749
  yield* this.ctx.drainPending()
750
+ if (this.stopForCompactionBreaker()) break
582
751
 
583
752
  // Cache discipline: keep the tools param byte-stable even on the
584
753
  // forced-final iteration and forbid tool use via tool_choice
@@ -630,6 +799,7 @@ export class IterationOrchestrator {
630
799
  if (stepModel !== contextModelBeforePreparation) await runCompactionCheck(this.ctx)
631
800
  // Publish context edits before entering a possibly slow provider.
632
801
  yield* this.ctx.drainPending()
802
+ if (this.stopForCompactionBreaker()) break
633
803
 
634
804
  const stepAllowedTools = step.allowedTools ?? this.ctx.allowedTools
635
805
  const deferredMcpContext = deferredMcpHints?.forNames(stepAllowedTools) ?? ''
@@ -730,6 +900,7 @@ export class IterationOrchestrator {
730
900
  this.projectObservations(requestHistory),
731
901
  this.ctx.turnConfig.maxRequestRichContentBytes ??
732
902
  DEFAULT_MAX_REQUEST_RICH_CONTENT_BYTES,
903
+ { imagesReadable: this.modelReadsImages(stepModel) },
733
904
  )
734
905
  appendWorkContext(shaping, messages, iterationNum, step)
735
906
  if (closingDirective) messages.push(closingDirective)
@@ -808,7 +979,8 @@ export class IterationOrchestrator {
808
979
  messages: Object.freeze(messages.map((m) => Object.freeze({ ...m }))),
809
980
  toolNames: Object.freeze(llmTools.map((t) => t.function.name)),
810
981
  temperature: step.temperature ?? turnConfig.temperature,
811
- maxTokens: step.maxResponseTokens ?? turnConfig.maxResponseTokens,
982
+ maxTokens:
983
+ outputCapOverride ?? step.maxResponseTokens ?? turnConfig.maxResponseTokens,
812
984
  }),
813
985
  },
814
986
  this.ctx.emitEvent,
@@ -881,7 +1053,8 @@ export class IterationOrchestrator {
881
1053
  ? step.toolChoice
882
1054
  : undefined,
883
1055
  temperature: step.temperature ?? turnConfig.temperature,
884
- maxTokens: step.maxResponseTokens ?? turnConfig.maxResponseTokens,
1056
+ maxTokens:
1057
+ outputCapOverride ?? step.maxResponseTokens ?? turnConfig.maxResponseTokens,
885
1058
  cacheControl: { type: 'auto' },
886
1059
  ...(turnConfig.thinking ? { thinking: turnConfig.thinking } : {}),
887
1060
  ...(turnConfig.effort ? { effort: turnConfig.effort } : {}),
@@ -903,6 +1076,8 @@ export class IterationOrchestrator {
903
1076
  stepMessageId,
904
1077
  {
905
1078
  onAccepted: (identity) => this.acceptProviderRejectedImage(identity),
1079
+ textOnly: this.textOnlyRecovery(stepModel),
1080
+ repair: this.requestRepair,
906
1081
  },
907
1082
  Boolean(
908
1083
  this.ctx.reviewAnswer || this.ctx.structuredOutput?.review || this.ctx.advisoryCtx,
@@ -956,7 +1131,10 @@ export class IterationOrchestrator {
956
1131
 
957
1132
  // The turn went through, so the turn is not sitting on an
958
1133
  // irreducible prompt any more. Re-arm relief for the next one.
959
- overflowRelieved = false
1134
+ overflowReliefs = 0
1135
+ clearCompactionErrorNote(this.ctx)
1136
+ outputCapOverride = undefined
1137
+ if (response.finishReason !== 'length') this.outputContinuations = 0
960
1138
 
961
1139
  if (this.ctx.pluginManager) {
962
1140
  const hookResults = await this.ctx.pluginManager.executeHooks(
@@ -1283,19 +1461,55 @@ export class IterationOrchestrator {
1283
1461
  // it has no room left to continue into, and the
1284
1462
  // continuation would only send a longer prompt into
1285
1463
  // the same full window. The reply stands as it is.
1464
+ const cutDuringReasoning =
1465
+ !hasContent &&
1466
+ (response.message.toolCalls?.length ?? 0) === 0 &&
1467
+ (response.message.reasoning?.length ?? 0) > 0
1286
1468
  if (
1287
1469
  !forceFinalize &&
1288
1470
  response.finishReason === 'length' &&
1289
1471
  response.finishDetail !== 'context_window' &&
1290
- hasContent
1472
+ (hasContent || cutDuringReasoning)
1291
1473
  ) {
1292
- this.ctx.log.info('LLM hit max_tokens mid-text — auto-continuing', {
1293
- [NAMZU.TURN_ID]: recorder.turnId,
1294
- [NAMZU.ITERATION]: iterationNum,
1295
- [GENAI.USAGE_OUTPUT_TOKENS]: response.usage.completionTokens,
1296
- })
1474
+ // A host that opted in gets a cap on the nudges: a model that
1475
+ // overruns its output limit every time would otherwise spend the
1476
+ // whole turn on it. Past the cap the turn stops, and says why.
1477
+ const cap = this.ctx.maxOutputContinuations
1478
+ if (cap !== undefined && this.outputContinuations >= cap) {
1479
+ this.ctx.log.warn('LLM kept hitting max_tokens — stopping the turn', {
1480
+ [NAMZU.TURN_ID]: recorder.turnId,
1481
+ [NAMZU.ITERATION]: iterationNum,
1482
+ 'namzu.runtime.output_continuations': this.outputContinuations,
1483
+ })
1484
+ recorder.setLastError(
1485
+ `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.`,
1486
+ )
1487
+ recorder.setStopReason('output_limit')
1488
+ await this.ctx.emitEvent({
1489
+ type: 'iteration_completed',
1490
+ turnId: recorder.turnId,
1491
+ iteration: iterationNum,
1492
+ hasToolCalls: false,
1493
+ })
1494
+ yield* this.ctx.drainPending()
1495
+ break
1496
+ }
1497
+ this.outputContinuations++
1498
+ this.ctx.log.info(
1499
+ cutDuringReasoning
1500
+ ? 'LLM hit max_tokens while reasoning — nudging to answer briefly'
1501
+ : 'LLM hit max_tokens mid-text — auto-continuing',
1502
+ {
1503
+ [NAMZU.TURN_ID]: recorder.turnId,
1504
+ [NAMZU.ITERATION]: iterationNum,
1505
+ [GENAI.USAGE_OUTPUT_TOKENS]: response.usage.completionTokens,
1506
+ },
1507
+ )
1297
1508
  recorder.pushMessage(
1298
- createRuntimeContextMessage(AUTO_CONTINUATION_USER_MESSAGE, 'auto-continuation'),
1509
+ createRuntimeContextMessage(
1510
+ cutDuringReasoning ? REASONING_CUT_USER_MESSAGE : AUTO_CONTINUATION_USER_MESSAGE,
1511
+ 'auto-continuation',
1512
+ ),
1299
1513
  )
1300
1514
  await this.ctx.emitEvent({
1301
1515
  type: 'iteration_completed',
@@ -1451,6 +1665,27 @@ export class IterationOrchestrator {
1451
1665
  // also settle as `end_turn`, and there the deferred predicate
1452
1666
  // is not why the turn ended: those decided the answer
1453
1667
  // themselves.
1668
+ // The model is done and has no tool calls. A `turn_end` (or
1669
+ // `subagent_stop`) hook may refuse to let it be: its reason goes
1670
+ // back to the model and the turn goes on.
1671
+ if (!forceFinalize && closingStopReason === undefined) {
1672
+ const feedback = yield* this.askStopHooks()
1673
+ const stoppedByHook = this.ctx.toolExecutor.takeHalt?.()
1674
+ if (stoppedByHook !== undefined) {
1675
+ recorder.setLastError(`Stopped by a hook: ${stoppedByHook}`)
1676
+ recorder.setStopReason('stop_condition')
1677
+ break
1678
+ }
1679
+ if (feedback !== undefined) {
1680
+ recorder.pushMessage(
1681
+ createRuntimeContextMessage(
1682
+ `A hook asked for more before this turn ends:\n${feedback}`,
1683
+ 'hook-feedback',
1684
+ ),
1685
+ )
1686
+ continue
1687
+ }
1688
+ }
1454
1689
  recorder.setStopReason(
1455
1690
  closingStopReason ??
1456
1691
  (stopWasDeferredForOutstandingWork ? 'stop_condition' : 'end_turn'),
@@ -1495,6 +1730,28 @@ export class IterationOrchestrator {
1495
1730
  return
1496
1731
  }
1497
1732
 
1733
+ // A hook asked for the turn to stop once this step's calls were
1734
+ // answered. Every `tool_use` has its `tool_result` by now, so the
1735
+ // transcript is whole; what is left is to end the turn and say why.
1736
+ const hookHalt = this.ctx.toolExecutor.takeHalt?.()
1737
+ if (hookHalt !== undefined) {
1738
+ this.ctx.log.info('A hook stopped the turn', {
1739
+ [NAMZU.TURN_ID]: recorder.turnId,
1740
+ [NAMZU.ITERATION]: iterationNum,
1741
+ 'namzu.hook.reason': hookHalt,
1742
+ })
1743
+ recorder.setLastError(`Stopped by a hook: ${hookHalt}`)
1744
+ recorder.setStopReason('stop_condition')
1745
+ await this.ctx.emitEvent({
1746
+ type: 'iteration_completed',
1747
+ turnId: recorder.turnId,
1748
+ iteration: iterationNum,
1749
+ hasToolCalls: true,
1750
+ })
1751
+ yield* this.ctx.drainPending()
1752
+ break
1753
+ }
1754
+
1498
1755
  if (reviewOutcome.decision === 'rejected') {
1499
1756
  continue
1500
1757
  }
@@ -1728,6 +1985,12 @@ export class IterationOrchestrator {
1728
1985
  yield* this.ctx.drainPending()
1729
1986
  } catch (err) {
1730
1987
  const cancelled = this.ctx.abortController.signal.aborted
1988
+ // A deferred sandbox that was cancelled or ran out of time. The
1989
+ // turn settles it as `cancelled` / `timeout` in `query()`, which is
1990
+ // not a failure of this iteration: it is recorded the way a turn
1991
+ // stopped by its own abort or timeout is (a cancelled step, no
1992
+ // error span), so the ledger and the stop reason agree.
1993
+ const sandboxStopped = err instanceof SandboxAcquisitionStopped
1731
1994
 
1732
1995
  // This iteration gets a step too, and it is the one the
1733
1996
  // argument three hundred lines above was actually about.
@@ -1788,12 +2051,13 @@ export class IterationOrchestrator {
1788
2051
  startedAt: stepStartedAt,
1789
2052
  usageBefore,
1790
2053
  costBefore,
1791
- unfinished: cancelled
1792
- ? { finishReason: 'cancelled' }
1793
- : {
1794
- finishReason: 'error',
1795
- failure: describeStepFailure(err, this.ctx.provider.id),
1796
- },
2054
+ unfinished:
2055
+ cancelled || sandboxStopped
2056
+ ? { finishReason: 'cancelled' }
2057
+ : {
2058
+ finishReason: 'error',
2059
+ failure: describeStepFailure(err, this.ctx.provider.id),
2060
+ },
1797
2061
  })
1798
2062
  }
1799
2063
 
@@ -1822,23 +2086,89 @@ export class IterationOrchestrator {
1822
2086
  // actually shed something. A second overflow after a successful
1823
2087
  // compaction means the prompt is irreducible, and looping on it
1824
2088
  // would burn the budget to arrive at the same error.
2089
+ // The prompt fit and only the requested output did not: ask for what
2090
+ // room is left (less a margin) and send it again, once. A reply that
2091
+ // would be too small to be useful, or a cap that is not smaller than
2092
+ // the one already tried, is not retried: the same failure would follow.
2093
+ const outputOverflow =
2094
+ err instanceof AnswerReviewFailure ? undefined : readOutputOverflow(err)
2095
+ if (outputOverflow) {
2096
+ const room = Math.max(
2097
+ 0,
2098
+ outputOverflow.contextLimit - outputOverflow.inputTokens - OUTPUT_CAP_MARGIN_TOKENS,
2099
+ )
2100
+ if (
2101
+ room >= MIN_OUTPUT_CAP_TOKENS &&
2102
+ (outputCapOverride === undefined || room < outputCapOverride)
2103
+ ) {
2104
+ outputCapOverride = room
2105
+ this.ctx.log.info('Output cap does not fit beside the prompt — retrying with less', {
2106
+ [NAMZU.TURN_ID]: recorder.turnId,
2107
+ [NAMZU.ITERATION]: iterationNum,
2108
+ 'namzu.runtime.input_tokens': outputOverflow.inputTokens,
2109
+ 'namzu.runtime.context_limit': outputOverflow.contextLimit,
2110
+ 'namzu.runtime.adjusted_max_tokens': room,
2111
+ })
2112
+ if (iterationActivity) {
2113
+ this.ctx.activityStore.complete(iterationActivity.id)
2114
+ }
2115
+ continue
2116
+ }
2117
+ }
2118
+
1825
2119
  if (
1826
- !overflowRelieved &&
1827
2120
  !(err instanceof AnswerReviewFailure) &&
1828
2121
  classifyProviderError(err, this.ctx.provider.id).code === 'context_length_exceeded'
1829
2122
  ) {
1830
- overflowRelieved = true
1831
- const shed = await relieveOverflow(this.ctx)
2123
+ // The rapid-refill check comes first, before the first reactive relief
2124
+ // of an overflow, as in the reference terminal. A relief that follows
2125
+ // the previous compaction within the refill window counts toward the
2126
+ // limit, so a tool that re-fills the window every time it is called
2127
+ // stops the turn instead of being cleared and called again without end.
2128
+ // The deeper rung of the same overflow is not counted again.
2129
+ if (overflowReliefs === 0 && (await stopOnRefill(this.ctx))) {
2130
+ if (iterationActivity) {
2131
+ this.ctx.activityStore.fail(iterationActivity.id, toErrorMessage(err))
2132
+ }
2133
+ if (this.stopForCompactionBreaker()) break
2134
+ }
2135
+ // A rung that sheds too little to change the verdict is not worth a
2136
+ // provider call; climb to the next one at once.
2137
+ let shed = false
2138
+ while (!shed && overflowReliefs < OVERFLOW_RELIEF_LIMIT) {
2139
+ overflowReliefs++
2140
+ shed = await relieveOverflow(this.ctx, { deeper: overflowReliefs > 1 })
2141
+ }
1832
2142
  if (shed) {
2143
+ // Clearing results is a compaction too: only summaries and reducers
2144
+ // recorded themselves, so a clear-only relief was invisible to the
2145
+ // breaker.
2146
+ compactionBreaker(this.ctx)?.recordCompaction(iterationNum)
1833
2147
  this.ctx.log.info('Retrying the turn after relieving a context overflow', {
1834
2148
  [NAMZU.TURN_ID]: recorder.turnId,
1835
2149
  [NAMZU.ITERATION]: iterationNum,
2150
+ 'namzu.runtime.relief_rung': overflowReliefs,
1836
2151
  })
1837
2152
  if (iterationActivity) {
1838
2153
  this.ctx.activityStore.complete(iterationActivity.id)
1839
2154
  }
1840
2155
  continue
1841
2156
  }
2157
+ // Out of rungs. The conversation is kept; say why compaction could not
2158
+ // make room when it errored, as the reference does.
2159
+ const cause = compactionErrorNote(this.ctx)
2160
+ if (cause !== undefined && err instanceof Error) {
2161
+ try {
2162
+ err.message = `${err.message}${AUTOMATIC_COMPACTION_FAILED}${cause}`
2163
+ } catch {
2164
+ // A frozen error keeps its own message; the cause stays in the log.
2165
+ }
2166
+ }
2167
+ }
2168
+
2169
+ if (sandboxStopped) {
2170
+ if (iterationActivity) this.ctx.activityStore.complete(iterationActivity.id)
2171
+ throw err
1842
2172
  }
1843
2173
 
1844
2174
  if (iterationActivity) {
@@ -1925,6 +2255,39 @@ export class IterationOrchestrator {
1925
2255
  }
1926
2256
  }
1927
2257
 
2258
+ /**
2259
+ * Ask the host's reminder source (`QueryParams.reminders`) what the model
2260
+ * should be told before this request, and append each text as a hidden
2261
+ * user-role message. The start of a request is a provider-valid boundary:
2262
+ * the history ends in the operator's message or in a finished tool batch.
2263
+ * A source that throws has nothing to say; it never fails the turn.
2264
+ */
2265
+ private async deliverReminders(iteration: number): Promise<void> {
2266
+ const source = this.ctx.reminders
2267
+ if (!source) return
2268
+ const { recorder } = this.ctx
2269
+ let texts: readonly string[] | undefined
2270
+ try {
2271
+ texts = await source({
2272
+ sessionId: recorder.sessionId,
2273
+ turnId: recorder.turnId,
2274
+ iteration,
2275
+ messages: recorder.messages,
2276
+ })
2277
+ } catch (error) {
2278
+ this.ctx.log.warn('A reminder source threw; the request goes without its reminders', {
2279
+ [NAMZU.TURN_ID]: recorder.turnId,
2280
+ [NAMZU.ITERATION]: iteration,
2281
+ 'exception.message': error instanceof Error ? error.message : String(error),
2282
+ })
2283
+ return
2284
+ }
2285
+ for (const text of texts ?? []) {
2286
+ if (text.trim().length === 0) continue
2287
+ recorder.pushMessage(createRuntimeContextMessage(text, 'reminder'))
2288
+ }
2289
+ }
2290
+
1928
2291
  private async deliverInbound(): Promise<number> {
1929
2292
  const queued = this.ctx.inboundMessages?.() ?? []
1930
2293
  for (const message of queued) {
@@ -2078,6 +2441,15 @@ export class IterationOrchestrator {
2078
2441
  return true
2079
2442
  }
2080
2443
 
2444
+ /** The compaction circuit breaker asked for the turn to stop (opt-in; see `compactionConfig.circuitBreaker`). */
2445
+ private stopForCompactionBreaker(): boolean {
2446
+ const reason = compactionBreaker(this.ctx)?.stop
2447
+ if (reason === undefined) return false
2448
+ this.ctx.recorder.setLastError(reason)
2449
+ this.ctx.recorder.setStopReason('error')
2450
+ return true
2451
+ }
2452
+
2081
2453
  private async recordToolStructuredFailure(
2082
2454
  results: readonly ToolCallOutcome[],
2083
2455
  iteration: number,
@@ -2404,6 +2776,32 @@ export class IterationOrchestrator {
2404
2776
  .some((t) => t.state !== 'completed' && t.state !== 'failed' && t.state !== 'canceled')
2405
2777
  }
2406
2778
 
2779
+ /** Media the provider could not process was left out of a request that then went through. */
2780
+ private async noteMediaRemoved(removal: MediaRemoval): Promise<void> {
2781
+ this.ctx.log.warn('Media the provider rejected was left out of the request', {
2782
+ [GENAI.SYSTEM]: this.ctx.provider.id,
2783
+ 'namzu.request.media_removed': removal.count,
2784
+ })
2785
+ const noun = removal.count === 1 ? 'a file' : `${removal.count} files`
2786
+ const it = removal.count === 1 ? 'it' : 'they'
2787
+ await this.ctx.emitEvent?.({
2788
+ type: 'capability_warning',
2789
+ turnId: this.ctx.recorder.turnId,
2790
+ capability: removal.kind === 'document' ? 'documents' : 'vision',
2791
+ // The person's own latest attachment is named as such; a count limit is
2792
+ // not a fault in any file.
2793
+ contentSource: removal.current
2794
+ ? 'media-removed-current'
2795
+ : removal.limit
2796
+ ? 'media-limit'
2797
+ : 'media-removed',
2798
+ providerId: this.ctx.provider.id,
2799
+ message: removal.limit
2800
+ ? `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}.`
2801
+ : `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}.`,
2802
+ })
2803
+ }
2804
+
2407
2805
  private async acceptProviderRejectedImage(identity: RequestImageIdentity): Promise<void> {
2408
2806
  const repaired = markProviderRejectedImage(this.ctx.recorder.messages, identity)
2409
2807
  if (repaired.count === 0) {
@@ -2461,6 +2859,7 @@ export class IterationOrchestrator {
2461
2859
  const finalMessages = projectRequestRichContent(
2462
2860
  this.projectObservations(finalHistory),
2463
2861
  this.ctx.turnConfig.maxRequestRichContentBytes ?? DEFAULT_MAX_REQUEST_RICH_CONTENT_BYTES,
2862
+ { imagesReadable: this.modelReadsImages(model) },
2464
2863
  )
2465
2864
  appendWorkContext(this.stepShaping(), finalMessages, this.steps.length + 1, { model })
2466
2865
  // The closing directive is the last thing the model reads: after
@@ -2507,8 +2906,13 @@ export class IterationOrchestrator {
2507
2906
  signal: this.ctx.abortController.signal,
2508
2907
  } satisfies import('../../../types/provider/index.js').ChatCompletionParams
2509
2908
  const response = await collectChatCompletion(
2510
- streamWithProviderRejectedImageRecovery(this.ctx.provider, finalParams, (identity) =>
2511
- this.acceptProviderRejectedImage(identity),
2909
+ streamWithProviderRejectedImageRecovery(
2910
+ this.ctx.provider,
2911
+ finalParams,
2912
+ (identity) => this.acceptProviderRejectedImage(identity),
2913
+ undefined,
2914
+ this.textOnlyRecovery(model),
2915
+ this.requestRepair,
2512
2916
  ),
2513
2917
  )
2514
2918