open-multi-agent-kit 0.98.5 → 1.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 (385) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +4 -3
  3. package/dist/bun/cli.d.ts.map +1 -1
  4. package/dist/bun/cli.js +1 -0
  5. package/dist/bun/cli.js.map +1 -1
  6. package/dist/bun/register-bundled-coding-agent.d.ts +10 -0
  7. package/dist/bun/register-bundled-coding-agent.d.ts.map +1 -0
  8. package/dist/bun/register-bundled-coding-agent.js +12 -0
  9. package/dist/bun/register-bundled-coding-agent.js.map +1 -0
  10. package/dist/cli/args.d.ts +1 -1
  11. package/dist/cli/args.d.ts.map +1 -1
  12. package/dist/cli/args.js +1 -1
  13. package/dist/cli/args.js.map +1 -1
  14. package/dist/cli/help.d.ts.map +1 -1
  15. package/dist/cli/help.js +2 -1
  16. package/dist/cli/help.js.map +1 -1
  17. package/dist/cli.d.ts.map +1 -1
  18. package/dist/cli.js +14 -2
  19. package/dist/cli.js.map +1 -1
  20. package/dist/commands/neo-cli.d.ts +9 -0
  21. package/dist/commands/neo-cli.d.ts.map +1 -0
  22. package/dist/commands/neo-cli.js +61 -0
  23. package/dist/commands/neo-cli.js.map +1 -0
  24. package/dist/commands/verified-run-cli.d.ts.map +1 -1
  25. package/dist/commands/verified-run-cli.js +2 -2
  26. package/dist/commands/verified-run-cli.js.map +1 -1
  27. package/dist/core/agent-session.d.ts +29 -2
  28. package/dist/core/agent-session.d.ts.map +1 -1
  29. package/dist/core/agent-session.js +126 -32
  30. package/dist/core/agent-session.js.map +1 -1
  31. package/dist/core/bundled-skills.d.ts +4 -0
  32. package/dist/core/bundled-skills.d.ts.map +1 -0
  33. package/dist/core/bundled-skills.js +32 -0
  34. package/dist/core/bundled-skills.js.map +1 -0
  35. package/dist/core/cli-diagnostics.d.ts +6 -0
  36. package/dist/core/cli-diagnostics.d.ts.map +1 -0
  37. package/dist/core/cli-diagnostics.js +20 -0
  38. package/dist/core/cli-diagnostics.js.map +1 -0
  39. package/dist/core/context-budget-headroom.d.ts +12 -0
  40. package/dist/core/context-budget-headroom.d.ts.map +1 -1
  41. package/dist/core/context-budget-headroom.js +35 -0
  42. package/dist/core/context-budget-headroom.js.map +1 -1
  43. package/dist/core/context-budget-v2-input-validation.d.ts +4 -0
  44. package/dist/core/context-budget-v2-input-validation.d.ts.map +1 -0
  45. package/dist/core/context-budget-v2-input-validation.js +79 -0
  46. package/dist/core/context-budget-v2-input-validation.js.map +1 -0
  47. package/dist/core/context-budget-v2-observability.d.ts +15 -0
  48. package/dist/core/context-budget-v2-observability.d.ts.map +1 -0
  49. package/dist/core/context-budget-v2-observability.js +35 -0
  50. package/dist/core/context-budget-v2-observability.js.map +1 -0
  51. package/dist/core/context-budget-v2-planned-items.d.ts +5 -0
  52. package/dist/core/context-budget-v2-planned-items.d.ts.map +1 -0
  53. package/dist/core/context-budget-v2-planned-items.js +40 -0
  54. package/dist/core/context-budget-v2-planned-items.js.map +1 -0
  55. package/dist/core/context-budget-v2-planner.d.ts.map +1 -1
  56. package/dist/core/context-budget-v2-planner.js +57 -38
  57. package/dist/core/context-budget-v2-planner.js.map +1 -1
  58. package/dist/core/context-budget-v2-selection.d.ts +19 -3
  59. package/dist/core/context-budget-v2-selection.d.ts.map +1 -1
  60. package/dist/core/context-budget-v2-selection.js +35 -57
  61. package/dist/core/context-budget-v2-selection.js.map +1 -1
  62. package/dist/core/context-budget-v2-tiers.d.ts.map +1 -1
  63. package/dist/core/context-budget-v2-tiers.js +9 -42
  64. package/dist/core/context-budget-v2-tiers.js.map +1 -1
  65. package/dist/core/context-budget-v2-types.d.ts +8 -1
  66. package/dist/core/context-budget-v2-types.d.ts.map +1 -1
  67. package/dist/core/context-budget-v2-types.js.map +1 -1
  68. package/dist/core/devin-harness-dispatch.d.ts +12 -0
  69. package/dist/core/devin-harness-dispatch.d.ts.map +1 -0
  70. package/dist/core/devin-harness-dispatch.js +12 -0
  71. package/dist/core/devin-harness-dispatch.js.map +1 -0
  72. package/dist/core/devin-harness.d.ts +53 -0
  73. package/dist/core/devin-harness.d.ts.map +1 -0
  74. package/dist/core/devin-harness.js +112 -0
  75. package/dist/core/devin-harness.js.map +1 -0
  76. package/dist/core/domain-dispatch.d.ts +4 -1
  77. package/dist/core/domain-dispatch.d.ts.map +1 -1
  78. package/dist/core/domain-dispatch.js +5 -0
  79. package/dist/core/domain-dispatch.js.map +1 -1
  80. package/dist/core/domain-loadouts-provider-harness.d.ts +12 -0
  81. package/dist/core/domain-loadouts-provider-harness.d.ts.map +1 -0
  82. package/dist/core/domain-loadouts-provider-harness.js +122 -0
  83. package/dist/core/domain-loadouts-provider-harness.js.map +1 -0
  84. package/dist/core/domain-loadouts.d.ts +2 -33
  85. package/dist/core/domain-loadouts.d.ts.map +1 -1
  86. package/dist/core/domain-loadouts.js +3 -56
  87. package/dist/core/domain-loadouts.js.map +1 -1
  88. package/dist/core/domain-profile.d.ts +40 -0
  89. package/dist/core/domain-profile.d.ts.map +1 -0
  90. package/dist/core/domain-profile.js +7 -0
  91. package/dist/core/domain-profile.js.map +1 -0
  92. package/dist/core/extensions/bundled-virtual-modules.d.ts +15 -0
  93. package/dist/core/extensions/bundled-virtual-modules.d.ts.map +1 -0
  94. package/dist/core/extensions/bundled-virtual-modules.js +63 -0
  95. package/dist/core/extensions/bundled-virtual-modules.js.map +1 -0
  96. package/dist/core/extensions/loader.d.ts.map +1 -1
  97. package/dist/core/extensions/loader.js +2 -42
  98. package/dist/core/extensions/loader.js.map +1 -1
  99. package/dist/core/extensions/runner.d.ts +1 -0
  100. package/dist/core/extensions/runner.d.ts.map +1 -1
  101. package/dist/core/extensions/runner.js +6 -0
  102. package/dist/core/extensions/runner.js.map +1 -1
  103. package/dist/core/extensions/types.d.ts +10 -0
  104. package/dist/core/extensions/types.d.ts.map +1 -1
  105. package/dist/core/extensions/types.js.map +1 -1
  106. package/dist/core/grok-harness-dispatch.d.ts +6 -20
  107. package/dist/core/grok-harness-dispatch.d.ts.map +1 -1
  108. package/dist/core/grok-harness-dispatch.js +6 -55
  109. package/dist/core/grok-harness-dispatch.js.map +1 -1
  110. package/dist/core/grok-harness.d.ts +7 -9
  111. package/dist/core/grok-harness.d.ts.map +1 -1
  112. package/dist/core/grok-harness.js +9 -23
  113. package/dist/core/grok-harness.js.map +1 -1
  114. package/dist/core/harness-skills.d.ts +20 -0
  115. package/dist/core/harness-skills.d.ts.map +1 -0
  116. package/dist/core/harness-skills.js +36 -0
  117. package/dist/core/harness-skills.js.map +1 -0
  118. package/dist/core/index.d.ts +2 -1
  119. package/dist/core/index.d.ts.map +1 -1
  120. package/dist/core/index.js +2 -1
  121. package/dist/core/index.js.map +1 -1
  122. package/dist/core/loadout-runtime-state.d.ts +25 -0
  123. package/dist/core/loadout-runtime-state.d.ts.map +1 -0
  124. package/dist/core/loadout-runtime-state.js +39 -0
  125. package/dist/core/loadout-runtime-state.js.map +1 -0
  126. package/dist/core/loadout-runtime.d.ts +4 -20
  127. package/dist/core/loadout-runtime.d.ts.map +1 -1
  128. package/dist/core/loadout-runtime.js +6 -13
  129. package/dist/core/loadout-runtime.js.map +1 -1
  130. package/dist/core/mcp/client.d.ts +27 -6
  131. package/dist/core/mcp/client.d.ts.map +1 -1
  132. package/dist/core/mcp/client.js +80 -20
  133. package/dist/core/mcp/client.js.map +1 -1
  134. package/dist/core/mcp/manager.d.ts +10 -1
  135. package/dist/core/mcp/manager.d.ts.map +1 -1
  136. package/dist/core/mcp/manager.js +68 -9
  137. package/dist/core/mcp/manager.js.map +1 -1
  138. package/dist/core/mcp/protocol.d.ts +9 -3
  139. package/dist/core/mcp/protocol.d.ts.map +1 -1
  140. package/dist/core/mcp/protocol.js +61 -15
  141. package/dist/core/mcp/protocol.js.map +1 -1
  142. package/dist/core/mcp/stdio-transport.d.ts +12 -1
  143. package/dist/core/mcp/stdio-transport.d.ts.map +1 -1
  144. package/dist/core/mcp/stdio-transport.js +30 -2
  145. package/dist/core/mcp/stdio-transport.js.map +1 -1
  146. package/dist/core/mcp-descriptor-injection.d.ts +26 -0
  147. package/dist/core/mcp-descriptor-injection.d.ts.map +1 -0
  148. package/dist/core/mcp-descriptor-injection.js +25 -0
  149. package/dist/core/mcp-descriptor-injection.js.map +1 -0
  150. package/dist/core/mcp-public-presets.d.ts +1 -3
  151. package/dist/core/mcp-public-presets.d.ts.map +1 -1
  152. package/dist/core/mcp-public-presets.js +3 -15
  153. package/dist/core/mcp-public-presets.js.map +1 -1
  154. package/dist/core/model-registry.d.ts.map +1 -1
  155. package/dist/core/model-registry.js +24 -3
  156. package/dist/core/model-registry.js.map +1 -1
  157. package/dist/core/model-resolver.d.ts +1 -41
  158. package/dist/core/model-resolver.d.ts.map +1 -1
  159. package/dist/core/model-resolver.js +2 -49
  160. package/dist/core/model-resolver.js.map +1 -1
  161. package/dist/core/neo/catalog.d.ts +20 -0
  162. package/dist/core/neo/catalog.d.ts.map +1 -0
  163. package/dist/core/neo/catalog.js +50 -0
  164. package/dist/core/neo/catalog.js.map +1 -0
  165. package/dist/core/neo/setup.d.ts +3 -0
  166. package/dist/core/neo/setup.d.ts.map +1 -0
  167. package/dist/core/neo/setup.js +47 -0
  168. package/dist/core/neo/setup.js.map +1 -0
  169. package/dist/core/provider-default-models.d.ts +43 -0
  170. package/dist/core/provider-default-models.d.ts.map +1 -0
  171. package/dist/core/provider-default-models.js +51 -0
  172. package/dist/core/provider-default-models.js.map +1 -0
  173. package/dist/core/provider-display-names.d.ts.map +1 -1
  174. package/dist/core/provider-display-names.js +2 -0
  175. package/dist/core/provider-display-names.js.map +1 -1
  176. package/dist/core/provider-error-classification.d.ts +57 -0
  177. package/dist/core/provider-error-classification.d.ts.map +1 -0
  178. package/dist/core/provider-error-classification.js +103 -0
  179. package/dist/core/provider-error-classification.js.map +1 -0
  180. package/dist/core/provider-harness-dispatch.d.ts +63 -0
  181. package/dist/core/provider-harness-dispatch.d.ts.map +1 -0
  182. package/dist/core/provider-harness-dispatch.js +60 -0
  183. package/dist/core/provider-harness-dispatch.js.map +1 -0
  184. package/dist/core/provider-resilience.d.ts +10 -0
  185. package/dist/core/provider-resilience.d.ts.map +1 -1
  186. package/dist/core/provider-resilience.js +36 -3
  187. package/dist/core/provider-resilience.js.map +1 -1
  188. package/dist/core/provider-usage-commandcode.d.ts +9 -0
  189. package/dist/core/provider-usage-commandcode.d.ts.map +1 -0
  190. package/dist/core/provider-usage-commandcode.js +198 -0
  191. package/dist/core/provider-usage-commandcode.js.map +1 -0
  192. package/dist/core/provider-usage-devin.d.ts +18 -0
  193. package/dist/core/provider-usage-devin.d.ts.map +1 -0
  194. package/dist/core/provider-usage-devin.js +71 -0
  195. package/dist/core/provider-usage-devin.js.map +1 -0
  196. package/dist/core/provider-usage-text.d.ts +5 -0
  197. package/dist/core/provider-usage-text.d.ts.map +1 -0
  198. package/dist/core/provider-usage-text.js +15 -0
  199. package/dist/core/provider-usage-text.js.map +1 -0
  200. package/dist/core/provider-usage-types.d.ts +2 -1
  201. package/dist/core/provider-usage-types.d.ts.map +1 -1
  202. package/dist/core/provider-usage-types.js.map +1 -1
  203. package/dist/core/provider-usage.d.ts +1 -2
  204. package/dist/core/provider-usage.d.ts.map +1 -1
  205. package/dist/core/provider-usage.js +18 -7
  206. package/dist/core/provider-usage.js.map +1 -1
  207. package/dist/core/resource-admission.d.ts +36 -0
  208. package/dist/core/resource-admission.d.ts.map +1 -1
  209. package/dist/core/resource-admission.js +59 -0
  210. package/dist/core/resource-admission.js.map +1 -1
  211. package/dist/core/resource-loader.d.ts.map +1 -1
  212. package/dist/core/resource-loader.js +2 -2
  213. package/dist/core/resource-loader.js.map +1 -1
  214. package/dist/core/run-execution-api.d.ts +1 -1
  215. package/dist/core/run-execution-api.d.ts.map +1 -1
  216. package/dist/core/run-execution-api.js.map +1 -1
  217. package/dist/core/run-usage-ledger.d.ts +47 -0
  218. package/dist/core/run-usage-ledger.d.ts.map +1 -0
  219. package/dist/core/run-usage-ledger.js +162 -0
  220. package/dist/core/run-usage-ledger.js.map +1 -0
  221. package/dist/core/run-usage-operation.d.ts +8 -0
  222. package/dist/core/run-usage-operation.d.ts.map +1 -0
  223. package/dist/core/run-usage-operation.js +12 -0
  224. package/dist/core/run-usage-operation.js.map +1 -0
  225. package/dist/core/sdk.d.ts.map +1 -1
  226. package/dist/core/sdk.js +20 -16
  227. package/dist/core/sdk.js.map +1 -1
  228. package/dist/core/session-failure-cause.d.ts.map +1 -1
  229. package/dist/core/session-failure-cause.js +14 -5
  230. package/dist/core/session-failure-cause.js.map +1 -1
  231. package/dist/core/session-prompt-lifecycle.d.ts +6 -0
  232. package/dist/core/session-prompt-lifecycle.d.ts.map +1 -1
  233. package/dist/core/session-prompt-lifecycle.js +47 -2
  234. package/dist/core/session-prompt-lifecycle.js.map +1 -1
  235. package/dist/core/session-termination.d.ts.map +1 -1
  236. package/dist/core/session-termination.js +4 -2
  237. package/dist/core/session-termination.js.map +1 -1
  238. package/dist/core/subagent-lane-authority.d.ts +28 -0
  239. package/dist/core/subagent-lane-authority.d.ts.map +1 -0
  240. package/dist/core/subagent-lane-authority.js +119 -0
  241. package/dist/core/subagent-lane-authority.js.map +1 -0
  242. package/dist/core/subagent-lane-contract.d.ts +114 -0
  243. package/dist/core/subagent-lane-contract.d.ts.map +1 -0
  244. package/dist/core/subagent-lane-contract.js +18 -0
  245. package/dist/core/subagent-lane-contract.js.map +1 -0
  246. package/dist/core/subagent-lane-launcher.d.ts +3 -6
  247. package/dist/core/subagent-lane-launcher.d.ts.map +1 -1
  248. package/dist/core/subagent-lane-launcher.js +53 -12
  249. package/dist/core/subagent-lane-launcher.js.map +1 -1
  250. package/dist/core/subagent-orchestration.d.ts +4 -17
  251. package/dist/core/subagent-orchestration.d.ts.map +1 -1
  252. package/dist/core/subagent-orchestration.js.map +1 -1
  253. package/dist/core/verified-run/broker.d.ts.map +1 -1
  254. package/dist/core/verified-run/broker.js +4 -1
  255. package/dist/core/verified-run/broker.js.map +1 -1
  256. package/dist/core/verified-run/dag-phase.d.ts +1 -1
  257. package/dist/core/verified-run/dag-phase.d.ts.map +1 -1
  258. package/dist/core/verified-run/dag-phase.js +46 -8
  259. package/dist/core/verified-run/dag-phase.js.map +1 -1
  260. package/dist/core/verified-run/dag-projection.d.ts.map +1 -1
  261. package/dist/core/verified-run/dag-projection.js +25 -12
  262. package/dist/core/verified-run/dag-projection.js.map +1 -1
  263. package/dist/core/verified-run/dag-types.d.ts +11 -0
  264. package/dist/core/verified-run/dag-types.d.ts.map +1 -1
  265. package/dist/core/verified-run/dag-types.js.map +1 -1
  266. package/dist/core/verified-run/event-parser.d.ts.map +1 -1
  267. package/dist/core/verified-run/event-parser.js +1 -0
  268. package/dist/core/verified-run/event-parser.js.map +1 -1
  269. package/dist/core/verified-run/owned-execution.d.ts +1 -0
  270. package/dist/core/verified-run/owned-execution.d.ts.map +1 -1
  271. package/dist/core/verified-run/owned-execution.js +7 -1
  272. package/dist/core/verified-run/owned-execution.js.map +1 -1
  273. package/dist/core/verified-run/process-projection.d.ts +13 -0
  274. package/dist/core/verified-run/process-projection.d.ts.map +1 -0
  275. package/dist/core/verified-run/process-projection.js +82 -0
  276. package/dist/core/verified-run/process-projection.js.map +1 -0
  277. package/dist/core/verified-run/projection.d.ts.map +1 -1
  278. package/dist/core/verified-run/projection.js +11 -55
  279. package/dist/core/verified-run/projection.js.map +1 -1
  280. package/dist/core/verified-run/run-types.d.ts +1 -0
  281. package/dist/core/verified-run/run-types.d.ts.map +1 -1
  282. package/dist/core/verified-run/run-types.js.map +1 -1
  283. package/dist/core/verified-run/task-execution.d.ts +9 -0
  284. package/dist/core/verified-run/task-execution.d.ts.map +1 -0
  285. package/dist/core/verified-run/task-execution.js +29 -0
  286. package/dist/core/verified-run/task-execution.js.map +1 -0
  287. package/dist/core/verified-run/writer-projection.d.ts.map +1 -1
  288. package/dist/core/verified-run/writer-projection.js +6 -5
  289. package/dist/core/verified-run/writer-projection.js.map +1 -1
  290. package/dist/core/workload-permit-pool.d.ts +1 -1
  291. package/dist/core/workload-permit-pool.d.ts.map +1 -1
  292. package/dist/core/workload-permit-pool.js +3 -0
  293. package/dist/core/workload-permit-pool.js.map +1 -1
  294. package/dist/guardrails/strict-evidence-approval-adapter.d.ts +34 -0
  295. package/dist/guardrails/strict-evidence-approval-adapter.d.ts.map +1 -0
  296. package/dist/guardrails/strict-evidence-approval-adapter.js +70 -0
  297. package/dist/guardrails/strict-evidence-approval-adapter.js.map +1 -0
  298. package/dist/index.d.ts +3 -0
  299. package/dist/index.d.ts.map +1 -1
  300. package/dist/index.js +1 -0
  301. package/dist/index.js.map +1 -1
  302. package/dist/main.d.ts.map +1 -1
  303. package/dist/main.js +11 -18
  304. package/dist/main.js.map +1 -1
  305. package/dist/modes/acp/acp-agent.d.ts +24 -0
  306. package/dist/modes/acp/acp-agent.d.ts.map +1 -0
  307. package/dist/modes/acp/acp-agent.js +133 -0
  308. package/dist/modes/acp/acp-agent.js.map +1 -0
  309. package/dist/modes/acp/acp-mode.d.ts +4 -0
  310. package/dist/modes/acp/acp-mode.d.ts.map +1 -0
  311. package/dist/modes/acp/acp-mode.js +34 -0
  312. package/dist/modes/acp/acp-mode.js.map +1 -0
  313. package/dist/modes/acp/acp-session.d.ts +4 -0
  314. package/dist/modes/acp/acp-session.d.ts.map +1 -0
  315. package/dist/modes/acp/acp-session.js +76 -0
  316. package/dist/modes/acp/acp-session.js.map +1 -0
  317. package/dist/modes/acp/acp-transport.d.ts +5 -0
  318. package/dist/modes/acp/acp-transport.d.ts.map +1 -0
  319. package/dist/modes/acp/acp-transport.js +90 -0
  320. package/dist/modes/acp/acp-transport.js.map +1 -0
  321. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  322. package/dist/modes/interactive/interactive-mode.js +3 -5
  323. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  324. package/dist/modes/interactive/resource-description.d.ts +3 -0
  325. package/dist/modes/interactive/resource-description.d.ts.map +1 -0
  326. package/dist/modes/interactive/resource-description.js +15 -0
  327. package/dist/modes/interactive/resource-description.js.map +1 -0
  328. package/docs/audit-revalidation-2026-09-17.md +77 -0
  329. package/docs/devin-harness.md +150 -0
  330. package/docs/docs.json +4 -0
  331. package/docs/ecraf-normalization.md +83 -0
  332. package/docs/environment-variables.md +2 -1
  333. package/docs/index.md +1 -0
  334. package/docs/loadout-domains/README.md +2 -1
  335. package/docs/loadout-domains/devin-harness.md +72 -0
  336. package/docs/metrics.md +57 -16
  337. package/docs/model-catalog-refresh.md +51 -1
  338. package/docs/models.md +1 -1
  339. package/docs/neo.md +134 -0
  340. package/docs/providers.md +143 -1
  341. package/docs/release-audit-0.98.5.md +17 -0
  342. package/docs/release-audit-0.99.0.md +68 -0
  343. package/docs/run-protocol.md +4 -3
  344. package/docs/run-usage-ledger.md +56 -0
  345. package/docs/runtime-algorithms.md +48 -1
  346. package/docs/sdk.md +9 -5
  347. package/docs/settings.md +1 -1
  348. package/docs/skills.md +1 -1
  349. package/docs/startup-resource-labels-testing.md +47 -0
  350. package/docs/tb21-audit.md +18 -4
  351. package/docs/usage.md +7 -1
  352. package/docs/verified-run-remaining-design.md +881 -0
  353. package/docs/verified-run-testing.md +91 -0
  354. package/docs/verified-run.md +30 -9
  355. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  356. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  357. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  358. package/examples/extensions/gondolin/package-lock.json +2 -2
  359. package/examples/extensions/gondolin/package.json +1 -1
  360. package/examples/extensions/sandbox/package-lock.json +2 -2
  361. package/examples/extensions/sandbox/package.json +1 -1
  362. package/examples/extensions/subagent/adaptive-agent-runtime.ts +14 -1
  363. package/examples/extensions/subagent/graph-result.ts +48 -0
  364. package/examples/extensions/subagent/index.ts +246 -111
  365. package/examples/extensions/subagent/managed-process-tree.ts +42 -0
  366. package/examples/extensions/subagent/managed-process.test.ts +24 -0
  367. package/examples/extensions/subagent/managed-process.ts +93 -112
  368. package/examples/extensions/subagent/subagent-runtime-types.ts +17 -1
  369. package/examples/extensions/subagent/subagent-stream.ts +161 -0
  370. package/examples/extensions/terminal-browser/README.md +54 -0
  371. package/examples/extensions/terminal-browser/bridge-protocol.ts +29 -0
  372. package/examples/extensions/terminal-browser/bridge.ts +219 -0
  373. package/examples/extensions/terminal-browser/browser-surface.ts +288 -0
  374. package/examples/extensions/terminal-browser/index.ts +215 -0
  375. package/examples/extensions/terminal-browser/placeholders.ts +49 -0
  376. package/examples/extensions/with-deps/package-lock.json +2 -2
  377. package/examples/extensions/with-deps/package.json +1 -1
  378. package/npm-shrinkwrap.json +18 -18
  379. package/package.json +8 -7
  380. package/resources/neo/skills/omk-browser/SKILL.md +32 -0
  381. package/resources/neo/skills/omk-code-review/SKILL.md +24 -0
  382. package/resources/neo/skills/omk-computeruse/SKILL.md +34 -0
  383. package/resources/neo/skills/omk-mcp-setup/SKILL.md +48 -0
  384. package/resources/neo/skills/omk-research/SKILL.md +24 -0
  385. package/resources/neo/skills/omk-site/SKILL.md +28 -0
@@ -13,6 +13,7 @@
13
13
  * Uses JSON mode to capture structured output from subagents.
14
14
  */
15
15
 
16
+ import { randomUUID } from "node:crypto";
16
17
  import * as fs from "node:fs";
17
18
  import * as os from "node:os";
18
19
  import * as path from "node:path";
@@ -42,8 +43,10 @@ import {
42
43
  resolveSubagentExecutionPolicy,
43
44
  } from "./deadline-budget.ts";
44
45
  import { DeadlineProfileStore } from "./deadline-profile-store.ts";
46
+ import { blockedResult, dependencyDigests, failedResult as isFailedResult, laneResult } from "./graph-result.ts";
45
47
  import { runManagedProcess } from "./managed-process.ts";
46
48
  import { emptyUsage, type SingleResult, type SubagentAttemptResult } from "./subagent-runtime-types.ts";
49
+ import { createSubagentStream } from "./subagent-stream.ts";
47
50
  import {
48
51
  type GraphTask,
49
52
  GraphValidationError,
@@ -233,10 +236,6 @@ function getFinalOutput(messages: Message[]): string {
233
236
  return "";
234
237
  }
235
238
 
236
- function isFailedResult(result: SingleResult): boolean {
237
- return result.exitCode !== 0 || result.stopReason === "error" || result.stopReason === "aborted";
238
- }
239
-
240
239
  function getResultOutput(result: SingleResult): string {
241
240
  const partialOutput = result.output || getFinalOutput(result.messages);
242
241
  if (isFailedResult(result)) {
@@ -385,6 +384,7 @@ async function runSingleAgentAttempt(
385
384
  let tmpPromptPath: string | null = null;
386
385
 
387
386
  const currentResult: SingleResult = {
387
+ attemptId: randomUUID(),
388
388
  agent: agentName,
389
389
  agentSource: agent.source,
390
390
  task: logicalTask,
@@ -419,38 +419,7 @@ async function runSingleAgentAttempt(
419
419
  }
420
420
 
421
421
  args.push(`Task: ${task}`);
422
- let buffer = "";
423
- const processLine = (line: string) => {
424
- const event = parseSubagentEvent(line);
425
- if (event === undefined) return;
426
- if (event.type === "message_end" && event.message !== undefined) {
427
- const msg = event.message;
428
- currentResult.messages.push(msg);
429
- if (msg.role === "assistant") {
430
- currentResult.usage.turns += 1;
431
- const usage = msg.usage;
432
- if (usage) {
433
- currentResult.usage.input += usage.input || 0;
434
- currentResult.usage.output += usage.output || 0;
435
- currentResult.usage.cacheRead += usage.cacheRead || 0;
436
- currentResult.usage.cacheWrite += usage.cacheWrite || 0;
437
- currentResult.usage.cost += usage.cost?.total || 0;
438
- currentResult.usage.contextTokens = Math.max(
439
- currentResult.usage.contextTokens,
440
- usage.totalTokens || 0,
441
- );
442
- }
443
- if (!currentResult.model && msg.model) currentResult.model = msg.model;
444
- if (msg.stopReason) currentResult.stopReason = msg.stopReason;
445
- if (msg.errorMessage) currentResult.errorMessage = msg.errorMessage;
446
- }
447
- emitUpdate();
448
- }
449
- if (event.type === "tool_result_end" && event.message !== undefined) {
450
- currentResult.messages.push(event.message);
451
- emitUpdate();
452
- }
453
- };
422
+ const stream = createSubagentStream(currentResult, emitUpdate);
454
423
  const invocation = getOmkInvocation(args);
455
424
  const processResult = await runManagedProcess({
456
425
  command: invocation.command,
@@ -458,17 +427,11 @@ async function runSingleAgentAttempt(
458
427
  cwd: cwd ?? defaultCwd,
459
428
  cutoffMs,
460
429
  signal,
461
- onStdout: (chunk) => {
462
- buffer += chunk;
463
- const lines = buffer.split("\n");
464
- buffer = lines.pop() || "";
465
- for (const line of lines) processLine(line);
466
- },
467
- onStderr: (chunk) => {
468
- currentResult.stderr += chunk;
469
- },
430
+ onStdout: stream.stdout,
431
+ onStderr: stream.stderr,
470
432
  });
471
- if (buffer.trim()) processLine(buffer);
433
+ currentResult.process = processResult;
434
+ const streamFailure = stream.finish(processResult.reason === "completed" && processResult.exitCode === 0);
472
435
  currentResult.exitCode = processResult.exitCode;
473
436
  currentResult.output = getFinalOutput(currentResult.messages);
474
437
  if (processResult.reason === "cutoff") {
@@ -484,6 +447,17 @@ async function runSingleAgentAttempt(
484
447
  currentResult.stopReason = "error";
485
448
  currentResult.errorMessage = processResult.errorMessage ?? "Unable to spawn subagent";
486
449
  }
450
+ if (processResult.reason === "signal" || processResult.reason === "callback-error" || streamFailure) {
451
+ currentResult.exitCode ||= 1;
452
+ currentResult.stopReason = "error";
453
+ currentResult.errorMessage =
454
+ streamFailure ?? processResult.errorMessage ?? `Subagent terminated by ${processResult.signal}`;
455
+ }
456
+ if (!processResult.terminationObserved) {
457
+ currentResult.exitCode ||= 1;
458
+ currentResult.stopReason = "unsettled";
459
+ currentResult.errorMessage = `${currentResult.errorMessage ?? "Subagent termination unconfirmed"} (unsettled)`;
460
+ }
487
461
  return { result: currentResult, process: processResult };
488
462
  } finally {
489
463
  if (tmpPromptPath)
@@ -501,30 +475,6 @@ async function runSingleAgentAttempt(
501
475
  }
502
476
  }
503
477
 
504
- interface SubagentJsonEvent {
505
- readonly type: string;
506
- readonly message?: Message;
507
- }
508
-
509
- function parseSubagentEvent(line: string): SubagentJsonEvent | undefined {
510
- if (line.trim() === "") return undefined;
511
- let value: unknown;
512
- try {
513
- value = JSON.parse(line);
514
- } catch {
515
- return undefined;
516
- }
517
- if (!isRecord(value) || typeof value.type !== "string") return undefined;
518
- const message = value.message;
519
- if (message === undefined) return { type: value.type };
520
- if (!isRecord(message) || typeof message.role !== "string" || !Array.isArray(message.content)) return undefined;
521
- return { type: value.type, message: message as unknown as Message };
522
- }
523
-
524
- function isRecord(value: unknown): value is Record<string, unknown> {
525
- return typeof value === "object" && value !== null && !Array.isArray(value);
526
- }
527
-
528
478
  async function runSingleAgent(
529
479
  defaultCwd: string,
530
480
  agents: AgentConfig[],
@@ -602,6 +552,11 @@ const TaskItem = Type.Object({
602
552
  agent: Type.String({ description: "Name of the agent to invoke" }),
603
553
  task: Type.String({ description: "Task to delegate to the agent" }),
604
554
  cwd: Type.Optional(Type.String({ description: "Working directory for the agent process" })),
555
+ writeScope: Type.Optional(
556
+ Type.Array(Type.String(), {
557
+ description: "Owned write paths for ownership-safe parallel scheduling (spec 020).",
558
+ }),
559
+ ),
605
560
  });
606
561
 
607
562
  const ChainItem = Type.Object({
@@ -616,6 +571,11 @@ const GraphItem = Type.Object({
616
571
  task: Type.String({ description: "Task; use {dependencies} to inject declared dependency outputs" }),
617
572
  cwd: Type.Optional(Type.String({ description: "Working directory for the agent process" })),
618
573
  dependsOn: Type.Optional(Type.Array(Type.String(), { description: "Task node ids that must complete first" })),
574
+ writeScope: Type.Optional(
575
+ Type.Array(Type.String(), {
576
+ description: "Owned write paths for ownership-safe parallel scheduling (spec 020).",
577
+ }),
578
+ ),
619
579
  });
620
580
 
621
581
  const AgentScopeSchema = StringEnum(["user", "project", "both"] as const, {
@@ -830,15 +790,46 @@ export default function (omk: ExtensionAPI) {
830
790
 
831
791
  const results: SingleResult[] = [];
832
792
  const outputs = new Map<string, string>();
793
+ const resultById = new Map<string, SingleResult>();
833
794
  const completedNodeIds: string[] = [];
834
795
  graphDetails = { waves: plan.waves, completedNodeIds };
835
- for (const [waveIndex, wave] of plan.waves.entries()) {
836
- const waveResults = await mapWithConcurrencyLimit<string, { id: string; result: SingleResult }>(
837
- [...wave],
838
- executionPolicy.concurrency,
839
- async (id) => {
840
- const task = plan.tasks.get(id);
841
- if (!task) throw new GraphValidationError(`node '${id}' disappeared after validation`);
796
+
797
+ // Spec 020 Req1 — when the session binds a lane authority, the DAG
798
+ // routes through buildSubagentOrchestrationPlan → launchSubagentLanes.
799
+ // Batches run sequentially, so renderDependencyContext sees completed
800
+ // dependency outputs at launch time.
801
+ const laneAuthority = ctx.getSubagentLaneAuthority?.();
802
+ if (laneAuthority) {
803
+ const nodeById = new Map(plan.tasks);
804
+ const lanes = params.graph.map((node) => ({
805
+ id: node.id,
806
+ role: "executor" as const,
807
+ task: node.task,
808
+ agentName: node.agent,
809
+ dependsOn: node.dependsOn ?? [],
810
+ ...(node.writeScope ? { writeScope: node.writeScope } : {}),
811
+ }));
812
+ const dispatch = await laneAuthority.dispatchLanes({
813
+ lanes,
814
+ spawnPlan: {
815
+ whyParallel: `${params.graph.length} DAG tasks in graph mode`,
816
+ whyNotLocal: "isolated subagent context per node",
817
+ independence: "explicit dependsOn DAG",
818
+ expectedReceiptShape: "per-node SingleResult map",
819
+ maxInlineTokens: 8192,
820
+ },
821
+ configuredMaxParallelLanes: executionPolicy.unbounded ? undefined : executionPolicy.concurrency,
822
+ heavyLaneIds: laneAuthority.getCurrentResourceAdmission()
823
+ ? new Set(lanes.map((lane) => lane.id))
824
+ : undefined,
825
+ signal,
826
+ launchLane: async ({ laneId, signal: laneSignal }) => {
827
+ const task = nodeById.get(laneId);
828
+ if (!task) return { status: "failed" as const };
829
+ if ((task.dependsOn ?? []).some((id) => !outputs.has(id))) {
830
+ resultById.set(laneId, blockedResult(task));
831
+ return { status: "failed" as const };
832
+ }
842
833
  const result = await runSingleAgent(
843
834
  ctx.cwd,
844
835
  agents,
@@ -846,52 +837,104 @@ export default function (omk: ExtensionAPI) {
846
837
  renderDependencyContext(task, outputs),
847
838
  task.cwd,
848
839
  undefined,
849
- signal,
840
+ laneSignal,
850
841
  undefined,
851
842
  makeDetails("graph"),
852
843
  executionPolicy.unbounded,
853
844
  executionBudget,
854
845
  profileStore,
855
- plan.waves.length - waveIndex,
846
+ 0,
856
847
  );
857
- return { id, result };
848
+ result.nodeId = laneId;
849
+ result.dependencyDigests = dependencyDigests(task, outputs);
850
+ resultById.set(laneId, result);
851
+ if (!isFailedResult(result)) {
852
+ outputs.set(laneId, getResultOutput(result));
853
+ completedNodeIds.push(laneId);
854
+ }
855
+ return laneResult(result);
858
856
  },
859
- );
860
- for (const { id, result } of waveResults) {
861
- results.push(result);
862
- if (!isFailedResult(result)) {
863
- outputs.set(id, getResultOutput(result));
864
- completedNodeIds.push(id);
857
+ });
858
+ const outcomes = new Map(dispatch.outcomes.map((outcome) => [outcome.laneId, outcome]));
859
+ for (const [id, task] of plan.tasks) {
860
+ if (!resultById.has(id)) {
861
+ const outcome = outcomes.get(id);
862
+ resultById.set(
863
+ id,
864
+ blockedResult(
865
+ task,
866
+ dispatch.blockers.join("; ") ||
867
+ outcome?.diagnostic ||
868
+ outcome?.status ||
869
+ "blocked-dependency",
870
+ ),
871
+ );
865
872
  }
866
873
  }
867
874
  graphDetails = { waves: plan.waves, completedNodeIds: [...completedNodeIds] };
868
- const failed = waveResults.filter(({ result }) => isFailedResult(result));
869
- if (failed.length > 0) {
870
- const failedIds = failed.map(({ id }) => id).join(", ");
871
- return {
872
- content: [
873
- {
874
- type: "text",
875
- text: `Graph stopped after wave ${waveIndex + 1}; failed nodes: ${failedIds}. Downstream nodes were not started.`,
876
- },
877
- ],
878
- details: makeDetails("graph")(results),
879
- isError: true,
880
- };
875
+ } else {
876
+ for (const [waveIndex, wave] of plan.waves.entries()) {
877
+ const waveResults = await mapWithConcurrencyLimit<string, { id: string; result: SingleResult }>(
878
+ [...wave],
879
+ executionPolicy.concurrency,
880
+ async (id) => {
881
+ const task = plan.tasks.get(id);
882
+ if (!task) throw new GraphValidationError(`node '${id}' disappeared after validation`);
883
+ if ((task.dependsOn ?? []).some((dependency) => !outputs.has(dependency)))
884
+ return { id, result: blockedResult(task) };
885
+ const result = await runSingleAgent(
886
+ ctx.cwd,
887
+ agents,
888
+ task.agent,
889
+ renderDependencyContext(task, outputs),
890
+ task.cwd,
891
+ undefined,
892
+ signal,
893
+ undefined,
894
+ makeDetails("graph"),
895
+ executionPolicy.unbounded,
896
+ executionBudget,
897
+ profileStore,
898
+ plan.waves.length - waveIndex,
899
+ );
900
+ return { id, result };
901
+ },
902
+ );
903
+ for (const { id, result } of waveResults) {
904
+ result.nodeId = id;
905
+ const task = plan.tasks.get(id);
906
+ if (task) result.dependencyDigests = dependencyDigests(task, outputs);
907
+ resultById.set(id, result);
908
+ if (!isFailedResult(result)) {
909
+ outputs.set(id, getResultOutput(result));
910
+ completedNodeIds.push(id);
911
+ }
912
+ }
913
+ graphDetails = { waves: plan.waves, completedNodeIds: [...completedNodeIds] };
914
+ const failed = waveResults.filter(({ result }) => isFailedResult(result));
915
+ if (failed.length > 0) break;
881
916
  }
882
917
  }
883
- const summaries = results.map((result, index) => {
884
- const id = completedNodeIds[index] ?? `node-${index + 1}`;
885
- return `### [${id}] completed\n\n${truncateParallelOutput(getResultOutput(result))}`;
918
+ for (const [id, task] of plan.tasks) results.push(resultById.get(id) ?? blockedResult(task));
919
+ graphDetails = {
920
+ waves: plan.waves,
921
+ completedNodeIds: results
922
+ .filter((result) => !isFailedResult(result))
923
+ .flatMap((result) => (result.nodeId ? [result.nodeId] : [])),
924
+ };
925
+ const summaries = results.map((result) => {
926
+ const status = isFailedResult(result) ? (result.stopReason ?? "failed") : "completed";
927
+ return `### [${result.nodeId}] ${status}\n\n${truncateParallelOutput(getResultOutput(result))}`;
886
928
  });
887
929
  return {
888
930
  content: [
889
931
  {
890
932
  type: "text",
891
- text: `Graph: ${results.length}/${params.graph.length} completed in ${plan.waves.length} waves\n\n${summaries.join("\n\n---\n\n")}`,
933
+ text: `Graph: ${graphDetails.completedNodeIds.length}/${params.graph.length} completed in ${plan.waves.length} waves\n\n${summaries.join("\n\n---\n\n")}`,
892
934
  },
893
935
  ],
894
936
  details: makeDetails("graph")(results),
937
+ isError: results.some(isFailedResult),
895
938
  };
896
939
  }
897
940
 
@@ -936,10 +979,101 @@ export default function (omk: ExtensionAPI) {
936
979
  }
937
980
  };
938
981
 
939
- const results = await mapWithConcurrencyLimit(
940
- params.tasks,
941
- executionPolicy.concurrency,
942
- async (t, index) => {
982
+ // Spec 020 Req1 — when the session binds a lane authority, parallel
983
+ // tasks route through buildSubagentOrchestrationPlan →
984
+ // launchSubagentLanes (parent admission + shared pool + settlement).
985
+ // Without one (bare/test contexts) keep the local concurrency path.
986
+ const laneAuthority = ctx.getSubagentLaneAuthority?.();
987
+ let results: SingleResult[];
988
+ if (laneAuthority) {
989
+ const laneIndexById = new Map<string, number>();
990
+ const lanes = params.tasks.map((t, index) => {
991
+ const id = `task-${index}`;
992
+ laneIndexById.set(id, index);
993
+ return {
994
+ id,
995
+ role: "executor" as const,
996
+ task: t.task,
997
+ agentName: t.agent,
998
+ ...(t.writeScope ? { writeScope: t.writeScope } : {}),
999
+ };
1000
+ });
1001
+ const dispatch = await laneAuthority.dispatchLanes({
1002
+ lanes,
1003
+ spawnPlan: {
1004
+ whyParallel: `${params.tasks.length} independent agent tasks requested in parallel mode`,
1005
+ whyNotLocal: "isolated subagent context per task",
1006
+ independence: "tasks declare no dependencies",
1007
+ expectedReceiptShape: "per-task SingleResult array",
1008
+ maxInlineTokens: 8192,
1009
+ },
1010
+ configuredMaxParallelLanes: executionPolicy.unbounded ? undefined : executionPolicy.concurrency,
1011
+ // Child omk processes are heavy — they draw the shared permit
1012
+ // only under a governed admission decision; observe/off keeps
1013
+ // the legacy breadth (spec Req1.3 vs. observe-mode semantics).
1014
+ heavyLaneIds: laneAuthority.getCurrentResourceAdmission()
1015
+ ? new Set(lanes.map((lane) => lane.id))
1016
+ : undefined,
1017
+ signal,
1018
+ launchLane: async ({ laneId, signal: laneSignal }) => {
1019
+ const index = laneIndexById.get(laneId);
1020
+ if (index === undefined) return;
1021
+ const t = params.tasks?.[index];
1022
+ if (!t) return;
1023
+ const result = await runSingleAgent(
1024
+ ctx.cwd,
1025
+ agents,
1026
+ t.agent,
1027
+ t.task,
1028
+ t.cwd,
1029
+ undefined,
1030
+ laneSignal,
1031
+ (partial) => {
1032
+ if (partial.details?.results[0]) {
1033
+ allResults[index] = partial.details.results[0];
1034
+ emitParallelUpdate();
1035
+ }
1036
+ },
1037
+ makeDetails("parallel"),
1038
+ executionPolicy.unbounded,
1039
+ executionBudget,
1040
+ profileStore,
1041
+ 1,
1042
+ );
1043
+ allResults[index] = result;
1044
+ emitParallelUpdate();
1045
+ return laneResult(result);
1046
+ },
1047
+ });
1048
+ if (dispatch.blockers.length > 0) {
1049
+ return {
1050
+ content: [{ type: "text", text: `Lane dispatch blocked: ${dispatch.blockers.join("; ")}` }],
1051
+ details: makeDetails("parallel")([]),
1052
+ };
1053
+ }
1054
+ // Req2.4 — lanes that never launched still yield a typed outcome,
1055
+ // never an empty-success. Convert non-launched outcomes to failed
1056
+ // results so the summary is honest.
1057
+ for (const outcome of dispatch.outcomes) {
1058
+ if (outcome.status === "completed") continue;
1059
+ const index = laneIndexById.get(outcome.laneId);
1060
+ if (index === undefined) continue;
1061
+ if (allResults[index].exitCode === -1) {
1062
+ allResults[index] = {
1063
+ agent: params.tasks[index].agent,
1064
+ agentSource: "unknown",
1065
+ task: params.tasks[index].task,
1066
+ exitCode: 1,
1067
+ messages: [],
1068
+ stderr: outcome.diagnostic ?? outcome.status,
1069
+ stopReason: outcome.status,
1070
+ usage: emptyUsage(),
1071
+ };
1072
+ }
1073
+ }
1074
+ results = allResults;
1075
+ } else {
1076
+ results = await mapWithConcurrencyLimit(params.tasks, executionPolicy.concurrency, async (t, index) => {
943
1077
  const result = await runSingleAgent(
944
1078
  ctx.cwd,
945
1079
  agents,
@@ -964,8 +1098,8 @@ export default function (omk: ExtensionAPI) {
964
1098
  allResults[index] = result;
965
1099
  emitParallelUpdate();
966
1100
  return result;
967
- },
968
- );
1101
+ });
1102
+ }
969
1103
 
970
1104
  const successCount = results.filter((r) => !isFailedResult(r)).length;
971
1105
  const summaries = results.map((r) => {
@@ -983,6 +1117,7 @@ export default function (omk: ExtensionAPI) {
983
1117
  },
984
1118
  ],
985
1119
  details: makeDetails("parallel")(results),
1120
+ isError: results.some(isFailedResult),
986
1121
  };
987
1122
  }
988
1123
 
@@ -1184,8 +1319,8 @@ export default function (omk: ExtensionAPI) {
1184
1319
  const failed = details.results.some(isFailedResult);
1185
1320
  const icon = failed ? theme.fg("error", "✗") : theme.fg("success", "✓");
1186
1321
  let text = `${icon} ${theme.fg("toolTitle", theme.bold("graph "))}${theme.fg("accent", `${completed}/${total} nodes · ${waves} waves`)}`;
1187
- for (const [index, result] of details.results.entries()) {
1188
- const id = details.graph?.completedNodeIds[index] ?? result.agent;
1322
+ for (const result of details.results) {
1323
+ const id = result.nodeId ?? result.agent;
1189
1324
  const resultIcon = isFailedResult(result) ? theme.fg("error", "✗") : theme.fg("success", "✓");
1190
1325
  text += `\n${theme.fg("muted", "─── ")}${theme.fg("accent", id)} ${resultIcon}`;
1191
1326
  if (expanded) text += `\n${theme.fg("toolOutput", getResultOutput(result))}`;
@@ -0,0 +1,42 @@
1
+ import { type ChildProcess, spawn } from "node:child_process";
2
+
3
+ /** EPERM and other observation errors cannot prove that a group is gone. */
4
+ export function processGroupState(pid: number): "alive" | "gone" | "unknown" {
5
+ if (process.platform === "win32" || pid < 1) return "gone";
6
+ try {
7
+ process.kill(-pid, 0);
8
+ return "alive";
9
+ } catch (error) {
10
+ return (error as NodeJS.ErrnoException).code === "ESRCH" ? "gone" : "unknown";
11
+ }
12
+ }
13
+
14
+ /** Requests termination, never confirms it. Only signal the child owned by this invocation. */
15
+ export function signalProcessTree(child: ChildProcess, signal: "SIGTERM" | "SIGKILL", processGroup: boolean): void {
16
+ const pid = child.pid;
17
+ if (pid === undefined) return;
18
+ if (process.platform === "win32" && signal === "SIGKILL") {
19
+ try {
20
+ const killer = spawn("taskkill", ["/F", "/T", "/PID", String(pid)], {
21
+ detached: true,
22
+ stdio: "ignore",
23
+ windowsHide: true,
24
+ });
25
+ killer.on("error", () => {});
26
+ killer.unref();
27
+ return;
28
+ } catch {
29
+ // Fall back to the owned direct child, without claiming descendant containment.
30
+ }
31
+ }
32
+ try {
33
+ if (process.platform === "win32") child.kill(signal);
34
+ else process.kill(processGroup ? -pid : pid, signal);
35
+ } catch {
36
+ try {
37
+ child.kill(signal);
38
+ } catch {
39
+ /* Termination remains unconfirmed. */
40
+ }
41
+ }
42
+ }
@@ -18,6 +18,30 @@ async function processExists(pid: number): Promise<boolean> {
18
18
  }
19
19
 
20
20
  describe("managed subagent process lifecycle", () => {
21
+ it.skipIf(process.platform === "win32").each(["SIGTERM", "SIGKILL"] as const)(
22
+ "does not report %s termination as success",
23
+ async (signal) => {
24
+ const result = await runManagedProcess({
25
+ command: process.execPath,
26
+ args: ["-e", `process.kill(process.pid, ${JSON.stringify(signal)})`],
27
+ cwd: process.cwd(),
28
+ cutoffMs: 5_000,
29
+ });
30
+ expect(result.signal).toBe(signal);
31
+ expect(result.exitCode).not.toBe(0);
32
+ },
33
+ );
34
+
35
+ it("preserves an explicit nonzero exit", async () => {
36
+ const result = await runManagedProcess({
37
+ command: process.execPath,
38
+ args: ["-e", "process.exit(7)"],
39
+ cwd: process.cwd(),
40
+ cutoffMs: 5_000,
41
+ });
42
+ expect(result.exitCode).toBe(7);
43
+ expect(result.signal).toBeNull();
44
+ });
21
45
  it("escalates a cutoff from SIGTERM to SIGKILL and reaps the process tree", async () => {
22
46
  let stdout = "";
23
47
  const result = await runManagedProcess({