@namzu/sdk 59.0.0 → 60.1.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 (370) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/dist/agents/QueryAgent.d.ts.map +1 -1
  3. package/dist/agents/QueryAgent.js +1 -0
  4. package/dist/agents/QueryAgent.js.map +1 -1
  5. package/dist/bridge/a2a/mapper.d.ts.map +1 -1
  6. package/dist/bridge/a2a/mapper.js +16 -0
  7. package/dist/bridge/a2a/mapper.js.map +1 -1
  8. package/dist/bridge/acp/update.d.ts.map +1 -1
  9. package/dist/bridge/acp/update.js +1 -0
  10. package/dist/bridge/acp/update.js.map +1 -1
  11. package/dist/bridge/sse/mapper.d.ts.map +1 -1
  12. package/dist/bridge/sse/mapper.js +12 -0
  13. package/dist/bridge/sse/mapper.js.map +1 -1
  14. package/dist/compaction/context-window.d.ts +53 -0
  15. package/dist/compaction/context-window.d.ts.map +1 -1
  16. package/dist/compaction/context-window.js +141 -2
  17. package/dist/compaction/context-window.js.map +1 -1
  18. package/dist/constants/continuation.d.ts +9 -0
  19. package/dist/constants/continuation.d.ts.map +1 -1
  20. package/dist/constants/continuation.js +9 -0
  21. package/dist/constants/continuation.js.map +1 -1
  22. package/dist/contracts/session/api.d.ts +7 -1
  23. package/dist/contracts/session/api.d.ts.map +1 -1
  24. package/dist/execution/workflow/index.d.ts +3 -1
  25. package/dist/execution/workflow/index.d.ts.map +1 -1
  26. package/dist/execution/workflow/index.js +2 -1
  27. package/dist/execution/workflow/index.js.map +1 -1
  28. package/dist/execution/workflow/journal.d.ts +24 -4
  29. package/dist/execution/workflow/journal.d.ts.map +1 -1
  30. package/dist/execution/workflow/journal.js +131 -9
  31. package/dist/execution/workflow/journal.js.map +1 -1
  32. package/dist/execution/workflow/prelude.d.ts +2 -0
  33. package/dist/execution/workflow/prelude.d.ts.map +1 -1
  34. package/dist/execution/workflow/prelude.js +49 -8
  35. package/dist/execution/workflow/prelude.js.map +1 -1
  36. package/dist/execution/workflow/resume.d.ts +59 -0
  37. package/dist/execution/workflow/resume.d.ts.map +1 -0
  38. package/dist/execution/workflow/resume.js +273 -0
  39. package/dist/execution/workflow/resume.js.map +1 -0
  40. package/dist/execution/workflow/runtime.d.ts.map +1 -1
  41. package/dist/execution/workflow/runtime.js +112 -8
  42. package/dist/execution/workflow/runtime.js.map +1 -1
  43. package/dist/execution/workflow/types.d.ts +105 -1
  44. package/dist/execution/workflow/types.d.ts.map +1 -1
  45. package/dist/index.d.ts +3 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +4 -0
  48. package/dist/index.js.map +1 -1
  49. package/dist/manager/session/turn-recorder.d.ts.map +1 -1
  50. package/dist/manager/session/turn-recorder.js +1 -0
  51. package/dist/manager/session/turn-recorder.js.map +1 -1
  52. package/dist/plugin/compat.d.ts +24 -0
  53. package/dist/plugin/compat.d.ts.map +1 -0
  54. package/dist/plugin/compat.js +149 -0
  55. package/dist/plugin/compat.js.map +1 -0
  56. package/dist/plugin/loader.d.ts +2 -1
  57. package/dist/plugin/loader.d.ts.map +1 -1
  58. package/dist/plugin/loader.js +6 -0
  59. package/dist/plugin/loader.js.map +1 -1
  60. package/dist/pricing/index.d.ts.map +1 -1
  61. package/dist/pricing/index.js +2 -1
  62. package/dist/pricing/index.js.map +1 -1
  63. package/dist/process/kill-tree.d.ts +44 -0
  64. package/dist/process/kill-tree.d.ts.map +1 -1
  65. package/dist/process/kill-tree.js +102 -4
  66. package/dist/process/kill-tree.js.map +1 -1
  67. package/dist/prompt/coding-agent-doctrine.d.ts +1 -1
  68. package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -1
  69. package/dist/prompt/coding-agent-doctrine.js +9 -1
  70. package/dist/prompt/coding-agent-doctrine.js.map +1 -1
  71. package/dist/provider/collect-chat-completion.d.ts.map +1 -1
  72. package/dist/provider/collect-chat-completion.js +4 -0
  73. package/dist/provider/collect-chat-completion.js.map +1 -1
  74. package/dist/provider/fallback.d.ts.map +1 -1
  75. package/dist/provider/fallback.js +11 -0
  76. package/dist/provider/fallback.js.map +1 -1
  77. package/dist/provider/idle-timeout.d.ts.map +1 -1
  78. package/dist/provider/idle-timeout.js +5 -0
  79. package/dist/provider/idle-timeout.js.map +1 -1
  80. package/dist/provider/mock.d.ts +2 -0
  81. package/dist/provider/mock.d.ts.map +1 -1
  82. package/dist/provider/mock.js +16 -8
  83. package/dist/provider/mock.js.map +1 -1
  84. package/dist/provider/model-window-suffix.d.ts +18 -0
  85. package/dist/provider/model-window-suffix.d.ts.map +1 -0
  86. package/dist/provider/model-window-suffix.js +78 -0
  87. package/dist/provider/model-window-suffix.js.map +1 -0
  88. package/dist/provider/retry.d.ts.map +1 -1
  89. package/dist/provider/retry.js +5 -0
  90. package/dist/provider/retry.js.map +1 -1
  91. package/dist/public-runtime.d.ts +5 -3
  92. package/dist/public-runtime.d.ts.map +1 -1
  93. package/dist/public-runtime.js +5 -4
  94. package/dist/public-runtime.js.map +1 -1
  95. package/dist/public-types.d.ts +1 -0
  96. package/dist/public-types.d.ts.map +1 -1
  97. package/dist/registry/command/kernel-commands.d.ts +1 -4
  98. package/dist/registry/command/kernel-commands.d.ts.map +1 -1
  99. package/dist/registry/command/kernel-commands.js +0 -28
  100. package/dist/registry/command/kernel-commands.js.map +1 -1
  101. package/dist/runtime/jobs/awaited-jobs.d.ts +6 -0
  102. package/dist/runtime/jobs/awaited-jobs.d.ts.map +1 -1
  103. package/dist/runtime/jobs/awaited-jobs.js +11 -0
  104. package/dist/runtime/jobs/awaited-jobs.js.map +1 -1
  105. package/dist/runtime/jobs/host-exit-finalizer.d.ts +20 -0
  106. package/dist/runtime/jobs/host-exit-finalizer.d.ts.map +1 -0
  107. package/dist/runtime/jobs/host-exit-finalizer.js +57 -0
  108. package/dist/runtime/jobs/host-exit-finalizer.js.map +1 -0
  109. package/dist/runtime/jobs/liveness.d.ts +67 -0
  110. package/dist/runtime/jobs/liveness.d.ts.map +1 -0
  111. package/dist/runtime/jobs/liveness.js +94 -0
  112. package/dist/runtime/jobs/liveness.js.map +1 -0
  113. package/dist/runtime/jobs/registry.d.ts +21 -0
  114. package/dist/runtime/jobs/registry.d.ts.map +1 -1
  115. package/dist/runtime/jobs/registry.js +104 -2
  116. package/dist/runtime/jobs/registry.js.map +1 -1
  117. package/dist/runtime/jobs/wake-budget.d.ts +35 -0
  118. package/dist/runtime/jobs/wake-budget.d.ts.map +1 -0
  119. package/dist/runtime/jobs/wake-budget.js +48 -0
  120. package/dist/runtime/jobs/wake-budget.js.map +1 -0
  121. package/dist/runtime/query/detached-calls.d.ts +65 -0
  122. package/dist/runtime/query/detached-calls.d.ts.map +1 -0
  123. package/dist/runtime/query/detached-calls.js +116 -0
  124. package/dist/runtime/query/detached-calls.js.map +1 -0
  125. package/dist/runtime/query/executor.d.ts +4 -1
  126. package/dist/runtime/query/executor.d.ts.map +1 -1
  127. package/dist/runtime/query/executor.js +90 -6
  128. package/dist/runtime/query/executor.js.map +1 -1
  129. package/dist/runtime/query/index.d.ts +34 -0
  130. package/dist/runtime/query/index.d.ts.map +1 -1
  131. package/dist/runtime/query/index.js +32 -5
  132. package/dist/runtime/query/index.js.map +1 -1
  133. package/dist/runtime/query/iteration/effort-clamp.d.ts +13 -0
  134. package/dist/runtime/query/iteration/effort-clamp.d.ts.map +1 -0
  135. package/dist/runtime/query/iteration/effort-clamp.js +42 -0
  136. package/dist/runtime/query/iteration/effort-clamp.js.map +1 -0
  137. package/dist/runtime/query/iteration/index.d.ts +34 -0
  138. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  139. package/dist/runtime/query/iteration/index.js +199 -6
  140. package/dist/runtime/query/iteration/index.js.map +1 -1
  141. package/dist/runtime/query/iteration/outstanding-work.d.ts.map +1 -1
  142. package/dist/runtime/query/iteration/outstanding-work.js +11 -3
  143. package/dist/runtime/query/iteration/outstanding-work.js.map +1 -1
  144. package/dist/runtime/query/iteration/phases/context.d.ts +9 -0
  145. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  146. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  147. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  148. package/dist/runtime/query/iteration/phases/tool-review.js +63 -1
  149. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  150. package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
  151. package/dist/runtime/query/iteration/stream-turn.js +6 -0
  152. package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
  153. package/dist/runtime/query/session-storage.d.ts.map +1 -1
  154. package/dist/runtime/query/session-storage.js +8 -3
  155. package/dist/runtime/query/session-storage.js.map +1 -1
  156. package/dist/runtime/query/steering.d.ts +29 -3
  157. package/dist/runtime/query/steering.d.ts.map +1 -1
  158. package/dist/runtime/query/steering.js +58 -3
  159. package/dist/runtime/query/steering.js.map +1 -1
  160. package/dist/runtime/query/tooling.d.ts +1 -0
  161. package/dist/runtime/query/tooling.d.ts.map +1 -1
  162. package/dist/runtime/query/tooling.js +1 -0
  163. package/dist/runtime/query/tooling.js.map +1 -1
  164. package/dist/runtime/system-events.d.ts +5 -0
  165. package/dist/runtime/system-events.d.ts.map +1 -1
  166. package/dist/runtime/system-events.js +7 -1
  167. package/dist/runtime/system-events.js.map +1 -1
  168. package/dist/sandbox/denial.d.ts +11 -0
  169. package/dist/sandbox/denial.d.ts.map +1 -0
  170. package/dist/sandbox/denial.js +61 -0
  171. package/dist/sandbox/denial.js.map +1 -0
  172. package/dist/sandbox/file-walk-program.d.ts.map +1 -1
  173. package/dist/sandbox/file-walk-program.js +31 -13
  174. package/dist/sandbox/file-walk-program.js.map +1 -1
  175. package/dist/sandbox/file-walk.d.ts +2 -0
  176. package/dist/sandbox/file-walk.d.ts.map +1 -1
  177. package/dist/sandbox/file-walk.js +64 -13
  178. package/dist/sandbox/file-walk.js.map +1 -1
  179. package/dist/sandbox/ignore-matcher.d.ts +30 -0
  180. package/dist/sandbox/ignore-matcher.d.ts.map +1 -0
  181. package/dist/sandbox/ignore-matcher.js +11 -0
  182. package/dist/sandbox/ignore-matcher.js.map +1 -0
  183. package/dist/sandbox/ignore-source.d.ts +26 -0
  184. package/dist/sandbox/ignore-source.d.ts.map +1 -0
  185. package/dist/sandbox/ignore-source.js +221 -0
  186. package/dist/sandbox/ignore-source.js.map +1 -0
  187. package/dist/sandbox/provider/local.d.ts.map +1 -1
  188. package/dist/sandbox/provider/local.js +11 -1
  189. package/dist/sandbox/provider/local.js.map +1 -1
  190. package/dist/scheduler/completion-inbox.d.ts.map +1 -1
  191. package/dist/scheduler/completion-inbox.js +1 -0
  192. package/dist/scheduler/completion-inbox.js.map +1 -1
  193. package/dist/session/log-hash.d.ts.map +1 -1
  194. package/dist/session/log-hash.js +18 -1
  195. package/dist/session/log-hash.js.map +1 -1
  196. package/dist/store/memory/disk.d.ts.map +1 -1
  197. package/dist/store/memory/disk.js +1 -1
  198. package/dist/store/memory/disk.js.map +1 -1
  199. package/dist/store/memory/markdown.d.ts.map +1 -1
  200. package/dist/store/memory/markdown.js +1 -1
  201. package/dist/store/memory/markdown.js.map +1 -1
  202. package/dist/store/memory/operation-lock.d.ts +20 -4
  203. package/dist/store/memory/operation-lock.d.ts.map +1 -1
  204. package/dist/store/memory/operation-lock.js +235 -58
  205. package/dist/store/memory/operation-lock.js.map +1 -1
  206. package/dist/store/session-index/sqlite.d.ts.map +1 -1
  207. package/dist/store/session-index/sqlite.js +169 -16
  208. package/dist/store/session-index/sqlite.js.map +1 -1
  209. package/dist/tools/builtins/bash.d.ts.map +1 -1
  210. package/dist/tools/builtins/bash.js +10 -4
  211. package/dist/tools/builtins/bash.js.map +1 -1
  212. package/dist/tools/builtins/computer-use.d.ts +35 -0
  213. package/dist/tools/builtins/computer-use.d.ts.map +1 -1
  214. package/dist/tools/builtins/computer-use.js +512 -67
  215. package/dist/tools/builtins/computer-use.js.map +1 -1
  216. package/dist/tools/builtins/glob.d.ts +1 -0
  217. package/dist/tools/builtins/glob.d.ts.map +1 -1
  218. package/dist/tools/builtins/glob.js +65 -28
  219. package/dist/tools/builtins/glob.js.map +1 -1
  220. package/dist/tools/builtins/grep.d.ts +2 -0
  221. package/dist/tools/builtins/grep.d.ts.map +1 -1
  222. package/dist/tools/builtins/grep.js +94 -40
  223. package/dist/tools/builtins/grep.js.map +1 -1
  224. package/dist/tools/builtins/job.d.ts +1 -1
  225. package/dist/tools/builtins/job.d.ts.map +1 -1
  226. package/dist/tools/builtins/job.js +34 -7
  227. package/dist/tools/builtins/job.js.map +1 -1
  228. package/dist/tools/builtins/search-support.d.ts +44 -0
  229. package/dist/tools/builtins/search-support.d.ts.map +1 -0
  230. package/dist/tools/builtins/search-support.js +89 -0
  231. package/dist/tools/builtins/search-support.js.map +1 -0
  232. package/dist/tools/builtins/wait-for-job-bounds.d.ts +2 -0
  233. package/dist/tools/builtins/wait-for-job-bounds.d.ts.map +1 -1
  234. package/dist/tools/builtins/wait-for-job-bounds.js +3 -1
  235. package/dist/tools/builtins/wait-for-job-bounds.js.map +1 -1
  236. package/dist/tools/builtins/wait-for-job.d.ts +1 -1
  237. package/dist/tools/builtins/wait-for-job.d.ts.map +1 -1
  238. package/dist/tools/builtins/wait-for-job.js +13 -2
  239. package/dist/tools/builtins/wait-for-job.js.map +1 -1
  240. package/dist/tools/command-shell.d.ts.map +1 -1
  241. package/dist/tools/command-shell.js +14 -5
  242. package/dist/tools/command-shell.js.map +1 -1
  243. package/dist/turn/reporter.d.ts.map +1 -1
  244. package/dist/turn/reporter.js +11 -0
  245. package/dist/turn/reporter.js.map +1 -1
  246. package/dist/types/agent/query.d.ts +2 -0
  247. package/dist/types/agent/query.d.ts.map +1 -1
  248. package/dist/types/computer-use/index.d.ts +129 -13
  249. package/dist/types/computer-use/index.d.ts.map +1 -1
  250. package/dist/types/computer-use/index.js.map +1 -1
  251. package/dist/types/message/index.d.ts +1 -1
  252. package/dist/types/message/index.d.ts.map +1 -1
  253. package/dist/types/message/index.js +1 -0
  254. package/dist/types/message/index.js.map +1 -1
  255. package/dist/types/plugin/index.d.ts +24 -0
  256. package/dist/types/plugin/index.d.ts.map +1 -1
  257. package/dist/types/plugin/index.js +4 -0
  258. package/dist/types/plugin/index.js.map +1 -1
  259. package/dist/types/provider/chat.d.ts +19 -0
  260. package/dist/types/provider/chat.d.ts.map +1 -1
  261. package/dist/types/provider/config.d.ts +7 -0
  262. package/dist/types/provider/config.d.ts.map +1 -1
  263. package/dist/types/provider/index.d.ts +1 -1
  264. package/dist/types/provider/index.d.ts.map +1 -1
  265. package/dist/types/provider/interface.d.ts +15 -0
  266. package/dist/types/provider/interface.d.ts.map +1 -1
  267. package/dist/types/provider/stream.d.ts +6 -0
  268. package/dist/types/provider/stream.d.ts.map +1 -1
  269. package/dist/types/sandbox/index.d.ts +38 -1
  270. package/dist/types/sandbox/index.d.ts.map +1 -1
  271. package/dist/types/sandbox/index.js.map +1 -1
  272. package/dist/types/session/events.d.ts +34 -0
  273. package/dist/types/session/events.d.ts.map +1 -1
  274. package/dist/types/session/events.js.map +1 -1
  275. package/dist/types/session/records.d.ts +298 -34
  276. package/dist/types/session/records.d.ts.map +1 -1
  277. package/dist/types/session/records.js +35 -1
  278. package/dist/types/session/records.js.map +1 -1
  279. package/dist/types/session/stop-reason.d.ts +11 -0
  280. package/dist/types/session/stop-reason.d.ts.map +1 -1
  281. package/dist/types/tool/index.d.ts +30 -0
  282. package/dist/types/tool/index.d.ts.map +1 -1
  283. package/dist/types/tool/index.js.map +1 -1
  284. package/dist/types/tool/presentation.d.ts +8 -0
  285. package/dist/types/tool/presentation.d.ts.map +1 -1
  286. package/package.json +1 -1
  287. package/src/agents/QueryAgent.ts +1 -0
  288. package/src/bridge/a2a/mapper.ts +19 -0
  289. package/src/bridge/acp/update.ts +6 -1
  290. package/src/bridge/sse/mapper.ts +13 -0
  291. package/src/compaction/context-window.ts +163 -2
  292. package/src/constants/continuation.ts +13 -0
  293. package/src/contracts/session/api.ts +6 -0
  294. package/src/execution/workflow/index.ts +14 -1
  295. package/src/execution/workflow/journal.ts +161 -11
  296. package/src/execution/workflow/prelude.ts +51 -8
  297. package/src/execution/workflow/resume.ts +313 -0
  298. package/src/execution/workflow/runtime.ts +127 -9
  299. package/src/execution/workflow/types.ts +109 -1
  300. package/src/index.ts +17 -0
  301. package/src/manager/session/turn-recorder.ts +1 -0
  302. package/src/plugin/compat.ts +173 -0
  303. package/src/plugin/loader.ts +7 -1
  304. package/src/pricing/index.ts +2 -1
  305. package/src/process/kill-tree.ts +106 -4
  306. package/src/prompt/coding-agent-doctrine.ts +9 -1
  307. package/src/provider/collect-chat-completion.ts +3 -0
  308. package/src/provider/fallback.ts +17 -0
  309. package/src/provider/idle-timeout.ts +9 -0
  310. package/src/provider/mock.ts +19 -9
  311. package/src/provider/model-window-suffix.ts +85 -0
  312. package/src/provider/retry.ts +9 -0
  313. package/src/public-runtime.ts +21 -2
  314. package/src/public-types.ts +1 -0
  315. package/src/registry/command/kernel-commands.ts +1 -32
  316. package/src/runtime/jobs/awaited-jobs.ts +11 -0
  317. package/src/runtime/jobs/host-exit-finalizer.ts +67 -0
  318. package/src/runtime/jobs/liveness.ts +144 -0
  319. package/src/runtime/jobs/registry.ts +114 -2
  320. package/src/runtime/jobs/wake-budget.ts +55 -0
  321. package/src/runtime/query/detached-calls.ts +135 -0
  322. package/src/runtime/query/executor.ts +92 -10
  323. package/src/runtime/query/index.ts +65 -7
  324. package/src/runtime/query/iteration/effort-clamp.ts +46 -0
  325. package/src/runtime/query/iteration/index.ts +260 -6
  326. package/src/runtime/query/iteration/outstanding-work.ts +12 -3
  327. package/src/runtime/query/iteration/phases/context.ts +9 -0
  328. package/src/runtime/query/iteration/phases/tool-review.ts +73 -8
  329. package/src/runtime/query/iteration/stream-turn.ts +5 -0
  330. package/src/runtime/query/session-storage.ts +8 -3
  331. package/src/runtime/query/steering.ts +77 -6
  332. package/src/runtime/query/tooling.ts +2 -0
  333. package/src/runtime/system-events.ts +12 -1
  334. package/src/sandbox/denial.ts +79 -0
  335. package/src/sandbox/file-walk-program.ts +32 -13
  336. package/src/sandbox/file-walk.ts +75 -12
  337. package/src/sandbox/ignore-matcher.ts +46 -0
  338. package/src/sandbox/ignore-source.ts +220 -0
  339. package/src/sandbox/provider/local.ts +11 -1
  340. package/src/scheduler/completion-inbox.ts +1 -0
  341. package/src/session/log-hash.ts +21 -0
  342. package/src/store/memory/disk.ts +1 -0
  343. package/src/store/memory/markdown.ts +1 -0
  344. package/src/store/memory/operation-lock.ts +288 -57
  345. package/src/store/session-index/sqlite.ts +171 -16
  346. package/src/tools/builtins/bash.ts +11 -4
  347. package/src/tools/builtins/computer-use.ts +593 -68
  348. package/src/tools/builtins/glob.ts +79 -32
  349. package/src/tools/builtins/grep.ts +107 -43
  350. package/src/tools/builtins/job.ts +42 -12
  351. package/src/tools/builtins/search-support.ts +107 -0
  352. package/src/tools/builtins/wait-for-job-bounds.ts +5 -1
  353. package/src/tools/builtins/wait-for-job.ts +14 -2
  354. package/src/tools/command-shell.ts +13 -5
  355. package/src/turn/reporter.ts +12 -0
  356. package/src/types/agent/query.ts +2 -0
  357. package/src/types/computer-use/index.ts +157 -15
  358. package/src/types/message/index.ts +5 -3
  359. package/src/types/plugin/index.ts +8 -0
  360. package/src/types/provider/chat.ts +20 -0
  361. package/src/types/provider/config.ts +7 -0
  362. package/src/types/provider/index.ts +1 -0
  363. package/src/types/provider/interface.ts +20 -0
  364. package/src/types/provider/stream.ts +6 -0
  365. package/src/types/sandbox/index.ts +40 -1
  366. package/src/types/session/events.ts +65 -5
  367. package/src/types/session/records.ts +39 -2
  368. package/src/types/session/stop-reason.ts +11 -0
  369. package/src/types/tool/index.ts +30 -0
  370. package/src/types/tool/presentation.ts +8 -0
@@ -11,6 +11,8 @@ import { type PluginManifest, PluginManifestSchema } from '../types/plugin/index
11
11
  import { toErrorMessage } from '../utils/error.js'
12
12
  import { SCOPE_ATTRIBUTE } from '../utils/log/types.js'
13
13
  import { type Logger, resolveLogger } from '../utils/logger.js'
14
+ import { VERSION } from '../version.js'
15
+ import { type PluginHostFacts, assertCompatible } from './compat.js'
14
16
 
15
17
  /**
16
18
  * Scans a directory for subdirectories containing a plugin manifest.
@@ -161,7 +163,7 @@ const REGISTRY_BACKED_CONTRIBUTIONS = ['skills'] as const
161
163
  * through host configuration; what does not exist is the manifest path
162
164
  * into them. Naming the types is what makes that actionable.
163
165
  */
164
- export interface PluginEnablementCapabilities {
166
+ export interface PluginEnablementCapabilities extends PluginHostFacts {
165
167
  /**
166
168
  * Whether the host wired a `SkillRegistry`.
167
169
  *
@@ -178,6 +180,10 @@ export function assertEnableable(
178
180
  manifest: PluginManifest,
179
181
  capabilities: PluginEnablementCapabilities = {},
180
182
  ): void {
183
+ assertCompatible(manifest, {
184
+ platform: capabilities.platform ?? platform(),
185
+ namzuVersion: capabilities.namzuVersion ?? VERSION,
186
+ })
181
187
  const record = manifest as unknown as Record<string, unknown[] | undefined>
182
188
  // One ordered pass over all three rather than two group filters
183
189
  // concatenated, so the message keeps naming them in the order the
@@ -14,6 +14,7 @@
14
14
  * than refreshed, and `rates.source.json` for the rates themselves.
15
15
  */
16
16
 
17
+ import { stripModelWindowSuffix } from '../compaction/context-window.js'
17
18
  import type { ModelPricing } from '../utils/cost.js'
18
19
  import { VENDOR_RATES } from './catalogue.generated.js'
19
20
 
@@ -35,7 +36,7 @@ const SNAPSHOT_SUFFIX = /[-@]\d{8}$/
35
36
 
36
37
  /** Lowercase, snapshot suffix removed. Exported because tests assert it. */
37
38
  export function normaliseModelId(model: string): string {
38
- return model.toLowerCase().replace(SNAPSHOT_SUFFIX, '')
39
+ return stripModelWindowSuffix(model).toLowerCase().replace(SNAPSHOT_SUFFIX, '')
39
40
  }
40
41
 
41
42
  /**
@@ -1,4 +1,5 @@
1
1
  import { execFileSync } from 'node:child_process'
2
+ import { readFileSync, readdirSync } from 'node:fs'
2
3
 
3
4
  /**
4
5
  * Git Bash children that the Windows process tree cannot reach.
@@ -11,13 +12,82 @@ import { execFileSync } from 'node:child_process'
11
12
  * in its environment, which every descendant inherits however it was started;
12
13
  * the tag is how the tree is found again.
13
14
  */
14
- const MSYS_SHELLS = new WeakMap<object, { readonly bash: string; readonly tag: string }>()
15
+ const MSYS_SHELLS = new WeakMap<object, { readonly bash?: string; readonly tag: string }>()
16
+
17
+ /**
18
+ * Whether a pid may be signalled, or addressed as a group with `-pid`.
19
+ *
20
+ * `kill(-1, sig)` signals every process the user may signal and `kill(0, sig)`
21
+ * signals the caller's own process group, so a recorded pid of 1, 0, a negative
22
+ * or a fractional number (a test double, a corrupt record, `NaN`) would take
23
+ * down the host's whole session. Only an integer above 1 that is not this
24
+ * process is a real, separate process. A refused pid is treated as already gone.
25
+ */
26
+ export function isSignalablePid(pid: unknown): pid is number {
27
+ return typeof pid === 'number' && Number.isInteger(pid) && pid > 1 && pid !== process.pid
28
+ }
15
29
 
16
30
  /** Remember how to find every process a Git Bash child started. Called by the spawn. */
17
31
  export function registerMsysShell(child: object, shell: { bash: string; tag: string }): void {
18
32
  MSYS_SHELLS.set(child, shell)
19
33
  }
20
34
 
35
+ /**
36
+ * Remember the tag a POSIX child's environment carries, so a kill can also find
37
+ * a descendant that left the child's process group (`setsid`, a double fork
38
+ * that starts its own session). Such a process has no group to signal, but it
39
+ * inherited the environment, and the tag is how it is found again.
40
+ *
41
+ * The limit is the same as the group's own: a process that clears its
42
+ * environment before it forks escapes this too.
43
+ */
44
+ export function registerProcessTag(child: object, tag: string): void {
45
+ MSYS_SHELLS.set(child, { tag })
46
+ }
47
+
48
+ /**
49
+ * Signal every process of this user whose environment carries one of the tags,
50
+ * by reading `/proc/<pid>/environ`. Linux only: other POSIX systems have no
51
+ * `/proc`, and there the process group is all a kill can reach. A process whose
52
+ * environment cannot be read (another user's, or one that has just ended) is
53
+ * skipped without further work.
54
+ *
55
+ * Several tags are looked for in one pass, so stopping many jobs at once reads
56
+ * each environment once, not once per job. The scan is synchronous and its cost
57
+ * grows with the number of processes on the machine. There is also a window
58
+ * between reading an environment and signalling its pid in which the pid could
59
+ * be reused by an unrelated process of the same user; the window is the length
60
+ * of one read, and the signal goes only to a pid whose environment carried the
61
+ * tag a moment before.
62
+ */
63
+ export function signalTagged(tags: string | readonly string[], signal: NodeJS.Signals): number {
64
+ const needles = (typeof tags === 'string' ? [tags] : tags).map(
65
+ (tag) => `${MSYS_TAG_VARIABLE}=${tag}\0`,
66
+ )
67
+ if (needles.length === 0) return 0
68
+ let entries: string[]
69
+ try {
70
+ entries = readdirSync('/proc')
71
+ } catch {
72
+ return 0
73
+ }
74
+ let signalled = 0
75
+ for (const entry of entries) {
76
+ if (!/^\d+$/.test(entry)) continue
77
+ const pid = Number(entry)
78
+ if (!isSignalablePid(pid)) continue
79
+ try {
80
+ const environ = readFileSync(`/proc/${entry}/environ`, 'latin1')
81
+ if (!needles.some((needle) => environ.includes(needle))) continue
82
+ process.kill(pid, signal)
83
+ signalled++
84
+ } catch {
85
+ // Not ours to read, or gone since the listing.
86
+ }
87
+ }
88
+ return signalled
89
+ }
90
+
21
91
  /** The environment variable that carries the tag. */
22
92
  export const MSYS_TAG_VARIABLE = 'NAMZU_SHELL_TAG'
23
93
 
@@ -86,17 +156,49 @@ export function killTree(
86
156
  child: { readonly pid?: number | undefined },
87
157
  signal: NodeJS.Signals,
88
158
  ): void {
89
- if (!child.pid) return
159
+ killTrees([child], signal)
160
+ }
161
+
162
+ /**
163
+ * `killTree` for several children at once: each child's group is signalled, then
164
+ * the environment scan runs once for all their tags. Used where the number of
165
+ * kills is unbounded and cannot wait, such as the host's exit.
166
+ */
167
+ export function killTrees(
168
+ children: readonly { readonly pid?: number | undefined }[],
169
+ signal: NodeJS.Signals,
170
+ ): void {
171
+ const tags: string[] = []
172
+ for (const child of children) {
173
+ if (!isSignalablePid(child.pid)) continue
174
+ killOne(child, signal, tags)
175
+ }
176
+ if (tags.length > 0 && process.platform === 'linux') signalTagged(tags, signal)
177
+ }
178
+
179
+ function killOne(
180
+ child: { readonly pid?: number | undefined },
181
+ signal: NodeJS.Signals,
182
+ tags: string[],
183
+ ): void {
184
+ if (!isSignalablePid(child.pid)) return
90
185
  try {
91
186
  if (process.platform === 'win32') {
92
187
  const msys = MSYS_SHELLS.get(child)
93
- if (msys !== undefined) killMsysTagged(msys)
188
+ if (msys?.bash !== undefined) killMsysTagged({ bash: msys.bash, tag: msys.tag })
94
189
  execFileSync(taskkillPath(), ['/pid', String(child.pid), '/t', '/f'], {
95
190
  stdio: 'ignore',
96
191
  windowsHide: true,
97
192
  })
98
193
  } else {
99
- process.kill(-child.pid, signal)
194
+ try {
195
+ process.kill(-child.pid, signal)
196
+ } finally {
197
+ // After the group, which is cheap and exact: what is left is only
198
+ // what started its own session. Also when the group is already gone.
199
+ const tagged = MSYS_SHELLS.get(child)
200
+ if (tagged !== undefined) tags.push(tagged.tag)
201
+ }
100
202
  }
101
203
  } catch {
102
204
  // Group (or tree) already gone
@@ -82,7 +82,15 @@ export const CODING_AGENT_DELEGATION_DOCTRINE = `### Planning and delegating
82
82
  - When the \`Agent\` tool is available, delegate independent work through it and give each delegation a short, specific description; the user watches those descriptions, not the prompts. Brief each sub-agent with four things: the objective; what is out of bounds; which tools and sources to use; and the shape of the reply you want, such as a short summary with a file path for anything long. A fresh sub-agent starts with none of your context and returns what you ask for.
83
83
  - When the \`Agent\` tool lists a \`fork\` type, use \`subagent_type: "fork"\` when a side task would fill your context with output you will not need again, such as an open-ended survey. A fork starts from this conversation under your own instructions, so its prompt is only the directive: what to do and where its scope ends, without the background. Never write or guess what a fork will find before its report arrives. A fork cannot start forks of its own.
84
84
  - When delegated agents finish, tell the user what each one found in your next message. Their results reach you and not the user, so a result you do not pass on stays unseen.
85
- - 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.`
85
+ - 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.
86
+
87
+ ### Answering how work is going
88
+ - When asked how things stand while background work runs, read the note about running work on your request first if the host gives you one, and add one status read from the tool that started an item only when the note does not say enough. The answer needs no waiting or polling and leaves the work running, because stopping or restarting an item to answer would lose what it has done.
89
+ - Give each item the state you were given for it (progressing, waiting, stalled or dead) and how long ago it last moved, so the person can judge it for themselves. Stalled means only quiet for a while, not stuck.
90
+ - Group the answer as finished (with its result), running (and where it is), queued, and needing a decision. Lead with the outcome, keep it short, and write in the language the person used, states included.
91
+ - Name each item by what it is doing, not by its id: an id means nothing to the person, so give one only when they ask for it or need it for a command.
92
+ - A result exists only once its completion message has arrived, so for an unfinished item say it is still running; a predicted result reads as a fact and may be wrong.
93
+ - For an item reported dead, say it ended without a result and offer to run it again; starting the same work again unasked could repeat what it already did.`
86
94
 
87
95
  /**
88
96
  * For a session whose permission layer refuses every mutation. Says what that
@@ -72,6 +72,7 @@ export async function collectChatCompletion(
72
72
  let replayState: unknown
73
73
  let finishReason: ChatCompletionResponse['finishReason'] = 'stop'
74
74
  let finishDetail: ChatCompletionResponse['finishDetail']
75
+ let refusal: ChatCompletionResponse['refusal']
75
76
  let usage: ChatCompletionResponse['usage'] = {
76
77
  promptTokens: 0,
77
78
  completionTokens: 0,
@@ -158,6 +159,7 @@ export async function collectChatCompletion(
158
159
  finishReason = chunk.finishReason
159
160
  finishDetail = chunk.finishReason === 'length' ? chunk.finishDetail : undefined
160
161
  }
162
+ if (chunk.refusal) refusal = chunk.refusal
161
163
  // Merge (per-field max), not last-write-wins: a late frame that omits
162
164
  // input/cache tokens must not zero the counts captured earlier in the stream.
163
165
  if (chunk.usage) usage = mergeTokenUsage(usage, chunk.usage)
@@ -207,6 +209,7 @@ export async function collectChatCompletion(
207
209
  },
208
210
  finishReason,
209
211
  ...(finishDetail ? { finishDetail } : {}),
212
+ ...(refusal && finishReason === 'content_filter' ? { refusal } : {}),
210
213
  usage,
211
214
  }
212
215
  }
@@ -572,6 +572,23 @@ export function withProviderFallback(
572
572
  ) => first.provider.effortLevelsFor?.(model, thinking) ?? [],
573
573
  }
574
574
  : {}),
575
+ // Answered by the member the next request goes to first, the one the
576
+ // cursor rests on: it is the member whose model just declined.
577
+ ...(members.some((member) => member.provider.refusalFallbackFor)
578
+ ? {
579
+ refusalFallbackFor: (
580
+ model: string,
581
+ refusal: Parameters<NonNullable<LLMProvider['refusalFallbackFor']>>[1],
582
+ signal?: AbortSignal,
583
+ ) => {
584
+ const serving = members[cursor] ?? first
585
+ return (
586
+ serving.provider.refusalFallbackFor?.(serving.model ?? model, refusal, signal) ??
587
+ Promise.resolve(undefined)
588
+ )
589
+ },
590
+ }
591
+ : {}),
575
592
  ...(first.provider.resolveContextWindow
576
593
  ? {
577
594
  resolveContextWindow: (model: string, signal?: AbortSignal) =>
@@ -247,6 +247,15 @@ export function withStreamIdleTimeout(
247
247
  ) => provider.effortLevelsFor?.(model, thinking) ?? [],
248
248
  }
249
249
  : {}),
250
+ ...(provider.refusalFallbackFor
251
+ ? {
252
+ refusalFallbackFor: (
253
+ model: string,
254
+ refusal: Parameters<NonNullable<LLMProvider['refusalFallbackFor']>>[1],
255
+ signal?: AbortSignal,
256
+ ) => provider.refusalFallbackFor?.(model, refusal, signal) ?? Promise.resolve(undefined),
257
+ }
258
+ : {}),
250
259
  ...(provider.resolveContextWindow
251
260
  ? {
252
261
  resolveContextWindow: (model: string, signal?: AbortSignal) =>
@@ -49,11 +49,19 @@ export class MockLLMProvider implements LLMProvider {
49
49
  /** Every request this provider has received, in order. */
50
50
  readonly requests: ChatCompletionParams[] = []
51
51
 
52
+ /** Present only when the script names a model to retry a refusal on. */
53
+ readonly refusalFallbackFor?: (model: string) => Promise<string | undefined>
54
+
52
55
  constructor(config: MockScript = {}) {
53
56
  this.capabilities = config.capabilities
54
57
  this.model = config.model ?? FALLBACK_MOCK_MODEL
55
58
  this.responseDelayMs = config.responseDelayMs ?? 0
56
59
  this.onRequest = config.onRequest
60
+ const refusalFallbackModel = config.refusalFallbackModel
61
+ if (refusalFallbackModel !== undefined) {
62
+ this.refusalFallbackFor = async (model) =>
63
+ model === refusalFallbackModel ? undefined : refusalFallbackModel
64
+ }
57
65
  this.nextTurn = config.nextTurn
58
66
  this.turns = config.turns ?? [{ text: config.responseText ?? 'Mock provider response' }] // Back-compat: the old single-string config becomes a one-turn script.
59
67
  }
@@ -158,18 +166,20 @@ export class MockLLMProvider implements LLMProvider {
158
166
  yield { id, delta: { toolCallEnd: { index, id: callId } } }
159
167
  }
160
168
 
169
+ const finishReason: NonNullable<StreamChunk['finishReason']> =
170
+ turn.finishReason ??
171
+ // A call cut off mid-JSON is what the output limit does, and the
172
+ // finish reason is how a consumer tells that from malformed JSON.
173
+ (toolCalls.some((call) => call.truncateArguments === true)
174
+ ? 'length'
175
+ : toolCalls.length > 0
176
+ ? 'tool_calls'
177
+ : 'stop')
161
178
  yield {
162
179
  id,
163
180
  delta: {},
164
- finishReason:
165
- turn.finishReason ??
166
- // A call cut off mid-JSON is what the output limit does, and the
167
- // finish reason is how a consumer tells that from malformed JSON.
168
- (toolCalls.some((call) => call.truncateArguments === true)
169
- ? 'length'
170
- : toolCalls.length > 0
171
- ? 'tool_calls'
172
- : 'stop'),
181
+ finishReason,
182
+ ...(finishReason === 'content_filter' ? { refusal: turn.refusal ?? {} } : {}),
173
183
  usage: { ...EMPTY_USAGE, ...turn.usage },
174
184
  }
175
185
  }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Keeps the `[1m]` window marker off the wire.
3
+ *
4
+ * A model id ending in `[1m]` declares a one-million-token window to Namzu
5
+ * (see `splitModelWindowSuffix`). The marker belongs to the person and the UI,
6
+ * not to the vendor, so every driver and every request sees the bare id. This
7
+ * wraps a provider once, where it is built, so no caller that holds the marked
8
+ * id (a main model, a sub-agent, a side-job model, a fallback chain member)
9
+ * has to remember to strip it.
10
+ */
11
+
12
+ import { stripModelWindowSuffix } from '../compaction/context-window.js'
13
+ import type { LLMProvider } from '../types/provider/interface.js'
14
+
15
+ /** Provider methods whose first argument is a model id. */
16
+ const MODEL_FIRST_METHODS: ReadonlySet<string> = new Set([
17
+ 'supportsHostedWebSearchFor',
18
+ 'supportsImageInputFor',
19
+ 'reasoningEffortLevelsFor',
20
+ 'reasoningEffortDefaultFor',
21
+ 'effortLevelsFor',
22
+ 'resolveContextWindow',
23
+ 'refusalFallbackFor',
24
+ 'healthCheck',
25
+ 'doctorCheck',
26
+ ])
27
+
28
+ function stripParams(params: unknown): unknown {
29
+ if (typeof params !== 'object' || params === null) return params
30
+ const model = (params as { model?: unknown }).model
31
+ if (typeof model !== 'string') return params
32
+ const bare = stripModelWindowSuffix(model)
33
+ return bare === model ? params : { ...params, model: bare }
34
+ }
35
+
36
+ const WRAPPED = Symbol.for('namzu.modelWindowSuffixStripped')
37
+
38
+ type AnyMethod = (...args: unknown[]) => unknown
39
+
40
+ function wrapMethod(name: string, method: AnyMethod): AnyMethod | undefined {
41
+ if (name === 'chatStream' || name === 'chat') {
42
+ return function (this: unknown, params: unknown, ...rest: unknown[]) {
43
+ return method.call(this, stripParams(params), ...rest)
44
+ }
45
+ }
46
+ if (MODEL_FIRST_METHODS.has(name)) {
47
+ return function (this: unknown, model: unknown, ...rest: unknown[]) {
48
+ return method.call(
49
+ this,
50
+ typeof model === 'string' ? stripModelWindowSuffix(model) : model,
51
+ ...rest,
52
+ )
53
+ }
54
+ }
55
+ return undefined
56
+ }
57
+
58
+ /**
59
+ * The same provider, now stripping the marker from every model it is handed.
60
+ * The instance is patched in place (not proxied) so a host that keys state on
61
+ * the provider object keeps finding it; wrapping twice changes nothing.
62
+ */
63
+ export function withModelWindowSuffixStripped<T extends LLMProvider>(provider: T): T {
64
+ const target = provider as unknown as Record<PropertyKey, unknown>
65
+ if (target[WRAPPED] === true) return provider
66
+ if (!Object.isExtensible(provider)) {
67
+ return new Proxy(provider, {
68
+ get(object, property) {
69
+ const value: unknown = Reflect.get(object, property, object)
70
+ if (typeof value !== 'function') return value
71
+ const wrapped =
72
+ typeof property === 'string' ? wrapMethod(property, value as AnyMethod) : undefined
73
+ return (wrapped ?? (value as AnyMethod)).bind(object)
74
+ },
75
+ })
76
+ }
77
+ for (const name of ['chatStream', 'chat', ...MODEL_FIRST_METHODS]) {
78
+ const method = target[name]
79
+ if (typeof method !== 'function') continue
80
+ const wrapped = wrapMethod(name, method as AnyMethod)
81
+ if (wrapped) target[name] = wrapped
82
+ }
83
+ Object.defineProperty(provider, WRAPPED, { value: true, enumerable: false })
84
+ return provider
85
+ }
@@ -417,6 +417,15 @@ export function withProviderRetry(
417
417
  ) => provider.effortLevelsFor?.(model, thinking) ?? [],
418
418
  }
419
419
  : {}),
420
+ ...(provider.refusalFallbackFor
421
+ ? {
422
+ refusalFallbackFor: (
423
+ model: string,
424
+ refusal: Parameters<NonNullable<LLMProvider['refusalFallbackFor']>>[1],
425
+ signal?: AbortSignal,
426
+ ) => provider.refusalFallbackFor?.(model, refusal, signal) ?? Promise.resolve(undefined),
427
+ }
428
+ : {}),
420
429
  ...(provider.resolveContextWindow
421
430
  ? {
422
431
  resolveContextWindow: (model: string, signal?: AbortSignal) =>
@@ -24,11 +24,17 @@ export { WorkerCodeRuntime } from './execution/code-runtime/worker.js'
24
24
  // interpreter above, with `agent()` as its only host call.
25
25
  export {
26
26
  FileWorkflowJournal,
27
+ formatWorkflowJournalLine,
28
+ loadWorkflowResume,
27
29
  parseWorkflowMeta,
30
+ readWorkflowJournalChecked,
28
31
  resolveWorkflowLimits,
29
32
  summarizeWorkflowPhases,
30
33
  WorkflowRuntime,
31
34
  WorkflowScriptError,
35
+ workflowCallKey,
36
+ workflowJournalLineHash,
37
+ workflowJournalSeed,
32
38
  } from './execution/workflow/index.js'
33
39
 
34
40
  export {
@@ -608,7 +614,7 @@ export { resolveSessionLogReplay } from './types/session/log-cursor.js'
608
614
 
609
615
  // Commands a HOST offers its operator. Deliberately NOT tools: no
610
616
  // descriptor reaches a provider and no dispatch path reaches the model — a
611
- // `/tasks` readout is a question the operator asked, and making it callable
617
+ // `/skills` readout is a question the operator asked, and making it callable
612
618
  // would let the model spend a turn on it and record the output as a finding.
613
619
  export {
614
620
  HostCommandNameCollisionError,
@@ -1109,6 +1115,7 @@ export { wrapProviderWithProbes } from './provider/instrumentation.js'
1109
1115
  export type { ProviderInstrumentationOptions } from './provider/instrumentation.js'
1110
1116
  export type { ResolvedProviderCapabilities } from './provider/capabilities.js'
1111
1117
  export { collectChatCompletion } from './provider/collect-chat-completion.js'
1118
+ export { withModelWindowSuffixStripped } from './provider/model-window-suffix.js'
1112
1119
 
1113
1120
  // Old spellings of renamed exports, still live for one deprecation window.
1114
1121
  // The file explains why each is a `const`+`type` pair rather than a
@@ -1591,7 +1598,18 @@ export {
1591
1598
  // consumer (`SpanProcessorLike`, then `CompactNowInput` and `CompactionResult`).
1592
1599
  // The gate is the reason this is a list rather than a fourth accident.
1593
1600
  export type { ResolvedContextWindow } from './compaction/context-window.js'
1594
- export { resolveTriggerThreshold } from './compaction/context-window.js'
1601
+ export {
1602
+ AUTO_COMPACT_WINDOW_MAX_TOKENS,
1603
+ AUTO_COMPACT_WINDOW_MIN_TOKENS,
1604
+ ONE_MILLION_WINDOW_TOKENS,
1605
+ oneMillionWindowDisabled,
1606
+ parseAutoCompactWindow,
1607
+ resolveAutoCompactWindowSetting,
1608
+ resolveTriggerThreshold,
1609
+ splitModelWindowSuffix,
1610
+ stripModelWindowSuffix,
1611
+ } from './compaction/context-window.js'
1612
+ export type { AutoCompactWindowParse } from './compaction/context-window.js'
1595
1613
  // What the context window is made of, for a host that draws the answer to
1596
1614
  // "where did my context go".
1597
1615
  export {
@@ -1744,6 +1762,7 @@ export {
1744
1762
  ChildSessionMetaSchema,
1745
1763
  CompactionRecordSchema,
1746
1764
  ExternalRefSchema,
1765
+ IgnorableRecordSchema,
1747
1766
  MessageRecordSchema,
1748
1767
  MessageReplacedRecordSchema,
1749
1768
  OriginSchema,
@@ -35,6 +35,7 @@ export type * from './types/job/index.js'
35
35
  export type * from './execution/workflow/types.js'
36
36
  export type { PhaseAgentView, PhaseRecord } from './execution/workflow/phases.js'
37
37
  export type { WorkflowRuntimeOptions } from './execution/workflow/runtime.js'
38
+ export type { WorkflowCallIdentity } from './execution/workflow/resume.js'
38
39
  export type { WorkerCodeRuntimeOptions } from './execution/code-runtime/worker.js'
39
40
  export type {
40
41
  CodeRunOutcome,
@@ -12,14 +12,13 @@ import type { GoalRef, SessionGoal } from '../../types/goal/index.js'
12
12
  import type { SessionId, TenantId } from '../../types/ids/index.js'
13
13
  import type { Skill } from '../../types/skills/index.js'
14
14
  import { isInvocableBy, skillInvocation } from '../../types/skills/index.js'
15
- import type { TaskStore } from '../../types/task/index.js'
16
15
 
17
16
  /**
18
17
  * The commands whose facts the kernel already owns.
19
18
  *
20
19
  * A registry with nothing in it is a declaration, and this repo has a rule
21
20
  * about those. These are facts a host would otherwise have to reach into
22
- * kernel internals to answer: the current Session goal, tracked work,
21
+ * kernel internals to answer: the current Session goal,
23
22
  * operator-invocable skills, and the agents this turn may delegate to. Each is
24
23
  * computed or owned here and should not acquire a second host-specific source.
25
24
  *
@@ -31,8 +30,6 @@ import type { TaskStore } from '../../types/task/index.js'
31
30
  */
32
31
 
33
32
  export interface KernelCommandOptions {
34
- /** Absent is a real state — not every turn tracks tasks. */
35
- readonly taskStore?: TaskStore
36
33
  readonly allowedAgentIds?: readonly string[]
37
34
  /**
38
35
  * The turn's skills, for the operator-facing half of the listing.
@@ -263,34 +260,6 @@ export function kernelHostCommands(options: KernelCommandOptions): HostCommandDe
263
260
  hint: GOAL_ARGUMENTS,
264
261
  handler: async ({ args }) => await runGoalCommand(options.goal, args),
265
262
  },
266
- {
267
- name: 'tasks',
268
- description: 'List the work this turn is tracking.',
269
- hint: 'id, status, owner and subject for every task in the store',
270
- async handler() {
271
- // REFUSED rather than an empty report. "There are no tasks" and
272
- // "this turn has no task store" are different answers, and a host
273
- // that shows the first for the second gives an operator a
274
- // confident zero nobody computed.
275
- if (!options.taskStore) {
276
- return {
277
- kind: 'refused',
278
- reason: 'This turn has no task store, so there is nothing to list.',
279
- }
280
- }
281
- const tasks = await options.taskStore.list()
282
- return {
283
- kind: 'report',
284
- title: 'Tasks',
285
- rows: tasks.map((task) => ({
286
- id: task.id,
287
- status: task.status,
288
- owner: task.owner ?? null,
289
- subject: task.subject,
290
- })),
291
- }
292
- },
293
- },
294
263
  {
295
264
  name: 'skills',
296
265
  description: 'List the skills an operator may invoke.',
@@ -327,6 +327,17 @@ export class AwaitedJobs {
327
327
  this.exits = []
328
328
  }
329
329
 
330
+ /**
331
+ * Say that a tool result has handed the model this job's exit and output.
332
+ * Its entry goes, so a hold is not opened for a result the model has read;
333
+ * the notice for the same exit is withdrawn by the caller.
334
+ */
335
+ collected(id: string): void {
336
+ this.outstanding.delete(id)
337
+ this.exits = this.exits.filter((job) => job.id !== id)
338
+ for (const wake of [...this.arrivals]) wake()
339
+ }
340
+
330
341
  /**
331
342
  * Take the recorded exits together with the text that delivers them, or
332
343
  * take neither.
@@ -0,0 +1,67 @@
1
+ import { killTrees } from '../../process/kill-tree.js'
2
+
3
+ /**
4
+ * What the host's exit has to stop: a job's process, and how to signal it.
5
+ */
6
+ export interface HostExitTarget {
7
+ readonly child: { readonly pid?: number | undefined }
8
+ /** The spawner's own kill, when it gave one; otherwise the process group is signalled. */
9
+ readonly kill?: (signal: NodeJS.Signals) => void
10
+ }
11
+
12
+ const live = new Set<HostExitTarget>()
13
+ let installed = false
14
+
15
+ /**
16
+ * Runs when the host process is about to end, by whatever road: `process.exit`,
17
+ * an uncaught exception, the event loop emptying. It is synchronous on purpose.
18
+ * An `exit` listener cannot await, so it starts no promise and no timer, signals
19
+ * each live job with SIGKILL (its own kill when it gave one, then its process
20
+ * group and any descendant found by the environment tag), and does not wait for any of them
21
+ * to stop or claim that they have. A job that was already stopped has been
22
+ * untracked and is not touched.
23
+ *
24
+ * It does not cover a SIGKILL of the host itself, which no listener can see.
25
+ */
26
+ function finalizeAtHostExit(): void {
27
+ const children: HostExitTarget['child'][] = []
28
+ for (const target of live) {
29
+ children.push(target.child)
30
+ try {
31
+ target.kill?.('SIGKILL')
32
+ } catch {
33
+ // The process is already gone; the host is exiting either way.
34
+ }
35
+ }
36
+ live.clear()
37
+ try {
38
+ // One pass over the machine's processes for all the jobs, not one each.
39
+ // Every job goes through it, whether or not it has a kill of its own, so
40
+ // a descendant that left the process group is found in both cases.
41
+ killTrees(children, 'SIGKILL')
42
+ } catch {
43
+ // As above.
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Track a running job so the host's exit stops it. Returns the untrack, to be
49
+ * called once the job has ended. The listener is installed once per process, on
50
+ * the first job, and is put first so a later listener that throws cannot
51
+ * keep it from running.
52
+ */
53
+ export function stopAtHostExit(target: HostExitTarget): () => void {
54
+ live.add(target)
55
+ if (!installed) {
56
+ installed = true
57
+ process.prependListener('exit', finalizeAtHostExit)
58
+ }
59
+ return () => {
60
+ live.delete(target)
61
+ }
62
+ }
63
+
64
+ /** Jobs the host's exit would stop. For tests. */
65
+ export function trackedForHostExit(): number {
66
+ return live.size
67
+ }