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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (670) hide show
  1. package/CHANGELOG.md +959 -0
  2. package/README.md +139 -10
  3. package/dist/build-info.json +8 -8
  4. package/dist/cli/argument-values.js +38 -0
  5. package/dist/cli/command-inputs.js +417 -0
  6. package/dist/cli/command-metadata.js +303 -0
  7. package/dist/cli/command-option-descriptions.js +192 -0
  8. package/dist/cli/completion-cli.js +70 -0
  9. package/dist/cli/completion.js +57 -0
  10. package/dist/cli/delivery.js +108 -0
  11. package/dist/cli/diagnostic-invocation.js +62 -0
  12. package/dist/cli/diagnostic.js +131 -0
  13. package/dist/cli/error-output.js +62 -0
  14. package/dist/cli/executor-onboarding.js +76 -0
  15. package/dist/cli/flows.js +29 -0
  16. package/dist/cli/harnesses.js +84 -0
  17. package/dist/cli/help.js +521 -19
  18. package/dist/cli/hook-ingress.js +89 -0
  19. package/dist/cli/input-preparation.js +193 -0
  20. package/dist/cli/interactive-invocation.js +33 -0
  21. package/dist/cli/interactive.js +114 -0
  22. package/dist/cli/memory-fix-invocation.js +52 -0
  23. package/dist/cli/memory-fix.js +100 -0
  24. package/dist/cli/memory-flow-admission.js +54 -0
  25. package/dist/cli/memory-init-invocation.js +85 -0
  26. package/dist/cli/memory-init.js +250 -0
  27. package/dist/cli/memory-storage-admission.js +54 -0
  28. package/dist/cli/memory-storage-qualification.js +105 -0
  29. package/dist/cli/memory-storage-transition.js +123 -0
  30. package/dist/cli/memory-upgrade.js +189 -0
  31. package/dist/cli/models.js +39 -0
  32. package/dist/cli/native-hook-ingress.js +94 -0
  33. package/dist/cli/native-hook-preparation.js +82 -0
  34. package/dist/cli/operator-profiles.js +61 -0
  35. package/dist/cli/output-options.js +35 -0
  36. package/dist/cli/response-file.js +72 -0
  37. package/dist/cli/run-cli.js +2020 -555
  38. package/dist/cli/runtime-maintenance-diagnostics.js +46 -0
  39. package/dist/cli/runtime-maintenance-ingress.js +52 -0
  40. package/dist/cli/runtime-process.js +153 -0
  41. package/dist/cli/setup.js +150 -0
  42. package/dist/cli.js +103 -2
  43. package/dist/domain/delivery-flows.js +8 -0
  44. package/dist/domain/diagnostic-aspects.js +103 -0
  45. package/dist/domain/diagnostic-flows.js +8 -0
  46. package/dist/domain/distill-flow.js +20 -0
  47. package/dist/domain/interactive-flow.js +1 -0
  48. package/dist/domain/memory-fix-flow.js +2 -0
  49. package/dist/domain/review-fix-flow.js +2 -0
  50. package/dist/domain/source-snapshot-flows.js +4 -0
  51. package/dist/domain/stage-catalog.js +1 -1
  52. package/dist/domain/stage-handler-identities.js +28 -0
  53. package/dist/harness-runtime/DD-ZCODE.md +112 -0
  54. package/dist/harness-runtime/OPENCODE-LICENSE.txt +24 -0
  55. package/dist/harness-runtime/bin/dd-agy.mjs +71 -0
  56. package/dist/harness-runtime/bin/dd-codex.mjs +27 -0
  57. package/dist/harness-runtime/bin/dd-droid.mjs +34 -0
  58. package/dist/harness-runtime/bin/dd-grok.mjs +50 -0
  59. package/dist/harness-runtime/bin/dd-opencode.mjs +22 -0
  60. package/dist/harness-runtime/bin/dd-zcode.mjs +95 -0
  61. package/dist/harness-runtime/lib/adapter-timeouts.d.mts +5 -0
  62. package/dist/harness-runtime/lib/adapter-timeouts.mjs +34 -0
  63. package/dist/harness-runtime/lib/agy-error-boundary.mjs +87 -0
  64. package/dist/harness-runtime/lib/agy-hooks.d.mts +1 -0
  65. package/dist/harness-runtime/lib/agy-hooks.mjs +9 -0
  66. package/dist/harness-runtime/lib/agy-native-inspection.mjs +225 -0
  67. package/dist/harness-runtime/lib/agy-native-inventory.mjs +19 -0
  68. package/dist/harness-runtime/lib/agy-native_pb.js +117 -0
  69. package/dist/harness-runtime/lib/agy-terminal-envelope.mjs +74 -0
  70. package/dist/harness-runtime/lib/agy-transcript-stream.mjs +54 -0
  71. package/dist/harness-runtime/lib/daemon-observation.d.mts +3 -0
  72. package/dist/harness-runtime/lib/daemon-observation.mjs +171 -0
  73. package/dist/harness-runtime/lib/daemon-operations.d.mts +2 -0
  74. package/dist/harness-runtime/lib/daemon-operations.mjs +512 -0
  75. package/dist/harness-runtime/lib/dd-agy-daemon.d.mts +7 -0
  76. package/dist/harness-runtime/lib/dd-agy-daemon.mjs +1362 -0
  77. package/dist/harness-runtime/lib/dd-agy.mjs +105 -0
  78. package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
  79. package/dist/harness-runtime/lib/dd-codex-daemon.mjs +271 -0
  80. package/dist/harness-runtime/lib/dd-codex.d.mts +1 -0
  81. package/dist/harness-runtime/lib/dd-codex.mjs +986 -0
  82. package/dist/harness-runtime/lib/dd-droid-daemon.mjs +155 -0
  83. package/dist/harness-runtime/lib/dd-droid.mjs +739 -0
  84. package/dist/harness-runtime/lib/dd-grok-daemon.d.mts +1 -0
  85. package/dist/harness-runtime/lib/dd-grok-daemon.mjs +667 -0
  86. package/dist/harness-runtime/lib/dd-grok.mjs +182 -0
  87. package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +772 -0
  88. package/dist/harness-runtime/lib/dd-opencode.mjs +411 -0
  89. package/dist/harness-runtime/lib/dd-zcode-daemon.d.mts +1 -0
  90. package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +845 -0
  91. package/dist/harness-runtime/lib/dd-zcode.mjs +1239 -0
  92. package/dist/harness-runtime/lib/delegation-instructions.d.mts +16 -0
  93. package/dist/harness-runtime/lib/delegation-instructions.mjs +179 -0
  94. package/dist/harness-runtime/lib/delivery-command.mjs +84 -0
  95. package/dist/harness-runtime/lib/dispatch-fence.mjs +14 -0
  96. package/dist/harness-runtime/lib/driver-recovery.mjs +121 -0
  97. package/dist/harness-runtime/lib/droid-observation.mjs +70 -0
  98. package/dist/harness-runtime/lib/entity-references.d.mts +2 -0
  99. package/dist/harness-runtime/lib/entity-references.mjs +9 -0
  100. package/dist/harness-runtime/lib/lease-renewal.d.mts +8 -0
  101. package/dist/harness-runtime/lib/lease-renewal.mjs +37 -0
  102. package/dist/harness-runtime/lib/lifecycle-command.d.mts +20 -0
  103. package/dist/harness-runtime/lib/lifecycle-command.mjs +273 -0
  104. package/dist/harness-runtime/lib/lifecycle-contract.d.mts +11 -0
  105. package/dist/harness-runtime/lib/lifecycle-contract.mjs +20 -0
  106. package/dist/harness-runtime/lib/managed-daemon.d.mts +16 -0
  107. package/dist/harness-runtime/lib/managed-daemon.mjs +698 -0
  108. package/dist/harness-runtime/lib/managed-flow-env.mjs +15 -0
  109. package/dist/harness-runtime/lib/model-observations.mjs +113 -0
  110. package/dist/harness-runtime/lib/native-children.d.mts +34 -0
  111. package/dist/harness-runtime/lib/native-children.mjs +265 -0
  112. package/dist/harness-runtime/lib/native-hook-command.d.mts +8 -0
  113. package/dist/harness-runtime/lib/native-hook-command.mjs +306 -0
  114. package/dist/harness-runtime/lib/native-request-proof.d.mts +2 -0
  115. package/dist/harness-runtime/lib/native-request-proof.mjs +65 -0
  116. package/dist/harness-runtime/lib/native-streams.mjs +38 -0
  117. package/dist/harness-runtime/lib/observation-clock.d.mts +18 -0
  118. package/dist/harness-runtime/lib/observation-clock.mjs +95 -0
  119. package/dist/harness-runtime/lib/opencode-plugin.mjs +112 -0
  120. package/dist/harness-runtime/lib/operation-context.mjs +5 -0
  121. package/dist/harness-runtime/lib/operation-errors.d.mts +16 -0
  122. package/dist/harness-runtime/lib/operation-errors.mjs +140 -0
  123. package/dist/harness-runtime/lib/operation-progress.d.mts +7 -0
  124. package/dist/harness-runtime/lib/operation-progress.mjs +118 -0
  125. package/dist/harness-runtime/lib/process-json.d.mts +5 -0
  126. package/dist/harness-runtime/lib/process-json.mjs +200 -0
  127. package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
  128. package/dist/harness-runtime/lib/provider-overload-policy.d.mts +23 -0
  129. package/dist/harness-runtime/lib/provider-overload-policy.mjs +242 -0
  130. package/dist/harness-runtime/lib/runner-events.mjs +202 -0
  131. package/dist/harness-runtime/lib/runner-lock.mjs +42 -0
  132. package/dist/harness-runtime/lib/session-settlement.mjs +30 -0
  133. package/dist/harness-runtime/lib/setup-progress.d.mts +9 -0
  134. package/dist/harness-runtime/lib/setup-progress.mjs +53 -0
  135. package/dist/harness-runtime/lib/tool-observations.d.mts +21 -0
  136. package/dist/harness-runtime/lib/tool-observations.mjs +238 -0
  137. package/dist/harness-runtime/lib/zcode-native-hook-ownership.mjs +109 -0
  138. package/dist/harness-runtime/lib/zcode-usage.mjs +55 -0
  139. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-1y4a697q.js +81 -0
  140. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-2ny4vxct.js +92 -0
  141. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-6mptkzbc.js +0 -0
  142. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-7x67cf1q.js +52 -0
  143. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9rqn6x4v.js +16 -0
  144. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9vdz0zwn.js +1661 -0
  145. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ecs5ftka.js +1466 -0
  146. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-frbwqjmf.js +145 -0
  147. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gne7h7mq.js +50 -0
  148. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gx9wnkzr.js +105 -0
  149. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-m2eywc8w.js +0 -0
  150. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-nwcpakzx.js +12 -0
  151. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-q4g3afnw.js +623 -0
  152. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-qav6mfdz.js +0 -0
  153. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-sxt88q69.js +12 -0
  154. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-t1xa31vm.js +39 -0
  155. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ve4aws36.js +103 -0
  156. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vmytr4q8.js +4 -0
  157. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vt9jbch8.js +36 -0
  158. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vyas3vgt.js +0 -0
  159. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-wjyeq25y.js +30 -0
  160. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-xgnj2w4v.js +20 -0
  161. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-y2etg5bs.js +9 -0
  162. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ydhz1zrh.js +238 -0
  163. package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-z6qdwcsf.js +62 -0
  164. package/dist/harness-runtime/vendor/opencode-client/contract.d.ts +1 -0
  165. package/dist/harness-runtime/vendor/opencode-client/contract.js +12 -0
  166. package/dist/harness-runtime/vendor/opencode-client/effect/api/api.d.ts +2806 -0
  167. package/dist/harness-runtime/vendor/opencode-client/effect/api/api.js +0 -0
  168. package/dist/harness-runtime/vendor/opencode-client/effect/api.d.ts +4 -0
  169. package/dist/harness-runtime/vendor/opencode-client/effect/api.js +0 -0
  170. package/dist/harness-runtime/vendor/opencode-client/effect/client.d.ts +2530 -0
  171. package/dist/harness-runtime/vendor/opencode-client/effect/client.js +15 -0
  172. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.d.ts +7 -0
  173. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.js +6 -0
  174. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.d.ts +2529 -0
  175. package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.js +8 -0
  176. package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.d.ts +2 -0
  177. package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.js +12 -0
  178. package/dist/harness-runtime/vendor/opencode-client/effect/index.d.ts +35 -0
  179. package/dist/harness-runtime/vendor/opencode-client/effect/index.js +75 -0
  180. package/dist/harness-runtime/vendor/opencode-client/effect/rpc.d.ts +20 -0
  181. package/dist/harness-runtime/vendor/opencode-client/effect/rpc.js +11 -0
  182. package/dist/harness-runtime/vendor/opencode-client/effect/service.d.ts +90 -0
  183. package/dist/harness-runtime/vendor/opencode-client/effect/service.js +187 -0
  184. package/dist/harness-runtime/vendor/opencode-client/package.json +84 -0
  185. package/dist/harness-runtime/vendor/opencode-client/promise/api.d.ts +21 -0
  186. package/dist/harness-runtime/vendor/opencode-client/promise/api.js +0 -0
  187. package/dist/harness-runtime/vendor/opencode-client/promise/client.d.ts +252 -0
  188. package/dist/harness-runtime/vendor/opencode-client/promise/client.js +14 -0
  189. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.d.ts +8 -0
  190. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.js +6 -0
  191. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.d.ts +259 -0
  192. package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.js +7 -0
  193. package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.d.ts +3 -0
  194. package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.js +79 -0
  195. package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.d.ts +8932 -0
  196. package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.js +70 -0
  197. package/dist/harness-runtime/vendor/opencode-client/promise/index.d.ts +6 -0
  198. package/dist/harness-runtime/vendor/opencode-client/promise/index.js +83 -0
  199. package/dist/harness-runtime/vendor/opencode-client/promise/rpc.d.ts +32 -0
  200. package/dist/harness-runtime/vendor/opencode-client/promise/rpc.js +7 -0
  201. package/dist/harness-runtime/vendor/opencode-client/promise/service.d.ts +24 -0
  202. package/dist/harness-runtime/vendor/opencode-client/promise/service.js +187 -0
  203. package/dist/harness-runtime/vendor/opencode-client/pty-handoff.d.ts +12 -0
  204. package/dist/harness-runtime/vendor/opencode-client/pty-handoff.js +14 -0
  205. package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.d.ts +20 -0
  206. package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.js +14 -0
  207. package/dist/harness-runtime/vendor/opencode-client/service-contender.d.ts +37 -0
  208. package/dist/harness-runtime/vendor/opencode-client/service-contender.js +10 -0
  209. package/dist/harness-runtime/vendor/opencode-client/service-probe.d.ts +70 -0
  210. package/dist/harness-runtime/vendor/opencode-client/service-probe.js +16 -0
  211. package/dist/harness-runtime/vendor/opencode-client/service-timing.d.ts +12 -0
  212. package/dist/harness-runtime/vendor/opencode-client/service-timing.js +10 -0
  213. package/dist/harness-runtime/vendor/opencode-client/service-version.d.ts +2 -0
  214. package/dist/harness-runtime/vendor/opencode-client/service-version.js +6 -0
  215. package/dist/harness-runtime/vendor/opencode-client/service.d.ts +52 -0
  216. package/dist/harness-runtime/vendor/opencode-client/service.js +1 -0
  217. package/dist/harness-runtime/vendor/opencode-client/shared-events.d.ts +11 -0
  218. package/dist/harness-runtime/vendor/opencode-client/shared-events.js +8 -0
  219. package/dist/harness-runtime/vendor/opencode-client/solid/connection.d.ts +37 -0
  220. package/dist/harness-runtime/vendor/opencode-client/solid/connection.js +10 -0
  221. package/dist/harness-runtime/vendor/opencode-client/solid/data.d.ts +211 -0
  222. package/dist/harness-runtime/vendor/opencode-client/solid/data.js +18 -0
  223. package/dist/harness-runtime/vendor/opencode-client/solid/index.d.ts +3 -0
  224. package/dist/harness-runtime/vendor/opencode-client/solid/index.js +32 -0
  225. package/dist/harness-runtime/vendor/opencode-client/solid/pty.d.ts +22 -0
  226. package/dist/harness-runtime/vendor/opencode-client/solid/pty.js +8 -0
  227. package/dist/harness-runtime/vendor/opencode-plugin.mjs +3 -0
  228. package/dist/protocol/local-files.js +18 -9
  229. package/dist/runtime/context.js +8 -4
  230. package/dist/schemas/agent-profile.schema.json +1 -1
  231. package/dist/schemas/code-review-decision.schema.json +9 -0
  232. package/dist/schemas/code-review-result.schema.json +1 -1
  233. package/dist/schemas/code-verification.schema.json +7 -5
  234. package/dist/schemas/code-work-batch.schema.json +2 -2
  235. package/dist/schemas/code-work-result.schema.json +2 -1
  236. package/dist/schemas/delivery-stage-result.schema.json +286 -0
  237. package/dist/schemas/diagnostic-stage-result.schema.json +354 -0
  238. package/dist/schemas/diagnostic-work-result.schema.json +296 -0
  239. package/dist/schemas/discovery-result.schema.json +184 -0
  240. package/dist/schemas/distill-stage-result.schema.json +667 -0
  241. package/dist/schemas/distill-work-result.schema.json +428 -0
  242. package/dist/schemas/execution-bundle.schema.json +215 -0
  243. package/dist/schemas/execution-settings.schema.json +162 -0
  244. package/dist/schemas/flow-descriptor.schema.json +54 -0
  245. package/dist/schemas/flow-run.schema.json +29 -0
  246. package/dist/schemas/harness-catalog.schema.json +80 -0
  247. package/dist/schemas/interactive-stage-result.schema.json +47 -0
  248. package/dist/schemas/interactive-work-result.schema.json +63 -0
  249. package/dist/schemas/mb-fix-stage-result.schema.json +93 -0
  250. package/dist/schemas/mb-init-compose-result.schema.json +457 -0
  251. package/dist/schemas/mb-init-discover-result.schema.json +500 -0
  252. package/dist/schemas/mb-init-finish-result.schema.json +422 -0
  253. package/dist/schemas/mb-init-preflight-result.schema.json +367 -0
  254. package/dist/schemas/mb-init-review-result.schema.json +396 -0
  255. package/dist/schemas/mb-init-verify-result.schema.json +391 -0
  256. package/dist/schemas/mb-upgrade-stage-result.schema.json +863 -0
  257. package/dist/schemas/memory-bootstrap-receipt-v2.schema.json +67 -0
  258. package/dist/schemas/memory-bootstrap-receipt.schema.json +354 -0
  259. package/dist/schemas/memory-composition-work-result.schema.json +38 -0
  260. package/dist/schemas/memory-repair-work-result.schema.json +56 -0
  261. package/dist/schemas/memory-review-correction-plan.schema.json +30 -0
  262. package/dist/schemas/memory-review-work-result.schema.json +43 -0
  263. package/dist/schemas/model-catalog.schema.json +153 -0
  264. package/dist/schemas/plan-review-decision.schema.json +12 -3
  265. package/dist/schemas/plan-review-result.schema.json +2 -2
  266. package/dist/schemas/profile-settings.schema.json +122 -0
  267. package/dist/schemas/project-flow-pack-manifest.schema.json +2 -2
  268. package/dist/schemas/release-impact.schema.json +3 -3
  269. package/dist/schemas/release-manifest.schema.json +291 -0
  270. package/dist/schemas/review-fix-stage-result.schema.json +210 -0
  271. package/dist/schemas/run-control-receipt.schema.json +119 -0
  272. package/dist/schemas/run-control-request.schema.json +36 -0
  273. package/dist/schemas/run-engine-binding.schema.json +1 -1
  274. package/dist/schemas/stage-start-response.schema.json +4 -0
  275. package/dist/schemas/vnext-protocol-plan.schema.json +3 -3
  276. package/dist/services/accepted-intent.js +139 -0
  277. package/dist/services/assigned-lifecycle.js +382 -0
  278. package/dist/services/branch-context.js +3 -3
  279. package/dist/services/canon.js +54 -15
  280. package/dist/services/check-environment.js +21 -0
  281. package/dist/services/check-runner-evidence.js +344 -0
  282. package/dist/services/check-runner-proof.js +373 -0
  283. package/dist/services/cleanup.js +133 -40
  284. package/dist/services/cli-operation-classifier.js +32 -64
  285. package/dist/services/cli-upgrade-allowlist.js +27 -0
  286. package/dist/services/code-checks.js +1426 -121
  287. package/dist/services/codex-capacity.js +367 -0
  288. package/dist/services/codex-hook-delivery.js +41 -0
  289. package/dist/services/command-context.js +80 -0
  290. package/dist/services/compatibility-preflight.js +16 -3
  291. package/dist/services/config.js +11 -8
  292. package/dist/services/continuation-outcome.js +25 -0
  293. package/dist/services/controller-child-capacity.js +71 -0
  294. package/dist/services/controller-child-observer.js +121 -0
  295. package/dist/services/controller-child-repair.js +117 -0
  296. package/dist/services/controller-execution.js +97 -0
  297. package/dist/services/controller-fanout.js +277 -0
  298. package/dist/services/controller-memory-task.js +45 -0
  299. package/dist/services/controller-memory-worker.js +51 -0
  300. package/dist/services/controller-result-authority.js +127 -0
  301. package/dist/services/controller-work-failure.js +198 -0
  302. package/dist/services/dashboard.js +145 -58
  303. package/dist/services/delivery-access.js +123 -0
  304. package/dist/services/delivery-approval.js +68 -0
  305. package/dist/services/delivery-artifact.js +86 -0
  306. package/dist/services/delivery-checks.js +178 -0
  307. package/dist/services/delivery-execution.js +181 -0
  308. package/dist/services/delivery-intents.js +212 -0
  309. package/dist/services/delivery-operation.js +81 -0
  310. package/dist/services/delivery-policy.js +91 -0
  311. package/dist/services/delivery-procedures.js +43 -0
  312. package/dist/services/delivery-request.js +99 -0
  313. package/dist/services/delivery-result.js +144 -0
  314. package/dist/services/delivery-stage-commands.js +26 -0
  315. package/dist/services/delivery-stage-entry.js +56 -0
  316. package/dist/services/delivery-stage-handlers.js +135 -0
  317. package/dist/services/delivery-stage-results.js +94 -0
  318. package/dist/services/descriptor-coordinator-session.js +37 -0
  319. package/dist/services/descriptor-root-work.js +53 -0
  320. package/dist/services/descriptor-run-settlement.js +107 -0
  321. package/dist/services/descriptor-run-start.js +495 -0
  322. package/dist/services/descriptor-stage-coordinator.js +318 -0
  323. package/dist/services/descriptor-stage-settlement.js +268 -0
  324. package/dist/services/descriptor-stage-start.js +175 -0
  325. package/dist/services/diagnostic-evidence.js +73 -0
  326. package/dist/services/diagnostic-repair-intake.js +123 -0
  327. package/dist/services/diagnostic-repair-source-intake.js +102 -0
  328. package/dist/services/diagnostic-report-publication.js +72 -0
  329. package/dist/services/diagnostic-report.js +97 -0
  330. package/dist/services/diagnostic-run-bundle.js +113 -0
  331. package/dist/services/diagnostic-snapshot.js +159 -0
  332. package/dist/services/diagnostic-stage-handlers.js +109 -0
  333. package/dist/services/diagnostic-stage-results.js +158 -0
  334. package/dist/services/diagnostic-start.js +134 -0
  335. package/dist/services/diagnostic-work-batch.js +34 -0
  336. package/dist/services/diagnostic-work-collection.js +97 -0
  337. package/dist/services/diagnostic-work-registration.js +101 -0
  338. package/dist/services/diagnostic-work-results.js +93 -0
  339. package/dist/services/discovery/agy-discovery.js +2 -0
  340. package/dist/services/discovery/codex-discovery.js +2 -0
  341. package/dist/services/discovery/droid-discovery.js +2 -0
  342. package/dist/services/discovery/grok-discovery.js +2 -0
  343. package/dist/services/discovery/opencode-discovery.js +2 -0
  344. package/dist/services/discovery/static.js +31 -0
  345. package/dist/services/discovery/zcode-discovery.js +2 -0
  346. package/dist/services/distill-document-publication.js +280 -0
  347. package/dist/services/distill-promotion.js +197 -0
  348. package/dist/services/distill-publication-custody.js +162 -0
  349. package/dist/services/distill-stage-handlers.js +249 -0
  350. package/dist/services/distill-stage-results.js +105 -0
  351. package/dist/services/distill-user-inputs.js +4 -0
  352. package/dist/services/distill-work-batch.js +42 -0
  353. package/dist/services/distill-work-collection.js +74 -0
  354. package/dist/services/distill-work-registration.js +93 -0
  355. package/dist/services/distill-work-results.js +98 -0
  356. package/dist/services/engines.js +354 -163
  357. package/dist/services/eval-snapshots.js +1326 -150
  358. package/dist/services/execution-bundles.js +275 -0
  359. package/dist/services/execution-policy.js +185 -0
  360. package/dist/services/executor-onboarding.js +56 -0
  361. package/dist/services/external-work-launch.js +316 -0
  362. package/dist/services/flow-catalog.js +54 -0
  363. package/dist/services/flow-descriptors.js +82 -0
  364. package/dist/services/frozen-engine.js +404 -0
  365. package/dist/services/generic-execution.js +165 -0
  366. package/dist/services/harness-adapter.js +342 -35
  367. package/dist/services/harness-config.js +2 -0
  368. package/dist/services/harness-discovery.js +29 -0
  369. package/dist/services/harness-doctor.js +75 -0
  370. package/dist/services/harness-probe-effects.js +51 -0
  371. package/dist/services/harness-version-probe.js +100 -0
  372. package/dist/services/hook-memory-routing.js +188 -0
  373. package/dist/services/hook-recovery-guard.js +14 -0
  374. package/dist/services/hook-session-observation.js +24 -0
  375. package/dist/services/hooks.js +813 -327
  376. package/dist/services/hybrid-contracts.js +479 -0
  377. package/dist/services/hybrid-digest.js +14 -0
  378. package/dist/services/ids.js +29 -1
  379. package/dist/services/installation-lock.js +95 -0
  380. package/dist/services/installed-flow-source.js +261 -0
  381. package/dist/services/interactive-attached-session.js +215 -0
  382. package/dist/services/interactive-baseline-custody.js +54 -0
  383. package/dist/services/interactive-before-versions.js +148 -0
  384. package/dist/services/interactive-bundle-data.js +85 -0
  385. package/dist/services/interactive-checks.js +179 -0
  386. package/dist/services/interactive-execution.js +33 -0
  387. package/dist/services/interactive-finish-integration.js +236 -0
  388. package/dist/services/interactive-git-baseline.js +230 -0
  389. package/dist/services/interactive-git-dispatch.js +278 -0
  390. package/dist/services/interactive-input-delivery.js +82 -0
  391. package/dist/services/interactive-mutation-scope.js +231 -0
  392. package/dist/services/interactive-request.js +287 -0
  393. package/dist/services/interactive-run-bundle.js +109 -0
  394. package/dist/services/interactive-stage-entry.js +41 -0
  395. package/dist/services/interactive-stage-handlers.js +289 -0
  396. package/dist/services/interactive-stage-observation.js +136 -0
  397. package/dist/services/interactive-stage-results.js +111 -0
  398. package/dist/services/interactive-user-inputs.js +142 -0
  399. package/dist/services/interactive-work-batch.js +43 -0
  400. package/dist/services/interactive-work-collection.js +119 -0
  401. package/dist/services/interactive-work-completion.js +180 -0
  402. package/dist/services/interactive-work-context.js +249 -0
  403. package/dist/services/interactive-work-history.js +112 -0
  404. package/dist/services/interactive-work-registration.js +124 -0
  405. package/dist/services/interactive-work-results.js +115 -0
  406. package/dist/services/interactive-work-start.js +155 -0
  407. package/dist/services/lanes.js +78 -65
  408. package/dist/services/lifecycle-command.js +1 -288
  409. package/dist/services/lifecycle-contract.js +1 -0
  410. package/dist/services/lifecycle-invocations.js +2549 -0
  411. package/dist/services/managed-daemon-binding.js +129 -0
  412. package/dist/services/managed-lease-monitor.js +133 -0
  413. package/dist/services/managed-processes.js +449 -69
  414. package/dist/services/memory-accepted-composition.js +78 -0
  415. package/dist/services/memory-baseline-preservation.js +65 -0
  416. package/dist/services/memory-completed-correction-wave.js +328 -0
  417. package/dist/services/memory-compose-handler.js +380 -0
  418. package/dist/services/memory-composition-acceptance.js +375 -0
  419. package/dist/services/memory-composition-graph.js +165 -0
  420. package/dist/services/memory-composition-registration.js +161 -0
  421. package/dist/services/memory-composition-work-custody.js +156 -0
  422. package/dist/services/memory-composition-work-results.js +108 -0
  423. package/dist/services/memory-corrected-review-acceptance.js +129 -0
  424. package/dist/services/memory-corrected-review-batch.js +37 -0
  425. package/dist/services/memory-corrected-review-findings.js +108 -0
  426. package/dist/services/memory-corrected-review-inputs.js +215 -0
  427. package/dist/services/memory-corrected-review-projection.js +78 -0
  428. package/dist/services/memory-corrected-review-receipt.js +178 -0
  429. package/dist/services/memory-corrected-review-registration.js +192 -0
  430. package/dist/services/memory-correction-check-receipt.js +235 -0
  431. package/dist/services/memory-correction-input-owner.js +6 -0
  432. package/dist/services/memory-custody-snapshot.js +36 -0
  433. package/dist/services/memory-discover-handler.js +104 -0
  434. package/dist/services/memory-finish-evidence.js +457 -0
  435. package/dist/services/memory-finish-handler.js +229 -0
  436. package/dist/services/memory-finish-integration.js +491 -0
  437. package/dist/services/memory-finish-readiness.js +169 -0
  438. package/dist/services/memory-fix-finish-integration.js +129 -0
  439. package/dist/services/memory-fix-git-dispatch.js +139 -0
  440. package/dist/services/memory-fix-intake-custody.js +11 -0
  441. package/dist/services/memory-fix-intake-settlement.js +127 -0
  442. package/dist/services/memory-fix-publication.js +196 -0
  443. package/dist/services/memory-fix-repair-admission.js +116 -0
  444. package/dist/services/memory-fix-request.js +141 -0
  445. package/dist/services/memory-fix-run-bundle.js +83 -0
  446. package/dist/services/memory-fix-scope.js +78 -0
  447. package/dist/services/memory-fix-sources.js +85 -0
  448. package/dist/services/memory-fix-stage-entry.js +24 -0
  449. package/dist/services/memory-fix-stage-handlers.js +282 -0
  450. package/dist/services/memory-fix-stage-results.js +106 -0
  451. package/dist/services/memory-fix-workspace.js +127 -0
  452. package/dist/services/memory-flow-user-inputs.js +88 -0
  453. package/dist/services/memory-git-integration.js +220 -0
  454. package/dist/services/memory-historical-corrected-review.js +212 -0
  455. package/dist/services/memory-historical-original-review.js +143 -0
  456. package/dist/services/memory-historical-repair-registration.js +105 -0
  457. package/dist/services/memory-historical-second-review.js +147 -0
  458. package/dist/services/memory-init-bootstrap-readiness.js +210 -0
  459. package/dist/services/memory-init-contexts.js +181 -0
  460. package/dist/services/memory-init-controller-start.js +119 -0
  461. package/dist/services/memory-init-engine-release.js +223 -0
  462. package/dist/services/memory-init-git-policy.js +165 -0
  463. package/dist/services/memory-init-lint-capability.js +203 -0
  464. package/dist/services/memory-init-native-capacity.js +309 -0
  465. package/dist/services/memory-init-preflight.js +334 -0
  466. package/dist/services/memory-init-project-bootstrap.js +599 -0
  467. package/dist/services/memory-init-request-candidate.js +213 -0
  468. package/dist/services/memory-init-request-input.js +82 -0
  469. package/dist/services/memory-init-resource-storage.js +125 -0
  470. package/dist/services/memory-init-run-allocation.js +81 -0
  471. package/dist/services/memory-init-run-bundle.js +135 -0
  472. package/dist/services/memory-init-start.js +181 -0
  473. package/dist/services/memory-init-startup-custody.js +80 -0
  474. package/dist/services/memory-init-startup-input.js +240 -0
  475. package/dist/services/memory-init-storage.js +91 -0
  476. package/dist/services/memory-init-stores.js +359 -0
  477. package/dist/services/memory-init-worktrunk-capability.js +174 -0
  478. package/dist/services/memory-layer-publication.js +611 -0
  479. package/dist/services/memory-layer.js +225 -0
  480. package/dist/services/memory-lint-config.js +194 -0
  481. package/dist/services/memory-native-review-copy.js +127 -0
  482. package/dist/services/memory-normal-flow-entry.js +87 -0
  483. package/dist/services/memory-ordinary-defaults.js +129 -0
  484. package/dist/services/memory-preflight-bootstrap-custody.js +115 -0
  485. package/dist/services/memory-preflight-handler.js +274 -0
  486. package/dist/services/memory-repair-current-view.js +299 -0
  487. package/dist/services/memory-repair-document-delta.js +489 -0
  488. package/dist/services/memory-repair-history.js +496 -0
  489. package/dist/services/memory-repair-physical-binding.js +194 -0
  490. package/dist/services/memory-repair-publication-files.js +152 -0
  491. package/dist/services/memory-repair-publication-intent.js +27 -0
  492. package/dist/services/memory-repair-publication-plan.js +162 -0
  493. package/dist/services/memory-repair-publication-source.js +225 -0
  494. package/dist/services/memory-repair-publication.js +795 -0
  495. package/dist/services/memory-repair-registration.js +240 -0
  496. package/dist/services/memory-repair-work-baseline.js +415 -0
  497. package/dist/services/memory-repair-work-completion.js +117 -0
  498. package/dist/services/memory-repair-work-custody.js +73 -0
  499. package/dist/services/memory-repair-work-results.js +108 -0
  500. package/dist/services/memory-repair-workspace.js +261 -0
  501. package/dist/services/memory-repair-writer-copy.js +736 -0
  502. package/dist/services/memory-result-binding.js +239 -0
  503. package/dist/services/memory-retained-review-authority.js +138 -0
  504. package/dist/services/memory-retained-review-results.js +70 -0
  505. package/dist/services/memory-retained-second-baseline.js +102 -0
  506. package/dist/services/memory-retained-second-repairs.js +162 -0
  507. package/dist/services/memory-review-acceptance.js +214 -0
  508. package/dist/services/memory-review-correction-plan.js +206 -0
  509. package/dist/services/memory-review-handler.js +585 -0
  510. package/dist/services/memory-review-inputs.js +246 -0
  511. package/dist/services/memory-review-limits.js +10 -0
  512. package/dist/services/memory-review-receipt.js +180 -0
  513. package/dist/services/memory-review-registration.js +117 -0
  514. package/dist/services/memory-review-work-custody.js +126 -0
  515. package/dist/services/memory-review-work-input.js +91 -0
  516. package/dist/services/memory-review-work-results.js +95 -0
  517. package/dist/services/memory-second-corrected-review-inputs.js +167 -0
  518. package/dist/services/memory-second-correction-plan.js +77 -0
  519. package/dist/services/memory-second-repair-baseline.js +91 -0
  520. package/dist/services/memory-second-repair-launch.js +614 -0
  521. package/dist/services/memory-second-repair-registration.js +88 -0
  522. package/dist/services/memory-second-repair-source.js +187 -0
  523. package/dist/services/memory-second-review-acceptance.js +115 -0
  524. package/dist/services/memory-source-observations.js +117 -0
  525. package/dist/services/memory-stage-dispatch.js +65 -0
  526. package/dist/services/memory-stage-results.js +290 -0
  527. package/dist/services/memory-tool-probe.js +25 -0
  528. package/dist/services/memory-upgrade-analysis-handler.js +243 -0
  529. package/dist/services/memory-upgrade-bootstrap-readiness.js +251 -0
  530. package/dist/services/memory-upgrade-evidence.js +100 -0
  531. package/dist/services/memory-upgrade-inventory.js +442 -0
  532. package/dist/services/memory-upgrade-publication-custody.js +73 -0
  533. package/dist/services/memory-upgrade-publication.js +413 -0
  534. package/dist/services/memory-upgrade-selection.js +430 -0
  535. package/dist/services/memory-upgrade-start.js +60 -0
  536. package/dist/services/memory-upgrade-storage-readiness.js +340 -0
  537. package/dist/services/memory-verification-acceptance.js +156 -0
  538. package/dist/services/memory-verification-checks.js +317 -0
  539. package/dist/services/memory-verification-receipt.js +160 -0
  540. package/dist/services/memory-verify-handler.js +137 -0
  541. package/dist/services/memory-work-result-json.js +12 -0
  542. package/dist/services/memory-work-result-preparation.js +64 -0
  543. package/dist/services/merge-queue.js +228 -149
  544. package/dist/services/merge-server.js +163 -35
  545. package/dist/services/migrations.js +164 -114
  546. package/dist/services/model-catalog.js +109 -0
  547. package/dist/services/model-discovery.js +46 -0
  548. package/dist/services/native-daemon-history.js +50 -0
  549. package/dist/services/native-session-control.js +39 -0
  550. package/dist/services/operator-profiles.js +236 -0
  551. package/dist/services/physical-startup.js +45 -0
  552. package/dist/services/plan-runtime.js +26 -11
  553. package/dist/services/plans.js +28 -25
  554. package/dist/services/project-flow-source.js +195 -0
  555. package/dist/services/project-git-integration.js +92 -0
  556. package/dist/services/project-registry.js +97 -0
  557. package/dist/services/project-summary.js +1 -1
  558. package/dist/services/projects.js +17 -87
  559. package/dist/services/prompt-delivery.js +71 -0
  560. package/dist/services/prompts.js +28 -17
  561. package/dist/services/protocols.js +163 -87
  562. package/dist/services/provider-capacity.js +463 -0
  563. package/dist/services/recovery-observation-budget.js +121 -0
  564. package/dist/services/recovery-snapshot-database.js +588 -0
  565. package/dist/services/release-archive-producer.js +52 -0
  566. package/dist/services/release-archive.js +143 -0
  567. package/dist/services/release-cache.js +150 -0
  568. package/dist/services/release-compatibility.js +18 -0
  569. package/dist/services/release-download.js +67 -0
  570. package/dist/services/release-envelope.js +104 -0
  571. package/dist/services/release-manifest.js +45 -0
  572. package/dist/services/release-payload.js +114 -0
  573. package/dist/services/release-producer.js +63 -0
  574. package/dist/services/release-provenance.js +160 -0
  575. package/dist/services/release-registry.js +66 -0
  576. package/dist/services/release-source.js +194 -0
  577. package/dist/services/repair-intents.js +29 -0
  578. package/dist/services/repair-receipt.js +13 -0
  579. package/dist/services/retained-native-hook.js +274 -0
  580. package/dist/services/review-copy.js +165 -0
  581. package/dist/services/review-deferral.js +23 -0
  582. package/dist/services/review-fix-handoff.js +82 -0
  583. package/dist/services/review-fix-intake-custody.js +17 -0
  584. package/dist/services/review-fix-stage-entry.js +43 -0
  585. package/dist/services/review-fix-stage-handlers.js +176 -0
  586. package/dist/services/review-fix-stage-results.js +86 -0
  587. package/dist/services/run-control-receipt.js +56 -0
  588. package/dist/services/run-control-worker.js +469 -0
  589. package/dist/services/run-control.js +1109 -0
  590. package/dist/services/run-controller-adapter.js +434 -0
  591. package/dist/services/run-controller-capture.js +186 -0
  592. package/dist/services/run-controller-process.js +226 -0
  593. package/dist/services/run-controller-recovery.js +335 -0
  594. package/dist/services/run-controller-state.js +106 -0
  595. package/dist/services/run-controller.js +1717 -0
  596. package/dist/services/run-engine-bindings.js +1 -1
  597. package/dist/services/run-fork.js +147 -0
  598. package/dist/services/run-health.js +149 -0
  599. package/dist/services/run-observations.js +152 -0
  600. package/dist/services/run-projection.js +22 -64
  601. package/dist/services/run-recovery-guard.js +107 -0
  602. package/dist/services/run-recovery-runtime.js +112 -0
  603. package/dist/services/run-recovery.js +485 -0
  604. package/dist/services/run-start-custody.js +189 -0
  605. package/dist/services/run-timeline.js +59 -0
  606. package/dist/services/runs.js +996 -271
  607. package/dist/services/runtime-budget.js +446 -0
  608. package/dist/services/runtime-command.js +43 -0
  609. package/dist/services/runtime-scope-capture.js +52 -0
  610. package/dist/services/runtime-scope-control.js +468 -0
  611. package/dist/services/runtime-scope-resume.js +605 -0
  612. package/dist/services/runtime-scope-stop.js +211 -0
  613. package/dist/services/runtime-scope-worker.js +314 -0
  614. package/dist/services/runtime-service.js +19 -14
  615. package/dist/services/schema-validation.js +10 -8
  616. package/dist/services/scope-writer-cleanup.js +27 -0
  617. package/dist/services/sessions.js +37 -91
  618. package/dist/services/setup.js +427 -0
  619. package/dist/services/stage-blocker.js +17 -7
  620. package/dist/services/stage-context.js +240 -18
  621. package/dist/services/stage-handlers.js +141 -0
  622. package/dist/services/stage-lifecycle.js +156 -119
  623. package/dist/services/stage-pause.js +222 -94
  624. package/dist/services/stage-report-renderer.js +230 -6
  625. package/dist/services/stage-work-graph.js +52 -0
  626. package/dist/services/technical-notes.js +31 -0
  627. package/dist/services/tooling.js +111 -44
  628. package/dist/services/usage.js +49 -101
  629. package/dist/services/verification-matrix.js +393 -0
  630. package/dist/services/version-status.js +1 -1
  631. package/dist/services/vnext-code-review.js +302 -91
  632. package/dist/services/vnext-code.js +325 -251
  633. package/dist/services/vnext-contracts.js +10 -1
  634. package/dist/services/vnext-execution-profile.js +80 -5
  635. package/dist/services/vnext-fanout.js +399 -20
  636. package/dist/services/vnext-merge.js +336 -114
  637. package/dist/services/vnext-plan-review.js +288 -133
  638. package/dist/services/vnext-plan.js +230 -106
  639. package/dist/services/vnext-protocolize.js +534 -96
  640. package/dist/services/vnext-specify.js +131 -84
  641. package/dist/services/vnext-workspace-policy.js +7 -2
  642. package/dist/services/work-registry.js +1950 -215
  643. package/dist/services/work-retry-context.js +55 -0
  644. package/dist/services/workspace-bootstrap.js +53 -15
  645. package/dist/services/workspace-files.js +85 -0
  646. package/dist/services/worktrees.js +108 -48
  647. package/dist/shared/entity-references.js +1 -0
  648. package/dist/shared/errors.js +27 -0
  649. package/dist/storage/agent-output.js +26 -0
  650. package/dist/storage/data-preservation.js +65 -0
  651. package/dist/storage/database.js +931 -40
  652. package/dist/storage/foreign-key-transaction.js +39 -0
  653. package/dist/storage/immutable-input.js +45 -0
  654. package/dist/storage/import-guard.js +159 -0
  655. package/dist/storage/memory-upgrade-schema-bootstrap.js +38 -0
  656. package/dist/storage/native-cleanup.js +52 -0
  657. package/dist/storage/paired-writer-migration.js +424 -0
  658. package/dist/storage/paths.js +17 -4
  659. package/dist/storage/projection-lock.js +73 -0
  660. package/dist/storage/work-references.js +23 -0
  661. package/dist/storage/writer-contract.js +96 -0
  662. package/dist/storage/writer-migration.js +346 -0
  663. package/dist/storage/writer-restore.js +217 -0
  664. package/dist/verification-components.json +384 -0
  665. package/package.json +52 -13
  666. package/tools/create-canon-release.mjs +33 -0
  667. package/tools/probe-codex-history.mjs +37 -0
  668. package/tools/produce-hybrid-release.mjs +41 -0
  669. package/tools/repair-paused-run-status.mjs +84 -0
  670. package/tools/verification-jobs.mjs +342 -0
@@ -0,0 +1,1362 @@
1
+ import { managedFlowEnv } from './managed-flow-env.mjs';
2
+ import { SETUP_STARTUP_MS, startupObserver, observationDetails } from './setup-progress.mjs';
3
+ import { callDaemonObserved } from './daemon-observation.mjs';
4
+ import { operationProgressReporter } from './operation-progress.mjs';
5
+ import { prepareDaemonRequest } from "./native-request-proof.mjs";
6
+ import { inspectAgyNative, inspectAgyNativeState, stopAgyNativeTree, ownedAgyEndpoint, readAgyNativeQualification, AGY_NATIVE_INSPECTION_CONTRACT } from './agy-native-inspection.mjs';
7
+ import { agyNativeInventory } from './agy-native-inventory.mjs';
8
+ import { processSnapshot } from './process-snapshot.mjs';
9
+ import { streamRequestKey } from './provider-overload-policy.mjs';
10
+ import { captureDispatchGuard, cancelPendingDispatch } from "./dispatch-fence.mjs";
11
+ import { errorRecord } from "./operation-errors.mjs";
12
+ import { ToolObservations, agyToolObservation, createAgyTranscriptObserver } from "./tool-observations.mjs";
13
+ import { readAgyTranscriptSnapshot } from "./agy-transcript-stream.mjs";
14
+ import { constants } from "node:fs";
15
+ import { fileURLToPath, pathToFileURL } from "node:url";
16
+ import { createHash, randomUUID } from "node:crypto";
17
+ import { appendFile, chmod, lstat, mkdir, open, readFile, readdir, realpath, rename, unlink, writeFile } from "node:fs/promises";
18
+ import { spawn } from "node:child_process";
19
+ import net from "node:net";
20
+ import { isDeepStrictEqual } from "node:util";
21
+ import { ObservationClock, observedTimeout, validateObservationDuration } from "./observation-clock.mjs";
22
+ import { hookBudget, retainNativeWorkStart, isStandaloneWorkStart } from "./native-hook-command.mjs";
23
+ import { parseLifecycleCommand } from "./lifecycle-command.mjs";
24
+ import { agyWorkspaceHooks } from "./agy-hooks.mjs";
25
+ import { durableDaemonDispatch, completeDaemonShutdown, assertDaemonDispatch, assertDaemonRestartable, trackDaemonConnections, retireDaemonEndpoint, retireFailedDaemon } from "./daemon-operations.mjs";
26
+ import path from "node:path";
27
+ import { observeModel } from "./model-observations.mjs";
28
+ import { AGY_VERSION_TIMEOUT_MS, AgyError, agyTerminalFailure, agyErrorDiagnostic, agyModelArgs, assertProfile, executable, observedProfile, runAgy, usageSnapshot, managedAgyEnv } from "./dd-agy.mjs";
29
+ import { agyTerminalEnvelope } from "./agy-terminal-envelope.mjs";
30
+ import { settledNativeTree, nativeExecutionReceipt } from "./native-children.mjs";
31
+ import { retainStartupFailure, prepareRuntimeOwner, cleanupFailedStart, cleanupFailedDaemonStart, confirmDaemonProcess, confirmDaemonStopped, finishDaemonProcess, scheduleDaemonProcessHeartbeat, registerDaemonProcess, stopProcessGroup } from "./managed-daemon.mjs";
32
+ import { readNativeLines, nativeObservationError, admitNativeRequestBuffer } from "./native-streams.mjs";
33
+
34
+ const REQUEST_SCHEMA = "dd-agy/daemon-request@1", RESPONSE_SCHEMA = "dd-agy/daemon-response@1", STATE_SCHEMA = "dd-agy/daemon-state@1";
35
+ class DaemonError extends AgyError {}
36
+ // Only unambiguously correlated native tool results can introduce children or
37
+ // classify services. This transcript format has no tool-call IDs: parallel
38
+ // calls therefore retain unknown resources, never FIFO-derived authority.
39
+ export function agyTranscriptFacts(text, source, sessionId) {
40
+ const reducer = createAgyTranscriptFacts(source, sessionId), lines = text.split('\n');
41
+ for (let index = 0; index < lines.length; index++) {
42
+ const line = lines[index];
43
+ if (!line && index === lines.length - 1) continue;
44
+ let event;
45
+ try { event = JSON.parse(line); } catch { if (index < lines.length - 1) reducer.gap(); break; }
46
+ reducer.add(event);
47
+ }
48
+ return reducer.finish();
49
+ }
50
+ export function createAgyTranscriptFacts(source, sessionId) {
51
+ const children = [], resources = new Map(), calls = [];
52
+ let invalid = false;
53
+ function add(event) {
54
+ if (invalid || !source?.locator || !source?.sha256 || !sessionId) return;
55
+ if (!event || typeof event !== 'object' || Array.isArray(event) || event.conversation_id && event.conversation_id !== sessionId) { invalid = true; return; }
56
+ const content = typeof event.content === 'string' ? event.content : '';
57
+ const native_source = { ...source, step_index: event.step_index };
58
+ if (event.source === 'SYSTEM' && event.type === 'SYSTEM_MESSAGE' && event.status === 'DONE') {
59
+ const terminal = agyTerminalEnvelope(event);
60
+ if (terminal) {
61
+ const prior = resources.get(terminal.task_id);
62
+ if (prior) resources.set(prior.resource_id, mergeAgyResource(prior, { ...prior, state: 'stopped', ...(terminal.command_outcome ? { native_outcome: terminal.command_outcome } : {}), terminal_source: native_source }));
63
+ }
64
+ return;
65
+ }
66
+ if (event.source !== 'MODEL') return;
67
+ if (event.type === 'PLANNER_RESPONSE') { calls.length = 0; if (Array.isArray(event.tool_calls) && event.tool_calls.length === 1) calls.push(event.tool_calls[0]); return; }
68
+ if (event.type !== 'GENERIC') return;
69
+ const call = calls.shift();
70
+ if (call?.name === 'manage_task' && event.status === 'DONE') {
71
+ const terminal = agyTerminalEnvelope(event, call), prior = resources.get(terminal?.task_id);
72
+ if (prior) resources.set(prior.resource_id, mergeAgyResource(prior, { ...prior, state: 'stopped', ...(terminal.command_outcome ? { native_outcome: terminal.command_outcome } : {}), terminal_source: native_source }));
73
+ }
74
+ if (call?.name === 'invoke_subagent' && event.status === 'DONE') {
75
+ const match = content.match(/^Created At: [^\n]+\nCompleted At: [^\n]+\nCreated the following subagent:\n(\{[\s\S]*?\})\nThe subagent will send you a message/);
76
+ if (!match) return;
77
+ try {
78
+ const child = JSON.parse(match[1]);
79
+ if (/^[\w-]+$/.test(child.conversationId) && typeof child.logAbsoluteUri === 'string') children.push({ provider_session_id: child.conversationId, parent_provider_session_id: sessionId, log_uri: child.logAbsoluteUri, workspace_uris: child.workspaceUris ?? [], status: 'unknown', native_source });
80
+ } catch { /* No partial/guessed native edge. */ }
81
+ }
82
+ if (event.status !== 'RUNNING') return;
83
+ const task = content.match(/^Created At: [^\n]+\nTool is running as a background task with task id: ([\w-]+\/task-\d+)\n/);
84
+ if (!task || !task[1].startsWith(`${sessionId}/`)) return;
85
+ let daemon = call?.name === 'run_command' ? call.args?.IsDaemon : null;
86
+ if (typeof daemon === 'string') { try { daemon = JSON.parse(daemon); } catch { daemon = null; } }
87
+ const resource = { resource_id: task[1], provider_session_id: sessionId, ...(call?.name === 'schedule' ? { kind: 'schedule' } : {}), classification: daemon === true ? 'support_service' : daemon === false ? 'required_work' : 'unknown', state: 'unknown', native_source,
88
+ ...(typeof daemon === 'boolean' ? { native_classification: { tool: 'run_command', IsDaemon: daemon } } : {}) };
89
+ resources.set(resource.resource_id, mergeAgyResource(resources.get(resource.resource_id), resource));
90
+ }
91
+ return { add, gap: () => { invalid = true; }, finish: () => ({ children, resources: [...resources.values()], ...(invalid ? { gap: 'agy_transcript_invalid_record' } : {}) }) };
92
+ }
93
+ export function mergeAgyResource(prior, next) {
94
+ if (!prior) return next;
95
+ if (prior.provider_session_id !== next.provider_session_id || prior.native_source?.locator !== next.native_source?.locator)
96
+ return { ...prior, state: 'unknown', observation_conflict: true };
97
+ if (prior.native_source?.step_index !== next.native_source?.step_index)
98
+ return { ...prior, state: 'unknown', observation_conflict: true };
99
+ if (prior.observation_conflict || next.observation_conflict || prior.native_outcome && next.native_outcome && prior.native_outcome !== next.native_outcome)
100
+ return { ...prior, state: 'unknown', observation_conflict: true };
101
+ if (next.terminal_source && (!Number.isSafeInteger(next.native_source?.step_index) || !Number.isSafeInteger(next.terminal_source?.step_index) || next.terminal_source.step_index <= next.native_source.step_index))
102
+ return { ...prior, state: 'unknown', observation_conflict: true };
103
+ if (prior.state === 'stopped' && next.state !== 'stopped') {
104
+ if (next.native_source?.step_index > (prior.terminal_source?.step_index ?? prior.native_source?.step_index))
105
+ return { ...prior, state: 'unknown', observation_conflict: true };
106
+ return prior;
107
+ }
108
+ return { ...prior, ...next };
109
+ }
110
+ export const agyTranscriptResources = (text, source, sessionId) => agyTranscriptFacts(text, source, sessionId).resources;
111
+ export async function captureAgyTranscript(runtimeRoot, child, rootSessionId, ownedSessionIds = [rootSessionId], inspect = null, onProgress = null) {
112
+ const session = { harness_id: "antigravity-cli", session_id: child.provider_session_id };
113
+ let handle;
114
+ try {
115
+ if (!rootSessionId || !child.provider_session_id || !ownedSessionIds.includes(child.parent_provider_session_id) || !child.log_uri || !/^[\w-]+$/.test(child.provider_session_id)) throw new Error("unbound native locator");
116
+ const expected = path.join(runtimeRoot, "brain", child.provider_session_id, ".system_generated", "logs", "transcript.jsonl");
117
+ const canonical = path.join(await realpath(runtimeRoot), "brain", child.provider_session_id, ".system_generated", "logs", "transcript.jsonl");
118
+ if (![expected, canonical].includes(fileURLToPath(child.log_uri)) || await realpath(expected) !== canonical) throw new Error("foreign or symlink native locator");
119
+ const before = await lstat(expected);
120
+ if (!before.isFile()) throw new Error("native locator is not a regular file");
121
+ handle = await open(expected, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
122
+ const stat = await handle.stat();
123
+ if (!before.isFile() || !stat.isFile() || stat.dev !== before.dev || stat.ino !== before.ino || await realpath(expected) !== canonical) throw new Error("native locator changed during open");
124
+ let observer, inspector;
125
+ const snapshot = await readAgyTranscriptSnapshot(handle, { locator: child.log_uri, onProgress,
126
+ assertIdentity: async current => { const pathStat = await lstat(expected); if (!pathStat.isFile() || current.dev !== pathStat.dev || current.ino !== pathStat.ino || await realpath(expected) !== canonical) throw new Error('native locator changed during read'); },
127
+ onRecord: (record, { line, ...source }) => {
128
+ observer ??= createAgyTranscriptObserver(child.provider_session_id, child.parent_provider_session_id ?? rootSessionId, source);
129
+ inspector ??= inspect?.(source);
130
+ observer.add(record, line); inspector?.add(record);
131
+ } });
132
+ observer ??= createAgyTranscriptObserver(child.provider_session_id, child.parent_provider_session_id ?? rootSessionId, snapshot.source);
133
+ if (!snapshot.partial_tail) inspector?.finish();
134
+ const events = observer.finish();
135
+ if (snapshot.partial_tail) events.push({ session, reason: "agy_transcript_partial_tail" });
136
+ return events;
137
+ } catch (error) { return [{ session, reason: error.code === "ENOENT" ? "agy_transcript_missing" : error.message === 'agy_transcript_invalid_record' ? 'agy_transcript_invalid_record' : "agy_transcript_locator_rejected" }]; }
138
+ finally { await handle?.close(); }
139
+ }
140
+ const absolute = (value, label) => { if (!value || !path.isAbsolute(value)) throw new DaemonError("invalid_path", `${label} must be an absolute path`); return path.resolve(value); };
141
+ function locations(stateDir) {
142
+ const dir = absolute(stateDir, "--state-dir"), digest = createHash("sha256").update(dir).digest("hex").slice(0, 22), local = path.join(dir, "daemon.sock");
143
+ // `tsx` creates Unix-domain sockets below TMPDIR. An evaluation path is
144
+ // intentionally descriptive and can exceed the platform's socket-path cap,
145
+ // so provider temp files must not inherit that path.
146
+ return { dir, socket: Buffer.byteLength(local) < 100 ? local : `/tmp/dd-agy-${digest}.sock`, state: path.join(dir, "daemon.json"), journal: path.join(dir, "events.jsonl"), log: path.join(dir, "daemon.log"), gemini: path.join(dir, "gemini"), runtime: path.join(dir, "gemini", "runtime"), config: path.join(dir, "gemini", "config"), temporary: `/tmp/dd-agy-tmp-${digest}` };
147
+ }
148
+ async function writeJson(file, value) { const temporary = `${file}.${process.pid}.${randomUUID()}.tmp`; await writeFile(temporary, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 }); await rename(temporary, file); }
149
+ async function readState(dir) { try { return JSON.parse(await readFile(locations(dir).state, "utf8")); } catch (error) { if (error.code === "ENOENT") return null; throw error; } }
150
+ async function removeSocket(paths) { try { if (!(await lstat(paths.socket)).isSocket()) throw new DaemonError("unsafe_socket", "refusing to replace a non-socket daemon path"); await unlink(paths.socket); } catch (error) { if (error.code !== "ENOENT") throw error; } }
151
+ const shell = value => `'${String(value).replaceAll("'", `'"'"'`)}'`;
152
+ export function sanitizedEnv(config) { const env = { ...process.env, TMPDIR: config.temporary, DD_AGY_HOOK_NODE: process.execPath, DD_AGY_HOOK_ENTRY: config.entryPath, DD_AGY_STATE_DIR: config.stateDir, DD_AGY_DAEMON_ID: config.daemonId }; for (const key of ["GEMINI_API_KEY","GOOGLE_API_KEY","ANTHROPIC_API_KEY","OPENAI_API_KEY"]) env[key] = undefined; return managedAgyEnv({ ...env, ...managedFlowEnv(config) }); }
153
+
154
+ const errorPayload = errorRecord;
155
+ async function forwardUsage(config, sessionId, result, tools, descendants) {
156
+ if (!config.ddFlowBin || !config.projectRoot) return;
157
+ const snapshot = usageSnapshot(result, tools), hasChildren = descendants.length > 0;
158
+ if (snapshot.total_tokens === null) return { status: "unavailable", reason: "provider_did_not_report_total_tokens" };
159
+ const payload = { provider_session_id: sessionId, daemon_id: config.daemonId, observed_at: new Date().toISOString(), usage_scope: hasChildren ? "unknown" : "physical_session", completeness: hasChildren ? "partial" : "complete", usage: { inputTokens: snapshot.input_tokens, outputTokens: snapshot.output_tokens, reasoningTokens: snapshot.reasoning_tokens, cacheReadInputTokens: snapshot.cache_read_tokens, totalTokens: snapshot.total_tokens }, tool_calls: snapshot.tool_calls };
160
+ const target = executable(config.ddFlowBin, ["agy", "usage", "ingest", "--project-root", config.projectRoot, "--json"]);
161
+ await commandText(target.command, target.args, { input: `${JSON.stringify(payload)}\n`, env: { DD_FLOW_HOME: config.ddFlowHome }, phase: "usage-ingest" });
162
+ return { status: "ingested" };
163
+ }
164
+
165
+ export async function prepare(paths, config) {
166
+ config.stateDir = paths.dir;
167
+ config.hookBinding = "environment-v1";
168
+ const hooks = agyWorkspaceHooks();
169
+ for (const dir of [paths.dir, paths.gemini, paths.runtime, paths.config, paths.temporary]) { await mkdir(dir, { recursive: true, mode: 0o700 }); await chmod(dir, 0o700); }
170
+ if (config.noFlow) {
171
+ // Technical probes still need native settlement, but must not install
172
+ // workspace hooks or forward any lifecycle mutation to dd-flow.
173
+ await writeJson(path.join(paths.config, "hooks.json"), { "dd-flow-observer": { Stop: hooks["dd-flow"].Stop } });
174
+ return;
175
+ }
176
+ // Native discovery follows cwd (feature worktree/reviewer clone), while
177
+ // projectRoot remains the stable lifecycle identity returned to dd-flow.
178
+ Object.assign(config, await qualifyAgyHooks(config.cwd ?? config.projectRoot, { stateDir: paths.dir }));
179
+ // Workspace discovery covers root and children without global duplicates.
180
+ await writeJson(path.join(paths.config, "hooks.json"), {});
181
+ }
182
+
183
+ export async function qualifyAgyHooks(projectRoot, { stateDir = null, install = false } = {}) {
184
+ const hooks = agyWorkspaceHooks();
185
+ // Register in workspace discovery before creating any Session: native root
186
+ // and children load these named hook definitions without a global duplicate.
187
+ // Replace our prior handlers, including the obsolete wildcard post hook,
188
+ // without dropping unrelated commands in a shared matcher group.
189
+ const workspaceConfig = path.join(absolute(projectRoot, "--project-root"), ".agents");
190
+ const workspaceHooks = path.join(workspaceConfig, "hooks.json");
191
+ let existing = {}, existingText = "";
192
+ try {
193
+ existingText = await readFile(workspaceHooks, "utf8");
194
+ const parsed = JSON.parse(existingText);
195
+ if (!parsed || Array.isArray(parsed) || typeof parsed !== "object") throw new Error("not an object");
196
+ existing = parsed;
197
+ } catch (error) {
198
+ if (error.code !== "ENOENT") throw new DaemonError("workspace_hooks_invalid", `cannot install Antigravity lifecycle hooks: ${error.message}`);
199
+ if (!install) throw new DaemonError("workspace_hooks_unqualified", "Antigravity hooks must be installed during qualification before daemon start", false, { hooks_path: workspaceHooks });
200
+ }
201
+ const isManagedHandler = handler => typeof handler?.command === "string"
202
+ && ((handler.command.includes("DD_AGY_HOOK_ENTRY") && handler.command.includes(" hook handle --event ") && handler.command.includes("DD_AGY_STATE_DIR")) || /(?:^|\/)dd-agy\.mjs['"]?\s+hook handle/.test(handler.command));
203
+ const events = ["PreToolUse", "PostToolUse", "PreInvocation", "PostInvocation", "Stop"];
204
+ const existingManaged = Object.entries(existing).flatMap(([name, definition]) => {
205
+ if (events.includes(name) && Array.isArray(definition)) return definition;
206
+ if (!definition || typeof definition !== "object" || Array.isArray(definition)) return [];
207
+ return events.flatMap(event => Array.isArray(definition[event]) ? definition[event] : []);
208
+ }).flatMap(item => Array.isArray(item?.hooks) ? item.hooks : [item]).filter(handler => isManagedHandler(handler) && handler.command.includes("--state-dir") && !handler.command.includes("DD_AGY_STATE_DIR"));
209
+ if (existingManaged.some(handler => !stateDir || !handler.command.includes(`--state-dir ${shell(stateDir)}`))) {
210
+ throw new DaemonError("workspace_hooks_drifted", "qualified Antigravity hook routes to another daemon state directory", false, { hooks_path: workspaceHooks, expected_state_dir: stateDir });
211
+ }
212
+ const retain = handlers => handlers.flatMap(item => {
213
+ if (!Array.isArray(item?.hooks)) return isManagedHandler(item) ? [] : [item];
214
+ const remaining = item.hooks.filter(handler => !isManagedHandler(handler));
215
+ return remaining.length ? [{ ...item, hooks: remaining }] : [];
216
+ });
217
+ const workspace = Object.create(null), legacy = Object.create(null);
218
+ for (const [name, definition] of Object.entries(existing)) {
219
+ // Migrate the old adapter's event-keyed workspace format.
220
+ if (events.includes(name) && Array.isArray(definition)) {
221
+ const remaining = retain(definition);
222
+ if (remaining.length) legacy[name] = remaining;
223
+ continue;
224
+ }
225
+ if (!definition || Array.isArray(definition) || typeof definition !== "object") {
226
+ throw new DaemonError("workspace_hooks_invalid", `invalid named hook definition: ${name}`);
227
+ }
228
+ const cleaned = { ...definition };
229
+ for (const event of events) if (Array.isArray(cleaned[event])) {
230
+ const remaining = retain(cleaned[event]);
231
+ if (remaining.length || cleaned[event].length === 0) cleaned[event] = remaining;
232
+ else delete cleaned[event];
233
+ }
234
+ workspace[name] = cleaned;
235
+ }
236
+ if (Object.keys(legacy).length) {
237
+ let name = "dd-flow-legacy";
238
+ while (Object.hasOwn(workspace, name)) name += "-retained";
239
+ workspace[name] = legacy;
240
+ }
241
+ const managed = workspace["dd-flow"] ?? {};
242
+ if (managed.enabled === false) throw new DaemonError("workspace_hooks_invalid", "dd-flow lifecycle hooks are disabled");
243
+ for (const [event, handlers] of Object.entries(hooks["dd-flow"])) {
244
+ managed[event] = [...(managed[event] ?? []), ...handlers];
245
+ }
246
+ workspace["dd-flow"] = managed;
247
+ const serializedWorkspace = `${JSON.stringify(workspace, null, 2)}\n`;
248
+ // Hook installation is a qualification/ownership boundary. Reusing the
249
+ // qualified command must not rewrite the project file when the pinned engine,
250
+ // daemon directory or workspace location changes.
251
+ const installed = isDeepStrictEqual(existing, JSON.parse(serializedWorkspace));
252
+ if (!installed && !install) throw new DaemonError("workspace_hooks_unqualified", "Antigravity hook definitions differ from the qualified contract", false, { hooks_path: workspaceHooks });
253
+ if (!installed) { await mkdir(workspaceConfig, { recursive: true, mode: 0o700 }); await writeFile(workspaceHooks, serializedWorkspace, { mode: 0o600 }); }
254
+ return { hooksSha256: createHash("sha256").update(JSON.stringify(hooks)).digest("hex"), workspaceHooksPath: workspaceHooks,
255
+ workspaceHooksSha256: createHash("sha256").update(installed ? existingText : serializedWorkspace).digest("hex"), workspaceHooksInstalled: installed ? "qualified_existing" : "qualified_install" };
256
+ }
257
+
258
+ export async function callDaemon(stateDir, operation, params = {}, timeoutMs = 30_000, requestId = null) {
259
+ const request = prepareDaemonRequest({ schema_id: REQUEST_SCHEMA, id: requestId ?? process.env.DD_EVAL_OPERATION_ID ?? randomUUID(), operation, params });
260
+ return await callDaemonObserved({ stateDir, socket: locations(stateDir).socket, request, responseSchema: RESPONSE_SCHEMA, harness: 'agy', timeoutMs });
261
+ }
262
+
263
+ function sameConfig(state, config) { return ["cwd","journal","bin","provider","model","reasoning","mode","projectRoot","ddFlowBin","ddFlowHome","resourceHome","noFlow","livenessTimeoutMs","nativeQualificationPath"].every(key => (state.config?.[key] ?? null) === (config[key] ?? null)); }
264
+ // Called only after authorizeRetainedDaemonResume verifies the old daemon,
265
+ // provider processes and operation ledger. Native outcomes remain historical;
266
+ // their old liveness flags cannot describe the replacement process tree.
267
+ export function retainedAgyState(previous, sessionId) {
268
+ if (!sessionId || !previous?.sessions?.some(session => session.provider_session_id === sessionId)) return {};
269
+ if (previous.shutdown_state !== "clean" || previous.active_tree !== false) return previous;
270
+ return {
271
+ ...previous,
272
+ unclaimed_activity: false,
273
+ native_resources_entries: [],
274
+ native_topology_error: null,
275
+ retained_tree_settlement: { daemon_id: previous.daemon_id, session_id: sessionId, turn_generation: previous.turn_generation ?? 0, evidence: "verified_clean_daemon_shutdown" },
276
+ session_observations: (previous.session_observations ?? []).map(([id, observation]) => [id, id === sessionId ? { ...observation, retained_stop: observation.stop ?? null, stop: null } : observation]),
277
+ descendants: (previous.descendants ?? []).map(child => child.tree_settled === true ? child : { ...child, tree_settled: true, settlement_evidence: "prior_clean_daemon_shutdown", settlement_daemon_id: previous.daemon_id })
278
+ };
279
+ }
280
+ export async function startDaemon(options) {
281
+ const paths = locations(options.stateDir); await mkdir(paths.dir, { recursive: true, mode: 0o700 }); await chmod(paths.dir, 0o700);
282
+ const previous = await readState(paths.dir), noFlow = options.noFlow === true, cwd = await realpath(absolute(options.cwd, "--cwd")), config = { cwd, journal: options.journal ? absolute(options.journal, "--journal") : paths.journal, bin: options.bin ?? process.env.DD_AGY_BIN ?? "agy", provider: String(options.provider ?? "google"), model: String(options.model ?? "gemini-3.1-pro-high"), reasoning: String(options.reasoning ?? "high"), mode: String(options.mode ?? "accept-edits"), projectRoot: noFlow ? cwd : await realpath(absolute(options.projectRoot, "--project-root")), ddFlowBin: noFlow ? null : absolute(options.ddFlowBin, "--dd-flow-bin"), ddFlowHome: noFlow ? null : absolute(options.ddFlowHome, "--dd-flow-home"), resourceHome: noFlow ? null : (process.env.DD_FLOW_RESOURCE_HOME ? absolute(process.env.DD_FLOW_RESOURCE_HOME, "DD_FLOW_RESOURCE_HOME") : null), env: noFlow ? {} : { DD_FLOW_HOME: absolute(options.ddFlowHome, "--dd-flow-home") }, entryPath: absolute(options.entryPath, "dd-agy entry"), daemonId: randomUUID(), resumeSessionId: options.sessionId ?? null, geminiDir: paths.gemini, appDataDir: "runtime", temporary: paths.temporary, noFlow };
283
+ config.livenessTimeoutMs = options.livenessTimeoutMs ?? 1_800_000;
284
+ config.nativeQualificationPath = process.env.DD_AGY_NATIVE_QUALIFICATION ? absolute(process.env.DD_AGY_NATIVE_QUALIFICATION, 'DD_AGY_NATIVE_QUALIFICATION') : null;
285
+ validateObservationDuration(config.livenessTimeoutMs);
286
+ await prepareRuntimeOwner(config, paths.dir);
287
+ assertDaemonRestartable(previous);
288
+ try { const status = await callDaemon(paths.dir, "daemon.status", {}, 1000); if (!sameConfig(status, config)) throw new DaemonError("daemon_config_mismatch", "a daemon is already running with different configuration"); return { ...status, already_running: true }; } catch (error) { if (!["daemon_not_running","daemon_timeout"].includes(error.code)) throw error; }
289
+ assertDaemonRestartable(await readState(paths.dir));
290
+ if (previous?.shutdown_state === "clean") await authorizeRetainedDaemonResume(paths.dir, previous, options.sessionId, config);
291
+ if (previous?.shutdown_state === "running" && previous.active_tree) throw new DaemonError("invalid_harness_crash", "previous Antigravity daemon died with an active or unproven tree");
292
+ await removeSocket(paths); await prepare(paths, config);
293
+ const version = (await runAgy(config.bin, ["--version"], { cwd: config.cwd, env: sanitizedEnv(config), timeoutMs: AGY_VERSION_TIMEOUT_MS })).stdout.trim();
294
+ const resourceProcess = await registerDaemonProcess(config, { kind: "agy-daemon", owner: `agy:${path.basename(paths.dir)}`, operation: `agy-daemon:${path.basename(paths.dir)}`, stdout: paths.log, stderr: paths.log });
295
+ const retained = retainedAgyState(previous, config.resumeSessionId);
296
+ await writeJson(paths.state, { schema_id: STATE_SCHEMA, daemon_id: config.daemonId, shutdown_state: "starting", active_tree: false, unclaimed_activity: retained.unclaimed_activity ?? false, retained_tree_settlement: retained.retained_tree_settlement ?? null, versions: { agy: version }, config, sessions: [], session_observations: retained.session_observations ?? [], descendants: retained.descendants ?? [], native_resources_entries: retained.native_resources_entries ?? [], native_topology_error: retained.native_topology_error ?? null, ...(Array.isArray(retained.tool_observations) ? { tool_observations: retained.tool_observations } : {}), tool_observation_gaps: retained.tool_observation_gaps ?? [], tool_calls: retained.tool_calls ?? [], completed_tool_steps: retained.completed_tool_steps ?? [], tool_identity_complete: retained.tool_identity_complete !== false, turn_generation: retained.turn_generation ?? 0, last_result: retained.last_result ?? null, resource_process: resourceProcess });
297
+ const log = await open(paths.log, "a", 0o600), child = spawn(process.execPath, [config.entryPath, "daemon", "serve", "--state-dir", paths.dir], { cwd: config.cwd, env: sanitizedEnv(config), detached: true, stdio: ["ignore", log.fd, log.fd] }); child.unref(); await log.close();
298
+ try {
299
+ await confirmDaemonProcess(config, resourceProcess, child);
300
+ const startupClock = new ObservationClock({ timeoutMs: SETUP_STARTUP_MS }); const startupProgress = startupObserver(paths.dir, config.daemonId); while (!startupClock.sample(await startupProgress())) { await new Promise(resolve => setTimeout(resolve, 100));
301
+ const persisted = await readState(paths.dir);
302
+ if (persisted?.shutdown_state === "failed") {
303
+ const failure = persisted.startup_error ?? {};
304
+ throw new DaemonError(failure.code ?? "daemon_start_failed", failure.message ?? "Antigravity daemon failed before becoming ready", failure.retryable === true, failure.details);
305
+ }
306
+ try { const status = await callDaemon(paths.dir, "daemon.status", {}, 1000); if (status.provider_ready) return status; } catch (error) { if (!["daemon_not_running","daemon_timeout"].includes(error.code)) throw error; }
307
+ }
308
+ throw Object.assign(new DaemonError("daemon_start_failed", "Antigravity daemon did not become ready", true), { details: observationDetails(startupClock, 'daemon-start', child.pid) });
309
+ } catch (error) {
310
+ await cleanupFailedDaemonStart(config, resourceProcess, child, error, () => readState(paths.dir), config.daemonId);
311
+ throw error;
312
+ }
313
+ }
314
+ export const stopDaemon = async options => { const timeoutMs = options.timeoutMs ?? 30_000; return await confirmDaemonStopped(() => callDaemon(options.stateDir, "daemon.stop", { cancelTree: options.cancelTree === true }, timeoutMs), locations(options.stateDir).socket, timeoutMs, { readState: () => readState(options.stateDir) }); };
315
+
316
+ export class Runtime {
317
+ constructor(paths, state) { this.paths = paths; this.state = state; this.config = state.config; this.child = null; this.providerProcess = state.provider_process ?? null; this.providerFinalization = Promise.resolve(); this.buffer = ""; this.init = null; this.readyPromise = null; this.active = null; this.exited = false; this.toolObservations = new ToolObservations(); for (const reason of state.tool_observation_gaps ?? []) this.toolObservations.gaps.add(reason); for (const event of state.tool_observations ?? []) this.toolObservations.add(event); if (state.tool_calls?.length && !state.tool_observations) this.toolObservations.gaps.add("legacy_tools_not_attributed"); this.turnGeneration = state.turn_generation ?? 0; this.descendants = new Map((state.descendants ?? []).map(child => [child.provider_session_id, child])); this.lastResult = state.last_result ?? null; this.sessionObservations = new Map(state.session_observations ?? []); this.startupError = null; this.persisting = Promise.resolve(); this.draining = Promise.resolve(); this.lastActivityAt = state.last_activity_at ?? null; this.usageIngest = null; this.lastProviderEvent = null;
318
+ const saved = state.native_observation_episode;
319
+ if (saved?.clock) {
320
+ this.observationEpisode = { operationId: saved.operation_id, generation: saved.turn_generation, connectionId: saved.connection_id, terminalMetadata: state.last_terminal_metadata,
321
+ clock: new ObservationClock({ timeoutMs: saved.clock.timeout_ms, saved: saved.clock }) };
322
+ this.connectionId = saved.connection_id;
323
+ }
324
+ }
325
+ persist(patch = {}) { Object.assign(this.state, patch, { native_observation_episode: this.episodeSnapshot(), pid: process.pid, updated_at: new Date().toISOString(), last_activity_at: this.lastActivityAt, session_observations: [...this.sessionObservations], tool_observations: [...this.toolObservations.calls.values(), ...this.toolObservations.sources.values()], tool_observation_gaps: [...this.toolObservations.gaps], turn_generation: this.turnGeneration, descendants: [...this.descendants.values()], sessions: this.init ? [{ provider_session_id: this.init.conversation_id, root_provider_session_id: this.init.conversation_id, cwd: this.config.cwd }] : [] }); const snapshot = structuredClone(this.state); const queued = performance.now(); this.persisting = this.persisting.catch(() => {}).then(async () => { const started = performance.now(); await writeJson(this.paths.state, snapshot); this.persistTiming = { queue_ms: Math.round(started - queued), write_ms: Math.round(performance.now() - started) }; }); return this.persisting; }
326
+ async journal(kind, payload = {}, reservedPosition = null) { const position = reservedPosition ?? (this.journalPosition = (this.journalPosition ?? 0) + 1); const line = `${JSON.stringify({ schema_id: "dd-agy/daemon-event@1", event_id: randomUUID(), position, observed_at: new Date().toISOString(), kind, ...payload })}\n`; for (const journal of new Set([this.paths.journal, this.config.journal].filter(Boolean))) { await mkdir(path.dirname(journal), { recursive: true, mode: 0o700 }); await appendFile(journal, line, { mode: 0o600 }); } return position; }
327
+ args(sessionId) { return ["--input-format", "stream-json", "--output-format", "stream-json", ...agyModelArgs(this.config.model, this.config.reasoning), "--mode", this.config.mode, "--dangerously-skip-permissions", "--print-timeout", "0", ...(this.nativeLogFile ? ['--log-file', this.nativeLogFile] : []), ...(this.nativeCsrfToken ? [`--csrf_token=${this.nativeCsrfToken}`] : []), `--gemini_dir=${this.paths.gemini}`, "--app_data_dir=runtime", ...(sessionId ? ["--conversation", sessionId] : ["--new-project"])]; }
328
+ requireSessionIdentity(requestedId) {
329
+ const observedId = this.init?.conversation_id ?? null;
330
+ if (!requestedId || requestedId === observedId) return observedId;
331
+ throw new DaemonError("session_identity_mismatch", "requested Antigravity Session is not owned by this daemon", false, {
332
+ harness: "antigravity-cli", expected_session_id: requestedId, observed_session_id: observedId
333
+ });
334
+ }
335
+ markActivity() { const current = this.active ?? this.observationEpisode; this.nativeRevision = (this.nativeRevision ?? 0) + 1; this.state.native_execution_revision = this.nativeRevision; this.nativeProof = null; this.nativeAbort?.abort(); this.lastActivityAt = new Date().toISOString(); this.progress = { cursor: (this.progress?.cursor ?? 0) + 1, observed_at: Date.now() }; current?.clock?.sample(this.progress); void current?.publishProgress?.('native_progress', { session_id: this.init?.conversation_id, observed_at: this.lastActivityAt }).catch(error => { if(this.active === current) current.reject(error); }); }
336
+ markTerminalActivity(result) {
337
+ const current = this.active;
338
+ if (!current) return;
339
+ const key = createHash("sha256").update(JSON.stringify({ conversation_id: result.conversation_id ?? this.init?.conversation_id, status: result.status, num_turns: result.num_turns, error: result.error, response: result.response ?? result.text })).digest("hex");
340
+ current.terminalProgress ??= new Set();
341
+ if (current.terminalProgress.has(key)) return;
342
+ current.terminalProgress.add(key); this.markActivity();
343
+ }
344
+ observeProviderActivity(event) {
345
+ const owner = this.active ?? this.observationEpisode;
346
+ if (!owner || owner.connectionId !== this.connectionId || owner.generation !== this.turnGeneration) return;
347
+ const root = this.init?.conversation_id, step = event.step_update;
348
+ const id = step?.conversation_id ?? event.result?.conversation_id ?? root;
349
+ let parent = id; const seen = new Set();
350
+ while (parent && parent !== root && !seen.has(parent)) { seen.add(parent); const child = this.descendants.get(parent); if (child?.activity_generation !== owner.generation) return; parent = child.parent_provider_session_id; }
351
+ if (!root || parent !== root) return;
352
+ if (event.event === "result") { if (["SUCCESS", "ERROR", "CANCELLED"].includes(event.result?.status)) this.markTerminalActivity(event.result); return; }
353
+ if (event.event !== "step_update") return;
354
+ const index = step?.step_index ?? (/^\d+$/.test(step?.step_id ?? "") ? Number(step.step_id) : null);
355
+ if (!Number.isInteger(index)) return;
356
+ const floor = this.sessionObservations.get(id)?.step_floor;
357
+ if (floor != null && index <= floor) return;
358
+ const tool = step.tool_call ?? step.tool_info ?? (step.step_type === "tool" ? step.tool_name : null);
359
+ if (!step.content && !step.text && !step.thought && !tool && !step.error && !(step.subagent_info?.subagents?.length)) return;
360
+ const fingerprint = createHash("sha256").update(JSON.stringify({ content: step.content, text: step.text, thought: step.thought, tool_call: step.tool_call, tool_info: step.tool_info, tool_name: step.tool_name, ...(tool ? { state: step.state, status: step.status } : {}), error: step.error, subagent_info: step.subagent_info })).digest("hex");
361
+ this.progressSteps ??= new Map(); const prior = this.progressSteps.get(id);
362
+ if (prior && index < prior.index) return;
363
+ const snapshots = prior?.index === index ? prior.seen : new Set();
364
+ if (snapshots.has(fingerprint)) return;
365
+ // Native step snapshots can alternate/replay; scope retained dedup to the
366
+ // current step. Unique later updates remain useful activity at any size.
367
+ snapshots.add(fingerprint); this.progressSteps.set(id, { index, seen: snapshots }); this.invalidateSettlement(id); this.markActivity();
368
+ }
369
+ async start(sessionId = null) {
370
+ if (this.drainingError) throw this.drainingError;
371
+ assertDaemonDispatch(this.paths?.dir);
372
+ if (this.child && !this.exited) {
373
+ await this.readyPromise;
374
+ this.requireSessionIdentity(sessionId);
375
+ return this.init;
376
+ }
377
+ await this.providerFinalization;
378
+ if (this.drainingError) throw this.drainingError;
379
+ assertDaemonDispatch(this.paths?.dir);
380
+ this.nativeAbort?.abort(); this.nativeProof = null;
381
+ this.nativeLogFile = path.join(this.paths.dir, `native-${randomUUID()}.log`);
382
+ this.nativeCsrfToken = randomUUID();
383
+ this.admissionClock = null;
384
+ this.nativeQualification = await readAgyNativeQualification(this.paths.dir, this.config.bin, this.config.nativeQualificationPath);
385
+ const target = executable(this.config.bin, this.args(sessionId)); this.exited = false; this.buffer = ""; this.init = null;
386
+ let readyResolve, readyReject; this.readyPromise = new Promise((resolve, reject) => { readyResolve = resolve; readyReject = reject; });
387
+ // The provider can reject before durable registration finishes. Keep a
388
+ // handler attached during that small window; `start()` still returns the
389
+ // original rejection below.
390
+ void this.readyPromise.catch(() => {});
391
+ this.providerProcess = await registerDaemonProcess(this.config, { kind: "agy-provider", owner: `agy:${path.basename(this.paths.dir)}`, operation: `agy-provider:${path.basename(this.paths.dir)}`, stdout: this.paths.log, stderr: this.paths.log });
392
+ const providerProcess = this.providerProcess;
393
+ try { await this.persist({ provider_process: providerProcess }); }
394
+ catch (error) { await cleanupFailedStart(this.config, providerProcess, null, error); throw error; }
395
+ try { assertDaemonDispatch(this.paths?.dir); this.connectionId = randomUUID(); this.child = spawn(target.command, target.args, { cwd: this.config.cwd, env: sanitizedEnv(this.config), detached: true, stdio: ["pipe", "pipe", "pipe"] }); }
396
+ catch (error) { await cleanupFailedStart(this.config, providerProcess, null, error); throw error; }
397
+ const providerChild = this.child;
398
+ this.nativeLaunch = { logFile: this.nativeLogFile, providerPid: providerChild.pid, providerBirth: (await processSnapshot()).find(item => item.pid === providerChild.pid)?.started, connectionId: this.connectionId };
399
+ let allowProviderFinalization;
400
+ const providerRegistered = new Promise(resolve => { allowProviderFinalization = resolve; });
401
+ const observationFailure = error => { this.drainingError ??= error; readyReject(error); this.active?.reject(error); };
402
+ this.nativeDrain = readNativeLines(this.child.stdout, line => { this.buffer = `${line}\n`; return this.draining = this.drain(readyResolve, readyReject); }, observationFailure);
403
+ const diagnosticConnection = this.connectionId;
404
+ this.diagnostics = readNativeLines(this.child.stderr, async line => {
405
+ const diagnostic = agyErrorDiagnostic(line);
406
+ if (diagnostic) await this.journal("provider_error_diagnostic", { ...diagnostic, connection_id: diagnosticConnection });
407
+ else if (!/^AGY_ERROR\s*[: ]/.test(line)) await this.journal("stderr", { text: line });
408
+ }, observationFailure);
409
+ this.child.on("error", error => { readyReject(error); if (this.active) { this.active.reject(error); this.active = null; } });
410
+ // A dead leader is not yet a finalized tree: stop must also join the
411
+ // pending drain, process-group check and final persistence/lease writes.
412
+ this.providerFinalization = new Promise((resolve, reject) => {
413
+ this.child.on("close", (code, signal) => {
414
+ void Promise.all([this.nativeDrain, this.diagnostics]).then(async () => {
415
+ const interrupted = Boolean(this.active); this.exited = true; const beforeInit = !this.init;
416
+ const error = this.startupError ?? new DaemonError(beforeInit ? "agy_init_missing" : "agy_terminal_result_missing", `Antigravity exited ${beforeInit ? "before init" : "before result"} (${code ?? signal})`);
417
+ if (beforeInit) readyReject(error);
418
+ if (this.active) { this.active.reject(error); this.active = null; }
419
+ const patch = beforeInit ? { shutdown_state: "failed", startup_error: errorPayload(error), provider_exit: { code, signal }, active_tree: false } : { provider_exit: { code, signal }, active_tree: interrupted };
420
+ await providerRegistered;
421
+ this.retryProviderFinalization = async (graceMs = 1_000) => {
422
+ try {
423
+ await stopProcessGroup(providerChild, graceMs);
424
+ await this.persist(patch);
425
+ await finishDaemonProcess(this.config, providerProcess, beforeInit || interrupted ? "failed" : "stopped", beforeInit || interrupted ? "provider_interrupted" : "provider_stopped");
426
+ this.retryProviderFinalization = null;
427
+ } catch (cleanup) { await this.persist({ shutdown_state: "cleanup_failed", active_tree: true, cleanup_error: errorPayload(cleanup) }); throw cleanup; }
428
+ finally { if (beforeInit) this.server?.close(); }
429
+ };
430
+ await this.retryProviderFinalization();
431
+ }).then(resolve, reject);
432
+ });
433
+ });
434
+ void this.providerFinalization.catch(() => {});
435
+ try { await confirmDaemonProcess(this.config, providerProcess, this.child); await this.persist({ provider_pid: this.child.pid }); scheduleDaemonProcessHeartbeat(this.config, providerProcess); }
436
+ catch (error) { await cleanupFailedStart(this.config, providerProcess, this.child, error); throw error; }
437
+ finally { allowProviderFinalization(); }
438
+ let readyTimer;
439
+ try { return await Promise.race([this.readyPromise, new Promise((_, reject) => { readyTimer = observedTimeout(() => reject(new DaemonError("agy_init_timeout", "Antigravity init timed out", true)), SETUP_STARTUP_MS, { progress: () => this.lastActivityAt }); })]); }
440
+ finally { clearTimeout(readyTimer); }
441
+ }
442
+ async drain(readyResolve, readyReject) { let index; while ((index = this.buffer.indexOf("\n")) >= 0) { const line = this.buffer.slice(0, index).trim(); this.buffer = this.buffer.slice(index + 1); if (!line) continue; let event; try { event = JSON.parse(line); } catch { await this.journal("invalid_json", { line: line.slice(0, 1000) }); continue; }
443
+ // Record an early provider rejection before journalling: a short-lived
444
+ // child can close while the asynchronous write is still pending.
445
+ if (event.event === "result" && ["ERROR", "CANCELLED"].includes(event.result?.status)) {
446
+ this.lastTerminalFailure ??= this.state.last_terminal_failure;
447
+ event.terminal_metadata = { observed_at: new Date().toISOString(), provider_session_id: event.result.conversation_id || this.init?.conversation_id || null,
448
+ operation_id: this.active?.operationId ?? null, turn_generation: this.active?.generation ?? null, journal_locator: this.paths.journal, native_source: "agy.result" };
449
+ if (!this.init) this.startupError ??= agyTerminalFailure(event.result, event.terminal_metadata);
450
+ this.receivedTerminalMetadata = event.terminal_metadata;
451
+ }
452
+ this.observeProviderActivity(event);
453
+ if (event.event === "result" && this.init && !this.startupError) { await this.finishTurn(event.result ?? {}, event.terminal_metadata, event); continue; }
454
+ await this.journal("provider_event", { event }); if (event.event === "init") { this.init = { conversation_id: event.conversation_id, ...(event.init ?? {}) }; if (!this.init.conversation_id) throw new DaemonError("agy_init_missing", "Antigravity init has no conversation ID"); const requested = { provider: this.config.provider, model: this.config.model, reasoning: this.config.reasoning, mode: this.config.mode, permission_mode: "always-proceed" }; this.profile = assertProfile(requested, observedProfile(this.init, requested)); await observeModel({ journal: this.config.journal, harness: "antigravity-cli", sessionId: this.init.conversation_id, requested, observed: this.profile.observed, source: "native.init" }); await this.persist({ shutdown_state: "running", active_tree: false }); readyResolve(this.init); } else if (event.event === "step_update") this.observeStep(event.step_update ?? {}); else if (event.event === "result") {
455
+ // A successfully read/journalled provider refusal rejects readiness, not
456
+ // the stream drain that physical cleanup must still join.
457
+ if (this.startupError) { readyReject(this.startupError); continue; }
458
+ await this.finishTurn(event.result ?? {});
459
+ } } }
460
+ observeStep(step) {
461
+ const sessionId = step.conversation_id ?? this.init?.conversation_id, observation = this.sessionObservations.get(sessionId) ?? {};
462
+ const fresh = Number.isInteger(step.step_index) && step.step_index > (observation.last_step_index ?? -1) && step.step_index > (observation.step_floor ?? -1);
463
+ const bound = sessionId === this.init?.conversation_id || this.descendants.get(sessionId)?.activity_generation === this.active?.generation;
464
+ // Tree liveness is broader than current-Turn model progress: fresh work
465
+ // from a retained child invalidates settlement without adopting this Turn.
466
+ if (fresh && (sessionId === this.init?.conversation_id || this.descendants.has(sessionId))) this.childActivity(sessionId);
467
+ if (Number.isInteger(step.step_index)) this.sessionObservations.set(sessionId, { ...this.sessionObservations.get(sessionId), ...(step.step_index > (observation.last_step_index ?? -1) ? { last_execution_num: null } : {}), last_step_index: Math.max(observation.last_step_index ?? -1, step.step_index) });
468
+ this.toolObservations.add(agyToolObservation(step, this.init?.conversation_id));
469
+ for (const child of step.subagent_info?.subagents ?? []) if (child.conversation_id) {
470
+ const prior = this.descendants.get(child.conversation_id);
471
+ if (child.conversation_id === this.init?.conversation_id || prior && prior.parent_provider_session_id !== sessionId) {
472
+ this.state.native_topology_error = 'agy_native_ancestry_contradiction'; this.invalidateSettlement(sessionId); continue;
473
+ }
474
+ this.descendants.set(child.conversation_id, { ...prior, provider_session_id: child.conversation_id, parent_provider_session_id: sessionId ?? null, role: child.role ?? null, subagent_type: child.type_name ?? null, log_uri: child.log_uri ?? null, workspace_uris: child.workspace_uris ?? [], status: prior?.status ?? "unknown",
475
+ ...(bound && Number.isInteger(step.step_index) ? { native_source: { kind: 'agy.step_update.subagent_info', parent_provider_session_id: sessionId, step_index: step.step_index } } : {}),
476
+ ...(fresh && bound && this.active ? { activity_generation: this.active.generation } : {}) });
477
+ }
478
+ }
479
+ toolSnapshot() { const summary = this.toolObservations.summary(); const failures_by_name = {}; for (const event of this.toolObservations.calls.values()) if (event.status === "failed") failures_by_name[event.name] = (failures_by_name[event.name] ?? 0) + 1; return { ...summary, by_name: summary.by_tool, failures_by_name }; }
480
+ async captureChildTools(onProgress = null) {
481
+ this.capturedToolFacts ??= new Map();
482
+ this.nativeResources ??= new Map(this.state.native_resources_entries?.map(item => [item.resource_id, item]) ?? []);
483
+ const root = this.init?.conversation_id ?? this.state.sessions?.[0]?.provider_session_id ?? this.lastResult?.conversation_id;
484
+ const queue = root && this.paths.runtime ? [{ provider_session_id: root, parent_provider_session_id: root, log_uri: pathToFileURL(path.join(this.paths.runtime, 'brain', root, '.system_generated', 'logs', 'transcript.jsonl')).href }, ...this.descendants.values()] : [...this.descendants.values()];
485
+ const visited = new Set();
486
+ for (let index = 0; index < queue.length && index < 256; index++) {
487
+ const child = queue[index];
488
+ if (visited.has(child.provider_session_id)) continue;
489
+ visited.add(child.provider_session_id);
490
+ const ancestors = new Set(); let parent = child.parent_provider_session_id;
491
+ while (parent && parent !== root && this.descendants.has(parent) && !ancestors.has(parent)) {
492
+ ancestors.add(parent); parent = this.descendants.get(parent).parent_provider_session_id;
493
+ }
494
+ const events = parent === root && root
495
+ ? await captureAgyTranscript(this.paths.runtime, child, root, [root, ...ancestors], source => {
496
+ const reducer = createAgyTranscriptFacts(source, child.provider_session_id);
497
+ return { add: reducer.add, finish: () => {
498
+ const facts = reducer.finish();
499
+ for (const resource of facts.resources) {
500
+ const prior = this.nativeResources.get(resource.resource_id);
501
+ this.nativeResources.set(resource.resource_id, { ...mergeAgyResource(prior, resource),
502
+ owner_daemon_id: prior?.owner_daemon_id ?? this.config.daemonId });
503
+ }
504
+ if (facts.gap) { this.state.native_topology_error = facts.gap; this.invalidateSettlement(root); }
505
+ for (const discovered of facts.children) {
506
+ const expected = path.join(this.paths.runtime, 'brain', discovered.provider_session_id, '.system_generated', 'logs', 'transcript.jsonl');
507
+ const sourceBrain = path.resolve(path.dirname(fileURLToPath(source.locator)), '../../..');
508
+ const sibling = path.join(sourceBrain, discovered.provider_session_id, '.system_generated', 'logs', 'transcript.jsonl');
509
+ let locator;
510
+ try { locator = fileURLToPath(discovered.log_uri); } catch { locator = null; }
511
+ if (locator !== expected && locator !== sibling) { this.state.native_topology_error = 'agy_native_locator_unbound'; this.invalidateSettlement(root); continue; }
512
+ const prior = this.descendants.get(discovered.provider_session_id);
513
+ if (discovered.provider_session_id === root || prior && (prior.parent_provider_session_id !== discovered.parent_provider_session_id || prior.log_uri && prior.log_uri !== discovered.log_uri)) {
514
+ this.state.native_topology_error = 'agy_native_ancestry_contradiction'; this.invalidateSettlement(root); continue;
515
+ }
516
+ const value = { ...discovered, ...prior, native_source: discovered.native_source };
517
+ this.descendants.set(value.provider_session_id, value);
518
+ queue.push(value);
519
+ }
520
+ } };
521
+ }, onProgress && (cursor => onProgress(`${child.provider_session_id}:${cursor}`)))
522
+ : [{ session: { harness_id: "antigravity-cli", session_id: child.provider_session_id }, reason: "agy_transcript_ancestry_unproven" }];
523
+ for (const event of events) {
524
+ this.toolObservations.add(event);
525
+ const key = JSON.stringify([event.session, event.call_id, event.reason]), encoded = JSON.stringify(event);
526
+ if (this.capturedToolFacts.get(key) === encoded) continue;
527
+ await this.journal("tool_observation", { schema_id: "dd-flow/tool-observation@1", event });
528
+ this.capturedToolFacts.set(key, encoded);
529
+ }
530
+ }
531
+ if (queue.length > 256 && visited.size < new Set(queue.map(item => item.provider_session_id)).size) { this.state.native_topology_error = 'agy_native_inventory_limit'; this.invalidateSettlement(root); }
532
+ this.state.native_resources_entries = [...this.nativeResources.values()];
533
+ }
534
+ async finishTurn(result, retainedMetadata = null, providerEvent = null) {
535
+ if (result.status === "RUNNING") { await this.persist({ active_tree: true }); return; }
536
+ if (!["SUCCESS", "ERROR", "CANCELLED"].includes(result.status)) throw new DaemonError("agy_terminal_result_invalid", "Antigravity result has no recognized terminal status", false, { provider_result: result });
537
+ if (result.conversation_id && result.conversation_id !== this.init?.conversation_id) {
538
+ await this.journal("foreign_terminal_observed", { conversation_id: result.conversation_id, result });
539
+ const child = this.descendants.get(result.conversation_id);
540
+ if (child) {
541
+ this.descendants.set(result.conversation_id, { ...child, status: result.status === "SUCCESS" ? "completed" : result.status === "ERROR" ? "failed" : "cancelled" });
542
+ await this.completePendingTurn();
543
+ }
544
+ return;
545
+ }
546
+ const current = this.active;
547
+ if (current?.dispatched === false) {
548
+ const error = new DaemonError("operation_observation_lost", "Unexpected native terminal before this stream request was dispatched", false, { operation_id: current.operationId, turn_generation: current.generation, native_outcome: "unknown" });
549
+ this.active = null; current.reject(error);
550
+ await this.journal("unexpected_terminal_before_dispatch", { result, operation_id: current.operationId });
551
+ await this.persist({ unclaimed_activity: true, active_tree: true });
552
+ return;
553
+ }
554
+ const terminalMetadata = { received_at: new Date().toISOString(), ...(retainedMetadata ?? this.receivedTerminalMetadata ?? { observed_at: new Date().toISOString(), provider_session_id: result.conversation_id || this.init?.conversation_id || null, operation_id: current?.operationId ?? null,
555
+ turn_generation: current?.generation ?? null, journal_locator: this.paths.journal, native_source: "agy.result" }) };
556
+ this.receivedTerminalMetadata = null;
557
+ // The ordered stream returns one result per input. Model usage counters
558
+ // are not request IDs: native command rejections can report zero turns.
559
+ // prompt() drains earlier events before accepting another input.
560
+ if (!current && this.lastResult) {
561
+ await this.journal("stale_terminal_observed", { result });
562
+ if (!isDeepStrictEqual(result, this.lastResult)) await this.persist({ unclaimed_activity: true, active_tree: true });
563
+ return;
564
+ }
565
+ this.lastResult = result; this.markTerminalActivity(result);
566
+ if (current) { current.terminalResult = result; current.terminalMetadata = terminalMetadata; current.terminalPersisted = false; }
567
+ this.state.terminal_observation_pending = true;
568
+ try {
569
+ await this.persist({ active_tree: true, last_result: result, last_terminal_metadata: terminalMetadata });
570
+ if (current) current.terminalPersisted = true;
571
+ const terminalPosition = await this.journal("provider_event", { event: providerEvent ?? { event: "result", result } });
572
+ terminalMetadata.stream_terminal_position = terminalPosition;
573
+ if (current?.outboundPosition && current.dispatchJournalSettled && !current.dispatchObservationError && this.config.daemonId && current.connectionId && current.operationId) terminalMetadata.request_proof = {
574
+ kind: "stream_request", daemon_instance: this.config.daemonId, connection_id: current.connectionId, operation_id: current.operationId,
575
+ turn_generation: current.generation, session_id: this.init.conversation_id, outbound_position: current.outboundPosition, terminal_position: terminalPosition
576
+ };
577
+ await this.captureChildTools();
578
+ } catch (error) { return this.failTerminalObservation(current, result, terminalMetadata, error); }
579
+ try { this.usageIngest = await forwardUsage(this.config, result.conversation_id ?? this.init.conversation_id, result, this.toolSnapshot(), [...this.descendants.values()]); }
580
+ catch (error) { this.usageIngest = { status: "failed", error: error.message ?? String(error) }; try { await this.journal("usage_ingest_failed", this.usageIngest); } catch (storage) { return this.failTerminalObservation(current, result, terminalMetadata, storage); } }
581
+ try { await this.persist({ terminal_observation_pending: false }); }
582
+ catch (error) { this.state.terminal_observation_pending = true; return this.failTerminalObservation(current, result, terminalMetadata, error); }
583
+ this.state.terminal_observation_pending = false;
584
+ this.queueNativeInspection();
585
+ if (!current) return;
586
+ if (this.active !== current) return;
587
+ if (result.status === "ERROR" || result.status === "CANCELLED") {
588
+ await this.observeHookFailures();
589
+ if (this.active !== current) return;
590
+ this.active = null;
591
+ const settled = this.receipt().settled;
592
+ this.lastTerminalFailure = agyTerminalFailure(result, { ...terminalMetadata, native_turn_items: { observed: settled, possible_effects: true, pending: !settled }, provider_error_scope: "terminal_result", usage_ingest: this.usageIngest });
593
+ try { await this.persist({ active_tree: !settled, last_terminal_failure: errorRecord(this.lastTerminalFailure) }); }
594
+ catch (error) { return this.failTerminalObservation(current, result, terminalMetadata, error); }
595
+ return current.reject(this.lastTerminalFailure);
596
+ }
597
+ try { await this.completePendingTurn(); }
598
+ catch (error) { return this.failTerminalObservation(current, result, terminalMetadata, error); }
599
+ }
600
+ async completePendingTurn() {
601
+ await this.observeHookFailures();
602
+ const current = this.active;
603
+ if (current?.observationError) throw current.observationError;
604
+ if (current?.terminalResult?.status !== "SUCCESS" || current.terminalResult !== this.lastResult) return;
605
+ this.queueNativeInspection();
606
+ await this.persist({ active_tree: !this.receipt().settled });
607
+ // Hooks can invalidate the tree while persistence is pending. Never
608
+ // publish a proof captured before that asynchronous boundary.
609
+ if (this.active !== current) return;
610
+ const receipt = this.receipt();
611
+ if (!receipt.settled) { await this.persist({ active_tree: true }); return; }
612
+ this.active = null;
613
+ current.resolve(receipt);
614
+ }
615
+ reconcilePendingHook(current) {
616
+ if (!current || this.active !== current) return Promise.resolve();
617
+ if (current.hookReconciliation) return current.hookReconciliation;
618
+ if (!current.pendingHookReconciliation) return Promise.resolve();
619
+ current.pendingHookReconciliation = false;
620
+ current.hookReconciliation = this.completePendingTurn().catch(error => {
621
+ current.observationError ??= error;
622
+ if (this.active === current) { this.drainingError ??= error; current.reject(error); }
623
+ }).finally(() => { current.hookReconciliation = null; });
624
+ return current.hookReconciliation;
625
+ }
626
+ failTerminalObservation(current, result, metadata, cause) {
627
+ this.state.terminal_observation_pending = true;
628
+ const details = { ...metadata, native_outcome: result, settled: false, terminal_persisted: current?.terminalPersisted === true, observation_error: errorRecord(cause), native_turn_items: { observed: false, possible_effects: true, pending: true } };
629
+ const error = ["ERROR", "CANCELLED"].includes(result.status)
630
+ ? agyTerminalFailure(result, details)
631
+ : new DaemonError("native_outcome_observation_failed", "Antigravity terminal was received but mandatory observation failed", false, details);
632
+ error.cause = cause;
633
+ if (result.status !== "SUCCESS") this.lastTerminalFailure = error;
634
+ if (current) { current.observationError = error; current.reject(error); }
635
+ throw error;
636
+ }
637
+ queueNativeInspection() {
638
+ const owner = this.active ?? this.observationEpisode, proof = owner?.terminalMetadata?.request_proof;
639
+ if (!owner?.clock || !proof || this.state.terminal_observation_pending || this.nativeFlight || this.nativeProof || this.exited) return;
640
+ if (proof.connection_id !== this.connectionId || proof.daemon_instance !== this.config.daemonId || proof.turn_generation !== this.turnGeneration || proof.operation_id !== owner.operationId || proof.session_id !== this.init?.conversation_id) { this.nativeInspection = { state: 'unproven', reason: 'native_owner_binding_changed' }; return; }
641
+ if (owner.clock.sample(this.progress)) { this.nativeInspection = { state: 'unproven', reason: 'observation_budget_exhausted' }; return; }
642
+ // The observer's raw wire result is never authority before P3 qualification.
643
+ if (!this.nativeObserver && !this.nativeQualification?.qualified) { this.nativeInspection = { state: 'unproven', reason: 'native_inspection_not_qualified' }; return; }
644
+ const revision = this.nativeRevision ?? 0, connection = this.connectionId;
645
+ const controller = this.nativeAbort = new AbortController();
646
+ let recording = false;
647
+ this.nativeInspection = { state: 'inspection_pending', reason: 'native_idle_barrier_pending' };
648
+ this.nativeFlight = Promise.resolve().then(async () => {
649
+ await owner.dispatchJournal;
650
+ const clock = owner.clock.state(), remainingMs = Math.max(0, clock.timeout_ms - clock.elapsed_ms);
651
+ if (!remainingMs || owner.clock.sample(this.progress)) throw new Error('observation_budget_exhausted');
652
+ const endpoint = this.nativeObserver ? null : await ownedAgyEndpoint(this.nativeLaunch);
653
+ const observed = await (this.nativeObserver ?? inspectAgyNative)({ endpoint, sessionId: proof.session_id, cwd: this.config.cwd, remainingMs, signal: controller.signal, rootRelation: this.nativeQualification?.rootRelation, csrfToken: this.nativeCsrfToken });
654
+ if (!this.nativeObserver && JSON.stringify(await ownedAgyEndpoint(this.nativeLaunch)) !== JSON.stringify(endpoint)) throw new Error('native_endpoint_changed');
655
+ const current = () => !controller.signal.aborted && this.connectionId === connection && (this.nativeRevision ?? 0) === revision && this.turnGeneration === owner.generation && this.observationEpisode === owner && !this.exited && !owner.clock.sample(this.progress);
656
+ if (!current()) return;
657
+ if (!observed.idle) { this.nativeInspection = { state: 'inspection_pending', reason: observed.reason ?? 'native_wait_pending' }; return; }
658
+ const candidate = { daemon_instance: this.config.daemonId, connection_id: connection, operation_id: owner.operationId, turn_generation: owner.generation, request_proof: proof, revision, method: observed.method, source: 'native_idle_barrier' };
659
+ recording = true;
660
+ await this.journal('native_tree_settlement', { proof: candidate });
661
+ if (!current()) return;
662
+ // Persist conservatively before publishing. A crash may require another
663
+ // read-only observation; no concurrent reader gets an uncommitted grant.
664
+ await this.persist({ active_tree: true });
665
+ if (!current()) return;
666
+ this.nativeProof = candidate;
667
+ this.state.native_execution_revision = revision;
668
+ this.state.unclaimed_activity = false;
669
+ this.projectNativeIdleChildren();
670
+ this.nativeInspection = { state: 'ready', reason: 'native_wait_fully_idle' };
671
+ this.state.active_tree = !this.receipt().settled;
672
+ await this.completePendingTurn();
673
+ }).catch(error => {
674
+ if (!controller.signal.aborted) {
675
+ this.nativeProof = null;
676
+ const transient = !recording && ['grpc_4', 'grpc_14'].includes(error.details?.reason) && !owner.clock.sample(this.progress);
677
+ this.nativeInspection = { state: transient ? 'inspection_pending' : 'unproven', reason: error.details?.reason ?? error.message ?? 'native_inspection_failed' };
678
+ if (recording && this.active === owner && owner.terminalResult) {
679
+ try { this.failTerminalObservation(owner, owner.terminalResult, owner.terminalMetadata, error); } catch { /* original prompt owns the typed failure */ }
680
+ }
681
+ if (recording) this.state.terminal_observation_pending = true;
682
+ }
683
+ })
684
+ .finally(() => { this.nativeFlight = null; });
685
+ }
686
+ projectNativeIdleChildren() {
687
+ for (const [id, child] of this.descendants) this.descendants.set(id, { ...child, ...(!['completed', 'failed', 'cancelled'].includes(child.status) ? { previous_native_status: child.status, status: 'settled_by_root' } : {}), ...(child.native_turn_items ? { native_turn_items: { ...child.native_turn_items, observed: true, pending: false } } : {}), tree_settled: true, settlement_evidence: 'native_idle_barrier' });
688
+ }
689
+ async reconcileRetainedAdmission() {
690
+ const retained = this.state.retained_tree_settlement;
691
+ const fresh = !this.lastResult;
692
+ if (this.active || this.receipt().settled || !fresh && (retained?.evidence !== 'verified_clean_daemon_shutdown' || retained.session_id !== this.init?.conversation_id)) return;
693
+ if (!this.nativeObserver && !this.nativeQualification?.qualified) return;
694
+ if (this.admissionFlight) return await this.admissionFlight;
695
+ const clock = this.admissionClock ??= new ObservationClock({ timeoutMs: SETUP_STARTUP_MS });
696
+ if (!clock || clock.sample()) throw new DaemonError('agy_native_inspection_unavailable', 'Retained AGY native admission budget exhausted');
697
+ const connection = this.connectionId, revision = this.nativeRevision ?? 0, generation = this.turnGeneration;
698
+ const controller = this.nativeAbort = new AbortController();
699
+ this.admissionFlight = (async () => {
700
+ const endpoint = this.nativeObserver ? null : await ownedAgyEndpoint(this.nativeLaunch);
701
+ const observed = await (this.nativeObserver ?? inspectAgyNative)({ endpoint, sessionId: this.init.conversation_id, cwd: this.config.cwd, remainingMs: Math.max(1, clock.timeoutMs - clock.elapsed), signal: controller.signal, rootRelation: this.nativeQualification?.rootRelation, csrfToken: this.nativeCsrfToken });
702
+ if (!this.nativeObserver && JSON.stringify(await ownedAgyEndpoint(this.nativeLaunch)) !== JSON.stringify(endpoint)) throw new DaemonError('agy_native_inspection_unavailable', 'AGY admission endpoint changed');
703
+ const current = () => !controller.signal.aborted && !this.exited && !this.active && !clock.sample() && this.connectionId === connection && this.turnGeneration === generation && (this.nativeRevision ?? 0) === revision;
704
+ if (!observed.idle || !current()) throw new DaemonError('agy_native_inspection_unavailable', 'AGY fresh native admission is unproven');
705
+ // Admission is not the old stream request: no invented operation/Turn or
706
+ // request proof. The old failed result remains historical diagnostics.
707
+ const candidate = { daemon_instance: this.config.daemonId, connection_id: connection, turn_generation: generation, revision, method: observed.method, source: 'native_idle_admission' };
708
+ await this.journal('native_idle_admission', { proof: candidate });
709
+ if (!current()) throw new DaemonError('agy_native_inspection_unavailable', 'AGY native admission changed during observation persistence');
710
+ await this.persist({ active_tree: true });
711
+ if (!current()) throw new DaemonError('agy_native_inspection_unavailable', 'AGY native admission changed during observation persistence');
712
+ this.nativeProof = candidate;
713
+ this.state.native_execution_revision = revision;
714
+ this.state.unclaimed_activity = false; this.projectNativeIdleChildren();
715
+ this.state.active_tree = !this.receipt().settled;
716
+ })().catch(error => { this.nativeProof = null; this.nativeInspection = { state: 'unproven', reason: error.details?.reason ?? error.code ?? 'native_admission_unproven' }; throw error; }).finally(() => { this.admissionFlight = null; });
717
+ return await this.admissionFlight;
718
+ }
719
+ episodeSnapshot() {
720
+ const owner = this.observationEpisode;
721
+ return owner?.clock ? { operation_id: owner.operationId, turn_generation: owner.generation, connection_id: owner.connectionId, clock: owner.clock.state() } : null;
722
+ }
723
+ receipt(result = this.lastResult) {
724
+ if (this.state.last_terminal_failure && !this.lastTerminalFailure) this.lastTerminalFailure = this.state.last_terminal_failure;
725
+ const metadata = this.active?.terminalResult === result ? this.active.terminalMetadata : this.state.last_result === result ? this.state.last_terminal_metadata : this.lastTerminalFailure && this.lastTerminalFailure.details?.provider_result?.error === result?.error
726
+ && this.lastTerminalFailure.details.provider_session_id === (result?.conversation_id || this.init?.conversation_id || null) ? this.lastTerminalFailure.details : null;
727
+ const terminalMetadata = metadata?.request_proof && (metadata.request_proof.connection_id !== this.connectionId || metadata.request_proof.daemon_instance !== this.config.daemonId)
728
+ ? { ...metadata, request_proof: undefined } : metadata;
729
+ const assistantText = typeof result?.response === "string" ? result.response : typeof result?.text === "string" ? result.text : null;
730
+ const root = this.init?.conversation_id; const observation = this.sessionObservations.get(root);
731
+ const treeSettled = [...this.descendants.values()].every(child => child.tree_settled === true || ["completed", "failed", "cancelled", "settled_by_root"].includes(child.status));
732
+ // SUCCESS can coexist with background tasks. Only a current native idle
733
+ // proof (or verified prior clean retirement before new activity) closes it.
734
+ const retained = this.state.retained_tree_settlement;
735
+ const rootSettled = this.nativeProof?.turn_generation === this.turnGeneration && this.nativeProof.connection_id === this.connectionId && this.nativeProof.revision === (this.nativeRevision ?? 0)
736
+ || retained?.evidence === "verified_clean_daemon_shutdown" && retained.session_id === root && retained.turn_generation === this.turnGeneration && !this.child;
737
+ const terminalPending = this.active?.terminalResult === result;
738
+ // A positively inspected owned scope before its first Turn has no model
739
+ // result to manufacture. Native admission alone proves this empty execution.
740
+ const emptyExecution = !result && this.turnGeneration === 0 && !this.active && !this.descendants.size && this.nativeProof?.source === 'native_idle_admission';
741
+ const executionTerminal = emptyExecution || result && ["SUCCESS", "ERROR", "CANCELLED"].includes(result.status);
742
+ const settled = Boolean(!this.state.native_topology_error && !this.state.terminal_observation_pending && !this.active?.observationError && !this.state.unclaimed_activity && (!this.active || terminalPending) && executionTerminal && treeSettled && rootSettled);
743
+ const clock = this.observationEpisode?.clock;
744
+ if (clock) clock.sample(this.progress);
745
+ const exhausted = clock && (clock.observationLost || clock.elapsed >= clock.timeoutMs);
746
+ const nativeSettlement = { confirmed: Boolean(settled), state: settled ? 'ready' : exhausted ? 'unproven' : this.nativeInspection?.state ?? 'unproven', reason: settled ? 'current_root_idle_proven' : exhausted ? 'observation_budget_exhausted' : this.nativeInspection?.reason ?? 'current_root_idle_proof_missing', capability: AGY_NATIVE_INSPECTION_CONTRACT, operation_id: terminalMetadata?.request_proof?.operation_id, request_key: streamRequestKey(terminalMetadata?.request_proof), provider_session_id: root, daemon_instance: this.config.daemonId, connection_epoch: this.connectionId, generation: this.turnGeneration, ...(this.nativeProof ? { source: this.nativeProof.source, method: this.nativeProof.method, proof_sha256: createHash('sha256').update(JSON.stringify(this.nativeProof)).digest('hex') } : {}), ...(clock ? { observation_clock: clock.state() } : {}) };
747
+ const hookDiagnostics = (this.state.native_hook_diagnostics ?? []).filter(item => item.turn_generation === this.turnGeneration);
748
+ return nativeExecutionReceipt({ harness: "antigravity-cli", runtime_family: "antigravity", provider_session_id: root ?? null, adapter_session_id: root ?? null, cwd: this.config.cwd, profile: this.profile, result, ...(terminalMetadata ? { terminal_metadata: terminalMetadata, ...(terminalMetadata.turn_id ? { terminal_turn_id: terminalMetadata.turn_id } : {}) } : {}), ...(assistantText === null ? {} : { assistant_text: assistantText }), ...(hookDiagnostics.length ? { native_hook_diagnostics: hookDiagnostics } : {}), usage: usageSnapshot(result ?? {}, this.toolSnapshot()), usage_ingest: this.usageIngest, descendants: [...this.descendants.values()], ...settledNativeTree(root, [...this.descendants.values()], settled, !this.state.unclaimed_activity && (!this.active || terminalPending) && rootSettled && ["SUCCESS", "ERROR", "CANCELLED"].includes(result?.status)), native_turn_items: { observed: Boolean(terminalMetadata?.request_proof), possible_effects: true, pending: !settled, settled, ...(terminalMetadata?.request_proof ? { request_proof: terminalMetadata.request_proof } : {}) }, transcript_path: observation?.transcript_path ?? null, settled, native_settled: settled, physical_stopped: this.exited === true, native_settlement: nativeSettlement, }, { daemonId: this.config.daemonId, generation: this.turnGeneration, revision: this.nativeRevision ?? 0, resources: { coverage: "partial", entries: [...(this.nativeResources?.values() ?? this.state.native_resources_entries ?? [])], ...(this.state.native_retirement_notifications?.length ? { pending_notifications: this.state.native_retirement_notifications } : {}) } });
749
+ }
750
+ async zeroUsage() { await forwardUsage(this.config, this.init.conversation_id, { usage: {} }, this.toolSnapshot(), []); }
751
+ async prompt(text, assertDispatch = captureDispatchGuard(this), operationId = null, livenessTimeoutMs = 1_800_000) {
752
+ validateObservationDuration(livenessTimeoutMs);
753
+ if (!text) throw new DaemonError("prompt_required", "prompt is required");
754
+ await this.start(); await this.draining;
755
+ if (!this.nativeObserver && !this.nativeQualification?.qualified && this.config.bin && this.paths.dir) this.nativeQualification = await readAgyNativeQualification(this.paths.dir, this.config.bin, this.config.nativeQualificationPath);
756
+ if (!this.nativeObserver && !this.nativeQualification?.qualified) throw new DaemonError('agy_native_inspection_unqualified', 'AGY whole-tree inspection requires a matching native qualification receipt before productive dispatch', false, { capability: AGY_NATIVE_INSPECTION_CONTRACT });
757
+ await this.reconcileRetainedAdmission();
758
+ if (this.active || this.promptObservation) throw new DaemonError("operation_in_progress", "an Antigravity turn or its mandatory dispatch observation is already running");
759
+ if (this.state.unclaimed_activity || ((this.state.active_tree || this.lastResult) && !this.receipt().settled)) throw new DaemonError("tree_not_settled", "the prior Antigravity agent tree is not fully settled");
760
+ let resolveTurn, rejectTurn;
761
+ const reply = new Promise((resolve, reject) => { resolveTurn = resolve; rejectTurn = reject; });
762
+ // Cancellation can reject while admission persistence is still pending.
763
+ void reply.catch(() => {});
764
+ this.connectionId ??= randomUUID();
765
+ const current = { resolve: resolveTurn, reject: rejectTurn, generation: ++this.turnGeneration, operationId, connectionId: this.connectionId, dispatched: false, guard: assertDispatch };
766
+ this.nativeAbort?.abort(); this.nativeProof = null; this.nativeInspection = null;
767
+ current.clock = new ObservationClock({ timeoutMs: livenessTimeoutMs, onGap: gap => { void this.journal('observation_gap', { gap, operation_id: operationId }).catch(() => {}); } });
768
+ this.observationEpisode = current;
769
+ let resolveDispatchJournal;
770
+ const dispatchJournal = current.dispatchJournal = new Promise(resolve => { resolveDispatchJournal = resolve; });
771
+ this.promptObservation = current;
772
+ const finishDispatchJournal = () => {
773
+ current.dispatchJournalSettled = true; resolveDispatchJournal();
774
+ if (current.observerReturned && this.promptObservation === current) this.promptObservation = null;
775
+ };
776
+ // Reserve before persistence so different operation IDs cannot overlap.
777
+ current.publishProgress = operationProgressReporter();
778
+ this.active = current;
779
+ this.lastNativeHookOperation = current;
780
+ // A callback's failure receipt may arrive after its Turn has returned.
781
+ // Keep its accepted request identity; it is an observation binding, not
782
+ // proof of which native Turn originally produced the callback.
783
+ this.hookRequests ??= new Map();
784
+ const root = this.init?.conversation_id, prior = this.sessionObservations.get(root) ?? {};
785
+ current.stepFloor = prior.last_step_index ?? -1;
786
+ this.sessionObservations.set(root, { ...prior, step_floor: prior.last_step_index ?? null, stop: null, last_hook: null });
787
+ this.lastProviderEvent = null; this.progressSteps = new Map(); this.progressHooks = new Map(); this.progress = undefined;
788
+ try {
789
+ await this.persist({ active_tree: true, last_hook_rejection: null });
790
+ assertDispatch();
791
+ assertDaemonDispatch(this.paths?.dir);
792
+ if (this.active !== current) throw new DaemonError("operation_cancelled", "prompt reservation was superseded");
793
+ current.outboundPosition = this.journalPosition = (this.journalPosition ?? 0) + 1;
794
+ current.dispatched = true;
795
+ this.child.stdin.write(`${JSON.stringify({ event: "user", message: { content: text } })}\n`, error => {
796
+ if (error) { this.drainingError ??= nativeObservationError(error); current.dispatchObservationError = this.drainingError; current.reject(this.drainingError); finishDispatchJournal(); }
797
+ else void Promise.resolve().then(() => this.journal("native_dispatch", { operation_id: operationId, provider_session_id: this.init?.conversation_id, turn_generation: current.generation, connection_id: current.connectionId }, current.outboundPosition)).then(finishDispatchJournal, cause => {
798
+ this.drainingError ??= nativeObservationError(cause); current.dispatchObservationError = this.drainingError;
799
+ current.reject(this.drainingError); finishDispatchJournal();
800
+ });
801
+ });
802
+ } catch (error) {
803
+ if (current.dispatched) { this.drainingError ??= nativeObservationError(error); error = this.drainingError; }
804
+ else if (this.active === current) this.active = null;
805
+ current.reject(error); finishDispatchJournal();
806
+ }
807
+ const clock = current.clock;
808
+ let checking = false;
809
+ const timer = setInterval(() => {
810
+ if (this.active === current && [...(this.hookRequests?.values() ?? [])].some(binding => binding.current && binding.current !== current)) {
811
+ void this.observeHookFailures().catch(() => { /* Retry the same retained request on the next observation. */ });
812
+ }
813
+ if (this.active === current && current.terminalResult && this.nativeInspection?.state === 'inspection_pending' && !this.nativeFlight) this.queueNativeInspection();
814
+ if (this.active === current && current.pendingHookReconciliation && !checking) {
815
+ checking = true;
816
+ void this.reconcilePendingHook(current).finally(() => { checking = false; });
817
+ return;
818
+ }
819
+ if (this.active !== current || checking || !clock.sample(this.progress)) return;
820
+ checking = true; const cursor = this.progress?.cursor;
821
+ void this.completePendingTurn().then(() => {
822
+ if (this.active !== current || this.progress?.cursor !== cursor || !clock.sample(this.progress)) return;
823
+ if (current.terminalResult) return this.failTerminalObservation(current, current.terminalResult, current.terminalMetadata, new Error("Native tree settlement was not confirmed within the productive silence window"));
824
+ current.reject(new DaemonError("operation_observation_lost", "Antigravity native outcome is unknown after the productive silence window", true, { operation_id: operationId, turn_generation: current.generation, native_outcome: "unknown", last_activity_at: this.lastActivityAt ?? null }));
825
+ }).catch(error => {
826
+ if (this.active === current && this.progress?.cursor === cursor) {
827
+ if (current.observationError) current.reject(current.observationError);
828
+ else if (current.terminalResult) { try { this.failTerminalObservation(current, current.terminalResult, current.terminalMetadata, error); } catch { /* reply owns the typed failure */ } }
829
+ else current.reject(new DaemonError("operation_observation_lost", "Antigravity quiet reconciliation could not confirm the native outcome", true, { operation_id: operationId, turn_generation: current.generation, native_outcome: "unknown", observation_error: errorRecord(error) }));
830
+ }
831
+ }).finally(() => { checking = false; });
832
+ }, Math.max(10, Math.min(1_000, livenessTimeoutMs / 4)));
833
+ try {
834
+ const result = await reply;
835
+ await dispatchJournal;
836
+ if (current.dispatchObservationError) {
837
+ const error = current.dispatchObservationError;
838
+ if (error.code !== "operation_observation_lost") throw error;
839
+ throw Object.assign(new Error("Antigravity completed natively but its dispatch journal could not be saved", { cause: error.cause ?? error }), {
840
+ code: "native_outcome_observation_failed", details: { native_outcome: result, settled: false, observation_error: errorRecord(error) }
841
+ });
842
+ }
843
+ return result;
844
+ } catch (error) {
845
+ await dispatchJournal;
846
+ if (error.code === "native_outcome_observation_failed") throw error;
847
+ if (current.dispatchObservationError) {
848
+ if (!current.terminalResult) throw current.dispatchObservationError;
849
+ const cause = current.dispatchObservationError;
850
+ const failed = current.terminalResult.status === 'SUCCESS'
851
+ ? new DaemonError('native_outcome_observation_failed', 'Antigravity completed natively but its dispatch journal could not be saved', false, { native_outcome: this.receipt(current.terminalResult), settled: false, observation_error: errorRecord(cause) })
852
+ : agyTerminalFailure(current.terminalResult, { ...current.terminalMetadata, native_outcome: current.terminalResult, settled: false, native_turn_items: { observed: false, possible_effects: true, pending: true }, observation_error: errorRecord(cause) });
853
+ failed.cause = cause.cause ?? cause;
854
+ current.observationError = failed; current.dispatchObservationError = failed;
855
+ this.drainingError = failed; this.state.terminal_observation_pending = true;
856
+ if (current.terminalResult.status !== 'SUCCESS') this.lastTerminalFailure = failed;
857
+ throw failed;
858
+ }
859
+ const metadata = current.terminalMetadata;
860
+ if (error === this.lastTerminalFailure && metadata && !metadata.request_proof && current.outboundPosition && metadata.stream_terminal_position && this.config.daemonId && current.operationId) {
861
+ metadata.request_proof = { kind: "stream_request", daemon_instance: this.config.daemonId, connection_id: current.connectionId, operation_id: current.operationId,
862
+ turn_generation: current.generation, session_id: this.init.conversation_id, outbound_position: current.outboundPosition, terminal_position: metadata.stream_terminal_position };
863
+ const proven = agyTerminalFailure(current.terminalResult, { ...error.details, ...metadata });
864
+ this.lastTerminalFailure = proven;
865
+ await this.persist({ last_terminal_metadata: metadata, last_terminal_failure: errorRecord(proven) });
866
+ throw proven;
867
+ }
868
+ throw error;
869
+ } finally {
870
+ clearInterval(timer); current.observerReturned = true;
871
+ if (current.dispatchJournalSettled && this.promptObservation === current) this.promptObservation = null;
872
+ }
873
+ }
874
+ childActivity(sessionId) {
875
+ this.invalidateSettlement(sessionId);
876
+ if (sessionId === this.init?.conversation_id && !this.active) this.state.unclaimed_activity = true;
877
+ const child = this.descendants.get(sessionId);
878
+ if (!child) return;
879
+ this.descendants.set(sessionId, { ...child, previous_terminal_status: child.status === "unknown" ? child.previous_terminal_status : child.status, status: "unknown", tree_settled: false, settlement_evidence: null, settlement_daemon_id: null });
880
+ }
881
+ invalidateSettlement(sessionId) {
882
+ this.nativeProof = null; this.nativeRevision = (this.nativeRevision ?? 0) + 1; this.nativeAbort?.abort();
883
+ this.state.native_execution_revision = this.nativeRevision;
884
+ if (this.init) this.state.active_tree = true;
885
+ this.state.retained_tree_settlement = null;
886
+ for (const id of new Set([this.init?.conversation_id, sessionId])) {
887
+ const observation = this.sessionObservations.get(id);
888
+ if (observation?.stop) this.sessionObservations.set(id, { ...observation, stop: null });
889
+ }
890
+ }
891
+ qualifiedChildForHook(conversationId) {
892
+ // A fully source-bound in-memory ancestry is already the result of the
893
+ // observation loop. Avoid re-reading every transcript on each child hook.
894
+ const retained = this.descendants.get(conversationId), seenRetained = new Set();
895
+ let node = retained, expected = conversationId, qualified = Boolean(node && !this.state.native_topology_error);
896
+ while (qualified) {
897
+ const source = node?.native_source, parent = node?.parent_provider_session_id;
898
+ let sourceBound = source?.kind === 'agy.step_update.subagent_info'
899
+ && source.parent_provider_session_id === parent && Number.isSafeInteger(source.step_index) && source.step_index >= 0;
900
+ if (!sourceBound && typeof source?.locator === 'string' && /^[a-f0-9]{64}$/.test(source?.sha256 ?? '') && typeof parent === 'string') {
901
+ try {
902
+ const locator = fileURLToPath(source.locator);
903
+ sourceBound = locator.endsWith(path.sep + path.join('brain', parent, '.system_generated', 'logs', 'transcript.jsonl'));
904
+ } catch { /* An invalid locator never grants a fast-path identity. */ }
905
+ }
906
+ if (!node || node.provider_session_id !== expected || !sourceBound || !parent || seenRetained.has(expected)
907
+ || source.parent_provider_session_id && source.parent_provider_session_id !== parent) { qualified = false; break; }
908
+ seenRetained.add(node.provider_session_id);
909
+ if (parent === this.init?.conversation_id) break;
910
+ expected = parent;
911
+ node = this.descendants.get(parent);
912
+ if (!node) qualified = false;
913
+ }
914
+ return qualified ? retained : null;
915
+ }
916
+ async childForHook(conversationId, onProgress = null) {
917
+ if (conversationId === this.init?.conversation_id) return null;
918
+ const retained = this.qualifiedChildForHook(conversationId);
919
+ if (retained) return retained;
920
+ await this.captureChildTools(onProgress);
921
+ const deadline = Date.now() + 1_000;
922
+ while (!this.descendants.has(conversationId) && Date.now() < deadline) { await new Promise(resolve => setTimeout(resolve, 25)); await this.captureChildTools(onProgress); }
923
+ const child = this.descendants.get(conversationId);
924
+ if (!child) throw new DaemonError("agy_child_identity_unconfirmed", "Antigravity hook has no confirmed native parent", true, { conversation_id: conversationId });
925
+ const seen = new Set([conversationId]); let current = child;
926
+ for (;;) {
927
+ if (this.state.native_topology_error || current.native_source?.parent_provider_session_id && current.native_source.parent_provider_session_id !== current.parent_provider_session_id)
928
+ throw new DaemonError("agy_child_parent_unqualified", "Antigravity native ancestry is contradictory");
929
+ if (current.parent_provider_session_id === this.init?.conversation_id) break;
930
+ const parent = this.descendants.get(current.parent_provider_session_id);
931
+ if (!current.native_source || !parent || seen.has(parent.provider_session_id)) throw new DaemonError("agy_child_parent_unqualified", "Antigravity worker has no source-bound ancestry to the controlled Session", false, { conversation_id: conversationId, parent_conversation_id: current.parent_provider_session_id });
932
+ seen.add(parent.provider_session_id); current = parent;
933
+ }
934
+ if (this.state.native_topology_error) throw new DaemonError("agy_child_parent_unqualified", "Antigravity native ancestry is contradictory");
935
+ return child;
936
+ }
937
+ async registerWorkStart(event, payload, callbackRequestId = null) {
938
+ if (event !== "PreToolUse" || !this.config.runtime_owner || !this.config.ddFlowBin || !this.init?.conversation_id
939
+ || typeof payload.conversationId !== "string" || !payload.conversationId
940
+ || !Number.isSafeInteger(payload.stepIdx) || payload.stepIdx < 0
941
+ || payload.executionNum !== undefined && (!Number.isSafeInteger(payload.executionNum) || payload.executionNum < 0)
942
+ || !["run_command", "run_terminal_command", "RunTerminalCommand"].includes(payload.toolCall?.name)) return null;
943
+ const input = payload.toolCall?.args ?? {}, command = input.command ?? input.cmd ?? input.CommandLine;
944
+ if (!isStandaloneWorkStart(command)) return null;
945
+ assertDaemonDispatch(this.paths.dir);
946
+ const tool = createHash("sha256").update(JSON.stringify([this.config.daemonId, payload.conversationId, payload.stepIdx, payload.executionNum ?? null, event])).digest("hex");
947
+ this.retainedWorkStarts ??= new Map();
948
+ this.retainedWorkActivity ??= new Set();
949
+ if (!this.retainedWorkActivity.has(tool)) {
950
+ this.retainedWorkActivity.add(tool);
951
+ // A callback is activity even before its ancestry is published. It
952
+ // invalidates old idle, but cannot identify or finish a creator prompt.
953
+ this.childActivity(payload.conversationId);
954
+ }
955
+ return await retainNativeWorkStart(this.paths.dir, { daemon_id: this.config.daemonId, source_harness: "antigravity-cli",
956
+ root_provider_session_id: this.init.conversation_id, provider_session_id: payload.conversationId, parent_provider_session_id: null,
957
+ tool_call_id: tool, event, native_command: command, native_cwd: this.config.cwd,
958
+ ...(callbackRequestId ? { callback_request_id: callbackRequestId } : {}),
959
+ native_payload: { schema_id: "dd-flow/agy-tool-event@1", event_id: tool, phase: "before", daemon_id: this.config.daemonId,
960
+ launch_cwd: this.config.cwd, conversation_id: payload.conversationId, execution_num: payload.executionNum, step_index: payload.stepIdx,
961
+ tool: payload.toolCall.name, input, workspace_paths: payload.workspacePaths, transcript_path: payload.transcriptPath,
962
+ profile: payload.model ? observedProfile(payload) : {} } }, this.active ?? this.lastNativeHookOperation, this.retainedWorkStarts);
963
+ }
964
+ async verifyWorkStart(params) {
965
+ const retained = this.retainedWorkStarts?.get(params.requestId);
966
+ if (!retained || !isDeepStrictEqual(retained.binding, params.binding)) throw new DaemonError("native_hook_unproven", "Unknown native Work start callback");
967
+ retained.guard(); assertDaemonDispatch(this.paths.dir);
968
+ if (retained.binding.root_provider_session_id !== this.init?.conversation_id) throw new DaemonError("native_hook_unproven", "Native Work start root changed");
969
+ const publish = operationProgressReporter();
970
+ const child = await this.childForHook(retained.binding.provider_session_id,
971
+ cursor => publish('verification_progress', { native_request_id: cursor, session_id: retained.binding.provider_session_id }));
972
+ if (retained.binding.provider_session_id !== this.init.conversation_id && this.qualifiedChildForHook(retained.binding.provider_session_id) !== child)
973
+ throw new DaemonError("native_hook_unproven", "Native Work start child has no source-bound ancestry");
974
+ retained.guard();
975
+ const parent = child?.parent_provider_session_id ?? null;
976
+ const verified = { ...retained.binding, parent_provider_session_id: parent, native_ancestry: "confirmed",
977
+ native_payload: { ...retained.binding.native_payload, parent_conversation_id: parent } };
978
+ if (retained.binding.callback_request_id) this.hookRequests?.delete(retained.binding.callback_request_id);
979
+ return { verified: true, registeredHookBinding: retained.binding, nativeHookBinding: verified };
980
+ }
981
+ async recordUnconfirmedHookFailure(error, event, payload, observer, requestId = null) {
982
+ // The request was observed while this operation was active, but AGY does
983
+ // not expose the native creator Turn. Preserve the refusal without
984
+ // attributing it to (or aborting) this potentially unrelated prompt.
985
+ const key = typeof requestId === 'string' && /^[A-Za-z0-9-]{1,128}$/.test(requestId) ? requestId : null;
986
+ const scope = key && JSON.stringify([key, observer?.operationId ?? null, observer?.generation ?? null]);
987
+ this.unconfirmedHookFlights ??= new Map();
988
+ if (scope && this.unconfirmedHookFlights.has(scope)) return await this.unconfirmedHookFlights.get(scope);
989
+ const flight = (async () => {
990
+ let diagnostic = key && (this.state.native_hook_diagnostics ?? []).find(item => item.request_id === key
991
+ && item.operation_id === (observer?.operationId ?? null) && item.turn_generation === (observer?.generation ?? null));
992
+ try {
993
+ if (!diagnostic) {
994
+ diagnostic = { code: 'native_hook_failed', message: 'Native hook failed without a verified creator Turn',
995
+ ...(key ? { request_id: key } : {}), primary_error: errorRecord(error), event, conversation_id: payload.conversationId ?? null,
996
+ daemon_id: this.config.daemonId, operation_id: observer?.operationId ?? null,
997
+ turn_generation: observer?.generation ?? null, native_creator: 'unconfirmed' };
998
+ // Do not grow in-memory state when durable journal writing fails.
999
+ await this.journal('unconfirmed_hook_failure', diagnostic);
1000
+ this.state.native_hook_diagnostics = [...(this.state.native_hook_diagnostics ?? []), diagnostic];
1001
+ }
1002
+ await this.persist(); return true;
1003
+ } catch { return false; /* Optional diagnostic cannot erase a native operation. */ }
1004
+ })();
1005
+ if (scope) this.unconfirmedHookFlights.set(scope, flight);
1006
+ try { return await flight; }
1007
+ finally { if (scope && this.unconfirmedHookFlights.get(scope) === flight) this.unconfirmedHookFlights.delete(scope); }
1008
+ }
1009
+ async observeHookFailures() {
1010
+ if (!this.hookRequests?.size) return;
1011
+ if (this.hookFailuresFlight) return await this.hookFailuresFlight;
1012
+ const flight = (async () => {
1013
+ let names;
1014
+ try { names = new Set(await readdir(path.join(this.paths.dir, "hook-failures"))); }
1015
+ catch (error) { if (error.code === "ENOENT") return; throw error; }
1016
+ for (const [requestId, binding] of this.hookRequests ?? []) {
1017
+ if (!binding.current || !names.has(`${requestId}.json`)) continue;
1018
+ let failure;
1019
+ try { failure = JSON.parse(await readFile(path.join(this.paths.dir, "hook-failures", `${requestId}.json`), "utf8")); }
1020
+ catch (error) { if (error.code === "ENOENT") continue; failure = { code: "native_hook_receipt_invalid", message: error.message }; }
1021
+ if (failure.request_id !== undefined && failure.request_id !== requestId) continue;
1022
+ if (binding.current !== this.active) {
1023
+ if (failure.request_id !== requestId) continue;
1024
+ // The callback request was accepted while this observer operation was
1025
+ // active. AGY has no native creator ID, so this is diagnostic only:
1026
+ // never mutate either the old receipt or the current Turn.
1027
+ await this.journal("late_hook_receipt", { request_id: requestId, daemon_id: this.config.daemonId,
1028
+ observer_operation_id: binding.current.operationId ?? null, observer_turn_generation: binding.current.generation ?? null,
1029
+ native_creator: "unconfirmed", event: binding.event, failure: errorRecord(failure) });
1030
+ this.hookRequests.delete(requestId);
1031
+ continue;
1032
+ }
1033
+ const saved = await this.recordUnconfirmedHookFailure(Object.assign(new Error(failure.message ?? "Native hook failed"), { code: failure.code, details: failure.details }), binding.event, binding.payload, binding.current, requestId);
1034
+ if (saved) this.hookRequests.delete(requestId);
1035
+ }
1036
+ })();
1037
+ this.hookFailuresFlight = flight;
1038
+ try { await flight; } finally { if (this.hookFailuresFlight === flight) this.hookFailuresFlight = null; }
1039
+ }
1040
+ async observeHook(event, payload, budget = null) {
1041
+ const hookOwner = this.active, hookGeneration = this.turnGeneration;
1042
+ const conversationId = payload.conversationId ?? this.init?.conversation_id;
1043
+ const shell = ["run_command", "run_terminal_command", "RunTerminalCommand"].includes(payload.toolCall?.name);
1044
+ const args = payload.toolCall?.args ?? {}, command = args.command ?? args.cmd ?? args.CommandLine;
1045
+ const observationOnly = Boolean(this.config.runtime_owner && this.config.ddFlowBin && (event === "Stop" || !shell
1046
+ || typeof command === "string" && parseLifecycleCommand(command).participation === "none"));
1047
+ // Ordinary native facts cannot grant Work authority. Keep them cheap;
1048
+ // independent observation verifies ancestry/idle after the callback ACK.
1049
+ const child = observationOnly ? this.qualifiedChildForHook(conversationId) : await this.childForHook(conversationId); budget?.remaining("identity");
1050
+ if (this.active !== hookOwner || this.turnGeneration !== hookGeneration) {
1051
+ await this.journal("stale_hook", { hook_event: event, conversation_id: conversationId, reason: "owner_changed_during_identity" });
1052
+ return { stale: true };
1053
+ }
1054
+ const prior = this.sessionObservations.get(conversationId) ?? {};
1055
+ const execution = Number.isInteger(payload.executionNum) ? payload.executionNum : null;
1056
+ const step = Number.isInteger(payload.stepIdx) ? payload.stepIdx : null;
1057
+ const stale = step !== null && (Number.isInteger(prior.step_floor) && step <= prior.step_floor
1058
+ || Number.isInteger(prior.last_step_index) && step < prior.last_step_index
1059
+ || step === prior.last_step_index && execution !== null && Number.isInteger(prior.last_execution_num) && execution < prior.last_execution_num);
1060
+ if (stale) { await this.journal("stale_hook", { hook_event: event, conversation_id: conversationId, step_index: step }); return { stale: true }; }
1061
+ const fingerprint = createHash("sha256").update(JSON.stringify({ event, payload })).digest("hex");
1062
+ if (event === "Stop" && prior.last_stop_hook === fingerprint && prior.last_stop_generation === this.turnGeneration) return { stale: true };
1063
+ this.progressHooks ??= new Map();
1064
+ const scoped = this.active && (!child || child.activity_generation === this.active.generation);
1065
+ // Native hook payloads/step updates carry no creator prompt identity.
1066
+ // A late old step above the new floor is indistinguishable from a new one:
1067
+ // Stop remains a hint, never a whole-tree or child idle grant.
1068
+ const sourceBound = false;
1069
+ const progressKey = JSON.stringify([conversationId, step, execution, event, payload.toolCall ?? null, payload.fullyIdle ?? null]);
1070
+ if (scoped && step !== null && (["PreToolUse", "PostToolUse"].includes(event) || event === "Stop" && payload.fullyIdle === true)) {
1071
+ let progress = this.progressHooks.get(conversationId);
1072
+ if (!progress || progress.step !== step) { progress = { step, seen: new Set() }; this.progressHooks.set(conversationId, progress); }
1073
+ if (!progress.seen.has(progressKey)) { progress.seen.add(progressKey); this.markActivity(); }
1074
+ }
1075
+ if (conversationId) this.sessionObservations.set(conversationId, { ...prior,
1076
+ ...(step !== null ? { last_step_index: step, last_execution_num: execution ?? (step === prior.last_step_index ? prior.last_execution_num : null) } : {}),
1077
+ last_hook: fingerprint, ...(event === "PreToolUse" ? { last_pre_hook: fingerprint } : {}),
1078
+ ...(payload.transcriptPath ? { transcript_path: payload.transcriptPath } : {}),
1079
+ ...(event === "Stop" ? { last_stop_hook: fingerprint, last_stop_generation: this.turnGeneration, stop: { fullyIdle: payload.fullyIdle === true, source_bound: sourceBound, terminationReason: payload.terminationReason ?? null, executionNum: execution } } : {}) });
1080
+ if (event === "PreToolUse" && prior.last_pre_hook !== fingerprint) this.childActivity(conversationId);
1081
+ const parent = child?.parent_provider_session_id ?? null;
1082
+ if (event === "Stop" && !stale) {
1083
+ const observation = this.sessionObservations.get(conversationId);
1084
+ this.sessionObservations.set(conversationId, { ...observation, stop: { ...observation.stop, generation: this.turnGeneration } });
1085
+ }
1086
+ const observed = payload.model ? observedProfile(payload) : {};
1087
+ // An omitted model carries no profile evidence. The hook journal below
1088
+ // retains that fact; do not read/lock/fsync the model history for every
1089
+ // ordinary tool or Stop callback. Actual model observations stay intact.
1090
+ if (payload.model && !observationOnly) await observeModel({ journal: this.config.journal, harness: "antigravity-cli", sessionId: conversationId, parentSessionId: parent, requested: this.config, observed, source: { channel: "native.hook", scope: "configured" }, evidence: "configured" });
1091
+ if (this.active !== hookOwner || this.turnGeneration !== hookGeneration) {
1092
+ await this.journal("stale_hook", { hook_event: event, conversation_id: conversationId, reason: "owner_changed_during_model_observation" });
1093
+ return { stale: true };
1094
+ }
1095
+ const eventId = createHash("sha256").update(JSON.stringify({ daemon: this.config.daemonId, event, conversationId, fully_idle: payload.fullyIdle ?? null, termination_reason: payload.terminationReason ?? null, execution: payload.executionNum ?? null, step: payload.stepIdx ?? null, tool: payload.toolCall?.name ?? null, args: payload.toolCall?.args ?? null })).digest("hex");
1096
+ await this.persist(); await this.journal("hook", { stale, hook_event: event, event_id: eventId, conversation_id: conversationId, turn_generation: hookGeneration, execution_num: execution, step_index: step, termination_reason: payload.terminationReason ?? null, fully_idle: payload.fullyIdle ?? null, ...(payload.model ? {} : { model_observation: "unavailable" }), persistence_timing: this.persistTiming ?? null });
1097
+ if (this.active !== hookOwner || this.turnGeneration !== hookGeneration) return { stale: true };
1098
+ this.queueNativeInspection();
1099
+ // A provider may wait for this hook's ACK before emitting the very event
1100
+ // needed to finish the turn. The fact above is persisted before ACK;
1101
+ // completion reconciliation belongs to the daemon, not the callback.
1102
+ if (hookOwner && this.active === hookOwner && this.turnGeneration === hookGeneration) hookOwner.pendingHookReconciliation = true;
1103
+ return { observationOnly, event_id: eventId, source_bound: sourceBound, daemon_id: this.config.daemonId, launch_cwd: this.config.cwd, parent_conversation_id: parent, profile: observed, requested_profile: { provider: this.config.provider, model: this.config.model, reasoning: this.config.reasoning, mode: this.config.mode }, project_root: this.config.projectRoot, dd_flow_bin: this.config.ddFlowBin, dd_flow_home: this.config.ddFlowHome };
1104
+ }
1105
+ async finalizeProviderForControl() {
1106
+ const pending = this.providerFinalization;
1107
+ try {
1108
+ while (this.active?.pendingHookReconciliation || this.active?.hookReconciliation) await this.reconcilePendingHook(this.active);
1109
+ try { await pending; }
1110
+ catch (error) {
1111
+ // Control retries only retained physical/resource cleanup, never a
1112
+ // missing mandatory stream record or a productive provider request.
1113
+ if (this.providerFinalization !== pending) await this.providerFinalization;
1114
+ else {
1115
+ if (!this.retryProviderFinalization) throw error;
1116
+ this.providerFinalization = this.retryProviderFinalization(5_000);
1117
+ await this.providerFinalization;
1118
+ }
1119
+ }
1120
+ await this.promptObservation?.dispatchJournal;
1121
+ await this.observeHookFailures();
1122
+ } catch (error) {
1123
+ if (!this.drainingError) throw error;
1124
+ this.drainingError.cleanup_error = errorRecord(error);
1125
+ this.drainingError.details = { ...this.drainingError.details, cleanup_error: this.drainingError.cleanup_error };
1126
+ throw this.drainingError;
1127
+ }
1128
+ if (this.drainingError) throw this.drainingError;
1129
+ }
1130
+ async prepareStop(cancelTree) {
1131
+ const current = this.active, priorResult = this.lastResult;
1132
+ if (!cancelTree && !current && !priorResult) await this.reconcileRetainedAdmission();
1133
+ if (cancelTree && (this.active || !this.receipt().settled)) await this.cancel();
1134
+ else if (this.active || this.state.active_tree || this.state.unclaimed_activity || (this.lastResult && !this.receipt().settled)) throw new DaemonError("tree_not_settled", "Antigravity root or child tree is still running");
1135
+ // A physical kill is settlement, not a native clean terminal receipt.
1136
+ // In particular, the previous Turn's SUCCESS cannot certify a cancelled
1137
+ // in-flight Turn that exited without its own result.
1138
+ const currentResult = !current || current.terminalResult === this.lastResult || this.lastResult !== priorResult;
1139
+ const clean = currentResult && this.receipt().settled;
1140
+ return { ...this.receipt(), clean, settled: Boolean(clean), native_settled: Boolean(clean), ...(clean ? {} : { native_settlement: { confirmed: false, reason: "native_terminal_unconfirmed" } }) };
1141
+ }
1142
+ async retireNativeServices() {
1143
+ if (!this.nativeQualification?.qualified) return;
1144
+ this.nativeResources ??= new Map((this.state.native_resources_entries ?? []).map(item => [item.resource_id, item]));
1145
+ const services = [...(this.nativeResources?.values() ?? [])].filter(item => item.classification === 'support_service' && item.state !== 'stopped');
1146
+ const schedules = [...(this.nativeResources?.values() ?? [])].filter(item => item.state !== 'stopped' &&
1147
+ (item.kind === 'schedule' || item.classification === 'unknown'));
1148
+ // Unknown task semantics can be reconciled by exact native handle inventory,
1149
+ // not by inventing a shell outcome or trusting a PID printed to stdout.
1150
+ const revision = this.nativeRevision ?? 0;
1151
+ const endpoint = this.nativeStopper ? null : await ownedAgyEndpoint(this.nativeLaunch);
1152
+ const input = { endpoint, sessionId: this.init.conversation_id, cwd: this.config.cwd,
1153
+ csrfToken: this.nativeCsrfToken, remainingMs: 10_000, rootRelation: this.nativeQualification.rootRelation };
1154
+ const clock = new ObservationClock({ timeoutMs: input.remainingMs });
1155
+ let progress = 0;
1156
+ const requestInput = () => {
1157
+ if (clock.sample()) throw new DaemonError('native_resources_unproven', 'AGY native retirement observation budget exhausted');
1158
+ const remainingMs = Math.max(1, Math.ceil(clock.timeoutMs - clock.elapsed));
1159
+ return { ...input, remainingMs };
1160
+ };
1161
+ const inventory = async () => {
1162
+ if (this.state.native_topology_error) throw new DaemonError('native_resources_unproven', 'AGY native topology is incomplete');
1163
+ const root = this.init.conversation_id, nodes = [{ provider_session_id: root, parent_provider_session_id: '' }, ...this.descendants.values()];
1164
+ for (const node of nodes.slice(1)) {
1165
+ const seen = new Set([node.provider_session_id]); let parent = node.parent_provider_session_id;
1166
+ while (parent !== root) {
1167
+ if (!parent || seen.has(parent)) throw new DaemonError('native_resources_unproven', 'AGY native resource ancestry is unbound');
1168
+ seen.add(parent); parent = this.descendants.get(parent)?.parent_provider_session_id;
1169
+ }
1170
+ }
1171
+ const result = [];
1172
+ for (const node of nodes) {
1173
+ const snapshot = await (this.nativeStateObserver ?? inspectAgyNativeState)({ ...requestInput(),
1174
+ sessionId: node.provider_session_id, rootSessionId: root, parentSessionId: node.parent_provider_session_id });
1175
+ result.push({ session_id: node.provider_session_id, snapshot, ...agyNativeInventory(snapshot, node.provider_session_id) });
1176
+ clock.sample(++progress); // A completed node read, not observer polling.
1177
+ }
1178
+ return result;
1179
+ };
1180
+ // Historical transcript envelopes need not explain whether a timer fired
1181
+ // or was cancelled. Fresh native absence proves retirement, not success.
1182
+ // The transcript ledger is partial. It cannot decide whether a fresh native
1183
+ // inventory is needed: even a missing ledger may hide an active daemon task.
1184
+ let states = await inventory();
1185
+ const required = states.find(state => !state.idle || state.tasks.some(task => task.isDaemon !== true));
1186
+ if (required) {
1187
+ this.childActivity(required.session_id);
1188
+ throw new DaemonError('native_resources_unproven', 'AGY required execution became active before native resource retirement');
1189
+ }
1190
+ const notificationKey = (session, trajectory, message) => JSON.stringify([session, trajectory, message.id, message.sender, message.recipient]);
1191
+ const notifications = new Map((this.state.native_retirement_notifications ?? []).filter(item => item.owner_daemon_id === this.config.daemonId)
1192
+ .map(item => [notificationKey(item.session_id, item.trajectory_id, item), item]));
1193
+ if (states.some(state => state.messages.some(message => !notifications.has(notificationKey(state.session_id, state.snapshot.trajectoryId, message)))))
1194
+ throw new DaemonError('native_resources_unproven', 'AGY has pending delivery before native resource retirement');
1195
+ const stopSenders = new Set(['system', ...states.map(state => state.session_id), ...states.flatMap(state => state.tasks.map(task => task.taskId))]);
1196
+ const trajectories = new Map(states.map(state => [state.session_id, state.snapshot.trajectoryId]));
1197
+ const activeTasks = states.some(state => state.snapshot.hasActiveChildren === true || state.tasks.length > 0);
1198
+ const stopped = services.length || activeTasks
1199
+ ? await (this.nativeStopper ?? stopAgyNativeTree)(requestInput(), [...this.descendants.keys()]) : null;
1200
+ if (stopped) clock.sample(++progress);
1201
+ const observed = await (this.nativeObserver ?? inspectAgyNative)(requestInput());
1202
+ clock.sample(++progress);
1203
+ if (!this.nativeStopper && !isDeepStrictEqual(await ownedAgyEndpoint(this.nativeLaunch), endpoint)
1204
+ || !observed.idle || revision !== (this.nativeRevision ?? 0) || !this.receipt().settled)
1205
+ throw new DaemonError('native_resources_unproven', 'AGY native service retirement lost its owner/execution fence');
1206
+ {
1207
+ states = await inventory();
1208
+ if (states.some(state => !state.idle || trajectories.get(state.session_id) !== state.snapshot.trajectoryId ||
1209
+ state.tasks.length) ||
1210
+ observed.trajectory_id && observed.trajectory_id !== trajectories.get(this.init.conversation_id) ||
1211
+ revision !== (this.nativeRevision ?? 0) || !this.receipt().settled ||
1212
+ !this.nativeStopper && !isDeepStrictEqual(await ownedAgyEndpoint(this.nativeLaunch), endpoint))
1213
+ throw new DaemonError('native_resources_unproven', 'AGY schedule retirement lacks fresh idle inventory and required delivery');
1214
+ for (const state of states) for (const message of state.messages) {
1215
+ const key = notificationKey(state.session_id, state.snapshot.trajectoryId, message);
1216
+ if (notifications.has(key)) continue;
1217
+ if (!stopped || typeof message.id !== 'string' || !message.id || message.recipient !== state.session_id || !stopSenders.has(message.sender))
1218
+ throw new DaemonError('native_resources_unproven', 'AGY post-stop delivery has no owned native control provenance');
1219
+ // Native cancellation creates notices after required execution is
1220
+ // already settled. They remain pending technical evidence until the
1221
+ // fenced provider is closed; do not prompt a model or claim delivery.
1222
+ notifications.set(key, { id: message.id, sender: message.sender, recipient: message.recipient, session_id: state.session_id,
1223
+ trajectory_id: state.snapshot.trajectoryId, owner_daemon_id: this.config.daemonId, generation: this.turnGeneration, revision,
1224
+ status: 'pending', disposition: 'after_owned_native_stop', method: stopped.method });
1225
+ }
1226
+ for (const schedule of schedules) {
1227
+ const state = states.find(item => item.session_id === schedule.provider_session_id);
1228
+ if (!state || state.tasks.some(task => task.taskId === schedule.resource_id))
1229
+ throw new DaemonError('native_resources_unproven', 'AGY schedule owner or retirement is unproven');
1230
+ this.nativeResources.set(schedule.resource_id, { ...schedule, state: 'stopped', native_retirement: {
1231
+ method: state.snapshot.method, reason: 'native_task_inventory_absent', revision,
1232
+ trajectory_id: state.snapshot.trajectoryId, observed_at: state.snapshot.observed_at } });
1233
+ }
1234
+ }
1235
+ // This is native handle retirement, not physical cleanup. Shared shutdown
1236
+ // still re-observes the retained local PID/birth inventory after close.
1237
+ for (const service of services) this.nativeResources.set(service.resource_id, { ...service, state: 'stopped',
1238
+ native_retirement: { method: stopped.method, stopped_session_ids: stopped.stopped_session_ids, revision } });
1239
+ await this.persist({ native_resources_entries: [...this.nativeResources.values()], native_retirement_notifications: [...notifications.values()] });
1240
+ if (revision !== (this.nativeRevision ?? 0) || !this.receipt().settled)
1241
+ throw new DaemonError('native_resources_unproven', 'AGY resource retirement was fenced during publication');
1242
+ }
1243
+ async cancel() { if (!this.active && this.receipt().settled) return { ...this.receipt(), cancelled: true }; this.nativeAbort?.abort(); this.nativeProof = null; this.nativeRevision = (this.nativeRevision ?? 0) + 1; this.state.native_execution_revision = this.nativeRevision; cancelPendingDispatch(this); if (!this.child) return this.receipt(); if (!this.exited) { try { process.kill(-this.child.pid, "SIGINT"); } catch (error) { if (error.code !== "ESRCH") throw error; } const deadline = Date.now() + 5000; while (!this.exited && Date.now() < deadline) await new Promise(resolve => setTimeout(resolve, 100)); if (!this.exited) await stopProcessGroup(this.child, 5_000); } await this.finalizeProviderForControl(); await this.persist({ active_tree: false }); return { ...this.receipt(), cancelled: true };
1244
+ }
1245
+ async close(cancelTree) { if (cancelTree) cancelPendingDispatch(this); if (!cancelTree && !this.exited && (this.active || this.state.active_tree || this.state.unclaimed_activity || (this.lastResult && !this.receipt().settled))) throw new DaemonError("tree_not_settled", "Antigravity root or child tree is still running"); if (cancelTree) await this.cancel(); else if (this.child && !this.exited) this.child.stdin.end(); const deadline = Date.now() + 5000; while (!this.exited && Date.now() < deadline) await new Promise(resolve => setTimeout(resolve, 100)); if (!this.exited) { if (!this.active && this.receipt().settled) await stopProcessGroup(this.child, 5_000); else await this.cancel(); } await this.finalizeProviderForControl(); }
1246
+ async abortProvider(reason) {
1247
+ const child = this.child, providerProcess = this.providerProcess;
1248
+ await stopProcessGroup(child);
1249
+ await this.finalizeProviderForControl();
1250
+ await finishDaemonProcess(this.config, providerProcess, "failed", reason);
1251
+ }
1252
+ async finishResource(state = "stopped", reason = "daemon_stopped") { await this.captureChildTools(); await finishDaemonProcess(this.config, this.state.resource_process, state, reason); }
1253
+ }
1254
+
1255
+ export async function serveDaemon(stateDir) {
1256
+ const paths = locations(stateDir), state = await readState(paths.dir); if (!state?.config) throw new DaemonError("daemon_state_missing", "Antigravity daemon state is missing");
1257
+ scheduleDaemonProcessHeartbeat(state.config, state.resource_process);
1258
+ const runtime = new Runtime(paths, state); await removeSocket(paths);
1259
+ const server = net.createServer(socket => {
1260
+ let buffer = "", consumed = false; socket.setEncoding("utf8");
1261
+ socket.on("data", chunk => {
1262
+ if (consumed) return;
1263
+ buffer += chunk; if (!admitNativeRequestBuffer(buffer, socket, RESPONSE_SCHEMA)) { consumed = true; return; } const at = buffer.indexOf("\n"); if (at < 0) return; consumed = true;
1264
+ void (async () => {
1265
+ let request;
1266
+ try {
1267
+ request = JSON.parse(buffer.slice(0, at));
1268
+ const result = await durableDaemonDispatch(paths.dir, request, () => dispatch(runtime, request), () => dispatch(runtime, { operation: "session.inspect", params: request.params ?? {} }));
1269
+ if (result._shutdown) retireDaemonEndpoint(server);
1270
+ socket.end(`${JSON.stringify({ schema_id: RESPONSE_SCHEMA, id: request.id, ok: true, result: { ...result, _shutdown: undefined } })}\n`);
1271
+ } catch (error) {
1272
+ await retireFailedDaemon(server, state, error, patch => runtime.persist(patch));
1273
+ socket.end(`${JSON.stringify({ schema_id: RESPONSE_SCHEMA, id: request?.id ?? null, ok: false, error: errorPayload(error) })}\n`);
1274
+ }
1275
+ })();
1276
+ });
1277
+ });
1278
+ trackDaemonConnections(server); runtime.server = server;
1279
+ await new Promise((resolve, reject) => { server.once("error", reject); server.listen(paths.socket, resolve); }); await chmod(paths.socket, 0o600);
1280
+ try { await runtime.start(state.config.resumeSessionId); runtime.requireSessionIdentity(state.config.resumeSessionId); }
1281
+ catch (error) {
1282
+ try { await runtime.abortProvider("daemon_start_failed"); }
1283
+ catch (cleanup) { error.details = { ...error.details, cleanup_failed: errorRecord(cleanup) }; }
1284
+ try { if (!error.details?.cleanup_failed) await runtime.finishResource("failed", "daemon_start_failed"); }
1285
+ catch (cleanup) { error.details = { ...error.details, resource_cleanup_failed: errorRecord(cleanup) }; }
1286
+ Object.assign(state, { shutdown_state: error.details?.cleanup_failed || error.details?.resource_cleanup_failed ? "cleanup_failed" : "failed", active_tree: Boolean(error.details?.cleanup_failed) });
1287
+ await retainStartupFailure({ state, publish: snapshot => runtime.persist(snapshot), log: paths.log, error });
1288
+ await new Promise(resolve => server.close(resolve));
1289
+ throw error;
1290
+ }
1291
+ await new Promise(resolve => server.once("close", resolve));
1292
+ }
1293
+
1294
+ async function dispatch(runtime, request) {
1295
+ if (request.schema_id !== REQUEST_SCHEMA) throw new DaemonError("daemon_protocol_mismatch", "unsupported dd-agy request schema"); const params = request.params ?? {};
1296
+ if (["session.create", "session.prompt", "session.start", "session.fork", "session.resume"].includes(request.operation)) assertDaemonDispatch(runtime.paths.dir);
1297
+ if (request.operation === "daemon.status") { await runtime.observeHookFailures(); return { daemon_id: runtime.config.daemonId, shutdown_state: runtime.state.shutdown_state, active_tree: runtime.state.active_tree, active_operation: runtime.active?.operationId ?? null, last_hook_rejection: runtime.state.last_hook_rejection ?? null, last_activity_at: runtime.lastActivityAt, provider_ready: Boolean(runtime.init && !runtime.exited), pid: process.pid, provider_pid: runtime.child?.pid ?? null, versions: runtime.state.versions, config: runtime.config, sessions: runtime.state.sessions ?? [], receipt: runtime.receipt() }; }
1298
+ if (request.operation === "session.create") { if (params.sessionId) throw new DaemonError("invalid_create", "create does not accept a Session ID"); if (params.prompt) throw new DaemonError("invalid_create", "create does not execute a prompt; use session.prompt"); await runtime.start(); await runtime.zeroUsage(); return runtime.receipt(); }
1299
+ if (request.operation === "session.prompt") { const assertDispatch = captureDispatchGuard(runtime); await runtime.start(params.sessionId ?? null); runtime.requireSessionIdentity(params.sessionId); return await runtime.prompt(params.prompt, assertDispatch, request.id, params.livenessTimeoutMs ?? runtime.config.livenessTimeoutMs ?? 1_800_000); }
1300
+ if (request.operation === "session.inspect") { runtime.requireSessionIdentity(params.sessionId); await runtime.captureChildTools(); void runtime.reconcileRetainedAdmission().catch(error => { runtime.nativeInspection = { state: 'unproven', reason: error.details?.reason ?? error.code ?? 'native_admission_unproven' }; }); runtime.queueNativeInspection(); await runtime.persist({ native_observation_episode: runtime.episodeSnapshot() }); return runtime.receipt(); }
1301
+ if (request.operation === "session.resume") { await runtime.start(params.sessionId ?? runtime.init?.conversation_id ?? null); runtime.requireSessionIdentity(params.sessionId); return runtime.receipt(); }
1302
+ if (request.operation === "session.cancel") { runtime.requireSessionIdentity(params.sessionId); return await runtime.cancel(); }
1303
+ if (request.operation === "hook.verify") {
1304
+ if (params.daemonId !== runtime.config.daemonId) throw new DaemonError("agy_hook_binding_invalid", "Antigravity hook binding does not name this daemon");
1305
+ assertDaemonDispatch(runtime.paths.dir);
1306
+ if (params.binding?.native_admission === "assigned_work_start") return await runtime.verifyWorkStart(params);
1307
+ const retained = runtime.verifiedHookFacts?.get(params.requestId);
1308
+ if (!retained || !isDeepStrictEqual(retained.binding, params.binding)) throw new DaemonError("native_hook_unproven", "Hook fact is not bound to a verified native delivery");
1309
+ retained.guard();
1310
+ return { verified: true, nativeHookBinding: retained.binding };
1311
+ }
1312
+ if (request.operation === "hook.complete") {
1313
+ if (params.daemonId !== runtime.config.daemonId)
1314
+ throw new DaemonError("agy_hook_binding_invalid", "Antigravity hook binding does not name this daemon");
1315
+ if (typeof params.requestId !== 'string' || !/^[A-Za-z0-9-]{1,128}$/.test(params.requestId))
1316
+ throw new DaemonError("agy_hook_binding_invalid", "Antigravity callback request ID is invalid");
1317
+ // Administrative ACK only: the success of a callback is not proof that a
1318
+ // Work mutation occurred or that a native agent Turn is complete.
1319
+ return { retired: runtime.hookRequests?.delete(params.requestId) === true };
1320
+ }
1321
+ if (request.operation === "hook.observe") {
1322
+ // Check the process binding before correlating a request with an active
1323
+ // turn. A stale/foreign daemon must not poison an unrelated operation.
1324
+ if (runtime.config.hookBinding === "environment-v1" && params.daemonId !== runtime.config.daemonId) throw new DaemonError("agy_hook_binding_invalid", "Antigravity hook binding does not name this daemon", false, { expected_daemon_id: runtime.config.daemonId, observed_daemon_id: params.daemonId ?? null });
1325
+ const current = runtime.active, budget = hookBudget(25_000, params.deadlineAt);
1326
+ runtime.hookRequests ??= new Map();
1327
+ if (/^[A-Za-z0-9-]{1,128}$/.test(request.id)) runtime.hookRequests.set(request.id, { current: current ?? runtime.lastNativeHookOperation, event: params.event, payload: { conversationId: params.payload?.conversationId, toolCall: { name: params.payload?.toolCall?.name } } });
1328
+ try {
1329
+ const deferred = await budget.run(() => runtime.registerWorkStart(params.event, params.payload ?? {}, request.id), "registration");
1330
+ if (deferred) return { fastAck: true, nativeHookBinding: deferred };
1331
+ const result = await budget.run(async () => {
1332
+ if (runtime.hookRequests.has(request.id) && current?.operationId) {
1333
+ const directory = path.join(runtime.paths.dir, "operations", createHash("sha256").update(current.operationId).digest("hex"), "hooks");
1334
+ await mkdir(directory, { recursive: true, mode: 0o700 });
1335
+ await writeJson(path.join(directory, `${request.id}.json`), { schema_id: "dd-flow/hook-request@1", request_id: request.id, daemon_id: runtime.config.daemonId, operation_id: current.operationId, turn_generation: current.generation, native_creator: "unconfirmed", event: params.event, conversation_id: params.payload?.conversationId ?? null, tool: params.payload?.toolCall?.name ?? null });
1336
+ }
1337
+ budget.remaining("observation");
1338
+ return await runtime.observeHook(params.event, params.payload ?? {}, budget);
1339
+ }, "observation");
1340
+ // The AGY stream does not prove which native prompt produced a step.
1341
+ // An unseen old step can exceed a newer prompt's observed floor. Do not
1342
+ // mint an optional CLI admission from event order; legacy hook performs
1343
+ // its existing scoped checks until a native creator binding exists.
1344
+ await runtime.observeHookFailures(); budget.remaining("observation");
1345
+ if (result.stale) runtime.hookRequests.delete(request.id);
1346
+ return result;
1347
+ }
1348
+ catch (error) { await runtime.recordUnconfirmedHookFailure(error, params.event, params.payload ?? {}, current, request.id); throw error; }
1349
+ }
1350
+ if (request.operation === "daemon.stop") return await completeDaemonShutdown(runtime.paths.dir, runtime.state, patch => runtime.persist(patch), () => runtime.prepareStop(params.cancelTree === true), [["provider", async ({ physicalOnly }) => {
1351
+ if (!physicalOnly) return await runtime.close(false);
1352
+ await stopProcessGroup(runtime.child);
1353
+ await finishDaemonProcess(runtime.config, runtime.providerProcess);
1354
+ }], ["daemon_resource", ({ physicalOnly }) => physicalOnly ? finishDaemonProcess(runtime.config, runtime.state.resource_process) : runtime.finishResource()]], async () => {
1355
+ await runtime.retireNativeServices();
1356
+ await runtime.captureChildTools();
1357
+ return runtime.receipt().native_resources;
1358
+ }, { cancelTree: params.cancelTree === true });
1359
+ throw new DaemonError("unknown_operation", `unknown dd-agy daemon operation: ${request.operation}`);
1360
+ }
1361
+ import { authorizeRetainedDaemonResume } from "./driver-recovery.mjs";
1362
+ import { commandText } from "./process-json.mjs";