@deksden-com/dd-flow-cli 0.9.0-beta.137 → 0.9.0-beta.139

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 (283) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +9 -0
  3. package/dist/build-info.json +6 -6
  4. package/dist/cli/argument-values.js +5 -5
  5. package/dist/cli/command-inputs.js +89 -20
  6. package/dist/cli/command-metadata.js +197 -0
  7. package/dist/cli/command-option-descriptions.js +191 -0
  8. package/dist/cli/completion-cli.js +70 -0
  9. package/dist/cli/completion.js +57 -0
  10. package/dist/cli/error-output.js +52 -0
  11. package/dist/cli/executor-onboarding.js +76 -0
  12. package/dist/cli/flows.js +29 -0
  13. package/dist/cli/harnesses.js +84 -0
  14. package/dist/cli/help.js +240 -6
  15. package/dist/cli/input-preparation.js +54 -5
  16. package/dist/cli/memory-flow-admission.js +44 -0
  17. package/dist/cli/memory-init-invocation.js +78 -0
  18. package/dist/cli/memory-init.js +240 -0
  19. package/dist/cli/memory-storage-admission.js +53 -0
  20. package/dist/cli/models.js +39 -0
  21. package/dist/cli/native-hook-ingress.js +1 -1
  22. package/dist/cli/operator-profiles.js +61 -0
  23. package/dist/cli/output-options.js +35 -0
  24. package/dist/cli/response-file.js +71 -0
  25. package/dist/cli/run-cli.js +283 -160
  26. package/dist/cli/runtime-process.js +1 -1
  27. package/dist/cli/setup.js +149 -0
  28. package/dist/cli.js +56 -2
  29. package/dist/domain/stage-handler-identities.js +20 -0
  30. package/dist/harness-runtime/lib/daemon-operations.d.mts +1 -0
  31. package/dist/harness-runtime/lib/daemon-operations.mjs +4 -3
  32. package/dist/harness-runtime/lib/dd-agy-daemon.mjs +53 -22
  33. package/dist/harness-runtime/lib/dd-agy.mjs +16 -5
  34. package/dist/harness-runtime/lib/dd-codex-daemon.mjs +12 -6
  35. package/dist/harness-runtime/lib/dd-codex.mjs +24 -12
  36. package/dist/harness-runtime/lib/dd-droid-daemon.mjs +2 -1
  37. package/dist/harness-runtime/lib/dd-droid.mjs +32 -9
  38. package/dist/harness-runtime/lib/dd-grok-daemon.mjs +11 -3
  39. package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +100 -15
  40. package/dist/harness-runtime/lib/dd-opencode.mjs +51 -20
  41. package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +22 -10
  42. package/dist/harness-runtime/lib/dd-zcode.mjs +17 -4
  43. package/dist/harness-runtime/lib/delegation-instructions.d.mts +1 -0
  44. package/dist/harness-runtime/lib/delegation-instructions.mjs +16 -3
  45. package/dist/harness-runtime/lib/lease-renewal.mjs +2 -1
  46. package/dist/harness-runtime/lib/lifecycle-command.mjs +1 -0
  47. package/dist/harness-runtime/lib/managed-daemon.d.mts +1 -0
  48. package/dist/harness-runtime/lib/managed-daemon.mjs +50 -15
  49. package/dist/harness-runtime/lib/managed-flow-env.mjs +15 -0
  50. package/dist/harness-runtime/lib/native-children.d.mts +2 -0
  51. package/dist/harness-runtime/lib/native-children.mjs +20 -1
  52. package/dist/harness-runtime/lib/native-hook-command.mjs +1 -1
  53. package/dist/harness-runtime/lib/native-request-proof.d.mts +2 -0
  54. package/dist/harness-runtime/lib/native-request-proof.mjs +65 -0
  55. package/dist/harness-runtime/lib/opencode-plugin.mjs +32 -9
  56. package/dist/harness-runtime/lib/operation-progress.mjs +3 -1
  57. package/dist/harness-runtime/lib/provider-overload-policy.d.mts +13 -0
  58. package/dist/harness-runtime/lib/provider-overload-policy.mjs +138 -0
  59. package/dist/runtime/context.js +1 -1
  60. package/dist/schemas/discovery-result.schema.json +184 -0
  61. package/dist/schemas/execution-bundle.schema.json +205 -0
  62. package/dist/schemas/execution-settings.schema.json +162 -0
  63. package/dist/schemas/flow-descriptor.schema.json +54 -0
  64. package/dist/schemas/flow-run.schema.json +26 -0
  65. package/dist/schemas/harness-catalog.schema.json +80 -0
  66. package/dist/schemas/mb-init-compose-result.schema.json +457 -0
  67. package/dist/schemas/mb-init-discover-result.schema.json +500 -0
  68. package/dist/schemas/mb-init-finish-result.schema.json +422 -0
  69. package/dist/schemas/mb-init-preflight-result.schema.json +367 -0
  70. package/dist/schemas/mb-init-review-result.schema.json +396 -0
  71. package/dist/schemas/mb-init-verify-result.schema.json +391 -0
  72. package/dist/schemas/memory-bootstrap-receipt-v2.schema.json +67 -0
  73. package/dist/schemas/memory-bootstrap-receipt.schema.json +354 -0
  74. package/dist/schemas/memory-composition-work-result.schema.json +38 -0
  75. package/dist/schemas/memory-repair-work-result.schema.json +49 -0
  76. package/dist/schemas/memory-review-correction-plan.schema.json +30 -0
  77. package/dist/schemas/memory-review-work-result.schema.json +43 -0
  78. package/dist/schemas/model-catalog.schema.json +153 -0
  79. package/dist/schemas/profile-settings.schema.json +122 -0
  80. package/dist/schemas/release-manifest.schema.json +291 -0
  81. package/dist/schemas/run-engine-binding.schema.json +1 -1
  82. package/dist/services/check-runner-evidence.js +51 -4
  83. package/dist/services/cli-operation-classifier.js +8 -47
  84. package/dist/services/cli-upgrade-allowlist.js +27 -0
  85. package/dist/services/code-checks.js +387 -34
  86. package/dist/services/controller-child-capacity.js +13 -0
  87. package/dist/services/controller-child-observer.js +18 -2
  88. package/dist/services/controller-execution.js +88 -0
  89. package/dist/services/controller-fanout.js +71 -12
  90. package/dist/services/dashboard.js +8 -2
  91. package/dist/services/descriptor-coordinator-session.js +35 -0
  92. package/dist/services/descriptor-root-work.js +53 -0
  93. package/dist/services/descriptor-run-settlement.js +105 -0
  94. package/dist/services/descriptor-run-start.js +449 -0
  95. package/dist/services/descriptor-stage-coordinator.js +197 -0
  96. package/dist/services/descriptor-stage-settlement.js +222 -0
  97. package/dist/services/descriptor-stage-start.js +139 -0
  98. package/dist/services/discovery/agy-discovery.js +2 -0
  99. package/dist/services/discovery/codex-discovery.js +2 -0
  100. package/dist/services/discovery/droid-discovery.js +2 -0
  101. package/dist/services/discovery/grok-discovery.js +2 -0
  102. package/dist/services/discovery/opencode-discovery.js +2 -0
  103. package/dist/services/discovery/static.js +31 -0
  104. package/dist/services/discovery/zcode-discovery.js +2 -0
  105. package/dist/services/engines.js +39 -22
  106. package/dist/services/eval-snapshots.js +83 -25
  107. package/dist/services/execution-bundles.js +263 -0
  108. package/dist/services/execution-policy.js +20 -16
  109. package/dist/services/executor-onboarding.js +56 -0
  110. package/dist/services/external-work-launch.js +93 -15
  111. package/dist/services/flow-catalog.js +21 -0
  112. package/dist/services/flow-descriptors.js +82 -0
  113. package/dist/services/frozen-engine.js +250 -0
  114. package/dist/services/generic-execution.js +164 -0
  115. package/dist/services/harness-adapter.js +28 -2
  116. package/dist/services/harness-discovery.js +29 -0
  117. package/dist/services/harness-doctor.js +75 -0
  118. package/dist/services/harness-probe-effects.js +51 -0
  119. package/dist/services/harness-version-probe.js +100 -0
  120. package/dist/services/hooks.js +24 -6
  121. package/dist/services/hybrid-contracts.js +483 -0
  122. package/dist/services/ids.js +29 -1
  123. package/dist/services/installation-lock.js +95 -0
  124. package/dist/services/installed-flow-source.js +183 -0
  125. package/dist/services/lifecycle-invocations.js +257 -78
  126. package/dist/services/managed-processes.js +31 -10
  127. package/dist/services/memory-accepted-composition.js +77 -0
  128. package/dist/services/memory-baseline-preservation.js +65 -0
  129. package/dist/services/memory-completed-correction-wave.js +320 -0
  130. package/dist/services/memory-compose-handler.js +258 -0
  131. package/dist/services/memory-composition-acceptance.js +199 -0
  132. package/dist/services/memory-composition-graph.js +134 -0
  133. package/dist/services/memory-composition-registration.js +146 -0
  134. package/dist/services/memory-composition-work-custody.js +151 -0
  135. package/dist/services/memory-composition-work-results.js +96 -0
  136. package/dist/services/memory-corrected-review-acceptance.js +129 -0
  137. package/dist/services/memory-corrected-review-batch.js +30 -0
  138. package/dist/services/memory-corrected-review-findings.js +106 -0
  139. package/dist/services/memory-corrected-review-inputs.js +210 -0
  140. package/dist/services/memory-corrected-review-projection.js +78 -0
  141. package/dist/services/memory-corrected-review-receipt.js +170 -0
  142. package/dist/services/memory-corrected-review-registration.js +191 -0
  143. package/dist/services/memory-correction-check-receipt.js +219 -0
  144. package/dist/services/memory-correction-input-owner.js +6 -0
  145. package/dist/services/memory-discover-handler.js +104 -0
  146. package/dist/services/memory-finish-evidence.js +435 -0
  147. package/dist/services/memory-finish-handler.js +215 -0
  148. package/dist/services/memory-finish-integration.js +280 -0
  149. package/dist/services/memory-finish-readiness.js +122 -0
  150. package/dist/services/memory-git-integration.js +151 -0
  151. package/dist/services/memory-historical-corrected-review.js +206 -0
  152. package/dist/services/memory-historical-original-review.js +130 -0
  153. package/dist/services/memory-historical-repair-registration.js +99 -0
  154. package/dist/services/memory-historical-second-review.js +128 -0
  155. package/dist/services/memory-init-bootstrap-readiness.js +210 -0
  156. package/dist/services/memory-init-contexts.js +89 -0
  157. package/dist/services/memory-init-controller-start.js +84 -0
  158. package/dist/services/memory-init-engine-release.js +223 -0
  159. package/dist/services/memory-init-git-policy.js +136 -0
  160. package/dist/services/memory-init-lint-capability.js +185 -0
  161. package/dist/services/memory-init-native-capacity.js +296 -0
  162. package/dist/services/memory-init-preflight.js +330 -0
  163. package/dist/services/memory-init-project-bootstrap.js +599 -0
  164. package/dist/services/memory-init-request-candidate.js +157 -0
  165. package/dist/services/memory-init-request-input.js +66 -0
  166. package/dist/services/memory-init-resource-storage.js +125 -0
  167. package/dist/services/memory-init-run-allocation.js +81 -0
  168. package/dist/services/memory-init-run-bundle.js +98 -0
  169. package/dist/services/memory-init-start.js +181 -0
  170. package/dist/services/memory-init-startup-custody.js +80 -0
  171. package/dist/services/memory-init-startup-input.js +240 -0
  172. package/dist/services/memory-init-storage.js +81 -0
  173. package/dist/services/memory-init-stores.js +359 -0
  174. package/dist/services/memory-init-worktrunk-capability.js +178 -0
  175. package/dist/services/memory-layer-publication.js +611 -0
  176. package/dist/services/memory-layer.js +156 -0
  177. package/dist/services/memory-lint-config.js +85 -0
  178. package/dist/services/memory-native-review-copy.js +165 -0
  179. package/dist/services/memory-normal-flow-entry.js +75 -0
  180. package/dist/services/memory-ordinary-defaults.js +112 -0
  181. package/dist/services/memory-preflight-bootstrap-custody.js +115 -0
  182. package/dist/services/memory-preflight-handler.js +259 -0
  183. package/dist/services/memory-repair-current-view.js +299 -0
  184. package/dist/services/memory-repair-document-delta.js +451 -0
  185. package/dist/services/memory-repair-history.js +491 -0
  186. package/dist/services/memory-repair-physical-binding.js +312 -0
  187. package/dist/services/memory-repair-publication-files.js +152 -0
  188. package/dist/services/memory-repair-publication-intent.js +27 -0
  189. package/dist/services/memory-repair-publication-plan.js +160 -0
  190. package/dist/services/memory-repair-publication-source.js +225 -0
  191. package/dist/services/memory-repair-publication.js +794 -0
  192. package/dist/services/memory-repair-registration.js +239 -0
  193. package/dist/services/memory-repair-work-baseline.js +413 -0
  194. package/dist/services/memory-repair-work-completion.js +97 -0
  195. package/dist/services/memory-repair-work-custody.js +73 -0
  196. package/dist/services/memory-repair-work-results.js +89 -0
  197. package/dist/services/memory-repair-workspace.js +252 -0
  198. package/dist/services/memory-repair-writer-copy.js +735 -0
  199. package/dist/services/memory-result-binding.js +173 -0
  200. package/dist/services/memory-retained-review-authority.js +129 -0
  201. package/dist/services/memory-retained-review-results.js +70 -0
  202. package/dist/services/memory-retained-second-baseline.js +102 -0
  203. package/dist/services/memory-retained-second-repairs.js +156 -0
  204. package/dist/services/memory-review-acceptance.js +214 -0
  205. package/dist/services/memory-review-correction-plan.js +171 -0
  206. package/dist/services/memory-review-handler.js +585 -0
  207. package/dist/services/memory-review-inputs.js +200 -0
  208. package/dist/services/memory-review-receipt.js +180 -0
  209. package/dist/services/memory-review-registration.js +108 -0
  210. package/dist/services/memory-review-work-custody.js +124 -0
  211. package/dist/services/memory-review-work-input.js +72 -0
  212. package/dist/services/memory-review-work-results.js +73 -0
  213. package/dist/services/memory-second-corrected-review-inputs.js +165 -0
  214. package/dist/services/memory-second-correction-plan.js +76 -0
  215. package/dist/services/memory-second-repair-baseline.js +91 -0
  216. package/dist/services/memory-second-repair-launch.js +614 -0
  217. package/dist/services/memory-second-repair-registration.js +88 -0
  218. package/dist/services/memory-second-repair-source.js +187 -0
  219. package/dist/services/memory-second-review-acceptance.js +115 -0
  220. package/dist/services/memory-source-observations.js +117 -0
  221. package/dist/services/memory-stage-dispatch.js +28 -0
  222. package/dist/services/memory-stage-results.js +199 -0
  223. package/dist/services/memory-verification-acceptance.js +135 -0
  224. package/dist/services/memory-verification-checks.js +252 -0
  225. package/dist/services/memory-verification-receipt.js +159 -0
  226. package/dist/services/memory-verify-handler.js +135 -0
  227. package/dist/services/memory-work-result-preparation.js +63 -0
  228. package/dist/services/merge-server.js +2 -4
  229. package/dist/services/model-catalog.js +109 -0
  230. package/dist/services/model-discovery.js +46 -0
  231. package/dist/services/operator-profiles.js +236 -0
  232. package/dist/services/projects.js +17 -2
  233. package/dist/services/{codex-capacity.js → provider-capacity.js} +34 -16
  234. package/dist/services/recovery-snapshot-database.js +417 -2
  235. package/dist/services/release-archive-producer.js +52 -0
  236. package/dist/services/release-archive.js +143 -0
  237. package/dist/services/release-cache.js +142 -0
  238. package/dist/services/release-compatibility.js +18 -0
  239. package/dist/services/release-download.js +67 -0
  240. package/dist/services/release-envelope.js +104 -0
  241. package/dist/services/release-manifest.js +45 -0
  242. package/dist/services/release-payload.js +114 -0
  243. package/dist/services/release-producer.js +63 -0
  244. package/dist/services/release-provenance.js +160 -0
  245. package/dist/services/release-registry.js +66 -0
  246. package/dist/services/release-source.js +194 -0
  247. package/dist/services/repair-intents.js +1 -1
  248. package/dist/services/review-copy.js +10 -2
  249. package/dist/services/run-control.js +80 -9
  250. package/dist/services/run-controller-adapter.js +1 -1
  251. package/dist/services/run-controller-capture.js +10 -3
  252. package/dist/services/run-controller-process.js +21 -0
  253. package/dist/services/run-controller-recovery.js +5 -5
  254. package/dist/services/run-controller-state.js +1 -1
  255. package/dist/services/run-controller.js +158 -64
  256. package/dist/services/run-recovery-guard.js +5 -3
  257. package/dist/services/run-recovery-runtime.js +3 -3
  258. package/dist/services/run-recovery.js +145 -5
  259. package/dist/services/run-start-custody.js +177 -0
  260. package/dist/services/runs.js +413 -30
  261. package/dist/services/setup.js +377 -0
  262. package/dist/services/stage-context.js +9 -1
  263. package/dist/services/stage-handlers.js +74 -0
  264. package/dist/services/stage-report-renderer.js +63 -0
  265. package/dist/services/stage-work-graph.js +17 -0
  266. package/dist/services/vnext-code-review.js +1 -1
  267. package/dist/services/vnext-code.js +6 -6
  268. package/dist/services/vnext-execution-profile.js +2 -2
  269. package/dist/services/vnext-fanout.js +73 -11
  270. package/dist/services/vnext-merge.js +8 -3
  271. package/dist/services/vnext-plan-review.js +7 -5
  272. package/dist/services/vnext-plan.js +3 -3
  273. package/dist/services/vnext-protocolize.js +20 -6
  274. package/dist/services/vnext-workspace-policy.js +7 -2
  275. package/dist/services/work-registry.js +616 -71
  276. package/dist/services/workspace-files.js +1 -1
  277. package/dist/storage/database.js +84 -6
  278. package/dist/storage/writer-contract.js +11 -7
  279. package/package.json +13 -3
  280. package/tools/create-canon-release.mjs +33 -0
  281. package/tools/produce-hybrid-release.mjs +41 -0
  282. package/dist/harness-runtime/lib/codex-capacity-policy.d.mts +0 -10
  283. package/dist/harness-runtime/lib/codex-capacity-policy.mjs +0 -102
@@ -91,7 +91,7 @@ export async function dispatchRuntimeProcess(context, action, parsed, prepared =
91
91
  return { ...receipt, ok: receipt.owned };
92
92
  }
93
93
  if (action === "admission") {
94
- const [{ controllerHitlAdmission }, { assertCapacityOwnerAdmission }] = await Promise.all([import("../services/run-controller-adapter.js"), import("../services/codex-capacity.js")]);
94
+ const [{ controllerHitlAdmission }, { assertCapacityOwnerAdmission }] = await Promise.all([import("../services/run-controller-adapter.js"), import("../services/provider-capacity.js")]);
95
95
  const record = managedProcessStatus(context).find(process => process.id === requiredOption(parsed, "id"));
96
96
  if (!record || record.lease_token !== requiredOption(parsed, "lease-token") || record.owner_id !== requiredOption(parsed, "owner") || record.state !== "running" || !record.pid || !record.pid_started_at || !processIsAlive(record))
97
97
  throw new AppError("process_ownership_unknown", "Productive dispatch requires its current confirmed runtime owner", 1);
@@ -0,0 +1,149 @@
1
+ import { prepareCommandRoute } from "./command-inputs.js";
2
+ import path from "node:path";
3
+ import { parseOutputOptions } from "./output-options.js";
4
+ import { resolveDdFlowHome } from "../storage/paths.js";
5
+ import { readSetupStatus, installAuthenticatedRelease, readRetainedSetupExecutor, hasDeferredSetupOnboarding, readSetupOnboarding, commitSetupOnboarding } from "../services/setup.js";
6
+ import { readTrustedReleaseProducers, readReleaseBundle } from "../services/release-source.js";
7
+ import { createReleaseCompatibility, installedCatalogVersions } from "../services/release-compatibility.js";
8
+ import { getCliVersionReport } from "../services/build-info.js";
9
+ import { readOperatorProfiles, readProfileCreationIntent } from "../services/operator-profiles.js";
10
+ import { onboardExecutor, selectOnboardingExecutor } from "../services/executor-onboarding.js";
11
+ import { askExecutorChoice } from "./executor-onboarding.js";
12
+ import { requiredOption, optionalOption } from "./argument-values.js";
13
+ import { AppError, errorRecord } from "../shared/errors.js";
14
+ import { hybridDigest } from "../services/hybrid-contracts.js";
15
+ import { prepareResponseDestination, writeResponseFile } from "./response-file.js";
16
+ import { readRegistryRelease } from "../services/release-registry.js";
17
+ import { writePreparationError } from "./error-output.js";
18
+ import { publishSetup } from "../harness-runtime/lib/setup-progress.mjs";
19
+ /** Lightweight global diagnostic ingress; never creates a runtime context. */
20
+ export async function runSetupCli(args, env, io = { stdout: process.stdout, stderr: process.stderr, stdin: process.stdin }) {
21
+ let json = false;
22
+ try {
23
+ const output = parseOutputOptions(args);
24
+ json = output.json;
25
+ if (output.responseFile)
26
+ prepareResponseDestination(output.responseFile);
27
+ const route = prepareCommandRoute(output.args);
28
+ const humanProgress = !output.json && !output.progressJsonl;
29
+ const reportHumanProgress = (message) => {
30
+ // Diagnostic delivery cannot turn a completed installation into failure.
31
+ try {
32
+ io.stderr.write(`setup: ${message}\n`);
33
+ }
34
+ catch { /* Best-effort human progress only. */ }
35
+ };
36
+ const home = resolveDdFlowHome(env);
37
+ let result;
38
+ if (route.key === "setup status")
39
+ result = { ok: true, action: "setup_status", observed_cli: getCliVersionReport().cli, ...readSetupStatus(home) };
40
+ else if (route.key === "setup" || route.key === "setup update") {
41
+ const parsed = route.parsed;
42
+ const source = optionalOption(parsed, "canon-source"), version = optionalOption(parsed, "canon-version");
43
+ const registryFile = optionalOption(parsed, "release-registry");
44
+ if (source && (version || registryFile))
45
+ throw new AppError("usage", "--canon-source is exclusive with --canon-version and --release-registry; see dd-flow setup --help", 2, { effect: "no_effect" });
46
+ const channel = (optionalOption(parsed, "channel") ?? "stable");
47
+ const selection = source ? null : readRegistryRelease(registryFile ?? env.DD_FLOW_RELEASE_REGISTRY ?? path.join(home, "release-registry.json"), channel, version);
48
+ const profile = optionalOption(parsed, "profile"), skip = parsed.options.has("skip-agent-setup");
49
+ const intentFile = optionalOption(parsed, "save-profile-intent");
50
+ if (intentFile && skip)
51
+ throw new AppError("usage", "--save-profile-intent conflicts with --skip-agent-setup", 2, { effect: "no_effect" });
52
+ let profileCreation = intentFile ? readProfileCreationIntent(intentFile) : undefined;
53
+ const stdin = io.stdin ?? process.stdin;
54
+ const nonInteractive = parsed.options.has("non-interactive") || output.json || output.progressJsonl
55
+ || !stdin.isTTY || !io.stderr.isTTY;
56
+ const profileFile = optionalOption(parsed, "profile-file");
57
+ const requestId = requiredOption(parsed, "request-id");
58
+ const retained = profile === undefined && profileFile === undefined && !intentFile && !skip ? readRetainedSetupExecutor(home, requestId) : null;
59
+ if (retained?.profileCreation)
60
+ profileCreation = retained.profileCreation;
61
+ const choice = { ...(profile !== undefined ? { profileId: profile } : {}),
62
+ ...(profileFile !== undefined ? { profileFile } : {}),
63
+ ...(retained?.executor ? { profile: retained.executor } : profileCreation && profile === undefined && profileFile === undefined ? { profile: profileCreation.profile } : {}),
64
+ skip: skip || retained !== null && retained.executor === null };
65
+ let deferred = hasDeferredSetupOnboarding(home, requestId);
66
+ let selected;
67
+ if (!deferred) {
68
+ try {
69
+ selected = selectOnboardingExecutor(choice, () => readOperatorProfiles(home), { allowSkip: true });
70
+ }
71
+ catch (error) {
72
+ if (!(error instanceof AppError) || error.code !== "agent_profile_missing" || nonInteractive
73
+ || profile !== undefined || profileFile !== undefined || intentFile || skip)
74
+ throw error;
75
+ deferred = true;
76
+ }
77
+ }
78
+ else if (profile !== undefined || profileFile !== undefined || intentFile || skip) {
79
+ // Explicit continuation inputs still fail before downloading or replaying.
80
+ selected = selectOnboardingExecutor(choice, () => readOperatorProfiles(home), { allowSkip: true });
81
+ }
82
+ const observedCli = getCliVersionReport().cli;
83
+ const compatibility = createReleaseCompatibility({ channel,
84
+ catalogVersions: installedCatalogVersions, routerVersion: observedCli.version });
85
+ const producers = readTrustedReleaseProducers(requiredOption(parsed, "trust-file"));
86
+ const bundle = await readReleaseBundle(source ?? selection.envelope_url, producers, compatibility, {
87
+ ...(selection ? { expectedVersion: selection.version } : {}),
88
+ ...((output.progressJsonl || humanProgress) ? { onProgress: (progress) => {
89
+ if (output.progressJsonl)
90
+ io.stdout.write(`${JSON.stringify({ type: "setup_download_progress", request_id: requestId, ...progress })}\n`);
91
+ else if (progress.received_bytes === progress.expected_bytes)
92
+ reportHumanProgress(`${progress.artifact} archive received (${progress.received_bytes} bytes); integrity validation follows`);
93
+ } } : {})
94
+ });
95
+ if (selection && bundle.authenticated.manifest.version !== selection.version)
96
+ throw new AppError("release_registry", "Authenticated release differs from selected registry version", 1, { reason: "selected_version_mismatch", phase: "release_selection", effect: "no_effect", help: "setup" });
97
+ const installed = installAuthenticatedRelease(home, { requestId, action: route.key === "setup update" ? "update" : "install",
98
+ envelope: bundle.envelope, trustedProducers: producers, compatibility, canonArchive: bundle.canonArchive, engineArchive: bundle.engineArchive,
99
+ ...(deferred ? { deferredOnboarding: true } : { executor: selected.profile, ...(profileCreation ? { profileCreation } : {}) }),
100
+ onCheckpoint: phase => {
101
+ publishSetup("dependency_check_completed", `release-setup:${phase}`);
102
+ if (output.progressJsonl)
103
+ io.stdout.write(`${JSON.stringify({ type: "setup_progress", request_id: requestId, phase })}\n`);
104
+ else if (humanProgress)
105
+ reportHumanProgress({ prepared: "verified release request retained", canon_cached: "canon cache verified", engine_cached: "engine cache verified",
106
+ profiles_saved: "profile settings saved", committed: "installation committed; runtime qualification remains unknown" }[phase]);
107
+ } });
108
+ if (humanProgress && installed.replayed)
109
+ reportHumanProgress("exact request replay verified; retained selection was not reactivated");
110
+ if (deferred) {
111
+ const saved = readSetupOnboarding(home, requestId);
112
+ const explicit = profile !== undefined || profileFile !== undefined || intentFile !== undefined || skip;
113
+ const deferredChoice = saved && !explicit ? { ...(saved.executor ? { profile: saved.executor } : { skip: true }),
114
+ ...(saved.profileCreation ? { profileCreation: saved.profileCreation } : {}) } : choice;
115
+ try {
116
+ selected ??= await onboardExecutor(deferredChoice, () => readOperatorProfiles(home), { allowSkip: true, nonInteractive }, settings => askExecutorChoice(settings, { stdin, stderr: io.stderr }));
117
+ const consent = commitSetupOnboarding(home, requestId, { executor: selected.profile,
118
+ ...(profileCreation ?? selected.profileCreation ? { profileCreation: profileCreation ?? selected.profileCreation } : {}) });
119
+ installed.executor = consent.executor;
120
+ if (humanProgress)
121
+ reportHumanProgress("executor onboarding committed after installation");
122
+ }
123
+ catch (error) {
124
+ if (error instanceof AppError) {
125
+ error.details.installation = "committed";
126
+ error.details.request_id = requestId;
127
+ error.details.next_action = "Replay the same setup request to resume executor onboarding; installation remains retained";
128
+ }
129
+ throw error;
130
+ }
131
+ }
132
+ result = { ...installed, action: route.key === "setup update" ? "setup_update" : "setup_install", observed_cli: observedCli,
133
+ executor: installed.executor ? { profile_id: installed.executor.id, revision: hybridDigest(installed.executor), readiness: "unknown_requires_qualification" } : { profile_id: null, readiness: "not_configured" },
134
+ ...readSetupStatus(home) };
135
+ }
136
+ else
137
+ throw new AppError("usage", "Unknown setup command; see dd-flow setup --help", 2, { effect: "no_effect" });
138
+ io.stdout.write(`${JSON.stringify(writeResponseFile(result, output), null, json ? undefined : 2)}\n`);
139
+ return 0;
140
+ }
141
+ catch (error) {
142
+ const record = errorRecord(error);
143
+ writePreparationError(io, args, record);
144
+ if (!json && error instanceof AppError && error.details.installation === "committed") {
145
+ io.stderr.write("setup: installation remains committed; replay the same request to resume executor onboarding\n");
146
+ }
147
+ return record.exitCode;
148
+ }
149
+ }
package/dist/cli.js CHANGED
@@ -1,14 +1,68 @@
1
1
  #!/usr/bin/env node
2
2
  import { isNativeHookIngress } from "./cli/native-hook-ingress.js";
3
3
  import { isRuntimeMaintenanceIngress, runRuntimeMaintenanceIngress } from "./cli/runtime-maintenance-ingress.js";
4
+ import { resolveHelp } from "./cli/help.js";
5
+ import { errorRecord } from "./shared/errors.js";
6
+ import { writePreparationError } from "./cli/error-output.js";
4
7
  import { publishSetup } from './harness-runtime/lib/setup-progress.mjs';
5
8
  const args = process.argv.slice(2);
6
- const managedHook = Boolean(process.env.DD_FLOW_CODEX_STATE_DIR) && args.slice(0, 3).join(" ") === "codex hook handle" && !args.includes("--help") && !args.includes("-h");
9
+ let help;
10
+ try {
11
+ help = resolveHelp(args);
12
+ }
13
+ catch (error) {
14
+ const record = errorRecord(error);
15
+ writePreparationError({ stderr: process.stderr }, args, record);
16
+ process.exit(record.exitCode);
17
+ }
18
+ if (help) {
19
+ process.stdout.write(`${help.json ? JSON.stringify(help.data) : help.text}\n`);
20
+ process.exit(0);
21
+ }
22
+ if (args[0] === "completion") {
23
+ const { runCompletionCli } = await import("./cli/completion-cli.js");
24
+ process.exit(runCompletionCli(args));
25
+ }
26
+ if (args[0] === "profiles") {
27
+ const { runOperatorProfilesCli } = await import("./cli/operator-profiles.js");
28
+ process.exit(runOperatorProfilesCli(args, process.env));
29
+ }
30
+ if (args[0] === "setup") {
31
+ const { runSetupCli } = await import("./cli/setup.js");
32
+ process.exit(await runSetupCli(args, process.env));
33
+ }
34
+ if (args[0] === "harnesses") {
35
+ const { runHarnessesCli } = await import("./cli/harnesses.js");
36
+ process.exit(await runHarnessesCli(args, process.env));
37
+ }
38
+ if (args[0] === "models") {
39
+ const { runModelsCli } = await import("./cli/models.js");
40
+ process.exit(runModelsCli(args));
41
+ }
42
+ if (args[0] === "flow" && ["list", "show"].includes(args[1] ?? "")) {
43
+ const { runFlowCatalogCli } = await import("./cli/flows.js");
44
+ process.exit(runFlowCatalogCli(args));
45
+ }
46
+ if (args[0] === "init" || args[0] === "flow" && args[1] === "mb-init") {
47
+ publishSetup('ingress_entered');
48
+ const { runMemoryInitCli } = await import("./cli/memory-init.js");
49
+ publishSetup('imports_ready');
50
+ process.exit(await runMemoryInitCli(args, process.env));
51
+ }
52
+ if (args.slice(0, 3).join(" ") === "runtime storage admit") {
53
+ const { runMemoryStorageAdmissionCli } = await import("./cli/memory-storage-admission.js");
54
+ process.exit(runMemoryStorageAdmissionCli(args));
55
+ }
56
+ if (args.slice(0, 3).join(" ") === "runtime flow admit") {
57
+ const { runMemoryFlowAdmissionCli } = await import("./cli/memory-flow-admission.js");
58
+ process.exit(await runMemoryFlowAdmissionCli(args));
59
+ }
60
+ const managedHook = Boolean(process.env.DD_FLOW_CODEX_STATE_DIR) && args.slice(0, 3).join(" ") === "codex hook handle";
7
61
  const exitCode = isRuntimeMaintenanceIngress(args, process.env)
8
62
  ? await runRuntimeMaintenanceIngress(args)
9
63
  : managedHook
10
64
  ? await (await import("./cli/hook-ingress.js")).runHookIngress(args)
11
- : isNativeHookIngress(args) && !args.includes("--help") && !args.includes("-h")
65
+ : isNativeHookIngress(args)
12
66
  ? await (await import("./cli/native-hook-ingress.js")).runNativeHookIngress(args)
13
67
  : await (async () => {
14
68
  publishSetup('ingress_entered');
@@ -0,0 +1,20 @@
1
+ /** Engine-owned identities shared by inspection and executable registration.
2
+ * Keep this module dependency-free: catalog inspection must not load handlers.
3
+ */
4
+ export const sdlcHandlerIdentities = Object.freeze({
5
+ specify: Object.freeze({ stage: "specify", entryId: "sdlc-specify-entry", finishId: "sdlc-specify-finish" }),
6
+ protocolize: Object.freeze({ stage: "protocolize", entryId: "sdlc-protocolize-entry", finishId: "sdlc-protocolize-finish" }),
7
+ plan: Object.freeze({ stage: "plan", entryId: "sdlc-plan-entry", finishId: "sdlc-plan-finish" }),
8
+ "plan-review": Object.freeze({ stage: "plan-review", entryId: "sdlc-plan-review-entry", finishId: "sdlc-plan-review-finish" }),
9
+ code: Object.freeze({ stage: "code", entryId: "sdlc-code-entry", finishId: "sdlc-code-finish" }),
10
+ "code-review": Object.freeze({ stage: "code-review", entryId: "sdlc-code-review-entry", finishId: "sdlc-code-review-finish" }),
11
+ merge: Object.freeze({ stage: "merge", entryId: "sdlc-merge-entry", finishId: "sdlc-merge-finish" })
12
+ });
13
+ export const memoryInitHandlerIdentities = Object.freeze({
14
+ preflight: Object.freeze({ stage: "preflight", entryId: "memory-stage-entry", finishId: "memory-stage-finish" }),
15
+ discover: Object.freeze({ stage: "discover", entryId: "memory-stage-entry", finishId: "memory-stage-finish" }),
16
+ compose: Object.freeze({ stage: "compose", entryId: "memory-stage-entry", finishId: "memory-stage-finish" }),
17
+ verify: Object.freeze({ stage: "verify", entryId: "memory-stage-entry", finishId: "memory-stage-finish" }),
18
+ review: Object.freeze({ stage: "review", entryId: "memory-stage-entry", finishId: "memory-stage-finish" }),
19
+ finish: Object.freeze({ stage: "finish", entryId: "memory-stage-entry", finishId: "memory-stage-finish" })
20
+ });
@@ -0,0 +1 @@
1
+ export function inspectDaemonOperation(root: string, id: string): Promise<Record<string, unknown>>;
@@ -8,6 +8,7 @@ import { errorRecord, isObservationLoss, isProvenShellCorrection, reportedError
8
8
  import { assertDaemonOwnership, budgetObservationScope, releaseBudgetObservation, completeBudgetOperation } from "./managed-daemon.mjs";
9
9
  import { readModelObservations, modelAttribution } from "./model-observations.mjs";
10
10
  import { publishOperationProgress, publishOperationWaiting, withOperationProgress } from './operation-progress.mjs';
11
+ import { prepareDaemonRequest } from "./native-request-proof.mjs";
11
12
 
12
13
  const productive = new Set(["session.create", "session.prompt", "session.start", "session.fork", "session.resume"]);
13
14
  const controls = new Map();
@@ -301,7 +302,7 @@ export async function durableDaemonStart(options, action) {
301
302
  // scope can reuse this exact adapter launch after retiring the old daemon.
302
303
  const launch = { schema_id: "dd-flow/native-launch@1", executable: options.entryPath ?? null, args: process.argv.slice(2), cwd: process.cwd(), runtime_owner: process.env.DD_FLOW_RUNTIME_OWNER ?? null,
303
304
  env: Object.fromEntries(["CODEX_HOME", "DD_FLOW_CONFIG_HOME", "DD_FLOW_HOME", "DD_FLOW_BIN", "DD_FLOW_RESOURCE_HOME"].map(key => [key, process.env[key] ?? null])) };
304
- const request = { id: process.env.DD_EVAL_OPERATION_ID ?? randomUUID(), operation: "daemon.start", params: { ...options, runtimeOwner: process.env.DD_FLOW_RUNTIME_OWNER ?? null, resourceHome: process.env.DD_FLOW_RESOURCE_HOME ?? null, launch } };
305
+ const request = prepareDaemonRequest({ id: process.env.DD_EVAL_OPERATION_ID ?? randomUUID(), operation: "daemon.start", params: { ...options, runtimeOwner: process.env.DD_FLOW_RUNTIME_OWNER ?? null, resourceHome: process.env.DD_FLOW_RESOURCE_HOME ?? null, launch } });
305
306
  return await journalDispatch(root, request, () => action(options));
306
307
  }
307
308
 
@@ -314,13 +315,13 @@ async function journalDispatch(root, request, action) {
314
315
  const daemon = await read(path.join(root, "daemon.json"));
315
316
  const startup = request.operation === "daemon.start";
316
317
  const daemonId = daemon?.daemon_id ?? daemon?.config?.daemonId ?? null;
317
- await writeJsonAtomic(temporary, { schema_id: "dd-flow/daemon-operation@1", operation_id: request.id, operation: request.operation, session_id: request.params?.sessionId ?? null, daemon_id: startup ? null : daemonId, owner_id: daemon?.config?.runtime_owner?.owner_id ?? null, generation: dispatchGenerations.get(root) ?? 0, ...(startup ? { predecessor_daemon_id: daemonId, launch: request.params?.launch } : {}), params_sha256: fingerprint, requested_at: new Date().toISOString() });
318
+ await writeJsonAtomic(temporary, { schema_id: "dd-flow/daemon-operation@1", operation_id: request.id, operation: request.operation, session_id: request.params?.sessionId ?? null, daemon_id: startup ? null : daemonId, owner_id: daemon?.config?.runtime_owner?.owner_id ?? null, generation: dispatchGenerations.get(root) ?? 0, ...(startup ? { predecessor_daemon_id: daemonId, launch: request.params?.launch } : {}), params_sha256: fingerprint, ...(request.invocation_sha256 ? { invocation_sha256: request.invocation_sha256 } : {}), requested_at: new Date().toISOString() });
318
319
  try {
319
320
  await link(temporary, path.join(directory, "requested.json"));
320
321
  } catch (error) {
321
322
  if (error.code !== "EEXIST") throw error;
322
323
  const saved = await inspectDaemonOperation(root, request.id);
323
- if (saved.params_sha256 !== fingerprint) throw Object.assign(new Error("operation id was reused with different parameters"), { code: "operation_conflict", details: { operation_id: request.id } });
324
+ if (saved.params_sha256 !== fingerprint || (saved.invocation_sha256 ?? null) !== (request.invocation_sha256 ?? null)) throw Object.assign(new Error("operation id was reused with different parameters or invocation"), { code: "operation_conflict", details: { operation_id: request.id } });
324
325
  if (saved.state === "completed") return saved.result;
325
326
  if (["failed", "interrupted"].includes(saved.state)) throw reportedError(saved.error, "provider operation failed");
326
327
  throw Object.assign(new Error("provider operation has no confirmed terminal result; inspect the existing operation instead of resending it"), { code: "operation_observation_lost", details: { operation_id: request.id, receipt_directory: directory } });
@@ -1,8 +1,10 @@
1
+ import { managedFlowEnv } from './managed-flow-env.mjs';
1
2
  import { SETUP_STARTUP_MS, startupObserver, observationDetails } from './setup-progress.mjs';
2
3
  import { callDaemonObserved } from './daemon-observation.mjs';
3
4
  import { operationProgressReporter } from './operation-progress.mjs';
5
+ import { prepareDaemonRequest } from "./native-request-proof.mjs";
4
6
  import { captureDispatchGuard, cancelPendingDispatch } from "./dispatch-fence.mjs";
5
- import { errorRecord } from "./operation-errors.mjs";
7
+ import { errorRecord, isProvenShellCorrection } from "./operation-errors.mjs";
6
8
  import { ToolObservations, agyToolObservation, agyTranscriptObservations } from "./tool-observations.mjs";
7
9
  import { constants } from "node:fs";
8
10
  import { fileURLToPath } from "node:url";
@@ -18,6 +20,7 @@ import { durableDaemonDispatch, completeDaemonShutdown, assertDaemonDispatch, as
18
20
  import path from "node:path";
19
21
  import { observeModel } from "./model-observations.mjs";
20
22
  import { AGY_VERSION_TIMEOUT_MS, AgyError, agyTerminalFailure, assertProfile, executable, observedProfile, runAgy, usageSnapshot } from "./dd-agy.mjs";
23
+ import { settledNativeTree } from "./native-children.mjs";
21
24
  import { prepareRuntimeOwner, cleanupFailedStart, cleanupFailedDaemonStart, confirmDaemonProcess, confirmDaemonStopped, finishDaemonProcess, scheduleDaemonProcessHeartbeat, registerDaemonProcess, stopProcessGroup } from "./managed-daemon.mjs";
22
25
  import { readNativeLines, streamNativeDiagnostics, nativeObservationError, admitNativeRequestBuffer } from "./native-streams.mjs";
23
26
 
@@ -58,7 +61,7 @@ async function writeJson(file, value) { const temporary = `${file}.${process.pid
58
61
  async function readState(dir) { try { return JSON.parse(await readFile(locations(dir).state, "utf8")); } catch (error) { if (error.code === "ENOENT") return null; throw error; } }
59
62
  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; } }
60
63
  const shell = value => `'${String(value).replaceAll("'", `'"'"'`)}'`;
61
- 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","DD_FLOW_DAEMON_ID"]) env[key] = undefined; if (config.ddFlowHome && config.daemonId) env.DD_FLOW_DAEMON_ID = config.daemonId; return env; }
64
+ 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 { ...env, ...managedFlowEnv(config) }; }
62
65
  const errorPayload = errorRecord;
63
66
  async function forwardUsage(config, sessionId, result, tools, descendants) {
64
67
  if (!config.ddFlowBin || !config.projectRoot) return;
@@ -164,7 +167,7 @@ export async function qualifyAgyHooks(projectRoot, { stateDir = null, install =
164
167
  }
165
168
 
166
169
  export async function callDaemon(stateDir, operation, params = {}, timeoutMs = 30_000, requestId = null) {
167
- const request = { schema_id: REQUEST_SCHEMA, id: requestId ?? process.env.DD_EVAL_OPERATION_ID ?? randomUUID(), operation, params };
170
+ const request = prepareDaemonRequest({ schema_id: REQUEST_SCHEMA, id: requestId ?? process.env.DD_EVAL_OPERATION_ID ?? randomUUID(), operation, params });
168
171
  return await callDaemonObserved({ stateDir, socket: locations(stateDir).socket, request, responseSchema: RESPONSE_SCHEMA, harness: 'agy', timeoutMs });
169
172
  }
170
173
 
@@ -339,7 +342,9 @@ export class Runtime {
339
342
  if (!this.init) this.startupError ??= agyTerminalFailure(event.result, event.terminal_metadata);
340
343
  this.receivedTerminalMetadata = event.terminal_metadata;
341
344
  }
342
- this.observeProviderActivity(event); 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") { if (this.startupError) throw this.startupError; await this.finishTurn(event.result ?? {}); } } }
345
+ this.observeProviderActivity(event);
346
+ if (event.event === "result" && this.init && !this.startupError) { await this.finishTurn(event.result ?? {}, event.terminal_metadata, event); continue; }
347
+ 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") { if (this.startupError) throw this.startupError; await this.finishTurn(event.result ?? {}); } } }
343
348
  observeStep(step) {
344
349
  const sessionId = step.conversation_id ?? this.init?.conversation_id, observation = this.sessionObservations.get(sessionId) ?? {};
345
350
  const fresh = Number.isInteger(step.step_index) && step.step_index > (observation.last_step_index ?? -1) && step.step_index > (observation.step_floor ?? -1);
@@ -376,7 +381,7 @@ export class Runtime {
376
381
  }
377
382
  }
378
383
  }
379
- async finishTurn(result, retainedMetadata = null) {
384
+ async finishTurn(result, retainedMetadata = null, providerEvent = null) {
380
385
  if (result.status === "RUNNING") { await this.persist({ active_tree: true }); return; }
381
386
  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 });
382
387
  if (result.conversation_id && result.conversation_id !== this.init?.conversation_id) {
@@ -389,40 +394,51 @@ export class Runtime {
389
394
  return;
390
395
  }
391
396
  const current = this.active;
392
- const terminalMetadata = retainedMetadata ?? this.receivedTerminalMetadata ?? (this.lastTerminalFailure && this.lastTerminalFailure.details?.provider_result?.error === result.error
397
+ const terminalMetadata = { received_at: new Date().toISOString(), ...(retainedMetadata ?? this.receivedTerminalMetadata ?? (this.lastTerminalFailure && this.lastTerminalFailure.details?.provider_result?.error === result.error
393
398
  && this.lastTerminalFailure.details.provider_session_id === (result.conversation_id || this.init?.conversation_id || null)
394
399
  && (current?.previousError === result.error || this.lastResult?.status === "ERROR" && this.lastResult.error === result.error) ? this.lastTerminalFailure.details
395
400
  : { observed_at: new Date().toISOString(), provider_session_id: result.conversation_id || this.init?.conversation_id || null, operation_id: current?.operationId ?? null,
396
- turn_generation: current?.generation ?? null, journal_locator: this.paths.journal, native_source: "agy.result" });
401
+ turn_generation: current?.generation ?? null, journal_locator: this.paths.journal, native_source: "agy.result" })) };
397
402
  this.receivedTerminalMetadata = null;
398
403
  // The ordered stream returns one result per input. Model usage counters
399
404
  // are not request IDs: native command rejections can report zero turns.
400
405
  // prompt() drains earlier events before accepting another input.
401
406
  if (!current && this.lastResult) { await this.journal("stale_terminal_observed", { result }); return; }
402
- await this.captureChildTools();
403
407
  this.lastResult = result; this.markTerminalActivity(result);
404
408
  const rootStop = this.sessionObservations.get(this.init?.conversation_id)?.stop;
405
409
  const pendingTerminal = current && result.status === "SUCCESS";
406
- if (pendingTerminal) current.terminalResult = result;
410
+ if (current) { current.terminalResult = result; current.terminalMetadata = terminalMetadata; current.terminalPersisted = false; }
407
411
  if (pendingTerminal && rootStop?.fullyIdle === true) for (const [id, descendant] of this.descendants) if (descendant.status === "unknown") this.descendants.set(id, { ...descendant, status: "settled_by_root", settlement_evidence: "root_fully_idle_stop" });
408
- const terminalReceipt = pendingTerminal ? this.receipt(result) : null;
409
- await this.persist({ active_tree: pendingTerminal ? !terminalReceipt.settled : result.status === "RUNNING" || rootStop?.fullyIdle === false, last_result: result });
412
+ this.state.terminal_observation_pending = true;
413
+ try {
414
+ await this.persist({ active_tree: true, last_result: result, last_terminal_metadata: terminalMetadata });
415
+ if (current) current.terminalPersisted = true;
416
+ if (providerEvent) await this.journal("provider_event", { event: providerEvent });
417
+ await this.captureChildTools();
418
+ } catch (error) { return this.failTerminalObservation(current, result, terminalMetadata, error); }
410
419
  try { this.usageIngest = await forwardUsage(this.config, result.conversation_id ?? this.init.conversation_id, result, this.toolSnapshot(), [...this.descendants.values()]); }
411
- catch (error) { this.usageIngest = { status: "failed", error: error.message ?? String(error) }; await this.journal("usage_ingest_failed", this.usageIngest); }
420
+ 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); } }
421
+ try { await this.persist({ terminal_observation_pending: false }); }
422
+ catch (error) { this.state.terminal_observation_pending = true; return this.failTerminalObservation(current, result, terminalMetadata, error); }
423
+ this.state.terminal_observation_pending = false;
412
424
  if (!current) return;
413
425
  if (this.active !== current) return;
414
426
  if (result.status === "ERROR" || result.status === "CANCELLED") {
415
427
  this.active = null;
416
- this.lastTerminalFailure = agyTerminalFailure(result, { ...terminalMetadata, provider_error_scope: current.previousError && current.previousError === result.error ? "repeated_conversation_error" : "terminal_result", usage_ingest: this.usageIngest });
417
- await this.persist({ last_terminal_failure: errorRecord(this.lastTerminalFailure) });
428
+ const settled = this.receipt().settled;
429
+ this.lastTerminalFailure = agyTerminalFailure(result, { ...terminalMetadata, native_turn_items: { observed: settled, possible_effects: true, pending: !settled }, provider_error_scope: current.previousError && current.previousError === result.error ? "repeated_conversation_error" : "terminal_result", usage_ingest: this.usageIngest });
430
+ try { await this.persist({ active_tree: !settled, last_terminal_failure: errorRecord(this.lastTerminalFailure) }); }
431
+ catch (error) { return this.failTerminalObservation(current, result, terminalMetadata, error); }
418
432
  return current.reject(this.lastTerminalFailure);
419
433
  }
420
- await this.completePendingTurn();
434
+ try { await this.completePendingTurn(); }
435
+ catch (error) { return this.failTerminalObservation(current, result, terminalMetadata, error); }
421
436
  }
422
437
  async completePendingTurn() {
423
438
  await this.observeHookFailures();
424
439
  const current = this.active;
425
- if (!current?.terminalResult || current.terminalResult !== this.lastResult) return;
440
+ if (current?.observationError) throw current.observationError;
441
+ if (current?.terminalResult?.status !== "SUCCESS" || current.terminalResult !== this.lastResult) return;
426
442
  await this.persist({ active_tree: !this.receipt().settled });
427
443
  // Hooks can invalidate the tree while persistence is pending. Never
428
444
  // publish a proof captured before that asynchronous boundary.
@@ -432,9 +448,20 @@ export class Runtime {
432
448
  this.active = null;
433
449
  current.resolve(receipt);
434
450
  }
451
+ failTerminalObservation(current, result, metadata, cause) {
452
+ this.state.terminal_observation_pending = true;
453
+ 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 } };
454
+ const error = ["ERROR", "CANCELLED"].includes(result.status)
455
+ ? agyTerminalFailure(result, details)
456
+ : new DaemonError("native_outcome_observation_failed", "Antigravity terminal was received but mandatory observation failed", false, details);
457
+ error.cause = cause;
458
+ if (result.status !== "SUCCESS") this.lastTerminalFailure = error;
459
+ if (current) { current.observationError = error; current.reject(error); }
460
+ throw error;
461
+ }
435
462
  receipt(result = this.lastResult) {
436
463
  if (this.state.last_terminal_failure && !this.lastTerminalFailure) this.lastTerminalFailure = this.state.last_terminal_failure;
437
- const terminalMetadata = this.lastTerminalFailure && this.lastTerminalFailure.details?.provider_result?.error === result?.error
464
+ const terminalMetadata = 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
438
465
  && this.lastTerminalFailure.details.provider_session_id === (result?.conversation_id || this.init?.conversation_id || null) ? this.lastTerminalFailure.details : null;
439
466
  const assistantText = typeof result?.response === "string" ? result.response : typeof result?.text === "string" ? result.text : null;
440
467
  const root = this.init?.conversation_id; const observation = this.sessionObservations.get(root);
@@ -444,7 +471,8 @@ export class Runtime {
444
471
  // non-idle Stop still wins, and every discovered child must be terminal.
445
472
  const rootSettled = observation?.stop?.fullyIdle !== false;
446
473
  const terminalPending = this.active?.terminalResult === result;
447
- return { 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 } : {}), ...(assistantText === null ? {} : { assistant_text: assistantText }), usage: usageSnapshot(result ?? {}, this.toolSnapshot()), usage_ingest: this.usageIngest, descendants: [...this.descendants.values()], transcript_path: observation?.transcript_path ?? null, settled: Boolean(!this.state.unclaimed_activity && (!this.active || terminalPending) && result && ["SUCCESS", "ERROR", "CANCELLED"].includes(result.status) && treeSettled && rootSettled) };
474
+ const settled = Boolean(!this.state.terminal_observation_pending && !this.active?.observationError && !this.state.unclaimed_activity && (!this.active || terminalPending) && result && ["SUCCESS", "ERROR", "CANCELLED"].includes(result.status) && treeSettled && rootSettled);
475
+ return { 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 }), 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)), transcript_path: observation?.transcript_path ?? null, settled };
448
476
  }
449
477
  async zeroUsage() { await forwardUsage(this.config, this.init.conversation_id, { usage: {} }, this.toolSnapshot(), []); }
450
478
  async prompt(text, assertDispatch = captureDispatchGuard(this), operationId = null, livenessTimeoutMs = 1_800_000) {
@@ -498,9 +526,14 @@ export class Runtime {
498
526
  checking = true; const cursor = this.progress?.cursor;
499
527
  void this.completePendingTurn().then(() => {
500
528
  if (this.active !== current || this.progress?.cursor !== cursor || !clock.sample(this.progress)) return;
529
+ if (current.terminalResult) return this.failTerminalObservation(current, current.terminalResult, current.terminalMetadata, new Error("Native tree settlement was not confirmed within the productive silence window"));
501
530
  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 }));
502
531
  }).catch(error => {
503
- if (this.active === current && this.progress?.cursor === cursor) 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) }));
532
+ if (this.active === current && this.progress?.cursor === cursor) {
533
+ if (current.observationError) current.reject(current.observationError);
534
+ else if (current.terminalResult) { try { this.failTerminalObservation(current, current.terminalResult, current.terminalMetadata, error); } catch { /* reply owns the typed failure */ } }
535
+ 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) }));
536
+ }
504
537
  }).finally(() => { checking = false; });
505
538
  }, Math.max(10, Math.min(1_000, livenessTimeoutMs / 4)));
506
539
  try {
@@ -536,9 +569,7 @@ export class Runtime {
536
569
  }
537
570
  async rejectHook(error, event, payload, current = this.active) {
538
571
  if (current && this.active !== current) return;
539
- const correctable = current && event === "PreToolUse" && error.code === "lifecycle_shell_syntax_invalid"
540
- && error.details?.effect === "no_effect" && error.details?.recoverable === true
541
- && error.details?.disposition === "pre_cli_shell_refusal" && error.details?.native_pre_cli_refusal === true;
572
+ const correctable = current && event === "PreToolUse" && isProvenShellCorrection(error);
542
573
  if (correctable && current.hookCorrectionGeneration !== current.generation) {
543
574
  // The request was correlated with this still-active Turn by
544
575
  // observeHookFailures. Persist before allowing native same-Turn recovery.
@@ -4,6 +4,7 @@ import path from "node:path";
4
4
  import { SETUP_STARTUP_MS } from './setup-progress.mjs';
5
5
 
6
6
  import { checkObservedProfile } from "./model-observations.mjs";
7
+ import { classifyProviderError, safeProviderError } from "./provider-overload-policy.mjs";
7
8
 
8
9
  const DEFAULT_MODEL = "gemini-3.1-pro-high";
9
10
  export const AGY_VERSION_TIMEOUT_MS = 30_000;
@@ -13,11 +14,21 @@ export class AgyError extends Error {
13
14
  }
14
15
 
15
16
  export function agyTerminalFailure(result, metadata = {}) {
16
- const message = String(result.error ?? "Antigravity returned ERROR"), normalized = message.toLowerCase();
17
- const code = result.status === "CANCELLED" ? "agy_turn_cancelled" : /quota|insufficient.?credit|billing|usage limit/.test(normalized) ? "agy_provider_quota_exhausted"
18
- : /rate.?limit|too many requests/.test(normalized) ? "agy_provider_rate_limited" : /\b429\b/.test(normalized) ? "agy_provider_limit_unknown"
19
- : /auth|unauthori[sz]ed|forbidden|account|eligib/.test(normalized) ? "agy_provider_account" : "agy_provider_failed";
20
- return new AgyError(code, message, code === "agy_provider_rate_limited", { provider_result: result, ...metadata });
17
+ const native = typeof result.error === "object" && result.error ? result.error : { message: result.error ?? "Antigravity returned ERROR" };
18
+ const category = classifyProviderError(native);
19
+ const legacy = { provider_quota_exhausted: "agy_provider_quota_exhausted", provider_limit_unknown: "agy_provider_limit_unknown", provider_auth_failed: "agy_provider_account", provider_failed: "agy_provider_failed" };
20
+ let code = result.status === "CANCELLED" ? "agy_turn_cancelled" : legacy[category] ?? category;
21
+ // A local generation/usage count is not a native request identity.
22
+ const identity = result.turn_id ?? result.request_id;
23
+ const turn = typeof identity === "string" && identity ? identity : null;
24
+ const proofMissing = result.status === "ERROR" && ["provider_capacity_transient", "provider_rate_limited"].includes(category) && !turn;
25
+ if (proofMissing) code = "provider_overload_evidence_unavailable";
26
+ return new AgyError(code, String(native.message ?? "Antigravity returned ERROR"), code === "provider_capacity_transient" || code === "provider_rate_limited", {
27
+ provider_result: result, ...metadata, provider_error: safeProviderError(native), native_source: "agy.result", terminal_status: result.status === "ERROR" ? "failed" : "cancelled",
28
+ ...(turn ? { turn_id: turn, native_turn_id: turn } : {}),
29
+ ...(proofMissing ? { provider_category: category, reason: "native_turn_identity_unavailable" } : {}),
30
+ will_retry: native.willRetry === true, native_retry_active: native.willRetry === true
31
+ });
21
32
  }
22
33
 
23
34
  export function executable(bin, args = []) {
@@ -1,20 +1,24 @@
1
+ import { managedFlowEnv } from './managed-flow-env.mjs';
1
2
  import { SETUP_STARTUP_MS, startupObserver, observationDetails } from './setup-progress.mjs';
2
3
  import { callDaemonObserved } from './daemon-observation.mjs';
3
4
  import { admitNativeRequestBuffer } from "./native-streams.mjs";
5
+ import { prepareDaemonRequest } from "./native-request-proof.mjs";
4
6
  import { captureDispatchGuard, cancelPendingDispatch } from "./dispatch-fence.mjs";
5
7
  import { createHash, randomUUID } from "node:crypto";
6
8
  import { chmod, lstat, mkdir, open, readFile, rename, unlink, writeFile } from "node:fs/promises";
7
9
  import net from "node:net";
8
10
  import { durableDaemonDispatch, completeDaemonShutdown, assertDaemonDispatch, assertDaemonRestartable, trackDaemonConnections, retireDaemonEndpoint, retireFailedDaemon } from "./daemon-operations.mjs";
9
11
  import { ObservationClock } from "./observation-clock.mjs";
10
- import { parseCapacityOperation } from "./codex-capacity-policy.mjs";
12
+ import { parseCapacityOperation } from "./provider-overload-policy.mjs";
11
13
  import path from "node:path";
12
14
  import { spawn } from "node:child_process";
13
15
  import { waitForSettlement } from "./session-settlement.mjs";
14
16
  import { errorRecord } from "./operation-errors.mjs";
15
- import { CodexBridge, cancelSessionWithBridge, createSessionWithBridge, inspectSessionWithBridge, promptSessionWithBridge, startSessionWithBridge } from "./dd-codex.mjs";
17
+ import { CODEX_REQUEST_TIMEOUT_MS, LATE_RESPONSE_GRACE_MS, CodexBridge, cancelSessionWithBridge, createSessionWithBridge, inspectSessionWithBridge, promptSessionWithBridge, startSessionWithBridge } from "./dd-codex.mjs";
16
18
  import { assertDaemonOwnership, prepareRuntimeOwner, cleanupFailedDaemonStart, confirmDaemonProcess, confirmDaemonStopped, finishDaemonProcess, scheduleDaemonProcessHeartbeat, registerDaemonProcess, startManagedBridge, stopProcessGroup } from "./managed-daemon.mjs";
17
19
 
20
+ export function codexBridgeEnv(config, daemonId) { return { ...config.env, ...managedFlowEnv(config, daemonId) }; }
21
+
18
22
  const requestSchema = "dd-codex/daemon-request@1";
19
23
  const responseSchema = "dd-codex/daemon-response@1";
20
24
  const stateSchema = "dd-codex/daemon-state@1";
@@ -72,13 +76,15 @@ export async function cancelCodexTree(bridge, options, { remember = () => {}, ac
72
76
  return { harness: "codex-desktop", provider_session_id: options.sessionId, adapter_session_id: options.sessionId, cancelled: true, settled: true, tree_session_ids: [...sessions], cleanup_errors: settlement.cleanup_errors, tree_observations: settlement.observations.map(item => ({ provider_session_id: item.provider_session_id, status: item.status, settled: item.settled })) };
73
77
  }
74
78
 
75
- export async function callDaemon(stateDir, operation, params = {}, timeoutMs = 30_000) {
76
- const request = { schema_id: requestSchema, id: process.env.DD_EVAL_OPERATION_ID ?? randomUUID(), operation, params };
79
+ export async function callDaemon(stateDir, operation, params = {}, timeoutMs = operation === "session.create" ? params.timeoutMs ?? CODEX_REQUEST_TIMEOUT_MS + LATE_RESPONSE_GRACE_MS : 30_000) {
80
+ const request = prepareDaemonRequest({ schema_id: requestSchema, id: process.env.DD_EVAL_OPERATION_ID ?? randomUUID(), operation, params });
77
81
  return await callDaemonObserved({ stateDir, socket: locations(stateDir).socket, request, responseSchema: responseSchema, harness: 'codex', timeoutMs });
78
82
  }
79
83
 
80
84
  export async function startDaemon(options) {
81
- const paths = locations(options.stateDir); const cwd = absolute(options.cwd, "--cwd"); const config = { cwd, journal: absolute(options.journal, "--journal"), bin: options.bin ?? null, ddFlowBin: options.ddFlowBin ? absolute(options.ddFlowBin, "--dd-flow-bin") : null, ddFlowHome: options.ddFlowHome ? absolute(options.ddFlowHome, "--dd-flow-home") : null, projectRoot: options.projectRoot ? absolute(options.projectRoot, "--project-root") : cwd, resourceHome: process.env.DD_FLOW_RESOURCE_HOME ? absolute(process.env.DD_FLOW_RESOURCE_HOME, "DD_FLOW_RESOURCE_HOME") : null, env: options.ddFlowHome ? { DD_FLOW_HOME: absolute(options.ddFlowHome, "--dd-flow-home"), ...(options.ddFlowBin ? { DD_FLOW_BIN: absolute(options.ddFlowBin, "--dd-flow-bin") } : {}) } : {} };
85
+ if (!options.noFlow && Boolean(options.ddFlowHome) !== Boolean(options.ddFlowBin)) throw Object.assign(new Error("--dd-flow-home and --dd-flow-bin must be provided together"), { code: "flow_context_incomplete" });
86
+ if (options.noFlow) options = { ...options, ddFlowHome: undefined, ddFlowBin: undefined };
87
+ const paths = locations(options.stateDir); const cwd = absolute(options.cwd, "--cwd"); const config = { cwd, journal: absolute(options.journal, "--journal"), bin: options.bin ?? null, ddFlowBin: options.ddFlowBin ? absolute(options.ddFlowBin, "--dd-flow-bin") : null, ddFlowHome: options.ddFlowHome ? absolute(options.ddFlowHome, "--dd-flow-home") : null, projectRoot: options.projectRoot ? absolute(options.projectRoot, "--project-root") : cwd, resourceHome: !options.noFlow && process.env.DD_FLOW_RESOURCE_HOME ? absolute(process.env.DD_FLOW_RESOURCE_HOME, "DD_FLOW_RESOURCE_HOME") : null, env: options.ddFlowHome ? { DD_FLOW_HOME: absolute(options.ddFlowHome, "--dd-flow-home"), ...(options.ddFlowBin ? { DD_FLOW_BIN: absolute(options.ddFlowBin, "--dd-flow-bin") } : {}) } : {} };
82
88
  await prepareRuntimeOwner(config, paths.dir);
83
89
  await mkdir(paths.dir, { recursive: true, mode: 0o700 }); await chmod(paths.dir, 0o700);
84
90
  let previous = await readState(paths.state); assertDaemonRestartable(previous);
@@ -95,7 +101,7 @@ export async function stopDaemon(options) { const timeoutMs = options.timeoutMs
95
101
 
96
102
  export async function serveDaemon(stateDir) {
97
103
  const paths = locations(stateDir); const state = await readState(paths.state); if (!state || state.schema_id !== stateSchema) throw Object.assign(new Error("daemon state missing"), { code: "daemon_state_missing" });
98
- const bridge = new CodexBridge({ ...state.config, env: { ...state.config.env, CODEX_HOME: process.env.CODEX_HOME, DD_FLOW_DAEMON_ID: state.daemon_id, DD_FLOW_CODEX_STATE_DIR: paths.dir } }); await startManagedBridge({ config: state.config, state, bridge, kind: "codex-provider", publish: snapshot => writeState(paths.state, snapshot) }); const sessions = new Set(state.sessions ?? []); let active = null; let server; let persist = Promise.resolve();
104
+ const bridge = new CodexBridge({ ...state.config, env: { ...codexBridgeEnv(state.config, state.daemon_id), CODEX_HOME: process.env.CODEX_HOME, DD_FLOW_CODEX_STATE_DIR: paths.dir } }); await startManagedBridge({ config: state.config, state, bridge, kind: "codex-provider", publish: snapshot => writeState(paths.state, snapshot) }); const sessions = new Set(state.sessions ?? []); let active = null; let server; let persist = Promise.resolve();
99
105
  scheduleDaemonProcessHeartbeat(state.config, state.resource_process);
100
106
  const save = (patch = {}) => { Object.assign(state, patch, { pid: process.pid, updated_at: new Date().toISOString(), sessions: [...sessions] }); const snapshot = structuredClone(state); persist = persist.catch(() => {}).then(() => writeState(paths.state, snapshot)); return persist; };
101
107
  const dispatch = async (operation, params, operationId) => {