@deksden-com/dd-flow-cli 0.9.0-beta.144 → 0.9.0-beta.149

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 (254) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/README.md +13 -8
  3. package/dist/build-info.json +7 -7
  4. package/dist/cli/command-inputs.js +13 -3
  5. package/dist/cli/command-metadata.js +51 -16
  6. package/dist/cli/diagnostic-invocation.js +46 -0
  7. package/dist/cli/diagnostic.js +120 -0
  8. package/dist/cli/help.js +67 -1
  9. package/dist/cli/hook-ingress.js +27 -3
  10. package/dist/cli/memory-flow-admission.js +9 -3
  11. package/dist/cli/memory-init-invocation.js +10 -3
  12. package/dist/cli/memory-init.js +77 -68
  13. package/dist/cli/memory-storage-admission.js +3 -2
  14. package/dist/cli/memory-storage-qualification.js +105 -0
  15. package/dist/cli/memory-storage-transition.js +123 -0
  16. package/dist/cli/memory-upgrade.js +189 -0
  17. package/dist/cli/native-hook-ingress.js +14 -4
  18. package/dist/cli/native-hook-preparation.js +55 -0
  19. package/dist/cli/response-file.js +2 -1
  20. package/dist/cli/run-cli.js +79 -40
  21. package/dist/cli/setup.js +2 -1
  22. package/dist/cli.js +18 -0
  23. package/dist/domain/diagnostic-aspects.js +103 -0
  24. package/dist/domain/diagnostic-flows.js +8 -0
  25. package/dist/domain/stage-handler-identities.js +8 -0
  26. package/dist/harness-runtime/bin/dd-agy.mjs +3 -3
  27. package/dist/harness-runtime/bin/dd-grok.mjs +3 -3
  28. package/dist/harness-runtime/bin/dd-zcode.mjs +5 -5
  29. package/dist/harness-runtime/lib/agy-error-boundary.mjs +58 -0
  30. package/dist/harness-runtime/lib/agy-native-inspection.mjs +216 -0
  31. package/dist/harness-runtime/lib/agy-native_pb.js +117 -0
  32. package/dist/harness-runtime/lib/daemon-operations.d.mts +1 -0
  33. package/dist/harness-runtime/lib/daemon-operations.mjs +84 -10
  34. package/dist/harness-runtime/lib/dd-agy-daemon.mjs +366 -52
  35. package/dist/harness-runtime/lib/dd-agy.mjs +17 -3
  36. package/dist/harness-runtime/lib/dd-codex-daemon.mjs +27 -13
  37. package/dist/harness-runtime/lib/dd-codex.mjs +4 -0
  38. package/dist/harness-runtime/lib/dd-droid-daemon.mjs +4 -3
  39. package/dist/harness-runtime/lib/dd-droid.mjs +71 -29
  40. package/dist/harness-runtime/lib/dd-grok-daemon.mjs +73 -16
  41. package/dist/harness-runtime/lib/dd-grok.mjs +6 -3
  42. package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +66 -20
  43. package/dist/harness-runtime/lib/dd-opencode.mjs +30 -3
  44. package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +7 -6
  45. package/dist/harness-runtime/lib/dd-zcode.mjs +44 -14
  46. package/dist/harness-runtime/lib/delegation-instructions.mjs +1 -1
  47. package/dist/harness-runtime/lib/driver-recovery.mjs +9 -8
  48. package/dist/harness-runtime/lib/entity-references.d.mts +2 -0
  49. package/dist/harness-runtime/lib/entity-references.mjs +9 -0
  50. package/dist/harness-runtime/lib/managed-daemon.d.mts +1 -1
  51. package/dist/harness-runtime/lib/managed-daemon.mjs +24 -9
  52. package/dist/harness-runtime/lib/native-children.d.mts +8 -1
  53. package/dist/harness-runtime/lib/native-children.mjs +114 -8
  54. package/dist/harness-runtime/lib/native-hook-command.mjs +4 -4
  55. package/dist/harness-runtime/lib/native-request-proof.mjs +1 -1
  56. package/dist/harness-runtime/lib/observation-clock.d.mts +2 -2
  57. package/dist/harness-runtime/lib/observation-clock.mjs +7 -4
  58. package/dist/harness-runtime/lib/opencode-plugin.mjs +3 -3
  59. package/dist/harness-runtime/lib/operation-errors.mjs +1 -1
  60. package/dist/harness-runtime/lib/provider-overload-policy.d.mts +5 -3
  61. package/dist/harness-runtime/lib/provider-overload-policy.mjs +20 -7
  62. package/dist/harness-runtime/lib/runner-events.mjs +3 -1
  63. package/dist/harness-runtime/lib/setup-progress.mjs +8 -6
  64. package/dist/harness-runtime/lib/tool-observations.mjs +1 -1
  65. package/dist/schemas/diagnostic-stage-result.schema.json +354 -0
  66. package/dist/schemas/diagnostic-work-result.schema.json +296 -0
  67. package/dist/schemas/execution-bundle.schema.json +7 -1
  68. package/dist/schemas/mb-upgrade-stage-result.schema.json +863 -0
  69. package/dist/schemas/memory-repair-work-result.schema.json +9 -2
  70. package/dist/schemas/project-flow-pack-manifest.schema.json +1 -1
  71. package/dist/schemas/release-impact.schema.json +3 -3
  72. package/dist/schemas/stage-start-response.schema.json +4 -0
  73. package/dist/services/accepted-intent.js +13 -1
  74. package/dist/services/canon.js +43 -7
  75. package/dist/services/check-environment.js +21 -0
  76. package/dist/services/check-runner-evidence.js +267 -15
  77. package/dist/services/cli-operation-classifier.js +1 -1
  78. package/dist/services/cli-upgrade-allowlist.js +1 -1
  79. package/dist/services/code-checks.js +34 -19
  80. package/dist/services/compatibility-preflight.js +5 -2
  81. package/dist/services/controller-execution.js +10 -4
  82. package/dist/services/controller-fanout.js +34 -10
  83. package/dist/services/controller-memory-task.js +45 -0
  84. package/dist/services/controller-memory-worker.js +49 -0
  85. package/dist/services/dashboard.js +2 -2
  86. package/dist/services/descriptor-run-settlement.js +5 -3
  87. package/dist/services/descriptor-run-start.js +46 -20
  88. package/dist/services/descriptor-stage-coordinator.js +66 -13
  89. package/dist/services/descriptor-stage-settlement.js +2 -1
  90. package/dist/services/descriptor-stage-start.js +40 -4
  91. package/dist/services/diagnostic-evidence.js +73 -0
  92. package/dist/services/diagnostic-report-publication.js +72 -0
  93. package/dist/services/diagnostic-report.js +67 -0
  94. package/dist/services/diagnostic-run-bundle.js +74 -0
  95. package/dist/services/diagnostic-snapshot.js +159 -0
  96. package/dist/services/diagnostic-stage-handlers.js +108 -0
  97. package/dist/services/diagnostic-stage-results.js +132 -0
  98. package/dist/services/diagnostic-start.js +84 -0
  99. package/dist/services/diagnostic-work-batch.js +34 -0
  100. package/dist/services/diagnostic-work-collection.js +97 -0
  101. package/dist/services/diagnostic-work-registration.js +101 -0
  102. package/dist/services/diagnostic-work-results.js +93 -0
  103. package/dist/services/engines.js +64 -14
  104. package/dist/services/eval-snapshots.js +186 -119
  105. package/dist/services/execution-bundles.js +19 -7
  106. package/dist/services/external-work-launch.js +5 -3
  107. package/dist/services/flow-catalog.js +13 -4
  108. package/dist/services/frozen-engine.js +148 -11
  109. package/dist/services/hook-memory-routing.js +188 -0
  110. package/dist/services/hook-recovery-guard.js +14 -0
  111. package/dist/services/hook-session-observation.js +24 -0
  112. package/dist/services/hooks.js +27 -8
  113. package/dist/services/hybrid-contracts.js +4 -13
  114. package/dist/services/hybrid-digest.js +14 -0
  115. package/dist/services/installed-flow-source.js +47 -4
  116. package/dist/services/lifecycle-invocations.js +179 -38
  117. package/dist/services/memory-accepted-composition.js +5 -4
  118. package/dist/services/memory-completed-correction-wave.js +23 -15
  119. package/dist/services/memory-compose-handler.js +163 -21
  120. package/dist/services/memory-composition-acceptance.js +186 -10
  121. package/dist/services/memory-composition-graph.js +42 -11
  122. package/dist/services/memory-composition-registration.js +23 -8
  123. package/dist/services/memory-composition-work-custody.js +14 -9
  124. package/dist/services/memory-composition-work-results.js +15 -3
  125. package/dist/services/memory-corrected-review-batch.js +10 -3
  126. package/dist/services/memory-corrected-review-findings.js +4 -2
  127. package/dist/services/memory-corrected-review-inputs.js +10 -5
  128. package/dist/services/memory-corrected-review-receipt.js +17 -9
  129. package/dist/services/memory-corrected-review-registration.js +3 -2
  130. package/dist/services/memory-correction-check-receipt.js +20 -4
  131. package/dist/services/memory-custody-snapshot.js +36 -0
  132. package/dist/services/memory-finish-evidence.js +44 -22
  133. package/dist/services/memory-finish-handler.js +37 -23
  134. package/dist/services/memory-finish-integration.js +218 -48
  135. package/dist/services/memory-finish-readiness.js +66 -19
  136. package/dist/services/memory-git-integration.js +87 -18
  137. package/dist/services/memory-historical-corrected-review.js +21 -15
  138. package/dist/services/memory-historical-original-review.js +22 -9
  139. package/dist/services/memory-historical-repair-registration.js +14 -8
  140. package/dist/services/memory-historical-second-review.js +30 -11
  141. package/dist/services/memory-init-contexts.js +44 -2
  142. package/dist/services/memory-init-controller-start.js +27 -8
  143. package/dist/services/memory-init-git-policy.js +50 -21
  144. package/dist/services/memory-init-lint-capability.js +24 -6
  145. package/dist/services/memory-init-native-capacity.js +17 -4
  146. package/dist/services/memory-init-preflight.js +21 -17
  147. package/dist/services/memory-init-request-candidate.js +73 -17
  148. package/dist/services/memory-init-request-input.js +23 -7
  149. package/dist/services/memory-init-run-bundle.js +49 -12
  150. package/dist/services/memory-init-storage.js +17 -7
  151. package/dist/services/memory-init-worktrunk-capability.js +3 -7
  152. package/dist/services/memory-layer.js +71 -6
  153. package/dist/services/memory-lint-config.js +111 -2
  154. package/dist/services/memory-native-review-copy.js +5 -67
  155. package/dist/services/memory-normal-flow-entry.js +12 -0
  156. package/dist/services/memory-ordinary-defaults.js +22 -5
  157. package/dist/services/memory-preflight-handler.js +15 -0
  158. package/dist/services/memory-repair-current-view.js +1 -1
  159. package/dist/services/memory-repair-document-delta.js +70 -33
  160. package/dist/services/memory-repair-history.js +12 -7
  161. package/dist/services/memory-repair-physical-binding.js +1 -119
  162. package/dist/services/memory-repair-publication-plan.js +5 -3
  163. package/dist/services/memory-repair-publication-source.js +1 -1
  164. package/dist/services/memory-repair-publication.js +3 -2
  165. package/dist/services/memory-repair-registration.js +2 -1
  166. package/dist/services/memory-repair-work-baseline.js +4 -2
  167. package/dist/services/memory-repair-work-completion.js +32 -12
  168. package/dist/services/memory-repair-work-results.js +21 -2
  169. package/dist/services/memory-repair-workspace.js +11 -2
  170. package/dist/services/memory-repair-writer-copy.js +2 -1
  171. package/dist/services/memory-result-binding.js +78 -12
  172. package/dist/services/memory-retained-review-authority.js +17 -8
  173. package/dist/services/memory-retained-second-baseline.js +1 -1
  174. package/dist/services/memory-retained-second-repairs.js +12 -6
  175. package/dist/services/memory-review-correction-plan.js +47 -12
  176. package/dist/services/memory-review-handler.js +17 -17
  177. package/dist/services/memory-review-inputs.js +55 -9
  178. package/dist/services/memory-review-limits.js +10 -0
  179. package/dist/services/memory-review-receipt.js +2 -2
  180. package/dist/services/memory-review-registration.js +13 -4
  181. package/dist/services/memory-review-work-custody.js +3 -1
  182. package/dist/services/memory-review-work-input.js +26 -7
  183. package/dist/services/memory-review-work-results.js +26 -4
  184. package/dist/services/memory-second-corrected-review-inputs.js +5 -3
  185. package/dist/services/memory-second-correction-plan.js +2 -1
  186. package/dist/services/memory-second-repair-launch.js +4 -4
  187. package/dist/services/memory-second-repair-source.js +1 -1
  188. package/dist/services/memory-stage-dispatch.js +38 -1
  189. package/dist/services/memory-stage-results.js +113 -22
  190. package/dist/services/memory-tool-probe.js +25 -0
  191. package/dist/services/memory-upgrade-analysis-handler.js +243 -0
  192. package/dist/services/memory-upgrade-bootstrap-readiness.js +251 -0
  193. package/dist/services/memory-upgrade-evidence.js +100 -0
  194. package/dist/services/memory-upgrade-inventory.js +442 -0
  195. package/dist/services/memory-upgrade-publication-custody.js +73 -0
  196. package/dist/services/memory-upgrade-publication.js +413 -0
  197. package/dist/services/memory-upgrade-selection.js +430 -0
  198. package/dist/services/memory-upgrade-start.js +60 -0
  199. package/dist/services/memory-upgrade-storage-readiness.js +340 -0
  200. package/dist/services/memory-verification-acceptance.js +38 -17
  201. package/dist/services/memory-verification-checks.js +92 -27
  202. package/dist/services/memory-verification-receipt.js +6 -5
  203. package/dist/services/memory-verify-handler.js +15 -13
  204. package/dist/services/memory-work-result-json.js +12 -0
  205. package/dist/services/memory-work-result-preparation.js +5 -4
  206. package/dist/services/merge-server.js +4 -2
  207. package/dist/services/migrations.js +127 -98
  208. package/dist/services/native-daemon-history.js +2 -1
  209. package/dist/services/physical-startup.js +45 -0
  210. package/dist/services/project-registry.js +97 -0
  211. package/dist/services/project-summary.js +1 -1
  212. package/dist/services/projects.js +3 -93
  213. package/dist/services/prompt-delivery.js +71 -0
  214. package/dist/services/provider-capacity.js +112 -46
  215. package/dist/services/recovery-snapshot-database.js +32 -4
  216. package/dist/services/release-cache.js +8 -0
  217. package/dist/services/repair-intents.js +1 -12
  218. package/dist/services/repair-receipt.js +13 -0
  219. package/dist/services/run-control-worker.js +35 -18
  220. package/dist/services/run-control.js +2 -1
  221. package/dist/services/run-controller-adapter.js +2 -1
  222. package/dist/services/run-controller-process.js +4 -7
  223. package/dist/services/run-controller-recovery.js +8 -2
  224. package/dist/services/run-controller.js +215 -94
  225. package/dist/services/run-fork.js +1 -1
  226. package/dist/services/run-recovery-guard.js +30 -0
  227. package/dist/services/run-recovery.js +11 -44
  228. package/dist/services/run-start-custody.js +17 -5
  229. package/dist/services/runs.js +6 -1
  230. package/dist/services/runtime-scope-resume.js +12 -6
  231. package/dist/services/runtime-scope-stop.js +3 -2
  232. package/dist/services/runtime-scope-worker.js +46 -19
  233. package/dist/services/runtime-service.js +2 -1
  234. package/dist/services/sessions.js +3 -24
  235. package/dist/services/setup.js +63 -13
  236. package/dist/services/stage-handlers.js +22 -7
  237. package/dist/services/stage-pause.js +111 -44
  238. package/dist/services/version-status.js +1 -1
  239. package/dist/services/vnext-code.js +2 -2
  240. package/dist/services/vnext-fanout.js +51 -9
  241. package/dist/services/vnext-plan-review.js +3 -5
  242. package/dist/services/work-registry.js +117 -39
  243. package/dist/services/workspace-bootstrap.js +2 -1
  244. package/dist/services/workspace-files.js +1 -1
  245. package/dist/shared/entity-references.js +1 -8
  246. package/dist/storage/data-preservation.js +65 -0
  247. package/dist/storage/database.js +53 -19
  248. package/dist/storage/import-guard.js +84 -27
  249. package/dist/storage/memory-upgrade-schema-bootstrap.js +38 -0
  250. package/dist/storage/paired-writer-migration.js +424 -0
  251. package/dist/storage/writer-contract.js +11 -4
  252. package/dist/storage/writer-migration.js +313 -52
  253. package/dist/storage/writer-restore.js +217 -0
  254. package/package.json +13 -6
@@ -0,0 +1,123 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { createHash } from "node:crypto";
4
+ import { prepareCommandRoute } from "./command-inputs.js";
5
+ import { parseOutputOptions } from "./output-options.js";
6
+ import { AppError, errorRecord } from "../shared/errors.js";
7
+ import { writePreparationError } from "./error-output.js";
8
+ import { restoreMemoryUpgradeSelection } from "../services/memory-upgrade-selection.js";
9
+ import { assertMemoryUpgradeProjectCurrent } from "../services/memory-upgrade-storage-readiness.js";
10
+ import { PreparedPairedWriterMigration, PreparedPairedWriterRecovery } from "../storage/paired-writer-migration.js";
11
+ import { readBoundedReleaseFile } from "../services/release-source.js";
12
+ import { shellArgument } from "../services/runtime-command.js";
13
+ function refuse(reason) { throw new AppError("memory_storage_transition_invalid", "Storage transition requires the original upgrade and its selected immutable engine", 1, { reason, effect: "no_effect" }); }
14
+ const hash = (bytes) => createHash("sha256").update(bytes).digest("hex");
15
+ /** Pre-context transport: no initialize/prepared-existing database connection,
16
+ * router fallback, provider or RUN creation. Executed by the selected engine. */
17
+ export function runMemoryStorageTransitionCli(args, env = process.env, io = process) {
18
+ let effects = false;
19
+ try {
20
+ const output = parseOutputOptions(args), route = prepareCommandRoute(output.args);
21
+ if (route.key !== "runtime storage transition" || output.responseFile || output.progressJsonl)
22
+ throw new AppError("usage", "Storage transition accepts stdout output only", 2, { effect: "no_effect" });
23
+ const option = (key) => route.parsed.options.get(key)?.[0];
24
+ const action = option("action"), expected = option("expected-sha256"), recoveryId = option("recovery-id");
25
+ if (env.DD_FLOW_INVOCATION_READONLY === "1" && !["status", "recovery-status"].includes(action))
26
+ refuse("readonly_invocation");
27
+ if (action !== "plan" && (!expected || !/^[a-f0-9]{64}$/.test(expected)))
28
+ throw new AppError("usage", "This action requires --expected-sha256 from its retained request", 2, { effect: "no_effect" });
29
+ if ((action === "recovery-plan") !== Boolean(recoveryId))
30
+ throw new AppError("usage", "--recovery-id is required only for recovery-plan", 2, { effect: "no_effect" });
31
+ if (["apply", "recovery-plan", "recover"].includes(action) && !route.parsed.options.has("offline"))
32
+ throw new AppError("usage", "Storage mutation requires --offline after draining all runtime owners", 2, { effect: "no_effect" });
33
+ const home = env.DD_FLOW_HOME, resource = env.DD_FLOW_RESOURCE_HOME;
34
+ for (const root of [home, resource])
35
+ if (!root || !path.isAbsolute(root) || fs.realpathSync(root) !== root)
36
+ refuse("explicit_canonical_homes_required");
37
+ const scope = { requestId: option("request-id"), projectRoot: option("project-root"), workspaceRoot: option("workspace-root") };
38
+ const selected = restoreMemoryUpgradeSelection(home, scope), engine = selected.engine;
39
+ if (option("selection-sha256") !== selected.sha256)
40
+ refuse("original_selection_changed");
41
+ if (!process.argv[1] || fs.realpathSync(process.argv[1]) !== engine.entrypoint)
42
+ refuse("selected_engine_entrypoint_required");
43
+ const file = path.join(path.dirname(selected.original.candidate.retain().file), "storage-migration.json");
44
+ const pairedId = `upgrade-${selected.sha256.slice(0, 32)}`;
45
+ const assertProjectCurrent = () => {
46
+ if (!selected.payload.git || !selected.oldTree)
47
+ refuse("productive_upgrade_selection_required");
48
+ assertMemoryUpgradeProjectCurrent(selected, scope, env);
49
+ };
50
+ const restorePair = (sha) => {
51
+ const pair = PreparedPairedWriterMigration.restore(file, sha), material = pair.recoveryMaterial();
52
+ if (material.intent.selection_sha256 !== selected.sha256 || material.intent.request_id !== pairedId
53
+ || material.intent.project_root !== scope.projectRoot || material.intent.workspace_root !== scope.workspaceRoot
54
+ || material.intent.stores[0]?.home !== home || material.intent.stores[1]?.home !== resource)
55
+ refuse("retained_pair_scope_changed");
56
+ return pair;
57
+ };
58
+ const command = (next, sha) => `DD_FLOW_RESOURCE_HOME=${shellArgument(resource)} ${engine.command(home, [
59
+ "runtime", "storage", "transition", "--action", next, "--project-root", scope.projectRoot, "--workspace-root", scope.workspaceRoot,
60
+ "--request-id", scope.requestId, "--selection-sha256", selected.sha256, "--expected-sha256", sha, ...(["apply", "recover"].includes(next) ? ["--offline"] : []), "--json"
61
+ ])}`;
62
+ let result;
63
+ if (action === "plan") {
64
+ let pair;
65
+ if (fs.existsSync(file))
66
+ pair = restorePair(expected ?? hash(readBoundedReleaseFile(file, 16 * 1024 * 1024)));
67
+ else {
68
+ if (expected)
69
+ refuse("original_pair_missing");
70
+ assertProjectCurrent();
71
+ pair = PreparedPairedWriterMigration.prepare({ requestId: pairedId, requestFile: file, projectRoot: scope.projectRoot, workspaceRoot: scope.workspaceRoot,
72
+ runHome: home, resourceHome: resource, selectionSha256: selected.sha256, bootstrapSchema: "memory-upgrade@1" });
73
+ }
74
+ effects = true;
75
+ const request = pair.retain();
76
+ result = { request, apply_command: command("apply", pair.sha256), status_command: command("status", pair.sha256) };
77
+ }
78
+ else if (action === "recover" || action === "recovery-status") {
79
+ const recovery = PreparedPairedWriterRecovery.restore(file + ".recovery.json", expected), reference = recovery.migrationReference();
80
+ if (reference.path !== file)
81
+ refuse("recovery_migration_scope_changed");
82
+ restorePair(reference.sha256);
83
+ if (action === "recover") {
84
+ effects = true;
85
+ result = recovery.apply();
86
+ }
87
+ else
88
+ result = recovery.reconcile();
89
+ }
90
+ else {
91
+ const pair = restorePair(expected);
92
+ if (action === "status") {
93
+ const observed = pair.reconcile();
94
+ result = { status: observed.status,
95
+ proof: fs.existsSync(file + ".result.json") ? pair.readEvidence() : null,
96
+ outcomes: observed.outcomes.map(outcome => ({ status: outcome.status, ...(outcome.reason ? { reason: outcome.reason } : {}) })) };
97
+ }
98
+ else if (action === "apply") {
99
+ if (pair.reconcile().status === "not_applied")
100
+ assertProjectCurrent();
101
+ effects = true;
102
+ pair.apply();
103
+ result = { status: "applied", proof: pair.readEvidence() };
104
+ }
105
+ else {
106
+ const recovery = PreparedPairedWriterRecovery.prepare(pair, recoveryId);
107
+ effects = true;
108
+ const request = recovery.retain();
109
+ result = { request, recover_command: command("recover", request.sha256), status_command: command("recovery-status", request.sha256) };
110
+ }
111
+ }
112
+ io.stdout.write(`${JSON.stringify({ ok: true, action: "runtime_storage_transition", operation: action, scope, selection_sha256: selected.sha256,
113
+ engine: { manifest_sha256: engine.manifestSha256, sha256: engine.manifest().integrity.checksum }, result }, null, output.json ? undefined : 2)}\n`);
114
+ return 0;
115
+ }
116
+ catch (error) {
117
+ const record = errorRecord(error);
118
+ if (effects)
119
+ record.details = { ...record.details, effect: "unknown", next_action: "reconcile_exact_request" };
120
+ writePreparationError(io, args, record);
121
+ return record.exitCode;
122
+ }
123
+ }
@@ -0,0 +1,189 @@
1
+ import { createHash } from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { createContext } from "../runtime/context.js";
5
+ import { AppError, errorRecord } from "../shared/errors.js";
6
+ import { resolveDdFlowHome } from "../storage/paths.js";
7
+ import { hybridDigest } from "../services/hybrid-contracts.js";
8
+ import { readSetupStatus } from "../services/setup.js";
9
+ import { readOperatorProfiles, readOperatorProfileInput } from "../services/operator-profiles.js";
10
+ import { onboardExecutor } from "../services/executor-onboarding.js";
11
+ import { readMemoryUpgradeRequestCandidate, prepareMemoryUpgradeRequestCandidate } from "../services/memory-init-request-candidate.js";
12
+ import { readMemoryUpgradeRequestInput } from "../services/memory-init-request-input.js";
13
+ import { PreparedMemoryUpgradeSelection, readMemoryUpgradeSelection, restoreMemoryUpgradeSelection } from "../services/memory-upgrade-selection.js";
14
+ import { PreparedUpgradeStorageReadiness, readUpgradeStorageReadiness, assertMemoryUpgradeProjectCurrent } from "../services/memory-upgrade-storage-readiness.js";
15
+ import { startMemoryUpgradeRun, registerMemoryUpgradeProject } from "../services/memory-upgrade-start.js";
16
+ import { openMemoryInitStores, assertMemoryInitStorePrerequisites } from "../services/memory-init-stores.js";
17
+ import { PreparedInstalledFlowSource } from "../services/installed-flow-source.js";
18
+ import { selectMemoryInitInstalledEngine } from "../services/memory-init-engine-release.js";
19
+ import { FrozenGenericExecution } from "../services/generic-execution.js";
20
+ import { prepareMemoryInitHarnessCommands } from "../services/memory-init-project-bootstrap.js";
21
+ import { produceMemoryInitNativeCapacity, restoreCompletedMemoryNativeCapacity } from "../services/memory-init-native-capacity.js";
22
+ import { inspectMemoryLintExecutable, inspectMemoryGitExecutable } from "../services/memory-init-preflight.js";
23
+ import { PreparedMemoryInitLintCapability } from "../services/memory-init-lint-capability.js";
24
+ import { observeMemoryGitPolicySelection } from "../services/memory-init-git-policy.js";
25
+ import { parseRuntimeBudget } from "../services/runtime-budget.js";
26
+ import { launchAndObserveMemoryFlow } from "./memory-init.js";
27
+ import { prepareMemoryUpgradeInvocation, assertMemoryInitInvocationReplay } from "./memory-init-invocation.js";
28
+ import { askExecutorChoice } from "./executor-onboarding.js";
29
+ import { parseOutputOptions } from "./output-options.js";
30
+ import { prepareResponseDestination } from "./response-file.js";
31
+ import { prepareFileBytes } from "./input-preparation.js";
32
+ import { writePreparationError } from "./error-output.js";
33
+ /** Upgrade never enters init's project publication path. Read-only original
34
+ * request lookup precedes ambient selection; writable stores are opened only
35
+ * after retaining the target/current selection, without initializing existing
36
+ * stores. Pristine stores use shared pair publication, never init documents.
37
+ * Storage-changing transitions must first settle their explicit migration.
38
+ */
39
+ export async function runMemoryUpgradeCli(args, env = process.env, io = process) {
40
+ let context, effects = false;
41
+ try {
42
+ const output = parseOutputOptions(args), invocation = prepareMemoryUpgradeInvocation(output.args);
43
+ parseRuntimeBudget(env.DD_FLOW_RUNTIME_BUDGET);
44
+ if (output.responseFile)
45
+ prepareResponseDestination(output.responseFile);
46
+ const home = resolveDdFlowHome(env), scope = invocation.scope;
47
+ const reader = createContext(env, "read_existing");
48
+ let storage;
49
+ try {
50
+ storage = readUpgradeStorageReadiness(reader, scope);
51
+ }
52
+ finally {
53
+ reader.db.close?.();
54
+ }
55
+ const candidate = storage ? null : readMemoryUpgradeRequestCandidate(home, scope);
56
+ const original = storage?.receipt.selection?.request_input ?? candidate?.input;
57
+ if (storage && !original)
58
+ throw new AppError("memory_upgrade_original_request_required", "Historical storage metadata cannot select a fresh upgrade", 1, { effect: "no_effect" });
59
+ if (original) {
60
+ const objective = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(Buffer.from(original.intake_base64, "base64"));
61
+ const raw = readMemoryUpgradeRequestInput(original, scope, objective);
62
+ if (invocation.profileFile)
63
+ invocation.execution.rootProfile = readOperatorProfileInput(invocation.profileFile);
64
+ assertMemoryInitInvocationReplay(invocation, raw.selection, original.intake_sha256);
65
+ if (invocation.nativeConfigHome) {
66
+ const selected = storage?.selection?.payload.execution ?? readMemoryUpgradeSelection(home, scope)?.payload.execution;
67
+ const reference = selected?.snapshot.settings.delegation.native_capacity_receipt;
68
+ const encoded = selected?.receipt_blobs?.find(([sha]) => sha === reference?.sha256)?.[1];
69
+ if (!encoded || JSON.parse(Buffer.from(encoded, "base64").toString("utf8")).input.native_config_home !== invocation.nativeConfigHome)
70
+ throw new AppError("memory_upgrade_request_conflict", "Original native configuration selection changed", 1, { effect: "no_effect" });
71
+ }
72
+ if (!storage && !readMemoryUpgradeSelection(home, scope)) {
73
+ // A raw candidate is not resolved selection authority. Resume only its
74
+ // original target/profile request; never replace it with new CLI input.
75
+ effects = true;
76
+ const native = raw.selection.execution.delegation?.mode === "native"
77
+ ? await restoreCompletedMemoryNativeCapacity({ home, scope, env }) : undefined;
78
+ if (native && hybridDigest(native.reference) !== hybridDigest(raw.selection.execution.delegation?.nativeCapacityReceipt))
79
+ throw new AppError("memory_upgrade_request_conflict", "Completed probe differs from the original request", 1, { effect: "unknown" });
80
+ PreparedMemoryUpgradeSelection.prepare(home, scope, candidate.candidate, { env, now: new Date().toISOString(),
81
+ ...(native ? { receipts: native.receipts, receiptBytes: native.receiptBytes } : {}) }).retain();
82
+ }
83
+ }
84
+ else {
85
+ if (!invocation.intakeFile || !invocation.intakeSha256 || !invocation.gitPolicy)
86
+ throw new AppError("usage", "Fresh upgrade requires original intake file/hash and explicit Git policy", 2, { effect: "no_effect" });
87
+ const intake = prepareFileBytes(invocation.intakeFile, "original upgrade intake", 64 * 1024).bytes;
88
+ if (createHash("sha256").update(intake).digest("hex") !== invocation.intakeSha256)
89
+ throw new AppError("memory_upgrade_intake_changed", "Original intake differs from its declared hash", 2, { effect: "no_effect" });
90
+ let objective;
91
+ try {
92
+ objective = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(intake);
93
+ }
94
+ catch {
95
+ throw new AppError("usage", "Original upgrade intake must be UTF-8", 2, { effect: "no_effect" });
96
+ }
97
+ const setup = readSetupStatus(home), installation = setup.installation;
98
+ if (!installation || setup.installation_readiness !== "installed")
99
+ throw new AppError("memory_upgrade_setup_required", "Install the target release with setup before project upgrade", 1, { effect: "no_effect" });
100
+ const stdin = io.stdin ?? process.stdin;
101
+ const selected = await onboardExecutor({
102
+ ...(typeof invocation.execution.rootProfile === "string" ? { profileId: invocation.execution.rootProfile } : {}),
103
+ ...(invocation.profileFile ? { profileFile: invocation.profileFile } : {})
104
+ }, () => readOperatorProfiles(home), { allowSkip: false, nonInteractive: invocation.nonInteractive || output.json || output.progressJsonl
105
+ || !stdin.isTTY || !io.stderr.isTTY }, settings => askExecutorChoice(settings, { stdin, stderr: io.stderr }, { allowSave: false, allowSkip: false }));
106
+ if (selected.source === "explicit-file" || selected.source === "explicit-profile")
107
+ invocation.execution.rootProfile = selected.profile;
108
+ else if (invocation.execution.rootProfile === undefined && selected.source === "explicit-id")
109
+ invocation.execution.rootProfile = selected.profile.id;
110
+ let native;
111
+ if (invocation.execution.delegation?.mode === "native") {
112
+ if (!invocation.allowLiveProbe)
113
+ throw new AppError("usage", "Fresh native upgrade requires --allow-live-probe", 2, { effect: "no_effect" });
114
+ const source = PreparedInstalledFlowSource.fromInstalledRelease(home, installation.manifest_sha256, "mb-upgrade");
115
+ const engine = selectMemoryInitInstalledEngine(home, installation.manifest_sha256);
116
+ engine.admitMemoryUpgradeFlow(source);
117
+ if (!fs.existsSync(path.join(home, "db.sqlite")))
118
+ assertMemoryInitStorePrerequisites(env, scope);
119
+ engine.qualifyMemoryUpgradeStorage(home, env.DD_FLOW_RESOURCE_HOME ?? home);
120
+ observeMemoryGitPolicySelection(env, { scope, gitExecutable: inspectMemoryGitExecutable(env).executable, selection: invocation.gitPolicy });
121
+ const lint = inspectMemoryLintExecutable(env);
122
+ PreparedMemoryInitLintCapability.prepareExecutable(lint.executable, lint.probe, env, { temporaryConfiguration: true });
123
+ const execution = FrozenGenericExecution.prepare(source.resolveRegisteredFlow(), readOperatorProfiles(home), {
124
+ ...invocation.execution, delegation: { mode: "external", max_parallel: invocation.execution.delegation.max_parallel ?? 1 }
125
+ }, { requestId: scope.requestId, projectRoot: scope.projectRoot, workspace: scope.workspaceRoot });
126
+ prepareMemoryInitHarnessCommands({ ddFlowHome: home }, execution);
127
+ const snapshot = execution.snapshot(), profile = snapshot.profiles.find(entry => entry.id === snapshot.settings.root_profile).profile;
128
+ if (snapshot.profiles.some(entry => hybridDigest({ ...entry.profile, id: profile.id }) !== hybridDigest(profile)))
129
+ throw new AppError("generic_execution_invalid", "Native delegation cannot change execution profiles", 2, { effect: "no_effect" });
130
+ effects = true;
131
+ native = await produceMemoryInitNativeCapacity({ home, scope, profile, maximum: invocation.execution.delegation.max_parallel ?? 1,
132
+ ...(invocation.nativeConfigHome ? { nativeConfigHome: invocation.nativeConfigHome } : {}), allowLiveProbe: true, env,
133
+ requestSha256: hybridDigest({ flow: "mb-upgrade", scope, execution: invocation.execution, gitPolicy: invocation.gitPolicy,
134
+ objective, intake: invocation.intakeSha256, target: installation.manifest_sha256 }) });
135
+ invocation.execution.delegation.nativeCapacityReceipt = native.reference;
136
+ }
137
+ const raw = { schema_id: "dd-flow/memory-upgrade-request-selection@1", scope,
138
+ execution: invocation.execution, git_policy: invocation.gitPolicy, objective, engine_checksum: null,
139
+ receipts: [...native?.receipts ?? []], target_installation_sha256: installation.manifest_sha256 };
140
+ const prepared = prepareMemoryUpgradeRequestCandidate(home, scope, Buffer.from(JSON.stringify(raw)), intake);
141
+ effects = true;
142
+ prepared.retain();
143
+ PreparedMemoryUpgradeSelection.prepare(home, scope, prepared, { env, now: new Date().toISOString(),
144
+ ...(native ? { receipts: native.receipts, receiptBytes: native.receiptBytes } : {}) }).retain();
145
+ }
146
+ if (!storage) {
147
+ const selected = restoreMemoryUpgradeSelection(home, scope);
148
+ if (!fs.existsSync(path.join(home, "db.sqlite"))) {
149
+ assertMemoryUpgradeProjectCurrent(selected, scope, env);
150
+ effects = true;
151
+ // Shared store creation owns pristine/replay checks and never writes
152
+ // project documents. Existing stores still take explicit migration.
153
+ const fresh = openMemoryInitStores(env, { scope, selectionSha256: selected.sha256 });
154
+ fresh.context.db.close?.();
155
+ }
156
+ try {
157
+ selected.engine.admitMemoryUpgradeStorage(home, env.DD_FLOW_RESOURCE_HOME ?? home);
158
+ }
159
+ catch (error) {
160
+ if (!(error instanceof AppError) || error.code !== "frozen_engine_storage_incompatible")
161
+ throw error;
162
+ // Retain only the selected engine's explicit plan. Never apply it as a
163
+ // side effect of starting upgrade or open the old stores for writes.
164
+ effects = true;
165
+ const planned = selected.engine.transitionMemoryUpgradeStorage({ home, resourceHome: env.DD_FLOW_RESOURCE_HOME ?? home,
166
+ scope, selectionSha256: selected.sha256, action: "plan" });
167
+ throw new AppError("memory_upgrade_storage_transition_required", "Drain runtime owners, apply the retained storage plan, then repeat this exact upgrade request", 1, { effect: "partial", storage_effect: "no_effect", migration: planned.result });
168
+ }
169
+ }
170
+ context = createContext(env, "prepared_existing", undefined, { resourceDatabaseMode: "prepared_existing" });
171
+ effects = true;
172
+ if (!storage) {
173
+ registerMemoryUpgradeProject(context, scope);
174
+ PreparedUpgradeStorageReadiness.prepareFromSelection(context, scope).apply(context);
175
+ }
176
+ const started = startMemoryUpgradeRun(context, scope);
177
+ return await launchAndObserveMemoryFlow(context, invocation, started, "mb-upgrade", output, env, io);
178
+ }
179
+ catch (error) {
180
+ const record = errorRecord(error);
181
+ if (effects)
182
+ record.details = { ...record.details, effect: record.details.effect === "partial" ? "partial" : "unknown", next_action: "observe_or_reconcile_exact_request" };
183
+ writePreparationError(io, args, record);
184
+ return record.exitCode;
185
+ }
186
+ finally {
187
+ context?.db.close?.();
188
+ }
189
+ }
@@ -1,6 +1,7 @@
1
1
  import { AppError, errorRecord } from "../shared/errors.js";
2
2
  import { parseCommandArgs } from "../services/lifecycle-command.js";
3
3
  import { channel } from "node:diagnostics_channel";
4
+ import { nativeHookNonParticipation } from "./native-hook-preparation.js";
4
5
  /** These ingress commands consume a prepared store; they never prepare it. */
5
6
  export function isNativeHookIngress(args) {
6
7
  return args[2] === "handle" && ((args[1] === "event" && ["agy", "grok", "droid", "opencode", "zcode"].includes(args[0] ?? ""))
@@ -8,7 +9,8 @@ export function isNativeHookIngress(args) {
8
9
  }
9
10
  export async function dispatchNativeHook(args, stdin, env) {
10
11
  const started = performance.now();
11
- const deadline = env.DD_FLOW_HOOK_DEADLINE_AT ? Number(env.DD_FLOW_HOOK_DEADLINE_AT) : undefined;
12
+ const deadline = env.DD_FLOW_HOOK_DEADLINE_AT ? Number(env.DD_FLOW_HOOK_DEADLINE_AT) : Date.now() + 25_000;
13
+ env = { ...env, DD_FLOW_HOOK_DEADLINE_AT: String(deadline) };
12
14
  if (deadline !== undefined && !Number.isSafeInteger(deadline))
13
15
  throw new AppError("hook_deadline_invalid", "Native hook deadline must be an absolute millisecond timestamp", 1, { effect: "unknown" });
14
16
  const phase = (name) => {
@@ -24,6 +26,11 @@ export async function dispatchNativeHook(args, stdin, env) {
24
26
  for (const [name, values] of parsed.options)
25
27
  if (!["project-root", "json"].includes(name) || values.length !== 1 || name === "json" && values[0] !== "__dd_flow_flag__")
26
28
  throw new AppError("usage", `Invalid native hook option: ${name}`, 2);
29
+ const unrelated = nativeHookNonParticipation(args, stdin);
30
+ if (unrelated) {
31
+ phase("ack");
32
+ return unrelated;
33
+ }
27
34
  const [hooks, { createContext }, { resolveDdFlowHome }] = await Promise.all([
28
35
  import("../services/hooks.js"), import("../runtime/context.js"), import("../storage/paths.js")
29
36
  ]);
@@ -58,7 +65,9 @@ export async function dispatchNativeHook(args, stdin, env) {
58
65
  timing.subscribe(log);
59
66
  try {
60
67
  phase("receipt");
61
- return await handler(context, { projectRoot, stdin });
68
+ const result = await handler(context, { projectRoot, stdin });
69
+ phase("ack");
70
+ return result;
62
71
  }
63
72
  finally {
64
73
  timing.unsubscribe(log);
@@ -68,8 +77,9 @@ export async function dispatchNativeHook(args, stdin, env) {
68
77
  export async function runNativeHookIngress(args) {
69
78
  try {
70
79
  const { readNativeHookInput, hookBudget } = await import("../harness-runtime/lib/native-hook-command.mjs");
71
- const stdin = await readNativeHookInput(process.stdin, hookBudget(25_000, process.env.DD_FLOW_HOOK_DEADLINE_AT ? Number(process.env.DD_FLOW_HOOK_DEADLINE_AT) : null));
72
- process.stdout.write(`${JSON.stringify(await dispatchNativeHook(args, stdin, process.env))}\n`);
80
+ const budget = hookBudget(25_000, process.env.DD_FLOW_HOOK_DEADLINE_AT ? Number(process.env.DD_FLOW_HOOK_DEADLINE_AT) : null);
81
+ const stdin = await readNativeHookInput(process.stdin, budget);
82
+ process.stdout.write(`${JSON.stringify(await dispatchNativeHook(args, stdin, { ...process.env, DD_FLOW_HOOK_DEADLINE_AT: String(budget.deadlineAt) }))}\n`);
73
83
  return 0;
74
84
  }
75
85
  catch (error) {
@@ -0,0 +1,55 @@
1
+ import path from "node:path";
2
+ import { parseJsonObject } from "../shared/json.js";
3
+ import { continuationCommand, parseLifecycleCommand } from "../services/lifecycle-command.js";
4
+ const object = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
5
+ const text = (value) => typeof value === "string" && value.length > 0 ? value : undefined;
6
+ /** Only a validated, ordinary before-tool command can skip the stateful handler.
7
+ * In particular after-tool, Stop, malformed and ambiguous events remain owned
8
+ * by their existing handler; this gate does not invent an admission policy. */
9
+ export function nativeHookNonParticipation(args, stdin) {
10
+ const payload = parseJsonObject(stdin || "{}", "native hook stdin");
11
+ let command;
12
+ if (args[0] === "zcode" && args[1] === "event") {
13
+ const update = object(object(payload.params).update);
14
+ if (payload.method !== "session/update" || update.sessionUpdate !== "tool_call")
15
+ return null;
16
+ const tool = text(object(object(update._meta).claudeCode).toolName);
17
+ if (!tool || !["Bash", "bash", "execute"].includes(tool))
18
+ return null;
19
+ const input = object(update.rawInput);
20
+ command = text(input.command) ?? text(input.cmd);
21
+ }
22
+ else if (args[0] === "grok" || args[0] === "zcode") {
23
+ const event = text(payload.hook_event_name) ?? text(payload.hookEventName);
24
+ if (event && !["PreToolUse", "pre_tool_use"].includes(event))
25
+ return null;
26
+ const tool = text(payload.tool_name) ?? text(payload.toolName);
27
+ if (tool && !(args[0] === "grok" ? ["Bash", "bash", "run_terminal_command", "RunTerminalCommand"] : ["Bash", "bash", "execute"]).includes(tool))
28
+ return null;
29
+ const input = object(payload.tool_input ?? payload.toolInput);
30
+ command = text(input.command) ?? text(input.cmd) ?? text(payload.command);
31
+ }
32
+ else {
33
+ const family = args[0];
34
+ if (payload.schema_id !== `dd-flow/${family}-tool-event@1` || payload.phase !== "before")
35
+ return null;
36
+ const session = object(payload.session), tool = text(payload.tool);
37
+ const directory = family === "agy" ? text(payload.launch_cwd) : text(session.directory);
38
+ const id = family === "agy" ? text(payload.conversation_id) : text(session.provider_session_id);
39
+ if (!directory || !path.isAbsolute(directory) || !id || !text(payload.daemon_id) || !text(payload.event_id)
40
+ || family === "opencode" && !text(payload.tool_call_id) || !tool
41
+ || !(family === "droid" ? ["Execute"] : ["bash", "Bash", "shell", "run_command", "run_terminal_command", "RunTerminalCommand"]).includes(tool)
42
+ || family === "droid" && session.parent_provider_session_id === id)
43
+ return null;
44
+ if (family === "agy" && Array.isArray(payload.workspace_paths)
45
+ && payload.workspace_paths.length && !payload.workspace_paths.some(value => typeof value === "string" && path.resolve(value) === path.resolve(directory)))
46
+ return null;
47
+ const input = object(payload.input);
48
+ command = text(input.command) ?? text(input.cmd) ?? text(input.CommandLine);
49
+ }
50
+ if (!command)
51
+ return null;
52
+ const parsed = parseLifecycleCommand(command);
53
+ return parsed.participation === "none" && !continuationCommand(command)
54
+ ? { ok: true, observed: false, reason: args[0] === "zcode" && args[1] === "event" ? "acp_evidence_only" : "event_not_participating" } : null;
55
+ }
@@ -40,7 +40,8 @@ export function writeResponseFile(payload, output) {
40
40
  if (!output.responseFile)
41
41
  return payload;
42
42
  const file = path.resolve(output.responseFile), temporary = `${file}.tmp-${randomUUID()}`;
43
- const visible = payload && typeof payload === "object" && !Array.isArray(payload) ? { ...payload } : { ok: true, result: payload };
43
+ const record = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
44
+ const visible = record ? { ...(record.prompt_delivery ? { prompt_delivery: record.prompt_delivery } : {}), ...record } : { ok: true, result: payload };
44
45
  let ownsTemporary = false;
45
46
  try {
46
47
  prepareResponseDestination(file);