@namzu/sdk 36.0.0 → 38.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 (412) hide show
  1. package/CHANGELOG.md +244 -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 +4 -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 +16 -1
  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/config/runtime.d.ts +16 -6
  21. package/dist/config/runtime.d.ts.map +1 -1
  22. package/dist/config/runtime.js +2 -0
  23. package/dist/config/runtime.js.map +1 -1
  24. package/dist/contracts/api.d.ts +1 -1
  25. package/dist/contracts/api.d.ts.map +1 -1
  26. package/dist/contracts/schemas.d.ts +10 -10
  27. package/dist/eval/harness-verification.d.ts +63 -0
  28. package/dist/eval/harness-verification.d.ts.map +1 -0
  29. package/dist/eval/harness-verification.js +165 -0
  30. package/dist/eval/harness-verification.js.map +1 -0
  31. package/dist/eval/index.d.ts +2 -0
  32. package/dist/eval/index.d.ts.map +1 -1
  33. package/dist/eval/index.js +1 -0
  34. package/dist/eval/index.js.map +1 -1
  35. package/dist/execution/code-runtime/json-value.d.ts +10 -0
  36. package/dist/execution/code-runtime/json-value.d.ts.map +1 -0
  37. package/dist/execution/code-runtime/json-value.js +89 -0
  38. package/dist/execution/code-runtime/json-value.js.map +1 -0
  39. package/dist/execution/code-runtime/worker-program.d.ts +8 -0
  40. package/dist/execution/code-runtime/worker-program.d.ts.map +1 -0
  41. package/dist/execution/code-runtime/worker-program.js +240 -0
  42. package/dist/execution/code-runtime/worker-program.js.map +1 -0
  43. package/dist/execution/code-runtime/worker.d.ts +22 -0
  44. package/dist/execution/code-runtime/worker.d.ts.map +1 -1
  45. package/dist/execution/code-runtime/worker.js +148 -111
  46. package/dist/execution/code-runtime/worker.js.map +1 -1
  47. package/dist/manager/agent/lifecycle.d.ts +12 -0
  48. package/dist/manager/agent/lifecycle.d.ts.map +1 -1
  49. package/dist/manager/agent/lifecycle.js +316 -46
  50. package/dist/manager/agent/lifecycle.js.map +1 -1
  51. package/dist/manager/run/persistence.d.ts +2 -0
  52. package/dist/manager/run/persistence.d.ts.map +1 -1
  53. package/dist/manager/run/persistence.js +6 -0
  54. package/dist/manager/run/persistence.js.map +1 -1
  55. package/dist/prompt/coding-agent-doctrine.d.ts +2 -2
  56. package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -1
  57. package/dist/prompt/coding-agent-doctrine.js +11 -5
  58. package/dist/prompt/coding-agent-doctrine.js.map +1 -1
  59. package/dist/provider/capabilities.d.ts +5 -1
  60. package/dist/provider/capabilities.d.ts.map +1 -1
  61. package/dist/provider/capabilities.js +24 -0
  62. package/dist/provider/capabilities.js.map +1 -1
  63. package/dist/provider/fallback.d.ts.map +1 -1
  64. package/dist/provider/fallback.js +6 -2
  65. package/dist/provider/fallback.js.map +1 -1
  66. package/dist/public-runtime.d.ts +4 -1
  67. package/dist/public-runtime.d.ts.map +1 -1
  68. package/dist/public-runtime.js +3 -1
  69. package/dist/public-runtime.js.map +1 -1
  70. package/dist/public-tools.d.ts +2 -1
  71. package/dist/public-tools.d.ts.map +1 -1
  72. package/dist/public-tools.js +2 -1
  73. package/dist/public-tools.js.map +1 -1
  74. package/dist/public-types.d.ts +2 -0
  75. package/dist/public-types.d.ts.map +1 -1
  76. package/dist/registry/tool/execute.d.ts +3 -0
  77. package/dist/registry/tool/execute.d.ts.map +1 -1
  78. package/dist/registry/tool/execute.js +11 -80
  79. package/dist/registry/tool/execute.js.map +1 -1
  80. package/dist/run/command-gate.d.ts.map +1 -1
  81. package/dist/run/command-gate.js +65 -12
  82. package/dist/run/command-gate.js.map +1 -1
  83. package/dist/run/index.d.ts +2 -0
  84. package/dist/run/index.d.ts.map +1 -1
  85. package/dist/run/index.js +1 -0
  86. package/dist/run/index.js.map +1 -1
  87. package/dist/run/memory-promoter.d.ts +3 -35
  88. package/dist/run/memory-promoter.d.ts.map +1 -1
  89. package/dist/run/memory-promoter.js +32 -40
  90. package/dist/run/memory-promoter.js.map +1 -1
  91. package/dist/run/memory-recall.d.ts +25 -0
  92. package/dist/run/memory-recall.d.ts.map +1 -0
  93. package/dist/run/memory-recall.js +228 -0
  94. package/dist/run/memory-recall.js.map +1 -0
  95. package/dist/run/reporter.d.ts.map +1 -1
  96. package/dist/run/reporter.js +2 -0
  97. package/dist/run/reporter.js.map +1 -1
  98. package/dist/run/token-budget.d.ts +12 -4
  99. package/dist/run/token-budget.d.ts.map +1 -1
  100. package/dist/run/token-budget.js +49 -14
  101. package/dist/run/token-budget.js.map +1 -1
  102. package/dist/run/workspace-fingerprint.d.ts +10 -3
  103. package/dist/run/workspace-fingerprint.d.ts.map +1 -1
  104. package/dist/run/workspace-fingerprint.js +17 -8
  105. package/dist/run/workspace-fingerprint.js.map +1 -1
  106. package/dist/runtime/query/checkpoint.d.ts +14 -0
  107. package/dist/runtime/query/checkpoint.d.ts.map +1 -1
  108. package/dist/runtime/query/checkpoint.js +82 -1
  109. package/dist/runtime/query/checkpoint.js.map +1 -1
  110. package/dist/runtime/query/executor.d.ts +8 -3
  111. package/dist/runtime/query/executor.d.ts.map +1 -1
  112. package/dist/runtime/query/executor.js +94 -30
  113. package/dist/runtime/query/executor.js.map +1 -1
  114. package/dist/runtime/query/index.d.ts +7 -8
  115. package/dist/runtime/query/index.d.ts.map +1 -1
  116. package/dist/runtime/query/index.js +55 -7
  117. package/dist/runtime/query/index.js.map +1 -1
  118. package/dist/runtime/query/iteration/index.d.ts +12 -0
  119. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  120. package/dist/runtime/query/iteration/index.js +348 -38
  121. package/dist/runtime/query/iteration/index.js.map +1 -1
  122. package/dist/runtime/query/iteration/native-output.d.ts +10 -0
  123. package/dist/runtime/query/iteration/native-output.d.ts.map +1 -0
  124. package/dist/runtime/query/iteration/native-output.js +32 -0
  125. package/dist/runtime/query/iteration/native-output.js.map +1 -0
  126. package/dist/runtime/query/iteration/phases/advisory.d.ts.map +1 -1
  127. package/dist/runtime/query/iteration/phases/advisory.js +18 -13
  128. package/dist/runtime/query/iteration/phases/advisory.js.map +1 -1
  129. package/dist/runtime/query/iteration/phases/compaction.d.ts +2 -0
  130. package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
  131. package/dist/runtime/query/iteration/phases/compaction.js +32 -11
  132. package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
  133. package/dist/runtime/query/iteration/phases/context.d.ts +9 -0
  134. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  135. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  136. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  137. package/dist/runtime/query/iteration/phases/tool-review.js +1 -1
  138. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  139. package/dist/runtime/query/iteration/phases/working-memory.d.ts +1 -1
  140. package/dist/runtime/query/iteration/phases/working-memory.d.ts.map +1 -1
  141. package/dist/runtime/query/iteration/phases/working-memory.js +23 -6
  142. package/dist/runtime/query/iteration/phases/working-memory.js.map +1 -1
  143. package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
  144. package/dist/runtime/query/iteration/stream-turn.js +12 -0
  145. package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
  146. package/dist/runtime/query/observation-context.d.ts +10 -0
  147. package/dist/runtime/query/observation-context.d.ts.map +1 -0
  148. package/dist/runtime/query/observation-context.js +84 -0
  149. package/dist/runtime/query/observation-context.js.map +1 -0
  150. package/dist/runtime/query/request-context.d.ts +30 -0
  151. package/dist/runtime/query/request-context.d.ts.map +1 -0
  152. package/dist/runtime/query/request-context.js +76 -0
  153. package/dist/runtime/query/request-context.js.map +1 -0
  154. package/dist/runtime/query/request-rich-content.d.ts.map +1 -1
  155. package/dist/runtime/query/request-rich-content.js +6 -3
  156. package/dist/runtime/query/request-rich-content.js.map +1 -1
  157. package/dist/runtime/query/review-policy.d.ts +3 -0
  158. package/dist/runtime/query/review-policy.d.ts.map +1 -1
  159. package/dist/runtime/query/review-policy.js +4 -1
  160. package/dist/runtime/query/review-policy.js.map +1 -1
  161. package/dist/runtime/query/steering.d.ts +6 -2
  162. package/dist/runtime/query/steering.d.ts.map +1 -1
  163. package/dist/runtime/query/steering.js +17 -2
  164. package/dist/runtime/query/steering.js.map +1 -1
  165. package/dist/runtime/query/tool-call-budget.d.ts +19 -0
  166. package/dist/runtime/query/tool-call-budget.d.ts.map +1 -0
  167. package/dist/runtime/query/tool-call-budget.js +110 -0
  168. package/dist/runtime/query/tool-call-budget.js.map +1 -0
  169. package/dist/runtime/query/tooling.d.ts +3 -0
  170. package/dist/runtime/query/tooling.d.ts.map +1 -1
  171. package/dist/runtime/query/tooling.js +5 -0
  172. package/dist/runtime/query/tooling.js.map +1 -1
  173. package/dist/sandbox/file-walk-program.d.ts.map +1 -1
  174. package/dist/sandbox/file-walk-program.js +8 -0
  175. package/dist/sandbox/file-walk-program.js.map +1 -1
  176. package/dist/sandbox/file-walk.d.ts.map +1 -1
  177. package/dist/sandbox/file-walk.js +9 -0
  178. package/dist/sandbox/file-walk.js.map +1 -1
  179. package/dist/scheduler/completion-inbox.d.ts.map +1 -1
  180. package/dist/scheduler/completion-inbox.js +3 -0
  181. package/dist/scheduler/completion-inbox.js.map +1 -1
  182. package/dist/scheduler/local.d.ts.map +1 -1
  183. package/dist/scheduler/local.js +1 -0
  184. package/dist/scheduler/local.js.map +1 -1
  185. package/dist/session/handoff/capacity.d.ts +2 -2
  186. package/dist/session/handoff/capacity.d.ts.map +1 -1
  187. package/dist/session/handoff/capacity.js +3 -2
  188. package/dist/session/handoff/capacity.js.map +1 -1
  189. package/dist/store/memory/disk.d.ts +6 -2
  190. package/dist/store/memory/disk.d.ts.map +1 -1
  191. package/dist/store/memory/disk.js +42 -25
  192. package/dist/store/memory/disk.js.map +1 -1
  193. package/dist/store/memory/index.d.ts +4 -0
  194. package/dist/store/memory/index.d.ts.map +1 -1
  195. package/dist/store/memory/index.js +55 -19
  196. package/dist/store/memory/index.js.map +1 -1
  197. package/dist/store/memory/memory.d.ts +3 -2
  198. package/dist/store/memory/memory.d.ts.map +1 -1
  199. package/dist/store/memory/memory.js +11 -2
  200. package/dist/store/memory/memory.js.map +1 -1
  201. package/dist/store/memory/operation-lock.d.ts +11 -0
  202. package/dist/store/memory/operation-lock.d.ts.map +1 -0
  203. package/dist/store/memory/operation-lock.js +94 -0
  204. package/dist/store/memory/operation-lock.js.map +1 -0
  205. package/dist/store/run/token-budget-disk.js +3 -0
  206. package/dist/store/run/token-budget-disk.js.map +1 -1
  207. package/dist/tools/builtins/computer-use.d.ts.map +1 -1
  208. package/dist/tools/builtins/computer-use.js +60 -10
  209. package/dist/tools/builtins/computer-use.js.map +1 -1
  210. package/dist/tools/builtins/edit.d.ts.map +1 -1
  211. package/dist/tools/builtins/edit.js +12 -26
  212. package/dist/tools/builtins/edit.js.map +1 -1
  213. package/dist/tools/builtins/file-write-result.d.ts +4 -0
  214. package/dist/tools/builtins/file-write-result.d.ts.map +1 -0
  215. package/dist/tools/builtins/file-write-result.js +72 -0
  216. package/dist/tools/builtins/file-write-result.js.map +1 -0
  217. package/dist/tools/builtins/glob.d.ts.map +1 -1
  218. package/dist/tools/builtins/glob.js +8 -1
  219. package/dist/tools/builtins/glob.js.map +1 -1
  220. package/dist/tools/builtins/grep.d.ts.map +1 -1
  221. package/dist/tools/builtins/grep.js +10 -0
  222. package/dist/tools/builtins/grep.js.map +1 -1
  223. package/dist/tools/builtins/job.d.ts.map +1 -1
  224. package/dist/tools/builtins/job.js +6 -0
  225. package/dist/tools/builtins/job.js.map +1 -1
  226. package/dist/tools/builtins/read-file.d.ts.map +1 -1
  227. package/dist/tools/builtins/read-file.js +10 -0
  228. package/dist/tools/builtins/read-file.js.map +1 -1
  229. package/dist/tools/builtins/run-code.d.ts +6 -0
  230. package/dist/tools/builtins/run-code.d.ts.map +1 -1
  231. package/dist/tools/builtins/run-code.js +15 -4
  232. package/dist/tools/builtins/run-code.js.map +1 -1
  233. package/dist/tools/builtins/search-tools.d.ts.map +1 -1
  234. package/dist/tools/builtins/search-tools.js +14 -2
  235. package/dist/tools/builtins/search-tools.js.map +1 -1
  236. package/dist/tools/builtins/write-file.d.ts.map +1 -1
  237. package/dist/tools/builtins/write-file.js +30 -44
  238. package/dist/tools/builtins/write-file.js.map +1 -1
  239. package/dist/tools/defineTool.d.ts +2 -0
  240. package/dist/tools/defineTool.d.ts.map +1 -1
  241. package/dist/tools/defineTool.js +3 -0
  242. package/dist/tools/defineTool.js.map +1 -1
  243. package/dist/tools/file-read-tracker.d.ts +4 -0
  244. package/dist/tools/file-read-tracker.d.ts.map +1 -0
  245. package/dist/tools/file-read-tracker.js +16 -0
  246. package/dist/tools/file-read-tracker.js.map +1 -0
  247. package/dist/tools/memory/delete.d.ts +4 -0
  248. package/dist/tools/memory/delete.d.ts.map +1 -0
  249. package/dist/tools/memory/delete.js +26 -0
  250. package/dist/tools/memory/delete.js.map +1 -0
  251. package/dist/tools/memory/index.d.ts +2 -0
  252. package/dist/tools/memory/index.d.ts.map +1 -1
  253. package/dist/tools/memory/index.js +6 -0
  254. package/dist/tools/memory/index.js.map +1 -1
  255. package/dist/tools/memory/save.d.ts.map +1 -1
  256. package/dist/tools/memory/save.js +3 -2
  257. package/dist/tools/memory/save.js.map +1 -1
  258. package/dist/tools/memory/search.d.ts.map +1 -1
  259. package/dist/tools/memory/search.js +12 -5
  260. package/dist/tools/memory/search.js.map +1 -1
  261. package/dist/tools/memory/update.d.ts +4 -0
  262. package/dist/tools/memory/update.d.ts.map +1 -0
  263. package/dist/tools/memory/update.js +47 -0
  264. package/dist/tools/memory/update.js.map +1 -0
  265. package/dist/tools/task/list.js +2 -2
  266. package/dist/tools/task/list.js.map +1 -1
  267. package/dist/types/agent/scheduler.d.ts +5 -0
  268. package/dist/types/agent/scheduler.d.ts.map +1 -1
  269. package/dist/types/agent/task.d.ts +10 -0
  270. package/dist/types/agent/task.d.ts.map +1 -1
  271. package/dist/types/computer-use/index.d.ts +6 -0
  272. package/dist/types/computer-use/index.d.ts.map +1 -1
  273. package/dist/types/computer-use/index.js.map +1 -1
  274. package/dist/types/errors/catalog.js +1 -1
  275. package/dist/types/errors/catalog.js.map +1 -1
  276. package/dist/types/hitl/index.d.ts +11 -1
  277. package/dist/types/hitl/index.d.ts.map +1 -1
  278. package/dist/types/hitl/index.js.map +1 -1
  279. package/dist/types/memory/index.d.ts +14 -1
  280. package/dist/types/memory/index.d.ts.map +1 -1
  281. package/dist/types/plugin/index.d.ts +7 -0
  282. package/dist/types/plugin/index.d.ts.map +1 -1
  283. package/dist/types/plugin/index.js.map +1 -1
  284. package/dist/types/project/entity.d.ts +1 -0
  285. package/dist/types/project/entity.d.ts.map +1 -1
  286. package/dist/types/provider/chat.d.ts +4 -0
  287. package/dist/types/provider/chat.d.ts.map +1 -1
  288. package/dist/types/provider/config.d.ts +4 -0
  289. package/dist/types/provider/config.d.ts.map +1 -1
  290. package/dist/types/provider/model.d.ts +4 -0
  291. package/dist/types/provider/model.d.ts.map +1 -1
  292. package/dist/types/provider/stream.d.ts +6 -0
  293. package/dist/types/provider/stream.d.ts.map +1 -1
  294. package/dist/types/run/answer-review.d.ts +2 -0
  295. package/dist/types/run/answer-review.d.ts.map +1 -1
  296. package/dist/types/run/config.d.ts +2 -0
  297. package/dist/types/run/config.d.ts.map +1 -1
  298. package/dist/types/run/events.d.ts +19 -0
  299. package/dist/types/run/events.d.ts.map +1 -1
  300. package/dist/types/run/events.js.map +1 -1
  301. package/dist/types/run/prepare-step.d.ts +19 -1
  302. package/dist/types/run/prepare-step.d.ts.map +1 -1
  303. package/dist/types/sandbox/index.d.ts +1 -0
  304. package/dist/types/sandbox/index.d.ts.map +1 -1
  305. package/dist/types/sandbox/index.js.map +1 -1
  306. package/dist/types/structured-output/index.d.ts +8 -2
  307. package/dist/types/structured-output/index.d.ts.map +1 -1
  308. package/dist/types/tool/index.d.ts +15 -0
  309. package/dist/types/tool/index.d.ts.map +1 -1
  310. package/dist/types/tool/index.js.map +1 -1
  311. package/dist/types/tool/presentation.d.ts +4 -0
  312. package/dist/types/tool/presentation.d.ts.map +1 -1
  313. package/dist/utils/json-snapshot.d.ts +3 -0
  314. package/dist/utils/json-snapshot.d.ts.map +1 -0
  315. package/dist/utils/json-snapshot.js +79 -0
  316. package/dist/utils/json-snapshot.js.map +1 -0
  317. package/package.json +4 -2
  318. package/src/bridge/a2a/mapper.ts +4 -0
  319. package/src/bridge/sse/mapper.ts +16 -1
  320. package/src/compaction/extractor.ts +21 -8
  321. package/src/compaction/manager.ts +11 -1
  322. package/src/compaction/verifier.ts +80 -18
  323. package/src/config/runtime.ts +2 -0
  324. package/src/contracts/api.ts +1 -0
  325. package/src/eval/harness-verification.ts +270 -0
  326. package/src/eval/index.ts +10 -0
  327. package/src/execution/code-runtime/json-value.ts +82 -0
  328. package/src/execution/code-runtime/worker-program.ts +239 -0
  329. package/src/execution/code-runtime/worker.ts +164 -111
  330. package/src/manager/agent/lifecycle.ts +352 -54
  331. package/src/manager/run/persistence.ts +7 -0
  332. package/src/prompt/coding-agent-doctrine.ts +11 -5
  333. package/src/provider/capabilities.ts +41 -1
  334. package/src/provider/fallback.ts +9 -2
  335. package/src/public-runtime.ts +6 -0
  336. package/src/public-tools.ts +7 -1
  337. package/src/public-types.ts +7 -0
  338. package/src/registry/tool/execute.ts +17 -79
  339. package/src/run/command-gate.ts +61 -13
  340. package/src/run/index.ts +2 -0
  341. package/src/run/memory-promoter.ts +32 -40
  342. package/src/run/memory-recall.ts +254 -0
  343. package/src/run/reporter.ts +2 -0
  344. package/src/run/token-budget.ts +55 -14
  345. package/src/run/workspace-fingerprint.ts +17 -8
  346. package/src/runtime/query/checkpoint.ts +96 -2
  347. package/src/runtime/query/executor.ts +100 -29
  348. package/src/runtime/query/index.ts +76 -16
  349. package/src/runtime/query/iteration/index.ts +405 -46
  350. package/src/runtime/query/iteration/native-output.ts +35 -0
  351. package/src/runtime/query/iteration/phases/advisory.ts +21 -11
  352. package/src/runtime/query/iteration/phases/compaction.ts +36 -14
  353. package/src/runtime/query/iteration/phases/context.ts +9 -0
  354. package/src/runtime/query/iteration/phases/tool-review.ts +5 -1
  355. package/src/runtime/query/iteration/phases/working-memory.ts +23 -5
  356. package/src/runtime/query/iteration/stream-turn.ts +15 -0
  357. package/src/runtime/query/observation-context.ts +86 -0
  358. package/src/runtime/query/request-context.ts +103 -0
  359. package/src/runtime/query/request-rich-content.ts +8 -3
  360. package/src/runtime/query/review-policy.ts +7 -1
  361. package/src/runtime/query/steering.ts +19 -2
  362. package/src/runtime/query/tool-call-budget.ts +115 -0
  363. package/src/runtime/query/tooling.ts +8 -0
  364. package/src/sandbox/file-walk-program.ts +8 -0
  365. package/src/sandbox/file-walk.ts +9 -0
  366. package/src/scheduler/completion-inbox.ts +3 -0
  367. package/src/scheduler/local.ts +1 -0
  368. package/src/session/handoff/capacity.ts +7 -3
  369. package/src/store/memory/disk.ts +52 -30
  370. package/src/store/memory/index.ts +75 -27
  371. package/src/store/memory/memory.ts +18 -6
  372. package/src/store/memory/operation-lock.ts +99 -0
  373. package/src/store/run/token-budget-disk.ts +3 -0
  374. package/src/tools/builtins/computer-use.ts +61 -7
  375. package/src/tools/builtins/edit.ts +12 -27
  376. package/src/tools/builtins/file-write-result.ts +84 -0
  377. package/src/tools/builtins/glob.ts +7 -1
  378. package/src/tools/builtins/grep.ts +10 -0
  379. package/src/tools/builtins/job.ts +8 -0
  380. package/src/tools/builtins/read-file.ts +10 -0
  381. package/src/tools/builtins/run-code.ts +24 -5
  382. package/src/tools/builtins/search-tools.ts +18 -3
  383. package/src/tools/builtins/write-file.ts +39 -44
  384. package/src/tools/defineTool.ts +5 -0
  385. package/src/tools/file-read-tracker.ts +16 -0
  386. package/src/tools/memory/delete.ts +29 -0
  387. package/src/tools/memory/index.ts +6 -0
  388. package/src/tools/memory/save.ts +4 -2
  389. package/src/tools/memory/search.ts +12 -5
  390. package/src/tools/memory/update.ts +50 -0
  391. package/src/tools/task/list.ts +2 -2
  392. package/src/types/agent/scheduler.ts +6 -0
  393. package/src/types/agent/task.ts +13 -0
  394. package/src/types/computer-use/index.ts +6 -0
  395. package/src/types/errors/catalog.ts +1 -1
  396. package/src/types/hitl/index.ts +11 -1
  397. package/src/types/memory/index.ts +16 -1
  398. package/src/types/plugin/index.ts +10 -0
  399. package/src/types/project/entity.ts +1 -0
  400. package/src/types/provider/chat.ts +2 -0
  401. package/src/types/provider/config.ts +4 -0
  402. package/src/types/provider/model.ts +4 -0
  403. package/src/types/provider/stream.ts +7 -0
  404. package/src/types/run/answer-review.ts +2 -0
  405. package/src/types/run/config.ts +2 -0
  406. package/src/types/run/events.ts +20 -0
  407. package/src/types/run/prepare-step.ts +19 -1
  408. package/src/types/sandbox/index.ts +1 -0
  409. package/src/types/structured-output/index.ts +15 -2
  410. package/src/types/tool/index.ts +16 -0
  411. package/src/types/tool/presentation.ts +4 -0
  412. package/src/utils/json-snapshot.ts +76 -0
@@ -1,4 +1,5 @@
1
1
  import { AGENT_MANAGER_DEFAULTS } from '../../constants/agent/index.js'
2
+ import { EMPTY_TOKEN_USAGE } from '../../constants/limits.js'
2
3
  import { GENAI } from '../../constants/telemetry/index.js'
3
4
  import type { AgentRegistry } from '../../registry/agent/definitions.js'
4
5
  import {
@@ -91,6 +92,18 @@ interface AgentManagerBaseDeps {
91
92
  */
92
93
  export type AgentManagerDeps = AgentManagerBaseDeps & TopicManagerDependency
93
94
 
95
+ /** Internal backpressure: the parent's unmeasured request owns its budget until its receipt. */
96
+ class ParentBudgetRequestPending extends Error {}
97
+
98
+ interface PendingSpawn {
99
+ ready: boolean
100
+ readonly task: AgentTask
101
+ readonly options: SendMessageOptions
102
+ readonly context: AgentTaskContext
103
+ readonly listener?: RunEventListener
104
+ readonly removeAbortListener: () => void
105
+ }
106
+
94
107
  interface ChildSpawnRecord {
95
108
  subSessionId: SubSessionId
96
109
  childSessionId: SessionId
@@ -141,6 +154,12 @@ export class AgentManager {
141
154
  private spawnLocks: Map<SessionId, Promise<void>> = new Map()
142
155
  private deps: AgentManagerDeps
143
156
  private topicManager: TopicManager
157
+ private readonly pendingSpawns = new Map<SessionId, PendingSpawn[]>()
158
+ private readonly drainingParents = new Set<SessionId>()
159
+ private readonly executingTasks = new Set<TaskId>()
160
+ private readonly cancelingTasks = new Set<TaskId>()
161
+ private admissionTimer: ReturnType<typeof setTimeout> | undefined
162
+ private disposed = false
144
163
 
145
164
  constructor(
146
165
  registry: AgentRegistry,
@@ -149,6 +168,9 @@ export class AgentManager {
149
168
  ) {
150
169
  this.registry = registry
151
170
  this.config = { ...AGENT_MANAGER_DEFAULTS, ...config }
171
+ const maxPending = this.config.maxPendingTasks ?? 128
172
+ if (!Number.isSafeInteger(maxPending) || maxPending < 1)
173
+ throw new Error('maxPendingTasks must be a positive safe integer')
152
174
  this.log = resolveLogger(deps.log).child({
153
175
  [SCOPE_ATTRIBUTE]: 'manager/agent/lifecycle',
154
176
  })
@@ -161,6 +183,182 @@ export class AgentManager {
161
183
  context: AgentTaskContext,
162
184
  listener?: RunEventListener,
163
185
  ): Promise<AgentTask> {
186
+ if (this.disposed) throw new Error('Agent manager is disposed')
187
+ if (this.config.capacityBehavior === 'queue')
188
+ return this.enqueueMessage(options, context, listener)
189
+ return this.startMessage(options, context, listener)
190
+ }
191
+
192
+ private async enqueueMessage(
193
+ options: SendMessageOptions,
194
+ context: AgentTaskContext,
195
+ listener?: RunEventListener,
196
+ ): Promise<AgentTask> {
197
+ context.parentAbortController.signal.throwIfAborted()
198
+ if (context.depth >= this.config.maxDepth)
199
+ throw new Error(`Max task depth ${this.config.maxDepth} exceeded`)
200
+ if (options.tenantId !== context.tenantId)
201
+ throw new Error('Tenant mismatch: cross-tenant spawn rejected')
202
+ const definition = this.registry.getOrThrow(options.agentId)
203
+ const count = [...this.pendingSpawns.values()].reduce((sum, queue) => sum + queue.length, 0)
204
+ if (count >= (this.config.maxPendingTasks ?? 128))
205
+ throw new Error(
206
+ `Delegation queue is full (${this.config.maxPendingTasks ?? 128} pending tasks). Wait for existing tasks before submitting more work.`,
207
+ )
208
+ const task: AgentTask = {
209
+ taskId: generateTaskId(),
210
+ agentId: options.agentId,
211
+ agent: definition.typedAgent,
212
+ childAbortController: createChildAbortController(context.parentAbortController),
213
+ context,
214
+ state: 'pending',
215
+ pendingMessages: [],
216
+ createdAt: Date.now(),
217
+ runEventListener: listener,
218
+ }
219
+ this.instances.set(task.taskId, task)
220
+ const onAbort = (): void => {
221
+ // Before admission this observer owns cancellation. Once child scope
222
+ // exists, startup rollback may abort its controller for a configuration
223
+ // error; that is a failure, not a user cancellation.
224
+ if (task.context === context || context.parentAbortController.signal.aborted)
225
+ this.cancel(task.taskId, 'parent')
226
+ }
227
+ task.childAbortController.signal.addEventListener('abort', onAbort, { once: true })
228
+ const entry: PendingSpawn = {
229
+ ready: false,
230
+ task,
231
+ options,
232
+ context,
233
+ listener,
234
+ removeAbortListener: () =>
235
+ task.childAbortController.signal.removeEventListener('abort', onAbort),
236
+ }
237
+ const queue = this.pendingSpawns.get(options.parentSessionId) ?? []
238
+ queue.push(entry)
239
+ this.pendingSpawns.set(options.parentSessionId, queue)
240
+ this.emit({
241
+ type: 'pending',
242
+ taskId: task.taskId,
243
+ agentId: options.agentId,
244
+ parentAgentId: context.parentAgentId,
245
+ depth: context.depth,
246
+ })
247
+ try {
248
+ await listener?.({
249
+ type: 'agent_pending',
250
+ runId: context.parentRunId,
251
+ taskId: task.taskId,
252
+ parentAgentId: context.parentAgentId,
253
+ childAgentId: options.agentId,
254
+ depth: context.depth,
255
+ })
256
+ entry.ready = true
257
+ } catch (error) {
258
+ this.failAdmission(entry, error)
259
+ }
260
+ this.pumpAdmissions(options.parentSessionId)
261
+ return task
262
+ }
263
+
264
+ private pumpAdmissions(parentSessionId: SessionId): void {
265
+ if (this.disposed || this.drainingParents.has(parentSessionId)) return
266
+ this.drainingParents.add(parentSessionId)
267
+ void this.drainAdmissions(parentSessionId)
268
+ .finally(() => {
269
+ this.drainingParents.delete(parentSessionId)
270
+ // External handoffs and metadata changes have no manager event. A
271
+ // single slow, unref'ed timer observes those without spinning or
272
+ // keeping a host alive. Actual completions wake admission directly.
273
+ if (!this.disposed && this.pendingSpawns.size > 0 && !this.admissionTimer) {
274
+ this.admissionTimer = setTimeout(() => {
275
+ this.admissionTimer = undefined
276
+ for (const parent of this.pendingSpawns.keys()) this.pumpAdmissions(parent)
277
+ }, 250)
278
+ this.admissionTimer.unref?.()
279
+ }
280
+ })
281
+ .catch((error) =>
282
+ this.log.error('Delegation admission failed', {
283
+ 'exception.message': toErrorMessage(error),
284
+ }),
285
+ )
286
+ }
287
+
288
+ private async drainAdmissions(parentSessionId: SessionId): Promise<void> {
289
+ const queue = this.pendingSpawns.get(parentSessionId)
290
+ if (!queue) return
291
+ while (!this.disposed && queue.length > 0) {
292
+ const entry = queue[0]
293
+ if (!entry) break
294
+ if (isTerminalAgentTaskState(entry.task.state)) {
295
+ entry.removeAbortListener()
296
+ queue.shift()
297
+ continue
298
+ }
299
+ // A concurrent enqueue can pump this same FIFO while the head's
300
+ // pending listener still owns its acknowledgement. Do not provision
301
+ // or spend until that acknowledgement succeeds.
302
+ if (!entry.ready) return
303
+ try {
304
+ await entry.options.beforeStart?.()
305
+ entry.task.childAbortController.signal.throwIfAborted()
306
+ await this.validateSpawn(entry.options, entry.context)
307
+ entry.task.childAbortController.signal.throwIfAborted()
308
+ await this.startMessage(entry.options, entry.context, entry.listener, entry.task)
309
+ entry.removeAbortListener()
310
+ } catch (error) {
311
+ if (
312
+ (error instanceof ParentBudgetRequestPending ||
313
+ (error instanceof DelegationCapacityExceeded && error.details.dimension === 'width')) &&
314
+ !isTerminalAgentTaskState(entry.task.state)
315
+ )
316
+ return
317
+ this.failAdmission(entry, error)
318
+ }
319
+ queue.shift()
320
+ }
321
+ if (queue.length === 0 && this.pendingSpawns.get(parentSessionId) === queue)
322
+ this.pendingSpawns.delete(parentSessionId)
323
+ }
324
+
325
+ private failAdmission(entry: PendingSpawn, reason: unknown): void {
326
+ entry.removeAbortListener()
327
+ const task = entry.task
328
+ if (isTerminalAgentTaskState(task.state)) return
329
+ task.childAbortController.abort(reason)
330
+ const error = toErrorMessage(reason)
331
+ task.result = {
332
+ runId: entry.context.parentRunId,
333
+ status: 'failed',
334
+ usage: { ...EMPTY_TOKEN_USAGE },
335
+ cost: { ...ZERO_COST },
336
+ iterations: 0,
337
+ durationMs: Date.now() - task.createdAt,
338
+ messages: [],
339
+ lastError: error,
340
+ }
341
+ task.state = 'failed'
342
+ task.completedAt = Date.now()
343
+ this.emit({ type: 'failed', taskId: task.taskId, error })
344
+ this.emitRunEvent(task, {
345
+ type: 'agent_failed',
346
+ runId: entry.context.parentRunId,
347
+ taskId: task.taskId,
348
+ error,
349
+ })
350
+ this.scheduleEviction(task.taskId)
351
+ this.resolveCompletionCallbacks(task.taskId)
352
+ }
353
+
354
+ private async startMessage(
355
+ options: SendMessageOptions,
356
+ context: AgentTaskContext,
357
+ listener?: RunEventListener,
358
+ queuedTask?: AgentTask,
359
+ ): Promise<AgentTask> {
360
+ await options.beforeStart?.()
361
+ queuedTask?.childAbortController.signal.throwIfAborted()
164
362
  if (context.depth >= this.config.maxDepth) {
165
363
  throw new Error(
166
364
  `Max task depth ${this.config.maxDepth} exceeded (current: ${context.depth}). Recursive agent delegation is limited to prevent resource exhaustion.`,
@@ -200,23 +398,53 @@ export class AgentManager {
200
398
  // Reserve synchronously before provisioning yields. The authority is shared
201
399
  // across managers and nested sessions, while the session lock below only
202
400
  // serializes filesystem capacity checks.
401
+ // Queued siblings share the available budget with the parent. The first
402
+ // of eight children receives at most one ninth; later siblings receive
403
+ // comparable grants instead of a geometric 1/2, 1/4, ... starvation tail.
404
+ let budgetShares = 1
405
+ if (queuedTask) {
406
+ const project = await requireOpenProject(
407
+ this.deps.sessionStore,
408
+ context.projectId,
409
+ context.tenantId,
410
+ 'spawn',
411
+ )
412
+ const children = await this.deps.sessionStore.getChildren(
413
+ options.parentSessionId,
414
+ context.tenantId,
415
+ )
416
+ const active = children.filter(
417
+ (child) =>
418
+ child.status !== 'idle' && child.status !== 'failed' && child.status !== 'archived',
419
+ ).length
420
+ budgetShares = Math.max(2, project.config.maxDelegationWidth - active + 1)
421
+ }
422
+ context.parentAbortController.signal.throwIfAborted()
423
+ queuedTask?.childAbortController.signal.throwIfAborted()
203
424
  const remaining = context.budget.remaining
204
425
  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)))
426
+ ? Math.floor(Math.min(remaining * this.config.maxBudgetFraction, remaining / budgetShares))
427
+ : (options.budgetAllocation?.tokenBudget ?? options.configOverrides?.tokenBudget ?? 200_000)
210
428
  const allocatedTokens = Math.min(
211
429
  options.budgetAllocation?.tokenBudget ?? maxAllocation,
212
430
  maxAllocation,
213
431
  )
214
- if (!Number.isSafeInteger(allocatedTokens) || allocatedTokens <= 0) {
432
+ if (
433
+ !Number.isSafeInteger(allocatedTokens) ||
434
+ allocatedTokens < 0 ||
435
+ (allocatedTokens === 0 && Number.isFinite(remaining))
436
+ ) {
215
437
  throw new NamzuError({
216
438
  code: 'invalid_config',
217
- message: `Cannot spawn "${options.agentId}": the parent has ${remaining} tokens remaining; a child allocation must be a finite positive integer.`,
439
+ message: `Cannot spawn "${options.agentId}": the parent has ${remaining} tokens remaining; a child allocation must be a finite positive integer, or zero under an unlimited parent budget.`,
218
440
  })
219
441
  }
442
+ // No await between this check and reserve: a parent's response may start
443
+ // during the scope reads above. Its own in-flight request is temporary
444
+ // backpressure; descendant requests are expected during a fan-out.
445
+ // Invalid/zero allocations above still fail rather than waiting forever.
446
+ if (queuedTask && context.budget.hasInFlightRequest)
447
+ throw new ParentBudgetRequestPending('The parent provider request is still in flight')
220
448
  const childBudget = context.budget.reserve(allocatedTokens)
221
449
  let spawnRecord: ChildSpawnRecord
222
450
  try {
@@ -230,9 +458,12 @@ export class AgentManager {
230
458
  let childAbortController: AbortController | undefined
231
459
  let agentTask: AgentTask | undefined
232
460
  try {
233
- childAbortController = createChildAbortController(context.parentAbortController)
461
+ childAbortController =
462
+ queuedTask?.childAbortController ??
463
+ createChildAbortController(context.parentAbortController)
234
464
 
235
- const taskId = generateTaskId()
465
+ childAbortController.signal.throwIfAborted()
466
+ const taskId = queuedTask?.taskId ?? generateTaskId()
236
467
 
237
468
  const childParentActor: ActorRef = {
238
469
  kind: 'agent',
@@ -279,40 +510,42 @@ export class AgentManager {
279
510
  ...(resolvedDenies.length > 0 ? { toolDenies: resolvedDenies } : {}),
280
511
  }
281
512
 
282
- agentTask = {
513
+ agentTask = Object.assign(queuedTask ?? {}, {
283
514
  taskId,
284
515
  agentId: options.agentId,
285
516
  agent,
286
517
  childAbortController,
287
518
  context: childContext,
288
519
  state: 'pending',
289
- pendingMessages: [],
290
- createdAt: Date.now(),
520
+ pendingMessages: queuedTask?.pendingMessages ?? [],
521
+ createdAt: queuedTask?.createdAt ?? Date.now(),
291
522
  runEventListener: listener,
292
- }
523
+ } satisfies AgentTask)
293
524
 
294
525
  childAbortController.signal.throwIfAborted()
295
526
  this.instances.set(taskId, agentTask)
296
527
  if (resolvedDenies.length > 0) spawnRecord.resolvedToolDenies = resolvedDenies
297
528
  this.spawnRecords.set(taskId, spawnRecord)
298
- this.emit({
299
- type: 'pending',
300
- taskId,
301
- agentId: options.agentId,
302
- parentAgentId: context.parentAgentId,
303
- depth: context.depth,
304
- })
305
-
306
- if (listener) {
307
- await listener({
308
- type: 'agent_pending',
309
- runId: context.parentRunId,
529
+ if (!queuedTask)
530
+ this.emit({
531
+ type: 'pending',
310
532
  taskId,
533
+ agentId: options.agentId,
311
534
  parentAgentId: context.parentAgentId,
312
- childAgentId: options.agentId,
313
535
  depth: context.depth,
314
536
  })
315
537
 
538
+ if (listener) {
539
+ if (!queuedTask)
540
+ await listener({
541
+ type: 'agent_pending',
542
+ runId: context.parentRunId,
543
+ taskId,
544
+ parentAgentId: context.parentAgentId,
545
+ childAgentId: options.agentId,
546
+ depth: context.depth,
547
+ })
548
+
316
549
  const lineage: Lineage = {
317
550
  parentSessionId: spawnRecord.parentSessionId,
318
551
  rootSessionId: spawnRecord.rootSessionId,
@@ -454,9 +687,11 @@ export class AgentManager {
454
687
  })
455
688
  }
456
689
  childConfig.tokenBudget =
457
- childConfig.tokenBudget === 0
458
- ? allocatedTokens
459
- : Math.min(childConfig.tokenBudget, allocatedTokens)
690
+ allocatedTokens === 0
691
+ ? childConfig.tokenBudget
692
+ : childConfig.tokenBudget === 0
693
+ ? allocatedTokens
694
+ : Math.min(childConfig.tokenBudget, allocatedTokens)
460
695
  childBudget.narrow(childConfig.tokenBudget)
461
696
 
462
697
  // Stamped AFTER the builder, for the fourth time and the same reason:
@@ -503,17 +738,35 @@ export class AgentManager {
503
738
 
504
739
  await childBudget.flush()
505
740
  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))
516
- })
741
+ this.executingTasks.add(taskId)
742
+ const runningTask = agentTask
743
+ this.runChild(runningTask, options, childConfig, listener)
744
+ .catch(async (err) => {
745
+ // A thrown invocation supplied no final usage receipt. Keep its
746
+ // reservation, including when its task handle was canceled or evicted.
747
+ let failure = err
748
+ try {
749
+ await childBudget.flush()
750
+ } catch (writeError) {
751
+ failure = writeError
752
+ }
753
+ // Cancellation may already have published a terminal handle. Its
754
+ // invocation has only NOW stopped; release the persisted edge here.
755
+ if (!this.instances.has(taskId) || isTerminalAgentTaskState(runningTask.state)) {
756
+ await this.failSubSession(spawnRecord)
757
+ } else this.markFailed(taskId, toErrorMessage(failure))
758
+ })
759
+ .finally(() => {
760
+ this.executingTasks.delete(taskId)
761
+ if (!this.instances.has(taskId)) this.spawnRecords.delete(taskId)
762
+ this.pumpAdmissions(options.parentSessionId)
763
+ })
764
+ .catch((error) =>
765
+ this.log.warn('Child cleanup failed', {
766
+ 'namzu.task.id': taskId,
767
+ 'exception.message': toErrorMessage(error),
768
+ }),
769
+ )
517
770
 
518
771
  return agentTask
519
772
  } catch (err) {
@@ -521,7 +774,7 @@ export class AgentManager {
521
774
  // provisioned resources are still ours to return. A builder/listener
522
775
  // failure must not strand a pending task the caller never received.
523
776
  if (agentTask) {
524
- await this.rollbackUnstartedSpawn(agentTask, spawnRecord, err)
777
+ await this.rollbackUnstartedSpawn(agentTask, spawnRecord, err, queuedTask !== undefined)
525
778
  } else {
526
779
  childAbortController?.abort()
527
780
  childBudget.settle(0)
@@ -536,6 +789,7 @@ export class AgentManager {
536
789
  agentTask: AgentTask,
537
790
  spawnRecord: ChildSpawnRecord,
538
791
  reason: unknown,
792
+ retainHandle = false,
539
793
  ): Promise<void> {
540
794
  agentTask.childAbortController.abort()
541
795
  agentTask.context.budget.settle(0)
@@ -559,7 +813,20 @@ export class AgentManager {
559
813
  }
560
814
  }
561
815
  this.clearEvictionTimer(agentTask.taskId)
562
- this.instances.delete(agentTask.taskId)
816
+ if (retainHandle) {
817
+ agentTask.result ??= {
818
+ runId: agentTask.context.parentRunId,
819
+ status: 'failed',
820
+ usage: { ...EMPTY_TOKEN_USAGE },
821
+ cost: { ...ZERO_COST },
822
+ iterations: 0,
823
+ durationMs: Date.now() - agentTask.createdAt,
824
+ messages: [],
825
+ lastError: toErrorMessage(reason),
826
+ }
827
+ agentTask.completedAt = Date.now()
828
+ this.scheduleEviction(agentTask.taskId)
829
+ } else this.instances.delete(agentTask.taskId)
563
830
  this.spawnRecords.delete(agentTask.taskId)
564
831
  this.resolveCompletionCallbacks(agentTask.taskId)
565
832
  await this.rollbackSpawnResources(spawnRecord)
@@ -581,14 +848,22 @@ export class AgentManager {
581
848
  }
582
849
 
583
850
  cancel(taskId: TaskId, cause?: CancelCause): void {
851
+ if (this.cancelingTasks.has(taskId)) return
584
852
  const agentTask = this.instances.get(taskId)
585
853
  if (!agentTask || isTerminalAgentTaskState(agentTask.state)) return
586
854
 
587
855
  // Was the bare string `'canceled'`, which `abortReasonText` suppresses
588
856
  // by name — its docblock cites this exact call site — so the child's
589
857
  // run saw a cancellation with no attributable origin at all.
590
- agentTask.childAbortController.abort(cause ? new RunCancelled(cause) : undefined)
591
- this.markCanceled(taskId, cause)
858
+ // Abort listeners run synchronously. A pending task's parent observer
859
+ // must not reenter this method and replace an explicit user's cause.
860
+ this.cancelingTasks.add(taskId)
861
+ try {
862
+ agentTask.childAbortController.abort(cause ? new RunCancelled(cause) : undefined)
863
+ this.markCanceled(taskId, cause)
864
+ } finally {
865
+ this.cancelingTasks.delete(taskId)
866
+ }
592
867
  }
593
868
 
594
869
  cancelAll(parentRunId: RunId, cause: CancelCause = 'parent'): void {
@@ -687,12 +962,18 @@ export class AgentManager {
687
962
  if (isTerminalAgentTaskState(agentTask.state)) {
688
963
  this.clearEvictionTimer(taskId)
689
964
  this.instances.delete(taskId)
690
- this.spawnRecords.delete(taskId)
965
+ if (!this.executingTasks.has(taskId)) this.spawnRecords.delete(taskId)
691
966
  }
692
967
  }
693
968
  }
694
969
 
695
970
  dispose(): void {
971
+ this.disposed = true
972
+ if (this.admissionTimer) clearTimeout(this.admissionTimer)
973
+ this.admissionTimer = undefined
974
+ for (const queue of this.pendingSpawns.values())
975
+ for (const entry of queue) entry.removeAbortListener()
976
+ this.pendingSpawns.clear()
696
977
  for (const taskId of this.instances.keys()) {
697
978
  this.clearEvictionTimer(taskId)
698
979
  }
@@ -704,7 +985,8 @@ export class AgentManager {
704
985
  this.cancel(taskId)
705
986
  }
706
987
  this.instances.clear()
707
- this.spawnRecords.clear()
988
+ for (const taskId of this.spawnRecords.keys())
989
+ if (!this.executingTasks.has(taskId)) this.spawnRecords.delete(taskId)
708
990
  this.listeners.length = 0
709
991
  }
710
992
 
@@ -748,10 +1030,10 @@ export class AgentManager {
748
1030
  }
749
1031
  }
750
1032
 
751
- private async provisionSpawnUnlocked(
1033
+ private async validateSpawn(
752
1034
  options: SendMessageOptions,
753
1035
  context: AgentTaskContext,
754
- ): Promise<ChildSpawnRecord> {
1036
+ ): Promise<void> {
755
1037
  // Phase 9: deps are unconditional required. Every spawn produces a
756
1038
  // SubSession + Session + WorkspaceRef triple (Convention #0: no
757
1039
  // partial/legacy path).
@@ -782,6 +1064,8 @@ export class AgentManager {
782
1064
  `Parent session ${options.parentSessionId} not found for tenant ${context.tenantId} — spawn rejected`,
783
1065
  )
784
1066
  }
1067
+ if (parentSession.status === 'archived')
1068
+ throw new Error('Delegation parent session is archived')
785
1069
  if (parentSession.topicId !== context.topicId) {
786
1070
  throw new Error(
787
1071
  `Topic mismatch on spawn: parent session ${parentSession.id} is on topic ${parentSession.topicId}, but context.topicId=${context.topicId}. Cross-topic spawn is forbidden (session-hierarchy.md §6.3).`,
@@ -812,6 +1096,15 @@ export class AgentManager {
812
1096
  project.config.maxDelegationWidth,
813
1097
  context.tenantId,
814
1098
  )
1099
+ }
1100
+
1101
+ private async provisionSpawnUnlocked(
1102
+ options: SendMessageOptions,
1103
+ context: AgentTaskContext,
1104
+ ): Promise<ChildSpawnRecord> {
1105
+ await this.validateSpawn(options, context)
1106
+ context.parentAbortController.signal.throwIfAborted()
1107
+ const store = this.deps.sessionStore
815
1108
 
816
1109
  // Ancestry walk gives both the child depth and the root session id
817
1110
  // attached to every sub-session event from here down.
@@ -1228,6 +1521,7 @@ export class AgentManager {
1228
1521
  }
1229
1522
 
1230
1523
  private scheduleEviction(taskId: TaskId): void {
1524
+ if (this.disposed) return
1231
1525
  const agentTask = this.instances.get(taskId)
1232
1526
  if (!agentTask) return
1233
1527
 
@@ -1235,7 +1529,7 @@ export class AgentManager {
1235
1529
 
1236
1530
  const timer = setTimeout(() => {
1237
1531
  this.instances.delete(taskId)
1238
- this.spawnRecords.delete(taskId)
1532
+ if (!this.executingTasks.has(taskId)) this.spawnRecords.delete(taskId)
1239
1533
  this.evictionTimers.delete(taskId)
1240
1534
  this.log.info('Agent task evicted', { 'namzu.agent.task_id': taskId })
1241
1535
  }, this.config.evictionMs)
@@ -1274,15 +1568,19 @@ export class AgentManager {
1274
1568
 
1275
1569
  private emitRunEvent(agentTask: AgentTask, event: RunEvent): void {
1276
1570
  if (!agentTask.runEventListener) return
1277
- try {
1278
- agentTask.runEventListener(event)
1279
- } catch (err) {
1571
+ const reportFailure = (error: unknown): void => {
1280
1572
  this.log.error('RunEvent emission error', {
1281
1573
  'namzu.event.type': event.type,
1282
- 'namzu.task.id': agentTask.taskId,
1283
- 'exception.message': toErrorMessage(err),
1574
+ 'exception.message': toErrorMessage(error),
1284
1575
  })
1285
1576
  }
1577
+ try {
1578
+ // Terminal observation must not delay completion or leak a rejected
1579
+ // listener promise into the host as an unhandled rejection.
1580
+ void Promise.resolve(agentTask.runEventListener(event)).catch(reportFailure)
1581
+ } catch (error) {
1582
+ reportFailure(error)
1583
+ }
1286
1584
  }
1287
1585
  }
1288
1586
 
@@ -306,6 +306,7 @@ export class RunPersistence {
306
306
  }
307
307
 
308
308
  markFailed(error: string, providerError?: ProviderErrorInfo): void {
309
+ this.clearStructuredOutput()
309
310
  this.run.status = 'failed'
310
311
  this.run.stopReason = 'error'
311
312
  this.run.lastError = error
@@ -314,6 +315,7 @@ export class RunPersistence {
314
315
  }
315
316
 
316
317
  markCancelled(): void {
318
+ this.clearStructuredOutput()
317
319
  this.run.status = 'cancelled'
318
320
  this.run.stopReason = 'cancelled'
319
321
  this.run.endedAt = Date.now()
@@ -459,6 +461,11 @@ export class RunPersistence {
459
461
  * and without the override flag it would walk the tail and put the stale
460
462
  * prose back.
461
463
  */
464
+ /** Invalidate a structured value without replacing the host's textual result. */
465
+ clearStructuredOutput(): void {
466
+ delete this.run.structuredOutput
467
+ }
468
+
462
469
  setStructuredOutput(value: unknown): void {
463
470
  this.run.structuredOutput = value
464
471
  this.setResult(typeof value === 'string' ? value : JSON.stringify(value))
@@ -49,11 +49,17 @@ export const CODING_AGENT_WORKING_DOCTRINE = `## How you work
49
49
  - Reference code as \`path:line\` so the user can jump to it.
50
50
  - Correct an earlier statement only when the error would change the user's code, conclusions or decisions; state it once, plainly, and continue. No apologies, no tally of past mistakes.
51
51
 
52
+ ### Using available evidence
53
+ - Carry the active task and established facts across turns. A question or status request during ongoing work does not cancel that work unless the user says so. Ask for clarification when the intended change is genuinely unspecified; do not ask the user to repeat information already available.
54
+ - Treat a successful tool result together with its exact inputs as evidence of what happened. Content you successfully wrote is available evidence, just like content you read. An announced action, proposed call, failed call, or another agent's unsupported claim is not proof of completion.
55
+ - Reuse relevant evidence still available in conversation instead of observing it again by habit. Distinguish the last observed state from a guarantee that external state has not changed. Re-observe when evidence is missing, partial or invalidated, when another actor may have changed the target, or when freshness matters to the next action. If a check or exact-match edit reports a mismatch, inspect the current state and revise the action; do not force the old plan through.
56
+ - When an observation is needed, ask the narrowest question that resolves the uncertainty. For a known file, inspect that file rather than listing the whole working directory. Recover missing observations with read-only tools, not by repeating a state-changing action.
57
+
52
58
  ### Reading and editing code
53
- - Read a file before you edit it, and read enough of the surrounding code to match its comment density, naming and idiom. Code that reads like the file it lives in is the goal; code that reads like a different author is a defect.
54
- - Prefer the dedicated tools over shell equivalents: \`read\` over \`cat\`, the \`grep\` tool over running \`grep\` or \`rg\` through bash, \`glob\` over \`find\` or \`ls -R\`, \`edit\` over \`sed -i\`. To change an existing file use \`edit\`; use \`write\` only to create a file or when the user asked for a whole-file rewrite. The dedicated tools are bounded, previewable and permission-aware; a shell command is none of those.
55
- - Independent tool calls go in one response. Reading three files, or running a grep and a glob, does not need three turns. Tools that mutate the workspace — \`bash\`, \`edit\`, \`write\` — are applied one after another even when you emit them together; read-only tools run in parallel.
56
- - After a change, run the checks that would catch a mistake in it — the package's tests, typecheck, lint — before you report it done. A change you did not verify is a change you are guessing about.
59
+ - Before editing, use the relevant file content already available from a successful read or write; if it is unavailable or may be stale, read the needed portion. Read additional surrounding code when needed to match its naming and idiom. Respect project instructions and tool-specific prerequisites even when they require a fresh read.
60
+ - Prefer the dedicated tools over shell equivalents: \`read\` over \`cat\`, the \`grep\` tool over running \`grep\` or \`rg\` through \`bash\`, \`glob\` over \`find\` or \`ls -R\`, \`edit\` over \`sed -i\`. To change an existing file use \`edit\`; use \`write\` only to create a file or when the user asked for a whole-file rewrite. Prefer their structured inputs and focused results; use shell commands when the task needs them, within the same configured permissions.
61
+ - Independent tool calls go in one response. Reading three files, or running a grep and a glob, does not need three turns. Only group independent operations. Wait for a mutation to finish before an observation or another action that depends on it; runtime ordering and concurrency limits remain authoritative.
62
+ - Match verification to the task and its risk. For a simple text-file operation, use the successful tool result and known content; do not run unrelated project tests or repeat a read merely to restate that result. For code changes, run the relevant tests, typecheck or lint that would catch a mistake, and follow required project checks. Never claim checks you did not run.
57
63
  - Never leave the working tree in a state you have not described. If you created scratch files, say where; if you touched files outside the request, say which.
58
64
 
59
65
  ### Working with git
@@ -72,7 +78,7 @@ export const CODING_AGENT_WORKING_DOCTRINE = `## How you work
72
78
  */
73
79
  export const CODING_AGENT_DELEGATION_DOCTRINE = `### Planning and delegating
74
80
  - For work that is genuinely multi-step — several files, several distinct stages, anything you would write a checklist for — open a task list with \`task_create\` and keep it current with \`task_update\`, marking each step done when it is done rather than at the end. Do not open one for a single inspect-edit-test cycle; the list is for the user to follow, and a list of one item tells them nothing.
75
- - When the \`Agent\` tool is available, delegate genuinely independent work through it and give each delegation a short, specific description; the user watches those descriptions, not the prompts. Each sub-agent starts with no context, so the prompt must carry everything it needs.
81
+ - When the \`Agent\` tool is available, delegate genuinely independent work through it and give each delegation a short, specific description; the user watches those descriptions, not the prompts. Give each sub-agent the objective, relevant context and completion criteria; do not assume it sees context the delegation interface did not supply.
76
82
  - Delegate lookups — where something is defined, which files reference it, how a module works — with \`subagent_type: "explore"\`: it has reading and searching tools only and never interrupts the user for permission. Reserve the default sub-agent for work that changes files or runs commands.`
77
83
 
78
84
  /**