@deksden-com/dd-flow-cli 0.9.0-beta.14 → 0.9.0-beta.140

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 (493) hide show
  1. package/CHANGELOG.md +733 -0
  2. package/README.md +143 -1
  3. package/dist/build-info.json +8 -8
  4. package/dist/cli/argument-values.js +38 -0
  5. package/dist/cli/command-inputs.js +376 -0
  6. package/dist/cli/command-metadata.js +197 -0
  7. package/dist/cli/command-option-descriptions.js +191 -0
  8. package/dist/cli/completion-cli.js +70 -0
  9. package/dist/cli/completion.js +57 -0
  10. package/dist/cli/error-output.js +52 -0
  11. package/dist/cli/executor-onboarding.js +76 -0
  12. package/dist/cli/flows.js +29 -0
  13. package/dist/cli/harnesses.js +84 -0
  14. package/dist/cli/help.js +328 -18
  15. package/dist/cli/hook-ingress.js +78 -0
  16. package/dist/cli/input-preparation.js +165 -0
  17. package/dist/cli/memory-flow-admission.js +44 -0
  18. package/dist/cli/memory-init-invocation.js +78 -0
  19. package/dist/cli/memory-init.js +240 -0
  20. package/dist/cli/memory-storage-admission.js +53 -0
  21. package/dist/cli/models.js +39 -0
  22. package/dist/cli/native-hook-ingress.js +80 -0
  23. package/dist/cli/operator-profiles.js +61 -0
  24. package/dist/cli/output-options.js +35 -0
  25. package/dist/cli/response-file.js +71 -0
  26. package/dist/cli/run-cli.js +1689 -545
  27. package/dist/cli/runtime-maintenance-diagnostics.js +46 -0
  28. package/dist/cli/runtime-maintenance-ingress.js +52 -0
  29. package/dist/cli/runtime-process.js +153 -0
  30. package/dist/cli/setup.js +149 -0
  31. package/dist/cli.js +71 -2
  32. package/dist/domain/stage-catalog.js +1 -1
  33. package/dist/domain/stage-handler-identities.js +20 -0
  34. package/dist/harness-runtime/DD-ZCODE.md +112 -0
  35. package/dist/harness-runtime/OPENCODE-LICENSE.txt +24 -0
  36. package/dist/harness-runtime/bin/dd-agy.mjs +56 -0
  37. package/dist/harness-runtime/bin/dd-codex.mjs +27 -0
  38. package/dist/harness-runtime/bin/dd-droid.mjs +34 -0
  39. package/dist/harness-runtime/bin/dd-grok.mjs +40 -0
  40. package/dist/harness-runtime/bin/dd-opencode.mjs +22 -0
  41. package/dist/harness-runtime/bin/dd-zcode.mjs +87 -0
  42. package/dist/harness-runtime/lib/adapter-timeouts.d.mts +5 -0
  43. package/dist/harness-runtime/lib/adapter-timeouts.mjs +34 -0
  44. package/dist/harness-runtime/lib/agy-hooks.d.mts +1 -0
  45. package/dist/harness-runtime/lib/agy-hooks.mjs +9 -0
  46. package/dist/harness-runtime/lib/daemon-observation.d.mts +2 -0
  47. package/dist/harness-runtime/lib/daemon-observation.mjs +163 -0
  48. package/dist/harness-runtime/lib/daemon-operations.d.mts +1 -0
  49. package/dist/harness-runtime/lib/daemon-operations.mjs +371 -0
  50. package/dist/harness-runtime/lib/dd-agy-daemon.d.mts +6 -0
  51. package/dist/harness-runtime/lib/dd-agy-daemon.mjs +724 -0
  52. package/dist/harness-runtime/lib/dd-agy.mjs +70 -0
  53. package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
  54. package/dist/harness-runtime/lib/dd-codex-daemon.mjs +196 -0
  55. package/dist/harness-runtime/lib/dd-codex.d.mts +1 -0
  56. package/dist/harness-runtime/lib/dd-codex.mjs +817 -0
  57. package/dist/harness-runtime/lib/dd-droid-daemon.mjs +129 -0
  58. package/dist/harness-runtime/lib/dd-droid.mjs +584 -0
  59. package/dist/harness-runtime/lib/dd-grok-daemon.mjs +498 -0
  60. package/dist/harness-runtime/lib/dd-grok.mjs +177 -0
  61. package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +507 -0
  62. package/dist/harness-runtime/lib/dd-opencode.mjs +354 -0
  63. package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +750 -0
  64. package/dist/harness-runtime/lib/dd-zcode.mjs +1145 -0
  65. package/dist/harness-runtime/lib/delegation-instructions.d.mts +14 -0
  66. package/dist/harness-runtime/lib/delegation-instructions.mjs +157 -0
  67. package/dist/harness-runtime/lib/dispatch-fence.mjs +14 -0
  68. package/dist/harness-runtime/lib/driver-recovery.mjs +139 -0
  69. package/dist/harness-runtime/lib/droid-observation.mjs +66 -0
  70. package/dist/harness-runtime/lib/lease-renewal.d.mts +8 -0
  71. package/dist/harness-runtime/lib/lease-renewal.mjs +37 -0
  72. package/dist/harness-runtime/lib/lifecycle-command.d.mts +20 -0
  73. package/dist/harness-runtime/lib/lifecycle-command.mjs +273 -0
  74. package/dist/harness-runtime/lib/lifecycle-contract.d.mts +10 -0
  75. package/dist/harness-runtime/lib/lifecycle-contract.mjs +19 -0
  76. package/dist/harness-runtime/lib/managed-daemon.d.mts +13 -0
  77. package/dist/harness-runtime/lib/managed-daemon.mjs +654 -0
  78. package/dist/harness-runtime/lib/managed-flow-env.mjs +15 -0
  79. package/dist/harness-runtime/lib/model-observations.mjs +101 -0
  80. package/dist/harness-runtime/lib/native-children.d.mts +16 -0
  81. package/dist/harness-runtime/lib/native-children.mjs +113 -0
  82. package/dist/harness-runtime/lib/native-hook-command.d.mts +7 -0
  83. package/dist/harness-runtime/lib/native-hook-command.mjs +251 -0
  84. package/dist/harness-runtime/lib/native-request-proof.d.mts +2 -0
  85. package/dist/harness-runtime/lib/native-request-proof.mjs +65 -0
  86. package/dist/harness-runtime/lib/native-streams.mjs +45 -0
  87. package/dist/harness-runtime/lib/observation-clock.d.mts +18 -0
  88. package/dist/harness-runtime/lib/observation-clock.mjs +92 -0
  89. package/dist/harness-runtime/lib/opencode-plugin.mjs +85 -0
  90. package/dist/harness-runtime/lib/operation-context.mjs +5 -0
  91. package/dist/harness-runtime/lib/operation-errors.d.mts +14 -0
  92. package/dist/harness-runtime/lib/operation-errors.mjs +116 -0
  93. package/dist/harness-runtime/lib/operation-progress.d.mts +7 -0
  94. package/dist/harness-runtime/lib/operation-progress.mjs +114 -0
  95. package/dist/harness-runtime/lib/process-json.d.mts +5 -0
  96. package/dist/harness-runtime/lib/process-json.mjs +201 -0
  97. package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
  98. package/dist/harness-runtime/lib/provider-overload-policy.d.mts +13 -0
  99. package/dist/harness-runtime/lib/provider-overload-policy.mjs +138 -0
  100. package/dist/harness-runtime/lib/runner-events.mjs +200 -0
  101. package/dist/harness-runtime/lib/runner-lock.mjs +42 -0
  102. package/dist/harness-runtime/lib/session-settlement.mjs +30 -0
  103. package/dist/harness-runtime/lib/setup-progress.d.mts +9 -0
  104. package/dist/harness-runtime/lib/setup-progress.mjs +51 -0
  105. package/dist/harness-runtime/lib/tool-observations.d.mts +21 -0
  106. package/dist/harness-runtime/lib/tool-observations.mjs +228 -0
  107. package/dist/harness-runtime/lib/zcode-usage.mjs +55 -0
  108. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-1y4a697q.js +81 -0
  109. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-2ny4vxct.js +92 -0
  110. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-6mptkzbc.js +0 -0
  111. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-7x67cf1q.js +52 -0
  112. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9rqn6x4v.js +16 -0
  113. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9vdz0zwn.js +1661 -0
  114. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ecs5ftka.js +1466 -0
  115. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-frbwqjmf.js +145 -0
  116. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gne7h7mq.js +50 -0
  117. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gx9wnkzr.js +105 -0
  118. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-m2eywc8w.js +0 -0
  119. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-nwcpakzx.js +12 -0
  120. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-q4g3afnw.js +623 -0
  121. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-qav6mfdz.js +0 -0
  122. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-sxt88q69.js +12 -0
  123. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-t1xa31vm.js +39 -0
  124. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ve4aws36.js +103 -0
  125. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vmytr4q8.js +4 -0
  126. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vt9jbch8.js +36 -0
  127. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vyas3vgt.js +0 -0
  128. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-wjyeq25y.js +30 -0
  129. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-xgnj2w4v.js +20 -0
  130. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-y2etg5bs.js +9 -0
  131. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ydhz1zrh.js +238 -0
  132. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-z6qdwcsf.js +62 -0
  133. package/dist/harness-runtime/vendor/opencode-client/contract.d.ts +1 -0
  134. package/dist/harness-runtime/vendor/opencode-client/contract.js +12 -0
  135. package/dist/harness-runtime/vendor/opencode-client/effect/api/api.d.ts +2806 -0
  136. package/dist/harness-runtime/vendor/opencode-client/effect/api/api.js +0 -0
  137. package/dist/harness-runtime/vendor/opencode-client/effect/api.d.ts +4 -0
  138. package/dist/harness-runtime/vendor/opencode-client/effect/api.js +0 -0
  139. package/dist/harness-runtime/vendor/opencode-client/effect/client.d.ts +2530 -0
  140. package/dist/harness-runtime/vendor/opencode-client/effect/client.js +15 -0
  141. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.d.ts +7 -0
  142. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.js +6 -0
  143. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.d.ts +2529 -0
  144. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.js +8 -0
  145. package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.d.ts +2 -0
  146. package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.js +12 -0
  147. package/dist/harness-runtime/vendor/opencode-client/effect/index.d.ts +35 -0
  148. package/dist/harness-runtime/vendor/opencode-client/effect/index.js +75 -0
  149. package/dist/harness-runtime/vendor/opencode-client/effect/rpc.d.ts +20 -0
  150. package/dist/harness-runtime/vendor/opencode-client/effect/rpc.js +11 -0
  151. package/dist/harness-runtime/vendor/opencode-client/effect/service.d.ts +90 -0
  152. package/dist/harness-runtime/vendor/opencode-client/effect/service.js +187 -0
  153. package/dist/harness-runtime/vendor/opencode-client/package.json +84 -0
  154. package/dist/harness-runtime/vendor/opencode-client/promise/api.d.ts +21 -0
  155. package/dist/harness-runtime/vendor/opencode-client/promise/api.js +0 -0
  156. package/dist/harness-runtime/vendor/opencode-client/promise/client.d.ts +252 -0
  157. package/dist/harness-runtime/vendor/opencode-client/promise/client.js +14 -0
  158. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.d.ts +8 -0
  159. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.js +6 -0
  160. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.d.ts +259 -0
  161. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.js +7 -0
  162. package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.d.ts +3 -0
  163. package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.js +79 -0
  164. package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.d.ts +8932 -0
  165. package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.js +70 -0
  166. package/dist/harness-runtime/vendor/opencode-client/promise/index.d.ts +6 -0
  167. package/dist/harness-runtime/vendor/opencode-client/promise/index.js +83 -0
  168. package/dist/harness-runtime/vendor/opencode-client/promise/rpc.d.ts +32 -0
  169. package/dist/harness-runtime/vendor/opencode-client/promise/rpc.js +7 -0
  170. package/dist/harness-runtime/vendor/opencode-client/promise/service.d.ts +24 -0
  171. package/dist/harness-runtime/vendor/opencode-client/promise/service.js +187 -0
  172. package/dist/harness-runtime/vendor/opencode-client/pty-handoff.d.ts +12 -0
  173. package/dist/harness-runtime/vendor/opencode-client/pty-handoff.js +14 -0
  174. package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.d.ts +20 -0
  175. package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.js +14 -0
  176. package/dist/harness-runtime/vendor/opencode-client/service-contender.d.ts +37 -0
  177. package/dist/harness-runtime/vendor/opencode-client/service-contender.js +10 -0
  178. package/dist/harness-runtime/vendor/opencode-client/service-probe.d.ts +70 -0
  179. package/dist/harness-runtime/vendor/opencode-client/service-probe.js +16 -0
  180. package/dist/harness-runtime/vendor/opencode-client/service-timing.d.ts +12 -0
  181. package/dist/harness-runtime/vendor/opencode-client/service-timing.js +10 -0
  182. package/dist/harness-runtime/vendor/opencode-client/service-version.d.ts +2 -0
  183. package/dist/harness-runtime/vendor/opencode-client/service-version.js +6 -0
  184. package/dist/harness-runtime/vendor/opencode-client/service.d.ts +52 -0
  185. package/dist/harness-runtime/vendor/opencode-client/service.js +1 -0
  186. package/dist/harness-runtime/vendor/opencode-client/shared-events.d.ts +11 -0
  187. package/dist/harness-runtime/vendor/opencode-client/shared-events.js +8 -0
  188. package/dist/harness-runtime/vendor/opencode-client/solid/connection.d.ts +37 -0
  189. package/dist/harness-runtime/vendor/opencode-client/solid/connection.js +10 -0
  190. package/dist/harness-runtime/vendor/opencode-client/solid/data.d.ts +211 -0
  191. package/dist/harness-runtime/vendor/opencode-client/solid/data.js +18 -0
  192. package/dist/harness-runtime/vendor/opencode-client/solid/index.d.ts +3 -0
  193. package/dist/harness-runtime/vendor/opencode-client/solid/index.js +32 -0
  194. package/dist/harness-runtime/vendor/opencode-client/solid/pty.d.ts +22 -0
  195. package/dist/harness-runtime/vendor/opencode-client/solid/pty.js +8 -0
  196. package/dist/harness-runtime/vendor/opencode-plugin.mjs +3 -0
  197. package/dist/protocol/local-files.js +18 -9
  198. package/dist/runtime/context.js +8 -4
  199. package/dist/schemas/agent-profile.schema.json +1 -1
  200. package/dist/schemas/code-review-result.schema.json +1 -1
  201. package/dist/schemas/code-verification.schema.json +7 -5
  202. package/dist/schemas/code-work-batch.schema.json +2 -2
  203. package/dist/schemas/discovery-result.schema.json +184 -0
  204. package/dist/schemas/execution-bundle.schema.json +205 -0
  205. package/dist/schemas/execution-settings.schema.json +162 -0
  206. package/dist/schemas/flow-descriptor.schema.json +54 -0
  207. package/dist/schemas/flow-run.schema.json +28 -0
  208. package/dist/schemas/harness-catalog.schema.json +80 -0
  209. package/dist/schemas/mb-init-compose-result.schema.json +457 -0
  210. package/dist/schemas/mb-init-discover-result.schema.json +500 -0
  211. package/dist/schemas/mb-init-finish-result.schema.json +422 -0
  212. package/dist/schemas/mb-init-preflight-result.schema.json +367 -0
  213. package/dist/schemas/mb-init-review-result.schema.json +396 -0
  214. package/dist/schemas/mb-init-verify-result.schema.json +391 -0
  215. package/dist/schemas/memory-bootstrap-receipt-v2.schema.json +67 -0
  216. package/dist/schemas/memory-bootstrap-receipt.schema.json +354 -0
  217. package/dist/schemas/memory-composition-work-result.schema.json +38 -0
  218. package/dist/schemas/memory-repair-work-result.schema.json +49 -0
  219. package/dist/schemas/memory-review-correction-plan.schema.json +30 -0
  220. package/dist/schemas/memory-review-work-result.schema.json +43 -0
  221. package/dist/schemas/model-catalog.schema.json +153 -0
  222. package/dist/schemas/plan-review-decision.schema.json +3 -3
  223. package/dist/schemas/plan-review-result.schema.json +2 -2
  224. package/dist/schemas/profile-settings.schema.json +122 -0
  225. package/dist/schemas/release-manifest.schema.json +291 -0
  226. package/dist/schemas/run-control-receipt.schema.json +118 -0
  227. package/dist/schemas/run-control-request.schema.json +36 -0
  228. package/dist/schemas/run-engine-binding.schema.json +1 -1
  229. package/dist/schemas/vnext-protocol-plan.schema.json +2 -2
  230. package/dist/services/branch-context.js +3 -3
  231. package/dist/services/canon.js +11 -8
  232. package/dist/services/check-runner-evidence.js +168 -0
  233. package/dist/services/cleanup.js +133 -40
  234. package/dist/services/cli-operation-classifier.js +25 -62
  235. package/dist/services/cli-upgrade-allowlist.js +27 -0
  236. package/dist/services/code-checks.js +1156 -107
  237. package/dist/services/codex-hook-delivery.js +32 -0
  238. package/dist/services/command-context.js +80 -0
  239. package/dist/services/compatibility-preflight.js +13 -3
  240. package/dist/services/config.js +11 -8
  241. package/dist/services/continuation-outcome.js +20 -0
  242. package/dist/services/controller-child-capacity.js +13 -0
  243. package/dist/services/controller-child-observer.js +115 -0
  244. package/dist/services/controller-execution.js +88 -0
  245. package/dist/services/controller-fanout.js +202 -0
  246. package/dist/services/dashboard.js +143 -58
  247. package/dist/services/descriptor-coordinator-session.js +35 -0
  248. package/dist/services/descriptor-root-work.js +53 -0
  249. package/dist/services/descriptor-run-settlement.js +105 -0
  250. package/dist/services/descriptor-run-start.js +449 -0
  251. package/dist/services/descriptor-stage-coordinator.js +197 -0
  252. package/dist/services/descriptor-stage-settlement.js +222 -0
  253. package/dist/services/descriptor-stage-start.js +139 -0
  254. package/dist/services/discovery/agy-discovery.js +2 -0
  255. package/dist/services/discovery/codex-discovery.js +2 -0
  256. package/dist/services/discovery/droid-discovery.js +2 -0
  257. package/dist/services/discovery/grok-discovery.js +2 -0
  258. package/dist/services/discovery/opencode-discovery.js +2 -0
  259. package/dist/services/discovery/static.js +31 -0
  260. package/dist/services/discovery/zcode-discovery.js +2 -0
  261. package/dist/services/engines.js +294 -153
  262. package/dist/services/eval-snapshots.js +1215 -116
  263. package/dist/services/execution-bundles.js +263 -0
  264. package/dist/services/execution-policy.js +185 -0
  265. package/dist/services/executor-onboarding.js +56 -0
  266. package/dist/services/external-work-launch.js +308 -0
  267. package/dist/services/flow-catalog.js +21 -0
  268. package/dist/services/flow-descriptors.js +82 -0
  269. package/dist/services/frozen-engine.js +250 -0
  270. package/dist/services/generic-execution.js +164 -0
  271. package/dist/services/harness-adapter.js +355 -36
  272. package/dist/services/harness-config.js +2 -0
  273. package/dist/services/harness-discovery.js +29 -0
  274. package/dist/services/harness-doctor.js +75 -0
  275. package/dist/services/harness-probe-effects.js +51 -0
  276. package/dist/services/harness-version-probe.js +100 -0
  277. package/dist/services/hooks.js +771 -325
  278. package/dist/services/hybrid-contracts.js +483 -0
  279. package/dist/services/ids.js +29 -1
  280. package/dist/services/installation-lock.js +95 -0
  281. package/dist/services/installed-flow-source.js +183 -0
  282. package/dist/services/lanes.js +78 -65
  283. package/dist/services/lifecycle-command.js +1 -288
  284. package/dist/services/lifecycle-contract.js +1 -0
  285. package/dist/services/lifecycle-invocations.js +2007 -0
  286. package/dist/services/managed-daemon-binding.js +38 -0
  287. package/dist/services/managed-lease-monitor.js +133 -0
  288. package/dist/services/managed-processes.js +475 -55
  289. package/dist/services/memory-accepted-composition.js +77 -0
  290. package/dist/services/memory-baseline-preservation.js +65 -0
  291. package/dist/services/memory-completed-correction-wave.js +320 -0
  292. package/dist/services/memory-compose-handler.js +258 -0
  293. package/dist/services/memory-composition-acceptance.js +199 -0
  294. package/dist/services/memory-composition-graph.js +134 -0
  295. package/dist/services/memory-composition-registration.js +146 -0
  296. package/dist/services/memory-composition-work-custody.js +151 -0
  297. package/dist/services/memory-composition-work-results.js +96 -0
  298. package/dist/services/memory-corrected-review-acceptance.js +129 -0
  299. package/dist/services/memory-corrected-review-batch.js +30 -0
  300. package/dist/services/memory-corrected-review-findings.js +106 -0
  301. package/dist/services/memory-corrected-review-inputs.js +210 -0
  302. package/dist/services/memory-corrected-review-projection.js +78 -0
  303. package/dist/services/memory-corrected-review-receipt.js +170 -0
  304. package/dist/services/memory-corrected-review-registration.js +191 -0
  305. package/dist/services/memory-correction-check-receipt.js +219 -0
  306. package/dist/services/memory-correction-input-owner.js +6 -0
  307. package/dist/services/memory-discover-handler.js +104 -0
  308. package/dist/services/memory-finish-evidence.js +435 -0
  309. package/dist/services/memory-finish-handler.js +215 -0
  310. package/dist/services/memory-finish-integration.js +280 -0
  311. package/dist/services/memory-finish-readiness.js +122 -0
  312. package/dist/services/memory-git-integration.js +151 -0
  313. package/dist/services/memory-historical-corrected-review.js +206 -0
  314. package/dist/services/memory-historical-original-review.js +130 -0
  315. package/dist/services/memory-historical-repair-registration.js +99 -0
  316. package/dist/services/memory-historical-second-review.js +128 -0
  317. package/dist/services/memory-init-bootstrap-readiness.js +210 -0
  318. package/dist/services/memory-init-contexts.js +89 -0
  319. package/dist/services/memory-init-controller-start.js +84 -0
  320. package/dist/services/memory-init-engine-release.js +223 -0
  321. package/dist/services/memory-init-git-policy.js +136 -0
  322. package/dist/services/memory-init-lint-capability.js +185 -0
  323. package/dist/services/memory-init-native-capacity.js +296 -0
  324. package/dist/services/memory-init-preflight.js +330 -0
  325. package/dist/services/memory-init-project-bootstrap.js +599 -0
  326. package/dist/services/memory-init-request-candidate.js +157 -0
  327. package/dist/services/memory-init-request-input.js +66 -0
  328. package/dist/services/memory-init-resource-storage.js +125 -0
  329. package/dist/services/memory-init-run-allocation.js +81 -0
  330. package/dist/services/memory-init-run-bundle.js +98 -0
  331. package/dist/services/memory-init-start.js +181 -0
  332. package/dist/services/memory-init-startup-custody.js +80 -0
  333. package/dist/services/memory-init-startup-input.js +240 -0
  334. package/dist/services/memory-init-storage.js +81 -0
  335. package/dist/services/memory-init-stores.js +359 -0
  336. package/dist/services/memory-init-worktrunk-capability.js +178 -0
  337. package/dist/services/memory-layer-publication.js +611 -0
  338. package/dist/services/memory-layer.js +156 -0
  339. package/dist/services/memory-lint-config.js +85 -0
  340. package/dist/services/memory-native-review-copy.js +165 -0
  341. package/dist/services/memory-normal-flow-entry.js +75 -0
  342. package/dist/services/memory-ordinary-defaults.js +112 -0
  343. package/dist/services/memory-preflight-bootstrap-custody.js +115 -0
  344. package/dist/services/memory-preflight-handler.js +259 -0
  345. package/dist/services/memory-repair-current-view.js +299 -0
  346. package/dist/services/memory-repair-document-delta.js +451 -0
  347. package/dist/services/memory-repair-history.js +491 -0
  348. package/dist/services/memory-repair-physical-binding.js +312 -0
  349. package/dist/services/memory-repair-publication-files.js +152 -0
  350. package/dist/services/memory-repair-publication-intent.js +27 -0
  351. package/dist/services/memory-repair-publication-plan.js +160 -0
  352. package/dist/services/memory-repair-publication-source.js +225 -0
  353. package/dist/services/memory-repair-publication.js +794 -0
  354. package/dist/services/memory-repair-registration.js +239 -0
  355. package/dist/services/memory-repair-work-baseline.js +413 -0
  356. package/dist/services/memory-repair-work-completion.js +97 -0
  357. package/dist/services/memory-repair-work-custody.js +73 -0
  358. package/dist/services/memory-repair-work-results.js +89 -0
  359. package/dist/services/memory-repair-workspace.js +252 -0
  360. package/dist/services/memory-repair-writer-copy.js +735 -0
  361. package/dist/services/memory-result-binding.js +173 -0
  362. package/dist/services/memory-retained-review-authority.js +129 -0
  363. package/dist/services/memory-retained-review-results.js +70 -0
  364. package/dist/services/memory-retained-second-baseline.js +102 -0
  365. package/dist/services/memory-retained-second-repairs.js +156 -0
  366. package/dist/services/memory-review-acceptance.js +214 -0
  367. package/dist/services/memory-review-correction-plan.js +171 -0
  368. package/dist/services/memory-review-handler.js +585 -0
  369. package/dist/services/memory-review-inputs.js +200 -0
  370. package/dist/services/memory-review-receipt.js +180 -0
  371. package/dist/services/memory-review-registration.js +108 -0
  372. package/dist/services/memory-review-work-custody.js +124 -0
  373. package/dist/services/memory-review-work-input.js +72 -0
  374. package/dist/services/memory-review-work-results.js +73 -0
  375. package/dist/services/memory-second-corrected-review-inputs.js +165 -0
  376. package/dist/services/memory-second-correction-plan.js +76 -0
  377. package/dist/services/memory-second-repair-baseline.js +91 -0
  378. package/dist/services/memory-second-repair-launch.js +614 -0
  379. package/dist/services/memory-second-repair-registration.js +88 -0
  380. package/dist/services/memory-second-repair-source.js +187 -0
  381. package/dist/services/memory-second-review-acceptance.js +115 -0
  382. package/dist/services/memory-source-observations.js +117 -0
  383. package/dist/services/memory-stage-dispatch.js +28 -0
  384. package/dist/services/memory-stage-results.js +199 -0
  385. package/dist/services/memory-verification-acceptance.js +135 -0
  386. package/dist/services/memory-verification-checks.js +252 -0
  387. package/dist/services/memory-verification-receipt.js +159 -0
  388. package/dist/services/memory-verify-handler.js +135 -0
  389. package/dist/services/memory-work-result-preparation.js +63 -0
  390. package/dist/services/merge-queue.js +228 -149
  391. package/dist/services/merge-server.js +161 -35
  392. package/dist/services/migrations.js +37 -16
  393. package/dist/services/model-catalog.js +109 -0
  394. package/dist/services/model-discovery.js +46 -0
  395. package/dist/services/native-daemon-history.js +49 -0
  396. package/dist/services/native-session-control.js +39 -0
  397. package/dist/services/operator-profiles.js +236 -0
  398. package/dist/services/plan-runtime.js +26 -11
  399. package/dist/services/plans.js +28 -25
  400. package/dist/services/projects.js +37 -17
  401. package/dist/services/prompts.js +28 -17
  402. package/dist/services/protocols.js +163 -87
  403. package/dist/services/provider-capacity.js +385 -0
  404. package/dist/services/recovery-observation-budget.js +121 -0
  405. package/dist/services/recovery-snapshot-database.js +560 -0
  406. package/dist/services/release-archive-producer.js +52 -0
  407. package/dist/services/release-archive.js +143 -0
  408. package/dist/services/release-cache.js +142 -0
  409. package/dist/services/release-compatibility.js +18 -0
  410. package/dist/services/release-download.js +67 -0
  411. package/dist/services/release-envelope.js +104 -0
  412. package/dist/services/release-manifest.js +45 -0
  413. package/dist/services/release-payload.js +114 -0
  414. package/dist/services/release-producer.js +63 -0
  415. package/dist/services/release-provenance.js +160 -0
  416. package/dist/services/release-registry.js +66 -0
  417. package/dist/services/release-source.js +194 -0
  418. package/dist/services/repair-intents.js +42 -0
  419. package/dist/services/review-copy.js +165 -0
  420. package/dist/services/review-deferral.js +23 -0
  421. package/dist/services/run-control-receipt.js +56 -0
  422. package/dist/services/run-control-worker.js +411 -0
  423. package/dist/services/run-control.js +1056 -0
  424. package/dist/services/run-controller-adapter.js +399 -0
  425. package/dist/services/run-controller-capture.js +186 -0
  426. package/dist/services/run-controller-process.js +208 -0
  427. package/dist/services/run-controller-recovery.js +289 -0
  428. package/dist/services/run-controller-state.js +97 -0
  429. package/dist/services/run-controller.js +1205 -0
  430. package/dist/services/run-engine-bindings.js +1 -1
  431. package/dist/services/run-fork.js +147 -0
  432. package/dist/services/run-health.js +149 -0
  433. package/dist/services/run-observations.js +152 -0
  434. package/dist/services/run-projection.js +22 -64
  435. package/dist/services/run-recovery-guard.js +77 -0
  436. package/dist/services/run-recovery-runtime.js +112 -0
  437. package/dist/services/run-recovery.js +489 -0
  438. package/dist/services/run-start-custody.js +177 -0
  439. package/dist/services/runs.js +1019 -247
  440. package/dist/services/runtime-budget.js +430 -0
  441. package/dist/services/runtime-command.js +43 -0
  442. package/dist/services/runtime-scope-capture.js +52 -0
  443. package/dist/services/runtime-scope-control.js +468 -0
  444. package/dist/services/runtime-scope-resume.js +599 -0
  445. package/dist/services/runtime-scope-stop.js +210 -0
  446. package/dist/services/runtime-scope-worker.js +287 -0
  447. package/dist/services/runtime-service.js +96 -0
  448. package/dist/services/schema-validation.js +10 -8
  449. package/dist/services/scope-writer-cleanup.js +27 -0
  450. package/dist/services/sessions.js +35 -69
  451. package/dist/services/setup.js +377 -0
  452. package/dist/services/stage-blocker.js +17 -7
  453. package/dist/services/stage-context.js +193 -19
  454. package/dist/services/stage-handlers.js +74 -0
  455. package/dist/services/stage-lifecycle.js +100 -108
  456. package/dist/services/stage-pause.js +89 -75
  457. package/dist/services/stage-report-renderer.js +234 -6
  458. package/dist/services/stage-work-graph.js +52 -0
  459. package/dist/services/tooling.js +32 -23
  460. package/dist/services/usage.js +49 -101
  461. package/dist/services/verification-matrix.js +374 -0
  462. package/dist/services/vnext-code-review.js +223 -88
  463. package/dist/services/vnext-code.js +327 -226
  464. package/dist/services/vnext-contracts.js +10 -1
  465. package/dist/services/vnext-execution-profile.js +80 -5
  466. package/dist/services/vnext-fanout.js +311 -19
  467. package/dist/services/vnext-merge.js +332 -111
  468. package/dist/services/vnext-plan-review.js +279 -127
  469. package/dist/services/vnext-plan.js +229 -104
  470. package/dist/services/vnext-protocolize.js +411 -86
  471. package/dist/services/vnext-specify.js +119 -81
  472. package/dist/services/vnext-workspace-policy.js +7 -2
  473. package/dist/services/work-registry.js +1627 -199
  474. package/dist/services/workspace-bootstrap.js +52 -15
  475. package/dist/services/workspace-files.js +85 -0
  476. package/dist/services/worktrees.js +98 -44
  477. package/dist/shared/entity-references.js +8 -0
  478. package/dist/shared/errors.js +17 -0
  479. package/dist/storage/agent-output.js +26 -0
  480. package/dist/storage/database.js +891 -39
  481. package/dist/storage/foreign-key-transaction.js +39 -0
  482. package/dist/storage/immutable-input.js +45 -0
  483. package/dist/storage/import-guard.js +102 -0
  484. package/dist/storage/native-cleanup.js +52 -0
  485. package/dist/storage/paths.js +17 -4
  486. package/dist/storage/projection-lock.js +73 -0
  487. package/dist/storage/work-references.js +23 -0
  488. package/dist/storage/writer-contract.js +89 -0
  489. package/dist/storage/writer-migration.js +85 -0
  490. package/package.json +43 -13
  491. package/tools/create-canon-release.mjs +33 -0
  492. package/tools/produce-hybrid-release.mjs +41 -0
  493. package/tools/repair-paused-run-status.mjs +84 -0
@@ -94,8 +94,11 @@ export function validateSchema(options) {
94
94
  throw new AppError("usage", "--schema must be a schema name such as mb-upgrade-review-data", 2);
95
95
  }
96
96
  const filePath = path.resolve(options.file);
97
- const data = readJson(filePath, "input file");
98
- const schemaResolution = resolveSchema(options);
97
+ const data = Object.hasOwn(options, "data") ? options.data : readJson(filePath, "input file");
98
+ // `data` may have come from stdin or from the prepare phase. Do not reopen
99
+ // `options.file` merely to infer the RUN-bound schema: apart from being a
100
+ // second read of caller input, `<stdin>` is not a filesystem path at all.
101
+ const schemaResolution = resolveSchema(options, data);
99
102
  const schema = readJson(schemaResolution.path, "schema");
100
103
  // Ajv's default export only carries the draft-07 meta-schema. New stage
101
104
  // contracts may deliberately use draft 2020-12, so choose the validator
@@ -133,8 +136,8 @@ export function validateSchema(options) {
133
136
  errors: []
134
137
  };
135
138
  }
136
- function resolveSchema(options) {
137
- const bound = resolveRunBoundSchema(options);
139
+ function resolveSchema(options, data) {
140
+ const bound = resolveRunBoundSchema(options, data);
138
141
  if (bound)
139
142
  return bound;
140
143
  const fileNames = [`${options.schemaName}.schema.json`];
@@ -180,10 +183,9 @@ const historicalSchemaRegistry = {
180
183
  checksum: "d9889af7f2c0afb47e2abab330aea3e1750879d0738d87d38d2d20e3fe0403f3"
181
184
  }]
182
185
  };
183
- function resolveRunBoundSchema(options) {
186
+ function resolveRunBoundSchema(options, data) {
184
187
  if (!options.runRoot || options.schemaDir)
185
188
  return null;
186
- const data = readJson(path.resolve(options.file), "input file");
187
189
  const root = asRecord(data);
188
190
  const run = root ? asRecord(objectValue(root, "run")) : undefined;
189
191
  const inferredRunId = run ? objectValue(run, "run_id") : objectValue(root ?? {}, "run_id");
@@ -268,13 +270,13 @@ function readJson(file, label) {
268
270
  text = fs.readFileSync(file, "utf8");
269
271
  }
270
272
  catch (error) {
271
- throw new AppError("validation", `Cannot read ${label}: ${file}`, 2, { cause: String(error) });
273
+ throw new AppError(label === "input file" ? "validation" : "schema_registry_invalid", `Cannot read ${label}: ${file}`, label === "input file" ? 2 : 1, { cause: String(error) });
272
274
  }
273
275
  try {
274
276
  return JSON.parse(text);
275
277
  }
276
278
  catch (error) {
277
- throw new AppError("invalid_json", `Invalid JSON in ${label}: ${file}`, 2, { cause: String(error) });
279
+ throw new AppError(label === "input file" ? "invalid_json" : "schema_registry_invalid", `Invalid JSON in ${label}: ${file}`, label === "input file" ? 2 : 1, { cause: String(error) });
278
280
  }
279
281
  }
280
282
  function formatAjvError(error) {
@@ -0,0 +1,27 @@
1
+ import { errorRecord } from "../shared/errors.js";
2
+ /** Cleanup is secondary to the arbiter's known commit outcome. Always visit
3
+ * every connection, including after a failed rollback or close. */
4
+ export function cleanupScopeWriters(targets) {
5
+ const errors = [];
6
+ for (const target of targets) {
7
+ let failed = false;
8
+ if (target.rollback) {
9
+ try {
10
+ target.db.exec("ROLLBACK");
11
+ }
12
+ catch (error) {
13
+ failed = true;
14
+ errors.push({ target: target.name, phase: "rollback", message: String(error), error: errorRecord(error) });
15
+ }
16
+ }
17
+ if (target.close === "always" || failed) {
18
+ try {
19
+ target.db.close?.();
20
+ }
21
+ catch (error) {
22
+ errors.push({ target: target.name, phase: "close", message: String(error), error: errorRecord(error) });
23
+ }
24
+ }
25
+ }
26
+ return errors;
27
+ }
@@ -1,9 +1,9 @@
1
1
  import crypto from "node:crypto";
2
- import fs from "node:fs";
3
2
  import { continuationPolicies, flowKinds } from "../domain/contracts.js";
4
3
  import { AppError } from "../shared/errors.js";
5
- import { commandOption, commandPosition, parseLifecycleCommand } from "./lifecycle-command.js";
4
+ import { hookSessionIdentity } from "./hooks.js";
6
5
  import { parseJsonObject } from "../shared/json.js";
6
+ import { prepareTextFile } from "../cli/input-preparation.js";
7
7
  import { resolveProjectRoot } from "../storage/paths.js";
8
8
  import { appendAudit } from "./audit.js";
9
9
  import { cancelLaneWaitersForWorker } from "./lanes.js";
@@ -30,8 +30,8 @@ export function recordFlowSessionObservation(context, input) {
30
30
  const open = context.db.get(`SELECT id, run_id, protocol_id FROM flow_session_segments
31
31
  WHERE project_id = ? AND session_id = ? AND ended_at IS NULL ORDER BY started_at DESC, id DESC LIMIT 1`, [input.projectId, input.sessionId]);
32
32
  const now = context.now();
33
- const runId = input.runId ?? null;
34
- const protocolId = input.protocolId ?? null;
33
+ const runId = input.runId === undefined ? open?.run_id ?? null : input.runId;
34
+ const protocolId = input.protocolId === undefined ? open?.protocol_id ?? null : input.protocolId;
35
35
  if (open && open.run_id === runId && open.protocol_id === protocolId) {
36
36
  return { changed: false, segment_id: open.id };
37
37
  }
@@ -42,27 +42,6 @@ export function recordFlowSessionObservation(context, input) {
42
42
  VALUES (?, ?, ?, ?, ?, NULL, ?, ?, ?)`, [input.projectId, input.sessionId, runId, protocolId, now, input.cwd ?? null, input.toolName ?? null, input.eventKey]);
43
43
  return { changed: true, segment_id: Number(result.lastInsertRowid) };
44
44
  }
45
- export function bindObservedFlowSession(context, project, payload, sessionId) {
46
- if (resolveProjectRoot(payload.project_root) !== project.root) {
47
- throw new AppError("project_mismatch", "Observed session payload project_root does not match hook cwd project", 1, {
48
- expected: project.root,
49
- actual: payload.project_root
50
- });
51
- }
52
- const session = upsertFlowSession(context, project, payload, sessionId);
53
- if (session.session_kind !== "orchestrator") {
54
- const now = context.now();
55
- for (const unit of payload.coverage_units ?? []) {
56
- if (!unit.job_id)
57
- continue;
58
- context.db.run(`UPDATE flow_jobs SET status = 'running', worker_session_id = ?, attempts = attempts + 1, started_at = COALESCE(started_at, ?), updated_at = ?
59
- WHERE project_id = ? AND run_id = ? AND job_id = ? AND status IN ('pending', 'failed')`, [session.session_id, now, now, project.id, session.run_id ?? "", unit.job_id]);
60
- }
61
- }
62
- if (session.run_id)
63
- refreshRunSessionProjection(context, project.id, session.run_id);
64
- return session;
65
- }
66
45
  export function registerFlowJob(context, input) {
67
46
  const jobId = input.jobId ?? `JOB-${input.runId}-${input.planItemId}`.replace(/[^A-Za-z0-9_-]+/g, "-");
68
47
  const now = context.now();
@@ -76,9 +55,33 @@ export function flowJobsForRun(context, projectId, runId) {
76
55
  return context.db.all("SELECT * FROM flow_jobs WHERE project_id = ? AND run_id = ? ORDER BY plan_item_id", [projectId, runId]);
77
56
  }
78
57
  export function registerFlowSession(context, input) {
79
- const payload = decodeFlowSessionPayload(input);
58
+ const payload = input.preparedPayload ?? prepareFlowSessionPayload(input);
80
59
  const project = requireProjectByRoot(context, resolveProjectRoot(payload.project_root));
81
- const session = upsertFlowSession(context, project, payload, input.sessionId);
60
+ if (payload.run_id && !context.db.get("SELECT id FROM runs WHERE project_id = ? AND id = ?", [project.id, payload.run_id])) {
61
+ throw new AppError("not_found", `Run is not registered: ${payload.run_id}`, 1, { run_id: payload.run_id, effect: "no_effect" });
62
+ }
63
+ const identity = input.hookEventId ? hookSessionIdentity(context, project.id, input.hookEventId) : undefined;
64
+ if (!identity && input.sessionId && payload.provider_session_id && payload.provider_session_id !== input.sessionId) {
65
+ throw new AppError("validation", "--session-id conflicts with payload provider_session_id", 2);
66
+ }
67
+ const session = upsertFlowSession(context, project, identity ? {
68
+ ...payload, harness: identity.harness, provider_session_id: identity.providerSessionId,
69
+ parent_session_id: identity.parentSessionId, agent_id: identity.agentId,
70
+ provider: identity.provider, model: identity.model, reasoning: identity.reasoning,
71
+ mode: identity.mode, agent_type: identity.agentType
72
+ } : { ...payload, ...(input.sessionId ? { provider_session_id: input.sessionId } : {}) }, identity?.sessionId ?? input.sessionId);
73
+ recordFlowSessionObservation(context, { projectId: project.id, sessionId: session.session_id,
74
+ runId: session.run_id, protocolId: session.protocol_id, cwd: session.cwd,
75
+ eventKey: `session-register-${crypto.randomUUID()}` });
76
+ // Logical Work/job binding belongs to the successful CLI registration, never
77
+ // to observation of model-supplied payload by a pre-tool hook.
78
+ if (session.session_kind !== "orchestrator")
79
+ for (const unit of payload.coverage_units ?? []) {
80
+ if (!unit.job_id)
81
+ continue;
82
+ context.db.run(`UPDATE flow_jobs SET status = 'running', worker_session_id = ?, attempts = attempts + 1, started_at = COALESCE(started_at, ?), updated_at = ?
83
+ WHERE project_id = ? AND run_id = ? AND job_id = ? AND status IN ('pending', 'failed')`, [session.session_id, context.now(), context.now(), project.id, session.run_id ?? "", unit.job_id]);
84
+ }
82
85
  if (session.run_id)
83
86
  appendFlowRunTimelineEvent(context, project.id, session.run_id, { type: "session_bound", session_id: session.session_id });
84
87
  if (session.run_id)
@@ -195,52 +198,15 @@ export function updateFlowSessionContinuation(context, projectId, sessionId, act
195
198
  refreshRunSessionProjection(context, projectId, session.run_id);
196
199
  return nextCount;
197
200
  }
198
- export function flowSessionPayloadFromRegisterCommand(command) {
199
- const invocation = typeof command === "string" ? invocationFromCommand(command) : command;
200
- if (!invocation)
201
- return undefined;
202
- if (invocation.operation === "stage_start")
203
- return flowSessionPayloadFromStageStartCommand(invocation);
204
- if (invocation.operation !== "session_register")
205
- return undefined;
206
- const payloadBase64 = commandOption(invocation, "payload-base64");
207
- const payloadJson = commandOption(invocation, "payload-json");
208
- const payloadFile = commandOption(invocation, "payload-file");
209
- if (!payloadBase64 && !payloadJson && !payloadFile) {
210
- return undefined;
211
- }
212
- return decodeFlowSessionPayload({ payloadBase64, payloadJson, payloadFile });
213
- }
214
- function flowSessionPayloadFromStageStartCommand(invocation) {
215
- const projectRoot = commandOption(invocation, "project-root");
216
- const stage = commandOption(invocation, "stage");
217
- const run = commandPosition(invocation, 0);
218
- if (!projectRoot || !stage || !run || run.startsWith("--"))
219
- return undefined;
220
- return {
221
- project_root: projectRoot,
222
- flow_kind: ["code", "implementation", "readiness", "merge"].includes(stage) ? "implementation" : "planning",
223
- run_id: run,
224
- protocol_id: null,
225
- worker_id: null,
226
- workspace_path: null,
227
- continuation_policy: "go_router",
228
- session_kind: "orchestrator",
229
- current_stage: stage,
230
- coverage_units: []
231
- };
232
- }
233
- function invocationFromCommand(command) {
234
- const analysis = parseLifecycleCommand(command);
235
- return analysis.kind === "none" ? undefined : analysis.invocation;
236
- }
237
- function decodeFlowSessionPayload(input) {
201
+ export function prepareFlowSessionPayload(input) {
202
+ if ([input.payloadFile, input.payloadBase64, input.payloadJson].filter(value => value !== undefined).length !== 1)
203
+ throw new AppError("usage", "session register requires exactly one of --payload-file, --payload-base64, or --payload-json", 2);
238
204
  if (input.payloadFile) {
239
- return normalizeFlowSessionPayload(parseJsonObject(fs.readFileSync(input.payloadFile, "utf8"), "session payload"));
205
+ return normalizeFlowSessionPayload(parseJsonObject(prepareTextFile(input.payloadFile, "session payload").text, "session payload"));
240
206
  }
241
207
  if (input.payloadBase64) {
242
208
  if (/\$[A-Za-z_{(]/.test(input.payloadBase64)) {
243
- throw new AppError("validation", "session register --payload-base64 must be a literal base64 value because Codex PreToolUse hooks run before shell variable expansion; use --payload-file from prompts", 2);
209
+ throw new AppError("validation", "session register received an unexpanded shell variable as --payload-base64; expand it before invoking the CLI or use --payload-file", 2);
244
210
  }
245
211
  return normalizeFlowSessionPayload(parseJsonObject(Buffer.from(input.payloadBase64, "base64").toString("utf8"), "session payload"));
246
212
  }
@@ -0,0 +1,377 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { createHash, randomUUID } from "node:crypto";
4
+ import { AppError } from "../shared/errors.js";
5
+ import { hybridDigest, validateHybridShape } from "./hybrid-contracts.js";
6
+ import { admitReleaseManifest } from "./release-manifest.js";
7
+ import { validateReleasePayloadIdentity } from "./release-payload.js";
8
+ import { publishReleaseCache, verifyPublishedReleaseCache } from "./release-cache.js";
9
+ import { acquireInstallationLock } from "./installation-lock.js";
10
+ import { authenticateReleaseEnvelope } from "./release-envelope.js";
11
+ import { prepareOperatorProfile, validateProfileCreationIntent, commitProfileCreation, readOperatorProfiles } from "./operator-profiles.js";
12
+ /** Production ingress authenticates the source before constructing any installation authority. */
13
+ export function installAuthenticatedRelease(home, input) {
14
+ const authenticated = authenticateReleaseEnvelope(input.envelope, input.trustedProducers, input.compatibility);
15
+ return installRelease(home, { requestId: input.requestId, action: input.action,
16
+ manifest: authenticated.manifest, manifestBytes: authenticated.manifestBytes, authority: authenticated.authority,
17
+ canonArchive: input.canonArchive, engineArchive: input.engineArchive,
18
+ ...(input.executor !== undefined ? { executor: input.executor } : {}),
19
+ ...(input.profileCreation !== undefined ? { profileCreation: input.profileCreation } : {}),
20
+ ...(input.deferredOnboarding ? { deferredOnboarding: true } : {}),
21
+ ...(input.onCheckpoint ? { onCheckpoint: input.onCheckpoint } : {}) });
22
+ }
23
+ function refuse(reason, effect = "no_effect") {
24
+ throw new AppError("setup_conflict", "Setup request cannot proceed", 1, { reason, phase: "setup", effect, help: "setup", next_action: "dd-flow setup status --json" });
25
+ }
26
+ const bytesDigest = (bytes) => createHash("sha256").update(bytes).digest("hex");
27
+ function manifestFromBytes(bytes) {
28
+ try {
29
+ return JSON.parse(bytes.toString("utf8"));
30
+ }
31
+ catch {
32
+ return refuse("invalid_manifest_bytes");
33
+ }
34
+ }
35
+ function regularJson(file) {
36
+ if (!fs.lstatSync(file).isFile())
37
+ refuse("unsafe_setup_file");
38
+ try {
39
+ return JSON.parse(fs.readFileSync(file, "utf8"));
40
+ }
41
+ catch {
42
+ return refuse("invalid_setup_file");
43
+ }
44
+ }
45
+ function atomicJson(file, value) {
46
+ const temporary = `${file}.${randomUUID()}.tmp`;
47
+ const fd = fs.openSync(temporary, "wx", 0o600);
48
+ try {
49
+ try {
50
+ fs.writeFileSync(fd, JSON.stringify(value));
51
+ fs.fsyncSync(fd);
52
+ }
53
+ finally {
54
+ fs.closeSync(fd);
55
+ }
56
+ fs.renameSync(temporary, file);
57
+ }
58
+ finally {
59
+ if (fs.existsSync(temporary))
60
+ fs.unlinkSync(temporary);
61
+ }
62
+ }
63
+ function readSetupRequest(file) {
64
+ const value = regularJson(file);
65
+ const required = ["schema_id", "request_id", "identity", "previous_selection", "phase", "installation"];
66
+ if (!value || typeof value !== "object" || Array.isArray(value)
67
+ || required.some(key => !Object.hasOwn(value, key))
68
+ || Object.keys(value).some(key => ![...required, "executor", "profileCreation", "deferredOnboarding"].includes(key)))
69
+ refuse("invalid_request_record");
70
+ const request = value;
71
+ if (Object.hasOwn(request, "deferredOnboarding") && (request.deferredOnboarding !== true || Object.hasOwn(request, "executor") || Object.hasOwn(request, "profileCreation")))
72
+ refuse("invalid_deferred_onboarding");
73
+ if (request.schema_id !== "dd-flow/setup-request@1"
74
+ || typeof request.request_id !== "string" || !/^[A-Za-z0-9][A-Za-z0-9_.-]{0,95}$/.test(request.request_id)
75
+ || typeof request.identity !== "string" || !/^[a-f0-9]{64}$/.test(request.identity)
76
+ || request.previous_selection !== null && (typeof request.previous_selection !== "string" || !/^[a-f0-9]{64}$/.test(request.previous_selection))
77
+ || !["prepared", "canon_cached", "engine_cached", "profiles_saved", "committed"].includes(request.phase)
78
+ || !request.installation || typeof request.installation !== "object" || Array.isArray(request.installation))
79
+ refuse("invalid_request_record");
80
+ if (Object.hasOwn(request, "executor") && request.executor !== null) {
81
+ try {
82
+ prepareOperatorProfile(request.executor);
83
+ }
84
+ catch {
85
+ refuse("invalid_request_executor");
86
+ }
87
+ }
88
+ if (Object.hasOwn(request, "profileCreation")) {
89
+ try {
90
+ validateProfileCreationIntent(request.profileCreation);
91
+ }
92
+ catch {
93
+ refuse("invalid_request_profile_creation");
94
+ }
95
+ if (!request.executor || hybridDigest(request.executor) !== hybridDigest(request.profileCreation.profile))
96
+ refuse("request_profile_executor_mismatch");
97
+ }
98
+ return request;
99
+ }
100
+ /** Read retained executor consent for an exact retry, without installation or profile effects.
101
+ * Full source/installation request identity is still checked by installRelease.
102
+ */
103
+ export function readRetainedSetupExecutor(home, requestId) {
104
+ if (!/^[A-Za-z0-9][A-Za-z0-9_.-]{0,95}$/.test(requestId))
105
+ refuse("invalid_request");
106
+ const root = path.resolve(home), directory = path.join(root, "setup-requests");
107
+ for (const candidate of [root, directory])
108
+ if (fs.existsSync(candidate) && !fs.lstatSync(candidate).isDirectory())
109
+ refuse("unsafe_setup_directory");
110
+ const file = path.join(directory, `${requestId}.json`);
111
+ if (!fs.existsSync(file))
112
+ return null;
113
+ const request = readSetupRequest(file);
114
+ if (request.request_id !== requestId)
115
+ refuse("request_identity_conflict");
116
+ if (!Object.hasOwn(request, "executor"))
117
+ return null;
118
+ return { executor: structuredClone(request.executor ?? null),
119
+ ...(request.profileCreation ? { profileCreation: structuredClone(request.profileCreation) } : {}) };
120
+ }
121
+ /** A deferred request is immutable installation consent, not an implicit skip. */
122
+ export function hasDeferredSetupOnboarding(home, requestId) {
123
+ if (!/^[A-Za-z0-9][A-Za-z0-9_.-]{0,95}$/.test(requestId))
124
+ refuse("invalid_request");
125
+ const root = path.resolve(home), directory = path.join(root, "setup-requests");
126
+ for (const candidate of [root, directory])
127
+ if (fs.existsSync(candidate) && !fs.lstatSync(candidate).isDirectory())
128
+ refuse("unsafe_setup_directory");
129
+ const file = path.join(directory, `${requestId}.json`);
130
+ if (!fs.existsSync(file))
131
+ return false;
132
+ const request = readSetupRequest(file);
133
+ if (request.request_id !== requestId)
134
+ refuse("request_identity_conflict");
135
+ return request.deferredOnboarding === true;
136
+ }
137
+ function deferredRequest(home, requestId) {
138
+ if (!hasDeferredSetupOnboarding(home, requestId))
139
+ refuse("onboarding_not_deferred");
140
+ const request = readSetupRequest(path.join(path.resolve(home), "setup-requests", `${requestId}.json`));
141
+ if (request.phase !== "committed")
142
+ refuse("installation_not_committed");
143
+ return request;
144
+ }
145
+ /** Retained consent allows recovery without asking again or importing current defaults. */
146
+ export function readSetupOnboarding(home, requestId) {
147
+ const request = deferredRequest(home, requestId);
148
+ const directory = path.join(path.resolve(home), "setup-onboarding");
149
+ if (fs.existsSync(directory) && !fs.lstatSync(directory).isDirectory())
150
+ refuse("unsafe_onboarding_directory");
151
+ const file = path.join(directory, `${requestId}.json`);
152
+ if (!fs.existsSync(file))
153
+ return null;
154
+ const receipt = regularJson(file);
155
+ if (!receipt || typeof receipt !== "object" || Array.isArray(receipt)
156
+ || Object.keys(receipt).some(key => !["schema_id", "request_id", "installation_identity", "phase", "executor", "profileCreation"].includes(key))
157
+ || receipt.schema_id !== "dd-flow/setup-onboarding@1" || receipt.request_id !== requestId
158
+ || receipt.installation_identity !== request.identity || !["selected", "committed"].includes(receipt.phase)
159
+ || !Object.hasOwn(receipt, "executor"))
160
+ refuse("invalid_onboarding_receipt");
161
+ try {
162
+ if (receipt.executor !== null)
163
+ prepareOperatorProfile(receipt.executor);
164
+ if (receipt.profileCreation !== undefined) {
165
+ validateProfileCreationIntent(receipt.profileCreation);
166
+ if (!receipt.executor || hybridDigest(receipt.executor) !== hybridDigest(receipt.profileCreation.profile))
167
+ refuse("onboarding_profile_mismatch");
168
+ }
169
+ }
170
+ catch {
171
+ refuse("invalid_onboarding_receipt");
172
+ }
173
+ return structuredClone(receipt);
174
+ }
175
+ /** Persist human consent before profile effects; replay never restores later profile edits. */
176
+ export function commitSetupOnboarding(home, requestId, selection) {
177
+ const request = deferredRequest(home, requestId), root = path.resolve(home);
178
+ const executor = selection.executor === null ? null : prepareOperatorProfile(selection.executor);
179
+ if (selection.profileCreation) {
180
+ validateProfileCreationIntent(selection.profileCreation);
181
+ if (!executor || hybridDigest(executor) !== hybridDigest(selection.profileCreation.profile))
182
+ refuse("onboarding_profile_mismatch");
183
+ }
184
+ const candidate = { schema_id: "dd-flow/setup-onboarding@1", request_id: requestId,
185
+ installation_identity: request.identity, phase: "selected", executor,
186
+ ...(selection.profileCreation ? { profileCreation: selection.profileCreation } : {}) };
187
+ const lease = acquireInstallationLock(path.join(root, "setup.lock"), hybridDigest({ request_id: requestId, identity: request.identity }));
188
+ let failure, result;
189
+ try {
190
+ const retained = readSetupOnboarding(root, requestId);
191
+ if (retained && hybridDigest({ ...retained, phase: "selected" }) !== hybridDigest(candidate))
192
+ refuse("onboarding_selection_conflict");
193
+ if (retained?.phase === "committed")
194
+ result = retained;
195
+ else {
196
+ if (hybridDigest(readSetupStatus(root).installation) !== hybridDigest(request.installation))
197
+ refuse("onboarding_installation_changed");
198
+ const directory = path.join(root, "setup-onboarding");
199
+ fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
200
+ const file = path.join(directory, `${requestId}.json`);
201
+ if (!retained)
202
+ atomicJson(file, candidate);
203
+ if (candidate.profileCreation)
204
+ commitProfileCreation(root, candidate.profileCreation);
205
+ result = { ...candidate, phase: "committed" };
206
+ atomicJson(file, result);
207
+ }
208
+ }
209
+ catch (error) {
210
+ failure = error instanceof AppError ? error : new AppError("setup_onboarding_interrupted", "Installed release is retained; executor onboarding requires reconciliation", 1, { phase: "executor_onboarding", effect: "unknown", request_id: requestId, help: "setup", next_action: "Replay the exact setup request" });
211
+ }
212
+ finally {
213
+ try {
214
+ lease.release();
215
+ }
216
+ catch {
217
+ if (failure)
218
+ failure.details.cleanup = "not_confirmed";
219
+ else
220
+ failure = new AppError("setup_cleanup_failed", "Onboarding owner cleanup requires reconciliation", 1, { effect: "unknown", cleanup: "not_confirmed", request_id: requestId, help: "setup" });
221
+ }
222
+ }
223
+ if (failure)
224
+ throw failure;
225
+ return result;
226
+ }
227
+ /** Read-only global readiness. No SQLite connection, migration, project registration or probes. */
228
+ export function readSetupStatus(home) {
229
+ const root = path.resolve(home);
230
+ if (fs.existsSync(root) && !fs.lstatSync(root).isDirectory())
231
+ refuse("unsafe_setup_home");
232
+ const file = path.join(root, "installation.json");
233
+ const installation = fs.existsSync(file) ? regularJson(file) : null;
234
+ if (installation) {
235
+ if (!installation.manifest || typeof installation.manifest_bytes_base64 !== "string" || validateHybridShape("release-manifest", installation.manifest).length ||
236
+ installation.canon_root !== path.join(root, "release-cache", "canon", installation.manifest.canon.version, "payload") ||
237
+ installation.engine_root !== path.join(root, "release-cache", "engine", installation.manifest.engine.version, "payload"))
238
+ refuse("invalid_installation_selection");
239
+ const bytes = Buffer.from(installation.manifest_bytes_base64, "base64");
240
+ if (bytes.toString("base64") !== installation.manifest_bytes_base64 || bytesDigest(bytes) !== installation.manifest_sha256 ||
241
+ hybridDigest(manifestFromBytes(bytes)) !== hybridDigest(installation.manifest))
242
+ refuse("invalid_installation_selection");
243
+ }
244
+ let readiness = installation ? "installed" : "not_installed";
245
+ if (installation) {
246
+ try {
247
+ verifyPublishedReleaseCache(path.dirname(installation.canon_root), installation.manifest.canon.archive);
248
+ verifyPublishedReleaseCache(path.dirname(installation.engine_root), installation.manifest.engine.archive);
249
+ }
250
+ catch {
251
+ readiness = "degraded";
252
+ }
253
+ }
254
+ const existingStorage = fs.existsSync(path.join(root, "db.sqlite"));
255
+ return { installation, installation_readiness: readiness,
256
+ storage_readiness: existingStorage ? "unknown_requires_diagnostic" : "not_initialized",
257
+ runtime_readiness: "unknown_requires_qualification",
258
+ next_action: !installation ? "dd-flow setup --help" : existingStorage ? "dd-flow migration plan --help" : "dd-flow engine doctor --json",
259
+ ...(existingStorage ? { storage_continuation: { kind: "storage_admission",
260
+ status: "requires_diagnostic", reason: "existing_storage_not_inspected",
261
+ help_command: "dd-flow migration plan --help", automatic_migration: false } } : {}) };
262
+ }
263
+ /** Exact-request installation transaction. Selected immutable caches do not affect existing RUNs. */
264
+ export function installRelease(home, input) {
265
+ if (!/^[A-Za-z0-9][A-Za-z0-9_.-]{0,95}$/.test(input.requestId) || !["install", "update"].includes(input.action))
266
+ refuse("invalid_request");
267
+ const executor = input.executor === undefined ? undefined : input.executor === null ? null : prepareOperatorProfile(input.executor);
268
+ const executorBinding = executor === undefined ? {} : { executor: structuredClone(executor) };
269
+ if (input.deferredOnboarding !== undefined && (input.deferredOnboarding !== true || executor !== undefined || input.profileCreation !== undefined))
270
+ refuse("invalid_deferred_onboarding");
271
+ const onboardingBinding = input.deferredOnboarding ? { deferredOnboarding: true } : {};
272
+ if (input.profileCreation !== undefined) {
273
+ validateProfileCreationIntent(input.profileCreation);
274
+ if (!executor || hybridDigest(executor) !== hybridDigest(input.profileCreation.profile))
275
+ refuse("profile_executor_mismatch");
276
+ }
277
+ const profileBinding = input.profileCreation === undefined ? {} : { profileCreation: structuredClone(input.profileCreation) };
278
+ const manifest = admitReleaseManifest(input.manifest, input.authority);
279
+ if (!Buffer.isBuffer(input.manifestBytes) || hybridDigest(manifestFromBytes(input.manifestBytes)) !== hybridDigest(manifest))
280
+ refuse("manifest_bytes_mismatch");
281
+ // Both archives must be valid before even creating the home or publishing the first cache.
282
+ validateReleasePayloadIdentity(manifest, input.canonArchive, input.engineArchive);
283
+ const root = path.resolve(home), requests = path.join(root, "setup-requests"), lock = path.join(root, "setup.lock");
284
+ const installation = { manifest, manifest_sha256: bytesDigest(input.manifestBytes), manifest_bytes_base64: input.manifestBytes.toString("base64"),
285
+ canon_root: path.join(root, "release-cache", "canon", manifest.canon.version, "payload"),
286
+ engine_root: path.join(root, "release-cache", "engine", manifest.engine.version, "payload") };
287
+ const identity = hybridDigest({ action: input.action, installation, ...executorBinding, ...profileBinding, ...onboardingBinding });
288
+ readSetupStatus(root);
289
+ for (const directory of [root, requests]) {
290
+ if (fs.existsSync(directory) && !fs.lstatSync(directory).isDirectory())
291
+ refuse("unsafe_setup_directory");
292
+ }
293
+ if (input.profileCreation) {
294
+ const existingFile = path.join(requests, `${input.requestId}.json`);
295
+ const retained = fs.existsSync(existingFile) ? readSetupRequest(existingFile) : null;
296
+ if (retained?.phase !== "committed") {
297
+ const revision = readOperatorProfiles(root).revision;
298
+ if (revision !== input.profileCreation.before.revision && revision !== input.profileCreation.after.revision)
299
+ refuse("profile_selection_changed");
300
+ }
301
+ }
302
+ fs.mkdirSync(requests, { recursive: true, mode: 0o700 });
303
+ const lease = acquireInstallationLock(lock, hybridDigest({ request_id: input.requestId, identity }));
304
+ let failure;
305
+ const perform = () => {
306
+ const file = path.join(requests, `${input.requestId}.json`);
307
+ const current = readSetupStatus(root).installation;
308
+ let request = { schema_id: "dd-flow/setup-request@1", request_id: input.requestId, identity,
309
+ previous_selection: current ? hybridDigest(current) : null, phase: "prepared", installation, ...executorBinding, ...profileBinding, ...onboardingBinding };
310
+ if (fs.existsSync(file)) {
311
+ request = readSetupRequest(file);
312
+ if (request.schema_id !== "dd-flow/setup-request@1" || request.request_id !== input.requestId || request.identity !== identity || hybridDigest(request.installation) !== hybridDigest(installation))
313
+ refuse("request_identity_conflict");
314
+ if (request.deferredOnboarding !== input.deferredOnboarding)
315
+ refuse("request_onboarding_conflict");
316
+ if (hybridDigest({ executor: request.executor ?? null, present: Object.hasOwn(request, "executor") }) !==
317
+ hybridDigest({ executor: executor ?? null, present: executor !== undefined }))
318
+ refuse("request_executor_conflict");
319
+ if (hybridDigest({ profileCreation: request.profileCreation ?? null }) !== hybridDigest({ profileCreation: input.profileCreation ?? null }))
320
+ refuse("request_profile_creation_conflict");
321
+ if (!["prepared", "canon_cached", "engine_cached", "profiles_saved", "committed"].includes(request.phase))
322
+ refuse("invalid_request_phase");
323
+ }
324
+ // Replay still revalidates retained payload bytes; it never reactivates an older selection.
325
+ if (request.phase === "committed") {
326
+ publishReleaseCache(root, "canon", manifest.canon.version, manifest.canon.archive, input.canonArchive);
327
+ publishReleaseCache(root, "engine", manifest.engine.version, manifest.engine.archive, input.engineArchive);
328
+ return { ok: true, replayed: true, installation: request.installation, ...executorBinding };
329
+ }
330
+ const selectedDigest = current ? hybridDigest(current) : null;
331
+ if (selectedDigest !== request.previous_selection && selectedDigest !== hybridDigest(installation))
332
+ refuse("selection_changed_since_request");
333
+ if (input.action === "install" && current && selectedDigest !== hybridDigest(installation))
334
+ refuse("existing_installation_requires_update");
335
+ const checkpoint = (phase) => {
336
+ request.phase = phase;
337
+ atomicJson(file, request);
338
+ input.onCheckpoint?.(phase);
339
+ };
340
+ checkpoint("prepared");
341
+ publishReleaseCache(root, "canon", manifest.canon.version, manifest.canon.archive, input.canonArchive);
342
+ checkpoint("canon_cached");
343
+ publishReleaseCache(root, "engine", manifest.engine.version, manifest.engine.archive, input.engineArchive);
344
+ checkpoint("engine_cached");
345
+ if (request.profileCreation) {
346
+ commitProfileCreation(root, request.profileCreation);
347
+ checkpoint("profiles_saved");
348
+ }
349
+ atomicJson(path.join(root, "installation.json"), installation);
350
+ checkpoint("committed");
351
+ return { ok: true, replayed: false, installation, ...executorBinding };
352
+ };
353
+ let result;
354
+ try {
355
+ result = perform();
356
+ }
357
+ catch (error) {
358
+ failure = error instanceof AppError ? error : new AppError("setup_interrupted", "Setup request requires reconciliation", 1, { phase: "setup", effect: "unknown", request_id: input.requestId, next_action: "Replay the exact setup request", help: "setup" });
359
+ }
360
+ finally {
361
+ try {
362
+ lease.release();
363
+ }
364
+ catch {
365
+ // Cleanup is secondary to the retained operation result. Never leak a
366
+ // raw filesystem cause or erase the original failure with an unlink error.
367
+ if (failure)
368
+ failure.details.cleanup = "not_confirmed";
369
+ else
370
+ failure = new AppError("setup_cleanup_failed", "Setup owner cleanup requires reconciliation", 1, { phase: "setup", effect: "unknown", cleanup: "not_confirmed", request_id: input.requestId,
371
+ next_action: "Inspect dd-flow setup status --json; replay the exact request after its owner exits", help: "setup" });
372
+ }
373
+ }
374
+ if (failure)
375
+ throw failure;
376
+ return result;
377
+ }
@@ -1,19 +1,24 @@
1
1
  import { AppError } from "../shared/errors.js";
2
2
  import { resolveProjectRoot } from "../storage/paths.js";
3
3
  import { requireProjectByRoot } from "./projects.js";
4
- import { blockFlowRunStage, unblockFlowRunStage } from "./runs.js";
4
+ import { blockFlowRunStage, unblockFlowRunStage, prepareFlowRunStageBlock, prepareFlowRunStageUnblock } from "./runs.js";
5
5
  import { flowCommand } from "./stage-pause.js";
6
6
  import { refreshRunWorkProjection } from "./work-registry.js";
7
- export function blockStageForRuntime(context, input) {
7
+ export function prepareStageRuntimeBlock(context, input) {
8
+ if (typeof input.code !== "string" || !input.code.trim() || typeof input.summary !== "string" || !input.summary.trim())
9
+ throw new AppError("validation", "stage block requires a code and summary", 2);
8
10
  const projectRoot = resolveProjectRoot(input.projectRoot);
9
11
  const project = requireProjectByRoot(context, projectRoot);
10
12
  const run = requireRun(context, project.id, input.runId);
11
13
  const work = requireWork(context, project.id, run.id, input.workId);
12
14
  if (work.status !== "running")
13
15
  throw new AppError("invalid_work_state", "Only a running Work can be blocked", 1, { work_id: work.work_id, status: work.status });
14
- if (!input.code.trim() || !input.summary.trim())
15
- throw new AppError("validation", "stage block requires a code and summary", 2);
16
- const state = blockFlowRunStage(context, { ...input, projectRoot });
16
+ const flow = prepareFlowRunStageBlock(context, { ...input, projectRoot });
17
+ return { projectRoot, project, work, flow };
18
+ }
19
+ export function blockStageForRuntime(context, input) {
20
+ const { projectRoot, project, work, flow } = prepareStageRuntimeBlock(context, input);
21
+ const state = blockFlowRunStage(context, { ...input, projectRoot }, flow);
17
22
  const now = context.now();
18
23
  context.db.run("UPDATE works SET status = 'paused', updated_at = ? WHERE work_id = ? AND status = 'running'", [now, work.work_id]);
19
24
  refreshRunWorkProjection(context, project.id, work.run_id);
@@ -30,14 +35,19 @@ export function blockStageForRuntime(context, input) {
30
35
  state
31
36
  };
32
37
  }
33
- export function unblockStageAfterRuntimeRepair(context, input) {
38
+ export function prepareStageRuntimeUnblock(context, input) {
34
39
  const projectRoot = resolveProjectRoot(input.projectRoot);
35
40
  const project = requireProjectByRoot(context, projectRoot);
36
41
  const run = requireRun(context, project.id, input.runId);
37
42
  const work = requireWork(context, project.id, run.id, input.workId);
38
43
  if (work.status !== "paused")
39
44
  throw new AppError("invalid_work_state", "stage unblock requires its paused Work", 1, { work_id: work.work_id, status: work.status });
40
- const state = unblockFlowRunStage(context, { projectRoot, runId: work.run_id, stage: input.stage, workId: work.work_id });
45
+ const flow = prepareFlowRunStageUnblock(context, { ...input, projectRoot, runId: work.run_id });
46
+ return { projectRoot, project, work, flow };
47
+ }
48
+ export function unblockStageAfterRuntimeRepair(context, input) {
49
+ const { projectRoot, project, work, flow } = prepareStageRuntimeUnblock(context, input);
50
+ const state = unblockFlowRunStage(context, { projectRoot, runId: work.run_id, stage: input.stage, workId: work.work_id }, flow);
41
51
  const now = context.now();
42
52
  context.db.run("UPDATE works SET status = 'running', updated_at = ? WHERE work_id = ? AND status = 'paused'", [now, work.work_id]);
43
53
  refreshRunWorkProjection(context, project.id, work.run_id);