open-multi-agent-kit 0.99.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +3 -3
  3. package/dist/bun/cli.d.ts.map +1 -1
  4. package/dist/bun/cli.js +1 -0
  5. package/dist/bun/cli.js.map +1 -1
  6. package/dist/bun/register-bundled-coding-agent.d.ts +10 -0
  7. package/dist/bun/register-bundled-coding-agent.d.ts.map +1 -0
  8. package/dist/bun/register-bundled-coding-agent.js +12 -0
  9. package/dist/bun/register-bundled-coding-agent.js.map +1 -0
  10. package/dist/cli/args.d.ts +1 -1
  11. package/dist/cli/args.d.ts.map +1 -1
  12. package/dist/cli/args.js +1 -1
  13. package/dist/cli/args.js.map +1 -1
  14. package/dist/cli/help.d.ts.map +1 -1
  15. package/dist/cli/help.js +1 -1
  16. package/dist/cli/help.js.map +1 -1
  17. package/dist/cli.d.ts.map +1 -1
  18. package/dist/cli.js +14 -2
  19. package/dist/cli.js.map +1 -1
  20. package/dist/commands/neo-cli.d.ts +9 -0
  21. package/dist/commands/neo-cli.d.ts.map +1 -0
  22. package/dist/commands/neo-cli.js +61 -0
  23. package/dist/commands/neo-cli.js.map +1 -0
  24. package/dist/core/agent-session.d.ts +29 -2
  25. package/dist/core/agent-session.d.ts.map +1 -1
  26. package/dist/core/agent-session.js +107 -30
  27. package/dist/core/agent-session.js.map +1 -1
  28. package/dist/core/bundled-skills.d.ts +4 -0
  29. package/dist/core/bundled-skills.d.ts.map +1 -0
  30. package/dist/core/bundled-skills.js +32 -0
  31. package/dist/core/bundled-skills.js.map +1 -0
  32. package/dist/core/cli-diagnostics.d.ts +6 -0
  33. package/dist/core/cli-diagnostics.d.ts.map +1 -0
  34. package/dist/core/cli-diagnostics.js +20 -0
  35. package/dist/core/cli-diagnostics.js.map +1 -0
  36. package/dist/core/context-budget-headroom.d.ts +12 -0
  37. package/dist/core/context-budget-headroom.d.ts.map +1 -1
  38. package/dist/core/context-budget-headroom.js +35 -0
  39. package/dist/core/context-budget-headroom.js.map +1 -1
  40. package/dist/core/context-budget-v2-input-validation.d.ts +4 -0
  41. package/dist/core/context-budget-v2-input-validation.d.ts.map +1 -0
  42. package/dist/core/context-budget-v2-input-validation.js +79 -0
  43. package/dist/core/context-budget-v2-input-validation.js.map +1 -0
  44. package/dist/core/context-budget-v2-observability.d.ts +15 -0
  45. package/dist/core/context-budget-v2-observability.d.ts.map +1 -0
  46. package/dist/core/context-budget-v2-observability.js +35 -0
  47. package/dist/core/context-budget-v2-observability.js.map +1 -0
  48. package/dist/core/context-budget-v2-planned-items.d.ts +5 -0
  49. package/dist/core/context-budget-v2-planned-items.d.ts.map +1 -0
  50. package/dist/core/context-budget-v2-planned-items.js +40 -0
  51. package/dist/core/context-budget-v2-planned-items.js.map +1 -0
  52. package/dist/core/context-budget-v2-planner.d.ts.map +1 -1
  53. package/dist/core/context-budget-v2-planner.js +57 -38
  54. package/dist/core/context-budget-v2-planner.js.map +1 -1
  55. package/dist/core/context-budget-v2-selection.d.ts +19 -3
  56. package/dist/core/context-budget-v2-selection.d.ts.map +1 -1
  57. package/dist/core/context-budget-v2-selection.js +35 -57
  58. package/dist/core/context-budget-v2-selection.js.map +1 -1
  59. package/dist/core/context-budget-v2-tiers.d.ts.map +1 -1
  60. package/dist/core/context-budget-v2-tiers.js +9 -42
  61. package/dist/core/context-budget-v2-tiers.js.map +1 -1
  62. package/dist/core/context-budget-v2-types.d.ts +8 -1
  63. package/dist/core/context-budget-v2-types.d.ts.map +1 -1
  64. package/dist/core/context-budget-v2-types.js.map +1 -1
  65. package/dist/core/extensions/bundled-virtual-modules.d.ts +15 -0
  66. package/dist/core/extensions/bundled-virtual-modules.d.ts.map +1 -0
  67. package/dist/core/extensions/bundled-virtual-modules.js +63 -0
  68. package/dist/core/extensions/bundled-virtual-modules.js.map +1 -0
  69. package/dist/core/extensions/loader.d.ts.map +1 -1
  70. package/dist/core/extensions/loader.js +2 -42
  71. package/dist/core/extensions/loader.js.map +1 -1
  72. package/dist/core/extensions/runner.d.ts +1 -0
  73. package/dist/core/extensions/runner.d.ts.map +1 -1
  74. package/dist/core/extensions/runner.js +6 -0
  75. package/dist/core/extensions/runner.js.map +1 -1
  76. package/dist/core/extensions/types.d.ts +10 -0
  77. package/dist/core/extensions/types.d.ts.map +1 -1
  78. package/dist/core/extensions/types.js.map +1 -1
  79. package/dist/core/index.d.ts +2 -1
  80. package/dist/core/index.d.ts.map +1 -1
  81. package/dist/core/index.js +2 -1
  82. package/dist/core/index.js.map +1 -1
  83. package/dist/core/loadout-runtime.d.ts +2 -8
  84. package/dist/core/loadout-runtime.d.ts.map +1 -1
  85. package/dist/core/loadout-runtime.js.map +1 -1
  86. package/dist/core/mcp/client.d.ts +27 -6
  87. package/dist/core/mcp/client.d.ts.map +1 -1
  88. package/dist/core/mcp/client.js +80 -20
  89. package/dist/core/mcp/client.js.map +1 -1
  90. package/dist/core/mcp/manager.d.ts +10 -1
  91. package/dist/core/mcp/manager.d.ts.map +1 -1
  92. package/dist/core/mcp/manager.js +68 -9
  93. package/dist/core/mcp/manager.js.map +1 -1
  94. package/dist/core/mcp/protocol.d.ts +9 -3
  95. package/dist/core/mcp/protocol.d.ts.map +1 -1
  96. package/dist/core/mcp/protocol.js +61 -15
  97. package/dist/core/mcp/protocol.js.map +1 -1
  98. package/dist/core/mcp/stdio-transport.d.ts +12 -1
  99. package/dist/core/mcp/stdio-transport.d.ts.map +1 -1
  100. package/dist/core/mcp/stdio-transport.js +30 -2
  101. package/dist/core/mcp/stdio-transport.js.map +1 -1
  102. package/dist/core/mcp-descriptor-injection.d.ts +26 -0
  103. package/dist/core/mcp-descriptor-injection.d.ts.map +1 -0
  104. package/dist/core/mcp-descriptor-injection.js +25 -0
  105. package/dist/core/mcp-descriptor-injection.js.map +1 -0
  106. package/dist/core/mcp-public-presets.d.ts +1 -3
  107. package/dist/core/mcp-public-presets.d.ts.map +1 -1
  108. package/dist/core/mcp-public-presets.js +3 -15
  109. package/dist/core/mcp-public-presets.js.map +1 -1
  110. package/dist/core/model-registry.d.ts.map +1 -1
  111. package/dist/core/model-registry.js +24 -3
  112. package/dist/core/model-registry.js.map +1 -1
  113. package/dist/core/neo/catalog.d.ts +20 -0
  114. package/dist/core/neo/catalog.d.ts.map +1 -0
  115. package/dist/core/neo/catalog.js +50 -0
  116. package/dist/core/neo/catalog.js.map +1 -0
  117. package/dist/core/neo/setup.d.ts +3 -0
  118. package/dist/core/neo/setup.d.ts.map +1 -0
  119. package/dist/core/neo/setup.js +47 -0
  120. package/dist/core/neo/setup.js.map +1 -0
  121. package/dist/core/provider-default-models.d.ts +1 -0
  122. package/dist/core/provider-default-models.d.ts.map +1 -1
  123. package/dist/core/provider-default-models.js +1 -0
  124. package/dist/core/provider-default-models.js.map +1 -1
  125. package/dist/core/provider-display-names.d.ts.map +1 -1
  126. package/dist/core/provider-display-names.js +1 -0
  127. package/dist/core/provider-display-names.js.map +1 -1
  128. package/dist/core/provider-error-classification.d.ts +57 -0
  129. package/dist/core/provider-error-classification.d.ts.map +1 -0
  130. package/dist/core/provider-error-classification.js +103 -0
  131. package/dist/core/provider-error-classification.js.map +1 -0
  132. package/dist/core/provider-resilience.d.ts +10 -0
  133. package/dist/core/provider-resilience.d.ts.map +1 -1
  134. package/dist/core/provider-resilience.js +36 -3
  135. package/dist/core/provider-resilience.js.map +1 -1
  136. package/dist/core/provider-usage-commandcode.d.ts +9 -0
  137. package/dist/core/provider-usage-commandcode.d.ts.map +1 -0
  138. package/dist/core/provider-usage-commandcode.js +198 -0
  139. package/dist/core/provider-usage-commandcode.js.map +1 -0
  140. package/dist/core/provider-usage-devin.d.ts +5 -3
  141. package/dist/core/provider-usage-devin.d.ts.map +1 -1
  142. package/dist/core/provider-usage-devin.js +15 -5
  143. package/dist/core/provider-usage-devin.js.map +1 -1
  144. package/dist/core/provider-usage-types.d.ts +1 -1
  145. package/dist/core/provider-usage-types.d.ts.map +1 -1
  146. package/dist/core/provider-usage-types.js.map +1 -1
  147. package/dist/core/provider-usage.d.ts.map +1 -1
  148. package/dist/core/provider-usage.js +6 -0
  149. package/dist/core/provider-usage.js.map +1 -1
  150. package/dist/core/resource-admission.d.ts +36 -0
  151. package/dist/core/resource-admission.d.ts.map +1 -1
  152. package/dist/core/resource-admission.js +59 -0
  153. package/dist/core/resource-admission.js.map +1 -1
  154. package/dist/core/resource-loader.d.ts.map +1 -1
  155. package/dist/core/resource-loader.js +2 -2
  156. package/dist/core/resource-loader.js.map +1 -1
  157. package/dist/core/run-usage-ledger.d.ts +47 -0
  158. package/dist/core/run-usage-ledger.d.ts.map +1 -0
  159. package/dist/core/run-usage-ledger.js +162 -0
  160. package/dist/core/run-usage-ledger.js.map +1 -0
  161. package/dist/core/run-usage-operation.d.ts +8 -0
  162. package/dist/core/run-usage-operation.d.ts.map +1 -0
  163. package/dist/core/run-usage-operation.js +12 -0
  164. package/dist/core/run-usage-operation.js.map +1 -0
  165. package/dist/core/sdk.d.ts.map +1 -1
  166. package/dist/core/sdk.js +5 -6
  167. package/dist/core/sdk.js.map +1 -1
  168. package/dist/core/session-failure-cause.d.ts.map +1 -1
  169. package/dist/core/session-failure-cause.js +14 -5
  170. package/dist/core/session-failure-cause.js.map +1 -1
  171. package/dist/core/session-prompt-lifecycle.d.ts +6 -0
  172. package/dist/core/session-prompt-lifecycle.d.ts.map +1 -1
  173. package/dist/core/session-prompt-lifecycle.js +47 -2
  174. package/dist/core/session-prompt-lifecycle.js.map +1 -1
  175. package/dist/core/session-termination.d.ts.map +1 -1
  176. package/dist/core/session-termination.js +4 -2
  177. package/dist/core/session-termination.js.map +1 -1
  178. package/dist/core/subagent-lane-authority.d.ts +28 -0
  179. package/dist/core/subagent-lane-authority.d.ts.map +1 -0
  180. package/dist/core/subagent-lane-authority.js +119 -0
  181. package/dist/core/subagent-lane-authority.js.map +1 -0
  182. package/dist/core/subagent-lane-contract.d.ts +114 -0
  183. package/dist/core/subagent-lane-contract.d.ts.map +1 -0
  184. package/dist/core/subagent-lane-contract.js +18 -0
  185. package/dist/core/subagent-lane-contract.js.map +1 -0
  186. package/dist/core/subagent-lane-launcher.d.ts +3 -6
  187. package/dist/core/subagent-lane-launcher.d.ts.map +1 -1
  188. package/dist/core/subagent-lane-launcher.js +53 -12
  189. package/dist/core/subagent-lane-launcher.js.map +1 -1
  190. package/dist/core/subagent-orchestration.d.ts +4 -17
  191. package/dist/core/subagent-orchestration.d.ts.map +1 -1
  192. package/dist/core/subagent-orchestration.js.map +1 -1
  193. package/dist/core/verified-run/broker.d.ts.map +1 -1
  194. package/dist/core/verified-run/broker.js +4 -1
  195. package/dist/core/verified-run/broker.js.map +1 -1
  196. package/dist/core/workload-permit-pool.d.ts +1 -1
  197. package/dist/core/workload-permit-pool.d.ts.map +1 -1
  198. package/dist/core/workload-permit-pool.js +3 -0
  199. package/dist/core/workload-permit-pool.js.map +1 -1
  200. package/dist/guardrails/strict-evidence-approval-adapter.d.ts +34 -0
  201. package/dist/guardrails/strict-evidence-approval-adapter.d.ts.map +1 -0
  202. package/dist/guardrails/strict-evidence-approval-adapter.js +70 -0
  203. package/dist/guardrails/strict-evidence-approval-adapter.js.map +1 -0
  204. package/dist/index.d.ts +3 -0
  205. package/dist/index.d.ts.map +1 -1
  206. package/dist/index.js +1 -0
  207. package/dist/index.js.map +1 -1
  208. package/dist/main.d.ts.map +1 -1
  209. package/dist/main.js +11 -18
  210. package/dist/main.js.map +1 -1
  211. package/dist/modes/acp/acp-agent.d.ts +24 -0
  212. package/dist/modes/acp/acp-agent.d.ts.map +1 -0
  213. package/dist/modes/acp/acp-agent.js +133 -0
  214. package/dist/modes/acp/acp-agent.js.map +1 -0
  215. package/dist/modes/acp/acp-mode.d.ts +4 -0
  216. package/dist/modes/acp/acp-mode.d.ts.map +1 -0
  217. package/dist/modes/acp/acp-mode.js +34 -0
  218. package/dist/modes/acp/acp-mode.js.map +1 -0
  219. package/dist/modes/acp/acp-session.d.ts +4 -0
  220. package/dist/modes/acp/acp-session.d.ts.map +1 -0
  221. package/dist/modes/acp/acp-session.js +76 -0
  222. package/dist/modes/acp/acp-session.js.map +1 -0
  223. package/dist/modes/acp/acp-transport.d.ts +5 -0
  224. package/dist/modes/acp/acp-transport.d.ts.map +1 -0
  225. package/dist/modes/acp/acp-transport.js +90 -0
  226. package/dist/modes/acp/acp-transport.js.map +1 -0
  227. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  228. package/dist/modes/interactive/interactive-mode.js +1 -0
  229. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  230. package/docs/audit-revalidation-2026-09-17.md +77 -0
  231. package/docs/devin-harness.md +33 -4
  232. package/docs/ecraf-normalization.md +83 -0
  233. package/docs/model-catalog-refresh.md +51 -1
  234. package/docs/models.md +1 -1
  235. package/docs/neo.md +134 -0
  236. package/docs/providers.md +72 -6
  237. package/docs/run-usage-ledger.md +56 -0
  238. package/docs/runtime-algorithms.md +44 -0
  239. package/docs/skills.md +1 -1
  240. package/docs/usage.md +2 -1
  241. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  242. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  243. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  244. package/examples/extensions/gondolin/package-lock.json +2 -2
  245. package/examples/extensions/gondolin/package.json +1 -1
  246. package/examples/extensions/sandbox/package-lock.json +2 -2
  247. package/examples/extensions/sandbox/package.json +1 -1
  248. package/examples/extensions/subagent/adaptive-agent-runtime.ts +14 -1
  249. package/examples/extensions/subagent/graph-result.ts +48 -0
  250. package/examples/extensions/subagent/index.ts +246 -111
  251. package/examples/extensions/subagent/managed-process-tree.ts +42 -0
  252. package/examples/extensions/subagent/managed-process.test.ts +24 -0
  253. package/examples/extensions/subagent/managed-process.ts +93 -112
  254. package/examples/extensions/subagent/subagent-runtime-types.ts +17 -1
  255. package/examples/extensions/subagent/subagent-stream.ts +161 -0
  256. package/examples/extensions/terminal-browser/README.md +54 -0
  257. package/examples/extensions/terminal-browser/bridge-protocol.ts +29 -0
  258. package/examples/extensions/terminal-browser/bridge.ts +219 -0
  259. package/examples/extensions/terminal-browser/browser-surface.ts +288 -0
  260. package/examples/extensions/terminal-browser/index.ts +215 -0
  261. package/examples/extensions/terminal-browser/placeholders.ts +49 -0
  262. package/examples/extensions/with-deps/package-lock.json +2 -2
  263. package/examples/extensions/with-deps/package.json +1 -1
  264. package/npm-shrinkwrap.json +18 -18
  265. package/package.json +8 -7
  266. package/resources/neo/skills/omk-browser/SKILL.md +32 -0
  267. package/resources/neo/skills/omk-code-review/SKILL.md +24 -0
  268. package/resources/neo/skills/omk-computeruse/SKILL.md +34 -0
  269. package/resources/neo/skills/omk-mcp-setup/SKILL.md +48 -0
  270. package/resources/neo/skills/omk-research/SKILL.md +24 -0
  271. package/resources/neo/skills/omk-site/SKILL.md +28 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subagent-lane-authority.d.ts","sourceRoot":"","sources":["../../src/core/subagent-lane-authority.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAA8B,KAAK,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACrG,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAIzE;;;;;;;;;;GAUG;AACH,YAAY,EACX,qBAAqB,EACrB,kCAAkC,EAClC,mCAAmC,GACnC,MAAM,6BAA6B,CAAC;AAErC,kFAAkF;AAClF,MAAM,WAAW,gCAAgC;IAChD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,yBAAyB,GAAG,IAAI,CAAC;IACpD,QAAQ,CAAC,UAAU,EAAE,OAAO,2BAA2B,EAAE,kBAAkB,CAAC;IAC5E,QAAQ,CAAC,SAAS,EAAE,mBAAmB,CAAC;IACxC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,MAAM,IAAI,CAAC;CAC7C;AAyBD,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,gCAAgC,GAAG,qBAAqB,CAgG5G","sourcesContent":["import type { CapabilityInventory } from \"./loadouts.ts\";\nimport { RESOURCE_ADMISSION_VERSION, type ResourceAdmissionDecision } from \"./resource-admission.ts\";\nimport type { SubagentLaneAuthority } from \"./subagent-lane-contract.ts\";\nimport { launchSubagentLanes } from \"./subagent-lane-launcher.ts\";\nimport { buildSubagentOrchestrationPlan } from \"./subagent-orchestration.ts\";\n\n/**\n * Spec 020 Req1 — extension-facing subagent lane authority.\n *\n * A bound implementation is created by AgentSession and exposed to extension\n * tool contexts. It routes a caller's lane plan through the shared core\n * primitives (`buildSubagentOrchestrationPlan` → `launchSubagentLanes`) while\n * injecting the parent's admission decision, the shared `WorkloadPermitPool`,\n * and prompt-settlement child counters. Extensions must never raise parent\n * caps; `launchSubagentLanes` enforces the effective width as a floor of the\n * caller's configured cap and the parent's admission.\n */\nexport type {\n\tSubagentLaneAuthority,\n\tSubagentLaneAuthorityDispatchInput,\n\tSubagentLaneAuthorityDispatchResult,\n} from \"./subagent-lane-contract.ts\";\n\n/** Internal host binding the session supplies when constructing the authority. */\nexport interface SubagentLaneAuthorityHostBinding {\n\treadonly runId: string;\n\treadonly promptRunId?: string;\n\treadonly decision: ResourceAdmissionDecision | null;\n\treadonly permitPool: import(\"./workload-permit-pool.ts\").WorkloadPermitPool;\n\treadonly inventory: CapabilityInventory;\n\treadonly signal?: AbortSignal;\n\treadonly spawnThreshold?: number;\n\treadonly noteDetachedChild: () => () => void;\n}\n\n/**\n * Bind a SubagentLaneAuthority to a session's admission decision, shared permit\n * pool, and prompt-settlement lifecycle. The returned authority's\n * dispatchLanes() enforces spec 020: the caller supplies lanes and a launcher;\n * the authority injects plan admission, the shared pool, and per-lane\n * settlement counters, and never lets the caller widen the parent's caps.\n */\n/**\n * Unresolved child settlements keyed by the shared permit pool. AgentSession\n * recreates the authority per dispatch over the same pool, so per-instance\n * state alone would let a fresh instance forget an owned, unsettled child.\n */\nconst pendingSettlementsByPool = new WeakMap<object, Set<Promise<void>>>();\n\nfunction unsettledSettlements(pool: object): Set<Promise<void>> {\n\tlet set = pendingSettlementsByPool.get(pool);\n\tif (set === undefined) {\n\t\tset = new Set();\n\t\tpendingSettlementsByPool.set(pool, set);\n\t}\n\treturn set;\n}\n\nexport function createSubagentLaneAuthority(binding: SubagentLaneAuthorityHostBinding): SubagentLaneAuthority {\n\tconst { runId, promptRunId, decision, permitPool, inventory, signal, spawnThreshold, noteDetachedChild } = binding;\n\tconst pendingSettlements = unsettledSettlements(permitPool);\n\treturn {\n\t\tpermitPool,\n\t\tactivePromptRunId: promptRunId,\n\t\tgetCurrentResourceAdmission: () => decision,\n\t\tnoteDetachedChild,\n\t\tdispatchLanes: async (input) => {\n\t\t\tif (pendingSettlements.size > 0) {\n\t\t\t\treturn {\n\t\t\t\t\toutcomes: [],\n\t\t\t\t\teffectiveLaneWidth: 0,\n\t\t\t\t\tmaxObservedConcurrency: 0,\n\t\t\t\t\tblockers: [\"ownership.unsettled\"],\n\t\t\t\t\twarnings: [],\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst plan = buildSubagentOrchestrationPlan({\n\t\t\t\trunId,\n\t\t\t\tlanes: input.lanes,\n\t\t\t\tinventory,\n\t\t\t\tspawnPlan: input.spawnPlan,\n\t\t\t\tspawnThreshold,\n\t\t\t\tmaxParallelLanes: input.configuredMaxParallelLanes,\n\t\t\t});\n\t\t\tif (plan.blockers.length > 0) {\n\t\t\t\treturn {\n\t\t\t\t\toutcomes: [],\n\t\t\t\t\teffectiveLaneWidth: 0,\n\t\t\t\t\tmaxObservedConcurrency: 0,\n\t\t\t\t\tblockers: plan.blockers,\n\t\t\t\t\twarnings: plan.warnings,\n\t\t\t\t};\n\t\t\t}\n\t\t\t// §14.1: the caller's admission decision, if present, is authoritative.\n\t\t\t// Without one the launcher sees an unbounded parent (observe mode).\n\t\t\tconst resolvedDecision =\n\t\t\t\tdecision ??\n\t\t\t\t({\n\t\t\t\t\tschemaVersion: RESOURCE_ADMISSION_VERSION,\n\t\t\t\t\tdecisionId: `admission-${runId}`,\n\t\t\t\t\tsnapshotDigest: \"none\",\n\t\t\t\t\tpressure: \"normal\",\n\t\t\t\t\taction: \"allow\",\n\t\t\t\t\tmaxToolConcurrency: Number.POSITIVE_INFINITY,\n\t\t\t\t\tmaxParallelLanes: Number.POSITIVE_INFINITY,\n\t\t\t\t\tmaxHeavyProcesses: Number.POSITIVE_INFINITY,\n\t\t\t\t\treasons: [],\n\t\t\t\t\tdecidedAt: new Date(0).toISOString(),\n\t\t\t\t} satisfies ResourceAdmissionDecision);\n\n\t\t\tconst result = await launchSubagentLanes({\n\t\t\t\tplan,\n\t\t\t\tpromptRunId: promptRunId ?? runId,\n\t\t\t\tdecision: resolvedDecision,\n\t\t\t\tpermitPool,\n\t\t\t\tconfiguredMaxParallelLanes: input.configuredMaxParallelLanes,\n\t\t\t\tsignal: signal && input.signal ? AbortSignal.any([signal, input.signal]) : (signal ?? input.signal),\n\t\t\t\theavyLaneIds: input.heavyLaneIds,\n\t\t\t\tpermitWaitTimeoutMs: input.permitWaitTimeoutMs,\n\t\t\t\tlaunchLane: async (context) => {\n\t\t\t\t\t// Spec 020 Req3.1/3.2 — child counter +1 immediately before the\n\t\t\t\t\t// admitted launch, -1 exactly once in terminal cleanup.\n\t\t\t\t\tconst release = noteDetachedChild();\n\t\t\t\t\tlet awaitingSettlement = false;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst result = await input.launchLane(context);\n\t\t\t\t\t\tif (result?.status === \"unsettled\" && result.settlement) {\n\t\t\t\t\t\t\tawaitingSettlement = true;\n\t\t\t\t\t\t\tpendingSettlements.add(result.settlement);\n\t\t\t\t\t\t\tvoid result.settlement.then(\n\t\t\t\t\t\t\t\t() => {\n\t\t\t\t\t\t\t\t\tpendingSettlements.delete(result.settlement);\n\t\t\t\t\t\t\t\t\trelease();\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t() => {\n\t\t\t\t\t\t\t\t\t// Rejection does not confirm child termination; retain ownership.\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tif (!awaitingSettlement) release();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn {\n\t\t\t\toutcomes: result.outcomes,\n\t\t\t\teffectiveLaneWidth: result.effectiveLaneWidth,\n\t\t\t\tmaxObservedConcurrency: result.maxObservedConcurrency,\n\t\t\t\tblockers: plan.blockers,\n\t\t\t\twarnings: plan.warnings,\n\t\t\t};\n\t\t},\n\t};\n}\n"]}
@@ -0,0 +1,119 @@
1
+ import { RESOURCE_ADMISSION_VERSION } from "./resource-admission.js";
2
+ import { launchSubagentLanes } from "./subagent-lane-launcher.js";
3
+ import { buildSubagentOrchestrationPlan } from "./subagent-orchestration.js";
4
+ /**
5
+ * Bind a SubagentLaneAuthority to a session's admission decision, shared permit
6
+ * pool, and prompt-settlement lifecycle. The returned authority's
7
+ * dispatchLanes() enforces spec 020: the caller supplies lanes and a launcher;
8
+ * the authority injects plan admission, the shared pool, and per-lane
9
+ * settlement counters, and never lets the caller widen the parent's caps.
10
+ */
11
+ /**
12
+ * Unresolved child settlements keyed by the shared permit pool. AgentSession
13
+ * recreates the authority per dispatch over the same pool, so per-instance
14
+ * state alone would let a fresh instance forget an owned, unsettled child.
15
+ */
16
+ const pendingSettlementsByPool = new WeakMap();
17
+ function unsettledSettlements(pool) {
18
+ let set = pendingSettlementsByPool.get(pool);
19
+ if (set === undefined) {
20
+ set = new Set();
21
+ pendingSettlementsByPool.set(pool, set);
22
+ }
23
+ return set;
24
+ }
25
+ export function createSubagentLaneAuthority(binding) {
26
+ const { runId, promptRunId, decision, permitPool, inventory, signal, spawnThreshold, noteDetachedChild } = binding;
27
+ const pendingSettlements = unsettledSettlements(permitPool);
28
+ return {
29
+ permitPool,
30
+ activePromptRunId: promptRunId,
31
+ getCurrentResourceAdmission: () => decision,
32
+ noteDetachedChild,
33
+ dispatchLanes: async (input) => {
34
+ if (pendingSettlements.size > 0) {
35
+ return {
36
+ outcomes: [],
37
+ effectiveLaneWidth: 0,
38
+ maxObservedConcurrency: 0,
39
+ blockers: ["ownership.unsettled"],
40
+ warnings: [],
41
+ };
42
+ }
43
+ const plan = buildSubagentOrchestrationPlan({
44
+ runId,
45
+ lanes: input.lanes,
46
+ inventory,
47
+ spawnPlan: input.spawnPlan,
48
+ spawnThreshold,
49
+ maxParallelLanes: input.configuredMaxParallelLanes,
50
+ });
51
+ if (plan.blockers.length > 0) {
52
+ return {
53
+ outcomes: [],
54
+ effectiveLaneWidth: 0,
55
+ maxObservedConcurrency: 0,
56
+ blockers: plan.blockers,
57
+ warnings: plan.warnings,
58
+ };
59
+ }
60
+ // §14.1: the caller's admission decision, if present, is authoritative.
61
+ // Without one the launcher sees an unbounded parent (observe mode).
62
+ const resolvedDecision = decision ??
63
+ {
64
+ schemaVersion: RESOURCE_ADMISSION_VERSION,
65
+ decisionId: `admission-${runId}`,
66
+ snapshotDigest: "none",
67
+ pressure: "normal",
68
+ action: "allow",
69
+ maxToolConcurrency: Number.POSITIVE_INFINITY,
70
+ maxParallelLanes: Number.POSITIVE_INFINITY,
71
+ maxHeavyProcesses: Number.POSITIVE_INFINITY,
72
+ reasons: [],
73
+ decidedAt: new Date(0).toISOString(),
74
+ };
75
+ const result = await launchSubagentLanes({
76
+ plan,
77
+ promptRunId: promptRunId ?? runId,
78
+ decision: resolvedDecision,
79
+ permitPool,
80
+ configuredMaxParallelLanes: input.configuredMaxParallelLanes,
81
+ signal: signal && input.signal ? AbortSignal.any([signal, input.signal]) : (signal ?? input.signal),
82
+ heavyLaneIds: input.heavyLaneIds,
83
+ permitWaitTimeoutMs: input.permitWaitTimeoutMs,
84
+ launchLane: async (context) => {
85
+ // Spec 020 Req3.1/3.2 — child counter +1 immediately before the
86
+ // admitted launch, -1 exactly once in terminal cleanup.
87
+ const release = noteDetachedChild();
88
+ let awaitingSettlement = false;
89
+ try {
90
+ const result = await input.launchLane(context);
91
+ if (result?.status === "unsettled" && result.settlement) {
92
+ awaitingSettlement = true;
93
+ pendingSettlements.add(result.settlement);
94
+ void result.settlement.then(() => {
95
+ pendingSettlements.delete(result.settlement);
96
+ release();
97
+ }, () => {
98
+ // Rejection does not confirm child termination; retain ownership.
99
+ });
100
+ }
101
+ return result;
102
+ }
103
+ finally {
104
+ if (!awaitingSettlement)
105
+ release();
106
+ }
107
+ },
108
+ });
109
+ return {
110
+ outcomes: result.outcomes,
111
+ effectiveLaneWidth: result.effectiveLaneWidth,
112
+ maxObservedConcurrency: result.maxObservedConcurrency,
113
+ blockers: plan.blockers,
114
+ warnings: plan.warnings,
115
+ };
116
+ },
117
+ };
118
+ }
119
+ //# sourceMappingURL=subagent-lane-authority.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subagent-lane-authority.js","sourceRoot":"","sources":["../../src/core/subagent-lane-authority.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,0BAA0B,EAAkC,MAAM,yBAAyB,CAAC;AAErG,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,8BAA8B,EAAE,MAAM,6BAA6B,CAAC;AA+B7E;;;;;;GAMG;AACH;;;;GAIG;AACH,MAAM,wBAAwB,GAAG,IAAI,OAAO,EAA8B,CAAC;AAE3E,SAAS,oBAAoB,CAAC,IAAY,EAAsB;IAC/D,IAAI,GAAG,GAAG,wBAAwB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACvB,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;QAChB,wBAAwB,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,GAAG,CAAC;AAAA,CACX;AAED,MAAM,UAAU,2BAA2B,CAAC,OAAyC,EAAyB;IAC7G,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAC;IACnH,MAAM,kBAAkB,GAAG,oBAAoB,CAAC,UAAU,CAAC,CAAC;IAC5D,OAAO;QACN,UAAU;QACV,iBAAiB,EAAE,WAAW;QAC9B,2BAA2B,EAAE,GAAG,EAAE,CAAC,QAAQ;QAC3C,iBAAiB;QACjB,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC;YAC/B,IAAI,kBAAkB,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;gBACjC,OAAO;oBACN,QAAQ,EAAE,EAAE;oBACZ,kBAAkB,EAAE,CAAC;oBACrB,sBAAsB,EAAE,CAAC;oBACzB,QAAQ,EAAE,CAAC,qBAAqB,CAAC;oBACjC,QAAQ,EAAE,EAAE;iBACZ,CAAC;YACH,CAAC;YACD,MAAM,IAAI,GAAG,8BAA8B,CAAC;gBAC3C,KAAK;gBACL,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,SAAS;gBACT,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,cAAc;gBACd,gBAAgB,EAAE,KAAK,CAAC,0BAA0B;aAClD,CAAC,CAAC;YACH,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC9B,OAAO;oBACN,QAAQ,EAAE,EAAE;oBACZ,kBAAkB,EAAE,CAAC;oBACrB,sBAAsB,EAAE,CAAC;oBACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;iBACvB,CAAC;YACH,CAAC;YACD,yEAAwE;YACxE,oEAAoE;YACpE,MAAM,gBAAgB,GACrB,QAAQ;gBACP;oBACA,aAAa,EAAE,0BAA0B;oBACzC,UAAU,EAAE,aAAa,KAAK,EAAE;oBAChC,cAAc,EAAE,MAAM;oBACtB,QAAQ,EAAE,QAAQ;oBAClB,MAAM,EAAE,OAAO;oBACf,kBAAkB,EAAE,MAAM,CAAC,iBAAiB;oBAC5C,gBAAgB,EAAE,MAAM,CAAC,iBAAiB;oBAC1C,iBAAiB,EAAE,MAAM,CAAC,iBAAiB;oBAC3C,OAAO,EAAE,EAAE;oBACX,SAAS,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;iBACC,CAAC;YAExC,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC;gBACxC,IAAI;gBACJ,WAAW,EAAE,WAAW,IAAI,KAAK;gBACjC,QAAQ,EAAE,gBAAgB;gBAC1B,UAAU;gBACV,0BAA0B,EAAE,KAAK,CAAC,0BAA0B;gBAC5D,MAAM,EAAE,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC;gBACnG,YAAY,EAAE,KAAK,CAAC,YAAY;gBAChC,mBAAmB,EAAE,KAAK,CAAC,mBAAmB;gBAC9C,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC;oBAC9B,kEAAgE;oBAChE,wDAAwD;oBACxD,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;oBACpC,IAAI,kBAAkB,GAAG,KAAK,CAAC;oBAC/B,IAAI,CAAC;wBACJ,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;wBAC/C,IAAI,MAAM,EAAE,MAAM,KAAK,WAAW,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;4BACzD,kBAAkB,GAAG,IAAI,CAAC;4BAC1B,kBAAkB,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;4BAC1C,KAAK,MAAM,CAAC,UAAU,CAAC,IAAI,CAC1B,GAAG,EAAE,CAAC;gCACL,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;gCAC7C,OAAO,EAAE,CAAC;4BAAA,CACV,EACD,GAAG,EAAE,CAAC;gCACL,kEAAkE;4BAD5D,CAEN,CACD,CAAC;wBACH,CAAC;wBACD,OAAO,MAAM,CAAC;oBACf,CAAC;4BAAS,CAAC;wBACV,IAAI,CAAC,kBAAkB;4BAAE,OAAO,EAAE,CAAC;oBACpC,CAAC;gBAAA,CACD;aACD,CAAC,CAAC;YACH,OAAO;gBACN,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;gBAC7C,sBAAsB,EAAE,MAAM,CAAC,sBAAsB;gBACrD,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;aACvB,CAAC;QAAA,CACF;KACD,CAAC;AAAA,CACF","sourcesContent":["import type { CapabilityInventory } from \"./loadouts.ts\";\nimport { RESOURCE_ADMISSION_VERSION, type ResourceAdmissionDecision } from \"./resource-admission.ts\";\nimport type { SubagentLaneAuthority } from \"./subagent-lane-contract.ts\";\nimport { launchSubagentLanes } from \"./subagent-lane-launcher.ts\";\nimport { buildSubagentOrchestrationPlan } from \"./subagent-orchestration.ts\";\n\n/**\n * Spec 020 Req1 — extension-facing subagent lane authority.\n *\n * A bound implementation is created by AgentSession and exposed to extension\n * tool contexts. It routes a caller's lane plan through the shared core\n * primitives (`buildSubagentOrchestrationPlan` → `launchSubagentLanes`) while\n * injecting the parent's admission decision, the shared `WorkloadPermitPool`,\n * and prompt-settlement child counters. Extensions must never raise parent\n * caps; `launchSubagentLanes` enforces the effective width as a floor of the\n * caller's configured cap and the parent's admission.\n */\nexport type {\n\tSubagentLaneAuthority,\n\tSubagentLaneAuthorityDispatchInput,\n\tSubagentLaneAuthorityDispatchResult,\n} from \"./subagent-lane-contract.ts\";\n\n/** Internal host binding the session supplies when constructing the authority. */\nexport interface SubagentLaneAuthorityHostBinding {\n\treadonly runId: string;\n\treadonly promptRunId?: string;\n\treadonly decision: ResourceAdmissionDecision | null;\n\treadonly permitPool: import(\"./workload-permit-pool.ts\").WorkloadPermitPool;\n\treadonly inventory: CapabilityInventory;\n\treadonly signal?: AbortSignal;\n\treadonly spawnThreshold?: number;\n\treadonly noteDetachedChild: () => () => void;\n}\n\n/**\n * Bind a SubagentLaneAuthority to a session's admission decision, shared permit\n * pool, and prompt-settlement lifecycle. The returned authority's\n * dispatchLanes() enforces spec 020: the caller supplies lanes and a launcher;\n * the authority injects plan admission, the shared pool, and per-lane\n * settlement counters, and never lets the caller widen the parent's caps.\n */\n/**\n * Unresolved child settlements keyed by the shared permit pool. AgentSession\n * recreates the authority per dispatch over the same pool, so per-instance\n * state alone would let a fresh instance forget an owned, unsettled child.\n */\nconst pendingSettlementsByPool = new WeakMap<object, Set<Promise<void>>>();\n\nfunction unsettledSettlements(pool: object): Set<Promise<void>> {\n\tlet set = pendingSettlementsByPool.get(pool);\n\tif (set === undefined) {\n\t\tset = new Set();\n\t\tpendingSettlementsByPool.set(pool, set);\n\t}\n\treturn set;\n}\n\nexport function createSubagentLaneAuthority(binding: SubagentLaneAuthorityHostBinding): SubagentLaneAuthority {\n\tconst { runId, promptRunId, decision, permitPool, inventory, signal, spawnThreshold, noteDetachedChild } = binding;\n\tconst pendingSettlements = unsettledSettlements(permitPool);\n\treturn {\n\t\tpermitPool,\n\t\tactivePromptRunId: promptRunId,\n\t\tgetCurrentResourceAdmission: () => decision,\n\t\tnoteDetachedChild,\n\t\tdispatchLanes: async (input) => {\n\t\t\tif (pendingSettlements.size > 0) {\n\t\t\t\treturn {\n\t\t\t\t\toutcomes: [],\n\t\t\t\t\teffectiveLaneWidth: 0,\n\t\t\t\t\tmaxObservedConcurrency: 0,\n\t\t\t\t\tblockers: [\"ownership.unsettled\"],\n\t\t\t\t\twarnings: [],\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst plan = buildSubagentOrchestrationPlan({\n\t\t\t\trunId,\n\t\t\t\tlanes: input.lanes,\n\t\t\t\tinventory,\n\t\t\t\tspawnPlan: input.spawnPlan,\n\t\t\t\tspawnThreshold,\n\t\t\t\tmaxParallelLanes: input.configuredMaxParallelLanes,\n\t\t\t});\n\t\t\tif (plan.blockers.length > 0) {\n\t\t\t\treturn {\n\t\t\t\t\toutcomes: [],\n\t\t\t\t\teffectiveLaneWidth: 0,\n\t\t\t\t\tmaxObservedConcurrency: 0,\n\t\t\t\t\tblockers: plan.blockers,\n\t\t\t\t\twarnings: plan.warnings,\n\t\t\t\t};\n\t\t\t}\n\t\t\t// §14.1: the caller's admission decision, if present, is authoritative.\n\t\t\t// Without one the launcher sees an unbounded parent (observe mode).\n\t\t\tconst resolvedDecision =\n\t\t\t\tdecision ??\n\t\t\t\t({\n\t\t\t\t\tschemaVersion: RESOURCE_ADMISSION_VERSION,\n\t\t\t\t\tdecisionId: `admission-${runId}`,\n\t\t\t\t\tsnapshotDigest: \"none\",\n\t\t\t\t\tpressure: \"normal\",\n\t\t\t\t\taction: \"allow\",\n\t\t\t\t\tmaxToolConcurrency: Number.POSITIVE_INFINITY,\n\t\t\t\t\tmaxParallelLanes: Number.POSITIVE_INFINITY,\n\t\t\t\t\tmaxHeavyProcesses: Number.POSITIVE_INFINITY,\n\t\t\t\t\treasons: [],\n\t\t\t\t\tdecidedAt: new Date(0).toISOString(),\n\t\t\t\t} satisfies ResourceAdmissionDecision);\n\n\t\t\tconst result = await launchSubagentLanes({\n\t\t\t\tplan,\n\t\t\t\tpromptRunId: promptRunId ?? runId,\n\t\t\t\tdecision: resolvedDecision,\n\t\t\t\tpermitPool,\n\t\t\t\tconfiguredMaxParallelLanes: input.configuredMaxParallelLanes,\n\t\t\t\tsignal: signal && input.signal ? AbortSignal.any([signal, input.signal]) : (signal ?? input.signal),\n\t\t\t\theavyLaneIds: input.heavyLaneIds,\n\t\t\t\tpermitWaitTimeoutMs: input.permitWaitTimeoutMs,\n\t\t\t\tlaunchLane: async (context) => {\n\t\t\t\t\t// Spec 020 Req3.1/3.2 — child counter +1 immediately before the\n\t\t\t\t\t// admitted launch, -1 exactly once in terminal cleanup.\n\t\t\t\t\tconst release = noteDetachedChild();\n\t\t\t\t\tlet awaitingSettlement = false;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst result = await input.launchLane(context);\n\t\t\t\t\t\tif (result?.status === \"unsettled\" && result.settlement) {\n\t\t\t\t\t\t\tawaitingSettlement = true;\n\t\t\t\t\t\t\tpendingSettlements.add(result.settlement);\n\t\t\t\t\t\t\tvoid result.settlement.then(\n\t\t\t\t\t\t\t\t() => {\n\t\t\t\t\t\t\t\t\tpendingSettlements.delete(result.settlement);\n\t\t\t\t\t\t\t\t\trelease();\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t() => {\n\t\t\t\t\t\t\t\t\t// Rejection does not confirm child termination; retain ownership.\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tif (!awaitingSettlement) release();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn {\n\t\t\t\toutcomes: result.outcomes,\n\t\t\t\teffectiveLaneWidth: result.effectiveLaneWidth,\n\t\t\t\tmaxObservedConcurrency: result.maxObservedConcurrency,\n\t\t\t\tblockers: plan.blockers,\n\t\t\t\twarnings: plan.warnings,\n\t\t\t};\n\t\t},\n\t};\n}\n"]}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Shared subagent-lane contract types (OMK v0.97.x roadmap §14, M6/PR10).
3
+ *
4
+ * A leaf module: it imports only `resource-admission`, `workload-permit-pool`,
5
+ * and `loadouts`, none of which reach back into the extensions graph. The lane
6
+ * orchestrator, launcher, and runtime re-export these declarations, so this
7
+ * file stays the single definition site for each name.
8
+ *
9
+ * Why the split exists: `extensions/types.ts` declares the extension-facing
10
+ * lane-authority accessor, so it must be able to name `SubagentLaneAuthority`.
11
+ * With the authority declared beside its runtime it could only be referenced
12
+ * by importing `subagent-lane-authority.ts`, which reaches `loadout-runtime` →
13
+ * `resource-loader` → `extensions/loader` → `extensions/types.ts` and closed an
14
+ * import cycle across 11 modules. Naming the contract here instead keeps the
15
+ * dependency pointing at a leaf and leaves the runtime where it belongs.
16
+ */
17
+ import type { LoadoutAuthority } from "./loadouts.ts";
18
+ import type { ResourceAdmissionDecision } from "./resource-admission.ts";
19
+ import type { WorkloadPermitPool } from "./workload-permit-pool.ts";
20
+ export type SubagentOrchestrationRole = "planner" | "architect" | "executor" | "critic" | "visual-qa" | "rhwp-doc" | "security" | "package-maintainer";
21
+ /** How much of the parent context a child lane inherits. */
22
+ export type LaneContextInheritanceMode = "none" | "receipt" | "last-turn" | "bounded" | "full";
23
+ /** A resolved callback is not necessarily a successful or settled execution. */
24
+ export type SubagentLaneExecutionResult = void | {
25
+ readonly status: "failed";
26
+ } | {
27
+ readonly status: "unsettled";
28
+ readonly settlement: Promise<void>;
29
+ };
30
+ export interface LaneOutcome {
31
+ readonly laneId: string;
32
+ readonly status: "completed" | "failed" | "cancelled" | "skipped-abort" | "permit-rejected" | "admission-deferred" | "blocked-dependency" | "unsettled";
33
+ readonly diagnostic?: string;
34
+ }
35
+ /** The spawn-plan receipt a caller supplies to justify parallel lane work. */
36
+ export interface LaneSpawnReceipt {
37
+ whyParallel: string;
38
+ whyNotLocal: string;
39
+ independence: string;
40
+ expectedReceiptShape: string;
41
+ maxInlineTokens: number;
42
+ }
43
+ export interface SubagentLaneSpec {
44
+ readonly id: string;
45
+ readonly role: SubagentOrchestrationRole;
46
+ readonly task: string;
47
+ readonly dependsOn?: readonly string[];
48
+ readonly readScope?: readonly string[];
49
+ readonly writeScope?: readonly string[];
50
+ readonly acceptance?: readonly string[];
51
+ readonly evidenceOutput?: string;
52
+ readonly blockedPaths?: readonly string[];
53
+ readonly contextInheritance?: LaneContextInheritanceMode;
54
+ readonly grantAuthority?: LoadoutAuthority;
55
+ readonly loadoutName?: string;
56
+ readonly agentName?: string;
57
+ }
58
+ /**
59
+ * Spec 020 Req1 — extension-facing subagent lane authority.
60
+ *
61
+ * A bound implementation is created by AgentSession and exposed to extension
62
+ * tool contexts. It routes a caller's lane plan through the shared core
63
+ * primitives (`buildSubagentOrchestrationPlan` → `launchSubagentLanes`) while
64
+ * injecting the parent's admission decision, the shared `WorkloadPermitPool`,
65
+ * and prompt-settlement child counters. Extensions must never raise parent
66
+ * caps; `launchSubagentLanes` enforces the effective width as a floor of the
67
+ * caller's configured cap and the parent's admission.
68
+ */
69
+ export interface SubagentLaneAuthorityDispatchInput {
70
+ readonly lanes: readonly SubagentLaneSpec[];
71
+ /** Why this is parallel / why not local / independence / receipt shape — spawn-plan receipt for gating. */
72
+ readonly spawnPlan?: LaneSpawnReceipt;
73
+ /** Caller's configured parallel cap (never widens the parent admission cap). */
74
+ readonly configuredMaxParallelLanes?: number;
75
+ /** Lane ids that run heavy work and must hold a shared §14.3 permit. */
76
+ readonly heavyLaneIds?: ReadonlySet<string>;
77
+ /** Actual child launcher (process spawn / SDK session / test double). Called per admitted lane. */
78
+ readonly launchLane: (context: {
79
+ readonly laneId: string;
80
+ readonly promptRunId: string;
81
+ readonly signal?: AbortSignal;
82
+ readonly decision: ResourceAdmissionDecision;
83
+ readonly effectiveLaneWidth: number;
84
+ }) => Promise<SubagentLaneExecutionResult>;
85
+ readonly signal?: AbortSignal;
86
+ readonly permitWaitTimeoutMs?: number;
87
+ }
88
+ export interface SubagentLaneAuthorityDispatchResult {
89
+ readonly outcomes: readonly LaneOutcome[];
90
+ readonly effectiveLaneWidth: number;
91
+ readonly maxObservedConcurrency: number;
92
+ readonly blockers: readonly string[];
93
+ readonly warnings: readonly string[];
94
+ }
95
+ export interface SubagentLaneAuthority {
96
+ /** §14.1 seam — the session's shared heavy-work pool. Never construct a private one. */
97
+ readonly permitPool: WorkloadPermitPool;
98
+ /** §14.4 read-only — the most recent resource admission decision for this prompt, if any. */
99
+ getCurrentResourceAdmission(): ResourceAdmissionDecision | null;
100
+ /** Current active prompt run id for settlement counters, if a run is open. */
101
+ readonly activePromptRunId: string | undefined;
102
+ /**
103
+ * §16.5 settlement counter — +1 before an admitted child launch, -1 in
104
+ * terminal cleanup. Returns a release closure; call it exactly once.
105
+ */
106
+ noteDetachedChild(): () => void;
107
+ /**
108
+ * Route a caller's lane plan through the shared launcher with the parent's
109
+ * admission decision and pool injected. `launchLane` is invoked per admitted
110
+ * lane; the authority wraps it with settlement counters.
111
+ */
112
+ dispatchLanes(input: SubagentLaneAuthorityDispatchInput): Promise<SubagentLaneAuthorityDispatchResult>;
113
+ }
114
+ //# sourceMappingURL=subagent-lane-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subagent-lane-contract.d.ts","sourceRoot":"","sources":["../../src/core/subagent-lane-contract.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared subagent-lane contract types (OMK v0.97.x roadmap §14, M6/PR10).\n *\n * A leaf module: it imports only `resource-admission`, `workload-permit-pool`,\n * and `loadouts`, none of which reach back into the extensions graph. The lane\n * orchestrator, launcher, and runtime re-export these declarations, so this\n * file stays the single definition site for each name.\n *\n * Why the split exists: `extensions/types.ts` declares the extension-facing\n * lane-authority accessor, so it must be able to name `SubagentLaneAuthority`.\n * With the authority declared beside its runtime it could only be referenced\n * by importing `subagent-lane-authority.ts`, which reaches `loadout-runtime` →\n * `resource-loader` → `extensions/loader` → `extensions/types.ts` and closed an\n * import cycle across 11 modules. Naming the contract here instead keeps the\n * dependency pointing at a leaf and leaves the runtime where it belongs.\n */\n\nimport type { LoadoutAuthority } from \"./loadouts.ts\";\nimport type { ResourceAdmissionDecision } from \"./resource-admission.ts\";\nimport type { WorkloadPermitPool } from \"./workload-permit-pool.ts\";\n\nexport type SubagentOrchestrationRole =\n\t| \"planner\"\n\t| \"architect\"\n\t| \"executor\"\n\t| \"critic\"\n\t| \"visual-qa\"\n\t| \"rhwp-doc\"\n\t| \"security\"\n\t| \"package-maintainer\";\n\n/** How much of the parent context a child lane inherits. */\nexport type LaneContextInheritanceMode = \"none\" | \"receipt\" | \"last-turn\" | \"bounded\" | \"full\";\n\n/** A resolved callback is not necessarily a successful or settled execution. */\nexport type SubagentLaneExecutionResult =\n\t| void\n\t| { readonly status: \"failed\" }\n\t| { readonly status: \"unsettled\"; readonly settlement: Promise<void> };\n\nexport interface LaneOutcome {\n\treadonly laneId: string;\n\treadonly status:\n\t\t| \"completed\"\n\t\t| \"failed\"\n\t\t| \"cancelled\"\n\t\t| \"skipped-abort\"\n\t\t| \"permit-rejected\"\n\t\t| \"admission-deferred\"\n\t\t| \"blocked-dependency\"\n\t\t| \"unsettled\";\n\treadonly diagnostic?: string;\n}\n\n/** The spawn-plan receipt a caller supplies to justify parallel lane work. */\nexport interface LaneSpawnReceipt {\n\twhyParallel: string;\n\twhyNotLocal: string;\n\tindependence: string;\n\texpectedReceiptShape: string;\n\tmaxInlineTokens: number;\n}\n\nexport interface SubagentLaneSpec {\n\treadonly id: string;\n\treadonly role: SubagentOrchestrationRole;\n\treadonly task: string;\n\treadonly dependsOn?: readonly string[];\n\treadonly readScope?: readonly string[];\n\treadonly writeScope?: readonly string[];\n\treadonly acceptance?: readonly string[];\n\treadonly evidenceOutput?: string;\n\treadonly blockedPaths?: readonly string[];\n\treadonly contextInheritance?: LaneContextInheritanceMode;\n\treadonly grantAuthority?: LoadoutAuthority;\n\treadonly loadoutName?: string;\n\treadonly agentName?: string;\n}\n\n/**\n * Spec 020 Req1 — extension-facing subagent lane authority.\n *\n * A bound implementation is created by AgentSession and exposed to extension\n * tool contexts. It routes a caller's lane plan through the shared core\n * primitives (`buildSubagentOrchestrationPlan` → `launchSubagentLanes`) while\n * injecting the parent's admission decision, the shared `WorkloadPermitPool`,\n * and prompt-settlement child counters. Extensions must never raise parent\n * caps; `launchSubagentLanes` enforces the effective width as a floor of the\n * caller's configured cap and the parent's admission.\n */\nexport interface SubagentLaneAuthorityDispatchInput {\n\treadonly lanes: readonly SubagentLaneSpec[];\n\t/** Why this is parallel / why not local / independence / receipt shape — spawn-plan receipt for gating. */\n\treadonly spawnPlan?: LaneSpawnReceipt;\n\t/** Caller's configured parallel cap (never widens the parent admission cap). */\n\treadonly configuredMaxParallelLanes?: number;\n\t/** Lane ids that run heavy work and must hold a shared §14.3 permit. */\n\treadonly heavyLaneIds?: ReadonlySet<string>;\n\t/** Actual child launcher (process spawn / SDK session / test double). Called per admitted lane. */\n\treadonly launchLane: (context: {\n\t\treadonly laneId: string;\n\t\treadonly promptRunId: string;\n\t\treadonly signal?: AbortSignal;\n\t\treadonly decision: ResourceAdmissionDecision;\n\t\treadonly effectiveLaneWidth: number;\n\t}) => Promise<SubagentLaneExecutionResult>;\n\treadonly signal?: AbortSignal;\n\treadonly permitWaitTimeoutMs?: number;\n}\n\nexport interface SubagentLaneAuthorityDispatchResult {\n\treadonly outcomes: readonly LaneOutcome[];\n\treadonly effectiveLaneWidth: number;\n\treadonly maxObservedConcurrency: number;\n\treadonly blockers: readonly string[];\n\treadonly warnings: readonly string[];\n}\n\nexport interface SubagentLaneAuthority {\n\t/** §14.1 seam — the session's shared heavy-work pool. Never construct a private one. */\n\treadonly permitPool: WorkloadPermitPool;\n\t/** §14.4 read-only — the most recent resource admission decision for this prompt, if any. */\n\tgetCurrentResourceAdmission(): ResourceAdmissionDecision | null;\n\t/** Current active prompt run id for settlement counters, if a run is open. */\n\treadonly activePromptRunId: string | undefined;\n\t/**\n\t * §16.5 settlement counter — +1 before an admitted child launch, -1 in\n\t * terminal cleanup. Returns a release closure; call it exactly once.\n\t */\n\tnoteDetachedChild(): () => void;\n\t/**\n\t * Route a caller's lane plan through the shared launcher with the parent's\n\t * admission decision and pool injected. `launchLane` is invoked per admitted\n\t * lane; the authority wraps it with settlement counters.\n\t */\n\tdispatchLanes(input: SubagentLaneAuthorityDispatchInput): Promise<SubagentLaneAuthorityDispatchResult>;\n}\n"]}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Shared subagent-lane contract types (OMK v0.97.x roadmap §14, M6/PR10).
3
+ *
4
+ * A leaf module: it imports only `resource-admission`, `workload-permit-pool`,
5
+ * and `loadouts`, none of which reach back into the extensions graph. The lane
6
+ * orchestrator, launcher, and runtime re-export these declarations, so this
7
+ * file stays the single definition site for each name.
8
+ *
9
+ * Why the split exists: `extensions/types.ts` declares the extension-facing
10
+ * lane-authority accessor, so it must be able to name `SubagentLaneAuthority`.
11
+ * With the authority declared beside its runtime it could only be referenced
12
+ * by importing `subagent-lane-authority.ts`, which reaches `loadout-runtime` →
13
+ * `resource-loader` → `extensions/loader` → `extensions/types.ts` and closed an
14
+ * import cycle across 11 modules. Naming the contract here instead keeps the
15
+ * dependency pointing at a leaf and leaves the runtime where it belongs.
16
+ */
17
+ export {};
18
+ //# sourceMappingURL=subagent-lane-contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subagent-lane-contract.js","sourceRoot":"","sources":["../../src/core/subagent-lane-contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG","sourcesContent":["/**\n * Shared subagent-lane contract types (OMK v0.97.x roadmap §14, M6/PR10).\n *\n * A leaf module: it imports only `resource-admission`, `workload-permit-pool`,\n * and `loadouts`, none of which reach back into the extensions graph. The lane\n * orchestrator, launcher, and runtime re-export these declarations, so this\n * file stays the single definition site for each name.\n *\n * Why the split exists: `extensions/types.ts` declares the extension-facing\n * lane-authority accessor, so it must be able to name `SubagentLaneAuthority`.\n * With the authority declared beside its runtime it could only be referenced\n * by importing `subagent-lane-authority.ts`, which reaches `loadout-runtime` →\n * `resource-loader` → `extensions/loader` → `extensions/types.ts` and closed an\n * import cycle across 11 modules. Naming the contract here instead keeps the\n * dependency pointing at a leaf and leaves the runtime where it belongs.\n */\n\nimport type { LoadoutAuthority } from \"./loadouts.ts\";\nimport type { ResourceAdmissionDecision } from \"./resource-admission.ts\";\nimport type { WorkloadPermitPool } from \"./workload-permit-pool.ts\";\n\nexport type SubagentOrchestrationRole =\n\t| \"planner\"\n\t| \"architect\"\n\t| \"executor\"\n\t| \"critic\"\n\t| \"visual-qa\"\n\t| \"rhwp-doc\"\n\t| \"security\"\n\t| \"package-maintainer\";\n\n/** How much of the parent context a child lane inherits. */\nexport type LaneContextInheritanceMode = \"none\" | \"receipt\" | \"last-turn\" | \"bounded\" | \"full\";\n\n/** A resolved callback is not necessarily a successful or settled execution. */\nexport type SubagentLaneExecutionResult =\n\t| void\n\t| { readonly status: \"failed\" }\n\t| { readonly status: \"unsettled\"; readonly settlement: Promise<void> };\n\nexport interface LaneOutcome {\n\treadonly laneId: string;\n\treadonly status:\n\t\t| \"completed\"\n\t\t| \"failed\"\n\t\t| \"cancelled\"\n\t\t| \"skipped-abort\"\n\t\t| \"permit-rejected\"\n\t\t| \"admission-deferred\"\n\t\t| \"blocked-dependency\"\n\t\t| \"unsettled\";\n\treadonly diagnostic?: string;\n}\n\n/** The spawn-plan receipt a caller supplies to justify parallel lane work. */\nexport interface LaneSpawnReceipt {\n\twhyParallel: string;\n\twhyNotLocal: string;\n\tindependence: string;\n\texpectedReceiptShape: string;\n\tmaxInlineTokens: number;\n}\n\nexport interface SubagentLaneSpec {\n\treadonly id: string;\n\treadonly role: SubagentOrchestrationRole;\n\treadonly task: string;\n\treadonly dependsOn?: readonly string[];\n\treadonly readScope?: readonly string[];\n\treadonly writeScope?: readonly string[];\n\treadonly acceptance?: readonly string[];\n\treadonly evidenceOutput?: string;\n\treadonly blockedPaths?: readonly string[];\n\treadonly contextInheritance?: LaneContextInheritanceMode;\n\treadonly grantAuthority?: LoadoutAuthority;\n\treadonly loadoutName?: string;\n\treadonly agentName?: string;\n}\n\n/**\n * Spec 020 Req1 — extension-facing subagent lane authority.\n *\n * A bound implementation is created by AgentSession and exposed to extension\n * tool contexts. It routes a caller's lane plan through the shared core\n * primitives (`buildSubagentOrchestrationPlan` → `launchSubagentLanes`) while\n * injecting the parent's admission decision, the shared `WorkloadPermitPool`,\n * and prompt-settlement child counters. Extensions must never raise parent\n * caps; `launchSubagentLanes` enforces the effective width as a floor of the\n * caller's configured cap and the parent's admission.\n */\nexport interface SubagentLaneAuthorityDispatchInput {\n\treadonly lanes: readonly SubagentLaneSpec[];\n\t/** Why this is parallel / why not local / independence / receipt shape — spawn-plan receipt for gating. */\n\treadonly spawnPlan?: LaneSpawnReceipt;\n\t/** Caller's configured parallel cap (never widens the parent admission cap). */\n\treadonly configuredMaxParallelLanes?: number;\n\t/** Lane ids that run heavy work and must hold a shared §14.3 permit. */\n\treadonly heavyLaneIds?: ReadonlySet<string>;\n\t/** Actual child launcher (process spawn / SDK session / test double). Called per admitted lane. */\n\treadonly launchLane: (context: {\n\t\treadonly laneId: string;\n\t\treadonly promptRunId: string;\n\t\treadonly signal?: AbortSignal;\n\t\treadonly decision: ResourceAdmissionDecision;\n\t\treadonly effectiveLaneWidth: number;\n\t}) => Promise<SubagentLaneExecutionResult>;\n\treadonly signal?: AbortSignal;\n\treadonly permitWaitTimeoutMs?: number;\n}\n\nexport interface SubagentLaneAuthorityDispatchResult {\n\treadonly outcomes: readonly LaneOutcome[];\n\treadonly effectiveLaneWidth: number;\n\treadonly maxObservedConcurrency: number;\n\treadonly blockers: readonly string[];\n\treadonly warnings: readonly string[];\n}\n\nexport interface SubagentLaneAuthority {\n\t/** §14.1 seam — the session's shared heavy-work pool. Never construct a private one. */\n\treadonly permitPool: WorkloadPermitPool;\n\t/** §14.4 read-only — the most recent resource admission decision for this prompt, if any. */\n\tgetCurrentResourceAdmission(): ResourceAdmissionDecision | null;\n\t/** Current active prompt run id for settlement counters, if a run is open. */\n\treadonly activePromptRunId: string | undefined;\n\t/**\n\t * §16.5 settlement counter — +1 before an admitted child launch, -1 in\n\t * terminal cleanup. Returns a release closure; call it exactly once.\n\t */\n\tnoteDetachedChild(): () => void;\n\t/**\n\t * Route a caller's lane plan through the shared launcher with the parent's\n\t * admission decision and pool injected. `launchLane` is invoked per admitted\n\t * lane; the authority wraps it with settlement counters.\n\t */\n\tdispatchLanes(input: SubagentLaneAuthorityDispatchInput): Promise<SubagentLaneAuthorityDispatchResult>;\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import type { ResourceAdmissionDecision } from "./resource-admission.ts";
2
+ import type { LaneOutcome, SubagentLaneExecutionResult } from "./subagent-lane-contract.ts";
2
3
  import type { SubagentOrchestrationPlan } from "./subagent-orchestration.ts";
3
4
  import { type WorkloadPermitPool } from "./workload-permit-pool.ts";
4
5
  /**
@@ -34,11 +35,7 @@ export interface SubagentLaneContext {
34
35
  readonly decision: ResourceAdmissionDecision;
35
36
  readonly effectiveLaneWidth: number;
36
37
  }
37
- export interface LaneOutcome {
38
- readonly laneId: string;
39
- readonly status: "completed" | "failed" | "cancelled" | "skipped-abort" | "permit-rejected" | "admission-deferred";
40
- readonly diagnostic?: string;
41
- }
38
+ export type { LaneOutcome };
42
39
  export interface LaunchSubagentLanesInput {
43
40
  readonly plan: SubagentOrchestrationPlan;
44
41
  readonly promptRunId: string;
@@ -49,7 +46,7 @@ export interface LaunchSubagentLanesInput {
49
46
  /** Lane ids that run heavy work and must hold a shared permit (§14.3). */
50
47
  readonly heavyLaneIds?: ReadonlySet<string>;
51
48
  /** The actual child launcher (process spawn, SDK session, or test double). */
52
- readonly launchLane: (context: SubagentLaneContext) => Promise<void>;
49
+ readonly launchLane: (context: SubagentLaneContext) => Promise<SubagentLaneExecutionResult>;
53
50
  readonly permitWaitTimeoutMs?: number;
54
51
  }
55
52
  export interface LaunchSubagentLanesResult {
@@ -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,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
+ {"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,WAAW,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAC;AAC5F,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,YAAY,EAAE,WAAW,EAAE,CAAC;AAE5B,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,2BAA2B,CAAC,CAAC;IAC5F,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;AAsBD;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAoE7G","sourcesContent":["import type { ResourceAdmissionDecision } from \"./resource-admission.ts\";\nimport type { LaneOutcome, SubagentLaneExecutionResult } from \"./subagent-lane-contract.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 type { LaneOutcome };\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<SubagentLaneExecutionResult>;\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 * Heavy admission gate for the effective lane width. Omitted `heavyLaneIds`\n * preserves the legacy contract: remaining pool capacity gates the whole width.\n * An explicitly declared set narrows the gate to heavy work only (spec F06): a\n * light-only declaration or a mixed plan must not let the heavy pool stall light\n * lanes; mixed plans defer heavy lanes individually at permit acquisition.\n */\nfunction computeHeavyAdmissionGate(\n\tpoolSnapshot: { readonly capacity: number; readonly activeWeight: number },\n\theavyLaneIds: ReadonlySet<string> | undefined,\n\tplan: SubagentOrchestrationPlan,\n\tmaxHeavyProcesses: number,\n): number {\n\tif (heavyLaneIds === undefined) return Math.max(0, poolSnapshot.capacity - poolSnapshot.activeWeight);\n\tif (heavyLaneIds.size === 0) return Number.POSITIVE_INFINITY;\n\tconst allHeavy = plan.batches.every((batch) => batch.laneIds.every((id) => heavyLaneIds.has(id)));\n\tif (!allHeavy) return Number.POSITIVE_INFINITY;\n\treturn Math.min(Math.max(0, poolSnapshot.capacity - poolSnapshot.activeWeight), maxHeavyProcesses);\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 heavyAdmission = computeHeavyAdmissionGate(\n\t\tpoolSnapshot,\n\t\tinput.heavyLaneIds,\n\t\tinput.plan,\n\t\tinput.decision.maxHeavyProcesses,\n\t);\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: heavyAdmission,\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\tconst blocked = outcomes.some((outcome) => outcome.status !== \"completed\");\n\t\tif (input.signal?.aborted || effectiveLaneWidth === 0 || blocked) {\n\t\t\tfor (const laneId of batch.laneIds) {\n\t\t\t\toutcomes.push({\n\t\t\t\t\tlaneId,\n\t\t\t\t\tstatus: input.signal?.aborted\n\t\t\t\t\t\t? \"skipped-abort\"\n\t\t\t\t\t\t: effectiveLaneWidth === 0\n\t\t\t\t\t\t\t? \"admission-deferred\"\n\t\t\t\t\t\t\t: \"blocked-dependency\",\n\t\t\t\t});\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 || outcomes.some((outcome) => outcome.status === \"unsettled\")) {\n\t\t\t\t\toutcomes.push({ laneId, status: input.signal?.aborted ? \"skipped-abort\" : \"blocked-dependency\" });\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\tblocked: () => outcomes.some((outcome) => outcome.status === \"unsettled\"),\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; readonly blocked: () => boolean },\n): Promise<LaneOutcome> {\n\tlet releasePermit: (() => void) | undefined;\n\tlet retained = false;\n\tif (input.heavyLaneIds?.has(laneId)) {\n\t\tif (\n\t\t\tinput.decision.action === \"defer-heavy\" ||\n\t\t\tinput.permitPool.snapshot().capacity === 0 ||\n\t\t\tinput.permitPool.snapshot().activeWeight >= input.decision.maxHeavyProcesses\n\t\t) {\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 || gauge.blocked()) {\n\t\treleasePermit?.();\n\t\treturn { laneId, status: input.signal?.aborted ? \"skipped-abort\" : \"blocked-dependency\" };\n\t}\n\tgauge.enter();\n\ttry {\n\t\tconst result = await 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\tif (result?.status === \"unsettled\") {\n\t\t\tretained = true;\n\t\t\t// A rejected settlement cannot prove termination; retain the reservation.\n\t\t\tvoid result.settlement.then(\n\t\t\t\t() => {\n\t\t\t\t\tgauge.exit();\n\t\t\t\t\treleasePermit?.();\n\t\t\t\t},\n\t\t\t\t() => {},\n\t\t\t);\n\t\t\treturn { laneId, status: \"unsettled\" };\n\t\t}\n\t\treturn { laneId, status: input.signal?.aborted ? \"cancelled\" : (result?.status ?? \"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\tif (!retained) {\n\t\t\tgauge.exit();\n\t\t\treleasePermit?.();\n\t\t}\n\t}\n}\n"]}
@@ -7,26 +7,52 @@ export function computeEffectiveLaneWidth(input) {
7
7
  const width = Math.min(input.planWidth, configured, input.admissionMaxParallelLanes, input.availableHeavyPermits, input.pathConflictFreeWidth);
8
8
  return Number.isFinite(width) ? Math.max(0, Math.floor(width)) : 0;
9
9
  }
10
+ /**
11
+ * Heavy admission gate for the effective lane width. Omitted `heavyLaneIds`
12
+ * preserves the legacy contract: remaining pool capacity gates the whole width.
13
+ * An explicitly declared set narrows the gate to heavy work only (spec F06): a
14
+ * light-only declaration or a mixed plan must not let the heavy pool stall light
15
+ * lanes; mixed plans defer heavy lanes individually at permit acquisition.
16
+ */
17
+ function computeHeavyAdmissionGate(poolSnapshot, heavyLaneIds, plan, maxHeavyProcesses) {
18
+ if (heavyLaneIds === undefined)
19
+ return Math.max(0, poolSnapshot.capacity - poolSnapshot.activeWeight);
20
+ if (heavyLaneIds.size === 0)
21
+ return Number.POSITIVE_INFINITY;
22
+ const allHeavy = plan.batches.every((batch) => batch.laneIds.every((id) => heavyLaneIds.has(id)));
23
+ if (!allHeavy)
24
+ return Number.POSITIVE_INFINITY;
25
+ return Math.min(Math.max(0, poolSnapshot.capacity - poolSnapshot.activeWeight), maxHeavyProcesses);
26
+ }
10
27
  /**
11
28
  * Execute a plan's batches with the §14.2 width as launcher authority.
12
29
  * Never throws for lane failures; the caller reads per-lane outcomes.
13
30
  */
14
31
  export async function launchSubagentLanes(input) {
15
32
  const poolSnapshot = input.permitPool.snapshot();
33
+ const heavyAdmission = computeHeavyAdmissionGate(poolSnapshot, input.heavyLaneIds, input.plan, input.decision.maxHeavyProcesses);
16
34
  const effectiveLaneWidth = computeEffectiveLaneWidth({
17
35
  planWidth: input.plan.route.width,
18
36
  configuredMaxParallelLanes: input.configuredMaxParallelLanes,
19
37
  admissionMaxParallelLanes: input.decision.maxParallelLanes,
20
- availableHeavyPermits: Math.min(Math.max(0, poolSnapshot.capacity - poolSnapshot.activeWeight), input.heavyLaneIds?.size ? input.decision.maxHeavyProcesses : Number.POSITIVE_INFINITY),
38
+ availableHeavyPermits: heavyAdmission,
21
39
  pathConflictFreeWidth: Math.max(0, ...input.plan.batches.map((batch) => batch.laneIds.length)),
22
40
  });
23
41
  const outcomes = [];
24
42
  let active = 0;
25
43
  let maxObservedConcurrency = 0;
26
44
  for (const batch of input.plan.batches) {
27
- if (input.signal?.aborted || effectiveLaneWidth === 0) {
45
+ const blocked = outcomes.some((outcome) => outcome.status !== "completed");
46
+ if (input.signal?.aborted || effectiveLaneWidth === 0 || blocked) {
28
47
  for (const laneId of batch.laneIds) {
29
- outcomes.push({ laneId, status: input.signal?.aborted ? "skipped-abort" : "admission-deferred" });
48
+ outcomes.push({
49
+ laneId,
50
+ status: input.signal?.aborted
51
+ ? "skipped-abort"
52
+ : effectiveLaneWidth === 0
53
+ ? "admission-deferred"
54
+ : "blocked-dependency",
55
+ });
30
56
  }
31
57
  continue;
32
58
  }
@@ -38,11 +64,12 @@ export async function launchSubagentLanes(input) {
38
64
  while (cursor < batch.laneIds.length) {
39
65
  const laneId = batch.laneIds[cursor];
40
66
  cursor += 1;
41
- if (input.signal?.aborted) {
42
- outcomes.push({ laneId, status: "skipped-abort" });
67
+ if (input.signal?.aborted || outcomes.some((outcome) => outcome.status === "unsettled")) {
68
+ outcomes.push({ laneId, status: input.signal?.aborted ? "skipped-abort" : "blocked-dependency" });
43
69
  continue;
44
70
  }
45
71
  outcomes.push(await runLane(input, laneId, effectiveLaneWidth, {
72
+ blocked: () => outcomes.some((outcome) => outcome.status === "unsettled"),
46
73
  enter: () => {
47
74
  active += 1;
48
75
  maxObservedConcurrency = Math.max(maxObservedConcurrency, active);
@@ -63,8 +90,11 @@ export async function launchSubagentLanes(input) {
63
90
  }
64
91
  async function runLane(input, laneId, effectiveLaneWidth, gauge) {
65
92
  let releasePermit;
93
+ let retained = false;
66
94
  if (input.heavyLaneIds?.has(laneId)) {
67
- if (input.decision.action === "defer-heavy" || input.decision.maxHeavyProcesses === 0) {
95
+ if (input.decision.action === "defer-heavy" ||
96
+ input.permitPool.snapshot().capacity === 0 ||
97
+ input.permitPool.snapshot().activeWeight >= input.decision.maxHeavyProcesses) {
68
98
  return { laneId, status: "admission-deferred" };
69
99
  }
70
100
  try {
@@ -87,20 +117,29 @@ async function runLane(input, laneId, effectiveLaneWidth, gauge) {
87
117
  };
88
118
  }
89
119
  }
90
- if (input.signal?.aborted) {
120
+ if (input.signal?.aborted || gauge.blocked()) {
91
121
  releasePermit?.();
92
- return { laneId, status: "skipped-abort" };
122
+ return { laneId, status: input.signal?.aborted ? "skipped-abort" : "blocked-dependency" };
93
123
  }
94
124
  gauge.enter();
95
125
  try {
96
- await input.launchLane({
126
+ const result = await input.launchLane({
97
127
  laneId,
98
128
  promptRunId: input.promptRunId,
99
129
  signal: input.signal,
100
130
  decision: input.decision,
101
131
  effectiveLaneWidth,
102
132
  });
103
- return { laneId, status: input.signal?.aborted ? "cancelled" : "completed" };
133
+ if (result?.status === "unsettled") {
134
+ retained = true;
135
+ // A rejected settlement cannot prove termination; retain the reservation.
136
+ void result.settlement.then(() => {
137
+ gauge.exit();
138
+ releasePermit?.();
139
+ }, () => { });
140
+ return { laneId, status: "unsettled" };
141
+ }
142
+ return { laneId, status: input.signal?.aborted ? "cancelled" : (result?.status ?? "completed") };
104
143
  }
105
144
  catch {
106
145
  // Child error text is untrusted and may contain credentials.
@@ -111,8 +150,10 @@ async function runLane(input, laneId, effectiveLaneWidth, gauge) {
111
150
  };
112
151
  }
113
152
  finally {
114
- gauge.exit();
115
- releasePermit?.();
153
+ if (!retained) {
154
+ gauge.exit();
155
+ releasePermit?.();
156
+ }
116
157
  }
117
158
  }
118
159
  //# sourceMappingURL=subagent-lane-launcher.js.map
@@ -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/** 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
+ {"version":3,"file":"subagent-lane-launcher.js","sourceRoot":"","sources":["../../src/core/subagent-lane-launcher.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAA2B,MAAM,2BAA2B,CAAC;AA2BzF,6FAA6F;AAC7F,MAAM,UAAU,yBAAyB,CAAC,KAA8B,EAAU;IACjF,MAAM,UAAU,GACf,KAAK,CAAC,0BAA0B,KAAK,SAAS,IAAI,KAAK,CAAC,0BAA0B,GAAG,CAAC;QACrF,CAAC,CAAC,KAAK,CAAC,0BAA0B;QAClC,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC;IAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CACrB,KAAK,CAAC,SAAS,EACf,UAAU,EACV,KAAK,CAAC,yBAAyB,EAC/B,KAAK,CAAC,qBAAqB,EAC3B,KAAK,CAAC,qBAAqB,CAC3B,CAAC;IACF,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAAA,CACnE;AAiCD;;;;;;GAMG;AACH,SAAS,yBAAyB,CACjC,YAA0E,EAC1E,YAA6C,EAC7C,IAA+B,EAC/B,iBAAyB,EAChB;IACT,IAAI,YAAY,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,CAAC,QAAQ,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IACtG,IAAI,YAAY,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC,iBAAiB,CAAC;IAC7D,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAClG,IAAI,CAAC,QAAQ;QAAE,OAAO,MAAM,CAAC,iBAAiB,CAAC;IAC/C,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,CAAC,QAAQ,GAAG,YAAY,CAAC,YAAY,CAAC,EAAE,iBAAiB,CAAC,CAAC;AAAA,CACnG;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,KAA+B,EAAsC;IAC9G,MAAM,YAAY,GAAG,KAAK,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;IACjD,MAAM,cAAc,GAAG,yBAAyB,CAC/C,YAAY,EACZ,KAAK,CAAC,YAAY,EAClB,KAAK,CAAC,IAAI,EACV,KAAK,CAAC,QAAQ,CAAC,iBAAiB,CAChC,CAAC;IACF,MAAM,kBAAkB,GAAG,yBAAyB,CAAC;QACpD,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK;QACjC,0BAA0B,EAAE,KAAK,CAAC,0BAA0B;QAC5D,yBAAyB,EAAE,KAAK,CAAC,QAAQ,CAAC,gBAAgB;QAC1D,qBAAqB,EAAE,cAAc;QACrC,qBAAqB,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;KAC9F,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAkB,EAAE,CAAC;IACnC,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,sBAAsB,GAAG,CAAC,CAAC;IAE/B,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC;QAC3E,IAAI,KAAK,CAAC,MAAM,EAAE,OAAO,IAAI,kBAAkB,KAAK,CAAC,IAAI,OAAO,EAAE,CAAC;YAClE,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;gBACpC,QAAQ,CAAC,IAAI,CAAC;oBACb,MAAM;oBACN,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO;wBAC5B,CAAC,CAAC,eAAe;wBACjB,CAAC,CAAC,kBAAkB,KAAK,CAAC;4BACzB,CAAC,CAAC,oBAAoB;4BACtB,CAAC,CAAC,oBAAoB;iBACxB,CAAC,CAAC;YACJ,CAAC;YACD,SAAS;QACV,CAAC;QACD,oEAAoE;QACpE,qEAAqE;QACrE,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,MAAM,OAAO,GAAoB,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,KAAK,IAAmB,EAAE,CAAC;YAC1C,OAAO,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;gBACtC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;gBACrC,MAAM,IAAI,CAAC,CAAC;gBACZ,IAAI,KAAK,CAAC,MAAM,EAAE,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,KAAK,WAAW,CAAC,EAAE,CAAC;oBACzF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,oBAAoB,EAAE,CAAC,CAAC;oBAClG,SAAS;gBACV,CAAC;gBACD,QAAQ,CAAC,IAAI,CACZ,MAAM,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE;oBAChD,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,KAAK,WAAW,CAAC;oBACzE,KAAK,EAAE,GAAG,EAAE,CAAC;wBACZ,MAAM,IAAI,CAAC,CAAC;wBACZ,sBAAsB,GAAG,IAAI,CAAC,GAAG,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAC;oBAAA,CAClE;oBACD,IAAI,EAAE,GAAG,EAAE,CAAC;wBACX,MAAM,IAAI,CAAC,CAAC;oBAAA,CACZ;iBACD,CAAC,CACF,CAAC;YACH,CAAC;QAAA,CACD,CAAC;QACF,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,kBAAkB,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACnE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC;YAClC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;QACzB,CAAC;QACD,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,CAAC;AAAA,CAChE;AAED,KAAK,UAAU,OAAO,CACrB,KAA+B,EAC/B,MAAc,EACd,kBAA0B,EAC1B,KAAiG,EAC1E;IACvB,IAAI,aAAuC,CAAC;IAC5C,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,KAAK,CAAC,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACrC,IACC,KAAK,CAAC,QAAQ,CAAC,MAAM,KAAK,aAAa;YACvC,KAAK,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC,QAAQ,KAAK,CAAC;YAC1C,KAAK,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC,YAAY,IAAI,KAAK,CAAC,QAAQ,CAAC,iBAAiB,EAC3E,CAAC;YACF,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC;QACjD,CAAC;QACD,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC;gBAC7C,SAAS,EAAE,QAAQ,MAAM,EAAE;gBAC3B,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,aAAa,EAAE,OAAO;gBACtB,MAAM,EAAE,CAAC;gBACT,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,SAAS,EAAE,KAAK,CAAC,mBAAmB,IAAI,MAAM;aAC9C,CAAC,CAAC;YACH,aAAa,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACxC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,GAAG,KAAK,YAAY,mBAAmB,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;YAC3E,OAAO;gBACN,MAAM;gBACN,MAAM,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,iBAAiB;gBAChE,UAAU,EAAE,UAAU,IAAI,EAAE;aAC5B,CAAC;QACH,CAAC;IACF,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,EAAE,OAAO,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QAC9C,aAAa,EAAE,EAAE,CAAC;QAClB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,oBAAoB,EAAE,CAAC;IAC3F,CAAC;IACD,KAAK,CAAC,KAAK,EAAE,CAAC;IACd,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,UAAU,CAAC;YACrC,MAAM;YACN,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,kBAAkB;SAClB,CAAC,CAAC;QACH,IAAI,MAAM,EAAE,MAAM,KAAK,WAAW,EAAE,CAAC;YACpC,QAAQ,GAAG,IAAI,CAAC;YAChB,0EAA0E;YAC1E,KAAK,MAAM,CAAC,UAAU,CAAC,IAAI,CAC1B,GAAG,EAAE,CAAC;gBACL,KAAK,CAAC,IAAI,EAAE,CAAC;gBACb,aAAa,EAAE,EAAE,CAAC;YAAA,CAClB,EACD,GAAG,EAAE,CAAC,EAAC,CAAC,CACR,CAAC;YACF,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;QACxC,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,IAAI,WAAW,CAAC,EAAE,CAAC;IAClG,CAAC;IAAC,MAAM,CAAC;QACR,6DAA6D;QAC7D,OAAO;YACN,MAAM;YACN,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ;YACtD,UAAU,EAAE,uBAAuB;SACnC,CAAC;IACH,CAAC;YAAS,CAAC;QACV,IAAI,CAAC,QAAQ,EAAE,CAAC;YACf,KAAK,CAAC,IAAI,EAAE,CAAC;YACb,aAAa,EAAE,EAAE,CAAC;QACnB,CAAC;IACF,CAAC;AAAA,CACD","sourcesContent":["import type { ResourceAdmissionDecision } from \"./resource-admission.ts\";\nimport type { LaneOutcome, SubagentLaneExecutionResult } from \"./subagent-lane-contract.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 type { LaneOutcome };\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<SubagentLaneExecutionResult>;\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 * Heavy admission gate for the effective lane width. Omitted `heavyLaneIds`\n * preserves the legacy contract: remaining pool capacity gates the whole width.\n * An explicitly declared set narrows the gate to heavy work only (spec F06): a\n * light-only declaration or a mixed plan must not let the heavy pool stall light\n * lanes; mixed plans defer heavy lanes individually at permit acquisition.\n */\nfunction computeHeavyAdmissionGate(\n\tpoolSnapshot: { readonly capacity: number; readonly activeWeight: number },\n\theavyLaneIds: ReadonlySet<string> | undefined,\n\tplan: SubagentOrchestrationPlan,\n\tmaxHeavyProcesses: number,\n): number {\n\tif (heavyLaneIds === undefined) return Math.max(0, poolSnapshot.capacity - poolSnapshot.activeWeight);\n\tif (heavyLaneIds.size === 0) return Number.POSITIVE_INFINITY;\n\tconst allHeavy = plan.batches.every((batch) => batch.laneIds.every((id) => heavyLaneIds.has(id)));\n\tif (!allHeavy) return Number.POSITIVE_INFINITY;\n\treturn Math.min(Math.max(0, poolSnapshot.capacity - poolSnapshot.activeWeight), maxHeavyProcesses);\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 heavyAdmission = computeHeavyAdmissionGate(\n\t\tpoolSnapshot,\n\t\tinput.heavyLaneIds,\n\t\tinput.plan,\n\t\tinput.decision.maxHeavyProcesses,\n\t);\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: heavyAdmission,\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\tconst blocked = outcomes.some((outcome) => outcome.status !== \"completed\");\n\t\tif (input.signal?.aborted || effectiveLaneWidth === 0 || blocked) {\n\t\t\tfor (const laneId of batch.laneIds) {\n\t\t\t\toutcomes.push({\n\t\t\t\t\tlaneId,\n\t\t\t\t\tstatus: input.signal?.aborted\n\t\t\t\t\t\t? \"skipped-abort\"\n\t\t\t\t\t\t: effectiveLaneWidth === 0\n\t\t\t\t\t\t\t? \"admission-deferred\"\n\t\t\t\t\t\t\t: \"blocked-dependency\",\n\t\t\t\t});\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 || outcomes.some((outcome) => outcome.status === \"unsettled\")) {\n\t\t\t\t\toutcomes.push({ laneId, status: input.signal?.aborted ? \"skipped-abort\" : \"blocked-dependency\" });\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\tblocked: () => outcomes.some((outcome) => outcome.status === \"unsettled\"),\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; readonly blocked: () => boolean },\n): Promise<LaneOutcome> {\n\tlet releasePermit: (() => void) | undefined;\n\tlet retained = false;\n\tif (input.heavyLaneIds?.has(laneId)) {\n\t\tif (\n\t\t\tinput.decision.action === \"defer-heavy\" ||\n\t\t\tinput.permitPool.snapshot().capacity === 0 ||\n\t\t\tinput.permitPool.snapshot().activeWeight >= input.decision.maxHeavyProcesses\n\t\t) {\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 || gauge.blocked()) {\n\t\treleasePermit?.();\n\t\treturn { laneId, status: input.signal?.aborted ? \"skipped-abort\" : \"blocked-dependency\" };\n\t}\n\tgauge.enter();\n\ttry {\n\t\tconst result = await 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\tif (result?.status === \"unsettled\") {\n\t\t\tretained = true;\n\t\t\t// A rejected settlement cannot prove termination; retain the reservation.\n\t\t\tvoid result.settlement.then(\n\t\t\t\t() => {\n\t\t\t\t\tgauge.exit();\n\t\t\t\t\treleasePermit?.();\n\t\t\t\t},\n\t\t\t\t() => {},\n\t\t\t);\n\t\t\treturn { laneId, status: \"unsettled\" };\n\t\t}\n\t\treturn { laneId, status: input.signal?.aborted ? \"cancelled\" : (result?.status ?? \"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\tif (!retained) {\n\t\t\tgauge.exit();\n\t\t\treleasePermit?.();\n\t\t}\n\t}\n}\n"]}