@namzu/sdk 35.0.0 → 37.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 (360) hide show
  1. package/CHANGELOG.md +218 -0
  2. package/README.md +68 -41
  3. package/dist/bridge/a2a/mapper.d.ts.map +1 -1
  4. package/dist/bridge/a2a/mapper.js +2 -0
  5. package/dist/bridge/a2a/mapper.js.map +1 -1
  6. package/dist/bridge/sse/mapper.d.ts.map +1 -1
  7. package/dist/bridge/sse/mapper.js +2 -0
  8. package/dist/bridge/sse/mapper.js.map +1 -1
  9. package/dist/compaction/extractor.d.ts +1 -1
  10. package/dist/compaction/extractor.d.ts.map +1 -1
  11. package/dist/compaction/extractor.js +15 -7
  12. package/dist/compaction/extractor.js.map +1 -1
  13. package/dist/compaction/manager.d.ts +2 -0
  14. package/dist/compaction/manager.d.ts.map +1 -1
  15. package/dist/compaction/manager.js +12 -1
  16. package/dist/compaction/manager.js.map +1 -1
  17. package/dist/compaction/verifier.d.ts.map +1 -1
  18. package/dist/compaction/verifier.js +82 -15
  19. package/dist/compaction/verifier.js.map +1 -1
  20. package/dist/contracts/schemas.d.ts +10 -10
  21. package/dist/eval/harness-verification.d.ts +63 -0
  22. package/dist/eval/harness-verification.d.ts.map +1 -0
  23. package/dist/eval/harness-verification.js +165 -0
  24. package/dist/eval/harness-verification.js.map +1 -0
  25. package/dist/eval/index.d.ts +2 -0
  26. package/dist/eval/index.d.ts.map +1 -1
  27. package/dist/eval/index.js +1 -0
  28. package/dist/eval/index.js.map +1 -1
  29. package/dist/execution/code-runtime/json-value.d.ts +10 -0
  30. package/dist/execution/code-runtime/json-value.d.ts.map +1 -0
  31. package/dist/execution/code-runtime/json-value.js +89 -0
  32. package/dist/execution/code-runtime/json-value.js.map +1 -0
  33. package/dist/execution/code-runtime/worker-program.d.ts +8 -0
  34. package/dist/execution/code-runtime/worker-program.d.ts.map +1 -0
  35. package/dist/execution/code-runtime/worker-program.js +240 -0
  36. package/dist/execution/code-runtime/worker-program.js.map +1 -0
  37. package/dist/execution/code-runtime/worker.d.ts +22 -0
  38. package/dist/execution/code-runtime/worker.d.ts.map +1 -1
  39. package/dist/execution/code-runtime/worker.js +148 -111
  40. package/dist/execution/code-runtime/worker.js.map +1 -1
  41. package/dist/manager/agent/lifecycle.d.ts +12 -0
  42. package/dist/manager/agent/lifecycle.d.ts.map +1 -1
  43. package/dist/manager/agent/lifecycle.js +306 -37
  44. package/dist/manager/agent/lifecycle.js.map +1 -1
  45. package/dist/manager/run/persistence.d.ts +2 -0
  46. package/dist/manager/run/persistence.d.ts.map +1 -1
  47. package/dist/manager/run/persistence.js +6 -0
  48. package/dist/manager/run/persistence.js.map +1 -1
  49. package/dist/provider/capabilities.d.ts +3 -1
  50. package/dist/provider/capabilities.d.ts.map +1 -1
  51. package/dist/provider/capabilities.js +13 -0
  52. package/dist/provider/capabilities.js.map +1 -1
  53. package/dist/provider/fallback.d.ts.map +1 -1
  54. package/dist/provider/fallback.js +2 -0
  55. package/dist/provider/fallback.js.map +1 -1
  56. package/dist/provider/token-budget.d.ts.map +1 -1
  57. package/dist/provider/token-budget.js +56 -8
  58. package/dist/provider/token-budget.js.map +1 -1
  59. package/dist/public-runtime.d.ts +5 -3
  60. package/dist/public-runtime.d.ts.map +1 -1
  61. package/dist/public-runtime.js +3 -2
  62. package/dist/public-runtime.js.map +1 -1
  63. package/dist/public-tools.d.ts +1 -1
  64. package/dist/public-tools.d.ts.map +1 -1
  65. package/dist/public-tools.js +1 -1
  66. package/dist/public-tools.js.map +1 -1
  67. package/dist/public-types.d.ts +1 -0
  68. package/dist/public-types.d.ts.map +1 -1
  69. package/dist/registry/tool/execute.d.ts +3 -0
  70. package/dist/registry/tool/execute.d.ts.map +1 -1
  71. package/dist/registry/tool/execute.js +11 -80
  72. package/dist/registry/tool/execute.js.map +1 -1
  73. package/dist/run/command-gate.d.ts.map +1 -1
  74. package/dist/run/command-gate.js +65 -12
  75. package/dist/run/command-gate.js.map +1 -1
  76. package/dist/run/index.d.ts +2 -0
  77. package/dist/run/index.d.ts.map +1 -1
  78. package/dist/run/index.js +1 -0
  79. package/dist/run/index.js.map +1 -1
  80. package/dist/run/memory-promoter.d.ts +3 -35
  81. package/dist/run/memory-promoter.d.ts.map +1 -1
  82. package/dist/run/memory-promoter.js +32 -40
  83. package/dist/run/memory-promoter.js.map +1 -1
  84. package/dist/run/memory-recall.d.ts +25 -0
  85. package/dist/run/memory-recall.d.ts.map +1 -0
  86. package/dist/run/memory-recall.js +228 -0
  87. package/dist/run/memory-recall.js.map +1 -0
  88. package/dist/run/reporter.d.ts.map +1 -1
  89. package/dist/run/reporter.js +1 -0
  90. package/dist/run/reporter.js.map +1 -1
  91. package/dist/run/token-budget.d.ts +2 -0
  92. package/dist/run/token-budget.d.ts.map +1 -1
  93. package/dist/run/token-budget.js +4 -0
  94. package/dist/run/token-budget.js.map +1 -1
  95. package/dist/run/workspace-fingerprint.d.ts +10 -3
  96. package/dist/run/workspace-fingerprint.d.ts.map +1 -1
  97. package/dist/run/workspace-fingerprint.js +17 -8
  98. package/dist/run/workspace-fingerprint.js.map +1 -1
  99. package/dist/runtime/query/checkpoint.d.ts +14 -0
  100. package/dist/runtime/query/checkpoint.d.ts.map +1 -1
  101. package/dist/runtime/query/checkpoint.js +82 -1
  102. package/dist/runtime/query/checkpoint.js.map +1 -1
  103. package/dist/runtime/query/executor.d.ts +6 -0
  104. package/dist/runtime/query/executor.d.ts.map +1 -1
  105. package/dist/runtime/query/executor.js +120 -24
  106. package/dist/runtime/query/executor.js.map +1 -1
  107. package/dist/runtime/query/index.d.ts +12 -8
  108. package/dist/runtime/query/index.d.ts.map +1 -1
  109. package/dist/runtime/query/index.js +55 -7
  110. package/dist/runtime/query/index.js.map +1 -1
  111. package/dist/runtime/query/iteration/index.d.ts +13 -3
  112. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  113. package/dist/runtime/query/iteration/index.js +356 -42
  114. package/dist/runtime/query/iteration/index.js.map +1 -1
  115. package/dist/runtime/query/iteration/native-output.d.ts +10 -0
  116. package/dist/runtime/query/iteration/native-output.d.ts.map +1 -0
  117. package/dist/runtime/query/iteration/native-output.js +32 -0
  118. package/dist/runtime/query/iteration/native-output.js.map +1 -0
  119. package/dist/runtime/query/iteration/phases/advisory.d.ts.map +1 -1
  120. package/dist/runtime/query/iteration/phases/advisory.js +18 -13
  121. package/dist/runtime/query/iteration/phases/advisory.js.map +1 -1
  122. package/dist/runtime/query/iteration/phases/compaction.d.ts +2 -0
  123. package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
  124. package/dist/runtime/query/iteration/phases/compaction.js +32 -11
  125. package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
  126. package/dist/runtime/query/iteration/phases/context.d.ts +11 -0
  127. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  128. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  129. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  130. package/dist/runtime/query/iteration/phases/tool-review.js +1 -1
  131. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  132. package/dist/runtime/query/iteration/phases/working-memory.d.ts +1 -1
  133. package/dist/runtime/query/iteration/phases/working-memory.d.ts.map +1 -1
  134. package/dist/runtime/query/iteration/phases/working-memory.js +23 -6
  135. package/dist/runtime/query/iteration/phases/working-memory.js.map +1 -1
  136. package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
  137. package/dist/runtime/query/iteration/stream-turn.js +12 -5
  138. package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
  139. package/dist/runtime/query/prompt.d.ts.map +1 -1
  140. package/dist/runtime/query/prompt.js +14 -4
  141. package/dist/runtime/query/prompt.js.map +1 -1
  142. package/dist/runtime/query/steering.d.ts +6 -2
  143. package/dist/runtime/query/steering.d.ts.map +1 -1
  144. package/dist/runtime/query/steering.js +17 -2
  145. package/dist/runtime/query/steering.js.map +1 -1
  146. package/dist/runtime/query/tool-call-budget.d.ts +19 -0
  147. package/dist/runtime/query/tool-call-budget.d.ts.map +1 -0
  148. package/dist/runtime/query/tool-call-budget.js +110 -0
  149. package/dist/runtime/query/tool-call-budget.js.map +1 -0
  150. package/dist/runtime/query/tooling.d.ts +2 -0
  151. package/dist/runtime/query/tooling.d.ts.map +1 -1
  152. package/dist/runtime/query/tooling.js +4 -0
  153. package/dist/runtime/query/tooling.js.map +1 -1
  154. package/dist/sandbox/file-walk-program.d.ts +3 -0
  155. package/dist/sandbox/file-walk-program.d.ts.map +1 -0
  156. package/dist/sandbox/file-walk-program.js +94 -0
  157. package/dist/sandbox/file-walk-program.js.map +1 -0
  158. package/dist/sandbox/file-walk.d.ts +12 -0
  159. package/dist/sandbox/file-walk.d.ts.map +1 -0
  160. package/dist/sandbox/file-walk.js +429 -0
  161. package/dist/sandbox/file-walk.js.map +1 -0
  162. package/dist/sandbox/index.d.ts +2 -0
  163. package/dist/sandbox/index.d.ts.map +1 -1
  164. package/dist/sandbox/index.js +1 -0
  165. package/dist/sandbox/index.js.map +1 -1
  166. package/dist/sandbox/provider/local.d.ts.map +1 -1
  167. package/dist/sandbox/provider/local.js +9 -0
  168. package/dist/sandbox/provider/local.js.map +1 -1
  169. package/dist/scheduler/completion-inbox.d.ts +3 -2
  170. package/dist/scheduler/completion-inbox.d.ts.map +1 -1
  171. package/dist/scheduler/completion-inbox.js +29 -15
  172. package/dist/scheduler/completion-inbox.js.map +1 -1
  173. package/dist/scheduler/local.d.ts.map +1 -1
  174. package/dist/scheduler/local.js +1 -0
  175. package/dist/scheduler/local.js.map +1 -1
  176. package/dist/session/handoff/capacity.d.ts +2 -2
  177. package/dist/session/handoff/capacity.d.ts.map +1 -1
  178. package/dist/session/handoff/capacity.js +3 -2
  179. package/dist/session/handoff/capacity.js.map +1 -1
  180. package/dist/store/memory/disk.d.ts +6 -2
  181. package/dist/store/memory/disk.d.ts.map +1 -1
  182. package/dist/store/memory/disk.js +42 -25
  183. package/dist/store/memory/disk.js.map +1 -1
  184. package/dist/store/memory/index.d.ts +4 -0
  185. package/dist/store/memory/index.d.ts.map +1 -1
  186. package/dist/store/memory/index.js +55 -19
  187. package/dist/store/memory/index.js.map +1 -1
  188. package/dist/store/memory/memory.d.ts +3 -2
  189. package/dist/store/memory/memory.d.ts.map +1 -1
  190. package/dist/store/memory/memory.js +11 -2
  191. package/dist/store/memory/memory.js.map +1 -1
  192. package/dist/store/memory/operation-lock.d.ts +11 -0
  193. package/dist/store/memory/operation-lock.d.ts.map +1 -0
  194. package/dist/store/memory/operation-lock.js +94 -0
  195. package/dist/store/memory/operation-lock.js.map +1 -0
  196. package/dist/tools/builtins/bash.d.ts.map +1 -1
  197. package/dist/tools/builtins/bash.js +215 -59
  198. package/dist/tools/builtins/bash.js.map +1 -1
  199. package/dist/tools/builtins/computer-use.d.ts.map +1 -1
  200. package/dist/tools/builtins/computer-use.js +60 -10
  201. package/dist/tools/builtins/computer-use.js.map +1 -1
  202. package/dist/tools/builtins/glob.d.ts +2 -1
  203. package/dist/tools/builtins/glob.d.ts.map +1 -1
  204. package/dist/tools/builtins/glob.js +95 -103
  205. package/dist/tools/builtins/glob.js.map +1 -1
  206. package/dist/tools/builtins/grep.d.ts.map +1 -1
  207. package/dist/tools/builtins/grep.js +155 -111
  208. package/dist/tools/builtins/grep.js.map +1 -1
  209. package/dist/tools/builtins/ls.js +2 -2
  210. package/dist/tools/builtins/ls.js.map +1 -1
  211. package/dist/tools/builtins/run-code.d.ts +6 -0
  212. package/dist/tools/builtins/run-code.d.ts.map +1 -1
  213. package/dist/tools/builtins/run-code.js +15 -4
  214. package/dist/tools/builtins/run-code.js.map +1 -1
  215. package/dist/tools/builtins/search-tools.d.ts.map +1 -1
  216. package/dist/tools/builtins/search-tools.js +14 -2
  217. package/dist/tools/builtins/search-tools.js.map +1 -1
  218. package/dist/tools/defineTool.d.ts +2 -0
  219. package/dist/tools/defineTool.d.ts.map +1 -1
  220. package/dist/tools/defineTool.js +3 -0
  221. package/dist/tools/defineTool.js.map +1 -1
  222. package/dist/tools/memory/delete.d.ts +4 -0
  223. package/dist/tools/memory/delete.d.ts.map +1 -0
  224. package/dist/tools/memory/delete.js +26 -0
  225. package/dist/tools/memory/delete.js.map +1 -0
  226. package/dist/tools/memory/index.d.ts +2 -0
  227. package/dist/tools/memory/index.d.ts.map +1 -1
  228. package/dist/tools/memory/index.js +6 -0
  229. package/dist/tools/memory/index.js.map +1 -1
  230. package/dist/tools/memory/save.d.ts.map +1 -1
  231. package/dist/tools/memory/save.js +3 -2
  232. package/dist/tools/memory/save.js.map +1 -1
  233. package/dist/tools/memory/search.d.ts.map +1 -1
  234. package/dist/tools/memory/search.js +12 -5
  235. package/dist/tools/memory/search.js.map +1 -1
  236. package/dist/tools/memory/update.d.ts +4 -0
  237. package/dist/tools/memory/update.d.ts.map +1 -0
  238. package/dist/tools/memory/update.js +47 -0
  239. package/dist/tools/memory/update.js.map +1 -0
  240. package/dist/types/agent/scheduler.d.ts +5 -0
  241. package/dist/types/agent/scheduler.d.ts.map +1 -1
  242. package/dist/types/agent/task.d.ts +10 -0
  243. package/dist/types/agent/task.d.ts.map +1 -1
  244. package/dist/types/computer-use/index.d.ts +6 -0
  245. package/dist/types/computer-use/index.d.ts.map +1 -1
  246. package/dist/types/computer-use/index.js.map +1 -1
  247. package/dist/types/errors/catalog.js +1 -1
  248. package/dist/types/errors/catalog.js.map +1 -1
  249. package/dist/types/hitl/index.d.ts +11 -1
  250. package/dist/types/hitl/index.d.ts.map +1 -1
  251. package/dist/types/hitl/index.js.map +1 -1
  252. package/dist/types/memory/index.d.ts +14 -1
  253. package/dist/types/memory/index.d.ts.map +1 -1
  254. package/dist/types/project/entity.d.ts +1 -0
  255. package/dist/types/project/entity.d.ts.map +1 -1
  256. package/dist/types/provider/config.d.ts +2 -0
  257. package/dist/types/provider/config.d.ts.map +1 -1
  258. package/dist/types/provider/model.d.ts +4 -0
  259. package/dist/types/provider/model.d.ts.map +1 -1
  260. package/dist/types/run/answer-review.d.ts +2 -0
  261. package/dist/types/run/answer-review.d.ts.map +1 -1
  262. package/dist/types/run/events.d.ts +9 -0
  263. package/dist/types/run/events.d.ts.map +1 -1
  264. package/dist/types/run/events.js.map +1 -1
  265. package/dist/types/run/prepare-step.d.ts +19 -1
  266. package/dist/types/run/prepare-step.d.ts.map +1 -1
  267. package/dist/types/sandbox/index.d.ts +20 -0
  268. package/dist/types/sandbox/index.d.ts.map +1 -1
  269. package/dist/types/sandbox/index.js.map +1 -1
  270. package/dist/types/structured-output/index.d.ts +8 -2
  271. package/dist/types/structured-output/index.d.ts.map +1 -1
  272. package/dist/types/tool/index.d.ts +15 -0
  273. package/dist/types/tool/index.d.ts.map +1 -1
  274. package/dist/types/tool/index.js.map +1 -1
  275. package/dist/utils/json-snapshot.d.ts +3 -0
  276. package/dist/utils/json-snapshot.d.ts.map +1 -0
  277. package/dist/utils/json-snapshot.js +79 -0
  278. package/dist/utils/json-snapshot.js.map +1 -0
  279. package/package.json +8 -1
  280. package/src/bridge/a2a/mapper.ts +2 -0
  281. package/src/bridge/sse/mapper.ts +2 -0
  282. package/src/compaction/extractor.ts +21 -8
  283. package/src/compaction/manager.ts +11 -1
  284. package/src/compaction/verifier.ts +80 -18
  285. package/src/eval/harness-verification.ts +270 -0
  286. package/src/eval/index.ts +10 -0
  287. package/src/execution/code-runtime/json-value.ts +82 -0
  288. package/src/execution/code-runtime/worker-program.ts +239 -0
  289. package/src/execution/code-runtime/worker.ts +164 -111
  290. package/src/manager/agent/lifecycle.ts +340 -45
  291. package/src/manager/run/persistence.ts +7 -0
  292. package/src/provider/capabilities.ts +25 -1
  293. package/src/provider/fallback.ts +2 -0
  294. package/src/provider/token-budget.ts +52 -8
  295. package/src/public-runtime.ts +6 -1
  296. package/src/public-tools.ts +5 -1
  297. package/src/public-types.ts +1 -0
  298. package/src/registry/tool/execute.ts +17 -79
  299. package/src/run/command-gate.ts +61 -13
  300. package/src/run/index.ts +2 -0
  301. package/src/run/memory-promoter.ts +32 -40
  302. package/src/run/memory-recall.ts +254 -0
  303. package/src/run/reporter.ts +1 -0
  304. package/src/run/token-budget.ts +4 -0
  305. package/src/run/workspace-fingerprint.ts +17 -8
  306. package/src/runtime/query/checkpoint.ts +96 -2
  307. package/src/runtime/query/executor.ts +131 -28
  308. package/src/runtime/query/index.ts +81 -16
  309. package/src/runtime/query/iteration/index.ts +406 -53
  310. package/src/runtime/query/iteration/native-output.ts +35 -0
  311. package/src/runtime/query/iteration/phases/advisory.ts +21 -11
  312. package/src/runtime/query/iteration/phases/compaction.ts +36 -14
  313. package/src/runtime/query/iteration/phases/context.ts +11 -0
  314. package/src/runtime/query/iteration/phases/tool-review.ts +5 -1
  315. package/src/runtime/query/iteration/phases/working-memory.ts +23 -5
  316. package/src/runtime/query/iteration/stream-turn.ts +11 -5
  317. package/src/runtime/query/prompt.ts +21 -4
  318. package/src/runtime/query/steering.ts +19 -2
  319. package/src/runtime/query/tool-call-budget.ts +115 -0
  320. package/src/runtime/query/tooling.ts +6 -0
  321. package/src/sandbox/file-walk-program.ts +93 -0
  322. package/src/sandbox/file-walk.ts +463 -0
  323. package/src/sandbox/index.ts +2 -0
  324. package/src/sandbox/provider/local.ts +13 -0
  325. package/src/scheduler/completion-inbox.ts +29 -19
  326. package/src/scheduler/local.ts +1 -0
  327. package/src/session/handoff/capacity.ts +7 -3
  328. package/src/store/memory/disk.ts +52 -30
  329. package/src/store/memory/index.ts +75 -27
  330. package/src/store/memory/memory.ts +18 -6
  331. package/src/store/memory/operation-lock.ts +99 -0
  332. package/src/tools/builtins/bash.ts +230 -58
  333. package/src/tools/builtins/computer-use.ts +61 -7
  334. package/src/tools/builtins/glob.ts +104 -122
  335. package/src/tools/builtins/grep.ts +166 -132
  336. package/src/tools/builtins/ls.ts +2 -2
  337. package/src/tools/builtins/run-code.ts +24 -5
  338. package/src/tools/builtins/search-tools.ts +18 -3
  339. package/src/tools/defineTool.ts +5 -0
  340. package/src/tools/memory/delete.ts +29 -0
  341. package/src/tools/memory/index.ts +6 -0
  342. package/src/tools/memory/save.ts +4 -2
  343. package/src/tools/memory/search.ts +12 -5
  344. package/src/tools/memory/update.ts +50 -0
  345. package/src/types/agent/scheduler.ts +6 -0
  346. package/src/types/agent/task.ts +13 -0
  347. package/src/types/computer-use/index.ts +6 -0
  348. package/src/types/errors/catalog.ts +1 -1
  349. package/src/types/hitl/index.ts +11 -1
  350. package/src/types/memory/index.ts +16 -1
  351. package/src/types/project/entity.ts +1 -0
  352. package/src/types/provider/config.ts +2 -0
  353. package/src/types/provider/model.ts +4 -0
  354. package/src/types/run/answer-review.ts +2 -0
  355. package/src/types/run/events.ts +10 -0
  356. package/src/types/run/prepare-step.ts +19 -1
  357. package/src/types/sandbox/index.ts +21 -0
  358. package/src/types/structured-output/index.ts +15 -2
  359. package/src/types/tool/index.ts +16 -0
  360. package/src/utils/json-snapshot.ts +76 -0
@@ -50,6 +50,7 @@ import type { Logger } from '../../utils/logger.js'
50
50
  import { compressShellOutput } from '../../utils/shell-compress.js'
51
51
  import { type BackgroundJobRegistry, type JobProcess, bindOwner } from '../jobs/registry.js'
52
52
  import type { ToolResultObservation } from './project-instructions.js'
53
+ import { ToolCallBudget, assertMaxToolCalls } from './tool-call-budget.js'
53
54
  import {
54
55
  DEFAULT_MAX_TOOL_OUTPUT_CHARS,
55
56
  type ToolOutputBudgetResult,
@@ -356,6 +357,10 @@ export interface ToolExecutorConfig {
356
357
  toolRetryBackoff?: Partial<BackoffPolicy>
357
358
  /** Max concurrently-executing concurrency-safe tools. */
358
359
  maxToolConcurrency?: number
360
+ /** Per-run cumulative attempt admission limit; unset is unlimited. */
361
+ maxToolCalls?: number
362
+ /** Complete strict event replay for restoring a configured call budget. */
363
+ readToolCallBudgetEvents?: () => Promise<readonly RunEvent[]>
359
364
 
360
365
  /**
361
366
  * Builds the durable-pause seam handed to one tool call.
@@ -492,6 +497,7 @@ export class ToolExecutor {
492
497
  private workingStateManager?: WorkingStateManager
493
498
  private probes: ProbeEnforcement
494
499
  private parentSpan?: Span
500
+ private readonly toolCallBudget?: ToolCallBudget
495
501
  private readonly preparedBatches = new WeakSet<PreparedToolBatch>()
496
502
  /** Set per turn by the orchestrator; see {@link setStepAllowedTools}. */
497
503
  private stepAllowedTools?: readonly string[]
@@ -517,6 +523,15 @@ export class ToolExecutor {
517
523
  log: Logger,
518
524
  probes: ProbeEnforcement = defaultProbeRegistry,
519
525
  ) {
526
+ assertMaxToolCalls(config.maxToolCalls)
527
+ if (config.maxToolCalls !== undefined) {
528
+ this.toolCallBudget = new ToolCallBudget(
529
+ config.maxToolCalls,
530
+ config.runId,
531
+ emitEvent,
532
+ config.readToolCallBudgetEvents,
533
+ )
534
+ }
520
535
  this.config = config
521
536
  this.activityStore = activityStore
522
537
  this.emitEvent = emitEvent
@@ -730,6 +745,18 @@ export class ToolExecutor {
730
745
  if (owned && !this.preparedBatches.has(owned)) {
731
746
  throw new Error('Prepared tool batch is not owned by this executor.')
732
747
  }
748
+ const refusal = this.toolCallBudget
749
+ ? await this.toolCallBudget.admit(
750
+ toolCalls.filter((call) => prior?.get(call.id) === undefined).length,
751
+ 'batch',
752
+ this.config.abortSignal,
753
+ )
754
+ : undefined
755
+ if (refusal) {
756
+ const refused = new Map(denials)
757
+ for (const call of toolCalls) if (!refused.has(call.id)) refused.set(call.id, refusal)
758
+ return await this.runBatch(toolCalls, refused, prior, owned)
759
+ }
733
760
  return await this.runBatch(toolCalls, denials, prior, owned)
734
761
  } finally {
735
762
  // Cleared so a later single execution outside a batch resolves
@@ -779,6 +806,24 @@ export class ToolExecutor {
779
806
  const results: ToolCallOutcome[] = new Array(toolCalls.length)
780
807
  const parallel: Promise<void>[] = []
781
808
  let serial: Promise<void> = Promise.resolve()
809
+ let barrier: Promise<void> | undefined
810
+ const schedule = (run: () => Promise<void>, safe: boolean, isBarrier: boolean): void => {
811
+ if (isBarrier) {
812
+ // Settle the entire preceding segment, even when host plumbing
813
+ // rejects. A rejected host operation prevents dependent execution.
814
+ barrier = Promise.allSettled([...parallel, serial]).then(async (settled) => {
815
+ const failed = settled.find((entry) => entry.status === 'rejected')
816
+ if (failed?.status === 'rejected') throw failed.reason
817
+ await run()
818
+ })
819
+ parallel.length = 0
820
+ serial = barrier
821
+ } else if (safe) {
822
+ parallel.push(barrier ? barrier.then(run) : run())
823
+ } else {
824
+ serial = serial.then(run)
825
+ }
826
+ }
782
827
  // Bounded fan-out. A model emitting fifty parallel reads used to open
783
828
  // fifty file handles and fifty activity records simultaneously; the
784
829
  // gate keeps that at a working-set size while preserving completion
@@ -786,6 +831,8 @@ export class ToolExecutor {
786
831
  const gate = new Semaphore(this.config.maxToolConcurrency ?? DEFAULT_TOOL_CONCURRENCY)
787
832
  toolCalls.forEach((toolCall, i) => {
788
833
  const preparedCall = preparedBatch?.calls.get(toolCall.id)
834
+ const tool = this.config.tools.get(preparedCall?.toolName ?? toolCall.function.name)
835
+ const isBarrier = tool?.executionBarrier === true
789
836
  const recovered = prior?.get(toolCall.id)
790
837
  if (recovered !== undefined) {
791
838
  // This call already ran, in a process that died before the
@@ -798,19 +845,21 @@ export class ToolExecutor {
798
845
  output: recovered.result,
799
846
  isError: recovered.isError,
800
847
  }
848
+ if (isBarrier) schedule(async () => {}, true, true)
801
849
  return
802
850
  }
803
851
 
804
852
  const denialReason = denials?.get(toolCall.id)
805
853
  if (denialReason !== undefined) {
806
854
  // Denied calls never touch the tool; they still get a result
807
- // message so the assistant turn stays fully answered. Run them
808
- // on the parallel branch — they perform no side effects, so
809
- // serialization would only add latency.
810
- parallel.push(
811
- this.recordDenial(toolCall, denialReason, preparedCall).then((r) => {
812
- results[i] = r
813
- }),
855
+ // message so the assistant turn stays fully answered. They can
856
+ // overlap other calls within a segment, respecting its barriers.
857
+ schedule(
858
+ async () => {
859
+ results[i] = await this.recordDenial(toolCall, denialReason, preparedCall)
860
+ },
861
+ true,
862
+ isBarrier,
814
863
  )
815
864
  return
816
865
  }
@@ -868,13 +917,8 @@ export class ToolExecutor {
868
917
  } catch {
869
918
  // non-JSON args → treat as unsafe (serialize), the conservative path
870
919
  }
871
- const preparedToolName = preparedCall?.toolName
872
- const safe =
873
- this.config.tools
874
- .get(preparedToolName ?? toolCall.function.name)
875
- ?.isConcurrencySafe?.(input) === true
876
- if (safe) parallel.push(gated())
877
- else serial = serial.then(run)
920
+ const safe = tool?.isConcurrencySafe?.(input) === true
921
+ schedule(safe ? gated : run, safe, isBarrier)
878
922
  })
879
923
  await Promise.all([...parallel, serial])
880
924
 
@@ -942,8 +986,14 @@ export class ToolExecutor {
942
986
  // retained closure must be observationally inert after its invocation
943
987
  // ends, not merely unable to finish the registry call it already started.
944
988
  signal.throwIfAborted()
989
+ // Preparation detaches input synchronously before its first await.
990
+ // Admission must not add an earlier yield where the caller can mutate it.
945
991
  const preparedCall = await this.prepareNestedCall(name, input, signal)
946
992
  signal.throwIfAborted()
993
+ if (this.toolCallBudget) {
994
+ const refusal = await this.toolCallBudget.admit(1, 'nested', signal)
995
+ if (refusal) return { success: false, output: '', error: refusal }
996
+ }
947
997
  const preparedInput = preparedCall.input
948
998
 
949
999
  const parent = context.toolUseId
@@ -1279,6 +1329,9 @@ export class ToolExecutor {
1279
1329
  settleProgress: () => Promise<void>,
1280
1330
  preparedCall?: PreparedDirectCall,
1281
1331
  ): Promise<ToolCallOutcome> {
1332
+ if (this.config.abortSignal.aborted) {
1333
+ return this.recordCancelledBeforeExecution(toolCall.id, toolCall.function.name, {})
1334
+ }
1282
1335
  if (preparedCall?.kind === 'synthetic') {
1283
1336
  return this.recordSyntheticPreparation(preparedCall)
1284
1337
  }
@@ -1376,7 +1429,15 @@ export class ToolExecutor {
1376
1429
 
1377
1430
  input = resolved.input
1378
1431
 
1379
- const preOutcome = await this.runPreToolHook(toolName, input)
1432
+ let preOutcome: PreToolHookOutcome
1433
+ try {
1434
+ preOutcome = await this.runPreToolHook(toolName, input)
1435
+ } catch (error) {
1436
+ if (!this.config.abortSignal.aborted) throw error
1437
+ // A later call's interrupted preparation must not reject the batch
1438
+ // that already holds an earlier call's settled side-effect receipt.
1439
+ return this.recordCancelledBeforeExecution(toolCall.id, toolName, input)
1440
+ }
1380
1441
  if (preOutcome.kind === 'skip' || preOutcome.kind === 'error') {
1381
1442
  return this.recordSyntheticHookOutcome(toolCall.id, toolName, preOutcome.input, preOutcome)
1382
1443
  }
@@ -1483,6 +1544,7 @@ export class ToolExecutor {
1483
1544
  ...this.config.toolRetryBackoff,
1484
1545
  }
1485
1546
  for (let attempt = 1; ; attempt++) {
1547
+ if (this.config.abortSignal.aborted) break
1486
1548
  // A missing file will not appear on the second attempt; burning
1487
1549
  // the budget on it only delays the error the model needs to see.
1488
1550
  const toolWants = !result.success && result.retryable === true
@@ -1538,6 +1600,19 @@ export class ToolExecutor {
1538
1600
  break
1539
1601
  }
1540
1602
 
1603
+ let refusal: string | undefined
1604
+ try {
1605
+ if (this.toolCallBudget)
1606
+ refusal = await this.toolCallBudget.admit(1, 'retry', this.config.abortSignal)
1607
+ } catch (error) {
1608
+ if (this.config.abortSignal.aborted) break
1609
+ throw error
1610
+ }
1611
+ if (refusal) {
1612
+ result = { success: false, output: '', error: refusal }
1613
+ post = { override: null, retry: false }
1614
+ break
1615
+ }
1541
1616
  result = await this.runOnce(toolName, input, toolContext, prepared)
1542
1617
  post = await this.runPostToolHook(toolName, input, result)
1543
1618
  }
@@ -1606,7 +1681,7 @@ export class ToolExecutor {
1606
1681
 
1607
1682
  if (this.workingStateManager) {
1608
1683
  extractFromToolResult(this.workingStateManager, toolName, output, effectiveIsError)
1609
- for (const pin of result.workingState ?? []) {
1684
+ for (const pin of this.config.abortSignal.aborted ? [] : (result.workingState ?? [])) {
1610
1685
  this.workingStateManager.pin(pin.key, pin.text, toolName)
1611
1686
  }
1612
1687
  }
@@ -1623,7 +1698,7 @@ export class ToolExecutor {
1623
1698
  [NAMZU.RUN_ID]: this.config.runId,
1624
1699
  [GENAI.TOOL_NAME]: toolName,
1625
1700
  'namzu.duration_ms': durationMs,
1626
- 'exception.message': result.error ?? 'unknown',
1701
+ 'exception.message': postOverride ? output : (result.error ?? 'unknown'),
1627
1702
  })
1628
1703
  }
1629
1704
 
@@ -2369,17 +2444,33 @@ export class ToolExecutor {
2369
2444
  toolResult: ToolResult,
2370
2445
  ): Promise<{ override: PostToolOverride | null; retry: boolean }> {
2371
2446
  if (!this.config.pluginManager) return { override: null, retry: false }
2372
- const results = await this.config.pluginManager.executeHooks(
2373
- 'post_tool_use',
2374
- {
2375
- runId: this.config.runId,
2376
- toolName,
2377
- toolInput: input,
2378
- toolResult,
2379
- signal: this.config.abortSignal,
2380
- },
2381
- this.emitEvent,
2382
- )
2447
+ let results: PluginHookResult[]
2448
+ try {
2449
+ results = await this.config.pluginManager.executeHooks(
2450
+ 'post_tool_use',
2451
+ {
2452
+ runId: this.config.runId,
2453
+ toolName,
2454
+ toolInput: input,
2455
+ toolResult,
2456
+ signal: this.config.abortSignal,
2457
+ },
2458
+ this.emitEvent,
2459
+ )
2460
+ } catch (error) {
2461
+ if (!this.config.abortSignal.aborted) throw error
2462
+ // The tool has already returned. Cancellation of a later redaction
2463
+ // cannot erase that execution or turn it into a safe-to-repeat call.
2464
+ // Withhold both channels until their post-tool policy has completed.
2465
+ const outcome = toolResult.success
2466
+ ? 'reported success before cancellation. The call already executed.'
2467
+ : 'reported failure while cancellation was in progress. Side effects are possible.'
2468
+ const output = `Tool "${toolName}" ${outcome} Output withheld because post-tool review was interrupted. Inspect external state before any retry.`
2469
+ return {
2470
+ override: { output, content: output, isError: !toolResult.success },
2471
+ retry: false,
2472
+ }
2473
+ }
2383
2474
  let override: PostToolOverride | null = null
2384
2475
  let retry = false
2385
2476
  for (const result of results) {
@@ -2484,6 +2575,18 @@ export class ToolExecutor {
2484
2575
  return { toolCallId: toolCall.id, toolName, output, isError: true }
2485
2576
  }
2486
2577
 
2578
+ private recordCancelledBeforeExecution(
2579
+ toolCallId: string,
2580
+ toolName: string,
2581
+ input: unknown,
2582
+ ): Promise<ToolCallOutcome> {
2583
+ const reason = abortReasonText(this.config.abortSignal.reason)
2584
+ return this.recordSyntheticHookOutcome(toolCallId, toolName, input, {
2585
+ kind: 'error',
2586
+ output: `Tool "${toolName}" was not started because the run was cancelled${reason ? `: ${reason}` : '.'}`,
2587
+ })
2588
+ }
2589
+
2487
2590
  private async recordSyntheticHookOutcome(
2488
2591
  toolCallId: string,
2489
2592
  toolName: string,
@@ -149,8 +149,9 @@ import {
149
149
  resolveSandboxTeardownTimeoutMs,
150
150
  teardownSandbox,
151
151
  } from './sandbox-lifecycle.js'
152
- import { SteeringBinding, type SteeringChannel } from './steering.js'
152
+ import { SteeringBinding, type SteeringChannel, isOperatorUserMessage } from './steering.js'
153
153
  import { resolveQueryBudget } from './token-budget.js'
154
+ import { assertMaxToolCalls } from './tool-call-budget.js'
154
155
  import { ToolGrantSet } from './tool-grants.js'
155
156
  import { createToolPause } from './tool-pause.js'
156
157
  import { ToolingBootstrap } from './tooling.js'
@@ -349,6 +350,8 @@ export interface QueryParams {
349
350
 
350
351
  /** Max concurrently-executing concurrency-safe tools in one batch. */
351
352
  maxToolConcurrency?: number
353
+ /** Per-run cumulative tool attempt limit, including nested calls and retries. Unset is unlimited. Re-supply on resume; durable reservations are never refunded. */
354
+ maxToolCalls?: number
352
355
 
353
356
  /**
354
357
  * Model-visible size cap for a single tool result. Over-budget output is
@@ -450,14 +453,9 @@ export interface QueryParams {
450
453
  beforeStep?: BeforeStep
451
454
 
452
455
  /**
453
- * Force the run to finish by calling a schema-validated tool, and land
454
- * the parsed value on `Run.structuredOutput`.
455
- *
456
- * Both leaf pieces already shipped and neither was reachable:
457
- * `createStructuredOutputTool` is excluded from the default builtin set,
458
- * and `StructuredOutputConfig` had no field on QueryParams at all. A host
459
- * needing a typed result had to register the tool by hand and hope —
460
- * nothing forced the call, and nothing stopped the loop when it came.
456
+ * Produce a locally validated structured result. Defaults to an output tool;
457
+ * mode:'native' requests JSON Schema from an explicitly capable driver.
458
+ * Host review and bounded corrections apply before publication.
461
459
  */
462
460
  structuredOutput?: StructuredOutputConfig
463
461
 
@@ -671,6 +669,13 @@ export interface QueryParams {
671
669
  * started. See `BaseAgentConfig.inboundMessages` for what it closes.
672
670
  */
673
671
  inboundMessages?: () => import('../../types/message/index.js').Message[]
672
+ /**
673
+ * Wake a background-task hold when operator input is available, without draining it.
674
+ * Resolve immediately if input is already pending. Otherwise wait for its arrival;
675
+ * the supplied signal ends the wait and must release any listeners. Messages are
676
+ * still consumed only through `inboundMessages` at a provider-valid boundary.
677
+ */
678
+ waitForInbound?: (signal: AbortSignal) => Promise<void>
674
679
 
675
680
  /**
676
681
  * Live project policy for this run. Unlike `inboundMessages`, snapshot
@@ -1020,6 +1025,19 @@ function withoutOwnedResumeTurn(
1020
1025
  }
1021
1026
 
1022
1027
  export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run> {
1028
+ assertMaxToolCalls(params.maxToolCalls)
1029
+ // Required types do not protect JavaScript callers. Reject missing scope
1030
+ // before opening a budget or persisting a run without its owning identity.
1031
+ const missingFields = (['sessionId', 'topicId', 'projectId', 'tenantId'] as const).filter(
1032
+ (field) => !params[field],
1033
+ )
1034
+ if (missingFields.length > 0) {
1035
+ throw new NamzuError({
1036
+ code: 'invalid_config',
1037
+ message: `query requires sessionId, topicId, projectId, and tenantId; missing: ${missingFields.join(', ')}.`,
1038
+ details: { missingFields },
1039
+ })
1040
+ }
1023
1041
  const selectedResumeState = selectedResumeStates.get(params)
1024
1042
  selectedResumeStates.delete(params)
1025
1043
  // Resolved at the DOOR, before a run id exists or a logger is built.
@@ -1147,6 +1165,8 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
1147
1165
  runConfig.timeoutMs,
1148
1166
  log,
1149
1167
  )
1168
+ const modelContextWindows = new Map<string, number | undefined>()
1169
+ if (runConfig.model) modelContextWindows.set(runConfig.model, providerContextWindow)
1150
1170
 
1151
1171
  // The mode this conversation was left in, when the run config names none.
1152
1172
  // Read once, before the loop exists, for the same reason the context
@@ -1549,7 +1569,11 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
1549
1569
  // invalidate the whole prompt cache for the rest of the run — the same
1550
1570
  // reason the forced-final turn keeps its tools array and uses
1551
1571
  // `toolChoice: 'none'` instead of dropping it.
1552
- if (params.structuredOutput && !params.tools.has(STRUCTURED_OUTPUT_TOOL_NAME)) {
1572
+ if (
1573
+ params.structuredOutput &&
1574
+ params.structuredOutput.mode !== 'native' &&
1575
+ !params.tools.has(STRUCTURED_OUTPUT_TOOL_NAME)
1576
+ ) {
1553
1577
  params.tools.register(createStructuredOutputTool(params.structuredOutput.schema))
1554
1578
  }
1555
1579
 
@@ -1711,6 +1735,13 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
1711
1735
  ...(params.toolRetryBackoff !== undefined
1712
1736
  ? { toolRetryBackoff: params.toolRetryBackoff }
1713
1737
  : {}),
1738
+ ...(params.maxToolCalls !== undefined
1739
+ ? {
1740
+ maxToolCalls: params.maxToolCalls,
1741
+ readToolCallBudgetEvents: () =>
1742
+ ctx.runMgr.getRunStore().readEvents({ integrity: 'strict' }),
1743
+ }
1744
+ : {}),
1714
1745
  ...(params.maxToolConcurrency !== undefined
1715
1746
  ? { maxToolConcurrency: params.maxToolConcurrency }
1716
1747
  : {}),
@@ -1963,10 +1994,27 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
1963
1994
  ...(params.repeatCallAdvisory === false ? {} : { repeatCalls: new RepeatCallTracker() }),
1964
1995
  compactionConfig,
1965
1996
  ...(params.inboundMessages ? { inboundMessages: params.inboundMessages } : {}),
1997
+ ...(params.resumeFromCheckpoint ? { resumedInput: queuedForThisRun } : {}),
1998
+ ...(params.waitForInbound ? { waitForInbound: params.waitForInbound } : {}),
1966
1999
  ...(params.projectInstructionContext
1967
2000
  ? { projectInstructionContext: params.projectInstructionContext }
1968
2001
  : {}),
1969
2002
  ...(providerContextWindow !== undefined ? { providerContextWindow } : {}),
2003
+ resolveModelContextWindow: async (model) => {
2004
+ if (!modelContextWindows.has(model)) {
2005
+ modelContextWindows.set(
2006
+ model,
2007
+ await resolveProviderContextWindow(
2008
+ resilientProvider,
2009
+ model,
2010
+ ctx.abortController.signal,
2011
+ runConfig.timeoutMs,
2012
+ log,
2013
+ ),
2014
+ )
2015
+ }
2016
+ return modelContextWindows.get(model)
2017
+ },
1970
2018
  workingStateManager,
1971
2019
  taskRouter: params.taskRouter,
1972
2020
  contextReducer: params.contextReducer,
@@ -2325,7 +2373,6 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
2325
2373
  }
2326
2374
  } else {
2327
2375
  pushSystemMessages()
2328
- let isFirstUserMessage = true
2329
2376
  for (const msg of initialMessages) {
2330
2377
  if (msg.role === 'system') {
2331
2378
  // A fresh run rebuilds its current static/dynamic prompt above,
@@ -2343,11 +2390,18 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
2343
2390
  continue
2344
2391
  }
2345
2392
  ctx.runMgr.pushMessage(msg)
2346
-
2347
- if (workingStateManager && msg.role === 'user' && msg.content) {
2348
- extractFromUserMessage(workingStateManager, msg.content, isFirstUserMessage)
2349
- isFirstUserMessage = false
2350
- }
2393
+ }
2394
+ }
2395
+ // Fresh and continuation histories seed the same operator state.
2396
+ // A worker report or project-policy message may have role `user`,
2397
+ // but it must not become the task or an operator requirement.
2398
+ // Resume restores this state and adopts only its newer queue arrivals.
2399
+ if (!params.resumeFromCheckpoint && workingStateManager) {
2400
+ let isFirstUserMessage = true
2401
+ for (const message of initialMessages) {
2402
+ if (!isOperatorUserMessage(message) || !message.content.trim()) continue
2403
+ extractFromUserMessage(workingStateManager, message.content, isFirstUserMessage)
2404
+ isFirstUserMessage = false
2351
2405
  }
2352
2406
  }
2353
2407
 
@@ -2705,6 +2759,16 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
2705
2759
  ctx.log,
2706
2760
  )
2707
2761
 
2762
+ if (outputVerdict.blocked || outputVerdict.rewritten !== undefined) {
2763
+ ctx.runMgr.clearStructuredOutput()
2764
+ if (
2765
+ params.structuredOutput &&
2766
+ outputVerdict.rewritten !== undefined &&
2767
+ ctx.runMgr.stopReason === 'end_turn'
2768
+ )
2769
+ ctx.runMgr.setStopReason('output_guardrail')
2770
+ }
2771
+
2708
2772
  if (outputVerdict.blocked) {
2709
2773
  await eventTranslator.emitEvent({
2710
2774
  type: 'guardrail_triggered',
@@ -2765,6 +2829,7 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
2765
2829
  }
2766
2830
  }
2767
2831
  }
2832
+ if (ctx.abortController.signal.aborted) ctx.runMgr.markCancelled()
2768
2833
  yield* resultAssembler.completeRun(rootSpan)
2769
2834
  } catch (err) {
2770
2835
  // A failed run still spent its steps; report them.