@deksden-com/dd-flow-cli 0.9.0-beta.14 → 0.9.0-beta.141
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.
- package/CHANGELOG.md +740 -0
- package/README.md +143 -1
- package/dist/build-info.json +8 -8
- package/dist/cli/argument-values.js +38 -0
- package/dist/cli/command-inputs.js +376 -0
- package/dist/cli/command-metadata.js +197 -0
- package/dist/cli/command-option-descriptions.js +191 -0
- package/dist/cli/completion-cli.js +70 -0
- package/dist/cli/completion.js +57 -0
- package/dist/cli/error-output.js +52 -0
- package/dist/cli/executor-onboarding.js +76 -0
- package/dist/cli/flows.js +29 -0
- package/dist/cli/harnesses.js +84 -0
- package/dist/cli/help.js +328 -18
- package/dist/cli/hook-ingress.js +78 -0
- package/dist/cli/input-preparation.js +165 -0
- package/dist/cli/memory-flow-admission.js +44 -0
- package/dist/cli/memory-init-invocation.js +78 -0
- package/dist/cli/memory-init.js +240 -0
- package/dist/cli/memory-storage-admission.js +53 -0
- package/dist/cli/models.js +39 -0
- package/dist/cli/native-hook-ingress.js +80 -0
- package/dist/cli/operator-profiles.js +61 -0
- package/dist/cli/output-options.js +35 -0
- package/dist/cli/response-file.js +71 -0
- package/dist/cli/run-cli.js +1689 -545
- package/dist/cli/runtime-maintenance-diagnostics.js +46 -0
- package/dist/cli/runtime-maintenance-ingress.js +52 -0
- package/dist/cli/runtime-process.js +153 -0
- package/dist/cli/setup.js +149 -0
- package/dist/cli.js +71 -2
- package/dist/domain/stage-catalog.js +1 -1
- package/dist/domain/stage-handler-identities.js +20 -0
- package/dist/harness-runtime/DD-ZCODE.md +112 -0
- package/dist/harness-runtime/OPENCODE-LICENSE.txt +24 -0
- package/dist/harness-runtime/bin/dd-agy.mjs +56 -0
- package/dist/harness-runtime/bin/dd-codex.mjs +27 -0
- package/dist/harness-runtime/bin/dd-droid.mjs +34 -0
- package/dist/harness-runtime/bin/dd-grok.mjs +40 -0
- package/dist/harness-runtime/bin/dd-opencode.mjs +22 -0
- package/dist/harness-runtime/bin/dd-zcode.mjs +87 -0
- package/dist/harness-runtime/lib/adapter-timeouts.d.mts +5 -0
- package/dist/harness-runtime/lib/adapter-timeouts.mjs +34 -0
- package/dist/harness-runtime/lib/agy-hooks.d.mts +1 -0
- package/dist/harness-runtime/lib/agy-hooks.mjs +9 -0
- package/dist/harness-runtime/lib/daemon-observation.d.mts +2 -0
- package/dist/harness-runtime/lib/daemon-observation.mjs +163 -0
- package/dist/harness-runtime/lib/daemon-operations.d.mts +1 -0
- package/dist/harness-runtime/lib/daemon-operations.mjs +371 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.d.mts +6 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +728 -0
- package/dist/harness-runtime/lib/dd-agy.mjs +70 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.mjs +196 -0
- package/dist/harness-runtime/lib/dd-codex.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex.mjs +817 -0
- package/dist/harness-runtime/lib/dd-droid-daemon.mjs +140 -0
- package/dist/harness-runtime/lib/dd-droid.mjs +584 -0
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +498 -0
- package/dist/harness-runtime/lib/dd-grok.mjs +177 -0
- package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +557 -0
- package/dist/harness-runtime/lib/dd-opencode.mjs +363 -0
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +751 -0
- package/dist/harness-runtime/lib/dd-zcode.mjs +1145 -0
- package/dist/harness-runtime/lib/delegation-instructions.d.mts +14 -0
- package/dist/harness-runtime/lib/delegation-instructions.mjs +157 -0
- package/dist/harness-runtime/lib/dispatch-fence.mjs +14 -0
- package/dist/harness-runtime/lib/driver-recovery.mjs +139 -0
- package/dist/harness-runtime/lib/droid-observation.mjs +66 -0
- package/dist/harness-runtime/lib/lease-renewal.d.mts +8 -0
- package/dist/harness-runtime/lib/lease-renewal.mjs +37 -0
- package/dist/harness-runtime/lib/lifecycle-command.d.mts +20 -0
- package/dist/harness-runtime/lib/lifecycle-command.mjs +273 -0
- package/dist/harness-runtime/lib/lifecycle-contract.d.mts +10 -0
- package/dist/harness-runtime/lib/lifecycle-contract.mjs +19 -0
- package/dist/harness-runtime/lib/managed-daemon.d.mts +16 -0
- package/dist/harness-runtime/lib/managed-daemon.mjs +683 -0
- package/dist/harness-runtime/lib/managed-flow-env.mjs +15 -0
- package/dist/harness-runtime/lib/model-observations.mjs +101 -0
- package/dist/harness-runtime/lib/native-children.d.mts +16 -0
- package/dist/harness-runtime/lib/native-children.mjs +113 -0
- package/dist/harness-runtime/lib/native-hook-command.d.mts +7 -0
- package/dist/harness-runtime/lib/native-hook-command.mjs +251 -0
- package/dist/harness-runtime/lib/native-request-proof.d.mts +2 -0
- package/dist/harness-runtime/lib/native-request-proof.mjs +65 -0
- package/dist/harness-runtime/lib/native-streams.mjs +45 -0
- package/dist/harness-runtime/lib/observation-clock.d.mts +18 -0
- package/dist/harness-runtime/lib/observation-clock.mjs +92 -0
- package/dist/harness-runtime/lib/opencode-plugin.mjs +85 -0
- package/dist/harness-runtime/lib/operation-context.mjs +5 -0
- package/dist/harness-runtime/lib/operation-errors.d.mts +16 -0
- package/dist/harness-runtime/lib/operation-errors.mjs +127 -0
- package/dist/harness-runtime/lib/operation-progress.d.mts +7 -0
- package/dist/harness-runtime/lib/operation-progress.mjs +114 -0
- package/dist/harness-runtime/lib/process-json.d.mts +5 -0
- package/dist/harness-runtime/lib/process-json.mjs +201 -0
- package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
- package/dist/harness-runtime/lib/provider-overload-policy.d.mts +13 -0
- package/dist/harness-runtime/lib/provider-overload-policy.mjs +138 -0
- package/dist/harness-runtime/lib/runner-events.mjs +200 -0
- package/dist/harness-runtime/lib/runner-lock.mjs +42 -0
- package/dist/harness-runtime/lib/session-settlement.mjs +30 -0
- package/dist/harness-runtime/lib/setup-progress.d.mts +9 -0
- package/dist/harness-runtime/lib/setup-progress.mjs +51 -0
- package/dist/harness-runtime/lib/tool-observations.d.mts +21 -0
- package/dist/harness-runtime/lib/tool-observations.mjs +228 -0
- package/dist/harness-runtime/lib/zcode-usage.mjs +55 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-1y4a697q.js +81 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-2ny4vxct.js +92 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-6mptkzbc.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-7x67cf1q.js +52 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9rqn6x4v.js +16 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9vdz0zwn.js +1661 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ecs5ftka.js +1466 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-frbwqjmf.js +145 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gne7h7mq.js +50 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gx9wnkzr.js +105 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-m2eywc8w.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-nwcpakzx.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-q4g3afnw.js +623 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-qav6mfdz.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-sxt88q69.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-t1xa31vm.js +39 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ve4aws36.js +103 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vmytr4q8.js +4 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vt9jbch8.js +36 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vyas3vgt.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-wjyeq25y.js +30 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-xgnj2w4v.js +20 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-y2etg5bs.js +9 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ydhz1zrh.js +238 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-z6qdwcsf.js +62 -0
- package/dist/harness-runtime/vendor/opencode-client/contract.d.ts +1 -0
- package/dist/harness-runtime/vendor/opencode-client/contract.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api/api.d.ts +2806 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api/api.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api.d.ts +4 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/client.d.ts +2530 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/client.js +15 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.d.ts +7 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.js +6 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.d.ts +2529 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.js +8 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.d.ts +2 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/index.d.ts +35 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/index.js +75 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/rpc.d.ts +20 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/rpc.js +11 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/service.d.ts +90 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/service.js +187 -0
- package/dist/harness-runtime/vendor/opencode-client/package.json +84 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/api.d.ts +21 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/api.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/client.d.ts +252 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/client.js +14 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.d.ts +8 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.js +6 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.d.ts +259 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.js +7 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.d.ts +3 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.js +79 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.d.ts +8932 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.js +70 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/index.d.ts +6 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/index.js +83 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/rpc.d.ts +32 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/rpc.js +7 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/service.d.ts +24 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/service.js +187 -0
- package/dist/harness-runtime/vendor/opencode-client/pty-handoff.d.ts +12 -0
- package/dist/harness-runtime/vendor/opencode-client/pty-handoff.js +14 -0
- package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.d.ts +20 -0
- package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.js +14 -0
- package/dist/harness-runtime/vendor/opencode-client/service-contender.d.ts +37 -0
- package/dist/harness-runtime/vendor/opencode-client/service-contender.js +10 -0
- package/dist/harness-runtime/vendor/opencode-client/service-probe.d.ts +70 -0
- package/dist/harness-runtime/vendor/opencode-client/service-probe.js +16 -0
- package/dist/harness-runtime/vendor/opencode-client/service-timing.d.ts +12 -0
- package/dist/harness-runtime/vendor/opencode-client/service-timing.js +10 -0
- package/dist/harness-runtime/vendor/opencode-client/service-version.d.ts +2 -0
- package/dist/harness-runtime/vendor/opencode-client/service-version.js +6 -0
- package/dist/harness-runtime/vendor/opencode-client/service.d.ts +52 -0
- package/dist/harness-runtime/vendor/opencode-client/service.js +1 -0
- package/dist/harness-runtime/vendor/opencode-client/shared-events.d.ts +11 -0
- package/dist/harness-runtime/vendor/opencode-client/shared-events.js +8 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/connection.d.ts +37 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/connection.js +10 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/data.d.ts +211 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/data.js +18 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/index.d.ts +3 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/index.js +32 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/pty.d.ts +22 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/pty.js +8 -0
- package/dist/harness-runtime/vendor/opencode-plugin.mjs +3 -0
- package/dist/protocol/local-files.js +18 -9
- package/dist/runtime/context.js +8 -4
- package/dist/schemas/agent-profile.schema.json +1 -1
- package/dist/schemas/code-review-result.schema.json +1 -1
- package/dist/schemas/code-verification.schema.json +7 -5
- package/dist/schemas/code-work-batch.schema.json +2 -2
- package/dist/schemas/discovery-result.schema.json +184 -0
- package/dist/schemas/execution-bundle.schema.json +205 -0
- package/dist/schemas/execution-settings.schema.json +162 -0
- package/dist/schemas/flow-descriptor.schema.json +54 -0
- package/dist/schemas/flow-run.schema.json +28 -0
- package/dist/schemas/harness-catalog.schema.json +80 -0
- package/dist/schemas/mb-init-compose-result.schema.json +457 -0
- package/dist/schemas/mb-init-discover-result.schema.json +500 -0
- package/dist/schemas/mb-init-finish-result.schema.json +422 -0
- package/dist/schemas/mb-init-preflight-result.schema.json +367 -0
- package/dist/schemas/mb-init-review-result.schema.json +396 -0
- package/dist/schemas/mb-init-verify-result.schema.json +391 -0
- package/dist/schemas/memory-bootstrap-receipt-v2.schema.json +67 -0
- package/dist/schemas/memory-bootstrap-receipt.schema.json +354 -0
- package/dist/schemas/memory-composition-work-result.schema.json +38 -0
- package/dist/schemas/memory-repair-work-result.schema.json +49 -0
- package/dist/schemas/memory-review-correction-plan.schema.json +30 -0
- package/dist/schemas/memory-review-work-result.schema.json +43 -0
- package/dist/schemas/model-catalog.schema.json +153 -0
- package/dist/schemas/plan-review-decision.schema.json +3 -3
- package/dist/schemas/plan-review-result.schema.json +2 -2
- package/dist/schemas/profile-settings.schema.json +122 -0
- package/dist/schemas/release-manifest.schema.json +291 -0
- package/dist/schemas/run-control-receipt.schema.json +118 -0
- package/dist/schemas/run-control-request.schema.json +36 -0
- package/dist/schemas/run-engine-binding.schema.json +1 -1
- package/dist/schemas/vnext-protocol-plan.schema.json +2 -2
- package/dist/services/branch-context.js +3 -3
- package/dist/services/canon.js +11 -8
- package/dist/services/check-runner-evidence.js +250 -0
- package/dist/services/cleanup.js +133 -40
- package/dist/services/cli-operation-classifier.js +25 -62
- package/dist/services/cli-upgrade-allowlist.js +27 -0
- package/dist/services/code-checks.js +1168 -107
- package/dist/services/codex-hook-delivery.js +32 -0
- package/dist/services/command-context.js +80 -0
- package/dist/services/compatibility-preflight.js +13 -3
- package/dist/services/config.js +11 -8
- package/dist/services/continuation-outcome.js +20 -0
- package/dist/services/controller-child-capacity.js +13 -0
- package/dist/services/controller-child-observer.js +115 -0
- package/dist/services/controller-execution.js +88 -0
- package/dist/services/controller-fanout.js +202 -0
- package/dist/services/dashboard.js +143 -58
- package/dist/services/descriptor-coordinator-session.js +35 -0
- package/dist/services/descriptor-root-work.js +53 -0
- package/dist/services/descriptor-run-settlement.js +105 -0
- package/dist/services/descriptor-run-start.js +449 -0
- package/dist/services/descriptor-stage-coordinator.js +197 -0
- package/dist/services/descriptor-stage-settlement.js +222 -0
- package/dist/services/descriptor-stage-start.js +139 -0
- package/dist/services/discovery/agy-discovery.js +2 -0
- package/dist/services/discovery/codex-discovery.js +2 -0
- package/dist/services/discovery/droid-discovery.js +2 -0
- package/dist/services/discovery/grok-discovery.js +2 -0
- package/dist/services/discovery/opencode-discovery.js +2 -0
- package/dist/services/discovery/static.js +31 -0
- package/dist/services/discovery/zcode-discovery.js +2 -0
- package/dist/services/engines.js +294 -153
- package/dist/services/eval-snapshots.js +1215 -116
- package/dist/services/execution-bundles.js +263 -0
- package/dist/services/execution-policy.js +185 -0
- package/dist/services/executor-onboarding.js +56 -0
- package/dist/services/external-work-launch.js +308 -0
- package/dist/services/flow-catalog.js +21 -0
- package/dist/services/flow-descriptors.js +82 -0
- package/dist/services/frozen-engine.js +250 -0
- package/dist/services/generic-execution.js +164 -0
- package/dist/services/harness-adapter.js +355 -36
- package/dist/services/harness-config.js +2 -0
- package/dist/services/harness-discovery.js +29 -0
- package/dist/services/harness-doctor.js +75 -0
- package/dist/services/harness-probe-effects.js +51 -0
- package/dist/services/harness-version-probe.js +100 -0
- package/dist/services/hooks.js +771 -325
- package/dist/services/hybrid-contracts.js +483 -0
- package/dist/services/ids.js +29 -1
- package/dist/services/installation-lock.js +95 -0
- package/dist/services/installed-flow-source.js +183 -0
- package/dist/services/lanes.js +78 -65
- package/dist/services/lifecycle-command.js +1 -288
- package/dist/services/lifecycle-contract.js +1 -0
- package/dist/services/lifecycle-invocations.js +2007 -0
- package/dist/services/managed-daemon-binding.js +38 -0
- package/dist/services/managed-lease-monitor.js +133 -0
- package/dist/services/managed-processes.js +475 -55
- package/dist/services/memory-accepted-composition.js +77 -0
- package/dist/services/memory-baseline-preservation.js +65 -0
- package/dist/services/memory-completed-correction-wave.js +320 -0
- package/dist/services/memory-compose-handler.js +258 -0
- package/dist/services/memory-composition-acceptance.js +199 -0
- package/dist/services/memory-composition-graph.js +134 -0
- package/dist/services/memory-composition-registration.js +146 -0
- package/dist/services/memory-composition-work-custody.js +151 -0
- package/dist/services/memory-composition-work-results.js +96 -0
- package/dist/services/memory-corrected-review-acceptance.js +129 -0
- package/dist/services/memory-corrected-review-batch.js +30 -0
- package/dist/services/memory-corrected-review-findings.js +106 -0
- package/dist/services/memory-corrected-review-inputs.js +210 -0
- package/dist/services/memory-corrected-review-projection.js +78 -0
- package/dist/services/memory-corrected-review-receipt.js +170 -0
- package/dist/services/memory-corrected-review-registration.js +191 -0
- package/dist/services/memory-correction-check-receipt.js +219 -0
- package/dist/services/memory-correction-input-owner.js +6 -0
- package/dist/services/memory-discover-handler.js +104 -0
- package/dist/services/memory-finish-evidence.js +435 -0
- package/dist/services/memory-finish-handler.js +215 -0
- package/dist/services/memory-finish-integration.js +280 -0
- package/dist/services/memory-finish-readiness.js +122 -0
- package/dist/services/memory-git-integration.js +151 -0
- package/dist/services/memory-historical-corrected-review.js +206 -0
- package/dist/services/memory-historical-original-review.js +130 -0
- package/dist/services/memory-historical-repair-registration.js +99 -0
- package/dist/services/memory-historical-second-review.js +128 -0
- package/dist/services/memory-init-bootstrap-readiness.js +210 -0
- package/dist/services/memory-init-contexts.js +89 -0
- package/dist/services/memory-init-controller-start.js +84 -0
- package/dist/services/memory-init-engine-release.js +223 -0
- package/dist/services/memory-init-git-policy.js +136 -0
- package/dist/services/memory-init-lint-capability.js +185 -0
- package/dist/services/memory-init-native-capacity.js +296 -0
- package/dist/services/memory-init-preflight.js +330 -0
- package/dist/services/memory-init-project-bootstrap.js +599 -0
- package/dist/services/memory-init-request-candidate.js +157 -0
- package/dist/services/memory-init-request-input.js +66 -0
- package/dist/services/memory-init-resource-storage.js +125 -0
- package/dist/services/memory-init-run-allocation.js +81 -0
- package/dist/services/memory-init-run-bundle.js +98 -0
- package/dist/services/memory-init-start.js +181 -0
- package/dist/services/memory-init-startup-custody.js +80 -0
- package/dist/services/memory-init-startup-input.js +240 -0
- package/dist/services/memory-init-storage.js +81 -0
- package/dist/services/memory-init-stores.js +359 -0
- package/dist/services/memory-init-worktrunk-capability.js +178 -0
- package/dist/services/memory-layer-publication.js +611 -0
- package/dist/services/memory-layer.js +156 -0
- package/dist/services/memory-lint-config.js +85 -0
- package/dist/services/memory-native-review-copy.js +165 -0
- package/dist/services/memory-normal-flow-entry.js +75 -0
- package/dist/services/memory-ordinary-defaults.js +112 -0
- package/dist/services/memory-preflight-bootstrap-custody.js +115 -0
- package/dist/services/memory-preflight-handler.js +259 -0
- package/dist/services/memory-repair-current-view.js +299 -0
- package/dist/services/memory-repair-document-delta.js +451 -0
- package/dist/services/memory-repair-history.js +491 -0
- package/dist/services/memory-repair-physical-binding.js +312 -0
- package/dist/services/memory-repair-publication-files.js +152 -0
- package/dist/services/memory-repair-publication-intent.js +27 -0
- package/dist/services/memory-repair-publication-plan.js +160 -0
- package/dist/services/memory-repair-publication-source.js +225 -0
- package/dist/services/memory-repair-publication.js +794 -0
- package/dist/services/memory-repair-registration.js +239 -0
- package/dist/services/memory-repair-work-baseline.js +413 -0
- package/dist/services/memory-repair-work-completion.js +97 -0
- package/dist/services/memory-repair-work-custody.js +73 -0
- package/dist/services/memory-repair-work-results.js +89 -0
- package/dist/services/memory-repair-workspace.js +252 -0
- package/dist/services/memory-repair-writer-copy.js +735 -0
- package/dist/services/memory-result-binding.js +173 -0
- package/dist/services/memory-retained-review-authority.js +129 -0
- package/dist/services/memory-retained-review-results.js +70 -0
- package/dist/services/memory-retained-second-baseline.js +102 -0
- package/dist/services/memory-retained-second-repairs.js +156 -0
- package/dist/services/memory-review-acceptance.js +214 -0
- package/dist/services/memory-review-correction-plan.js +171 -0
- package/dist/services/memory-review-handler.js +585 -0
- package/dist/services/memory-review-inputs.js +200 -0
- package/dist/services/memory-review-receipt.js +180 -0
- package/dist/services/memory-review-registration.js +108 -0
- package/dist/services/memory-review-work-custody.js +124 -0
- package/dist/services/memory-review-work-input.js +72 -0
- package/dist/services/memory-review-work-results.js +73 -0
- package/dist/services/memory-second-corrected-review-inputs.js +165 -0
- package/dist/services/memory-second-correction-plan.js +76 -0
- package/dist/services/memory-second-repair-baseline.js +91 -0
- package/dist/services/memory-second-repair-launch.js +614 -0
- package/dist/services/memory-second-repair-registration.js +88 -0
- package/dist/services/memory-second-repair-source.js +187 -0
- package/dist/services/memory-second-review-acceptance.js +115 -0
- package/dist/services/memory-source-observations.js +117 -0
- package/dist/services/memory-stage-dispatch.js +28 -0
- package/dist/services/memory-stage-results.js +199 -0
- package/dist/services/memory-verification-acceptance.js +135 -0
- package/dist/services/memory-verification-checks.js +252 -0
- package/dist/services/memory-verification-receipt.js +159 -0
- package/dist/services/memory-verify-handler.js +135 -0
- package/dist/services/memory-work-result-preparation.js +63 -0
- package/dist/services/merge-queue.js +228 -149
- package/dist/services/merge-server.js +161 -35
- package/dist/services/migrations.js +37 -16
- package/dist/services/model-catalog.js +109 -0
- package/dist/services/model-discovery.js +46 -0
- package/dist/services/native-daemon-history.js +49 -0
- package/dist/services/native-session-control.js +39 -0
- package/dist/services/operator-profiles.js +236 -0
- package/dist/services/plan-runtime.js +26 -11
- package/dist/services/plans.js +28 -25
- package/dist/services/projects.js +37 -17
- package/dist/services/prompts.js +28 -17
- package/dist/services/protocols.js +163 -87
- package/dist/services/provider-capacity.js +385 -0
- package/dist/services/recovery-observation-budget.js +121 -0
- package/dist/services/recovery-snapshot-database.js +560 -0
- package/dist/services/release-archive-producer.js +52 -0
- package/dist/services/release-archive.js +143 -0
- package/dist/services/release-cache.js +142 -0
- package/dist/services/release-compatibility.js +18 -0
- package/dist/services/release-download.js +67 -0
- package/dist/services/release-envelope.js +104 -0
- package/dist/services/release-manifest.js +45 -0
- package/dist/services/release-payload.js +114 -0
- package/dist/services/release-producer.js +63 -0
- package/dist/services/release-provenance.js +160 -0
- package/dist/services/release-registry.js +66 -0
- package/dist/services/release-source.js +194 -0
- package/dist/services/repair-intents.js +42 -0
- package/dist/services/review-copy.js +165 -0
- package/dist/services/review-deferral.js +23 -0
- package/dist/services/run-control-receipt.js +56 -0
- package/dist/services/run-control-worker.js +411 -0
- package/dist/services/run-control.js +1056 -0
- package/dist/services/run-controller-adapter.js +399 -0
- package/dist/services/run-controller-capture.js +186 -0
- package/dist/services/run-controller-process.js +208 -0
- package/dist/services/run-controller-recovery.js +289 -0
- package/dist/services/run-controller-state.js +97 -0
- package/dist/services/run-controller.js +1205 -0
- package/dist/services/run-engine-bindings.js +1 -1
- package/dist/services/run-fork.js +147 -0
- package/dist/services/run-health.js +149 -0
- package/dist/services/run-observations.js +152 -0
- package/dist/services/run-projection.js +22 -64
- package/dist/services/run-recovery-guard.js +77 -0
- package/dist/services/run-recovery-runtime.js +112 -0
- package/dist/services/run-recovery.js +489 -0
- package/dist/services/run-start-custody.js +177 -0
- package/dist/services/runs.js +1019 -247
- package/dist/services/runtime-budget.js +430 -0
- package/dist/services/runtime-command.js +43 -0
- package/dist/services/runtime-scope-capture.js +52 -0
- package/dist/services/runtime-scope-control.js +468 -0
- package/dist/services/runtime-scope-resume.js +599 -0
- package/dist/services/runtime-scope-stop.js +210 -0
- package/dist/services/runtime-scope-worker.js +287 -0
- package/dist/services/runtime-service.js +96 -0
- package/dist/services/schema-validation.js +10 -8
- package/dist/services/scope-writer-cleanup.js +27 -0
- package/dist/services/sessions.js +35 -69
- package/dist/services/setup.js +377 -0
- package/dist/services/stage-blocker.js +17 -7
- package/dist/services/stage-context.js +193 -19
- package/dist/services/stage-handlers.js +74 -0
- package/dist/services/stage-lifecycle.js +152 -116
- package/dist/services/stage-pause.js +89 -75
- package/dist/services/stage-report-renderer.js +234 -6
- package/dist/services/stage-work-graph.js +52 -0
- package/dist/services/tooling.js +41 -30
- package/dist/services/usage.js +49 -101
- package/dist/services/verification-matrix.js +375 -0
- package/dist/services/vnext-code-review.js +223 -88
- package/dist/services/vnext-code.js +327 -226
- package/dist/services/vnext-contracts.js +10 -1
- package/dist/services/vnext-execution-profile.js +80 -5
- package/dist/services/vnext-fanout.js +311 -19
- package/dist/services/vnext-merge.js +332 -111
- package/dist/services/vnext-plan-review.js +279 -127
- package/dist/services/vnext-plan.js +228 -104
- package/dist/services/vnext-protocolize.js +531 -95
- package/dist/services/vnext-specify.js +119 -81
- package/dist/services/vnext-workspace-policy.js +7 -2
- package/dist/services/work-registry.js +1627 -199
- package/dist/services/workspace-bootstrap.js +52 -15
- package/dist/services/workspace-files.js +85 -0
- package/dist/services/worktrees.js +105 -48
- package/dist/shared/entity-references.js +8 -0
- package/dist/shared/errors.js +27 -0
- package/dist/storage/agent-output.js +26 -0
- package/dist/storage/database.js +891 -39
- package/dist/storage/foreign-key-transaction.js +39 -0
- package/dist/storage/immutable-input.js +45 -0
- package/dist/storage/import-guard.js +102 -0
- package/dist/storage/native-cleanup.js +52 -0
- package/dist/storage/paths.js +17 -4
- package/dist/storage/projection-lock.js +73 -0
- package/dist/storage/work-references.js +23 -0
- package/dist/storage/writer-contract.js +89 -0
- package/dist/storage/writer-migration.js +85 -0
- package/package.json +43 -13
- package/tools/create-canon-release.mjs +33 -0
- package/tools/produce-hybrid-release.mjs +41 -0
- package/tools/repair-paused-run-status.mjs +84 -0
|
@@ -94,8 +94,11 @@ export function validateSchema(options) {
|
|
|
94
94
|
throw new AppError("usage", "--schema must be a schema name such as mb-upgrade-review-data", 2);
|
|
95
95
|
}
|
|
96
96
|
const filePath = path.resolve(options.file);
|
|
97
|
-
const data = readJson(filePath, "input file");
|
|
98
|
-
|
|
97
|
+
const data = Object.hasOwn(options, "data") ? options.data : readJson(filePath, "input file");
|
|
98
|
+
// `data` may have come from stdin or from the prepare phase. Do not reopen
|
|
99
|
+
// `options.file` merely to infer the RUN-bound schema: apart from being a
|
|
100
|
+
// second read of caller input, `<stdin>` is not a filesystem path at all.
|
|
101
|
+
const schemaResolution = resolveSchema(options, data);
|
|
99
102
|
const schema = readJson(schemaResolution.path, "schema");
|
|
100
103
|
// Ajv's default export only carries the draft-07 meta-schema. New stage
|
|
101
104
|
// contracts may deliberately use draft 2020-12, so choose the validator
|
|
@@ -133,8 +136,8 @@ export function validateSchema(options) {
|
|
|
133
136
|
errors: []
|
|
134
137
|
};
|
|
135
138
|
}
|
|
136
|
-
function resolveSchema(options) {
|
|
137
|
-
const bound = resolveRunBoundSchema(options);
|
|
139
|
+
function resolveSchema(options, data) {
|
|
140
|
+
const bound = resolveRunBoundSchema(options, data);
|
|
138
141
|
if (bound)
|
|
139
142
|
return bound;
|
|
140
143
|
const fileNames = [`${options.schemaName}.schema.json`];
|
|
@@ -180,10 +183,9 @@ const historicalSchemaRegistry = {
|
|
|
180
183
|
checksum: "d9889af7f2c0afb47e2abab330aea3e1750879d0738d87d38d2d20e3fe0403f3"
|
|
181
184
|
}]
|
|
182
185
|
};
|
|
183
|
-
function resolveRunBoundSchema(options) {
|
|
186
|
+
function resolveRunBoundSchema(options, data) {
|
|
184
187
|
if (!options.runRoot || options.schemaDir)
|
|
185
188
|
return null;
|
|
186
|
-
const data = readJson(path.resolve(options.file), "input file");
|
|
187
189
|
const root = asRecord(data);
|
|
188
190
|
const run = root ? asRecord(objectValue(root, "run")) : undefined;
|
|
189
191
|
const inferredRunId = run ? objectValue(run, "run_id") : objectValue(root ?? {}, "run_id");
|
|
@@ -268,13 +270,13 @@ function readJson(file, label) {
|
|
|
268
270
|
text = fs.readFileSync(file, "utf8");
|
|
269
271
|
}
|
|
270
272
|
catch (error) {
|
|
271
|
-
throw new AppError("validation", `Cannot read ${label}: ${file}`, 2, { cause: String(error) });
|
|
273
|
+
throw new AppError(label === "input file" ? "validation" : "schema_registry_invalid", `Cannot read ${label}: ${file}`, label === "input file" ? 2 : 1, { cause: String(error) });
|
|
272
274
|
}
|
|
273
275
|
try {
|
|
274
276
|
return JSON.parse(text);
|
|
275
277
|
}
|
|
276
278
|
catch (error) {
|
|
277
|
-
throw new AppError("invalid_json", `Invalid JSON in ${label}: ${file}`, 2, { cause: String(error) });
|
|
279
|
+
throw new AppError(label === "input file" ? "invalid_json" : "schema_registry_invalid", `Invalid JSON in ${label}: ${file}`, label === "input file" ? 2 : 1, { cause: String(error) });
|
|
278
280
|
}
|
|
279
281
|
}
|
|
280
282
|
function formatAjvError(error) {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { errorRecord } from "../shared/errors.js";
|
|
2
|
+
/** Cleanup is secondary to the arbiter's known commit outcome. Always visit
|
|
3
|
+
* every connection, including after a failed rollback or close. */
|
|
4
|
+
export function cleanupScopeWriters(targets) {
|
|
5
|
+
const errors = [];
|
|
6
|
+
for (const target of targets) {
|
|
7
|
+
let failed = false;
|
|
8
|
+
if (target.rollback) {
|
|
9
|
+
try {
|
|
10
|
+
target.db.exec("ROLLBACK");
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
failed = true;
|
|
14
|
+
errors.push({ target: target.name, phase: "rollback", message: String(error), error: errorRecord(error) });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (target.close === "always" || failed) {
|
|
18
|
+
try {
|
|
19
|
+
target.db.close?.();
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
errors.push({ target: target.name, phase: "close", message: String(error), error: errorRecord(error) });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return errors;
|
|
27
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
|
-
import fs from "node:fs";
|
|
3
2
|
import { continuationPolicies, flowKinds } from "../domain/contracts.js";
|
|
4
3
|
import { AppError } from "../shared/errors.js";
|
|
5
|
-
import {
|
|
4
|
+
import { hookSessionIdentity } from "./hooks.js";
|
|
6
5
|
import { parseJsonObject } from "../shared/json.js";
|
|
6
|
+
import { prepareTextFile } from "../cli/input-preparation.js";
|
|
7
7
|
import { resolveProjectRoot } from "../storage/paths.js";
|
|
8
8
|
import { appendAudit } from "./audit.js";
|
|
9
9
|
import { cancelLaneWaitersForWorker } from "./lanes.js";
|
|
@@ -30,8 +30,8 @@ export function recordFlowSessionObservation(context, input) {
|
|
|
30
30
|
const open = context.db.get(`SELECT id, run_id, protocol_id FROM flow_session_segments
|
|
31
31
|
WHERE project_id = ? AND session_id = ? AND ended_at IS NULL ORDER BY started_at DESC, id DESC LIMIT 1`, [input.projectId, input.sessionId]);
|
|
32
32
|
const now = context.now();
|
|
33
|
-
const runId = input.runId ?? null;
|
|
34
|
-
const protocolId = input.protocolId ?? null;
|
|
33
|
+
const runId = input.runId === undefined ? open?.run_id ?? null : input.runId;
|
|
34
|
+
const protocolId = input.protocolId === undefined ? open?.protocol_id ?? null : input.protocolId;
|
|
35
35
|
if (open && open.run_id === runId && open.protocol_id === protocolId) {
|
|
36
36
|
return { changed: false, segment_id: open.id };
|
|
37
37
|
}
|
|
@@ -42,27 +42,6 @@ export function recordFlowSessionObservation(context, input) {
|
|
|
42
42
|
VALUES (?, ?, ?, ?, ?, NULL, ?, ?, ?)`, [input.projectId, input.sessionId, runId, protocolId, now, input.cwd ?? null, input.toolName ?? null, input.eventKey]);
|
|
43
43
|
return { changed: true, segment_id: Number(result.lastInsertRowid) };
|
|
44
44
|
}
|
|
45
|
-
export function bindObservedFlowSession(context, project, payload, sessionId) {
|
|
46
|
-
if (resolveProjectRoot(payload.project_root) !== project.root) {
|
|
47
|
-
throw new AppError("project_mismatch", "Observed session payload project_root does not match hook cwd project", 1, {
|
|
48
|
-
expected: project.root,
|
|
49
|
-
actual: payload.project_root
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
const session = upsertFlowSession(context, project, payload, sessionId);
|
|
53
|
-
if (session.session_kind !== "orchestrator") {
|
|
54
|
-
const now = context.now();
|
|
55
|
-
for (const unit of payload.coverage_units ?? []) {
|
|
56
|
-
if (!unit.job_id)
|
|
57
|
-
continue;
|
|
58
|
-
context.db.run(`UPDATE flow_jobs SET status = 'running', worker_session_id = ?, attempts = attempts + 1, started_at = COALESCE(started_at, ?), updated_at = ?
|
|
59
|
-
WHERE project_id = ? AND run_id = ? AND job_id = ? AND status IN ('pending', 'failed')`, [session.session_id, now, now, project.id, session.run_id ?? "", unit.job_id]);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
if (session.run_id)
|
|
63
|
-
refreshRunSessionProjection(context, project.id, session.run_id);
|
|
64
|
-
return session;
|
|
65
|
-
}
|
|
66
45
|
export function registerFlowJob(context, input) {
|
|
67
46
|
const jobId = input.jobId ?? `JOB-${input.runId}-${input.planItemId}`.replace(/[^A-Za-z0-9_-]+/g, "-");
|
|
68
47
|
const now = context.now();
|
|
@@ -76,9 +55,33 @@ export function flowJobsForRun(context, projectId, runId) {
|
|
|
76
55
|
return context.db.all("SELECT * FROM flow_jobs WHERE project_id = ? AND run_id = ? ORDER BY plan_item_id", [projectId, runId]);
|
|
77
56
|
}
|
|
78
57
|
export function registerFlowSession(context, input) {
|
|
79
|
-
const payload =
|
|
58
|
+
const payload = input.preparedPayload ?? prepareFlowSessionPayload(input);
|
|
80
59
|
const project = requireProjectByRoot(context, resolveProjectRoot(payload.project_root));
|
|
81
|
-
|
|
60
|
+
if (payload.run_id && !context.db.get("SELECT id FROM runs WHERE project_id = ? AND id = ?", [project.id, payload.run_id])) {
|
|
61
|
+
throw new AppError("not_found", `Run is not registered: ${payload.run_id}`, 1, { run_id: payload.run_id, effect: "no_effect" });
|
|
62
|
+
}
|
|
63
|
+
const identity = input.hookEventId ? hookSessionIdentity(context, project.id, input.hookEventId) : undefined;
|
|
64
|
+
if (!identity && input.sessionId && payload.provider_session_id && payload.provider_session_id !== input.sessionId) {
|
|
65
|
+
throw new AppError("validation", "--session-id conflicts with payload provider_session_id", 2);
|
|
66
|
+
}
|
|
67
|
+
const session = upsertFlowSession(context, project, identity ? {
|
|
68
|
+
...payload, harness: identity.harness, provider_session_id: identity.providerSessionId,
|
|
69
|
+
parent_session_id: identity.parentSessionId, agent_id: identity.agentId,
|
|
70
|
+
provider: identity.provider, model: identity.model, reasoning: identity.reasoning,
|
|
71
|
+
mode: identity.mode, agent_type: identity.agentType
|
|
72
|
+
} : { ...payload, ...(input.sessionId ? { provider_session_id: input.sessionId } : {}) }, identity?.sessionId ?? input.sessionId);
|
|
73
|
+
recordFlowSessionObservation(context, { projectId: project.id, sessionId: session.session_id,
|
|
74
|
+
runId: session.run_id, protocolId: session.protocol_id, cwd: session.cwd,
|
|
75
|
+
eventKey: `session-register-${crypto.randomUUID()}` });
|
|
76
|
+
// Logical Work/job binding belongs to the successful CLI registration, never
|
|
77
|
+
// to observation of model-supplied payload by a pre-tool hook.
|
|
78
|
+
if (session.session_kind !== "orchestrator")
|
|
79
|
+
for (const unit of payload.coverage_units ?? []) {
|
|
80
|
+
if (!unit.job_id)
|
|
81
|
+
continue;
|
|
82
|
+
context.db.run(`UPDATE flow_jobs SET status = 'running', worker_session_id = ?, attempts = attempts + 1, started_at = COALESCE(started_at, ?), updated_at = ?
|
|
83
|
+
WHERE project_id = ? AND run_id = ? AND job_id = ? AND status IN ('pending', 'failed')`, [session.session_id, context.now(), context.now(), project.id, session.run_id ?? "", unit.job_id]);
|
|
84
|
+
}
|
|
82
85
|
if (session.run_id)
|
|
83
86
|
appendFlowRunTimelineEvent(context, project.id, session.run_id, { type: "session_bound", session_id: session.session_id });
|
|
84
87
|
if (session.run_id)
|
|
@@ -195,52 +198,15 @@ export function updateFlowSessionContinuation(context, projectId, sessionId, act
|
|
|
195
198
|
refreshRunSessionProjection(context, projectId, session.run_id);
|
|
196
199
|
return nextCount;
|
|
197
200
|
}
|
|
198
|
-
export function
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return undefined;
|
|
202
|
-
if (invocation.operation === "stage_start")
|
|
203
|
-
return flowSessionPayloadFromStageStartCommand(invocation);
|
|
204
|
-
if (invocation.operation !== "session_register")
|
|
205
|
-
return undefined;
|
|
206
|
-
const payloadBase64 = commandOption(invocation, "payload-base64");
|
|
207
|
-
const payloadJson = commandOption(invocation, "payload-json");
|
|
208
|
-
const payloadFile = commandOption(invocation, "payload-file");
|
|
209
|
-
if (!payloadBase64 && !payloadJson && !payloadFile) {
|
|
210
|
-
return undefined;
|
|
211
|
-
}
|
|
212
|
-
return decodeFlowSessionPayload({ payloadBase64, payloadJson, payloadFile });
|
|
213
|
-
}
|
|
214
|
-
function flowSessionPayloadFromStageStartCommand(invocation) {
|
|
215
|
-
const projectRoot = commandOption(invocation, "project-root");
|
|
216
|
-
const stage = commandOption(invocation, "stage");
|
|
217
|
-
const run = commandPosition(invocation, 0);
|
|
218
|
-
if (!projectRoot || !stage || !run || run.startsWith("--"))
|
|
219
|
-
return undefined;
|
|
220
|
-
return {
|
|
221
|
-
project_root: projectRoot,
|
|
222
|
-
flow_kind: ["code", "implementation", "readiness", "merge"].includes(stage) ? "implementation" : "planning",
|
|
223
|
-
run_id: run,
|
|
224
|
-
protocol_id: null,
|
|
225
|
-
worker_id: null,
|
|
226
|
-
workspace_path: null,
|
|
227
|
-
continuation_policy: "go_router",
|
|
228
|
-
session_kind: "orchestrator",
|
|
229
|
-
current_stage: stage,
|
|
230
|
-
coverage_units: []
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
function invocationFromCommand(command) {
|
|
234
|
-
const analysis = parseLifecycleCommand(command);
|
|
235
|
-
return analysis.kind === "none" ? undefined : analysis.invocation;
|
|
236
|
-
}
|
|
237
|
-
function decodeFlowSessionPayload(input) {
|
|
201
|
+
export function prepareFlowSessionPayload(input) {
|
|
202
|
+
if ([input.payloadFile, input.payloadBase64, input.payloadJson].filter(value => value !== undefined).length !== 1)
|
|
203
|
+
throw new AppError("usage", "session register requires exactly one of --payload-file, --payload-base64, or --payload-json", 2);
|
|
238
204
|
if (input.payloadFile) {
|
|
239
|
-
return normalizeFlowSessionPayload(parseJsonObject(
|
|
205
|
+
return normalizeFlowSessionPayload(parseJsonObject(prepareTextFile(input.payloadFile, "session payload").text, "session payload"));
|
|
240
206
|
}
|
|
241
207
|
if (input.payloadBase64) {
|
|
242
208
|
if (/\$[A-Za-z_{(]/.test(input.payloadBase64)) {
|
|
243
|
-
throw new AppError("validation", "session register
|
|
209
|
+
throw new AppError("validation", "session register received an unexpanded shell variable as --payload-base64; expand it before invoking the CLI or use --payload-file", 2);
|
|
244
210
|
}
|
|
245
211
|
return normalizeFlowSessionPayload(parseJsonObject(Buffer.from(input.payloadBase64, "base64").toString("utf8"), "session payload"));
|
|
246
212
|
}
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
4
|
+
import { AppError } from "../shared/errors.js";
|
|
5
|
+
import { hybridDigest, validateHybridShape } from "./hybrid-contracts.js";
|
|
6
|
+
import { admitReleaseManifest } from "./release-manifest.js";
|
|
7
|
+
import { validateReleasePayloadIdentity } from "./release-payload.js";
|
|
8
|
+
import { publishReleaseCache, verifyPublishedReleaseCache } from "./release-cache.js";
|
|
9
|
+
import { acquireInstallationLock } from "./installation-lock.js";
|
|
10
|
+
import { authenticateReleaseEnvelope } from "./release-envelope.js";
|
|
11
|
+
import { prepareOperatorProfile, validateProfileCreationIntent, commitProfileCreation, readOperatorProfiles } from "./operator-profiles.js";
|
|
12
|
+
/** Production ingress authenticates the source before constructing any installation authority. */
|
|
13
|
+
export function installAuthenticatedRelease(home, input) {
|
|
14
|
+
const authenticated = authenticateReleaseEnvelope(input.envelope, input.trustedProducers, input.compatibility);
|
|
15
|
+
return installRelease(home, { requestId: input.requestId, action: input.action,
|
|
16
|
+
manifest: authenticated.manifest, manifestBytes: authenticated.manifestBytes, authority: authenticated.authority,
|
|
17
|
+
canonArchive: input.canonArchive, engineArchive: input.engineArchive,
|
|
18
|
+
...(input.executor !== undefined ? { executor: input.executor } : {}),
|
|
19
|
+
...(input.profileCreation !== undefined ? { profileCreation: input.profileCreation } : {}),
|
|
20
|
+
...(input.deferredOnboarding ? { deferredOnboarding: true } : {}),
|
|
21
|
+
...(input.onCheckpoint ? { onCheckpoint: input.onCheckpoint } : {}) });
|
|
22
|
+
}
|
|
23
|
+
function refuse(reason, effect = "no_effect") {
|
|
24
|
+
throw new AppError("setup_conflict", "Setup request cannot proceed", 1, { reason, phase: "setup", effect, help: "setup", next_action: "dd-flow setup status --json" });
|
|
25
|
+
}
|
|
26
|
+
const bytesDigest = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
27
|
+
function manifestFromBytes(bytes) {
|
|
28
|
+
try {
|
|
29
|
+
return JSON.parse(bytes.toString("utf8"));
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return refuse("invalid_manifest_bytes");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function regularJson(file) {
|
|
36
|
+
if (!fs.lstatSync(file).isFile())
|
|
37
|
+
refuse("unsafe_setup_file");
|
|
38
|
+
try {
|
|
39
|
+
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return refuse("invalid_setup_file");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function atomicJson(file, value) {
|
|
46
|
+
const temporary = `${file}.${randomUUID()}.tmp`;
|
|
47
|
+
const fd = fs.openSync(temporary, "wx", 0o600);
|
|
48
|
+
try {
|
|
49
|
+
try {
|
|
50
|
+
fs.writeFileSync(fd, JSON.stringify(value));
|
|
51
|
+
fs.fsyncSync(fd);
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
fs.closeSync(fd);
|
|
55
|
+
}
|
|
56
|
+
fs.renameSync(temporary, file);
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
if (fs.existsSync(temporary))
|
|
60
|
+
fs.unlinkSync(temporary);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function readSetupRequest(file) {
|
|
64
|
+
const value = regularJson(file);
|
|
65
|
+
const required = ["schema_id", "request_id", "identity", "previous_selection", "phase", "installation"];
|
|
66
|
+
if (!value || typeof value !== "object" || Array.isArray(value)
|
|
67
|
+
|| required.some(key => !Object.hasOwn(value, key))
|
|
68
|
+
|| Object.keys(value).some(key => ![...required, "executor", "profileCreation", "deferredOnboarding"].includes(key)))
|
|
69
|
+
refuse("invalid_request_record");
|
|
70
|
+
const request = value;
|
|
71
|
+
if (Object.hasOwn(request, "deferredOnboarding") && (request.deferredOnboarding !== true || Object.hasOwn(request, "executor") || Object.hasOwn(request, "profileCreation")))
|
|
72
|
+
refuse("invalid_deferred_onboarding");
|
|
73
|
+
if (request.schema_id !== "dd-flow/setup-request@1"
|
|
74
|
+
|| typeof request.request_id !== "string" || !/^[A-Za-z0-9][A-Za-z0-9_.-]{0,95}$/.test(request.request_id)
|
|
75
|
+
|| typeof request.identity !== "string" || !/^[a-f0-9]{64}$/.test(request.identity)
|
|
76
|
+
|| request.previous_selection !== null && (typeof request.previous_selection !== "string" || !/^[a-f0-9]{64}$/.test(request.previous_selection))
|
|
77
|
+
|| !["prepared", "canon_cached", "engine_cached", "profiles_saved", "committed"].includes(request.phase)
|
|
78
|
+
|| !request.installation || typeof request.installation !== "object" || Array.isArray(request.installation))
|
|
79
|
+
refuse("invalid_request_record");
|
|
80
|
+
if (Object.hasOwn(request, "executor") && request.executor !== null) {
|
|
81
|
+
try {
|
|
82
|
+
prepareOperatorProfile(request.executor);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
refuse("invalid_request_executor");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (Object.hasOwn(request, "profileCreation")) {
|
|
89
|
+
try {
|
|
90
|
+
validateProfileCreationIntent(request.profileCreation);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
refuse("invalid_request_profile_creation");
|
|
94
|
+
}
|
|
95
|
+
if (!request.executor || hybridDigest(request.executor) !== hybridDigest(request.profileCreation.profile))
|
|
96
|
+
refuse("request_profile_executor_mismatch");
|
|
97
|
+
}
|
|
98
|
+
return request;
|
|
99
|
+
}
|
|
100
|
+
/** Read retained executor consent for an exact retry, without installation or profile effects.
|
|
101
|
+
* Full source/installation request identity is still checked by installRelease.
|
|
102
|
+
*/
|
|
103
|
+
export function readRetainedSetupExecutor(home, requestId) {
|
|
104
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9_.-]{0,95}$/.test(requestId))
|
|
105
|
+
refuse("invalid_request");
|
|
106
|
+
const root = path.resolve(home), directory = path.join(root, "setup-requests");
|
|
107
|
+
for (const candidate of [root, directory])
|
|
108
|
+
if (fs.existsSync(candidate) && !fs.lstatSync(candidate).isDirectory())
|
|
109
|
+
refuse("unsafe_setup_directory");
|
|
110
|
+
const file = path.join(directory, `${requestId}.json`);
|
|
111
|
+
if (!fs.existsSync(file))
|
|
112
|
+
return null;
|
|
113
|
+
const request = readSetupRequest(file);
|
|
114
|
+
if (request.request_id !== requestId)
|
|
115
|
+
refuse("request_identity_conflict");
|
|
116
|
+
if (!Object.hasOwn(request, "executor"))
|
|
117
|
+
return null;
|
|
118
|
+
return { executor: structuredClone(request.executor ?? null),
|
|
119
|
+
...(request.profileCreation ? { profileCreation: structuredClone(request.profileCreation) } : {}) };
|
|
120
|
+
}
|
|
121
|
+
/** A deferred request is immutable installation consent, not an implicit skip. */
|
|
122
|
+
export function hasDeferredSetupOnboarding(home, requestId) {
|
|
123
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9_.-]{0,95}$/.test(requestId))
|
|
124
|
+
refuse("invalid_request");
|
|
125
|
+
const root = path.resolve(home), directory = path.join(root, "setup-requests");
|
|
126
|
+
for (const candidate of [root, directory])
|
|
127
|
+
if (fs.existsSync(candidate) && !fs.lstatSync(candidate).isDirectory())
|
|
128
|
+
refuse("unsafe_setup_directory");
|
|
129
|
+
const file = path.join(directory, `${requestId}.json`);
|
|
130
|
+
if (!fs.existsSync(file))
|
|
131
|
+
return false;
|
|
132
|
+
const request = readSetupRequest(file);
|
|
133
|
+
if (request.request_id !== requestId)
|
|
134
|
+
refuse("request_identity_conflict");
|
|
135
|
+
return request.deferredOnboarding === true;
|
|
136
|
+
}
|
|
137
|
+
function deferredRequest(home, requestId) {
|
|
138
|
+
if (!hasDeferredSetupOnboarding(home, requestId))
|
|
139
|
+
refuse("onboarding_not_deferred");
|
|
140
|
+
const request = readSetupRequest(path.join(path.resolve(home), "setup-requests", `${requestId}.json`));
|
|
141
|
+
if (request.phase !== "committed")
|
|
142
|
+
refuse("installation_not_committed");
|
|
143
|
+
return request;
|
|
144
|
+
}
|
|
145
|
+
/** Retained consent allows recovery without asking again or importing current defaults. */
|
|
146
|
+
export function readSetupOnboarding(home, requestId) {
|
|
147
|
+
const request = deferredRequest(home, requestId);
|
|
148
|
+
const directory = path.join(path.resolve(home), "setup-onboarding");
|
|
149
|
+
if (fs.existsSync(directory) && !fs.lstatSync(directory).isDirectory())
|
|
150
|
+
refuse("unsafe_onboarding_directory");
|
|
151
|
+
const file = path.join(directory, `${requestId}.json`);
|
|
152
|
+
if (!fs.existsSync(file))
|
|
153
|
+
return null;
|
|
154
|
+
const receipt = regularJson(file);
|
|
155
|
+
if (!receipt || typeof receipt !== "object" || Array.isArray(receipt)
|
|
156
|
+
|| Object.keys(receipt).some(key => !["schema_id", "request_id", "installation_identity", "phase", "executor", "profileCreation"].includes(key))
|
|
157
|
+
|| receipt.schema_id !== "dd-flow/setup-onboarding@1" || receipt.request_id !== requestId
|
|
158
|
+
|| receipt.installation_identity !== request.identity || !["selected", "committed"].includes(receipt.phase)
|
|
159
|
+
|| !Object.hasOwn(receipt, "executor"))
|
|
160
|
+
refuse("invalid_onboarding_receipt");
|
|
161
|
+
try {
|
|
162
|
+
if (receipt.executor !== null)
|
|
163
|
+
prepareOperatorProfile(receipt.executor);
|
|
164
|
+
if (receipt.profileCreation !== undefined) {
|
|
165
|
+
validateProfileCreationIntent(receipt.profileCreation);
|
|
166
|
+
if (!receipt.executor || hybridDigest(receipt.executor) !== hybridDigest(receipt.profileCreation.profile))
|
|
167
|
+
refuse("onboarding_profile_mismatch");
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
refuse("invalid_onboarding_receipt");
|
|
172
|
+
}
|
|
173
|
+
return structuredClone(receipt);
|
|
174
|
+
}
|
|
175
|
+
/** Persist human consent before profile effects; replay never restores later profile edits. */
|
|
176
|
+
export function commitSetupOnboarding(home, requestId, selection) {
|
|
177
|
+
const request = deferredRequest(home, requestId), root = path.resolve(home);
|
|
178
|
+
const executor = selection.executor === null ? null : prepareOperatorProfile(selection.executor);
|
|
179
|
+
if (selection.profileCreation) {
|
|
180
|
+
validateProfileCreationIntent(selection.profileCreation);
|
|
181
|
+
if (!executor || hybridDigest(executor) !== hybridDigest(selection.profileCreation.profile))
|
|
182
|
+
refuse("onboarding_profile_mismatch");
|
|
183
|
+
}
|
|
184
|
+
const candidate = { schema_id: "dd-flow/setup-onboarding@1", request_id: requestId,
|
|
185
|
+
installation_identity: request.identity, phase: "selected", executor,
|
|
186
|
+
...(selection.profileCreation ? { profileCreation: selection.profileCreation } : {}) };
|
|
187
|
+
const lease = acquireInstallationLock(path.join(root, "setup.lock"), hybridDigest({ request_id: requestId, identity: request.identity }));
|
|
188
|
+
let failure, result;
|
|
189
|
+
try {
|
|
190
|
+
const retained = readSetupOnboarding(root, requestId);
|
|
191
|
+
if (retained && hybridDigest({ ...retained, phase: "selected" }) !== hybridDigest(candidate))
|
|
192
|
+
refuse("onboarding_selection_conflict");
|
|
193
|
+
if (retained?.phase === "committed")
|
|
194
|
+
result = retained;
|
|
195
|
+
else {
|
|
196
|
+
if (hybridDigest(readSetupStatus(root).installation) !== hybridDigest(request.installation))
|
|
197
|
+
refuse("onboarding_installation_changed");
|
|
198
|
+
const directory = path.join(root, "setup-onboarding");
|
|
199
|
+
fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
200
|
+
const file = path.join(directory, `${requestId}.json`);
|
|
201
|
+
if (!retained)
|
|
202
|
+
atomicJson(file, candidate);
|
|
203
|
+
if (candidate.profileCreation)
|
|
204
|
+
commitProfileCreation(root, candidate.profileCreation);
|
|
205
|
+
result = { ...candidate, phase: "committed" };
|
|
206
|
+
atomicJson(file, result);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
failure = error instanceof AppError ? error : new AppError("setup_onboarding_interrupted", "Installed release is retained; executor onboarding requires reconciliation", 1, { phase: "executor_onboarding", effect: "unknown", request_id: requestId, help: "setup", next_action: "Replay the exact setup request" });
|
|
211
|
+
}
|
|
212
|
+
finally {
|
|
213
|
+
try {
|
|
214
|
+
lease.release();
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
if (failure)
|
|
218
|
+
failure.details.cleanup = "not_confirmed";
|
|
219
|
+
else
|
|
220
|
+
failure = new AppError("setup_cleanup_failed", "Onboarding owner cleanup requires reconciliation", 1, { effect: "unknown", cleanup: "not_confirmed", request_id: requestId, help: "setup" });
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (failure)
|
|
224
|
+
throw failure;
|
|
225
|
+
return result;
|
|
226
|
+
}
|
|
227
|
+
/** Read-only global readiness. No SQLite connection, migration, project registration or probes. */
|
|
228
|
+
export function readSetupStatus(home) {
|
|
229
|
+
const root = path.resolve(home);
|
|
230
|
+
if (fs.existsSync(root) && !fs.lstatSync(root).isDirectory())
|
|
231
|
+
refuse("unsafe_setup_home");
|
|
232
|
+
const file = path.join(root, "installation.json");
|
|
233
|
+
const installation = fs.existsSync(file) ? regularJson(file) : null;
|
|
234
|
+
if (installation) {
|
|
235
|
+
if (!installation.manifest || typeof installation.manifest_bytes_base64 !== "string" || validateHybridShape("release-manifest", installation.manifest).length ||
|
|
236
|
+
installation.canon_root !== path.join(root, "release-cache", "canon", installation.manifest.canon.version, "payload") ||
|
|
237
|
+
installation.engine_root !== path.join(root, "release-cache", "engine", installation.manifest.engine.version, "payload"))
|
|
238
|
+
refuse("invalid_installation_selection");
|
|
239
|
+
const bytes = Buffer.from(installation.manifest_bytes_base64, "base64");
|
|
240
|
+
if (bytes.toString("base64") !== installation.manifest_bytes_base64 || bytesDigest(bytes) !== installation.manifest_sha256 ||
|
|
241
|
+
hybridDigest(manifestFromBytes(bytes)) !== hybridDigest(installation.manifest))
|
|
242
|
+
refuse("invalid_installation_selection");
|
|
243
|
+
}
|
|
244
|
+
let readiness = installation ? "installed" : "not_installed";
|
|
245
|
+
if (installation) {
|
|
246
|
+
try {
|
|
247
|
+
verifyPublishedReleaseCache(path.dirname(installation.canon_root), installation.manifest.canon.archive);
|
|
248
|
+
verifyPublishedReleaseCache(path.dirname(installation.engine_root), installation.manifest.engine.archive);
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
readiness = "degraded";
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
const existingStorage = fs.existsSync(path.join(root, "db.sqlite"));
|
|
255
|
+
return { installation, installation_readiness: readiness,
|
|
256
|
+
storage_readiness: existingStorage ? "unknown_requires_diagnostic" : "not_initialized",
|
|
257
|
+
runtime_readiness: "unknown_requires_qualification",
|
|
258
|
+
next_action: !installation ? "dd-flow setup --help" : existingStorage ? "dd-flow migration plan --help" : "dd-flow engine doctor --json",
|
|
259
|
+
...(existingStorage ? { storage_continuation: { kind: "storage_admission",
|
|
260
|
+
status: "requires_diagnostic", reason: "existing_storage_not_inspected",
|
|
261
|
+
help_command: "dd-flow migration plan --help", automatic_migration: false } } : {}) };
|
|
262
|
+
}
|
|
263
|
+
/** Exact-request installation transaction. Selected immutable caches do not affect existing RUNs. */
|
|
264
|
+
export function installRelease(home, input) {
|
|
265
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9_.-]{0,95}$/.test(input.requestId) || !["install", "update"].includes(input.action))
|
|
266
|
+
refuse("invalid_request");
|
|
267
|
+
const executor = input.executor === undefined ? undefined : input.executor === null ? null : prepareOperatorProfile(input.executor);
|
|
268
|
+
const executorBinding = executor === undefined ? {} : { executor: structuredClone(executor) };
|
|
269
|
+
if (input.deferredOnboarding !== undefined && (input.deferredOnboarding !== true || executor !== undefined || input.profileCreation !== undefined))
|
|
270
|
+
refuse("invalid_deferred_onboarding");
|
|
271
|
+
const onboardingBinding = input.deferredOnboarding ? { deferredOnboarding: true } : {};
|
|
272
|
+
if (input.profileCreation !== undefined) {
|
|
273
|
+
validateProfileCreationIntent(input.profileCreation);
|
|
274
|
+
if (!executor || hybridDigest(executor) !== hybridDigest(input.profileCreation.profile))
|
|
275
|
+
refuse("profile_executor_mismatch");
|
|
276
|
+
}
|
|
277
|
+
const profileBinding = input.profileCreation === undefined ? {} : { profileCreation: structuredClone(input.profileCreation) };
|
|
278
|
+
const manifest = admitReleaseManifest(input.manifest, input.authority);
|
|
279
|
+
if (!Buffer.isBuffer(input.manifestBytes) || hybridDigest(manifestFromBytes(input.manifestBytes)) !== hybridDigest(manifest))
|
|
280
|
+
refuse("manifest_bytes_mismatch");
|
|
281
|
+
// Both archives must be valid before even creating the home or publishing the first cache.
|
|
282
|
+
validateReleasePayloadIdentity(manifest, input.canonArchive, input.engineArchive);
|
|
283
|
+
const root = path.resolve(home), requests = path.join(root, "setup-requests"), lock = path.join(root, "setup.lock");
|
|
284
|
+
const installation = { manifest, manifest_sha256: bytesDigest(input.manifestBytes), manifest_bytes_base64: input.manifestBytes.toString("base64"),
|
|
285
|
+
canon_root: path.join(root, "release-cache", "canon", manifest.canon.version, "payload"),
|
|
286
|
+
engine_root: path.join(root, "release-cache", "engine", manifest.engine.version, "payload") };
|
|
287
|
+
const identity = hybridDigest({ action: input.action, installation, ...executorBinding, ...profileBinding, ...onboardingBinding });
|
|
288
|
+
readSetupStatus(root);
|
|
289
|
+
for (const directory of [root, requests]) {
|
|
290
|
+
if (fs.existsSync(directory) && !fs.lstatSync(directory).isDirectory())
|
|
291
|
+
refuse("unsafe_setup_directory");
|
|
292
|
+
}
|
|
293
|
+
if (input.profileCreation) {
|
|
294
|
+
const existingFile = path.join(requests, `${input.requestId}.json`);
|
|
295
|
+
const retained = fs.existsSync(existingFile) ? readSetupRequest(existingFile) : null;
|
|
296
|
+
if (retained?.phase !== "committed") {
|
|
297
|
+
const revision = readOperatorProfiles(root).revision;
|
|
298
|
+
if (revision !== input.profileCreation.before.revision && revision !== input.profileCreation.after.revision)
|
|
299
|
+
refuse("profile_selection_changed");
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
fs.mkdirSync(requests, { recursive: true, mode: 0o700 });
|
|
303
|
+
const lease = acquireInstallationLock(lock, hybridDigest({ request_id: input.requestId, identity }));
|
|
304
|
+
let failure;
|
|
305
|
+
const perform = () => {
|
|
306
|
+
const file = path.join(requests, `${input.requestId}.json`);
|
|
307
|
+
const current = readSetupStatus(root).installation;
|
|
308
|
+
let request = { schema_id: "dd-flow/setup-request@1", request_id: input.requestId, identity,
|
|
309
|
+
previous_selection: current ? hybridDigest(current) : null, phase: "prepared", installation, ...executorBinding, ...profileBinding, ...onboardingBinding };
|
|
310
|
+
if (fs.existsSync(file)) {
|
|
311
|
+
request = readSetupRequest(file);
|
|
312
|
+
if (request.schema_id !== "dd-flow/setup-request@1" || request.request_id !== input.requestId || request.identity !== identity || hybridDigest(request.installation) !== hybridDigest(installation))
|
|
313
|
+
refuse("request_identity_conflict");
|
|
314
|
+
if (request.deferredOnboarding !== input.deferredOnboarding)
|
|
315
|
+
refuse("request_onboarding_conflict");
|
|
316
|
+
if (hybridDigest({ executor: request.executor ?? null, present: Object.hasOwn(request, "executor") }) !==
|
|
317
|
+
hybridDigest({ executor: executor ?? null, present: executor !== undefined }))
|
|
318
|
+
refuse("request_executor_conflict");
|
|
319
|
+
if (hybridDigest({ profileCreation: request.profileCreation ?? null }) !== hybridDigest({ profileCreation: input.profileCreation ?? null }))
|
|
320
|
+
refuse("request_profile_creation_conflict");
|
|
321
|
+
if (!["prepared", "canon_cached", "engine_cached", "profiles_saved", "committed"].includes(request.phase))
|
|
322
|
+
refuse("invalid_request_phase");
|
|
323
|
+
}
|
|
324
|
+
// Replay still revalidates retained payload bytes; it never reactivates an older selection.
|
|
325
|
+
if (request.phase === "committed") {
|
|
326
|
+
publishReleaseCache(root, "canon", manifest.canon.version, manifest.canon.archive, input.canonArchive);
|
|
327
|
+
publishReleaseCache(root, "engine", manifest.engine.version, manifest.engine.archive, input.engineArchive);
|
|
328
|
+
return { ok: true, replayed: true, installation: request.installation, ...executorBinding };
|
|
329
|
+
}
|
|
330
|
+
const selectedDigest = current ? hybridDigest(current) : null;
|
|
331
|
+
if (selectedDigest !== request.previous_selection && selectedDigest !== hybridDigest(installation))
|
|
332
|
+
refuse("selection_changed_since_request");
|
|
333
|
+
if (input.action === "install" && current && selectedDigest !== hybridDigest(installation))
|
|
334
|
+
refuse("existing_installation_requires_update");
|
|
335
|
+
const checkpoint = (phase) => {
|
|
336
|
+
request.phase = phase;
|
|
337
|
+
atomicJson(file, request);
|
|
338
|
+
input.onCheckpoint?.(phase);
|
|
339
|
+
};
|
|
340
|
+
checkpoint("prepared");
|
|
341
|
+
publishReleaseCache(root, "canon", manifest.canon.version, manifest.canon.archive, input.canonArchive);
|
|
342
|
+
checkpoint("canon_cached");
|
|
343
|
+
publishReleaseCache(root, "engine", manifest.engine.version, manifest.engine.archive, input.engineArchive);
|
|
344
|
+
checkpoint("engine_cached");
|
|
345
|
+
if (request.profileCreation) {
|
|
346
|
+
commitProfileCreation(root, request.profileCreation);
|
|
347
|
+
checkpoint("profiles_saved");
|
|
348
|
+
}
|
|
349
|
+
atomicJson(path.join(root, "installation.json"), installation);
|
|
350
|
+
checkpoint("committed");
|
|
351
|
+
return { ok: true, replayed: false, installation, ...executorBinding };
|
|
352
|
+
};
|
|
353
|
+
let result;
|
|
354
|
+
try {
|
|
355
|
+
result = perform();
|
|
356
|
+
}
|
|
357
|
+
catch (error) {
|
|
358
|
+
failure = error instanceof AppError ? error : new AppError("setup_interrupted", "Setup request requires reconciliation", 1, { phase: "setup", effect: "unknown", request_id: input.requestId, next_action: "Replay the exact setup request", help: "setup" });
|
|
359
|
+
}
|
|
360
|
+
finally {
|
|
361
|
+
try {
|
|
362
|
+
lease.release();
|
|
363
|
+
}
|
|
364
|
+
catch {
|
|
365
|
+
// Cleanup is secondary to the retained operation result. Never leak a
|
|
366
|
+
// raw filesystem cause or erase the original failure with an unlink error.
|
|
367
|
+
if (failure)
|
|
368
|
+
failure.details.cleanup = "not_confirmed";
|
|
369
|
+
else
|
|
370
|
+
failure = new AppError("setup_cleanup_failed", "Setup owner cleanup requires reconciliation", 1, { phase: "setup", effect: "unknown", cleanup: "not_confirmed", request_id: input.requestId,
|
|
371
|
+
next_action: "Inspect dd-flow setup status --json; replay the exact request after its owner exits", help: "setup" });
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (failure)
|
|
375
|
+
throw failure;
|
|
376
|
+
return result;
|
|
377
|
+
}
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
import { AppError } from "../shared/errors.js";
|
|
2
2
|
import { resolveProjectRoot } from "../storage/paths.js";
|
|
3
3
|
import { requireProjectByRoot } from "./projects.js";
|
|
4
|
-
import { blockFlowRunStage, unblockFlowRunStage } from "./runs.js";
|
|
4
|
+
import { blockFlowRunStage, unblockFlowRunStage, prepareFlowRunStageBlock, prepareFlowRunStageUnblock } from "./runs.js";
|
|
5
5
|
import { flowCommand } from "./stage-pause.js";
|
|
6
6
|
import { refreshRunWorkProjection } from "./work-registry.js";
|
|
7
|
-
export function
|
|
7
|
+
export function prepareStageRuntimeBlock(context, input) {
|
|
8
|
+
if (typeof input.code !== "string" || !input.code.trim() || typeof input.summary !== "string" || !input.summary.trim())
|
|
9
|
+
throw new AppError("validation", "stage block requires a code and summary", 2);
|
|
8
10
|
const projectRoot = resolveProjectRoot(input.projectRoot);
|
|
9
11
|
const project = requireProjectByRoot(context, projectRoot);
|
|
10
12
|
const run = requireRun(context, project.id, input.runId);
|
|
11
13
|
const work = requireWork(context, project.id, run.id, input.workId);
|
|
12
14
|
if (work.status !== "running")
|
|
13
15
|
throw new AppError("invalid_work_state", "Only a running Work can be blocked", 1, { work_id: work.work_id, status: work.status });
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
const flow = prepareFlowRunStageBlock(context, { ...input, projectRoot });
|
|
17
|
+
return { projectRoot, project, work, flow };
|
|
18
|
+
}
|
|
19
|
+
export function blockStageForRuntime(context, input) {
|
|
20
|
+
const { projectRoot, project, work, flow } = prepareStageRuntimeBlock(context, input);
|
|
21
|
+
const state = blockFlowRunStage(context, { ...input, projectRoot }, flow);
|
|
17
22
|
const now = context.now();
|
|
18
23
|
context.db.run("UPDATE works SET status = 'paused', updated_at = ? WHERE work_id = ? AND status = 'running'", [now, work.work_id]);
|
|
19
24
|
refreshRunWorkProjection(context, project.id, work.run_id);
|
|
@@ -30,14 +35,19 @@ export function blockStageForRuntime(context, input) {
|
|
|
30
35
|
state
|
|
31
36
|
};
|
|
32
37
|
}
|
|
33
|
-
export function
|
|
38
|
+
export function prepareStageRuntimeUnblock(context, input) {
|
|
34
39
|
const projectRoot = resolveProjectRoot(input.projectRoot);
|
|
35
40
|
const project = requireProjectByRoot(context, projectRoot);
|
|
36
41
|
const run = requireRun(context, project.id, input.runId);
|
|
37
42
|
const work = requireWork(context, project.id, run.id, input.workId);
|
|
38
43
|
if (work.status !== "paused")
|
|
39
44
|
throw new AppError("invalid_work_state", "stage unblock requires its paused Work", 1, { work_id: work.work_id, status: work.status });
|
|
40
|
-
const
|
|
45
|
+
const flow = prepareFlowRunStageUnblock(context, { ...input, projectRoot, runId: work.run_id });
|
|
46
|
+
return { projectRoot, project, work, flow };
|
|
47
|
+
}
|
|
48
|
+
export function unblockStageAfterRuntimeRepair(context, input) {
|
|
49
|
+
const { projectRoot, project, work, flow } = prepareStageRuntimeUnblock(context, input);
|
|
50
|
+
const state = unblockFlowRunStage(context, { projectRoot, runId: work.run_id, stage: input.stage, workId: work.work_id }, flow);
|
|
41
51
|
const now = context.now();
|
|
42
52
|
context.db.run("UPDATE works SET status = 'running', updated_at = ? WHERE work_id = ? AND status = 'paused'", [now, work.work_id]);
|
|
43
53
|
refreshRunWorkProjection(context, project.id, work.run_id);
|