@deksden-com/dd-flow-cli 0.9.0-beta.14 → 0.9.0-beta.140
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 +733 -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 +724 -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 +129 -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 +507 -0
- package/dist/harness-runtime/lib/dd-opencode.mjs +354 -0
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +750 -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 +13 -0
- package/dist/harness-runtime/lib/managed-daemon.mjs +654 -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 +14 -0
- package/dist/harness-runtime/lib/operation-errors.mjs +116 -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 +168 -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 +1156 -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 +100 -108
- 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 +32 -23
- package/dist/services/usage.js +49 -101
- package/dist/services/verification-matrix.js +374 -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 +229 -104
- package/dist/services/vnext-protocolize.js +411 -86
- 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 +98 -44
- package/dist/shared/entity-references.js +8 -0
- package/dist/shared/errors.js +17 -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
package/dist/cli/run-cli.js
CHANGED
|
@@ -1,56 +1,105 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
+
import { prepareResponseDestination, writeResponseFile } from "./response-file.js";
|
|
3
|
+
import { requiredOption, optionalOption, optionalNumber, optionalPositiveNumber } from "./argument-values.js";
|
|
4
|
+
import { resolveCommandPaths, resolveCommandProjectRoot } from "../services/command-context.js";
|
|
5
|
+
import { assertAuxiliaryCommandScope, cliManagedInvocationScope, manualRecoveryInvocationScope, assertManualRecoveryInvocationTarget } from "../services/lifecycle-invocations.js";
|
|
2
6
|
import path from "node:path";
|
|
7
|
+
import { randomUUID } from "node:crypto";
|
|
8
|
+
import { quote } from "shell-quote";
|
|
9
|
+
import { observeLifecycleCommand, observedLifecycleNativeEvent } from "../services/lifecycle-invocations.js";
|
|
10
|
+
import { verifyCodexHookDelivery } from "../services/codex-hook-delivery.js";
|
|
11
|
+
import { assertLifecycleInvocationCurrent, awaitLifecycleInvocation, bindLifecyclePublishedPacket, expandLifecycleInvocationArgs, issueLifecycleInvocation, prepareLifecycleIssuance, lifecycleInvocationScope, observedLifecycleDiagnostic, observedLifecycleInvocation, resolveObservedLifecycleArgs, retainObservedLifecycleSemantics, settleLifecycleInvocation, settleLifecyclePreparationRejection, settleLifecycleRejection, validateLifecyclePathAliases } from "../services/lifecycle-invocations.js";
|
|
3
12
|
import { createContext, createRouterContext } from "../runtime/context.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
13
|
+
import { dispatchNativeHook, isNativeHookIngress } from "./native-hook-ingress.js";
|
|
14
|
+
import { isRuntimeMaintenance, runtimeMaintenanceDiagnostics, withMaintenanceDiagnosticScope } from "./runtime-maintenance-diagnostics.js";
|
|
15
|
+
import { migrateStoreWriter } from "../storage/writer-migration.js";
|
|
16
|
+
import { resolveHelp } from "./help.js";
|
|
17
|
+
import { renderHumanError, writePreparationError } from "./error-output.js";
|
|
18
|
+
import { runOperatorProfilesCli } from "./operator-profiles.js";
|
|
19
|
+
import { runSetupCli } from "./setup.js";
|
|
20
|
+
import { runCompletionCli } from "./completion-cli.js";
|
|
21
|
+
import { runHarnessesCli } from "./harnesses.js";
|
|
22
|
+
import { runModelsCli } from "./models.js";
|
|
23
|
+
import { parseOutputOptions } from "./output-options.js";
|
|
24
|
+
import { AppError, errorRecord, isAppError } from "../shared/errors.js";
|
|
25
|
+
import { entityReferenceVariants } from "../shared/entity-references.js";
|
|
26
|
+
import { writeJson, parseJsonObject } from "../shared/json.js";
|
|
27
|
+
import { archiveProject, prepareProjectArchive, getProjectStatus, migrateProjectIds, registerProject, resolveProject } from "../services/projects.js";
|
|
28
|
+
import { cancelProtocol, getProtocolBlockers, getProtocolStatus, getReadyProtocols, implementProtocol, readyForMerge, registerProtocol, prepareProtocolRegistration, requireProtocol, syncProtocolFromRun, prepareProtocolTransition, transitionProtocol } from "../services/protocols.js";
|
|
29
|
+
import { getPlanStatus, preparePlanItemMutation, updatePlanItem } from "../services/plans.js";
|
|
30
|
+
import { cancelMergeQueueJob, prepareMergeQueueCancellation, prepareMergeQueueWait, prepareMergeClaim, prepareMergeBundleClaim, claimNextMergeJob, claimMergeJob, claimMergeBundle, completeMergeBundle, prepareMergeBundleCompletion, completeMergeJob, prepareMergeJobCompletion, failMergeBundle, prepareMergeBundleFailure, failMergeJob, prepareMergeJobFailure, getMergeBundleStatus, getMergeQueueStatus, noteMergeJob, prepareMergeJobNote, waitNextMergeJob } from "../services/merge-queue.js";
|
|
11
31
|
import { getMergeWorkerStatus, oneShotMergeClaim, startMergeWorker, stopProjectMergeWorker } from "../services/merge-worker.js";
|
|
12
|
-
import { cleanupApply, cleanupScan } from "../services/cleanup.js";
|
|
13
|
-
import { getCodexHomeStatus, getCodexHooksStatus, handleCodexHook, handleAgyEvent, handleGrokEvent, handleOpenCodeEvent, handleZcodeEvent, isolatedDdFlowHomeFromHookStdin, claimBootstrapHookEvent, findRecentMatchingHookEvent, stageStartMatchKey, initCodexHome, installCodexHooks, planCodexHome, printCodexHomeEnv, printCodexHooks, removeCodexHome, removeCodexHooks } from "../services/hooks.js";
|
|
14
|
-
import { bootstrapWorktree, closeWorktree, createWorktreeRecord, getWorktreeStatus, planWorktree } from "../services/worktrees.js";
|
|
15
|
-
import { acquireLaneLock, cancelLaneWaiter, checkLaneWorkspace, getLaneWaiters, getLaneStatus, heartbeatLaneLock, releaseLaneLock, setLaneWorkspace, waitAcquireLaneLock, waitForLaneLock } from "../services/lanes.js";
|
|
16
|
-
import { getProjectConfigStatus, setProjectConfigValue } from "../services/config.js";
|
|
17
|
-
import { getCanonStatus, registerCanonRoot, resolveCanonRoot } from "../services/canon.js";
|
|
32
|
+
import { cleanupApply, cleanupScan, prepareCleanupApply } from "../services/cleanup.js";
|
|
33
|
+
import { getCodexHomeStatus, getCodexHooksStatus, handleCodexHook, handleAgyEvent, handleDroidEvent, handleGrokEvent, handleOpenCodeEvent, handleZcodeHook, handleZcodeEvent, isolatedDdFlowHomeFromHookStdin, claimBootstrapHookEvent, findRecentMatchingHookEvent, stageStartMatchKey, stageLifecycleMatchKey, initCodexHome, prepareCodexHome, prepareCodexHooks, installCodexHooks, planCodexHome, printCodexHomeEnv, printCodexHooks, removeCodexHome, prepareCodexHomeRemoval, removeCodexHooks } from "../services/hooks.js";
|
|
34
|
+
import { bootstrapWorktree, closeWorktree, createWorktreeRecord, prepareWorktreeCreate, prepareWorktreeBootstrap, prepareWorktreeClose, getWorktreeStatus, planWorktree } from "../services/worktrees.js";
|
|
35
|
+
import { acquireLaneLock, cancelLaneWaiter, prepareLaneWaiterCancellation, checkLaneWorkspace, getLaneWaiters, getLaneStatus, heartbeatLaneLock, releaseLaneLock, setLaneWorkspace, prepareLaneWorkspace, prepareLaneLock, waitAcquireLaneLock, waitForLaneLock } from "../services/lanes.js";
|
|
36
|
+
import { getProjectConfigStatus, prepareProjectConfigValue, setProjectConfigValue } from "../services/config.js";
|
|
37
|
+
import { getCanonStatus, registerCanonRoot, resolveCanonRoot, prepareCanonRegistration } from "../services/canon.js";
|
|
18
38
|
import { getRuntimeStatus } from "../services/status.js";
|
|
19
39
|
import { getCliVersionReport } from "../services/build-info.js";
|
|
20
40
|
import { parseCommandArgs } from "../services/lifecycle-command.js";
|
|
21
|
-
import {
|
|
41
|
+
import { prepareError, prepareJsonFile, prepareJsonSchema, prepareJsonText, prepareTextFile, prepareFileBytes, readBoundedUtf8Stream } from "./input-preparation.js";
|
|
42
|
+
import { autoRefreshDashboards, dashboardData, getCmuxStatus, openDashboard, openGlobalDashboard, openProtocolDashboard, refreshAllDashboards, prepareDashboardRefresh, prepareDashboardRender, prepareGlobalDashboardRender, prepareDashboardOptions, refreshDashboard, refreshGlobalDashboard, renderDashboard, renderGlobalDashboard } from "../services/dashboard.js";
|
|
22
43
|
import { dashboardTargetSummary, preferredDashboardCommand, relatedDashboardCommands, resolveDashboardTarget } from "../services/dashboard-targets.js";
|
|
23
|
-
import { getFlowSessionStatus, registerFlowSession, stopFlowSession, stopMergeWorker, syncFlowSessionUsage } from "../services/sessions.js";
|
|
44
|
+
import { getFlowSessionStatus, prepareFlowSessionPayload, registerFlowSession, stopFlowSession, stopMergeWorker, syncFlowSessionUsage } from "../services/sessions.js";
|
|
24
45
|
import { getProtocolBranchContext } from "../services/branch-context.js";
|
|
25
|
-
import { attachFlowRunStage, completeFlowRun, completeFlowRunStage, getFlowRunSessions, getFlowRunTimeline, getStoredFlowRunUsage, getFlowRunFlagsStatus, getFlowRunStatus, getFlowRunConfig, getFlowRunVariables, listFlowRuns, reviseFlowRunFlags, setFlowRunConfig, setFlowRunVariable, startFlowRun } from "../services/runs.js";
|
|
46
|
+
import { attachFlowRunStage, readFlowRunExecutionBundle, prepareFlowRunStageAttachment, prepareRawFlowRunStageCompletion, completeFlowRun, prepareFlowRunCompletion, completeFlowRunStage, getFlowRunSessions, getFlowRunTimeline, getStoredFlowRunUsage, getFlowRunFlagsStatus, getFlowRunStatus, getFlowRunConfig, getFlowRunVariables, listFlowRuns, reviseFlowRunFlags, setFlowRunConfig, prepareFlowRunConfig, prepareFlowRunVariable, setFlowRunVariable, startFlowRun, resolveRun, prepareFlowRunStart } from "../services/runs.js";
|
|
26
47
|
import { previewNextEntityId } from "../services/ids.js";
|
|
27
48
|
import { validateSchema } from "../services/schema-validation.js";
|
|
28
49
|
import { preflightMemoryPermissions } from "../services/memory-permissions.js";
|
|
29
|
-
import { ingestAgyUsage, ingestGrokUsage, ingestOpenCodeUsage, ingestZcodeUsage } from "../services/usage.js";
|
|
30
|
-
import { applyMigration, assessMigrationImpact, assertUpgradeRunStorageReady, planMigration, verifyMigrationReport } from "../services/migrations.js";
|
|
50
|
+
import { ingestDroidUsage, ingestAgyUsage, ingestGrokUsage, ingestOpenCodeUsage, ingestZcodeUsage } from "../services/usage.js";
|
|
51
|
+
import { applyMigration, assessMigrationImpact, assertUpgradeRunStorageReady, planMigration, prepareMigrationApply, verifyMigrationReport } from "../services/migrations.js";
|
|
31
52
|
import { publishProjectSummary } from "../services/project-summary.js";
|
|
32
53
|
import { requireProjectByRoot } from "../services/projects.js";
|
|
33
|
-
import { resolveProjectRoot } from "../storage/paths.js";
|
|
34
|
-
import { doctorEngines, bindRunEngine, engineInfo, installCurrentEngine, listEngines, resolveEngine, resolveExplicitProjectRoot, resolveOperationProjectRoot, routeArgsThroughEngine } from "../services/engines.js";
|
|
54
|
+
import { resolveProjectRoot, resolveDdFlowHome } from "../storage/paths.js";
|
|
55
|
+
import { doctorEngines, bindRunEngine, prepareRunEngineBind, engineInfo, installCurrentEngine, listEngines, resolveEngine, resolveExplicitProjectRoot, resolveOperationProjectRoot, routeArgsThroughEngine } from "../services/engines.js";
|
|
35
56
|
import { preflightCliCompatibility } from "../services/compatibility-preflight.js";
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import { dispatchVnextPlanReview,
|
|
40
|
-
import { addVnextCodeRepair,
|
|
41
|
-
import {
|
|
42
|
-
import { applyVnextMerge,
|
|
57
|
+
import { getVnextWorkStatus, isVnextSpecifyFlow, isVnextSpecifyRun, prepareVnextSpecifyStart } from "../services/vnext-specify.js";
|
|
58
|
+
import { isVnextProtocolizeRun, prepareProtocolizeResultPath, prepareVnextProtocolizeStart } from "../services/vnext-protocolize.js";
|
|
59
|
+
import { isVnextPlanRun, prepareVnextPlanStart, prepareVnextPlanFinish } from "../services/vnext-plan.js";
|
|
60
|
+
import { dispatchVnextPlanReview, isVnextPlanReviewRun, prepareVnextPlanReviewStart, recordVnextPlanReviewCapacity } from "../services/vnext-plan-review.js";
|
|
61
|
+
import { addVnextCodeRepair, prepareVnextCodeRepair, prepareVnextCodeStart } from "../services/vnext-code.js";
|
|
62
|
+
import { isVnextCodeReviewRun, prepareCodeReviewDecision, prepareVnextCodeReviewRepair, prepareVnextCodeReviewStart } from "../services/vnext-code-review.js";
|
|
63
|
+
import { applyVnextMerge, getVnextMergeRequest, isVnextMergeRun, prepareVnextMergeFinish, prepareVnextMergeStart, repairVnextMerge, routeVnextMergeRequest } from "../services/vnext-merge.js";
|
|
64
|
+
import { sdlcStageHandlers } from "../services/stage-handlers.js";
|
|
65
|
+
import { startMemoryStage, finishMemoryStage } from "../services/memory-stage-dispatch.js";
|
|
66
|
+
import { bindStageCoordinatorWork } from "../services/work-registry.js";
|
|
67
|
+
import { registerMemoryCompositionFromStage } from "../services/memory-compose-handler.js";
|
|
68
|
+
import { runMemoryVerificationChecks } from "../services/memory-verify-handler.js";
|
|
69
|
+
import { registerMemoryReviewFromStage, collectMemoryInitialReviewFromStage } from "../services/memory-review-handler.js";
|
|
70
|
+
import { PreparedRetainedMemoryReviewAuthority } from "../services/memory-retained-review-authority.js";
|
|
71
|
+
import { PreparedRetainedMemoryReviewResults } from "../services/memory-retained-review-results.js";
|
|
72
|
+
import { registerMemoryCorrectionsFromStage, registerMemorySecondCorrectionsFromStage, runMemoryCorrectionChecksFromStage, runMemorySecondCorrectionChecksFromStage, registerCorrectedMemoryReviewFromStage, registerSecondCorrectedMemoryReviewFromStage, collectCorrectedMemoryReviewFromStage, collectSecondCorrectedMemoryReviewFromStage } from "../services/memory-review-handler.js";
|
|
73
|
+
import { integrateMemoryFinishFromStage, registerMemoryFinishEvidenceFromStage } from "../services/memory-finish-handler.js";
|
|
74
|
+
import { registerMemoryRepairBaselineFromStage, registerMemorySecondRepairBaselineFromStage, reserveMemoryRepairWriterCopyFromStage, reserveMemorySecondRepairCopyFromStage, materializeMemoryRepairWriterCopyFromStage, materializeMemorySecondRepairCopyFromStage, prepareMemoryRepairDispatchFromStage, prepareMemorySecondRepairDispatchFromStage, registerMemoryRepairCurrentViewFromStage, registerMemorySecondRepairCurrentViewFromStage, registerMemoryRepairPublicationSourceFromStage, reserveMemoryRepairPublicationAgainstSourceFromStage, reserveMemorySecondRepairPublicationFromStage, applyMemoryRepairPublicationFromStage, applyMemorySecondRepairPublicationFromStage, finishMemoryRepairPublicationFromStage, finishMemorySecondRepairPublicationFromStage } from "../services/memory-review-handler.js";
|
|
43
75
|
import { mergeServerStatus, serveMergeRequests, stopMergeServer } from "../services/merge-server.js";
|
|
44
|
-
import { getVnextFanoutStatus } from "../services/vnext-fanout.js";
|
|
45
|
-
import { pauseStageForUser, resumeStageAfterUser } from "../services/stage-pause.js";
|
|
46
|
-
import {
|
|
76
|
+
import { getVnextFanoutStatus, memoryStageFanoutDescriptor, observeVnextNativeChildren, prepareNativeObservations, reconcileVnextFanout } from "../services/vnext-fanout.js";
|
|
77
|
+
import { pauseStageForUser, resumeStageAfterUser, prepareStagePause, prepareStageResume, stagePauseCommand } from "../services/stage-pause.js";
|
|
78
|
+
import { runtimeCommand } from "../services/runtime-command.js";
|
|
79
|
+
import { managedLifecycleCommand } from "../services/lifecycle-invocations.js";
|
|
80
|
+
import { blockStageForRuntime, unblockStageAfterRuntimeRepair, prepareStageRuntimeBlock, prepareStageRuntimeUnblock } from "../services/stage-blocker.js";
|
|
47
81
|
import { classifyCliOperation, requiresExplicitUpgradeAuthorization } from "../services/cli-operation-classifier.js";
|
|
48
|
-
import { renderWorkerPrompt } from "../services/prompts.js";
|
|
49
|
-
import { finishStage, startStage } from "../services/stage-lifecycle.js";
|
|
50
|
-
import { addWorkBatch, cancelWork, deleteWork, failWork, finishWork, listWorks, mutateWorkDeps, retryWork, showWork, startWork } from "../services/work-registry.js";
|
|
51
|
-
import {
|
|
52
|
-
import {
|
|
53
|
-
import {
|
|
82
|
+
import { prepareWorkerPrompt, renderWorkerPrompt } from "../services/prompts.js";
|
|
83
|
+
import { finishStage, prepareStageFinish, startStage, prepareLegacyStageStart } from "../services/stage-lifecycle.js";
|
|
84
|
+
import { addWorkBatch, prepareWorkBatch, prepareWorkDeps, prepareWorkDelete, prepareWorkSettlement, prepareWorkRetry, assertStageLifecycleOwner, assertWorkLifecycleOwner, cancelWork, deleteWork, failWork, finishWork, listWorks, mutateWorkDeps, prepareWorkResult, workResultByteLimit, retryWork, showWork, startWork } from "../services/work-registry.js";
|
|
85
|
+
import { launchExternalWork } from "../services/external-work-launch.js";
|
|
86
|
+
import { createEvalBootstrapSnapshot, prepareEvalBootstrapSnapshot, createEvalRunSnapshot, prepareEvalRunSnapshotCreation, prepareVnextSpecifyRun, prepareEvalRunRestore, prepareEvalBootstrapRestore, restoreEvalBootstrapSnapshot, restoreEvalRunSnapshot } from "../services/eval-snapshots.js";
|
|
87
|
+
import { forkRunFromSnapshot, prepareRunFork } from "../services/run-fork.js";
|
|
88
|
+
import { acceptRunRecovery, beginRunRecovery, inspectRunRecovery, prepareRecoveryCapture, prepareRecoveryPayload, prepareRunRecoveryResume, recordRecoveryCapture, resumeRunRecovery, sealRunRecovery } from "../services/run-recovery.js";
|
|
89
|
+
import { loadExternalStageContext, prepareExternalStageContext } from "../services/stage-context.js";
|
|
90
|
+
import { vnextStageDirectory } from "../domain/stage-catalog.js";
|
|
91
|
+
import { prepareCommandRoute } from "./command-inputs.js";
|
|
92
|
+
import { parseLifecycleCommand } from "../services/lifecycle-command.js";
|
|
93
|
+
import { dispatchRuntimeProcess, prepareRuntimeProcess } from "./runtime-process.js";
|
|
94
|
+
import { reconcileFlowRunHealth } from "../services/run-health.js";
|
|
95
|
+
import { fenceRuntimeBudget, runtimeBudgetStatus, prepareRuntimeScopeControl, prepareRuntimeScopeFence } from "../services/runtime-budget.js";
|
|
96
|
+
import { stopRuntimeScope } from "../services/runtime-scope-stop.js";
|
|
97
|
+
import { launchRuntimeScopeControl, launchRuntimeScopeResume, prepareRuntimeScopeReconciliation, prepareRuntimeScopeServing, reconcileRuntimeScopeControl, serveRuntimeScopeControl } from "../services/runtime-scope-worker.js";
|
|
98
|
+
import { prepareRuntimeScopeResumeRequest } from "../services/runtime-scope-resume.js";
|
|
99
|
+
import { launchRunController, prepareRunControllerLaunch, resumeRunController, serveRunController, runControllerStatus, answerRunController, prepareRunControllerAnswer, supplyRunControllerContext, prepareRunControllerContext } from "../services/run-controller.js";
|
|
100
|
+
import { controlLifecycleReply, runControlStatus } from "../services/run-control.js";
|
|
101
|
+
import { reconcileRunControl, requestRunControl, serveRunControl } from "../services/run-control-worker.js";
|
|
102
|
+
import { startRuntimeService, prepareRuntimeService } from "../services/runtime-service.js";
|
|
54
103
|
const defaultIo = {
|
|
55
104
|
stdout: process.stdout,
|
|
56
105
|
stderr: process.stderr,
|
|
@@ -58,9 +107,58 @@ const defaultIo = {
|
|
|
58
107
|
};
|
|
59
108
|
const flagValue = "__dd_flow_flag__";
|
|
60
109
|
export async function runCli(args, io = defaultIo, env = process.env) {
|
|
61
|
-
|
|
110
|
+
return withMaintenanceDiagnosticScope(() => runCliRequest(args, io, env));
|
|
111
|
+
}
|
|
112
|
+
async function runCliRequest(args, io, env) {
|
|
113
|
+
try {
|
|
114
|
+
const help = resolveHelp(args);
|
|
115
|
+
if (help) {
|
|
116
|
+
if (help.json)
|
|
117
|
+
writeJson(io.stdout, help.data);
|
|
118
|
+
else
|
|
119
|
+
io.stdout.write(`${help.text}\n`);
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
const record = errorRecord(error);
|
|
125
|
+
writePreparationError(io, args, record);
|
|
126
|
+
return record.exitCode;
|
|
127
|
+
}
|
|
128
|
+
let output;
|
|
129
|
+
try {
|
|
130
|
+
output = parseOutputOptions(args);
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
const rejection = isAppError(error) ? error : new AppError("usage", error instanceof Error ? error.message : String(error), 2);
|
|
134
|
+
Object.assign(rejection.details, { phase: "prepare", effect: "no_effect", recoverable: true, ...rejection.details });
|
|
135
|
+
try {
|
|
136
|
+
const command = quote(["dd-flow", ...args]);
|
|
137
|
+
const analysis = parseLifecycleCommand(command);
|
|
138
|
+
const event = analysis.kind !== "none" || analysis.auxiliary
|
|
139
|
+
? observedLifecycleNativeEvent(createRouterContext(env), command, env.DD_FLOW_DAEMON_ID) : null;
|
|
140
|
+
if (event) {
|
|
141
|
+
const context = createContext({ ...env }, "hook");
|
|
142
|
+
try {
|
|
143
|
+
observeLifecycleCommand(context, command, event)({ error: rejection });
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
context.db.close?.();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch (persistence) {
|
|
151
|
+
rejection.details.lifecycle_diagnostic_error = String(persistence);
|
|
152
|
+
}
|
|
153
|
+
writePreparationError(io, args, errorRecord(rejection));
|
|
154
|
+
return rejection.exitCode;
|
|
155
|
+
}
|
|
62
156
|
const progress = progressForCommand(output.args);
|
|
63
157
|
let heartbeat;
|
|
158
|
+
let invocationSettlement;
|
|
159
|
+
let settleHook;
|
|
160
|
+
let diagnosticHome;
|
|
161
|
+
let maintenanceDiagnostics;
|
|
64
162
|
try {
|
|
65
163
|
if (output.args.length === 1 && output.args[0] === "--version") {
|
|
66
164
|
const payload = getCliVersionReport();
|
|
@@ -70,11 +168,60 @@ export async function runCli(args, io = defaultIo, env = process.env) {
|
|
|
70
168
|
io.stdout.write(`dd-flow ${payload.cli.version}\n`);
|
|
71
169
|
return 0;
|
|
72
170
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
171
|
+
if (output.args[0] === "completion")
|
|
172
|
+
return runCompletionCli(args, io);
|
|
173
|
+
if (output.args[0] === "setup")
|
|
174
|
+
return runSetupCli(args, env, io);
|
|
175
|
+
if (output.args[0] === "harnesses")
|
|
176
|
+
return runHarnessesCli(args, env, io);
|
|
177
|
+
if (output.args[0] === "models")
|
|
178
|
+
return runModelsCli(args, io);
|
|
179
|
+
if (output.args[0] === "profiles")
|
|
180
|
+
return runOperatorProfilesCli(args, env, io);
|
|
181
|
+
const suppliedInvocationId = optionalOption(parseArgs(output.args.slice(2)), "invocation-id");
|
|
182
|
+
if (isNativeHookIngress(output.args)) {
|
|
183
|
+
writeJson(io.stdout, await dispatchNativeHook(output.args, await readStdin(io.stdin), env));
|
|
76
184
|
return 0;
|
|
77
185
|
}
|
|
186
|
+
if (output.args.slice(0, 3).join(" ") === "codex hook handle") {
|
|
187
|
+
const parsed = prepareCommandRoute(output.args).parsed;
|
|
188
|
+
const stdin = await readStdin(io.stdin);
|
|
189
|
+
const payload = parseJsonObject(stdin || "{}", "codex hook stdin");
|
|
190
|
+
const event = requiredOption(parsed, "event");
|
|
191
|
+
const isolatedHome = event === "PreToolUse" ? isolatedDdFlowHomeFromHookStdin(stdin) : null;
|
|
192
|
+
const hookEnv = isolatedHome ? { ...env, DD_FLOW_HOME: isolatedHome } : env;
|
|
193
|
+
const proof = await verifyCodexHookDelivery(hookEnv, payload);
|
|
194
|
+
let opened;
|
|
195
|
+
const mode = databaseModeForCommand(output.args, classifyCliOperation(output.args, hookEnv));
|
|
196
|
+
const context = {
|
|
197
|
+
env: hookEnv, ddFlowHome: resolveDdFlowHome(hookEnv), now: () => new Date().toISOString(),
|
|
198
|
+
get db() { return (opened ??= createContext(hookEnv, mode)).db; }
|
|
199
|
+
};
|
|
200
|
+
try {
|
|
201
|
+
// Retired events retain the explicit startup/migration path. PreToolUse
|
|
202
|
+
// only opens its selected prepared store when the handler needs it.
|
|
203
|
+
if (mode !== "hook")
|
|
204
|
+
opened = createContext(hookEnv, mode);
|
|
205
|
+
if (proof)
|
|
206
|
+
context.codexHookProof = proof;
|
|
207
|
+
const result = handleCodexHook(context, { event, projectRoot: optionalOption(parsed, "project-root"), stdin });
|
|
208
|
+
writeJson(io.stdout, result && typeof result === "object" && !Array.isArray(result) && "hookSpecificOutput" in result
|
|
209
|
+
? { hookSpecificOutput: result.hookSpecificOutput } : {});
|
|
210
|
+
return 0;
|
|
211
|
+
}
|
|
212
|
+
finally {
|
|
213
|
+
opened?.db.close?.();
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (output.args[0] === "run" && ["drive", "control"].includes(output.args[1] ?? "")) {
|
|
217
|
+
const controlArgs = parseArgs(output.args.slice(2));
|
|
218
|
+
requiredOption(controlArgs, "run");
|
|
219
|
+
requiredOption(controlArgs, "project-root");
|
|
220
|
+
}
|
|
221
|
+
if (io.stdin?.isTTY && ["work", "stage"].includes(output.args[0] ?? "") && output.args[1] === "finish"
|
|
222
|
+
&& hasOption(parseArgs(output.args.slice(2)), "result-stdin")) {
|
|
223
|
+
throw new AppError("usage", "--result-stdin requires non-interactive input. Pipe or redirect the complete JSON into this command, or use --result-file; interactive terminals can truncate long JSON and wait indefinitely for EOF.", 2);
|
|
224
|
+
}
|
|
78
225
|
const classification = classifyCliOperation(output.args, env);
|
|
79
226
|
if (requiresExplicitUpgradeAuthorization(output.args) && classification.mode !== "mb_upgrade") {
|
|
80
227
|
throw new AppError("upgrade_authorization_required", "This mb-upgrade command requires the explicit mb-upgrade authorization marker and allowlisted target context", 1, {
|
|
@@ -82,26 +229,191 @@ export async function runCli(args, io = defaultIo, env = process.env) {
|
|
|
82
229
|
required_marker: "DD_FLOW_COMPATIBILITY_MODE=mb-upgrade"
|
|
83
230
|
});
|
|
84
231
|
}
|
|
85
|
-
|
|
232
|
+
// Registration may bootstrap the resource registry, but never the unrelated
|
|
233
|
+
// RUN store. Its caller's absolute deadline also bounds that first open.
|
|
234
|
+
const maintenanceOptions = isRuntimeMaintenance(output.args) ? { resourceDatabaseMode: output.args[2] === "register" ? "initialize" : output.args[2] === "status" ? "read_existing" : "prepared_existing", ...(env.DD_FLOW_HOOK_DEADLINE_AT ? { databaseDeadlineAt: Number(env.DD_FLOW_HOOK_DEADLINE_AT) } : {}) } : {};
|
|
235
|
+
if (output.args[0] === "runtime" && output.args[1] === "scope" && ["fence", "stop"].includes(output.args[2] ?? ""))
|
|
236
|
+
maintenanceOptions.resourceDatabaseMode = "retirement";
|
|
237
|
+
// Keep ordinary CLI JSON/stderr compatibility; bounded maintenance callers
|
|
238
|
+
// provide this request's deadline and consume its structured phase evidence.
|
|
239
|
+
if (maintenanceOptions.databaseDeadlineAt !== undefined)
|
|
240
|
+
maintenanceDiagnostics = runtimeMaintenanceDiagnostics(output.args, maintenanceOptions.databaseDeadlineAt, line => io.stderr.write(line));
|
|
241
|
+
const routerContext = createRouterContext(env, maintenanceOptions.databaseDeadlineAt);
|
|
242
|
+
const publicCommand = quote(["dd-flow", ...output.args]);
|
|
243
|
+
const nativeCommand = quote(["dd-flow", ...args]);
|
|
244
|
+
const lifecycleCommand = parseLifecycleCommand(publicCommand);
|
|
245
|
+
if (lifecycleCommand.kind === "standalone")
|
|
246
|
+
validateLifecyclePathAliases(output.args, lifecycleCommand.invocation.operation);
|
|
247
|
+
const managedScope = lifecycleCommand.kind === "standalone" && env.DD_FLOW_DAEMON_ID ? cliManagedInvocationScope({ ...routerContext, env }) : null;
|
|
248
|
+
const nativeEvent = env.DD_FLOW_DAEMON_ID && lifecycleCommand.kind === "standalone"
|
|
249
|
+
? observedLifecycleNativeEvent(routerContext, nativeCommand, env.DD_FLOW_DAEMON_ID) : null;
|
|
250
|
+
const nativeEventKey = nativeEvent && routerContext.db.get("SELECT event_key FROM hook_events WHERE project_id = ? AND id = ?", [nativeEvent.project_id, nativeEvent.id])?.event_key;
|
|
251
|
+
const observedInvocation = managedScope ? observedLifecycleInvocation(routerContext, publicCommand, managedScope, nativeEventKey ? { eventKey: nativeEventKey } : undefined) : null;
|
|
252
|
+
if (managedScope && suppliedInvocationId
|
|
253
|
+
&& routerContext.db.get("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'lifecycle_invocations'")
|
|
254
|
+
&& routerContext.db.get("SELECT id FROM lifecycle_invocations WHERE id = ?", [suppliedInvocationId])) {
|
|
255
|
+
const suppliedScope = lifecycleInvocationScope({ ...routerContext, env }, suppliedInvocationId);
|
|
256
|
+
if (["projectRoot", "daemonId", "rootSessionId", "runId", "generation"].some(key => suppliedScope[key] !== managedScope[key])) {
|
|
257
|
+
throw new AppError("invocation_scope_mismatch", "Explicit lifecycle assignment differs from the current physical native owner", 1, { effect: "no_effect", recoverable: false });
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
const diagnostic = env.DD_FLOW_DAEMON_ID && lifecycleCommand.kind === "standalone"
|
|
261
|
+
? observedLifecycleDiagnostic(routerContext, publicCommand, env.DD_FLOW_DAEMON_ID, observedInvocation?.id) : null;
|
|
262
|
+
if (diagnostic && (!nativeEvent || Number(diagnostic.hook_event_id) > nativeEvent.id)) {
|
|
263
|
+
throw new AppError("lifecycle_shell_syntax_invalid", `Native observer rejected the shell form: ${String(diagnostic.reason ?? "unsupported shell composition")}. Write structured input to a file and run the returned dd-flow command separately.`, 2, { ...diagnostic, phase: "prepare", effect: "no_effect", recoverable: true });
|
|
264
|
+
}
|
|
265
|
+
let manualRecoveryHookEventKey;
|
|
266
|
+
if (!observedInvocation && !suppliedInvocationId && env.DD_FLOW_DAEMON_ID && lifecycleCommand.kind === "standalone") {
|
|
267
|
+
const receipt = nativeEvent && routerContext.db.get("SELECT event_key,harness,provider_session_id,parent_session_id,daemon_id,status FROM hook_events WHERE id = ? AND project_id = ?", [nativeEvent.id, nativeEvent.project_id]);
|
|
268
|
+
const manual = managedScope && receipt?.status === "observed" ? manualRecoveryInvocationScope({ ...routerContext, env }, managedScope.projectRoot, { daemonId: receipt.daemon_id, rootSessionId: receipt.provider_session_id, sessionId: receipt.provider_session_id, parentSessionId: receipt.parent_session_id, toolCallId: String(nativeEvent.id) }, receipt.harness) : null;
|
|
269
|
+
if (!manual || JSON.stringify(manual) !== JSON.stringify(managedScope))
|
|
270
|
+
throw new AppError(nativeEvent ? "invocation_assignment_missing" : "invocation_receipt_missing", nativeEvent ? "Native call was observed, but its arguments do not match an issued lifecycle assignment" : "Managed lifecycle command has no committed native receipt", 1, { effect: "no_effect", recoverable: false });
|
|
271
|
+
assertManualRecoveryInvocationTarget({ ...routerContext, env }, manual, publicCommand);
|
|
272
|
+
manualRecoveryHookEventKey = receipt.event_key;
|
|
273
|
+
}
|
|
274
|
+
const invocationId = observedInvocation?.id ?? suppliedInvocationId;
|
|
275
|
+
let boundInvocationArgs;
|
|
276
|
+
try {
|
|
277
|
+
boundInvocationArgs = observedInvocation
|
|
278
|
+
? resolveObservedLifecycleArgs(routerContext, observedInvocation.id, output.args)
|
|
279
|
+
: invocationId && invocationId !== suppliedInvocationId
|
|
280
|
+
? [...output.args.filter((_, index, values) => values[index - 1] !== "--invocation-id" && values[index] !== "--invocation-id"), "--invocation-id", invocationId]
|
|
281
|
+
: output.args;
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
if (!(observedInvocation && error instanceof AppError))
|
|
285
|
+
throw error;
|
|
286
|
+
const rejectionContext = createContext({ ...env }, "hook");
|
|
287
|
+
try {
|
|
288
|
+
Object.assign(error.details, settleLifecyclePreparationRejection(rejectionContext, observedInvocation.id, error));
|
|
289
|
+
}
|
|
290
|
+
finally {
|
|
291
|
+
rejectionContext.db.close?.();
|
|
292
|
+
}
|
|
293
|
+
throw error;
|
|
294
|
+
}
|
|
295
|
+
const observedOperation = lifecycleCommand.kind === "standalone" ? lifecycleCommand.invocation.operation : null;
|
|
296
|
+
if (observedInvocation && !observedOperation)
|
|
297
|
+
throw new AppError("invocation_command_invalid", "Observed lifecycle command could not be parsed", 1);
|
|
298
|
+
const invocationScope = invocationId ? lifecycleInvocationScope({ ...routerContext, env }, invocationId) : undefined;
|
|
299
|
+
const expandedInvocationArgs = invocationScope && observedOperation
|
|
300
|
+
? expandLifecycleInvocationArgs(routerContext, boundInvocationArgs, invocationScope, observedOperation)
|
|
301
|
+
: boundInvocationArgs;
|
|
302
|
+
const retainedOutput = observedInvocation ? parseOutputOptions(expandedInvocationArgs) : null;
|
|
303
|
+
if (retainedOutput?.responseFile)
|
|
304
|
+
output.responseFile = retainedOutput.responseFile;
|
|
305
|
+
let invocationArgs = retainedOutput?.args ?? expandedInvocationArgs;
|
|
86
306
|
emitHumanProgress(io, output, progress, "start");
|
|
87
307
|
if (output.progressJsonl) {
|
|
88
308
|
writeProgressJsonl(io, "start", `Starting ${output.args.slice(0, 3).join(" ")}`);
|
|
89
309
|
heartbeat = setInterval(() => writeProgressJsonl(io, "running", "Command is still running"), 15_000);
|
|
90
310
|
}
|
|
91
|
-
|
|
92
|
-
|
|
311
|
+
// Route by retained authority, so a mistyped project path reaches the CLI
|
|
312
|
+
// validator instead of selecting a different engine or failing preflight.
|
|
313
|
+
const scopeProjectRoot = invocationScope?.projectRoot ?? resolveCommandProjectRoot(routerContext, output.args, resolveProjectRootBeforeRouting(output.args, env, maintenanceOptions.databaseDeadlineAt));
|
|
314
|
+
assertAuxiliaryCommandScope({ ...routerContext, env }, publicCommand, scopeProjectRoot);
|
|
315
|
+
invocationArgs = resolveCommandPaths(routerContext, invocationArgs, scopeProjectRoot);
|
|
316
|
+
const routedArgs = output.responseFile ? [...invocationArgs, "--response-file", output.responseFile] : invocationArgs;
|
|
93
317
|
const routed = await routeArgsThroughEngine(routerContext, output.json ? [...routedArgs, "--json", ...(output.progressJsonl ? ["--progress-jsonl"] : [])] : routedArgs, io, io.stdin, env, scopeProjectRoot);
|
|
94
318
|
if (routed?.routed && !routed.inProcess) {
|
|
95
319
|
if (heartbeat)
|
|
96
320
|
clearInterval(heartbeat);
|
|
97
|
-
emitHumanProgress(io, output, progress, "done");
|
|
321
|
+
emitHumanProgress(io, output, progress, routed.exitCode === 0 ? "done" : "failed");
|
|
322
|
+
if (output.progressJsonl && routed.exitCode !== 0)
|
|
323
|
+
writeProgressJsonl(io, "failed", "Routed engine failed", { exit_code: routed.exitCode });
|
|
98
324
|
return routed.exitCode;
|
|
99
325
|
}
|
|
326
|
+
let preparedInput;
|
|
327
|
+
let preparedRoute;
|
|
328
|
+
if (!invocationId) {
|
|
329
|
+
const preparationContext = createContext({ ...env }, "read_existing", undefined, maintenanceOptions);
|
|
330
|
+
try {
|
|
331
|
+
preparedInput = await prepareCliInput(invocationArgs, preparationContext, io, scopeProjectRoot, output.responseFile);
|
|
332
|
+
}
|
|
333
|
+
finally {
|
|
334
|
+
preparationContext.db.close?.();
|
|
335
|
+
}
|
|
336
|
+
}
|
|
100
337
|
const dispatchContext = routed?.inProcess
|
|
101
|
-
? createContext({ ...env, DD_FLOW_ENGINE_MODE: "1", DD_FLOW_ENGINE_HOME: "in-process", DD_FLOW_ROUTED_FROM: getCliVersionReport().cli.version }, databaseModeForCommand(
|
|
102
|
-
: createContext(env, databaseModeForCommand(
|
|
103
|
-
|
|
104
|
-
|
|
338
|
+
? createContext({ ...env, DD_FLOW_ENGINE_MODE: "1", DD_FLOW_ENGINE_HOME: "in-process", DD_FLOW_ROUTED_FROM: getCliVersionReport().cli.version }, databaseModeForCommand(invocationArgs, classification), undefined, maintenanceOptions)
|
|
339
|
+
: createContext({ ...env }, databaseModeForCommand(invocationArgs, classification), undefined, maintenanceOptions);
|
|
340
|
+
diagnosticHome = dispatchContext.ddFlowHome;
|
|
341
|
+
dispatchContext.env.DD_FLOW_INVOCATION_READONLY = classification.mode === "read_only_diagnostics" ? "1" : undefined;
|
|
342
|
+
let dispatchArgs = invocationArgs;
|
|
343
|
+
let retainedOutcome;
|
|
344
|
+
// Issued transports may deliver their receipt asynchronously. Observe only
|
|
345
|
+
// after admission; retained outcomes must not need a fresh hook receipt.
|
|
346
|
+
if (invocationId) {
|
|
347
|
+
const command = quote(["dd-flow", ...invocationArgs]);
|
|
348
|
+
const admission = await awaitLifecycleInvocation(dispatchContext, { id: invocationId, command,
|
|
349
|
+
validateArgs: () => {
|
|
350
|
+
try {
|
|
351
|
+
preparedRoute = prepareCommandRoute(invocationArgs);
|
|
352
|
+
}
|
|
353
|
+
catch (error) {
|
|
354
|
+
if (error instanceof AppError && error.code === "usage" && ["invocation-id", "hook-event-id"].includes(String(error.details.parameter ?? ""))) {
|
|
355
|
+
throw new AppError("lifecycle_contract_invalid", `The runtime generated arguments rejected by its own CLI contract: ${error.message}`, 1, { ...error.details, recoverable: false, operation: observedOperation });
|
|
356
|
+
}
|
|
357
|
+
throw error;
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
prepare: async () => { preparedInput = await prepareCliInput(invocationArgs, dispatchContext, io, scopeProjectRoot, output.responseFile, preparedRoute); },
|
|
361
|
+
assertCurrent: scope => assertLifecycleInvocationCurrent(dispatchContext, scope, command) });
|
|
362
|
+
if (admission.replay) {
|
|
363
|
+
retainedOutcome = admission.outcome;
|
|
364
|
+
settleHook = undefined;
|
|
365
|
+
delete dispatchContext.observeLifecycleHook;
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
invocationSettlement = { context: dispatchContext, id: invocationId };
|
|
369
|
+
if (observedInvocation)
|
|
370
|
+
retainObservedLifecycleSemantics(dispatchContext, invocationId, invocationArgs);
|
|
371
|
+
dispatchArgs = [...invocationArgs, "--hook-event-id", admission.eventKey];
|
|
372
|
+
preparedInput?.route?.parsed.options.set("hook-event-id", [admission.eventKey]);
|
|
373
|
+
dispatchContext.env.DD_FLOW_INVOCATION_SCOPE = JSON.stringify(lifecycleInvocationScope(dispatchContext, invocationId));
|
|
374
|
+
dispatchContext.env.DD_FLOW_CURRENT_INVOCATION = invocationId;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (!invocationId && manualRecoveryHookEventKey) {
|
|
378
|
+
// Keep the physically scoped receipt selected by admission; a service must
|
|
379
|
+
// not rediscover a different daemon's matching command event.
|
|
380
|
+
if (!invocationArgs.includes("--hook-event-id"))
|
|
381
|
+
dispatchArgs = [...invocationArgs, "--hook-event-id", manualRecoveryHookEventKey];
|
|
382
|
+
preparedInput?.route?.parsed.options.set("hook-event-id", [manualRecoveryHookEventKey]);
|
|
383
|
+
}
|
|
384
|
+
if (retainedOutcome?.error) {
|
|
385
|
+
const error = retainedOutcome.error;
|
|
386
|
+
throw new AppError(error.code, error.message, error.exitCode, error.details);
|
|
387
|
+
}
|
|
388
|
+
if (!retainedOutcome && !preparedInput)
|
|
389
|
+
preparedInput = await prepareCliInput(invocationArgs, dispatchContext, io, scopeProjectRoot, output.responseFile);
|
|
390
|
+
if (!retainedOutcome)
|
|
391
|
+
await preflightCliCompatibility(dispatchContext, invocationArgs, env, scopeProjectRoot);
|
|
392
|
+
if (!retainedOutcome) {
|
|
393
|
+
const command = !invocationId ? nativeCommand : quote(["dd-flow", ...dispatchArgs]);
|
|
394
|
+
const event = !invocationId ? nativeEvent : null;
|
|
395
|
+
settleHook = observeLifecycleCommand(dispatchContext, command, event ?? undefined);
|
|
396
|
+
}
|
|
397
|
+
const result = retainedOutcome ? retainedOutcome.result : await dispatch(dispatchArgs, dispatchContext, io, scopeProjectRoot, classification, output, preparedInput);
|
|
398
|
+
if (!retainedOutcome && invocationArgs[0] === "stage" && invocationArgs[1] === "start"
|
|
399
|
+
&& result && typeof result === "object" && "prompt_path" in result && typeof result.prompt_path === "string") {
|
|
400
|
+
try {
|
|
401
|
+
bindLifecyclePublishedPacket(dispatchContext, fs.readFileSync(result.prompt_path, "utf8"));
|
|
402
|
+
}
|
|
403
|
+
catch (cause) {
|
|
404
|
+
throw new AppError("invocation_packet_binding_failed", "Stage started but its published lifecycle assignments could not be retained", 1, { effect: "committed", cause: String(cause) });
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
const completedHook = settleHook;
|
|
408
|
+
settleHook = undefined;
|
|
409
|
+
completedHook?.({ result });
|
|
410
|
+
if (invocationSettlement) {
|
|
411
|
+
const completed = invocationSettlement;
|
|
412
|
+
invocationSettlement = undefined;
|
|
413
|
+
// A failure to persist an already completed result is an unknown reply,
|
|
414
|
+
// not a failed operation eligible for a new productive retry.
|
|
415
|
+
settleLifecycleInvocation(completed.context, completed.id, { result });
|
|
416
|
+
}
|
|
105
417
|
const refreshWarnings = refreshDashboardsAfterMutation(dispatchContext, output.args, result, scopeProjectRoot);
|
|
106
418
|
const resultWithWarnings = addWarnings(result, refreshWarnings);
|
|
107
419
|
if (heartbeat)
|
|
@@ -111,47 +423,157 @@ export async function runCli(args, io = defaultIo, env = process.env) {
|
|
|
111
423
|
emitHumanProgress(io, output, progress, "done");
|
|
112
424
|
const payload = attachProgress(resultWithWarnings, progress, output);
|
|
113
425
|
const visible = writeResponseFile(payload, output);
|
|
426
|
+
const maintenanceReceipt = payload;
|
|
427
|
+
const maintenanceExpiry = maintenanceReceipt?.lease_expires_at ?? maintenanceReceipt?.process?.lease_expires_at;
|
|
428
|
+
maintenanceDiagnostics?.phase("ack", { ...(typeof maintenanceExpiry === "string" && Number.isFinite(Date.parse(maintenanceExpiry)) ? { lease_expires_at: maintenanceExpiry } : {}) });
|
|
114
429
|
if (output.json)
|
|
115
430
|
writeJson(io.stdout, visible);
|
|
116
431
|
else
|
|
117
432
|
io.stdout.write(renderHumanResult(output.args, payload));
|
|
118
433
|
return completedCommandExitCode(payload);
|
|
119
434
|
}
|
|
120
|
-
catch (
|
|
435
|
+
catch (caught) {
|
|
436
|
+
const record = errorRecord(caught);
|
|
437
|
+
maintenanceDiagnostics?.failure(caught);
|
|
438
|
+
const error = isAppError(caught) ? caught : Object.assign(new AppError(record.code, record.message, record.exitCode, { effect: "unknown", ...record.details, ...(caught?.errcode !== undefined ? { sqlite_extended_code: caught.errcode } : {}) }), { ...(record.cause ? { cause: record.cause } : {}), ...(record.cleanup_error ? { cleanup_error: record.cleanup_error } : {}), ...(record.retryable !== undefined ? { retryable: record.retryable } : {}) });
|
|
439
|
+
if (!settleHook && !invocationSettlement) {
|
|
440
|
+
let earlyContext;
|
|
441
|
+
try {
|
|
442
|
+
const command = quote(["dd-flow", ...args]);
|
|
443
|
+
const analysis = parseLifecycleCommand(command);
|
|
444
|
+
// Maintenance failures must not start an unrelated, unbounded hook
|
|
445
|
+
// lookup after their own request deadline has already expired.
|
|
446
|
+
const router = analysis.kind !== "none" || analysis.auxiliary ? createRouterContext(env) : null;
|
|
447
|
+
const event = router ? observedLifecycleNativeEvent(router, command, env.DD_FLOW_DAEMON_ID) : null;
|
|
448
|
+
// A correctable prepare rejection precedes the terminal command's
|
|
449
|
+
// single-use claim. Correlate its unique hook by the same lifecycle
|
|
450
|
+
// match key used at dispatch, including compound provider tool shells;
|
|
451
|
+
// otherwise a repaired retry sees two fresh, unclaimed events.
|
|
452
|
+
if (router && (event || (error.details.phase === "prepare" && error.details.effect === "no_effect"
|
|
453
|
+
&& fs.existsSync(path.join(router.ddFlowHome, "db.sqlite"))))) {
|
|
454
|
+
earlyContext = createContext({ ...env }, "hook");
|
|
455
|
+
observeLifecycleCommand(earlyContext, command, event ?? undefined)({ error });
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
catch (persistence) {
|
|
459
|
+
error.details.lifecycle_diagnostic_error = String(persistence);
|
|
460
|
+
}
|
|
461
|
+
finally {
|
|
462
|
+
earlyContext?.db.close?.();
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
if (diagnosticHome && (!isAppError(caught) || error.code === "storage_write_failed")) {
|
|
466
|
+
try {
|
|
467
|
+
const directory = path.join(diagnosticHome, "diagnostics", "cli");
|
|
468
|
+
fs.mkdirSync(directory, { recursive: true });
|
|
469
|
+
const artifact = path.join(directory, `${randomUUID()}.json`);
|
|
470
|
+
fs.writeFileSync(artifact, JSON.stringify({ code: error.code, message: error.message, details: error.details, stack: caught instanceof Error ? caught.stack : null }), { flag: "wx", mode: 0o600 });
|
|
471
|
+
error.details.diagnostic_artifact = artifact;
|
|
472
|
+
}
|
|
473
|
+
catch (secondary) {
|
|
474
|
+
io.stderr.write(`CLI diagnostic persistence failed: ${String(secondary)}\n`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
121
477
|
if (heartbeat)
|
|
122
478
|
clearInterval(heartbeat);
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
479
|
+
if (invocationSettlement && error.details.effect !== "committed") {
|
|
480
|
+
try {
|
|
481
|
+
if (isAppError(error)) {
|
|
482
|
+
delete error.details.retry_command;
|
|
483
|
+
delete error.details.retry_instruction;
|
|
484
|
+
const settled = errorRecord(error);
|
|
485
|
+
settleLifecycleRejection(invocationSettlement.context, invocationSettlement.id, settled);
|
|
486
|
+
// Serialization owns a copy. Publish the committed settlement, not
|
|
487
|
+
// the original rejection which has no successor authority.
|
|
488
|
+
Object.assign(error.details, settled.details);
|
|
489
|
+
}
|
|
490
|
+
else
|
|
491
|
+
settleLifecycleInvocation(invocationSettlement.context, invocationSettlement.id, { error: { code: "unexpected", message: String(error), exitCode: 1, details: {} } });
|
|
492
|
+
}
|
|
493
|
+
catch (persistenceError) {
|
|
494
|
+
if (isAppError(error))
|
|
495
|
+
error.details.recoverable = false;
|
|
496
|
+
io.stderr.write(`Invocation settlement persistence failed: ${String(persistenceError)}\n`);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
if (settleHook) {
|
|
500
|
+
try {
|
|
501
|
+
settleHook({ error: isAppError(error) ? error : { code: "unexpected", message: String(error), details: {} } });
|
|
502
|
+
}
|
|
503
|
+
catch (persistence) {
|
|
504
|
+
io.stderr.write(`${JSON.stringify({ primary: String(error), lifecycle_persistence_error: String(persistence) })}\n`);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
const payload = { ok: false, error: errorRecord(error) };
|
|
508
|
+
progress.splice(0, progress.length, ...progress.filter(event => event.phase !== "done"));
|
|
509
|
+
if (progress.length)
|
|
510
|
+
progress.push({ phase: "failed", message: payload.error.message, at: new Date().toISOString() });
|
|
126
511
|
emitHumanProgress(io, output, progress, "failed");
|
|
127
512
|
if (output.progressJsonl)
|
|
128
513
|
writeProgressJsonl(io, "failed", payload.error.message, { code: payload.error.code });
|
|
129
514
|
const errorPayload = attachProgress(payload, progress, output);
|
|
130
|
-
|
|
515
|
+
let visible = errorPayload;
|
|
516
|
+
try {
|
|
517
|
+
if (error.details.parameter !== "response-file")
|
|
518
|
+
visible = writeResponseFile(errorPayload, output);
|
|
519
|
+
}
|
|
520
|
+
catch (publication) {
|
|
521
|
+
error.details.response_publication_error = { code: publication?.code ?? "response_publication_failed", message: publication instanceof Error ? publication.message : String(publication) };
|
|
522
|
+
}
|
|
131
523
|
if (output.json && output.progressJsonl)
|
|
132
524
|
writeJson(io.stdout, errorPayload);
|
|
133
525
|
else if (output.json)
|
|
134
526
|
writeJson(io.stderr, visible);
|
|
135
527
|
else
|
|
136
|
-
io.stderr.write(renderHumanError(payload));
|
|
528
|
+
io.stderr.write(renderHumanError(payload, args));
|
|
137
529
|
return isAppError(error) ? error.exitCode : 1;
|
|
138
530
|
}
|
|
531
|
+
finally {
|
|
532
|
+
maintenanceDiagnostics?.close();
|
|
533
|
+
}
|
|
139
534
|
}
|
|
140
|
-
function resolveProjectRootBeforeRouting(args, env) {
|
|
535
|
+
function resolveProjectRootBeforeRouting(args, env, deadlineAt) {
|
|
141
536
|
const explicit = resolveExplicitProjectRoot(args);
|
|
142
537
|
if (explicit)
|
|
143
538
|
return explicit;
|
|
144
539
|
try {
|
|
145
540
|
// Router identity lookup is read-only; it must not initialize or migrate runtime storage.
|
|
146
|
-
|
|
541
|
+
const context = createContext(env, "read_existing", undefined, deadlineAt === undefined ? {} : { databaseDeadlineAt: deadlineAt });
|
|
542
|
+
try {
|
|
543
|
+
return resolveOperationProjectRoot(context, args);
|
|
544
|
+
}
|
|
545
|
+
finally {
|
|
546
|
+
context.db.close?.();
|
|
547
|
+
}
|
|
147
548
|
}
|
|
148
549
|
catch {
|
|
149
550
|
return null;
|
|
150
551
|
}
|
|
151
552
|
}
|
|
152
553
|
function databaseModeForCommand(args, classification) {
|
|
554
|
+
if (isNativeHookIngress(args))
|
|
555
|
+
return "hook";
|
|
556
|
+
if (args[0] === "run" && args[1] === "reconcile")
|
|
557
|
+
return args.includes("--apply") ? "reconcile" : "read_existing";
|
|
558
|
+
if (isRuntimeMaintenance(args))
|
|
559
|
+
return "read_existing";
|
|
153
560
|
const [family, command] = args;
|
|
561
|
+
// Managed attempts are issued only from a prepared store. Opening that
|
|
562
|
+
// existing writer must not run migrations before their input is admitted.
|
|
563
|
+
if (parseArgs(args.slice(2)).options.has("invocation-id"))
|
|
564
|
+
return "hook";
|
|
565
|
+
// Native hook ingress must remain synchronous, but it must not run schema
|
|
566
|
+
// migration/VACUUM while the provider is waiting for its decision. Startup
|
|
567
|
+
// prepares the store; the hook mode only verifies the prepared contract and
|
|
568
|
+
// records the receipt.
|
|
569
|
+
if (family === "codex" && command === "hook" && args[2] === "handle") {
|
|
570
|
+
// Only the participating PreToolUse ingress needs the prepared hot-path
|
|
571
|
+
// contract. SessionStart/Stop/PostToolUse are observational no-ops here
|
|
572
|
+
// and retain the ordinary startup path for backwards compatibility.
|
|
573
|
+
return optionalOption(parseArgs(args.slice(3)), "event") === "PreToolUse" ? "hook" : "initialize";
|
|
574
|
+
}
|
|
154
575
|
if (family === "engine" ||
|
|
576
|
+
family === "storage" && command === "migrate-writer" ||
|
|
155
577
|
family === "version" ||
|
|
156
578
|
family === "schema" ||
|
|
157
579
|
family === "canon" && command === "resolve" ||
|
|
@@ -165,86 +587,6 @@ function databaseModeForCommand(args, classification) {
|
|
|
165
587
|
return "read_existing";
|
|
166
588
|
return "initialize";
|
|
167
589
|
}
|
|
168
|
-
function renderHumanError(payload) {
|
|
169
|
-
const lines = [`dd-flow: ${payload.error.message}`];
|
|
170
|
-
const details = recordObject(payload.error.details);
|
|
171
|
-
const compatibility = recordObject(details?.compatibility);
|
|
172
|
-
const remediation = recordObject(details?.remediation);
|
|
173
|
-
if (compatibility) {
|
|
174
|
-
lines.push(`Compatibility: ${String(compatibility.verdict ?? "unknown")} (${String(compatibility.engine_resolution ?? "unknown")})`);
|
|
175
|
-
if (compatibility.blocked_operation)
|
|
176
|
-
lines.push(`Blocked operation: ${String(compatibility.blocked_operation)}`);
|
|
177
|
-
if (compatibility.install_hint)
|
|
178
|
-
lines.push(`Install compatible engine: ${String(compatibility.install_hint)}`);
|
|
179
|
-
}
|
|
180
|
-
if (remediation?.mb_upgrade_mode)
|
|
181
|
-
lines.push(`Upgrade mode: ${String(remediation.mb_upgrade_mode)}`);
|
|
182
|
-
if (details?.preferred_command)
|
|
183
|
-
lines.push(`Preferred command: ${String(details.preferred_command)}`);
|
|
184
|
-
const related = Array.isArray(details?.related_commands) ? details.related_commands.map(String) : [];
|
|
185
|
-
if (related.length > 0) {
|
|
186
|
-
lines.push("", "Related commands:");
|
|
187
|
-
for (const command of related)
|
|
188
|
-
lines.push(` ${command}`);
|
|
189
|
-
}
|
|
190
|
-
return `${lines.join("\n")}\n`;
|
|
191
|
-
}
|
|
192
|
-
function parseOutputOptions(args) {
|
|
193
|
-
const stripped = [];
|
|
194
|
-
let json = false;
|
|
195
|
-
let progressJsonl = false;
|
|
196
|
-
let responseFile = null;
|
|
197
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
198
|
-
const arg = args[index];
|
|
199
|
-
if (arg === undefined)
|
|
200
|
-
continue;
|
|
201
|
-
if (arg === "--json") {
|
|
202
|
-
json = true;
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
if (arg === "--progress-jsonl") {
|
|
206
|
-
progressJsonl = true;
|
|
207
|
-
continue;
|
|
208
|
-
}
|
|
209
|
-
if (arg === "--response-file") {
|
|
210
|
-
const file = args[++index];
|
|
211
|
-
if (!file)
|
|
212
|
-
throw new AppError("usage", "--response-file requires a path", 2);
|
|
213
|
-
responseFile = file;
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
stripped.push(arg);
|
|
217
|
-
}
|
|
218
|
-
if (responseFile && !json)
|
|
219
|
-
throw new AppError("usage", "--response-file requires --json", 2);
|
|
220
|
-
const hookJsonOnly = stripped[0] === "codex" && stripped[1] === "hook" && stripped[2] === "handle";
|
|
221
|
-
return { json: json || hookJsonOnly, progressJsonl, args: stripped, hookJsonOnly, responseFile };
|
|
222
|
-
}
|
|
223
|
-
/** Persist the complete machine response for harnesses that cap tool output.
|
|
224
|
-
* The visible response keeps routing fields but omits the potentially large
|
|
225
|
-
* rendered prompt; an agent can safely read the named file without rerunning
|
|
226
|
-
* the lifecycle command. */
|
|
227
|
-
function writeResponseFile(payload, output) {
|
|
228
|
-
if (!output.responseFile)
|
|
229
|
-
return payload;
|
|
230
|
-
const file = path.resolve(output.responseFile);
|
|
231
|
-
try {
|
|
232
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
233
|
-
const temporary = `${file}.tmp-${process.pid}-${crypto.randomUUID()}`;
|
|
234
|
-
fs.writeFileSync(temporary, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
235
|
-
fs.renameSync(temporary, file);
|
|
236
|
-
}
|
|
237
|
-
catch (error) {
|
|
238
|
-
// The lifecycle mutation may already be accepted. Do not retry it just
|
|
239
|
-
// because its optional full-response file could not be written.
|
|
240
|
-
return { ...(payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true, result: payload }), response_file: file, response_file_error: String(error) };
|
|
241
|
-
}
|
|
242
|
-
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
243
|
-
return { ok: true, response_file: file };
|
|
244
|
-
const visible = { ...payload, response_file: file };
|
|
245
|
-
delete visible.worker_prompt_markdown;
|
|
246
|
-
return visible;
|
|
247
|
-
}
|
|
248
590
|
function progressForCommand(args) {
|
|
249
591
|
const [family, command, subcommand] = args;
|
|
250
592
|
if (family === "merge-queue" && command === "wait-next") {
|
|
@@ -500,27 +842,676 @@ function stringValue(value) {
|
|
|
500
842
|
function shortSha(value) {
|
|
501
843
|
return value ? value.slice(0, 12) : "unknown";
|
|
502
844
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
845
|
+
/** All caller supplied file/stdin data is consumed here, before a managed
|
|
846
|
+
* invocation is allowed to claim execution or a stage claims its hook event. */
|
|
847
|
+
async function prepareCliInput(...args) {
|
|
848
|
+
try {
|
|
849
|
+
return await prepareCliInputValue(...args);
|
|
850
|
+
}
|
|
851
|
+
catch (error) {
|
|
852
|
+
return prepareError(error);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
async function prepareCliInputValue(args, context, io, scopeProjectRoot, responseFile = null, route = prepareCommandRoute(args)) {
|
|
856
|
+
const { family, command, parsed } = route;
|
|
857
|
+
const prepared = { route };
|
|
858
|
+
if (optionalOption(parsed, "reason-file")) {
|
|
859
|
+
const value = prepareJsonFile(requiredOption(parsed, "reason-file"), "Check recovery evidence").value;
|
|
860
|
+
if (!value || typeof value.reason !== "string" || !value.reason.trim() || Object.keys(value).some(key => key !== "reason"))
|
|
861
|
+
throw new AppError("validation", "Recovery evidence must be a JSON object with one nonempty reason", 2);
|
|
862
|
+
prepared.retryReason = value.reason;
|
|
863
|
+
}
|
|
864
|
+
else if (optionalOption(parsed, "retry-check"))
|
|
865
|
+
prepared.retryReason = optionalOption(parsed, "reason");
|
|
866
|
+
if (route.key === "project register")
|
|
867
|
+
resolveProjectRoot(requiredOption(parsed, "root"));
|
|
868
|
+
if (route.key === "project archive")
|
|
869
|
+
prepareProjectArchive(context, { idOrAlias: parsed.positional[0], root: optionalOption(parsed, "root"), reason: requiredOption(parsed, "reason") });
|
|
870
|
+
if (route.key === "protocol register")
|
|
871
|
+
prepareProtocolRegistration(context, { handshakeId: requiredPosition(parsed, 0, "handshake-id"), projectRoot: requiredOption(parsed, "project-root"), workspacePath: optionalOption(parsed, "workspace-path") });
|
|
872
|
+
if (route.key === "canon register")
|
|
873
|
+
prepared.canonRegistration = prepareCanonRegistration(context, { root: requiredOption(parsed, "root") });
|
|
874
|
+
if (route.key === "run attach-stage")
|
|
875
|
+
prepareFlowRunStageAttachment(context, stageAttachmentInput(parsed));
|
|
876
|
+
if (route.key === "run complete-stage")
|
|
877
|
+
prepareRawFlowRunStageCompletion(context, stageCompletionInput(parsed));
|
|
878
|
+
if (route.key === "run override")
|
|
879
|
+
prepareFlowRunCompletion(context, runOverrideInput(parsed));
|
|
880
|
+
if (route.key === "engine bind-run")
|
|
881
|
+
prepared.engineBind = await prepareRunEngineBind(context, engineBindInput(parsed));
|
|
882
|
+
if (route.key === "worktree create")
|
|
883
|
+
prepared.worktreeCreate = prepareWorktreeCreate(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "worktree create"), branch: requiredOption(parsed, "branch"), base: requiredOption(parsed, "base"), path: requiredOption(parsed, "path") });
|
|
884
|
+
if (route.key === "worktree bootstrap")
|
|
885
|
+
prepared.worktreeBootstrap = prepareWorktreeBootstrap(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "worktree bootstrap") });
|
|
886
|
+
if (route.key === "worktree close")
|
|
887
|
+
prepareWorktreeClose(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "worktree close"), mode: requiredOption(parsed, "mode") });
|
|
888
|
+
if (route.key.startsWith("plan item "))
|
|
889
|
+
preparePlanItemMutation(context, planItemInput(parsed, requiredScopeProjectRoot(scopeProjectRoot, route.key)));
|
|
890
|
+
if (route.key === "lane workspace set")
|
|
891
|
+
prepareLaneWorkspace(context, laneWorkspaceInput(parsed));
|
|
892
|
+
if (route.key === "lane waiter cancel")
|
|
893
|
+
prepareLaneWaiterCancellation(context, laneWaiterCancelInput(parsed));
|
|
894
|
+
if (route.key === "merge-queue complete")
|
|
895
|
+
prepareMergeJobCompletion(context, { ...mergeJobTarget(parsed, scopeProjectRoot), summary: requiredOption(parsed, "summary") });
|
|
896
|
+
if (route.key === "merge-queue cancel")
|
|
897
|
+
prepareMergeQueueCancellation(context, mergeCancellationInput(parsed, scopeProjectRoot));
|
|
898
|
+
if (route.key === "merge-queue wait-next")
|
|
899
|
+
prepareMergeQueueWait(context, mergeWaitInput(parsed, scopeProjectRoot));
|
|
900
|
+
if (route.key === "merge-queue next" || route.key === "merge-queue claim")
|
|
901
|
+
prepareMergeClaim(context, { projectRoot: requiredOptionOrScope(parsed, scopeProjectRoot, "project-root"), workerId: requiredWorkerId(parsed), workspacePath: workspacePathOption(parsed), ...(route.key === "merge-queue claim" ? { protocolId: requiredPosition(parsed, 0, "protocol-id") } : {}) });
|
|
902
|
+
if (route.key === "merge-queue note")
|
|
903
|
+
prepareMergeJobNote(context, { protocolId: requiredPosition(parsed, 0, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue note"), workerId: requiredWorkerId(parsed), summary: requiredOption(parsed, "summary") });
|
|
904
|
+
if (route.key === "merge bundle complete")
|
|
905
|
+
prepareMergeBundleCompletion(context, { projectRoot: requiredOption(parsed, "project-root"), workspacePath: workspacePathOption(parsed), workerId: requiredWorkerId(parsed), summary: requiredOption(parsed, "summary") });
|
|
906
|
+
if (route.key === "merge bundle claim")
|
|
907
|
+
prepareMergeBundleClaim(context, { projectRoot: requiredOption(parsed, "project-root"), workspacePath: workspacePathOption(parsed), workerId: requiredWorkerId(parsed) });
|
|
908
|
+
if (route.key === "merge bundle fail")
|
|
909
|
+
prepareMergeBundleFailure(context, { projectRoot: requiredOption(parsed, "project-root"), workspacePath: workspacePathOption(parsed), workerId: requiredWorkerId(parsed), reason: requiredOption(parsed, "reason"), requeue: parseBoolean(requiredOption(parsed, "requeue"), "requeue") });
|
|
910
|
+
if (route.key === "merge-queue fail")
|
|
911
|
+
prepareMergeJobFailure(context, { ...mergeJobTarget(parsed, scopeProjectRoot), reason: requiredOption(parsed, "reason"), requeue: parseBoolean(requiredOption(parsed, "requeue"), "requeue") });
|
|
912
|
+
if (route.key.startsWith("lane lock ") && route.key !== "lane lock status") {
|
|
913
|
+
const action = requiredPosition(parsed, 0, "lane lock action");
|
|
914
|
+
prepareLaneLock(context, {
|
|
915
|
+
projectRoot: requiredOption(parsed, "project-root"), lane: requiredOption(parsed, "lane"), workerId: requiredOption(parsed, "worker-id"),
|
|
916
|
+
workspacePath: workspacePathOption(parsed), leaseToken: optionalOption(parsed, "lease-token"), reason: optionalOption(parsed, "reason"),
|
|
917
|
+
ttlSeconds: optionalNumber(parsed, "ttl"), timeoutSeconds: optionalNumber(parsed, "timeout"), pollIntervalSeconds: optionalNumber(parsed, "poll-interval")
|
|
918
|
+
}, action);
|
|
919
|
+
}
|
|
920
|
+
if (route.key === "run recovery begin")
|
|
921
|
+
prepared.recoveryPayload = prepareRecoveryPayload(requiredOption(parsed, "interruption-json"), "interruption");
|
|
922
|
+
if (route.key === "run recovery seal")
|
|
923
|
+
prepared.recoveryPayload = prepareRecoveryPayload(requiredOption(parsed, "settlement-json"), "settlement");
|
|
924
|
+
if (route.key === "run recovery capture")
|
|
925
|
+
prepared.recoveryCapture = prepareRecoveryCapture(context, recoveryCaptureInput(parsed));
|
|
926
|
+
if (route.key === "run recovery resume")
|
|
927
|
+
prepared.recoveryResume = prepareRunRecoveryResume(context, recoveryResumeInput(parsed));
|
|
928
|
+
if (route.key === "run drive answer")
|
|
929
|
+
prepared.controllerAnswer = prepareRunControllerAnswer(context, controllerAnswerInput(parsed));
|
|
930
|
+
if (route.key === "run drive launch")
|
|
931
|
+
prepared.controllerLaunch = prepareRunControllerLaunch(context, controllerLaunchInput(parsed));
|
|
932
|
+
if (route.key === "run drive context")
|
|
933
|
+
prepared.controllerContext = prepareRunControllerContext(context, controllerContextInput(parsed));
|
|
934
|
+
if (route.key === "protocol transition" || route.key === "transition")
|
|
935
|
+
prepared.protocolTransition = prepareProtocolTransition(context, protocolTransitionInput(parsed, scopeProjectRoot ?? resolveOperationProjectRoot(context, args)));
|
|
936
|
+
if (route.key === "codex home init")
|
|
937
|
+
prepared.codexHome = prepareCodexHome(context, codexHomeInput(parsed));
|
|
938
|
+
if (route.key === "codex home remove")
|
|
939
|
+
prepareCodexHomeRemoval(context, { projectRoot: requiredOption(parsed, "project-root"), profile: optionalOption(parsed, "profile"), mode: requiredOption(parsed, "mode") });
|
|
940
|
+
if (["codex hooks install", "codex hooks remove"].includes(route.key))
|
|
941
|
+
prepared.codexHooks = prepareCodexHooks(context, { projectRoot: requiredOption(parsed, "project-root"), target: optionalOption(parsed, "target"), profile: optionalOption(parsed, "profile"), yes: hasOption(parsed, "yes") });
|
|
942
|
+
if (route.key === "migration apply")
|
|
943
|
+
prepared.migrationApply = prepareMigrationApply(context, migrationApplyInput(parsed, classifyCliOperation(args, context.env)));
|
|
944
|
+
if (route.key === "migration verify")
|
|
945
|
+
prepared.migrationVerification = verifyMigrationReport(context, { file: requiredOption(parsed, "file") });
|
|
946
|
+
if (responseFile)
|
|
947
|
+
prepareResponseDestination(responseFile);
|
|
948
|
+
if (family === "dashboard") {
|
|
949
|
+
const action = dashboardAction(command);
|
|
950
|
+
prepareDashboardOptions({ format: optionalOption(parsed, "format"), viewer: optionalOption(parsed, "viewer"), open: optionalOption(parsed, "open") });
|
|
951
|
+
const target = resolveDashboardTarget(context, { action, project: optionalOption(parsed, "project"), projectRoot: optionalOption(parsed, "project-root"), protocol: optionalOption(parsed, "protocol"), all: hasOption(parsed, "all"), global: hasOption(parsed, "global") || command?.endsWith("-global") });
|
|
952
|
+
if (action === "refresh" && target.kind !== "global" && optionalOption(parsed, "output") !== undefined) {
|
|
953
|
+
throw new AppError("validation", "--output is supported only for global dashboard refresh; use dashboard render for a custom project/protocol output", 2, { parameter: "output" });
|
|
954
|
+
}
|
|
955
|
+
if (target.kind === "protocol")
|
|
956
|
+
requireProtocol(context, target.protocolId, target.project.id);
|
|
957
|
+
if (action !== "data") {
|
|
958
|
+
if (target.kind === "global" || target.kind === "all_projects") {
|
|
959
|
+
prepareGlobalDashboardRender(context, { output: optionalOption(parsed, "output"), format: optionalOption(parsed, "format") ?? (command?.endsWith("-global") ? undefined : "html") });
|
|
960
|
+
}
|
|
961
|
+
else {
|
|
962
|
+
const format = optionalOption(parsed, "format") ?? (action === "open" || hasOption(parsed, "project") ? "html" : undefined);
|
|
963
|
+
const input = { projectRoot: target.project.root, format, ...(target.kind === "protocol" ? { protocol: target.protocolId } : {}) };
|
|
964
|
+
if (action === "refresh")
|
|
965
|
+
prepareDashboardRefresh(context, { ...input, open: optionalOption(parsed, "open") });
|
|
966
|
+
else if (action === "render")
|
|
967
|
+
prepareDashboardRender(context, { ...input, output: optionalOption(parsed, "output") });
|
|
968
|
+
else if (target.kind === "protocol") {
|
|
969
|
+
if (format !== "html")
|
|
970
|
+
throw new AppError("validation", "Protocol dashboard open supports --format html", 2, { protocol: target.protocolId });
|
|
971
|
+
prepareDashboardRender(context, input);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
if (route.key === "prompt render") {
|
|
977
|
+
const input = workerPromptInput(parsed);
|
|
978
|
+
prepared.workerPrompt = { input, value: prepareWorkerPrompt(context, input) };
|
|
979
|
+
}
|
|
980
|
+
if (route.key === "run snapshot restore" || route.key === "run snapshot bootstrap restore") {
|
|
981
|
+
try {
|
|
982
|
+
const input = snapshotRestoreInput(parsed);
|
|
983
|
+
if (route.key === "run snapshot restore")
|
|
984
|
+
prepared.snapshotRestore = prepareEvalRunRestore(context, input);
|
|
985
|
+
else
|
|
986
|
+
prepared.bootstrapRestore = prepareEvalBootstrapRestore(input);
|
|
987
|
+
}
|
|
988
|
+
catch (error) {
|
|
989
|
+
return prepareError(error, { parameter: "snapshot/project-root" });
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
if (route.key === "run snapshot bootstrap create") {
|
|
993
|
+
try {
|
|
994
|
+
prepareEvalBootstrapSnapshot(context, { projectRoot: requiredOption(parsed, "project-root"), output: requiredOption(parsed, "output"), flow: optionalOption(parsed, "flow") });
|
|
995
|
+
}
|
|
996
|
+
catch (error) {
|
|
997
|
+
return prepareError(error, { parameter: "project-root/output" });
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
if (route.key === "run snapshot create") {
|
|
1001
|
+
prepareEvalRunSnapshotCreation(context, snapshotCreateInput(parsed));
|
|
1002
|
+
}
|
|
1003
|
+
if (route.key === "run start") {
|
|
1004
|
+
try {
|
|
1005
|
+
const input = runStartInput(parsed);
|
|
1006
|
+
prepared.runStart = { input, value: prepareFlowRunStart(context, input) };
|
|
1007
|
+
}
|
|
1008
|
+
catch (error) {
|
|
1009
|
+
return prepareError(error, { parameter: "run start" });
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
if (route.key === "run fork") {
|
|
1013
|
+
try {
|
|
1014
|
+
prepared.runFork = prepareRunFork(runForkInput(parsed));
|
|
1015
|
+
}
|
|
1016
|
+
catch (error) {
|
|
1017
|
+
return prepareError(error, { parameter: "from/output" });
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
if (["runtime scope fence", "runtime scope stop"].includes(route.key))
|
|
1021
|
+
prepareRuntimeScopeFence(requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id"));
|
|
1022
|
+
if (route.key === "runtime scope resume")
|
|
1023
|
+
prepareRuntimeScopeResumeRequest(context, runtimeScopeResumeInput(parsed));
|
|
1024
|
+
if (route.key === "runtime scope reconcile")
|
|
1025
|
+
prepareRuntimeScopeReconciliation(context, requiredOption(parsed, "scope-id"), Number(requiredOption(parsed, "generation")), requiredOption(parsed, "request-id"));
|
|
1026
|
+
if (route.key === "runtime scope serve")
|
|
1027
|
+
prepareRuntimeScopeServing(context, requiredOption(parsed, "scope-id"), Number(requiredOption(parsed, "generation")));
|
|
1028
|
+
if (route.key === "runtime scope control") {
|
|
1029
|
+
try {
|
|
1030
|
+
const raw = optionalOption(parsed, "manifest-json");
|
|
1031
|
+
prepared.runtimeManifest = prepareRuntimeScopeControl(requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id"), requiredOption(parsed, "mode"), raw === undefined ? undefined : prepareJsonText(raw, { file: "<manifest-json>", label: "scope manifest" }).value, context);
|
|
1032
|
+
}
|
|
1033
|
+
catch (error) {
|
|
1034
|
+
return prepareError(error, { parameter: "scope-id/request-id/manifest-json" });
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
if (family === "runtime" && command === "process")
|
|
1038
|
+
Object.assign(prepared, prepareRuntimeProcess(context, route.key, parsed));
|
|
1039
|
+
if (route.key === "runtime process start") {
|
|
1040
|
+
try {
|
|
1041
|
+
prepareRuntimeService(context, runtimeServiceInput(parsed));
|
|
1042
|
+
}
|
|
1043
|
+
catch (error) {
|
|
1044
|
+
return prepareError(error, { parameter: "runtime process start" });
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
if (family === "work" && command === "retry") {
|
|
1048
|
+
try {
|
|
1049
|
+
prepareWorkRetry(context, requiredPosition(parsed, 0, "work-id"), requiredOption(parsed, "reason"));
|
|
1050
|
+
}
|
|
1051
|
+
catch (error) {
|
|
1052
|
+
return prepareError(error, { parameter: "work-id/reason" });
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
if (family === "work" && (command === "fail" || command === "cancel")) {
|
|
1056
|
+
try {
|
|
1057
|
+
prepareWorkSettlement(context, requiredPosition(parsed, 0, "work-id"), command === "fail" ? "failed" : "cancelled", requiredOption(parsed, "reason"));
|
|
1058
|
+
}
|
|
1059
|
+
catch (error) {
|
|
1060
|
+
return prepareError(error, { parameter: "work-id/reason" });
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
if (family === "work" && (command === "deps" || command === "delete")) {
|
|
1064
|
+
try {
|
|
1065
|
+
if (command === "delete")
|
|
1066
|
+
prepareWorkDelete(context, requiredPosition(parsed, 0, "work-id"));
|
|
1067
|
+
else
|
|
1068
|
+
prepareWorkDeps(context, { workId: requiredPosition(parsed, 1, "work-id"), action: requiredPosition(parsed, 0, "action"), on: parsed.options.get("on") ?? [] });
|
|
1069
|
+
}
|
|
1070
|
+
catch (error) {
|
|
1071
|
+
return prepareError(error, { parameter: "work-id/on" });
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
if (route.key === "cleanup apply") {
|
|
1075
|
+
const planFile = requiredOption(parsed, "plan-file");
|
|
1076
|
+
try {
|
|
1077
|
+
prepared.cleanup = prepareCleanupApply(context, { projectRoot: requiredOption(parsed, "project-root"), planFile, reason: requiredOption(parsed, "reason"), force: hasOption(parsed, "force") });
|
|
1078
|
+
}
|
|
1079
|
+
catch (error) {
|
|
1080
|
+
return prepareError(error, { parameter: "plan-file", file: planFile });
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
if (route.key === "stage block" || route.key === "stage unblock") {
|
|
1084
|
+
const input = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), stage: requiredOption(parsed, "stage"), workId: requiredOption(parsed, "work") };
|
|
1085
|
+
try {
|
|
1086
|
+
if (route.key === "stage block") {
|
|
1087
|
+
const summaryFile = optionalOption(parsed, "summary-file"), summaryStdin = hasOption(parsed, "summary-stdin");
|
|
1088
|
+
if (Boolean(summaryFile) === summaryStdin)
|
|
1089
|
+
throw new AppError("usage", "stage block requires exactly one of --summary-file or --summary-stdin", 2);
|
|
1090
|
+
prepared.interactionText = summaryFile ? prepareTextFile(summaryFile, "Runtime block summary").text : await readStdin(io.stdin);
|
|
1091
|
+
prepareStageRuntimeBlock(context, { ...input, kind: requiredOption(parsed, "kind"), code: requiredOption(parsed, "code"), summary: prepared.interactionText, retryable: hasOption(parsed, "retryable") });
|
|
1092
|
+
}
|
|
1093
|
+
else
|
|
1094
|
+
prepareStageRuntimeUnblock(context, input);
|
|
1095
|
+
}
|
|
1096
|
+
catch (error) {
|
|
1097
|
+
return prepareError(error, { parameter: "stage/work/summary" });
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
if (route.key === "zcode invocation prepare") {
|
|
1101
|
+
const value = prepareJsonText(await readStdin(io.stdin), { file: "<stdin>", label: "invocation issuance" }).value;
|
|
1102
|
+
const payload = value && typeof value === "object" ? value : {};
|
|
1103
|
+
try {
|
|
1104
|
+
prepared.lifecycleIssuance = prepareLifecycleIssuance(payload.command, payload.scope);
|
|
1105
|
+
}
|
|
1106
|
+
catch (error) {
|
|
1107
|
+
if (error instanceof AppError && ["invocation_command_invalid", "invocation_scope_invalid"].includes(error.code))
|
|
1108
|
+
return prepareError(new AppError("usage", error.message, 2, { cause_code: error.code }), { parameter: "stdin" });
|
|
1109
|
+
throw error;
|
|
1110
|
+
}
|
|
1111
|
+
assertLifecycleInvocationCurrent(context, prepared.lifecycleIssuance.scope, prepared.lifecycleIssuance.command);
|
|
1112
|
+
}
|
|
1113
|
+
if (route.key === "work add-batch") {
|
|
1114
|
+
const file = requiredOption(parsed, "file");
|
|
1115
|
+
try {
|
|
1116
|
+
prepared.workBatch = prepareWorkBatch(context, { parentWorkId: requiredOption(parsed, "parent"), file,
|
|
1117
|
+
...(optionalOption(parsed, "stage") ? { stage: optionalOption(parsed, "stage") } : {}),
|
|
1118
|
+
...(optionalOption(parsed, "request-key") ? { requestKey: optionalOption(parsed, "request-key") } : {}) });
|
|
1119
|
+
}
|
|
1120
|
+
catch (error) {
|
|
1121
|
+
return prepareError(error, { parameter: "file/parent", file });
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
if (route.key === "work repair add") {
|
|
1125
|
+
const taskFile = optionalOption(parsed, "task-file"), taskStdin = hasOption(parsed, "task-stdin");
|
|
1126
|
+
if (Boolean(taskFile) === taskStdin)
|
|
1127
|
+
throw new AppError("usage", "work repair add requires exactly one of --task-file or --task-stdin", 2);
|
|
1128
|
+
if (taskStdin && io.stdin?.isTTY)
|
|
1129
|
+
throw new AppError("usage", "--task-stdin requires piped or redirected input", 2);
|
|
1130
|
+
const task = taskFile ? prepareTextFile(taskFile, "Repair task").text : await readStdin(io.stdin);
|
|
1131
|
+
prepared.workRepair = prepareCliWorkRepair(context, parsed, task, scopeProjectRoot);
|
|
1132
|
+
}
|
|
1133
|
+
if (route.key === "run config set" || route.key === "run vars set") {
|
|
1134
|
+
const input = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 1, "run-id"), key: requiredOption(parsed, "key"), value: requiredOption(parsed, "value") };
|
|
1135
|
+
try {
|
|
1136
|
+
if (route.key === "run config set")
|
|
1137
|
+
prepareFlowRunConfig(context, { ...input, reason: requiredOption(parsed, "reason") });
|
|
1138
|
+
else
|
|
1139
|
+
prepareFlowRunVariable(context, input);
|
|
1140
|
+
}
|
|
1141
|
+
catch (error) {
|
|
1142
|
+
return prepareError(error, { parameter: "run config/vars" });
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
if (route.key === "schema validate") {
|
|
1146
|
+
try {
|
|
1147
|
+
prepared.schemaValidation = dispatchSchema(context, command, parsed);
|
|
1148
|
+
}
|
|
1149
|
+
catch (error) {
|
|
1150
|
+
return prepareError(error, { parameter: "file", file: requiredOption(parsed, "file") });
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
if (route.key === "project config set") {
|
|
1154
|
+
try {
|
|
1155
|
+
prepareProjectConfigValue(requiredOption(parsed, "key"), requiredOption(parsed, "value"));
|
|
1156
|
+
}
|
|
1157
|
+
catch (error) {
|
|
1158
|
+
return prepareError(error, { parameter: "key/value" });
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
if (family === "session" && command === "register") {
|
|
1162
|
+
try {
|
|
1163
|
+
prepared.sessionPayload = prepareFlowSessionPayload({ payloadBase64: optionalOption(parsed, "payload-base64"), payloadFile: optionalOption(parsed, "payload-file"), payloadJson: optionalOption(parsed, "payload-json") });
|
|
1164
|
+
}
|
|
1165
|
+
catch (error) {
|
|
1166
|
+
return prepareError(error, { parameter: "session payload" });
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
const run = (projectRoot, runId) => {
|
|
1170
|
+
try {
|
|
1171
|
+
const project = requireProjectByRoot(context, resolveProjectRoot(projectRoot));
|
|
1172
|
+
const row = context.db.get("SELECT run_root, workspace_root, index_json FROM runs WHERE project_id = ? AND id = ?", [project.id, runId]);
|
|
1173
|
+
if (!row)
|
|
1174
|
+
throw new AppError("not_found", "RUN is not registered", 2, { run_id: runId });
|
|
1175
|
+
if (!row.run_root)
|
|
1176
|
+
throw new AppError("runtime_missing", "RUN artifact root is unavailable", 1, { run_id: runId });
|
|
1177
|
+
return { runRoot: row.run_root, workspaceRoot: row.workspace_root, indexJson: row.index_json };
|
|
1178
|
+
}
|
|
1179
|
+
catch (error) {
|
|
1180
|
+
return prepareError(error, { parameter: "run-id", run_id: runId });
|
|
1181
|
+
}
|
|
1182
|
+
};
|
|
1183
|
+
const json = (file, stdin, label, schema) => {
|
|
1184
|
+
if (stdin) {
|
|
1185
|
+
// The caller awaits this helper; the synchronous facade below is only
|
|
1186
|
+
// used for file inputs. Keep stdin handling in the stage branch.
|
|
1187
|
+
throw new AppError("usage", `${label} stdin preparation must be awaited`, 2);
|
|
1188
|
+
}
|
|
1189
|
+
if (!file)
|
|
1190
|
+
throw new AppError("usage", `${label} input is required`, 2);
|
|
1191
|
+
return prepareJsonSchema(file, label, schema);
|
|
1192
|
+
};
|
|
1193
|
+
if (family === "stage" && command === "start") {
|
|
1194
|
+
const stage = requiredOption(parsed, "stage");
|
|
1195
|
+
if (stage !== "specify" && ["bootstrap", "intake-file", "intake-stdin"].some(option => hasOption(parsed, option)))
|
|
1196
|
+
throw new AppError("usage", "--bootstrap and intake inputs are only supported for SPECIFY", 2);
|
|
1197
|
+
requiredOption(parsed, "project-root");
|
|
1198
|
+
if (!hasOption(parsed, "bootstrap"))
|
|
1199
|
+
requiredPosition(parsed, 0, "run-id");
|
|
1200
|
+
if (hasOption(parsed, "session-id"))
|
|
1201
|
+
throw new AppError("usage", "--session-id is managed by the harness lifecycle adapter and is not accepted", 2);
|
|
1202
|
+
const contextFile = optionalOption(parsed, "context-file"), contextSha256 = optionalOption(parsed, "context-sha256");
|
|
1203
|
+
if (Boolean(contextFile) !== Boolean(contextSha256))
|
|
1204
|
+
throw new AppError("usage", "stage start requires --context-file together with --context-sha256", 2);
|
|
1205
|
+
if ([hasOption(parsed, "intake-file"), hasOption(parsed, "intake-stdin"), Boolean(contextFile)].filter(Boolean).length > 1)
|
|
1206
|
+
throw new AppError("usage", "Use exactly one of --intake-file, --intake-stdin or --context-file", 2);
|
|
1207
|
+
if (!hasOption(parsed, "bootstrap")) {
|
|
1208
|
+
const bundle = readFlowRunExecutionBundle(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") });
|
|
1209
|
+
if (bundle)
|
|
1210
|
+
prepared.stageBindingBundle = { stage, bundle, ...(contextSha256 ? { contextSha256 } : {}) };
|
|
1211
|
+
if (bundle?.descriptor.descriptor.id === "mb-init") {
|
|
1212
|
+
bundle.descriptor.stage(stage);
|
|
1213
|
+
if (hasOption(parsed, "dir"))
|
|
1214
|
+
throw new AppError("usage", "Memory stage layout belongs to its frozen descriptor", 2);
|
|
1215
|
+
const loaded = contextFile && contextSha256 ? loadExternalStageContext({ file: contextFile, expectedSha256: contextSha256, stage }) : undefined;
|
|
1216
|
+
if (loaded)
|
|
1217
|
+
prepared.stageStart = { externalContext: loaded };
|
|
1218
|
+
prepared.memoryStage = { operation: "start", bytes: loaded ? Buffer.from(loaded.bytes) : Buffer.alloc(0) };
|
|
1219
|
+
return prepared;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
if (contextFile && contextSha256) {
|
|
1223
|
+
try {
|
|
1224
|
+
prepared.stageStart = { externalContext: loadExternalStageContext({ file: contextFile, expectedSha256: contextSha256, stage }) };
|
|
1225
|
+
}
|
|
1226
|
+
catch (error) {
|
|
1227
|
+
return prepareError(error, { file: contextFile, parameter: "context-file" });
|
|
1228
|
+
}
|
|
1229
|
+
if (!hasOption(parsed, "bootstrap")) {
|
|
1230
|
+
const { runRoot } = run(requiredOption(parsed, "project-root"), requiredPosition(parsed, 0, "run-id"));
|
|
1231
|
+
prepareExternalStageContext({ stageRoot: path.join(runRoot, vnextStageDirectory(stage)), loaded: prepared.stageStart.externalContext });
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
if (stage === "specify") {
|
|
1235
|
+
const intakeFile = optionalOption(parsed, "intake-file");
|
|
1236
|
+
if (intakeFile)
|
|
1237
|
+
prepared.stageStart = { intakeMarkdown: prepareTextFile(intakeFile, "SPECIFY intake").text };
|
|
1238
|
+
else if (hasOption(parsed, "intake-stdin"))
|
|
1239
|
+
prepared.stageStart = { intakeMarkdown: await readStdin(io.stdin) };
|
|
1240
|
+
else if (prepared.stageStart?.externalContext)
|
|
1241
|
+
prepared.stageStart.intakeMarkdown = runnerIntake(prepared.stageStart.externalContext);
|
|
1242
|
+
const projectRoot = requiredOption(parsed, "project-root");
|
|
1243
|
+
const bootstrap = hasOption(parsed, "bootstrap");
|
|
1244
|
+
if (isVnextSpecifyFlow(projectRoot) && (bootstrap || isVnextSpecifyRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") }))) {
|
|
1245
|
+
prepared.specifyStart = prepareVnextSpecifyStart(context, { projectRoot, ...(prepared.stageStart?.intakeMarkdown !== undefined ? { intakeMarkdown: prepared.stageStart.intakeMarkdown } : {}), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}), slug: optionalOption(parsed, "subject"), ...(!bootstrap ? { runId: requiredPosition(parsed, 0, "run-id") } : {}) });
|
|
1246
|
+
}
|
|
1247
|
+
else if (bootstrap) {
|
|
1248
|
+
if (hasOption(parsed, "intake-stdin") || contextFile)
|
|
1249
|
+
throw new AppError("usage", "Legacy SPECIFY bootstrap supports --intake-file, not stdin or runner context", 2);
|
|
1250
|
+
prepared.legacyStageStart = prepareLegacyStageStart(context, { projectRoot, stage, bootstrap: { subject: requiredOption(parsed, "subject"), ...(intakeFile ? { intakeFile } : {}) }, ...(optionalOption(parsed, "dir") ? { dir: optionalOption(parsed, "dir") } : {}) }, prepared.stageStart?.intakeMarkdown);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
if (stage === "merge" && isVnextMergeRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1254
|
+
prepared.mergeStart = prepareVnextMergeStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1255
|
+
}
|
|
1256
|
+
if (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1257
|
+
prepared.protocolizeStart = prepareVnextProtocolizeStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1258
|
+
}
|
|
1259
|
+
if (stage === "plan" && isVnextPlanRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1260
|
+
prepared.planStart = prepareVnextPlanStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1261
|
+
}
|
|
1262
|
+
if (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1263
|
+
prepared.planReviewStart = prepareVnextPlanReviewStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1264
|
+
}
|
|
1265
|
+
if (stage === "code" && isVnextPlanRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1266
|
+
prepared.codeStart = prepareVnextCodeStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1267
|
+
}
|
|
1268
|
+
if (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1269
|
+
prepared.codeReviewStart = prepareVnextCodeReviewStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1270
|
+
}
|
|
1271
|
+
if (![prepared.specifyStart, prepared.protocolizeStart, prepared.planStart, prepared.planReviewStart, prepared.codeStart, prepared.codeReviewStart, prepared.mergeStart, prepared.legacyStageStart].some(Boolean)) {
|
|
1272
|
+
if (prepared.stageStart?.externalContext)
|
|
1273
|
+
throw new AppError("usage", "--context-file is currently supported only by vNext flow stages", 2);
|
|
1274
|
+
prepared.legacyStageStart = prepareLegacyStageStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), stage, ...(optionalOption(parsed, "dir") ? { dir: optionalOption(parsed, "dir") } : {}) });
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
if (family === "stage" && (command === "pause" || command === "resume")) {
|
|
1278
|
+
requiredPosition(parsed, 0, "run-id");
|
|
1279
|
+
requiredOption(parsed, "project-root");
|
|
1280
|
+
requiredOption(parsed, "stage");
|
|
1281
|
+
requiredOption(parsed, "work");
|
|
1282
|
+
const fileParameter = command === "pause" ? "question-file" : "answer-file";
|
|
1283
|
+
const stdinParameter = command === "pause" ? "question-stdin" : "answer-stdin";
|
|
1284
|
+
const inputFile = optionalOption(parsed, fileParameter);
|
|
1285
|
+
const fromStdin = hasOption(parsed, stdinParameter);
|
|
1286
|
+
if (Boolean(inputFile) === fromStdin)
|
|
1287
|
+
throw new AppError("usage", `stage ${command} requires exactly one of --${fileParameter} or --${stdinParameter}`, 2);
|
|
1288
|
+
if (fromStdin && io.stdin?.isTTY)
|
|
1289
|
+
throw new AppError("usage", `--${stdinParameter} requires piped or redirected input`, 2);
|
|
1290
|
+
prepared.interactionText = inputFile ? prepareTextFile(inputFile, command === "pause" ? "User question" : "User answer").text : await readStdin(io.stdin);
|
|
1291
|
+
if (!prepared.interactionText.trim())
|
|
1292
|
+
return prepareError(new AppError("validation", `stage ${command} requires non-empty input`, 2), { parameter: inputFile ? fileParameter : stdinParameter });
|
|
1293
|
+
const input = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), stage: requiredOption(parsed, "stage"), workId: requiredOption(parsed, "work") };
|
|
1294
|
+
try {
|
|
1295
|
+
if (command === "pause")
|
|
1296
|
+
prepareStagePause(context, { ...input, question: prepared.interactionText });
|
|
1297
|
+
else
|
|
1298
|
+
prepared.resumeEvidence = prepareStageResume(context, { ...input, answer: prepared.interactionText }).evidence;
|
|
1299
|
+
}
|
|
1300
|
+
catch (error) {
|
|
1301
|
+
return prepareError(error, { parameter: "stage/work" });
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
if (family === "work" && command === "finish") {
|
|
1305
|
+
const retryReceiptId = optionalOption(parsed, "retry-check"), retryReason = prepared.retryReason;
|
|
1306
|
+
if (Boolean(retryReceiptId) !== Boolean(retryReason))
|
|
1307
|
+
throw new AppError("usage", "--retry-check and --reason must be supplied together", 2);
|
|
1308
|
+
const resultFile = optionalOption(parsed, "result-file");
|
|
1309
|
+
const resultStdin = hasOption(parsed, "result-stdin");
|
|
1310
|
+
if (Boolean(resultFile) === resultStdin)
|
|
1311
|
+
throw new AppError("usage", "work finish requires exactly one of --result-file or --result-stdin", 2);
|
|
1312
|
+
const maxBytes = workResultByteLimit(context, requiredPosition(parsed, 0, "work-id"));
|
|
1313
|
+
prepared.workFinish = { result: resultFile ? prepareTextFile(resultFile, "Work result", maxBytes).text : await readStdin(io.stdin, maxBytes) };
|
|
1314
|
+
prepareWorkResult(context, requiredPosition(parsed, 0, "work-id"), prepared.workFinish.result);
|
|
1315
|
+
}
|
|
1316
|
+
if (family === "stage" && command === "fanout" && requiredPosition(parsed, 0, "fanout action") === "reconcile") {
|
|
1317
|
+
prepared.observations = prepareNativeObservations(prepareJsonFile(requiredOption(parsed, "observations-file"), "native observations").value);
|
|
1318
|
+
}
|
|
1319
|
+
if (family === "stage" && command === "native" && requiredPosition(parsed, 0, "native action") === "observe") {
|
|
1320
|
+
prepared.observations = prepareNativeObservations(prepareJsonFile(requiredOption(parsed, "observations-file"), "native observations").value);
|
|
1321
|
+
}
|
|
1322
|
+
if (family === "memory" && (["compose", "corrections"].includes(command ?? "") && parsed.positional[0] === "register"
|
|
1323
|
+
|| command === "repair" && parsed.positional[0] === "publication-reserve")) {
|
|
1324
|
+
const bytes = prepareFileBytes(requiredOption(parsed, "result-file"), `Memory ${command} result`, 1024 * 1024).bytes;
|
|
1325
|
+
if (!bytes.length || bytes.length > 1024 * 1024)
|
|
1326
|
+
throw new AppError("usage", "Memory operation result exceeds its byte bound", 2);
|
|
1327
|
+
prepared.memoryOperationBytes = bytes;
|
|
1328
|
+
}
|
|
1329
|
+
if (family !== "stage" || command !== "finish")
|
|
1330
|
+
return prepared;
|
|
1331
|
+
const projectRoot = requiredOption(parsed, "project-root");
|
|
1332
|
+
const runId = requiredPosition(parsed, 0, "run-id");
|
|
1333
|
+
const stage = requiredOption(parsed, "stage");
|
|
1334
|
+
if (hasOption(parsed, "status"))
|
|
1335
|
+
throw new AppError("usage", "Stage status belongs in the semantic input file", 2);
|
|
1336
|
+
if (["result-file", "semantic-file", "data", "result-stdin"].filter(key => hasOption(parsed, key)).length > 1)
|
|
1337
|
+
throw new AppError("usage", "Use exactly one result input: --result-file, --semantic-file, --data or --result-stdin", 2);
|
|
1338
|
+
if (Boolean(optionalOption(parsed, "retry-check")) !== Boolean(prepared.retryReason))
|
|
1339
|
+
throw new AppError("usage", "--retry-check requires recovery evidence", 2);
|
|
1340
|
+
const resultStdin = hasOption(parsed, "result-stdin");
|
|
1341
|
+
const semanticFile = optionalOption(parsed, "semantic-file") ?? optionalOption(parsed, "data");
|
|
1342
|
+
const resultFile = optionalOption(parsed, "result-file") ?? semanticFile;
|
|
1343
|
+
const runtime = run(projectRoot, runId);
|
|
1344
|
+
if (JSON.parse(runtime.indexJson).execution_bundle?.flow_id === "mb-init") {
|
|
1345
|
+
const bundle = readFlowRunExecutionBundle(context, { projectRoot, runId });
|
|
1346
|
+
if (!bundle || bundle.descriptor.descriptor.id !== "mb-init")
|
|
1347
|
+
throw new AppError("cli_input_unprepared", "Memory RUN bundle is unavailable", 1);
|
|
1348
|
+
bundle.descriptor.stage(stage);
|
|
1349
|
+
if (["dir", "outcome", "decision-file", "verification-file", "retry-check", "reason", "reason-file", "request", "work"].some(option => hasOption(parsed, option)))
|
|
1350
|
+
throw new AppError("usage", "Memory stage finish requires its registered JSON result, not legacy result options", 2);
|
|
1351
|
+
if (Boolean(resultFile) === resultStdin)
|
|
1352
|
+
throw new AppError("usage", "Memory stage finish requires exactly one result input", 2);
|
|
1353
|
+
const bytes = resultStdin ? Buffer.from(await readStdin(io.stdin, 1024 * 1024)) : prepareFileBytes(resultFile, "Memory stage result", 1024 * 1024).bytes;
|
|
1354
|
+
if (!bytes.length || bytes.length > 1024 * 1024)
|
|
1355
|
+
throw new AppError("usage", "Memory stage result exceeds its byte bound", 2);
|
|
1356
|
+
prepared.memoryStage = { operation: "finish", bytes };
|
|
1357
|
+
return prepared;
|
|
1358
|
+
}
|
|
1359
|
+
const terminalReplay = (name) => {
|
|
1360
|
+
let index;
|
|
1361
|
+
try {
|
|
1362
|
+
index = JSON.parse(runtime.indexJson);
|
|
1363
|
+
}
|
|
1364
|
+
catch (error) {
|
|
1365
|
+
throw new AppError("runtime_artifact_invalid", "RUN index is not valid retained JSON", 1, { run_id: runId, cause: error instanceof Error ? error.message : String(error) });
|
|
1366
|
+
}
|
|
1367
|
+
return index.stage_runs?.some(entry => entry.stage === name && entry.status === "done") === true
|
|
1368
|
+
&& fs.existsSync(path.join(runtime.runRoot, vnextStageDirectory(name), "stage-report.json"));
|
|
1369
|
+
};
|
|
1370
|
+
const acceptStageInputs = (allowed) => {
|
|
1371
|
+
const options = allowed.split(" ");
|
|
1372
|
+
for (const name of ["dir", "semantic-file", "data", "result-file", "result-stdin", "outcome", "decision-file", "verification-file", "retry-check", "reason", "reason-file", "request", "work"]) {
|
|
1373
|
+
if (hasOption(parsed, name) && !options.includes(name === "reason-file" ? "reason" : name))
|
|
1374
|
+
throw new AppError("usage", `stage finish ${stage} does not accept --${name} for this flow`, 2, { parameter: name });
|
|
1375
|
+
}
|
|
1376
|
+
};
|
|
1377
|
+
const fromStdin = async (label, schema) => {
|
|
1378
|
+
const value = prepareJsonText(await readStdin(io.stdin), { file: "<stdin>", label });
|
|
1379
|
+
try {
|
|
1380
|
+
validateSchema({ ...schema, file: value.file, data: value.value });
|
|
1381
|
+
}
|
|
1382
|
+
catch (error) {
|
|
1383
|
+
return prepareError(error, { file: value.file, label });
|
|
1384
|
+
}
|
|
1385
|
+
return value;
|
|
1386
|
+
};
|
|
1387
|
+
if (stage === "specify" && isVnextSpecifyRun(context, { projectRoot, runId })) {
|
|
1388
|
+
acceptStageInputs("result-file semantic-file data result-stdin outcome");
|
|
1389
|
+
const outcome = optionalOption(parsed, "outcome");
|
|
1390
|
+
if (!outcome || !["specified", "failed", "cancelled"].includes(outcome))
|
|
1391
|
+
throw new AppError("usage", "vNext SPECIFY requires --outcome specified|failed|cancelled; use stage pause for a user question", 2);
|
|
1392
|
+
if (Boolean(resultFile) === resultStdin)
|
|
1393
|
+
throw new AppError("usage", "vNext SPECIFY finish requires exactly one result input", 2);
|
|
1394
|
+
const schema = { schemaName: "vnext-specify", projectRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot };
|
|
1395
|
+
const input = resultStdin ? await fromStdin("SPECIFY result", schema) : json(resultFile, false, "SPECIFY result", schema);
|
|
1396
|
+
prepared.stageFinish = { value: input.value, text: input.text };
|
|
1397
|
+
}
|
|
1398
|
+
else if (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId })) {
|
|
1399
|
+
acceptStageInputs("result-file semantic-file data");
|
|
1400
|
+
if (!resultFile || resultStdin || hasOption(parsed, "outcome"))
|
|
1401
|
+
throw new AppError("usage", "vNext PROTOCOLIZE requires exactly --result-file", 2);
|
|
1402
|
+
let absolute;
|
|
1403
|
+
try {
|
|
1404
|
+
absolute = prepareProtocolizeResultPath(runtime.runRoot, resultFile);
|
|
1405
|
+
}
|
|
1406
|
+
catch (error) {
|
|
1407
|
+
return prepareError(error, { file: resultFile });
|
|
1408
|
+
}
|
|
1409
|
+
const input = json(absolute, false, "PROTOCOLIZE result", { schemaName: "vnext-protocolize-result", projectRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot });
|
|
1410
|
+
prepared.stageFinish = { value: input.value, text: input.text };
|
|
1411
|
+
}
|
|
1412
|
+
else if (stage === "plan" && isVnextPlanRun(context, { projectRoot, runId })) {
|
|
1413
|
+
acceptStageInputs("");
|
|
1414
|
+
if (hasOption(parsed, "code-work-batch-file"))
|
|
1415
|
+
throw new AppError("usage", "vNext PLAN derives its CODE batch from accepted semantic plans; do not supply a batch file", 2);
|
|
1416
|
+
try {
|
|
1417
|
+
prepared.planFinish = prepareVnextPlanFinish(context, { projectRoot, runId });
|
|
1418
|
+
}
|
|
1419
|
+
catch (error) {
|
|
1420
|
+
return prepareError(error, { parameter: "PLAN artifacts" });
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
else if (stage === "merge" && isVnextMergeRun(context, { projectRoot, runId })) {
|
|
1424
|
+
acceptStageInputs("request work retry-check reason");
|
|
1425
|
+
if (resultStdin || resultFile || hasOption(parsed, "outcome"))
|
|
1426
|
+
throw new AppError("usage", "vNext MERGE uses the Work result path returned by stage start", 2);
|
|
1427
|
+
requiredOption(parsed, "request");
|
|
1428
|
+
requiredOption(parsed, "work");
|
|
1429
|
+
try {
|
|
1430
|
+
prepared.mergeFinish = prepareVnextMergeFinish(context, { projectRoot, runId, requestId: requiredOption(parsed, "request"), workId: requiredOption(parsed, "work"), ...(optionalOption(parsed, "retry-check") ? { retryCheckId: optionalOption(parsed, "retry-check"), retryReason: prepared.retryReason } : {}) });
|
|
1431
|
+
}
|
|
1432
|
+
catch (error) {
|
|
1433
|
+
return prepareError(error, { parameter: "MERGE finish" });
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
else if (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId })) {
|
|
1437
|
+
acceptStageInputs("decision-file");
|
|
1438
|
+
if (resultStdin || hasOption(parsed, "outcome"))
|
|
1439
|
+
throw new AppError("usage", "vNext PLAN-REVIEW uses --decision-file", 2);
|
|
1440
|
+
const decision = optionalOption(parsed, "decision-file") ?? path.join(runtime.runRoot, "04-plan-review", "decision.json");
|
|
1441
|
+
const input = json(decision, false, "PLAN-REVIEW decision", { schemaName: "plan-review-decision", projectRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot });
|
|
1442
|
+
prepared.stageFinish = { value: input.value, text: input.text };
|
|
1443
|
+
}
|
|
1444
|
+
else if (stage === "code" && isVnextPlanRun(context, { projectRoot, runId })) {
|
|
1445
|
+
acceptStageInputs("verification-file retry-check reason");
|
|
1446
|
+
if (semanticFile || resultFile || resultStdin || hasOption(parsed, "outcome"))
|
|
1447
|
+
throw new AppError("usage", "vNext CODE finish accepts only --verification-file", 2);
|
|
1448
|
+
const verification = optionalOption(parsed, "verification-file");
|
|
1449
|
+
if (!verification)
|
|
1450
|
+
throw new AppError("usage", "vNext CODE finish requires --verification-file", 2);
|
|
1451
|
+
if (!terminalReplay(stage)) {
|
|
1452
|
+
const input = json(verification, false, "CODE verification", { schemaName: "code-verification", projectRoot: runtime.workspaceRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot });
|
|
1453
|
+
prepared.stageFinish = { value: input.value, text: input.text };
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
else if (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId })) {
|
|
1457
|
+
acceptStageInputs("decision-file retry-check reason");
|
|
1458
|
+
if (resultStdin || semanticFile || resultFile || hasOption(parsed, "outcome"))
|
|
1459
|
+
throw new AppError("usage", "vNext CODE-REVIEW uses --decision-file", 2);
|
|
1460
|
+
const decision = optionalOption(parsed, "decision-file") ?? path.join(runtime.runRoot, "06-code-review", "decision.json");
|
|
1461
|
+
if (!terminalReplay(stage)) {
|
|
1462
|
+
const input = prepareCodeReviewDecision(runtime.runRoot, optionalOption(parsed, "decision-file"));
|
|
1463
|
+
try {
|
|
1464
|
+
validateSchema({ schemaName: "code-review-decision", file: decision, data: input.value, projectRoot: runtime.workspaceRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot });
|
|
1465
|
+
}
|
|
1466
|
+
catch (error) {
|
|
1467
|
+
return prepareError(error, { file: decision, label: "CODE-REVIEW decision" });
|
|
1468
|
+
}
|
|
1469
|
+
prepared.stageFinish = input;
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
else {
|
|
1473
|
+
acceptStageInputs("dir semantic-file data");
|
|
1474
|
+
if (hasOption(parsed, "outcome") || hasOption(parsed, "result-file") || resultStdin)
|
|
1475
|
+
throw new AppError("usage", "--outcome, --result-file and --result-stdin are only available for vNext SPECIFY", 2);
|
|
1476
|
+
prepared.legacyStageFinish = prepareStageFinish(context, { projectRoot, runId, stage, ...(optionalOption(parsed, "dir") ? { dir: optionalOption(parsed, "dir") } : {}), ...(semanticFile ? { semanticFile } : {}) });
|
|
1477
|
+
}
|
|
1478
|
+
return prepared;
|
|
1479
|
+
}
|
|
1480
|
+
async function dispatch(args, context, io, scopeProjectRoot = null, classification = classifyCliOperation(args, context.env), output = parseOutputOptions(args), prepared = {}) {
|
|
1481
|
+
if (!prepared.route)
|
|
1482
|
+
throw new AppError("command_not_prepared", "Command dispatch requires prepared arguments", 1);
|
|
1483
|
+
const { family, command, parsed } = prepared.route;
|
|
1484
|
+
if (family === "zcode" && command === "invocation" && parsed.positional[0] === "prepare") {
|
|
1485
|
+
const payload = prepared.lifecycleIssuance;
|
|
1486
|
+
if (!payload)
|
|
1487
|
+
throw new AppError("command_not_prepared", "Invocation issuance requires prepared input", 1);
|
|
1488
|
+
assertLifecycleInvocationCurrent(context, payload.scope, payload.command);
|
|
1489
|
+
return { ok: true, ...issueLifecycleInvocation(context, payload.command, payload.scope) };
|
|
1490
|
+
}
|
|
1491
|
+
if (family === "storage" && command === "migrate-writer") {
|
|
1492
|
+
if (parsed.positional.length || [...parsed.options.keys()].some(key => !["database", "backup", "offline"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
1493
|
+
throw new AppError("usage", "Usage: dd-flow storage migrate-writer --database ABSOLUTE_DB --backup NEW_ABSOLUTE_FILE --offline", 2);
|
|
1494
|
+
if (parsed.options.get("offline")?.[0] !== flagValue)
|
|
1495
|
+
throw new AppError("usage", "Writer migration requires --offline after draining all local and external runtime owners", 2);
|
|
1496
|
+
return { ok: true, ...migrateStoreWriter(requiredOption(parsed, "database"), requiredOption(parsed, "backup")) };
|
|
1497
|
+
}
|
|
506
1498
|
if (family === "engine") {
|
|
507
|
-
return dispatchEngine(context, command, parsed, classification);
|
|
1499
|
+
return dispatchEngine(context, command, parsed, classification, prepared);
|
|
508
1500
|
}
|
|
509
1501
|
if (family === "status") {
|
|
510
|
-
const rootParsed = parseArgs([command ?? "", ...rest]);
|
|
511
1502
|
return getRuntimeStatus(context, {
|
|
512
|
-
projectRoot: optionalOption(
|
|
513
|
-
checkRegistry: hasOption(
|
|
1503
|
+
projectRoot: optionalOption(parsed, "project-root") ?? optionalOption(parsed, "root"),
|
|
1504
|
+
checkRegistry: hasOption(parsed, "check-registry")
|
|
514
1505
|
});
|
|
515
1506
|
}
|
|
516
1507
|
if (family === "version") {
|
|
517
1508
|
return getCliVersionReport();
|
|
518
1509
|
}
|
|
519
1510
|
if (family === "runtime") {
|
|
520
|
-
return await dispatchRuntime(context, command, parsed);
|
|
1511
|
+
return await dispatchRuntime(context, command, parsed, commandProgress(io, output), prepared);
|
|
521
1512
|
}
|
|
522
1513
|
if (family === "canon") {
|
|
523
|
-
return dispatchCanon(context, command, parsed);
|
|
1514
|
+
return dispatchCanon(context, command, parsed, prepared);
|
|
524
1515
|
}
|
|
525
1516
|
if (family === "project" && command === "register") {
|
|
526
1517
|
return registerProject(context, { root: requiredOption(parsed, "root") });
|
|
@@ -597,16 +1588,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
597
1588
|
return readyForMerge(context, { protocolId, projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "protocol ready-for-merge") });
|
|
598
1589
|
}
|
|
599
1590
|
if (family === "protocol" && command === "transition") {
|
|
600
|
-
|
|
601
|
-
const reason = optionalOption(parsed, "reason");
|
|
602
|
-
return transitionProtocol(context, {
|
|
603
|
-
protocolId,
|
|
604
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "protocol transition"),
|
|
605
|
-
to: requiredOption(parsed, "to"),
|
|
606
|
-
jsonFile: requiredOptionAlias(parsed, ["payload-file", "json-file"]),
|
|
607
|
-
force: hasOption(parsed, "force"),
|
|
608
|
-
...(reason ? { reason } : {})
|
|
609
|
-
});
|
|
1591
|
+
return transitionProtocol(context, protocolTransitionInput(parsed, scopeProjectRoot), prepared.protocolTransition);
|
|
610
1592
|
}
|
|
611
1593
|
if (family === "protocol" && command === "sync-from-run") {
|
|
612
1594
|
const protocolId = requiredPosition(parsed, 0, "protocol-id");
|
|
@@ -631,17 +1613,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
631
1613
|
});
|
|
632
1614
|
}
|
|
633
1615
|
if (family === "transition") {
|
|
634
|
-
|
|
635
|
-
const protocolId = requiredPosition(transitionParsed, 0, "protocol-id");
|
|
636
|
-
const reason = optionalOption(transitionParsed, "reason");
|
|
637
|
-
return transitionProtocol(context, {
|
|
638
|
-
protocolId,
|
|
639
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "transition"),
|
|
640
|
-
to: requiredOption(transitionParsed, "to"),
|
|
641
|
-
jsonFile: requiredOptionAlias(transitionParsed, ["payload-file", "json-file"]),
|
|
642
|
-
force: hasOption(transitionParsed, "force"),
|
|
643
|
-
...(reason ? { reason } : {})
|
|
644
|
-
});
|
|
1616
|
+
return transitionProtocol(context, protocolTransitionInput(parsed, scopeProjectRoot), prepared.protocolTransition);
|
|
645
1617
|
}
|
|
646
1618
|
if (family === "plan" && command === "status") {
|
|
647
1619
|
const protocolId = requiredPosition(parsed, 0, "protocol-id");
|
|
@@ -655,17 +1627,25 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
655
1627
|
}
|
|
656
1628
|
if (family === "stage" && command === "fanout") {
|
|
657
1629
|
const action = requiredPosition(parsed, 0, "fanout action");
|
|
658
|
-
const runId = requiredPosition(parsed, 1, "run-id");
|
|
659
1630
|
const stage = requiredOption(parsed, "stage");
|
|
660
1631
|
const projectRoot = requiredScopeProjectRoot(scopeProjectRoot, "stage fanout");
|
|
1632
|
+
const project = requireProjectByRoot(context, resolveProjectRoot(projectRoot));
|
|
1633
|
+
const runId = resolveRun(context, project.id, requiredPosition(parsed, 1, "run-id")).id;
|
|
661
1634
|
if (action === "status")
|
|
662
1635
|
return getVnextFanoutStatus(context, { projectRoot, runId, stage });
|
|
1636
|
+
if (action === "reconcile")
|
|
1637
|
+
return reconcileVnextFanout(context, { projectRoot, runId, stage, observations: prepared.observations ?? prepareJsonFile(requiredOption(parsed, "observations-file"), "native observations").value });
|
|
663
1638
|
if (action === "dispatch") {
|
|
664
1639
|
if (stage === "plan-review")
|
|
665
1640
|
return dispatchVnextPlanReview(context, { projectRoot, runId });
|
|
666
1641
|
return getVnextFanoutStatus(context, { projectRoot, runId, stage });
|
|
667
1642
|
}
|
|
668
|
-
throw new AppError("usage", "Use: dd-flow stage fanout <status|dispatch> <RUN> --stage <stage> --project-root <path>", 2);
|
|
1643
|
+
throw new AppError("usage", "Use: dd-flow stage fanout <status|dispatch|reconcile> <RUN> --stage <stage> --project-root <path>; reconcile requires --observations-file <native-observations.json>", 2);
|
|
1644
|
+
}
|
|
1645
|
+
if (family === "stage" && command === "native") {
|
|
1646
|
+
if (requiredPosition(parsed, 0, "native action") !== "observe")
|
|
1647
|
+
throw new AppError("usage", "Use: dd-flow stage native observe <RUN> --stage <stage> --observations-file <native-observations.json> --project-root <path>", 2);
|
|
1648
|
+
return observeVnextNativeChildren(context, { projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "stage native observe"), runId: requiredPosition(parsed, 1, "run-id"), stage: requiredOption(parsed, "stage"), observations: prepared.observations ?? prepareJsonFile(requiredOption(parsed, "observations-file"), "native observations").value });
|
|
669
1649
|
}
|
|
670
1650
|
if (family === "run" && command === "capacity") {
|
|
671
1651
|
if (requiredPosition(parsed, 0, "capacity action") !== "record")
|
|
@@ -677,16 +1657,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
677
1657
|
return dispatchPlanItem(context, parsed, requiredScopeProjectRoot(scopeProjectRoot, "plan item"));
|
|
678
1658
|
}
|
|
679
1659
|
if (family === "prompt" && command === "render") {
|
|
680
|
-
|
|
681
|
-
const taskFile = optionalOption(parsed, "task-file");
|
|
682
|
-
return renderWorkerPrompt(context, {
|
|
683
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
684
|
-
runId: requiredOption(parsed, "run"),
|
|
685
|
-
stage: requiredOption(parsed, "stage"),
|
|
686
|
-
profile: requiredOption(parsed, "profile"),
|
|
687
|
-
...(taskFile ? { taskFile } : { planItemId: requiredOption(parsed, "plan-item") }),
|
|
688
|
-
...(workspaceRoot ? { workspaceRoot } : {})
|
|
689
|
-
});
|
|
1660
|
+
return renderWorkerPrompt(context, prepared.workerPrompt?.input ?? workerPromptInput(parsed), prepared.workerPrompt?.value);
|
|
690
1661
|
}
|
|
691
1662
|
if (family === "work" && command === "status") {
|
|
692
1663
|
return getVnextWorkStatus(context, {
|
|
@@ -695,7 +1666,9 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
695
1666
|
});
|
|
696
1667
|
}
|
|
697
1668
|
if (family === "work" && command === "add-batch") {
|
|
698
|
-
return addWorkBatch(context, { parentWorkId: requiredOption(parsed, "parent"), file: requiredOption(parsed, "file")
|
|
1669
|
+
return addWorkBatch(context, { parentWorkId: requiredOption(parsed, "parent"), file: requiredOption(parsed, "file"), prepared: prepared.workBatch,
|
|
1670
|
+
...(optionalOption(parsed, "stage") ? { stage: optionalOption(parsed, "stage") } : {}),
|
|
1671
|
+
...(optionalOption(parsed, "request-key") ? { requestKey: optionalOption(parsed, "request-key") } : {}) });
|
|
699
1672
|
}
|
|
700
1673
|
if (family === "work" && command === "ls") {
|
|
701
1674
|
return listWorks(context, { ...(optionalOption(parsed, "run") ? { runId: optionalOption(parsed, "run") } : {}), ...(optionalOption(parsed, "parent") ? { parentWorkId: optionalOption(parsed, "parent") } : {}), ...(optionalOption(parsed, "status") ? { status: optionalOption(parsed, "status") } : {}), ...(hasOption(parsed, "ready") ? { ready: true } : {}), ...(hasOption(parsed, "include-results") ? { includeResults: true } : {}), ...(optionalOption(parsed, "limit") ? { limit: Number(optionalOption(parsed, "limit")) } : {}) });
|
|
@@ -703,48 +1676,9 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
703
1676
|
if (family === "work" && command === "show")
|
|
704
1677
|
return showWork(context, requiredPosition(parsed, 0, "work-id"));
|
|
705
1678
|
if (family === "work" && command === "repair" && requiredPosition(parsed, 0, "action") === "add") {
|
|
706
|
-
if (!
|
|
707
|
-
throw new AppError("
|
|
708
|
-
|
|
709
|
-
const findings = parsed.options.get("from-finding") ?? [];
|
|
710
|
-
const unresolved = parsed.options.get("from-unresolved") ?? [];
|
|
711
|
-
if (!origins.length && !findings.length)
|
|
712
|
-
throw new AppError("usage", "work repair add requires --from-check with --origin-work, --from-finding, or --from-unresolved with --origin-work", 2);
|
|
713
|
-
if (findings.length)
|
|
714
|
-
return addVnextCodeReviewRepair(context, {
|
|
715
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "work repair add"),
|
|
716
|
-
runId: requiredOption(parsed, "run"),
|
|
717
|
-
findingIds: findings,
|
|
718
|
-
checkRefsByFinding: Object.fromEntries(findings.map((finding) => [finding, parsed.options.get("check-ref") ?? []])),
|
|
719
|
-
objective: await readStdin(io.stdin)
|
|
720
|
-
});
|
|
721
|
-
if (unresolved.length) {
|
|
722
|
-
if (!origins.length)
|
|
723
|
-
throw new AppError("usage", "semantic repair requires at least one --origin-work", 2);
|
|
724
|
-
const verificationPath = requiredOption(parsed, "verification-file");
|
|
725
|
-
return addVnextCodeRepair(context, {
|
|
726
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "work repair add"),
|
|
727
|
-
runId: requiredOption(parsed, "run"),
|
|
728
|
-
originWorkIds: origins,
|
|
729
|
-
semanticUnresolved: unresolved,
|
|
730
|
-
verificationPath,
|
|
731
|
-
objective: await readStdin(io.stdin)
|
|
732
|
-
});
|
|
733
|
-
}
|
|
734
|
-
if (!hasOption(parsed, "from-check"))
|
|
735
|
-
throw new AppError("usage", "check repair requires --from-check", 2);
|
|
736
|
-
if (!origins.length)
|
|
737
|
-
throw new AppError("usage", "check repair requires at least one --origin-work", 2);
|
|
738
|
-
const receipts = parsed.options.get("from-check") ?? [];
|
|
739
|
-
if (receipts.length !== 1)
|
|
740
|
-
throw new AppError("usage", "check repair accepts exactly one --from-check; create one causal repair Work per failed receipt", 2);
|
|
741
|
-
return addVnextCodeRepair(context, {
|
|
742
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "work repair add"),
|
|
743
|
-
runId: requiredOption(parsed, "run"),
|
|
744
|
-
checkReceiptId: receipts[0],
|
|
745
|
-
originWorkIds: origins,
|
|
746
|
-
objective: await readStdin(io.stdin)
|
|
747
|
-
});
|
|
1679
|
+
if (!prepared.workRepair)
|
|
1680
|
+
throw new AppError("cli_input_unprepared", "Repair input was not prepared", 1);
|
|
1681
|
+
return addVnextCodeRepair(context, prepared.workRepair.input, prepared.workRepair.value);
|
|
748
1682
|
}
|
|
749
1683
|
if (family === "work" && command === "deps") {
|
|
750
1684
|
const on = parsed.options.get("on") ?? [];
|
|
@@ -752,26 +1686,42 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
752
1686
|
}
|
|
753
1687
|
if (family === "work" && command === "delete")
|
|
754
1688
|
return deleteWork(context, requiredPosition(parsed, 0, "work-id"));
|
|
1689
|
+
if (family === "work" && command === "launch")
|
|
1690
|
+
return await launchExternalWork(context, { workId: requiredPosition(parsed, 0, "work-id"), ...(optionalOption(parsed, "stage") ? { stage: optionalOption(parsed, "stage") } : {}), progress: commandProgress(io, output) });
|
|
755
1691
|
if (family === "work" && command === "start") {
|
|
756
1692
|
const hookEventId = optionalOption(parsed, "hook-event-id");
|
|
1693
|
+
const recoveryId = optionalOption(parsed, "recovery-id");
|
|
757
1694
|
const projectRoot = requiredScopeProjectRoot(scopeProjectRoot, "work start");
|
|
758
|
-
return startWork(context, requiredPosition(parsed, 0, "work-id"), { ...(hookEventId ? { hookEventId } : {}), projectRoot });
|
|
1695
|
+
return startWork(context, requiredPosition(parsed, 0, "work-id"), { ...(hookEventId ? { hookEventId } : {}), ...(recoveryId ? { recoveryId } : {}), projectRoot });
|
|
759
1696
|
}
|
|
760
1697
|
if (family === "work" && command === "finish") {
|
|
761
1698
|
const resultFile = optionalOption(parsed, "result-file");
|
|
762
1699
|
const resultStdin = hasOption(parsed, "result-stdin");
|
|
763
1700
|
if (Boolean(resultFile) === resultStdin)
|
|
764
1701
|
throw new AppError("usage", "work finish requires exactly one of --result-file or --result-stdin", 2);
|
|
765
|
-
const
|
|
766
|
-
|
|
1702
|
+
const workId = requiredPosition(parsed, 0, "work-id");
|
|
1703
|
+
const projectRoot = requiredScopeProjectRoot(scopeProjectRoot, "work finish");
|
|
1704
|
+
assertWorkLifecycleOwner(context, { workId, projectRoot, operation: "work_finish", ...(optionalOption(parsed, "hook-event-id") ? { hookEventId: optionalOption(parsed, "hook-event-id") } : {}) });
|
|
1705
|
+
const retryReceiptId = optionalOption(parsed, "retry-check"), retryReason = prepared.retryReason;
|
|
1706
|
+
if (Boolean(retryReceiptId) !== Boolean(retryReason))
|
|
1707
|
+
throw new AppError("usage", "--retry-check and --reason must be supplied together", 2);
|
|
1708
|
+
const maxBytes = workResultByteLimit(context, workId);
|
|
1709
|
+
const result = prepared.workFinish?.result ?? (resultFile ? prepareTextFile(resultFile, "Work result", maxBytes).text : await readStdin(io.stdin, maxBytes));
|
|
1710
|
+
return await finishWork(context, workId, result, commandProgress(io, output), retryReceiptId ? { receiptId: retryReceiptId, reason: retryReason } : undefined);
|
|
1711
|
+
}
|
|
1712
|
+
if (family === "work" && command === "fail") {
|
|
1713
|
+
const workId = requiredPosition(parsed, 0, "work-id");
|
|
1714
|
+
const projectRoot = requiredScopeProjectRoot(scopeProjectRoot, "work fail");
|
|
1715
|
+
assertWorkLifecycleOwner(context, { workId, projectRoot, operation: "work_fail", ...(optionalOption(parsed, "hook-event-id") ? { hookEventId: optionalOption(parsed, "hook-event-id") } : {}) });
|
|
1716
|
+
return failWork(context, workId, requiredOption(parsed, "reason"));
|
|
767
1717
|
}
|
|
768
|
-
if (family === "work" && command === "fail")
|
|
769
|
-
return failWork(context, requiredPosition(parsed, 0, "work-id"), requiredOption(parsed, "reason"));
|
|
770
1718
|
if (family === "work" && command === "cancel")
|
|
771
1719
|
return cancelWork(context, requiredPosition(parsed, 0, "work-id"), requiredOption(parsed, "reason"));
|
|
772
1720
|
if (family === "work" && command === "retry")
|
|
773
1721
|
return retryWork(context, requiredPosition(parsed, 0, "work-id"), requiredOption(parsed, "reason"));
|
|
774
1722
|
if (family === "stage" && command === "start") {
|
|
1723
|
+
if (prepared.stageBindingBundle)
|
|
1724
|
+
context = { ...context, stageBindingBundle: prepared.stageBindingBundle };
|
|
775
1725
|
const dir = optionalOption(parsed, "dir");
|
|
776
1726
|
const bootstrap = hasOption(parsed, "bootstrap");
|
|
777
1727
|
const subject = optionalOption(parsed, "subject");
|
|
@@ -788,12 +1738,40 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
788
1738
|
const requireSessionBinding = hasOption(parsed, "require-session-binding");
|
|
789
1739
|
const projectRoot = requiredOption(parsed, "project-root");
|
|
790
1740
|
const stage = requiredOption(parsed, "stage");
|
|
791
|
-
const externalContext =
|
|
1741
|
+
const externalContext = prepared.stageStart?.externalContext;
|
|
1742
|
+
if (prepared.memoryStage?.operation === "start") {
|
|
1743
|
+
const runId = requiredPosition(parsed, 0, "run-id");
|
|
1744
|
+
const event = hookEventId ?? findRecentMatchingHookEvent(context, {
|
|
1745
|
+
projectId: requireProjectByRoot(context, resolveProjectRoot(projectRoot)).id,
|
|
1746
|
+
matchKey: entityReferenceVariants(runId).map(reference => stageStartMatchKey(reference, stage, projectRoot, contextSha256)),
|
|
1747
|
+
errorCode: "trusted_session_binding_required", operation: "Memory stage start"
|
|
1748
|
+
}).eventKey;
|
|
1749
|
+
const work = startMemoryStage(context, { projectRoot, runId, stage, bytes: prepared.memoryStage.bytes });
|
|
1750
|
+
try {
|
|
1751
|
+
const binding = bindStageCoordinatorWork(context, { workId: work.work_id, hookEventId: event, stage,
|
|
1752
|
+
promptPath: work.prompt_path, resultPath: work.result_path, ...(contextSha256 ? { contextSha256 } : {}) });
|
|
1753
|
+
// The frozen semantic prompt is also restored by auxiliary owners in
|
|
1754
|
+
// other CLI contexts. Keep native invocation authority in this current
|
|
1755
|
+
// delivery, not in those immutable instruction/custody bytes.
|
|
1756
|
+
const finish = managedLifecycleCommand(context, runtimeCommand(context, ["stage", "finish", runId,
|
|
1757
|
+
"--stage", stage, "--project-root", projectRoot, "--result-file", work.result_path, "--json"]));
|
|
1758
|
+
const pause = stagePauseCommand(context, { projectRoot, runId, stage, workId: work.work_id });
|
|
1759
|
+
const orchestration = memoryStageFanoutDescriptor(context, { projectRoot, runId, stage });
|
|
1760
|
+
const delivery = { ...work, binding, ...(orchestration ? { orchestration } : {}), next: { finish_command: finish, pause_command: pause },
|
|
1761
|
+
worker_prompt_markdown: `${work.worker_prompt_markdown}\n## Current lifecycle commands\n\nWrite the semantic result to the assigned result path, then execute exactly:\n${finish}\n\nFor a required user choice, write the question file named by this command, then execute exactly:\n${pause}\n` };
|
|
1762
|
+
bindLifecyclePublishedPacket(context, delivery.worker_prompt_markdown);
|
|
1763
|
+
return controlLifecycleReply(context, { projectRoot, runId }, delivery);
|
|
1764
|
+
}
|
|
1765
|
+
catch (error) {
|
|
1766
|
+
throw new AppError("memory_stage_binding_pending", "Memory Stage allocated; trusted coordinator binding remains incomplete", 1, { effect: "committed", run_id: runId, stage, work_id: work.work_id, next_action: "reconcile_memory_stage_binding",
|
|
1767
|
+
cause: error instanceof AppError ? error.code : "coordinator_binding_failure" });
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
792
1770
|
const claimVnextStageStart = (runId) => {
|
|
793
1771
|
registerProject(context, { root: projectRoot });
|
|
794
1772
|
return findRecentMatchingHookEvent(context, {
|
|
795
1773
|
projectId: requireProjectByRoot(context, resolveProjectRoot(projectRoot)).id,
|
|
796
|
-
matchKey: stageStartMatchKey(
|
|
1774
|
+
matchKey: entityReferenceVariants(runId).map((reference) => stageStartMatchKey(reference, stage, projectRoot, contextSha256)),
|
|
797
1775
|
errorCode: "trusted_session_binding_required",
|
|
798
1776
|
operation: `vNext ${stage.toUpperCase()} start`
|
|
799
1777
|
}).eventKey;
|
|
@@ -817,39 +1795,39 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
817
1795
|
})
|
|
818
1796
|
: claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
819
1797
|
}
|
|
820
|
-
const result =
|
|
1798
|
+
const result = sdlcStageHandlers.specify.entry(context, {
|
|
821
1799
|
projectRoot,
|
|
822
|
-
...(externalContext ? { intakeMarkdown: runnerIntake(externalContext) } : intakeFile ? { intakeFile } : { intakeMarkdown: await readStdin(io.stdin) }),
|
|
1800
|
+
...(prepared.stageStart?.intakeMarkdown !== undefined ? { intakeMarkdown: prepared.stageStart.intakeMarkdown } : externalContext ? { intakeMarkdown: runnerIntake(externalContext) } : intakeFile ? { intakeFile } : { intakeMarkdown: await readStdin(io.stdin) }),
|
|
823
1801
|
hookEventId,
|
|
824
1802
|
...(subject ? { slug: subject } : {}),
|
|
825
1803
|
...(externalContext ? { externalContext } : {}),
|
|
826
1804
|
...(!bootstrap ? { runId: requiredPosition(parsed, 0, "run-id") } : {})
|
|
827
|
-
});
|
|
1805
|
+
}, prepared.specifyStart);
|
|
828
1806
|
return attachExternalStageContext(result, externalContext);
|
|
829
1807
|
}
|
|
830
1808
|
if (!bootstrap && stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
831
1809
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
832
|
-
return attachExternalStageContext(
|
|
1810
|
+
return attachExternalStageContext(await sdlcStageHandlers.protocolize.entry(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.protocolizeStart), externalContext);
|
|
833
1811
|
}
|
|
834
1812
|
if (!bootstrap && stage === "plan" && isVnextPlanRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
835
1813
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
836
|
-
return attachExternalStageContext(
|
|
1814
|
+
return attachExternalStageContext(sdlcStageHandlers.plan.entry(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.planStart), externalContext);
|
|
837
1815
|
}
|
|
838
1816
|
if (!bootstrap && stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
839
1817
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
840
|
-
return attachExternalStageContext(
|
|
1818
|
+
return attachExternalStageContext(sdlcStageHandlers["plan-review"].entry(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.planReviewStart), externalContext);
|
|
841
1819
|
}
|
|
842
1820
|
if (!bootstrap && stage === "code" && isVnextPlanRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
843
1821
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
844
|
-
return attachExternalStageContext(await
|
|
1822
|
+
return attachExternalStageContext(await sdlcStageHandlers.code.entry(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, progress: commandProgress(io, output), ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.codeStart), externalContext);
|
|
845
1823
|
}
|
|
846
1824
|
if (!bootstrap && stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
847
1825
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
848
|
-
return attachExternalStageContext(
|
|
1826
|
+
return attachExternalStageContext(sdlcStageHandlers["code-review"].entry(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.codeReviewStart), externalContext);
|
|
849
1827
|
}
|
|
850
1828
|
if (!bootstrap && stage === "merge" && isVnextMergeRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
851
1829
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
852
|
-
return attachExternalStageContext(await
|
|
1830
|
+
return attachExternalStageContext(await sdlcStageHandlers.merge.entry(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, progress: commandProgress(io, output), ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.mergeStart), externalContext);
|
|
853
1831
|
}
|
|
854
1832
|
if (intakeStdin) {
|
|
855
1833
|
throw new AppError("usage", "--intake-stdin is only available for vNext SPECIFY", 2);
|
|
@@ -864,7 +1842,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
864
1842
|
...(hookEventId ? { hookEventId } : {}),
|
|
865
1843
|
...(requireSessionBinding ? { requireSessionBinding: true } : {}),
|
|
866
1844
|
...(dir ? { dir } : {})
|
|
867
|
-
});
|
|
1845
|
+
}, prepared.legacyStageStart);
|
|
868
1846
|
}
|
|
869
1847
|
if (family === "stage" && command === "finish") {
|
|
870
1848
|
const dir = optionalOption(parsed, "dir");
|
|
@@ -874,9 +1852,34 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
874
1852
|
const projectRoot = requiredOption(parsed, "project-root");
|
|
875
1853
|
const runId = requiredPosition(parsed, 0, "run-id");
|
|
876
1854
|
const stage = requiredOption(parsed, "stage");
|
|
1855
|
+
const controlledReply = (result) => controlLifecycleReply(context, { projectRoot, runId }, result);
|
|
877
1856
|
const semanticFile = optionalOption(parsed, "semantic-file") ?? optionalOption(parsed, "data");
|
|
878
1857
|
const resultFile = optionalOption(parsed, "result-file") ?? semanticFile;
|
|
879
1858
|
const resultStdin = hasOption(parsed, "result-stdin");
|
|
1859
|
+
const retryCheckId = optionalOption(parsed, "retry-check");
|
|
1860
|
+
const retryReason = prepared.retryReason;
|
|
1861
|
+
const hookEventId = optionalOption(parsed, "hook-event-id");
|
|
1862
|
+
if (retryCheckId && !retryReason?.trim())
|
|
1863
|
+
throw new AppError("usage", "--retry-check requires --reason explaining the recovered environment", 2);
|
|
1864
|
+
if (prepared.memoryStage?.operation === "finish") {
|
|
1865
|
+
// Descriptor settlement validates exact coordinator custody and claims
|
|
1866
|
+
// the fresh hook atomically with result acceptance. Do not consume it
|
|
1867
|
+
// through the SDLC lifecycle owner before that settlement.
|
|
1868
|
+
return controlledReply(finishMemoryStage(context, { projectRoot, runId, stage, bytes: prepared.memoryStage.bytes,
|
|
1869
|
+
hookEventId: hookEventId ?? findRecentMatchingHookEvent(context, {
|
|
1870
|
+
projectId: requireProjectByRoot(context, resolveProjectRoot(projectRoot)).id,
|
|
1871
|
+
matchKey: entityReferenceVariants(runId).map(reference => stageLifecycleMatchKey("stage_finish", reference, stage, projectRoot)),
|
|
1872
|
+
errorCode: "trusted_session_binding_required", operation: "Memory stage finish"
|
|
1873
|
+
}).eventKey }));
|
|
1874
|
+
}
|
|
1875
|
+
const protectedLifecycle = (stage === "specify" && isVnextSpecifyRun(context, { projectRoot, runId }))
|
|
1876
|
+
|| (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId }))
|
|
1877
|
+
|| (stage === "plan" && isVnextPlanRun(context, { projectRoot, runId }))
|
|
1878
|
+
|| (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId }))
|
|
1879
|
+
|| (stage === "code" && isVnextPlanRun(context, { projectRoot, runId }))
|
|
1880
|
+
|| (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId }));
|
|
1881
|
+
if (protectedLifecycle)
|
|
1882
|
+
assertStageLifecycleOwner(context, { projectRoot, runId, stage, operation: "stage_finish", ...(hookEventId ? { hookEventId } : {}) });
|
|
880
1883
|
if (stage === "specify" && isVnextSpecifyRun(context, { projectRoot, runId })) {
|
|
881
1884
|
const outcome = optionalOption(parsed, "outcome");
|
|
882
1885
|
if (!outcome || !["specified", "failed", "cancelled"].includes(outcome)) {
|
|
@@ -888,84 +1891,92 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
888
1891
|
if (!resultFile && !resultStdin) {
|
|
889
1892
|
throw new AppError("usage", "vNext SPECIFY finish requires --result-file or --result-stdin", 2);
|
|
890
1893
|
}
|
|
891
|
-
return
|
|
1894
|
+
return controlledReply(await sdlcStageHandlers.specify.finish(context, {
|
|
892
1895
|
projectRoot,
|
|
893
1896
|
runId,
|
|
894
|
-
...(resultFile ? { resultFile } : { resultJson: await readStdin(io.stdin) }),
|
|
1897
|
+
...(resultFile ? { resultFile } : prepared.stageFinish?.value ? {} : { resultJson: await readStdin(io.stdin) }),
|
|
1898
|
+
...(prepared.stageFinish?.value ? { result: prepared.stageFinish.value } : {}),
|
|
895
1899
|
outcome: outcome
|
|
896
|
-
});
|
|
1900
|
+
}));
|
|
897
1901
|
}
|
|
898
1902
|
if (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId })) {
|
|
899
1903
|
if (!resultFile || resultStdin || hasOption(parsed, "outcome")) {
|
|
900
1904
|
throw new AppError("usage", "vNext PROTOCOLIZE requires exactly --result-file", 2);
|
|
901
1905
|
}
|
|
902
|
-
return
|
|
1906
|
+
return controlledReply(await sdlcStageHandlers.protocolize.finish(context, { projectRoot, runId, resultFile, ...(prepared.stageFinish?.value ? { result: prepared.stageFinish.value } : {}) }));
|
|
903
1907
|
}
|
|
904
1908
|
if (stage === "plan" && isVnextPlanRun(context, { projectRoot, runId })) {
|
|
905
1909
|
if (optionalOption(parsed, "code-work-batch-file"))
|
|
906
1910
|
throw new AppError("usage", "vNext PLAN derives its CODE batch from accepted semantic plans; do not supply a batch file", 2);
|
|
907
|
-
return
|
|
1911
|
+
return controlledReply(await sdlcStageHandlers.plan.finish(context, { projectRoot, runId, ...(prepared.planFinish ? { prepared: prepared.planFinish } : {}) }));
|
|
908
1912
|
}
|
|
909
1913
|
if (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId })) {
|
|
910
1914
|
if (resultStdin || hasOption(parsed, "outcome"))
|
|
911
1915
|
throw new AppError("usage", "vNext PLAN-REVIEW uses --decision-file", 2);
|
|
912
1916
|
const decisionFile = optionalOption(parsed, "decision-file");
|
|
913
|
-
return
|
|
1917
|
+
return controlledReply(await sdlcStageHandlers["plan-review"].finish(context, { projectRoot, runId, ...(decisionFile ? { decisionFile } : {}), ...(prepared.stageFinish?.value ? { decision: prepared.stageFinish.value, decisionText: prepared.stageFinish.text } : {}) }));
|
|
914
1918
|
}
|
|
915
1919
|
if (stage === "code" && isVnextPlanRun(context, { projectRoot, runId })) {
|
|
916
1920
|
if (semanticFile || resultFile || resultStdin || hasOption(parsed, "outcome")) {
|
|
917
1921
|
throw new AppError("usage", "vNext CODE finish accepts only --verification-file", 2);
|
|
918
1922
|
}
|
|
919
|
-
return await
|
|
1923
|
+
return controlledReply(await sdlcStageHandlers.code.finish(context, { projectRoot, runId, verificationFile: optionalOption(parsed, "verification-file") ?? path.join("<missing>", "code-verification.json"), ...(prepared.stageFinish?.value ? { verification: prepared.stageFinish.value } : {}), ...(retryCheckId ? { retryCheckId, retryReason: retryReason } : {}), progress: commandProgress(io, output) }));
|
|
920
1924
|
}
|
|
921
1925
|
if (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId })) {
|
|
922
1926
|
if (resultStdin || semanticFile || resultFile || hasOption(parsed, "outcome"))
|
|
923
1927
|
throw new AppError("usage", "vNext CODE-REVIEW uses --decision-file", 2);
|
|
924
|
-
return await
|
|
1928
|
+
return controlledReply(await sdlcStageHandlers["code-review"].finish(context, { projectRoot, runId, ...(optionalOption(parsed, "decision-file") ? { decisionFile: optionalOption(parsed, "decision-file") } : {}), ...(prepared.stageFinish?.value ? { decision: prepared.stageFinish.value, decisionText: prepared.stageFinish.text } : {}), ...(retryCheckId ? { retryCheckId, retryReason: retryReason } : {}), progress: commandProgress(io, output) }));
|
|
925
1929
|
}
|
|
926
1930
|
if (stage === "merge" && isVnextMergeRun(context, { projectRoot, runId })) {
|
|
927
1931
|
if (resultStdin || semanticFile || resultFile || hasOption(parsed, "outcome"))
|
|
928
1932
|
throw new AppError("usage", "vNext MERGE uses the Work result path returned by stage start", 2);
|
|
929
|
-
return await
|
|
1933
|
+
return controlledReply(await sdlcStageHandlers.merge.finish(context, { projectRoot, runId, requestId: requiredOption(parsed, "request"), workId: requiredOption(parsed, "work"), ...(retryCheckId ? { retryCheckId, retryReason: retryReason } : {}), progress: commandProgress(io, output) }, prepared.mergeFinish));
|
|
930
1934
|
}
|
|
931
1935
|
if (hasOption(parsed, "outcome") || hasOption(parsed, "result-file") || resultStdin) {
|
|
932
1936
|
throw new AppError("usage", "--outcome, --result-file and --result-stdin are only available for vNext SPECIFY", 2);
|
|
933
1937
|
}
|
|
934
|
-
return finishStage(context, {
|
|
1938
|
+
return controlledReply(await finishStage(context, {
|
|
935
1939
|
projectRoot,
|
|
936
1940
|
runId,
|
|
937
1941
|
stage,
|
|
938
1942
|
...(dir ? { dir } : {}),
|
|
939
1943
|
...(semanticFile ? { semanticFile } : {})
|
|
940
|
-
});
|
|
1944
|
+
}, prepared.legacyStageFinish));
|
|
941
1945
|
}
|
|
942
1946
|
if (family === "stage" && command === "pause") {
|
|
943
|
-
|
|
944
|
-
|
|
1947
|
+
const projectRoot = requiredOption(parsed, "project-root");
|
|
1948
|
+
const runId = requiredPosition(parsed, 0, "run-id");
|
|
1949
|
+
const stage = requiredOption(parsed, "stage");
|
|
1950
|
+
const workId = requiredOption(parsed, "work");
|
|
1951
|
+
const protectedLifecycle = (stage === "specify" && isVnextSpecifyRun(context, { projectRoot, runId }))
|
|
1952
|
+
|| (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId }))
|
|
1953
|
+
|| (stage === "plan" && isVnextPlanRun(context, { projectRoot, runId }))
|
|
1954
|
+
|| (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId }))
|
|
1955
|
+
|| (stage === "code" && isVnextPlanRun(context, { projectRoot, runId }))
|
|
1956
|
+
|| (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId }));
|
|
1957
|
+
if (protectedLifecycle)
|
|
1958
|
+
assertStageLifecycleOwner(context, { projectRoot, runId, stage, workId, operation: "stage_pause", ...(optionalOption(parsed, "hook-event-id") ? { hookEventId: optionalOption(parsed, "hook-event-id") } : {}) });
|
|
945
1959
|
return pauseStageForUser(context, {
|
|
946
|
-
projectRoot
|
|
947
|
-
runId
|
|
948
|
-
stage
|
|
949
|
-
workId
|
|
950
|
-
question: await readStdin(io.stdin)
|
|
1960
|
+
projectRoot,
|
|
1961
|
+
runId,
|
|
1962
|
+
stage,
|
|
1963
|
+
workId,
|
|
1964
|
+
question: prepared.interactionText ?? await readStdin(io.stdin)
|
|
951
1965
|
});
|
|
952
1966
|
}
|
|
953
1967
|
if (family === "stage" && command === "resume") {
|
|
954
|
-
if (!hasOption(parsed, "answer-stdin"))
|
|
955
|
-
throw new AppError("usage", "stage resume requires --answer-stdin", 2);
|
|
956
1968
|
const hookEventId = optionalOption(parsed, "hook-event-id");
|
|
957
1969
|
return resumeStageAfterUser(context, {
|
|
958
1970
|
projectRoot: requiredOption(parsed, "project-root"),
|
|
959
1971
|
runId: requiredPosition(parsed, 0, "run-id"),
|
|
960
1972
|
stage: requiredOption(parsed, "stage"),
|
|
961
1973
|
workId: requiredOption(parsed, "work"),
|
|
962
|
-
answer: await readStdin(io.stdin),
|
|
1974
|
+
answer: prepared.interactionText ?? await readStdin(io.stdin),
|
|
1975
|
+
evidence: prepared.resumeEvidence,
|
|
963
1976
|
...(hookEventId ? { hookEventId } : {})
|
|
964
1977
|
});
|
|
965
1978
|
}
|
|
966
1979
|
if (family === "stage" && command === "block") {
|
|
967
|
-
if (!hasOption(parsed, "summary-stdin"))
|
|
968
|
-
throw new AppError("usage", "stage block requires --summary-stdin", 2);
|
|
969
1980
|
const kind = requiredOption(parsed, "kind");
|
|
970
1981
|
if (kind !== "engine" && kind !== "harness" && kind !== "environment")
|
|
971
1982
|
throw new AppError("usage", "--kind must be engine, harness or environment", 2);
|
|
@@ -976,7 +1987,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
976
1987
|
workId: requiredOption(parsed, "work"),
|
|
977
1988
|
kind,
|
|
978
1989
|
code: requiredOption(parsed, "code"),
|
|
979
|
-
summary: await readStdin(io.stdin),
|
|
1990
|
+
summary: prepared.interactionText ?? await readStdin(io.stdin),
|
|
980
1991
|
retryable: hasOption(parsed, "retryable")
|
|
981
1992
|
});
|
|
982
1993
|
}
|
|
@@ -1001,13 +2012,55 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1001
2012
|
return dispatchMergeWorker(context, command, parsed);
|
|
1002
2013
|
}
|
|
1003
2014
|
if (family === "cleanup") {
|
|
1004
|
-
return dispatchCleanup(context, command, parsed);
|
|
2015
|
+
return dispatchCleanup(context, command, parsed, prepared.cleanup);
|
|
1005
2016
|
}
|
|
1006
2017
|
if (family === "session") {
|
|
1007
|
-
return dispatchSession(context, command, parsed);
|
|
2018
|
+
return dispatchSession(context, command, parsed, prepared.sessionPayload);
|
|
1008
2019
|
}
|
|
1009
2020
|
if (family === "run") {
|
|
1010
|
-
|
|
2021
|
+
if (command === "control") {
|
|
2022
|
+
const action = requiredPosition(parsed, 0, "control action");
|
|
2023
|
+
const scope = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run") };
|
|
2024
|
+
if (action === "status")
|
|
2025
|
+
return runControlStatus(context, scope);
|
|
2026
|
+
if (action === "reconcile") {
|
|
2027
|
+
if ([...parsed.options.keys()].some(key => !["run", "project-root", "control-id", "request-id"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
2028
|
+
throw new AppError("usage", "Use run control reconcile --run ID --project-root PATH --control-id ID --request-id ID", 2);
|
|
2029
|
+
return await reconcileRunControl(context, { ...scope, controlId: requiredOption(parsed, "control-id"), requestId: requiredOption(parsed, "request-id") });
|
|
2030
|
+
}
|
|
2031
|
+
if (action === "resume") {
|
|
2032
|
+
if (["force", "grace-ms", "escalate"].some(option => hasOption(parsed, option)))
|
|
2033
|
+
throw new AppError("usage", "Resume does not accept stop or pause escalation options", 2);
|
|
2034
|
+
return await resumeRunController(context, { ...scope, requestId: requiredOption(parsed, "request-id"), controlId: requiredOption(parsed, "from"), ...(hasOption(parsed, "wait-ms") ? { waitMs: Number(requiredOption(parsed, "wait-ms")) } : {}) });
|
|
2035
|
+
}
|
|
2036
|
+
if (action === "serve")
|
|
2037
|
+
return await serveRunControl(context, { ...scope, controlId: requiredOption(parsed, "control-id") });
|
|
2038
|
+
if (action === "pause" || action === "stop")
|
|
2039
|
+
return await requestRunControl(context, {
|
|
2040
|
+
...scope, action, requestId: requiredOption(parsed, "request-id"),
|
|
2041
|
+
...(hasOption(parsed, "force") ? { force: true } : {}),
|
|
2042
|
+
...(hasOption(parsed, "grace-ms") ? { graceMs: Number(requiredOption(parsed, "grace-ms")) } : {}),
|
|
2043
|
+
...(hasOption(parsed, "escalate") ? { escalate: requiredOption(parsed, "escalate") } : {}),
|
|
2044
|
+
...(hasOption(parsed, "wait-ms") ? { waitMs: Number(requiredOption(parsed, "wait-ms")) } : {})
|
|
2045
|
+
});
|
|
2046
|
+
throw new AppError("usage", "Use: dd-flow run control pause|stop|resume|reconcile|status", 2);
|
|
2047
|
+
}
|
|
2048
|
+
if (command === "drive") {
|
|
2049
|
+
const action = requiredPosition(parsed, 0, "drive action");
|
|
2050
|
+
const scope = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run") };
|
|
2051
|
+
if (action === "status")
|
|
2052
|
+
return runControllerStatus(context, { ...scope, ...(optionalOption(parsed, "after") ? { after: Number(optionalOption(parsed, "after")) } : {}) });
|
|
2053
|
+
if (action === "launch")
|
|
2054
|
+
return await launchRunController(context, controllerLaunchInput(parsed), prepared.controllerLaunch);
|
|
2055
|
+
if (action === "serve")
|
|
2056
|
+
return await serveRunController(context, { ...scope, controllerId: requiredOption(parsed, "controller-id") });
|
|
2057
|
+
if (action === "answer")
|
|
2058
|
+
return answerRunController(context, controllerAnswerInput(parsed), prepared.controllerAnswer);
|
|
2059
|
+
if (action === "context")
|
|
2060
|
+
return supplyRunControllerContext(context, controllerContextInput(parsed), prepared.controllerContext);
|
|
2061
|
+
throw new AppError("usage", "Use: dd-flow run drive launch|status|answer|context", 2);
|
|
2062
|
+
}
|
|
2063
|
+
return dispatchRun(context, command, parsed, prepared);
|
|
1011
2064
|
}
|
|
1012
2065
|
if (family === "stat") {
|
|
1013
2066
|
return dispatchStat(context, command, parsed);
|
|
@@ -1022,22 +2075,26 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1022
2075
|
return dispatchDashboard(context, command, parsed);
|
|
1023
2076
|
}
|
|
1024
2077
|
if (family === "schema") {
|
|
1025
|
-
return
|
|
2078
|
+
return prepared.schemaValidation;
|
|
1026
2079
|
}
|
|
1027
2080
|
if (family === "memory") {
|
|
1028
|
-
return dispatchMemory(command, parsed);
|
|
2081
|
+
return dispatchMemory(context, command, parsed, prepared);
|
|
1029
2082
|
}
|
|
1030
2083
|
if (family === "migration") {
|
|
1031
|
-
return dispatchMigration(context, command, parsed, classification);
|
|
2084
|
+
return dispatchMigration(context, command, parsed, classification, prepared);
|
|
1032
2085
|
}
|
|
1033
2086
|
if (family === "codex" && command === "hooks") {
|
|
1034
|
-
return dispatchCodexHooks(context, parsed);
|
|
2087
|
+
return dispatchCodexHooks(context, parsed, prepared);
|
|
1035
2088
|
}
|
|
1036
2089
|
if (family === "codex" && command === "home") {
|
|
1037
|
-
return dispatchCodexHome(context, parsed);
|
|
2090
|
+
return dispatchCodexHome(context, parsed, prepared);
|
|
1038
2091
|
}
|
|
1039
2092
|
if (family === "codex" && command === "hook") {
|
|
1040
|
-
|
|
2093
|
+
// `handle` is owned by the pre-context ingress above, never generic dispatch.
|
|
2094
|
+
throw new AppError("usage", "Use: dd-flow codex hook handle --event EVENT", 2);
|
|
2095
|
+
}
|
|
2096
|
+
if (family === "zcode" && command === "hook") {
|
|
2097
|
+
return dispatchZcodeHook(context, parsed, await readStdin(io.stdin));
|
|
1041
2098
|
}
|
|
1042
2099
|
if (family === "zcode" && command === "event") {
|
|
1043
2100
|
return dispatchZcodeEvent(context, parsed, await readStdin(io.stdin));
|
|
@@ -1063,6 +2120,16 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1063
2120
|
throw new AppError("usage", "Unknown opencode usage action", 2);
|
|
1064
2121
|
return ingestOpenCodeUsage(context, { projectRoot: requiredOption(parsed, "project-root"), stdin: await readStdin(io.stdin) });
|
|
1065
2122
|
}
|
|
2123
|
+
if (family === "droid" && command === "event") {
|
|
2124
|
+
if (requiredPosition(parsed, 0, "droid event action") !== "handle")
|
|
2125
|
+
throw new AppError("usage", "Unknown droid event action", 2);
|
|
2126
|
+
return handleDroidEvent(context, { projectRoot: requiredOption(parsed, "project-root"), stdin: await readStdin(io.stdin) });
|
|
2127
|
+
}
|
|
2128
|
+
if (family === "droid" && command === "usage") {
|
|
2129
|
+
if (requiredPosition(parsed, 0, "droid usage action") !== "ingest")
|
|
2130
|
+
throw new AppError("usage", "Unknown droid usage action", 2);
|
|
2131
|
+
return ingestDroidUsage(context, { projectRoot: requiredOption(parsed, "project-root"), stdin: await readStdin(io.stdin) });
|
|
2132
|
+
}
|
|
1066
2133
|
if (family === "agy" && command === "event")
|
|
1067
2134
|
return dispatchAgyEvent(context, parsed, await readStdin(io.stdin));
|
|
1068
2135
|
if (family === "agy" && command === "usage") {
|
|
@@ -1071,11 +2138,14 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1071
2138
|
return ingestAgyUsage(context, { projectRoot: requiredOption(parsed, "project-root"), stdin: await readStdin(io.stdin) });
|
|
1072
2139
|
}
|
|
1073
2140
|
if (family === "worktree") {
|
|
1074
|
-
return dispatchWorktree(context, command, parsed, requiredScopeProjectRoot(scopeProjectRoot, `worktree ${command ?? ""}`));
|
|
2141
|
+
return dispatchWorktree(context, command, parsed, requiredScopeProjectRoot(scopeProjectRoot, `worktree ${command ?? ""}`), prepared);
|
|
1075
2142
|
}
|
|
1076
2143
|
throw new AppError("usage", `Unknown command: ${args.join(" ") || "<empty>"}`, 2);
|
|
1077
2144
|
}
|
|
1078
|
-
function
|
|
2145
|
+
function engineBindInput(parsed) {
|
|
2146
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), packageName: optionalOption(parsed, "package"), engineVersion: requiredOption(parsed, "engine-version"), integrityChecksum: optionalOption(parsed, "integrity-checksum"), reason: requiredOption(parsed, "reason") };
|
|
2147
|
+
}
|
|
2148
|
+
function dispatchEngine(context, command, parsed, classification, prepared) {
|
|
1079
2149
|
if (command === "install") {
|
|
1080
2150
|
return installCurrentEngine(context, { force: hasOption(parsed, "force") });
|
|
1081
2151
|
}
|
|
@@ -1100,18 +2170,11 @@ function dispatchEngine(context, command, parsed, classification) {
|
|
|
1100
2170
|
});
|
|
1101
2171
|
}
|
|
1102
2172
|
if (command === "bind-run") {
|
|
1103
|
-
return bindRunEngine(context,
|
|
1104
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1105
|
-
runId: requiredPosition(parsed, 0, "run-id"),
|
|
1106
|
-
packageName: optionalOption(parsed, "package"),
|
|
1107
|
-
engineVersion: requiredOption(parsed, "engine-version"),
|
|
1108
|
-
integrityChecksum: optionalOption(parsed, "integrity-checksum"),
|
|
1109
|
-
reason: requiredOption(parsed, "reason")
|
|
1110
|
-
});
|
|
2173
|
+
return bindRunEngine(context, engineBindInput(parsed), prepared.engineBind);
|
|
1111
2174
|
}
|
|
1112
2175
|
throw new AppError("usage", `Unknown engine command: ${command ?? "<empty>"}`, 2);
|
|
1113
2176
|
}
|
|
1114
|
-
function dispatchMigration(context, command, parsed, classification) {
|
|
2177
|
+
function dispatchMigration(context, command, parsed, classification, prepared) {
|
|
1115
2178
|
if (command === "impact")
|
|
1116
2179
|
return assessMigrationImpact(context, { projectRoot: requiredOption(parsed, "project-root"), ...(optionalOption(parsed, "target-version") ? { targetVersion: optionalOption(parsed, "target-version") } : {}), ...(optionalOption(parsed, "mode") ? { mode: optionalOption(parsed, "mode") } : {}) });
|
|
1117
2180
|
if (command === "plan" || command === "report") {
|
|
@@ -1127,26 +2190,42 @@ function dispatchMigration(context, command, parsed, classification) {
|
|
|
1127
2190
|
});
|
|
1128
2191
|
}
|
|
1129
2192
|
if (command === "verify") {
|
|
1130
|
-
return verifyMigrationReport(context, { file: requiredOption(parsed, "file") });
|
|
2193
|
+
return prepared.migrationVerification ?? verifyMigrationReport(context, { file: requiredOption(parsed, "file") });
|
|
1131
2194
|
}
|
|
1132
2195
|
if (command === "apply") {
|
|
1133
|
-
return applyMigration(context,
|
|
1134
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1135
|
-
sourceVersion: optionalOption(parsed, "source-version"),
|
|
1136
|
-
targetVersion: optionalOption(parsed, "target-version"),
|
|
1137
|
-
runId: optionalOption(parsed, "run"),
|
|
1138
|
-
backupPath: optionalOption(parsed, "backup-path"),
|
|
1139
|
-
backupCreatedAt: optionalOption(parsed, "backup-created-at"),
|
|
1140
|
-
reportFile: optionalOption(parsed, "file"),
|
|
1141
|
-
allowActive: false,
|
|
1142
|
-
classification
|
|
1143
|
-
});
|
|
2196
|
+
return applyMigration(context, migrationApplyInput(parsed, classification), prepared.migrationApply);
|
|
1144
2197
|
}
|
|
1145
2198
|
throw new AppError("usage", `Unknown migration command: ${command ?? "<empty>"}`, 2);
|
|
1146
2199
|
}
|
|
1147
|
-
function
|
|
2200
|
+
function protocolTransitionInput(parsed, scopeProjectRoot) {
|
|
2201
|
+
const reason = optionalOption(parsed, "reason");
|
|
2202
|
+
return { protocolId: requiredPosition(parsed, 0, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "protocol transition"), to: requiredOption(parsed, "to"), jsonFile: requiredOptionAlias(parsed, ["payload-file", "json-file"]), force: hasOption(parsed, "force"), ...(reason ? { reason } : {}) };
|
|
2203
|
+
}
|
|
2204
|
+
function controllerAnswerInput(parsed) {
|
|
2205
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run"), requestId: requiredOption(parsed, "request-id"), pauseId: requiredOption(parsed, "pause-id"), answerFile: requiredOption(parsed, "answer-file") };
|
|
2206
|
+
}
|
|
2207
|
+
function controllerLaunchInput(parsed) {
|
|
2208
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run"), requestId: requiredOption(parsed, "request-id"), contextFile: requiredOption(parsed, "context-file"), contextSha256: requiredOption(parsed, "context-sha256"), ...(optionalOption(parsed, "stop-after") ? { stopAfter: optionalOption(parsed, "stop-after") } : {}), ...(optionalOption(parsed, "capture-root") ? { captureRoot: optionalOption(parsed, "capture-root") } : {}) };
|
|
2209
|
+
}
|
|
2210
|
+
function controllerContextInput(parsed) {
|
|
2211
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run"), controllerId: requiredOption(parsed, "controller-id"), requestId: requiredOption(parsed, "request-id"), stage: requiredOption(parsed, "stage"), contextFile: requiredOption(parsed, "context-file"), contextSha256: requiredOption(parsed, "context-sha256") };
|
|
2212
|
+
}
|
|
2213
|
+
function migrationApplyInput(parsed, classification) {
|
|
2214
|
+
return {
|
|
2215
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
2216
|
+
sourceVersion: optionalOption(parsed, "source-version"),
|
|
2217
|
+
targetVersion: optionalOption(parsed, "target-version"),
|
|
2218
|
+
runId: optionalOption(parsed, "run"),
|
|
2219
|
+
backupPath: optionalOption(parsed, "backup-path"),
|
|
2220
|
+
backupCreatedAt: optionalOption(parsed, "backup-created-at"),
|
|
2221
|
+
reportFile: optionalOption(parsed, "file"),
|
|
2222
|
+
allowActive: false,
|
|
2223
|
+
classification
|
|
2224
|
+
};
|
|
2225
|
+
}
|
|
2226
|
+
function dispatchCanon(context, command, parsed, prepared) {
|
|
1148
2227
|
if (command === "register") {
|
|
1149
|
-
return registerCanonRoot(context, { root: requiredOption(parsed, "root") });
|
|
2228
|
+
return registerCanonRoot(context, { root: requiredOption(parsed, "root") }, prepared.canonRegistration);
|
|
1150
2229
|
}
|
|
1151
2230
|
if (command === "status") {
|
|
1152
2231
|
return getCanonStatus(context, { root: optionalOption(parsed, "root") });
|
|
@@ -1156,6 +2235,15 @@ function dispatchCanon(context, command, parsed) {
|
|
|
1156
2235
|
}
|
|
1157
2236
|
throw new AppError("usage", `Unknown canon command: ${command ?? "<empty>"}`, 2);
|
|
1158
2237
|
}
|
|
2238
|
+
function mergeJobTarget(parsed, scopeProjectRoot) {
|
|
2239
|
+
return { protocolId: requiredPosition(parsed, 0, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue"), workerId: requiredWorkerId(parsed), workspacePath: workspacePathOption(parsed) };
|
|
2240
|
+
}
|
|
2241
|
+
function mergeCancellationInput(parsed, scopeProjectRoot) {
|
|
2242
|
+
return { protocolId: requiredPosition(parsed, 0, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue cancel"), reason: requiredOption(parsed, "reason"), workerId: optionalOption(parsed, "worker-id"), workspacePath: optionalOption(parsed, "path"), force: hasOption(parsed, "force") };
|
|
2243
|
+
}
|
|
2244
|
+
function mergeWaitInput(parsed, scopeProjectRoot) {
|
|
2245
|
+
return { projectRoot: requiredOptionOrScope(parsed, scopeProjectRoot, "project-root"), workerId: requiredWorkerId(parsed), timeoutSeconds: optionalNumber(parsed, "timeout"), pollIntervalSeconds: optionalNumber(parsed, "poll-interval"), workspacePath: workspacePathOption(parsed), acquireLock: parseOptionalBoolean(optionalOption(parsed, "acquire-lock"), "acquire-lock") ?? false };
|
|
2246
|
+
}
|
|
1159
2247
|
function dispatchMergeQueue(context, command, parsed, scopeProjectRoot) {
|
|
1160
2248
|
if (hasOption(parsed, "session-id")) {
|
|
1161
2249
|
throw new AppError("usage", "--session-id is no longer accepted on merge-queue commands; use --worker-id", 2);
|
|
@@ -1179,32 +2267,17 @@ function dispatchMergeQueue(context, command, parsed, scopeProjectRoot) {
|
|
|
1179
2267
|
});
|
|
1180
2268
|
}
|
|
1181
2269
|
if (command === "wait-next") {
|
|
1182
|
-
return waitNextMergeJob(context,
|
|
1183
|
-
projectRoot: requiredOptionOrScope(parsed, scopeProjectRoot, "project-root"),
|
|
1184
|
-
workerId: requiredWorkerId(parsed),
|
|
1185
|
-
timeoutSeconds: optionalNumber(parsed, "timeout"),
|
|
1186
|
-
pollIntervalSeconds: optionalNumber(parsed, "poll-interval"),
|
|
1187
|
-
workspacePath: workspacePathOption(parsed),
|
|
1188
|
-
acquireLock: parseOptionalBoolean(optionalOption(parsed, "acquire-lock"), "acquire-lock") ?? false
|
|
1189
|
-
});
|
|
2270
|
+
return waitNextMergeJob(context, mergeWaitInput(parsed, scopeProjectRoot));
|
|
1190
2271
|
}
|
|
1191
2272
|
if (command === "complete") {
|
|
1192
|
-
const protocolId = requiredPosition(parsed, 0, "protocol-id");
|
|
1193
2273
|
return completeMergeJob(context, {
|
|
1194
|
-
|
|
1195
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue complete"),
|
|
1196
|
-
workerId: requiredWorkerId(parsed),
|
|
1197
|
-
workspacePath: workspacePathOption(parsed),
|
|
2274
|
+
...mergeJobTarget(parsed, scopeProjectRoot),
|
|
1198
2275
|
summary: requiredOption(parsed, "summary")
|
|
1199
2276
|
});
|
|
1200
2277
|
}
|
|
1201
2278
|
if (command === "fail") {
|
|
1202
|
-
const protocolId = requiredPosition(parsed, 0, "protocol-id");
|
|
1203
2279
|
return failMergeJob(context, {
|
|
1204
|
-
|
|
1205
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue fail"),
|
|
1206
|
-
workerId: requiredWorkerId(parsed),
|
|
1207
|
-
workspacePath: workspacePathOption(parsed),
|
|
2280
|
+
...mergeJobTarget(parsed, scopeProjectRoot),
|
|
1208
2281
|
reason: requiredOption(parsed, "reason"),
|
|
1209
2282
|
requeue: parseBoolean(requiredOption(parsed, "requeue"), "requeue")
|
|
1210
2283
|
});
|
|
@@ -1219,15 +2292,7 @@ function dispatchMergeQueue(context, command, parsed, scopeProjectRoot) {
|
|
|
1219
2292
|
});
|
|
1220
2293
|
}
|
|
1221
2294
|
if (command === "cancel") {
|
|
1222
|
-
|
|
1223
|
-
return cancelMergeQueueJob(context, {
|
|
1224
|
-
protocolId,
|
|
1225
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue cancel"),
|
|
1226
|
-
reason: requiredOption(parsed, "reason"),
|
|
1227
|
-
workerId: optionalOption(parsed, "worker-id"),
|
|
1228
|
-
workspacePath: optionalOption(parsed, "path"),
|
|
1229
|
-
force: hasOption(parsed, "force")
|
|
1230
|
-
});
|
|
2295
|
+
return cancelMergeQueueJob(context, mergeCancellationInput(parsed, scopeProjectRoot));
|
|
1231
2296
|
}
|
|
1232
2297
|
throw new AppError("usage", `Unknown merge-queue command: ${command ?? "<empty>"}`, 2);
|
|
1233
2298
|
}
|
|
@@ -1326,7 +2391,7 @@ function dispatchMergeWorker(context, command, parsed) {
|
|
|
1326
2391
|
}
|
|
1327
2392
|
throw new AppError("usage", `Unknown merge-worker command: ${command ?? "<empty>"}`, 2);
|
|
1328
2393
|
}
|
|
1329
|
-
function dispatchMemory(command, parsed) {
|
|
2394
|
+
function dispatchMemory(context, command, parsed, prepared) {
|
|
1330
2395
|
const subcommand = requiredPosition(parsed, 0, "memory subcommand");
|
|
1331
2396
|
if (command === "permissions" && subcommand === "preflight") {
|
|
1332
2397
|
return preflightMemoryPermissions({
|
|
@@ -1337,6 +2402,103 @@ function dispatchMemory(command, parsed) {
|
|
|
1337
2402
|
mode: requiredOption(parsed, "mode")
|
|
1338
2403
|
});
|
|
1339
2404
|
}
|
|
2405
|
+
const scope = () => {
|
|
2406
|
+
const projectRoot = requiredOption(parsed, "project-root");
|
|
2407
|
+
const project = requireProjectByRoot(context, resolveProjectRoot(projectRoot));
|
|
2408
|
+
return { projectRoot, runId: resolveRun(context, project.id, requiredOption(parsed, "run")).id };
|
|
2409
|
+
};
|
|
2410
|
+
if (command === "repair") {
|
|
2411
|
+
const wave = requiredOption(parsed, "wave");
|
|
2412
|
+
if (wave !== "1" && wave !== "2")
|
|
2413
|
+
throw new AppError("usage", "Memory repairs support only waves 1 and 2", 2);
|
|
2414
|
+
const work = { ...scope(), workId: requiredOption(parsed, "work") };
|
|
2415
|
+
if (subcommand === "baseline")
|
|
2416
|
+
return (wave === "1" ? registerMemoryRepairBaselineFromStage : registerMemorySecondRepairBaselineFromStage)(context, work);
|
|
2417
|
+
if (subcommand === "copy-reserve")
|
|
2418
|
+
return (wave === "1" ? reserveMemoryRepairWriterCopyFromStage : reserveMemorySecondRepairCopyFromStage)(context, work);
|
|
2419
|
+
if (subcommand === "copy-materialize")
|
|
2420
|
+
return (wave === "1" ? materializeMemoryRepairWriterCopyFromStage : materializeMemorySecondRepairCopyFromStage)(context, work);
|
|
2421
|
+
if (subcommand === "copy-dispatch")
|
|
2422
|
+
return (wave === "1" ? prepareMemoryRepairDispatchFromStage : prepareMemorySecondRepairDispatchFromStage)(context, work);
|
|
2423
|
+
const workspaceRoot = requiredOption(parsed, "workspace-root");
|
|
2424
|
+
if (wave === "2") {
|
|
2425
|
+
const bundle = readFlowRunExecutionBundle(context, work);
|
|
2426
|
+
if (!bundle || bundle.descriptor.descriptor.id !== "mb-init" || bundle.execution.snapshot().scope.workspace !== workspaceRoot)
|
|
2427
|
+
throw new AppError("memory_repair_scope_invalid", "Repair workspace does not match its frozen memory RUN", 2, { phase: "prepare", effect: "no_effect" });
|
|
2428
|
+
}
|
|
2429
|
+
if (subcommand === "current-view")
|
|
2430
|
+
return wave === "1"
|
|
2431
|
+
? registerMemoryRepairCurrentViewFromStage(context, { ...work, workspaceRoot })
|
|
2432
|
+
: registerMemorySecondRepairCurrentViewFromStage(context, work);
|
|
2433
|
+
const output = { ...work, workspaceRoot, workSessionId: requiredOption(parsed, "work-session") };
|
|
2434
|
+
if (subcommand === "publication-source" && wave === "1")
|
|
2435
|
+
return registerMemoryRepairPublicationSourceFromStage(context, output);
|
|
2436
|
+
if (subcommand === "publication-reserve") {
|
|
2437
|
+
if (!prepared.memoryOperationBytes)
|
|
2438
|
+
throw new AppError("cli_input_unprepared", "Memory repair output bytes were not prepared", 1);
|
|
2439
|
+
return (wave === "1" ? reserveMemoryRepairPublicationAgainstSourceFromStage : reserveMemorySecondRepairPublicationFromStage)(context, { ...output, bytes: prepared.memoryOperationBytes });
|
|
2440
|
+
}
|
|
2441
|
+
if (subcommand === "publication-apply")
|
|
2442
|
+
return (wave === "1" ? applyMemoryRepairPublicationFromStage : applyMemorySecondRepairPublicationFromStage)(context, output);
|
|
2443
|
+
if (subcommand === "publication-finish")
|
|
2444
|
+
return (wave === "1" ? finishMemoryRepairPublicationFromStage : finishMemorySecondRepairPublicationFromStage)(context, output);
|
|
2445
|
+
}
|
|
2446
|
+
if (command === "compose" && subcommand === "register") {
|
|
2447
|
+
if (!prepared.memoryOperationBytes)
|
|
2448
|
+
throw new AppError("cli_input_unprepared", "Memory composition bytes were not prepared", 1);
|
|
2449
|
+
try {
|
|
2450
|
+
return registerMemoryCompositionFromStage(context, { ...scope(), requestKey: requiredOption(parsed, "request-key"), bytes: prepared.memoryOperationBytes });
|
|
2451
|
+
}
|
|
2452
|
+
catch (error) {
|
|
2453
|
+
if (error instanceof AppError && (error.code === "memory_stage_result_invalid"
|
|
2454
|
+
|| error.code === "memory_preflight_handler_invalid" && ["unselected_preflight_source", "proposed_source_sha256_mismatch"].includes(String(error.details.reason))) && error.details.phase === "prepare"
|
|
2455
|
+
&& error.details.effect === "no_effect" && error.details.recoverable === true) {
|
|
2456
|
+
error.details.retry_command = runtimeCommand(context, ["memory", "compose", "register", "--run", requiredOption(parsed, "run"),
|
|
2457
|
+
"--project-root", requiredOption(parsed, "project-root"), "--request-key", requiredOption(parsed, "request-key"),
|
|
2458
|
+
"--result-file", requiredOption(parsed, "result-file"), "--json", "--progress-jsonl"]);
|
|
2459
|
+
error.details.retry_instruction = "Correct only the rejected composition proposal, then execute retry_command in this same Session. No Work graph was registered by this rejected call; do not dispatch children or finish the Stage until registration succeeds.";
|
|
2460
|
+
}
|
|
2461
|
+
throw error;
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
if (command === "verify" && subcommand === "checks")
|
|
2465
|
+
return runMemoryVerificationChecks(context, scope());
|
|
2466
|
+
if (command === "review" && subcommand === "register")
|
|
2467
|
+
return registerMemoryReviewFromStage(context, scope());
|
|
2468
|
+
if (command === "review" && subcommand === "collect") {
|
|
2469
|
+
const wave = collectMemoryInitialReviewFromStage(context, scope());
|
|
2470
|
+
const selected = scope(), bundle = readFlowRunExecutionBundle(context, selected);
|
|
2471
|
+
if (!bundle)
|
|
2472
|
+
throw new AppError("memory_review_handler_invalid", "Registered Memory bundle required", 2);
|
|
2473
|
+
const project = requireProjectByRoot(context, resolveProjectRoot(selected.projectRoot));
|
|
2474
|
+
const retained = PreparedRetainedMemoryReviewResults.prepare(context, PreparedRetainedMemoryReviewAuthority.prepare(context, { projectId: project.id, runId: selected.runId }, bundle));
|
|
2475
|
+
return { references: wave.references(), projection: wave.resultProjection(),
|
|
2476
|
+
correction_plan_binding: { schema_id: "dd-flow/memory-review-correction-plan@1", run_id: selected.runId, wave: 1, review_sha256: retained.sha256 } };
|
|
2477
|
+
}
|
|
2478
|
+
if (command === "finish" && subcommand === "evidence")
|
|
2479
|
+
return registerMemoryFinishEvidenceFromStage(context, scope());
|
|
2480
|
+
if (command === "finish" && subcommand === "integrate")
|
|
2481
|
+
return integrateMemoryFinishFromStage(context, scope());
|
|
2482
|
+
if (command === "corrections" || command === "review" && ["selective-register", "selective-collect"].includes(subcommand)) {
|
|
2483
|
+
const wave = requiredOption(parsed, "wave");
|
|
2484
|
+
if (wave !== "1" && wave !== "2")
|
|
2485
|
+
throw new AppError("usage", "Memory corrections support only waves 1 and 2", 2);
|
|
2486
|
+
if (command === "corrections" && subcommand === "register") {
|
|
2487
|
+
if (!prepared.memoryOperationBytes)
|
|
2488
|
+
throw new AppError("cli_input_unprepared", "Memory correction plan bytes were not prepared", 1);
|
|
2489
|
+
const register = wave === "1" ? registerMemoryCorrectionsFromStage : registerMemorySecondCorrectionsFromStage;
|
|
2490
|
+
return register(context, { ...scope(), bytes: prepared.memoryOperationBytes });
|
|
2491
|
+
}
|
|
2492
|
+
if (command === "corrections" && subcommand === "checks")
|
|
2493
|
+
return (wave === "1" ? runMemoryCorrectionChecksFromStage : runMemorySecondCorrectionChecksFromStage)(context, scope());
|
|
2494
|
+
if (command === "review" && subcommand === "selective-register")
|
|
2495
|
+
return (wave === "1" ? registerCorrectedMemoryReviewFromStage : registerSecondCorrectedMemoryReviewFromStage)(context, scope());
|
|
2496
|
+
if (command === "review" && subcommand === "selective-collect") {
|
|
2497
|
+
const accepted = (wave === "1" ? collectCorrectedMemoryReviewFromStage : collectSecondCorrectedMemoryReviewFromStage)(context, scope());
|
|
2498
|
+
return { wave: Number(wave), references: accepted.references(), projection: accepted.resultProjection(),
|
|
2499
|
+
...(wave === "1" ? { correction_plan_binding: { schema_id: "dd-flow/memory-review-correction-plan@1", run_id: scope().runId, wave: 2, review_sha256: accepted.sha256 } } : {}) };
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
1340
2502
|
throw new AppError("usage", `Unknown memory command: ${["memory", command, subcommand].filter(Boolean).join(" ")}`, 2);
|
|
1341
2503
|
}
|
|
1342
2504
|
function completedCommandExitCode(payload) {
|
|
@@ -1345,7 +2507,7 @@ function completedCommandExitCode(payload) {
|
|
|
1345
2507
|
const exitCode = payload.exit_code;
|
|
1346
2508
|
return typeof exitCode === "number" && Number.isInteger(exitCode) ? exitCode : 0;
|
|
1347
2509
|
}
|
|
1348
|
-
function dispatchCleanup(context, command, parsed) {
|
|
2510
|
+
function dispatchCleanup(context, command, parsed, prepared) {
|
|
1349
2511
|
if (command === "scan") {
|
|
1350
2512
|
return cleanupScan(context, { projectRoot: requiredOption(parsed, "project-root") });
|
|
1351
2513
|
}
|
|
@@ -1353,6 +2515,7 @@ function dispatchCleanup(context, command, parsed) {
|
|
|
1353
2515
|
return cleanupApply(context, {
|
|
1354
2516
|
projectRoot: requiredOption(parsed, "project-root"),
|
|
1355
2517
|
planFile: requiredOption(parsed, "plan-file"),
|
|
2518
|
+
prepared,
|
|
1356
2519
|
reason: requiredOption(parsed, "reason"),
|
|
1357
2520
|
force: hasOption(parsed, "force")
|
|
1358
2521
|
});
|
|
@@ -1373,12 +2536,14 @@ function dispatchProjectConfig(context, parsed) {
|
|
|
1373
2536
|
}
|
|
1374
2537
|
throw new AppError("usage", `Unknown project config action: ${action}`, 2);
|
|
1375
2538
|
}
|
|
1376
|
-
function dispatchSession(context, command, parsed) {
|
|
2539
|
+
function dispatchSession(context, command, parsed, preparedPayload) {
|
|
1377
2540
|
if (command === "register") {
|
|
1378
2541
|
return registerFlowSession(context, {
|
|
2542
|
+
...(preparedPayload ? { preparedPayload } : {}),
|
|
1379
2543
|
payloadBase64: optionalOption(parsed, "payload-base64"),
|
|
1380
2544
|
payloadJson: optionalOption(parsed, "payload-json"),
|
|
1381
2545
|
payloadFile: optionalOption(parsed, "payload-file"),
|
|
2546
|
+
hookEventId: optionalOption(parsed, "hook-event-id"),
|
|
1382
2547
|
sessionId: optionalOption(parsed, "session-id")
|
|
1383
2548
|
});
|
|
1384
2549
|
}
|
|
@@ -1411,7 +2576,47 @@ function dispatchSession(context, command, parsed) {
|
|
|
1411
2576
|
}
|
|
1412
2577
|
throw new AppError("usage", `Unknown session command: ${command ?? "<empty>"}`, 2);
|
|
1413
2578
|
}
|
|
1414
|
-
function
|
|
2579
|
+
function snapshotCreateInput(parsed) {
|
|
2580
|
+
const recoveryId = optionalOption(parsed, "recovery-id"), stageEntry = optionalOption(parsed, "stage-entry");
|
|
2581
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 1, "run-id"), output: requiredOption(parsed, "output"), candidate: parsed.options.has("candidate"), incomplete: parsed.options.has("incomplete"), ...(recoveryId !== undefined ? { recoveryId } : {}), ...(stageEntry !== undefined ? { stageEntry } : {}) };
|
|
2582
|
+
}
|
|
2583
|
+
function snapshotRestoreInput(parsed) {
|
|
2584
|
+
return { snapshot: requiredOption(parsed, "snapshot"), projectRoot: requiredOption(parsed, "project-root"), executionRoutingFile: optionalOption(parsed, "execution-routing-file"), operationalDecisionFile: optionalOption(parsed, "operational-decision-file"), ...(optionalOption(parsed, "recovery-id") ? { recoveryId: requiredOption(parsed, "recovery-id") } : {}) };
|
|
2585
|
+
}
|
|
2586
|
+
function runStartInput(parsed) {
|
|
2587
|
+
const protocolOverrides = readOptionalJsonFile(optionalOption(parsed, "protocol-override-file"));
|
|
2588
|
+
return { projectRoot: requiredOption(parsed, "project-root"), workspaceRoot: optionalOption(parsed, "workspace-root"), flowKind: requiredOption(parsed, "flow-kind"), subjectType: requiredOption(parsed, "subject-type"), subjectId: requiredOption(parsed, "subject-id"), slug: requiredOption(parsed, "slug"), nextAction: optionalOption(parsed, "next-action"), preset: optionalOption(parsed, "preset"), taskProfile: readOptionalJsonFile(optionalOption(parsed, "task-profile-file")), executionRoutingFile: optionalOption(parsed, "execution-routing-file"), ...(protocolOverrides !== undefined ? { protocolOverrides } : {}), runOverrides: parseFlowFlagOverrides(allOptions(parsed, "flag")) };
|
|
2589
|
+
}
|
|
2590
|
+
function runForkInput(parsed) {
|
|
2591
|
+
return { snapshot: requiredOption(parsed, "from"), output: requiredOption(parsed, "output"), engineVersion: requiredOption(parsed, "engine-version"), requestId: requiredOption(parsed, "request-id"), ...(optionalOption(parsed, "integrity-checksum") ? { integrityChecksum: optionalOption(parsed, "integrity-checksum") } : {}), ...(optionalOption(parsed, "operational-decision-file") ? { operationalDecisionFile: optionalOption(parsed, "operational-decision-file") } : {}) };
|
|
2592
|
+
}
|
|
2593
|
+
function recoveryCaptureInput(parsed) {
|
|
2594
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 1, "run-id"), recoveryId: requiredOption(parsed, "recovery-id"), capturePath: requiredOption(parsed, "capture-path") };
|
|
2595
|
+
}
|
|
2596
|
+
function recoveryResumeInput(parsed) {
|
|
2597
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 1, "run-id"), recoveryId: requiredOption(parsed, "recovery-id"), daemonId: requiredOption(parsed, "daemon-id"), harness: requiredOption(parsed, "harness"), nativeSessionId: requiredOption(parsed, "native-session-id") };
|
|
2598
|
+
}
|
|
2599
|
+
function dispatchRun(context, command, parsed, prepared = {}) {
|
|
2600
|
+
if (command === "reconcile")
|
|
2601
|
+
return reconcileFlowRunHealth(context, { runId: parsed.positional[0], projectRoot: optionalOption(parsed, "project-root"), apply: hasOption(parsed, "apply"), noProgressMs: optionalNumber(parsed, "no-progress-ms") });
|
|
2602
|
+
if (command === "recovery") {
|
|
2603
|
+
const action = requiredPosition(parsed, 0, "recovery action");
|
|
2604
|
+
const projectRoot = requiredOption(parsed, "project-root");
|
|
2605
|
+
const runId = requiredPosition(parsed, 1, "run-id");
|
|
2606
|
+
if (action === "inspect")
|
|
2607
|
+
return inspectRunRecovery(context, { projectRoot, runId });
|
|
2608
|
+
if (action === "begin")
|
|
2609
|
+
return beginRunRecovery(context, { projectRoot, runId, interruption: requiredOption(parsed, "interruption-json") }, prepared.recoveryPayload);
|
|
2610
|
+
if (action === "seal")
|
|
2611
|
+
return sealRunRecovery(context, { projectRoot, runId, recoveryId: requiredOption(parsed, "recovery-id"), settlement: requiredOption(parsed, "settlement-json") }, prepared.recoveryPayload);
|
|
2612
|
+
if (action === "capture")
|
|
2613
|
+
return recordRecoveryCapture(context, recoveryCaptureInput(parsed), prepared.recoveryCapture);
|
|
2614
|
+
if (action === "resume")
|
|
2615
|
+
return resumeRunRecovery(context, recoveryResumeInput(parsed), prepared.recoveryResume);
|
|
2616
|
+
if (action === "accept")
|
|
2617
|
+
return acceptRunRecovery(context, { projectRoot, runId, recoveryId: requiredOption(parsed, "recovery-id"), ...(optionalOption(parsed, "hook-event-id") ? { hookEventId: optionalOption(parsed, "hook-event-id") } : {}) });
|
|
2618
|
+
throw new AppError("usage", "Use: dd-flow run recovery inspect|begin|seal|capture|resume|accept", 2);
|
|
2619
|
+
}
|
|
1415
2620
|
if (command === "snapshot") {
|
|
1416
2621
|
const action = requiredPosition(parsed, 0, "snapshot action");
|
|
1417
2622
|
if (action === "bootstrap") {
|
|
@@ -1419,59 +2624,31 @@ function dispatchRun(context, command, parsed) {
|
|
|
1419
2624
|
if (bootstrapAction === "create")
|
|
1420
2625
|
return createEvalBootstrapSnapshot(context, {
|
|
1421
2626
|
projectRoot: requiredOption(parsed, "project-root"),
|
|
1422
|
-
output: requiredOption(parsed, "output")
|
|
2627
|
+
output: requiredOption(parsed, "output"),
|
|
2628
|
+
flow: optionalOption(parsed, "flow")
|
|
1423
2629
|
});
|
|
1424
2630
|
if (bootstrapAction === "restore")
|
|
1425
|
-
return restoreEvalBootstrapSnapshot(context,
|
|
1426
|
-
snapshot: requiredOption(parsed, "snapshot"),
|
|
1427
|
-
projectRoot: requiredOption(parsed, "project-root")
|
|
1428
|
-
});
|
|
2631
|
+
return restoreEvalBootstrapSnapshot(context, snapshotRestoreInput(parsed), prepared.bootstrapRestore);
|
|
1429
2632
|
throw new AppError("usage", "Use: dd-flow run snapshot bootstrap create|restore", 2);
|
|
1430
2633
|
}
|
|
1431
2634
|
if (action === "create") {
|
|
1432
|
-
|
|
1433
|
-
const stageEntry = optionalOption(parsed, "stage-entry");
|
|
1434
|
-
if (candidate && stageEntry !== undefined)
|
|
1435
|
-
throw new AppError("usage", "Use either --candidate or --stage-entry, not both", 2);
|
|
1436
|
-
if (!candidate && stageEntry === undefined)
|
|
1437
|
-
throw new AppError("usage", "Snapshot create requires --candidate or --stage-entry", 2);
|
|
1438
|
-
const input = {
|
|
1439
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1440
|
-
runId: requiredPosition(parsed, 1, "run-id"),
|
|
1441
|
-
output: requiredOption(parsed, "output")
|
|
1442
|
-
};
|
|
1443
|
-
return candidate
|
|
1444
|
-
? createEvalRunSnapshot(context, { ...input, candidate: true })
|
|
1445
|
-
: createEvalRunSnapshot(context, { ...input, stageEntry: stageEntry });
|
|
2635
|
+
return createEvalRunSnapshot(context, snapshotCreateInput(parsed));
|
|
1446
2636
|
}
|
|
1447
2637
|
if (action === "restore")
|
|
1448
|
-
return restoreEvalRunSnapshot(context,
|
|
1449
|
-
snapshot: requiredOption(parsed, "snapshot"),
|
|
1450
|
-
projectRoot: requiredOption(parsed, "project-root")
|
|
1451
|
-
});
|
|
2638
|
+
return restoreEvalRunSnapshot(context, snapshotRestoreInput(parsed), prepared.snapshotRestore);
|
|
1452
2639
|
throw new AppError("usage", "Use: dd-flow run snapshot bootstrap create|restore | dd-flow run snapshot create <RUN> (--stage-entry <stage>|--candidate) --project-root <path> --output <directory> | dd-flow run snapshot restore --snapshot <directory> --project-root <path>", 2);
|
|
1453
2640
|
}
|
|
2641
|
+
if (command === "fork") {
|
|
2642
|
+
return forkRunFromSnapshot(context, runForkInput(parsed), prepared.runFork);
|
|
2643
|
+
}
|
|
1454
2644
|
if (command === "prepare-vnext-specify") {
|
|
1455
|
-
return prepareVnextSpecifyRun(context, { projectRoot: requiredOption(parsed, "project-root"), slug: requiredOption(parsed, "slug") });
|
|
2645
|
+
return prepareVnextSpecifyRun(context, { projectRoot: requiredOption(parsed, "project-root"), slug: requiredOption(parsed, "slug"), executionRoutingFile: optionalOption(parsed, "execution-routing-file") });
|
|
1456
2646
|
}
|
|
1457
2647
|
if (command === "start") {
|
|
1458
2648
|
if (optionalOption(parsed, "flow-kind") === "mb-upgrade") {
|
|
1459
2649
|
assertUpgradeRunStorageReady(context, requiredOption(parsed, "project-root"));
|
|
1460
2650
|
}
|
|
1461
|
-
|
|
1462
|
-
return startFlowRun(context, {
|
|
1463
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1464
|
-
workspaceRoot: optionalOption(parsed, "workspace-root"),
|
|
1465
|
-
flowKind: requiredOption(parsed, "flow-kind"),
|
|
1466
|
-
subjectType: requiredOption(parsed, "subject-type"),
|
|
1467
|
-
subjectId: requiredOption(parsed, "subject-id"),
|
|
1468
|
-
slug: requiredOption(parsed, "slug"),
|
|
1469
|
-
nextAction: optionalOption(parsed, "next-action"),
|
|
1470
|
-
preset: optionalOption(parsed, "preset"),
|
|
1471
|
-
taskProfile: readOptionalJsonFile(optionalOption(parsed, "task-profile-file")),
|
|
1472
|
-
...(protocolOverrides ? { protocolOverrides } : {}),
|
|
1473
|
-
runOverrides: parseFlowFlagOverrides(allOptions(parsed, "flag"))
|
|
1474
|
-
});
|
|
2651
|
+
return startFlowRun(context, prepared.runStart?.input ?? runStartInput(parsed), prepared.runStart?.value);
|
|
1475
2652
|
}
|
|
1476
2653
|
if (command === "status") {
|
|
1477
2654
|
return getFlowRunStatus(context, {
|
|
@@ -1541,84 +2718,80 @@ function dispatchRun(context, command, parsed) {
|
|
|
1541
2718
|
throw new AppError("usage", `Unknown run flags command: ${action}`, 2);
|
|
1542
2719
|
}
|
|
1543
2720
|
if (command === "attach-stage") {
|
|
1544
|
-
return attachFlowRunStage(context,
|
|
1545
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1546
|
-
runId: requiredPosition(parsed, 0, "run-id"),
|
|
1547
|
-
stage: requiredOption(parsed, "stage"),
|
|
1548
|
-
dir: requiredOption(parsed, "dir"),
|
|
1549
|
-
status: requiredOption(parsed, "status"),
|
|
1550
|
-
dataSchemaId: optionalOption(parsed, "data-schema-id")
|
|
1551
|
-
});
|
|
2721
|
+
return attachFlowRunStage(context, stageAttachmentInput(parsed));
|
|
1552
2722
|
}
|
|
1553
2723
|
if (command === "complete-stage") {
|
|
1554
|
-
const
|
|
1555
|
-
return completeFlowRunStage(context,
|
|
1556
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1557
|
-
runId: requiredPosition(parsed, 0, "run-id"),
|
|
1558
|
-
stage: requiredOption(parsed, "stage"),
|
|
1559
|
-
status: requiredOption(parsed, "status"),
|
|
1560
|
-
stageReport,
|
|
1561
|
-
data: optionalOption(parsed, "data"),
|
|
1562
|
-
dataSchemaId: optionalOption(parsed, "data-schema-id"),
|
|
1563
|
-
report: optionalOption(parsed, "report"),
|
|
1564
|
-
aliases: allOptions(parsed, "alias")
|
|
1565
|
-
});
|
|
2724
|
+
const input = stageCompletionInput(parsed);
|
|
2725
|
+
return completeFlowRunStage(context, input, prepareRawFlowRunStageCompletion(context, input));
|
|
1566
2726
|
}
|
|
1567
2727
|
if (command === "complete") {
|
|
1568
2728
|
throw new AppError("usage", "run complete is retired; only an operator may use run override", 2);
|
|
1569
2729
|
}
|
|
1570
2730
|
if (command === "override") {
|
|
1571
|
-
|
|
1572
|
-
if (!['cancelled', 'failed'].includes(status)) {
|
|
1573
|
-
throw new AppError("usage", "run override status must be cancelled or failed", 2, { status });
|
|
1574
|
-
}
|
|
1575
|
-
return completeFlowRun(context, {
|
|
1576
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1577
|
-
runId: requiredPosition(parsed, 0, "run-id"),
|
|
1578
|
-
status,
|
|
1579
|
-
verdict: optionalOption(parsed, "verdict"),
|
|
1580
|
-
nextAction: optionalOption(parsed, "next-action"),
|
|
1581
|
-
manualOverrideReason: requiredOption(parsed, "reason")
|
|
1582
|
-
});
|
|
2731
|
+
return completeFlowRun(context, runOverrideInput(parsed));
|
|
1583
2732
|
}
|
|
1584
2733
|
throw new AppError("usage", `Unknown run command: ${command ?? "<empty>"}`, 2);
|
|
1585
2734
|
}
|
|
1586
|
-
|
|
2735
|
+
function stageAttachmentInput(parsed) {
|
|
2736
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), stage: requiredOption(parsed, "stage"), dir: requiredOption(parsed, "dir"), status: requiredOption(parsed, "status"), dataSchemaId: optionalOption(parsed, "data-schema-id") };
|
|
2737
|
+
}
|
|
2738
|
+
function runOverrideInput(parsed) {
|
|
2739
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), status: requiredOption(parsed, "status"), verdict: optionalOption(parsed, "verdict"), nextAction: optionalOption(parsed, "next-action"), manualOverrideReason: requiredOption(parsed, "reason") };
|
|
2740
|
+
}
|
|
2741
|
+
function stageCompletionInput(parsed) {
|
|
2742
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), stage: requiredOption(parsed, "stage"), status: requiredOption(parsed, "status"), stageReport: optionalOption(parsed, "stage-report") ?? optionalOption(parsed, "dashboard"), data: optionalOption(parsed, "data"), dataSchemaId: optionalOption(parsed, "data-schema-id"), report: optionalOption(parsed, "report"), aliases: allOptions(parsed, "alias") };
|
|
2743
|
+
}
|
|
2744
|
+
function runtimeServiceInput(parsed) {
|
|
2745
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run"), command: requiredOption(parsed, "command"), ports: requiredOption(parsed, "ports").split(","), readyPort: requiredOption(parsed, "ready-port"), readyPath: optionalOption(parsed, "ready-path") ?? "/", timeoutMs: optionalPositiveNumber(parsed, "ready-timeout-ms", true) ?? 30_000 };
|
|
2746
|
+
}
|
|
2747
|
+
function runtimeScopeResumeInput(parsed) {
|
|
2748
|
+
return { scopeId: requiredOption(parsed, "scope-id"), requestId: requiredOption(parsed, "request-id"), generation: Number(requiredOption(parsed, "generation")), captureKey: requiredOption(parsed, "capture-key") };
|
|
2749
|
+
}
|
|
2750
|
+
async function dispatchRuntime(context, command, parsed, progress, prepared = {}) {
|
|
2751
|
+
if (command === "scope") {
|
|
2752
|
+
if (parsed.positional.length === 1 && parsed.positional[0] === "reconcile") {
|
|
2753
|
+
if ([...parsed.options.keys()].some(key => !["scope-id", "request-id", "generation"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
2754
|
+
throw new AppError("usage", "Use runtime scope reconcile --scope-id ID --generation N --request-id ID", 2);
|
|
2755
|
+
return { ok: true, ...await reconcileRuntimeScopeControl(context, requiredOption(parsed, "scope-id"), Number(requiredOption(parsed, "generation")), requiredOption(parsed, "request-id")) };
|
|
2756
|
+
}
|
|
2757
|
+
if (parsed.positional.length === 1 && parsed.positional[0] === "resume") {
|
|
2758
|
+
if ([...parsed.options.keys()].some(key => !["scope-id", "request-id", "generation", "capture-key"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
2759
|
+
throw new AppError("usage", "Use runtime scope resume --scope-id ID --request-id ID --generation N --capture-key SHA256", 2);
|
|
2760
|
+
return { ok: true, ...await launchRuntimeScopeResume(context, runtimeScopeResumeInput(parsed)) };
|
|
2761
|
+
}
|
|
2762
|
+
if (parsed.positional.length === 1 && parsed.positional[0] === "control") {
|
|
2763
|
+
if ([...parsed.options.keys()].some(key => !["scope-id", "request-id", "mode", "manifest-json"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
2764
|
+
throw new AppError("usage", "Use runtime scope control --scope-id ID --request-id ID --mode pause|stop [--manifest-json JSON]", 2);
|
|
2765
|
+
const mode = requiredOption(parsed, "mode");
|
|
2766
|
+
if (mode !== "pause" && mode !== "stop")
|
|
2767
|
+
throw new AppError("usage", "Scope control mode must be pause or stop", 2);
|
|
2768
|
+
return { ok: true, ...await launchRuntimeScopeControl(context, requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id"), mode, undefined, undefined, undefined, prepared.runtimeManifest) };
|
|
2769
|
+
}
|
|
2770
|
+
if (parsed.positional.length === 1 && parsed.positional[0] === "serve") {
|
|
2771
|
+
if ([...parsed.options.keys()].some(key => !["scope-id", "generation"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
2772
|
+
throw new AppError("usage", "Invalid internal scope worker arguments", 2);
|
|
2773
|
+
return { ok: true, ...await serveRuntimeScopeControl(context, requiredOption(parsed, "scope-id"), Number(requiredOption(parsed, "generation"))) };
|
|
2774
|
+
}
|
|
2775
|
+
if (parsed.positional.length === 1 && parsed.positional[0] === "stop") {
|
|
2776
|
+
if ([...parsed.options.keys()].some(key => !["scope-id", "request-id"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
2777
|
+
throw new AppError("usage", "Usage: dd-flow runtime scope stop --scope-id EVAL_ID --request-id ID", 2);
|
|
2778
|
+
return { ok: true, ...await stopRuntimeScope(context, requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id")) };
|
|
2779
|
+
}
|
|
2780
|
+
if (parsed.positional.length === 1 && parsed.positional[0] === "status") {
|
|
2781
|
+
if ([...parsed.options.keys()].some(key => key !== "scope-id") || [...parsed.options.values()].some(values => values.length !== 1))
|
|
2782
|
+
throw new AppError("usage", "Usage: dd-flow runtime scope status --scope-id EVAL_ID", 2);
|
|
2783
|
+
return { ok: true, ...runtimeBudgetStatus(context, requiredOption(parsed, "scope-id")) };
|
|
2784
|
+
}
|
|
2785
|
+
if (parsed.positional.length !== 1 || parsed.positional[0] !== "fence" || [...parsed.options.keys()].some(key => !["scope-id", "request-id"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
2786
|
+
throw new AppError("usage", "Usage: dd-flow runtime scope fence --scope-id EVAL_ID --request-id ID", 2);
|
|
2787
|
+
return { ok: true, ...fenceRuntimeBudget(context, requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id")) };
|
|
2788
|
+
}
|
|
1587
2789
|
if (command !== "process")
|
|
1588
|
-
throw new AppError("usage", "Usage: dd-flow runtime process
|
|
2790
|
+
throw new AppError("usage", "Usage: dd-flow runtime <scope|process>; see dd-flow runtime scope --help", 2);
|
|
1589
2791
|
const action = requiredPosition(parsed, 0, "runtime process action");
|
|
1590
|
-
if (action === "
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
kind: requiredOption(parsed, "kind"), ownerId: requiredOption(parsed, "owner"),
|
|
1594
|
-
ownerPid: optionalPositiveNumber(parsed, "owner-pid", true),
|
|
1595
|
-
projectId: optionalOption(parsed, "project-id") ?? null, runId: optionalOption(parsed, "run") ?? null,
|
|
1596
|
-
workId: optionalOption(parsed, "work") ?? null, checkId: optionalOption(parsed, "check") ?? null,
|
|
1597
|
-
operationId: optionalOption(parsed, "operation") ?? null, stdoutPath: optionalOption(parsed, "stdout") ?? null,
|
|
1598
|
-
stderrPath: optionalOption(parsed, "stderr") ?? null, ...(leaseMs ? { leaseMs } : {})
|
|
1599
|
-
});
|
|
1600
|
-
return { ok: true, process };
|
|
1601
|
-
}
|
|
1602
|
-
if (action === "confirm") {
|
|
1603
|
-
const leaseMs = optionalPositiveNumber(parsed, "lease-ms", true);
|
|
1604
|
-
const processGroupId = optionalPositiveNumber(parsed, "process-group-id", true);
|
|
1605
|
-
return { ok: true, process: confirmManagedProcess(context, { id: requiredOption(parsed, "id"), leaseToken: requiredOption(parsed, "lease-token"), ownerPid: optionalPositiveNumber(parsed, "owner-pid", true), pid: optionalPositiveNumber(parsed, "pid", true) ?? 0, ...(processGroupId ? { processGroupId } : {}), ...(leaseMs ? { leaseMs } : {}) }) };
|
|
1606
|
-
}
|
|
1607
|
-
if (action === "heartbeat") {
|
|
1608
|
-
const leaseMs = optionalPositiveNumber(parsed, "lease-ms", true);
|
|
1609
|
-
return { ok: heartbeatManagedProcess(context, { id: requiredOption(parsed, "id"), leaseToken: requiredOption(parsed, "lease-token"), ...(leaseMs ? { leaseMs } : {}) }) };
|
|
1610
|
-
}
|
|
1611
|
-
if (action === "finish") {
|
|
1612
|
-
const state = requiredOption(parsed, "state");
|
|
1613
|
-
if (state !== "stopped" && state !== "failed")
|
|
1614
|
-
throw new AppError("validation", "--state must be stopped or failed", 2);
|
|
1615
|
-
return { ok: finishManagedProcess(context, { id: requiredOption(parsed, "id"), leaseToken: requiredOption(parsed, "lease-token"), state: state, reason: optionalOption(parsed, "reason") ?? null }) };
|
|
1616
|
-
}
|
|
1617
|
-
if (action === "status")
|
|
1618
|
-
return { ok: true, processes: managedProcessStatus(context) };
|
|
1619
|
-
if (action === "reconcile")
|
|
1620
|
-
return { ok: true, processes: await reconcileExpiredManagedProcesses(context, requiredOption(parsed, "owner"), optionalPositiveNumber(parsed, "grace-ms", true)) };
|
|
1621
|
-
throw new AppError("usage", "Usage: dd-flow runtime process <register|confirm|heartbeat|finish|status|reconcile>", 2);
|
|
2792
|
+
if (action === "start")
|
|
2793
|
+
return await startRuntimeService(context, { ...runtimeServiceInput(parsed), progress });
|
|
2794
|
+
return await dispatchRuntimeProcess(context, action, parsed, prepared);
|
|
1622
2795
|
}
|
|
1623
2796
|
function dispatchStat(context, command, parsed) {
|
|
1624
2797
|
if (command === "usage") {
|
|
@@ -1652,7 +2825,7 @@ function dispatchCmux(context, parsed) {
|
|
|
1652
2825
|
}
|
|
1653
2826
|
throw new AppError("usage", `Unknown integration cmux action: ${action}`, 2);
|
|
1654
2827
|
}
|
|
1655
|
-
function dispatchDashboard(context, command, parsed) {
|
|
2828
|
+
async function dispatchDashboard(context, command, parsed) {
|
|
1656
2829
|
const action = dashboardAction(command);
|
|
1657
2830
|
if (command === "data") {
|
|
1658
2831
|
const target = resolveDashboardTarget(context, {
|
|
@@ -1687,7 +2860,7 @@ function dispatchDashboard(context, command, parsed) {
|
|
|
1687
2860
|
projectRoot: optionalOption(parsed, "project-root"),
|
|
1688
2861
|
protocol: optionalOption(parsed, "protocol")
|
|
1689
2862
|
});
|
|
1690
|
-
return withDashboardGuidance(action, target, openDashboardForResolvedTarget(context, target, parsed));
|
|
2863
|
+
return withDashboardGuidance(action, target, await openDashboardForResolvedTarget(context, target, parsed));
|
|
1691
2864
|
}
|
|
1692
2865
|
if (command === "refresh") {
|
|
1693
2866
|
const open = optionalOption(parsed, "open");
|
|
@@ -1701,7 +2874,7 @@ function dispatchDashboard(context, command, parsed) {
|
|
|
1701
2874
|
protocol: optionalOption(parsed, "protocol"),
|
|
1702
2875
|
all: hasOption(parsed, "all")
|
|
1703
2876
|
});
|
|
1704
|
-
return withDashboardGuidance(action, target, refreshDashboardForResolvedTarget(context, target, parsed, open));
|
|
2877
|
+
return withDashboardGuidance(action, target, await refreshDashboardForResolvedTarget(context, target, parsed, open));
|
|
1705
2878
|
}
|
|
1706
2879
|
if (command === "refresh-global") {
|
|
1707
2880
|
const target = resolveDashboardTarget(context, { action: "refresh", global: true });
|
|
@@ -1805,7 +2978,9 @@ function dispatchSchema(context, command, parsed) {
|
|
|
1805
2978
|
// searching project directories: run_id is a structural field in the
|
|
1806
2979
|
// submitted artifact and the database owns its materialized run_root.
|
|
1807
2980
|
const explicitRunId = optionalOption(parsed, "run");
|
|
1808
|
-
const
|
|
2981
|
+
const data = prepareJsonFile(file, "schema input").value;
|
|
2982
|
+
const declared = data && typeof data === "object" && "run_id" in data ? data.run_id : undefined;
|
|
2983
|
+
const declaredRunId = explicitRunId ?? (typeof declared === "string" && declared.trim() ? declared : undefined);
|
|
1809
2984
|
const fileRunRoot = path.basename(file) === "run.json" ? path.dirname(path.resolve(file)) : undefined;
|
|
1810
2985
|
const bound = projectRoot
|
|
1811
2986
|
? context.db.get("SELECT r.id, r.run_root FROM runs r JOIN projects p ON p.id = r.project_id WHERE p.root = ? AND (r.id = ? OR r.run_root = ?) LIMIT 1", [fs.realpathSync(projectRoot), declaredRunId ?? "", fileRunRoot ?? ""])
|
|
@@ -1813,6 +2988,7 @@ function dispatchSchema(context, command, parsed) {
|
|
|
1813
2988
|
return validateSchema({
|
|
1814
2989
|
schemaName: requiredOption(parsed, "schema"),
|
|
1815
2990
|
file,
|
|
2991
|
+
data,
|
|
1816
2992
|
...(schemaDir ? { schemaDir } : {}),
|
|
1817
2993
|
...(projectRoot ? { projectRoot } : {}),
|
|
1818
2994
|
...(declaredRunId || bound?.id ? { runId: declaredRunId ?? bound.id } : {}),
|
|
@@ -1822,14 +2998,11 @@ function dispatchSchema(context, command, parsed) {
|
|
|
1822
2998
|
}
|
|
1823
2999
|
throw new AppError("usage", `Unknown schema command: ${command ?? "<empty>"}`, 2);
|
|
1824
3000
|
}
|
|
1825
|
-
function
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
}
|
|
1830
|
-
catch {
|
|
1831
|
-
return undefined;
|
|
1832
|
-
}
|
|
3001
|
+
function laneWorkspaceInput(parsed) {
|
|
3002
|
+
return { projectRoot: requiredOption(parsed, "project-root"), lane: requiredOption(parsed, "lane"), workspacePath: requiredOption(parsed, "path"), branch: optionalOption(parsed, "branch") };
|
|
3003
|
+
}
|
|
3004
|
+
function laneWaiterCancelInput(parsed) {
|
|
3005
|
+
return { projectRoot: requiredOption(parsed, "project-root"), lane: requiredOption(parsed, "lane"), workerId: requiredOption(parsed, "worker-id"), reason: requiredOption(parsed, "reason") };
|
|
1833
3006
|
}
|
|
1834
3007
|
async function dispatchLane(context, command, parsed) {
|
|
1835
3008
|
if (command === "status") {
|
|
@@ -1841,24 +3014,14 @@ async function dispatchLane(context, command, parsed) {
|
|
|
1841
3014
|
if (command === "waiter") {
|
|
1842
3015
|
const action = requiredPosition(parsed, 0, "lane waiter action");
|
|
1843
3016
|
if (action === "cancel") {
|
|
1844
|
-
return cancelLaneWaiter(context,
|
|
1845
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1846
|
-
lane: requiredOption(parsed, "lane"),
|
|
1847
|
-
workerId: requiredOption(parsed, "worker-id"),
|
|
1848
|
-
reason: requiredOption(parsed, "reason")
|
|
1849
|
-
});
|
|
3017
|
+
return cancelLaneWaiter(context, laneWaiterCancelInput(parsed));
|
|
1850
3018
|
}
|
|
1851
3019
|
throw new AppError("usage", `Unknown lane waiter action: ${action}`, 2);
|
|
1852
3020
|
}
|
|
1853
3021
|
if (command === "workspace") {
|
|
1854
3022
|
const action = requiredPosition(parsed, 0, "lane workspace action");
|
|
1855
3023
|
if (action === "set") {
|
|
1856
|
-
return setLaneWorkspace(context,
|
|
1857
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1858
|
-
lane: requiredOption(parsed, "lane"),
|
|
1859
|
-
workspacePath: requiredOption(parsed, "path"),
|
|
1860
|
-
branch: optionalOption(parsed, "branch")
|
|
1861
|
-
});
|
|
3024
|
+
return setLaneWorkspace(context, laneWorkspaceInput(parsed));
|
|
1862
3025
|
}
|
|
1863
3026
|
if (action === "check") {
|
|
1864
3027
|
return checkLaneWorkspace(context, {
|
|
@@ -1930,7 +3093,7 @@ async function dispatchLane(context, command, parsed) {
|
|
|
1930
3093
|
}
|
|
1931
3094
|
throw new AppError("usage", `Unknown lane command: ${command ?? "<empty>"}`, 2);
|
|
1932
3095
|
}
|
|
1933
|
-
function dispatchCodexHooks(context, parsed) {
|
|
3096
|
+
function dispatchCodexHooks(context, parsed, prepared) {
|
|
1934
3097
|
const action = requiredPosition(parsed, 0, "codex hooks action");
|
|
1935
3098
|
if (action === "print") {
|
|
1936
3099
|
return printCodexHooks(context, {
|
|
@@ -1952,7 +3115,7 @@ function dispatchCodexHooks(context, parsed) {
|
|
|
1952
3115
|
target: optionalOption(parsed, "target"),
|
|
1953
3116
|
profile: optionalOption(parsed, "profile"),
|
|
1954
3117
|
yes: hasOption(parsed, "yes")
|
|
1955
|
-
});
|
|
3118
|
+
}, prepared.codexHooks);
|
|
1956
3119
|
}
|
|
1957
3120
|
if (action === "remove") {
|
|
1958
3121
|
return removeCodexHooks(context, {
|
|
@@ -1960,11 +3123,14 @@ function dispatchCodexHooks(context, parsed) {
|
|
|
1960
3123
|
target: optionalOption(parsed, "target"),
|
|
1961
3124
|
profile: optionalOption(parsed, "profile"),
|
|
1962
3125
|
yes: hasOption(parsed, "yes")
|
|
1963
|
-
});
|
|
3126
|
+
}, prepared.codexHooks);
|
|
1964
3127
|
}
|
|
1965
3128
|
throw new AppError("usage", `Unknown codex hooks action: ${action}`, 2);
|
|
1966
3129
|
}
|
|
1967
|
-
function
|
|
3130
|
+
function codexHomeInput(parsed) {
|
|
3131
|
+
return { projectRoot: requiredOption(parsed, "project-root"), profile: optionalOption(parsed, "profile"), sourceHome: optionalOption(parsed, "source-home"), targetHome: optionalOption(parsed, "target-home") };
|
|
3132
|
+
}
|
|
3133
|
+
function dispatchCodexHome(context, parsed, prepared) {
|
|
1968
3134
|
const action = requiredPosition(parsed, 0, "codex home action");
|
|
1969
3135
|
if (action === "plan") {
|
|
1970
3136
|
return planCodexHome(context, {
|
|
@@ -1975,12 +3141,7 @@ function dispatchCodexHome(context, parsed) {
|
|
|
1975
3141
|
});
|
|
1976
3142
|
}
|
|
1977
3143
|
if (action === "init") {
|
|
1978
|
-
return initCodexHome(context,
|
|
1979
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1980
|
-
profile: optionalOption(parsed, "profile"),
|
|
1981
|
-
sourceHome: optionalOption(parsed, "source-home"),
|
|
1982
|
-
targetHome: optionalOption(parsed, "target-home")
|
|
1983
|
-
});
|
|
3144
|
+
return initCodexHome(context, codexHomeInput(parsed), prepared.codexHome);
|
|
1984
3145
|
}
|
|
1985
3146
|
if (action === "status") {
|
|
1986
3147
|
return getCodexHomeStatus(context, {
|
|
@@ -2003,23 +3164,6 @@ function dispatchCodexHome(context, parsed) {
|
|
|
2003
3164
|
}
|
|
2004
3165
|
throw new AppError("usage", `Unknown codex home action: ${action}`, 2);
|
|
2005
3166
|
}
|
|
2006
|
-
function dispatchCodexHook(context, parsed, stdin) {
|
|
2007
|
-
const action = requiredPosition(parsed, 0, "codex hook action");
|
|
2008
|
-
if (action === "handle") {
|
|
2009
|
-
const isolatedHome = isolatedDdFlowHomeFromHookStdin(stdin);
|
|
2010
|
-
const hookContext = isolatedHome ? createContext({ ...context.env, DD_FLOW_HOME: isolatedHome }) : context;
|
|
2011
|
-
const result = handleCodexHook(hookContext, {
|
|
2012
|
-
event: requiredOption(parsed, "event"),
|
|
2013
|
-
projectRoot: optionalOption(parsed, "project-root"),
|
|
2014
|
-
stdin
|
|
2015
|
-
});
|
|
2016
|
-
if (result && typeof result === "object" && !Array.isArray(result) && "hookSpecificOutput" in result) {
|
|
2017
|
-
return { hookSpecificOutput: result.hookSpecificOutput };
|
|
2018
|
-
}
|
|
2019
|
-
return {};
|
|
2020
|
-
}
|
|
2021
|
-
throw new AppError("usage", `Unknown codex hook action: ${action}`, 2);
|
|
2022
|
-
}
|
|
2023
3167
|
function dispatchZcodeEvent(context, parsed, stdin) {
|
|
2024
3168
|
const action = requiredPosition(parsed, 0, "zcode event action");
|
|
2025
3169
|
if (action === "handle") {
|
|
@@ -2030,6 +3174,15 @@ function dispatchZcodeEvent(context, parsed, stdin) {
|
|
|
2030
3174
|
}
|
|
2031
3175
|
throw new AppError("usage", `Unknown zcode event action: ${action}`, 2);
|
|
2032
3176
|
}
|
|
3177
|
+
function dispatchZcodeHook(context, parsed, stdin) {
|
|
3178
|
+
if (requiredPosition(parsed, 0, "zcode hook action") !== "handle")
|
|
3179
|
+
throw new AppError("usage", "Unknown zcode hook action", 2);
|
|
3180
|
+
const result = handleZcodeHook(context, { projectRoot: requiredOption(parsed, "project-root"), stdin });
|
|
3181
|
+
if (result && typeof result === "object" && !Array.isArray(result) && "hookSpecificOutput" in result) {
|
|
3182
|
+
return { hookSpecificOutput: result.hookSpecificOutput };
|
|
3183
|
+
}
|
|
3184
|
+
return {};
|
|
3185
|
+
}
|
|
2033
3186
|
function dispatchGrokEvent(context, parsed, stdin) {
|
|
2034
3187
|
const action = requiredPosition(parsed, 0, "grok event action");
|
|
2035
3188
|
if (action === "handle") {
|
|
@@ -2053,7 +3206,7 @@ function dispatchAgyEvent(context, parsed, stdin) {
|
|
|
2053
3206
|
return handleAgyEvent(context, { projectRoot: requiredOption(parsed, "project-root"), stdin });
|
|
2054
3207
|
throw new AppError("usage", `Unknown agy event action: ${action}`, 2);
|
|
2055
3208
|
}
|
|
2056
|
-
function dispatchWorktree(context, command, parsed, scopeProjectRoot) {
|
|
3209
|
+
function dispatchWorktree(context, command, parsed, scopeProjectRoot, prepared) {
|
|
2057
3210
|
if (command === "plan") {
|
|
2058
3211
|
return planWorktree(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: scopeProjectRoot });
|
|
2059
3212
|
}
|
|
@@ -2064,13 +3217,13 @@ function dispatchWorktree(context, command, parsed, scopeProjectRoot) {
|
|
|
2064
3217
|
branch: requiredOption(parsed, "branch"),
|
|
2065
3218
|
base: requiredOption(parsed, "base"),
|
|
2066
3219
|
path: requiredOption(parsed, "path")
|
|
2067
|
-
});
|
|
3220
|
+
}, prepared.worktreeCreate);
|
|
2068
3221
|
}
|
|
2069
3222
|
if (command === "status") {
|
|
2070
3223
|
return getWorktreeStatus(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: scopeProjectRoot });
|
|
2071
3224
|
}
|
|
2072
3225
|
if (command === "bootstrap") {
|
|
2073
|
-
return bootstrapWorktree(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: scopeProjectRoot });
|
|
3226
|
+
return bootstrapWorktree(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: scopeProjectRoot }, prepared.worktreeBootstrap);
|
|
2074
3227
|
}
|
|
2075
3228
|
if (command === "close") {
|
|
2076
3229
|
return closeWorktree(context, {
|
|
@@ -2082,52 +3235,50 @@ function dispatchWorktree(context, command, parsed, scopeProjectRoot) {
|
|
|
2082
3235
|
throw new AppError("usage", `Unknown worktree command: ${command ?? "<empty>"}`, 2);
|
|
2083
3236
|
}
|
|
2084
3237
|
function dispatchPlanItem(context, parsed, projectRoot) {
|
|
3238
|
+
return updatePlanItem(context, planItemInput(parsed, projectRoot));
|
|
3239
|
+
}
|
|
3240
|
+
function planItemInput(parsed, projectRoot) {
|
|
2085
3241
|
const action = requiredPosition(parsed, 0, "plan item action");
|
|
2086
3242
|
const protocolId = requiredPosition(parsed, 1, "protocol-id");
|
|
2087
3243
|
const itemId = requiredPosition(parsed, 2, "item-id");
|
|
2088
3244
|
if (action === "start") {
|
|
2089
|
-
return
|
|
3245
|
+
return { protocolId, projectRoot, itemId, action };
|
|
2090
3246
|
}
|
|
2091
3247
|
if (action === "done") {
|
|
2092
|
-
return
|
|
3248
|
+
return {
|
|
3249
|
+
action,
|
|
2093
3250
|
protocolId,
|
|
2094
3251
|
projectRoot,
|
|
2095
3252
|
itemId,
|
|
2096
3253
|
summary: requiredOption(parsed, "summary"),
|
|
2097
3254
|
evidence: parsed.options.get("evidence") ?? []
|
|
2098
|
-
}
|
|
3255
|
+
};
|
|
2099
3256
|
}
|
|
2100
3257
|
if (action === "block") {
|
|
2101
|
-
return
|
|
3258
|
+
return {
|
|
3259
|
+
action,
|
|
2102
3260
|
protocolId,
|
|
2103
3261
|
projectRoot,
|
|
2104
3262
|
itemId,
|
|
2105
3263
|
reason: requiredOption(parsed, "reason"),
|
|
2106
3264
|
userRequired: parseBoolean(requiredOption(parsed, "user-required"), "user-required")
|
|
2107
|
-
}
|
|
3265
|
+
};
|
|
2108
3266
|
}
|
|
2109
3267
|
if (action === "skip") {
|
|
2110
|
-
return
|
|
3268
|
+
return {
|
|
3269
|
+
action,
|
|
2111
3270
|
protocolId,
|
|
2112
3271
|
projectRoot,
|
|
2113
3272
|
itemId,
|
|
2114
3273
|
reason: requiredOption(parsed, "reason")
|
|
2115
|
-
}
|
|
3274
|
+
};
|
|
2116
3275
|
}
|
|
2117
3276
|
throw new AppError("usage", `Unknown plan item action: ${action}`, 2);
|
|
2118
3277
|
}
|
|
2119
3278
|
function readOptionalJsonFile(file) {
|
|
2120
3279
|
if (!file)
|
|
2121
3280
|
return undefined;
|
|
2122
|
-
|
|
2123
|
-
throw new AppError("not_found", `JSON file does not exist: ${file}`, 1);
|
|
2124
|
-
}
|
|
2125
|
-
try {
|
|
2126
|
-
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
2127
|
-
}
|
|
2128
|
-
catch {
|
|
2129
|
-
throw new AppError("validation", `JSON file is malformed: ${file}`, 2);
|
|
2130
|
-
}
|
|
3281
|
+
return prepareJsonFile(file, "JSON input").value;
|
|
2131
3282
|
}
|
|
2132
3283
|
function parseFlowFlagOverrides(values) {
|
|
2133
3284
|
const result = {};
|
|
@@ -2155,29 +3306,17 @@ function parseArgs(args) {
|
|
|
2155
3306
|
function requiredPosition(parsed, index, label) {
|
|
2156
3307
|
const value = parsed.positional[index];
|
|
2157
3308
|
if (!value) {
|
|
2158
|
-
throw new AppError("usage", `Missing required argument: ${label}`, 2);
|
|
3309
|
+
throw new AppError("usage", `Missing required argument: ${label}`, 2, { parameter: label, reason: "missing_required_argument" });
|
|
2159
3310
|
}
|
|
2160
3311
|
return value;
|
|
2161
3312
|
}
|
|
2162
|
-
function optionalOption(parsed, key) {
|
|
2163
|
-
const values = parsed.options.get(key);
|
|
2164
|
-
const value = values?.[values.length - 1];
|
|
2165
|
-
return value === flagValue ? undefined : value;
|
|
2166
|
-
}
|
|
2167
3313
|
function allOptions(parsed, key) {
|
|
2168
3314
|
return (parsed.options.get(key) ?? []).filter((value) => value !== flagValue);
|
|
2169
3315
|
}
|
|
2170
|
-
function requiredOption(parsed, key) {
|
|
2171
|
-
const value = optionalOption(parsed, key);
|
|
2172
|
-
if (!value) {
|
|
2173
|
-
throw new AppError("usage", `Missing required option: --${key}`, 2);
|
|
2174
|
-
}
|
|
2175
|
-
return value;
|
|
2176
|
-
}
|
|
2177
3316
|
function requiredScopeProjectRoot(root, operation) {
|
|
2178
3317
|
if (root)
|
|
2179
3318
|
return root;
|
|
2180
|
-
throw new AppError("usage", `${operation} requires --project-root or a unique protocol context`, 2);
|
|
3319
|
+
throw new AppError("usage", `${operation} requires --project-root or a unique protocol context`, 2, { parameter: "project-root", reason: "missing_required_option" });
|
|
2181
3320
|
}
|
|
2182
3321
|
function requiredOptionOrScope(parsed, root, key) {
|
|
2183
3322
|
return root ?? requiredOption(parsed, key);
|
|
@@ -2196,7 +3335,7 @@ function requiredOptionAlias(parsed, keys) {
|
|
|
2196
3335
|
if (value)
|
|
2197
3336
|
return value;
|
|
2198
3337
|
}
|
|
2199
|
-
throw new AppError("usage", `Missing required option: ${keys.map((key) => `--${key}`).join(" or ")}`, 2);
|
|
3338
|
+
throw new AppError("usage", `Missing required option: ${keys.map((key) => `--${key}`).join(" or ")}`, 2, { parameter: [...keys].sort(), reason: "missing_required_option" });
|
|
2200
3339
|
}
|
|
2201
3340
|
function hasOption(parsed, key) {
|
|
2202
3341
|
return parsed.options.has(key);
|
|
@@ -2213,26 +3352,6 @@ function parseBoolean(value, key) {
|
|
|
2213
3352
|
function parseOptionalBoolean(value, key) {
|
|
2214
3353
|
return typeof value === "string" ? parseBoolean(value, key) : undefined;
|
|
2215
3354
|
}
|
|
2216
|
-
function optionalNumber(parsed, key) {
|
|
2217
|
-
const value = optionalOption(parsed, key);
|
|
2218
|
-
if (typeof value === "undefined") {
|
|
2219
|
-
return undefined;
|
|
2220
|
-
}
|
|
2221
|
-
const parsedNumber = Number(value);
|
|
2222
|
-
if (!Number.isFinite(parsedNumber)) {
|
|
2223
|
-
throw new AppError("validation", `--${key} must be a number`, 2);
|
|
2224
|
-
}
|
|
2225
|
-
return parsedNumber;
|
|
2226
|
-
}
|
|
2227
|
-
function optionalPositiveNumber(parsed, key, integer = false) {
|
|
2228
|
-
const value = optionalNumber(parsed, key);
|
|
2229
|
-
if (typeof value === "undefined")
|
|
2230
|
-
return undefined;
|
|
2231
|
-
if (value <= 0 || (integer && !Number.isInteger(value))) {
|
|
2232
|
-
throw new AppError("validation", `--${key} must be a positive${integer ? " integer" : ""} number`, 2);
|
|
2233
|
-
}
|
|
2234
|
-
return value;
|
|
2235
|
-
}
|
|
2236
3355
|
function requiredWorkerId(parsed) {
|
|
2237
3356
|
return requiredOption(parsed, "worker-id");
|
|
2238
3357
|
}
|
|
@@ -2246,11 +3365,13 @@ function parseWorktreePolicy(value) {
|
|
|
2246
3365
|
}
|
|
2247
3366
|
throw new AppError("validation", "--worktree must be keep or remove", 2);
|
|
2248
3367
|
}
|
|
2249
|
-
async function readStdin(stream) {
|
|
3368
|
+
async function readStdin(stream, maxBytes) {
|
|
2250
3369
|
if (!stream) {
|
|
2251
3370
|
return "";
|
|
2252
3371
|
}
|
|
2253
3372
|
let data = "";
|
|
3373
|
+
if (maxBytes !== undefined)
|
|
3374
|
+
return readBoundedUtf8Stream(stream, maxBytes);
|
|
2254
3375
|
stream.setEncoding("utf8");
|
|
2255
3376
|
for await (const chunk of stream) {
|
|
2256
3377
|
data += chunk;
|
|
@@ -2266,7 +3387,8 @@ function refreshDashboardsAfterMutation(context, args, result, resolvedScope) {
|
|
|
2266
3387
|
const projectRoot = projectRootForMutation(context, args, result, resolvedScope);
|
|
2267
3388
|
if (!projectRoot)
|
|
2268
3389
|
return [];
|
|
2269
|
-
|
|
3390
|
+
const runId = result && typeof result === "object" && "run_id" in result && typeof result.run_id === "string" ? result.run_id : undefined;
|
|
3391
|
+
autoRefreshDashboards(context, { projectRoot, ...(runId ? { runId } : {}) });
|
|
2270
3392
|
return [];
|
|
2271
3393
|
}
|
|
2272
3394
|
catch (error) {
|
|
@@ -2328,7 +3450,7 @@ function projectRootForMutation(context, args, result, resolvedScope) {
|
|
|
2328
3450
|
return projectRootForMergeQueueMutation(context, command, parsed);
|
|
2329
3451
|
}
|
|
2330
3452
|
if (family === "merge" && ["one-shot", "apply", "repair", "request"].includes(command ?? "")) {
|
|
2331
|
-
return requiredOption(parsed, "project-root");
|
|
3453
|
+
return resolvedScope ?? requiredOption(parsed, "project-root");
|
|
2332
3454
|
}
|
|
2333
3455
|
if (family === "merge-worker" && ["start", "stop"].includes(command ?? "")) {
|
|
2334
3456
|
return requiredOption(parsed, "project-root");
|
|
@@ -2346,7 +3468,7 @@ function projectRootForMutation(context, args, result, resolvedScope) {
|
|
|
2346
3468
|
return requiredOption(parsed, "project-root");
|
|
2347
3469
|
}
|
|
2348
3470
|
if (family === "stage" && ["start", "pause", "resume", "finish"].includes(command ?? "")) {
|
|
2349
|
-
return requiredOption(parsed, "project-root");
|
|
3471
|
+
return resolvedScope ?? requiredOption(parsed, "project-root");
|
|
2350
3472
|
}
|
|
2351
3473
|
if (family === "worktree" && ["create", "bootstrap", "close"].includes(command ?? "")) {
|
|
2352
3474
|
return protocolProjectRoot(context, requiredOption(parsed, "protocol-id"));
|
|
@@ -2360,7 +3482,7 @@ function projectRootForMutation(context, args, result, resolvedScope) {
|
|
|
2360
3482
|
if (family === "opencode" && ((command === "event" && requiredPosition(parsed, 0, "opencode event action") === "handle") || command === "usage")) {
|
|
2361
3483
|
return requiredOption(parsed, "project-root");
|
|
2362
3484
|
}
|
|
2363
|
-
if (family
|
|
3485
|
+
if (["agy", "droid"].includes(family ?? "") && ((command === "event" && requiredPosition(parsed, 0, `${family} event action`) === "handle") || command === "usage"))
|
|
2364
3486
|
return requiredOption(parsed, "project-root");
|
|
2365
3487
|
if (family === "codex" && command === "hooks") {
|
|
2366
3488
|
const action = requiredPosition(parsed, 0, "codex hooks action");
|
|
@@ -2397,14 +3519,36 @@ function projectRootForMergeQueueMutation(context, command, parsed) {
|
|
|
2397
3519
|
function protocolProjectRoot(context, protocolId) {
|
|
2398
3520
|
return requireProtocol(context, protocolId).project_root;
|
|
2399
3521
|
}
|
|
3522
|
+
function prepareCliWorkRepair(context, parsed, objective, scopeProjectRoot) {
|
|
3523
|
+
const origins = parsed.options.get("origin-work") ?? [], findings = parsed.options.get("from-finding") ?? [], unresolved = parsed.options.get("from-unresolved") ?? [], receipts = parsed.options.get("from-check") ?? [];
|
|
3524
|
+
if ([findings.length > 0, unresolved.length > 0, receipts.length > 0].filter(Boolean).length !== 1)
|
|
3525
|
+
throw new AppError("usage", "Repair requires exactly one source: --from-check, --from-finding or --from-unresolved", 2);
|
|
3526
|
+
const scope = { projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "work repair add"), runId: requiredOption(parsed, "run"), objective };
|
|
3527
|
+
if (findings.length) {
|
|
3528
|
+
if (origins.length || hasOption(parsed, "verification-file"))
|
|
3529
|
+
throw new AppError("usage", "Finding repair derives its origins and cannot accept --origin-work or --verification-file", 2);
|
|
3530
|
+
return prepareVnextCodeReviewRepair(context, { ...scope, findingIds: findings, checkRefsByFinding: Object.fromEntries(findings.map(finding => [finding, parsed.options.get("check-ref") ?? []])) });
|
|
3531
|
+
}
|
|
3532
|
+
if (!origins.length)
|
|
3533
|
+
throw new AppError("usage", "Repair requires at least one --origin-work", 2);
|
|
3534
|
+
if (hasOption(parsed, "check-ref"))
|
|
3535
|
+
throw new AppError("usage", "--check-ref is only supported with --from-finding", 2);
|
|
3536
|
+
if (receipts.length > 1)
|
|
3537
|
+
throw new AppError("usage", "Create one repair Work per --from-check receipt", 2);
|
|
3538
|
+
if (!unresolved.length && hasOption(parsed, "verification-file"))
|
|
3539
|
+
throw new AppError("usage", "--verification-file is only supported with --from-unresolved", 2);
|
|
3540
|
+
const input = { ...scope, originWorkIds: origins, ...(unresolved.length ? { semanticUnresolved: unresolved, verificationPath: requiredOption(parsed, "verification-file") } : { checkReceiptId: receipts[0] }) };
|
|
3541
|
+
return { input, value: prepareVnextCodeRepair(context, input) };
|
|
3542
|
+
}
|
|
3543
|
+
function workerPromptInput(parsed) {
|
|
3544
|
+
const workspaceRoot = optionalOption(parsed, "workspace-root"), taskFile = optionalOption(parsed, "task-file");
|
|
3545
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run"), stage: requiredOption(parsed, "stage"), profile: requiredOption(parsed, "profile"), ...(taskFile ? { taskFile } : { planItemId: requiredOption(parsed, "plan-item") }), ...(workspaceRoot ? { workspaceRoot } : {}) };
|
|
3546
|
+
}
|
|
2400
3547
|
function runnerIntake(context) {
|
|
2401
3548
|
const input = context.value.task_input?.[0];
|
|
2402
3549
|
if (!input?.path)
|
|
2403
3550
|
throw new AppError("validation", "Runner SPECIFY context requires one ordered task_input file", 2);
|
|
2404
|
-
|
|
2405
|
-
if (!fs.existsSync(file) || !fs.statSync(file).isFile())
|
|
2406
|
-
throw new AppError("not_found", "Runner SPECIFY task input is missing", 2, { task_input: file });
|
|
2407
|
-
return fs.readFileSync(file, "utf8");
|
|
3551
|
+
return prepareTextFile(input.path, "Runner SPECIFY task input").text;
|
|
2408
3552
|
}
|
|
2409
3553
|
/** Stage services install context before their lifecycle transition. Keeping
|
|
2410
3554
|
* this check here makes an incomplete service migration fail closed instead of
|