open-multi-agent-kit 0.98.4 → 0.99.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 (357) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +4 -2
  3. package/dist/cli/help.d.ts.map +1 -1
  4. package/dist/cli/help.js +1 -0
  5. package/dist/cli/help.js.map +1 -1
  6. package/dist/commands/run-command.d.ts +2 -1
  7. package/dist/commands/run-command.d.ts.map +1 -1
  8. package/dist/commands/run-command.js +9 -1
  9. package/dist/commands/run-command.js.map +1 -1
  10. package/dist/commands/verified-run-cli.d.ts +6 -0
  11. package/dist/commands/verified-run-cli.d.ts.map +1 -0
  12. package/dist/commands/verified-run-cli.js +190 -0
  13. package/dist/commands/verified-run-cli.js.map +1 -0
  14. package/dist/core/agent-session-services.d.ts +8 -1
  15. package/dist/core/agent-session-services.d.ts.map +1 -1
  16. package/dist/core/agent-session-services.js +41 -0
  17. package/dist/core/agent-session-services.js.map +1 -1
  18. package/dist/core/agent-session.d.ts +7 -8
  19. package/dist/core/agent-session.d.ts.map +1 -1
  20. package/dist/core/agent-session.js +80 -43
  21. package/dist/core/agent-session.js.map +1 -1
  22. package/dist/core/devin-harness-dispatch.d.ts +12 -0
  23. package/dist/core/devin-harness-dispatch.d.ts.map +1 -0
  24. package/dist/core/devin-harness-dispatch.js +12 -0
  25. package/dist/core/devin-harness-dispatch.js.map +1 -0
  26. package/dist/core/devin-harness.d.ts +53 -0
  27. package/dist/core/devin-harness.d.ts.map +1 -0
  28. package/dist/core/devin-harness.js +112 -0
  29. package/dist/core/devin-harness.js.map +1 -0
  30. package/dist/core/domain-dispatch.d.ts +4 -1
  31. package/dist/core/domain-dispatch.d.ts.map +1 -1
  32. package/dist/core/domain-dispatch.js +5 -0
  33. package/dist/core/domain-dispatch.js.map +1 -1
  34. package/dist/core/domain-loadouts-provider-harness.d.ts +12 -0
  35. package/dist/core/domain-loadouts-provider-harness.d.ts.map +1 -0
  36. package/dist/core/domain-loadouts-provider-harness.js +122 -0
  37. package/dist/core/domain-loadouts-provider-harness.js.map +1 -0
  38. package/dist/core/domain-loadouts.d.ts +2 -33
  39. package/dist/core/domain-loadouts.d.ts.map +1 -1
  40. package/dist/core/domain-loadouts.js +3 -56
  41. package/dist/core/domain-loadouts.js.map +1 -1
  42. package/dist/core/domain-profile.d.ts +40 -0
  43. package/dist/core/domain-profile.d.ts.map +1 -0
  44. package/dist/core/domain-profile.js +7 -0
  45. package/dist/core/domain-profile.js.map +1 -0
  46. package/dist/core/grok-harness-dispatch.d.ts +6 -20
  47. package/dist/core/grok-harness-dispatch.d.ts.map +1 -1
  48. package/dist/core/grok-harness-dispatch.js +6 -55
  49. package/dist/core/grok-harness-dispatch.js.map +1 -1
  50. package/dist/core/grok-harness.d.ts +7 -9
  51. package/dist/core/grok-harness.d.ts.map +1 -1
  52. package/dist/core/grok-harness.js +9 -23
  53. package/dist/core/grok-harness.js.map +1 -1
  54. package/dist/core/harness-skills.d.ts +20 -0
  55. package/dist/core/harness-skills.d.ts.map +1 -0
  56. package/dist/core/harness-skills.js +36 -0
  57. package/dist/core/harness-skills.js.map +1 -0
  58. package/dist/core/loadout-runtime-state.d.ts +25 -0
  59. package/dist/core/loadout-runtime-state.d.ts.map +1 -0
  60. package/dist/core/loadout-runtime-state.js +39 -0
  61. package/dist/core/loadout-runtime-state.js.map +1 -0
  62. package/dist/core/loadout-runtime.d.ts +2 -12
  63. package/dist/core/loadout-runtime.d.ts.map +1 -1
  64. package/dist/core/loadout-runtime.js +6 -13
  65. package/dist/core/loadout-runtime.js.map +1 -1
  66. package/dist/core/model-resolver.d.ts +1 -41
  67. package/dist/core/model-resolver.d.ts.map +1 -1
  68. package/dist/core/model-resolver.js +2 -49
  69. package/dist/core/model-resolver.js.map +1 -1
  70. package/dist/core/prompt-settlement.d.ts +4 -3
  71. package/dist/core/prompt-settlement.d.ts.map +1 -1
  72. package/dist/core/prompt-settlement.js.map +1 -1
  73. package/dist/core/provider-default-models.d.ts +42 -0
  74. package/dist/core/provider-default-models.d.ts.map +1 -0
  75. package/dist/core/provider-default-models.js +50 -0
  76. package/dist/core/provider-default-models.js.map +1 -0
  77. package/dist/core/provider-display-names.d.ts.map +1 -1
  78. package/dist/core/provider-display-names.js +1 -0
  79. package/dist/core/provider-display-names.js.map +1 -1
  80. package/dist/core/provider-harness-dispatch.d.ts +63 -0
  81. package/dist/core/provider-harness-dispatch.d.ts.map +1 -0
  82. package/dist/core/provider-harness-dispatch.js +60 -0
  83. package/dist/core/provider-harness-dispatch.js.map +1 -0
  84. package/dist/core/provider-usage-devin.d.ts +16 -0
  85. package/dist/core/provider-usage-devin.d.ts.map +1 -0
  86. package/dist/core/provider-usage-devin.js +61 -0
  87. package/dist/core/provider-usage-devin.js.map +1 -0
  88. package/dist/core/provider-usage-text.d.ts +5 -0
  89. package/dist/core/provider-usage-text.d.ts.map +1 -0
  90. package/dist/core/provider-usage-text.js +15 -0
  91. package/dist/core/provider-usage-text.js.map +1 -0
  92. package/dist/core/provider-usage-types.d.ts +2 -1
  93. package/dist/core/provider-usage-types.d.ts.map +1 -1
  94. package/dist/core/provider-usage-types.js.map +1 -1
  95. package/dist/core/provider-usage.d.ts +1 -2
  96. package/dist/core/provider-usage.d.ts.map +1 -1
  97. package/dist/core/provider-usage.js +12 -7
  98. package/dist/core/provider-usage.js.map +1 -1
  99. package/dist/core/run-budget-policy.d.ts +18 -0
  100. package/dist/core/run-budget-policy.d.ts.map +1 -0
  101. package/dist/core/run-budget-policy.js +52 -0
  102. package/dist/core/run-budget-policy.js.map +1 -0
  103. package/dist/core/run-budget.d.ts +33 -0
  104. package/dist/core/run-budget.d.ts.map +1 -0
  105. package/dist/core/run-budget.js +89 -0
  106. package/dist/core/run-budget.js.map +1 -0
  107. package/dist/core/run-execution-api.d.ts +15 -0
  108. package/dist/core/run-execution-api.d.ts.map +1 -0
  109. package/dist/core/run-execution-api.js +7 -0
  110. package/dist/core/run-execution-api.js.map +1 -0
  111. package/dist/core/run-journal.d.ts.map +1 -1
  112. package/dist/core/run-journal.js +30 -208
  113. package/dist/core/run-journal.js.map +1 -1
  114. package/dist/core/sdk.d.ts.map +1 -1
  115. package/dist/core/sdk.js +23 -18
  116. package/dist/core/sdk.js.map +1 -1
  117. package/dist/core/session-bash-service.d.ts +2 -2
  118. package/dist/core/session-bash-service.d.ts.map +1 -1
  119. package/dist/core/session-bash-service.js +21 -9
  120. package/dist/core/session-bash-service.js.map +1 -1
  121. package/dist/core/session-failure-cause.d.ts.map +1 -1
  122. package/dist/core/session-failure-cause.js +5 -0
  123. package/dist/core/session-failure-cause.js.map +1 -1
  124. package/dist/core/session-prompt-lifecycle.d.ts +25 -0
  125. package/dist/core/session-prompt-lifecycle.d.ts.map +1 -0
  126. package/dist/core/session-prompt-lifecycle.js +87 -0
  127. package/dist/core/session-prompt-lifecycle.js.map +1 -0
  128. package/dist/core/session-run-budget.d.ts +28 -0
  129. package/dist/core/session-run-budget.d.ts.map +1 -0
  130. package/dist/core/session-run-budget.js +127 -0
  131. package/dist/core/session-run-budget.js.map +1 -0
  132. package/dist/core/session-run-termination.d.ts.map +1 -1
  133. package/dist/core/session-run-termination.js +14 -3
  134. package/dist/core/session-run-termination.js.map +1 -1
  135. package/dist/core/session-termination-types.d.ts +97 -0
  136. package/dist/core/session-termination-types.d.ts.map +1 -0
  137. package/dist/core/session-termination-types.js +26 -0
  138. package/dist/core/session-termination-types.js.map +1 -0
  139. package/dist/core/session-termination.d.ts +3 -97
  140. package/dist/core/session-termination.d.ts.map +1 -1
  141. package/dist/core/session-termination.js +24 -28
  142. package/dist/core/session-termination.js.map +1 -1
  143. package/dist/core/slash-commands.d.ts.map +1 -1
  144. package/dist/core/slash-commands.js +1 -0
  145. package/dist/core/slash-commands.js.map +1 -1
  146. package/dist/core/subagent-lane-launcher.d.ts +3 -2
  147. package/dist/core/subagent-lane-launcher.d.ts.map +1 -1
  148. package/dist/core/subagent-lane-launcher.js +24 -13
  149. package/dist/core/subagent-lane-launcher.js.map +1 -1
  150. package/dist/core/verified-run/broker.d.ts +26 -0
  151. package/dist/core/verified-run/broker.d.ts.map +1 -0
  152. package/dist/core/verified-run/broker.js +210 -0
  153. package/dist/core/verified-run/broker.js.map +1 -0
  154. package/dist/core/verified-run/candidate.d.ts +24 -0
  155. package/dist/core/verified-run/candidate.d.ts.map +1 -0
  156. package/dist/core/verified-run/candidate.js +167 -0
  157. package/dist/core/verified-run/candidate.js.map +1 -0
  158. package/dist/core/verified-run/check-receipt.d.ts +18 -0
  159. package/dist/core/verified-run/check-receipt.d.ts.map +1 -0
  160. package/dist/core/verified-run/check-receipt.js +92 -0
  161. package/dist/core/verified-run/check-receipt.js.map +1 -0
  162. package/dist/core/verified-run/coordinator.d.ts +39 -0
  163. package/dist/core/verified-run/coordinator.d.ts.map +1 -0
  164. package/dist/core/verified-run/coordinator.js +190 -0
  165. package/dist/core/verified-run/coordinator.js.map +1 -0
  166. package/dist/core/verified-run/dag-candidates.d.ts +8 -0
  167. package/dist/core/verified-run/dag-candidates.d.ts.map +1 -0
  168. package/dist/core/verified-run/dag-candidates.js +65 -0
  169. package/dist/core/verified-run/dag-candidates.js.map +1 -0
  170. package/dist/core/verified-run/dag-phase.d.ts +7 -0
  171. package/dist/core/verified-run/dag-phase.d.ts.map +1 -0
  172. package/dist/core/verified-run/dag-phase.js +130 -0
  173. package/dist/core/verified-run/dag-phase.js.map +1 -0
  174. package/dist/core/verified-run/dag-projection.d.ts +7 -0
  175. package/dist/core/verified-run/dag-projection.d.ts.map +1 -0
  176. package/dist/core/verified-run/dag-projection.js +103 -0
  177. package/dist/core/verified-run/dag-projection.js.map +1 -0
  178. package/dist/core/verified-run/dag-recovery.d.ts +13 -0
  179. package/dist/core/verified-run/dag-recovery.d.ts.map +1 -0
  180. package/dist/core/verified-run/dag-recovery.js +98 -0
  181. package/dist/core/verified-run/dag-recovery.js.map +1 -0
  182. package/dist/core/verified-run/dag-retry-projection.d.ts +9 -0
  183. package/dist/core/verified-run/dag-retry-projection.d.ts.map +1 -0
  184. package/dist/core/verified-run/dag-retry-projection.js +43 -0
  185. package/dist/core/verified-run/dag-retry-projection.js.map +1 -0
  186. package/dist/core/verified-run/dag-types.d.ts +58 -0
  187. package/dist/core/verified-run/dag-types.d.ts.map +1 -0
  188. package/dist/core/verified-run/dag-types.js +2 -0
  189. package/dist/core/verified-run/dag-types.js.map +1 -0
  190. package/dist/core/verified-run/event-parser.d.ts +3 -0
  191. package/dist/core/verified-run/event-parser.d.ts.map +1 -0
  192. package/dist/core/verified-run/event-parser.js +154 -0
  193. package/dist/core/verified-run/event-parser.js.map +1 -0
  194. package/dist/core/verified-run/events.d.ts +4 -0
  195. package/dist/core/verified-run/events.d.ts.map +1 -0
  196. package/dist/core/verified-run/events.js +3 -0
  197. package/dist/core/verified-run/events.js.map +1 -0
  198. package/dist/core/verified-run/evidence-binding.d.ts +18 -0
  199. package/dist/core/verified-run/evidence-binding.d.ts.map +1 -0
  200. package/dist/core/verified-run/evidence-binding.js +81 -0
  201. package/dist/core/verified-run/evidence-binding.js.map +1 -0
  202. package/dist/core/verified-run/evidence.d.ts +27 -0
  203. package/dist/core/verified-run/evidence.d.ts.map +1 -0
  204. package/dist/core/verified-run/evidence.js +100 -0
  205. package/dist/core/verified-run/evidence.js.map +1 -0
  206. package/dist/core/verified-run/journal.d.ts +32 -0
  207. package/dist/core/verified-run/journal.d.ts.map +1 -0
  208. package/dist/core/verified-run/journal.js +103 -0
  209. package/dist/core/verified-run/journal.js.map +1 -0
  210. package/dist/core/verified-run/namespace-identity.d.ts +11 -0
  211. package/dist/core/verified-run/namespace-identity.d.ts.map +1 -0
  212. package/dist/core/verified-run/namespace-identity.js +77 -0
  213. package/dist/core/verified-run/namespace-identity.js.map +1 -0
  214. package/dist/core/verified-run/owned-execution.d.ts +22 -0
  215. package/dist/core/verified-run/owned-execution.d.ts.map +1 -0
  216. package/dist/core/verified-run/owned-execution.js +49 -0
  217. package/dist/core/verified-run/owned-execution.js.map +1 -0
  218. package/dist/core/verified-run/phase-context.d.ts +9 -0
  219. package/dist/core/verified-run/phase-context.d.ts.map +1 -0
  220. package/dist/core/verified-run/phase-context.js +2 -0
  221. package/dist/core/verified-run/phase-context.js.map +1 -0
  222. package/dist/core/verified-run/process-gate.d.ts +5 -0
  223. package/dist/core/verified-run/process-gate.d.ts.map +1 -0
  224. package/dist/core/verified-run/process-gate.js +31 -0
  225. package/dist/core/verified-run/process-gate.js.map +1 -0
  226. package/dist/core/verified-run/process-projection.d.ts +13 -0
  227. package/dist/core/verified-run/process-projection.d.ts.map +1 -0
  228. package/dist/core/verified-run/process-projection.js +82 -0
  229. package/dist/core/verified-run/process-projection.js.map +1 -0
  230. package/dist/core/verified-run/projection.d.ts +4 -0
  231. package/dist/core/verified-run/projection.d.ts.map +1 -0
  232. package/dist/core/verified-run/projection.js +175 -0
  233. package/dist/core/verified-run/projection.js.map +1 -0
  234. package/dist/core/verified-run/recovery-clock.d.ts +19 -0
  235. package/dist/core/verified-run/recovery-clock.d.ts.map +1 -0
  236. package/dist/core/verified-run/recovery-clock.js +69 -0
  237. package/dist/core/verified-run/recovery-clock.js.map +1 -0
  238. package/dist/core/verified-run/recovery-command.d.ts +11 -0
  239. package/dist/core/verified-run/recovery-command.d.ts.map +1 -0
  240. package/dist/core/verified-run/recovery-command.js +73 -0
  241. package/dist/core/verified-run/recovery-command.js.map +1 -0
  242. package/dist/core/verified-run/recovery-projection.d.ts +12 -0
  243. package/dist/core/verified-run/recovery-projection.d.ts.map +1 -0
  244. package/dist/core/verified-run/recovery-projection.js +88 -0
  245. package/dist/core/verified-run/recovery-projection.js.map +1 -0
  246. package/dist/core/verified-run/recovery.d.ts +12 -0
  247. package/dist/core/verified-run/recovery.d.ts.map +1 -0
  248. package/dist/core/verified-run/recovery.js +115 -0
  249. package/dist/core/verified-run/recovery.js.map +1 -0
  250. package/dist/core/verified-run/run-types.d.ts +104 -0
  251. package/dist/core/verified-run/run-types.d.ts.map +1 -0
  252. package/dist/core/verified-run/run-types.js +2 -0
  253. package/dist/core/verified-run/run-types.js.map +1 -0
  254. package/dist/core/verified-run/scripted-writer.d.ts +15 -0
  255. package/dist/core/verified-run/scripted-writer.d.ts.map +1 -0
  256. package/dist/core/verified-run/scripted-writer.js +94 -0
  257. package/dist/core/verified-run/scripted-writer.js.map +1 -0
  258. package/dist/core/verified-run/session-port.d.ts +27 -0
  259. package/dist/core/verified-run/session-port.d.ts.map +1 -0
  260. package/dist/core/verified-run/session-port.js +2 -0
  261. package/dist/core/verified-run/session-port.js.map +1 -0
  262. package/dist/core/verified-run/storage.d.ts +15 -0
  263. package/dist/core/verified-run/storage.d.ts.map +1 -0
  264. package/dist/core/verified-run/storage.js +96 -0
  265. package/dist/core/verified-run/storage.js.map +1 -0
  266. package/dist/core/verified-run/task-execution.d.ts +9 -0
  267. package/dist/core/verified-run/task-execution.d.ts.map +1 -0
  268. package/dist/core/verified-run/task-execution.js +29 -0
  269. package/dist/core/verified-run/task-execution.js.map +1 -0
  270. package/dist/core/verified-run/verification-phase.d.ts +5 -0
  271. package/dist/core/verified-run/verification-phase.d.ts.map +1 -0
  272. package/dist/core/verified-run/verification-phase.js +56 -0
  273. package/dist/core/verified-run/verification-phase.js.map +1 -0
  274. package/dist/core/verified-run/work-recovery.d.ts +8 -0
  275. package/dist/core/verified-run/work-recovery.d.ts.map +1 -0
  276. package/dist/core/verified-run/work-recovery.js +44 -0
  277. package/dist/core/verified-run/work-recovery.js.map +1 -0
  278. package/dist/core/verified-run/writer-completion.d.ts +4 -0
  279. package/dist/core/verified-run/writer-completion.d.ts.map +1 -0
  280. package/dist/core/verified-run/writer-completion.js +27 -0
  281. package/dist/core/verified-run/writer-completion.js.map +1 -0
  282. package/dist/core/verified-run/writer-phase.d.ts +8 -0
  283. package/dist/core/verified-run/writer-phase.d.ts.map +1 -0
  284. package/dist/core/verified-run/writer-phase.js +76 -0
  285. package/dist/core/verified-run/writer-phase.js.map +1 -0
  286. package/dist/core/verified-run/writer-projection.d.ts +11 -0
  287. package/dist/core/verified-run/writer-projection.d.ts.map +1 -0
  288. package/dist/core/verified-run/writer-projection.js +71 -0
  289. package/dist/core/verified-run/writer-projection.js.map +1 -0
  290. package/dist/core/verified-run/writer-recovery.d.ts +16 -0
  291. package/dist/core/verified-run/writer-recovery.d.ts.map +1 -0
  292. package/dist/core/verified-run/writer-recovery.js +107 -0
  293. package/dist/core/verified-run/writer-recovery.js.map +1 -0
  294. package/dist/core/workload-permit-pool.d.ts +1 -3
  295. package/dist/core/workload-permit-pool.d.ts.map +1 -1
  296. package/dist/core/workload-permit-pool.js +11 -11
  297. package/dist/core/workload-permit-pool.js.map +1 -1
  298. package/dist/index.d.ts +3 -2
  299. package/dist/index.d.ts.map +1 -1
  300. package/dist/index.js +2 -2
  301. package/dist/index.js.map +1 -1
  302. package/dist/main.d.ts.map +1 -1
  303. package/dist/main.js +2 -8
  304. package/dist/main.js.map +1 -1
  305. package/dist/modes/interactive/components/session-failure.d.ts +13 -0
  306. package/dist/modes/interactive/components/session-failure.d.ts.map +1 -0
  307. package/dist/modes/interactive/components/session-failure.js +81 -0
  308. package/dist/modes/interactive/components/session-failure.js.map +1 -0
  309. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  310. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  311. package/dist/modes/interactive/interactive-mode.js +47 -36
  312. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  313. package/dist/modes/interactive/resource-description.d.ts +3 -0
  314. package/dist/modes/interactive/resource-description.d.ts.map +1 -0
  315. package/dist/modes/interactive/resource-description.js +15 -0
  316. package/dist/modes/interactive/resource-description.js.map +1 -0
  317. package/dist/modes/interactive/tui-diagnostics.d.ts +36 -0
  318. package/dist/modes/interactive/tui-diagnostics.d.ts.map +1 -0
  319. package/dist/modes/interactive/tui-diagnostics.js +105 -0
  320. package/dist/modes/interactive/tui-diagnostics.js.map +1 -0
  321. package/dist/modes/interactive/tui-runtime-info.d.ts +20 -0
  322. package/dist/modes/interactive/tui-runtime-info.d.ts.map +1 -0
  323. package/dist/modes/interactive/tui-runtime-info.js +76 -0
  324. package/dist/modes/interactive/tui-runtime-info.js.map +1 -0
  325. package/docs/development.md +10 -0
  326. package/docs/devin-harness.md +121 -0
  327. package/docs/docs.json +4 -0
  328. package/docs/environment-variables.md +2 -1
  329. package/docs/index.md +1 -0
  330. package/docs/keybindings.md +1 -1
  331. package/docs/loadout-domains/README.md +2 -1
  332. package/docs/loadout-domains/devin-harness.md +72 -0
  333. package/docs/metrics.md +57 -16
  334. package/docs/providers.md +76 -0
  335. package/docs/release-audit-0.98.5.md +101 -0
  336. package/docs/release-audit-0.99.0.md +68 -0
  337. package/docs/run-protocol.md +34 -1
  338. package/docs/runtime-algorithms.md +30 -1
  339. package/docs/sdk.md +198 -1
  340. package/docs/settings.md +1 -1
  341. package/docs/startup-resource-labels-testing.md +47 -0
  342. package/docs/tb21-audit.md +18 -4
  343. package/docs/usage.md +59 -0
  344. package/docs/verified-run-remaining-design.md +881 -0
  345. package/docs/verified-run-testing.md +508 -0
  346. package/docs/verified-run.md +409 -0
  347. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  348. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  349. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  350. package/examples/extensions/gondolin/package-lock.json +2 -2
  351. package/examples/extensions/gondolin/package.json +1 -1
  352. package/examples/extensions/sandbox/package-lock.json +2 -2
  353. package/examples/extensions/sandbox/package.json +1 -1
  354. package/examples/extensions/with-deps/package-lock.json +2 -2
  355. package/examples/extensions/with-deps/package.json +1 -1
  356. package/npm-shrinkwrap.json +18 -18
  357. package/package.json +6 -6
@@ -1 +1 @@
1
- {"version":3,"file":"subagent-lane-launcher.d.ts","sourceRoot":"","sources":["../../src/core/subagent-lane-launcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACzE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AAC7E,OAAO,EAAuB,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEzF;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,0BAA0B,CAAC,EAAE,MAAM,CAAC;IAC7C,QAAQ,CAAC,yBAAyB,EAAE,MAAM,CAAC;IAC3C,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,kFAAkF;IAClF,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;CACvC;AAED,gGAA+F;AAC/F,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,uBAAuB,GAAG,MAAM,CAahF;AAED,qFAAoF;AACpF,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,yBAAyB,CAAC;IAC7C,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;CACpC;AAED,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,eAAe,GAAG,iBAAiB,CAAC;IAC9E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,IAAI,EAAE,yBAAyB,CAAC;IACzC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,yBAAyB,CAAC;IAC7C,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAAC;IACxC,QAAQ,CAAC,0BAA0B,CAAC,EAAE,MAAM,CAAC;IAC7C,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,2EAA0E;IAC1E,QAAQ,CAAC,YAAY,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5C,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;CACtC;AAED,MAAM,WAAW,yBAAyB;IACzC,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;IAC1C,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;CACxC;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAsD7G","sourcesContent":["import type { ResourceAdmissionDecision } from \"./resource-admission.ts\";\nimport type { SubagentOrchestrationPlan } from \"./subagent-orchestration.ts\";\nimport { WorkloadPermitError, type WorkloadPermitPool } from \"./workload-permit-pool.ts\";\n\n/**\n * Subagent lane launch authority (OMK v0.97.x roadmap §14, M6/PR10).\n *\n * Turns `buildSubagentOrchestrationPlan()` output into enforced execution:\n * the §14.2 effective width is a hard cap on concurrently active children,\n * batches run in order (writer serialization / path conflicts are encoded in\n * the plan's batches, which supplies the `pathConflictFreeWidth` term), and\n * heavy lanes draw permits from the PARENT's shared pool (§14.1 — a child\n * never constructs its own pool here; it receives this launcher's budget).\n *\n * §14.3 guarantees: child failure releases its permit (leak 0), parent\n * abort aborts queued permit waits and never launches unstarted lanes, and\n * the child context carries the parent decision with a width the child\n * cannot raise (§14.4: no API exists to widen — the context is read-only).\n */\n\nexport interface EffectiveLaneWidthInput {\n\treadonly planWidth: number;\n\treadonly configuredMaxParallelLanes?: number;\n\treadonly admissionMaxParallelLanes: number;\n\treadonly availableHeavyPermits: number;\n\t/** Widest conflict-free batch; the plan's batching already serializes writers. */\n\treadonly pathConflictFreeWidth: number;\n}\n\n/** §14.2: `min(plan, configured, admission, availableHeavyPermits, conflictFree)`, floor 1. */\nexport function computeEffectiveLaneWidth(input: EffectiveLaneWidthInput): number {\n\tconst configured =\n\t\tinput.configuredMaxParallelLanes !== undefined && input.configuredMaxParallelLanes > 0\n\t\t\t? input.configuredMaxParallelLanes\n\t\t\t: Number.POSITIVE_INFINITY;\n\tconst width = Math.min(\n\t\tinput.planWidth,\n\t\tconfigured,\n\t\tinput.admissionMaxParallelLanes,\n\t\tMath.max(1, input.availableHeavyPermits),\n\t\tinput.pathConflictFreeWidth,\n\t);\n\treturn Math.max(1, Math.floor(width));\n}\n\n/** Read-only budget handed to every child (§14.1). Nothing here can raise a cap. */\nexport interface SubagentLaneContext {\n\treadonly laneId: string;\n\treadonly promptRunId: string;\n\treadonly decision: ResourceAdmissionDecision;\n\treadonly effectiveLaneWidth: number;\n}\n\nexport interface LaneOutcome {\n\treadonly laneId: string;\n\treadonly status: \"completed\" | \"failed\" | \"skipped-abort\" | \"permit-rejected\";\n\treadonly diagnostic?: string;\n}\n\nexport interface LaunchSubagentLanesInput {\n\treadonly plan: SubagentOrchestrationPlan;\n\treadonly promptRunId: string;\n\treadonly decision: ResourceAdmissionDecision;\n\treadonly permitPool: WorkloadPermitPool;\n\treadonly configuredMaxParallelLanes?: number;\n\treadonly signal?: AbortSignal;\n\t/** Lane ids that run heavy work and must hold a shared permit (§14.3). */\n\treadonly heavyLaneIds?: ReadonlySet<string>;\n\t/** The actual child launcher (process spawn, SDK session, or test double). */\n\treadonly launchLane: (context: SubagentLaneContext) => Promise<void>;\n\treadonly permitWaitTimeoutMs?: number;\n}\n\nexport interface LaunchSubagentLanesResult {\n\treadonly outcomes: readonly LaneOutcome[];\n\treadonly effectiveLaneWidth: number;\n\treadonly maxObservedConcurrency: number;\n}\n\n/**\n * Execute a plan's batches with the §14.2 width as launcher authority.\n * Never throws for lane failures; the caller reads per-lane outcomes.\n */\nexport async function launchSubagentLanes(input: LaunchSubagentLanesInput): Promise<LaunchSubagentLanesResult> {\n\tconst poolSnapshot = input.permitPool.snapshot();\n\tconst effectiveLaneWidth = computeEffectiveLaneWidth({\n\t\tplanWidth: Math.max(1, input.plan.route.width),\n\t\tconfiguredMaxParallelLanes: input.configuredMaxParallelLanes,\n\t\tadmissionMaxParallelLanes: input.decision.maxParallelLanes,\n\t\tavailableHeavyPermits: Math.max(0, poolSnapshot.capacity - poolSnapshot.activeWeight),\n\t\tpathConflictFreeWidth: Math.max(1, ...input.plan.batches.map((batch) => batch.laneIds.length)),\n\t});\n\n\tconst outcomes: LaneOutcome[] = [];\n\tlet active = 0;\n\tlet maxObservedConcurrency = 0;\n\n\tfor (const batch of input.plan.batches) {\n\t\tif (input.signal?.aborted) {\n\t\t\t// Parent abort: unstarted lanes are never launched (§14.3).\n\t\t\tfor (const laneId of batch.laneIds) {\n\t\t\t\toutcomes.push({ laneId, status: \"skipped-abort\" });\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\t// Batches execute sequentially (dependency + writer serialization);\n\t\t// inside one batch, the effective width bounds the running children.\n\t\tlet cursor = 0;\n\t\tconst runners: Promise<void>[] = [];\n\t\tconst runNext = async (): Promise<void> => {\n\t\t\twhile (cursor < batch.laneIds.length) {\n\t\t\t\tconst laneId = batch.laneIds[cursor];\n\t\t\t\tcursor += 1;\n\t\t\t\tif (input.signal?.aborted) {\n\t\t\t\t\toutcomes.push({ laneId, status: \"skipped-abort\" });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\toutcomes.push(\n\t\t\t\t\tawait runLane(input, laneId, effectiveLaneWidth, {\n\t\t\t\t\t\tenter: () => {\n\t\t\t\t\t\t\tactive += 1;\n\t\t\t\t\t\t\tmaxObservedConcurrency = Math.max(maxObservedConcurrency, active);\n\t\t\t\t\t\t},\n\t\t\t\t\t\texit: () => {\n\t\t\t\t\t\t\tactive -= 1;\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t};\n\t\tconst workers = Math.min(effectiveLaneWidth, batch.laneIds.length);\n\t\tfor (let i = 0; i < workers; i++) {\n\t\t\trunners.push(runNext());\n\t\t}\n\t\tawait Promise.all(runners);\n\t}\n\treturn { outcomes, effectiveLaneWidth, maxObservedConcurrency };\n}\n\nasync function runLane(\n\tinput: LaunchSubagentLanesInput,\n\tlaneId: string,\n\teffectiveLaneWidth: number,\n\tgauge: { readonly enter: () => void; readonly exit: () => void },\n): Promise<LaneOutcome> {\n\tlet releasePermit: (() => void) | undefined;\n\tif (input.heavyLaneIds?.has(laneId)) {\n\t\ttry {\n\t\t\tconst permit = await input.permitPool.acquire({\n\t\t\t\trequestId: `lane-${laneId}`,\n\t\t\t\tpromptRunId: input.promptRunId,\n\t\t\t\tworkloadClass: \"heavy\",\n\t\t\t\tweight: 1,\n\t\t\t\tsignal: input.signal,\n\t\t\t\ttimeoutMs: input.permitWaitTimeoutMs ?? 60_000,\n\t\t\t});\n\t\t\treleasePermit = () => permit.release();\n\t\t} catch (error) {\n\t\t\tconst code = error instanceof WorkloadPermitError ? error.code : \"unknown\";\n\t\t\treturn {\n\t\t\t\tlaneId,\n\t\t\t\tstatus: code === \"aborted\" ? \"skipped-abort\" : \"permit-rejected\",\n\t\t\t\tdiagnostic: `permit.${code}`,\n\t\t\t};\n\t\t}\n\t}\n\tgauge.enter();\n\ttry {\n\t\tawait input.launchLane({\n\t\t\tlaneId,\n\t\t\tpromptRunId: input.promptRunId,\n\t\t\tdecision: input.decision,\n\t\t\teffectiveLaneWidth,\n\t\t});\n\t\treturn { laneId, status: \"completed\" };\n\t} catch (error) {\n\t\t// §14.3: a failing child must not leak its permit; release below.\n\t\treturn { laneId, status: \"failed\", diagnostic: String(error).slice(0, 200) };\n\t} finally {\n\t\tgauge.exit();\n\t\treleasePermit?.();\n\t}\n}\n"]}
1
+ {"version":3,"file":"subagent-lane-launcher.d.ts","sourceRoot":"","sources":["../../src/core/subagent-lane-launcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACzE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AAC7E,OAAO,EAAuB,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEzF;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,0BAA0B,CAAC,EAAE,MAAM,CAAC;IAC7C,QAAQ,CAAC,yBAAyB,EAAE,MAAM,CAAC;IAC3C,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,kFAAkF;IAClF,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;CACvC;AAED,6FAA6F;AAC7F,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,uBAAuB,GAAG,MAAM,CAahF;AAED,qFAAoF;AACpF,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,yBAAyB,CAAC;IAC7C,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;CACpC;AAED,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,WAAW,GAAG,eAAe,GAAG,iBAAiB,GAAG,oBAAoB,CAAC;IACnH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,IAAI,EAAE,yBAAyB,CAAC;IACzC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,yBAAyB,CAAC;IAC7C,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAAC;IACxC,QAAQ,CAAC,0BAA0B,CAAC,EAAE,MAAM,CAAC;IAC7C,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,2EAA0E;IAC1E,QAAQ,CAAC,YAAY,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5C,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;CACtC;AAED,MAAM,WAAW,yBAAyB;IACzC,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;IAC1C,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;CACxC;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAwD7G","sourcesContent":["import type { ResourceAdmissionDecision } from \"./resource-admission.ts\";\nimport type { SubagentOrchestrationPlan } from \"./subagent-orchestration.ts\";\nimport { WorkloadPermitError, type WorkloadPermitPool } from \"./workload-permit-pool.ts\";\n\n/**\n * Subagent lane launch authority (OMK v0.97.x roadmap §14, M6/PR10).\n *\n * Turns `buildSubagentOrchestrationPlan()` output into enforced execution:\n * the §14.2 effective width is a hard cap on concurrently active children,\n * batches run in order (writer serialization / path conflicts are encoded in\n * the plan's batches, which supplies the `pathConflictFreeWidth` term), and\n * heavy lanes draw permits from the PARENT's shared pool (§14.1 — a child\n * never constructs its own pool here; it receives this launcher's budget).\n *\n * §14.3 guarantees: child failure releases its permit (leak 0), parent\n * abort aborts queued permit waits and never launches unstarted lanes, and\n * the child context carries the parent decision with a width the child\n * cannot raise (§14.4: no API exists to widen — the context is read-only).\n */\n\nexport interface EffectiveLaneWidthInput {\n\treadonly planWidth: number;\n\treadonly configuredMaxParallelLanes?: number;\n\treadonly admissionMaxParallelLanes: number;\n\treadonly availableHeavyPermits: number;\n\t/** Widest conflict-free batch; the plan's batching already serializes writers. */\n\treadonly pathConflictFreeWidth: number;\n}\n\n/** Zero authority defers execution; configured zero retains its legacy unlimited meaning. */\nexport function computeEffectiveLaneWidth(input: EffectiveLaneWidthInput): number {\n\tconst configured =\n\t\tinput.configuredMaxParallelLanes !== undefined && input.configuredMaxParallelLanes > 0\n\t\t\t? input.configuredMaxParallelLanes\n\t\t\t: Number.POSITIVE_INFINITY;\n\tconst width = Math.min(\n\t\tinput.planWidth,\n\t\tconfigured,\n\t\tinput.admissionMaxParallelLanes,\n\t\tinput.availableHeavyPermits,\n\t\tinput.pathConflictFreeWidth,\n\t);\n\treturn Number.isFinite(width) ? Math.max(0, Math.floor(width)) : 0;\n}\n\n/** Read-only budget handed to every child (§14.1). Nothing here can raise a cap. */\nexport interface SubagentLaneContext {\n\treadonly laneId: string;\n\treadonly promptRunId: string;\n\treadonly signal?: AbortSignal;\n\treadonly decision: ResourceAdmissionDecision;\n\treadonly effectiveLaneWidth: number;\n}\n\nexport interface LaneOutcome {\n\treadonly laneId: string;\n\treadonly status: \"completed\" | \"failed\" | \"cancelled\" | \"skipped-abort\" | \"permit-rejected\" | \"admission-deferred\";\n\treadonly diagnostic?: string;\n}\n\nexport interface LaunchSubagentLanesInput {\n\treadonly plan: SubagentOrchestrationPlan;\n\treadonly promptRunId: string;\n\treadonly decision: ResourceAdmissionDecision;\n\treadonly permitPool: WorkloadPermitPool;\n\treadonly configuredMaxParallelLanes?: number;\n\treadonly signal?: AbortSignal;\n\t/** Lane ids that run heavy work and must hold a shared permit (§14.3). */\n\treadonly heavyLaneIds?: ReadonlySet<string>;\n\t/** The actual child launcher (process spawn, SDK session, or test double). */\n\treadonly launchLane: (context: SubagentLaneContext) => Promise<void>;\n\treadonly permitWaitTimeoutMs?: number;\n}\n\nexport interface LaunchSubagentLanesResult {\n\treadonly outcomes: readonly LaneOutcome[];\n\treadonly effectiveLaneWidth: number;\n\treadonly maxObservedConcurrency: number;\n}\n\n/**\n * Execute a plan's batches with the §14.2 width as launcher authority.\n * Never throws for lane failures; the caller reads per-lane outcomes.\n */\nexport async function launchSubagentLanes(input: LaunchSubagentLanesInput): Promise<LaunchSubagentLanesResult> {\n\tconst poolSnapshot = input.permitPool.snapshot();\n\tconst effectiveLaneWidth = computeEffectiveLaneWidth({\n\t\tplanWidth: input.plan.route.width,\n\t\tconfiguredMaxParallelLanes: input.configuredMaxParallelLanes,\n\t\tadmissionMaxParallelLanes: input.decision.maxParallelLanes,\n\t\tavailableHeavyPermits: Math.min(\n\t\t\tMath.max(0, poolSnapshot.capacity - poolSnapshot.activeWeight),\n\t\t\tinput.heavyLaneIds?.size ? input.decision.maxHeavyProcesses : Number.POSITIVE_INFINITY,\n\t\t),\n\t\tpathConflictFreeWidth: Math.max(0, ...input.plan.batches.map((batch) => batch.laneIds.length)),\n\t});\n\n\tconst outcomes: LaneOutcome[] = [];\n\tlet active = 0;\n\tlet maxObservedConcurrency = 0;\n\n\tfor (const batch of input.plan.batches) {\n\t\tif (input.signal?.aborted || effectiveLaneWidth === 0) {\n\t\t\tfor (const laneId of batch.laneIds) {\n\t\t\t\toutcomes.push({ laneId, status: input.signal?.aborted ? \"skipped-abort\" : \"admission-deferred\" });\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\t// Batches execute sequentially (dependency + writer serialization);\n\t\t// inside one batch, the effective width bounds the running children.\n\t\tlet cursor = 0;\n\t\tconst runners: Promise<void>[] = [];\n\t\tconst runNext = async (): Promise<void> => {\n\t\t\twhile (cursor < batch.laneIds.length) {\n\t\t\t\tconst laneId = batch.laneIds[cursor];\n\t\t\t\tcursor += 1;\n\t\t\t\tif (input.signal?.aborted) {\n\t\t\t\t\toutcomes.push({ laneId, status: \"skipped-abort\" });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\toutcomes.push(\n\t\t\t\t\tawait runLane(input, laneId, effectiveLaneWidth, {\n\t\t\t\t\t\tenter: () => {\n\t\t\t\t\t\t\tactive += 1;\n\t\t\t\t\t\t\tmaxObservedConcurrency = Math.max(maxObservedConcurrency, active);\n\t\t\t\t\t\t},\n\t\t\t\t\t\texit: () => {\n\t\t\t\t\t\t\tactive -= 1;\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t};\n\t\tconst workers = Math.min(effectiveLaneWidth, batch.laneIds.length);\n\t\tfor (let i = 0; i < workers; i++) {\n\t\t\trunners.push(runNext());\n\t\t}\n\t\tawait Promise.all(runners);\n\t}\n\treturn { outcomes, effectiveLaneWidth, maxObservedConcurrency };\n}\n\nasync function runLane(\n\tinput: LaunchSubagentLanesInput,\n\tlaneId: string,\n\teffectiveLaneWidth: number,\n\tgauge: { readonly enter: () => void; readonly exit: () => void },\n): Promise<LaneOutcome> {\n\tlet releasePermit: (() => void) | undefined;\n\tif (input.heavyLaneIds?.has(laneId)) {\n\t\tif (input.decision.action === \"defer-heavy\" || input.decision.maxHeavyProcesses === 0) {\n\t\t\treturn { laneId, status: \"admission-deferred\" };\n\t\t}\n\t\ttry {\n\t\t\tconst permit = await input.permitPool.acquire({\n\t\t\t\trequestId: `lane-${laneId}`,\n\t\t\t\tpromptRunId: input.promptRunId,\n\t\t\t\tworkloadClass: \"heavy\",\n\t\t\t\tweight: 1,\n\t\t\t\tsignal: input.signal,\n\t\t\t\ttimeoutMs: input.permitWaitTimeoutMs ?? 60_000,\n\t\t\t});\n\t\t\treleasePermit = () => permit.release();\n\t\t} catch (error) {\n\t\t\tconst code = error instanceof WorkloadPermitError ? error.code : \"unknown\";\n\t\t\treturn {\n\t\t\t\tlaneId,\n\t\t\t\tstatus: code === \"aborted\" ? \"skipped-abort\" : \"permit-rejected\",\n\t\t\t\tdiagnostic: `permit.${code}`,\n\t\t\t};\n\t\t}\n\t}\n\tif (input.signal?.aborted) {\n\t\treleasePermit?.();\n\t\treturn { laneId, status: \"skipped-abort\" };\n\t}\n\tgauge.enter();\n\ttry {\n\t\tawait input.launchLane({\n\t\t\tlaneId,\n\t\t\tpromptRunId: input.promptRunId,\n\t\t\tsignal: input.signal,\n\t\t\tdecision: input.decision,\n\t\t\teffectiveLaneWidth,\n\t\t});\n\t\treturn { laneId, status: input.signal?.aborted ? \"cancelled\" : \"completed\" };\n\t} catch {\n\t\t// Child error text is untrusted and may contain credentials.\n\t\treturn {\n\t\t\tlaneId,\n\t\t\tstatus: input.signal?.aborted ? \"cancelled\" : \"failed\",\n\t\t\tdiagnostic: \"lane.execution_failed\",\n\t\t};\n\t} finally {\n\t\tgauge.exit();\n\t\treleasePermit?.();\n\t}\n}\n"]}
@@ -1,11 +1,11 @@
1
1
  import { WorkloadPermitError } from "./workload-permit-pool.js";
2
- /** §14.2: `min(plan, configured, admission, availableHeavyPermits, conflictFree)`, floor 1. */
2
+ /** Zero authority defers execution; configured zero retains its legacy unlimited meaning. */
3
3
  export function computeEffectiveLaneWidth(input) {
4
4
  const configured = input.configuredMaxParallelLanes !== undefined && input.configuredMaxParallelLanes > 0
5
5
  ? input.configuredMaxParallelLanes
6
6
  : Number.POSITIVE_INFINITY;
7
- const width = Math.min(input.planWidth, configured, input.admissionMaxParallelLanes, Math.max(1, input.availableHeavyPermits), input.pathConflictFreeWidth);
8
- return Math.max(1, Math.floor(width));
7
+ const width = Math.min(input.planWidth, configured, input.admissionMaxParallelLanes, input.availableHeavyPermits, input.pathConflictFreeWidth);
8
+ return Number.isFinite(width) ? Math.max(0, Math.floor(width)) : 0;
9
9
  }
10
10
  /**
11
11
  * Execute a plan's batches with the §14.2 width as launcher authority.
@@ -14,20 +14,19 @@ export function computeEffectiveLaneWidth(input) {
14
14
  export async function launchSubagentLanes(input) {
15
15
  const poolSnapshot = input.permitPool.snapshot();
16
16
  const effectiveLaneWidth = computeEffectiveLaneWidth({
17
- planWidth: Math.max(1, input.plan.route.width),
17
+ planWidth: input.plan.route.width,
18
18
  configuredMaxParallelLanes: input.configuredMaxParallelLanes,
19
19
  admissionMaxParallelLanes: input.decision.maxParallelLanes,
20
- availableHeavyPermits: Math.max(0, poolSnapshot.capacity - poolSnapshot.activeWeight),
21
- pathConflictFreeWidth: Math.max(1, ...input.plan.batches.map((batch) => batch.laneIds.length)),
20
+ availableHeavyPermits: Math.min(Math.max(0, poolSnapshot.capacity - poolSnapshot.activeWeight), input.heavyLaneIds?.size ? input.decision.maxHeavyProcesses : Number.POSITIVE_INFINITY),
21
+ pathConflictFreeWidth: Math.max(0, ...input.plan.batches.map((batch) => batch.laneIds.length)),
22
22
  });
23
23
  const outcomes = [];
24
24
  let active = 0;
25
25
  let maxObservedConcurrency = 0;
26
26
  for (const batch of input.plan.batches) {
27
- if (input.signal?.aborted) {
28
- // Parent abort: unstarted lanes are never launched (§14.3).
27
+ if (input.signal?.aborted || effectiveLaneWidth === 0) {
29
28
  for (const laneId of batch.laneIds) {
30
- outcomes.push({ laneId, status: "skipped-abort" });
29
+ outcomes.push({ laneId, status: input.signal?.aborted ? "skipped-abort" : "admission-deferred" });
31
30
  }
32
31
  continue;
33
32
  }
@@ -65,6 +64,9 @@ export async function launchSubagentLanes(input) {
65
64
  async function runLane(input, laneId, effectiveLaneWidth, gauge) {
66
65
  let releasePermit;
67
66
  if (input.heavyLaneIds?.has(laneId)) {
67
+ if (input.decision.action === "defer-heavy" || input.decision.maxHeavyProcesses === 0) {
68
+ return { laneId, status: "admission-deferred" };
69
+ }
68
70
  try {
69
71
  const permit = await input.permitPool.acquire({
70
72
  requestId: `lane-${laneId}`,
@@ -85,19 +87,28 @@ async function runLane(input, laneId, effectiveLaneWidth, gauge) {
85
87
  };
86
88
  }
87
89
  }
90
+ if (input.signal?.aborted) {
91
+ releasePermit?.();
92
+ return { laneId, status: "skipped-abort" };
93
+ }
88
94
  gauge.enter();
89
95
  try {
90
96
  await input.launchLane({
91
97
  laneId,
92
98
  promptRunId: input.promptRunId,
99
+ signal: input.signal,
93
100
  decision: input.decision,
94
101
  effectiveLaneWidth,
95
102
  });
96
- return { laneId, status: "completed" };
103
+ return { laneId, status: input.signal?.aborted ? "cancelled" : "completed" };
97
104
  }
98
- catch (error) {
99
- // §14.3: a failing child must not leak its permit; release below.
100
- return { laneId, status: "failed", diagnostic: String(error).slice(0, 200) };
105
+ catch {
106
+ // Child error text is untrusted and may contain credentials.
107
+ return {
108
+ laneId,
109
+ status: input.signal?.aborted ? "cancelled" : "failed",
110
+ diagnostic: "lane.execution_failed",
111
+ };
101
112
  }
102
113
  finally {
103
114
  gauge.exit();
@@ -1 +1 @@
1
- {"version":3,"file":"subagent-lane-launcher.js","sourceRoot":"","sources":["../../src/core/subagent-lane-launcher.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ResourceAdmissionDecision } from \"./resource-admission.ts\";\nimport type { SubagentOrchestrationPlan } from \"./subagent-orchestration.ts\";\nimport { WorkloadPermitError, type WorkloadPermitPool } from \"./workload-permit-pool.ts\";\n\n/**\n * Subagent lane launch authority (OMK v0.97.x roadmap §14, M6/PR10).\n *\n * Turns `buildSubagentOrchestrationPlan()` output into enforced execution:\n * the §14.2 effective width is a hard cap on concurrently active children,\n * batches run in order (writer serialization / path conflicts are encoded in\n * the plan's batches, which supplies the `pathConflictFreeWidth` term), and\n * heavy lanes draw permits from the PARENT's shared pool (§14.1 — a child\n * never constructs its own pool here; it receives this launcher's budget).\n *\n * §14.3 guarantees: child failure releases its permit (leak 0), parent\n * abort aborts queued permit waits and never launches unstarted lanes, and\n * the child context carries the parent decision with a width the child\n * cannot raise (§14.4: no API exists to widen — the context is read-only).\n */\n\nexport interface EffectiveLaneWidthInput {\n\treadonly planWidth: number;\n\treadonly configuredMaxParallelLanes?: number;\n\treadonly admissionMaxParallelLanes: number;\n\treadonly availableHeavyPermits: number;\n\t/** Widest conflict-free batch; the plan's batching already serializes writers. */\n\treadonly pathConflictFreeWidth: number;\n}\n\n/** §14.2: `min(plan, configured, admission, availableHeavyPermits, conflictFree)`, floor 1. */\nexport function computeEffectiveLaneWidth(input: EffectiveLaneWidthInput): number {\n\tconst configured =\n\t\tinput.configuredMaxParallelLanes !== undefined && input.configuredMaxParallelLanes > 0\n\t\t\t? input.configuredMaxParallelLanes\n\t\t\t: Number.POSITIVE_INFINITY;\n\tconst width = Math.min(\n\t\tinput.planWidth,\n\t\tconfigured,\n\t\tinput.admissionMaxParallelLanes,\n\t\tMath.max(1, input.availableHeavyPermits),\n\t\tinput.pathConflictFreeWidth,\n\t);\n\treturn Math.max(1, Math.floor(width));\n}\n\n/** Read-only budget handed to every child (§14.1). Nothing here can raise a cap. */\nexport interface SubagentLaneContext {\n\treadonly laneId: string;\n\treadonly promptRunId: string;\n\treadonly decision: ResourceAdmissionDecision;\n\treadonly effectiveLaneWidth: number;\n}\n\nexport interface LaneOutcome {\n\treadonly laneId: string;\n\treadonly status: \"completed\" | \"failed\" | \"skipped-abort\" | \"permit-rejected\";\n\treadonly diagnostic?: string;\n}\n\nexport interface LaunchSubagentLanesInput {\n\treadonly plan: SubagentOrchestrationPlan;\n\treadonly promptRunId: string;\n\treadonly decision: ResourceAdmissionDecision;\n\treadonly permitPool: WorkloadPermitPool;\n\treadonly configuredMaxParallelLanes?: number;\n\treadonly signal?: AbortSignal;\n\t/** Lane ids that run heavy work and must hold a shared permit (§14.3). */\n\treadonly heavyLaneIds?: ReadonlySet<string>;\n\t/** The actual child launcher (process spawn, SDK session, or test double). */\n\treadonly launchLane: (context: SubagentLaneContext) => Promise<void>;\n\treadonly permitWaitTimeoutMs?: number;\n}\n\nexport interface LaunchSubagentLanesResult {\n\treadonly outcomes: readonly LaneOutcome[];\n\treadonly effectiveLaneWidth: number;\n\treadonly maxObservedConcurrency: number;\n}\n\n/**\n * Execute a plan's batches with the §14.2 width as launcher authority.\n * Never throws for lane failures; the caller reads per-lane outcomes.\n */\nexport async function launchSubagentLanes(input: LaunchSubagentLanesInput): Promise<LaunchSubagentLanesResult> {\n\tconst poolSnapshot = input.permitPool.snapshot();\n\tconst effectiveLaneWidth = computeEffectiveLaneWidth({\n\t\tplanWidth: Math.max(1, input.plan.route.width),\n\t\tconfiguredMaxParallelLanes: input.configuredMaxParallelLanes,\n\t\tadmissionMaxParallelLanes: input.decision.maxParallelLanes,\n\t\tavailableHeavyPermits: Math.max(0, poolSnapshot.capacity - poolSnapshot.activeWeight),\n\t\tpathConflictFreeWidth: Math.max(1, ...input.plan.batches.map((batch) => batch.laneIds.length)),\n\t});\n\n\tconst outcomes: LaneOutcome[] = [];\n\tlet active = 0;\n\tlet maxObservedConcurrency = 0;\n\n\tfor (const batch of input.plan.batches) {\n\t\tif (input.signal?.aborted) {\n\t\t\t// Parent abort: unstarted lanes are never launched (§14.3).\n\t\t\tfor (const laneId of batch.laneIds) {\n\t\t\t\toutcomes.push({ laneId, status: \"skipped-abort\" });\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\t// Batches execute sequentially (dependency + writer serialization);\n\t\t// inside one batch, the effective width bounds the running children.\n\t\tlet cursor = 0;\n\t\tconst runners: Promise<void>[] = [];\n\t\tconst runNext = async (): Promise<void> => {\n\t\t\twhile (cursor < batch.laneIds.length) {\n\t\t\t\tconst laneId = batch.laneIds[cursor];\n\t\t\t\tcursor += 1;\n\t\t\t\tif (input.signal?.aborted) {\n\t\t\t\t\toutcomes.push({ laneId, status: \"skipped-abort\" });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\toutcomes.push(\n\t\t\t\t\tawait runLane(input, laneId, effectiveLaneWidth, {\n\t\t\t\t\t\tenter: () => {\n\t\t\t\t\t\t\tactive += 1;\n\t\t\t\t\t\t\tmaxObservedConcurrency = Math.max(maxObservedConcurrency, active);\n\t\t\t\t\t\t},\n\t\t\t\t\t\texit: () => {\n\t\t\t\t\t\t\tactive -= 1;\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t};\n\t\tconst workers = Math.min(effectiveLaneWidth, batch.laneIds.length);\n\t\tfor (let i = 0; i < workers; i++) {\n\t\t\trunners.push(runNext());\n\t\t}\n\t\tawait Promise.all(runners);\n\t}\n\treturn { outcomes, effectiveLaneWidth, maxObservedConcurrency };\n}\n\nasync function runLane(\n\tinput: LaunchSubagentLanesInput,\n\tlaneId: string,\n\teffectiveLaneWidth: number,\n\tgauge: { readonly enter: () => void; readonly exit: () => void },\n): Promise<LaneOutcome> {\n\tlet releasePermit: (() => void) | undefined;\n\tif (input.heavyLaneIds?.has(laneId)) {\n\t\ttry {\n\t\t\tconst permit = await input.permitPool.acquire({\n\t\t\t\trequestId: `lane-${laneId}`,\n\t\t\t\tpromptRunId: input.promptRunId,\n\t\t\t\tworkloadClass: \"heavy\",\n\t\t\t\tweight: 1,\n\t\t\t\tsignal: input.signal,\n\t\t\t\ttimeoutMs: input.permitWaitTimeoutMs ?? 60_000,\n\t\t\t});\n\t\t\treleasePermit = () => permit.release();\n\t\t} catch (error) {\n\t\t\tconst code = error instanceof WorkloadPermitError ? error.code : \"unknown\";\n\t\t\treturn {\n\t\t\t\tlaneId,\n\t\t\t\tstatus: code === \"aborted\" ? \"skipped-abort\" : \"permit-rejected\",\n\t\t\t\tdiagnostic: `permit.${code}`,\n\t\t\t};\n\t\t}\n\t}\n\tgauge.enter();\n\ttry {\n\t\tawait input.launchLane({\n\t\t\tlaneId,\n\t\t\tpromptRunId: input.promptRunId,\n\t\t\tdecision: input.decision,\n\t\t\teffectiveLaneWidth,\n\t\t});\n\t\treturn { laneId, status: \"completed\" };\n\t} catch (error) {\n\t\t// §14.3: a failing child must not leak its permit; release below.\n\t\treturn { laneId, status: \"failed\", diagnostic: String(error).slice(0, 200) };\n\t} finally {\n\t\tgauge.exit();\n\t\treleasePermit?.();\n\t}\n}\n"]}
1
+ {"version":3,"file":"subagent-lane-launcher.js","sourceRoot":"","sources":["../../src/core/subagent-lane-launcher.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ResourceAdmissionDecision } from \"./resource-admission.ts\";\nimport type { SubagentOrchestrationPlan } from \"./subagent-orchestration.ts\";\nimport { WorkloadPermitError, type WorkloadPermitPool } from \"./workload-permit-pool.ts\";\n\n/**\n * Subagent lane launch authority (OMK v0.97.x roadmap §14, M6/PR10).\n *\n * Turns `buildSubagentOrchestrationPlan()` output into enforced execution:\n * the §14.2 effective width is a hard cap on concurrently active children,\n * batches run in order (writer serialization / path conflicts are encoded in\n * the plan's batches, which supplies the `pathConflictFreeWidth` term), and\n * heavy lanes draw permits from the PARENT's shared pool (§14.1 — a child\n * never constructs its own pool here; it receives this launcher's budget).\n *\n * §14.3 guarantees: child failure releases its permit (leak 0), parent\n * abort aborts queued permit waits and never launches unstarted lanes, and\n * the child context carries the parent decision with a width the child\n * cannot raise (§14.4: no API exists to widen — the context is read-only).\n */\n\nexport interface EffectiveLaneWidthInput {\n\treadonly planWidth: number;\n\treadonly configuredMaxParallelLanes?: number;\n\treadonly admissionMaxParallelLanes: number;\n\treadonly availableHeavyPermits: number;\n\t/** Widest conflict-free batch; the plan's batching already serializes writers. */\n\treadonly pathConflictFreeWidth: number;\n}\n\n/** Zero authority defers execution; configured zero retains its legacy unlimited meaning. */\nexport function computeEffectiveLaneWidth(input: EffectiveLaneWidthInput): number {\n\tconst configured =\n\t\tinput.configuredMaxParallelLanes !== undefined && input.configuredMaxParallelLanes > 0\n\t\t\t? input.configuredMaxParallelLanes\n\t\t\t: Number.POSITIVE_INFINITY;\n\tconst width = Math.min(\n\t\tinput.planWidth,\n\t\tconfigured,\n\t\tinput.admissionMaxParallelLanes,\n\t\tinput.availableHeavyPermits,\n\t\tinput.pathConflictFreeWidth,\n\t);\n\treturn Number.isFinite(width) ? Math.max(0, Math.floor(width)) : 0;\n}\n\n/** Read-only budget handed to every child (§14.1). Nothing here can raise a cap. */\nexport interface SubagentLaneContext {\n\treadonly laneId: string;\n\treadonly promptRunId: string;\n\treadonly signal?: AbortSignal;\n\treadonly decision: ResourceAdmissionDecision;\n\treadonly effectiveLaneWidth: number;\n}\n\nexport interface LaneOutcome {\n\treadonly laneId: string;\n\treadonly status: \"completed\" | \"failed\" | \"cancelled\" | \"skipped-abort\" | \"permit-rejected\" | \"admission-deferred\";\n\treadonly diagnostic?: string;\n}\n\nexport interface LaunchSubagentLanesInput {\n\treadonly plan: SubagentOrchestrationPlan;\n\treadonly promptRunId: string;\n\treadonly decision: ResourceAdmissionDecision;\n\treadonly permitPool: WorkloadPermitPool;\n\treadonly configuredMaxParallelLanes?: number;\n\treadonly signal?: AbortSignal;\n\t/** Lane ids that run heavy work and must hold a shared permit (§14.3). */\n\treadonly heavyLaneIds?: ReadonlySet<string>;\n\t/** The actual child launcher (process spawn, SDK session, or test double). */\n\treadonly launchLane: (context: SubagentLaneContext) => Promise<void>;\n\treadonly permitWaitTimeoutMs?: number;\n}\n\nexport interface LaunchSubagentLanesResult {\n\treadonly outcomes: readonly LaneOutcome[];\n\treadonly effectiveLaneWidth: number;\n\treadonly maxObservedConcurrency: number;\n}\n\n/**\n * Execute a plan's batches with the §14.2 width as launcher authority.\n * Never throws for lane failures; the caller reads per-lane outcomes.\n */\nexport async function launchSubagentLanes(input: LaunchSubagentLanesInput): Promise<LaunchSubagentLanesResult> {\n\tconst poolSnapshot = input.permitPool.snapshot();\n\tconst effectiveLaneWidth = computeEffectiveLaneWidth({\n\t\tplanWidth: input.plan.route.width,\n\t\tconfiguredMaxParallelLanes: input.configuredMaxParallelLanes,\n\t\tadmissionMaxParallelLanes: input.decision.maxParallelLanes,\n\t\tavailableHeavyPermits: Math.min(\n\t\t\tMath.max(0, poolSnapshot.capacity - poolSnapshot.activeWeight),\n\t\t\tinput.heavyLaneIds?.size ? input.decision.maxHeavyProcesses : Number.POSITIVE_INFINITY,\n\t\t),\n\t\tpathConflictFreeWidth: Math.max(0, ...input.plan.batches.map((batch) => batch.laneIds.length)),\n\t});\n\n\tconst outcomes: LaneOutcome[] = [];\n\tlet active = 0;\n\tlet maxObservedConcurrency = 0;\n\n\tfor (const batch of input.plan.batches) {\n\t\tif (input.signal?.aborted || effectiveLaneWidth === 0) {\n\t\t\tfor (const laneId of batch.laneIds) {\n\t\t\t\toutcomes.push({ laneId, status: input.signal?.aborted ? \"skipped-abort\" : \"admission-deferred\" });\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\t// Batches execute sequentially (dependency + writer serialization);\n\t\t// inside one batch, the effective width bounds the running children.\n\t\tlet cursor = 0;\n\t\tconst runners: Promise<void>[] = [];\n\t\tconst runNext = async (): Promise<void> => {\n\t\t\twhile (cursor < batch.laneIds.length) {\n\t\t\t\tconst laneId = batch.laneIds[cursor];\n\t\t\t\tcursor += 1;\n\t\t\t\tif (input.signal?.aborted) {\n\t\t\t\t\toutcomes.push({ laneId, status: \"skipped-abort\" });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\toutcomes.push(\n\t\t\t\t\tawait runLane(input, laneId, effectiveLaneWidth, {\n\t\t\t\t\t\tenter: () => {\n\t\t\t\t\t\t\tactive += 1;\n\t\t\t\t\t\t\tmaxObservedConcurrency = Math.max(maxObservedConcurrency, active);\n\t\t\t\t\t\t},\n\t\t\t\t\t\texit: () => {\n\t\t\t\t\t\t\tactive -= 1;\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t};\n\t\tconst workers = Math.min(effectiveLaneWidth, batch.laneIds.length);\n\t\tfor (let i = 0; i < workers; i++) {\n\t\t\trunners.push(runNext());\n\t\t}\n\t\tawait Promise.all(runners);\n\t}\n\treturn { outcomes, effectiveLaneWidth, maxObservedConcurrency };\n}\n\nasync function runLane(\n\tinput: LaunchSubagentLanesInput,\n\tlaneId: string,\n\teffectiveLaneWidth: number,\n\tgauge: { readonly enter: () => void; readonly exit: () => void },\n): Promise<LaneOutcome> {\n\tlet releasePermit: (() => void) | undefined;\n\tif (input.heavyLaneIds?.has(laneId)) {\n\t\tif (input.decision.action === \"defer-heavy\" || input.decision.maxHeavyProcesses === 0) {\n\t\t\treturn { laneId, status: \"admission-deferred\" };\n\t\t}\n\t\ttry {\n\t\t\tconst permit = await input.permitPool.acquire({\n\t\t\t\trequestId: `lane-${laneId}`,\n\t\t\t\tpromptRunId: input.promptRunId,\n\t\t\t\tworkloadClass: \"heavy\",\n\t\t\t\tweight: 1,\n\t\t\t\tsignal: input.signal,\n\t\t\t\ttimeoutMs: input.permitWaitTimeoutMs ?? 60_000,\n\t\t\t});\n\t\t\treleasePermit = () => permit.release();\n\t\t} catch (error) {\n\t\t\tconst code = error instanceof WorkloadPermitError ? error.code : \"unknown\";\n\t\t\treturn {\n\t\t\t\tlaneId,\n\t\t\t\tstatus: code === \"aborted\" ? \"skipped-abort\" : \"permit-rejected\",\n\t\t\t\tdiagnostic: `permit.${code}`,\n\t\t\t};\n\t\t}\n\t}\n\tif (input.signal?.aborted) {\n\t\treleasePermit?.();\n\t\treturn { laneId, status: \"skipped-abort\" };\n\t}\n\tgauge.enter();\n\ttry {\n\t\tawait input.launchLane({\n\t\t\tlaneId,\n\t\t\tpromptRunId: input.promptRunId,\n\t\t\tsignal: input.signal,\n\t\t\tdecision: input.decision,\n\t\t\teffectiveLaneWidth,\n\t\t});\n\t\treturn { laneId, status: input.signal?.aborted ? \"cancelled\" : \"completed\" };\n\t} catch {\n\t\t// Child error text is untrusted and may contain credentials.\n\t\treturn {\n\t\t\tlaneId,\n\t\t\tstatus: input.signal?.aborted ? \"cancelled\" : \"failed\",\n\t\t\tdiagnostic: \"lane.execution_failed\",\n\t\t};\n\t} finally {\n\t\tgauge.exit();\n\t\treleasePermit?.();\n\t}\n}\n"]}
@@ -0,0 +1,26 @@
1
+ import type { RunContract } from "omk-protocol";
2
+ import type { NamespaceIdentity } from "./namespace-identity.ts";
3
+ export interface SandboxExecution {
4
+ readonly workspace: string;
5
+ readonly argv: readonly string[];
6
+ readonly writable: boolean;
7
+ readonly timeoutMs: number;
8
+ readonly cleanupMs: number;
9
+ readonly maxOutputBytes: number;
10
+ readonly signal?: AbortSignal;
11
+ readonly onReady?: (identity: NamespaceIdentity) => void | Promise<void>;
12
+ }
13
+ export interface SandboxOutcome {
14
+ readonly stdout: Buffer;
15
+ readonly stderr: Buffer;
16
+ readonly exitCode: number | null;
17
+ readonly failure: string | null;
18
+ readonly startedAt: string;
19
+ readonly finishedAt: string;
20
+ readonly durationMs: number;
21
+ }
22
+ export declare function commandEnvironmentDigest(contract: RunContract, driver?: "legacy" | "gated-v1"): string;
23
+ export declare function probeVerifiedSandbox(): void;
24
+ /** The only process dispatch port; close means the PID namespace and its inherited pipes have drained. */
25
+ export declare function executeSandbox(request: SandboxExecution): Promise<SandboxOutcome>;
26
+ //# sourceMappingURL=broker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"broker.d.ts","sourceRoot":"","sources":["../../../src/core/verified-run/broker.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAsCjE,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,iBAAiB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzE;AACD,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,WAAW,EAAE,MAAM,GAAE,QAAQ,GAAG,UAAqB,GAAG,MAAM,CA0ChH;AAED,wBAAgB,oBAAoB,IAAI,IAAI,CAS3C;AAED,0GAA0G;AAC1G,wBAAsB,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,CAqGvF","sourcesContent":["import { spawn, spawnSync } from \"node:child_process\";\nimport { existsSync, readFileSync, realpathSync } from \"node:fs\";\nimport { release } from \"node:os\";\nimport { performance } from \"node:perf_hooks\";\nimport type { RunContract } from \"omk-protocol\";\nimport type { NamespaceIdentity } from \"./namespace-identity.ts\";\nimport { identityFromSandboxInfo, PROCESS_GATE_ARGV } from \"./process-gate.ts\";\nimport { digestBytes, digestObject, VerifiedRunError } from \"./storage.ts\";\n\nconst BWRAP = \"/usr/bin/bwrap\";\nconst SYSTEM_ARGS = [\n\t\"--unshare-all\",\n\t\"--die-with-parent\",\n\t\"--new-session\",\n\t\"--cap-drop\",\n\t\"ALL\",\n\t\"--clearenv\",\n\t\"--ro-bind\",\n\t\"/usr\",\n\t\"/usr\",\n\t\"--symlink\",\n\t\"usr/bin\",\n\t\"/bin\",\n\t\"--symlink\",\n\t\"usr/lib\",\n\t\"/lib\",\n\t\"--symlink\",\n\t\"usr/lib64\",\n\t\"/lib64\",\n\t\"--proc\",\n\t\"/proc\",\n\t\"--dev\",\n\t\"/dev\",\n\t\"--tmpfs\",\n\t\"/tmp\",\n\t\"--setenv\",\n\t\"PATH\",\n\t\"/usr/bin:/bin\",\n\t\"--setenv\",\n\t\"LANG\",\n\t\"C.UTF-8\",\n] as const;\n\nexport interface SandboxExecution {\n\treadonly workspace: string;\n\treadonly argv: readonly string[];\n\treadonly writable: boolean;\n\treadonly timeoutMs: number;\n\treadonly cleanupMs: number;\n\treadonly maxOutputBytes: number;\n\treadonly signal?: AbortSignal;\n\treadonly onReady?: (identity: NamespaceIdentity) => void | Promise<void>;\n}\nexport interface SandboxOutcome {\n\treadonly stdout: Buffer;\n\treadonly stderr: Buffer;\n\treadonly exitCode: number | null;\n\treadonly failure: string | null;\n\treadonly startedAt: string;\n\treadonly finishedAt: string;\n\treadonly durationMs: number;\n}\n\nexport function commandEnvironmentDigest(contract: RunContract, driver: \"legacy\" | \"gated-v1\" = \"legacy\"): string {\n\tif (process.platform !== \"linux\" || !existsSync(BWRAP)) throw new VerifiedRunError(\"unsupported\");\n\tlet writers: readonly (readonly string[])[];\n\tswitch (contract.profile) {\n\t\tcase \"linux-command-v1\":\n\t\t\twriters = [contract.writer];\n\t\t\tbreak;\n\t\tcase \"linux-scripted-agent-v1\":\n\t\t\twriters = contract.writer.steps;\n\t\t\tbreak;\n\t\tcase \"linux-command-dag-v1\":\n\t\t\twriters = contract.writer.tasks.flatMap((task) => task.attempts);\n\t\t\tbreak;\n\t\tdefault: {\n\t\t\tconst exhaustive: never = contract;\n\t\t\tthrow new VerifiedRunError(String(exhaustive));\n\t\t}\n\t}\n\tconst binaries = [\n\t\t...new Set([\n\t\t\tBWRAP,\n\t\t\t...writers.map((argv) => argv[0]),\n\t\t\t...contract.checks.map((check) => check.argv[0]),\n\t\t\t...(driver === \"gated-v1\" ? [PROCESS_GATE_ARGV[0]] : []),\n\t\t]),\n\t];\n\tconst materials = binaries.map((binary) => {\n\t\tif (!binary || (!binary.startsWith(\"/usr/bin/\") && !binary.startsWith(\"/bin/\")))\n\t\t\tthrow new VerifiedRunError(\"unsupported_executable\");\n\t\tconst path = realpathSync(binary);\n\t\tif (!path.startsWith(\"/usr/\")) throw new VerifiedRunError(\"unsupported_executable\");\n\t\treturn { path, digest: digestBytes(readFileSync(path)) };\n\t});\n\treturn digestObject({\n\t\tprofile: contract.profile,\n\t\tplatform: process.platform,\n\t\tarchitecture: process.arch,\n\t\tkernel: release(),\n\t\tsandbox: SYSTEM_ARGS,\n\t\t...(driver === \"gated-v1\" ? { gate: PROCESS_GATE_ARGV, infoFd: 3 } : {}),\n\t\tbinaries: materials,\n\t});\n}\n\nexport function probeVerifiedSandbox(): void {\n\tif (process.platform !== \"linux\" || !existsSync(BWRAP)) throw new VerifiedRunError(\"unsupported\");\n\tconst probe = spawnSync(BWRAP, [...SYSTEM_ARGS, \"--\", \"/bin/true\"], {\n\t\tenv: {},\n\t\ttimeout: 3000,\n\t\tmaxBuffer: 4096,\n\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t});\n\tif (probe.error || probe.status !== 0) throw new VerifiedRunError(\"unsupported\");\n}\n\n/** The only process dispatch port; close means the PID namespace and its inherited pipes have drained. */\nexport async function executeSandbox(request: SandboxExecution): Promise<SandboxOutcome> {\n\tif (request.signal?.aborted) throw new VerifiedRunError(\"cancelled\");\n\tif (request.timeoutMs <= 0) throw new VerifiedRunError(\"deadline\");\n\tconst started = performance.now();\n\tconst startedAt = new Date().toISOString();\n\tconst argv = [\n\t\t...SYSTEM_ARGS,\n\t\trequest.writable ? \"--bind\" : \"--ro-bind\",\n\t\trequest.workspace,\n\t\t\"/workspace\",\n\t\t\"--chdir\",\n\t\t\"/workspace\",\n\t\t...(request.onReady ? [\"--info-fd\", \"3\", \"--\", ...PROCESS_GATE_ARGV] : [\"--\"]),\n\t\t...request.argv,\n\t];\n\treturn new Promise((resolve, reject) => {\n\t\tconst child = request.onReady\n\t\t\t? spawn(BWRAP, argv, { env: {}, stdio: [\"pipe\", \"pipe\", \"pipe\", \"pipe\"] })\n\t\t\t: spawn(BWRAP, argv, { env: {}, stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n\t\tlet gateFailed = false;\n\t\tlet gateError: unknown;\n\t\tconst stdout: Buffer[] = [];\n\t\tconst stderr: Buffer[] = [];\n\t\tlet size = 0;\n\t\tlet failure: string | null = null;\n\t\tlet cleanup: ReturnType<typeof setTimeout> | undefined;\n\t\tlet finished = false;\n\t\tconst stop = (reason: string): void => {\n\t\t\tif (finished || failure) return;\n\t\t\tfailure = reason;\n\t\t\tchild.kill(\"SIGKILL\");\n\t\t\tcleanup = setTimeout(() => reject(new VerifiedRunError(\"unsettled\")), request.cleanupMs);\n\t\t};\n\t\tconst deadline = setTimeout(() => stop(\"deadline\"), request.timeoutMs);\n\t\tconst abort = (): void => stop(\"cancelled\");\n\t\trequest.signal?.addEventListener(\"abort\", abort, { once: true });\n\t\tif (request.signal?.aborted) abort();\n\t\tconst collect = (target: Buffer[], chunk: Buffer): void => {\n\t\t\tif (failure) return;\n\t\t\tsize += chunk.length;\n\t\t\tif (size > request.maxOutputBytes) {\n\t\t\t\tstop(\"output_limit\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttarget.push(chunk);\n\t\t};\n\t\tchild.stdout?.on(\"data\", (chunk: Buffer) => collect(stdout, chunk));\n\t\tchild.stderr?.on(\"data\", (chunk: Buffer) => collect(stderr, chunk));\n\t\tif (request.onReady) {\n\t\t\tconst info = child.stdio[3];\n\t\t\tlet metadata = \"\";\n\t\t\tif (!info || !child.stdin) {\n\t\t\t\tgateFailed = true;\n\t\t\t\tgateError = new VerifiedRunError(\"process_identity\");\n\t\t\t\tstop(\"process_identity\");\n\t\t\t} else {\n\t\t\t\tchild.stdin.on(\"error\", () => stop(\"execution_failed\"));\n\t\t\t\tinfo.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\tmetadata += chunk.toString(\"utf8\");\n\t\t\t\t\tif (metadata.length > 4096) stop(\"process_identity\");\n\t\t\t\t});\n\t\t\t\tinfo.once(\"end\", async () => {\n\t\t\t\t\tif (finished || failure) return;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait request.onReady?.(identityFromSandboxInfo(metadata));\n\t\t\t\t\t\tif (performance.now() - started >= request.timeoutMs || request.signal?.aborted)\n\t\t\t\t\t\t\tstop(request.signal?.aborted ? \"cancelled\" : \"deadline\");\n\t\t\t\t\t\tif (!finished && !failure) child.stdin?.end(\"go\\n\");\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tgateFailed = true;\n\t\t\t\t\t\tgateError = error;\n\t\t\t\t\t\tstop(\"process_identity\");\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tchild.once(\"error\", () => {\n\t\t\tfailure = \"execution_failed\";\n\t\t});\n\t\tchild.once(\"close\", (exitCode) => {\n\t\t\tfinished = true;\n\t\t\tclearTimeout(deadline);\n\t\t\tclearTimeout(cleanup);\n\t\t\trequest.signal?.removeEventListener(\"abort\", abort);\n\t\t\tif (gateFailed) {\n\t\t\t\treject(gateError);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst durationMs = Math.max(0, Math.round(performance.now() - started));\n\t\t\tresolve({\n\t\t\t\tstdout: Buffer.concat(stdout),\n\t\t\t\tstderr: Buffer.concat(stderr),\n\t\t\t\texitCode,\n\t\t\t\tfailure: failure ?? (exitCode === 0 ? null : \"execution_failed\"),\n\t\t\t\tstartedAt,\n\t\t\t\t// Project the finish instant from the start's wall reference and monotonic elapsed time.\n\t\t\t\tfinishedAt: new Date(Date.parse(startedAt) + durationMs).toISOString(),\n\t\t\t\tdurationMs,\n\t\t\t});\n\t\t});\n\t});\n}\n"]}
@@ -0,0 +1,210 @@
1
+ import { spawn, spawnSync } from "node:child_process";
2
+ import { existsSync, readFileSync, realpathSync } from "node:fs";
3
+ import { release } from "node:os";
4
+ import { performance } from "node:perf_hooks";
5
+ import { identityFromSandboxInfo, PROCESS_GATE_ARGV } from "./process-gate.js";
6
+ import { digestBytes, digestObject, VerifiedRunError } from "./storage.js";
7
+ const BWRAP = "/usr/bin/bwrap";
8
+ const SYSTEM_ARGS = [
9
+ "--unshare-all",
10
+ "--die-with-parent",
11
+ "--new-session",
12
+ "--cap-drop",
13
+ "ALL",
14
+ "--clearenv",
15
+ "--ro-bind",
16
+ "/usr",
17
+ "/usr",
18
+ "--symlink",
19
+ "usr/bin",
20
+ "/bin",
21
+ "--symlink",
22
+ "usr/lib",
23
+ "/lib",
24
+ "--symlink",
25
+ "usr/lib64",
26
+ "/lib64",
27
+ "--proc",
28
+ "/proc",
29
+ "--dev",
30
+ "/dev",
31
+ "--tmpfs",
32
+ "/tmp",
33
+ "--setenv",
34
+ "PATH",
35
+ "/usr/bin:/bin",
36
+ "--setenv",
37
+ "LANG",
38
+ "C.UTF-8",
39
+ ];
40
+ export function commandEnvironmentDigest(contract, driver = "legacy") {
41
+ if (process.platform !== "linux" || !existsSync(BWRAP))
42
+ throw new VerifiedRunError("unsupported");
43
+ let writers;
44
+ switch (contract.profile) {
45
+ case "linux-command-v1":
46
+ writers = [contract.writer];
47
+ break;
48
+ case "linux-scripted-agent-v1":
49
+ writers = contract.writer.steps;
50
+ break;
51
+ case "linux-command-dag-v1":
52
+ writers = contract.writer.tasks.flatMap((task) => task.attempts);
53
+ break;
54
+ default: {
55
+ const exhaustive = contract;
56
+ throw new VerifiedRunError(String(exhaustive));
57
+ }
58
+ }
59
+ const binaries = [
60
+ ...new Set([
61
+ BWRAP,
62
+ ...writers.map((argv) => argv[0]),
63
+ ...contract.checks.map((check) => check.argv[0]),
64
+ ...(driver === "gated-v1" ? [PROCESS_GATE_ARGV[0]] : []),
65
+ ]),
66
+ ];
67
+ const materials = binaries.map((binary) => {
68
+ if (!binary || (!binary.startsWith("/usr/bin/") && !binary.startsWith("/bin/")))
69
+ throw new VerifiedRunError("unsupported_executable");
70
+ const path = realpathSync(binary);
71
+ if (!path.startsWith("/usr/"))
72
+ throw new VerifiedRunError("unsupported_executable");
73
+ return { path, digest: digestBytes(readFileSync(path)) };
74
+ });
75
+ return digestObject({
76
+ profile: contract.profile,
77
+ platform: process.platform,
78
+ architecture: process.arch,
79
+ kernel: release(),
80
+ sandbox: SYSTEM_ARGS,
81
+ ...(driver === "gated-v1" ? { gate: PROCESS_GATE_ARGV, infoFd: 3 } : {}),
82
+ binaries: materials,
83
+ });
84
+ }
85
+ export function probeVerifiedSandbox() {
86
+ if (process.platform !== "linux" || !existsSync(BWRAP))
87
+ throw new VerifiedRunError("unsupported");
88
+ const probe = spawnSync(BWRAP, [...SYSTEM_ARGS, "--", "/bin/true"], {
89
+ env: {},
90
+ timeout: 3000,
91
+ maxBuffer: 4096,
92
+ stdio: ["ignore", "pipe", "pipe"],
93
+ });
94
+ if (probe.error || probe.status !== 0)
95
+ throw new VerifiedRunError("unsupported");
96
+ }
97
+ /** The only process dispatch port; close means the PID namespace and its inherited pipes have drained. */
98
+ export async function executeSandbox(request) {
99
+ if (request.signal?.aborted)
100
+ throw new VerifiedRunError("cancelled");
101
+ if (request.timeoutMs <= 0)
102
+ throw new VerifiedRunError("deadline");
103
+ const started = performance.now();
104
+ const startedAt = new Date().toISOString();
105
+ const argv = [
106
+ ...SYSTEM_ARGS,
107
+ request.writable ? "--bind" : "--ro-bind",
108
+ request.workspace,
109
+ "/workspace",
110
+ "--chdir",
111
+ "/workspace",
112
+ ...(request.onReady ? ["--info-fd", "3", "--", ...PROCESS_GATE_ARGV] : ["--"]),
113
+ ...request.argv,
114
+ ];
115
+ return new Promise((resolve, reject) => {
116
+ const child = request.onReady
117
+ ? spawn(BWRAP, argv, { env: {}, stdio: ["pipe", "pipe", "pipe", "pipe"] })
118
+ : spawn(BWRAP, argv, { env: {}, stdio: ["ignore", "pipe", "pipe"] });
119
+ let gateFailed = false;
120
+ let gateError;
121
+ const stdout = [];
122
+ const stderr = [];
123
+ let size = 0;
124
+ let failure = null;
125
+ let cleanup;
126
+ let finished = false;
127
+ const stop = (reason) => {
128
+ if (finished || failure)
129
+ return;
130
+ failure = reason;
131
+ child.kill("SIGKILL");
132
+ cleanup = setTimeout(() => reject(new VerifiedRunError("unsettled")), request.cleanupMs);
133
+ };
134
+ const deadline = setTimeout(() => stop("deadline"), request.timeoutMs);
135
+ const abort = () => stop("cancelled");
136
+ request.signal?.addEventListener("abort", abort, { once: true });
137
+ if (request.signal?.aborted)
138
+ abort();
139
+ const collect = (target, chunk) => {
140
+ if (failure)
141
+ return;
142
+ size += chunk.length;
143
+ if (size > request.maxOutputBytes) {
144
+ stop("output_limit");
145
+ return;
146
+ }
147
+ target.push(chunk);
148
+ };
149
+ child.stdout?.on("data", (chunk) => collect(stdout, chunk));
150
+ child.stderr?.on("data", (chunk) => collect(stderr, chunk));
151
+ if (request.onReady) {
152
+ const info = child.stdio[3];
153
+ let metadata = "";
154
+ if (!info || !child.stdin) {
155
+ gateFailed = true;
156
+ gateError = new VerifiedRunError("process_identity");
157
+ stop("process_identity");
158
+ }
159
+ else {
160
+ child.stdin.on("error", () => stop("execution_failed"));
161
+ info.on("data", (chunk) => {
162
+ metadata += chunk.toString("utf8");
163
+ if (metadata.length > 4096)
164
+ stop("process_identity");
165
+ });
166
+ info.once("end", async () => {
167
+ if (finished || failure)
168
+ return;
169
+ try {
170
+ await request.onReady?.(identityFromSandboxInfo(metadata));
171
+ if (performance.now() - started >= request.timeoutMs || request.signal?.aborted)
172
+ stop(request.signal?.aborted ? "cancelled" : "deadline");
173
+ if (!finished && !failure)
174
+ child.stdin?.end("go\n");
175
+ }
176
+ catch (error) {
177
+ gateFailed = true;
178
+ gateError = error;
179
+ stop("process_identity");
180
+ }
181
+ });
182
+ }
183
+ }
184
+ child.once("error", () => {
185
+ failure = "execution_failed";
186
+ });
187
+ child.once("close", (exitCode) => {
188
+ finished = true;
189
+ clearTimeout(deadline);
190
+ clearTimeout(cleanup);
191
+ request.signal?.removeEventListener("abort", abort);
192
+ if (gateFailed) {
193
+ reject(gateError);
194
+ return;
195
+ }
196
+ const durationMs = Math.max(0, Math.round(performance.now() - started));
197
+ resolve({
198
+ stdout: Buffer.concat(stdout),
199
+ stderr: Buffer.concat(stderr),
200
+ exitCode,
201
+ failure: failure ?? (exitCode === 0 ? null : "execution_failed"),
202
+ startedAt,
203
+ // Project the finish instant from the start's wall reference and monotonic elapsed time.
204
+ finishedAt: new Date(Date.parse(startedAt) + durationMs).toISOString(),
205
+ durationMs,
206
+ });
207
+ });
208
+ });
209
+ }
210
+ //# sourceMappingURL=broker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"broker.js","sourceRoot":"","sources":["../../../src/core/verified-run/broker.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn, spawnSync } from \"node:child_process\";\nimport { existsSync, readFileSync, realpathSync } from \"node:fs\";\nimport { release } from \"node:os\";\nimport { performance } from \"node:perf_hooks\";\nimport type { RunContract } from \"omk-protocol\";\nimport type { NamespaceIdentity } from \"./namespace-identity.ts\";\nimport { identityFromSandboxInfo, PROCESS_GATE_ARGV } from \"./process-gate.ts\";\nimport { digestBytes, digestObject, VerifiedRunError } from \"./storage.ts\";\n\nconst BWRAP = \"/usr/bin/bwrap\";\nconst SYSTEM_ARGS = [\n\t\"--unshare-all\",\n\t\"--die-with-parent\",\n\t\"--new-session\",\n\t\"--cap-drop\",\n\t\"ALL\",\n\t\"--clearenv\",\n\t\"--ro-bind\",\n\t\"/usr\",\n\t\"/usr\",\n\t\"--symlink\",\n\t\"usr/bin\",\n\t\"/bin\",\n\t\"--symlink\",\n\t\"usr/lib\",\n\t\"/lib\",\n\t\"--symlink\",\n\t\"usr/lib64\",\n\t\"/lib64\",\n\t\"--proc\",\n\t\"/proc\",\n\t\"--dev\",\n\t\"/dev\",\n\t\"--tmpfs\",\n\t\"/tmp\",\n\t\"--setenv\",\n\t\"PATH\",\n\t\"/usr/bin:/bin\",\n\t\"--setenv\",\n\t\"LANG\",\n\t\"C.UTF-8\",\n] as const;\n\nexport interface SandboxExecution {\n\treadonly workspace: string;\n\treadonly argv: readonly string[];\n\treadonly writable: boolean;\n\treadonly timeoutMs: number;\n\treadonly cleanupMs: number;\n\treadonly maxOutputBytes: number;\n\treadonly signal?: AbortSignal;\n\treadonly onReady?: (identity: NamespaceIdentity) => void | Promise<void>;\n}\nexport interface SandboxOutcome {\n\treadonly stdout: Buffer;\n\treadonly stderr: Buffer;\n\treadonly exitCode: number | null;\n\treadonly failure: string | null;\n\treadonly startedAt: string;\n\treadonly finishedAt: string;\n\treadonly durationMs: number;\n}\n\nexport function commandEnvironmentDigest(contract: RunContract, driver: \"legacy\" | \"gated-v1\" = \"legacy\"): string {\n\tif (process.platform !== \"linux\" || !existsSync(BWRAP)) throw new VerifiedRunError(\"unsupported\");\n\tlet writers: readonly (readonly string[])[];\n\tswitch (contract.profile) {\n\t\tcase \"linux-command-v1\":\n\t\t\twriters = [contract.writer];\n\t\t\tbreak;\n\t\tcase \"linux-scripted-agent-v1\":\n\t\t\twriters = contract.writer.steps;\n\t\t\tbreak;\n\t\tcase \"linux-command-dag-v1\":\n\t\t\twriters = contract.writer.tasks.flatMap((task) => task.attempts);\n\t\t\tbreak;\n\t\tdefault: {\n\t\t\tconst exhaustive: never = contract;\n\t\t\tthrow new VerifiedRunError(String(exhaustive));\n\t\t}\n\t}\n\tconst binaries = [\n\t\t...new Set([\n\t\t\tBWRAP,\n\t\t\t...writers.map((argv) => argv[0]),\n\t\t\t...contract.checks.map((check) => check.argv[0]),\n\t\t\t...(driver === \"gated-v1\" ? [PROCESS_GATE_ARGV[0]] : []),\n\t\t]),\n\t];\n\tconst materials = binaries.map((binary) => {\n\t\tif (!binary || (!binary.startsWith(\"/usr/bin/\") && !binary.startsWith(\"/bin/\")))\n\t\t\tthrow new VerifiedRunError(\"unsupported_executable\");\n\t\tconst path = realpathSync(binary);\n\t\tif (!path.startsWith(\"/usr/\")) throw new VerifiedRunError(\"unsupported_executable\");\n\t\treturn { path, digest: digestBytes(readFileSync(path)) };\n\t});\n\treturn digestObject({\n\t\tprofile: contract.profile,\n\t\tplatform: process.platform,\n\t\tarchitecture: process.arch,\n\t\tkernel: release(),\n\t\tsandbox: SYSTEM_ARGS,\n\t\t...(driver === \"gated-v1\" ? { gate: PROCESS_GATE_ARGV, infoFd: 3 } : {}),\n\t\tbinaries: materials,\n\t});\n}\n\nexport function probeVerifiedSandbox(): void {\n\tif (process.platform !== \"linux\" || !existsSync(BWRAP)) throw new VerifiedRunError(\"unsupported\");\n\tconst probe = spawnSync(BWRAP, [...SYSTEM_ARGS, \"--\", \"/bin/true\"], {\n\t\tenv: {},\n\t\ttimeout: 3000,\n\t\tmaxBuffer: 4096,\n\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t});\n\tif (probe.error || probe.status !== 0) throw new VerifiedRunError(\"unsupported\");\n}\n\n/** The only process dispatch port; close means the PID namespace and its inherited pipes have drained. */\nexport async function executeSandbox(request: SandboxExecution): Promise<SandboxOutcome> {\n\tif (request.signal?.aborted) throw new VerifiedRunError(\"cancelled\");\n\tif (request.timeoutMs <= 0) throw new VerifiedRunError(\"deadline\");\n\tconst started = performance.now();\n\tconst startedAt = new Date().toISOString();\n\tconst argv = [\n\t\t...SYSTEM_ARGS,\n\t\trequest.writable ? \"--bind\" : \"--ro-bind\",\n\t\trequest.workspace,\n\t\t\"/workspace\",\n\t\t\"--chdir\",\n\t\t\"/workspace\",\n\t\t...(request.onReady ? [\"--info-fd\", \"3\", \"--\", ...PROCESS_GATE_ARGV] : [\"--\"]),\n\t\t...request.argv,\n\t];\n\treturn new Promise((resolve, reject) => {\n\t\tconst child = request.onReady\n\t\t\t? spawn(BWRAP, argv, { env: {}, stdio: [\"pipe\", \"pipe\", \"pipe\", \"pipe\"] })\n\t\t\t: spawn(BWRAP, argv, { env: {}, stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n\t\tlet gateFailed = false;\n\t\tlet gateError: unknown;\n\t\tconst stdout: Buffer[] = [];\n\t\tconst stderr: Buffer[] = [];\n\t\tlet size = 0;\n\t\tlet failure: string | null = null;\n\t\tlet cleanup: ReturnType<typeof setTimeout> | undefined;\n\t\tlet finished = false;\n\t\tconst stop = (reason: string): void => {\n\t\t\tif (finished || failure) return;\n\t\t\tfailure = reason;\n\t\t\tchild.kill(\"SIGKILL\");\n\t\t\tcleanup = setTimeout(() => reject(new VerifiedRunError(\"unsettled\")), request.cleanupMs);\n\t\t};\n\t\tconst deadline = setTimeout(() => stop(\"deadline\"), request.timeoutMs);\n\t\tconst abort = (): void => stop(\"cancelled\");\n\t\trequest.signal?.addEventListener(\"abort\", abort, { once: true });\n\t\tif (request.signal?.aborted) abort();\n\t\tconst collect = (target: Buffer[], chunk: Buffer): void => {\n\t\t\tif (failure) return;\n\t\t\tsize += chunk.length;\n\t\t\tif (size > request.maxOutputBytes) {\n\t\t\t\tstop(\"output_limit\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttarget.push(chunk);\n\t\t};\n\t\tchild.stdout?.on(\"data\", (chunk: Buffer) => collect(stdout, chunk));\n\t\tchild.stderr?.on(\"data\", (chunk: Buffer) => collect(stderr, chunk));\n\t\tif (request.onReady) {\n\t\t\tconst info = child.stdio[3];\n\t\t\tlet metadata = \"\";\n\t\t\tif (!info || !child.stdin) {\n\t\t\t\tgateFailed = true;\n\t\t\t\tgateError = new VerifiedRunError(\"process_identity\");\n\t\t\t\tstop(\"process_identity\");\n\t\t\t} else {\n\t\t\t\tchild.stdin.on(\"error\", () => stop(\"execution_failed\"));\n\t\t\t\tinfo.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\tmetadata += chunk.toString(\"utf8\");\n\t\t\t\t\tif (metadata.length > 4096) stop(\"process_identity\");\n\t\t\t\t});\n\t\t\t\tinfo.once(\"end\", async () => {\n\t\t\t\t\tif (finished || failure) return;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait request.onReady?.(identityFromSandboxInfo(metadata));\n\t\t\t\t\t\tif (performance.now() - started >= request.timeoutMs || request.signal?.aborted)\n\t\t\t\t\t\t\tstop(request.signal?.aborted ? \"cancelled\" : \"deadline\");\n\t\t\t\t\t\tif (!finished && !failure) child.stdin?.end(\"go\\n\");\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tgateFailed = true;\n\t\t\t\t\t\tgateError = error;\n\t\t\t\t\t\tstop(\"process_identity\");\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tchild.once(\"error\", () => {\n\t\t\tfailure = \"execution_failed\";\n\t\t});\n\t\tchild.once(\"close\", (exitCode) => {\n\t\t\tfinished = true;\n\t\t\tclearTimeout(deadline);\n\t\t\tclearTimeout(cleanup);\n\t\t\trequest.signal?.removeEventListener(\"abort\", abort);\n\t\t\tif (gateFailed) {\n\t\t\t\treject(gateError);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst durationMs = Math.max(0, Math.round(performance.now() - started));\n\t\t\tresolve({\n\t\t\t\tstdout: Buffer.concat(stdout),\n\t\t\t\tstderr: Buffer.concat(stderr),\n\t\t\t\texitCode,\n\t\t\t\tfailure: failure ?? (exitCode === 0 ? null : \"execution_failed\"),\n\t\t\t\tstartedAt,\n\t\t\t\t// Project the finish instant from the start's wall reference and monotonic elapsed time.\n\t\t\t\tfinishedAt: new Date(Date.parse(startedAt) + durationMs).toISOString(),\n\t\t\t\tdurationMs,\n\t\t\t});\n\t\t});\n\t});\n}\n"]}
@@ -0,0 +1,24 @@
1
+ import type { RunPhaseBudget } from "omk-protocol";
2
+ export interface CandidateFile {
3
+ readonly path: string;
4
+ readonly mode: number;
5
+ readonly digest: string;
6
+ readonly size: number;
7
+ }
8
+ export interface CandidateManifest {
9
+ readonly version: 1;
10
+ readonly directories: readonly string[];
11
+ readonly files: readonly CandidateFile[];
12
+ }
13
+ export interface CandidateSnapshot {
14
+ readonly manifest: CandidateManifest;
15
+ readonly digest: string;
16
+ readonly contents: ReadonlyMap<string, Buffer>;
17
+ }
18
+ /** Entire regular-file tree, including dot/untracked files; .git/.omk metadata is excluded by this profile. */
19
+ export declare function captureCandidate(root: string, limits: RunPhaseBudget): CandidateSnapshot;
20
+ export declare function materializeCandidate(snapshot: CandidateSnapshot, target: string): void;
21
+ export declare function assertCandidateScope(base: CandidateManifest, candidate: CandidateManifest, writable: readonly string[]): void;
22
+ export declare function storeCandidate(snapshot: CandidateSnapshot, runPath: string): void;
23
+ export declare function loadCandidate(runPath: string, digest: string, limits: RunPhaseBudget): CandidateSnapshot;
24
+ //# sourceMappingURL=candidate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"candidate.d.ts","sourceRoot":"","sources":["../../../src/core/verified-run/candidate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAYnD,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AACD,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;CACzC;AACD,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/C;AAYD,+GAA+G;AAC/G,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,iBAAiB,CAsCxF;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAStF;AAED,wBAAgB,oBAAoB,CACnC,IAAI,EAAE,iBAAiB,EACvB,SAAS,EAAE,iBAAiB,EAC5B,QAAQ,EAAE,SAAS,MAAM,EAAE,GACzB,IAAI,CAWN;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAGjF;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,iBAAiB,CAyExG","sourcesContent":["import { chmodSync, lstatSync, mkdirSync, readdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { RunPhaseBudget } from \"omk-protocol\";\nimport {\n\tassertDirectory,\n\tdigestBytes,\n\tdigestObject,\n\tpublishBytes,\n\tpublishObject,\n\treadJson,\n\treadRegularFile,\n\tVerifiedRunError,\n} from \"./storage.ts\";\n\nexport interface CandidateFile {\n\treadonly path: string;\n\treadonly mode: number;\n\treadonly digest: string;\n\treadonly size: number;\n}\nexport interface CandidateManifest {\n\treadonly version: 1;\n\treadonly directories: readonly string[];\n\treadonly files: readonly CandidateFile[];\n}\nexport interface CandidateSnapshot {\n\treadonly manifest: CandidateManifest;\n\treadonly digest: string;\n\treadonly contents: ReadonlyMap<string, Buffer>;\n}\n\nfunction safePath(path: string): void {\n\tif (\n\t\t!path ||\n\t\t/[\\\\\\u0000-\\u001f\\u007f]/.test(path) ||\n\t\tpath.split(\"/\").some((part) => [\"\", \".\", \"..\", \".git\", \".omk\"].includes(part))\n\t) {\n\t\tthrow new VerifiedRunError(\"file_type\");\n\t}\n}\n\n/** Entire regular-file tree, including dot/untracked files; .git/.omk metadata is excluded by this profile. */\nexport function captureCandidate(root: string, limits: RunPhaseBudget): CandidateSnapshot {\n\tassertDirectory(root);\n\tconst files: CandidateFile[] = [];\n\tconst directories: string[] = [];\n\tconst contents = new Map<string, Buffer>();\n\tlet bytesUsed = 0;\n\tconst visit = (prefix: string): void => {\n\t\tconst names = readdirSync(join(root, prefix), { encoding: \"buffer\" })\n\t\t\t.map((name) => new TextDecoder(\"utf-8\", { fatal: true, ignoreBOM: true }).decode(name))\n\t\t\t.sort();\n\t\tfor (const name of names) {\n\t\t\tif (!prefix && (name === \".git\" || name === \".omk\")) continue;\n\t\t\tconst path = prefix ? `${prefix}/${name}` : name;\n\t\t\tsafePath(path);\n\t\t\tif (files.length + directories.length >= limits.maxFiles) throw new VerifiedRunError(\"storage_limit\");\n\t\t\tconst absolute = join(root, path);\n\t\t\tconst stat = lstatSync(absolute);\n\t\t\tif (stat.isDirectory()) {\n\t\t\t\tif ((stat.mode & 0o7777) !== 0o755) throw new VerifiedRunError(\"file_type\");\n\t\t\t\tdirectories.push(path);\n\t\t\t\tvisit(path);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!stat.isFile() || stat.nlink !== 1 || (stat.mode & 0o7000) !== 0) throw new VerifiedRunError(\"file_type\");\n\t\t\tconst bytes = readRegularFile(absolute, limits.maxBytes - bytesUsed);\n\t\t\tbytesUsed += bytes.length;\n\t\t\tconst digest = digestBytes(bytes);\n\t\t\tcontents.set(digest, bytes);\n\t\t\tfiles.push(Object.freeze({ path, mode: stat.mode & 0o777, digest, size: bytes.length }));\n\t\t}\n\t};\n\tvisit(\"\");\n\tconst manifest: CandidateManifest = Object.freeze({\n\t\tversion: 1,\n\t\tdirectories: Object.freeze(directories),\n\t\tfiles: Object.freeze(files),\n\t});\n\treturn { manifest, digest: digestObject(manifest), contents };\n}\n\nexport function materializeCandidate(snapshot: CandidateSnapshot, target: string): void {\n\tmkdirSync(target, { mode: 0o700 });\n\tfor (const directory of snapshot.manifest.directories) mkdirSync(join(target, directory), { mode: 0o755 });\n\tfor (const file of snapshot.manifest.files) {\n\t\tconst bytes = snapshot.contents.get(file.digest);\n\t\tif (!bytes) throw new VerifiedRunError(\"integrity\");\n\t\tpublishBytes(join(target, file.path), bytes);\n\t\tchmodSync(join(target, file.path), file.mode);\n\t}\n}\n\nexport function assertCandidateScope(\n\tbase: CandidateManifest,\n\tcandidate: CandidateManifest,\n\twritable: readonly string[],\n): void {\n\tconst before = new Map(base.files.map((file) => [file.path, digestObject(file)]));\n\tconst after = new Map(candidate.files.map((file) => [file.path, digestObject(file)]));\n\tfor (const directory of base.directories) before.set(`${directory}/`, \"directory\");\n\tfor (const directory of candidate.directories) after.set(`${directory}/`, \"directory\");\n\tfor (const path of new Set([...before.keys(), ...after.keys()])) {\n\t\tif (before.get(path) === after.get(path)) continue;\n\t\tconst normalized = path.endsWith(\"/\") ? path.slice(0, -1) : path;\n\t\tif (!writable.some((scope) => normalized === scope || normalized.startsWith(`${scope}/`)))\n\t\t\tthrow new VerifiedRunError(\"scope_changed\");\n\t}\n}\n\nexport function storeCandidate(snapshot: CandidateSnapshot, runPath: string): void {\n\tfor (const [digest, bytes] of snapshot.contents) publishBytes(join(runPath, \"blobs\", digest), bytes);\n\tpublishObject(join(runPath, \"candidates\", `${snapshot.digest}.json`), snapshot.manifest);\n}\n\nexport function loadCandidate(runPath: string, digest: string, limits: RunPhaseBudget): CandidateSnapshot {\n\tif (!/^[a-f0-9]{64}$/.test(digest)) throw new VerifiedRunError(\"integrity\");\n\tconst raw = readJson(join(runPath, \"candidates\", `${digest}.json`), 4194304);\n\tif (\n\t\tdigestObject(raw) !== digest ||\n\t\ttypeof raw !== \"object\" ||\n\t\traw === null ||\n\t\t!(\"version\" in raw) ||\n\t\traw.version !== 1 ||\n\t\t!(\"files\" in raw) ||\n\t\t!Array.isArray(raw.files) ||\n\t\t!(\"directories\" in raw) ||\n\t\t!Array.isArray(raw.directories)\n\t) {\n\t\tthrow new VerifiedRunError(\"integrity\");\n\t}\n\tif (raw.files.length + raw.directories.length > limits.maxFiles) throw new VerifiedRunError(\"integrity\");\n\tconst files: CandidateFile[] = [];\n\tconst directories: string[] = [];\n\tconst paths = new Set<string>();\n\tlet total = 0;\n\tfor (const directory of raw.directories) {\n\t\tif (typeof directory !== \"string\") throw new VerifiedRunError(\"integrity\");\n\t\tsafePath(directory);\n\t\tif (paths.has(directory)) throw new VerifiedRunError(\"integrity\");\n\t\tpaths.add(directory);\n\t\tdirectories.push(directory);\n\t}\n\tconst contents = new Map<string, Buffer>();\n\tconst rawFiles: readonly unknown[] = raw.files;\n\tfor (const file of rawFiles) {\n\t\tif (\n\t\t\ttypeof file !== \"object\" ||\n\t\t\tfile === null ||\n\t\t\t!(\"path\" in file) ||\n\t\t\ttypeof file.path !== \"string\" ||\n\t\t\t!(\"digest\" in file) ||\n\t\t\ttypeof file.digest !== \"string\" ||\n\t\t\t!/^[a-f0-9]{64}$/.test(file.digest) ||\n\t\t\t!(\"mode\" in file) ||\n\t\t\ttypeof file.mode !== \"number\" ||\n\t\t\t!Number.isSafeInteger(file.mode) ||\n\t\t\tfile.mode < 0 ||\n\t\t\tfile.mode > 0o777 ||\n\t\t\t!(\"size\" in file) ||\n\t\t\ttypeof file.size !== \"number\" ||\n\t\t\t!Number.isSafeInteger(file.size) ||\n\t\t\tfile.size < 0\n\t\t)\n\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\tsafePath(file.path);\n\t\tif (paths.has(file.path)) throw new VerifiedRunError(\"integrity\");\n\t\tpaths.add(file.path);\n\t\ttotal += file.size;\n\t\tif (total > limits.maxBytes) throw new VerifiedRunError(\"integrity\");\n\t\tlet bytes: Buffer;\n\t\ttry {\n\t\t\tbytes = readRegularFile(join(runPath, \"blobs\", file.digest), file.size);\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error) throw new VerifiedRunError(\"integrity\");\n\t\t\tthrow error;\n\t\t}\n\t\tif (bytes.length !== file.size || digestBytes(bytes) !== file.digest) throw new VerifiedRunError(\"integrity\");\n\t\tcontents.set(file.digest, bytes);\n\t\tfiles.push(Object.freeze({ path: file.path, mode: file.mode, digest: file.digest, size: file.size }));\n\t}\n\tconst manifest: CandidateManifest = Object.freeze({\n\t\tversion: 1,\n\t\tdirectories: Object.freeze(directories),\n\t\tfiles: Object.freeze(files),\n\t});\n\tif (digestObject(manifest) !== digest) throw new VerifiedRunError(\"integrity\");\n\treturn { manifest, digest, contents };\n}\n"]}