@namzu/sdk 34.2.0 → 35.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 (637) hide show
  1. package/CHANGELOG.md +133 -0
  2. package/README.md +2 -6
  3. package/dist/agents/PipelineAgent.d.ts.map +1 -1
  4. package/dist/agents/PipelineAgent.js +118 -100
  5. package/dist/agents/PipelineAgent.js.map +1 -1
  6. package/dist/agents/ReactiveAgent.d.ts.map +1 -1
  7. package/dist/agents/ReactiveAgent.js +2 -0
  8. package/dist/agents/ReactiveAgent.js.map +1 -1
  9. package/dist/agents/RouterAgent.d.ts.map +1 -1
  10. package/dist/agents/RouterAgent.js +105 -61
  11. package/dist/agents/RouterAgent.js.map +1 -1
  12. package/dist/agents/SupervisorAgent.d.ts.map +1 -1
  13. package/dist/agents/SupervisorAgent.js +8 -4
  14. package/dist/agents/SupervisorAgent.js.map +1 -1
  15. package/dist/agents/budget.d.ts +6 -0
  16. package/dist/agents/budget.d.ts.map +1 -0
  17. package/dist/agents/budget.js +33 -0
  18. package/dist/agents/budget.js.map +1 -0
  19. package/dist/agents/explore.d.ts +20 -0
  20. package/dist/agents/explore.d.ts.map +1 -0
  21. package/dist/agents/explore.js +26 -0
  22. package/dist/agents/explore.js.map +1 -0
  23. package/dist/agents/file-definitions.d.ts +80 -0
  24. package/dist/agents/file-definitions.d.ts.map +1 -0
  25. package/dist/agents/file-definitions.js +179 -0
  26. package/dist/agents/file-definitions.js.map +1 -0
  27. package/dist/agents/index.d.ts +3 -0
  28. package/dist/agents/index.d.ts.map +1 -1
  29. package/dist/agents/index.js +2 -0
  30. package/dist/agents/index.js.map +1 -1
  31. package/dist/bridge/a2a/mapper.d.ts.map +1 -1
  32. package/dist/bridge/a2a/mapper.js +6 -0
  33. package/dist/bridge/a2a/mapper.js.map +1 -1
  34. package/dist/bridge/sse/mapper.d.ts.map +1 -1
  35. package/dist/bridge/sse/mapper.js +22 -0
  36. package/dist/bridge/sse/mapper.js.map +1 -1
  37. package/dist/bus/lock.d.ts.map +1 -1
  38. package/dist/bus/lock.js +1 -10
  39. package/dist/bus/lock.js.map +1 -1
  40. package/dist/compaction/consolidation.d.ts +31 -0
  41. package/dist/compaction/consolidation.d.ts.map +1 -0
  42. package/dist/compaction/consolidation.js +63 -0
  43. package/dist/compaction/consolidation.js.map +1 -0
  44. package/dist/compaction/factory.d.ts.map +1 -1
  45. package/dist/compaction/factory.js +3 -0
  46. package/dist/compaction/factory.js.map +1 -1
  47. package/dist/compaction/index.d.ts +6 -1
  48. package/dist/compaction/index.d.ts.map +1 -1
  49. package/dist/compaction/index.js +6 -0
  50. package/dist/compaction/index.js.map +1 -1
  51. package/dist/compaction/manager.d.ts +7 -0
  52. package/dist/compaction/manager.d.ts.map +1 -1
  53. package/dist/compaction/manager.js +31 -0
  54. package/dist/compaction/manager.js.map +1 -1
  55. package/dist/compaction/plan.d.ts +23 -0
  56. package/dist/compaction/plan.d.ts.map +1 -1
  57. package/dist/compaction/plan.js +41 -24
  58. package/dist/compaction/plan.js.map +1 -1
  59. package/dist/compaction/salience/bm25.d.ts +26 -0
  60. package/dist/compaction/salience/bm25.d.ts.map +1 -0
  61. package/dist/compaction/salience/bm25.js +56 -0
  62. package/dist/compaction/salience/bm25.js.map +1 -0
  63. package/dist/compaction/salience/goal.d.ts +21 -0
  64. package/dist/compaction/salience/goal.d.ts.map +1 -0
  65. package/dist/compaction/salience/goal.js +32 -0
  66. package/dist/compaction/salience/goal.js.map +1 -0
  67. package/dist/compaction/salience/index.d.ts +4 -0
  68. package/dist/compaction/salience/index.d.ts.map +1 -0
  69. package/dist/compaction/salience/index.js +4 -0
  70. package/dist/compaction/salience/index.js.map +1 -0
  71. package/dist/compaction/salience/minhash.d.ts +17 -0
  72. package/dist/compaction/salience/minhash.d.ts.map +1 -0
  73. package/dist/compaction/salience/minhash.js +54 -0
  74. package/dist/compaction/salience/minhash.js.map +1 -0
  75. package/dist/compaction/salience/score.d.ts +64 -0
  76. package/dist/compaction/salience/score.d.ts.map +1 -0
  77. package/dist/compaction/salience/score.js +256 -0
  78. package/dist/compaction/salience/score.js.map +1 -0
  79. package/dist/compaction/salience/tokenize.d.ts +20 -0
  80. package/dist/compaction/salience/tokenize.d.ts.map +1 -0
  81. package/dist/compaction/salience/tokenize.js +92 -0
  82. package/dist/compaction/salience/tokenize.js.map +1 -0
  83. package/dist/compaction/salience/working-set.d.ts +59 -0
  84. package/dist/compaction/salience/working-set.d.ts.map +1 -0
  85. package/dist/compaction/salience/working-set.js +137 -0
  86. package/dist/compaction/salience/working-set.js.map +1 -0
  87. package/dist/compaction/serializer.d.ts +2 -0
  88. package/dist/compaction/serializer.d.ts.map +1 -1
  89. package/dist/compaction/serializer.js +15 -0
  90. package/dist/compaction/serializer.js.map +1 -1
  91. package/dist/compaction/token-estimate.d.ts +13 -0
  92. package/dist/compaction/token-estimate.d.ts.map +1 -0
  93. package/dist/compaction/token-estimate.js +58 -0
  94. package/dist/compaction/token-estimate.js.map +1 -0
  95. package/dist/compaction/tool-result-editing.d.ts +10 -2
  96. package/dist/compaction/tool-result-editing.d.ts.map +1 -1
  97. package/dist/compaction/tool-result-editing.js +22 -24
  98. package/dist/compaction/tool-result-editing.js.map +1 -1
  99. package/dist/compaction/types.d.ts +22 -1
  100. package/dist/compaction/types.d.ts.map +1 -1
  101. package/dist/compaction/wire.d.ts +3 -1
  102. package/dist/compaction/wire.d.ts.map +1 -1
  103. package/dist/compaction/wire.js +2 -0
  104. package/dist/compaction/wire.js.map +1 -1
  105. package/dist/config/runtime.d.ts +58 -28
  106. package/dist/config/runtime.d.ts.map +1 -1
  107. package/dist/config/runtime.js +13 -1
  108. package/dist/config/runtime.js.map +1 -1
  109. package/dist/connector/builtins/http.d.ts.map +1 -1
  110. package/dist/connector/builtins/http.js +2 -8
  111. package/dist/connector/builtins/http.js.map +1 -1
  112. package/dist/connector/builtins/webhook.js +2 -2
  113. package/dist/connector/builtins/webhook.js.map +1 -1
  114. package/dist/connector/mcp/adapter.d.ts +2 -0
  115. package/dist/connector/mcp/adapter.d.ts.map +1 -1
  116. package/dist/connector/mcp/adapter.js +30 -0
  117. package/dist/connector/mcp/adapter.js.map +1 -1
  118. package/dist/connector/tools/adapter.js +2 -2
  119. package/dist/connector/tools/adapter.js.map +1 -1
  120. package/dist/connector/tools/definitions.js +2 -2
  121. package/dist/connector/tools/definitions.js.map +1 -1
  122. package/dist/constants/compaction/index.d.ts +1 -0
  123. package/dist/constants/compaction/index.d.ts.map +1 -1
  124. package/dist/constants/compaction/index.js +1 -0
  125. package/dist/constants/compaction/index.js.map +1 -1
  126. package/dist/constants/telemetry/index.d.ts +0 -1
  127. package/dist/constants/telemetry/index.d.ts.map +1 -1
  128. package/dist/constants/telemetry/index.js +0 -1
  129. package/dist/constants/telemetry/index.js.map +1 -1
  130. package/dist/contracts/a2a.d.ts +5 -5
  131. package/dist/contracts/api.d.ts +4 -2
  132. package/dist/contracts/api.d.ts.map +1 -1
  133. package/dist/contracts/schemas.d.ts +1 -15
  134. package/dist/contracts/schemas.d.ts.map +1 -1
  135. package/dist/contracts/schemas.js +5 -20
  136. package/dist/contracts/schemas.js.map +1 -1
  137. package/dist/invariants/index.d.ts +1 -1
  138. package/dist/manager/agent/lifecycle.d.ts +2 -18
  139. package/dist/manager/agent/lifecycle.d.ts.map +1 -1
  140. package/dist/manager/agent/lifecycle.js +373 -340
  141. package/dist/manager/agent/lifecycle.js.map +1 -1
  142. package/dist/manager/run/persistence.d.ts +3 -0
  143. package/dist/manager/run/persistence.d.ts.map +1 -1
  144. package/dist/manager/run/persistence.js +35 -5
  145. package/dist/manager/run/persistence.js.map +1 -1
  146. package/dist/plugin/index.d.ts +2 -0
  147. package/dist/plugin/index.d.ts.map +1 -1
  148. package/dist/plugin/index.js +1 -0
  149. package/dist/plugin/index.js.map +1 -1
  150. package/dist/plugin/shell-hook.d.ts +128 -0
  151. package/dist/plugin/shell-hook.d.ts.map +1 -0
  152. package/dist/plugin/shell-hook.js +299 -0
  153. package/dist/plugin/shell-hook.js.map +1 -0
  154. package/dist/prompt/coding-agent-doctrine.d.ts +63 -0
  155. package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -0
  156. package/dist/prompt/coding-agent-doctrine.js +100 -0
  157. package/dist/prompt/coding-agent-doctrine.js.map +1 -0
  158. package/dist/prompt/index.d.ts +2 -0
  159. package/dist/prompt/index.d.ts.map +1 -1
  160. package/dist/prompt/index.js +1 -0
  161. package/dist/prompt/index.js.map +1 -1
  162. package/dist/provider/errors.d.ts +1 -1
  163. package/dist/provider/errors.js +1 -1
  164. package/dist/provider/fallback.d.ts +2 -0
  165. package/dist/provider/fallback.d.ts.map +1 -1
  166. package/dist/provider/fallback.js +3 -1
  167. package/dist/provider/fallback.js.map +1 -1
  168. package/dist/provider/retry.d.ts +2 -0
  169. package/dist/provider/retry.d.ts.map +1 -1
  170. package/dist/provider/retry.js +3 -1
  171. package/dist/provider/retry.js.map +1 -1
  172. package/dist/provider/token-budget.d.ts +13 -0
  173. package/dist/provider/token-budget.d.ts.map +1 -0
  174. package/dist/provider/token-budget.js +102 -0
  175. package/dist/provider/token-budget.js.map +1 -0
  176. package/dist/public-runtime.d.ts +12 -12
  177. package/dist/public-runtime.d.ts.map +1 -1
  178. package/dist/public-runtime.js +14 -9
  179. package/dist/public-runtime.js.map +1 -1
  180. package/dist/public-tools.d.ts +2 -0
  181. package/dist/public-tools.d.ts.map +1 -1
  182. package/dist/public-tools.js +2 -0
  183. package/dist/public-tools.js.map +1 -1
  184. package/dist/public-types.d.ts +12 -2
  185. package/dist/public-types.d.ts.map +1 -1
  186. package/dist/registry/command/kernel-commands.d.ts.map +1 -1
  187. package/dist/registry/command/kernel-commands.js +18 -6
  188. package/dist/registry/command/kernel-commands.js.map +1 -1
  189. package/dist/registry/tool/execute.d.ts.map +1 -1
  190. package/dist/registry/tool/execute.js +4 -1
  191. package/dist/registry/tool/execute.js.map +1 -1
  192. package/dist/run/LimitChecker.js +1 -1
  193. package/dist/run/LimitChecker.js.map +1 -1
  194. package/dist/run/drain.js +1 -1
  195. package/dist/run/reporter.d.ts.map +1 -1
  196. package/dist/run/reporter.js +15 -0
  197. package/dist/run/reporter.js.map +1 -1
  198. package/dist/run/token-budget.d.ts +108 -0
  199. package/dist/run/token-budget.d.ts.map +1 -0
  200. package/dist/run/token-budget.js +591 -0
  201. package/dist/run/token-budget.js.map +1 -0
  202. package/dist/runtime/jobs/registry.d.ts +29 -0
  203. package/dist/runtime/jobs/registry.d.ts.map +1 -1
  204. package/dist/runtime/jobs/registry.js +98 -18
  205. package/dist/runtime/jobs/registry.js.map +1 -1
  206. package/dist/runtime/query/checkpoint.d.ts.map +1 -1
  207. package/dist/runtime/query/checkpoint.js +10 -3
  208. package/dist/runtime/query/checkpoint.js.map +1 -1
  209. package/dist/runtime/query/context.d.ts +2 -26
  210. package/dist/runtime/query/context.d.ts.map +1 -1
  211. package/dist/runtime/query/context.js +1 -29
  212. package/dist/runtime/query/context.js.map +1 -1
  213. package/dist/runtime/query/executor.d.ts +12 -9
  214. package/dist/runtime/query/executor.d.ts.map +1 -1
  215. package/dist/runtime/query/executor.js +95 -46
  216. package/dist/runtime/query/executor.js.map +1 -1
  217. package/dist/runtime/query/guard.d.ts.map +1 -1
  218. package/dist/runtime/query/guard.js +8 -0
  219. package/dist/runtime/query/guard.js.map +1 -1
  220. package/dist/runtime/query/index.d.ts +35 -4
  221. package/dist/runtime/query/index.d.ts.map +1 -1
  222. package/dist/runtime/query/index.js +152 -52
  223. package/dist/runtime/query/index.js.map +1 -1
  224. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  225. package/dist/runtime/query/iteration/index.js +16 -5
  226. package/dist/runtime/query/iteration/index.js.map +1 -1
  227. package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
  228. package/dist/runtime/query/iteration/phases/compaction.js +98 -30
  229. package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
  230. package/dist/runtime/query/iteration/phases/context.d.ts +2 -0
  231. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  232. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  233. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  234. package/dist/runtime/query/iteration/phases/tool-review.js +29 -7
  235. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  236. package/dist/runtime/query/iteration/phases/working-memory.d.ts.map +1 -1
  237. package/dist/runtime/query/iteration/phases/working-memory.js +25 -15
  238. package/dist/runtime/query/iteration/phases/working-memory.js.map +1 -1
  239. package/dist/runtime/query/plugin-hooks.d.ts +1 -1
  240. package/dist/runtime/query/plugin-hooks.d.ts.map +1 -1
  241. package/dist/runtime/query/plugin-hooks.js +20 -1
  242. package/dist/runtime/query/plugin-hooks.js.map +1 -1
  243. package/dist/runtime/query/repeat-call.d.ts +20 -2
  244. package/dist/runtime/query/repeat-call.d.ts.map +1 -1
  245. package/dist/runtime/query/repeat-call.js +35 -7
  246. package/dist/runtime/query/repeat-call.js.map +1 -1
  247. package/dist/runtime/query/result.d.ts.map +1 -1
  248. package/dist/runtime/query/result.js +9 -0
  249. package/dist/runtime/query/result.js.map +1 -1
  250. package/dist/runtime/query/review-policy.d.ts +142 -0
  251. package/dist/runtime/query/review-policy.d.ts.map +1 -0
  252. package/dist/runtime/query/review-policy.js +144 -0
  253. package/dist/runtime/query/review-policy.js.map +1 -0
  254. package/dist/runtime/query/run-state.d.ts.map +1 -1
  255. package/dist/runtime/query/run-state.js +4 -0
  256. package/dist/runtime/query/run-state.js.map +1 -1
  257. package/dist/runtime/query/steering.d.ts +4 -0
  258. package/dist/runtime/query/steering.d.ts.map +1 -1
  259. package/dist/runtime/query/steering.js +29 -0
  260. package/dist/runtime/query/steering.js.map +1 -1
  261. package/dist/runtime/query/token-budget.d.ts +7 -0
  262. package/dist/runtime/query/token-budget.d.ts.map +1 -0
  263. package/dist/runtime/query/token-budget.js +101 -0
  264. package/dist/runtime/query/token-budget.js.map +1 -0
  265. package/dist/runtime/query/tool-output-budget.d.ts +2 -0
  266. package/dist/runtime/query/tool-output-budget.d.ts.map +1 -1
  267. package/dist/runtime/query/tool-output-budget.js +29 -11
  268. package/dist/runtime/query/tool-output-budget.js.map +1 -1
  269. package/dist/runtime/query/tool-pause.d.ts.map +1 -1
  270. package/dist/runtime/query/tool-pause.js +4 -2
  271. package/dist/runtime/query/tool-pause.js.map +1 -1
  272. package/dist/runtime/query/tooling.d.ts +4 -0
  273. package/dist/runtime/query/tooling.d.ts.map +1 -1
  274. package/dist/runtime/query/tooling.js +4 -0
  275. package/dist/runtime/query/tooling.js.map +1 -1
  276. package/dist/sandbox/provider/local.d.ts +3 -1
  277. package/dist/sandbox/provider/local.d.ts.map +1 -1
  278. package/dist/sandbox/provider/local.js +112 -12
  279. package/dist/sandbox/provider/local.js.map +1 -1
  280. package/dist/scheduler/delegating.d.ts +1 -0
  281. package/dist/scheduler/delegating.d.ts.map +1 -1
  282. package/dist/scheduler/delegating.js +29 -12
  283. package/dist/scheduler/delegating.js.map +1 -1
  284. package/dist/scheduler/local.d.ts +1 -0
  285. package/dist/scheduler/local.d.ts.map +1 -1
  286. package/dist/scheduler/local.js +4 -7
  287. package/dist/scheduler/local.js.map +1 -1
  288. package/dist/session/index.d.ts +0 -1
  289. package/dist/session/index.d.ts.map +1 -1
  290. package/dist/session/index.js +0 -1
  291. package/dist/session/index.js.map +1 -1
  292. package/dist/session/workspace/path-builder.d.ts.map +1 -1
  293. package/dist/session/workspace/path-builder.js +5 -4
  294. package/dist/session/workspace/path-builder.js.map +1 -1
  295. package/dist/store/feedback/disk.d.ts.map +1 -1
  296. package/dist/store/feedback/disk.js +4 -6
  297. package/dist/store/feedback/disk.js.map +1 -1
  298. package/dist/store/index.d.ts +2 -0
  299. package/dist/store/index.d.ts.map +1 -1
  300. package/dist/store/index.js +1 -0
  301. package/dist/store/index.js.map +1 -1
  302. package/dist/store/memory/disk.d.ts.map +1 -1
  303. package/dist/store/memory/disk.js +3 -12
  304. package/dist/store/memory/disk.js.map +1 -1
  305. package/dist/store/run/checkpoint-disk.d.ts +3 -3
  306. package/dist/store/run/checkpoint-disk.d.ts.map +1 -1
  307. package/dist/store/run/checkpoint-disk.js +14 -26
  308. package/dist/store/run/checkpoint-disk.js.map +1 -1
  309. package/dist/store/run/claim-disk.js +1 -1
  310. package/dist/store/run/conformance.d.ts +1 -1
  311. package/dist/store/run/conformance.d.ts.map +1 -1
  312. package/dist/store/run/conformance.js +41 -33
  313. package/dist/store/run/conformance.js.map +1 -1
  314. package/dist/store/run/disk.d.ts.map +1 -1
  315. package/dist/store/run/disk.js +20 -4
  316. package/dist/store/run/disk.js.map +1 -1
  317. package/dist/store/run/token-budget-disk.d.ts +37 -0
  318. package/dist/store/run/token-budget-disk.d.ts.map +1 -0
  319. package/dist/store/run/token-budget-disk.js +208 -0
  320. package/dist/store/run/token-budget-disk.js.map +1 -0
  321. package/dist/store/session/disk.d.ts +4 -13
  322. package/dist/store/session/disk.d.ts.map +1 -1
  323. package/dist/store/session/disk.js +34 -31
  324. package/dist/store/session/disk.js.map +1 -1
  325. package/dist/store/session/memory.d.ts +2 -0
  326. package/dist/store/session/memory.d.ts.map +1 -1
  327. package/dist/store/session/memory.js +41 -2
  328. package/dist/store/session/memory.js.map +1 -1
  329. package/dist/store/task/disk.d.ts.map +1 -1
  330. package/dist/store/task/disk.js +7 -8
  331. package/dist/store/task/disk.js.map +1 -1
  332. package/dist/store/topic/memory.d.ts +2 -0
  333. package/dist/store/topic/memory.d.ts.map +1 -1
  334. package/dist/store/topic/memory.js +13 -1
  335. package/dist/store/topic/memory.js.map +1 -1
  336. package/dist/test-support/ids.d.ts +4 -19
  337. package/dist/test-support/ids.d.ts.map +1 -1
  338. package/dist/test-support/ids.js +51 -59
  339. package/dist/test-support/ids.js.map +1 -1
  340. package/dist/tools/advisory/index.d.ts.map +1 -1
  341. package/dist/tools/advisory/index.js +3 -1
  342. package/dist/tools/advisory/index.js.map +1 -1
  343. package/dist/tools/builtins/bash.d.ts +2 -0
  344. package/dist/tools/builtins/bash.d.ts.map +1 -1
  345. package/dist/tools/builtins/bash.js +9 -11
  346. package/dist/tools/builtins/bash.js.map +1 -1
  347. package/dist/tools/builtins/computer-use.d.ts +2 -2
  348. package/dist/tools/builtins/computer-use.d.ts.map +1 -1
  349. package/dist/tools/builtins/computer-use.js +4 -2
  350. package/dist/tools/builtins/computer-use.js.map +1 -1
  351. package/dist/tools/builtins/edit.js +2 -2
  352. package/dist/tools/builtins/edit.js.map +1 -1
  353. package/dist/tools/builtins/glob.js +3 -3
  354. package/dist/tools/builtins/glob.js.map +1 -1
  355. package/dist/tools/builtins/grep.js +2 -2
  356. package/dist/tools/builtins/grep.js.map +1 -1
  357. package/dist/tools/builtins/ls.js +2 -2
  358. package/dist/tools/builtins/ls.js.map +1 -1
  359. package/dist/tools/builtins/lsp.js +2 -2
  360. package/dist/tools/builtins/lsp.js.map +1 -1
  361. package/dist/tools/builtins/read-file.js +2 -2
  362. package/dist/tools/builtins/read-file.js.map +1 -1
  363. package/dist/tools/builtins/write-file.js +2 -2
  364. package/dist/tools/builtins/write-file.js.map +1 -1
  365. package/dist/tools/coordinator/ask-user-question.d.ts +33 -0
  366. package/dist/tools/coordinator/ask-user-question.d.ts.map +1 -0
  367. package/dist/tools/coordinator/ask-user-question.js +237 -0
  368. package/dist/tools/coordinator/ask-user-question.js.map +1 -0
  369. package/dist/tools/coordinator/index.d.ts +1 -2
  370. package/dist/tools/coordinator/index.d.ts.map +1 -1
  371. package/dist/tools/coordinator/index.js +4 -220
  372. package/dist/tools/coordinator/index.js.map +1 -1
  373. package/dist/tools/memory/read.d.ts.map +1 -1
  374. package/dist/tools/memory/read.js +2 -4
  375. package/dist/tools/memory/read.js.map +1 -1
  376. package/dist/tools/paths.d.ts +13 -0
  377. package/dist/tools/paths.d.ts.map +1 -1
  378. package/dist/tools/paths.js +53 -0
  379. package/dist/tools/paths.js.map +1 -1
  380. package/dist/tools/roster.d.ts +27 -0
  381. package/dist/tools/roster.d.ts.map +1 -0
  382. package/dist/tools/roster.js +42 -0
  383. package/dist/tools/roster.js.map +1 -0
  384. package/dist/types/agent/base.d.ts +5 -0
  385. package/dist/types/agent/base.d.ts.map +1 -1
  386. package/dist/types/agent/pipeline.d.ts +3 -0
  387. package/dist/types/agent/pipeline.d.ts.map +1 -1
  388. package/dist/types/agent/router.d.ts +2 -3
  389. package/dist/types/agent/router.d.ts.map +1 -1
  390. package/dist/types/agent/scheduler.d.ts +3 -0
  391. package/dist/types/agent/scheduler.d.ts.map +1 -1
  392. package/dist/types/agent/task.d.ts +5 -16
  393. package/dist/types/agent/task.d.ts.map +1 -1
  394. package/dist/types/agent/task.js.map +1 -1
  395. package/dist/types/common/index.d.ts.map +1 -1
  396. package/dist/types/common/index.js +3 -0
  397. package/dist/types/common/index.js.map +1 -1
  398. package/dist/types/computer-use/index.d.ts +8 -0
  399. package/dist/types/computer-use/index.d.ts.map +1 -1
  400. package/dist/types/computer-use/index.js.map +1 -1
  401. package/dist/types/hitl/index.d.ts +5 -0
  402. package/dist/types/hitl/index.d.ts.map +1 -1
  403. package/dist/types/hitl/index.js.map +1 -1
  404. package/dist/types/ids/brand.d.ts +5 -47
  405. package/dist/types/ids/brand.d.ts.map +1 -1
  406. package/dist/types/ids/brand.js +2 -39
  407. package/dist/types/ids/brand.js.map +1 -1
  408. package/dist/types/ids/index.d.ts +59 -69
  409. package/dist/types/ids/index.d.ts.map +1 -1
  410. package/dist/types/ids/index.js +12 -10
  411. package/dist/types/ids/index.js.map +1 -1
  412. package/dist/types/permission/index.d.ts +9 -0
  413. package/dist/types/permission/index.d.ts.map +1 -1
  414. package/dist/types/permission/index.js +9 -1
  415. package/dist/types/permission/index.js.map +1 -1
  416. package/dist/types/plugin/index.d.ts +45 -4
  417. package/dist/types/plugin/index.d.ts.map +1 -1
  418. package/dist/types/plugin/index.js +7 -0
  419. package/dist/types/plugin/index.js.map +1 -1
  420. package/dist/types/run/config.d.ts +3 -0
  421. package/dist/types/run/config.d.ts.map +1 -1
  422. package/dist/types/run/emergency.d.ts +5 -0
  423. package/dist/types/run/emergency.d.ts.map +1 -1
  424. package/dist/types/run/emergency.js.map +1 -1
  425. package/dist/types/run/entity.d.ts +6 -2
  426. package/dist/types/run/entity.d.ts.map +1 -1
  427. package/dist/types/run/events.d.ts +35 -0
  428. package/dist/types/run/events.d.ts.map +1 -1
  429. package/dist/types/run/events.js.map +1 -1
  430. package/dist/types/run/index.d.ts +1 -0
  431. package/dist/types/run/index.d.ts.map +1 -1
  432. package/dist/types/run/index.js +1 -0
  433. package/dist/types/run/index.js.map +1 -1
  434. package/dist/types/run/state.d.ts +16 -15
  435. package/dist/types/run/state.d.ts.map +1 -1
  436. package/dist/types/run/state.js +41 -22
  437. package/dist/types/run/state.js.map +1 -1
  438. package/dist/types/run/stop-reason.d.ts +3 -14
  439. package/dist/types/run/stop-reason.d.ts.map +1 -1
  440. package/dist/types/run/token-budget-store.d.ts +29 -0
  441. package/dist/types/run/token-budget-store.d.ts.map +1 -0
  442. package/dist/types/run/token-budget-store.js +25 -0
  443. package/dist/types/run/token-budget-store.js.map +1 -0
  444. package/dist/types/sandbox/index.d.ts +27 -2
  445. package/dist/types/sandbox/index.d.ts.map +1 -1
  446. package/dist/types/sandbox/index.js.map +1 -1
  447. package/dist/types/session/store.d.ts +8 -0
  448. package/dist/types/session/store.d.ts.map +1 -1
  449. package/dist/types/tool/index.d.ts +15 -0
  450. package/dist/types/tool/index.d.ts.map +1 -1
  451. package/dist/types/tool/index.js.map +1 -1
  452. package/dist/utils/frontmatter.d.ts +1 -1
  453. package/dist/utils/frontmatter.js +1 -1
  454. package/dist/utils/id-format.d.ts +3 -0
  455. package/dist/utils/id-format.d.ts.map +1 -0
  456. package/dist/utils/id-format.js +9 -0
  457. package/dist/utils/id-format.js.map +1 -0
  458. package/dist/utils/id.d.ts +65 -32
  459. package/dist/utils/id.d.ts.map +1 -1
  460. package/dist/utils/id.js +168 -148
  461. package/dist/utils/id.js.map +1 -1
  462. package/dist/utils/log/exception.js +1 -1
  463. package/dist/utils/log/exception.js.map +1 -1
  464. package/dist/utils/log/index.d.ts.map +1 -1
  465. package/dist/utils/log/index.js +1 -2
  466. package/dist/utils/log/index.js.map +1 -1
  467. package/dist/utils/log/templates.d.ts.map +1 -1
  468. package/dist/utils/log/templates.js +0 -1
  469. package/dist/utils/log/templates.js.map +1 -1
  470. package/package.json +1 -1
  471. package/src/agents/AGENTS.md +1 -1
  472. package/src/agents/PipelineAgent.ts +129 -109
  473. package/src/agents/ReactiveAgent.ts +2 -0
  474. package/src/agents/RouterAgent.ts +120 -73
  475. package/src/agents/SupervisorAgent.ts +12 -4
  476. package/src/agents/budget.ts +42 -0
  477. package/src/agents/explore.ts +29 -0
  478. package/src/agents/file-definitions.ts +237 -0
  479. package/src/agents/index.ts +15 -0
  480. package/src/bridge/a2a/mapper.ts +6 -0
  481. package/src/bridge/sse/mapper.ts +22 -0
  482. package/src/bus/lock.ts +1 -10
  483. package/src/compaction/consolidation.ts +79 -0
  484. package/src/compaction/factory.ts +3 -0
  485. package/src/compaction/index.ts +34 -4
  486. package/src/compaction/manager.ts +39 -1
  487. package/src/compaction/plan.ts +52 -21
  488. package/src/compaction/salience/bm25.ts +72 -0
  489. package/src/compaction/salience/goal.ts +44 -0
  490. package/src/compaction/salience/index.ts +19 -0
  491. package/src/compaction/salience/minhash.ts +53 -0
  492. package/src/compaction/salience/score.ts +305 -0
  493. package/src/compaction/salience/tokenize.ts +89 -0
  494. package/src/compaction/salience/working-set.ts +187 -0
  495. package/src/compaction/serializer.ts +16 -0
  496. package/src/compaction/token-estimate.ts +57 -0
  497. package/src/compaction/tool-result-editing.ts +28 -26
  498. package/src/compaction/types.ts +24 -1
  499. package/src/compaction/wire.ts +5 -1
  500. package/src/config/runtime.ts +13 -1
  501. package/src/connector/builtins/http.ts +2 -8
  502. package/src/connector/builtins/webhook.ts +2 -2
  503. package/src/connector/mcp/adapter.ts +32 -0
  504. package/src/connector/tools/adapter.ts +2 -2
  505. package/src/connector/tools/definitions.ts +2 -2
  506. package/src/constants/compaction/index.ts +1 -0
  507. package/src/constants/telemetry/index.ts +0 -1
  508. package/src/contracts/api.ts +5 -1
  509. package/src/contracts/schemas.ts +6 -20
  510. package/src/invariants/index.ts +1 -1
  511. package/src/manager/agent/lifecycle.ts +380 -352
  512. package/src/manager/run/persistence.ts +35 -5
  513. package/src/plugin/index.ts +19 -0
  514. package/src/plugin/shell-hook.ts +403 -0
  515. package/src/prompt/coding-agent-doctrine.ts +118 -0
  516. package/src/prompt/index.ts +8 -0
  517. package/src/provider/errors.ts +1 -1
  518. package/src/provider/fallback.ts +4 -1
  519. package/src/provider/retry.ts +4 -1
  520. package/src/provider/token-budget.ts +114 -0
  521. package/src/public-runtime.ts +54 -27
  522. package/src/public-tools.ts +5 -0
  523. package/src/public-types.ts +65 -0
  524. package/src/registry/command/kernel-commands.ts +16 -6
  525. package/src/registry/tool/execute.ts +4 -1
  526. package/src/run/LimitChecker.ts +1 -1
  527. package/src/run/drain.ts +1 -1
  528. package/src/run/reporter.ts +15 -0
  529. package/src/run/token-budget.ts +692 -0
  530. package/src/runtime/jobs/registry.ts +123 -17
  531. package/src/runtime/query/checkpoint.ts +10 -3
  532. package/src/runtime/query/context.ts +3 -57
  533. package/src/runtime/query/executor.ts +128 -58
  534. package/src/runtime/query/guard.ts +8 -0
  535. package/src/runtime/query/index.ts +201 -62
  536. package/src/runtime/query/iteration/index.ts +24 -6
  537. package/src/runtime/query/iteration/phases/compaction.ts +130 -31
  538. package/src/runtime/query/iteration/phases/context.ts +2 -0
  539. package/src/runtime/query/iteration/phases/tool-review.ts +39 -7
  540. package/src/runtime/query/iteration/phases/working-memory.ts +23 -14
  541. package/src/runtime/query/plugin-hooks.ts +24 -2
  542. package/src/runtime/query/repeat-call.ts +42 -7
  543. package/src/runtime/query/result.ts +9 -0
  544. package/src/runtime/query/review-policy.ts +231 -0
  545. package/src/runtime/query/run-state.ts +4 -0
  546. package/src/runtime/query/steering.ts +32 -0
  547. package/src/runtime/query/token-budget.ts +127 -0
  548. package/src/runtime/query/tool-output-budget.ts +33 -10
  549. package/src/runtime/query/tool-pause.ts +4 -2
  550. package/src/runtime/query/tooling.ts +8 -0
  551. package/src/sandbox/provider/local.ts +123 -13
  552. package/src/scheduler/delegating.ts +35 -13
  553. package/src/scheduler/local.ts +5 -7
  554. package/src/session/index.ts +0 -1
  555. package/src/session/workspace/path-builder.ts +5 -4
  556. package/src/store/feedback/disk.ts +4 -6
  557. package/src/store/index.ts +2 -0
  558. package/src/store/memory/disk.ts +3 -11
  559. package/src/store/run/checkpoint-disk.ts +14 -27
  560. package/src/store/run/claim-disk.ts +1 -1
  561. package/src/store/run/conformance.ts +55 -39
  562. package/src/store/run/disk.ts +19 -4
  563. package/src/store/run/token-budget-disk.ts +247 -0
  564. package/src/store/session/disk.ts +36 -29
  565. package/src/store/session/memory.ts +38 -1
  566. package/src/store/task/disk.ts +7 -8
  567. package/src/store/topic/memory.ts +13 -1
  568. package/src/test-support/ids.ts +62 -59
  569. package/src/tools/advisory/index.ts +3 -1
  570. package/src/tools/builtins/bash.ts +11 -12
  571. package/src/tools/builtins/computer-use.ts +6 -2
  572. package/src/tools/builtins/edit.ts +2 -2
  573. package/src/tools/builtins/glob.ts +3 -3
  574. package/src/tools/builtins/grep.ts +2 -2
  575. package/src/tools/builtins/ls.ts +2 -2
  576. package/src/tools/builtins/lsp.ts +2 -2
  577. package/src/tools/builtins/read-file.ts +2 -2
  578. package/src/tools/builtins/write-file.ts +2 -2
  579. package/src/tools/coordinator/ask-user-question.ts +284 -0
  580. package/src/tools/coordinator/index.ts +5 -248
  581. package/src/tools/memory/read.ts +2 -4
  582. package/src/tools/paths.ts +64 -0
  583. package/src/tools/roster.ts +46 -0
  584. package/src/types/agent/base.ts +6 -0
  585. package/src/types/agent/pipeline.ts +3 -0
  586. package/src/types/agent/router.ts +2 -3
  587. package/src/types/agent/scheduler.ts +4 -0
  588. package/src/types/agent/task.ts +5 -18
  589. package/src/types/common/index.ts +3 -0
  590. package/src/types/computer-use/index.ts +8 -0
  591. package/src/types/hitl/index.ts +5 -0
  592. package/src/types/ids/brand.ts +5 -48
  593. package/src/types/ids/index.ts +66 -75
  594. package/src/types/permission/index.ts +11 -0
  595. package/src/types/plugin/index.ts +49 -1
  596. package/src/types/run/config.ts +3 -0
  597. package/src/types/run/emergency.ts +5 -0
  598. package/src/types/run/entity.ts +6 -2
  599. package/src/types/run/events.ts +37 -0
  600. package/src/types/run/index.ts +1 -0
  601. package/src/types/run/state.ts +58 -30
  602. package/src/types/run/stop-reason.ts +3 -14
  603. package/src/types/run/token-budget-store.ts +54 -0
  604. package/src/types/sandbox/index.ts +32 -0
  605. package/src/types/session/store.ts +8 -0
  606. package/src/types/tool/index.ts +15 -0
  607. package/src/utils/frontmatter.ts +1 -1
  608. package/src/utils/id-format.ts +9 -0
  609. package/src/utils/id.ts +222 -181
  610. package/src/utils/log/exception.ts +1 -1
  611. package/src/utils/log/index.ts +1 -2
  612. package/src/utils/log/templates.ts +0 -1
  613. package/dist/session/migration/errors.d.ts +0 -26
  614. package/dist/session/migration/errors.d.ts.map +0 -1
  615. package/dist/session/migration/errors.js +0 -22
  616. package/dist/session/migration/errors.js.map +0 -1
  617. package/dist/session/migration/filesystem.d.ts +0 -122
  618. package/dist/session/migration/filesystem.d.ts.map +0 -1
  619. package/dist/session/migration/filesystem.js +0 -379
  620. package/dist/session/migration/filesystem.js.map +0 -1
  621. package/dist/session/migration/id-prefix.d.ts +0 -113
  622. package/dist/session/migration/id-prefix.d.ts.map +0 -1
  623. package/dist/session/migration/id-prefix.js +0 -132
  624. package/dist/session/migration/id-prefix.js.map +0 -1
  625. package/dist/session/migration/index.d.ts +0 -12
  626. package/dist/session/migration/index.d.ts.map +0 -1
  627. package/dist/session/migration/index.js +0 -12
  628. package/dist/session/migration/index.js.map +0 -1
  629. package/dist/session/migration/marker.d.ts +0 -57
  630. package/dist/session/migration/marker.d.ts.map +0 -1
  631. package/dist/session/migration/marker.js +0 -123
  632. package/dist/session/migration/marker.js.map +0 -1
  633. package/src/session/migration/errors.ts +0 -23
  634. package/src/session/migration/filesystem.ts +0 -468
  635. package/src/session/migration/id-prefix.ts +0 -166
  636. package/src/session/migration/index.ts +0 -41
  637. package/src/session/migration/marker.ts +0 -142
@@ -1,5 +1,4 @@
1
1
  import { AGENT_MANAGER_DEFAULTS } from '../../constants/agent/index.js'
2
- import { EMPTY_TOKEN_USAGE } from '../../constants/limits.js'
3
2
  import { GENAI } from '../../constants/telemetry/index.js'
4
3
  import type { AgentRegistry } from '../../registry/agent/definitions.js'
5
4
  import {
@@ -196,288 +195,389 @@ export class AgentManager {
196
195
  const sharedAgent = definitionForSpawn.typedAgent
197
196
  const agent = definitionForSpawn.createAgent?.() ?? sharedAgent.forRun?.() ?? sharedAgent
198
197
 
199
- const childAbortController = createChildAbortController(context.parentAbortController)
200
-
201
- // The allocation is computed INSIDE the spawn lock, not here. Reading
202
- // the parent's remaining budget at this point and debiting it after
203
- // `provisionSpawn` put the two halves of a read-modify-write on either
204
- // side of an await — so N siblings launched from one turn all read the
205
- // same undebited number and each took a fraction of it. Measured: four
206
- // concurrent children were handed 50 000 + 50 000 + 50 000 + 50 000
207
- // from a pool of 100 000.
208
- //
209
- // `create_task`'s own description instructs exactly this shape ("'fan
210
- // out 8 specialists' is one assistant message with 8 create_task
211
- // blocks"), so the documented usage was the reproduction.
212
- //
213
- // Nothing pinned it because the only concurrent test built a fresh
214
- // context per call — each spawn got its own tracker, which measures
215
- // width and not budget.
216
-
217
- // Phase 6: SubSession + child Session + WorkspaceRef triple. Happens
218
- // before taskId minting so a capacity failure short-circuits cleanly
219
- // with no observable state change.
220
- //
221
- // The allocation now travels with it, because the read and the debit
222
- // have to be on the same side of every await to mean anything.
223
- const { spawnRecord, allocatedTokens } = await this.provisionSpawn(options, context)
198
+ context.parentAbortController.signal.throwIfAborted()
199
+
200
+ // Reserve synchronously before provisioning yields. The authority is shared
201
+ // across managers and nested sessions, while the session lock below only
202
+ // serializes filesystem capacity checks.
203
+ const remaining = context.budget.remaining
204
+ const maxAllocation = Number.isFinite(remaining)
205
+ ? Math.floor(remaining * this.config.maxBudgetFraction)
206
+ : (options.budgetAllocation?.tokenBudget ??
207
+ (options.configOverrides?.tokenBudget === 0
208
+ ? 200_000
209
+ : (options.configOverrides?.tokenBudget ?? 200_000)))
210
+ const allocatedTokens = Math.min(
211
+ options.budgetAllocation?.tokenBudget ?? maxAllocation,
212
+ maxAllocation,
213
+ )
214
+ if (!Number.isSafeInteger(allocatedTokens) || allocatedTokens <= 0) {
215
+ throw new NamzuError({
216
+ code: 'invalid_config',
217
+ message: `Cannot spawn "${options.agentId}": the parent has ${remaining} tokens remaining; a child allocation must be a finite positive integer.`,
218
+ })
219
+ }
220
+ const childBudget = context.budget.reserve(allocatedTokens)
221
+ let spawnRecord: ChildSpawnRecord
222
+ try {
223
+ await childBudget.flush()
224
+ spawnRecord = await this.provisionSpawn(options, context)
225
+ } catch (error) {
226
+ childBudget.settle(0)
227
+ await childBudget.flush()
228
+ throw error
229
+ }
230
+ let childAbortController: AbortController | undefined
231
+ let agentTask: AgentTask | undefined
232
+ try {
233
+ childAbortController = createChildAbortController(context.parentAbortController)
224
234
 
225
- const taskId = generateTaskId()
235
+ const taskId = generateTaskId()
226
236
 
227
- const childParentActor: ActorRef = {
228
- kind: 'agent',
229
- agentId: context.parentAgentId,
230
- tenantId: context.tenantId,
231
- parentActor: context.parentActor,
232
- }
237
+ const childParentActor: ActorRef = {
238
+ kind: 'agent',
239
+ agentId: context.parentAgentId,
240
+ tenantId: context.tenantId,
241
+ parentActor: context.parentActor,
242
+ }
233
243
 
234
- // The union of every deny along the chain, plus this spawn's own.
235
- //
236
- // Without it, NZ-GATE-09's scope stopped at one level: a child denied
237
- // `bash` could spawn a grandchild naming no scope, and the grandchild
238
- // got bash back. A restriction that a descendant can shed by
239
- // delegating is not a restriction.
240
- //
241
- // No containment CHECK here, deliberately. The obvious shape is to
242
- // confirm with `isDescendantOfActor` that the child really sits under
243
- // the actor whose scope is being inherited — but `childParentActor`
244
- // is built two statements up FROM `context.parentActor`, so the
245
- // answer is yes by construction and the branch could never be taken.
246
- // A check that cannot fail reads as a safeguard and is not one. The
247
- // predicate is exported for the callers that do face an actor they
248
- // did not construct: an audit walking a subtree, a host asking
249
- // whether one run's actor is contained by another's.
250
- //
251
- // Union, not replace, and not "innermost wins": a descendant may
252
- // narrow further and may never widen.
253
- const inheritedDenies = context.toolDenies ?? []
254
- const ownDenies = options.toolScope?.deny ?? []
255
- const resolvedDenies = [...new Set([...inheritedDenies, ...ownDenies])]
256
-
257
- const childContext: AgentTaskContext = {
258
- parentRunId: context.parentRunId,
259
- parentAgentId: context.parentAgentId,
260
- parentAbortController: context.parentAbortController,
261
- depth: context.depth + 1,
262
- budgetTracker: context.budgetTracker,
263
- factoryOptions: context.factoryOptions,
264
- tenantId: context.tenantId,
265
- topicId: context.topicId,
266
- sessionId: spawnRecord.childSessionId,
267
- projectId: context.projectId,
268
- parentActor: childParentActor,
269
- ...(resolvedDenies.length > 0 ? { toolDenies: resolvedDenies } : {}),
270
- }
244
+ // The union of every deny along the chain, plus this spawn's own.
245
+ //
246
+ // Without it, NZ-GATE-09's scope stopped at one level: a child denied
247
+ // `bash` could spawn a grandchild naming no scope, and the grandchild
248
+ // got bash back. A restriction that a descendant can shed by
249
+ // delegating is not a restriction.
250
+ //
251
+ // No containment CHECK here, deliberately. The obvious shape is to
252
+ // confirm with `isDescendantOfActor` that the child really sits under
253
+ // the actor whose scope is being inherited — but `childParentActor`
254
+ // is built two statements up FROM `context.parentActor`, so the
255
+ // answer is yes by construction and the branch could never be taken.
256
+ // A check that cannot fail reads as a safeguard and is not one. The
257
+ // predicate is exported for the callers that do face an actor they
258
+ // did not construct: an audit walking a subtree, a host asking
259
+ // whether one run's actor is contained by another's.
260
+ //
261
+ // Union, not replace, and not "innermost wins": a descendant may
262
+ // narrow further and may never widen.
263
+ const inheritedDenies = context.toolDenies ?? []
264
+ const ownDenies = options.toolScope?.deny ?? []
265
+ const resolvedDenies = [...new Set([...inheritedDenies, ...ownDenies])]
271
266
 
272
- const agentTask: AgentTask = {
273
- taskId,
274
- agentId: options.agentId,
275
- agent,
276
- childAbortController,
277
- context: childContext,
278
- state: 'pending',
279
- budgetReservation: allocatedTokens,
280
- pendingMessages: [],
281
- createdAt: Date.now(),
282
- runEventListener: listener,
283
- }
267
+ const childContext: AgentTaskContext = {
268
+ parentRunId: context.parentRunId,
269
+ parentAgentId: context.parentAgentId,
270
+ parentAbortController: context.parentAbortController,
271
+ depth: context.depth + 1,
272
+ budget: childBudget,
273
+ factoryOptions: context.factoryOptions,
274
+ tenantId: context.tenantId,
275
+ topicId: context.topicId,
276
+ sessionId: spawnRecord.childSessionId,
277
+ projectId: context.projectId,
278
+ parentActor: childParentActor,
279
+ ...(resolvedDenies.length > 0 ? { toolDenies: resolvedDenies } : {}),
280
+ }
284
281
 
285
- this.instances.set(taskId, agentTask)
286
- if (resolvedDenies.length > 0) spawnRecord.resolvedToolDenies = resolvedDenies
287
- this.spawnRecords.set(taskId, spawnRecord)
288
- this.emit({
289
- type: 'pending',
290
- taskId,
291
- agentId: options.agentId,
292
- parentAgentId: context.parentAgentId,
293
- depth: context.depth,
294
- })
282
+ agentTask = {
283
+ taskId,
284
+ agentId: options.agentId,
285
+ agent,
286
+ childAbortController,
287
+ context: childContext,
288
+ state: 'pending',
289
+ pendingMessages: [],
290
+ createdAt: Date.now(),
291
+ runEventListener: listener,
292
+ }
295
293
 
296
- if (listener) {
297
- listener({
298
- type: 'agent_pending',
299
- runId: context.parentRunId,
294
+ childAbortController.signal.throwIfAborted()
295
+ this.instances.set(taskId, agentTask)
296
+ if (resolvedDenies.length > 0) spawnRecord.resolvedToolDenies = resolvedDenies
297
+ this.spawnRecords.set(taskId, spawnRecord)
298
+ this.emit({
299
+ type: 'pending',
300
300
  taskId,
301
+ agentId: options.agentId,
301
302
  parentAgentId: context.parentAgentId,
302
- childAgentId: options.agentId,
303
303
  depth: context.depth,
304
304
  })
305
305
 
306
- const lineage: Lineage = {
307
- parentSessionId: spawnRecord.parentSessionId,
308
- rootSessionId: spawnRecord.rootSessionId,
309
- depth: spawnRecord.childDepth,
306
+ if (listener) {
307
+ await listener({
308
+ type: 'agent_pending',
309
+ runId: context.parentRunId,
310
+ taskId,
311
+ parentAgentId: context.parentAgentId,
312
+ childAgentId: options.agentId,
313
+ depth: context.depth,
314
+ })
315
+
316
+ const lineage: Lineage = {
317
+ parentSessionId: spawnRecord.parentSessionId,
318
+ rootSessionId: spawnRecord.rootSessionId,
319
+ depth: spawnRecord.childDepth,
320
+ }
321
+ await listener({
322
+ type: 'subsession_spawned',
323
+ runId: context.parentRunId,
324
+ subSessionId: spawnRecord.subSessionId,
325
+ parentSessionId: spawnRecord.parentSessionId,
326
+ spawnedBy: context.parentActor,
327
+ lineage,
328
+ schemaVersion: RUN_EVENT_SCHEMA_VERSION,
329
+ at: new Date(),
330
+ })
310
331
  }
311
- listener({
312
- type: 'subsession_spawned',
313
- runId: context.parentRunId,
314
- subSessionId: spawnRecord.subSessionId,
315
- parentSessionId: spawnRecord.parentSessionId,
316
- spawnedBy: context.parentActor,
317
- lineage,
318
- schemaVersion: RUN_EVENT_SCHEMA_VERSION,
319
- at: new Date(),
332
+ this.log.info('Agent task pending', {
333
+ 'namzu.agent.task_id': taskId,
334
+ 'namzu.agent.definition_id': options.agentId,
335
+ 'namzu.agent.depth': context.depth,
320
336
  })
321
- }
322
- this.log.info('Agent task pending', {
323
- 'namzu.agent.task_id': taskId,
324
- 'namzu.agent.definition_id': options.agentId,
325
- 'namzu.agent.depth': context.depth,
326
- })
327
337
 
328
- const definition = this.registry.getOrThrow(options.agentId)
329
- let childConfig: BaseAgentConfig
330
- if (definition.configBuilder) {
331
- // Call the configBuilder regardless of whether factoryOptions were
332
- // supplied. BYO-provider flows (ambient cloud credentials, a custom registry)
333
- // commonly omit factoryOptions because the provider resolves its own
334
- // credentials; the builder still needs to run to wire provider+tools.
335
- // Defaults: empty factoryOptions when omitted; configOverrides win.
336
- childConfig = await definition.configBuilder({
337
- ...(context.factoryOptions ?? {}),
338
- tokenBudget: allocatedTokens,
339
- timeoutMs: options.budgetAllocation?.timeoutMs ?? this.config.childTimeoutMs,
340
- parentRunId: context.parentRunId as string | undefined,
341
- depth: context.depth + 1,
342
- ...options.configOverrides,
343
- })
338
+ const definition = this.registry.getOrThrow(options.agentId)
339
+ let childConfig: BaseAgentConfig
340
+ if (definition.configBuilder) {
341
+ // Call the configBuilder regardless of whether factoryOptions were
342
+ // supplied. BYO-provider flows (ambient cloud credentials, a custom registry)
343
+ // commonly omit factoryOptions because the provider resolves its own
344
+ // credentials; the builder still needs to run to wire provider+tools.
345
+ // Defaults: empty factoryOptions when omitted; configOverrides win.
346
+ childConfig = {
347
+ ...(await definition.configBuilder({
348
+ ...(context.factoryOptions ?? {}),
349
+ tokenBudget: allocatedTokens,
350
+ timeoutMs: options.budgetAllocation?.timeoutMs ?? this.config.childTimeoutMs,
351
+ parentRunId: context.parentRunId as string | undefined,
352
+ depth: context.depth + 1,
353
+ ...options.configOverrides,
354
+ })),
355
+ }
356
+
357
+ if (!childConfig.contextLevel && definition.contextLevel) {
358
+ childConfig.contextLevel = definition.contextLevel
359
+ }
360
+
361
+ // Propagate session-hierarchy scoping onto the child config. The
362
+ // configBuilder may not have been updated to emit these yet; we
363
+ // stamp them here so query() sees them regardless.
364
+ childConfig.sessionId = spawnRecord?.childSessionId ?? context.sessionId
365
+ childConfig.topicId = context.topicId
366
+ childConfig.projectId = context.projectId
367
+ childConfig.tenantId = context.tenantId
368
+ // Stamp the trace parent the same way, rather than trusting every
369
+ // configBuilder to forward an option it may not know about.
370
+ if (options.configOverrides?.parentSpan) {
371
+ childConfig.parentSpan = options.configOverrides.parentSpan
372
+ }
373
+ // Stamped for the same reason as the trace parent above: a
374
+ // `configBuilder` is written by whoever registered the agent and
375
+ // cannot be trusted to forward something it was never told about.
376
+ // An explicit override still wins, so a host can hand one child a
377
+ // different channel — or none.
378
+ //
379
+ // Without this every delegated child fell through to
380
+ // `autoApproveHandler`, so a host's "ask before acting" gate
381
+ // covered the top-level run and nothing it delegated.
382
+ const inheritedHandler = options.configOverrides?.resumeHandler ?? context.resumeHandler
383
+ if (inheritedHandler) childConfig.resumeHandler = inheritedHandler
344
384
 
345
- if (!childConfig.contextLevel && definition.contextLevel) {
346
- childConfig.contextLevel = definition.contextLevel
385
+ // And the environment, for the third time and the same reason.
386
+ //
387
+ // The bare-config branch below has always carried `env`; this one
388
+ // never did, so a delegate registered WITH a `configBuilder` — the
389
+ // normal way, and what every host in this repo does — silently ran
390
+ // with none of the environment its parent had been given. The
391
+ // builder is written by whoever registered the agent and cannot be
392
+ // expected to forward a field it was never told about, which is
393
+ // exactly why `parentSpan` and `resumeHandler` are stamped here too.
394
+ //
395
+ // Merged per key rather than replaced. `configOverrides` is a
396
+ // `Partial`, so assigning the whole map would drop every key the
397
+ // builder set and the caller did not restate — the override wins per
398
+ // key, the same direction it already wins for `model` and `effort`.
399
+ const inheritedEnv = mergeEnv(childConfig.env, options.configOverrides?.env)
400
+ if (inheritedEnv) childConfig.env = inheritedEnv
401
+ // A builder may ignore a newly-added factory option. The requested root
402
+ // policy is an execution boundary, so stamp an explicit child override
403
+ // after the builder rather than silently falling back to ephemeral.
404
+ if (options.configOverrides?.sandbox) {
405
+ childConfig.sandbox = options.configOverrides.sandbox
406
+ }
407
+ } else {
408
+ this.log.warn('No configBuilder, using bare config', {
409
+ [GENAI.AGENT_ID]: options.agentId,
410
+ })
411
+ childConfig = {
412
+ model: options.configOverrides?.model ?? 'default',
413
+ tokenBudget: allocatedTokens,
414
+ timeoutMs: options.budgetAllocation?.timeoutMs ?? this.config.childTimeoutMs,
415
+ streamIdleTimeoutMs: options.configOverrides?.streamIdleTimeoutMs,
416
+ maxRequestRichContentBytes: options.configOverrides?.maxRequestRichContentBytes,
417
+ attachmentResolveTimeoutMs: options.configOverrides?.attachmentResolveTimeoutMs,
418
+ temperature: options.configOverrides?.temperature,
419
+ parentSpan: options.configOverrides?.parentSpan,
420
+ maxIterations: options.configOverrides?.maxIterations,
421
+ maxResponseTokens: options.configOverrides?.maxResponseTokens,
422
+ // A delegate spawned without a configBuilder lands here, and this
423
+ // list is the only thing it inherits. Omitting these meant a child
424
+ // silently ran at the default depth and effort its parent had
425
+ // deliberately moved off.
426
+ thinking: options.configOverrides?.thinking,
427
+ effort: options.configOverrides?.effort,
428
+ env: options.configOverrides?.env,
429
+ sandbox: options.configOverrides?.sandbox,
430
+ sessionId: spawnRecord.childSessionId,
431
+ topicId: context.topicId,
432
+ projectId: context.projectId,
433
+ tenantId: context.tenantId,
434
+ parentRunId: context.parentRunId,
435
+ depth: context.depth + 1,
436
+ resumeHandler: options.configOverrides?.resumeHandler ?? context.resumeHandler,
437
+ }
347
438
  }
348
439
 
349
- // Propagate session-hierarchy scoping onto the child config. The
350
- // configBuilder may not have been updated to emit these yet; we
351
- // stamp them here so query() sees them regardless.
352
- childConfig.sessionId = spawnRecord?.childSessionId ?? context.sessionId
353
- childConfig.topicId = context.topicId
354
- childConfig.projectId = context.projectId
355
- childConfig.tenantId = context.tenantId
356
- // Stamp the trace parent the same way, rather than trusting every
357
- // configBuilder to forward an option it may not know about.
358
- if (options.configOverrides?.parentSpan) {
359
- childConfig.parentSpan = options.configOverrides.parentSpan
440
+ // Lineage is assigned by the spawning manager, not proposed by the
441
+ // child definition. A fixed configBuilder can ignore its inputs and
442
+ // configOverrides is caller-authored; neither may turn a child back
443
+ // into depth zero or attach it to a different parent run.
444
+ childConfig.parentRunId = context.parentRunId
445
+ childConfig.depth = context.depth + 1
446
+ childConfig.budget = childBudget
447
+ // The reservation is the execution ceiling, regardless of a builder's
448
+ // defaults or configOverrides. Zero means unlimited to query(), so it
449
+ // must inherit the finite allocation rather than erase that ceiling.
450
+ if (!Number.isFinite(childConfig.tokenBudget) || childConfig.tokenBudget < 0) {
451
+ throw new NamzuError({
452
+ code: 'invalid_config',
453
+ message: `Invalid child token budget for "${options.agentId}": ${childConfig.tokenBudget}`,
454
+ })
360
455
  }
361
- // Stamped for the same reason as the trace parent above: a
362
- // `configBuilder` is written by whoever registered the agent and
363
- // cannot be trusted to forward something it was never told about.
364
- // An explicit override still wins, so a host can hand one child a
365
- // different channel — or none.
456
+ childConfig.tokenBudget =
457
+ childConfig.tokenBudget === 0
458
+ ? allocatedTokens
459
+ : Math.min(childConfig.tokenBudget, allocatedTokens)
460
+ childBudget.narrow(childConfig.tokenBudget)
461
+
462
+ // Stamped AFTER the builder, for the fourth time and the same reason:
463
+ // a `configBuilder` is written by whoever registered the agent and
464
+ // cannot forward a field it was never told about. A scope applied
465
+ // before it would be silently discarded by every builder that returns
466
+ // a fixed config — which is most of them.
467
+ //
468
+ // OUTSIDE the branch, unlike the four stamps above, and that is the
469
+ // point. Written inside the `configBuilder` arm it read correctly and
470
+ // left the bare-config arm below ignoring `toolScope` entirely — a
471
+ // caller that asked for a narrower child got a wider one, silently,
472
+ // and in the only direction that matters. Every other field here is
473
+ // an inheritance, where missing it costs the child something; this
474
+ // one is a restriction, where missing it costs the CALLER something.
366
475
  //
367
- // Without this every delegated child fell through to
368
- // `autoApproveHandler`, so a host's "ask before acting" gate
369
- // covered the top-level run and nothing it delegated.
370
- const inheritedHandler = options.configOverrides?.resumeHandler ?? context.resumeHandler
371
- if (inheritedHandler) childConfig.resumeHandler = inheritedHandler
476
+ // Narrowing only: the deny list is SUBTRACTED from whatever the child
477
+ // would otherwise have. `allowedTools` absent means "every registered
478
+ // tool", so a deny with no existing allow-list has to be resolved
479
+ // against the registry at the run rather than here — which `query()`
480
+ // does, and which is why this appends to `deniedTools` rather than
481
+ // synthesising an allow-list.
482
+ if (resolvedDenies.length > 0) {
483
+ childConfig.deniedTools = [
484
+ ...new Set([...(childConfig.deniedTools ?? []), ...resolvedDenies]),
485
+ ]
486
+ }
487
+ if (options.personaOverride) childConfig.persona = options.personaOverride
372
488
 
373
- // And the environment, for the third time and the same reason.
489
+ // Outside the branch, like the scope above it and for the same reason:
490
+ // a `configBuilder` cannot forward a field it was never told about.
374
491
  //
375
- // The bare-config branch below has always carried `env`; this one
376
- // never did, so a delegate registered WITH a `configBuilder` the
377
- // normal way, and what every host in this repo does — silently ran
378
- // with none of the environment its parent had been given. The
379
- // builder is written by whoever registered the agent and cannot be
380
- // expected to forward a field it was never told about, which is
381
- // exactly why `parentSpan` and `resumeHandler` are stamped here too.
492
+ // Bound to the taskId rather than to the task object, so a drain after
493
+ // the task is gone throws where the caller is instead of returning an
494
+ // empty array from a closure over a corpse.
382
495
  //
383
- // Merged per key rather than replaced. `configOverrides` is a
384
- // `Partial`, so assigning the whole map would drop every key the
385
- // builder set and the caller did not restate the override wins per
386
- // key, the same direction it already wins for `model` and `effort`.
387
- const inheritedEnv = mergeEnv(childConfig.env, options.configOverrides?.env)
388
- if (inheritedEnv) childConfig.env = inheritedEnv
389
- // A builder may ignore a newly-added factory option. The requested root
390
- // policy is an execution boundary, so stamp an explicit child override
391
- // after the builder rather than silently falling back to ephemeral.
392
- if (options.configOverrides?.sandbox) {
393
- childConfig.sandbox = options.configOverrides.sandbox
394
- }
395
- } else {
396
- this.log.warn('No configBuilder, using bare config', {
397
- [GENAI.AGENT_ID]: options.agentId,
496
+ // This is the delivery point `pendingMessages` never had.
497
+ // `continueTask` and `queueMessage` pushed onto it and nothing in the
498
+ // kernel drained it the manager interface's own docblock said "the
499
+ // runtime does not deliver it", and `continue_task` was unmounted from
500
+ // the coordinator tools because of that.
501
+ childConfig.inboundMessages = () => this.drainMessages(taskId)
502
+ childAbortController.signal.throwIfAborted()
503
+
504
+ await childBudget.flush()
505
+ childAbortController.signal.throwIfAborted()
506
+ this.runChild(agentTask, options, childConfig, listener).catch(async (err) => {
507
+ // A thrown invocation supplied no final usage receipt. Keep its
508
+ // reservation, including when its task handle was canceled or evicted.
509
+ let failure = err
510
+ try {
511
+ await childBudget.flush()
512
+ } catch (writeError) {
513
+ failure = writeError
514
+ }
515
+ this.markFailed(taskId, toErrorMessage(failure))
398
516
  })
399
- childConfig = {
400
- model: options.configOverrides?.model ?? 'default',
401
- tokenBudget: allocatedTokens,
402
- timeoutMs: options.budgetAllocation?.timeoutMs ?? this.config.childTimeoutMs,
403
- streamIdleTimeoutMs: options.configOverrides?.streamIdleTimeoutMs,
404
- maxRequestRichContentBytes: options.configOverrides?.maxRequestRichContentBytes,
405
- attachmentResolveTimeoutMs: options.configOverrides?.attachmentResolveTimeoutMs,
406
- temperature: options.configOverrides?.temperature,
407
- parentSpan: options.configOverrides?.parentSpan,
408
- maxIterations: options.configOverrides?.maxIterations,
409
- maxResponseTokens: options.configOverrides?.maxResponseTokens,
410
- // A delegate spawned without a configBuilder lands here, and this
411
- // list is the only thing it inherits. Omitting these meant a child
412
- // silently ran at the default depth and effort its parent had
413
- // deliberately moved off.
414
- thinking: options.configOverrides?.thinking,
415
- effort: options.configOverrides?.effort,
416
- env: options.configOverrides?.env,
417
- sandbox: options.configOverrides?.sandbox,
418
- sessionId: spawnRecord.childSessionId,
419
- topicId: context.topicId,
420
- projectId: context.projectId,
421
- tenantId: context.tenantId,
422
- parentRunId: context.parentRunId,
423
- depth: context.depth + 1,
424
- resumeHandler: options.configOverrides?.resumeHandler ?? context.resumeHandler,
517
+
518
+ return agentTask
519
+ } catch (err) {
520
+ // No child invocation has been admitted: all reserved tokens and all
521
+ // provisioned resources are still ours to return. A builder/listener
522
+ // failure must not strand a pending task the caller never received.
523
+ if (agentTask) {
524
+ await this.rollbackUnstartedSpawn(agentTask, spawnRecord, err)
525
+ } else {
526
+ childAbortController?.abort()
527
+ childBudget.settle(0)
528
+ await childBudget.flush()
529
+ await this.rollbackSpawnResources(spawnRecord)
425
530
  }
531
+ throw err
426
532
  }
533
+ }
427
534
 
428
- // Lineage is assigned by the spawning manager, not proposed by the
429
- // child definition. A fixed configBuilder can ignore its inputs and
430
- // configOverrides is caller-authored; neither may turn a child back
431
- // into depth zero or attach it to a different parent run.
432
- childConfig.parentRunId = context.parentRunId
433
- childConfig.depth = context.depth + 1
434
-
435
- // Stamped AFTER the builder, for the fourth time and the same reason:
436
- // a `configBuilder` is written by whoever registered the agent and
437
- // cannot forward a field it was never told about. A scope applied
438
- // before it would be silently discarded by every builder that returns
439
- // a fixed config which is most of them.
440
- //
441
- // OUTSIDE the branch, unlike the four stamps above, and that is the
442
- // point. Written inside the `configBuilder` arm it read correctly and
443
- // left the bare-config arm below ignoring `toolScope` entirely — a
444
- // caller that asked for a narrower child got a wider one, silently,
445
- // and in the only direction that matters. Every other field here is
446
- // an inheritance, where missing it costs the child something; this
447
- // one is a restriction, where missing it costs the CALLER something.
448
- //
449
- // Narrowing only: the deny list is SUBTRACTED from whatever the child
450
- // would otherwise have. `allowedTools` absent means "every registered
451
- // tool", so a deny with no existing allow-list has to be resolved
452
- // against the registry at the run rather than here — which `query()`
453
- // does, and which is why this appends to `deniedTools` rather than
454
- // synthesising an allow-list.
455
- if (resolvedDenies.length > 0) {
456
- childConfig.deniedTools = [
457
- ...new Set([...(childConfig.deniedTools ?? []), ...resolvedDenies]),
458
- ]
535
+ private async rollbackUnstartedSpawn(
536
+ agentTask: AgentTask,
537
+ spawnRecord: ChildSpawnRecord,
538
+ reason: unknown,
539
+ ): Promise<void> {
540
+ agentTask.childAbortController.abort()
541
+ agentTask.context.budget.settle(0)
542
+ await agentTask.context.budget.flush()
543
+ if (!isTerminalAgentTaskState(agentTask.state)) {
544
+ agentTask.state = 'failed'
545
+ const error = toErrorMessage(reason)
546
+ this.emit({ type: 'failed', taskId: agentTask.taskId, error })
547
+ try {
548
+ await agentTask.runEventListener?.({
549
+ type: 'agent_failed',
550
+ runId: agentTask.context.parentRunId,
551
+ taskId: agentTask.taskId,
552
+ error,
553
+ })
554
+ } catch (err) {
555
+ this.log.warn('Unstarted child failure notification failed', {
556
+ 'namzu.task.id': agentTask.taskId,
557
+ 'exception.message': toErrorMessage(err),
558
+ })
559
+ }
459
560
  }
460
- if (options.personaOverride) childConfig.persona = options.personaOverride
461
-
462
- // Outside the branch, like the scope above it and for the same reason:
463
- // a `configBuilder` cannot forward a field it was never told about.
464
- //
465
- // Bound to the taskId rather than to the task object, so a drain after
466
- // the task is gone throws where the caller is instead of returning an
467
- // empty array from a closure over a corpse.
468
- //
469
- // This is the delivery point `pendingMessages` never had.
470
- // `continueTask` and `queueMessage` pushed onto it and nothing in the
471
- // kernel drained it — the manager interface's own docblock said "the
472
- // runtime does not deliver it", and `continue_task` was unmounted from
473
- // the coordinator tools because of that.
474
- childConfig.inboundMessages = () => this.drainMessages(taskId)
475
-
476
- this.runChild(agentTask, options, childConfig, listener).catch((err) => {
477
- this.markFailed(taskId, toErrorMessage(err))
478
- })
561
+ this.clearEvictionTimer(agentTask.taskId)
562
+ this.instances.delete(agentTask.taskId)
563
+ this.spawnRecords.delete(agentTask.taskId)
564
+ this.resolveCompletionCallbacks(agentTask.taskId)
565
+ await this.rollbackSpawnResources(spawnRecord)
566
+ }
479
567
 
480
- return agentTask
568
+ private async rollbackSpawnResources(spawnRecord: ChildSpawnRecord): Promise<void> {
569
+ await this.disposeChildWorkspace(spawnRecord)
570
+ try {
571
+ // The edge must be removed before its child: stores reject deletion
572
+ // of a session that still has a subsession reference.
573
+ await this.deps.sessionStore.deleteSubSession(spawnRecord.subSessionId, spawnRecord.tenantId)
574
+ await this.deps.sessionStore.deleteSession(spawnRecord.childSessionId, spawnRecord.tenantId)
575
+ } catch (err) {
576
+ this.log.warn('Unstarted child rollback failed', {
577
+ 'namzu.sub_session.id': spawnRecord.subSessionId,
578
+ 'exception.message': toErrorMessage(err),
579
+ })
580
+ }
481
581
  }
482
582
 
483
583
  cancel(taskId: TaskId, cause?: CancelCause): void {
@@ -625,7 +725,7 @@ export class AgentManager {
625
725
  private async provisionSpawn(
626
726
  options: SendMessageOptions,
627
727
  context: AgentTaskContext,
628
- ): Promise<{ spawnRecord: ChildSpawnRecord; allocatedTokens: number }> {
728
+ ): Promise<ChildSpawnRecord> {
629
729
  const key = options.parentSessionId
630
730
  const queued = (this.spawnLocks.get(key) ?? Promise.resolve()).then(
631
731
  () => this.provisionSpawnUnlocked(options, context),
@@ -651,42 +751,7 @@ export class AgentManager {
651
751
  private async provisionSpawnUnlocked(
652
752
  options: SendMessageOptions,
653
753
  context: AgentTaskContext,
654
- ): Promise<{ spawnRecord: ChildSpawnRecord; allocatedTokens: number }> {
655
- // Read the parent's remaining budget HERE, inside the lock, so that
656
- // concurrent siblings queue behind one another rather than all reading
657
- // the same untouched number. The debit at the end of this method closes
658
- // the pair: read and write are now on the same side of every await.
659
- const maxAllocation = Math.floor(
660
- context.budgetTracker.remaining * this.config.maxBudgetFraction,
661
- )
662
- const allocatedTokens = Math.min(
663
- options.budgetAllocation?.tokenBudget ?? maxAllocation,
664
- maxAllocation,
665
- )
666
-
667
- // Budget exhaustion must not INVERT into no budget at all. Downstream,
668
- // `tokenBudget: 0` means "uncapped" (`LimitChecker`: `tokenBudget > 0
669
- // && total >= tokenBudget`), and `maxAllocation` floors to 0 as soon as
670
- // the parent's remaining drops below `1 / maxBudgetFraction`. So the
671
- // most depleted parent in the tree was the one that spawned an
672
- // unlimited child. Refuse instead: a caller that wants an uncapped
673
- // child can say so explicitly with its own `budgetAllocation`.
674
- //
675
- // Refusing before any provisioning work also preserves the property the
676
- // debit's placement was chosen for: a spawn this call rejects makes no
677
- // state change at all, and burns no allocation.
678
- if (allocatedTokens <= 0) {
679
- throw new NamzuError({
680
- code: 'invalid_config',
681
- message: `Cannot spawn "${options.agentId}": the parent has ${context.budgetTracker.remaining} tokens remaining, which allocates 0 to the child — and a token budget of 0 means UNLIMITED downstream.`,
682
- details: {
683
- agentId: options.agentId,
684
- parentRemaining: context.budgetTracker.remaining,
685
- maxBudgetFraction: this.config.maxBudgetFraction,
686
- },
687
- })
688
- }
689
-
754
+ ): Promise<ChildSpawnRecord> {
690
755
  // Phase 9: deps are unconditional required. Every spawn produces a
691
756
  // SubSession + Session + WorkspaceRef triple (Convention #0: no
692
757
  // partial/legacy path).
@@ -831,6 +896,12 @@ export class AgentManager {
831
896
  await store.updateSubSession(subSession, context.tenantId)
832
897
  }
833
898
  } catch (err) {
899
+ if (workspaceRef && subSession) {
900
+ await this.disposeChildWorkspace({
901
+ workspaceRef,
902
+ subSessionId: subSession.id,
903
+ })
904
+ }
834
905
  // Compensating rollback order is mandated by the store's
835
906
  // deny-by-default cascade policy (Convention #5): `deleteSession`
836
907
  // throws when any subsession still references it, so the subsession
@@ -848,24 +919,14 @@ export class AgentManager {
848
919
  throw err
849
920
  }
850
921
 
851
- // Debited only now, with the provisioning committed. Every path that
852
- // could still have thrown is behind us, so a rejected spawn leaves the
853
- // parent's budget untouched — the property the debit's original
854
- // placement was chosen for, kept while closing the race that placement
855
- // opened.
856
- context.budgetTracker.remaining -= allocatedTokens
857
-
858
922
  return {
859
- spawnRecord: {
860
- subSessionId: subSession.id,
861
- childSessionId: childSession.id,
862
- tenantId: context.tenantId,
863
- parentSessionId: options.parentSessionId,
864
- rootSessionId,
865
- childDepth,
866
- workspaceRef,
867
- },
868
- allocatedTokens,
923
+ subSessionId: subSession.id,
924
+ childSessionId: childSession.id,
925
+ tenantId: context.tenantId,
926
+ parentSessionId: options.parentSessionId,
927
+ rootSessionId,
928
+ childDepth,
929
+ workspaceRef,
869
930
  }
870
931
  }
871
932
 
@@ -887,6 +948,9 @@ export class AgentManager {
887
948
  const childListener = this.wrapChildListener(listener, spawnRecord)
888
949
 
889
950
  const result = await agentTask.agent.run(input, childConfig, childListener)
951
+ agentTask.context.budget.bindRun(result.runId)
952
+ agentTask.context.budget.settle(result.usage?.totalTokens)
953
+ await agentTask.context.budget.flush()
890
954
  await this.finalizeChild(agentTask, result)
891
955
  }
892
956
 
@@ -1019,44 +1083,10 @@ export class AgentManager {
1019
1083
  this.markCompleted(agentTask.taskId, result)
1020
1084
  }
1021
1085
 
1022
- /**
1023
- * Return the unspent part of a settled child's reservation.
1024
- *
1025
- * The debit at spawn reserves headroom so siblings cannot each be
1026
- * promised the same tokens. Nothing returned it, so a pool shrank by
1027
- * the full allocation on every spawn no matter what the child used: at
1028
- * a half-pool fraction, ten delegations left a parent with a
1029
- * thousandth of its budget and the next spawn was refused for a budget
1030
- * that had barely been spent.
1031
- *
1032
- * Idempotent — the reservation is cleared as it is returned, so a
1033
- * second terminal transition for the same task cannot credit twice.
1034
- * Spend above the reservation returns nothing rather than going
1035
- * negative; the child was capped at its allocation, so that case means
1036
- * the accounting was already wrong and inventing headroom would hide
1037
- * it.
1038
- */
1039
- private releaseBudget(agentTask: AgentTask, spent: number): void {
1040
- const reserved = agentTask.budgetReservation
1041
- if (reserved === undefined) return
1042
- agentTask.budgetReservation = undefined
1043
-
1044
- const unused = Math.max(0, reserved - Math.max(0, spent))
1045
- if (unused === 0) return
1046
- agentTask.context.budgetTracker.remaining += unused
1047
- this.log.debug('Returned unspent child budget', {
1048
- 'namzu.task.id': agentTask.taskId,
1049
- 'namzu.manager.reserved': reserved,
1050
- 'namzu.manager.spent': spent,
1051
- 'namzu.manager.returned': unused,
1052
- })
1053
- }
1054
-
1055
1086
  private markCompleted(taskId: TaskId, result: BaseAgentResult): void {
1056
1087
  const agentTask = this.instances.get(taskId)
1057
1088
  if (!agentTask || isTerminalAgentTaskState(agentTask.state)) return
1058
1089
 
1059
- this.releaseBudget(agentTask, result.usage?.totalTokens ?? 0)
1060
1090
  agentTask.result = result
1061
1091
  agentTask.completedAt = Date.now()
1062
1092
  this.updateState(taskId, 'completed')
@@ -1076,16 +1106,12 @@ export class AgentManager {
1076
1106
  const agentTask = this.instances.get(taskId)
1077
1107
  if (!agentTask || isTerminalAgentTaskState(agentTask.state)) return
1078
1108
 
1079
- // A child that failed still spent whatever it spent, and holding the
1080
- // rest of its reservation would punish the parent twice for one
1081
- // failure.
1082
- this.releaseBudget(agentTask, agentTask.result?.usage?.totalTokens ?? 0)
1083
-
1084
1109
  agentTask.result = {
1085
- runId: agentTask.context.parentRunId,
1110
+ runId: agentTask.context.budget.runId ?? agentTask.context.parentRunId,
1086
1111
  status: 'failed',
1087
- usage: { ...EMPTY_TOKEN_USAGE },
1088
- cost: { ...ZERO_COST },
1112
+ usage: agentTask.context.budget.ownUsage,
1113
+ budget: agentTask.context.budget.summary(),
1114
+ cost: { ...ZERO_COST, unpricedTokens: agentTask.context.budget.ownTokens },
1089
1115
  iterations: 0,
1090
1116
  durationMs: Date.now() - agentTask.createdAt,
1091
1117
  messages: [],
@@ -1149,7 +1175,9 @@ export class AgentManager {
1149
1175
  * The failure is logged instead, because a worktree that could not be
1150
1176
  * removed is an operator's problem and silence is how it stays one.
1151
1177
  */
1152
- private async disposeChildWorkspace(spawnRecord: ChildSpawnRecord): Promise<void> {
1178
+ private async disposeChildWorkspace(
1179
+ spawnRecord: Pick<ChildSpawnRecord, 'workspaceRef' | 'subSessionId'>,
1180
+ ): Promise<void> {
1153
1181
  if (!spawnRecord.workspaceRef) return
1154
1182
  const backend = spawnRecord.workspaceRef.meta.backend
1155
1183
  if (!this.deps.workspaceRegistry.has(backend)) return