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

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 +740 -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 +728 -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 +140 -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 +557 -0
  62. package/dist/harness-runtime/lib/dd-opencode.mjs +363 -0
  63. package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +751 -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 +16 -0
  77. package/dist/harness-runtime/lib/managed-daemon.mjs +683 -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 +16 -0
  92. package/dist/harness-runtime/lib/operation-errors.mjs +127 -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 +250 -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 +1168 -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 +152 -116
  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 +41 -30
  460. package/dist/services/usage.js +49 -101
  461. package/dist/services/verification-matrix.js +375 -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 +228 -104
  470. package/dist/services/vnext-protocolize.js +531 -95
  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 +105 -48
  477. package/dist/shared/entity-references.js +8 -0
  478. package/dist/shared/errors.js +27 -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
@@ -0,0 +1,15 @@
1
+ /** Final native overlay: launch-bound routing, never inherited invocation authority. */
2
+ export function managedFlowEnv(config, daemonId = config.daemonId) {
3
+ const managed = !config.noFlow && Boolean(config.ddFlowHome && config.ddFlowBin && daemonId);
4
+ return {
5
+ DD_FLOW_DAEMON_ID: managed ? daemonId : undefined,
6
+ DD_FLOW_HOME: managed ? config.ddFlowHome : undefined,
7
+ DD_FLOW_BIN: managed ? config.ddFlowBin : undefined,
8
+ DD_FLOW_RESOURCE_HOME: config.resourceHome ?? undefined,
9
+ DD_FLOW_RUNTIME_OWNER: managed && config.runtime_owner ? JSON.stringify(config.runtime_owner) : undefined,
10
+ DD_FLOW_INVOCATION_SCOPE: undefined, DD_FLOW_CURRENT_INVOCATION: undefined,
11
+ DD_FLOW_SESSION_ID: undefined, DD_FLOW_WORK_ID: undefined, DD_FLOW_CONTROLLER_TOKEN: undefined,
12
+ DD_FLOW_CONTROL_TOKEN: undefined, DD_FLOW_SCOPE_CONTROL_TOKEN: undefined,
13
+ DD_FLOW_HOOK_DEADLINE_AT: undefined,
14
+ };
15
+ }
@@ -0,0 +1,101 @@
1
+ import { createHash } from "node:crypto";
2
+ import { mkdir, open, readFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { withRunnerLock } from "./runner-lock.mjs";
5
+
6
+ const text = value => typeof value === "string" && value.trim() ? value.trim() : null;
7
+ const fields = ["provider", "model", "reasoning", "mode", "permission_mode"];
8
+ const observationScope = source => JSON.stringify([source?.channel ?? null, source?.scope ?? null, source?.attempt_id ?? null, source?.turn_id ?? null, source?.non_asserting === true]);
9
+ export const observedFields = value => Object.fromEntries(fields.map(key => [key, text(value?.[key])]));
10
+ export const modelObservationFile = journal => path.join(path.dirname(journal), "model-observations.jsonl");
11
+
12
+ /** Routing is allowed. Permission/autonomy changes remain integrity failures.
13
+ * Missing native fields are unknown, never proof that the request was applied. */
14
+ export function checkObservedProfile(requested, observed, { strict = false, required = [] } = {}) {
15
+ const unknown = fields.filter(key => requested?.[key] && !observed?.[key]);
16
+ const changes = fields.filter(key => requested?.[key] && observed?.[key] && requested[key] !== observed[key]);
17
+ const violations = changes.filter(key => strict || ["mode", "permission_mode"].includes(key));
18
+ const missing = required.filter(key => !observed?.[key]);
19
+ if (violations.length || missing.length) throw Object.assign(new Error("native execution profile integrity is not confirmed"), {
20
+ code: "profile_integrity_violation", details: { requested: observedFields(requested), observed: observedFields(observed), violations, missing }
21
+ });
22
+ return { requested: observedFields(requested), observed: observedFields(observed), status: unknown.length ? "incomplete" : changes.length ? "mixed" : "matched", matched: !unknown.length && !changes.length, unknown, changes };
23
+ }
24
+
25
+ export async function readModelObservations(journal) {
26
+ try { const text = await readFile(modelObservationFile(journal), "utf8"); return text.slice(0, text.lastIndexOf("\n") + 1).split("\n").filter(Boolean).map(JSON.parse); }
27
+ catch (error) { if (error.code === "ENOENT") return []; throw error; }
28
+ }
29
+
30
+ /** Only adapter-verified native facts enter this file. It records changes,
31
+ * not every tool or token; reading it never requires replaying native history. */
32
+ export async function observeModel({ journal, harness, sessionId, parentSessionId = null, requested = {}, observed = {}, source, evidence = "configured", reason = null, nativeTimestamp = null, previousModel = null }) {
33
+ if (!journal || !sessionId) return null;
34
+ if (!source || !["configured", "request", "response", "unavailable"].includes(evidence)) throw new Error("model observation requires a native source and evidence level");
35
+ if (typeof source === "string") source = { channel: source, ...(["configured", "response"].includes(evidence) ? { scope: evidence } : {}) };
36
+ const file = modelObservationFile(journal), profile = observedFields(observed);
37
+ await mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
38
+ return withRunnerLock(file, async () => {
39
+ const events = await readModelObservations(journal);
40
+ const replay = source?.event_sha256 ? events.find(item => item.harness === harness && item.session_id === sessionId && observationScope(item.source) === observationScope(source) && item.source?.event_sha256 === source.event_sha256) : null;
41
+ if (replay) return replay;
42
+ const previous = events.findLast(item => item.harness === harness && item.session_id === sessionId && observationScope(item.source) === observationScope(source));
43
+ const normalizedReason = text(reason)?.slice(0, 200) ?? null;
44
+ if (previous && JSON.stringify(previous.observed) === JSON.stringify(profile) && previous.reason === normalizedReason && previous.evidence === evidence
45
+ && previous.source?.raw_model === source?.raw_model && previous.source?.raw_provider === source?.raw_provider) return previous;
46
+ const previousKnown = events.findLast(item => item.harness === harness && item.session_id === sessionId && item.evidence === evidence && item.observed?.model
47
+ && (item.source?.scope ?? "session") === (source?.scope ?? "session") && (item.source?.attempt_id ?? null) === (source?.attempt_id ?? null));
48
+ const fromModel = text(previousModel) ?? previousKnown?.observed.model ?? null;
49
+ const previousRouting = events.findLast(item => item.harness === harness && item.session_id === sessionId && item.evidence === evidence && item.observed?.provider
50
+ && (item.source?.scope ?? "session") === (source?.scope ?? "session") && (item.source?.attempt_id ?? null) === (source?.attempt_id ?? null));
51
+ const changed = Boolean(profile.model && fromModel && (profile.model !== fromModel || profile.provider && previousRouting && profile.provider !== previousRouting.observed.provider));
52
+ const value = { schema_id: "dd-flow/model-observation@1", sequence: events.length + 1,
53
+ harness, session_id: sessionId, parent_session_id: parentSessionId,
54
+ kind: changed ? "model_changed" : !profile.model ? "model_observation_unavailable" : "model_observed",
55
+ previous_model: fromModel, requested: observedFields(requested), observed: profile,
56
+ source, evidence, reason: normalizedReason, native_timestamp: text(nativeTimestamp), observed_at: new Date().toISOString() };
57
+ value.id = createHash("sha256").update(JSON.stringify(value)).digest("hex");
58
+ const handle = await open(file, "a", 0o600);
59
+ try {
60
+ const bytes = await readFile(file); const committed = bytes.lastIndexOf(10) + 1;
61
+ if (committed < bytes.length) await handle.truncate(committed);
62
+ await handle.write(`${JSON.stringify(value)}\n`); await handle.sync();
63
+ } finally { await handle.close(); }
64
+ const directory = await open(path.dirname(file), "r");
65
+ try { await directory.sync(); } finally { await directory.close(); }
66
+ return value;
67
+ });
68
+ }
69
+
70
+ export function modelAttribution(events, expectedSessions = []) {
71
+ const sessions = new Map();
72
+ for (const event of events) {
73
+ const key = `${event.harness}:${event.session_id}`;
74
+ const session = sessions.get(key) ?? { harness: event.harness, session_id: event.session_id, parent_session_id: event.parent_session_id, requested: event.requested, observations: [] };
75
+ session.parent_session_id ??= event.parent_session_id;
76
+ if (!session.requested || !Object.values(session.requested).some(Boolean)) session.requested = event.requested;
77
+ session.observations.push(event); sessions.set(key, session);
78
+ }
79
+ const observedIds = new Set([...sessions.values()].map(session => session.session_id));
80
+ const missingSessions = expectedSessions.filter(session => typeof session === "string" ? !observedIds.has(session)
81
+ : !sessions.has(`${session.harness}:${session.session_id}`));
82
+ const models = [...new Set(events.map(event => event.observed?.model).filter(Boolean))];
83
+ const providers = [...new Set(events.map(event => event.observed?.provider).filter(Boolean))];
84
+ const incomplete = !events.length || missingSessions.length > 0 || [...sessions.values()].some(session => {
85
+ const known = session.observations.filter(event => event.observed?.model);
86
+ if (!known.length) return true;
87
+ const knownScopes = new Set(known.map(event => `${event.evidence}:${event.source?.scope ?? "session"}:${event.source?.attempt_id ?? ""}:${event.source?.turn_id ?? ""}`));
88
+ return session.observations.some(event => {
89
+ if (event.source?.non_asserting) return false;
90
+ if (["configured", "response"].includes(event.evidence) && !event.observed?.model) return true;
91
+ if (event.evidence !== "unavailable") return false;
92
+ const scope = event.source?.scope;
93
+ return !scope || !knownScopes.has(`configured:${scope}:${event.source?.attempt_id ?? ""}:${event.source?.turn_id ?? ""}`)
94
+ && !knownScopes.has(`response:${scope}:${event.source?.attempt_id ?? ""}:${event.source?.turn_id ?? ""}`);
95
+ });
96
+ });
97
+ return { models, providers, mixed: models.length > 1 || providers.length > 1 || events.some(event => event.observed?.model && event.requested?.model && event.observed.model !== event.requested.model
98
+ || event.observed?.provider && event.requested?.provider && event.observed.provider !== event.requested.provider),
99
+ observation_completeness: incomplete ? "incomplete" : "available_native_sources", transitions: events.filter(event => event.kind === "model_changed"),
100
+ sessions: [...sessions.values()], missing_sessions: missingSessions, usage_attribution: "session_totals_unattributed_to_model", note: "Native settings/events do not prove undisclosed server routing or per-model token costs." };
101
+ }
@@ -0,0 +1,16 @@
1
+ export const NATIVE_CHILD_CONTRACT_VERSION: "native-children@1";
2
+ export function settledNativeTree(root: string, children: Record<string, unknown>[], settled: boolean, rootSettled?: boolean): Record<string, unknown>;
3
+ export function safeNativeFailure(value: unknown): Record<string, unknown> | undefined;
4
+ export interface NativeChild {
5
+ session_id: string;
6
+ parent_session_id: string;
7
+ status: "running" | "unknown" | "completed" | "failed" | "cancelled" | "settled_by_root";
8
+ source: string;
9
+ provenance?: "root_child" | "parent_mismatch";
10
+ terminal_turn_id?: string;
11
+ terminal_observed_at?: string;
12
+ provider_error?: unknown;
13
+ native_source?: "opencode.v2.session.execution" | "agy.result" | "acp.error_response" | "droid.agent_turn_outcome" | "grok.subagent.event";
14
+ native_turn_items?: { observed?: boolean; possible_effects?: boolean; pending?: boolean };
15
+ }
16
+ export function normalizeNativeChildren(receipt: Record<string, unknown>, rootSessionId: string): NativeChild[];
@@ -0,0 +1,113 @@
1
+ export const NATIVE_CHILD_CONTRACT_VERSION = "native-children@1";
2
+ import { errorRecord } from "./operation-errors.mjs";
3
+ import { safeProviderError } from "./provider-overload-policy.mjs";
4
+
5
+ function nativeFailure(code, message, exitCode, details) { return Object.assign(new Error(message), { code, exitCode, details }); }
6
+
7
+ /** Project a positively settled native tree, never infer it from missing activity. */
8
+ export function settledNativeTree(root, children, settled, rootSettled = false) {
9
+ if (settled !== true && rootSettled !== true || typeof root !== "string" || !root || !Array.isArray(children)) return {};
10
+ const ids = children.map(child => child.provider_session_id ?? child.session_id ?? child.childSessionId ?? child.sessionId);
11
+ if (ids.some(id => typeof id !== "string" || !id || id === root) || new Set(ids).size !== ids.length) return {};
12
+ const observations = [{ provider_session_id: root, status: "idle", settled: true }, ...children.map((child, index) => {
13
+ const status = String(child.status ?? child.state ?? "unknown").toLowerCase();
14
+ const terminal = settled === true || ["completed", "success", "succeeded", "failed", "cancelled", "canceled", "settled_by_root"].includes(status);
15
+ return { provider_session_id: ids[index], status: terminal ? "idle" : ["running", "active", "inprogress", "pending"].includes(status) ? "running" : "unknown", settled: terminal };
16
+ })];
17
+ return { tree_session_ids: [root, ...ids], unsettled_sessions: observations.filter(item => !item.settled).map(item => item.provider_session_id), tree_observations: observations };
18
+ }
19
+
20
+ /** Retain typed failure facts, never provider headers, request bodies or free-form messages. */
21
+ export function safeNativeFailure(value, depth = 0) {
22
+ if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
23
+ const result = {};
24
+ for (const key of ["type", "code", "source", "reason", "errorPhase", "exceptionKind", "providerId", "modelId", "providerKind", "transport", "retryable", "willRetry", "statusCode", "resets_at", "retryAfterMs", "resetAt"]) {
25
+ const scalar = value[key];
26
+ if (typeof scalar === "boolean" || typeof scalar === "number" && Number.isFinite(scalar)
27
+ || typeof scalar === "string" && scalar.length <= 256 && /^[\w.:/ -]+$/.test(scalar)) result[key] = scalar;
28
+ }
29
+ // Timing metadata is not an identifier: HTTP dates contain commas and an
30
+ // invalid value must reach the policy as invalid, not disappear into default backoff.
31
+ const timing = safeProviderError(value);
32
+ if (Object.hasOwn(timing, "retryAfter")) result.retryAfter = timing.retryAfter;
33
+ if (typeof value.codexErrorInfo === "string" && /^[\w-]{1,128}$/.test(value.codexErrorInfo)) result.codexErrorInfo = value.codexErrorInfo;
34
+ else if (value.codexErrorInfo && typeof value.codexErrorInfo.code === "string" && /^[\w-]{1,128}$/.test(value.codexErrorInfo.code)) result.codexErrorInfo = { code: value.codexErrorInfo.code };
35
+ const attribution = depth === 0 && value.attribution && safeNativeFailure(value.attribution, 1);
36
+ if (attribution && Object.keys(attribution).length) result.attribution = attribution;
37
+ return Object.keys(result).length ? result : undefined;
38
+ }
39
+
40
+
41
+ /** Normalize provider-observed topology only; never parse assistant prose. */
42
+ export function normalizeNativeChildren(receipt, rootSessionId) {
43
+ const candidates = [];
44
+ const object = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
45
+ receipt = object(receipt);
46
+ const list = (value) => Array.isArray(value) ? value : [];
47
+ const text = (value, keys) => { for (const key of keys) if (typeof value[key] === "string" && value[key]) return value[key]; return null; };
48
+ const settledTree = new Set(
49
+ list(receipt.tree_observations)
50
+ .filter(value => object(value).settled === true)
51
+ .map(value => text(object(value), ["provider_session_id", "session_id"]))
52
+ .filter(id => id !== null)
53
+ );
54
+ const add = (raw, source, scopedParent = null, fallback = "unknown") => {
55
+ const value = object(raw);
56
+ const id = text(value, ["provider_session_id", "session_id", "sessionId", "childSessionId", "conversation_id", "conversationId", "id", "subagentId"]);
57
+ const parent = text(value, ["parent_provider_session_id", "parent_session_id", "parentSessionId", "parentID", "parentId"]) ?? scopedParent;
58
+ if (!id) return;
59
+ const outcomeError = object(value.outcome_error);
60
+ if (typeof outcomeError.code === "string") {
61
+ const record = errorRecord(outcomeError);
62
+ throw Object.assign(nativeFailure(record.code, record.message, 1, record.details), record, { details: { ...object(record.details), session_id: id, source } });
63
+ }
64
+ const state = String(value.status ?? value.state ?? fallback).toLowerCase();
65
+ if (state === "lost") throw nativeFailure("native_child_outcome_lost", "Native child ended without a recoverable outcome", 1, { session_id: id, parent_session_id: parent, source, native_status: state });
66
+ // Provider idle means no current activity, not a Work result. Only an
67
+ // explicit terminal result or a separately proven root settlement may be
68
+ // terminal here; unknown states stay unknown for reconciliation.
69
+ const status = ["completed", "complete", "done", "success", "succeeded"].includes(state) ? "completed" : state === "settled_by_root" ? "settled_by_root" : ["failed", "error"].includes(state) ? "failed" : ["cancelled", "canceled", "interrupted"].includes(state) ? "cancelled" : ["running", "active", "pending"].includes(state) ? "running" : fallback;
70
+ const rootChild = parent === rootSessionId;
71
+ const settled = rootChild && status === "unknown" && settledTree.has(id) ? "settled_by_root" : status;
72
+ candidates.push({ session_id: id, parent_session_id: parent ?? "", status: settled, source, ...(rootChild ? {} : { provenance: "parent_mismatch" }),
73
+ ...(text(value, ["terminal_turn_id"]) ? { terminal_turn_id: text(value, ["terminal_turn_id"]) } : {}),
74
+ ...(text(value, ["terminal_observed_at"]) ? { terminal_observed_at: text(value, ["terminal_observed_at"]) } : {}),
75
+ ...(value.provider_error !== undefined ? { provider_error: value.provider_error } : {}),
76
+ ...(["opencode.v2.session.execution", "agy.result", "acp.error_response", "droid.agent_turn_outcome", "grok.subagent.event"].includes(value.native_source) ? { native_source: value.native_source } : {}),
77
+ ...(value.native_turn_items && typeof value.native_turn_items === "object" ? { native_turn_items: value.native_turn_items } : {}) });
78
+ };
79
+ for (const value of list(receipt.descendants)) add(value, "adapter.descendants");
80
+ for (const value of list(receipt.children)) add(value, "opencode.session.children");
81
+ const subagents = object(object(receipt.evidence).subagents ?? receipt.subagents);
82
+ for (const value of list(subagents.running)) add(value, "zcode/session/subagents", rootSessionId, "running");
83
+ for (const value of list(subagents.completed)) add(value, "zcode/session/subagents", rootSessionId, "completed");
84
+ for (const value of list(object(subagents.ended).items)) {
85
+ const status = String(object(value).status ?? "unknown");
86
+ if (!["success", "failed", "cancelled", "lost"].includes(status)) throw nativeFailure("native_child_outcome_unavailable", "ZCode ended child has no recognized native outcome", 1, { child: value });
87
+ add(value, "zcode/session/subagents", rootSessionId);
88
+ }
89
+ const unique = new Map();
90
+ const rank = { unknown: 0, running: 1, settled_by_root: 2, completed: 3, failed: 3, cancelled: 3 };
91
+ for (const child of candidates) {
92
+ const previous = unique.get(child.session_id);
93
+ if (previous && previous.parent_session_id !== child.parent_session_id) {
94
+ // Keep the contradictory parent visible to the existing provenance gate,
95
+ // independently of input order or which source happened to say success.
96
+ const foreign = [previous, child].filter(item => item.parent_session_id !== rootSessionId).sort((a, b) => a.parent_session_id.localeCompare(b.parent_session_id))[0];
97
+ unique.set(child.session_id, { ...foreign, provenance: "parent_mismatch" });
98
+ continue;
99
+ }
100
+ if (previous && rank[previous.status] === 3 && rank[child.status] === 3 && previous.status !== child.status) throw nativeFailure("native_child_outcome_conflict", "Native child has contradictory terminal outcomes", 1, { session_id: child.session_id, observations: [previous, child] });
101
+ if (previous && previous.status === child.status && previous.terminal_turn_id && child.terminal_turn_id && previous.terminal_turn_id !== child.terminal_turn_id)
102
+ throw nativeFailure("native_child_turn_conflict", "Native child has contradictory terminal Turn identities", 1, { session_id: child.session_id, observations: [previous, child] });
103
+ if (previous && previous.status === child.status) {
104
+ unique.set(child.session_id, { ...previous, ...child,
105
+ ...(child.terminal_turn_id ?? previous.terminal_turn_id ? { terminal_turn_id: child.terminal_turn_id ?? previous.terminal_turn_id } : {}),
106
+ ...(child.provider_error ?? previous.provider_error ? { provider_error: child.provider_error ?? previous.provider_error } : {}),
107
+ ...(child.native_turn_items ?? previous.native_turn_items ? { native_turn_items: child.native_turn_items ?? previous.native_turn_items } : {}) });
108
+ continue;
109
+ }
110
+ if (!previous || rank[child.status] > rank[previous.status]) unique.set(child.session_id, child);
111
+ }
112
+ return [...unique.values()].sort((left, right) => left.session_id.localeCompare(right.session_id));
113
+ }
@@ -0,0 +1,7 @@
1
+ export function invokeNativeHook(input: { bin: string; args: string[]; nodeBin?: string; payload: unknown; home?: string | undefined; cwd?: string; env?: NodeJS.ProcessEnv; timeoutMs?: number }): Promise<Record<string, unknown> & { timing?: Record<string, number> }>;
2
+ export function hookBudget(timeoutMs?: number, deadlineAt?: number | null): { deadlineAt: number; remaining(phase: string, maximum?: number): number; run<T>(action: () => T | Promise<T>, phase: string): Promise<T> };
3
+ export function readNativeHookInput(stream: NodeJS.ReadStream, budget: { remaining(phase: string): number }, maxBytes?: number): Promise<string>;
4
+ export function acceptNativeHookRequest(stateDir: string, binding: Record<string, unknown>, current?: { requests: Map<string, { binding: Record<string, unknown> }>; accepting?: Map<string, Promise<Record<string, unknown>>> } | null): Promise<Record<string, unknown>>;
5
+ export function writeNativeHookFailure(stateDir: string, binding: Record<string, unknown> | undefined, error: unknown, budget: ReturnType<typeof hookBudget>): Promise<void>;
6
+ export function readNativeHookFailures(stateDir: string, requests: Map<string, { current: unknown; binding: Record<string, unknown> }>, current: unknown, reject: (error: Error & { code?: string; details?: Record<string, unknown> }) => void): Promise<void>;
7
+ export function runBoundedCommand(input: { bin: string; args: string[]; nodeBin?: string; input?: string | null; cwd?: string; env?: NodeJS.ProcessEnv; timeoutMs?: number; timeoutCode?: string; inheritDeadline?: boolean; cleanupWithinBudget?: boolean; maintenanceProgress?: { action: string; processId: string | null; startupMs?: number; inactivityMs: number } | null; signal?: AbortSignal }): Promise<string>;
@@ -0,0 +1,251 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createHash, randomUUID } from "node:crypto";
3
+ import { readFile } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { reportedFailure } from "./process-json.mjs";
6
+ import { errorRecord, reportedError } from "./operation-errors.mjs";
7
+ import { writeJsonAtomic } from "./runner-events.mjs";
8
+
9
+ /** Retain accepted native requests in the existing operation hook ledger. */
10
+ export async function acceptNativeHookRequest(stateDir, binding, current = null) {
11
+ if (["operation_id", "daemon_id", "tool_call_id", "root_provider_session_id", "provider_session_id"].some(key => typeof binding[key] !== "string" || !binding[key])
12
+ || !Number.isSafeInteger(binding.turn_generation) || binding.turn_generation < 0
13
+ || !/^[a-f0-9]{64}$/.test(binding.command_sha256 ?? "")) throw Object.assign(new Error("Native hook has no current owned operation"), { code: "native_hook_unproven" });
14
+ const key = JSON.stringify([binding.provider_session_id, binding.tool_call_id, binding.event]);
15
+ const existing = current && [...current.requests.values()].find(value => JSON.stringify([value.binding.provider_session_id, value.binding.tool_call_id, value.binding.event]) === key)?.binding;
16
+ const same = value => {
17
+ if (value.command_sha256 !== binding.command_sha256 || value.native_cwd !== binding.native_cwd) throw Object.assign(new Error("Native tool identity was reused with changed hook input"), { code: "native_hook_identity_conflict" });
18
+ return value;
19
+ };
20
+ if (existing) return same(existing);
21
+ current && (current.accepting ??= new Map());
22
+ if (current?.accepting.has(key)) return same(await current.accepting.get(key));
23
+ const accepted = (async () => {
24
+ const directory = path.join(stateDir, "operations", createHash("sha256").update(binding.operation_id).digest("hex"));
25
+ const requested = JSON.parse(await readFile(path.join(directory, "requested.json"), "utf8"));
26
+ if (requested.operation_id !== binding.operation_id || requested.daemon_id !== binding.daemon_id || !["session.prompt", "session.create", "session.start"].includes(requested.operation)
27
+ || requested.operation === "session.prompt" && requested.session_id && requested.session_id !== binding.root_provider_session_id) throw Object.assign(new Error("Native hook operation does not match its retained owner"), { code: "native_hook_unproven" });
28
+ const receipt = { ...binding, schema_id: "dd-flow/hook-request@1", request_id: randomUUID() };
29
+ await writeJsonAtomic(path.join(directory, "hooks", `${receipt.request_id}.json`), receipt);
30
+ return receipt;
31
+ })();
32
+ current?.accepting.set(key, accepted);
33
+ try { return await accepted; } finally { current?.accepting.delete(key); }
34
+ }
35
+
36
+ export async function writeNativeHookFailure(stateDir, binding, error, budget) {
37
+ if (!binding?.request_id || !/^[A-Za-z0-9-]{1,128}$/.test(binding.request_id)) return;
38
+ await budget.run(() => writeJsonAtomic(path.join(stateDir, "hook-failures", `${binding.request_id}.json`), {
39
+ ...errorRecord(error), request_id: binding.request_id, observed_at: new Date().toISOString(), native_hook_binding: binding,
40
+ }), "failure_receipt");
41
+ }
42
+
43
+ /** Read only requests accepted for this exact live operation, never scan a mailbox. */
44
+ export async function readNativeHookFailures(stateDir, requests, current, reject) {
45
+ for (const [id, accepted] of requests) {
46
+ if (accepted.current !== current) continue;
47
+ let value;
48
+ try { value = JSON.parse(await readFile(path.join(stateDir, "hook-failures", `${id}.json`), "utf8")); }
49
+ catch (error) { if (error.code === "ENOENT") continue; value = { code: "native_hook_receipt_invalid", message: error.message }; }
50
+ if (value.request_id !== id || !value.native_hook_binding || JSON.stringify(value.native_hook_binding) !== JSON.stringify(accepted.binding) || typeof value.code !== "string" || typeof value.message !== "string") {
51
+ if (!accepted.diagnostic) {
52
+ accepted.diagnostic = { code: "native_hook_receipt_invalid", message: "Accepted hook request has an unreadable or unbound failure receipt", request_id: id, operation_id: accepted.binding.operation_id, effect: "unknown", receipt_bound: false };
53
+ process.stderr.write(`${JSON.stringify({ native_hook_diagnostic: accepted.diagnostic })}\n`);
54
+ }
55
+ continue;
56
+ }
57
+ requests.delete(id);
58
+ const error = reportedError(value, "Native hook failed");
59
+ error.details = { ...error.details, native_hook_binding: accepted.binding };
60
+ reject(error);
61
+ }
62
+ }
63
+
64
+ /** The caller supplies its qualified native limit; phases share that deadline. */
65
+ export function hookBudget(timeoutMs = 25_000, deadlineAt = process.env.DD_FLOW_HOOK_DEADLINE_AT ? Number(process.env.DD_FLOW_HOOK_DEADLINE_AT) : null) {
66
+ if (deadlineAt !== null && (!Number.isSafeInteger(deadlineAt) || deadlineAt < 0)) throw Object.assign(new Error("Invalid native hook deadline"), { code: "hook_deadline_invalid", details: { effect: "unknown" } });
67
+ const started = performance.now();
68
+ const duration = Math.max(0, Math.min(timeoutMs, Number.isFinite(deadlineAt) ? deadlineAt - Date.now() : timeoutMs));
69
+ const remaining = (phase, maximum = duration) => {
70
+ const value = duration - (performance.now() - started);
71
+ if (value <= 0) throw Object.assign(new Error(`Native hook deadline exceeded during ${phase}`), { code: "native_hook_timeout", details: { phase, elapsed_ms: Math.round(performance.now() - started), effect: "unknown" } });
72
+ return Math.max(1, Math.floor(Math.min(maximum, value)));
73
+ };
74
+ return { deadlineAt: Math.floor(Date.now() + duration), remaining, async run(action, phase) {
75
+ const timeoutMs = remaining(phase);
76
+ let timer;
77
+ try { return await Promise.race([Promise.resolve().then(action), new Promise((_, reject) => { timer = setTimeout(() => reject(Object.assign(new Error(`Native hook deadline exceeded during ${phase}`), { code: "native_hook_timeout", details: { phase, effect: "unknown" } })), timeoutMs); })]); }
78
+ finally { clearTimeout(timer); }
79
+ } };
80
+ }
81
+
82
+ /** Bound native hook ingress before parsing or opening adapter state. */
83
+ export function readNativeHookInput(stream, budget, maxBytes = 1_048_576) {
84
+ return new Promise((resolve, reject) => {
85
+ const chunks = []; let bytes = 0, done = false;
86
+ const cleanup = () => { clearTimeout(timer); stream.off("data", onData); stream.off("end", onEnd); stream.off("error", onError); };
87
+ const finish = (error, value) => { if (done) return; done = true; cleanup(); if (error) { stream.destroy(); reject(error); } else resolve(value); };
88
+ const onData = chunk => {
89
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
90
+ bytes += buffer.length;
91
+ if (bytes > maxBytes) finish(Object.assign(new Error("Native hook input exceeds 1 MiB"), { code: "native_hook_input_too_large" }));
92
+ else chunks.push(buffer);
93
+ };
94
+ const onEnd = () => finish(null, Buffer.concat(chunks).toString("utf8"));
95
+ const onError = error => finish(error);
96
+ let timeoutMs;
97
+ try { timeoutMs = budget.remaining("input"); } catch (error) { reject(error); return; }
98
+ const timer = setTimeout(() => finish(Object.assign(new Error("Native hook deadline exceeded during input"), { code: "native_hook_timeout", details: { phase: "input", effect: "unknown" } })), timeoutMs);
99
+ stream.on("data", onData).once("end", onEnd).once("error", onError);
100
+ stream.resume();
101
+ });
102
+ }
103
+
104
+ export async function terminateCommandTree(child, { platform = process.platform, launch = spawn, timeoutMs = 400 } = {}) {
105
+ if (!child.pid) return;
106
+ if (platform !== "win32") { process.kill(-child.pid, "SIGKILL"); return; }
107
+ // Node's child.kill on Windows only terminates the leader. taskkill is the
108
+ // native subtree primitive; bound its own lifetime as well as the request.
109
+ await new Promise((resolve, reject) => {
110
+ const killer = launch("taskkill", ["/PID", String(child.pid), "/T", "/F"], { windowsHide: true, stdio: "ignore" });
111
+ let settled = false, timer;
112
+ const finish = error => { if (settled) return; settled = true; clearTimeout(timer); error ? reject(error) : resolve(); };
113
+ timer = setTimeout(() => { killer.kill("SIGKILL"); killer.unref(); finish(Object.assign(new Error("Windows subtree cleanup timed out"), { code: "process_cleanup_timeout" })); }, timeoutMs);
114
+ killer.once("error", finish);
115
+ killer.once("close", code => finish(code === 0 ? null : Object.assign(new Error(`taskkill exited ${code}`), { code: "process_cleanup_failed" })));
116
+ });
117
+ }
118
+
119
+ /** Preserve the native hook protocol across the CLI process boundary. */
120
+ export async function runBoundedCommand({ bin, args, nodeBin = process.execPath, input = null, cwd, env = {}, timeoutMs = 15_000, timeoutCode = "native_hook_timeout", inheritDeadline = true, cleanupWithinBudget = false, maintenanceProgress = null, signal }) {
121
+ if (signal?.aborted) throw Object.assign(new Error("Bounded CLI request cancelled before launch"), { code: "process_maintenance_cancelled", details: { effect: "no_effect" } });
122
+ const inherited = inheritDeadline ? env.DD_FLOW_HOOK_DEADLINE_AT ?? process.env.DD_FLOW_HOOK_DEADLINE_AT : undefined;
123
+ if (inherited !== undefined) {
124
+ const deadline = Number(inherited);
125
+ if (!Number.isSafeInteger(deadline) || deadline < 0) throw Object.assign(new Error("Invalid native hook deadline"), { code: "hook_deadline_invalid", details: { effect: "no_effect" } });
126
+ timeoutMs = Math.min(timeoutMs, deadline - Date.now());
127
+ }
128
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) throw Object.assign(new Error("Native hook budget exhausted before subprocess launch"), { code: timeoutCode, details: { phase: "subprocess", effect: "no_effect" } });
129
+ if (maintenanceProgress && (!Number.isFinite(maintenanceProgress.inactivityMs) || maintenanceProgress.inactivityMs <= 0
130
+ || maintenanceProgress.startupMs !== undefined && (!Number.isFinite(maintenanceProgress.startupMs) || maintenanceProgress.startupMs <= 0)
131
+ || typeof maintenanceProgress.action !== "string" || !(maintenanceProgress.processId === null || typeof maintenanceProgress.processId === "string")))
132
+ throw Object.assign(new Error("Invalid maintenance progress budget"), { code: "process_maintenance_timeout", details: { effect: "no_effect", native_dispatch_started: false } });
133
+ const started = performance.now();
134
+ // Transport/physical cleanup has its own small tail inside the outer limit.
135
+ const cleanupMs = Math.min(500, timeoutMs / 5);
136
+ const commandMs = cleanupWithinBudget ? Math.max(1, timeoutMs - cleanupMs) : timeoutMs;
137
+ const deadlineAt = Math.floor(Date.now() + Math.max(0, commandMs - Math.min(250, commandMs / 10)));
138
+ const script = /\.[cm]?js$/.test(bin);
139
+ const output = await new Promise((resolve, reject) => {
140
+ const child = spawn(script ? nodeBin : bin, [...(script ? [bin] : []), ...args], {
141
+ cwd, env: { ...process.env, ...env, DD_FLOW_HOOK_DEADLINE_AT: String(deadlineAt) }, detached: process.platform !== "win32", stdio: ["pipe", "pipe", "pipe"]
142
+ });
143
+ let stdout = "", stderr = "", failure, cleanupTimer, settled = false, termination;
144
+ let timer, phaseBuffer = "", lastProgress = started;
145
+ const progressPhases = new Set();
146
+ let importsReady = maintenanceProgress?.startupMs === undefined;
147
+ const silenceMs = () => importsReady ? maintenanceProgress?.inactivityMs : maintenanceProgress.startupMs;
148
+ const commandDeadline = started + commandMs;
149
+ const finish = (error, value) => {
150
+ if (settled) return;
151
+ settled = true; clearTimeout(timer); clearTimeout(cleanupTimer); signal?.removeEventListener("abort", abort);
152
+ if (error) {
153
+ const diagnostics = hookDiagnostics(stderr);
154
+ if (diagnostics.length) error.details = { ...error.details, hook_diagnostics: diagnostics };
155
+ reject(error);
156
+ } else resolve(value);
157
+ };
158
+ const stop = error => {
159
+ if (termination || settled) return;
160
+ failure ??= error;
161
+ clearTimeout(timer);
162
+ failure.details = { ...failure.details, failure_at: new Date().toISOString(), elapsed_ms: Math.round(performance.now() - started), effect: "unknown", subprocess_pid: child.pid ?? null };
163
+ termination = terminateCommandTree(child).catch(cleanup => {
164
+ if (cleanup.code === "ESRCH") return;
165
+ failure.details.cleanup_error = { code: cleanup.code, message: cleanup.message };
166
+ failure.details.cleanup_unconfirmed = true;
167
+ child.kill("SIGKILL");
168
+ });
169
+ cleanupTimer ??= setTimeout(() => {
170
+ failure.details.cleanup_unconfirmed = true;
171
+ child.stdin.destroy(); child.stdout.destroy(); child.stderr.destroy(); child.unref();
172
+ finish(failure);
173
+ }, cleanupWithinBudget ? cleanupMs : 500);
174
+ };
175
+ // A useful, request-bound phase advances the silence window, not ownership
176
+ // authority. The child storage deadline and parent uncertainty horizon stay
177
+ // fixed; repeated phases, logging and polling cannot keep a writer alive.
178
+ const arm = () => {
179
+ clearTimeout(timer);
180
+ const now = performance.now();
181
+ const remaining = Math.min(commandDeadline - now, maintenanceProgress ? lastProgress + silenceMs() - now : Infinity);
182
+ if (remaining <= 0) { stop(Object.assign(new Error("Bounded CLI request timed out"), { code: timeoutCode,
183
+ details: maintenanceProgress ? { phase: importsReady ? "storage" : "startup", inactivity_ms: silenceMs(),
184
+ ownership_horizon_ms: commandMs } : {} })); return; }
185
+ timer = setTimeout(arm, Math.ceil(remaining));
186
+ };
187
+ arm();
188
+ const abort = () => stop(Object.assign(new Error("Bounded CLI request cancelled"), { code: "process_maintenance_cancelled" }));
189
+ signal?.addEventListener("abort", abort, { once: true });
190
+ if (signal?.aborted) abort();
191
+ child.stdout.setEncoding("utf8").on("data", chunk => { stdout += chunk; if (stdout.length > 1_048_576) stop(new Error("Native hook response exceeds protocol limit")); });
192
+ // Match the existing response bound: a legal bounded error tree may exceed
193
+ // 64KiB when pretty-printed, plus maintenance phases preceding the receipt.
194
+ child.stderr.setEncoding("utf8").on("data", chunk => {
195
+ stderr = (stderr + chunk).slice(-1_048_576);
196
+ if (!maintenanceProgress || termination || settled) return;
197
+ phaseBuffer += chunk;
198
+ const lines = phaseBuffer.split("\n"); phaseBuffer = lines.pop().slice(-16_384);
199
+ for (const line of lines) {
200
+ if (line.length > 16_384) continue;
201
+ let phase; try { phase = JSON.parse(line); } catch { continue; }
202
+ if (phase?.kind !== "process_maintenance_phase" || phase.action !== maintenanceProgress.action
203
+ || (phase.process_id ?? null) !== maintenanceProgress.processId || phase.native_dispatch_started !== false
204
+ || !["request", "imports_ready", "store_open", "store_validation", "lookup", "writer_reservation", "writer_reserved", "write", "commit", "committed", "ack"].includes(phase.phase)
205
+ || phase.store_kind !== undefined && !["run", "resource"].includes(phase.store_kind)) continue;
206
+ const key = `${phase.store_kind ?? ""}:${phase.phase}`;
207
+ if (progressPhases.has(key)) continue;
208
+ // Delayed observation cannot revive an already-expired silence window.
209
+ const now = performance.now();
210
+ if (now >= commandDeadline || now - lastProgress >= silenceMs()) { arm(); return; }
211
+ // Cold imports are not a stalled storage operation. Readiness is an
212
+ // actual boundary, never a timer heartbeat; the ownership horizon stays fixed.
213
+ if (["imports_ready", "store_open"].includes(phase.phase)) importsReady = true;
214
+ progressPhases.add(key); lastProgress = now; arm();
215
+ }
216
+ });
217
+ child.stdin.on("error", error => { failure ??= error; });
218
+ child.on("error", error => finish(failure ?? error));
219
+ child.on("close", code => {
220
+ if (failure) { void (termination ?? Promise.resolve()).then(() => finish(failure)); return; }
221
+ if (code !== 0) {
222
+ return finish(reportedFailure(stdout, stderr, "Native hook admission failed", "native_hook_failed"));
223
+ }
224
+ finish(null, stdout);
225
+ });
226
+ child.stdin.end(input);
227
+ });
228
+ return output;
229
+ }
230
+
231
+ /** Only our structured phase allowlist crosses a timeout, never raw stderr/env. */
232
+ function hookDiagnostics(stderr) {
233
+ return stderr.split("\n").flatMap(line => {
234
+ try {
235
+ const value = JSON.parse(line);
236
+ if (!["native_hook_phase", "process_maintenance_phase"].includes(value?.kind)) return [];
237
+ const phase = typeof value.phase === "string" && /^[a-z_]{1,48}$/.test(value.phase) ? value.phase : null;
238
+ if (!phase) return [];
239
+ return [{ phase, ...Object.fromEntries(["elapsed_ms", "remaining_ms", "sqlite_extended_code"].filter(key => Number.isFinite(value[key])).map(key => [key, value[key]])), ...(value.kind === "process_maintenance_phase" ? { ...Object.fromEntries(["action", "process_id", "operation_id", "lease_expires_at", "store_kind", "maintenance_effect", "attempt_kind", "role"].filter(key => typeof value[key] === "string" && /^[A-Za-z0-9:._-]{1,128}$/.test(value[key])).map(key => [key, value[key]])), ...(typeof value.native_dispatch_started === "boolean" ? { native_dispatch_started: value.native_dispatch_started } : {}) } : {}) }];
240
+ } catch { return []; }
241
+ }).slice(-16);
242
+ }
243
+
244
+ export async function invokeNativeHook({ bin, args, nodeBin, payload, home, cwd, env, timeoutMs = 15_000 }) {
245
+ const output = await runBoundedCommand({ bin, args, nodeBin, input: `${JSON.stringify(payload)}\n`, cwd, env: { ...env, DD_FLOW_HOME: home }, timeoutMs });
246
+ let result;
247
+ try { result = JSON.parse(output); } catch { throw Object.assign(new Error("Invalid native hook JSON response"), { code: "native_hook_response_invalid" }); }
248
+ if (result?.ok === false && typeof result.error?.code === "string") throw Object.assign(new Error(result.error.message ?? "Native hook failed"), { code: result.error.code, details: result.error.details });
249
+ if (!result || typeof result !== "object" || Array.isArray(result) || result.ok === false) throw Object.assign(new Error("Native hook did not return a successful protocol object"), { code: "native_hook_response_invalid" });
250
+ return result;
251
+ }
@@ -0,0 +1,2 @@
1
+ export function nativeCommandFingerprint(input: { executable: string; args: string[]; cwd: string; env: Record<string, string | undefined>; operationId: string }): string;
2
+ export function prepareDaemonRequest<T extends { id: string; operation: string; params?: Record<string, unknown> }>(request: T): T & { invocation_sha256?: string };
@@ -0,0 +1,65 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import process from 'node:process';
4
+ import { randomUUID } from 'node:crypto';
5
+ import { hashJson } from './runner-events.mjs';
6
+
7
+ const environmentKeys = ['HOME', 'CODEX_HOME', 'DD_FLOW_CONFIG_HOME', 'DD_FLOW_RESOURCE_HOME', 'DD_FLOW_HOME', 'DD_FLOW_BIN', 'DD_FLOW_ENGINE_HOME'];
8
+ const fail = reason => { throw Object.assign(new Error('Native request proof does not match its prepared command'), {
9
+ code: 'native_request_proof_conflict', details: { reason, effect: 'no_effect' }
10
+ }); };
11
+
12
+ /** Independent transport identity: prepared by the caller before launching
13
+ * the adapter, and recomputed by the adapter's shared request builder. */
14
+ export function nativeCommandFingerprint({ executable, args, cwd, env, operationId }) {
15
+ return hashJson({ executable, args, cwd, operation_id: operationId,
16
+ environment: Object.fromEntries(environmentKeys.map(key => [key, env[key] ?? null])) });
17
+ }
18
+
19
+ /** Called after the adapter's own argument preparation but before its RPC or
20
+ * daemon-start action. No provider response can supply this fingerprint. */
21
+ export function prepareDaemonRequest(request) {
22
+ const file = process.env.DD_FLOW_NATIVE_REQUEST_PROOF_FILE;
23
+ const expected = process.env.DD_FLOW_NATIVE_COMMAND_SHA256;
24
+ const operation = process.env.DD_FLOW_NATIVE_REQUEST_OPERATION;
25
+ if (file === undefined && expected === undefined) return request;
26
+ // Control helpers may inspect status before their owned stop RPC. Status
27
+ // cannot consume the productive/control request's independently bound proof.
28
+ if (operation && request.operation !== operation) {
29
+ if (request.operation === 'daemon.status' && operation === 'daemon.stop') return request;
30
+ fail('operation_changed');
31
+ }
32
+ const ownerText = process.env.DD_FLOW_NATIVE_REQUEST_PROOF_OWNER;
33
+ // Never forward caller proof authority into the daemon/provider process.
34
+ delete process.env.DD_FLOW_NATIVE_REQUEST_PROOF_FILE;
35
+ delete process.env.DD_FLOW_NATIVE_COMMAND_SHA256;
36
+ delete process.env.DD_FLOW_NATIVE_REQUEST_OPERATION;
37
+ delete process.env.DD_FLOW_NATIVE_REQUEST_PROOF_OWNER;
38
+ if (!file || !path.isAbsolute(file) || !/^[a-f0-9]{64}$/.test(expected ?? '')
39
+ || typeof request.id !== 'string' || !request.id || typeof request.operation !== 'string' || request.operation !== operation) fail('invalid_owner');
40
+ const command = nativeCommandFingerprint({ executable: process.argv[1], args: process.argv.slice(2),
41
+ cwd: process.cwd(), env: process.env, operationId: request.id });
42
+ if (command !== expected || fs.realpathSync(path.dirname(file)) !== path.dirname(file)) fail('command_changed');
43
+ let owner;
44
+ try { owner = JSON.parse(ownerText ?? 'null'); } catch { fail('invalid_parent_owner'); }
45
+ const parent = fs.lstatSync(path.dirname(file));
46
+ if (!parent.isDirectory() || parent.dev !== owner?.dev || parent.ino !== owner?.ino) fail('proof_parent_changed');
47
+ const proof = { schema_id: 'dd-flow/native-request-proof@1', operation_id: request.id, operation: request.operation,
48
+ command_sha256: command, params_sha256: hashJson({ operation: request.operation, params: request.params ?? {} }),
49
+ session_id: request.params?.sessionId ?? null };
50
+ const temporary = `${file}.${randomUUID()}.tmp`;
51
+ const fd = fs.openSync(temporary, 'wx', 0o600);
52
+ try {
53
+ fs.writeFileSync(fd, JSON.stringify(proof)); fs.fsyncSync(fd);
54
+ const current = fs.lstatSync(path.dirname(file));
55
+ if (parent.dev !== current.dev || parent.ino !== current.ino || !current.isDirectory()) fail('proof_parent_changed');
56
+ try { fs.linkSync(temporary, file); }
57
+ catch (error) {
58
+ if (error.code !== 'EEXIST') throw error;
59
+ const stat = fs.lstatSync(file);
60
+ if (!stat.isFile() || stat.nlink !== 1 || stat.size > 16 * 1024
61
+ || hashJson(JSON.parse(fs.readFileSync(file, 'utf8'))) !== hashJson(proof)) fail('retained_request_changed');
62
+ }
63
+ } finally { fs.closeSync(fd); fs.unlinkSync(temporary); }
64
+ return { ...request, invocation_sha256: command };
65
+ }