@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 { DelegationCapacityExceeded, } from '../../session/handoff/capacity.js';
4
5
  import { isTerminalAgentTaskState } from '../../types/agent/task.js';
@@ -13,6 +14,9 @@ import { SCOPE_ATTRIBUTE } from '../../utils/log/types.js';
13
14
  import { resolveLogger } from '../../utils/logger.js';
14
15
  import { requireOpenProject } from '../project/lifecycle.js';
15
16
  import { resolveTopicManager } from '../topic/dependency.js';
17
+ /** Internal backpressure: the parent's unmeasured request owns its budget until its receipt. */
18
+ class ParentBudgetRequestPending extends Error {
19
+ }
16
20
  /**
17
21
  * Combine a child's own environment with what its parent passed down.
18
22
  *
@@ -42,9 +46,18 @@ export class AgentManager {
42
46
  spawnLocks = new Map();
43
47
  deps;
44
48
  topicManager;
49
+ pendingSpawns = new Map();
50
+ drainingParents = new Set();
51
+ executingTasks = new Set();
52
+ cancelingTasks = new Set();
53
+ admissionTimer;
54
+ disposed = false;
45
55
  constructor(registry, config, deps) {
46
56
  this.registry = registry;
47
57
  this.config = { ...AGENT_MANAGER_DEFAULTS, ...config };
58
+ const maxPending = this.config.maxPendingTasks ?? 128;
59
+ if (!Number.isSafeInteger(maxPending) || maxPending < 1)
60
+ throw new Error('maxPendingTasks must be a positive safe integer');
48
61
  this.log = resolveLogger(deps.log).child({
49
62
  [SCOPE_ATTRIBUTE]: 'manager/agent/lifecycle',
50
63
  });
@@ -52,6 +65,170 @@ export class AgentManager {
52
65
  this.topicManager = resolveTopicManager(deps);
53
66
  }
54
67
  async sendMessage(options, context, listener) {
68
+ if (this.disposed)
69
+ throw new Error('Agent manager is disposed');
70
+ if (this.config.capacityBehavior === 'queue')
71
+ return this.enqueueMessage(options, context, listener);
72
+ return this.startMessage(options, context, listener);
73
+ }
74
+ async enqueueMessage(options, context, listener) {
75
+ context.parentAbortController.signal.throwIfAborted();
76
+ if (context.depth >= this.config.maxDepth)
77
+ throw new Error(`Max task depth ${this.config.maxDepth} exceeded`);
78
+ if (options.tenantId !== context.tenantId)
79
+ throw new Error('Tenant mismatch: cross-tenant spawn rejected');
80
+ const definition = this.registry.getOrThrow(options.agentId);
81
+ const count = [...this.pendingSpawns.values()].reduce((sum, queue) => sum + queue.length, 0);
82
+ if (count >= (this.config.maxPendingTasks ?? 128))
83
+ throw new Error(`Delegation queue is full (${this.config.maxPendingTasks ?? 128} pending tasks). Wait for existing tasks before submitting more work.`);
84
+ const task = {
85
+ taskId: generateTaskId(),
86
+ agentId: options.agentId,
87
+ agent: definition.typedAgent,
88
+ childAbortController: createChildAbortController(context.parentAbortController),
89
+ context,
90
+ state: 'pending',
91
+ pendingMessages: [],
92
+ createdAt: Date.now(),
93
+ runEventListener: listener,
94
+ };
95
+ this.instances.set(task.taskId, task);
96
+ const onAbort = () => {
97
+ // Before admission this observer owns cancellation. Once child scope
98
+ // exists, startup rollback may abort its controller for a configuration
99
+ // error; that is a failure, not a user cancellation.
100
+ if (task.context === context || context.parentAbortController.signal.aborted)
101
+ this.cancel(task.taskId, 'parent');
102
+ };
103
+ task.childAbortController.signal.addEventListener('abort', onAbort, { once: true });
104
+ const entry = {
105
+ ready: false,
106
+ task,
107
+ options,
108
+ context,
109
+ listener,
110
+ removeAbortListener: () => task.childAbortController.signal.removeEventListener('abort', onAbort),
111
+ };
112
+ const queue = this.pendingSpawns.get(options.parentSessionId) ?? [];
113
+ queue.push(entry);
114
+ this.pendingSpawns.set(options.parentSessionId, queue);
115
+ this.emit({
116
+ type: 'pending',
117
+ taskId: task.taskId,
118
+ agentId: options.agentId,
119
+ parentAgentId: context.parentAgentId,
120
+ depth: context.depth,
121
+ });
122
+ try {
123
+ await listener?.({
124
+ type: 'agent_pending',
125
+ runId: context.parentRunId,
126
+ taskId: task.taskId,
127
+ parentAgentId: context.parentAgentId,
128
+ childAgentId: options.agentId,
129
+ depth: context.depth,
130
+ });
131
+ entry.ready = true;
132
+ }
133
+ catch (error) {
134
+ this.failAdmission(entry, error);
135
+ }
136
+ this.pumpAdmissions(options.parentSessionId);
137
+ return task;
138
+ }
139
+ pumpAdmissions(parentSessionId) {
140
+ if (this.disposed || this.drainingParents.has(parentSessionId))
141
+ return;
142
+ this.drainingParents.add(parentSessionId);
143
+ void this.drainAdmissions(parentSessionId)
144
+ .finally(() => {
145
+ this.drainingParents.delete(parentSessionId);
146
+ // External handoffs and metadata changes have no manager event. A
147
+ // single slow, unref'ed timer observes those without spinning or
148
+ // keeping a host alive. Actual completions wake admission directly.
149
+ if (!this.disposed && this.pendingSpawns.size > 0 && !this.admissionTimer) {
150
+ this.admissionTimer = setTimeout(() => {
151
+ this.admissionTimer = undefined;
152
+ for (const parent of this.pendingSpawns.keys())
153
+ this.pumpAdmissions(parent);
154
+ }, 250);
155
+ this.admissionTimer.unref?.();
156
+ }
157
+ })
158
+ .catch((error) => this.log.error('Delegation admission failed', {
159
+ 'exception.message': toErrorMessage(error),
160
+ }));
161
+ }
162
+ async drainAdmissions(parentSessionId) {
163
+ const queue = this.pendingSpawns.get(parentSessionId);
164
+ if (!queue)
165
+ return;
166
+ while (!this.disposed && queue.length > 0) {
167
+ const entry = queue[0];
168
+ if (!entry)
169
+ break;
170
+ if (isTerminalAgentTaskState(entry.task.state)) {
171
+ entry.removeAbortListener();
172
+ queue.shift();
173
+ continue;
174
+ }
175
+ // A concurrent enqueue can pump this same FIFO while the head's
176
+ // pending listener still owns its acknowledgement. Do not provision
177
+ // or spend until that acknowledgement succeeds.
178
+ if (!entry.ready)
179
+ return;
180
+ try {
181
+ await entry.options.beforeStart?.();
182
+ entry.task.childAbortController.signal.throwIfAborted();
183
+ await this.validateSpawn(entry.options, entry.context);
184
+ entry.task.childAbortController.signal.throwIfAborted();
185
+ await this.startMessage(entry.options, entry.context, entry.listener, entry.task);
186
+ entry.removeAbortListener();
187
+ }
188
+ catch (error) {
189
+ if ((error instanceof ParentBudgetRequestPending ||
190
+ (error instanceof DelegationCapacityExceeded && error.details.dimension === 'width')) &&
191
+ !isTerminalAgentTaskState(entry.task.state))
192
+ return;
193
+ this.failAdmission(entry, error);
194
+ }
195
+ queue.shift();
196
+ }
197
+ if (queue.length === 0 && this.pendingSpawns.get(parentSessionId) === queue)
198
+ this.pendingSpawns.delete(parentSessionId);
199
+ }
200
+ failAdmission(entry, reason) {
201
+ entry.removeAbortListener();
202
+ const task = entry.task;
203
+ if (isTerminalAgentTaskState(task.state))
204
+ return;
205
+ task.childAbortController.abort(reason);
206
+ const error = toErrorMessage(reason);
207
+ task.result = {
208
+ runId: entry.context.parentRunId,
209
+ status: 'failed',
210
+ usage: { ...EMPTY_TOKEN_USAGE },
211
+ cost: { ...ZERO_COST },
212
+ iterations: 0,
213
+ durationMs: Date.now() - task.createdAt,
214
+ messages: [],
215
+ lastError: error,
216
+ };
217
+ task.state = 'failed';
218
+ task.completedAt = Date.now();
219
+ this.emit({ type: 'failed', taskId: task.taskId, error });
220
+ this.emitRunEvent(task, {
221
+ type: 'agent_failed',
222
+ runId: entry.context.parentRunId,
223
+ taskId: task.taskId,
224
+ error,
225
+ });
226
+ this.scheduleEviction(task.taskId);
227
+ this.resolveCompletionCallbacks(task.taskId);
228
+ }
229
+ async startMessage(options, context, listener, queuedTask) {
230
+ await options.beforeStart?.();
231
+ queuedTask?.childAbortController.signal.throwIfAborted();
55
232
  if (context.depth >= this.config.maxDepth) {
56
233
  throw new Error(`Max task depth ${this.config.maxDepth} exceeded (current: ${context.depth}). Recursive agent delegation is limited to prevent resource exhaustion.`);
57
234
  }
@@ -83,20 +260,37 @@ export class AgentManager {
83
260
  // Reserve synchronously before provisioning yields. The authority is shared
84
261
  // across managers and nested sessions, while the session lock below only
85
262
  // serializes filesystem capacity checks.
263
+ // Queued siblings share the available budget with the parent. The first
264
+ // of eight children receives at most one ninth; later siblings receive
265
+ // comparable grants instead of a geometric 1/2, 1/4, ... starvation tail.
266
+ let budgetShares = 1;
267
+ if (queuedTask) {
268
+ const project = await requireOpenProject(this.deps.sessionStore, context.projectId, context.tenantId, 'spawn');
269
+ const children = await this.deps.sessionStore.getChildren(options.parentSessionId, context.tenantId);
270
+ const active = children.filter((child) => child.status !== 'idle' && child.status !== 'failed' && child.status !== 'archived').length;
271
+ budgetShares = Math.max(2, project.config.maxDelegationWidth - active + 1);
272
+ }
273
+ context.parentAbortController.signal.throwIfAborted();
274
+ queuedTask?.childAbortController.signal.throwIfAborted();
86
275
  const remaining = context.budget.remaining;
87
276
  const maxAllocation = Number.isFinite(remaining)
88
- ? Math.floor(remaining * this.config.maxBudgetFraction)
89
- : (options.budgetAllocation?.tokenBudget ??
90
- (options.configOverrides?.tokenBudget === 0
91
- ? 200_000
92
- : (options.configOverrides?.tokenBudget ?? 200_000)));
277
+ ? Math.floor(Math.min(remaining * this.config.maxBudgetFraction, remaining / budgetShares))
278
+ : (options.budgetAllocation?.tokenBudget ?? options.configOverrides?.tokenBudget ?? 200_000);
93
279
  const allocatedTokens = Math.min(options.budgetAllocation?.tokenBudget ?? maxAllocation, maxAllocation);
94
- if (!Number.isSafeInteger(allocatedTokens) || allocatedTokens <= 0) {
280
+ if (!Number.isSafeInteger(allocatedTokens) ||
281
+ allocatedTokens < 0 ||
282
+ (allocatedTokens === 0 && Number.isFinite(remaining))) {
95
283
  throw new NamzuError({
96
284
  code: 'invalid_config',
97
- message: `Cannot spawn "${options.agentId}": the parent has ${remaining} tokens remaining; a child allocation must be a finite positive integer.`,
285
+ 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.`,
98
286
  });
99
287
  }
288
+ // No await between this check and reserve: a parent's response may start
289
+ // during the scope reads above. Its own in-flight request is temporary
290
+ // backpressure; descendant requests are expected during a fan-out.
291
+ // Invalid/zero allocations above still fail rather than waiting forever.
292
+ if (queuedTask && context.budget.hasInFlightRequest)
293
+ throw new ParentBudgetRequestPending('The parent provider request is still in flight');
100
294
  const childBudget = context.budget.reserve(allocatedTokens);
101
295
  let spawnRecord;
102
296
  try {
@@ -111,8 +305,11 @@ export class AgentManager {
111
305
  let childAbortController;
112
306
  let agentTask;
113
307
  try {
114
- childAbortController = createChildAbortController(context.parentAbortController);
115
- const taskId = generateTaskId();
308
+ childAbortController =
309
+ queuedTask?.childAbortController ??
310
+ createChildAbortController(context.parentAbortController);
311
+ childAbortController.signal.throwIfAborted();
312
+ const taskId = queuedTask?.taskId ?? generateTaskId();
116
313
  const childParentActor = {
117
314
  kind: 'agent',
118
315
  agentId: context.parentAgentId,
@@ -155,38 +352,40 @@ export class AgentManager {
155
352
  parentActor: childParentActor,
156
353
  ...(resolvedDenies.length > 0 ? { toolDenies: resolvedDenies } : {}),
157
354
  };
158
- agentTask = {
355
+ agentTask = Object.assign(queuedTask ?? {}, {
159
356
  taskId,
160
357
  agentId: options.agentId,
161
358
  agent,
162
359
  childAbortController,
163
360
  context: childContext,
164
361
  state: 'pending',
165
- pendingMessages: [],
166
- createdAt: Date.now(),
362
+ pendingMessages: queuedTask?.pendingMessages ?? [],
363
+ createdAt: queuedTask?.createdAt ?? Date.now(),
167
364
  runEventListener: listener,
168
- };
365
+ });
169
366
  childAbortController.signal.throwIfAborted();
170
367
  this.instances.set(taskId, agentTask);
171
368
  if (resolvedDenies.length > 0)
172
369
  spawnRecord.resolvedToolDenies = resolvedDenies;
173
370
  this.spawnRecords.set(taskId, spawnRecord);
174
- this.emit({
175
- type: 'pending',
176
- taskId,
177
- agentId: options.agentId,
178
- parentAgentId: context.parentAgentId,
179
- depth: context.depth,
180
- });
181
- if (listener) {
182
- await listener({
183
- type: 'agent_pending',
184
- runId: context.parentRunId,
371
+ if (!queuedTask)
372
+ this.emit({
373
+ type: 'pending',
185
374
  taskId,
375
+ agentId: options.agentId,
186
376
  parentAgentId: context.parentAgentId,
187
- childAgentId: options.agentId,
188
377
  depth: context.depth,
189
378
  });
379
+ if (listener) {
380
+ if (!queuedTask)
381
+ await listener({
382
+ type: 'agent_pending',
383
+ runId: context.parentRunId,
384
+ taskId,
385
+ parentAgentId: context.parentAgentId,
386
+ childAgentId: options.agentId,
387
+ depth: context.depth,
388
+ });
190
389
  const lineage = {
191
390
  parentSessionId: spawnRecord.parentSessionId,
192
391
  rootSessionId: spawnRecord.rootSessionId,
@@ -326,9 +525,11 @@ export class AgentManager {
326
525
  });
327
526
  }
328
527
  childConfig.tokenBudget =
329
- childConfig.tokenBudget === 0
330
- ? allocatedTokens
331
- : Math.min(childConfig.tokenBudget, allocatedTokens);
528
+ allocatedTokens === 0
529
+ ? childConfig.tokenBudget
530
+ : childConfig.tokenBudget === 0
531
+ ? allocatedTokens
532
+ : Math.min(childConfig.tokenBudget, allocatedTokens);
332
533
  childBudget.narrow(childConfig.tokenBudget);
333
534
  // Stamped AFTER the builder, for the fourth time and the same reason:
334
535
  // a `configBuilder` is written by whoever registered the agent and
@@ -373,7 +574,10 @@ export class AgentManager {
373
574
  childAbortController.signal.throwIfAborted();
374
575
  await childBudget.flush();
375
576
  childAbortController.signal.throwIfAborted();
376
- this.runChild(agentTask, options, childConfig, listener).catch(async (err) => {
577
+ this.executingTasks.add(taskId);
578
+ const runningTask = agentTask;
579
+ this.runChild(runningTask, options, childConfig, listener)
580
+ .catch(async (err) => {
377
581
  // A thrown invocation supplied no final usage receipt. Keep its
378
582
  // reservation, including when its task handle was canceled or evicted.
379
583
  let failure = err;
@@ -383,8 +587,24 @@ export class AgentManager {
383
587
  catch (writeError) {
384
588
  failure = writeError;
385
589
  }
386
- this.markFailed(taskId, toErrorMessage(failure));
387
- });
590
+ // Cancellation may already have published a terminal handle. Its
591
+ // invocation has only NOW stopped; release the persisted edge here.
592
+ if (!this.instances.has(taskId) || isTerminalAgentTaskState(runningTask.state)) {
593
+ await this.failSubSession(spawnRecord);
594
+ }
595
+ else
596
+ this.markFailed(taskId, toErrorMessage(failure));
597
+ })
598
+ .finally(() => {
599
+ this.executingTasks.delete(taskId);
600
+ if (!this.instances.has(taskId))
601
+ this.spawnRecords.delete(taskId);
602
+ this.pumpAdmissions(options.parentSessionId);
603
+ })
604
+ .catch((error) => this.log.warn('Child cleanup failed', {
605
+ 'namzu.task.id': taskId,
606
+ 'exception.message': toErrorMessage(error),
607
+ }));
388
608
  return agentTask;
389
609
  }
390
610
  catch (err) {
@@ -392,7 +612,7 @@ export class AgentManager {
392
612
  // provisioned resources are still ours to return. A builder/listener
393
613
  // failure must not strand a pending task the caller never received.
394
614
  if (agentTask) {
395
- await this.rollbackUnstartedSpawn(agentTask, spawnRecord, err);
615
+ await this.rollbackUnstartedSpawn(agentTask, spawnRecord, err, queuedTask !== undefined);
396
616
  }
397
617
  else {
398
618
  childAbortController?.abort();
@@ -403,7 +623,7 @@ export class AgentManager {
403
623
  throw err;
404
624
  }
405
625
  }
406
- async rollbackUnstartedSpawn(agentTask, spawnRecord, reason) {
626
+ async rollbackUnstartedSpawn(agentTask, spawnRecord, reason, retainHandle = false) {
407
627
  agentTask.childAbortController.abort();
408
628
  agentTask.context.budget.settle(0);
409
629
  await agentTask.context.budget.flush();
@@ -427,7 +647,22 @@ export class AgentManager {
427
647
  }
428
648
  }
429
649
  this.clearEvictionTimer(agentTask.taskId);
430
- this.instances.delete(agentTask.taskId);
650
+ if (retainHandle) {
651
+ agentTask.result ??= {
652
+ runId: agentTask.context.parentRunId,
653
+ status: 'failed',
654
+ usage: { ...EMPTY_TOKEN_USAGE },
655
+ cost: { ...ZERO_COST },
656
+ iterations: 0,
657
+ durationMs: Date.now() - agentTask.createdAt,
658
+ messages: [],
659
+ lastError: toErrorMessage(reason),
660
+ };
661
+ agentTask.completedAt = Date.now();
662
+ this.scheduleEviction(agentTask.taskId);
663
+ }
664
+ else
665
+ this.instances.delete(agentTask.taskId);
431
666
  this.spawnRecords.delete(agentTask.taskId);
432
667
  this.resolveCompletionCallbacks(agentTask.taskId);
433
668
  await this.rollbackSpawnResources(spawnRecord);
@@ -448,14 +683,24 @@ export class AgentManager {
448
683
  }
449
684
  }
450
685
  cancel(taskId, cause) {
686
+ if (this.cancelingTasks.has(taskId))
687
+ return;
451
688
  const agentTask = this.instances.get(taskId);
452
689
  if (!agentTask || isTerminalAgentTaskState(agentTask.state))
453
690
  return;
454
691
  // Was the bare string `'canceled'`, which `abortReasonText` suppresses
455
692
  // by name — its docblock cites this exact call site — so the child's
456
693
  // run saw a cancellation with no attributable origin at all.
457
- agentTask.childAbortController.abort(cause ? new RunCancelled(cause) : undefined);
458
- this.markCanceled(taskId, cause);
694
+ // Abort listeners run synchronously. A pending task's parent observer
695
+ // must not reenter this method and replace an explicit user's cause.
696
+ this.cancelingTasks.add(taskId);
697
+ try {
698
+ agentTask.childAbortController.abort(cause ? new RunCancelled(cause) : undefined);
699
+ this.markCanceled(taskId, cause);
700
+ }
701
+ finally {
702
+ this.cancelingTasks.delete(taskId);
703
+ }
459
704
  }
460
705
  cancelAll(parentRunId, cause = 'parent') {
461
706
  // `'parent'` by default, because this call site IS a parent
@@ -539,11 +784,20 @@ export class AgentManager {
539
784
  if (isTerminalAgentTaskState(agentTask.state)) {
540
785
  this.clearEvictionTimer(taskId);
541
786
  this.instances.delete(taskId);
542
- this.spawnRecords.delete(taskId);
787
+ if (!this.executingTasks.has(taskId))
788
+ this.spawnRecords.delete(taskId);
543
789
  }
544
790
  }
545
791
  }
546
792
  dispose() {
793
+ this.disposed = true;
794
+ if (this.admissionTimer)
795
+ clearTimeout(this.admissionTimer);
796
+ this.admissionTimer = undefined;
797
+ for (const queue of this.pendingSpawns.values())
798
+ for (const entry of queue)
799
+ entry.removeAbortListener();
800
+ this.pendingSpawns.clear();
547
801
  for (const taskId of this.instances.keys()) {
548
802
  this.clearEvictionTimer(taskId);
549
803
  }
@@ -555,7 +809,9 @@ export class AgentManager {
555
809
  this.cancel(taskId);
556
810
  }
557
811
  this.instances.clear();
558
- this.spawnRecords.clear();
812
+ for (const taskId of this.spawnRecords.keys())
813
+ if (!this.executingTasks.has(taskId))
814
+ this.spawnRecords.delete(taskId);
559
815
  this.listeners.length = 0;
560
816
  }
561
817
  /**
@@ -591,7 +847,7 @@ export class AgentManager {
591
847
  this.spawnLocks.delete(key);
592
848
  }
593
849
  }
594
- async provisionSpawnUnlocked(options, context) {
850
+ async validateSpawn(options, context) {
595
851
  // Phase 9: deps are unconditional required. Every spawn produces a
596
852
  // SubSession + Session + WorkspaceRef triple (Convention #0: no
597
853
  // partial/legacy path).
@@ -618,6 +874,8 @@ export class AgentManager {
618
874
  if (!parentSession) {
619
875
  throw new Error(`Parent session ${options.parentSessionId} not found for tenant ${context.tenantId} — spawn rejected`);
620
876
  }
877
+ if (parentSession.status === 'archived')
878
+ throw new Error('Delegation parent session is archived');
621
879
  if (parentSession.topicId !== context.topicId) {
622
880
  throw new Error(`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).`);
623
881
  }
@@ -633,6 +891,11 @@ export class AgentManager {
633
891
  // chain; width counts existing direct children of the parent.
634
892
  await this.deps.capacity.validateDepth(options.parentSessionId, project.config.maxDelegationDepth, context.tenantId);
635
893
  await this.deps.capacity.validateWidth(options.parentSessionId, 1, project.config.maxDelegationWidth, context.tenantId);
894
+ }
895
+ async provisionSpawnUnlocked(options, context) {
896
+ await this.validateSpawn(options, context);
897
+ context.parentAbortController.signal.throwIfAborted();
898
+ const store = this.deps.sessionStore;
636
899
  // Ancestry walk gives both the child depth and the root session id
637
900
  // attached to every sub-session event from here down.
638
901
  const parentAncestry = await store.getAncestry(options.parentSessionId, context.tenantId);
@@ -995,13 +1258,16 @@ export class AgentManager {
995
1258
  return agentTask;
996
1259
  }
997
1260
  scheduleEviction(taskId) {
1261
+ if (this.disposed)
1262
+ return;
998
1263
  const agentTask = this.instances.get(taskId);
999
1264
  if (!agentTask)
1000
1265
  return;
1001
1266
  agentTask.evictAfter = Date.now() + this.config.evictionMs;
1002
1267
  const timer = setTimeout(() => {
1003
1268
  this.instances.delete(taskId);
1004
- this.spawnRecords.delete(taskId);
1269
+ if (!this.executingTasks.has(taskId))
1270
+ this.spawnRecords.delete(taskId);
1005
1271
  this.evictionTimers.delete(taskId);
1006
1272
  this.log.info('Agent task evicted', { 'namzu.agent.task_id': taskId });
1007
1273
  }, this.config.evictionMs);
@@ -1038,15 +1304,19 @@ export class AgentManager {
1038
1304
  emitRunEvent(agentTask, event) {
1039
1305
  if (!agentTask.runEventListener)
1040
1306
  return;
1041
- try {
1042
- agentTask.runEventListener(event);
1043
- }
1044
- catch (err) {
1307
+ const reportFailure = (error) => {
1045
1308
  this.log.error('RunEvent emission error', {
1046
1309
  'namzu.event.type': event.type,
1047
- 'namzu.task.id': agentTask.taskId,
1048
- 'exception.message': toErrorMessage(err),
1310
+ 'exception.message': toErrorMessage(error),
1049
1311
  });
1312
+ };
1313
+ try {
1314
+ // Terminal observation must not delay completion or leak a rejected
1315
+ // listener promise into the host as an unhandled rejection.
1316
+ void Promise.resolve(agentTask.runEventListener(event)).catch(reportFailure);
1317
+ }
1318
+ catch (error) {
1319
+ reportFailure(error);
1050
1320
  }
1051
1321
  }
1052
1322
  }