@deksden-com/dd-flow-cli 0.9.0-beta.15 → 0.9.0-beta.151

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 (560) hide show
  1. package/CHANGELOG.md +907 -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 +386 -0
  6. package/dist/cli/command-metadata.js +232 -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/diagnostic-invocation.js +46 -0
  11. package/dist/cli/diagnostic.js +120 -0
  12. package/dist/cli/error-output.js +52 -0
  13. package/dist/cli/executor-onboarding.js +76 -0
  14. package/dist/cli/flows.js +29 -0
  15. package/dist/cli/harnesses.js +84 -0
  16. package/dist/cli/help.js +396 -19
  17. package/dist/cli/hook-ingress.js +102 -0
  18. package/dist/cli/input-preparation.js +165 -0
  19. package/dist/cli/memory-flow-admission.js +50 -0
  20. package/dist/cli/memory-init-invocation.js +85 -0
  21. package/dist/cli/memory-init.js +249 -0
  22. package/dist/cli/memory-storage-admission.js +54 -0
  23. package/dist/cli/memory-storage-qualification.js +105 -0
  24. package/dist/cli/memory-storage-transition.js +123 -0
  25. package/dist/cli/memory-upgrade.js +189 -0
  26. package/dist/cli/models.js +39 -0
  27. package/dist/cli/native-hook-ingress.js +90 -0
  28. package/dist/cli/native-hook-preparation.js +55 -0
  29. package/dist/cli/operator-profiles.js +61 -0
  30. package/dist/cli/output-options.js +35 -0
  31. package/dist/cli/response-file.js +72 -0
  32. package/dist/cli/run-cli.js +1734 -550
  33. package/dist/cli/runtime-maintenance-diagnostics.js +46 -0
  34. package/dist/cli/runtime-maintenance-ingress.js +52 -0
  35. package/dist/cli/runtime-process.js +153 -0
  36. package/dist/cli/setup.js +150 -0
  37. package/dist/cli.js +89 -2
  38. package/dist/domain/diagnostic-aspects.js +103 -0
  39. package/dist/domain/diagnostic-flows.js +8 -0
  40. package/dist/domain/stage-catalog.js +1 -1
  41. package/dist/domain/stage-handler-identities.js +28 -0
  42. package/dist/harness-runtime/DD-ZCODE.md +112 -0
  43. package/dist/harness-runtime/OPENCODE-LICENSE.txt +24 -0
  44. package/dist/harness-runtime/bin/dd-agy.mjs +56 -0
  45. package/dist/harness-runtime/bin/dd-codex.mjs +27 -0
  46. package/dist/harness-runtime/bin/dd-droid.mjs +34 -0
  47. package/dist/harness-runtime/bin/dd-grok.mjs +40 -0
  48. package/dist/harness-runtime/bin/dd-opencode.mjs +22 -0
  49. package/dist/harness-runtime/bin/dd-zcode.mjs +87 -0
  50. package/dist/harness-runtime/lib/adapter-timeouts.d.mts +5 -0
  51. package/dist/harness-runtime/lib/adapter-timeouts.mjs +34 -0
  52. package/dist/harness-runtime/lib/agy-error-boundary.mjs +79 -0
  53. package/dist/harness-runtime/lib/agy-hooks.d.mts +1 -0
  54. package/dist/harness-runtime/lib/agy-hooks.mjs +9 -0
  55. package/dist/harness-runtime/lib/agy-native-inspection.mjs +216 -0
  56. package/dist/harness-runtime/lib/agy-native_pb.js +117 -0
  57. package/dist/harness-runtime/lib/agy-terminal-envelope.mjs +67 -0
  58. package/dist/harness-runtime/lib/daemon-observation.d.mts +2 -0
  59. package/dist/harness-runtime/lib/daemon-observation.mjs +163 -0
  60. package/dist/harness-runtime/lib/daemon-operations.d.mts +2 -0
  61. package/dist/harness-runtime/lib/daemon-operations.mjs +462 -0
  62. package/dist/harness-runtime/lib/dd-agy-daemon.d.mts +6 -0
  63. package/dist/harness-runtime/lib/dd-agy-daemon.mjs +1092 -0
  64. package/dist/harness-runtime/lib/dd-agy.mjs +86 -0
  65. package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
  66. package/dist/harness-runtime/lib/dd-codex-daemon.mjs +231 -0
  67. package/dist/harness-runtime/lib/dd-codex.d.mts +1 -0
  68. package/dist/harness-runtime/lib/dd-codex.mjs +954 -0
  69. package/dist/harness-runtime/lib/dd-droid-daemon.mjs +141 -0
  70. package/dist/harness-runtime/lib/dd-droid.mjs +626 -0
  71. package/dist/harness-runtime/lib/dd-grok-daemon.mjs +555 -0
  72. package/dist/harness-runtime/lib/dd-grok.mjs +180 -0
  73. package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +627 -0
  74. package/dist/harness-runtime/lib/dd-opencode.mjs +390 -0
  75. package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +752 -0
  76. package/dist/harness-runtime/lib/dd-zcode.mjs +1200 -0
  77. package/dist/harness-runtime/lib/delegation-instructions.d.mts +14 -0
  78. package/dist/harness-runtime/lib/delegation-instructions.mjs +166 -0
  79. package/dist/harness-runtime/lib/dispatch-fence.mjs +14 -0
  80. package/dist/harness-runtime/lib/driver-recovery.mjs +140 -0
  81. package/dist/harness-runtime/lib/droid-observation.mjs +66 -0
  82. package/dist/harness-runtime/lib/entity-references.d.mts +2 -0
  83. package/dist/harness-runtime/lib/entity-references.mjs +9 -0
  84. package/dist/harness-runtime/lib/lease-renewal.d.mts +8 -0
  85. package/dist/harness-runtime/lib/lease-renewal.mjs +37 -0
  86. package/dist/harness-runtime/lib/lifecycle-command.d.mts +20 -0
  87. package/dist/harness-runtime/lib/lifecycle-command.mjs +273 -0
  88. package/dist/harness-runtime/lib/lifecycle-contract.d.mts +10 -0
  89. package/dist/harness-runtime/lib/lifecycle-contract.mjs +19 -0
  90. package/dist/harness-runtime/lib/managed-daemon.d.mts +16 -0
  91. package/dist/harness-runtime/lib/managed-daemon.mjs +698 -0
  92. package/dist/harness-runtime/lib/managed-flow-env.mjs +15 -0
  93. package/dist/harness-runtime/lib/model-observations.mjs +113 -0
  94. package/dist/harness-runtime/lib/native-children.d.mts +34 -0
  95. package/dist/harness-runtime/lib/native-children.mjs +265 -0
  96. package/dist/harness-runtime/lib/native-hook-command.d.mts +7 -0
  97. package/dist/harness-runtime/lib/native-hook-command.mjs +251 -0
  98. package/dist/harness-runtime/lib/native-request-proof.d.mts +2 -0
  99. package/dist/harness-runtime/lib/native-request-proof.mjs +65 -0
  100. package/dist/harness-runtime/lib/native-streams.mjs +45 -0
  101. package/dist/harness-runtime/lib/observation-clock.d.mts +18 -0
  102. package/dist/harness-runtime/lib/observation-clock.mjs +95 -0
  103. package/dist/harness-runtime/lib/opencode-plugin.mjs +85 -0
  104. package/dist/harness-runtime/lib/operation-context.mjs +5 -0
  105. package/dist/harness-runtime/lib/operation-errors.d.mts +16 -0
  106. package/dist/harness-runtime/lib/operation-errors.mjs +127 -0
  107. package/dist/harness-runtime/lib/operation-progress.d.mts +7 -0
  108. package/dist/harness-runtime/lib/operation-progress.mjs +114 -0
  109. package/dist/harness-runtime/lib/process-json.d.mts +5 -0
  110. package/dist/harness-runtime/lib/process-json.mjs +201 -0
  111. package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
  112. package/dist/harness-runtime/lib/provider-overload-policy.d.mts +23 -0
  113. package/dist/harness-runtime/lib/provider-overload-policy.mjs +242 -0
  114. package/dist/harness-runtime/lib/runner-events.mjs +202 -0
  115. package/dist/harness-runtime/lib/runner-lock.mjs +42 -0
  116. package/dist/harness-runtime/lib/session-settlement.mjs +30 -0
  117. package/dist/harness-runtime/lib/setup-progress.d.mts +9 -0
  118. package/dist/harness-runtime/lib/setup-progress.mjs +53 -0
  119. package/dist/harness-runtime/lib/tool-observations.d.mts +21 -0
  120. package/dist/harness-runtime/lib/tool-observations.mjs +228 -0
  121. package/dist/harness-runtime/lib/zcode-usage.mjs +55 -0
  122. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-1y4a697q.js +81 -0
  123. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-2ny4vxct.js +92 -0
  124. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-6mptkzbc.js +0 -0
  125. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-7x67cf1q.js +52 -0
  126. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9rqn6x4v.js +16 -0
  127. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9vdz0zwn.js +1661 -0
  128. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ecs5ftka.js +1466 -0
  129. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-frbwqjmf.js +145 -0
  130. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gne7h7mq.js +50 -0
  131. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gx9wnkzr.js +105 -0
  132. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-m2eywc8w.js +0 -0
  133. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-nwcpakzx.js +12 -0
  134. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-q4g3afnw.js +623 -0
  135. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-qav6mfdz.js +0 -0
  136. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-sxt88q69.js +12 -0
  137. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-t1xa31vm.js +39 -0
  138. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ve4aws36.js +103 -0
  139. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vmytr4q8.js +4 -0
  140. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vt9jbch8.js +36 -0
  141. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vyas3vgt.js +0 -0
  142. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-wjyeq25y.js +30 -0
  143. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-xgnj2w4v.js +20 -0
  144. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-y2etg5bs.js +9 -0
  145. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ydhz1zrh.js +238 -0
  146. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-z6qdwcsf.js +62 -0
  147. package/dist/harness-runtime/vendor/opencode-client/contract.d.ts +1 -0
  148. package/dist/harness-runtime/vendor/opencode-client/contract.js +12 -0
  149. package/dist/harness-runtime/vendor/opencode-client/effect/api/api.d.ts +2806 -0
  150. package/dist/harness-runtime/vendor/opencode-client/effect/api/api.js +0 -0
  151. package/dist/harness-runtime/vendor/opencode-client/effect/api.d.ts +4 -0
  152. package/dist/harness-runtime/vendor/opencode-client/effect/api.js +0 -0
  153. package/dist/harness-runtime/vendor/opencode-client/effect/client.d.ts +2530 -0
  154. package/dist/harness-runtime/vendor/opencode-client/effect/client.js +15 -0
  155. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.d.ts +7 -0
  156. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.js +6 -0
  157. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.d.ts +2529 -0
  158. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.js +8 -0
  159. package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.d.ts +2 -0
  160. package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.js +12 -0
  161. package/dist/harness-runtime/vendor/opencode-client/effect/index.d.ts +35 -0
  162. package/dist/harness-runtime/vendor/opencode-client/effect/index.js +75 -0
  163. package/dist/harness-runtime/vendor/opencode-client/effect/rpc.d.ts +20 -0
  164. package/dist/harness-runtime/vendor/opencode-client/effect/rpc.js +11 -0
  165. package/dist/harness-runtime/vendor/opencode-client/effect/service.d.ts +90 -0
  166. package/dist/harness-runtime/vendor/opencode-client/effect/service.js +187 -0
  167. package/dist/harness-runtime/vendor/opencode-client/package.json +84 -0
  168. package/dist/harness-runtime/vendor/opencode-client/promise/api.d.ts +21 -0
  169. package/dist/harness-runtime/vendor/opencode-client/promise/api.js +0 -0
  170. package/dist/harness-runtime/vendor/opencode-client/promise/client.d.ts +252 -0
  171. package/dist/harness-runtime/vendor/opencode-client/promise/client.js +14 -0
  172. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.d.ts +8 -0
  173. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.js +6 -0
  174. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.d.ts +259 -0
  175. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.js +7 -0
  176. package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.d.ts +3 -0
  177. package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.js +79 -0
  178. package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.d.ts +8932 -0
  179. package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.js +70 -0
  180. package/dist/harness-runtime/vendor/opencode-client/promise/index.d.ts +6 -0
  181. package/dist/harness-runtime/vendor/opencode-client/promise/index.js +83 -0
  182. package/dist/harness-runtime/vendor/opencode-client/promise/rpc.d.ts +32 -0
  183. package/dist/harness-runtime/vendor/opencode-client/promise/rpc.js +7 -0
  184. package/dist/harness-runtime/vendor/opencode-client/promise/service.d.ts +24 -0
  185. package/dist/harness-runtime/vendor/opencode-client/promise/service.js +187 -0
  186. package/dist/harness-runtime/vendor/opencode-client/pty-handoff.d.ts +12 -0
  187. package/dist/harness-runtime/vendor/opencode-client/pty-handoff.js +14 -0
  188. package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.d.ts +20 -0
  189. package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.js +14 -0
  190. package/dist/harness-runtime/vendor/opencode-client/service-contender.d.ts +37 -0
  191. package/dist/harness-runtime/vendor/opencode-client/service-contender.js +10 -0
  192. package/dist/harness-runtime/vendor/opencode-client/service-probe.d.ts +70 -0
  193. package/dist/harness-runtime/vendor/opencode-client/service-probe.js +16 -0
  194. package/dist/harness-runtime/vendor/opencode-client/service-timing.d.ts +12 -0
  195. package/dist/harness-runtime/vendor/opencode-client/service-timing.js +10 -0
  196. package/dist/harness-runtime/vendor/opencode-client/service-version.d.ts +2 -0
  197. package/dist/harness-runtime/vendor/opencode-client/service-version.js +6 -0
  198. package/dist/harness-runtime/vendor/opencode-client/service.d.ts +52 -0
  199. package/dist/harness-runtime/vendor/opencode-client/service.js +1 -0
  200. package/dist/harness-runtime/vendor/opencode-client/shared-events.d.ts +11 -0
  201. package/dist/harness-runtime/vendor/opencode-client/shared-events.js +8 -0
  202. package/dist/harness-runtime/vendor/opencode-client/solid/connection.d.ts +37 -0
  203. package/dist/harness-runtime/vendor/opencode-client/solid/connection.js +10 -0
  204. package/dist/harness-runtime/vendor/opencode-client/solid/data.d.ts +211 -0
  205. package/dist/harness-runtime/vendor/opencode-client/solid/data.js +18 -0
  206. package/dist/harness-runtime/vendor/opencode-client/solid/index.d.ts +3 -0
  207. package/dist/harness-runtime/vendor/opencode-client/solid/index.js +32 -0
  208. package/dist/harness-runtime/vendor/opencode-client/solid/pty.d.ts +22 -0
  209. package/dist/harness-runtime/vendor/opencode-client/solid/pty.js +8 -0
  210. package/dist/harness-runtime/vendor/opencode-plugin.mjs +3 -0
  211. package/dist/protocol/local-files.js +18 -9
  212. package/dist/runtime/context.js +8 -4
  213. package/dist/schemas/agent-profile.schema.json +1 -1
  214. package/dist/schemas/code-review-decision.schema.json +9 -0
  215. package/dist/schemas/code-review-result.schema.json +1 -1
  216. package/dist/schemas/code-verification.schema.json +7 -5
  217. package/dist/schemas/code-work-batch.schema.json +2 -2
  218. package/dist/schemas/diagnostic-stage-result.schema.json +354 -0
  219. package/dist/schemas/diagnostic-work-result.schema.json +296 -0
  220. package/dist/schemas/discovery-result.schema.json +184 -0
  221. package/dist/schemas/execution-bundle.schema.json +211 -0
  222. package/dist/schemas/execution-settings.schema.json +162 -0
  223. package/dist/schemas/flow-descriptor.schema.json +54 -0
  224. package/dist/schemas/flow-run.schema.json +29 -0
  225. package/dist/schemas/harness-catalog.schema.json +80 -0
  226. package/dist/schemas/mb-init-compose-result.schema.json +457 -0
  227. package/dist/schemas/mb-init-discover-result.schema.json +500 -0
  228. package/dist/schemas/mb-init-finish-result.schema.json +422 -0
  229. package/dist/schemas/mb-init-preflight-result.schema.json +367 -0
  230. package/dist/schemas/mb-init-review-result.schema.json +396 -0
  231. package/dist/schemas/mb-init-verify-result.schema.json +391 -0
  232. package/dist/schemas/mb-upgrade-stage-result.schema.json +863 -0
  233. package/dist/schemas/memory-bootstrap-receipt-v2.schema.json +67 -0
  234. package/dist/schemas/memory-bootstrap-receipt.schema.json +354 -0
  235. package/dist/schemas/memory-composition-work-result.schema.json +38 -0
  236. package/dist/schemas/memory-repair-work-result.schema.json +56 -0
  237. package/dist/schemas/memory-review-correction-plan.schema.json +30 -0
  238. package/dist/schemas/memory-review-work-result.schema.json +43 -0
  239. package/dist/schemas/model-catalog.schema.json +153 -0
  240. package/dist/schemas/plan-review-decision.schema.json +12 -3
  241. package/dist/schemas/plan-review-result.schema.json +2 -2
  242. package/dist/schemas/profile-settings.schema.json +122 -0
  243. package/dist/schemas/project-flow-pack-manifest.schema.json +1 -1
  244. package/dist/schemas/release-impact.schema.json +3 -3
  245. package/dist/schemas/release-manifest.schema.json +291 -0
  246. package/dist/schemas/run-control-receipt.schema.json +118 -0
  247. package/dist/schemas/run-control-request.schema.json +36 -0
  248. package/dist/schemas/run-engine-binding.schema.json +1 -1
  249. package/dist/schemas/stage-start-response.schema.json +4 -0
  250. package/dist/schemas/vnext-protocol-plan.schema.json +2 -2
  251. package/dist/services/accepted-intent.js +139 -0
  252. package/dist/services/branch-context.js +3 -3
  253. package/dist/services/canon.js +54 -15
  254. package/dist/services/check-environment.js +21 -0
  255. package/dist/services/check-runner-evidence.js +277 -0
  256. package/dist/services/check-runner-proof.js +305 -0
  257. package/dist/services/cleanup.js +133 -40
  258. package/dist/services/cli-operation-classifier.js +26 -63
  259. package/dist/services/cli-upgrade-allowlist.js +27 -0
  260. package/dist/services/code-checks.js +1401 -119
  261. package/dist/services/codex-hook-delivery.js +32 -0
  262. package/dist/services/command-context.js +80 -0
  263. package/dist/services/compatibility-preflight.js +16 -3
  264. package/dist/services/config.js +11 -8
  265. package/dist/services/continuation-outcome.js +20 -0
  266. package/dist/services/controller-child-capacity.js +71 -0
  267. package/dist/services/controller-child-observer.js +121 -0
  268. package/dist/services/controller-execution.js +94 -0
  269. package/dist/services/controller-fanout.js +226 -0
  270. package/dist/services/controller-memory-task.js +45 -0
  271. package/dist/services/controller-memory-worker.js +49 -0
  272. package/dist/services/dashboard.js +143 -58
  273. package/dist/services/descriptor-coordinator-session.js +35 -0
  274. package/dist/services/descriptor-root-work.js +53 -0
  275. package/dist/services/descriptor-run-settlement.js +107 -0
  276. package/dist/services/descriptor-run-start.js +475 -0
  277. package/dist/services/descriptor-stage-coordinator.js +250 -0
  278. package/dist/services/descriptor-stage-settlement.js +223 -0
  279. package/dist/services/descriptor-stage-start.js +175 -0
  280. package/dist/services/diagnostic-evidence.js +73 -0
  281. package/dist/services/diagnostic-report-publication.js +72 -0
  282. package/dist/services/diagnostic-report.js +67 -0
  283. package/dist/services/diagnostic-run-bundle.js +74 -0
  284. package/dist/services/diagnostic-snapshot.js +159 -0
  285. package/dist/services/diagnostic-stage-handlers.js +108 -0
  286. package/dist/services/diagnostic-stage-results.js +132 -0
  287. package/dist/services/diagnostic-start.js +84 -0
  288. package/dist/services/diagnostic-work-batch.js +34 -0
  289. package/dist/services/diagnostic-work-collection.js +97 -0
  290. package/dist/services/diagnostic-work-registration.js +101 -0
  291. package/dist/services/diagnostic-work-results.js +93 -0
  292. package/dist/services/discovery/agy-discovery.js +2 -0
  293. package/dist/services/discovery/codex-discovery.js +2 -0
  294. package/dist/services/discovery/droid-discovery.js +2 -0
  295. package/dist/services/discovery/grok-discovery.js +2 -0
  296. package/dist/services/discovery/opencode-discovery.js +2 -0
  297. package/dist/services/discovery/static.js +31 -0
  298. package/dist/services/discovery/zcode-discovery.js +2 -0
  299. package/dist/services/engines.js +354 -163
  300. package/dist/services/eval-snapshots.js +1316 -150
  301. package/dist/services/execution-bundles.js +275 -0
  302. package/dist/services/execution-policy.js +185 -0
  303. package/dist/services/executor-onboarding.js +56 -0
  304. package/dist/services/external-work-launch.js +310 -0
  305. package/dist/services/flow-catalog.js +30 -0
  306. package/dist/services/flow-descriptors.js +82 -0
  307. package/dist/services/frozen-engine.js +387 -0
  308. package/dist/services/generic-execution.js +164 -0
  309. package/dist/services/harness-adapter.js +355 -36
  310. package/dist/services/harness-config.js +2 -0
  311. package/dist/services/harness-discovery.js +29 -0
  312. package/dist/services/harness-doctor.js +75 -0
  313. package/dist/services/harness-probe-effects.js +51 -0
  314. package/dist/services/harness-version-probe.js +100 -0
  315. package/dist/services/hook-memory-routing.js +188 -0
  316. package/dist/services/hook-recovery-guard.js +14 -0
  317. package/dist/services/hook-session-observation.js +24 -0
  318. package/dist/services/hooks.js +791 -326
  319. package/dist/services/hybrid-contracts.js +474 -0
  320. package/dist/services/hybrid-digest.js +14 -0
  321. package/dist/services/ids.js +29 -1
  322. package/dist/services/installation-lock.js +95 -0
  323. package/dist/services/installed-flow-source.js +226 -0
  324. package/dist/services/lanes.js +78 -65
  325. package/dist/services/lifecycle-command.js +1 -288
  326. package/dist/services/lifecycle-contract.js +1 -0
  327. package/dist/services/lifecycle-invocations.js +2148 -0
  328. package/dist/services/managed-daemon-binding.js +38 -0
  329. package/dist/services/managed-lease-monitor.js +133 -0
  330. package/dist/services/managed-processes.js +449 -69
  331. package/dist/services/memory-accepted-composition.js +78 -0
  332. package/dist/services/memory-baseline-preservation.js +65 -0
  333. package/dist/services/memory-completed-correction-wave.js +328 -0
  334. package/dist/services/memory-compose-handler.js +400 -0
  335. package/dist/services/memory-composition-acceptance.js +375 -0
  336. package/dist/services/memory-composition-graph.js +165 -0
  337. package/dist/services/memory-composition-registration.js +161 -0
  338. package/dist/services/memory-composition-work-custody.js +156 -0
  339. package/dist/services/memory-composition-work-results.js +108 -0
  340. package/dist/services/memory-corrected-review-acceptance.js +129 -0
  341. package/dist/services/memory-corrected-review-batch.js +37 -0
  342. package/dist/services/memory-corrected-review-findings.js +108 -0
  343. package/dist/services/memory-corrected-review-inputs.js +215 -0
  344. package/dist/services/memory-corrected-review-projection.js +78 -0
  345. package/dist/services/memory-corrected-review-receipt.js +178 -0
  346. package/dist/services/memory-corrected-review-registration.js +192 -0
  347. package/dist/services/memory-correction-check-receipt.js +235 -0
  348. package/dist/services/memory-correction-input-owner.js +6 -0
  349. package/dist/services/memory-custody-snapshot.js +36 -0
  350. package/dist/services/memory-discover-handler.js +104 -0
  351. package/dist/services/memory-finish-evidence.js +457 -0
  352. package/dist/services/memory-finish-handler.js +229 -0
  353. package/dist/services/memory-finish-integration.js +450 -0
  354. package/dist/services/memory-finish-readiness.js +169 -0
  355. package/dist/services/memory-git-integration.js +220 -0
  356. package/dist/services/memory-historical-corrected-review.js +212 -0
  357. package/dist/services/memory-historical-original-review.js +143 -0
  358. package/dist/services/memory-historical-repair-registration.js +105 -0
  359. package/dist/services/memory-historical-second-review.js +147 -0
  360. package/dist/services/memory-init-bootstrap-readiness.js +210 -0
  361. package/dist/services/memory-init-contexts.js +131 -0
  362. package/dist/services/memory-init-controller-start.js +103 -0
  363. package/dist/services/memory-init-engine-release.js +223 -0
  364. package/dist/services/memory-init-git-policy.js +165 -0
  365. package/dist/services/memory-init-lint-capability.js +203 -0
  366. package/dist/services/memory-init-native-capacity.js +309 -0
  367. package/dist/services/memory-init-preflight.js +334 -0
  368. package/dist/services/memory-init-project-bootstrap.js +599 -0
  369. package/dist/services/memory-init-request-candidate.js +213 -0
  370. package/dist/services/memory-init-request-input.js +82 -0
  371. package/dist/services/memory-init-resource-storage.js +125 -0
  372. package/dist/services/memory-init-run-allocation.js +81 -0
  373. package/dist/services/memory-init-run-bundle.js +135 -0
  374. package/dist/services/memory-init-start.js +181 -0
  375. package/dist/services/memory-init-startup-custody.js +80 -0
  376. package/dist/services/memory-init-startup-input.js +240 -0
  377. package/dist/services/memory-init-storage.js +91 -0
  378. package/dist/services/memory-init-stores.js +359 -0
  379. package/dist/services/memory-init-worktrunk-capability.js +174 -0
  380. package/dist/services/memory-layer-publication.js +611 -0
  381. package/dist/services/memory-layer.js +221 -0
  382. package/dist/services/memory-lint-config.js +194 -0
  383. package/dist/services/memory-native-review-copy.js +103 -0
  384. package/dist/services/memory-normal-flow-entry.js +87 -0
  385. package/dist/services/memory-ordinary-defaults.js +129 -0
  386. package/dist/services/memory-preflight-bootstrap-custody.js +115 -0
  387. package/dist/services/memory-preflight-handler.js +274 -0
  388. package/dist/services/memory-repair-current-view.js +299 -0
  389. package/dist/services/memory-repair-document-delta.js +488 -0
  390. package/dist/services/memory-repair-history.js +496 -0
  391. package/dist/services/memory-repair-physical-binding.js +194 -0
  392. package/dist/services/memory-repair-publication-files.js +152 -0
  393. package/dist/services/memory-repair-publication-intent.js +27 -0
  394. package/dist/services/memory-repair-publication-plan.js +162 -0
  395. package/dist/services/memory-repair-publication-source.js +225 -0
  396. package/dist/services/memory-repair-publication.js +795 -0
  397. package/dist/services/memory-repair-registration.js +240 -0
  398. package/dist/services/memory-repair-work-baseline.js +415 -0
  399. package/dist/services/memory-repair-work-completion.js +117 -0
  400. package/dist/services/memory-repair-work-custody.js +73 -0
  401. package/dist/services/memory-repair-work-results.js +108 -0
  402. package/dist/services/memory-repair-workspace.js +261 -0
  403. package/dist/services/memory-repair-writer-copy.js +736 -0
  404. package/dist/services/memory-result-binding.js +239 -0
  405. package/dist/services/memory-retained-review-authority.js +138 -0
  406. package/dist/services/memory-retained-review-results.js +70 -0
  407. package/dist/services/memory-retained-second-baseline.js +102 -0
  408. package/dist/services/memory-retained-second-repairs.js +162 -0
  409. package/dist/services/memory-review-acceptance.js +214 -0
  410. package/dist/services/memory-review-correction-plan.js +206 -0
  411. package/dist/services/memory-review-handler.js +585 -0
  412. package/dist/services/memory-review-inputs.js +246 -0
  413. package/dist/services/memory-review-limits.js +10 -0
  414. package/dist/services/memory-review-receipt.js +180 -0
  415. package/dist/services/memory-review-registration.js +117 -0
  416. package/dist/services/memory-review-work-custody.js +126 -0
  417. package/dist/services/memory-review-work-input.js +91 -0
  418. package/dist/services/memory-review-work-results.js +95 -0
  419. package/dist/services/memory-second-corrected-review-inputs.js +167 -0
  420. package/dist/services/memory-second-correction-plan.js +77 -0
  421. package/dist/services/memory-second-repair-baseline.js +91 -0
  422. package/dist/services/memory-second-repair-launch.js +614 -0
  423. package/dist/services/memory-second-repair-registration.js +88 -0
  424. package/dist/services/memory-second-repair-source.js +187 -0
  425. package/dist/services/memory-second-review-acceptance.js +115 -0
  426. package/dist/services/memory-source-observations.js +117 -0
  427. package/dist/services/memory-stage-dispatch.js +65 -0
  428. package/dist/services/memory-stage-results.js +290 -0
  429. package/dist/services/memory-tool-probe.js +25 -0
  430. package/dist/services/memory-upgrade-analysis-handler.js +243 -0
  431. package/dist/services/memory-upgrade-bootstrap-readiness.js +251 -0
  432. package/dist/services/memory-upgrade-evidence.js +100 -0
  433. package/dist/services/memory-upgrade-inventory.js +442 -0
  434. package/dist/services/memory-upgrade-publication-custody.js +73 -0
  435. package/dist/services/memory-upgrade-publication.js +413 -0
  436. package/dist/services/memory-upgrade-selection.js +430 -0
  437. package/dist/services/memory-upgrade-start.js +60 -0
  438. package/dist/services/memory-upgrade-storage-readiness.js +340 -0
  439. package/dist/services/memory-verification-acceptance.js +156 -0
  440. package/dist/services/memory-verification-checks.js +317 -0
  441. package/dist/services/memory-verification-receipt.js +160 -0
  442. package/dist/services/memory-verify-handler.js +137 -0
  443. package/dist/services/memory-work-result-json.js +12 -0
  444. package/dist/services/memory-work-result-preparation.js +64 -0
  445. package/dist/services/merge-queue.js +228 -149
  446. package/dist/services/merge-server.js +163 -35
  447. package/dist/services/migrations.js +164 -114
  448. package/dist/services/model-catalog.js +109 -0
  449. package/dist/services/model-discovery.js +46 -0
  450. package/dist/services/native-daemon-history.js +50 -0
  451. package/dist/services/native-session-control.js +39 -0
  452. package/dist/services/operator-profiles.js +236 -0
  453. package/dist/services/physical-startup.js +45 -0
  454. package/dist/services/plan-runtime.js +26 -11
  455. package/dist/services/plans.js +28 -25
  456. package/dist/services/project-registry.js +97 -0
  457. package/dist/services/project-summary.js +1 -1
  458. package/dist/services/projects.js +17 -87
  459. package/dist/services/prompt-delivery.js +71 -0
  460. package/dist/services/prompts.js +28 -17
  461. package/dist/services/protocols.js +163 -87
  462. package/dist/services/provider-capacity.js +463 -0
  463. package/dist/services/recovery-observation-budget.js +121 -0
  464. package/dist/services/recovery-snapshot-database.js +588 -0
  465. package/dist/services/release-archive-producer.js +52 -0
  466. package/dist/services/release-archive.js +143 -0
  467. package/dist/services/release-cache.js +150 -0
  468. package/dist/services/release-compatibility.js +18 -0
  469. package/dist/services/release-download.js +67 -0
  470. package/dist/services/release-envelope.js +104 -0
  471. package/dist/services/release-manifest.js +45 -0
  472. package/dist/services/release-payload.js +114 -0
  473. package/dist/services/release-producer.js +63 -0
  474. package/dist/services/release-provenance.js +160 -0
  475. package/dist/services/release-registry.js +66 -0
  476. package/dist/services/release-source.js +194 -0
  477. package/dist/services/repair-intents.js +31 -0
  478. package/dist/services/repair-receipt.js +13 -0
  479. package/dist/services/review-copy.js +165 -0
  480. package/dist/services/review-deferral.js +23 -0
  481. package/dist/services/run-control-receipt.js +56 -0
  482. package/dist/services/run-control-worker.js +428 -0
  483. package/dist/services/run-control.js +1092 -0
  484. package/dist/services/run-controller-adapter.js +400 -0
  485. package/dist/services/run-controller-capture.js +186 -0
  486. package/dist/services/run-controller-process.js +205 -0
  487. package/dist/services/run-controller-recovery.js +295 -0
  488. package/dist/services/run-controller-state.js +97 -0
  489. package/dist/services/run-controller.js +1395 -0
  490. package/dist/services/run-engine-bindings.js +1 -1
  491. package/dist/services/run-fork.js +147 -0
  492. package/dist/services/run-health.js +149 -0
  493. package/dist/services/run-observations.js +152 -0
  494. package/dist/services/run-projection.js +22 -64
  495. package/dist/services/run-recovery-guard.js +107 -0
  496. package/dist/services/run-recovery-runtime.js +112 -0
  497. package/dist/services/run-recovery.js +456 -0
  498. package/dist/services/run-start-custody.js +189 -0
  499. package/dist/services/runs.js +1023 -246
  500. package/dist/services/runtime-budget.js +446 -0
  501. package/dist/services/runtime-command.js +43 -0
  502. package/dist/services/runtime-scope-capture.js +52 -0
  503. package/dist/services/runtime-scope-control.js +468 -0
  504. package/dist/services/runtime-scope-resume.js +605 -0
  505. package/dist/services/runtime-scope-stop.js +211 -0
  506. package/dist/services/runtime-scope-worker.js +314 -0
  507. package/dist/services/runtime-service.js +19 -14
  508. package/dist/services/schema-validation.js +10 -8
  509. package/dist/services/scope-writer-cleanup.js +27 -0
  510. package/dist/services/sessions.js +36 -91
  511. package/dist/services/setup.js +427 -0
  512. package/dist/services/stage-blocker.js +17 -7
  513. package/dist/services/stage-context.js +240 -18
  514. package/dist/services/stage-handlers.js +89 -0
  515. package/dist/services/stage-lifecycle.js +152 -116
  516. package/dist/services/stage-pause.js +202 -91
  517. package/dist/services/stage-report-renderer.js +234 -6
  518. package/dist/services/stage-work-graph.js +52 -0
  519. package/dist/services/tooling.js +41 -30
  520. package/dist/services/usage.js +49 -101
  521. package/dist/services/verification-matrix.js +393 -0
  522. package/dist/services/version-status.js +1 -1
  523. package/dist/services/vnext-code-review.js +235 -88
  524. package/dist/services/vnext-code.js +327 -226
  525. package/dist/services/vnext-contracts.js +10 -1
  526. package/dist/services/vnext-execution-profile.js +80 -5
  527. package/dist/services/vnext-fanout.js +355 -21
  528. package/dist/services/vnext-merge.js +332 -111
  529. package/dist/services/vnext-plan-review.js +287 -132
  530. package/dist/services/vnext-plan.js +228 -104
  531. package/dist/services/vnext-protocolize.js +533 -95
  532. package/dist/services/vnext-specify.js +123 -81
  533. package/dist/services/vnext-workspace-policy.js +7 -2
  534. package/dist/services/work-registry.js +1706 -200
  535. package/dist/services/workspace-bootstrap.js +53 -15
  536. package/dist/services/workspace-files.js +85 -0
  537. package/dist/services/worktrees.js +105 -48
  538. package/dist/shared/entity-references.js +1 -0
  539. package/dist/shared/errors.js +27 -0
  540. package/dist/storage/agent-output.js +26 -0
  541. package/dist/storage/data-preservation.js +65 -0
  542. package/dist/storage/database.js +930 -40
  543. package/dist/storage/foreign-key-transaction.js +39 -0
  544. package/dist/storage/immutable-input.js +45 -0
  545. package/dist/storage/import-guard.js +159 -0
  546. package/dist/storage/memory-upgrade-schema-bootstrap.js +38 -0
  547. package/dist/storage/native-cleanup.js +52 -0
  548. package/dist/storage/paired-writer-migration.js +424 -0
  549. package/dist/storage/paths.js +17 -4
  550. package/dist/storage/projection-lock.js +73 -0
  551. package/dist/storage/work-references.js +23 -0
  552. package/dist/storage/writer-contract.js +96 -0
  553. package/dist/storage/writer-migration.js +346 -0
  554. package/dist/storage/writer-restore.js +217 -0
  555. package/dist/verification-components.json +299 -0
  556. package/package.json +51 -13
  557. package/tools/create-canon-release.mjs +33 -0
  558. package/tools/probe-codex-history.mjs +37 -0
  559. package/tools/produce-hybrid-release.mjs +41 -0
  560. package/tools/repair-paused-run-status.mjs +84 -0
@@ -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
+ }, () => budget.remaining("failure_receipt_commit")), "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(() => { remaining(phase); return 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 = 25_000 }) {
245
+ const output = await runBoundedCommand({ bin, args, nodeBin, input: `${JSON.stringify(payload)}\n`, cwd, env: { ...env, DD_FLOW_HOME: home }, timeoutMs, cleanupWithinBudget: true });
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, ...(env.PLAYWRIGHT_BROWSERS_PATH === undefined ? [] : ['PLAYWRIGHT_BROWSERS_PATH'])].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
+ }
@@ -0,0 +1,45 @@
1
+ // Async iteration backpressures the native pipe while its existing evidence
2
+ // sink drains. Bound one JSON reply, never lifetime diagnostics.
3
+ export const MAX_NATIVE_LINE_BYTES = 8 * 1024 * 1024;
4
+ export async function readNativeLines(stream, onLine, onError) {
5
+ let buffer = "";
6
+ stream.setEncoding("utf8");
7
+ try {
8
+ for await (const chunk of stream.iterator({ destroyOnReturn: false })) {
9
+ buffer += chunk;
10
+ let at;
11
+ while ((at = buffer.indexOf("\n")) >= 0) {
12
+ const line = buffer.slice(0, at); buffer = buffer.slice(at + 1);
13
+ assertLine(line);
14
+ if (line.trim()) await onLine(line);
15
+ }
16
+ assertLine(buffer);
17
+ }
18
+ if (buffer.trim()) { assertLine(buffer); await onLine(buffer); }
19
+ } catch (cause) { stream.pause(); await observationFailure(cause, onError); }
20
+ }
21
+ export function assertNativeLine(line, phase = "reply") {
22
+ if (Buffer.byteLength(line) > MAX_NATIVE_LINE_BYTES) throw Object.assign(new Error(`Native JSON ${phase} exceeds the bounded capture limit`), { code: phase === "request" ? "native_request_limit" : "operation_observation_lost", details: { reason: `native_${phase}_limit`, limit_bytes: MAX_NATIVE_LINE_BYTES, ...(phase === "request" ? { effect: "no_effect" } : { native_outcome: "unknown" }) } });
23
+ }
24
+ const assertLine = assertNativeLine;
25
+ export function admitNativeRequestBuffer(buffer, socket, schema) {
26
+ try { assertNativeLine(buffer, "request"); return true; }
27
+ catch (error) { socket.pause(); socket.end(`${JSON.stringify({ schema_id: schema, id: null, ok: false, error: { code: error.code, message: error.message, details: error.details } })}\n`, () => socket.destroy()); return false; }
28
+ }
29
+ export function retainNativeReplyBuffer(buffer, finish) {
30
+ try { assertNativeLine(buffer); return true; }
31
+ catch (error) { finish(error); return false; }
32
+ }
33
+ export async function streamNativeDiagnostics(stream, onChunk, onError) {
34
+ stream.setEncoding("utf8");
35
+ try { for await (const chunk of stream.iterator({ destroyOnReturn: false })) await onChunk(String(chunk)); }
36
+ catch (cause) { stream.pause(); await observationFailure(cause, onError); }
37
+ }
38
+ async function observationFailure(cause, onError) {
39
+ const error = nativeObservationError(cause);
40
+ try { await onError(error); }
41
+ catch (publication) { error.cleanup_error ??= { code: publication.code ?? null, message: publication.message }; }
42
+ }
43
+ export function nativeObservationError(cause, details = {}) {
44
+ return !cause.code || /^[A-Z_]+$/.test(cause.code) ? Object.assign(new Error("Native evidence reader failed", { cause }), { code: "operation_observation_lost", details: { native_outcome: "unknown", observation_error: { code: cause.code ?? null, message: cause.message }, ...details } }) : cause;
45
+ }
@@ -0,0 +1,18 @@
1
+ export const OBSERVATION_CLOCK_CONTRACT_VERSION: "operation-progress@1";
2
+ export function validateObservationDuration(value: number, label?: string): number;
3
+ export interface ObservationState {
4
+ policy: string; timeout_ms: number; started_at: number; cursor?: unknown;
5
+ observed_at: number | null; elapsed_ms: number; uncertainty_elapsed_ms: number;
6
+ gaps: number; uncertain?: boolean; observation_lost: boolean;
7
+ }
8
+ export class ObservationClock {
9
+ constructor(options: { timeoutMs: number; gapMs?: number; uncertaintyMs?: number;
10
+ wall?: () => number; monotonic?: () => number; onGap?: (value: Record<string, unknown>) => void;
11
+ saved?: ObservationState | null; snapshotIsLive?: boolean });
12
+ observationLost: boolean;
13
+ sample(progress?: unknown, progressAt?: number): boolean;
14
+ state(): ObservationState;
15
+ }
16
+ export function observedTimeout(callback: (clock: ObservationClock) => void, timeoutMs: number,
17
+ options?: { gapMs?: number; uncertaintyMs?: number; wall?: () => number; monotonic?: () => number;
18
+ onGap?: (value: Record<string, unknown>) => void; progress?: () => unknown; saved?: ObservationState | null }): ReturnType<typeof setInterval>;
@@ -0,0 +1,95 @@
1
+ export const OBSERVATION_CLOCK_CONTRACT_VERSION = "operation-progress@1";
2
+ export function validateObservationDuration(value, label = "observation duration") {
3
+ if (!Number.isSafeInteger(value) || value < 1 || value > 2_147_483_647)
4
+ throw new RangeError(`${label} must be integer milliseconds in 1..2147483647`);
5
+ return value;
6
+ }
7
+
8
+ /** Only observed monotonic time is silence. A clock gap is uncertainty, never
9
+ * progress or proof of provider failure. Durable snapshots retain that episode. */
10
+ export class ObservationClock {
11
+ constructor({ timeoutMs, gapMs = 60_000, uncertaintyMs = 120_000, wall = Date.now,
12
+ monotonic = () => performance.now(), onGap = () => {}, saved = null, snapshotIsLive = false }) {
13
+ validateObservationDuration(timeoutMs); validateObservationDuration(gapMs, "gap duration");
14
+ validateObservationDuration(uncertaintyMs, "observation recovery duration");
15
+ Object.assign(this, { timeoutMs, gapMs, uncertaintyMs, wall, monotonic, onGap });
16
+ this.lastWall = wall(); this.lastMono = monotonic(); this.startedWall = this.lastWall;
17
+ this.elapsed = 0; this.progress = undefined; this.progressAt = null;
18
+ this.uncertain = false; this.uncertaintyElapsed = 0; this.gaps = 0; this.observationLost = false;
19
+ if (saved) {
20
+ if (saved.policy !== OBSERVATION_CLOCK_CONTRACT_VERSION || saved.timeout_ms !== timeoutMs
21
+ || !Number.isFinite(saved.started_at) || !Number.isSafeInteger(saved.gaps) || saved.gaps < 0
22
+ || saved.observed_at !== null && !Number.isFinite(saved.observed_at)
23
+ || typeof saved.observation_lost !== "boolean"
24
+ || saved.uncertain !== undefined && typeof saved.uncertain !== "boolean"
25
+ || !Number.isFinite(saved.elapsed_ms) || saved.elapsed_ms < 0
26
+ || !Number.isFinite(saved.uncertainty_elapsed_ms) || saved.uncertainty_elapsed_ms < 0)
27
+ throw new RangeError("invalid retained observation clock");
28
+ this.startedWall = saved.started_at; this.elapsed = saved.elapsed_ms;
29
+ this.progress = saved.cursor; this.progressAt = saved.observed_at;
30
+ // Durable reconstruction has an observation gap. Transferring a freshly
31
+ // observed producer clock is not a second restart of that same producer.
32
+ this.uncertain = snapshotIsLive ? saved.uncertain ?? true : true;
33
+ this.gaps = saved.gaps + (snapshotIsLive ? 0 : 1);
34
+ this.uncertaintyElapsed = saved.uncertainty_elapsed_ms;
35
+ this.observationLost = saved.observation_lost || this.gaps > 1 || this.uncertaintyElapsed >= uncertaintyMs;
36
+ }
37
+ }
38
+ sample(progress, progressAt) {
39
+ if (progress && typeof progress === "object" && Object.hasOwn(progress, "cursor")) {
40
+ progressAt = progress.observed_at; progress = progress.cursor;
41
+ }
42
+ const wall = this.wall(), mono = this.monotonic();
43
+ const delta = mono - this.lastMono, wallDelta = wall - this.lastWall;
44
+ const gap = !Number.isFinite(delta) || !Number.isFinite(wallDelta)
45
+ || delta > this.gapMs || wallDelta > this.gapMs || delta < 0 || wallDelta < 0;
46
+ if (gap) {
47
+ this.uncertain = true; this.gaps++;
48
+ this.onGap({ source: "observer_clock", classification: "observation_gap", confirmed_sleep: false,
49
+ started_at: Number.isFinite(this.lastWall) ? this.lastWall : null, observed_at: Number.isFinite(wall) ? wall : null,
50
+ wall_delta_ms: wallDelta, monotonic_delta_ms: delta });
51
+ } else {
52
+ this.elapsed += delta;
53
+ if (this.uncertain) this.uncertaintyElapsed += delta;
54
+ }
55
+ this.lastWall = wall; this.lastMono = mono;
56
+ const validCursor = typeof progress === "string" || typeof progress === "boolean"
57
+ || typeof progress === "number" && Number.isFinite(progress);
58
+ const changed = validCursor && progress !== this.progress;
59
+ const timestamped = progressAt !== undefined;
60
+ const validTime = !timestamped ? wall >= this.startedWall && (this.progressAt === null || wall >= this.progressAt)
61
+ : Number.isFinite(progressAt) && progressAt >= this.startedWall
62
+ && progressAt <= wall && (this.progressAt === null || progressAt >= this.progressAt);
63
+ if (changed && validTime && !this.observationLost) {
64
+ const eventAt = timestamped ? progressAt : wall;
65
+ // A delayed pre-gap event cannot establish that observation has recovered.
66
+ const recovers = !this.uncertain || eventAt >= wall - Math.max(0, gap ? 0 : delta);
67
+ this.progress = progress; this.progressAt = eventAt;
68
+ this.elapsed = Math.max(0, wall - eventAt);
69
+ if (recovers) { this.uncertain = false; this.gaps = 0; this.uncertaintyElapsed = 0; }
70
+ }
71
+ if (changed && timestamped && Number.isFinite(progressAt) && !validTime && progressAt > wall) {
72
+ if (!this.uncertain) this.onGap({ source: "observer_clock", classification: "clock_skew",
73
+ confirmed_sleep: false, observed_at: wall, signal_at: progressAt });
74
+ this.uncertain = true;
75
+ }
76
+ if (this.uncertain && (this.gaps > 1 || this.uncertaintyElapsed >= this.uncertaintyMs)) this.observationLost = true;
77
+ return this.observationLost || !this.uncertain && this.elapsed >= this.timeoutMs;
78
+ }
79
+ state() {
80
+ return { policy: OBSERVATION_CLOCK_CONTRACT_VERSION, timeout_ms: this.timeoutMs,
81
+ started_at: this.startedWall, cursor: this.progress, observed_at: this.progressAt,
82
+ elapsed_ms: this.elapsed, uncertainty_elapsed_ms: this.uncertaintyElapsed,
83
+ gaps: this.gaps, uncertain: this.uncertain, observation_lost: this.observationLost };
84
+ }
85
+ }
86
+
87
+ /** Node timer handle, compatible with existing clearTimeout cleanup. */
88
+ export function observedTimeout(callback, timeoutMs, options = {}) {
89
+ const clock = new ObservationClock({ timeoutMs, ...options });
90
+ const timer = setInterval(() => {
91
+ const snapshot = options.progress?.();
92
+ if (clock.sample(snapshot)) { clearInterval(timer); callback(clock); }
93
+ }, Math.max(1, Math.min(1_000, timeoutMs / 4)));
94
+ return timer;
95
+ }
@@ -0,0 +1,85 @@
1
+ import path from "node:path";
2
+ import { managedFlowEnv } from './managed-flow-env.mjs';
3
+ import * as Plugin from "../vendor/opencode-plugin.mjs";
4
+ import { hookBudget, invokeNativeHook, writeNativeHookFailure } from "./native-hook-command.mjs";
5
+ import { callDaemon } from "./dd-opencode-daemon.mjs";
6
+ import { errorRecord } from "./operation-errors.mjs";
7
+
8
+ /** Launch-bound server plugin; never uses a process-global 'last Session'. */
9
+ export function makeOpenCodePlugin(binding, invoke = invokeNativeHook, resolve = callDaemon) {
10
+ return Plugin.define({ id: binding.ddFlowBin ? "dd-flow" : "dd-flow-security", async setup(ctx) {
11
+ const participating = new Map();
12
+ const key = event => JSON.stringify([binding.daemonId, event.sessionID, event.messageID, event.id]);
13
+ const envelope = async (phase, event, budget, outcome) => {
14
+ const input = event.input;
15
+ if (!input || typeof input !== "object" || typeof input.command !== "string") throw new Error("OpenCode shell input is unavailable");
16
+ const identity = await budget.run(() => resolve(binding.stateDir, "hook.resolve", { sessionId: event.sessionID,
17
+ payload: { phase, message_id: event.messageID, tool_call_id: event.id, input } }, budget.remaining("identity")), "identity");
18
+ const info = identity?.nativeSession;
19
+ if (!identity?.nativeHookBinding || info?.id !== event.sessionID || !info.directory) throw Object.assign(new Error("OpenCode Session identity is unavailable"), { code: "native_hook_unproven" });
20
+ const cwd = path.resolve(info.directory, input.workdir ?? ".");
21
+ return { schema_id: "dd-flow/opencode-tool-event@1", native_protocol: 2,
22
+ native_hook_binding: identity.nativeHookBinding,
23
+ event_id: [binding.daemonId, event.sessionID, event.messageID, event.id].join(":"), phase,
24
+ daemon_id: binding.daemonId, server_version: binding.version, plugin_sha256: process.env.DD_OPENCODE_PLUGIN_SHA256,
25
+ session: { provider_session_id: info.id, parent_provider_session_id: info.parentID ?? null,
26
+ agent_id: event.agent, directory: cwd, project_id: info.projectID },
27
+ message_id: event.messageID, tool_call_id: event.id, tool: "shell", native_tool: event.tool,
28
+ profile: { provider: info.model?.providerID, model: info.model?.id, variant: info.model?.variant ?? "default", agent: event.agent },
29
+ input: { ...input, workdir: cwd }, ...(outcome ? { outcome } : {}) };
30
+ };
31
+ const deliver = async (payload, budget) => {
32
+ const identity = { nativeHookBinding: payload.native_hook_binding };
33
+ try {
34
+ const result = await invoke({ bin: binding.ddFlowBin, nodeBin: binding.nodeBin,
35
+ args: ["opencode", "event", "handle", "--project-root", binding.projectRoot, "--json"],
36
+ payload: { ...payload, native_hook_binding: identity.nativeHookBinding }, home: binding.ddFlowHome, timeoutMs: budget.remaining("admission") });
37
+ await budget.run(() => resolve(binding.stateDir, "hook.verify", { binding: identity.nativeHookBinding }, budget.remaining("generation")), "generation");
38
+ if (result?.permissionDecision === "deny" || result?.hookSpecificOutput?.permissionDecision === "deny") throw Object.assign(new Error("dd-flow denied native shell admission"), { code: "native_hook_rejected" });
39
+ return result;
40
+ } catch (error) {
41
+ error.details = { ...error.details, native_hook_binding: identity.nativeHookBinding, ...(payload.phase === "after" ? { effect: "unknown", tool_outcome: payload.outcome } : {}) };
42
+ try { await writeNativeHookFailure(binding.stateDir, identity.nativeHookBinding, error, budget); await resolve(binding.stateDir, "hook.failed", {}, budget.remaining("failure_notification")); }
43
+ catch (storage) {
44
+ error.details.receipt_error = { code: storage.code ?? "hook_receipt_write_failed", message: storage.message };
45
+ try { await resolve(binding.stateDir, "hook.failed", { binding: identity.nativeHookBinding, failure: errorRecord(error) }, budget.remaining("failure_notification")); }
46
+ catch (notify) { error.details.receipt_error.notification_error = notify.code ?? "hook_notification_failed"; }
47
+ }
48
+ throw error;
49
+ }
50
+ };
51
+ await ctx.shell.hook("create.before", event => {
52
+ Object.assign(event.env, managedFlowEnv(binding));
53
+ delete event.env.OPENCODE_PASSWORD; delete event.env.OPENCODE_SERVER_PASSWORD;
54
+ });
55
+ await ctx.tool.hook("execute.before", async event => {
56
+ if (event.tool !== "shell") return;
57
+ if (event.input.timeout === undefined) event.input.timeout = 0;
58
+ if (!binding.ddFlowBin) return;
59
+ const budget = hookBudget(), payload = await envelope("before", event, budget);
60
+ event.input = { ...event.input, workdir: payload.session.directory };
61
+ const result = await deliver({ ...payload, input: event.input }, budget);
62
+ if (result?.observed) {
63
+ participating.set(key(event), payload);
64
+ await ctx.storage.set(key(event), JSON.parse(JSON.stringify(payload)));
65
+ // The common observer, not OpenCode's fixed 120-second cutoff, owns
66
+ // flow CLI inactivity. No lifecycle process is detached here.
67
+ event.input = { ...event.input, ...(result.hookSpecificOutput?.updatedInput ?? {}), timeout: 0, background: false };
68
+ } else if (event.input.timeout === undefined) event.input.timeout = 0;
69
+ });
70
+ await ctx.tool.hook("execute.after", async event => {
71
+ if (event.tool !== "shell") return;
72
+ const budget = hookBudget(), original = participating.get(key(event)) ?? await ctx.storage.get(key(event));
73
+ if (!original) return; // Ordinary shell tools do not create lifecycle receipts.
74
+ const output = event.result?.output;
75
+ const success = event.status === "completed" && output?.status !== "running" && output?.exit === 0 && !output?.timeout && !output?.signal;
76
+ const outcome = { status: success ? "completed" : output?.status === "running" ? "running" : "failed",
77
+ exit_code: output?.exit ?? null, signal: output?.signal ?? null, shell_id: output?.shellID ?? null };
78
+ const payload = await envelope("after", event, budget, outcome);
79
+ await deliver({ ...payload, ...(original ? { event_id: original.event_id } : {}) }, budget);
80
+ participating.delete(key(event));
81
+ await ctx.storage.remove(key(event));
82
+ });
83
+ return () => participating.clear();
84
+ } });
85
+ }
@@ -0,0 +1,5 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+
3
+ // The existing runner operation owns its child driver requests, including calls
4
+ // nested in Judge/HITL/fanout. Async context keeps parallel executions separate.
5
+ export const operationContext = new AsyncLocalStorage();
@@ -0,0 +1,16 @@
1
+ export const OPERATION_ERROR_CONTRACT_VERSION: "operation-errors@2";
2
+ export interface ErrorRecord {
3
+ code: string;
4
+ message: string;
5
+ retryable?: boolean;
6
+ exitCode?: number;
7
+ details?: unknown;
8
+ cause?: ErrorRecord;
9
+ cleanup_error?: ErrorRecord;
10
+ }
11
+ export function errorRecord(error: unknown, depth?: number): ErrorRecord;
12
+ export function redactDiagnostic(value: string): string;
13
+ export function diagnosticJson(value: unknown): string;
14
+ export function isObservationLoss(error: unknown): boolean;
15
+ export function isProvenShellCorrection(error: unknown): boolean;
16
+ export function reportedError(value: Record<string, unknown>, fallback: string): Error & ErrorRecord;