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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (670) hide show
  1. package/CHANGELOG.md +965 -0
  2. package/README.md +139 -10
  3. package/dist/build-info.json +8 -8
  4. package/dist/cli/argument-values.js +38 -0
  5. package/dist/cli/command-inputs.js +417 -0
  6. package/dist/cli/command-metadata.js +303 -0
  7. package/dist/cli/command-option-descriptions.js +192 -0
  8. package/dist/cli/completion-cli.js +70 -0
  9. package/dist/cli/completion.js +57 -0
  10. package/dist/cli/delivery.js +108 -0
  11. package/dist/cli/diagnostic-invocation.js +62 -0
  12. package/dist/cli/diagnostic.js +131 -0
  13. package/dist/cli/error-output.js +62 -0
  14. package/dist/cli/executor-onboarding.js +76 -0
  15. package/dist/cli/flows.js +29 -0
  16. package/dist/cli/harnesses.js +84 -0
  17. package/dist/cli/help.js +521 -19
  18. package/dist/cli/hook-ingress.js +89 -0
  19. package/dist/cli/input-preparation.js +193 -0
  20. package/dist/cli/interactive-invocation.js +33 -0
  21. package/dist/cli/interactive.js +114 -0
  22. package/dist/cli/memory-fix-invocation.js +52 -0
  23. package/dist/cli/memory-fix.js +100 -0
  24. package/dist/cli/memory-flow-admission.js +54 -0
  25. package/dist/cli/memory-init-invocation.js +85 -0
  26. package/dist/cli/memory-init.js +250 -0
  27. package/dist/cli/memory-storage-admission.js +54 -0
  28. package/dist/cli/memory-storage-qualification.js +105 -0
  29. package/dist/cli/memory-storage-transition.js +123 -0
  30. package/dist/cli/memory-upgrade.js +189 -0
  31. package/dist/cli/models.js +39 -0
  32. package/dist/cli/native-hook-ingress.js +94 -0
  33. package/dist/cli/native-hook-preparation.js +82 -0
  34. package/dist/cli/operator-profiles.js +61 -0
  35. package/dist/cli/output-options.js +35 -0
  36. package/dist/cli/response-file.js +72 -0
  37. package/dist/cli/run-cli.js +2020 -555
  38. package/dist/cli/runtime-maintenance-diagnostics.js +46 -0
  39. package/dist/cli/runtime-maintenance-ingress.js +52 -0
  40. package/dist/cli/runtime-process.js +153 -0
  41. package/dist/cli/setup.js +150 -0
  42. package/dist/cli.js +103 -2
  43. package/dist/domain/delivery-flows.js +8 -0
  44. package/dist/domain/diagnostic-aspects.js +103 -0
  45. package/dist/domain/diagnostic-flows.js +8 -0
  46. package/dist/domain/distill-flow.js +20 -0
  47. package/dist/domain/interactive-flow.js +1 -0
  48. package/dist/domain/memory-fix-flow.js +2 -0
  49. package/dist/domain/review-fix-flow.js +2 -0
  50. package/dist/domain/source-snapshot-flows.js +4 -0
  51. package/dist/domain/stage-catalog.js +1 -1
  52. package/dist/domain/stage-handler-identities.js +28 -0
  53. package/dist/harness-runtime/DD-ZCODE.md +112 -0
  54. package/dist/harness-runtime/OPENCODE-LICENSE.txt +24 -0
  55. package/dist/harness-runtime/bin/dd-agy.mjs +71 -0
  56. package/dist/harness-runtime/bin/dd-codex.mjs +27 -0
  57. package/dist/harness-runtime/bin/dd-droid.mjs +34 -0
  58. package/dist/harness-runtime/bin/dd-grok.mjs +50 -0
  59. package/dist/harness-runtime/bin/dd-opencode.mjs +22 -0
  60. package/dist/harness-runtime/bin/dd-zcode.mjs +95 -0
  61. package/dist/harness-runtime/lib/adapter-timeouts.d.mts +5 -0
  62. package/dist/harness-runtime/lib/adapter-timeouts.mjs +34 -0
  63. package/dist/harness-runtime/lib/agy-error-boundary.mjs +87 -0
  64. package/dist/harness-runtime/lib/agy-hooks.d.mts +1 -0
  65. package/dist/harness-runtime/lib/agy-hooks.mjs +9 -0
  66. package/dist/harness-runtime/lib/agy-native-inspection.mjs +225 -0
  67. package/dist/harness-runtime/lib/agy-native-inventory.mjs +19 -0
  68. package/dist/harness-runtime/lib/agy-native_pb.js +117 -0
  69. package/dist/harness-runtime/lib/agy-terminal-envelope.mjs +74 -0
  70. package/dist/harness-runtime/lib/agy-transcript-stream.mjs +54 -0
  71. package/dist/harness-runtime/lib/daemon-observation.d.mts +3 -0
  72. package/dist/harness-runtime/lib/daemon-observation.mjs +171 -0
  73. package/dist/harness-runtime/lib/daemon-operations.d.mts +2 -0
  74. package/dist/harness-runtime/lib/daemon-operations.mjs +512 -0
  75. package/dist/harness-runtime/lib/dd-agy-daemon.d.mts +7 -0
  76. package/dist/harness-runtime/lib/dd-agy-daemon.mjs +1362 -0
  77. package/dist/harness-runtime/lib/dd-agy.mjs +105 -0
  78. package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
  79. package/dist/harness-runtime/lib/dd-codex-daemon.mjs +271 -0
  80. package/dist/harness-runtime/lib/dd-codex.d.mts +1 -0
  81. package/dist/harness-runtime/lib/dd-codex.mjs +986 -0
  82. package/dist/harness-runtime/lib/dd-droid-daemon.mjs +155 -0
  83. package/dist/harness-runtime/lib/dd-droid.mjs +739 -0
  84. package/dist/harness-runtime/lib/dd-grok-daemon.d.mts +1 -0
  85. package/dist/harness-runtime/lib/dd-grok-daemon.mjs +667 -0
  86. package/dist/harness-runtime/lib/dd-grok.mjs +182 -0
  87. package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +772 -0
  88. package/dist/harness-runtime/lib/dd-opencode.mjs +411 -0
  89. package/dist/harness-runtime/lib/dd-zcode-daemon.d.mts +1 -0
  90. package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +845 -0
  91. package/dist/harness-runtime/lib/dd-zcode.mjs +1239 -0
  92. package/dist/harness-runtime/lib/delegation-instructions.d.mts +16 -0
  93. package/dist/harness-runtime/lib/delegation-instructions.mjs +179 -0
  94. package/dist/harness-runtime/lib/delivery-command.mjs +84 -0
  95. package/dist/harness-runtime/lib/dispatch-fence.mjs +14 -0
  96. package/dist/harness-runtime/lib/driver-recovery.mjs +121 -0
  97. package/dist/harness-runtime/lib/droid-observation.mjs +70 -0
  98. package/dist/harness-runtime/lib/entity-references.d.mts +2 -0
  99. package/dist/harness-runtime/lib/entity-references.mjs +9 -0
  100. package/dist/harness-runtime/lib/lease-renewal.d.mts +8 -0
  101. package/dist/harness-runtime/lib/lease-renewal.mjs +37 -0
  102. package/dist/harness-runtime/lib/lifecycle-command.d.mts +20 -0
  103. package/dist/harness-runtime/lib/lifecycle-command.mjs +273 -0
  104. package/dist/harness-runtime/lib/lifecycle-contract.d.mts +11 -0
  105. package/dist/harness-runtime/lib/lifecycle-contract.mjs +20 -0
  106. package/dist/harness-runtime/lib/managed-daemon.d.mts +16 -0
  107. package/dist/harness-runtime/lib/managed-daemon.mjs +698 -0
  108. package/dist/harness-runtime/lib/managed-flow-env.mjs +15 -0
  109. package/dist/harness-runtime/lib/model-observations.mjs +113 -0
  110. package/dist/harness-runtime/lib/native-children.d.mts +34 -0
  111. package/dist/harness-runtime/lib/native-children.mjs +265 -0
  112. package/dist/harness-runtime/lib/native-hook-command.d.mts +8 -0
  113. package/dist/harness-runtime/lib/native-hook-command.mjs +306 -0
  114. package/dist/harness-runtime/lib/native-request-proof.d.mts +2 -0
  115. package/dist/harness-runtime/lib/native-request-proof.mjs +65 -0
  116. package/dist/harness-runtime/lib/native-streams.mjs +38 -0
  117. package/dist/harness-runtime/lib/observation-clock.d.mts +18 -0
  118. package/dist/harness-runtime/lib/observation-clock.mjs +95 -0
  119. package/dist/harness-runtime/lib/opencode-plugin.mjs +112 -0
  120. package/dist/harness-runtime/lib/operation-context.mjs +5 -0
  121. package/dist/harness-runtime/lib/operation-errors.d.mts +16 -0
  122. package/dist/harness-runtime/lib/operation-errors.mjs +140 -0
  123. package/dist/harness-runtime/lib/operation-progress.d.mts +7 -0
  124. package/dist/harness-runtime/lib/operation-progress.mjs +118 -0
  125. package/dist/harness-runtime/lib/process-json.d.mts +5 -0
  126. package/dist/harness-runtime/lib/process-json.mjs +200 -0
  127. package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
  128. package/dist/harness-runtime/lib/provider-overload-policy.d.mts +23 -0
  129. package/dist/harness-runtime/lib/provider-overload-policy.mjs +242 -0
  130. package/dist/harness-runtime/lib/runner-events.mjs +202 -0
  131. package/dist/harness-runtime/lib/runner-lock.mjs +42 -0
  132. package/dist/harness-runtime/lib/session-settlement.mjs +30 -0
  133. package/dist/harness-runtime/lib/setup-progress.d.mts +9 -0
  134. package/dist/harness-runtime/lib/setup-progress.mjs +53 -0
  135. package/dist/harness-runtime/lib/tool-observations.d.mts +21 -0
  136. package/dist/harness-runtime/lib/tool-observations.mjs +238 -0
  137. package/dist/harness-runtime/lib/zcode-native-hook-ownership.mjs +109 -0
  138. package/dist/harness-runtime/lib/zcode-usage.mjs +55 -0
  139. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-1y4a697q.js +81 -0
  140. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-2ny4vxct.js +92 -0
  141. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-6mptkzbc.js +0 -0
  142. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-7x67cf1q.js +52 -0
  143. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9rqn6x4v.js +16 -0
  144. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9vdz0zwn.js +1661 -0
  145. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ecs5ftka.js +1466 -0
  146. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-frbwqjmf.js +145 -0
  147. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gne7h7mq.js +50 -0
  148. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gx9wnkzr.js +105 -0
  149. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-m2eywc8w.js +0 -0
  150. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-nwcpakzx.js +12 -0
  151. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-q4g3afnw.js +623 -0
  152. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-qav6mfdz.js +0 -0
  153. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-sxt88q69.js +12 -0
  154. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-t1xa31vm.js +39 -0
  155. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ve4aws36.js +103 -0
  156. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vmytr4q8.js +4 -0
  157. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vt9jbch8.js +36 -0
  158. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vyas3vgt.js +0 -0
  159. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-wjyeq25y.js +30 -0
  160. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-xgnj2w4v.js +20 -0
  161. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-y2etg5bs.js +9 -0
  162. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ydhz1zrh.js +238 -0
  163. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-z6qdwcsf.js +62 -0
  164. package/dist/harness-runtime/vendor/opencode-client/contract.d.ts +1 -0
  165. package/dist/harness-runtime/vendor/opencode-client/contract.js +12 -0
  166. package/dist/harness-runtime/vendor/opencode-client/effect/api/api.d.ts +2806 -0
  167. package/dist/harness-runtime/vendor/opencode-client/effect/api/api.js +0 -0
  168. package/dist/harness-runtime/vendor/opencode-client/effect/api.d.ts +4 -0
  169. package/dist/harness-runtime/vendor/opencode-client/effect/api.js +0 -0
  170. package/dist/harness-runtime/vendor/opencode-client/effect/client.d.ts +2530 -0
  171. package/dist/harness-runtime/vendor/opencode-client/effect/client.js +15 -0
  172. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.d.ts +7 -0
  173. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.js +6 -0
  174. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.d.ts +2529 -0
  175. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.js +8 -0
  176. package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.d.ts +2 -0
  177. package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.js +12 -0
  178. package/dist/harness-runtime/vendor/opencode-client/effect/index.d.ts +35 -0
  179. package/dist/harness-runtime/vendor/opencode-client/effect/index.js +75 -0
  180. package/dist/harness-runtime/vendor/opencode-client/effect/rpc.d.ts +20 -0
  181. package/dist/harness-runtime/vendor/opencode-client/effect/rpc.js +11 -0
  182. package/dist/harness-runtime/vendor/opencode-client/effect/service.d.ts +90 -0
  183. package/dist/harness-runtime/vendor/opencode-client/effect/service.js +187 -0
  184. package/dist/harness-runtime/vendor/opencode-client/package.json +84 -0
  185. package/dist/harness-runtime/vendor/opencode-client/promise/api.d.ts +21 -0
  186. package/dist/harness-runtime/vendor/opencode-client/promise/api.js +0 -0
  187. package/dist/harness-runtime/vendor/opencode-client/promise/client.d.ts +252 -0
  188. package/dist/harness-runtime/vendor/opencode-client/promise/client.js +14 -0
  189. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.d.ts +8 -0
  190. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.js +6 -0
  191. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.d.ts +259 -0
  192. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.js +7 -0
  193. package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.d.ts +3 -0
  194. package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.js +79 -0
  195. package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.d.ts +8932 -0
  196. package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.js +70 -0
  197. package/dist/harness-runtime/vendor/opencode-client/promise/index.d.ts +6 -0
  198. package/dist/harness-runtime/vendor/opencode-client/promise/index.js +83 -0
  199. package/dist/harness-runtime/vendor/opencode-client/promise/rpc.d.ts +32 -0
  200. package/dist/harness-runtime/vendor/opencode-client/promise/rpc.js +7 -0
  201. package/dist/harness-runtime/vendor/opencode-client/promise/service.d.ts +24 -0
  202. package/dist/harness-runtime/vendor/opencode-client/promise/service.js +187 -0
  203. package/dist/harness-runtime/vendor/opencode-client/pty-handoff.d.ts +12 -0
  204. package/dist/harness-runtime/vendor/opencode-client/pty-handoff.js +14 -0
  205. package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.d.ts +20 -0
  206. package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.js +14 -0
  207. package/dist/harness-runtime/vendor/opencode-client/service-contender.d.ts +37 -0
  208. package/dist/harness-runtime/vendor/opencode-client/service-contender.js +10 -0
  209. package/dist/harness-runtime/vendor/opencode-client/service-probe.d.ts +70 -0
  210. package/dist/harness-runtime/vendor/opencode-client/service-probe.js +16 -0
  211. package/dist/harness-runtime/vendor/opencode-client/service-timing.d.ts +12 -0
  212. package/dist/harness-runtime/vendor/opencode-client/service-timing.js +10 -0
  213. package/dist/harness-runtime/vendor/opencode-client/service-version.d.ts +2 -0
  214. package/dist/harness-runtime/vendor/opencode-client/service-version.js +6 -0
  215. package/dist/harness-runtime/vendor/opencode-client/service.d.ts +52 -0
  216. package/dist/harness-runtime/vendor/opencode-client/service.js +1 -0
  217. package/dist/harness-runtime/vendor/opencode-client/shared-events.d.ts +11 -0
  218. package/dist/harness-runtime/vendor/opencode-client/shared-events.js +8 -0
  219. package/dist/harness-runtime/vendor/opencode-client/solid/connection.d.ts +37 -0
  220. package/dist/harness-runtime/vendor/opencode-client/solid/connection.js +10 -0
  221. package/dist/harness-runtime/vendor/opencode-client/solid/data.d.ts +211 -0
  222. package/dist/harness-runtime/vendor/opencode-client/solid/data.js +18 -0
  223. package/dist/harness-runtime/vendor/opencode-client/solid/index.d.ts +3 -0
  224. package/dist/harness-runtime/vendor/opencode-client/solid/index.js +32 -0
  225. package/dist/harness-runtime/vendor/opencode-client/solid/pty.d.ts +22 -0
  226. package/dist/harness-runtime/vendor/opencode-client/solid/pty.js +8 -0
  227. package/dist/harness-runtime/vendor/opencode-plugin.mjs +3 -0
  228. package/dist/protocol/local-files.js +18 -9
  229. package/dist/runtime/context.js +8 -4
  230. package/dist/schemas/agent-profile.schema.json +1 -1
  231. package/dist/schemas/code-review-decision.schema.json +9 -0
  232. package/dist/schemas/code-review-result.schema.json +1 -1
  233. package/dist/schemas/code-verification.schema.json +7 -5
  234. package/dist/schemas/code-work-batch.schema.json +2 -2
  235. package/dist/schemas/code-work-result.schema.json +2 -1
  236. package/dist/schemas/delivery-stage-result.schema.json +286 -0
  237. package/dist/schemas/diagnostic-stage-result.schema.json +354 -0
  238. package/dist/schemas/diagnostic-work-result.schema.json +296 -0
  239. package/dist/schemas/discovery-result.schema.json +184 -0
  240. package/dist/schemas/distill-stage-result.schema.json +667 -0
  241. package/dist/schemas/distill-work-result.schema.json +428 -0
  242. package/dist/schemas/execution-bundle.schema.json +215 -0
  243. package/dist/schemas/execution-settings.schema.json +162 -0
  244. package/dist/schemas/flow-descriptor.schema.json +54 -0
  245. package/dist/schemas/flow-run.schema.json +29 -0
  246. package/dist/schemas/harness-catalog.schema.json +80 -0
  247. package/dist/schemas/interactive-stage-result.schema.json +47 -0
  248. package/dist/schemas/interactive-work-result.schema.json +63 -0
  249. package/dist/schemas/mb-fix-stage-result.schema.json +93 -0
  250. package/dist/schemas/mb-init-compose-result.schema.json +457 -0
  251. package/dist/schemas/mb-init-discover-result.schema.json +500 -0
  252. package/dist/schemas/mb-init-finish-result.schema.json +422 -0
  253. package/dist/schemas/mb-init-preflight-result.schema.json +367 -0
  254. package/dist/schemas/mb-init-review-result.schema.json +396 -0
  255. package/dist/schemas/mb-init-verify-result.schema.json +391 -0
  256. package/dist/schemas/mb-upgrade-stage-result.schema.json +863 -0
  257. package/dist/schemas/memory-bootstrap-receipt-v2.schema.json +67 -0
  258. package/dist/schemas/memory-bootstrap-receipt.schema.json +354 -0
  259. package/dist/schemas/memory-composition-work-result.schema.json +38 -0
  260. package/dist/schemas/memory-repair-work-result.schema.json +56 -0
  261. package/dist/schemas/memory-review-correction-plan.schema.json +30 -0
  262. package/dist/schemas/memory-review-work-result.schema.json +43 -0
  263. package/dist/schemas/model-catalog.schema.json +153 -0
  264. package/dist/schemas/plan-review-decision.schema.json +12 -3
  265. package/dist/schemas/plan-review-result.schema.json +2 -2
  266. package/dist/schemas/profile-settings.schema.json +122 -0
  267. package/dist/schemas/project-flow-pack-manifest.schema.json +2 -2
  268. package/dist/schemas/release-impact.schema.json +3 -3
  269. package/dist/schemas/release-manifest.schema.json +291 -0
  270. package/dist/schemas/review-fix-stage-result.schema.json +210 -0
  271. package/dist/schemas/run-control-receipt.schema.json +119 -0
  272. package/dist/schemas/run-control-request.schema.json +36 -0
  273. package/dist/schemas/run-engine-binding.schema.json +1 -1
  274. package/dist/schemas/stage-start-response.schema.json +4 -0
  275. package/dist/schemas/vnext-protocol-plan.schema.json +3 -3
  276. package/dist/services/accepted-intent.js +139 -0
  277. package/dist/services/assigned-lifecycle.js +382 -0
  278. package/dist/services/branch-context.js +3 -3
  279. package/dist/services/canon.js +54 -15
  280. package/dist/services/check-environment.js +21 -0
  281. package/dist/services/check-runner-evidence.js +344 -0
  282. package/dist/services/check-runner-proof.js +373 -0
  283. package/dist/services/cleanup.js +133 -40
  284. package/dist/services/cli-operation-classifier.js +32 -64
  285. package/dist/services/cli-upgrade-allowlist.js +27 -0
  286. package/dist/services/code-checks.js +1426 -121
  287. package/dist/services/codex-capacity.js +367 -0
  288. package/dist/services/codex-hook-delivery.js +41 -0
  289. package/dist/services/command-context.js +80 -0
  290. package/dist/services/compatibility-preflight.js +16 -3
  291. package/dist/services/config.js +11 -8
  292. package/dist/services/continuation-outcome.js +25 -0
  293. package/dist/services/controller-child-capacity.js +71 -0
  294. package/dist/services/controller-child-observer.js +121 -0
  295. package/dist/services/controller-child-repair.js +117 -0
  296. package/dist/services/controller-execution.js +97 -0
  297. package/dist/services/controller-fanout.js +277 -0
  298. package/dist/services/controller-memory-task.js +45 -0
  299. package/dist/services/controller-memory-worker.js +51 -0
  300. package/dist/services/controller-result-authority.js +127 -0
  301. package/dist/services/controller-work-failure.js +198 -0
  302. package/dist/services/dashboard.js +145 -58
  303. package/dist/services/delivery-access.js +123 -0
  304. package/dist/services/delivery-approval.js +68 -0
  305. package/dist/services/delivery-artifact.js +86 -0
  306. package/dist/services/delivery-checks.js +178 -0
  307. package/dist/services/delivery-execution.js +181 -0
  308. package/dist/services/delivery-intents.js +212 -0
  309. package/dist/services/delivery-operation.js +81 -0
  310. package/dist/services/delivery-policy.js +91 -0
  311. package/dist/services/delivery-procedures.js +43 -0
  312. package/dist/services/delivery-request.js +99 -0
  313. package/dist/services/delivery-result.js +144 -0
  314. package/dist/services/delivery-stage-commands.js +26 -0
  315. package/dist/services/delivery-stage-entry.js +56 -0
  316. package/dist/services/delivery-stage-handlers.js +135 -0
  317. package/dist/services/delivery-stage-results.js +94 -0
  318. package/dist/services/descriptor-coordinator-session.js +37 -0
  319. package/dist/services/descriptor-root-work.js +53 -0
  320. package/dist/services/descriptor-run-settlement.js +107 -0
  321. package/dist/services/descriptor-run-start.js +495 -0
  322. package/dist/services/descriptor-stage-coordinator.js +318 -0
  323. package/dist/services/descriptor-stage-settlement.js +268 -0
  324. package/dist/services/descriptor-stage-start.js +175 -0
  325. package/dist/services/diagnostic-evidence.js +73 -0
  326. package/dist/services/diagnostic-repair-intake.js +123 -0
  327. package/dist/services/diagnostic-repair-source-intake.js +102 -0
  328. package/dist/services/diagnostic-report-publication.js +72 -0
  329. package/dist/services/diagnostic-report.js +97 -0
  330. package/dist/services/diagnostic-run-bundle.js +113 -0
  331. package/dist/services/diagnostic-snapshot.js +159 -0
  332. package/dist/services/diagnostic-stage-handlers.js +109 -0
  333. package/dist/services/diagnostic-stage-results.js +158 -0
  334. package/dist/services/diagnostic-start.js +134 -0
  335. package/dist/services/diagnostic-work-batch.js +34 -0
  336. package/dist/services/diagnostic-work-collection.js +97 -0
  337. package/dist/services/diagnostic-work-registration.js +101 -0
  338. package/dist/services/diagnostic-work-results.js +93 -0
  339. package/dist/services/discovery/agy-discovery.js +2 -0
  340. package/dist/services/discovery/codex-discovery.js +2 -0
  341. package/dist/services/discovery/droid-discovery.js +2 -0
  342. package/dist/services/discovery/grok-discovery.js +2 -0
  343. package/dist/services/discovery/opencode-discovery.js +2 -0
  344. package/dist/services/discovery/static.js +31 -0
  345. package/dist/services/discovery/zcode-discovery.js +2 -0
  346. package/dist/services/distill-document-publication.js +280 -0
  347. package/dist/services/distill-promotion.js +197 -0
  348. package/dist/services/distill-publication-custody.js +162 -0
  349. package/dist/services/distill-stage-handlers.js +249 -0
  350. package/dist/services/distill-stage-results.js +105 -0
  351. package/dist/services/distill-user-inputs.js +4 -0
  352. package/dist/services/distill-work-batch.js +42 -0
  353. package/dist/services/distill-work-collection.js +74 -0
  354. package/dist/services/distill-work-registration.js +93 -0
  355. package/dist/services/distill-work-results.js +98 -0
  356. package/dist/services/engines.js +354 -163
  357. package/dist/services/eval-snapshots.js +1326 -150
  358. package/dist/services/execution-bundles.js +275 -0
  359. package/dist/services/execution-policy.js +185 -0
  360. package/dist/services/executor-onboarding.js +56 -0
  361. package/dist/services/external-work-launch.js +316 -0
  362. package/dist/services/flow-catalog.js +54 -0
  363. package/dist/services/flow-descriptors.js +82 -0
  364. package/dist/services/frozen-engine.js +404 -0
  365. package/dist/services/generic-execution.js +165 -0
  366. package/dist/services/harness-adapter.js +342 -35
  367. package/dist/services/harness-config.js +2 -0
  368. package/dist/services/harness-discovery.js +29 -0
  369. package/dist/services/harness-doctor.js +75 -0
  370. package/dist/services/harness-probe-effects.js +51 -0
  371. package/dist/services/harness-version-probe.js +100 -0
  372. package/dist/services/hook-memory-routing.js +188 -0
  373. package/dist/services/hook-recovery-guard.js +14 -0
  374. package/dist/services/hook-session-observation.js +24 -0
  375. package/dist/services/hooks.js +813 -327
  376. package/dist/services/hybrid-contracts.js +479 -0
  377. package/dist/services/hybrid-digest.js +14 -0
  378. package/dist/services/ids.js +29 -1
  379. package/dist/services/installation-lock.js +95 -0
  380. package/dist/services/installed-flow-source.js +261 -0
  381. package/dist/services/interactive-attached-session.js +215 -0
  382. package/dist/services/interactive-baseline-custody.js +54 -0
  383. package/dist/services/interactive-before-versions.js +148 -0
  384. package/dist/services/interactive-bundle-data.js +85 -0
  385. package/dist/services/interactive-checks.js +179 -0
  386. package/dist/services/interactive-execution.js +33 -0
  387. package/dist/services/interactive-finish-integration.js +236 -0
  388. package/dist/services/interactive-git-baseline.js +230 -0
  389. package/dist/services/interactive-git-dispatch.js +278 -0
  390. package/dist/services/interactive-input-delivery.js +82 -0
  391. package/dist/services/interactive-mutation-scope.js +231 -0
  392. package/dist/services/interactive-request.js +287 -0
  393. package/dist/services/interactive-run-bundle.js +109 -0
  394. package/dist/services/interactive-stage-entry.js +41 -0
  395. package/dist/services/interactive-stage-handlers.js +289 -0
  396. package/dist/services/interactive-stage-observation.js +136 -0
  397. package/dist/services/interactive-stage-results.js +111 -0
  398. package/dist/services/interactive-user-inputs.js +142 -0
  399. package/dist/services/interactive-work-batch.js +43 -0
  400. package/dist/services/interactive-work-collection.js +119 -0
  401. package/dist/services/interactive-work-completion.js +180 -0
  402. package/dist/services/interactive-work-context.js +249 -0
  403. package/dist/services/interactive-work-history.js +112 -0
  404. package/dist/services/interactive-work-registration.js +124 -0
  405. package/dist/services/interactive-work-results.js +115 -0
  406. package/dist/services/interactive-work-start.js +155 -0
  407. package/dist/services/lanes.js +78 -65
  408. package/dist/services/lifecycle-command.js +1 -288
  409. package/dist/services/lifecycle-contract.js +1 -0
  410. package/dist/services/lifecycle-invocations.js +2549 -0
  411. package/dist/services/managed-daemon-binding.js +129 -0
  412. package/dist/services/managed-lease-monitor.js +133 -0
  413. package/dist/services/managed-processes.js +449 -69
  414. package/dist/services/memory-accepted-composition.js +78 -0
  415. package/dist/services/memory-baseline-preservation.js +65 -0
  416. package/dist/services/memory-completed-correction-wave.js +328 -0
  417. package/dist/services/memory-compose-handler.js +380 -0
  418. package/dist/services/memory-composition-acceptance.js +375 -0
  419. package/dist/services/memory-composition-graph.js +165 -0
  420. package/dist/services/memory-composition-registration.js +161 -0
  421. package/dist/services/memory-composition-work-custody.js +156 -0
  422. package/dist/services/memory-composition-work-results.js +108 -0
  423. package/dist/services/memory-corrected-review-acceptance.js +129 -0
  424. package/dist/services/memory-corrected-review-batch.js +37 -0
  425. package/dist/services/memory-corrected-review-findings.js +108 -0
  426. package/dist/services/memory-corrected-review-inputs.js +215 -0
  427. package/dist/services/memory-corrected-review-projection.js +78 -0
  428. package/dist/services/memory-corrected-review-receipt.js +178 -0
  429. package/dist/services/memory-corrected-review-registration.js +192 -0
  430. package/dist/services/memory-correction-check-receipt.js +235 -0
  431. package/dist/services/memory-correction-input-owner.js +6 -0
  432. package/dist/services/memory-custody-snapshot.js +36 -0
  433. package/dist/services/memory-discover-handler.js +104 -0
  434. package/dist/services/memory-finish-evidence.js +457 -0
  435. package/dist/services/memory-finish-handler.js +229 -0
  436. package/dist/services/memory-finish-integration.js +491 -0
  437. package/dist/services/memory-finish-readiness.js +169 -0
  438. package/dist/services/memory-fix-finish-integration.js +129 -0
  439. package/dist/services/memory-fix-git-dispatch.js +139 -0
  440. package/dist/services/memory-fix-intake-custody.js +11 -0
  441. package/dist/services/memory-fix-intake-settlement.js +127 -0
  442. package/dist/services/memory-fix-publication.js +196 -0
  443. package/dist/services/memory-fix-repair-admission.js +116 -0
  444. package/dist/services/memory-fix-request.js +141 -0
  445. package/dist/services/memory-fix-run-bundle.js +83 -0
  446. package/dist/services/memory-fix-scope.js +78 -0
  447. package/dist/services/memory-fix-sources.js +85 -0
  448. package/dist/services/memory-fix-stage-entry.js +24 -0
  449. package/dist/services/memory-fix-stage-handlers.js +282 -0
  450. package/dist/services/memory-fix-stage-results.js +106 -0
  451. package/dist/services/memory-fix-workspace.js +127 -0
  452. package/dist/services/memory-flow-user-inputs.js +88 -0
  453. package/dist/services/memory-git-integration.js +220 -0
  454. package/dist/services/memory-historical-corrected-review.js +212 -0
  455. package/dist/services/memory-historical-original-review.js +143 -0
  456. package/dist/services/memory-historical-repair-registration.js +105 -0
  457. package/dist/services/memory-historical-second-review.js +147 -0
  458. package/dist/services/memory-init-bootstrap-readiness.js +210 -0
  459. package/dist/services/memory-init-contexts.js +181 -0
  460. package/dist/services/memory-init-controller-start.js +119 -0
  461. package/dist/services/memory-init-engine-release.js +223 -0
  462. package/dist/services/memory-init-git-policy.js +165 -0
  463. package/dist/services/memory-init-lint-capability.js +203 -0
  464. package/dist/services/memory-init-native-capacity.js +309 -0
  465. package/dist/services/memory-init-preflight.js +334 -0
  466. package/dist/services/memory-init-project-bootstrap.js +599 -0
  467. package/dist/services/memory-init-request-candidate.js +213 -0
  468. package/dist/services/memory-init-request-input.js +82 -0
  469. package/dist/services/memory-init-resource-storage.js +125 -0
  470. package/dist/services/memory-init-run-allocation.js +81 -0
  471. package/dist/services/memory-init-run-bundle.js +135 -0
  472. package/dist/services/memory-init-start.js +181 -0
  473. package/dist/services/memory-init-startup-custody.js +80 -0
  474. package/dist/services/memory-init-startup-input.js +240 -0
  475. package/dist/services/memory-init-storage.js +91 -0
  476. package/dist/services/memory-init-stores.js +359 -0
  477. package/dist/services/memory-init-worktrunk-capability.js +174 -0
  478. package/dist/services/memory-layer-publication.js +611 -0
  479. package/dist/services/memory-layer.js +225 -0
  480. package/dist/services/memory-lint-config.js +194 -0
  481. package/dist/services/memory-native-review-copy.js +127 -0
  482. package/dist/services/memory-normal-flow-entry.js +87 -0
  483. package/dist/services/memory-ordinary-defaults.js +129 -0
  484. package/dist/services/memory-preflight-bootstrap-custody.js +115 -0
  485. package/dist/services/memory-preflight-handler.js +274 -0
  486. package/dist/services/memory-repair-current-view.js +299 -0
  487. package/dist/services/memory-repair-document-delta.js +489 -0
  488. package/dist/services/memory-repair-history.js +496 -0
  489. package/dist/services/memory-repair-physical-binding.js +194 -0
  490. package/dist/services/memory-repair-publication-files.js +152 -0
  491. package/dist/services/memory-repair-publication-intent.js +27 -0
  492. package/dist/services/memory-repair-publication-plan.js +162 -0
  493. package/dist/services/memory-repair-publication-source.js +225 -0
  494. package/dist/services/memory-repair-publication.js +795 -0
  495. package/dist/services/memory-repair-registration.js +240 -0
  496. package/dist/services/memory-repair-work-baseline.js +415 -0
  497. package/dist/services/memory-repair-work-completion.js +117 -0
  498. package/dist/services/memory-repair-work-custody.js +73 -0
  499. package/dist/services/memory-repair-work-results.js +108 -0
  500. package/dist/services/memory-repair-workspace.js +261 -0
  501. package/dist/services/memory-repair-writer-copy.js +736 -0
  502. package/dist/services/memory-result-binding.js +239 -0
  503. package/dist/services/memory-retained-review-authority.js +138 -0
  504. package/dist/services/memory-retained-review-results.js +70 -0
  505. package/dist/services/memory-retained-second-baseline.js +102 -0
  506. package/dist/services/memory-retained-second-repairs.js +162 -0
  507. package/dist/services/memory-review-acceptance.js +214 -0
  508. package/dist/services/memory-review-correction-plan.js +206 -0
  509. package/dist/services/memory-review-handler.js +585 -0
  510. package/dist/services/memory-review-inputs.js +246 -0
  511. package/dist/services/memory-review-limits.js +10 -0
  512. package/dist/services/memory-review-receipt.js +180 -0
  513. package/dist/services/memory-review-registration.js +117 -0
  514. package/dist/services/memory-review-work-custody.js +126 -0
  515. package/dist/services/memory-review-work-input.js +91 -0
  516. package/dist/services/memory-review-work-results.js +95 -0
  517. package/dist/services/memory-second-corrected-review-inputs.js +167 -0
  518. package/dist/services/memory-second-correction-plan.js +77 -0
  519. package/dist/services/memory-second-repair-baseline.js +91 -0
  520. package/dist/services/memory-second-repair-launch.js +614 -0
  521. package/dist/services/memory-second-repair-registration.js +88 -0
  522. package/dist/services/memory-second-repair-source.js +187 -0
  523. package/dist/services/memory-second-review-acceptance.js +115 -0
  524. package/dist/services/memory-source-observations.js +117 -0
  525. package/dist/services/memory-stage-dispatch.js +65 -0
  526. package/dist/services/memory-stage-results.js +290 -0
  527. package/dist/services/memory-tool-probe.js +25 -0
  528. package/dist/services/memory-upgrade-analysis-handler.js +243 -0
  529. package/dist/services/memory-upgrade-bootstrap-readiness.js +251 -0
  530. package/dist/services/memory-upgrade-evidence.js +100 -0
  531. package/dist/services/memory-upgrade-inventory.js +442 -0
  532. package/dist/services/memory-upgrade-publication-custody.js +73 -0
  533. package/dist/services/memory-upgrade-publication.js +413 -0
  534. package/dist/services/memory-upgrade-selection.js +430 -0
  535. package/dist/services/memory-upgrade-start.js +60 -0
  536. package/dist/services/memory-upgrade-storage-readiness.js +340 -0
  537. package/dist/services/memory-verification-acceptance.js +156 -0
  538. package/dist/services/memory-verification-checks.js +317 -0
  539. package/dist/services/memory-verification-receipt.js +160 -0
  540. package/dist/services/memory-verify-handler.js +137 -0
  541. package/dist/services/memory-work-result-json.js +12 -0
  542. package/dist/services/memory-work-result-preparation.js +64 -0
  543. package/dist/services/merge-queue.js +228 -149
  544. package/dist/services/merge-server.js +163 -35
  545. package/dist/services/migrations.js +164 -114
  546. package/dist/services/model-catalog.js +109 -0
  547. package/dist/services/model-discovery.js +46 -0
  548. package/dist/services/native-daemon-history.js +50 -0
  549. package/dist/services/native-session-control.js +39 -0
  550. package/dist/services/operator-profiles.js +236 -0
  551. package/dist/services/physical-startup.js +45 -0
  552. package/dist/services/plan-runtime.js +26 -11
  553. package/dist/services/plans.js +28 -25
  554. package/dist/services/project-flow-source.js +195 -0
  555. package/dist/services/project-git-integration.js +92 -0
  556. package/dist/services/project-registry.js +97 -0
  557. package/dist/services/project-summary.js +1 -1
  558. package/dist/services/projects.js +17 -87
  559. package/dist/services/prompt-delivery.js +71 -0
  560. package/dist/services/prompts.js +28 -17
  561. package/dist/services/protocols.js +163 -87
  562. package/dist/services/provider-capacity.js +463 -0
  563. package/dist/services/recovery-observation-budget.js +121 -0
  564. package/dist/services/recovery-snapshot-database.js +588 -0
  565. package/dist/services/release-archive-producer.js +52 -0
  566. package/dist/services/release-archive.js +143 -0
  567. package/dist/services/release-cache.js +150 -0
  568. package/dist/services/release-compatibility.js +18 -0
  569. package/dist/services/release-download.js +67 -0
  570. package/dist/services/release-envelope.js +104 -0
  571. package/dist/services/release-manifest.js +45 -0
  572. package/dist/services/release-payload.js +114 -0
  573. package/dist/services/release-producer.js +63 -0
  574. package/dist/services/release-provenance.js +160 -0
  575. package/dist/services/release-registry.js +66 -0
  576. package/dist/services/release-source.js +194 -0
  577. package/dist/services/repair-intents.js +29 -0
  578. package/dist/services/repair-receipt.js +13 -0
  579. package/dist/services/retained-native-hook.js +274 -0
  580. package/dist/services/review-copy.js +165 -0
  581. package/dist/services/review-deferral.js +23 -0
  582. package/dist/services/review-fix-handoff.js +82 -0
  583. package/dist/services/review-fix-intake-custody.js +17 -0
  584. package/dist/services/review-fix-stage-entry.js +43 -0
  585. package/dist/services/review-fix-stage-handlers.js +176 -0
  586. package/dist/services/review-fix-stage-results.js +86 -0
  587. package/dist/services/run-control-receipt.js +56 -0
  588. package/dist/services/run-control-worker.js +469 -0
  589. package/dist/services/run-control.js +1109 -0
  590. package/dist/services/run-controller-adapter.js +434 -0
  591. package/dist/services/run-controller-capture.js +186 -0
  592. package/dist/services/run-controller-process.js +226 -0
  593. package/dist/services/run-controller-recovery.js +335 -0
  594. package/dist/services/run-controller-state.js +106 -0
  595. package/dist/services/run-controller.js +1717 -0
  596. package/dist/services/run-engine-bindings.js +1 -1
  597. package/dist/services/run-fork.js +147 -0
  598. package/dist/services/run-health.js +149 -0
  599. package/dist/services/run-observations.js +152 -0
  600. package/dist/services/run-projection.js +22 -64
  601. package/dist/services/run-recovery-guard.js +107 -0
  602. package/dist/services/run-recovery-runtime.js +112 -0
  603. package/dist/services/run-recovery.js +485 -0
  604. package/dist/services/run-start-custody.js +189 -0
  605. package/dist/services/run-timeline.js +59 -0
  606. package/dist/services/runs.js +996 -271
  607. package/dist/services/runtime-budget.js +446 -0
  608. package/dist/services/runtime-command.js +43 -0
  609. package/dist/services/runtime-scope-capture.js +52 -0
  610. package/dist/services/runtime-scope-control.js +468 -0
  611. package/dist/services/runtime-scope-resume.js +605 -0
  612. package/dist/services/runtime-scope-stop.js +211 -0
  613. package/dist/services/runtime-scope-worker.js +314 -0
  614. package/dist/services/runtime-service.js +19 -14
  615. package/dist/services/schema-validation.js +10 -8
  616. package/dist/services/scope-writer-cleanup.js +27 -0
  617. package/dist/services/sessions.js +37 -91
  618. package/dist/services/setup.js +427 -0
  619. package/dist/services/stage-blocker.js +17 -7
  620. package/dist/services/stage-context.js +240 -18
  621. package/dist/services/stage-handlers.js +141 -0
  622. package/dist/services/stage-lifecycle.js +156 -119
  623. package/dist/services/stage-pause.js +222 -94
  624. package/dist/services/stage-report-renderer.js +230 -6
  625. package/dist/services/stage-work-graph.js +52 -0
  626. package/dist/services/technical-notes.js +31 -0
  627. package/dist/services/tooling.js +111 -44
  628. package/dist/services/usage.js +49 -101
  629. package/dist/services/verification-matrix.js +393 -0
  630. package/dist/services/version-status.js +1 -1
  631. package/dist/services/vnext-code-review.js +302 -91
  632. package/dist/services/vnext-code.js +325 -251
  633. package/dist/services/vnext-contracts.js +10 -1
  634. package/dist/services/vnext-execution-profile.js +80 -5
  635. package/dist/services/vnext-fanout.js +399 -20
  636. package/dist/services/vnext-merge.js +336 -114
  637. package/dist/services/vnext-plan-review.js +288 -133
  638. package/dist/services/vnext-plan.js +257 -110
  639. package/dist/services/vnext-protocolize.js +534 -96
  640. package/dist/services/vnext-specify.js +131 -84
  641. package/dist/services/vnext-workspace-policy.js +7 -2
  642. package/dist/services/work-registry.js +1950 -215
  643. package/dist/services/work-retry-context.js +55 -0
  644. package/dist/services/workspace-bootstrap.js +53 -15
  645. package/dist/services/workspace-files.js +85 -0
  646. package/dist/services/worktrees.js +108 -48
  647. package/dist/shared/entity-references.js +1 -0
  648. package/dist/shared/errors.js +27 -0
  649. package/dist/storage/agent-output.js +26 -0
  650. package/dist/storage/data-preservation.js +65 -0
  651. package/dist/storage/database.js +931 -40
  652. package/dist/storage/foreign-key-transaction.js +39 -0
  653. package/dist/storage/immutable-input.js +45 -0
  654. package/dist/storage/import-guard.js +159 -0
  655. package/dist/storage/memory-upgrade-schema-bootstrap.js +38 -0
  656. package/dist/storage/native-cleanup.js +52 -0
  657. package/dist/storage/paired-writer-migration.js +424 -0
  658. package/dist/storage/paths.js +17 -4
  659. package/dist/storage/projection-lock.js +73 -0
  660. package/dist/storage/work-references.js +23 -0
  661. package/dist/storage/writer-contract.js +96 -0
  662. package/dist/storage/writer-migration.js +346 -0
  663. package/dist/storage/writer-restore.js +217 -0
  664. package/dist/verification-components.json +384 -0
  665. package/package.json +52 -13
  666. package/tools/create-canon-release.mjs +33 -0
  667. package/tools/probe-codex-history.mjs +37 -0
  668. package/tools/produce-hybrid-release.mjs +41 -0
  669. package/tools/repair-paused-run-status.mjs +84 -0
  670. package/tools/verification-jobs.mjs +342 -0
@@ -0,0 +1,739 @@
1
+ import { managedFlowEnv } from './managed-flow-env.mjs';
2
+ import { processSnapshot } from "./process-snapshot.mjs";
3
+ import { ToolObservations, droidToolObservations, journalToolObservations } from "./tool-observations.mjs";
4
+ export { processSnapshot } from "./process-snapshot.mjs";
5
+ import { captureDispatchGuard, cancelPendingDispatch } from "./dispatch-fence.mjs";
6
+ import { createHash, randomUUID } from "node:crypto";
7
+ import { appendFile, chmod, copyFile, mkdir, readFile, readdir, realpath, stat, unlink, writeFile } from "node:fs/promises";
8
+ import { spawn } from "node:child_process";
9
+ import os from "node:os";
10
+ import path from "node:path";
11
+ import { ObservationClock, observedTimeout, validateObservationDuration } from "./observation-clock.mjs";
12
+ import { operationProgressReporter } from "./operation-progress.mjs";
13
+ import { writeJsonAtomic } from "./runner-events.mjs";
14
+ import { errorRecord, reportedError, isObservationLoss, isNativeHookObservationLoss, isProvenShellCorrection } from "./operation-errors.mjs";
15
+ import { hookBudget, invokeNativeHook, retainNativeWorkStart, isStandaloneWorkStart } from "./native-hook-command.mjs";
16
+ import { assertDaemonDispatch } from "./daemon-operations.mjs";
17
+ import { isDeepStrictEqual } from "node:util";
18
+ import { parseLifecycleCommand } from "./lifecycle-command.mjs";
19
+ import { checkObservedProfile, observeModel } from "./model-observations.mjs";
20
+ import { readNativeLines, streamNativeDiagnostics, nativeObservationError } from "./native-streams.mjs";
21
+ import { commandText } from "./process-json.mjs";
22
+ import { DroidMetadataReader, readDroidRoutingLog } from "./droid-observation.mjs";
23
+ const metadataReaders = new Map();
24
+ function metadataReader(factory) { if (!metadataReaders.has(factory)) metadataReaders.set(factory, new DroidMetadataReader(factory)); return metadataReaders.get(factory); }
25
+ import { confirmDaemonProcess, finishDaemonProcess, scheduleDaemonProcessHeartbeat, registerDaemonProcess, stopProcessGroup } from "./managed-daemon.mjs";
26
+ import { renderAdapterPolicy, lifecycleRetryInstruction } from "./delegation-instructions.mjs";
27
+ import { classifyRecoveryError as classifyProviderError, safeProviderError } from "./provider-overload-policy.mjs";
28
+ import { settledNativeTree, nativeExecutionReceipt } from "./native-children.mjs";
29
+
30
+ export const DROID_CONTRACT = "dd-droid-harness@1";
31
+ export const DROID_PROTOCOL = "1.201.0";
32
+ const LATE_RESPONSE_GRACE_MS = 5_000;
33
+ export class DroidError extends Error {
34
+ constructor(code, message, details) { super(message); this.code = code; if (details) this.details = details; }
35
+ }
36
+ export const absolute = (value, label) => { if (!value || !path.isAbsolute(value)) throw new DroidError("invalid_path", `${label} must be absolute`); return path.resolve(value); };
37
+ export const executable = (bin, args = []) => /\.[cm]?js$/.test(bin) ? { command: process.execPath, args: [bin, ...args] } : { command: bin, args };
38
+ const shell = value => `'${String(value).replaceAll("'", `'"'"'`)}'`;
39
+ const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
40
+ const responseSummary = event => ({ request_id: event?.id ?? null, has_result: event?.result !== undefined,
41
+ result_keys: event?.result && typeof event.result === "object" && !Array.isArray(event.result) ? Object.keys(event.result).slice(0, 32) : [], error: event?.error ?? null });
42
+ export async function readJson(file) { try { return JSON.parse(await readFile(file, "utf8")); } catch (error) { if (error.code === "ENOENT") return null; throw error; } }
43
+ export function droidPaths(stateDir) {
44
+ const dir = absolute(stateDir, "state directory"), digest = createHash("sha256").update(dir).digest("hex").slice(0, 24);
45
+ const local = path.join(dir, "daemon.sock");
46
+ return { dir, socket: Buffer.byteLength(local) < 100 ? local : `/tmp/dd-droid-${digest}.sock`, state: path.join(dir, "daemon.json"), log: path.join(dir, "daemon.log"), home: path.join(dir, "home"), factory: path.join(dir, "home", ".factory"), bindings: path.join(dir, "native-operations") };
47
+ }
48
+ export function droidEnvironment(config) {
49
+ const env = { ...process.env, FACTORY_HOME_OVERRIDE: config.home, FACTORY_DROID_AUTO_UPDATE_ENABLED: "false", ...(config.ddFlowHome ? { DD_FLOW_HOME: config.ddFlowHome, DD_FLOW_BIN: config.ddFlowBin } : {}) };
50
+ // This adapter deliberately uses the operator's existing Factory login.
51
+ // Undefined is a tombstone: downstream env overlays must not resurrect keys.
52
+ for (const key of Object.keys(env)) if (key.endsWith("API_KEY")) env[key] = undefined;
53
+ return { ...env, ...managedFlowEnv(config) };
54
+ }
55
+ export async function prepareDroidHome(config) {
56
+ const paths = droidPaths(config.stateDir);
57
+ for (const dir of [paths.dir, paths.home, paths.factory, paths.bindings, path.join(paths.factory, "droids")]) { await mkdir(dir, { recursive: true, mode: 0o700 }); await chmod(dir, 0o700); }
58
+ const auth = path.join(config.authHome ?? os.homedir(), ".factory");
59
+ for (const name of ["auth.encrypted", "auth.v2.key", "auth.v2.file", "auth.json"]) {
60
+ try { await copyFile(path.join(auth, name), path.join(paths.factory, name)); await chmod(path.join(paths.factory, name), 0o600); }
61
+ catch (error) { if (error.code !== "ENOENT") throw error; }
62
+ }
63
+ await writeJsonAtomic(path.join(paths.factory, "settings.json"), { cloudSessionSync: false, enabledPlugins: {}, enableHooks: true, hooksDisabled: false, enableCustomDroids: true, ideAutoConnect: false, subagentAutonomyLevel: "inherit" });
64
+ const command = `${shell(process.execPath)} ${shell(config.entryPath)} hook handle --state-dir ${shell(paths.dir)}`;
65
+ const hooks = Object.fromEntries(["SessionStart", "PreToolUse", "PostToolUse", "Stop", "SessionEnd", "SubagentStop"].map(event => [event, [{ matcher: "*", hooks: [{ type: "command", command, timeout: 30 }] }]]));
66
+ await writeJsonAtomic(path.join(paths.factory, "hooks.json"), hooks);
67
+ const worker = `---\nname: dd-flow-worker\ndescription: Execute one explicit dd-flow Work packet or a technical capacity marker\nmodel: ${config.model}\nreasoningEffort: ${config.reasoning}\nmcpServers: []\n---\nFor a Work assignment, your first technical action is the exact standalone work start command supplied by the coordinator. If that command is absent, report an incomplete packet and stop: do not run stage pause, stage finish, or another lifecycle command. Execute the supplied operation and target exactly; lifecycle invocation IDs are internal runtime authority, so never add or invent them. Continue only after a successful start returns the Work packet. ${lifecycleRetryInstruction} Follow only the returned Work packet, invoke its exact work finish or fail command, then stop. A Task child never executes or finishes its coordinator's Stage Work. Do not create children or take another Work. For a technical capacity marker, only return the requested marker; do not read the project or call dd-flow.\n`;
68
+ await writeFile(path.join(paths.factory, "droids", "dd-flow-worker.md"), worker, { mode: 0o600 });
69
+ return { hooks_sha256: createHash("sha256").update(JSON.stringify(hooks)).digest("hex"), worker_sha256: createHash("sha256").update(worker).digest("hex") };
70
+ }
71
+ export async function removeDroidAuth(config) {
72
+ for (const name of ["auth.encrypted", "auth.v2.key", "auth.v2.file", "auth.json"]) await unlink(path.join(config.home, ".factory", name)).catch(error => { if (error.code !== "ENOENT") throw error; });
73
+ }
74
+ export async function droidVersion(bin) { const target = executable(bin, ["--version"]); return await commandText(target.command, target.args, { phase: "native-version" }); }
75
+
76
+ /** The qualified OpenAI profile reports uncached input separately from caches;
77
+ * output already includes thinking. Keep native counters alongside normalization. */
78
+ export function physicalUsage(value) {
79
+ if (!value || !["inputTokens", "outputTokens", "cacheReadTokens", "cacheCreationTokens", "thinkingTokens"].every(key => Number.isFinite(value[key]) && value[key] >= 0)) return null;
80
+ const input = value.inputTokens + value.cacheReadTokens + value.cacheCreationTokens;
81
+ return { totalTokens: input + value.outputTokens, inputTokens: input, uncachedInputTokens: value.inputTokens, cacheReadTokens: value.cacheReadTokens, cacheCreationTokens: value.cacheCreationTokens, outputTokens: value.outputTokens, reasoningTokens: value.thinkingTokens, native: value };
82
+ }
83
+ export function parseDroidTranscript(text, settings, expectedId) {
84
+ const records = []; let complete = true;
85
+ for (const line of text.split("\n")) { if (!line.trim()) continue; try { records.push(JSON.parse(line)); } catch { complete = false; } }
86
+ const header = records.find(record => record.type === "session_start");
87
+ if (!header || header.id !== expectedId) throw new DroidError("session_identity_mismatch", "Droid transcript has a different or missing native Session ID", { expected_session_id: expectedId, observed_session_id: header?.id ?? null });
88
+ const tools = new ToolObservations();
89
+ for (const record of records) for (const event of droidToolObservations(record, expectedId)) tools.add(event);
90
+ if (!complete) tools.gaps.add("transcript_incomplete");
91
+ return { header, records, outcomes: records.filter(record => record.type === "agent_turn_outcome"), usage: physicalUsage(settings?.tokenUsage), settings, complete, tool_calls: tools.summary() };
92
+ }
93
+ export async function readDroidSession(factory, id) {
94
+ const file = await metadataReader(factory).locate(id);
95
+ if (!file) return null;
96
+ const text = await readFile(file, "utf8"); let settings = null, complete = true;
97
+ try { settings = await readJson(file.replace(/\.jsonl$/, ".settings.json")); } catch (error) { if (!(error instanceof SyntaxError)) throw error; complete = false; }
98
+ const parsed = parseDroidTranscript(text, settings, id);
99
+ return { ...parsed, complete: parsed.complete && complete, transcript_path: file, source_sha256: createHash("sha256").update(text).digest("hex") };
100
+ }
101
+ const terminal = value => ["completed", "cancelled", "failed", "process_exit", "error"].includes(value);
102
+ const status = value => value === "completed" ? "completed" : ["cancelled", "process_exit"].includes(value) ? "cancelled" : ["failed", "error"].includes(value) ? "failed" : "unknown";
103
+
104
+ export function droidTerminalFailure(outcome, sessionId, settled) {
105
+ if (!["failed", "error"].includes(outcome?.reason) || !outcome.error || typeof outcome.error !== "object") return null;
106
+ const code = classifyProviderError(outcome.error);
107
+ if (code === "provider_failed") return null;
108
+ const turnId = outcome.turnId;
109
+ return Object.assign(new DroidError(code, "Droid native provider request failed", {
110
+ provider_session_id: sessionId, session_id: sessionId, turn_id: turnId, native_turn_id: turnId,
111
+ terminal_status: "failed", native_source: "droid.agent_turn_outcome", observed_at: new Date().toISOString(),
112
+ provider_error: safeProviderError(outcome.error), will_retry: outcome.error.willRetry === true, native_retry_active: outcome.error.willRetry === true,
113
+ native_turn_items: { observed: settled === true, possible_effects: true, pending: settled !== true }
114
+ }), { retryable: ["provider_capacity_transient", "provider_rate_limited", "provider_network_transient"].includes(code) });
115
+ }
116
+
117
+
118
+ function descendants(rows, rootPid) { const ids = new Set([rootPid]); for (;;) { const size = ids.size; for (const row of rows) if (ids.has(row.ppid)) ids.add(row.pid); if (size === ids.size) return rows.filter(row => ids.has(row.pid)); } }
119
+
120
+ export class DroidRuntime {
121
+ constructor(config, state, persist) {
122
+ this.config = config; this.state = state; this.paths = droidPaths(config.stateDir); this.persist = persist; this.pending = new Map(); this.lateResponses = new Map(); this.active = null; this.child = null; this.closed = true; this.loaded = false; this.ownedProcesses = state.owned_processes ?? []; this.closing = false; this.workingState = "idle"; this.protocol = null; this.rootId = state.root_session_id ?? null; this.topology = new Map(state.descendants?.map(child => [child.provider_session_id, child]) ?? []); this.nativeCreatorTurns = new Map(); this.draining = Promise.resolve(); this.journalling = Promise.resolve(); this.activity = 0; this.providerProcess = null; this.cancelled = false; this.lastResult = state.last_result ?? null; this.metadata = metadataReader(this.paths.factory); this.fatalError = state.fatal_error ? reportedError(state.fatal_error) : null;
123
+ }
124
+ async journal(kind, payload) { const line = JSON.stringify({ schema_id: "dd-droid/event@1", observed_at: new Date().toISOString(), kind, ...payload }); this.journalling = this.journalling.then(() => appendFile(this.config.journal, `${line}\n`, { mode: 0o600 })); return await this.journalling; }
125
+ async save(patch = {}) { return await this.persist({ root_session_id: this.rootId, sessions: this.rootId ? [this.rootId] : [], descendants: [...this.topology.values()], working_state: this.workingState, last_activity: this.activity, ...patch }); }
126
+ async start() {
127
+ if (this.child && !this.closed) return;
128
+ this.closed = false; this.loaded = false; this.closing = false; this.protocol = null; this.buffer = "";
129
+ const args = ["exec", "--input-format", "stream-jsonrpc", "--output-format", "stream-jsonrpc"];
130
+ const target = executable(this.config.bin, args);
131
+ this.providerProcess = await registerDaemonProcess(this.config, { kind: "droid-provider", owner: `droid:${this.config.daemonId}`, operation: `droid-provider:${randomUUID()}`, stdout: this.paths.log, stderr: this.paths.log });
132
+ this.child = spawn(target.command, target.args, { cwd: this.config.cwd, env: droidEnvironment(this.config), detached: true, stdio: ["pipe", "pipe", "pipe"] });
133
+ const observationFailure = async error => {
134
+ if (error.code === "operation_observation_lost") { this.observationError = error; this.rejectPending(error); return; }
135
+ await this.handleObservationError(error); if (!this.fatalError) this.rejectPending(error);
136
+ };
137
+ this.nativeDrain = readNativeLines(this.child.stdout, line => this.draining = this.event(JSON.parse(line)), observationFailure);
138
+ this.diagnostics = streamNativeDiagnostics(this.child.stderr, chunk => this.journal("stderr", { text: chunk }), observationFailure);
139
+ this.child.on("error", error => this.rejectPending(error));
140
+ this.child.on("close", (code, signal) => { this.closed = true; void Promise.all([this.nativeDrain, this.diagnostics]).finally(() => { const error = new DroidError("operation_observation_lost", "Droid process exited before a confirmed reply", { code, signal }); this.rejectPending(error); }); });
141
+ await this.captureProcesses(); await confirmDaemonProcess(this.config, this.providerProcess, this.child); scheduleDaemonProcessHeartbeat(this.config, this.providerProcess); await this.save({ provider_pid: this.child.pid, provider_process: this.providerProcess });
142
+ clearInterval(this.processObserver);
143
+ this.processObserver = setInterval(() => { if (!this.processObservation) this.processObservation = this.captureProcesses().catch(error => { this.ownershipError = error; }).finally(() => { this.processObservation = null; }); }, 1_000);
144
+ this.processObserver.unref();
145
+ const models = await this.request("droid.list_models", {});
146
+ if (this.protocol !== DROID_PROTOCOL) throw new DroidError("droid_protocol_mismatch", "Unqualified Factory protocol", { expected: DROID_PROTOCOL, observed: this.protocol });
147
+ this.models = models.models ?? [];
148
+ clearInterval(this.modelObserver);
149
+ this.modelObserver = setInterval(() => {
150
+ if (!this.loaded || this.closing || this.modelObservation) return;
151
+ this.modelObservation = this.observeModels().catch(error => this.handleObservationError(error)).finally(() => { this.modelObservation = null; });
152
+ }, 2_000);
153
+ this.modelObserver.unref();
154
+ }
155
+ rejectPending(error) { for (const pending of this.pending.values()) { clearTimeout(pending.timer); pending.reject(error); } this.pending.clear(); if (this.active && !this.closing) this.active.reject(error); }
156
+ async event(event) {
157
+ await this.journal("native", { event });
158
+ if (event.type === "response") {
159
+ this.protocol ??= event.factoryProtocolVersion;
160
+ const pending = this.pending.get(event.id); if (!pending) { this.lateResponses.set(event.id, event); while (this.lateResponses.size > 128) this.lateResponses.delete(this.lateResponses.keys().next().value); await this.journal("late_response", responseSummary(event)); return; }
161
+ this.pending.delete(event.id); clearTimeout(pending.timer);
162
+ if (pending.timedOut) await this.journal("late_response", { ...responseSummary(event), method: pending.method, grace_ms: LATE_RESPONSE_GRACE_MS });
163
+ event.error ? pending.reject(new DroidError("droid_rpc_error", event.error.message ?? "Droid rejected request", event.error)) : pending.resolve(event.result ?? {}); return;
164
+ }
165
+ if (event.method === "droid.request_permission" || event.method === "droid.ask_user") {
166
+ const result = event.method === "droid.request_permission" ? { selectedOption: "cancel" } : { cancelled: true, answers: [] };
167
+ this.send({ jsonrpc: "2.0", type: "response", factoryApiVersion: "1.0.0", factoryProtocolVersion: DROID_PROTOCOL, id: event.id, result });
168
+ await this.journal("unexpected_input_rejected", { method: event.method }); return;
169
+ }
170
+ if (event.method !== "droid.session_notification") return;
171
+ const { sessionId, notification: n } = event.params ?? {}; if (!n) return;
172
+ const fingerprint = createHash("sha256").update(JSON.stringify(event.params)).digest("hex");
173
+ if (sessionId && this.rootId && sessionId !== this.rootId && !this.topology.has(sessionId)) throw new DroidError("session_identity_mismatch", "Unrelated native Session notification");
174
+ const current = this.active;
175
+ let currentGuarded = Boolean(current && typeof current.guard === "function");
176
+ try { if (currentGuarded) current.guard(); } catch { currentGuarded = false; }
177
+ const scoped = currentGuarded && sessionId && (sessionId === this.rootId || this.topology.get(sessionId)?.operation_id === current.operationId)
178
+ && (!n.turnId || n.turnId === current.turnId) && (!n.requestId || n.requestId === current.requestId);
179
+ let productive = ["assistant_text_delta", "assistant_thought_delta"].includes(n.type) && Boolean(n.textDelta ?? n.thoughtDelta)
180
+ || ["agent_turn_completed", "create_message"].includes(n.type);
181
+ if (scoped && ["agent_turn_completed", "create_message"].includes(n.type)) {
182
+ current.lifecycleProgress ??= new Set();
183
+ const key = JSON.stringify([sessionId, n.type, n.requestId ?? null, n.turnId ?? null, n.message?.id ?? null]);
184
+ if (current.lifecycleProgress.has(key)) productive = false;
185
+ else {
186
+ if (current.lifecycleProgress.size >= 1_024) current.lifecycleProgress.delete(current.lifecycleProgress.values().next().value);
187
+ current.lifecycleProgress.add(key);
188
+ }
189
+ }
190
+ if (scoped && ["tool_call_started", "tool_call_update", "tool_call_completed"].includes(n.type)) {
191
+ const toolId = n.toolUseId ?? n.toolCallId ?? n.toolCall?.id;
192
+ current.toolProgress ??= new Set();
193
+ const key = `${sessionId}:${toolId}`, semantic = JSON.stringify({ type: n.type, toolCall: n.toolCall, status: n.status, input: n.input, output: n.output, content: n.content, error: n.error });
194
+ // Keep fixed-size fingerprints, not 1,024 copies of growing tool output.
195
+ const snapshot = createHash("sha256").update(JSON.stringify([key, semantic])).digest("hex");
196
+ if (toolId && !current.toolProgress.has(snapshot)) {
197
+ productive = n.type !== "tool_call_update" || n.status !== undefined || n.output !== undefined || n.content !== undefined || n.toolCall !== undefined || n.error !== undefined;
198
+ if (productive) {
199
+ // ponytail: retain the latest 1,024 snapshots for replay detection,
200
+ // not a lifetime progress quota. Native sequence IDs can extend proof.
201
+ if (current.toolProgress.size >= 1_024) current.toolProgress.delete(current.toolProgress.values().next().value);
202
+ current.toolProgress.add(snapshot);
203
+ }
204
+ }
205
+ }
206
+ if (scoped && productive) {
207
+ this.activity += 1; this.progress = { cursor: this.activity, observed_at: Date.now() };
208
+ const pending = this.pending.get(current.requestId);
209
+ if (pending?.method === "droid.add_user_message") {
210
+ pending.progress = this.progress;
211
+ }
212
+ await current.publish?.("native_progress", { native_request_id: String(current.requestId), session_id: this.rootId, observed_at: new Date(this.progress.observed_at).toISOString() });
213
+ }
214
+ if (n.type === "droid_working_state_changed" && (!sessionId || sessionId === this.rootId)) this.workingState = n.newState;
215
+ if (n.type === "child_session_available") {
216
+ if (!this.rootId || sessionId !== this.rootId || !n.childSessionId || !n.toolUseId) throw new DroidError("droid_child_identity_invalid", "Native child event lacks physical parent or tool identity");
217
+ // A notification may arrive after another prompt starts. Session and
218
+ // child identity alone do not identify the originating operation.
219
+ const byTurn = typeof n.turnId === "string" && n.turnId ? this.nativeCreatorTurns.get(`turn:${n.turnId}`) : null;
220
+ const byRequest = typeof n.requestId === "string" && n.requestId ? this.nativeCreatorTurns.get(`request:${n.requestId}`) : null;
221
+ const candidate = byTurn ?? byRequest ?? current;
222
+ const exact = candidate && (typeof n.turnId === "string" && n.turnId && n.turnId === candidate.turnId
223
+ || typeof n.requestId === "string" && n.requestId && n.requestId === candidate.requestId)
224
+ && (!n.turnId || n.turnId === candidate.turnId) && (!n.requestId || n.requestId === candidate.requestId)
225
+ && (!byTurn || !byRequest || byTurn === byRequest);
226
+ const previous = this.topology.get(n.childSessionId), sameTool = previous?.tool_use_id === n.toolUseId;
227
+ if (previous && ((previous.parent_provider_session_id && previous.parent_provider_session_id !== sessionId)
228
+ || previous.tool_use_id && !sameTool))
229
+ throw new DroidError("droid_child_identity_invalid", "Native child parent or creation tool contradicts the retained identity");
230
+ const retainedCreator = sameTool ? previous.creator_operation_id ?? previous.operation_id : null;
231
+ if (retainedCreator && exact && retainedCreator !== candidate.operationId)
232
+ throw new DroidError("droid_child_identity_invalid", "Native child creator contradicts the retained operation");
233
+ let proven = Boolean(exact && typeof candidate.guard === "function");
234
+ try { if (proven) candidate.guard(); } catch { proven = false; }
235
+ const creator = retainedCreator || (proven ? candidate.operationId : null);
236
+ this.topology.set(n.childSessionId, { ...(previous ?? {}), provider_session_id: n.childSessionId, parent_provider_session_id: sessionId, tool_use_id: n.toolUseId,
237
+ creator_operation_id: creator, operation_id: previous?.latest_tool_use_id && previous.latest_tool_use_id !== n.toolUseId ? null : creator,
238
+ status: "running", source: "droid.child_session_available" }); await this.save();
239
+ }
240
+ if (n.type === "settings_updated" && this.loaded) await this.observeSettings(sessionId ?? this.rootId, n.settings, { kind: "native.settings_updated", event_sha256: fingerprint });
241
+ if (n.type === "create_message" && n.requestId === current?.requestId) {
242
+ if (n.message?.id !== current.turnId) throw new DroidError("droid_turn_identity_mismatch", "Droid did not preserve the requested message ID");
243
+ current.accepted = true; await this.saveBinding(current, { accepted: true });
244
+ }
245
+ if (n.type === "assistant_text_delta" && scoped && sessionId === this.rootId) current.text += n.textDelta ?? "";
246
+ if (n.type === "agent_turn_completed" && current && n.turnId === current.turnId && (!sessionId || sessionId === this.rootId)) {
247
+ current.outcome = n; this.lastResult = n; await this.saveBinding(current, { native_outcome: n, assistant_text: current.text }); current.resolve(n);
248
+ if (current.observerReturned && this.active === current) { this.active = null; await this.save({ active_tree: this.workingState !== "idle" }); }
249
+ }
250
+ }
251
+ request(method, params, id = randomUUID(), timeoutMs = 30_000) {
252
+ if (!this.child || this.closed) return Promise.reject(new DroidError("operation_observation_lost", "Droid runtime is not connected"));
253
+ return new Promise((resolve, reject) => {
254
+ const timer = observedTimeout(() => {
255
+ const pending = this.pending.get(id); if (!pending || pending.timedOut) return;
256
+ pending.timedOut = true;
257
+ const timeoutError = new DroidError("rpc_timeout", `${method} has no observed reply`, { request_id: id, timeout_ms: timeoutMs, grace_ms: LATE_RESPONSE_GRACE_MS });
258
+ void this.journal("request_deadline", { method, request_id: id, timeout_ms: timeoutMs, grace_ms: LATE_RESPONSE_GRACE_MS });
259
+ pending.timer = observedTimeout(() => { if (this.pending.get(id) !== pending) return; this.pending.delete(id); reject(timeoutError); }, LATE_RESPONSE_GRACE_MS, { progress: () => pending.progress });
260
+ }, timeoutMs, { progress: () => this.pending.get(id)?.progress });
261
+ this.pending.set(id, { resolve, reject, timer, method });
262
+ try { this.send({ jsonrpc: "2.0", type: "request", factoryApiVersion: "1.0.0", factoryProtocolVersion: DROID_PROTOCOL, id, method, params }, error => { if (error) { clearTimeout(timer); this.pending.delete(id); reject(nativeObservationError(error)); } }); }
263
+ catch (error) { clearTimeout(timer); this.pending.delete(id); reject(nativeObservationError(error)); }
264
+ });
265
+ }
266
+ send(value, callback) {
267
+ const stdin = this.child?.stdin;
268
+ if (!stdin || stdin.destroyed) {
269
+ const error = new DroidError("bridge_pipe_broken", "Droid runtime stdin is closed");
270
+ this.rejectPending(error);
271
+ throw error;
272
+ }
273
+ try { stdin.write(`${JSON.stringify(value)}\n`, callback); }
274
+ catch (cause) { const error = nativeObservationError(cause); this.rejectPending(error); throw error; }
275
+ }
276
+ profile(settings) {
277
+ const model = settings?.modelId ?? settings?.model ?? null;
278
+ const nativeModel = this.models?.find(item => (item.id ?? item.modelId) === model);
279
+ const result = { provider: settings?.provider ?? nativeModel?.provider ?? null, model, reasoning: settings?.reasoningEffort ?? null, mode: settings?.autonomyMode ?? (settings?.autonomyLevel ? `auto-${settings.autonomyLevel}` : null) };
280
+ checkObservedProfile(this.config, result, { required: this.config.mode ? ["mode"] : [] });
281
+ return result;
282
+ }
283
+ async observeSettings(id, settings, source, extra = {}) {
284
+ const observed = this.profile(settings);
285
+ const event = await observeModel({ journal: this.config.journal, harness: "droid-cli", sessionId: id, parentSessionId: id === this.rootId ? null : this.topology.get(id)?.parent_provider_session_id ?? this.rootId,
286
+ requested: this.config, observed, source, ...extra });
287
+ if (id === this.rootId) this.observedProfile = observed;
288
+ this.modelEventIds ??= new Map();
289
+ if (event && this.modelEventIds.get(id) !== event.id) {
290
+ this.modelEventIds.set(id, event.id);
291
+ await this.save({ model_observation: { last_event_id: event.id, observed_at: event.observed_at } });
292
+ }
293
+ return observed;
294
+ }
295
+ validateMetadata(id, native) {
296
+ const parent = native?.header.callingSessionId;
297
+ if (!native || path.resolve(native.header.cwd) !== this.config.cwd || native.header.id !== id || (id !== this.rootId && (id === parent || parent !== this.rootId && !this.topology.has(parent) || this.topology.has(id) && this.topology.get(id).parent_provider_session_id !== parent))) throw new DroidError("session_identity_mismatch", "Native model observation is outside the owned Session tree");
298
+ if (id !== this.rootId && !this.topology.has(id)) this.topology.set(id, { provider_session_id: id, parent_provider_session_id: parent, tool_use_id: native.header.callingToolUseId, status: "unknown", source: "native.metadata" });
299
+ }
300
+ async observeModels() {
301
+ if (!this.rootId || !this.loaded) return;
302
+ const catalog = path.join(this.paths.factory, "task-invocations.json");
303
+ const info = await stat(catalog).catch(error => { if (error.code === "ENOENT") return null; throw error; });
304
+ if (info && info.mtimeMs !== this.modelCatalogMtime) {
305
+ const value = await readJson(catalog);
306
+ const tasks = Array.isArray(value?.invocations) ? value.invocations : [];
307
+ for (let size = -1; size !== this.topology.size;) { size = this.topology.size;
308
+ for (const task of tasks) if (task?.childSessionId && (task.parentSessionId === this.rootId || this.topology.has(task.parentSessionId))) {
309
+ const native = await this.metadata.read(task.childSessionId);
310
+ if (native) this.validateMetadata(task.childSessionId, native);
311
+ }
312
+ }
313
+ this.modelCatalogMtime = info.mtimeMs;
314
+ }
315
+ const log = path.join(this.paths.factory, "logs", "droid-log-single.log");
316
+ const routing = await readDroidRoutingLog(log, this.state.model_log_cursor);
317
+ for (const transition of routing.events) {
318
+ const native = await this.metadata.read(transition.session_id);
319
+ if (!native || (transition.session_id !== this.rootId && native.header.callingSessionId !== this.rootId && !this.topology.has(native.header.callingSessionId))) continue;
320
+ this.validateMetadata(transition.session_id, native);
321
+ await this.observeSettings(transition.session_id, { ...native.settings, modelId: transition.model, model: transition.model },
322
+ { path: log, channel: "native_router", event_sha256: transition.event_sha256 }, { reason: transition.reason, nativeTimestamp: transition.native_timestamp, previousModel: transition.previous_model });
323
+ }
324
+ await this.save({ model_log_cursor: routing.cursor });
325
+ for (const id of [this.rootId, ...this.topology.keys()]) {
326
+ const native = await this.metadata.read(id);
327
+ if (!native) {
328
+ await observeModel({ journal: this.config.journal, harness: "droid-cli", sessionId: id, requested: this.config, source: "native.settings", evidence: "unavailable", reason: "settings_missing_or_partial" });
329
+ continue;
330
+ }
331
+ this.validateMetadata(id, native);
332
+ await this.observeSettings(id, native.settings, native.source);
333
+ }
334
+ }
335
+ async handleObservationError(error) {
336
+ if (error.code === "model_observation_storage_failed") {
337
+ this.observationError = Object.assign(new Error("Droid model observation could not be published", { cause: error }), { code: "operation_observation_lost", details: { native_outcome: "unknown", observation_error: errorRecord(error) } });
338
+ this.rejectPending(this.observationError);
339
+ return;
340
+ }
341
+ if (["profile_integrity_violation", "session_identity_mismatch", "droid_child_identity_invalid", "droid_turn_identity_mismatch", "droid_hook_identity_invalid"].includes(error.code)) {
342
+ if (!this.fatalError) {
343
+ this.fatalError = error;
344
+ try { await this.save({ fatal_error: errorRecord(error) }); await this.journal("integrity_failure", { error: errorRecord(error) }); }
345
+ finally {
346
+ this.rejectPending(error);
347
+ // Never await cleanup from the native event drain: closeTree waits for it.
348
+ setTimeout(() => { void this.closeTree(true).catch(cleanup => this.save({ cleanup_error: errorRecord(cleanup) }).catch(() => {})); }, 0);
349
+ }
350
+ }
351
+ return;
352
+ }
353
+ try { await observeModel({ journal: this.config.journal, harness: "droid-cli", sessionId: this.rootId, requested: this.config, source: "native.observer", evidence: "unavailable", reason: error.code ?? "observation_unavailable" }); }
354
+ catch (failure) { await this.handleObservationError(Object.assign(new Error(`Cannot persist model observation: ${failure.message}`), { code: "model_observation_storage_failed" })); }
355
+ }
356
+ async saveBinding(binding, patch = {}) { Object.assign(binding.saved, patch); await writeJsonAtomic(path.join(this.paths.bindings, `${createHash("sha256").update(binding.operationId).digest("hex")}.json`), binding.saved); }
357
+ async create(operationId) {
358
+ if (this.rootId) throw new DroidError("session_already_created", "This execution daemon already owns a root Session");
359
+ await this.start(); const sessionId = randomUUID(); this.rootId = sessionId; const requestId = randomUUID();
360
+ const binding = { operationId, saved: { operation: "session.create", provider_session_id: sessionId, request_id: requestId } }; await this.saveBinding(binding);
361
+ const result = await this.request("droid.initialize_session", { machineId: "dd-eval", cwd: this.config.cwd, sessionId, modelId: this.config.model, reasoningEffort: this.config.reasoning, interactionMode: "auto", autonomyLevel: "high", autoRejectPermissionRequests: true, disableBuiltinSkills: true, disabledToolIds: ["AskUser"], mcpServers: [], systemPrompt: { type: "preset", preset: "droid", append: `${renderAdapterPolicy({ harness: "droid-cli" })} Use the same worker for technical capacity markers and separately declared productive Work. Never delegate an already-bound coordinator or Stage Work through Task: execute it in this root Session. A productive Task must receive the runner's exact work start command and may execute only that child Work; otherwise it must stop. User clarification belongs to the root coordinator through dd-flow stage pause, not native AskUser. At a work_fanout boundary, stop when the launcher says runner dispatches and wait for the runner's next instruction. Never use Missions.` } }, requestId);
362
+ if (result.sessionId !== sessionId) throw new DroidError("session_identity_mismatch", "Droid initialized a different Session");
363
+ this.rootId = result.sessionId; this.loaded = true; this.observedProfile = await this.observeSettings(this.rootId, result.settings, "native.initialize_session"); this.workingState = "idle"; await this.save();
364
+ return await this.inspect(sessionId);
365
+ }
366
+ requireIdentity(id) { if (!id || id !== this.rootId) throw new DroidError("session_identity_mismatch", "Requested root Session is not owned by this daemon", { requested: id ?? null, observed: this.rootId }); }
367
+ async resume(id) {
368
+ this.requireIdentity(id); if (this.state.cleanup?.forced) throw new DroidError("invalid_harness_crash", "Forced teardown is unclean; same-ID resume is not qualified for this execution"); if (this.closed && (await this.captureProcesses()).length) throw new DroidError("tree_not_settled", "Previous provider still owns processes; cancel the tree before resume"); if (!this.closed && this.loaded) return await this.inspect(id);
369
+ await this.start(); const result = await this.request("droid.load_session", { sessionId: id, loadAllMessages: false, autoRejectPermissionRequests: true, disableBuiltinSkills: true, disabledToolIds: ["AskUser"], mcpServers: [] });
370
+ const source = await readDroidSession(this.paths.factory, id); if (!source || path.resolve(source.header.cwd) !== this.config.cwd) throw new DroidError("session_identity_mismatch", "Resumed Session does not match its controlled workspace");
371
+ this.loaded = true; this.observedProfile = await this.observeSettings(id, result.settings, "native.load_session"); this.workingState = result.workingState ?? (result.isAgentLoopInProgress ? "unknown" : "idle"); this.cancelled = false; await this.save(); return await this.inspect(id);
372
+ }
373
+ async prompt(id, prompt, operationId, livenessTimeoutMs = this.config.inactivityMs ?? 600_000) {
374
+ validateObservationDuration(livenessTimeoutMs);
375
+ const assertDispatch = captureDispatchGuard(this);
376
+ if (this.fatalError) throw this.fatalError;
377
+ this.requireIdentity(id); if (!prompt) throw new DroidError("prompt_required", "prompt is required"); if (this.active) throw new DroidError("operation_busy", "Another Droid operation is active");
378
+ if (this.closed || !this.loaded) await this.resume(id);
379
+ if (this.ownershipError) throw new DroidError("process_ownership_unknown", "Owned process observation failed", { error: errorRecord(this.ownershipError) });
380
+ const before = await this.inspect(id); if (!before.settled) throw new DroidError("tree_not_settled", "Prior Droid tree has not settled");
381
+ let resolve, reject; const completed = new Promise((a, b) => { resolve = a; reject = b; }); void completed.catch(() => {});
382
+ const requestId = randomUUID(), turnId = randomUUID();
383
+ const current = { operationId, requestId, turnId, sessionId: id, generation: (this.state.hook_generation ?? 0) + 1, resolve, reject, guard: assertDispatch, text: "", publish: operationProgressReporter(), saved: { operation: "session.prompt", provider_session_id: id, request_id: requestId, turn_id: turnId, accepted: false } };
384
+ this.state.hook_generation = current.generation; this.lastNativeHookOperation = current;
385
+ this.active = current; this.cancelled = false; await this.saveBinding(current); await this.save({ active_tree: true });
386
+ const creator = { operationId, turnId, requestId, guard: assertDispatch };
387
+ this.nativeCreatorTurns.set(`turn:${turnId}`, creator); this.nativeCreatorTurns.set(`request:${requestId}`, creator);
388
+ const clock = new ObservationClock({ timeoutMs: livenessTimeoutMs, onGap: gap => { void this.journal("observation_gap", { gap }).catch(() => {}); } });
389
+ let checkingTerminal = false;
390
+ const timer = setInterval(() => {
391
+ if (checkingTerminal || !clock.sample(this.progress)) return;
392
+ checkingTerminal = true;
393
+ const cursor = this.progress?.cursor;
394
+ void this.recoverOperation(operationId).then(() => {
395
+ if (this.active === current && !current.outcome && this.progress?.cursor === cursor && clock.sample(this.progress)) reject(new DroidError("operation_observation_lost", "Droid native outcome remains unknown after its silence window", { operation_id: operationId, native_outcome: "unknown" }));
396
+ }).catch(error => reject(new DroidError("operation_observation_lost", "Cannot reconcile native outcome before declaring inactivity", { error: errorRecord(error) }))).finally(() => { checkingTerminal = false; });
397
+ }, Math.max(10, Math.min(1_000, livenessTimeoutMs / 4)));
398
+ let primary;
399
+ try {
400
+ assertDispatch();
401
+ current.dispatched = true;
402
+ await this.request("droid.add_user_message", { text: prompt, messageId: turnId }, requestId);
403
+ try { await this.journal("native_dispatch", { operation_id: operationId, provider_session_id: id, native_request_id: requestId }); }
404
+ catch (cause) {
405
+ const error = nativeObservationError(cause);
406
+ if (!current.outcome || error.code !== "operation_observation_lost") throw error;
407
+ throw Object.assign(new Error("Droid completed natively but its dispatch journal could not be saved", { cause }), {
408
+ code: "native_outcome_observation_failed", details: { native_outcome: { provider_session_id: id, turn_id: turnId, result: current.outcome }, settled: false, observation_error: errorRecord(error) }
409
+ });
410
+ }
411
+ const outcome = await completed; await this.draining;
412
+ let receipt;
413
+ try {
414
+ receipt = await this.inspect(id, { ignoreActive: true });
415
+ } catch (error) {
416
+ // A terminal native outcome is durable independently of a later
417
+ // filesystem/topology observation. Preserve it and let settlement
418
+ // reconcile; do not convert it into a failed prompt.
419
+ return { provider_session_id: id, adapter_session_id: id, result: { ...outcome, status: status(outcome.reason) }, assistant_text: current.text, turn_id: turnId, settled: false, settlement_observation_error: errorRecord(error), ...(current.hookDiagnostics?.length ? { native_hook_diagnostics: current.hookDiagnostics } : {}) };
420
+ }
421
+ const providerFailure = droidTerminalFailure(outcome, id, receipt.settled);
422
+ if (providerFailure) throw providerFailure;
423
+ return { ...receipt, result: { ...outcome, status: status(outcome.reason) }, assistant_text: current.text, turn_id: turnId, ...(current.hookDiagnostics?.length ? { native_hook_diagnostics: current.hookDiagnostics } : {}) };
424
+ } catch (error) {
425
+ if (current.outcome && isObservationLoss(error)) error = Object.assign(new Error("Droid completed natively but its dispatch acknowledgement was lost", { cause: error }), {
426
+ code: "native_outcome_observation_failed", details: { native_outcome: { provider_session_id: id, turn_id: turnId, result: current.outcome }, settled: false, observation_error: errorRecord(error) }
427
+ });
428
+ primary = error;
429
+ try { await this.journal("prompt_error", { operation_id: operationId, error: errorRecord(error) }); }
430
+ catch (publication) { error.cleanup_error ??= errorRecord(publication); }
431
+ throw error;
432
+ } finally {
433
+ clearInterval(timer); current.observerReturned = true;
434
+ if (this.active === current && (current.outcome || !current.dispatched)) this.active = null;
435
+ try { await this.save({ active_tree: Boolean(this.active) || this.workingState !== "idle" }); }
436
+ catch (publication) {
437
+ if (primary) primary.cleanup_error ??= errorRecord(publication);
438
+ else throw Object.assign(new Error("Droid outcome bookkeeping could not be published", { cause: publication }), { code: current.outcome ? "native_outcome_observation_failed" : "operation_observation_lost", details: { native_outcome: current.outcome ?? "unknown", observation_error: errorRecord(publication) } });
439
+ }
440
+ }
441
+ }
442
+ async recoverOperation(operationId) {
443
+ const binding = await readJson(path.join(this.paths.bindings, `${createHash("sha256").update(operationId).digest("hex")}.json`));
444
+ if (!binding || binding.operation !== "session.prompt") return null;
445
+ this.requireIdentity(binding.provider_session_id);
446
+ const native = await readDroidSession(this.paths.factory, binding.provider_session_id);
447
+ const outcome = native?.outcomes.find(item => item.turnId === binding.turn_id);
448
+ if (!outcome || !terminal(outcome.reason)) return null;
449
+ let receipt;
450
+ try { receipt = await this.inspect(binding.provider_session_id, { ignoreActive: true }); }
451
+ catch (error) {
452
+ receipt = { provider_session_id: binding.provider_session_id, adapter_session_id: binding.provider_session_id, settled: false, settlement_observation_error: errorRecord(error) };
453
+ }
454
+ const failure = droidTerminalFailure(outcome, binding.provider_session_id, receipt.settled);
455
+ if (failure) throw failure;
456
+ // Native outcome and tree settlement are independent. Retain the former
457
+ // even while children are running; settled:false keeps capacity reserved.
458
+ const current = this.active;
459
+ if (current?.operationId === operationId && current.turnId === binding.turn_id) {
460
+ current.outcome = outcome; this.lastResult = outcome;
461
+ await this.saveBinding(current, { native_outcome: outcome });
462
+ current.resolve(outcome);
463
+ if (current.observerReturned) {
464
+ if (this.active === current) this.active = null;
465
+ await this.save({ active_tree: this.workingState !== "idle" });
466
+ return { ...receipt, result: { ...outcome, status: status(outcome.reason) }, assistant_text: binding.assistant_text ?? "", turn_id: binding.turn_id, recovered_from: "native.agent_turn_outcome" };
467
+ }
468
+ // Let the original dispatch persist its one terminal reply. Settlement
469
+ // is reconciled separately; do not race a second writer or replay it.
470
+ return null;
471
+ }
472
+ return { ...receipt, result: { ...outcome, status: status(outcome.reason) }, assistant_text: binding.assistant_text ?? "", turn_id: binding.turn_id, recovered_from: "native.agent_turn_outcome" };
473
+ }
474
+ async refreshTopology() {
475
+ let catalog, missing = false;
476
+ try { catalog = JSON.parse(await readFile(path.join(this.paths.factory, "task-invocations.json"), "utf8")); }
477
+ catch (error) { if (error.code === "ENOENT") missing = true; else { if (!(error instanceof SyntaxError)) throw error; this.topologyIncomplete = true; return [...this.topology.values()]; } }
478
+ this.topologyIncomplete = false;
479
+ const root = this.rootId ? await readDroidSession(this.paths.factory, this.rootId) : null;
480
+ const taskTools = native => new Map((native?.records ?? []).flatMap(record => record.message?.content ?? []).filter(block => block.type === "tool_use" && block.name === "Task").map(block => [block.id, block]));
481
+ const validCatalog = catalog && !Array.isArray(catalog) && Array.isArray(catalog.invocations);
482
+ if (!validCatalog) {
483
+ const noChildren = !this.topology.size && !taskTools(root).size;
484
+ const noDispatch = !this.active && !this.lastResult && !this.state.active_tree && (!root || root.complete && root.records.every(record => record.type === "session_start"));
485
+ const completeNoTask = root?.complete && terminal(root.outcomes.at(-1)?.reason) && root.tool_calls.completeness === "complete";
486
+ this.topologyIncomplete = !(missing && noChildren && (noDispatch || completeNoTask));
487
+ return [...this.topology.values()];
488
+ }
489
+ const invocations = catalog.invocations, owned = new Set([this.rootId, ...this.topology.keys()]);
490
+ // Resolve ancestry before validating records so malformed foreign entries
491
+ // do not block this tree, while malformed reachable children do.
492
+ for (let size = -1; size !== owned.size;) { size = owned.size; for (const task of invocations) if (owned.has(task?.parentSessionId) && task?.childSessionId) owned.add(task.childSessionId); }
493
+ const parents = new Map([[this.rootId, root]]);
494
+ for (const id of owned) if (id !== this.rootId) parents.set(id, await readDroidSession(this.paths.factory, id));
495
+ for (const [parentId, native] of parents) {
496
+ if (!native?.complete) this.topologyIncomplete = true;
497
+ for (const toolId of taskTools(native).keys()) if (!invocations.some(task => task?.parentSessionId === parentId && task?.parentToolUseId === toolId)) this.topologyIncomplete = true;
498
+ }
499
+ const groups = new Map();
500
+ for (const task of invocations) {
501
+ if (!task || typeof task !== "object") { this.topologyIncomplete = true; continue; }
502
+ if (!owned.has(task.parentSessionId) && !owned.has(task.childSessionId) && ![...parents.values()].some(native => taskTools(native).has(task.parentToolUseId))) continue;
503
+ if (![task.parentSessionId, task.childSessionId, task.parentToolUseId, task.taskInvocationId, task.status].every(value => typeof value === "string" && value.length)) { this.topologyIncomplete = true; continue; }
504
+ const tasks = groups.get(task.childSessionId) ?? []; tasks.push(task); groups.set(task.childSessionId, tasks);
505
+ }
506
+ for (const id of this.topology.keys()) if (!groups.has(id)) this.topologyIncomplete = true;
507
+ for (const [id, tasks] of groups) {
508
+ const native = parents.get(id), parentId = tasks[0].parentSessionId, rootTools = taskTools(parents.get(parentId));
509
+ if (!native || !native.settings) { this.topologyIncomplete = true; continue; }
510
+ if (id === this.rootId || id === parentId || tasks.some(task => task.parentSessionId !== parentId) || native.header.callingSessionId !== parentId || path.resolve(native.header.cwd) !== this.config.cwd) throw new DroidError("droid_child_identity_invalid", "Task invocation disagrees with physical child parent/workspace");
511
+ if (new Set(tasks.map(task => task.taskInvocationId)).size !== tasks.length) this.topologyIncomplete = true;
512
+ const creationTool = native.header.callingToolUseId;
513
+ for (const task of tasks) {
514
+ if (!rootTools.has(task.parentToolUseId)) {
515
+ const block = (parents.get(parentId)?.records ?? []).flatMap(record => record.message?.content ?? []).find(block => block.type === "tool_use" && block.id === task.parentToolUseId);
516
+ if (block) throw new DroidError("droid_child_identity_invalid", "Task invocation is bound to a non-Task tool");
517
+ this.topologyIncomplete = true; continue;
518
+ }
519
+ if (task.parentToolUseId === creationTool) continue;
520
+ const tool = rootTools.get(task.parentToolUseId);
521
+ if (tool?.name !== "Task" || tool.input?.resume !== id) throw new DroidError("droid_child_identity_invalid", "Repeated child invocation has no matching native Task resume evidence");
522
+ }
523
+ await this.observeSettings(id, native.settings, { path: native.transcript_path, sha256: native.source_sha256 });
524
+ // Task resume adds a Turn to the same physical child, not another
525
+ // Session. Its transcript retains the original creation edge. Work
526
+ // reassignment is checked by dd-flow's physical Session binding.
527
+ const order = [...rootTools.keys()];
528
+ const ordered = tasks.toSorted((a, b) => order.indexOf(a.parentToolUseId) - order.indexOf(b.parentToolUseId));
529
+ const latest = ordered.at(-1); const running = !terminal(latest.status);
530
+ const outcome = native.outcomes.at(-1), nativeTurn = outcome && terminal(outcome.reason) && typeof outcome.turnId === "string" ? outcome.turnId : null;
531
+ const parentResult = (parents.get(parentId)?.records ?? []).flatMap(record => record.message?.content ?? []).some(block => block.type === "tool_result" && block.tool_use_id === latest.parentToolUseId);
532
+ const exactTurn = nativeTurn && (tasks.length === 1 || latest.turnId === nativeTurn);
533
+ // A resumed Task's previous terminal Turn is not its current outcome.
534
+ // The matching native Task result proves completion without attributing
535
+ // a previous error/Turn ID to this invocation.
536
+ const evidence = !running && nativeTurn && (tasks.length === 1 || parentResult || exactTurn) ? {
537
+ ...(exactTurn ? { terminal_turn_id: nativeTurn } : {}), native_source: "droid.agent_turn_outcome",
538
+ native_turn_items: { observed: native.complete && native.tool_calls.completeness === "complete", possible_effects: native.tool_calls.total > 0, pending: native.tool_calls.pending > 0 },
539
+ ...(exactTurn && outcome.error && typeof outcome.error === "object" ? { provider_error: safeProviderError(outcome.error) } : {}) } : {};
540
+ const previous = this.topology.get(id);
541
+ if (previous && (previous.parent_provider_session_id && previous.parent_provider_session_id !== parentId
542
+ || previous.tool_use_id && previous.tool_use_id !== creationTool))
543
+ throw new DroidError("droid_child_identity_invalid", "Native task inventory contradicts the retained child creation edge");
544
+ const creator = previous?.creator_operation_id ?? (previous?.latest_tool_use_id && previous.latest_tool_use_id !== creationTool ? null : previous?.operation_id) ?? null;
545
+ this.topology.set(id, { provider_session_id: id, parent_provider_session_id: parentId, tool_use_id: creationTool, latest_tool_use_id: latest.parentToolUseId,
546
+ creator_operation_id: creator, operation_id: latest.parentToolUseId === creationTool ? creator : null,
547
+ task_id: latest.taskInvocationId, task_invocation_ids: ordered.map(task => task.taskInvocationId), status: running ? "running" : status(latest.status), source: "droid.task_invocations", transcript_path: native.transcript_path, ...evidence });
548
+ }
549
+ return [...this.topology.values()];
550
+ }
551
+ async snapshotUsage(id, { ingest = true } = {}) {
552
+ const native = await readDroidSession(this.paths.factory, id); if (!native) return { status: "unavailable", provider_session_id: id };
553
+ this.journaledTools ??= new Map();
554
+ journalToolObservations(this.config.journal, [...native.records.flatMap(record => droidToolObservations(record, id)), ...native.tool_calls.reasons.map(reason => ({ reason }))], this.journaledTools);
555
+ const measured = native.usage;
556
+ const payload = { provider_session_id: id, daemon_id: this.config.daemonId, observed_at: new Date().toISOString(), usage_scope: "physical_session", completeness: native.complete && measured ? "complete" : "partial", usage: measured, tool_calls: native.tool_calls, source: { path: native.transcript_path, sha256: native.source_sha256 } };
557
+ if (ingest && measured && this.config.ddFlowBin) await this.flow("usage", "ingest", payload);
558
+ return { status: measured ? "measured" : "unavailable", ...payload };
559
+ }
560
+ async flow(family, command, payload, timeoutMs = 25_000) {
561
+ return await invokeNativeHook({ bin: this.config.ddFlowBin, args: ["droid", family, command, "--project-root", this.config.projectRoot, "--json"], payload,
562
+ cwd: this.config.cwd, env: droidEnvironment(this.config), home: this.config.ddFlowHome, timeoutMs });
563
+ }
564
+ async inspect(id, { ignoreActive = false, ingestUsage = true } = {}) {
565
+ this.requireIdentity(id); await this.captureProcesses(); const children = await this.refreshTopology(); const source = await readDroidSession(this.paths.factory, id);
566
+ const usage = []; for (const sessionId of [id, ...children.map(child => child.provider_session_id)]) usage.push(await this.snapshotUsage(sessionId, { ingest: ingestUsage }));
567
+ const settled = !this.topologyIncomplete && (!this.active || ignoreActive) && (this.closed || this.workingState === "idle") && source?.tool_calls.pending === 0 && children.every(child => terminal(child.status) && child.native_turn_items?.observed === true && child.native_turn_items.pending === false);
568
+ const rootSettled = source?.complete && source.tool_calls.completeness === "complete" && source.tool_calls.pending === 0 && (!this.active || ignoreActive) && (this.closed || this.workingState === "idle") && terminal((source?.outcomes?.at(-1) ?? this.lastResult)?.reason);
569
+ const neverPrompted = source?.complete && !this.active && !this.lastResult && source.records.every(record => record.type === "session_start");
570
+ const nativeSettled = Boolean(settled && (rootSettled || neverPrompted));
571
+ return nativeExecutionReceipt({ harness: "droid-cli", runtime_family: "droid", session: { harness_id: "droid-cli", session_id: id }, provider_session_id: id, adapter_session_id: id, cwd: this.config.cwd, observed_profile: source?.settings ? await this.observeSettings(id, source.settings, { path: source.transcript_path, sha256: source.source_sha256 }) : this.observedProfile, observed_runtime: { droid: this.config.version, factory_protocol: this.protocol ?? DROID_PROTOCOL, dd_harness_contract: DROID_CONTRACT }, status: settled ? "idle" : "running", settled, native_settled: nativeSettled, ...settledNativeTree(id, children, settled, rootSettled && !this.topologyIncomplete), terminal_turn_id: source?.outcomes?.at(-1)?.turnId ?? this.lastResult?.turnId, descendants: children, transcript_path: source?.transcript_path ?? null, usage, result: this.lastResult, provider_pid: this.closed ? null : this.child?.pid ?? null,
572
+ ...(this.topologyIncomplete ? { native_settlement: { confirmed: false, state: "unproven", reason: "native_task_inventory_incomplete" } } : {}) }, { daemonId: this.config.daemonId });
573
+ }
574
+ async observeHook(payload, eventId, deadlineAt = null) {
575
+ if (!this.config.noFlow && this.config.runtime_owner && this.config.ddFlowBin && this.rootId
576
+ && ["PreToolUse", "PostToolUse"].includes(payload.hook_event_name)
577
+ && typeof payload.tool_name === "string" && payload.tool_name && typeof payload.session_id === "string" && payload.session_id
578
+ && typeof eventId === "string" && eventId) {
579
+ if (payload.tool_name !== "Execute") return {}; // Read/Edit/etc. never carry lifecycle commands.
580
+ const analysis = typeof payload.tool_input?.command === "string" ? parseLifecycleCommand(payload.tool_input.command) : null;
581
+ // No RUN/Work receipt or native ancestry is needed for ordinary shell.
582
+ // Real notification progress remains source-bound in observe().
583
+ if (analysis?.participation === "none" && !analysis.unresolved) return {};
584
+ }
585
+ if (payload.hook_event_name === "PreToolUse" && payload.tool_name === "Execute" && this.config.runtime_owner && this.config.ddFlowBin
586
+ && typeof payload.session_id === "string" && payload.session_id && typeof eventId === "string" && eventId && this.rootId
587
+ && isStandaloneWorkStart(payload.tool_input?.command)) {
588
+ assertDaemonDispatch(this.paths.dir);
589
+ this.retainedWorkStarts ??= new Map();
590
+ const command = payload.tool_input?.command;
591
+ const accepted = await retainNativeWorkStart(this.paths.dir, { daemon_id: this.config.daemonId, source_harness: "droid-cli",
592
+ root_provider_session_id: this.rootId, provider_session_id: payload.session_id, parent_provider_session_id: null,
593
+ tool_call_id: payload.tool_use_id ?? eventId, native_tool_identity: payload.tool_use_id ? "native" : "callback_request",
594
+ event: "PreToolUse", native_command: command, native_cwd: payload.cwd ?? null,
595
+ native_payload: { schema_id: "dd-flow/droid-tool-event@1", phase: "before", event_id: eventId, tool_call_id: payload.tool_use_id ?? eventId,
596
+ daemon_id: this.config.daemonId, session: { provider_session_id: payload.session_id }, tool: "Execute", input: payload.tool_input,
597
+ transcript_path: payload.transcript_path, callback_cwd: payload.cwd } }, this.active ?? this.lastNativeHookOperation, this.retainedWorkStarts);
598
+ if (accepted) return {};
599
+ }
600
+ try { return await this.observeHookChecked(payload, eventId, hookBudget(23_000, deadlineAt)); }
601
+ catch (error) { if (error.details?.scope !== "foreign_hook") await this.handleObservationError(error); throw error; }
602
+ }
603
+ async verifyWorkStart(params) {
604
+ const retained = this.retainedWorkStarts?.get(params.requestId);
605
+ if (!retained || !isDeepStrictEqual(retained.binding, params.binding)) throw new DroidError("native_hook_unproven", "Unknown native Work start callback");
606
+ retained.guard();
607
+ const binding = retained.binding, id = binding.provider_session_id, payload = binding.native_payload;
608
+ if (binding.root_provider_session_id !== this.rootId) throw new DroidError("droid_hook_identity_invalid", "Native Work start root changed");
609
+ const report = operationProgressReporter();
610
+ const checkpoint = cursor => report('verification_progress', { native_request_id: cursor, session_id: id });
611
+ const native = await this.metadata.read(id, checkpoint);
612
+ if (!native || !path.isAbsolute(payload.callback_cwd ?? "") || path.resolve(payload.callback_cwd) !== this.config.cwd
613
+ || path.resolve(native.header.cwd) !== this.config.cwd || await realpath(payload.transcript_path) !== await realpath(native.transcript_path))
614
+ throw new DroidError("droid_hook_identity_invalid", "Native Work start has no verified physical Session/workspace");
615
+ const parent = native.header.callingSessionId ?? null, seen = new Set([id]);
616
+ let ancestor = parent;
617
+ while (ancestor && ancestor !== this.rootId) {
618
+ if (seen.has(ancestor)) throw new DroidError("droid_hook_identity_invalid", "Native Work start ancestry contains a cycle");
619
+ seen.add(ancestor);
620
+ const owner = await this.metadata.read(ancestor, checkpoint);
621
+ if (!owner || path.resolve(owner.header.cwd) !== this.config.cwd) throw new DroidError("droid_hook_identity_invalid", "Native Work start ancestry is unproven");
622
+ ancestor = owner.header.callingSessionId ?? null;
623
+ }
624
+ if (id !== this.rootId && ancestor !== this.rootId || id === this.rootId && parent)
625
+ throw new DroidError("droid_hook_identity_invalid", "Native Work start belongs to another root");
626
+ const previous = this.topology.get(id);
627
+ if (previous && (previous.parent_provider_session_id !== parent || previous.tool_use_id && previous.tool_use_id !== native.header.callingToolUseId))
628
+ throw new DroidError("droid_hook_identity_invalid", "Native Work start contradicts retained ancestry");
629
+ const profile = await this.observeSettings(id, native.settings, { path: native.transcript_path, sha256: native.source_sha256 ?? native.source?.sha256 });
630
+ retained.guard();
631
+ return { verified: true, registeredHookBinding: binding, nativeHookBinding: { ...binding, parent_provider_session_id: parent,
632
+ native_ancestry: "confirmed", native_payload: { ...payload, session: { provider_session_id: id, parent_provider_session_id: parent, directory: this.config.cwd }, profile } } };
633
+ }
634
+ async observeHookChecked(payload, eventId, budget = hookBudget(23_000)) {
635
+ const id = payload.session_id, event = payload.hook_event_name;
636
+ if (!id || !event || !eventId) throw new DroidError("droid_hook_invalid", "Hook lacks native identity or event ID");
637
+ // SessionStart can be invoked synchronously by initialize/load before the
638
+ // provider creates its transcript. Acknowledge only the outstanding owned
639
+ // root request; this is not evidence that initialization succeeded.
640
+ const initPending = [...this.pending.values()].some(item => ["droid.initialize_session", "droid.load_session"].includes(item.method));
641
+ if (event === "SessionStart" && initPending && id === this.rootId && typeof payload.cwd === "string" && path.resolve(payload.cwd) === this.config.cwd && typeof payload.transcript_path === "string") {
642
+ const sessions = path.join(this.paths.factory, "sessions"), transcript = path.resolve(payload.transcript_path), relative = path.relative(sessions, transcript);
643
+ if (path.isAbsolute(payload.transcript_path) && relative && !relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative) && path.basename(transcript) === `${id}.jsonl`) return {};
644
+ }
645
+ // Hooks can precede the root initialize response or the child notification.
646
+ const deadline = Date.now() + 3_000; let native;
647
+ do { native = ["PreToolUse", "PostToolUse"].includes(event) ? await this.metadata.read(id) : await readDroidSession(this.paths.factory, id); if (native) break; await sleep(25); } while (Date.now() < deadline);
648
+ const foreign = message => new DroidError("droid_hook_identity_invalid", message, { scope: "foreign_hook", provider_session_id: id });
649
+ if (!native || path.resolve(payload.cwd) !== this.config.cwd || await realpath(payload.transcript_path) !== await realpath(native.transcript_path)) throw foreign("Hook is outside its controlled native Session/workspace");
650
+ const parent = native.header.callingSessionId ?? null;
651
+ if (parent && parent !== this.rootId && !this.topology.has(parent)) throw foreign("Hook child belongs to another root");
652
+ if (parent && (id === parent || this.topology.has(id) && this.topology.get(id).parent_provider_session_id !== parent)) throw foreign("Hook child ancestry changed");
653
+ if (!parent && this.rootId && id !== this.rootId) throw foreign("Hook came from an unrelated root");
654
+ if (path.resolve(native.header.cwd) !== this.config.cwd) throw new DroidError("droid_hook_identity_invalid", "Owned native Session workspace changed");
655
+ if (parent && !terminal(this.topology.get(id)?.status)) {
656
+ const previous = this.topology.get(id), creationTool = native.header.callingToolUseId;
657
+ if (previous?.tool_use_id && previous.tool_use_id !== creationTool) throw foreign("Hook child creation tool contradicts retained native identity");
658
+ this.topology.set(id, { ...(previous ?? {}), provider_session_id: id, parent_provider_session_id: parent, tool_use_id: creationTool, status: "running", source: "droid.session_start" });
659
+ }
660
+ this.activity += 1; await this.journal("hook", { event_id: eventId, event, session_id: id, parent_session_id: parent }); await this.save();
661
+ if (!["PreToolUse", "PostToolUse"].includes(event)) return {};
662
+ if (this.fatalError) return { continue: false, stopReason: this.fatalError.message };
663
+ let profile;
664
+ try { profile = await this.observeSettings(id, native.settings, { path: native.transcript_path, sha256: native.source_sha256 }); }
665
+ catch (error) { await this.handleObservationError(error); return { continue: false, stopReason: error.message }; }
666
+ if (!this.config.ddFlowBin || payload.tool_name !== "Execute") return {};
667
+ // Usage is ingested by inspect/turn completion, never ahead of admission.
668
+ try {
669
+ return await this.flow("event", "handle", { schema_id: "dd-flow/droid-tool-event@1", phase: event === "PreToolUse" ? "before" : "after", event_id: eventId, daemon_id: this.config.daemonId, session: { provider_session_id: id, parent_provider_session_id: parent, directory: this.config.cwd }, tool: payload.tool_name, input: payload.tool_input, profile, transcript_path: native.transcript_path, outcome: { status: payload.tool_response?.is_error ? "error" : "completed" } }, budget.remaining("admission"));
670
+ } catch (error) {
671
+ if (event === "PreToolUse" && isProvenShellCorrection(error)) {
672
+ const refusal = errorRecord(error);
673
+ await this.journal("hook_rejected", { error: refusal, event_id: eventId, session_id: id });
674
+ await this.save();
675
+ return { continue: false, stopReason: error.message };
676
+ }
677
+ if (isNativeHookObservationLoss(error)) {
678
+ // A lost hook observation does not establish the tool or CLI outcome.
679
+ // Do not authorize this pre-tool request, but do not poison the Turn.
680
+ if (this.active) (this.active.hookDiagnostics ??= []).push(errorRecord(error));
681
+ try { await this.journal("native_hook_observation_unavailable", { event_id: eventId, session_id: id, error: errorRecord(error) }); } catch { /* optional diagnostic */ }
682
+ return event === "PreToolUse" ? { continue: false, stopReason: "Lifecycle observation unavailable; retry the command." } : {};
683
+ }
684
+ // Identity is proven above; only this owned admission can fail its turn.
685
+ this.fatalError ??= Object.assign(error, { details: { ...error.details, provider_session_id: id, event_id: eventId, effect: error.details?.effect ?? "unknown", native_turn_items: { observed: false, possible_effects: true, pending: true } } });
686
+ this.rejectPending(this.fatalError);
687
+ try { await budget.run(async () => { await this.save({ fatal_error: errorRecord(this.fatalError) }); await this.journal("hook_rejected", { error: errorRecord(this.fatalError) }); }, "failure_persistence"); }
688
+ catch (storage) { this.fatalError.details.receipt_error = errorRecord(storage); }
689
+ throw this.fatalError;
690
+ }
691
+ }
692
+ async captureProcesses() {
693
+ const rows = await processSnapshot();
694
+ const roots = this.ownedProcesses.filter(old => rows.some(row => row.pid === old.pid && row.started === old.started));
695
+ if (this.child && !this.closed) roots.push({ pid: this.child.pid });
696
+ for (const root of roots) for (const row of descendants(rows, root.pid)) if (!this.ownedProcesses.some(old => old.pid === row.pid && old.started === row.started)) this.ownedProcesses.push(row);
697
+ await this.save({ owned_processes: this.ownedProcesses });
698
+ return rows.filter(row => !row.zombie && this.ownedProcesses.some(old => old.pid === row.pid && old.started === row.started));
699
+ }
700
+ async closeTree(cancel = false) {
701
+ if (this.cleanupPromise) return await this.cleanupPromise;
702
+ this.cleanupPromise = this.closeOwnedTree(cancel);
703
+ try { return await this.cleanupPromise; } finally { this.cleanupPromise = null; }
704
+ }
705
+ async closeOwnedTree(cancel) {
706
+ await this.captureProcesses();
707
+ if (cancel) cancelPendingDispatch(this);
708
+ this.closing = true; this.cancelled = cancel;
709
+ if (cancel && !this.closed) { try { await this.request("droid.interrupt_session", {}, randomUUID(), 5_000); } catch (error) { await this.journal("interrupt_unobserved", { error: errorRecord(error) }); } }
710
+ let forced = this.state.cleanup?.forced === true;
711
+ try { if (!this.closed && this.rootId) await this.request("droid.close_session", { reason: "other" }, randomUUID(), 10_000); else if (!this.closed) this.child.stdin.end(); } catch (error) { await this.journal("close_unobserved", { error: errorRecord(error) }); }
712
+ const alive = () => this.captureProcesses();
713
+ const deadline = Date.now() + 8_000; while ((!this.closed || (await alive()).length) && Date.now() < deadline) await sleep(100);
714
+ if (!this.closed || (await alive()).length) {
715
+ forced = true; if (!this.closed) await stopProcessGroup(this.child, 2_000).catch(error => this.journal("group_stop_unconfirmed", { error: errorRecord(error) }));
716
+ // Execute can own a separate process group. Kill only still-matching
717
+ // observed descendants; never use a process-name search or broad pkill.
718
+ for (const row of await alive()) { try { process.kill(row.pid, "SIGTERM"); } catch (error) { if (error.code !== "ESRCH") throw error; } }
719
+ await sleep(1_000);
720
+ for (const row of await alive()) { try { process.kill(row.pid, "SIGKILL"); } catch (error) { if (error.code !== "ESRCH") throw error; } }
721
+ await sleep(200);
722
+ }
723
+ const survivors = await alive(); if (!this.closed || survivors.length) throw new DroidError("tree_not_settled", "Droid cleanup did not settle its owned process tree", { survivors });
724
+ clearInterval(this.processObserver); clearInterval(this.modelObserver); await this.processObservation;
725
+ await this.draining; this.workingState = "idle";
726
+ if (this.active && !this.active.outcome) this.active.reject(new DroidError(cancel ? "operation_cancelled" : "operation_observation_lost", "Droid runtime closed before a native terminal reply"));
727
+ await finishDaemonProcess(this.config, this.providerProcess, forced ? "failed" : "stopped", forced ? "forced_tree_shutdown" : "native_tree_closed");
728
+ let topologyError = null;
729
+ try { await this.refreshTopology(); }
730
+ catch (error) { topologyError = errorRecord(error); await this.journal("closed_tree_topology_error", { error: topologyError }); }
731
+ const root = this.rootId ? await readDroidSession(this.paths.factory, this.rootId) : null;
732
+ const nativeSettled = !forced && !topologyError && !this.topologyIncomplete
733
+ && (!this.rootId || root?.complete && (terminal(root.outcomes.at(-1)?.reason) || !this.active && !this.lastResult && root.records.every(record => record.type === "session_start")) && root.tool_calls.pending === 0)
734
+ && [...this.topology.values()].every(child => terminal(child.status) && child.native_turn_items?.observed === true && child.native_turn_items.pending === false);
735
+ const cleanup = { settled: true, physical_stopped: true, native_settled: nativeSettled, forced, observed_processes: this.ownedProcesses.length, ...(topologyError ? { topology_error: topologyError } : {}) };
736
+ await this.save({ active_tree: false, cleanup });
737
+ return cleanup;
738
+ }
739
+ }