@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
@@ -0,0 +1,210 @@
1
+ import { createHash } from "node:crypto";
2
+ import { Ajv2020 } from "ajv/dist/2020.js";
3
+ import { AppError } from "../shared/errors.js";
4
+ import { appendAudit } from "./audit.js";
5
+ import { AdmittedFrozenEngine } from "./frozen-engine.js";
6
+ import { hybridDigest, validateHybridContract } from "./hybrid-contracts.js";
7
+ import { PreparedMemoryInitProjectBootstrap, readMemoryInitProjectBootstrap, readMemoryInitProjectBootstrapEngineAdmission, readMemoryInitReleaseSelection } from "./memory-init-project-bootstrap.js";
8
+ const receiptEvent = "memory.bootstrap_ready", intentEvent = "memory.bootstrap_readiness_intent";
9
+ const bytes = (value) => Buffer.from(JSON.stringify(value));
10
+ const hash = (value) => createHash("sha256").update(value).digest("hex");
11
+ function refuse(reason) {
12
+ throw new AppError("memory_init_bootstrap_readiness_invalid", "Bootstrap readiness lacks exact independently accepted custody", 1, { reason, effect: "no_effect", next_action: "reconcile_exact_request" });
13
+ }
14
+ function decoded(base64, sha256) {
15
+ if (typeof base64 !== "string" || base64.length > 2 * 1024 * 1024 || !/^[a-f0-9]{64}$/.test(sha256))
16
+ refuse("invalid_manifest_binding");
17
+ const content = Buffer.from(base64, "base64");
18
+ if (!content.length || content.length > 1024 * 1024 || content.toString("base64") !== base64 || hash(content) !== sha256)
19
+ refuse("changed_manifest_bytes");
20
+ return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(content));
21
+ }
22
+ /** Exact selected contract support, before invoking bootstrap publication. The
23
+ * public dispatcher must call this before its first project effect, not discover
24
+ * an unsupported engine after installing drafts. No provider operation occurs.
25
+ */
26
+ export function prepareMemoryInitBootstrapSchema(engine) {
27
+ if (!(engine instanceof AdmittedFrozenEngine))
28
+ refuse("selected_engine_not_admitted");
29
+ const manifest = engine.manifest(), id = "dd-flow/memory-bootstrap-receipt@2";
30
+ const entries = manifest.schema_registry?.entries.filter(entry => entry.id === id) ?? [];
31
+ if (!Object.values(manifest.supports_contracts).flat().includes(id) || entries.length !== 1)
32
+ refuse("bootstrap_contract_not_supported");
33
+ return new Ajv2020({ strict: true, validateFormats: false }).compile(JSON.parse(engine.schema(entries[0].name).toString("utf8")));
34
+ }
35
+ /** Pure receipt construction from the prepared selection and accepted SQL
36
+ * dependencies. It is also used by writers/readers; no filesystem/schema IO.
37
+ */
38
+ function material(context, binding) {
39
+ const scope = binding.scope, registered = readMemoryInitProjectBootstrap(context, scope);
40
+ const accepted = readMemoryInitProjectBootstrapEngineAdmission(context, scope);
41
+ const selection = readMemoryInitReleaseSelection(context, scope);
42
+ if (!registered || !accepted || registered.receipt.schema_id !== "dd-flow/memory-init-project-bootstrap@4"
43
+ || accepted.bootstrap_sha256 !== registered.sha256
44
+ || registered.receipt.target_sha256 !== hybridDigest(binding.target)
45
+ || registered.receipt.execution_sha256 !== binding.execution_sha256
46
+ || binding.target.request_id !== scope.requestId || binding.target.project_root !== scope.projectRoot
47
+ || binding.target.workspace_root !== scope.workspaceRoot
48
+ || binding.git_policy_sha256 !== accepted.git_policy_sha256
49
+ || !/^[a-f0-9]{64}$/.test(binding.pack_sha256))
50
+ refuse("changed_bootstrap_dependencies");
51
+ if (!selection || selection.bootstrap_identity !== registered.receipt.identity
52
+ || selection.execution_sha256 !== binding.execution_sha256 || selection.pack_sha256 !== binding.pack_sha256
53
+ || selection.installation_manifest_sha256 !== binding.installation_manifest_sha256
54
+ || selection.installation_manifest_base64 !== binding.installation_manifest_base64)
55
+ refuse("changed_release_selection");
56
+ if (!context.db.get("SELECT 1 FROM projects WHERE id=? AND root=? AND status='active'", [registered.receipt.project_id, scope.projectRoot]))
57
+ refuse("project_not_active");
58
+ const release = decoded(binding.installation_manifest_base64, binding.installation_manifest_sha256);
59
+ const engine = decoded(binding.engine_manifest_base64, accepted.proof.engine_manifest_sha256);
60
+ const engineSha256 = accepted.proof.engine_sha256;
61
+ if (typeof engineSha256 !== "string" || !/^[a-f0-9]{64}$/.test(engineSha256))
62
+ refuse("invalid_engine_identity");
63
+ if (release.engine.package !== engine.package_name || release.engine.version !== engine.package_version
64
+ || engine.integrity.checksum !== accepted.proof.engine_sha256)
65
+ refuse("foreign_selected_engine");
66
+ const fact = { requestId: scope.requestId, projectRoot: scope.projectRoot, workspace: scope.workspaceRoot };
67
+ const evidence = [
68
+ { kind: "storage-compatibility", path: "receipts/storage-compatibility.json", value: { schema_id: "dd-flow/memory-init-storage-evidence@1", scope, admission_identity: accepted.identity, proof: accepted.proof } },
69
+ { kind: "scaffold", path: "receipts/scaffold.json", value: { schema_id: "dd-flow/memory-init-scaffold-evidence@1", scope, bootstrap_sha256: registered.sha256, receipt: registered.receipt } },
70
+ { kind: "pack-install", path: "receipts/pack-install.json", value: { schema_id: "dd-flow/memory-init-pack-evidence@1", scope, bootstrap_sha256: registered.sha256, layer_sha256: registered.receipt.layer_sha256, pack_sha256: binding.pack_sha256, installation_sha256: binding.installation_manifest_sha256 } }
71
+ ].map(entry => ({ kind: entry.kind, path: entry.path, sha256: hash(bytes(entry.value)), base64: bytes(entry.value).toString("base64") }));
72
+ const reference = (index) => ({ path: evidence[index].path, sha256: evidence[index].sha256 });
73
+ const receipt = { schema_id: "dd-flow/memory-bootstrap-receipt@2", request_id: scope.requestId, flow_id: "mb-init",
74
+ project_root: scope.projectRoot, workspace: { path: scope.workspaceRoot, repository: binding.target.repository,
75
+ branch: binding.target.branch, commit: binding.target.head, git_state: binding.target.head === null ? "unborn" : "committed" },
76
+ baseline_sha256: registered.receipt.target_sha256, project_current: null,
77
+ upgrade_target: { canon_version: release.canon.version, pack_sha256: binding.pack_sha256 },
78
+ effective_execution: { engine_sha256: engineSha256, canon_sha256: release.canon.archive.sha256 },
79
+ readiness: "ready", storage: { route: "verified-no-op", outcome: "no-op", compatibility_receipt: reference(0),
80
+ drain_receipt: null, backup_receipt: null, plan_receipt: null, apply_receipt: null, verification_receipt: null },
81
+ effects: [{ kind: "scaffold", receipt: reference(1) }, { kind: "pack-install", receipt: reference(2) }] };
82
+ const admission = { receipts: new Map(evidence.map(entry => [entry.sha256, { kind: entry.kind, ...fact }])),
83
+ bootstrapIdentity: { ...fact, baselineSha256: registered.receipt.target_sha256,
84
+ engineSha256, canonSha256: release.canon.archive.sha256,
85
+ workspaceGit: { repository: binding.target.repository, branch: binding.target.branch, commit: binding.target.head } } };
86
+ const authority = { schema_id: "dd-flow/memory-bootstrap-authority@1", request_id: scope.requestId, project_root: scope.projectRoot,
87
+ workspace_root: scope.workspaceRoot, flow_id: "mb-init", readiness: "ready", model_access: "unknown",
88
+ bootstrap_sha256: hash(bytes(receipt)), project_bootstrap_sha256: registered.sha256,
89
+ engine_admission_identity: accepted.identity, binding, receipt, evidence };
90
+ return { authority, admission, projectId: registered.receipt.project_id };
91
+ }
92
+ function row(context, scope, event) {
93
+ const rows = context.db.all("SELECT project_id,payload_json FROM audit_events WHERE event_type=? AND json_extract(payload_json,'$.request_id')=? AND json_extract(payload_json,'$.project_root')=? AND json_extract(payload_json,'$.workspace_root')=? LIMIT 2", [event, scope.requestId, scope.projectRoot, scope.workspaceRoot]);
94
+ if (rows.length > 1)
95
+ refuse("ambiguous_readiness_custody");
96
+ if (!rows.length)
97
+ return null;
98
+ if (Buffer.byteLength(rows[0].payload_json) > 4 * 1024 * 1024)
99
+ refuse("unbounded_readiness_custody");
100
+ return { ...rows[0], value: JSON.parse(rows[0].payload_json) };
101
+ }
102
+ /** Historical readiness custody only. Current physical admission belongs to
103
+ * prepare/apply. Loss of either independent record cannot republish authority.
104
+ */
105
+ export function readMemoryInitBootstrapReadiness(context, scope) {
106
+ try {
107
+ return context.db.readSnapshot(() => {
108
+ const retained = row(context, scope, receiptEvent), intent = row(context, scope, intentEvent);
109
+ if (!retained && !intent)
110
+ return null;
111
+ if (!retained || !intent)
112
+ refuse("readiness_custody_missing");
113
+ const binding = retained.value.binding;
114
+ if (!binding || hybridDigest(binding.scope) !== hybridDigest(scope)
115
+ || (Object.hasOwn(binding, "git_policy_sha256") && (typeof binding.git_policy_sha256 !== "string" || !/^[a-f0-9]{64}$/.test(binding.git_policy_sha256)))
116
+ || Object.keys(binding).sort().join() !== (Object.hasOwn(binding, "git_policy_sha256")
117
+ ? "engine_manifest_base64,execution_sha256,git_policy_sha256,installation_manifest_base64,installation_manifest_sha256,pack_sha256,scope,target"
118
+ : "engine_manifest_base64,execution_sha256,installation_manifest_base64,installation_manifest_sha256,pack_sha256,scope,target"))
119
+ refuse("invalid_readiness_binding");
120
+ const selected = material(context, binding), expected = { schema_id: "dd-flow/memory-bootstrap-readiness-intent@1", request_id: scope.requestId,
121
+ project_root: scope.projectRoot, workspace_root: scope.workspaceRoot, authority_sha256: hybridDigest(selected.authority) };
122
+ if (retained.project_id !== selected.projectId || intent.project_id !== selected.projectId
123
+ || hybridDigest(retained.value) !== hybridDigest(selected.authority) || hybridDigest(intent.value) !== hybridDigest(expected))
124
+ refuse("changed_readiness_custody");
125
+ return { ...selected, authority: structuredClone(selected.authority) };
126
+ });
127
+ }
128
+ catch (error) {
129
+ if (error instanceof AppError)
130
+ throw error;
131
+ return refuse("invalid_readiness_custody");
132
+ }
133
+ }
134
+ /** Request-owned runtime readiness, not semantic memory acceptance or account
135
+ * qualification. Provider failures after this receipt keep a partial bootstrap;
136
+ * no hidden model turn is dispatched to obtain the receipt.
137
+ */
138
+ export class PreparedMemoryInitBootstrapReadiness {
139
+ #bootstrap;
140
+ #engine;
141
+ #binding;
142
+ #material;
143
+ constructor(bootstrap, engine, binding, selected) {
144
+ this.#bootstrap = bootstrap;
145
+ this.#engine = engine;
146
+ this.#binding = structuredClone(binding);
147
+ this.#material = selected;
148
+ Object.freeze(this);
149
+ }
150
+ static prepare(context, input) {
151
+ if (!(input.bootstrap instanceof PreparedMemoryInitProjectBootstrap))
152
+ refuse("unprepared_project_bootstrap");
153
+ const validate = prepareMemoryInitBootstrapSchema(input.engine);
154
+ const { registered: _registered, accepted: _accepted, ...binding } = input.bootstrap.prepareReadiness(context, input.engine);
155
+ void _registered;
156
+ void _accepted;
157
+ const selected = context.db.readSnapshot(() => material(context, binding));
158
+ if (!validate(selected.authority.receipt) || validateHybridContract("memory-bootstrap-receipt", selected.authority.receipt, selected.admission).length)
159
+ refuse("invalid_bootstrap_contract");
160
+ const prior = readMemoryInitBootstrapReadiness(context, binding.scope);
161
+ if (prior && hybridDigest(prior.authority) !== hybridDigest(selected.authority))
162
+ refuse("changed_readiness_request");
163
+ return new PreparedMemoryInitBootstrapReadiness(input.bootstrap, input.engine, binding, selected);
164
+ }
165
+ apply(context) {
166
+ const { registered: _registered, accepted: _accepted, ...current } = this.#bootstrap.prepareReadiness(context, this.#engine);
167
+ void _registered;
168
+ void _accepted;
169
+ if (hybridDigest(current) !== hybridDigest(this.#binding))
170
+ refuse("readiness_changed_after_preparation");
171
+ return context.db.writeTransaction(() => {
172
+ const selected = material(context, this.#binding);
173
+ if (hybridDigest(selected.authority) !== hybridDigest(this.#material.authority))
174
+ refuse("changed_bootstrap_dependencies");
175
+ const prior = readMemoryInitBootstrapReadiness(context, this.#binding.scope);
176
+ if (prior) {
177
+ if (hybridDigest(prior.authority) !== hybridDigest(selected.authority))
178
+ refuse("changed_readiness_request");
179
+ return { ...prior, changed: false };
180
+ }
181
+ const scope = this.#binding.scope;
182
+ appendAudit(context, { projectId: selected.projectId, eventType: intentEvent,
183
+ payload: { schema_id: "dd-flow/memory-bootstrap-readiness-intent@1", request_id: scope.requestId, project_root: scope.projectRoot,
184
+ workspace_root: scope.workspaceRoot, authority_sha256: hybridDigest(selected.authority) } });
185
+ appendAudit(context, { projectId: selected.projectId, eventType: receiptEvent, payload: selected.authority });
186
+ const accepted = readMemoryInitBootstrapReadiness(context, scope);
187
+ if (!accepted)
188
+ refuse("readiness_not_committed");
189
+ return { ...accepted, changed: true };
190
+ }, "memoryInit.bootstrapReadiness");
191
+ }
192
+ }
193
+ /** Productive bootstrap entry: contract admission precedes all project effects.
194
+ * A failure after draft publication is explicitly partial and resumes the same
195
+ * request; it never substitutes fresh initialization or a paid route probe.
196
+ */
197
+ export function bootstrapMemoryInit(context, input, checkpoint) {
198
+ if (!(input.bootstrap instanceof PreparedMemoryInitProjectBootstrap))
199
+ refuse("unprepared_project_bootstrap");
200
+ prepareMemoryInitBootstrapSchema(input.engine);
201
+ const registered = input.bootstrap.applyWithEngine(context, input.engine, checkpoint);
202
+ try {
203
+ return PreparedMemoryInitBootstrapReadiness.prepare(context, input).apply(context);
204
+ }
205
+ catch (error) {
206
+ throw new AppError("memory_init_bootstrap_readiness_failed", "Project drafts are retained but bootstrap readiness is incomplete", 1, { effect: "partial", project_bootstrap_sha256: registered.sha256,
207
+ reason: error instanceof AppError ? error.details.reason ?? error.code : "readiness_operation_failed",
208
+ next_action: "reconcile_exact_request" });
209
+ }
210
+ }
@@ -0,0 +1,89 @@
1
+ import path from "node:path";
2
+ import fs from "node:fs";
3
+ import { createHash } from "node:crypto";
4
+ import { AppError } from "../shared/errors.js";
5
+ import { RetainedExecutionBundle } from "./execution-bundles.js";
6
+ import { publishImmutableInput } from "../storage/immutable-input.js";
7
+ import { readBoundedReleaseFile } from "./release-source.js";
8
+ import { readMemoryInitStartupInput } from "./memory-init-startup-custody.js";
9
+ /** Public init consumes the independently retained intake, never a newly
10
+ * inferred objective. Historical v1 SDK requests must not invent one. */
11
+ export function prepareMemoryInitContextsFromStartup(context, bundle, directory) {
12
+ if (!(bundle instanceof RetainedExecutionBundle) || bundle.manifest.flow_id !== "mb-init") {
13
+ throw new AppError("memory_init_context_invalid", "Init contexts require a retained memory bundle", 2, { effect: "no_effect" });
14
+ }
15
+ const scope = bundle.execution.snapshot().scope;
16
+ const retained = readMemoryInitStartupInput(context, { requestId: scope.requestId, projectRoot: scope.projectRoot, workspaceRoot: scope.workspace });
17
+ if (!retained || !["dd-flow/memory-init-startup-input@2", "dd-flow/memory-init-startup-input@3"].includes(retained.payload.schema_id)
18
+ || retained.payload.objective === undefined || retained.payload.execution.sha256 !== bundle.execution.sha256
19
+ || retained.payload.engine.manifest_sha256 !== bundle.manifest.engine_manifest.sha256) {
20
+ throw new AppError("memory_init_context_invalid", "Init contexts require the exact retained objective and execution selection", 1, { effect: "no_effect" });
21
+ }
22
+ return { ...prepareMemoryInitContexts(bundle, retained.payload.objective, directory), objective: retained.payload.objective };
23
+ }
24
+ /** Prepare bytes only. Immutable publication and the controller retain their
25
+ * existing ownership; this does not create directories, Sessions or RUNs. */
26
+ export function prepareMemoryInitContexts(bundle, objective, directory) {
27
+ if (!(bundle instanceof RetainedExecutionBundle) || bundle.manifest.flow_id !== "mb-init"
28
+ || typeof objective !== "string" || !objective.trim() || Buffer.byteLength(objective) > 64 * 1024
29
+ || !path.isAbsolute(directory) || path.resolve(directory) !== directory) {
30
+ throw new AppError("memory_init_context_invalid", "Init contexts require a retained memory bundle, bounded objective and absolute directory", 2, { effect: "no_effect" });
31
+ }
32
+ const files = new Map(), stages = Object.create(null);
33
+ const hash = (bytes) => createHash("sha256").update(bytes).digest("hex");
34
+ for (const stage of bundle.descriptor.descriptor.stages) {
35
+ const file = path.join(directory, `${stage.id}.json`);
36
+ const bytes = Buffer.from(JSON.stringify({ schema_id: "dd-flow/stage-context@1", stage: stage.id, objective }));
37
+ files.set(file, bytes);
38
+ stages[stage.id] = { file, sha256: hash(bytes) };
39
+ }
40
+ const file = path.join(directory, "managed-context.json"), bytes = Buffer.from(JSON.stringify({ schema_id: "dd-flow/managed-context@1", stages }));
41
+ files.set(file, bytes);
42
+ return { file, sha256: hash(bytes), files };
43
+ }
44
+ /** Publish candidates only; an existing controller SQL receipt still owns
45
+ * their execution authority. The caller supplies an already owned directory
46
+ * and independently retained package digest. Never replaces conflicting files. */
47
+ export function publishMemoryInitContexts(bundle, objective, directory, expectedSha256, expectedOwner) {
48
+ const prepared = prepareMemoryInitContexts(bundle, objective, directory);
49
+ if (prepared.sha256 !== expectedSha256)
50
+ throw new AppError("memory_init_context_invalid", "Context package selection changed", 1, { effect: "no_effect" });
51
+ const owner = fs.lstatSync(directory);
52
+ if (expectedOwner && (owner.dev !== expectedOwner.dev || owner.ino !== expectedOwner.ino)) {
53
+ throw new AppError("memory_init_context_invalid", "Context directory differs from its retained owner", 1, { effect: "no_effect" });
54
+ }
55
+ const assertOwner = () => {
56
+ const current = fs.lstatSync(directory);
57
+ if (!current.isDirectory() || current.dev !== owner.dev || current.ino !== owner.ino || fs.realpathSync(directory) !== directory) {
58
+ throw new AppError("memory_init_context_invalid", "Context directory owner changed", 1, { effect: "no_effect" });
59
+ }
60
+ };
61
+ assertOwner();
62
+ // Reject any known collision before creating the first candidate.
63
+ for (const [file, bytes] of prepared.files) {
64
+ try {
65
+ fs.lstatSync(file);
66
+ }
67
+ catch (error) {
68
+ if (error.code === "ENOENT")
69
+ continue;
70
+ throw error;
71
+ }
72
+ if (!readBoundedReleaseFile(file, bytes.length).equals(bytes))
73
+ throw new AppError("memory_init_context_invalid", "Existing context bytes differ", 1, { effect: "no_effect", path: file });
74
+ }
75
+ let attempted = false;
76
+ try {
77
+ for (const [file, bytes] of prepared.files) {
78
+ assertOwner();
79
+ attempted = true;
80
+ publishImmutableInput(file, bytes, "memory_init_context_invalid");
81
+ }
82
+ assertOwner();
83
+ return { file: prepared.file, sha256: prepared.sha256 };
84
+ }
85
+ catch (error) {
86
+ throw new AppError("memory_init_context_publication_incomplete", "Context candidates require exact-request reconciliation", 1, { effect: attempted ? "partial" : "no_effect", directory, expected_sha256: expectedSha256,
87
+ reason: error instanceof AppError ? error.code : "context_publication_failed" });
88
+ }
89
+ }
@@ -0,0 +1,84 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { AppError, errorRecord } from "../shared/errors.js";
4
+ import { validateMemoryInitStartupScope, readMemoryInitStartupInput } from "./memory-init-startup-custody.js";
5
+ import { startMemoryInitRun } from "./memory-init-start.js";
6
+ import { requireProjectByRoot } from "./projects.js";
7
+ import { readRunStartRequest } from "./run-start-custody.js";
8
+ import { readFlowRunExecutionBundle } from "./runs.js";
9
+ import { requireRun } from "./run-controller-state.js";
10
+ import { prepareRunControllerLaunch } from "./run-controller.js";
11
+ import { prepareMemoryInitContextsFromStartup, publishMemoryInitContexts } from "./memory-init-contexts.js";
12
+ import { parseRuntimeBudget } from "./runtime-budget.js";
13
+ function refuse(reason) {
14
+ throw new AppError("memory_init_controller_start_invalid", "Init controller requires its exact published startup request", 1, { reason, effect: "no_effect", next_action: "reconcile_exact_request" });
15
+ }
16
+ /** Bridge the already published logical RUN to the existing controller. The
17
+ * RUN-start owner admits the root; startup custody supplies the objective.
18
+ * An existing controller replays before candidate publication, so missing
19
+ * productive inputs are never reconstructed here. No process is launched. */
20
+ export function prepareMemoryInitControllerStart(context, scope) {
21
+ validateMemoryInitStartupScope(scope);
22
+ if (context.db.isOwnedWriteTransaction?.())
23
+ refuse("preparation_outside_writer_required");
24
+ parseRuntimeBudget(context.env.DD_FLOW_RUNTIME_BUDGET);
25
+ const startup = readMemoryInitStartupInput(context, scope);
26
+ if (!startup || !["dd-flow/memory-init-startup-input@2", "dd-flow/memory-init-startup-input@3"].includes(startup.payload.schema_id))
27
+ refuse("retained_objective_required");
28
+ const project = requireProjectByRoot(context, scope.projectRoot);
29
+ const request = readRunStartRequest(context, project.id, scope.requestId);
30
+ if (!request?.published || request.payload.initial_index.flow_kind !== "mb-init"
31
+ || request.payload.workspace_root !== scope.workspaceRoot)
32
+ refuse("published_run_required");
33
+ // Restores the exact request and verifies independent SQL/physical root
34
+ // ownership without consulting current canon/profiles or creating a RUN.
35
+ const started = startMemoryInitRun(context, scope);
36
+ const run = requireRun(context, project.id, started.runId);
37
+ const bundle = readFlowRunExecutionBundle(context, { projectRoot: scope.projectRoot, runId: run.id });
38
+ if (!bundle || bundle.sha256 !== started.bundleSha256)
39
+ refuse("retained_bundle_required");
40
+ if (bundle.execution.settings.execution !== "managed")
41
+ refuse("managed_execution_required");
42
+ const contexts = prepareMemoryInitContextsFromStartup(context, bundle, run.run_root);
43
+ const input = { projectRoot: scope.projectRoot, runId: run.id, requestId: scope.requestId,
44
+ contextFile: contexts.file, contextSha256: contexts.sha256 };
45
+ const controllers = context.db.get("SELECT 1 FROM sqlite_schema WHERE type='table' AND name='run_controllers'")
46
+ ? context.db.all("SELECT request_id FROM run_controllers WHERE project_id=? AND run_id=?", [project.id, run.id]) : [];
47
+ if (controllers.some(row => row.request_id === scope.requestId))
48
+ return { input, prepared: prepareRunControllerLaunch(context, input) };
49
+ if (controllers.length)
50
+ refuse("foreign_controller_request");
51
+ // A directory without a receipt is an interrupted/unknown outcome, not
52
+ // evidence that no owner ran. Do not create another physical controller.
53
+ let controllerRootFound = false;
54
+ try {
55
+ fs.lstatSync(path.join(run.run_root, "controllers"));
56
+ controllerRootFound = true;
57
+ }
58
+ catch (error) {
59
+ if (error.code !== "ENOENT")
60
+ throw error;
61
+ }
62
+ if (controllerRootFound)
63
+ refuse("controller_custody_missing");
64
+ if (context.db.get("SELECT 1 FROM sessions WHERE project_id=? AND run_id=? LIMIT 1", [project.id, run.id]))
65
+ refuse("session_owner_requires_reconciliation");
66
+ const rootOwner = context.db.readSnapshot(() => {
67
+ if (readRunStartRequest(context, project.id, scope.requestId)?.sha256 !== request.sha256)
68
+ refuse("run_request_changed");
69
+ const row = context.db.get("SELECT payload_json FROM audit_events WHERE project_id=? AND event_type='flow_run.start_root_owned' AND json_extract(payload_json,'$.run_id')=?", [project.id, run.id]);
70
+ if (!row)
71
+ refuse("root_owner_missing");
72
+ return JSON.parse(row.payload_json).owner;
73
+ });
74
+ if (!rootOwner || !Number.isSafeInteger(rootOwner.dev) || !Number.isSafeInteger(rootOwner.ino))
75
+ refuse("root_owner_changed");
76
+ publishMemoryInitContexts(bundle, contexts.objective, run.run_root, contexts.sha256, rootOwner);
77
+ try {
78
+ return { input, prepared: prepareRunControllerLaunch(context, input) };
79
+ }
80
+ catch (error) {
81
+ throw new AppError("memory_init_controller_start_incomplete", "Retained context candidates require exact-request reconciliation", 1, { effect: "partial", run_id: run.id, request_id: scope.requestId, context_sha256: contexts.sha256,
82
+ cause: errorRecord(error), next_action: "reconcile_exact_request" });
83
+ }
84
+ }
@@ -0,0 +1,223 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { createHash } from "node:crypto";
4
+ import { AppError } from "../shared/errors.js";
5
+ import { AdmittedFrozenEngine } from "./frozen-engine.js";
6
+ import { readSetupStatus } from "./setup.js";
7
+ import { inspectReleaseArchive } from "./release-archive.js";
8
+ import { readBoundedReleaseFile } from "./release-source.js";
9
+ import { hybridDigest } from "./hybrid-contracts.js";
10
+ import { engineVersionRoot } from "../storage/paths.js";
11
+ import { engineSnapshotChecksum, installPackageEngine } from "./engines.js";
12
+ const hash = (bytes) => createHash("sha256").update(bytes).digest("hex");
13
+ function refuse(reason) {
14
+ throw new AppError("memory_init_engine_release_invalid", "Selected engine is not the verified installed release payload", 1, { reason, phase: "prepare", effect: "no_effect" });
15
+ }
16
+ /** Select already delivered release bytes, never the invoking CLI or highest
17
+ * package version. Ambiguous dependency snapshots require explicit selection. */
18
+ export function selectMemoryInitInstalledEngine(home, installationSha256, checksum) {
19
+ if (!path.isAbsolute(home) || path.resolve(home) !== home || fs.realpathSync(home) !== home)
20
+ refuse("noncanonical_installation_home");
21
+ const selected = readSetupStatus(home), installation = selected.installation;
22
+ if (!installation || selected.installation_readiness !== "installed" || installation.manifest_sha256 !== installationSha256)
23
+ refuse("installation_selection_changed");
24
+ if (checksum !== undefined && !/^[a-f0-9]{64}$/.test(checksum))
25
+ refuse("invalid_engine_checksum");
26
+ const base = engineVersionRoot(home, installation.manifest.engine.package, installation.manifest.engine.version);
27
+ const parent = path.dirname(base), version = path.basename(base), candidates = [];
28
+ if (!fs.existsSync(parent))
29
+ refuse("selected_engine_not_delivered");
30
+ if (!fs.lstatSync(parent).isDirectory() || fs.realpathSync(parent) !== parent)
31
+ refuse("unsafe_engine_store");
32
+ for (const entry of fs.readdirSync(parent, { withFileTypes: true })) {
33
+ if (!entry.isDirectory() || !(entry.name === version || entry.name.startsWith(`${version}--`)
34
+ && /^[a-f0-9]{64}$/.test(entry.name.slice(version.length + 2))))
35
+ continue;
36
+ const root = path.join(parent, entry.name);
37
+ try {
38
+ const bytes = readBoundedReleaseFile(path.join(root, "engine.json"), 1024 * 1024);
39
+ const manifest = JSON.parse(bytes.toString("utf8"));
40
+ if (manifest.snapshot_root !== root || entry.name !== version && entry.name !== `${version}--${manifest.integrity?.checksum}`
41
+ || checksum !== undefined && manifest.integrity?.checksum !== checksum)
42
+ continue;
43
+ const engine = AdmittedFrozenEngine.admitSelection({ package_name: manifest.package_name, package_version: manifest.package_version,
44
+ engine_version: manifest.engine_version, snapshot_root: root, integrity_checksum: manifest.integrity?.checksum }, hash(bytes));
45
+ prepareMemoryInitEngineRelease(home, installationSha256, engine);
46
+ candidates.push(engine);
47
+ }
48
+ catch (error) {
49
+ // An unavailable or mismatching old snapshot cannot authorize this release.
50
+ if (error instanceof AppError || error instanceof SyntaxError || error.code === "ENOENT")
51
+ continue;
52
+ throw error;
53
+ }
54
+ }
55
+ if (candidates.length !== 1)
56
+ refuse(candidates.length ? "ambiguous_selected_engine" : "selected_engine_not_delivered");
57
+ return candidates[0];
58
+ }
59
+ function checkFile(root, entry) {
60
+ let file = root;
61
+ const parts = entry.path.split("/");
62
+ for (const [index, part] of parts.entries()) {
63
+ file = path.join(file, part);
64
+ const stat = fs.lstatSync(file);
65
+ if (stat.isSymbolicLink() || (index === parts.length - 1 ? !stat.isFile() : !stat.isDirectory()))
66
+ refuse("unsafe_snapshot_path");
67
+ }
68
+ if ((fs.lstatSync(file).mode & 0o111) !== entry.executable_mode)
69
+ refuse("snapshot_payload_mode_changed");
70
+ if (entry.bytes === 0) {
71
+ const fd = fs.openSync(file, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
72
+ try {
73
+ const stat = fs.fstatSync(fd);
74
+ if (!stat.isFile() || stat.size !== 0 || fs.readSync(fd, Buffer.alloc(1), 0, 1, 0) !== 0)
75
+ refuse("snapshot_payload_changed");
76
+ }
77
+ finally {
78
+ fs.closeSync(fd);
79
+ }
80
+ }
81
+ else if (hash(readBoundedReleaseFile(file, entry.bytes)) !== entry.sha256)
82
+ refuse("snapshot_payload_changed");
83
+ }
84
+ function checkInventory(root, inventory) {
85
+ const expected = new Set(inventory.map(entry => entry.path));
86
+ for (const entry of fs.readdirSync(root, { recursive: true, withFileTypes: true })) {
87
+ const logical = path.relative(root, path.join(entry.parentPath, entry.name)).split(path.sep).join("/");
88
+ if (!entry.isFile() && !entry.isDirectory())
89
+ refuse("unsafe_snapshot_path");
90
+ if (logical !== "engine.json" && !expected.has(logical)
91
+ && (entry.name === "engine.json" || entry.isFile() && !logical.startsWith("node_modules/")))
92
+ refuse("unselected_snapshot_file");
93
+ }
94
+ }
95
+ function archiveInventory(archive, engineRoot) {
96
+ const entries = inspectReleaseArchive(archive, readBoundedReleaseFile(path.join(path.dirname(engineRoot), "archive.tgz"), Math.min(archive.bytes, 256 * 1024 * 1024)));
97
+ const inventory = [];
98
+ for (const entry of entries) {
99
+ if (entry.path !== "package" && !entry.path.startsWith("package/"))
100
+ refuse("foreign_payload_root");
101
+ if (entry.kind !== "file")
102
+ continue;
103
+ const logical = entry.path.slice("package/".length);
104
+ if (!logical || logical === "engine.json")
105
+ refuse("reserved_payload_path");
106
+ inventory.push({ path: logical, sha256: hash(entry.bytes), executable_mode: entry.mode & 0o111, bytes: entry.bytes.length });
107
+ }
108
+ return inventory;
109
+ }
110
+ /** Existing package/dependency acquisition supplies the independently selected
111
+ * full checksum. This gate binds transferred package files to verified setup
112
+ * bytes before delivery and again in the owned staging directory. */
113
+ export function deliverMemoryInitReleaseEngine(context, input) {
114
+ const home = context.ddFlowHome;
115
+ if (!path.isAbsolute(home) || fs.realpathSync(home) !== home || !path.isAbsolute(input.packageRoot)
116
+ || fs.realpathSync(input.packageRoot) !== input.packageRoot || !/^[a-f0-9]{64}$/.test(input.expectedChecksum))
117
+ refuse("invalid_delivery_selection");
118
+ const selected = readSetupStatus(home), installation = selected.installation;
119
+ if (!installation || selected.installation_readiness !== "installed" || installation.manifest_sha256 !== input.installationSha256)
120
+ refuse("installation_selection_changed");
121
+ const inventory = archiveInventory(installation.manifest.engine.archive, installation.engine_root);
122
+ const check = (root) => {
123
+ try {
124
+ for (const file of inventory)
125
+ checkFile(root, file);
126
+ checkInventory(root, inventory);
127
+ if (engineSnapshotChecksum(root) !== input.expectedChecksum)
128
+ refuse("delivery_checksum_changed");
129
+ const current = readSetupStatus(home);
130
+ if (current.installation_readiness !== "installed" || hybridDigest(current.installation) !== hybridDigest(installation))
131
+ refuse("installation_changed_during_delivery");
132
+ }
133
+ catch (error) {
134
+ if (error instanceof AppError && error.code === "memory_init_engine_release_invalid")
135
+ throw error;
136
+ return refuse("payload_unavailable_or_changed");
137
+ }
138
+ };
139
+ check(input.packageRoot);
140
+ const delivered = installPackageEngine(context, { packageRoot: input.packageRoot, packageName: installation.manifest.engine.package,
141
+ version: installation.manifest.engine.version, expectedChecksum: input.expectedChecksum, validateStaged: check });
142
+ try {
143
+ return selectMemoryInitInstalledEngine(home, input.installationSha256, input.expectedChecksum);
144
+ }
145
+ catch (error) {
146
+ throw new AppError("memory_init_engine_delivery_incomplete", "Delivered engine requires exact selection reconciliation", 1, { effect: delivered.changed ? "partial" : "no_effect", path: delivered.path, reason: error instanceof AppError ? error.details.reason ?? error.code : "delivered_selection_changed" });
147
+ }
148
+ }
149
+ /** expectedSha256 must come from independent request custody. No current
150
+ * installation/canon/profile reads and no restoration of missing files. */
151
+ export function revalidateMemoryInitEngineRelease(input, expectedSha256, engine) {
152
+ try {
153
+ if (!(engine instanceof AdmittedFrozenEngine) || !input || typeof input !== "object" || Array.isArray(input))
154
+ refuse("invalid_retained_pairing");
155
+ const facts = input, digest = (value) => typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
156
+ if (Object.keys(facts).sort().join() !== "archive_sha256,engine_manifest_sha256,engine_sha256,installation_sha256,inventory,schema_id"
157
+ || facts.schema_id !== "dd-flow/memory-init-engine-release@1" || !digest(expectedSha256)
158
+ || [facts.installation_sha256, facts.archive_sha256, facts.engine_manifest_sha256, facts.engine_sha256].some(value => !digest(value))
159
+ || !Array.isArray(facts.inventory) || !facts.inventory.length || facts.inventory.length > 100_000
160
+ || hybridDigest(facts) !== expectedSha256)
161
+ refuse("invalid_retained_pairing");
162
+ const manifest = engine.manifest();
163
+ if (engine.manifestSha256 !== facts.engine_manifest_sha256 || manifest.integrity.checksum !== facts.engine_sha256)
164
+ refuse("foreign_engine_identity");
165
+ const seen = new Set();
166
+ let total = 0;
167
+ for (const entry of facts.inventory) {
168
+ if (!entry || Object.keys(entry).sort().join() !== "bytes,executable_mode,path,sha256" || typeof entry.path !== "string"
169
+ || entry.path.length > 1024 || entry.path !== entry.path.normalize("NFC") || /[\\:]/.test(entry.path)
170
+ || [...entry.path].some(character => character.charCodeAt(0) < 32 || character.charCodeAt(0) === 127)
171
+ || entry.path.split("/").some(part => !part || part === "." || part === "..") || entry.path === "engine.json"
172
+ || seen.has(entry.path.toLowerCase()) || !digest(entry.sha256) || !Number.isSafeInteger(entry.bytes) || entry.bytes < 0
173
+ || entry.bytes === 0 && entry.sha256 !== hash(Buffer.alloc(0))
174
+ || !Number.isSafeInteger(entry.executable_mode) || entry.executable_mode < 0 || entry.executable_mode > 0o111 || (entry.executable_mode & ~0o111) !== 0
175
+ || (total += entry.bytes) > 512 * 1024 * 1024)
176
+ refuse("invalid_retained_inventory");
177
+ seen.add(entry.path.toLowerCase());
178
+ checkFile(manifest.snapshot_root, entry);
179
+ }
180
+ checkInventory(manifest.snapshot_root, facts.inventory);
181
+ AdmittedFrozenEngine.admitSelection({ package_name: manifest.package_name, package_version: manifest.package_version,
182
+ engine_version: manifest.engine_version, snapshot_root: manifest.snapshot_root, integrity_checksum: facts.engine_sha256 }, facts.engine_manifest_sha256);
183
+ return { facts: structuredClone(facts), sha256: expectedSha256 };
184
+ }
185
+ catch (error) {
186
+ if (error instanceof AppError && error.code === "memory_init_engine_release_invalid")
187
+ throw error;
188
+ return refuse("payload_unavailable_or_changed");
189
+ }
190
+ }
191
+ /** Fresh selection only. Restarts must retain this inventory independently,
192
+ * not consult the current installation. This does not install dependencies,
193
+ * admit handlers/storage/provider access or grant project effects. */
194
+ export function prepareMemoryInitEngineRelease(home, installationSha256, engine) {
195
+ try {
196
+ if (!(engine instanceof AdmittedFrozenEngine))
197
+ refuse("unprepared_engine");
198
+ if (!path.isAbsolute(home) || path.resolve(home) !== home || fs.realpathSync(home) !== home)
199
+ refuse("noncanonical_installation_home");
200
+ const selected = readSetupStatus(home), installation = selected.installation;
201
+ if (!installation || selected.installation_readiness !== "installed" || installation.manifest_sha256 !== installationSha256)
202
+ refuse("installation_selection_changed");
203
+ const manifest = engine.manifest();
204
+ if (manifest.package_name !== installation.manifest.engine.package || manifest.package_version !== installation.manifest.engine.version)
205
+ refuse("foreign_engine_identity");
206
+ AdmittedFrozenEngine.admitSelection({ package_name: manifest.package_name, package_version: manifest.package_version,
207
+ engine_version: manifest.engine_version, snapshot_root: manifest.snapshot_root, integrity_checksum: manifest.integrity.checksum }, engine.manifestSha256);
208
+ const archive = installation.manifest.engine.archive;
209
+ const inventory = archiveInventory(archive, installation.engine_root);
210
+ const final = readSetupStatus(home);
211
+ if (final.installation_readiness !== "installed" || hybridDigest(final.installation) !== hybridDigest(installation))
212
+ refuse("installation_changed_during_admission");
213
+ const facts = { schema_id: "dd-flow/memory-init-engine-release@1", installation_sha256: installationSha256,
214
+ archive_sha256: archive.sha256, engine_manifest_sha256: engine.manifestSha256,
215
+ engine_sha256: manifest.integrity.checksum, inventory: inventory.sort((a, b) => a.path.localeCompare(b.path, "en")) };
216
+ return revalidateMemoryInitEngineRelease(facts, hybridDigest(facts), engine);
217
+ }
218
+ catch (error) {
219
+ if (error instanceof AppError && error.code === "memory_init_engine_release_invalid")
220
+ throw error;
221
+ return refuse("payload_unavailable_or_changed");
222
+ }
223
+ }