@deksden-com/dd-flow-cli 0.9.0-beta.16 → 0.9.0-beta.160

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 (670) hide show
  1. package/CHANGELOG.md +959 -0
  2. package/README.md +139 -10
  3. package/dist/build-info.json +8 -8
  4. package/dist/cli/argument-values.js +38 -0
  5. package/dist/cli/command-inputs.js +417 -0
  6. package/dist/cli/command-metadata.js +303 -0
  7. package/dist/cli/command-option-descriptions.js +192 -0
  8. package/dist/cli/completion-cli.js +70 -0
  9. package/dist/cli/completion.js +57 -0
  10. package/dist/cli/delivery.js +108 -0
  11. package/dist/cli/diagnostic-invocation.js +62 -0
  12. package/dist/cli/diagnostic.js +131 -0
  13. package/dist/cli/error-output.js +62 -0
  14. package/dist/cli/executor-onboarding.js +76 -0
  15. package/dist/cli/flows.js +29 -0
  16. package/dist/cli/harnesses.js +84 -0
  17. package/dist/cli/help.js +521 -19
  18. package/dist/cli/hook-ingress.js +89 -0
  19. package/dist/cli/input-preparation.js +193 -0
  20. package/dist/cli/interactive-invocation.js +33 -0
  21. package/dist/cli/interactive.js +114 -0
  22. package/dist/cli/memory-fix-invocation.js +52 -0
  23. package/dist/cli/memory-fix.js +100 -0
  24. package/dist/cli/memory-flow-admission.js +54 -0
  25. package/dist/cli/memory-init-invocation.js +85 -0
  26. package/dist/cli/memory-init.js +250 -0
  27. package/dist/cli/memory-storage-admission.js +54 -0
  28. package/dist/cli/memory-storage-qualification.js +105 -0
  29. package/dist/cli/memory-storage-transition.js +123 -0
  30. package/dist/cli/memory-upgrade.js +189 -0
  31. package/dist/cli/models.js +39 -0
  32. package/dist/cli/native-hook-ingress.js +94 -0
  33. package/dist/cli/native-hook-preparation.js +82 -0
  34. package/dist/cli/operator-profiles.js +61 -0
  35. package/dist/cli/output-options.js +35 -0
  36. package/dist/cli/response-file.js +72 -0
  37. package/dist/cli/run-cli.js +2020 -555
  38. package/dist/cli/runtime-maintenance-diagnostics.js +46 -0
  39. package/dist/cli/runtime-maintenance-ingress.js +52 -0
  40. package/dist/cli/runtime-process.js +153 -0
  41. package/dist/cli/setup.js +150 -0
  42. package/dist/cli.js +103 -2
  43. package/dist/domain/delivery-flows.js +8 -0
  44. package/dist/domain/diagnostic-aspects.js +103 -0
  45. package/dist/domain/diagnostic-flows.js +8 -0
  46. package/dist/domain/distill-flow.js +20 -0
  47. package/dist/domain/interactive-flow.js +1 -0
  48. package/dist/domain/memory-fix-flow.js +2 -0
  49. package/dist/domain/review-fix-flow.js +2 -0
  50. package/dist/domain/source-snapshot-flows.js +4 -0
  51. package/dist/domain/stage-catalog.js +1 -1
  52. package/dist/domain/stage-handler-identities.js +28 -0
  53. package/dist/harness-runtime/DD-ZCODE.md +112 -0
  54. package/dist/harness-runtime/OPENCODE-LICENSE.txt +24 -0
  55. package/dist/harness-runtime/bin/dd-agy.mjs +71 -0
  56. package/dist/harness-runtime/bin/dd-codex.mjs +27 -0
  57. package/dist/harness-runtime/bin/dd-droid.mjs +34 -0
  58. package/dist/harness-runtime/bin/dd-grok.mjs +50 -0
  59. package/dist/harness-runtime/bin/dd-opencode.mjs +22 -0
  60. package/dist/harness-runtime/bin/dd-zcode.mjs +95 -0
  61. package/dist/harness-runtime/lib/adapter-timeouts.d.mts +5 -0
  62. package/dist/harness-runtime/lib/adapter-timeouts.mjs +34 -0
  63. package/dist/harness-runtime/lib/agy-error-boundary.mjs +87 -0
  64. package/dist/harness-runtime/lib/agy-hooks.d.mts +1 -0
  65. package/dist/harness-runtime/lib/agy-hooks.mjs +9 -0
  66. package/dist/harness-runtime/lib/agy-native-inspection.mjs +225 -0
  67. package/dist/harness-runtime/lib/agy-native-inventory.mjs +19 -0
  68. package/dist/harness-runtime/lib/agy-native_pb.js +117 -0
  69. package/dist/harness-runtime/lib/agy-terminal-envelope.mjs +74 -0
  70. package/dist/harness-runtime/lib/agy-transcript-stream.mjs +54 -0
  71. package/dist/harness-runtime/lib/daemon-observation.d.mts +3 -0
  72. package/dist/harness-runtime/lib/daemon-observation.mjs +171 -0
  73. package/dist/harness-runtime/lib/daemon-operations.d.mts +2 -0
  74. package/dist/harness-runtime/lib/daemon-operations.mjs +512 -0
  75. package/dist/harness-runtime/lib/dd-agy-daemon.d.mts +7 -0
  76. package/dist/harness-runtime/lib/dd-agy-daemon.mjs +1362 -0
  77. package/dist/harness-runtime/lib/dd-agy.mjs +105 -0
  78. package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
  79. package/dist/harness-runtime/lib/dd-codex-daemon.mjs +271 -0
  80. package/dist/harness-runtime/lib/dd-codex.d.mts +1 -0
  81. package/dist/harness-runtime/lib/dd-codex.mjs +986 -0
  82. package/dist/harness-runtime/lib/dd-droid-daemon.mjs +155 -0
  83. package/dist/harness-runtime/lib/dd-droid.mjs +739 -0
  84. package/dist/harness-runtime/lib/dd-grok-daemon.d.mts +1 -0
  85. package/dist/harness-runtime/lib/dd-grok-daemon.mjs +667 -0
  86. package/dist/harness-runtime/lib/dd-grok.mjs +182 -0
  87. package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +772 -0
  88. package/dist/harness-runtime/lib/dd-opencode.mjs +411 -0
  89. package/dist/harness-runtime/lib/dd-zcode-daemon.d.mts +1 -0
  90. package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +845 -0
  91. package/dist/harness-runtime/lib/dd-zcode.mjs +1239 -0
  92. package/dist/harness-runtime/lib/delegation-instructions.d.mts +16 -0
  93. package/dist/harness-runtime/lib/delegation-instructions.mjs +179 -0
  94. package/dist/harness-runtime/lib/delivery-command.mjs +84 -0
  95. package/dist/harness-runtime/lib/dispatch-fence.mjs +14 -0
  96. package/dist/harness-runtime/lib/driver-recovery.mjs +121 -0
  97. package/dist/harness-runtime/lib/droid-observation.mjs +70 -0
  98. package/dist/harness-runtime/lib/entity-references.d.mts +2 -0
  99. package/dist/harness-runtime/lib/entity-references.mjs +9 -0
  100. package/dist/harness-runtime/lib/lease-renewal.d.mts +8 -0
  101. package/dist/harness-runtime/lib/lease-renewal.mjs +37 -0
  102. package/dist/harness-runtime/lib/lifecycle-command.d.mts +20 -0
  103. package/dist/harness-runtime/lib/lifecycle-command.mjs +273 -0
  104. package/dist/harness-runtime/lib/lifecycle-contract.d.mts +11 -0
  105. package/dist/harness-runtime/lib/lifecycle-contract.mjs +20 -0
  106. package/dist/harness-runtime/lib/managed-daemon.d.mts +16 -0
  107. package/dist/harness-runtime/lib/managed-daemon.mjs +698 -0
  108. package/dist/harness-runtime/lib/managed-flow-env.mjs +15 -0
  109. package/dist/harness-runtime/lib/model-observations.mjs +113 -0
  110. package/dist/harness-runtime/lib/native-children.d.mts +34 -0
  111. package/dist/harness-runtime/lib/native-children.mjs +265 -0
  112. package/dist/harness-runtime/lib/native-hook-command.d.mts +8 -0
  113. package/dist/harness-runtime/lib/native-hook-command.mjs +306 -0
  114. package/dist/harness-runtime/lib/native-request-proof.d.mts +2 -0
  115. package/dist/harness-runtime/lib/native-request-proof.mjs +65 -0
  116. package/dist/harness-runtime/lib/native-streams.mjs +38 -0
  117. package/dist/harness-runtime/lib/observation-clock.d.mts +18 -0
  118. package/dist/harness-runtime/lib/observation-clock.mjs +95 -0
  119. package/dist/harness-runtime/lib/opencode-plugin.mjs +112 -0
  120. package/dist/harness-runtime/lib/operation-context.mjs +5 -0
  121. package/dist/harness-runtime/lib/operation-errors.d.mts +16 -0
  122. package/dist/harness-runtime/lib/operation-errors.mjs +140 -0
  123. package/dist/harness-runtime/lib/operation-progress.d.mts +7 -0
  124. package/dist/harness-runtime/lib/operation-progress.mjs +118 -0
  125. package/dist/harness-runtime/lib/process-json.d.mts +5 -0
  126. package/dist/harness-runtime/lib/process-json.mjs +200 -0
  127. package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
  128. package/dist/harness-runtime/lib/provider-overload-policy.d.mts +23 -0
  129. package/dist/harness-runtime/lib/provider-overload-policy.mjs +242 -0
  130. package/dist/harness-runtime/lib/runner-events.mjs +202 -0
  131. package/dist/harness-runtime/lib/runner-lock.mjs +42 -0
  132. package/dist/harness-runtime/lib/session-settlement.mjs +30 -0
  133. package/dist/harness-runtime/lib/setup-progress.d.mts +9 -0
  134. package/dist/harness-runtime/lib/setup-progress.mjs +53 -0
  135. package/dist/harness-runtime/lib/tool-observations.d.mts +21 -0
  136. package/dist/harness-runtime/lib/tool-observations.mjs +238 -0
  137. package/dist/harness-runtime/lib/zcode-native-hook-ownership.mjs +109 -0
  138. package/dist/harness-runtime/lib/zcode-usage.mjs +55 -0
  139. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-1y4a697q.js +81 -0
  140. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-2ny4vxct.js +92 -0
  141. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-6mptkzbc.js +0 -0
  142. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-7x67cf1q.js +52 -0
  143. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9rqn6x4v.js +16 -0
  144. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9vdz0zwn.js +1661 -0
  145. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ecs5ftka.js +1466 -0
  146. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-frbwqjmf.js +145 -0
  147. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gne7h7mq.js +50 -0
  148. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gx9wnkzr.js +105 -0
  149. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-m2eywc8w.js +0 -0
  150. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-nwcpakzx.js +12 -0
  151. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-q4g3afnw.js +623 -0
  152. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-qav6mfdz.js +0 -0
  153. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-sxt88q69.js +12 -0
  154. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-t1xa31vm.js +39 -0
  155. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ve4aws36.js +103 -0
  156. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vmytr4q8.js +4 -0
  157. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vt9jbch8.js +36 -0
  158. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vyas3vgt.js +0 -0
  159. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-wjyeq25y.js +30 -0
  160. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-xgnj2w4v.js +20 -0
  161. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-y2etg5bs.js +9 -0
  162. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ydhz1zrh.js +238 -0
  163. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-z6qdwcsf.js +62 -0
  164. package/dist/harness-runtime/vendor/opencode-client/contract.d.ts +1 -0
  165. package/dist/harness-runtime/vendor/opencode-client/contract.js +12 -0
  166. package/dist/harness-runtime/vendor/opencode-client/effect/api/api.d.ts +2806 -0
  167. package/dist/harness-runtime/vendor/opencode-client/effect/api/api.js +0 -0
  168. package/dist/harness-runtime/vendor/opencode-client/effect/api.d.ts +4 -0
  169. package/dist/harness-runtime/vendor/opencode-client/effect/api.js +0 -0
  170. package/dist/harness-runtime/vendor/opencode-client/effect/client.d.ts +2530 -0
  171. package/dist/harness-runtime/vendor/opencode-client/effect/client.js +15 -0
  172. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.d.ts +7 -0
  173. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.js +6 -0
  174. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.d.ts +2529 -0
  175. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.js +8 -0
  176. package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.d.ts +2 -0
  177. package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.js +12 -0
  178. package/dist/harness-runtime/vendor/opencode-client/effect/index.d.ts +35 -0
  179. package/dist/harness-runtime/vendor/opencode-client/effect/index.js +75 -0
  180. package/dist/harness-runtime/vendor/opencode-client/effect/rpc.d.ts +20 -0
  181. package/dist/harness-runtime/vendor/opencode-client/effect/rpc.js +11 -0
  182. package/dist/harness-runtime/vendor/opencode-client/effect/service.d.ts +90 -0
  183. package/dist/harness-runtime/vendor/opencode-client/effect/service.js +187 -0
  184. package/dist/harness-runtime/vendor/opencode-client/package.json +84 -0
  185. package/dist/harness-runtime/vendor/opencode-client/promise/api.d.ts +21 -0
  186. package/dist/harness-runtime/vendor/opencode-client/promise/api.js +0 -0
  187. package/dist/harness-runtime/vendor/opencode-client/promise/client.d.ts +252 -0
  188. package/dist/harness-runtime/vendor/opencode-client/promise/client.js +14 -0
  189. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.d.ts +8 -0
  190. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.js +6 -0
  191. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.d.ts +259 -0
  192. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.js +7 -0
  193. package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.d.ts +3 -0
  194. package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.js +79 -0
  195. package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.d.ts +8932 -0
  196. package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.js +70 -0
  197. package/dist/harness-runtime/vendor/opencode-client/promise/index.d.ts +6 -0
  198. package/dist/harness-runtime/vendor/opencode-client/promise/index.js +83 -0
  199. package/dist/harness-runtime/vendor/opencode-client/promise/rpc.d.ts +32 -0
  200. package/dist/harness-runtime/vendor/opencode-client/promise/rpc.js +7 -0
  201. package/dist/harness-runtime/vendor/opencode-client/promise/service.d.ts +24 -0
  202. package/dist/harness-runtime/vendor/opencode-client/promise/service.js +187 -0
  203. package/dist/harness-runtime/vendor/opencode-client/pty-handoff.d.ts +12 -0
  204. package/dist/harness-runtime/vendor/opencode-client/pty-handoff.js +14 -0
  205. package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.d.ts +20 -0
  206. package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.js +14 -0
  207. package/dist/harness-runtime/vendor/opencode-client/service-contender.d.ts +37 -0
  208. package/dist/harness-runtime/vendor/opencode-client/service-contender.js +10 -0
  209. package/dist/harness-runtime/vendor/opencode-client/service-probe.d.ts +70 -0
  210. package/dist/harness-runtime/vendor/opencode-client/service-probe.js +16 -0
  211. package/dist/harness-runtime/vendor/opencode-client/service-timing.d.ts +12 -0
  212. package/dist/harness-runtime/vendor/opencode-client/service-timing.js +10 -0
  213. package/dist/harness-runtime/vendor/opencode-client/service-version.d.ts +2 -0
  214. package/dist/harness-runtime/vendor/opencode-client/service-version.js +6 -0
  215. package/dist/harness-runtime/vendor/opencode-client/service.d.ts +52 -0
  216. package/dist/harness-runtime/vendor/opencode-client/service.js +1 -0
  217. package/dist/harness-runtime/vendor/opencode-client/shared-events.d.ts +11 -0
  218. package/dist/harness-runtime/vendor/opencode-client/shared-events.js +8 -0
  219. package/dist/harness-runtime/vendor/opencode-client/solid/connection.d.ts +37 -0
  220. package/dist/harness-runtime/vendor/opencode-client/solid/connection.js +10 -0
  221. package/dist/harness-runtime/vendor/opencode-client/solid/data.d.ts +211 -0
  222. package/dist/harness-runtime/vendor/opencode-client/solid/data.js +18 -0
  223. package/dist/harness-runtime/vendor/opencode-client/solid/index.d.ts +3 -0
  224. package/dist/harness-runtime/vendor/opencode-client/solid/index.js +32 -0
  225. package/dist/harness-runtime/vendor/opencode-client/solid/pty.d.ts +22 -0
  226. package/dist/harness-runtime/vendor/opencode-client/solid/pty.js +8 -0
  227. package/dist/harness-runtime/vendor/opencode-plugin.mjs +3 -0
  228. package/dist/protocol/local-files.js +18 -9
  229. package/dist/runtime/context.js +8 -4
  230. package/dist/schemas/agent-profile.schema.json +1 -1
  231. package/dist/schemas/code-review-decision.schema.json +9 -0
  232. package/dist/schemas/code-review-result.schema.json +1 -1
  233. package/dist/schemas/code-verification.schema.json +7 -5
  234. package/dist/schemas/code-work-batch.schema.json +2 -2
  235. package/dist/schemas/code-work-result.schema.json +2 -1
  236. package/dist/schemas/delivery-stage-result.schema.json +286 -0
  237. package/dist/schemas/diagnostic-stage-result.schema.json +354 -0
  238. package/dist/schemas/diagnostic-work-result.schema.json +296 -0
  239. package/dist/schemas/discovery-result.schema.json +184 -0
  240. package/dist/schemas/distill-stage-result.schema.json +667 -0
  241. package/dist/schemas/distill-work-result.schema.json +428 -0
  242. package/dist/schemas/execution-bundle.schema.json +215 -0
  243. package/dist/schemas/execution-settings.schema.json +162 -0
  244. package/dist/schemas/flow-descriptor.schema.json +54 -0
  245. package/dist/schemas/flow-run.schema.json +29 -0
  246. package/dist/schemas/harness-catalog.schema.json +80 -0
  247. package/dist/schemas/interactive-stage-result.schema.json +47 -0
  248. package/dist/schemas/interactive-work-result.schema.json +63 -0
  249. package/dist/schemas/mb-fix-stage-result.schema.json +93 -0
  250. package/dist/schemas/mb-init-compose-result.schema.json +457 -0
  251. package/dist/schemas/mb-init-discover-result.schema.json +500 -0
  252. package/dist/schemas/mb-init-finish-result.schema.json +422 -0
  253. package/dist/schemas/mb-init-preflight-result.schema.json +367 -0
  254. package/dist/schemas/mb-init-review-result.schema.json +396 -0
  255. package/dist/schemas/mb-init-verify-result.schema.json +391 -0
  256. package/dist/schemas/mb-upgrade-stage-result.schema.json +863 -0
  257. package/dist/schemas/memory-bootstrap-receipt-v2.schema.json +67 -0
  258. package/dist/schemas/memory-bootstrap-receipt.schema.json +354 -0
  259. package/dist/schemas/memory-composition-work-result.schema.json +38 -0
  260. package/dist/schemas/memory-repair-work-result.schema.json +56 -0
  261. package/dist/schemas/memory-review-correction-plan.schema.json +30 -0
  262. package/dist/schemas/memory-review-work-result.schema.json +43 -0
  263. package/dist/schemas/model-catalog.schema.json +153 -0
  264. package/dist/schemas/plan-review-decision.schema.json +12 -3
  265. package/dist/schemas/plan-review-result.schema.json +2 -2
  266. package/dist/schemas/profile-settings.schema.json +122 -0
  267. package/dist/schemas/project-flow-pack-manifest.schema.json +2 -2
  268. package/dist/schemas/release-impact.schema.json +3 -3
  269. package/dist/schemas/release-manifest.schema.json +291 -0
  270. package/dist/schemas/review-fix-stage-result.schema.json +210 -0
  271. package/dist/schemas/run-control-receipt.schema.json +119 -0
  272. package/dist/schemas/run-control-request.schema.json +36 -0
  273. package/dist/schemas/run-engine-binding.schema.json +1 -1
  274. package/dist/schemas/stage-start-response.schema.json +4 -0
  275. package/dist/schemas/vnext-protocol-plan.schema.json +3 -3
  276. package/dist/services/accepted-intent.js +139 -0
  277. package/dist/services/assigned-lifecycle.js +382 -0
  278. package/dist/services/branch-context.js +3 -3
  279. package/dist/services/canon.js +54 -15
  280. package/dist/services/check-environment.js +21 -0
  281. package/dist/services/check-runner-evidence.js +344 -0
  282. package/dist/services/check-runner-proof.js +373 -0
  283. package/dist/services/cleanup.js +133 -40
  284. package/dist/services/cli-operation-classifier.js +32 -64
  285. package/dist/services/cli-upgrade-allowlist.js +27 -0
  286. package/dist/services/code-checks.js +1426 -121
  287. package/dist/services/codex-capacity.js +367 -0
  288. package/dist/services/codex-hook-delivery.js +41 -0
  289. package/dist/services/command-context.js +80 -0
  290. package/dist/services/compatibility-preflight.js +16 -3
  291. package/dist/services/config.js +11 -8
  292. package/dist/services/continuation-outcome.js +25 -0
  293. package/dist/services/controller-child-capacity.js +71 -0
  294. package/dist/services/controller-child-observer.js +121 -0
  295. package/dist/services/controller-child-repair.js +117 -0
  296. package/dist/services/controller-execution.js +97 -0
  297. package/dist/services/controller-fanout.js +277 -0
  298. package/dist/services/controller-memory-task.js +45 -0
  299. package/dist/services/controller-memory-worker.js +51 -0
  300. package/dist/services/controller-result-authority.js +127 -0
  301. package/dist/services/controller-work-failure.js +198 -0
  302. package/dist/services/dashboard.js +145 -58
  303. package/dist/services/delivery-access.js +123 -0
  304. package/dist/services/delivery-approval.js +68 -0
  305. package/dist/services/delivery-artifact.js +86 -0
  306. package/dist/services/delivery-checks.js +178 -0
  307. package/dist/services/delivery-execution.js +181 -0
  308. package/dist/services/delivery-intents.js +212 -0
  309. package/dist/services/delivery-operation.js +81 -0
  310. package/dist/services/delivery-policy.js +91 -0
  311. package/dist/services/delivery-procedures.js +43 -0
  312. package/dist/services/delivery-request.js +99 -0
  313. package/dist/services/delivery-result.js +144 -0
  314. package/dist/services/delivery-stage-commands.js +26 -0
  315. package/dist/services/delivery-stage-entry.js +56 -0
  316. package/dist/services/delivery-stage-handlers.js +135 -0
  317. package/dist/services/delivery-stage-results.js +94 -0
  318. package/dist/services/descriptor-coordinator-session.js +37 -0
  319. package/dist/services/descriptor-root-work.js +53 -0
  320. package/dist/services/descriptor-run-settlement.js +107 -0
  321. package/dist/services/descriptor-run-start.js +495 -0
  322. package/dist/services/descriptor-stage-coordinator.js +318 -0
  323. package/dist/services/descriptor-stage-settlement.js +268 -0
  324. package/dist/services/descriptor-stage-start.js +175 -0
  325. package/dist/services/diagnostic-evidence.js +73 -0
  326. package/dist/services/diagnostic-repair-intake.js +123 -0
  327. package/dist/services/diagnostic-repair-source-intake.js +102 -0
  328. package/dist/services/diagnostic-report-publication.js +72 -0
  329. package/dist/services/diagnostic-report.js +97 -0
  330. package/dist/services/diagnostic-run-bundle.js +113 -0
  331. package/dist/services/diagnostic-snapshot.js +159 -0
  332. package/dist/services/diagnostic-stage-handlers.js +109 -0
  333. package/dist/services/diagnostic-stage-results.js +158 -0
  334. package/dist/services/diagnostic-start.js +134 -0
  335. package/dist/services/diagnostic-work-batch.js +34 -0
  336. package/dist/services/diagnostic-work-collection.js +97 -0
  337. package/dist/services/diagnostic-work-registration.js +101 -0
  338. package/dist/services/diagnostic-work-results.js +93 -0
  339. package/dist/services/discovery/agy-discovery.js +2 -0
  340. package/dist/services/discovery/codex-discovery.js +2 -0
  341. package/dist/services/discovery/droid-discovery.js +2 -0
  342. package/dist/services/discovery/grok-discovery.js +2 -0
  343. package/dist/services/discovery/opencode-discovery.js +2 -0
  344. package/dist/services/discovery/static.js +31 -0
  345. package/dist/services/discovery/zcode-discovery.js +2 -0
  346. package/dist/services/distill-document-publication.js +280 -0
  347. package/dist/services/distill-promotion.js +197 -0
  348. package/dist/services/distill-publication-custody.js +162 -0
  349. package/dist/services/distill-stage-handlers.js +249 -0
  350. package/dist/services/distill-stage-results.js +105 -0
  351. package/dist/services/distill-user-inputs.js +4 -0
  352. package/dist/services/distill-work-batch.js +42 -0
  353. package/dist/services/distill-work-collection.js +74 -0
  354. package/dist/services/distill-work-registration.js +93 -0
  355. package/dist/services/distill-work-results.js +98 -0
  356. package/dist/services/engines.js +354 -163
  357. package/dist/services/eval-snapshots.js +1326 -150
  358. package/dist/services/execution-bundles.js +275 -0
  359. package/dist/services/execution-policy.js +185 -0
  360. package/dist/services/executor-onboarding.js +56 -0
  361. package/dist/services/external-work-launch.js +316 -0
  362. package/dist/services/flow-catalog.js +54 -0
  363. package/dist/services/flow-descriptors.js +82 -0
  364. package/dist/services/frozen-engine.js +404 -0
  365. package/dist/services/generic-execution.js +165 -0
  366. package/dist/services/harness-adapter.js +342 -35
  367. package/dist/services/harness-config.js +2 -0
  368. package/dist/services/harness-discovery.js +29 -0
  369. package/dist/services/harness-doctor.js +75 -0
  370. package/dist/services/harness-probe-effects.js +51 -0
  371. package/dist/services/harness-version-probe.js +100 -0
  372. package/dist/services/hook-memory-routing.js +188 -0
  373. package/dist/services/hook-recovery-guard.js +14 -0
  374. package/dist/services/hook-session-observation.js +24 -0
  375. package/dist/services/hooks.js +813 -327
  376. package/dist/services/hybrid-contracts.js +479 -0
  377. package/dist/services/hybrid-digest.js +14 -0
  378. package/dist/services/ids.js +29 -1
  379. package/dist/services/installation-lock.js +95 -0
  380. package/dist/services/installed-flow-source.js +261 -0
  381. package/dist/services/interactive-attached-session.js +215 -0
  382. package/dist/services/interactive-baseline-custody.js +54 -0
  383. package/dist/services/interactive-before-versions.js +148 -0
  384. package/dist/services/interactive-bundle-data.js +85 -0
  385. package/dist/services/interactive-checks.js +179 -0
  386. package/dist/services/interactive-execution.js +33 -0
  387. package/dist/services/interactive-finish-integration.js +236 -0
  388. package/dist/services/interactive-git-baseline.js +230 -0
  389. package/dist/services/interactive-git-dispatch.js +278 -0
  390. package/dist/services/interactive-input-delivery.js +82 -0
  391. package/dist/services/interactive-mutation-scope.js +231 -0
  392. package/dist/services/interactive-request.js +287 -0
  393. package/dist/services/interactive-run-bundle.js +109 -0
  394. package/dist/services/interactive-stage-entry.js +41 -0
  395. package/dist/services/interactive-stage-handlers.js +289 -0
  396. package/dist/services/interactive-stage-observation.js +136 -0
  397. package/dist/services/interactive-stage-results.js +111 -0
  398. package/dist/services/interactive-user-inputs.js +142 -0
  399. package/dist/services/interactive-work-batch.js +43 -0
  400. package/dist/services/interactive-work-collection.js +119 -0
  401. package/dist/services/interactive-work-completion.js +180 -0
  402. package/dist/services/interactive-work-context.js +249 -0
  403. package/dist/services/interactive-work-history.js +112 -0
  404. package/dist/services/interactive-work-registration.js +124 -0
  405. package/dist/services/interactive-work-results.js +115 -0
  406. package/dist/services/interactive-work-start.js +155 -0
  407. package/dist/services/lanes.js +78 -65
  408. package/dist/services/lifecycle-command.js +1 -288
  409. package/dist/services/lifecycle-contract.js +1 -0
  410. package/dist/services/lifecycle-invocations.js +2549 -0
  411. package/dist/services/managed-daemon-binding.js +129 -0
  412. package/dist/services/managed-lease-monitor.js +133 -0
  413. package/dist/services/managed-processes.js +449 -69
  414. package/dist/services/memory-accepted-composition.js +78 -0
  415. package/dist/services/memory-baseline-preservation.js +65 -0
  416. package/dist/services/memory-completed-correction-wave.js +328 -0
  417. package/dist/services/memory-compose-handler.js +380 -0
  418. package/dist/services/memory-composition-acceptance.js +375 -0
  419. package/dist/services/memory-composition-graph.js +165 -0
  420. package/dist/services/memory-composition-registration.js +161 -0
  421. package/dist/services/memory-composition-work-custody.js +156 -0
  422. package/dist/services/memory-composition-work-results.js +108 -0
  423. package/dist/services/memory-corrected-review-acceptance.js +129 -0
  424. package/dist/services/memory-corrected-review-batch.js +37 -0
  425. package/dist/services/memory-corrected-review-findings.js +108 -0
  426. package/dist/services/memory-corrected-review-inputs.js +215 -0
  427. package/dist/services/memory-corrected-review-projection.js +78 -0
  428. package/dist/services/memory-corrected-review-receipt.js +178 -0
  429. package/dist/services/memory-corrected-review-registration.js +192 -0
  430. package/dist/services/memory-correction-check-receipt.js +235 -0
  431. package/dist/services/memory-correction-input-owner.js +6 -0
  432. package/dist/services/memory-custody-snapshot.js +36 -0
  433. package/dist/services/memory-discover-handler.js +104 -0
  434. package/dist/services/memory-finish-evidence.js +457 -0
  435. package/dist/services/memory-finish-handler.js +229 -0
  436. package/dist/services/memory-finish-integration.js +491 -0
  437. package/dist/services/memory-finish-readiness.js +169 -0
  438. package/dist/services/memory-fix-finish-integration.js +129 -0
  439. package/dist/services/memory-fix-git-dispatch.js +139 -0
  440. package/dist/services/memory-fix-intake-custody.js +11 -0
  441. package/dist/services/memory-fix-intake-settlement.js +127 -0
  442. package/dist/services/memory-fix-publication.js +196 -0
  443. package/dist/services/memory-fix-repair-admission.js +116 -0
  444. package/dist/services/memory-fix-request.js +141 -0
  445. package/dist/services/memory-fix-run-bundle.js +83 -0
  446. package/dist/services/memory-fix-scope.js +78 -0
  447. package/dist/services/memory-fix-sources.js +85 -0
  448. package/dist/services/memory-fix-stage-entry.js +24 -0
  449. package/dist/services/memory-fix-stage-handlers.js +282 -0
  450. package/dist/services/memory-fix-stage-results.js +106 -0
  451. package/dist/services/memory-fix-workspace.js +127 -0
  452. package/dist/services/memory-flow-user-inputs.js +88 -0
  453. package/dist/services/memory-git-integration.js +220 -0
  454. package/dist/services/memory-historical-corrected-review.js +212 -0
  455. package/dist/services/memory-historical-original-review.js +143 -0
  456. package/dist/services/memory-historical-repair-registration.js +105 -0
  457. package/dist/services/memory-historical-second-review.js +147 -0
  458. package/dist/services/memory-init-bootstrap-readiness.js +210 -0
  459. package/dist/services/memory-init-contexts.js +181 -0
  460. package/dist/services/memory-init-controller-start.js +119 -0
  461. package/dist/services/memory-init-engine-release.js +223 -0
  462. package/dist/services/memory-init-git-policy.js +165 -0
  463. package/dist/services/memory-init-lint-capability.js +203 -0
  464. package/dist/services/memory-init-native-capacity.js +309 -0
  465. package/dist/services/memory-init-preflight.js +334 -0
  466. package/dist/services/memory-init-project-bootstrap.js +599 -0
  467. package/dist/services/memory-init-request-candidate.js +213 -0
  468. package/dist/services/memory-init-request-input.js +82 -0
  469. package/dist/services/memory-init-resource-storage.js +125 -0
  470. package/dist/services/memory-init-run-allocation.js +81 -0
  471. package/dist/services/memory-init-run-bundle.js +135 -0
  472. package/dist/services/memory-init-start.js +181 -0
  473. package/dist/services/memory-init-startup-custody.js +80 -0
  474. package/dist/services/memory-init-startup-input.js +240 -0
  475. package/dist/services/memory-init-storage.js +91 -0
  476. package/dist/services/memory-init-stores.js +359 -0
  477. package/dist/services/memory-init-worktrunk-capability.js +174 -0
  478. package/dist/services/memory-layer-publication.js +611 -0
  479. package/dist/services/memory-layer.js +225 -0
  480. package/dist/services/memory-lint-config.js +194 -0
  481. package/dist/services/memory-native-review-copy.js +127 -0
  482. package/dist/services/memory-normal-flow-entry.js +87 -0
  483. package/dist/services/memory-ordinary-defaults.js +129 -0
  484. package/dist/services/memory-preflight-bootstrap-custody.js +115 -0
  485. package/dist/services/memory-preflight-handler.js +274 -0
  486. package/dist/services/memory-repair-current-view.js +299 -0
  487. package/dist/services/memory-repair-document-delta.js +489 -0
  488. package/dist/services/memory-repair-history.js +496 -0
  489. package/dist/services/memory-repair-physical-binding.js +194 -0
  490. package/dist/services/memory-repair-publication-files.js +152 -0
  491. package/dist/services/memory-repair-publication-intent.js +27 -0
  492. package/dist/services/memory-repair-publication-plan.js +162 -0
  493. package/dist/services/memory-repair-publication-source.js +225 -0
  494. package/dist/services/memory-repair-publication.js +795 -0
  495. package/dist/services/memory-repair-registration.js +240 -0
  496. package/dist/services/memory-repair-work-baseline.js +415 -0
  497. package/dist/services/memory-repair-work-completion.js +117 -0
  498. package/dist/services/memory-repair-work-custody.js +73 -0
  499. package/dist/services/memory-repair-work-results.js +108 -0
  500. package/dist/services/memory-repair-workspace.js +261 -0
  501. package/dist/services/memory-repair-writer-copy.js +736 -0
  502. package/dist/services/memory-result-binding.js +239 -0
  503. package/dist/services/memory-retained-review-authority.js +138 -0
  504. package/dist/services/memory-retained-review-results.js +70 -0
  505. package/dist/services/memory-retained-second-baseline.js +102 -0
  506. package/dist/services/memory-retained-second-repairs.js +162 -0
  507. package/dist/services/memory-review-acceptance.js +214 -0
  508. package/dist/services/memory-review-correction-plan.js +206 -0
  509. package/dist/services/memory-review-handler.js +585 -0
  510. package/dist/services/memory-review-inputs.js +246 -0
  511. package/dist/services/memory-review-limits.js +10 -0
  512. package/dist/services/memory-review-receipt.js +180 -0
  513. package/dist/services/memory-review-registration.js +117 -0
  514. package/dist/services/memory-review-work-custody.js +126 -0
  515. package/dist/services/memory-review-work-input.js +91 -0
  516. package/dist/services/memory-review-work-results.js +95 -0
  517. package/dist/services/memory-second-corrected-review-inputs.js +167 -0
  518. package/dist/services/memory-second-correction-plan.js +77 -0
  519. package/dist/services/memory-second-repair-baseline.js +91 -0
  520. package/dist/services/memory-second-repair-launch.js +614 -0
  521. package/dist/services/memory-second-repair-registration.js +88 -0
  522. package/dist/services/memory-second-repair-source.js +187 -0
  523. package/dist/services/memory-second-review-acceptance.js +115 -0
  524. package/dist/services/memory-source-observations.js +117 -0
  525. package/dist/services/memory-stage-dispatch.js +65 -0
  526. package/dist/services/memory-stage-results.js +290 -0
  527. package/dist/services/memory-tool-probe.js +25 -0
  528. package/dist/services/memory-upgrade-analysis-handler.js +243 -0
  529. package/dist/services/memory-upgrade-bootstrap-readiness.js +251 -0
  530. package/dist/services/memory-upgrade-evidence.js +100 -0
  531. package/dist/services/memory-upgrade-inventory.js +442 -0
  532. package/dist/services/memory-upgrade-publication-custody.js +73 -0
  533. package/dist/services/memory-upgrade-publication.js +413 -0
  534. package/dist/services/memory-upgrade-selection.js +430 -0
  535. package/dist/services/memory-upgrade-start.js +60 -0
  536. package/dist/services/memory-upgrade-storage-readiness.js +340 -0
  537. package/dist/services/memory-verification-acceptance.js +156 -0
  538. package/dist/services/memory-verification-checks.js +317 -0
  539. package/dist/services/memory-verification-receipt.js +160 -0
  540. package/dist/services/memory-verify-handler.js +137 -0
  541. package/dist/services/memory-work-result-json.js +12 -0
  542. package/dist/services/memory-work-result-preparation.js +64 -0
  543. package/dist/services/merge-queue.js +228 -149
  544. package/dist/services/merge-server.js +163 -35
  545. package/dist/services/migrations.js +164 -114
  546. package/dist/services/model-catalog.js +109 -0
  547. package/dist/services/model-discovery.js +46 -0
  548. package/dist/services/native-daemon-history.js +50 -0
  549. package/dist/services/native-session-control.js +39 -0
  550. package/dist/services/operator-profiles.js +236 -0
  551. package/dist/services/physical-startup.js +45 -0
  552. package/dist/services/plan-runtime.js +26 -11
  553. package/dist/services/plans.js +28 -25
  554. package/dist/services/project-flow-source.js +195 -0
  555. package/dist/services/project-git-integration.js +92 -0
  556. package/dist/services/project-registry.js +97 -0
  557. package/dist/services/project-summary.js +1 -1
  558. package/dist/services/projects.js +17 -87
  559. package/dist/services/prompt-delivery.js +71 -0
  560. package/dist/services/prompts.js +28 -17
  561. package/dist/services/protocols.js +163 -87
  562. package/dist/services/provider-capacity.js +463 -0
  563. package/dist/services/recovery-observation-budget.js +121 -0
  564. package/dist/services/recovery-snapshot-database.js +588 -0
  565. package/dist/services/release-archive-producer.js +52 -0
  566. package/dist/services/release-archive.js +143 -0
  567. package/dist/services/release-cache.js +150 -0
  568. package/dist/services/release-compatibility.js +18 -0
  569. package/dist/services/release-download.js +67 -0
  570. package/dist/services/release-envelope.js +104 -0
  571. package/dist/services/release-manifest.js +45 -0
  572. package/dist/services/release-payload.js +114 -0
  573. package/dist/services/release-producer.js +63 -0
  574. package/dist/services/release-provenance.js +160 -0
  575. package/dist/services/release-registry.js +66 -0
  576. package/dist/services/release-source.js +194 -0
  577. package/dist/services/repair-intents.js +29 -0
  578. package/dist/services/repair-receipt.js +13 -0
  579. package/dist/services/retained-native-hook.js +274 -0
  580. package/dist/services/review-copy.js +165 -0
  581. package/dist/services/review-deferral.js +23 -0
  582. package/dist/services/review-fix-handoff.js +82 -0
  583. package/dist/services/review-fix-intake-custody.js +17 -0
  584. package/dist/services/review-fix-stage-entry.js +43 -0
  585. package/dist/services/review-fix-stage-handlers.js +176 -0
  586. package/dist/services/review-fix-stage-results.js +86 -0
  587. package/dist/services/run-control-receipt.js +56 -0
  588. package/dist/services/run-control-worker.js +469 -0
  589. package/dist/services/run-control.js +1109 -0
  590. package/dist/services/run-controller-adapter.js +434 -0
  591. package/dist/services/run-controller-capture.js +186 -0
  592. package/dist/services/run-controller-process.js +226 -0
  593. package/dist/services/run-controller-recovery.js +335 -0
  594. package/dist/services/run-controller-state.js +106 -0
  595. package/dist/services/run-controller.js +1717 -0
  596. package/dist/services/run-engine-bindings.js +1 -1
  597. package/dist/services/run-fork.js +147 -0
  598. package/dist/services/run-health.js +149 -0
  599. package/dist/services/run-observations.js +152 -0
  600. package/dist/services/run-projection.js +22 -64
  601. package/dist/services/run-recovery-guard.js +107 -0
  602. package/dist/services/run-recovery-runtime.js +112 -0
  603. package/dist/services/run-recovery.js +485 -0
  604. package/dist/services/run-start-custody.js +189 -0
  605. package/dist/services/run-timeline.js +59 -0
  606. package/dist/services/runs.js +996 -271
  607. package/dist/services/runtime-budget.js +446 -0
  608. package/dist/services/runtime-command.js +43 -0
  609. package/dist/services/runtime-scope-capture.js +52 -0
  610. package/dist/services/runtime-scope-control.js +468 -0
  611. package/dist/services/runtime-scope-resume.js +605 -0
  612. package/dist/services/runtime-scope-stop.js +211 -0
  613. package/dist/services/runtime-scope-worker.js +314 -0
  614. package/dist/services/runtime-service.js +19 -14
  615. package/dist/services/schema-validation.js +10 -8
  616. package/dist/services/scope-writer-cleanup.js +27 -0
  617. package/dist/services/sessions.js +37 -91
  618. package/dist/services/setup.js +427 -0
  619. package/dist/services/stage-blocker.js +17 -7
  620. package/dist/services/stage-context.js +240 -18
  621. package/dist/services/stage-handlers.js +141 -0
  622. package/dist/services/stage-lifecycle.js +156 -119
  623. package/dist/services/stage-pause.js +222 -94
  624. package/dist/services/stage-report-renderer.js +230 -6
  625. package/dist/services/stage-work-graph.js +52 -0
  626. package/dist/services/technical-notes.js +31 -0
  627. package/dist/services/tooling.js +111 -44
  628. package/dist/services/usage.js +49 -101
  629. package/dist/services/verification-matrix.js +393 -0
  630. package/dist/services/version-status.js +1 -1
  631. package/dist/services/vnext-code-review.js +302 -91
  632. package/dist/services/vnext-code.js +325 -251
  633. package/dist/services/vnext-contracts.js +10 -1
  634. package/dist/services/vnext-execution-profile.js +80 -5
  635. package/dist/services/vnext-fanout.js +399 -20
  636. package/dist/services/vnext-merge.js +336 -114
  637. package/dist/services/vnext-plan-review.js +288 -133
  638. package/dist/services/vnext-plan.js +230 -106
  639. package/dist/services/vnext-protocolize.js +534 -96
  640. package/dist/services/vnext-specify.js +131 -84
  641. package/dist/services/vnext-workspace-policy.js +7 -2
  642. package/dist/services/work-registry.js +1950 -215
  643. package/dist/services/work-retry-context.js +55 -0
  644. package/dist/services/workspace-bootstrap.js +53 -15
  645. package/dist/services/workspace-files.js +85 -0
  646. package/dist/services/worktrees.js +108 -48
  647. package/dist/shared/entity-references.js +1 -0
  648. package/dist/shared/errors.js +27 -0
  649. package/dist/storage/agent-output.js +26 -0
  650. package/dist/storage/data-preservation.js +65 -0
  651. package/dist/storage/database.js +931 -40
  652. package/dist/storage/foreign-key-transaction.js +39 -0
  653. package/dist/storage/immutable-input.js +45 -0
  654. package/dist/storage/import-guard.js +159 -0
  655. package/dist/storage/memory-upgrade-schema-bootstrap.js +38 -0
  656. package/dist/storage/native-cleanup.js +52 -0
  657. package/dist/storage/paired-writer-migration.js +424 -0
  658. package/dist/storage/paths.js +17 -4
  659. package/dist/storage/projection-lock.js +73 -0
  660. package/dist/storage/work-references.js +23 -0
  661. package/dist/storage/writer-contract.js +96 -0
  662. package/dist/storage/writer-migration.js +346 -0
  663. package/dist/storage/writer-restore.js +217 -0
  664. package/dist/verification-components.json +384 -0
  665. package/package.json +52 -13
  666. package/tools/create-canon-release.mjs +33 -0
  667. package/tools/probe-codex-history.mjs +37 -0
  668. package/tools/produce-hybrid-release.mjs +41 -0
  669. package/tools/repair-paused-run-status.mjs +84 -0
  670. package/tools/verification-jobs.mjs +342 -0
@@ -1,73 +1,310 @@
1
1
  import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
2
4
  import { spawnSync } from "node:child_process";
3
5
  import net from "node:net";
4
6
  import { getResourceDatabase } from "../storage/database.js";
7
+ import { AppError, errorRecord } from "../shared/errors.js";
8
+ import { maintenanceRetryable } from "../harness-runtime/lib/lease-renewal.mjs";
9
+ export function managedProcessBinding(record, home) {
10
+ return { resource_home: home, process_id: record.id, registration_sha256: crypto.createHash("sha256").update(JSON.stringify([home, record.id, record.kind, record.owner_id, record.lease_token, record.pid, record.pid_started_at, record.project_id, record.run_id, record.work_id, record.check_id, record.operation_id, record.metadata_json])).digest("hex") };
11
+ }
12
+ export function assertManagedProcessAdmission(context, record) {
13
+ const expiry = Date.parse(record.lease_expires_at);
14
+ if (!Number.isFinite(expiry))
15
+ throw new AppError("runtime_artifact_invalid", "Managed lease expiry is malformed", 1, { process_id: record.id });
16
+ if (expiry <= Date.parse(context.now()))
17
+ throw new AppError("process_ownership_unknown", "Managed owner needs fresh renewal before admission", 1, { process_id: record.id, reason: "renewal_required", lease_expires_at: record.lease_expires_at });
18
+ }
5
19
  const defaultLeaseMs = 15 * 60_000;
6
20
  export function resourceHome(context) {
7
21
  return context.env?.DD_FLOW_RESOURCE_HOME ?? context.ddFlowHome ?? "/tmp/dd-flow-runtime";
8
22
  }
9
- export function registerManagedProcess(context, input) {
23
+ /** Retained owners may name the same registry through a historical alias. */
24
+ export function retainedResourceHomeMatches(context, retained) {
25
+ if (retained === resourceHome(context))
26
+ return true;
27
+ if (typeof retained !== "string" || !path.isAbsolute(retained))
28
+ return false;
29
+ try {
30
+ return fs.realpathSync(retained) === fs.realpathSync(resourceHome(context));
31
+ }
32
+ catch {
33
+ return false;
34
+ }
35
+ }
36
+ /** Runtime IDs are home-local; a shared host registry needs this extra scope. */
37
+ export function managedProcessRuntimeHome(record, authority) {
38
+ const metadata = parseMetadata(record.metadata_json);
39
+ if (metadata.dd_flow_home !== undefined)
40
+ return typeof metadata.dd_flow_home === "string" && path.isAbsolute(metadata.dd_flow_home) ? metadata.dd_flow_home : null;
41
+ // Older native registrations can still prove their home from the retained
42
+ // exact daemon owner; PID or project/RUN labels alone never establish it.
43
+ if (typeof metadata.state_dir === "string" && path.isAbsolute(metadata.state_dir)) {
44
+ try {
45
+ const state = JSON.parse(fs.readFileSync(path.join(metadata.state_dir, "daemon.json"), "utf8"));
46
+ const owner = state.config?.runtime_owner;
47
+ if (owner?.owner_id === record.owner_id && owner.project_id === record.project_id && owner.run_id === record.run_id && owner.state_dir === metadata.state_dir && typeof owner.dd_flow_home === "string" && path.isAbsolute(owner.dd_flow_home))
48
+ return owner.dd_flow_home;
49
+ }
50
+ catch { /* No retained native scope proof. */ }
51
+ }
52
+ if (record.kind === "run-controller" && authority.db.get("SELECT 1 FROM sqlite_schema WHERE name = 'run_controllers'") && authority.db.get("SELECT 1 FROM run_controllers WHERE controller_id = ? AND process_id = ? AND process_lease_token = ? AND project_id = ? AND run_id = ?", [record.owner_id, record.id, record.lease_token, record.project_id, record.run_id]))
53
+ return authority.ddFlowHome;
54
+ if (record.kind === "run-control" && authority.db.get("SELECT 1 FROM sqlite_schema WHERE name = 'run_control_workers'") && authority.db.get("SELECT 1 FROM run_control_workers w JOIN run_controls c ON c.control_id = w.control_id WHERE w.control_id = ? AND w.process_id = ? AND w.process_lease_token = ? AND c.project_id = ? AND c.run_id = ?", [record.owner_id, record.id, record.lease_token, record.project_id, record.run_id]))
55
+ return authority.ddFlowHome;
56
+ return null;
57
+ }
58
+ export function prepareManagedProcessRegistration(context, input) {
59
+ requireProcessText(input.kind, "kind");
60
+ requireProcessText(input.ownerId, "owner");
61
+ for (const field of ["id", "projectId", "runId", "workId", "checkId", "operationId", "stdoutPath", "stderrPath"]) {
62
+ if (input[field] !== undefined && input[field] !== null)
63
+ requireProcessText(input[field], field);
64
+ }
65
+ for (const field of ["pid", "ownerPid", "processGroupId"]) {
66
+ if (input[field] !== undefined)
67
+ requireProcessPid(input[field], field);
68
+ }
69
+ if (input.uniqueActiveOperation !== undefined && typeof input.uniqueActiveOperation !== "boolean")
70
+ throw new AppError("validation", "uniqueActiveOperation must be boolean", 2);
71
+ if (input.metadata !== undefined && (!input.metadata || typeof input.metadata !== "object" || Array.isArray(input.metadata)))
72
+ throw new AppError("validation", "metadata must be a JSON object", 2);
73
+ prepareManagedProcessLease(context, input.leaseMs);
74
+ const observer = input.kind === "eval-observer";
75
+ const observerBirth = observer && input.pid ? processStartedAt(input.pid) : null;
76
+ if (observer) {
77
+ if (!Number.isSafeInteger(input.pid) || !input.pid || input.pid < 1 || input.ownerPid !== undefined && input.ownerPid !== input.pid || input.processGroupId !== undefined && input.processGroupId !== input.pid)
78
+ throw new AppError("process_ownership_unknown", "EVAL observer registration requires its live self-owned process identity", 1);
79
+ if (!observerBirth)
80
+ throw new AppError("process_ownership_unknown", "EVAL observer physical identity observation is unavailable", 1, { process_id: input.id, reason: "physical_identity_unavailable", phase: "observer_registration", effect: "no_effect", native_dispatch_started: false });
81
+ input = { ...input, ownerPid: input.pid, uniqueActiveOperation: true };
82
+ }
83
+ if (input.uniqueActiveOperation && !input.operationId)
84
+ throw new AppError("validation", "Unique managed operation requires operationId", 2);
85
+ let metadataJson;
86
+ try {
87
+ const metadata = { ...input.metadata, dd_flow_home: context.ddFlowHome, ...(input.ownerPid ? { owner_pid: input.ownerPid, owner_pid_started_at: processStartedAt(input.ownerPid) } : {}) };
88
+ delete metadata.process_birth;
89
+ delete metadata.owner_birth;
90
+ if (input.ownerPid)
91
+ metadata.owner_birth = stableProcessBirth(input.ownerPid);
92
+ metadataJson = JSON.stringify(metadata);
93
+ }
94
+ catch {
95
+ throw new AppError("validation", "metadata must be JSON-serializable", 2);
96
+ }
97
+ const reader = getResourceDatabase(resourceHome(context), "read_existing", context.databaseDeadlineAt);
98
+ try {
99
+ if (input.id && reader.get("SELECT 1 FROM managed_processes WHERE id = ?", [input.id]))
100
+ throw new AppError("validation", "Managed process ID already exists; use its retained registration", 2, { process_id: input.id });
101
+ if (input.uniqueActiveOperation) {
102
+ const existing = reader.get("SELECT * FROM managed_processes WHERE operation_id = ? AND state IN ('starting','running','stopping','orphaned')", [input.operationId]);
103
+ if (existing)
104
+ throw new AppError("managed_operation_in_progress", "The operation already owns a managed process; observe it instead of spawning a duplicate", 1, { process_id: existing.id, operation_id: input.operationId });
105
+ }
106
+ }
107
+ finally {
108
+ reader.close?.();
109
+ }
110
+ return { input, observer, observerBirth, metadataJson };
111
+ }
112
+ export function registerManagedProcess(context, rawInput) {
113
+ const { input, observer, observerBirth, metadataJson } = prepareManagedProcessRegistration(context, rawInput);
10
114
  if (!input.uniqueActiveOperation)
11
- return insertManagedProcess(context, input);
12
- if (!input.operationId)
13
- throw new Error("Unique managed operation requires operationId");
115
+ return registry(context).writeTransaction(() => insertManagedProcess(context, input, metadataJson));
116
+ const observerProcessBirth = observer ? stableProcessBirth(input.pid) : null;
117
+ const observerOwner = observer ? parseMetadata(metadataJson) : null;
118
+ if (observer && (!observerProcessBirth || !observerOwner?.owner_pid_started_at || !parseBirth(observerOwner.owner_birth)))
119
+ throw new AppError("process_ownership_unknown", "EVAL observer physical identity observation is unavailable", 1, { process_id: input.id, reason: "physical_identity_unavailable", phase: "observer_registration", effect: "no_effect", native_dispatch_started: false });
120
+ if (observer && !processIsAlive({ pid: input.pid, pid_started_at: observerBirth, metadata_json: JSON.stringify({ process_birth: observerProcessBirth }) }))
121
+ throw new AppError("process_ownership_unknown", "EVAL observer changed before registration", 1);
14
122
  const db = registry(context);
15
- db.exec("BEGIN IMMEDIATE");
16
- try {
123
+ return db.writeTransaction(() => {
17
124
  const existing = db.get("SELECT * FROM managed_processes WHERE operation_id = ? AND state IN ('starting','running','stopping','orphaned')", [input.operationId]);
18
125
  if (existing)
19
- throw Object.assign(new Error("The operation already owns a managed process; observe it instead of spawning a duplicate"), { code: "managed_operation_in_progress", details: { process_id: existing.id, operation_id: input.operationId } });
20
- const result = insertManagedProcess(context, input);
21
- db.exec("COMMIT");
126
+ throw new AppError("managed_operation_in_progress", "The operation already owns a managed process; observe it instead of spawning a duplicate", 1, { process_id: existing.id, operation_id: input.operationId });
127
+ let result = insertManagedProcess(context, input, metadataJson);
128
+ if (observer) {
129
+ const metadata = JSON.parse(metadataJson);
130
+ if (input.processGroupId)
131
+ metadata.process_group_id = input.processGroupId;
132
+ metadata.process_birth = observerProcessBirth;
133
+ const now = context.now();
134
+ db.run("UPDATE managed_processes SET pid = ?, pid_started_at = ?, state = 'running', lease_expires_at = ?, updated_at = ?, metadata_json = ? WHERE id = ? AND lease_token = ? AND state = 'starting'", [input.pid, observerBirth, leaseExpiry(now, input.leaseMs), now, JSON.stringify(metadata), result.id, result.lease_token]);
135
+ result = requireProcess(db, result.id);
136
+ }
22
137
  return result;
23
- }
24
- catch (error) {
25
- db.exec("ROLLBACK");
26
- throw error;
27
- }
138
+ });
28
139
  }
29
- function insertManagedProcess(context, input) {
140
+ function insertManagedProcess(context, input, metadataJson) {
30
141
  const db = registry(context);
31
142
  const now = context.now();
32
143
  const id = input.id ?? `PROC-${crypto.randomUUID()}`;
33
144
  const token = crypto.randomUUID();
34
145
  db.run(`INSERT INTO managed_processes
35
146
  (id, kind, pid, pid_started_at, owner_id, lease_token, lease_expires_at, project_id, run_id, work_id, check_id, operation_id, stdout_path, stderr_path, state, started_at, updated_at, finished_at, termination_reason, metadata_json)
36
- VALUES (?, ?, NULL, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'starting', ?, ?, NULL, NULL, ?)`, [id, input.kind, input.ownerId, token, leaseExpiry(now, input.leaseMs), input.projectId ?? null, input.runId ?? null, input.workId ?? null, input.checkId ?? null, input.operationId ?? null, input.stdoutPath ?? null, input.stderrPath ?? null, now, now, JSON.stringify({ ...input.metadata, ...(input.ownerPid ? { owner_pid: input.ownerPid, owner_pid_started_at: processStartedAt(input.ownerPid) } : {}) })]);
147
+ VALUES (?, ?, NULL, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'starting', ?, ?, NULL, NULL, ?)`, [id, input.kind, input.ownerId, token, leaseExpiry(now, input.leaseMs), input.projectId ?? null, input.runId ?? null, input.workId ?? null, input.checkId ?? null, input.operationId ?? null, input.stdoutPath ?? null, input.stderrPath ?? null, now, now, metadataJson]);
37
148
  return requireProcess(db, id);
38
149
  }
150
+ export function prepareManagedProcessConfirmation(context, input) {
151
+ requireProcessText(input.id, "id");
152
+ requireProcessText(input.leaseToken, "lease-token");
153
+ requireProcessPid(input.pid, "pid");
154
+ if (input.ownerPid !== undefined)
155
+ requireProcessPid(input.ownerPid, "owner-pid");
156
+ if (input.processGroupId !== undefined && input.processGroupId !== null)
157
+ requireProcessPid(input.processGroupId, "process-group-id");
158
+ prepareManagedProcessLease(context, input.leaseMs);
159
+ const current = readManagedProcess(context, input.id);
160
+ if (current.lease_token !== input.leaseToken)
161
+ throw new AppError("process_lease_lost", "Managed process lease does not match", 1);
162
+ if (current.state !== "starting" && !(current.state === "running" && current.pid === input.pid && processIsAlive(current) && (!input.processGroupId || parseMetadata(current.metadata_json).process_group_id === input.processGroupId) && (!input.ownerPid || parseMetadata(current.metadata_json).owner_pid === input.ownerPid)))
163
+ throw new AppError("process_confirmation_rejected", "Only the retained physical process may be confirmed", 1, { process_id: current.id, state: current.state });
164
+ return current;
165
+ }
39
166
  export function confirmManagedProcess(context, input) {
167
+ const prepared = prepareManagedProcessConfirmation(context, input);
168
+ const retainedIdentity = parseMetadata(prepared.metadata_json);
169
+ const retainedOwner = retainedIdentity.owner_pid;
170
+ const validRetainedOwner = typeof retainedOwner === "number" && Number.isSafeInteger(retainedOwner) && retainedOwner > 0;
171
+ const ownerUnknown = Object.hasOwn(retainedIdentity, "owner_pid") && (!validRetainedOwner || typeof retainedIdentity.owner_pid_started_at !== "string" || !retainedIdentity.owner_pid_started_at.trim() || Object.hasOwn(retainedIdentity, "owner_birth") && !parseBirth(retainedIdentity.owner_birth));
172
+ if (prepared.state === "running" && (!prepared.pid_started_at || Object.hasOwn(retainedIdentity, "process_birth") && !parseBirth(retainedIdentity.process_birth) || ownerUnknown) || input.ownerPid === undefined && Object.hasOwn(retainedIdentity, "owner_pid") && !validRetainedOwner)
173
+ throw new AppError("process_ownership_unknown", "Retained managed physical identity is unconfirmed", 1, { process_id: input.id, reason: "stored_identity_unconfirmed", phase: "process_confirmation", effect: "no_effect", native_dispatch_started: false });
174
+ // Initial confirmation must observe the registered owner too, even when a
175
+ // bootstrap caller omits its optional override. Running bindings never repair.
176
+ const ownerPid = input.ownerPid ?? (validRetainedOwner ? retainedOwner : undefined);
177
+ const pidStartedAt = processStartedAt(input.pid), processBirth = stableProcessBirth(input.pid);
178
+ const ownerStartedAt = ownerPid ? processStartedAt(ownerPid) : null, ownerBirth = ownerPid ? stableProcessBirth(ownerPid) : null;
179
+ if (!pidStartedAt || !processBirth || ownerPid && (!ownerStartedAt || !ownerBirth))
180
+ throw new AppError("process_ownership_unknown", "Managed physical identity observation is unavailable", 1, { process_id: input.id, reason: "physical_identity_unavailable", phase: "process_confirmation", effect: "no_effect", native_dispatch_started: false });
40
181
  const db = registry(context);
41
- const now = context.now();
42
- const current = requireProcess(db, input.id);
43
- const metadata = parseMetadata(current.metadata_json);
44
- if (input.processGroupId)
45
- metadata.process_group_id = input.processGroupId;
46
- if (input.ownerPid) {
47
- metadata.owner_pid = input.ownerPid;
48
- metadata.owner_pid_started_at = processStartedAt(input.ownerPid);
49
- }
50
- const result = db.run(`UPDATE managed_processes
182
+ return db.writeTransaction(() => {
183
+ const current = requireProcess(db, input.id);
184
+ if (current.lease_token !== input.leaseToken || !["starting", "running"].includes(current.state))
185
+ throw new AppError("process_confirmation_rejected", "Managed process changed before confirmation", 1);
186
+ if (current.state === "running") {
187
+ if (prepared.state !== "running" || managedProcessBinding(current, resourceHome(context)).registration_sha256 !== managedProcessBinding(prepared, resourceHome(context)).registration_sha256 || current.pid !== input.pid || input.processGroupId && parseMetadata(current.metadata_json).process_group_id !== input.processGroupId || input.ownerPid && parseMetadata(current.metadata_json).owner_pid !== input.ownerPid)
188
+ throw new AppError("process_confirmation_rejected", "Confirmed process binding changed", 1);
189
+ return { ...current, registration_sha256: managedProcessBinding(current, resourceHome(context)).registration_sha256 };
190
+ }
191
+ const now = context.now();
192
+ const metadata = parseMetadata(current.metadata_json);
193
+ if (input.processGroupId)
194
+ metadata.process_group_id = input.processGroupId;
195
+ metadata.process_birth = processBirth;
196
+ if (ownerPid) {
197
+ metadata.owner_pid = ownerPid;
198
+ metadata.owner_pid_started_at = ownerStartedAt;
199
+ metadata.owner_birth = ownerBirth;
200
+ }
201
+ const result = db.run(`UPDATE managed_processes
51
202
  SET pid = ?, pid_started_at = ?, state = 'running', lease_expires_at = ?, updated_at = ?, metadata_json = ?
52
- WHERE id = ? AND lease_token = ? AND state = 'starting'`, [input.pid, processStartedAt(input.pid), leaseExpiry(now, input.leaseMs), now, JSON.stringify(metadata), input.id, input.leaseToken]);
53
- if (result.changes !== 1)
54
- throw new Error(`Managed process cannot be confirmed: ${input.id}`);
55
- return requireProcess(db, input.id);
203
+ WHERE id = ? AND lease_token = ? AND state = 'starting'`, [input.pid, pidStartedAt, leaseExpiry(now, input.leaseMs), now, JSON.stringify(metadata), input.id, input.leaseToken]);
204
+ if (result.changes !== 1)
205
+ throw new Error(`Managed process cannot be confirmed: ${input.id}`);
206
+ const confirmed = requireProcess(db, input.id);
207
+ return { ...confirmed, registration_sha256: managedProcessBinding(confirmed, resourceHome(context)).registration_sha256 };
208
+ });
209
+ }
210
+ export function prepareManagedProcessHeartbeat(context, input) {
211
+ requireProcessText(input.id, "id");
212
+ requireProcessText(input.leaseToken, "lease-token");
213
+ prepareManagedProcessLease(context, input.leaseMs);
56
214
  }
57
215
  export function heartbeatManagedProcess(context, input) {
58
- const db = registry(context);
59
- return db.run("UPDATE managed_processes SET lease_expires_at = ?, updated_at = ? WHERE id = ? AND lease_token = ? AND state IN ('starting','running','stopping')", [leaseExpiry(context.now(), input.leaseMs), context.now(), input.id, input.leaseToken]).changes === 1;
216
+ return renewManagedProcess(context, input).owned;
217
+ }
218
+ export function renewManagedProcess(context, input) {
219
+ prepareManagedProcessHeartbeat(context, input);
220
+ let db;
221
+ try {
222
+ if (context.resourceDatabaseMode === "prepared_existing")
223
+ registry(context);
224
+ const reader = getResourceDatabase(resourceHome(context), "read_existing", context.databaseDeadlineAt);
225
+ try {
226
+ if (!reader.get("SELECT 1 FROM managed_processes WHERE id=? AND lease_token=? AND state IN ('starting','running','stopping')", [input.id, input.leaseToken]))
227
+ return { owned: false, process_id: input.id };
228
+ }
229
+ finally {
230
+ reader.close?.();
231
+ }
232
+ db = registry(context);
233
+ }
234
+ catch (error) {
235
+ // No lease mutation has started. The storage helper cannot know that
236
+ // operation boundary and deliberately defaults to an unknown effect.
237
+ if (error instanceof AppError && error.code === "process_heartbeat_timeout"
238
+ && maintenanceRetryable({ ...error, details: { ...error.details, effect: "no_effect" } }))
239
+ throw new AppError(error.code, error.message, error.exitCode, { ...error.details, effect: "no_effect", maintenance_phase: "renewal_prepare" });
240
+ throw error;
241
+ }
242
+ return db.writeTransaction(() => {
243
+ const now = context.now();
244
+ if (!db.get("SELECT 1 FROM managed_processes WHERE id=? AND lease_token=? AND state IN ('starting','running','stopping')", [input.id, input.leaseToken]))
245
+ return { owned: false, process_id: input.id };
246
+ const changed = db.run("UPDATE managed_processes SET lease_expires_at = ?, updated_at = ? WHERE id = ? AND lease_token = ? AND state IN ('starting','running','stopping')", [leaseExpiry(now, input.leaseMs), now, input.id, input.leaseToken]).changes === 1;
247
+ if (!changed)
248
+ return { owned: false, process_id: input.id };
249
+ const record = requireProcess(db, input.id);
250
+ return { owned: true, process_id: record.id, lease_expires_at: record.lease_expires_at, registration_sha256: managedProcessBinding(record, resourceHome(context)).registration_sha256 };
251
+ });
252
+ }
253
+ /** Read-only lease check for a caller already holding a RUN writer. Renewal
254
+ * must happen before entering that writer, never from this assertion. */
255
+ export function assertManagedProcessLease(context, input) {
256
+ let reader;
257
+ try {
258
+ reader = getResourceDatabase(resourceHome(context), "read_isolated", context.databaseDeadlineAt);
259
+ const current = reader.get("SELECT pid, lease_expires_at, state FROM managed_processes WHERE id = ? AND lease_token = ?", [input.id, input.leaseToken]);
260
+ if (!current || current.pid !== input.pid || !["starting", "running"].includes(current.state)) {
261
+ throw new AppError("controller_lease_lost", "Managed owner lease changed before productive dispatch", 1, { process_id: input.id });
262
+ }
263
+ if (!Number.isFinite(Date.parse(current.lease_expires_at)) || Date.parse(current.lease_expires_at) <= Date.now())
264
+ throw new AppError("controller_lease_unconfirmed", "Managed owner requires renewal before productive dispatch", 1, { process_id: input.id, reason: "renewal_required" });
265
+ }
266
+ catch (error) {
267
+ const failure = errorRecord(error);
268
+ // This assertion only reads. Unavailable observation is not evidence of
269
+ // owner loss; the caller must reconfirm outside its RUN writer.
270
+ if (maintenanceRetryable({ ...failure, details: { ...failure.details, effect: "no_effect" } }))
271
+ throw new AppError("controller_lease_unconfirmed", "Managed owner observation requires confirmation before productive dispatch", 1, { process_id: input.id, reason: "observation_unavailable", cause: failure });
272
+ throw error;
273
+ }
274
+ finally {
275
+ reader?.close?.();
276
+ }
277
+ }
278
+ export function prepareManagedProcessFinish(context, input) {
279
+ requireProcessText(input.id, "id");
280
+ requireProcessText(input.leaseToken, "lease-token");
281
+ if (input.state !== "stopped" && input.state !== "failed")
282
+ throw new AppError("validation", "--state must be stopped or failed", 2);
283
+ if (input.reason !== undefined && input.reason !== null && typeof input.reason !== "string")
284
+ throw new AppError("validation", "reason must be a string or null", 2);
285
+ return readManagedProcess(context, input.id);
60
286
  }
61
287
  export function finishManagedProcess(context, input) {
288
+ prepareManagedProcessFinish(context, input);
62
289
  const db = registry(context);
63
- const existing = requireProcess(db, input.id);
64
- if (existing.lease_token === input.leaseToken && existing.state === input.state)
65
- return true;
66
- const now = context.now();
67
- const updated = db.run("UPDATE managed_processes SET state = ?, termination_reason = ?, finished_at = ?, updated_at = ?, lease_expires_at = ? WHERE id = ? AND lease_token = ? AND state IN ('starting','running','stopping','orphaned')", [input.state, input.reason ?? null, now, now, now, input.id, input.leaseToken]).changes === 1;
68
- if (updated)
69
- db.run("DELETE FROM managed_resources WHERE process_id = ?", [input.id]);
70
- return updated;
290
+ return db.writeTransaction(() => {
291
+ const existing = requireProcess(db, input.id);
292
+ if (existing.lease_token !== input.leaseToken || existing.state !== input.state && !["starting", "running", "stopping", "orphaned"].includes(existing.state))
293
+ return false;
294
+ if (existing.state === input.state) {
295
+ db.run("DELETE FROM managed_resources WHERE process_id = ?", [input.id]);
296
+ return true;
297
+ }
298
+ const now = context.now();
299
+ const updated = db.run("UPDATE managed_processes SET state = ?, termination_reason = ?, finished_at = ?, updated_at = ?, lease_expires_at = ? WHERE id = ? AND lease_token = ? AND state IN ('starting','running','stopping','orphaned')", [input.state, input.reason ?? null, now, now, now, input.id, input.leaseToken]).changes === 1;
300
+ if (updated)
301
+ db.run("DELETE FROM managed_resources WHERE process_id = ?", [input.id]);
302
+ if (updated)
303
+ return true;
304
+ // Another client may have completed the same lease after our initial read.
305
+ const current = requireProcess(db, input.id);
306
+ return current.lease_token === input.leaseToken && current.state === input.state;
307
+ });
71
308
  }
72
309
  export function processIsAlive(record) {
73
310
  if (!record.pid)
@@ -76,15 +313,71 @@ export function processIsAlive(record) {
76
313
  process.kill(record.pid, 0);
77
314
  }
78
315
  catch (error) {
79
- return error.code !== "ESRCH";
316
+ const code = error.code;
317
+ if (code === "ESRCH")
318
+ return false;
319
+ // A reused PID may now belong to another user. EPERM proves presence,
320
+ // not identity; still compare its observed birth before calling it ours.
321
+ if (code !== "EPERM")
322
+ return true;
323
+ }
324
+ const metadata = record.metadata_json ? parseMetadata(record.metadata_json) : {};
325
+ if (Object.hasOwn(metadata, "process_birth")) {
326
+ const birth = parseBirth(metadata.process_birth), observed = stableProcessBirth(record.pid);
327
+ return !birth || !observed || observed === birth;
80
328
  }
81
329
  const started = processStartedAt(record.pid);
82
330
  return !record.pid_started_at || !started || record.pid_started_at === started;
83
331
  }
332
+ /** Unlike an admission guard, observability must distinguish unknown from
333
+ * alive. Unknown identity still cannot authorize termination or finalization. */
334
+ export function managedProcessObservation(record) {
335
+ if (!record.pid || !record.pid_started_at)
336
+ return 'unknown';
337
+ try {
338
+ process.kill(record.pid, 0);
339
+ }
340
+ catch (error) {
341
+ const code = error.code;
342
+ if (code === "ESRCH")
343
+ return "absent";
344
+ if (code !== "EPERM")
345
+ return "unknown";
346
+ }
347
+ const reply = spawnSync('ps', ['-o', 'lstart=,stat=', '-p', String(record.pid)], { encoding: 'utf8', timeout: 1000, env: { ...process.env, TZ: 'UTC', LC_ALL: 'C' } });
348
+ if (reply.status !== 0 || !reply.stdout.trim())
349
+ return 'unknown';
350
+ const parts = reply.stdout.trim().split(/\s+/), state = parts.pop();
351
+ if (state?.startsWith('Z'))
352
+ return 'absent';
353
+ const metadata = parseMetadata(record.metadata_json);
354
+ if (Object.hasOwn(metadata, 'process_birth')) {
355
+ const expected = parseBirth(metadata.process_birth);
356
+ if (!expected)
357
+ return 'unknown';
358
+ const observed = `ps-lstart-utc-c@1:${parts.join(' ')}`;
359
+ return expected.replace(/\s+/g, ' ') === observed ? 'alive' : 'absent';
360
+ }
361
+ // Legacy lstart lacks a recorded timezone. A mismatch might be timezone
362
+ // drift, so only equal identity or a missing PID establishes an outcome.
363
+ const legacy = processStartedAt(record.pid);
364
+ return legacy && legacy === record.pid_started_at ? 'alive' : 'unknown';
365
+ }
84
366
  export function processTreeIsAlive(record) {
85
367
  if (processIsAlive(record))
86
368
  return true;
87
369
  const group = parseMetadata(record.metadata_json).process_group_id;
370
+ const birth = parseBirth(parseMetadata(record.metadata_json).process_birth);
371
+ // POSIX cannot reuse a leader PID while its old process group survives.
372
+ if (process.platform !== "win32" && group === record.pid && record.pid && birth) {
373
+ try {
374
+ process.kill(record.pid, 0);
375
+ }
376
+ catch { /* A missing leader can still have living children. */ }
377
+ const observed = stableProcessBirth(record.pid);
378
+ if (observed && observed !== birth)
379
+ return false;
380
+ }
88
381
  if (process.platform !== "win32" && typeof group === "number") {
89
382
  try {
90
383
  process.kill(-group, 0);
@@ -100,7 +393,7 @@ export function managedOwnerIsAlive(record) {
100
393
  const metadata = parseMetadata(record.metadata_json);
101
394
  if (typeof metadata.owner_pid !== "number")
102
395
  return null;
103
- return processIsAlive({ pid: metadata.owner_pid, pid_started_at: typeof metadata.owner_pid_started_at === "string" ? metadata.owner_pid_started_at : null });
396
+ return processIsAlive({ pid: metadata.owner_pid, pid_started_at: typeof metadata.owner_pid_started_at === "string" ? metadata.owner_pid_started_at : null, metadata_json: JSON.stringify({ process_birth: metadata.owner_birth }) });
104
397
  }
105
398
  /**
106
399
  * Expiry proves that observation stopped, not that the owner died. A live PID
@@ -110,52 +403,93 @@ export function managedOwnerIsAlive(record) {
110
403
  export function claimExpiredManagedProcesses(context, ownerId) {
111
404
  const db = registry(context);
112
405
  const now = context.now();
113
- db.exec("BEGIN IMMEDIATE");
114
- try {
115
- const candidates = db.all("SELECT * FROM managed_processes WHERE state IN ('starting','running','stopping','orphaned') AND lease_expires_at < ? ORDER BY lease_expires_at, id", [now]);
406
+ const candidates = db.all("SELECT * FROM managed_processes WHERE state IN ('starting','running','stopping','orphaned') AND lease_expires_at < ? ORDER BY lease_expires_at, id", [now]).filter(candidate => {
407
+ const ownerAlive = managedOwnerIsAlive(candidate);
408
+ return ownerAlive !== true && !(ownerAlive === null && processTreeIsAlive(candidate));
409
+ });
410
+ return db.writeTransaction(() => {
116
411
  const claimed = [];
117
412
  for (const candidate of candidates) {
118
- const ownerAlive = managedOwnerIsAlive(candidate);
119
- if (ownerAlive === true || (ownerAlive === null && processTreeIsAlive(candidate)))
413
+ // A heartbeat or replacement registration invalidates the pre-writer
414
+ // death proof; expiry alone never authorizes claiming that new owner.
415
+ if (JSON.stringify(db.get("SELECT * FROM managed_processes WHERE id = ?", [candidate.id])) !== JSON.stringify(candidate))
120
416
  continue;
121
417
  const token = crypto.randomUUID();
122
418
  const result = db.run("UPDATE managed_processes SET owner_id = ?, lease_token = ?, state = 'orphaned', lease_expires_at = ?, updated_at = ? WHERE id = ? AND lease_token = ? AND state IN ('starting','running','stopping','orphaned') AND lease_expires_at < ?", [ownerId, token, leaseExpiry(now), now, candidate.id, candidate.lease_token, now]);
123
419
  if (result.changes === 1)
124
420
  claimed.push({ ...candidate, owner_id: ownerId, lease_token: token, state: "orphaned", updated_at: now });
125
421
  }
126
- db.exec("COMMIT");
127
422
  return claimed;
423
+ });
424
+ }
425
+ /** Workspace gates are leases, not spawned processes. Children retain their own registrations. */
426
+ export function managedLeaseReleased(record) {
427
+ const metadata = parseMetadata(record.metadata_json);
428
+ return record.kind === "workspace-gate" && record.pid === null && record.pid_started_at === null
429
+ && metadata.process_group_id == null && metadata.role === "check_gate"
430
+ && ["stopped", "failed"].includes(record.state) && Boolean(record.finished_at);
431
+ }
432
+ export function managedProcessStatus(context) {
433
+ const db = getResourceDatabase(resourceHome(context), "read_existing", context.databaseDeadlineAt);
434
+ try {
435
+ return db.all("SELECT * FROM managed_processes ORDER BY updated_at DESC, id");
128
436
  }
129
- catch (error) {
130
- db.exec("ROLLBACK");
131
- throw error;
437
+ finally {
438
+ db.close?.();
132
439
  }
133
440
  }
134
- export function managedProcessStatus(context) {
135
- return registry(context).all("SELECT * FROM managed_processes ORDER BY updated_at DESC, id");
441
+ export function prepareManagedProcessStopOperation(context, input) {
442
+ const graceMs = prepareManagedProcessStop(input);
443
+ const record = readManagedProcess(context, input.id);
444
+ assertStopOwnership(record, input.leaseToken);
445
+ return { record, graceMs };
446
+ }
447
+ function readManagedProcess(context, id) {
448
+ const db = getResourceDatabase(resourceHome(context), "read_existing", context.databaseDeadlineAt);
449
+ try {
450
+ return requireProcess(db, id);
451
+ }
452
+ finally {
453
+ db.close?.();
454
+ }
455
+ }
456
+ function assertStopOwnership(record, leaseToken) {
457
+ if (record.lease_token !== leaseToken)
458
+ throw new AppError("process_lease_lost", "Managed process lease does not match", 1);
459
+ if (["stopped", "failed"].includes(record.state) && !processTreeIsAlive(record))
460
+ return;
461
+ if (!record.pid || !record.pid_started_at)
462
+ throw new AppError("process_identity_unknown", "Managed process has no confirmed physical identity; retain its pending launch", 1);
136
463
  }
137
464
  export async function stopManagedProcess(context, input) {
465
+ const { graceMs } = prepareManagedProcessStopOperation(context, input);
138
466
  const db = registry(context), record = requireProcess(db, input.id);
139
- if (record.lease_token !== input.leaseToken)
140
- throw new Error("Managed process lease does not match");
467
+ assertStopOwnership(record, input.leaseToken);
141
468
  if (["stopped", "failed"].includes(record.state) && !processTreeIsAlive(record))
142
469
  return record;
143
470
  db.run("UPDATE managed_processes SET state = 'stopping', updated_at = ? WHERE id = ? AND lease_token = ? AND state IN ('starting','running','orphaned')", [context.now(), record.id, record.lease_token]);
144
471
  if (processTreeIsAlive(record)) {
145
- terminateOwnedProcess(record, "SIGTERM");
146
- await delay(input.graceMs ?? 1_000);
147
- if (processTreeIsAlive(record)) {
148
- terminateOwnedProcess(record, "SIGKILL");
472
+ const signalErrors = [];
473
+ const termError = terminateOwnedProcess(record, "SIGTERM");
474
+ if (termError)
475
+ signalErrors.push(errorRecord(termError));
476
+ await delay(graceMs);
477
+ if (!termError && input.force !== false && processTreeIsAlive(record)) {
478
+ const killError = terminateOwnedProcess(record, "SIGKILL");
479
+ if (killError)
480
+ signalErrors.push(errorRecord(killError));
149
481
  await delay(250);
150
482
  }
151
483
  if (processTreeIsAlive(record))
152
- throw Object.assign(new Error("Owned process tree could not be safely stopped; resources retained"), { code: "process_tree_not_settled", details: { process_id: record.id } });
484
+ throw Object.assign(new Error("Owned process tree could not be safely stopped; resources retained"), { code: "process_tree_not_settled", details: { process_id: record.id, signal_errors: signalErrors } });
153
485
  }
154
- finishManagedProcess(context, { id: record.id, leaseToken: record.lease_token, state: "stopped", reason: input.reason ?? "requested_stop" });
486
+ if (!finishManagedProcess(context, { id: record.id, leaseToken: record.lease_token, state: "stopped", reason: input.reason ?? "requested_stop" }))
487
+ throw Object.assign(new Error("Managed process ownership changed before settlement"), { code: "process_lease_lost" });
155
488
  return requireProcess(db, record.id);
156
489
  }
157
490
  /** Reconcile only records the system owns and has atomically claimed. */
158
491
  export async function reconcileExpiredManagedProcesses(context, ownerId, graceMs = 1_000) {
492
+ prepareManagedProcessReconcile(ownerId, graceMs);
159
493
  const claimed = claimExpiredManagedProcesses(context, ownerId);
160
494
  const outcomes = [];
161
495
  for (const processRecord of claimed) {
@@ -176,7 +510,34 @@ export async function reconcileExpiredManagedProcesses(context, ownerId, graceMs
176
510
  }
177
511
  return outcomes;
178
512
  }
513
+ export function prepareManagedProcessStop(input) {
514
+ requireProcessText(input.id, "id");
515
+ requireProcessText(input.leaseToken, "lease-token");
516
+ if (input.reason !== undefined)
517
+ requireProcessText(input.reason, "reason");
518
+ if (input.force !== undefined && typeof input.force !== "boolean")
519
+ throw new AppError("validation", "force must be boolean", 2);
520
+ return prepareProcessGrace(input.graceMs);
521
+ }
522
+ export function prepareManagedProcessReconcile(ownerId, graceMs) {
523
+ requireProcessText(ownerId, "owner");
524
+ return prepareProcessGrace(graceMs);
525
+ }
526
+ function requireProcessText(value, parameter) {
527
+ if (typeof value !== "string" || !value.trim())
528
+ throw new AppError("validation", `--${parameter} must be nonempty text`, 2);
529
+ }
530
+ function requireProcessPid(value, parameter) {
531
+ if (!Number.isSafeInteger(value) || Number(value) <= 0)
532
+ throw new AppError("validation", `--${parameter} must be a positive integer`, 2);
533
+ }
534
+ function prepareProcessGrace(value = 1_000) {
535
+ if (!Number.isSafeInteger(value) || value < 0 || value > 2 ** 31 - 1)
536
+ throw new AppError("validation", "--grace-ms must be a nonnegative integer within the supported timer range", 2);
537
+ return value;
538
+ }
179
539
  export async function reservePorts(context, input) {
540
+ prepareManagedProcessLease(context, input.leaseMs);
180
541
  const db = registry(context);
181
542
  const claims = [];
182
543
  const ports = {};
@@ -202,23 +563,42 @@ export async function reservePorts(context, input) {
202
563
  }
203
564
  return { ports, release: () => releasePortClaims(db, claims) };
204
565
  }
205
- function registry(context) { return getResourceDatabase(resourceHome(context)); }
566
+ function registry(context) { return getResourceDatabase(resourceHome(context), context.resourceDatabaseMode, context.databaseDeadlineAt); }
206
567
  function requireProcess(db, id) { const row = db.get("SELECT * FROM managed_processes WHERE id = ?", [id]); if (!row)
207
- throw new Error(`Managed process is missing: ${id}`); return row; }
208
- function leaseExpiry(now, leaseMs = defaultLeaseMs) { return new Date(Date.parse(now) + leaseMs).toISOString(); }
209
- function processStartedAt(pid) { const result = spawnSync("ps", ["-o", "lstart=", "-p", String(pid)], { encoding: "utf8" }); const value = result.status === 0 ? result.stdout.trim() : ""; return value || null; }
568
+ throw new AppError("not_found", `Managed process is missing: ${id}`, 1); return row; }
569
+ export function prepareManagedProcessLease(context, leaseMs) { return leaseExpiry(context.now(), leaseMs); }
570
+ function leaseExpiry(now, leaseMs = defaultLeaseMs) {
571
+ if (!Number.isSafeInteger(leaseMs) || leaseMs <= 0 || !Number.isFinite(new Date(Date.parse(now) + leaseMs).getTime()))
572
+ throw new AppError("validation", "--lease-ms must be a positive integer within the supported date range", 2);
573
+ return new Date(Date.parse(now) + leaseMs).toISOString();
574
+ }
575
+ export function processStartedAt(pid) { const result = spawnSync("ps", ["-o", "lstart=", "-p", String(pid)], { encoding: "utf8", timeout: 1_000 }); const value = result.status === 0 ? result.stdout.trim() : ""; return value || null; }
576
+ function stableProcessBirth(pid) {
577
+ const result = spawnSync("ps", ["-o", "lstart=", "-p", String(pid)], { encoding: "utf8", timeout: 1_000, env: { ...process.env, TZ: "UTC", LC_ALL: "C" } });
578
+ const value = result.status === 0 ? result.stdout.trim() : "";
579
+ return value ? `ps-lstart-utc-c@1:${value}` : null;
580
+ }
581
+ function parseBirth(value) { return typeof value === "string" && value.startsWith("ps-lstart-utc-c@1:") && value.length > "ps-lstart-utc-c@1:".length ? value : null; }
210
582
  function releasePortClaims(db, claims) { for (const claim of claims)
211
583
  db.run("DELETE FROM managed_resources WHERE resource_kind = 'port' AND resource_key = ? AND lease_token = ?", [claim.key, claim.token]); }
212
584
  function terminateOwnedProcess(record, signal) {
213
585
  // Never signal a group merely because its former leader PID was once ours.
214
586
  // PID reuse or a departed leader makes group ownership unprovable.
215
- if (!record.pid || !record.pid_started_at || processStartedAt(record.pid) !== record.pid_started_at)
587
+ if (!record.pid || !record.pid_started_at)
216
588
  return;
217
- const group = parseMetadata(record.metadata_json).process_group_id;
589
+ const metadata = parseMetadata(record.metadata_json);
590
+ const birth = parseBirth(metadata.process_birth);
591
+ if (Object.hasOwn(metadata, "process_birth") ? !birth || stableProcessBirth(record.pid) !== birth : processStartedAt(record.pid) !== record.pid_started_at)
592
+ return;
593
+ const group = metadata.process_group_id;
594
+ // A non-leader PID does not establish ownership of its whole process group.
218
595
  try {
219
- process.kill(process.platform === "win32" || typeof group !== "number" ? record.pid : -group, signal);
596
+ process.kill(process.platform === "win32" || group !== record.pid ? record.pid : -group, signal);
597
+ }
598
+ catch (error) {
599
+ if (error.code !== "ESRCH")
600
+ return Object.assign(error, { details: { process_id: record.id, pid: record.pid, pgid: group ?? null, signal, phase: "owned_process_stop" } });
220
601
  }
221
- catch { /* inspect again below */ }
222
602
  }
223
603
  function parseMetadata(value) { try {
224
604
  const parsed = JSON.parse(value);