@namzu/sdk 51.0.0 → 52.0.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 (352) hide show
  1. package/CHANGELOG.md +110 -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/bridge/sse/mapper.d.ts.map +1 -1
  39. package/dist/bridge/sse/mapper.js +1 -0
  40. package/dist/bridge/sse/mapper.js.map +1 -1
  41. package/dist/compaction/context-window.d.ts +24 -0
  42. package/dist/compaction/context-window.d.ts.map +1 -1
  43. package/dist/compaction/context-window.js +34 -0
  44. package/dist/compaction/context-window.js.map +1 -1
  45. package/dist/compaction/manual.d.ts.map +1 -1
  46. package/dist/compaction/manual.js +10 -2
  47. package/dist/compaction/manual.js.map +1 -1
  48. package/dist/compaction/plan.d.ts +4 -0
  49. package/dist/compaction/plan.d.ts.map +1 -1
  50. package/dist/compaction/plan.js +6 -2
  51. package/dist/compaction/plan.js.map +1 -1
  52. package/dist/compaction/verifier.d.ts +11 -1
  53. package/dist/compaction/verifier.d.ts.map +1 -1
  54. package/dist/compaction/verifier.js +19 -7
  55. package/dist/compaction/verifier.js.map +1 -1
  56. package/dist/config/runtime.d.ts +55 -7
  57. package/dist/config/runtime.d.ts.map +1 -1
  58. package/dist/config/runtime.js +23 -2
  59. package/dist/config/runtime.js.map +1 -1
  60. package/dist/connector/web/guarded-fetch.d.ts.map +1 -1
  61. package/dist/connector/web/guarded-fetch.js +24 -0
  62. package/dist/connector/web/guarded-fetch.js.map +1 -1
  63. package/dist/connector/web/index.d.ts +1 -1
  64. package/dist/connector/web/index.d.ts.map +1 -1
  65. package/dist/connector/web/page.d.ts +59 -0
  66. package/dist/connector/web/page.d.ts.map +1 -0
  67. package/dist/connector/web/page.js +112 -0
  68. package/dist/connector/web/page.js.map +1 -0
  69. package/dist/connector/web/types.d.ts +47 -0
  70. package/dist/connector/web/types.d.ts.map +1 -1
  71. package/dist/connector/web/types.js.map +1 -1
  72. package/dist/constants/continuation.d.ts +8 -0
  73. package/dist/constants/continuation.d.ts.map +1 -1
  74. package/dist/constants/continuation.js +8 -0
  75. package/dist/constants/continuation.js.map +1 -1
  76. package/dist/constants/plugin/index.d.ts +1 -1
  77. package/dist/constants/plugin/index.d.ts.map +1 -1
  78. package/dist/constants/plugin/index.js +1 -1
  79. package/dist/constants/plugin/index.js.map +1 -1
  80. package/dist/manager/agent/lifecycle.d.ts +7 -0
  81. package/dist/manager/agent/lifecycle.d.ts.map +1 -1
  82. package/dist/manager/agent/lifecycle.js +11 -2
  83. package/dist/manager/agent/lifecycle.js.map +1 -1
  84. package/dist/manager/session/turn-recorder.d.ts.map +1 -1
  85. package/dist/manager/session/turn-recorder.js +4 -2
  86. package/dist/manager/session/turn-recorder.js.map +1 -1
  87. package/dist/plugin/index.d.ts +1 -1
  88. package/dist/plugin/index.d.ts.map +1 -1
  89. package/dist/plugin/index.js +1 -1
  90. package/dist/plugin/index.js.map +1 -1
  91. package/dist/plugin/lifecycle.d.ts +6 -0
  92. package/dist/plugin/lifecycle.d.ts.map +1 -1
  93. package/dist/plugin/lifecycle.js +98 -114
  94. package/dist/plugin/lifecycle.js.map +1 -1
  95. package/dist/plugin/shell-hook.d.ts +90 -22
  96. package/dist/plugin/shell-hook.d.ts.map +1 -1
  97. package/dist/plugin/shell-hook.js +434 -41
  98. package/dist/plugin/shell-hook.js.map +1 -1
  99. package/dist/provider/errors.d.ts +10 -0
  100. package/dist/provider/errors.d.ts.map +1 -1
  101. package/dist/provider/errors.js +16 -0
  102. package/dist/provider/errors.js.map +1 -1
  103. package/dist/provider/retry.d.ts +27 -0
  104. package/dist/provider/retry.d.ts.map +1 -1
  105. package/dist/provider/retry.js +19 -3
  106. package/dist/provider/retry.js.map +1 -1
  107. package/dist/public-runtime.d.ts +4 -1
  108. package/dist/public-runtime.d.ts.map +1 -1
  109. package/dist/public-runtime.js +4 -1
  110. package/dist/public-runtime.js.map +1 -1
  111. package/dist/public-types.d.ts +2 -2
  112. package/dist/public-types.d.ts.map +1 -1
  113. package/dist/runtime/jobs/registry.d.ts +21 -0
  114. package/dist/runtime/jobs/registry.d.ts.map +1 -1
  115. package/dist/runtime/jobs/registry.js +15 -2
  116. package/dist/runtime/jobs/registry.js.map +1 -1
  117. package/dist/runtime/query/executor/tool-call-admission.d.ts +10 -0
  118. package/dist/runtime/query/executor/tool-call-admission.d.ts.map +1 -1
  119. package/dist/runtime/query/executor/tool-call-admission.js +74 -13
  120. package/dist/runtime/query/executor/tool-call-admission.js.map +1 -1
  121. package/dist/runtime/query/executor.d.ts +41 -2
  122. package/dist/runtime/query/executor.d.ts.map +1 -1
  123. package/dist/runtime/query/executor.js +190 -6
  124. package/dist/runtime/query/executor.js.map +1 -1
  125. package/dist/runtime/query/finalize-turn.d.ts +6 -0
  126. package/dist/runtime/query/finalize-turn.d.ts.map +1 -1
  127. package/dist/runtime/query/finalize-turn.js +1 -1
  128. package/dist/runtime/query/finalize-turn.js.map +1 -1
  129. package/dist/runtime/query/index.d.ts +29 -0
  130. package/dist/runtime/query/index.d.ts.map +1 -1
  131. package/dist/runtime/query/index.js +15 -3
  132. package/dist/runtime/query/index.js.map +1 -1
  133. package/dist/runtime/query/iteration/index.d.ts +31 -0
  134. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  135. package/dist/runtime/query/iteration/index.js +216 -7
  136. package/dist/runtime/query/iteration/index.js.map +1 -1
  137. package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts +58 -0
  138. package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts.map +1 -0
  139. package/dist/runtime/query/iteration/phases/compaction-breaker.js +91 -0
  140. package/dist/runtime/query/iteration/phases/compaction-breaker.js.map +1 -0
  141. package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
  142. package/dist/runtime/query/iteration/phases/compaction.js +89 -7
  143. package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
  144. package/dist/runtime/query/iteration/phases/context.d.ts +10 -0
  145. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  146. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  147. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  148. package/dist/runtime/query/iteration/phases/tool-review.js +165 -2
  149. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  150. package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
  151. package/dist/runtime/query/iteration/stream-turn.js +24 -1
  152. package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
  153. package/dist/runtime/query/path-escalation.d.ts +36 -0
  154. package/dist/runtime/query/path-escalation.d.ts.map +1 -0
  155. package/dist/runtime/query/path-escalation.js +184 -0
  156. package/dist/runtime/query/path-escalation.js.map +1 -0
  157. package/dist/runtime/query/plugin-hooks.d.ts.map +1 -1
  158. package/dist/runtime/query/plugin-hooks.js +8 -0
  159. package/dist/runtime/query/plugin-hooks.js.map +1 -1
  160. package/dist/runtime/query/reminders.d.ts +35 -0
  161. package/dist/runtime/query/reminders.d.ts.map +1 -0
  162. package/dist/runtime/query/reminders.js +7 -0
  163. package/dist/runtime/query/reminders.js.map +1 -0
  164. package/dist/runtime/query/resume-pending.d.ts.map +1 -1
  165. package/dist/runtime/query/resume-pending.js +26 -0
  166. package/dist/runtime/query/resume-pending.js.map +1 -1
  167. package/dist/runtime/query/review-policy.d.ts +26 -0
  168. package/dist/runtime/query/review-policy.d.ts.map +1 -1
  169. package/dist/runtime/query/review-policy.js +79 -5
  170. package/dist/runtime/query/review-policy.js.map +1 -1
  171. package/dist/runtime/query/tool-output-budget.d.ts +46 -0
  172. package/dist/runtime/query/tool-output-budget.d.ts.map +1 -1
  173. package/dist/runtime/query/tool-output-budget.js +85 -1
  174. package/dist/runtime/query/tool-output-budget.js.map +1 -1
  175. package/dist/runtime/query/tooling.d.ts +1 -0
  176. package/dist/runtime/query/tooling.d.ts.map +1 -1
  177. package/dist/runtime/query/tooling.js +5 -0
  178. package/dist/runtime/query/tooling.js.map +1 -1
  179. package/dist/session/paths.d.ts +5 -1
  180. package/dist/session/paths.d.ts.map +1 -1
  181. package/dist/session/paths.js +8 -0
  182. package/dist/session/paths.js.map +1 -1
  183. package/dist/store/memory/disk.d.ts.map +1 -1
  184. package/dist/store/memory/disk.js +2 -2
  185. package/dist/store/memory/disk.js.map +1 -1
  186. package/dist/store/memory/markdown.d.ts.map +1 -1
  187. package/dist/store/memory/markdown.js +2 -2
  188. package/dist/store/memory/markdown.js.map +1 -1
  189. package/dist/store/memory/naming.d.ts +9 -0
  190. package/dist/store/memory/naming.d.ts.map +1 -1
  191. package/dist/store/memory/naming.js +12 -0
  192. package/dist/store/memory/naming.js.map +1 -1
  193. package/dist/tools/builtins/bash.d.ts +30 -1
  194. package/dist/tools/builtins/bash.d.ts.map +1 -1
  195. package/dist/tools/builtins/bash.js +170 -26
  196. package/dist/tools/builtins/bash.js.map +1 -1
  197. package/dist/tools/builtins/content-fingerprint.d.ts +2 -0
  198. package/dist/tools/builtins/content-fingerprint.d.ts.map +1 -1
  199. package/dist/tools/builtins/content-fingerprint.js +4 -0
  200. package/dist/tools/builtins/content-fingerprint.js.map +1 -1
  201. package/dist/tools/builtins/edit.d.ts.map +1 -1
  202. package/dist/tools/builtins/edit.js +60 -18
  203. package/dist/tools/builtins/edit.js.map +1 -1
  204. package/dist/tools/builtins/grep.d.ts +8 -1
  205. package/dist/tools/builtins/grep.d.ts.map +1 -1
  206. package/dist/tools/builtins/grep.js +152 -33
  207. package/dist/tools/builtins/grep.js.map +1 -1
  208. package/dist/tools/builtins/read-file.d.ts +1 -0
  209. package/dist/tools/builtins/read-file.d.ts.map +1 -1
  210. package/dist/tools/builtins/read-file.js +44 -5
  211. package/dist/tools/builtins/read-file.js.map +1 -1
  212. package/dist/tools/builtins/read-render.d.ts +52 -0
  213. package/dist/tools/builtins/read-render.d.ts.map +1 -1
  214. package/dist/tools/builtins/read-render.js +143 -0
  215. package/dist/tools/builtins/read-render.js.map +1 -1
  216. package/dist/tools/builtins/web.d.ts +13 -2
  217. package/dist/tools/builtins/web.d.ts.map +1 -1
  218. package/dist/tools/builtins/web.js +225 -32
  219. package/dist/tools/builtins/web.js.map +1 -1
  220. package/dist/tools/coordinator/ask-user-question.d.ts +2 -0
  221. package/dist/tools/coordinator/ask-user-question.d.ts.map +1 -1
  222. package/dist/tools/coordinator/ask-user-question.js +256 -92
  223. package/dist/tools/coordinator/ask-user-question.js.map +1 -1
  224. package/dist/tools/coordinator/question-options.d.ts +1 -0
  225. package/dist/tools/coordinator/question-options.d.ts.map +1 -1
  226. package/dist/tools/coordinator/question-options.js +6 -1
  227. package/dist/tools/coordinator/question-options.js.map +1 -1
  228. package/dist/tools/defineTool.d.ts +4 -1
  229. package/dist/tools/defineTool.d.ts.map +1 -1
  230. package/dist/tools/defineTool.js +6 -1
  231. package/dist/tools/defineTool.js.map +1 -1
  232. package/dist/tools/paths.d.ts +6 -0
  233. package/dist/tools/paths.d.ts.map +1 -1
  234. package/dist/tools/paths.js +1 -1
  235. package/dist/tools/paths.js.map +1 -1
  236. package/dist/types/agent/query.d.ts +3 -0
  237. package/dist/types/agent/query.d.ts.map +1 -1
  238. package/dist/types/errors/catalog.js +1 -1
  239. package/dist/types/errors/catalog.js.map +1 -1
  240. package/dist/types/hitl/index.d.ts +104 -0
  241. package/dist/types/hitl/index.d.ts.map +1 -1
  242. package/dist/types/hitl/index.js.map +1 -1
  243. package/dist/types/message/index.d.ts +1 -1
  244. package/dist/types/message/index.d.ts.map +1 -1
  245. package/dist/types/message/index.js +2 -0
  246. package/dist/types/message/index.js.map +1 -1
  247. package/dist/types/plugin/index.d.ts +93 -19
  248. package/dist/types/plugin/index.d.ts.map +1 -1
  249. package/dist/types/plugin/index.js +3 -0
  250. package/dist/types/plugin/index.js.map +1 -1
  251. package/dist/types/provider/error.d.ts +6 -0
  252. package/dist/types/provider/error.d.ts.map +1 -1
  253. package/dist/types/provider/errors.d.ts +19 -0
  254. package/dist/types/provider/errors.d.ts.map +1 -1
  255. package/dist/types/provider/errors.js +39 -1
  256. package/dist/types/provider/errors.js.map +1 -1
  257. package/dist/types/provider/stream.d.ts +6 -0
  258. package/dist/types/provider/stream.d.ts.map +1 -1
  259. package/dist/types/session/events.d.ts +15 -2
  260. package/dist/types/session/events.d.ts.map +1 -1
  261. package/dist/types/session/events.js.map +1 -1
  262. package/dist/types/session/records.d.ts +9 -9
  263. package/dist/types/session/records.d.ts.map +1 -1
  264. package/dist/types/session/records.js +1 -0
  265. package/dist/types/session/records.js.map +1 -1
  266. package/dist/types/session/stop-reason.d.ts +5 -0
  267. package/dist/types/session/stop-reason.d.ts.map +1 -1
  268. package/dist/types/tool/index.d.ts +52 -0
  269. package/dist/types/tool/index.d.ts.map +1 -1
  270. package/dist/types/tool/index.js.map +1 -1
  271. package/dist/utils/backoff.d.ts +12 -0
  272. package/dist/utils/backoff.d.ts.map +1 -1
  273. package/dist/utils/backoff.js +15 -0
  274. package/dist/utils/backoff.js.map +1 -1
  275. package/package.json +4 -2
  276. package/src/agents/QueryAgent.ts +5 -1
  277. package/src/agents/SupervisorAgent.ts +1 -1
  278. package/src/agents/examples/SupervisorAgent.ts +217 -106
  279. package/src/agents/index.ts +1 -1
  280. package/src/authorization/protected-paths.ts +110 -0
  281. package/src/authorization/readonly-commands.ts +771 -0
  282. package/src/authorization/readonly-flags.ts +196 -0
  283. package/src/authorization/removal.ts +326 -0
  284. package/src/authorization/shell-paths.ts +927 -0
  285. package/src/bridge/acp/update.ts +1 -0
  286. package/src/bridge/sse/mapper.ts +1 -0
  287. package/src/compaction/context-window.ts +41 -0
  288. package/src/compaction/manual.ts +10 -2
  289. package/src/compaction/plan.ts +11 -2
  290. package/src/compaction/verifier.ts +28 -3
  291. package/src/config/runtime.ts +23 -2
  292. package/src/connector/web/guarded-fetch.ts +26 -0
  293. package/src/connector/web/index.ts +3 -0
  294. package/src/connector/web/page.ts +130 -0
  295. package/src/connector/web/types.ts +47 -0
  296. package/src/constants/continuation.ts +10 -0
  297. package/src/constants/plugin/index.ts +1 -1
  298. package/src/manager/agent/lifecycle.ts +12 -4
  299. package/src/manager/session/turn-recorder.ts +10 -2
  300. package/src/plugin/index.ts +1 -0
  301. package/src/plugin/lifecycle.ts +107 -119
  302. package/src/plugin/shell-hook.ts +477 -43
  303. package/src/provider/errors.ts +18 -0
  304. package/src/provider/retry.ts +53 -3
  305. package/src/public-runtime.ts +8 -0
  306. package/src/public-types.ts +4 -0
  307. package/src/runtime/jobs/registry.ts +39 -2
  308. package/src/runtime/query/executor/tool-call-admission.ts +79 -13
  309. package/src/runtime/query/executor.ts +242 -8
  310. package/src/runtime/query/finalize-turn.ts +7 -1
  311. package/src/runtime/query/index.ts +45 -3
  312. package/src/runtime/query/iteration/index.ts +251 -12
  313. package/src/runtime/query/iteration/phases/compaction-breaker.ts +106 -0
  314. package/src/runtime/query/iteration/phases/compaction.ts +99 -12
  315. package/src/runtime/query/iteration/phases/context.ts +11 -0
  316. package/src/runtime/query/iteration/phases/tool-review.ts +177 -2
  317. package/src/runtime/query/iteration/stream-turn.ts +24 -1
  318. package/src/runtime/query/path-escalation.ts +204 -0
  319. package/src/runtime/query/plugin-hooks.ts +8 -0
  320. package/src/runtime/query/reminders.ts +45 -0
  321. package/src/runtime/query/resume-pending.ts +27 -0
  322. package/src/runtime/query/review-policy.ts +106 -5
  323. package/src/runtime/query/tool-output-budget.ts +115 -1
  324. package/src/runtime/query/tooling.ts +6 -0
  325. package/src/session/paths.ts +11 -1
  326. package/src/store/memory/disk.ts +2 -1
  327. package/src/store/memory/markdown.ts +2 -1
  328. package/src/store/memory/naming.ts +13 -0
  329. package/src/tools/builtins/bash.ts +203 -34
  330. package/src/tools/builtins/content-fingerprint.ts +5 -0
  331. package/src/tools/builtins/edit.ts +69 -19
  332. package/src/tools/builtins/grep.ts +159 -38
  333. package/src/tools/builtins/read-file.ts +48 -5
  334. package/src/tools/builtins/read-render.ts +193 -0
  335. package/src/tools/builtins/web.ts +258 -33
  336. package/src/tools/coordinator/ask-user-question.ts +309 -103
  337. package/src/tools/coordinator/question-options.ts +8 -1
  338. package/src/tools/defineTool.ts +11 -2
  339. package/src/tools/paths.ts +1 -1
  340. package/src/types/agent/query.ts +3 -0
  341. package/src/types/errors/catalog.ts +1 -1
  342. package/src/types/hitl/index.ts +104 -0
  343. package/src/types/message/index.ts +2 -0
  344. package/src/types/plugin/index.ts +101 -18
  345. package/src/types/provider/error.ts +6 -0
  346. package/src/types/provider/errors.ts +49 -1
  347. package/src/types/provider/stream.ts +6 -0
  348. package/src/types/session/events.ts +15 -2
  349. package/src/types/session/records.ts +1 -0
  350. package/src/types/session/stop-reason.ts +5 -0
  351. package/src/types/tool/index.ts +53 -2
  352. package/src/utils/backoff.ts +21 -0
@@ -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,
@@ -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 {
@@ -75,6 +78,7 @@ import {
75
78
  } from './outstanding-work.js'
76
79
  import { runAdvisoryPhase } from './phases/advisory.js'
77
80
  import { runIterationCheckpoint } from './phases/checkpoint.js'
81
+ import { compactionBreaker } from './phases/compaction-breaker.js'
78
82
  import {
79
83
  activeContextWindow,
80
84
  measureContext,
@@ -125,6 +129,11 @@ export type { ToolReviewOutcome } from './phases/index.js'
125
129
  */
126
130
  const DEFAULT_ANSWER_REVIEW_LIMIT = 3
127
131
 
132
+ /** Left unused when an output cap is shrunk to fit the window (reference: 1000). */
133
+ const OUTPUT_CAP_MARGIN_TOKENS = 1000
134
+ /** A shrunk output cap below this is not worth a call (reference floor: 3000). */
135
+ const MIN_OUTPUT_CAP_TOKENS = 3000
136
+
128
137
  // Ending a turn changes the available actions, not the strength of its evidence.
129
138
  // Use the same standard for warning closure and empty-completion recovery.
130
139
  const CLOSING_RESPONSE_GUIDANCE =
@@ -194,6 +203,74 @@ export class IterationOrchestrator {
194
203
  * sets it.
195
204
  */
196
205
  private stopDeferredForOutstandingWork = false
206
+ /** Nudges sent since the model last finished a call without hitting its output cap. */
207
+ private outputContinuations = 0
208
+ /**
209
+ * `turn_end` (and, for a child, `subagent_stop`) fired when the model
210
+ * finished and no hook asked for more. `finalizeTurn` then does not fire
211
+ * them a second time.
212
+ */
213
+ private turnEndFired = false
214
+ /** A hook already kept this turn going once: it is asked again with `stop_hook_active`. */
215
+ private stopHookActive = false
216
+ get turnEndHooksFired(): boolean {
217
+ return this.turnEndFired
218
+ }
219
+
220
+ /**
221
+ * Fire `turn_end` (and `subagent_stop` for a child) at the point the model
222
+ * has finished, and read the answer.
223
+ *
224
+ * A hook that answers `skip` is refusing to let the turn end: its reasons
225
+ * are returned, joined, as feedback for the model. A hook that asks for the
226
+ * turn to halt wins over any such feedback (`takeHalt`). With nothing asked,
227
+ * the events have fired for this turn and the finalizer does not repeat them.
228
+ */
229
+ private async *askStopHooks(): AsyncGenerator<SessionEvent, string | undefined> {
230
+ const manager = this.ctx.pluginManager
231
+ if (!manager) return undefined
232
+ const recorder = this.ctx.recorder
233
+ const feedback: string[] = []
234
+ const ask = async (
235
+ event: 'turn_end' | 'subagent_stop',
236
+ extra: { parentSessionId?: SessionId; parentTurnId?: TurnId },
237
+ ): Promise<void> => {
238
+ const results = await manager.executeHooks(
239
+ event,
240
+ {
241
+ sessionId: recorder.sessionId,
242
+ turnId: recorder.turnId,
243
+ ...extra,
244
+ ...(this.stopHookActive ? { stopHookActive: true } : {}),
245
+ signal: this.ctx.abortController.signal,
246
+ },
247
+ this.ctx.emitEvent,
248
+ )
249
+ applyLifecycleHookResults(event, results)
250
+ for (const result of results) {
251
+ if (result.action === 'skip') feedback.push(result.reason)
252
+ if ((result.action === 'continue' || result.action === 'skip') && result.halt) {
253
+ this.ctx.toolExecutor.requestHalt?.(result.halt.reason)
254
+ }
255
+ }
256
+ }
257
+ await ask('turn_end', {})
258
+ yield* this.ctx.drainPending()
259
+ if (this.ctx.parentSessionId !== undefined) {
260
+ await ask('subagent_stop', {
261
+ parentSessionId: this.ctx.parentSessionId,
262
+ ...(this.ctx.parentTurnId !== undefined ? { parentTurnId: this.ctx.parentTurnId } : {}),
263
+ })
264
+ yield* this.ctx.drainPending()
265
+ }
266
+ if (feedback.length === 0) {
267
+ this.turnEndFired = true
268
+ return undefined
269
+ }
270
+ this.stopHookActive = true
271
+ return feedback.join('\n')
272
+ }
273
+
197
274
  /** One current input, independent of the compactable history array. */
198
275
  private latestUserMessage: UserMessage | undefined
199
276
  private initialDurableDelivered = false
@@ -375,6 +452,9 @@ export class IterationOrchestrator {
375
452
  // obvious moves left. It is cleared by a turn that actually
376
453
  // succeeded, which is the evidence that the turn is no longer stuck.
377
454
  let overflowRelieved = false
455
+ // The output cap a provider told us fits beside this prompt. One request's
456
+ // worth: it is cleared by a call that went through.
457
+ let outputCapOverride: number | undefined
378
458
 
379
459
  // A `finally` rather than a line at each exit, for the reason written
380
460
  // beside `iterSpan.end()` below: this loop leaves by eight `break`s,
@@ -573,12 +653,15 @@ export class IterationOrchestrator {
573
653
  yield* this.ctx.drainPending()
574
654
  }
575
655
 
656
+ if (!forceFinalize) await this.deliverReminders(iterationNum)
657
+
576
658
  // Re-pin the working-memory block from ground truth at the primacy
577
659
  // edge BEFORE compaction runs (so the refreshed slot is what
578
660
  // compaction preserves). No-op when no provider is configured.
579
661
  await refreshWorkingMemory(this.ctx)
580
662
  await runCompactionCheck(this.ctx)
581
663
  yield* this.ctx.drainPending()
664
+ if (this.stopForCompactionBreaker()) break
582
665
 
583
666
  // Cache discipline: keep the tools param byte-stable even on the
584
667
  // forced-final iteration and forbid tool use via tool_choice
@@ -630,6 +713,7 @@ export class IterationOrchestrator {
630
713
  if (stepModel !== contextModelBeforePreparation) await runCompactionCheck(this.ctx)
631
714
  // Publish context edits before entering a possibly slow provider.
632
715
  yield* this.ctx.drainPending()
716
+ if (this.stopForCompactionBreaker()) break
633
717
 
634
718
  const stepAllowedTools = step.allowedTools ?? this.ctx.allowedTools
635
719
  const deferredMcpContext = deferredMcpHints?.forNames(stepAllowedTools) ?? ''
@@ -808,7 +892,8 @@ export class IterationOrchestrator {
808
892
  messages: Object.freeze(messages.map((m) => Object.freeze({ ...m }))),
809
893
  toolNames: Object.freeze(llmTools.map((t) => t.function.name)),
810
894
  temperature: step.temperature ?? turnConfig.temperature,
811
- maxTokens: step.maxResponseTokens ?? turnConfig.maxResponseTokens,
895
+ maxTokens:
896
+ outputCapOverride ?? step.maxResponseTokens ?? turnConfig.maxResponseTokens,
812
897
  }),
813
898
  },
814
899
  this.ctx.emitEvent,
@@ -881,7 +966,8 @@ export class IterationOrchestrator {
881
966
  ? step.toolChoice
882
967
  : undefined,
883
968
  temperature: step.temperature ?? turnConfig.temperature,
884
- maxTokens: step.maxResponseTokens ?? turnConfig.maxResponseTokens,
969
+ maxTokens:
970
+ outputCapOverride ?? step.maxResponseTokens ?? turnConfig.maxResponseTokens,
885
971
  cacheControl: { type: 'auto' },
886
972
  ...(turnConfig.thinking ? { thinking: turnConfig.thinking } : {}),
887
973
  ...(turnConfig.effort ? { effort: turnConfig.effort } : {}),
@@ -957,6 +1043,8 @@ export class IterationOrchestrator {
957
1043
  // The turn went through, so the turn is not sitting on an
958
1044
  // irreducible prompt any more. Re-arm relief for the next one.
959
1045
  overflowRelieved = false
1046
+ outputCapOverride = undefined
1047
+ if (response.finishReason !== 'length') this.outputContinuations = 0
960
1048
 
961
1049
  if (this.ctx.pluginManager) {
962
1050
  const hookResults = await this.ctx.pluginManager.executeHooks(
@@ -1283,19 +1371,55 @@ export class IterationOrchestrator {
1283
1371
  // it has no room left to continue into, and the
1284
1372
  // continuation would only send a longer prompt into
1285
1373
  // the same full window. The reply stands as it is.
1374
+ const cutDuringReasoning =
1375
+ !hasContent &&
1376
+ (response.message.toolCalls?.length ?? 0) === 0 &&
1377
+ (response.message.reasoning?.length ?? 0) > 0
1286
1378
  if (
1287
1379
  !forceFinalize &&
1288
1380
  response.finishReason === 'length' &&
1289
1381
  response.finishDetail !== 'context_window' &&
1290
- hasContent
1382
+ (hasContent || cutDuringReasoning)
1291
1383
  ) {
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
- })
1384
+ // A host that opted in gets a cap on the nudges: a model that
1385
+ // overruns its output limit every time would otherwise spend the
1386
+ // whole turn on it. Past the cap the turn stops, and says why.
1387
+ const cap = this.ctx.maxOutputContinuations
1388
+ if (cap !== undefined && this.outputContinuations >= cap) {
1389
+ this.ctx.log.warn('LLM kept hitting max_tokens — stopping the turn', {
1390
+ [NAMZU.TURN_ID]: recorder.turnId,
1391
+ [NAMZU.ITERATION]: iterationNum,
1392
+ 'namzu.runtime.output_continuations': this.outputContinuations,
1393
+ })
1394
+ recorder.setLastError(
1395
+ `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.`,
1396
+ )
1397
+ recorder.setStopReason('output_limit')
1398
+ await this.ctx.emitEvent({
1399
+ type: 'iteration_completed',
1400
+ turnId: recorder.turnId,
1401
+ iteration: iterationNum,
1402
+ hasToolCalls: false,
1403
+ })
1404
+ yield* this.ctx.drainPending()
1405
+ break
1406
+ }
1407
+ this.outputContinuations++
1408
+ this.ctx.log.info(
1409
+ cutDuringReasoning
1410
+ ? 'LLM hit max_tokens while reasoning — nudging to answer briefly'
1411
+ : 'LLM hit max_tokens mid-text — auto-continuing',
1412
+ {
1413
+ [NAMZU.TURN_ID]: recorder.turnId,
1414
+ [NAMZU.ITERATION]: iterationNum,
1415
+ [GENAI.USAGE_OUTPUT_TOKENS]: response.usage.completionTokens,
1416
+ },
1417
+ )
1297
1418
  recorder.pushMessage(
1298
- createRuntimeContextMessage(AUTO_CONTINUATION_USER_MESSAGE, 'auto-continuation'),
1419
+ createRuntimeContextMessage(
1420
+ cutDuringReasoning ? REASONING_CUT_USER_MESSAGE : AUTO_CONTINUATION_USER_MESSAGE,
1421
+ 'auto-continuation',
1422
+ ),
1299
1423
  )
1300
1424
  await this.ctx.emitEvent({
1301
1425
  type: 'iteration_completed',
@@ -1451,6 +1575,27 @@ export class IterationOrchestrator {
1451
1575
  // also settle as `end_turn`, and there the deferred predicate
1452
1576
  // is not why the turn ended: those decided the answer
1453
1577
  // themselves.
1578
+ // The model is done and has no tool calls. A `turn_end` (or
1579
+ // `subagent_stop`) hook may refuse to let it be: its reason goes
1580
+ // back to the model and the turn goes on.
1581
+ if (!forceFinalize && closingStopReason === undefined) {
1582
+ const feedback = yield* this.askStopHooks()
1583
+ const stoppedByHook = this.ctx.toolExecutor.takeHalt?.()
1584
+ if (stoppedByHook !== undefined) {
1585
+ recorder.setLastError(`Stopped by a hook: ${stoppedByHook}`)
1586
+ recorder.setStopReason('stop_condition')
1587
+ break
1588
+ }
1589
+ if (feedback !== undefined) {
1590
+ recorder.pushMessage(
1591
+ createRuntimeContextMessage(
1592
+ `A hook asked for more before this turn ends:\n${feedback}`,
1593
+ 'hook-feedback',
1594
+ ),
1595
+ )
1596
+ continue
1597
+ }
1598
+ }
1454
1599
  recorder.setStopReason(
1455
1600
  closingStopReason ??
1456
1601
  (stopWasDeferredForOutstandingWork ? 'stop_condition' : 'end_turn'),
@@ -1495,6 +1640,28 @@ export class IterationOrchestrator {
1495
1640
  return
1496
1641
  }
1497
1642
 
1643
+ // A hook asked for the turn to stop once this step's calls were
1644
+ // answered. Every `tool_use` has its `tool_result` by now, so the
1645
+ // transcript is whole; what is left is to end the turn and say why.
1646
+ const hookHalt = this.ctx.toolExecutor.takeHalt?.()
1647
+ if (hookHalt !== undefined) {
1648
+ this.ctx.log.info('A hook stopped the turn', {
1649
+ [NAMZU.TURN_ID]: recorder.turnId,
1650
+ [NAMZU.ITERATION]: iterationNum,
1651
+ 'namzu.hook.reason': hookHalt,
1652
+ })
1653
+ recorder.setLastError(`Stopped by a hook: ${hookHalt}`)
1654
+ recorder.setStopReason('stop_condition')
1655
+ await this.ctx.emitEvent({
1656
+ type: 'iteration_completed',
1657
+ turnId: recorder.turnId,
1658
+ iteration: iterationNum,
1659
+ hasToolCalls: true,
1660
+ })
1661
+ yield* this.ctx.drainPending()
1662
+ break
1663
+ }
1664
+
1498
1665
  if (reviewOutcome.decision === 'rejected') {
1499
1666
  continue
1500
1667
  }
@@ -1822,6 +1989,36 @@ export class IterationOrchestrator {
1822
1989
  // actually shed something. A second overflow after a successful
1823
1990
  // compaction means the prompt is irreducible, and looping on it
1824
1991
  // would burn the budget to arrive at the same error.
1992
+ // The prompt fit and only the requested output did not: ask for what
1993
+ // room is left (less a margin) and send it again, once. A reply that
1994
+ // would be too small to be useful, or a cap that is not smaller than
1995
+ // the one already tried, is not retried: the same failure would follow.
1996
+ const outputOverflow =
1997
+ err instanceof AnswerReviewFailure ? undefined : readOutputOverflow(err)
1998
+ if (outputOverflow) {
1999
+ const room = Math.max(
2000
+ 0,
2001
+ outputOverflow.contextLimit - outputOverflow.inputTokens - OUTPUT_CAP_MARGIN_TOKENS,
2002
+ )
2003
+ if (
2004
+ room >= MIN_OUTPUT_CAP_TOKENS &&
2005
+ (outputCapOverride === undefined || room < outputCapOverride)
2006
+ ) {
2007
+ outputCapOverride = room
2008
+ this.ctx.log.info('Output cap does not fit beside the prompt — retrying with less', {
2009
+ [NAMZU.TURN_ID]: recorder.turnId,
2010
+ [NAMZU.ITERATION]: iterationNum,
2011
+ 'namzu.runtime.input_tokens': outputOverflow.inputTokens,
2012
+ 'namzu.runtime.context_limit': outputOverflow.contextLimit,
2013
+ 'namzu.runtime.adjusted_max_tokens': room,
2014
+ })
2015
+ if (iterationActivity) {
2016
+ this.ctx.activityStore.complete(iterationActivity.id)
2017
+ }
2018
+ continue
2019
+ }
2020
+ }
2021
+
1825
2022
  if (
1826
2023
  !overflowRelieved &&
1827
2024
  !(err instanceof AnswerReviewFailure) &&
@@ -1925,6 +2122,39 @@ export class IterationOrchestrator {
1925
2122
  }
1926
2123
  }
1927
2124
 
2125
+ /**
2126
+ * Ask the host's reminder source (`QueryParams.reminders`) what the model
2127
+ * should be told before this request, and append each text as a hidden
2128
+ * user-role message. The start of a request is a provider-valid boundary:
2129
+ * the history ends in the operator's message or in a finished tool batch.
2130
+ * A source that throws has nothing to say; it never fails the turn.
2131
+ */
2132
+ private async deliverReminders(iteration: number): Promise<void> {
2133
+ const source = this.ctx.reminders
2134
+ if (!source) return
2135
+ const { recorder } = this.ctx
2136
+ let texts: readonly string[] | undefined
2137
+ try {
2138
+ texts = await source({
2139
+ sessionId: recorder.sessionId,
2140
+ turnId: recorder.turnId,
2141
+ iteration,
2142
+ messages: recorder.messages,
2143
+ })
2144
+ } catch (error) {
2145
+ this.ctx.log.warn('A reminder source threw; the request goes without its reminders', {
2146
+ [NAMZU.TURN_ID]: recorder.turnId,
2147
+ [NAMZU.ITERATION]: iteration,
2148
+ 'exception.message': error instanceof Error ? error.message : String(error),
2149
+ })
2150
+ return
2151
+ }
2152
+ for (const text of texts ?? []) {
2153
+ if (text.trim().length === 0) continue
2154
+ recorder.pushMessage(createRuntimeContextMessage(text, 'reminder'))
2155
+ }
2156
+ }
2157
+
1928
2158
  private async deliverInbound(): Promise<number> {
1929
2159
  const queued = this.ctx.inboundMessages?.() ?? []
1930
2160
  for (const message of queued) {
@@ -2078,6 +2308,15 @@ export class IterationOrchestrator {
2078
2308
  return true
2079
2309
  }
2080
2310
 
2311
+ /** The compaction circuit breaker asked for the turn to stop (opt-in; see `compactionConfig.circuitBreaker`). */
2312
+ private stopForCompactionBreaker(): boolean {
2313
+ const reason = compactionBreaker(this.ctx)?.stop
2314
+ if (reason === undefined) return false
2315
+ this.ctx.recorder.setLastError(reason)
2316
+ this.ctx.recorder.setStopReason('error')
2317
+ return true
2318
+ }
2319
+
2081
2320
  private async recordToolStructuredFailure(
2082
2321
  results: readonly ToolCallOutcome[],
2083
2322
  iteration: number,
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Two ways automatic compaction gives up for the rest of a turn.
3
+ *
4
+ * - Three failures in a row (a pass that ran and shed nothing): every one of
5
+ * them paid for a summary or a reducer call and left the history as it was,
6
+ * so a fourth would do the same. Compaction is skipped from then on and the
7
+ * turn carries on; a pass the provider forces by rejecting the prompt is not
8
+ * gated.
9
+ * - Three compactions in a row, each followed by the context filling back up
10
+ * within three iterations: something in the loop (a huge file read, a tool
11
+ * that prints without limit) refills the window faster than any summary can
12
+ * empty it. The turn stops, because carrying on only buys the same wall.
13
+ *
14
+ * Off unless `compactionConfig.circuitBreaker` is true: stopping compaction,
15
+ * and above all stopping a turn, is not something a host had before.
16
+ *
17
+ * State is per turn, held against the turn's recorder, so two turns never
18
+ * share a count.
19
+ */
20
+
21
+ import type { IterationContext } from './context.js'
22
+
23
+ /** Consecutive failed passes after which automatic compaction stops. */
24
+ export const COMPACTION_FAILURE_LIMIT = 3
25
+ /** A compaction followed by a trigger sooner than this many iterations is a refill. */
26
+ export const COMPACTION_REFILL_ITERATIONS = 3
27
+ /** Consecutive refills that stop the turn. */
28
+ export const COMPACTION_REFILL_LIMIT = 3
29
+
30
+ /** One short line, shown once, when the turn stops on refills. */
31
+ export const COMPACTION_REFILL_MESSAGE =
32
+ 'The context filled up again right after being compacted, three times in a row. A file or tool output is probably too big for the window: read it in smaller parts, or start fresh with /clear.'
33
+ /** One short line, shown once, when compaction stops after failures. */
34
+ export const COMPACTION_FAILURE_MESSAGE =
35
+ 'Automatic compaction stopped after three failed attempts. Run /compact yourself, or start fresh with /clear.'
36
+
37
+ export class CompactionBreaker {
38
+ private failures = 0
39
+ private refills = 0
40
+ private pendingRefills = 0
41
+ private lastCompactedIteration: number | undefined
42
+ private failureNoticeSent = false
43
+ private stopReason: string | undefined
44
+
45
+ /** Whether three failures in a row have switched automatic compaction off. */
46
+ get failuresOpen(): boolean {
47
+ return this.failures >= COMPACTION_FAILURE_LIMIT
48
+ }
49
+
50
+ /** Mark the failure notice sent; true the first time only. */
51
+ takeFailureNotice(): boolean {
52
+ if (this.failureNoticeSent) return false
53
+ this.failureNoticeSent = true
54
+ return true
55
+ }
56
+
57
+ recordFailure(): void {
58
+ this.failures++
59
+ }
60
+
61
+ /** A pass committed an edit; failures start over. */
62
+ recordRelief(): void {
63
+ this.failures = 0
64
+ }
65
+
66
+ /**
67
+ * A summary or reducer pass is about to run at `iteration`. True when it
68
+ * would be the third refill in a row, in which case it must not run.
69
+ */
70
+ wouldTrip(iteration: number): boolean {
71
+ const quick =
72
+ this.lastCompactedIteration !== undefined &&
73
+ iteration - this.lastCompactedIteration < COMPACTION_REFILL_ITERATIONS
74
+ this.pendingRefills = quick ? this.refills + 1 : 0
75
+ return this.pendingRefills >= COMPACTION_REFILL_LIMIT
76
+ }
77
+
78
+ /** The pass that `wouldTrip` allowed completed at `iteration`. */
79
+ recordCompaction(iteration: number): void {
80
+ this.refills = this.pendingRefills
81
+ this.lastCompactedIteration = iteration
82
+ this.recordRelief()
83
+ }
84
+
85
+ stopTurn(message: string): void {
86
+ this.stopReason ??= message
87
+ }
88
+
89
+ /** The reason the turn must stop, when a breaker has asked for it. */
90
+ get stop(): string | undefined {
91
+ return this.stopReason
92
+ }
93
+ }
94
+
95
+ const breakers = new WeakMap<object, CompactionBreaker>()
96
+
97
+ /** The turn's breaker, or `undefined` when the host did not opt in. */
98
+ export function compactionBreaker(ctx: IterationContext): CompactionBreaker | undefined {
99
+ if (ctx.compactionConfig?.circuitBreaker !== true) return undefined
100
+ let breaker = breakers.get(ctx.recorder)
101
+ if (!breaker) {
102
+ breaker = new CompactionBreaker()
103
+ breakers.set(ctx.recorder, breaker)
104
+ }
105
+ return breaker
106
+ }