@deksden-com/dd-flow-cli 0.9.0-beta.15 → 0.9.0-beta.150
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 +895 -0
- package/README.md +140 -6
- package/dist/build-info.json +8 -8
- package/dist/cli/argument-values.js +38 -0
- package/dist/cli/command-inputs.js +386 -0
- package/dist/cli/command-metadata.js +232 -0
- package/dist/cli/command-option-descriptions.js +192 -0
- package/dist/cli/completion-cli.js +70 -0
- package/dist/cli/completion.js +57 -0
- package/dist/cli/diagnostic-invocation.js +46 -0
- package/dist/cli/diagnostic.js +120 -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 +396 -19
- package/dist/cli/hook-ingress.js +102 -0
- package/dist/cli/input-preparation.js +165 -0
- package/dist/cli/memory-flow-admission.js +50 -0
- package/dist/cli/memory-init-invocation.js +85 -0
- package/dist/cli/memory-init.js +249 -0
- package/dist/cli/memory-storage-admission.js +54 -0
- package/dist/cli/memory-storage-qualification.js +105 -0
- package/dist/cli/memory-storage-transition.js +123 -0
- package/dist/cli/memory-upgrade.js +189 -0
- package/dist/cli/models.js +39 -0
- package/dist/cli/native-hook-ingress.js +90 -0
- package/dist/cli/native-hook-preparation.js +55 -0
- package/dist/cli/operator-profiles.js +61 -0
- package/dist/cli/output-options.js +35 -0
- package/dist/cli/response-file.js +72 -0
- package/dist/cli/run-cli.js +1734 -550
- 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 +150 -0
- package/dist/cli.js +89 -2
- package/dist/domain/diagnostic-aspects.js +103 -0
- package/dist/domain/diagnostic-flows.js +8 -0
- package/dist/domain/stage-catalog.js +1 -1
- package/dist/domain/stage-handler-identities.js +28 -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-error-boundary.mjs +58 -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/agy-native-inspection.mjs +216 -0
- package/dist/harness-runtime/lib/agy-native_pb.js +117 -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 +2 -0
- package/dist/harness-runtime/lib/daemon-operations.mjs +462 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.d.mts +6 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +1096 -0
- package/dist/harness-runtime/lib/dd-agy.mjs +86 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.mjs +231 -0
- package/dist/harness-runtime/lib/dd-codex.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex.mjs +954 -0
- package/dist/harness-runtime/lib/dd-droid-daemon.mjs +141 -0
- package/dist/harness-runtime/lib/dd-droid.mjs +626 -0
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +555 -0
- package/dist/harness-runtime/lib/dd-grok.mjs +180 -0
- package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +622 -0
- package/dist/harness-runtime/lib/dd-opencode.mjs +390 -0
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +752 -0
- package/dist/harness-runtime/lib/dd-zcode.mjs +1175 -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 +140 -0
- package/dist/harness-runtime/lib/droid-observation.mjs +66 -0
- package/dist/harness-runtime/lib/entity-references.d.mts +2 -0
- package/dist/harness-runtime/lib/entity-references.mjs +9 -0
- package/dist/harness-runtime/lib/lease-renewal.d.mts +8 -0
- package/dist/harness-runtime/lib/lease-renewal.mjs +37 -0
- package/dist/harness-runtime/lib/lifecycle-command.d.mts +20 -0
- package/dist/harness-runtime/lib/lifecycle-command.mjs +273 -0
- package/dist/harness-runtime/lib/lifecycle-contract.d.mts +10 -0
- package/dist/harness-runtime/lib/lifecycle-contract.mjs +19 -0
- package/dist/harness-runtime/lib/managed-daemon.d.mts +16 -0
- package/dist/harness-runtime/lib/managed-daemon.mjs +698 -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 +23 -0
- package/dist/harness-runtime/lib/native-children.mjs +219 -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 +95 -0
- package/dist/harness-runtime/lib/opencode-plugin.mjs +85 -0
- package/dist/harness-runtime/lib/operation-context.mjs +5 -0
- package/dist/harness-runtime/lib/operation-errors.d.mts +16 -0
- package/dist/harness-runtime/lib/operation-errors.mjs +127 -0
- package/dist/harness-runtime/lib/operation-progress.d.mts +7 -0
- package/dist/harness-runtime/lib/operation-progress.mjs +114 -0
- package/dist/harness-runtime/lib/process-json.d.mts +5 -0
- package/dist/harness-runtime/lib/process-json.mjs +201 -0
- package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
- package/dist/harness-runtime/lib/provider-overload-policy.d.mts +15 -0
- package/dist/harness-runtime/lib/provider-overload-policy.mjs +151 -0
- package/dist/harness-runtime/lib/runner-events.mjs +202 -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 +53 -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-decision.schema.json +9 -0
- 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/diagnostic-stage-result.schema.json +354 -0
- package/dist/schemas/diagnostic-work-result.schema.json +296 -0
- package/dist/schemas/discovery-result.schema.json +184 -0
- package/dist/schemas/execution-bundle.schema.json +211 -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 +29 -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/mb-upgrade-stage-result.schema.json +863 -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 +56 -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 +12 -3
- package/dist/schemas/plan-review-result.schema.json +2 -2
- package/dist/schemas/profile-settings.schema.json +122 -0
- package/dist/schemas/project-flow-pack-manifest.schema.json +1 -1
- package/dist/schemas/release-impact.schema.json +3 -3
- 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/stage-start-response.schema.json +4 -0
- package/dist/schemas/vnext-protocol-plan.schema.json +2 -2
- package/dist/services/accepted-intent.js +139 -0
- package/dist/services/branch-context.js +3 -3
- package/dist/services/canon.js +54 -15
- package/dist/services/check-environment.js +21 -0
- package/dist/services/check-runner-evidence.js +562 -0
- package/dist/services/cleanup.js +133 -40
- package/dist/services/cli-operation-classifier.js +26 -63
- package/dist/services/cli-upgrade-allowlist.js +27 -0
- package/dist/services/code-checks.js +1398 -119
- package/dist/services/codex-hook-delivery.js +32 -0
- package/dist/services/command-context.js +80 -0
- package/dist/services/compatibility-preflight.js +16 -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 +94 -0
- package/dist/services/controller-fanout.js +226 -0
- package/dist/services/controller-memory-task.js +45 -0
- package/dist/services/controller-memory-worker.js +49 -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 +107 -0
- package/dist/services/descriptor-run-start.js +475 -0
- package/dist/services/descriptor-stage-coordinator.js +250 -0
- package/dist/services/descriptor-stage-settlement.js +223 -0
- package/dist/services/descriptor-stage-start.js +175 -0
- package/dist/services/diagnostic-evidence.js +73 -0
- package/dist/services/diagnostic-report-publication.js +72 -0
- package/dist/services/diagnostic-report.js +67 -0
- package/dist/services/diagnostic-run-bundle.js +74 -0
- package/dist/services/diagnostic-snapshot.js +159 -0
- package/dist/services/diagnostic-stage-handlers.js +108 -0
- package/dist/services/diagnostic-stage-results.js +132 -0
- package/dist/services/diagnostic-start.js +84 -0
- package/dist/services/diagnostic-work-batch.js +34 -0
- package/dist/services/diagnostic-work-collection.js +97 -0
- package/dist/services/diagnostic-work-registration.js +101 -0
- package/dist/services/diagnostic-work-results.js +93 -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 +354 -163
- package/dist/services/eval-snapshots.js +1316 -150
- package/dist/services/execution-bundles.js +275 -0
- package/dist/services/execution-policy.js +185 -0
- package/dist/services/executor-onboarding.js +56 -0
- package/dist/services/external-work-launch.js +310 -0
- package/dist/services/flow-catalog.js +30 -0
- package/dist/services/flow-descriptors.js +82 -0
- package/dist/services/frozen-engine.js +387 -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/hook-memory-routing.js +188 -0
- package/dist/services/hook-recovery-guard.js +14 -0
- package/dist/services/hook-session-observation.js +24 -0
- package/dist/services/hooks.js +791 -326
- package/dist/services/hybrid-contracts.js +474 -0
- package/dist/services/hybrid-digest.js +14 -0
- package/dist/services/ids.js +29 -1
- package/dist/services/installation-lock.js +95 -0
- package/dist/services/installed-flow-source.js +226 -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 +2148 -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 +449 -69
- package/dist/services/memory-accepted-composition.js +78 -0
- package/dist/services/memory-baseline-preservation.js +65 -0
- package/dist/services/memory-completed-correction-wave.js +328 -0
- package/dist/services/memory-compose-handler.js +400 -0
- package/dist/services/memory-composition-acceptance.js +375 -0
- package/dist/services/memory-composition-graph.js +165 -0
- package/dist/services/memory-composition-registration.js +161 -0
- package/dist/services/memory-composition-work-custody.js +156 -0
- package/dist/services/memory-composition-work-results.js +108 -0
- package/dist/services/memory-corrected-review-acceptance.js +129 -0
- package/dist/services/memory-corrected-review-batch.js +37 -0
- package/dist/services/memory-corrected-review-findings.js +108 -0
- package/dist/services/memory-corrected-review-inputs.js +215 -0
- package/dist/services/memory-corrected-review-projection.js +78 -0
- package/dist/services/memory-corrected-review-receipt.js +178 -0
- package/dist/services/memory-corrected-review-registration.js +192 -0
- package/dist/services/memory-correction-check-receipt.js +235 -0
- package/dist/services/memory-correction-input-owner.js +6 -0
- package/dist/services/memory-custody-snapshot.js +36 -0
- package/dist/services/memory-discover-handler.js +104 -0
- package/dist/services/memory-finish-evidence.js +457 -0
- package/dist/services/memory-finish-handler.js +229 -0
- package/dist/services/memory-finish-integration.js +450 -0
- package/dist/services/memory-finish-readiness.js +169 -0
- package/dist/services/memory-git-integration.js +220 -0
- package/dist/services/memory-historical-corrected-review.js +212 -0
- package/dist/services/memory-historical-original-review.js +143 -0
- package/dist/services/memory-historical-repair-registration.js +105 -0
- package/dist/services/memory-historical-second-review.js +147 -0
- package/dist/services/memory-init-bootstrap-readiness.js +210 -0
- package/dist/services/memory-init-contexts.js +131 -0
- package/dist/services/memory-init-controller-start.js +103 -0
- package/dist/services/memory-init-engine-release.js +223 -0
- package/dist/services/memory-init-git-policy.js +165 -0
- package/dist/services/memory-init-lint-capability.js +203 -0
- package/dist/services/memory-init-native-capacity.js +309 -0
- package/dist/services/memory-init-preflight.js +334 -0
- package/dist/services/memory-init-project-bootstrap.js +599 -0
- package/dist/services/memory-init-request-candidate.js +213 -0
- package/dist/services/memory-init-request-input.js +82 -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 +135 -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 +91 -0
- package/dist/services/memory-init-stores.js +359 -0
- package/dist/services/memory-init-worktrunk-capability.js +174 -0
- package/dist/services/memory-layer-publication.js +611 -0
- package/dist/services/memory-layer.js +221 -0
- package/dist/services/memory-lint-config.js +194 -0
- package/dist/services/memory-native-review-copy.js +103 -0
- package/dist/services/memory-normal-flow-entry.js +87 -0
- package/dist/services/memory-ordinary-defaults.js +129 -0
- package/dist/services/memory-preflight-bootstrap-custody.js +115 -0
- package/dist/services/memory-preflight-handler.js +274 -0
- package/dist/services/memory-repair-current-view.js +299 -0
- package/dist/services/memory-repair-document-delta.js +488 -0
- package/dist/services/memory-repair-history.js +496 -0
- package/dist/services/memory-repair-physical-binding.js +194 -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 +162 -0
- package/dist/services/memory-repair-publication-source.js +225 -0
- package/dist/services/memory-repair-publication.js +795 -0
- package/dist/services/memory-repair-registration.js +240 -0
- package/dist/services/memory-repair-work-baseline.js +415 -0
- package/dist/services/memory-repair-work-completion.js +117 -0
- package/dist/services/memory-repair-work-custody.js +73 -0
- package/dist/services/memory-repair-work-results.js +108 -0
- package/dist/services/memory-repair-workspace.js +261 -0
- package/dist/services/memory-repair-writer-copy.js +736 -0
- package/dist/services/memory-result-binding.js +239 -0
- package/dist/services/memory-retained-review-authority.js +138 -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 +162 -0
- package/dist/services/memory-review-acceptance.js +214 -0
- package/dist/services/memory-review-correction-plan.js +206 -0
- package/dist/services/memory-review-handler.js +585 -0
- package/dist/services/memory-review-inputs.js +246 -0
- package/dist/services/memory-review-limits.js +10 -0
- package/dist/services/memory-review-receipt.js +180 -0
- package/dist/services/memory-review-registration.js +117 -0
- package/dist/services/memory-review-work-custody.js +126 -0
- package/dist/services/memory-review-work-input.js +91 -0
- package/dist/services/memory-review-work-results.js +95 -0
- package/dist/services/memory-second-corrected-review-inputs.js +167 -0
- package/dist/services/memory-second-correction-plan.js +77 -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 +65 -0
- package/dist/services/memory-stage-results.js +290 -0
- package/dist/services/memory-tool-probe.js +25 -0
- package/dist/services/memory-upgrade-analysis-handler.js +243 -0
- package/dist/services/memory-upgrade-bootstrap-readiness.js +251 -0
- package/dist/services/memory-upgrade-evidence.js +100 -0
- package/dist/services/memory-upgrade-inventory.js +442 -0
- package/dist/services/memory-upgrade-publication-custody.js +73 -0
- package/dist/services/memory-upgrade-publication.js +413 -0
- package/dist/services/memory-upgrade-selection.js +430 -0
- package/dist/services/memory-upgrade-start.js +60 -0
- package/dist/services/memory-upgrade-storage-readiness.js +340 -0
- package/dist/services/memory-verification-acceptance.js +156 -0
- package/dist/services/memory-verification-checks.js +317 -0
- package/dist/services/memory-verification-receipt.js +160 -0
- package/dist/services/memory-verify-handler.js +137 -0
- package/dist/services/memory-work-result-json.js +12 -0
- package/dist/services/memory-work-result-preparation.js +64 -0
- package/dist/services/merge-queue.js +228 -149
- package/dist/services/merge-server.js +163 -35
- package/dist/services/migrations.js +164 -114
- package/dist/services/model-catalog.js +109 -0
- package/dist/services/model-discovery.js +46 -0
- package/dist/services/native-daemon-history.js +50 -0
- package/dist/services/native-session-control.js +39 -0
- package/dist/services/operator-profiles.js +236 -0
- package/dist/services/physical-startup.js +45 -0
- package/dist/services/plan-runtime.js +26 -11
- package/dist/services/plans.js +28 -25
- package/dist/services/project-registry.js +97 -0
- package/dist/services/project-summary.js +1 -1
- package/dist/services/projects.js +17 -87
- package/dist/services/prompt-delivery.js +71 -0
- package/dist/services/prompts.js +28 -17
- package/dist/services/protocols.js +163 -87
- package/dist/services/provider-capacity.js +455 -0
- package/dist/services/recovery-observation-budget.js +121 -0
- package/dist/services/recovery-snapshot-database.js +588 -0
- package/dist/services/release-archive-producer.js +52 -0
- package/dist/services/release-archive.js +143 -0
- package/dist/services/release-cache.js +150 -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 +31 -0
- package/dist/services/repair-receipt.js +13 -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 +428 -0
- package/dist/services/run-control.js +1092 -0
- package/dist/services/run-controller-adapter.js +400 -0
- package/dist/services/run-controller-capture.js +186 -0
- package/dist/services/run-controller-process.js +205 -0
- package/dist/services/run-controller-recovery.js +295 -0
- package/dist/services/run-controller-state.js +97 -0
- package/dist/services/run-controller.js +1326 -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 +107 -0
- package/dist/services/run-recovery-runtime.js +112 -0
- package/dist/services/run-recovery.js +456 -0
- package/dist/services/run-start-custody.js +189 -0
- package/dist/services/runs.js +1023 -246
- package/dist/services/runtime-budget.js +446 -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 +605 -0
- package/dist/services/runtime-scope-stop.js +211 -0
- package/dist/services/runtime-scope-worker.js +314 -0
- package/dist/services/runtime-service.js +19 -14
- package/dist/services/schema-validation.js +10 -8
- package/dist/services/scope-writer-cleanup.js +27 -0
- package/dist/services/sessions.js +36 -91
- package/dist/services/setup.js +427 -0
- package/dist/services/stage-blocker.js +17 -7
- package/dist/services/stage-context.js +193 -19
- package/dist/services/stage-handlers.js +89 -0
- package/dist/services/stage-lifecycle.js +152 -116
- package/dist/services/stage-pause.js +202 -91
- package/dist/services/stage-report-renderer.js +234 -6
- package/dist/services/stage-work-graph.js +52 -0
- package/dist/services/tooling.js +41 -30
- package/dist/services/usage.js +49 -101
- package/dist/services/verification-matrix.js +393 -0
- package/dist/services/version-status.js +1 -1
- package/dist/services/vnext-code-review.js +235 -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 +355 -21
- package/dist/services/vnext-merge.js +332 -111
- package/dist/services/vnext-plan-review.js +287 -132
- package/dist/services/vnext-plan.js +228 -104
- package/dist/services/vnext-protocolize.js +533 -95
- package/dist/services/vnext-specify.js +123 -81
- package/dist/services/vnext-workspace-policy.js +7 -2
- package/dist/services/work-registry.js +1706 -200
- package/dist/services/workspace-bootstrap.js +53 -15
- package/dist/services/workspace-files.js +85 -0
- package/dist/services/worktrees.js +105 -48
- package/dist/shared/entity-references.js +1 -0
- package/dist/shared/errors.js +27 -0
- package/dist/storage/agent-output.js +26 -0
- package/dist/storage/data-preservation.js +65 -0
- package/dist/storage/database.js +930 -40
- package/dist/storage/foreign-key-transaction.js +39 -0
- package/dist/storage/immutable-input.js +45 -0
- package/dist/storage/import-guard.js +159 -0
- package/dist/storage/memory-upgrade-schema-bootstrap.js +38 -0
- package/dist/storage/native-cleanup.js +52 -0
- package/dist/storage/paired-writer-migration.js +424 -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 +96 -0
- package/dist/storage/writer-migration.js +346 -0
- package/dist/storage/writer-restore.js +217 -0
- package/package.json +50 -13
- package/tools/create-canon-release.mjs +33 -0
- package/tools/probe-codex-history.mjs +37 -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,57 +1,109 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
+
import { prepareResponseDestination, writeResponseFile } from "./response-file.js";
|
|
3
|
+
import { preparePromptDelivery, publishPromptDelivery } from "../services/prompt-delivery.js";
|
|
4
|
+
import { requiredOption, optionalOption, optionalNumber, optionalPositiveNumber } from "./argument-values.js";
|
|
5
|
+
import { resolveCommandPaths, resolveCommandProjectRoot } from "../services/command-context.js";
|
|
6
|
+
import { assertAuxiliaryCommandScope, cliManagedInvocationScope, manualRecoveryInvocationScope, assertManualRecoveryInvocationTarget } from "../services/lifecycle-invocations.js";
|
|
2
7
|
import path from "node:path";
|
|
8
|
+
import { randomUUID } from "node:crypto";
|
|
9
|
+
import { quote } from "shell-quote";
|
|
10
|
+
import { observeLifecycleCommand, observedLifecycleNativeEvent } from "../services/lifecycle-invocations.js";
|
|
11
|
+
import { verifyCodexHookDelivery } from "../services/codex-hook-delivery.js";
|
|
12
|
+
import { assertLifecycleInvocationCurrent, awaitLifecycleInvocation, bindLifecyclePublishedPacket, expandLifecycleInvocationArgs, issueLifecycleInvocation, prepareLifecycleIssuance, lifecycleInvocationScope, observedLifecycleDiagnostic, observedLifecycleInvocation, resolveObservedLifecycleArgs, retainObservedLifecycleSemantics, settleLifecycleInvocation, settleLifecyclePreparationRejection, settleLifecycleRejection, validateLifecyclePathAliases } from "../services/lifecycle-invocations.js";
|
|
3
13
|
import { createContext, createRouterContext } from "../runtime/context.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
14
|
+
import { dispatchNativeHook, isNativeHookIngress } from "./native-hook-ingress.js";
|
|
15
|
+
import { isRuntimeMaintenance, runtimeMaintenanceDiagnostics, withMaintenanceDiagnosticScope } from "./runtime-maintenance-diagnostics.js";
|
|
16
|
+
import { migrateStoreWriter } from "../storage/writer-migration.js";
|
|
17
|
+
import { resolveHelp } from "./help.js";
|
|
18
|
+
import { renderHumanError, writePreparationError } from "./error-output.js";
|
|
19
|
+
import { runOperatorProfilesCli } from "./operator-profiles.js";
|
|
20
|
+
import { runSetupCli } from "./setup.js";
|
|
21
|
+
import { runCompletionCli } from "./completion-cli.js";
|
|
22
|
+
import { runHarnessesCli } from "./harnesses.js";
|
|
23
|
+
import { runModelsCli } from "./models.js";
|
|
24
|
+
import { parseOutputOptions } from "./output-options.js";
|
|
25
|
+
import { AppError, errorRecord, isAppError } from "../shared/errors.js";
|
|
26
|
+
import { entityReferenceVariants } from "../shared/entity-references.js";
|
|
27
|
+
import { writeJson, parseJsonObject } from "../shared/json.js";
|
|
28
|
+
import { archiveProject, prepareProjectArchive, getProjectStatus, migrateProjectIds, registerProject, resolveProject } from "../services/projects.js";
|
|
29
|
+
import { cancelProtocol, getProtocolBlockers, getProtocolStatus, getReadyProtocols, implementProtocol, readyForMerge, registerProtocol, prepareProtocolRegistration, requireProtocol, syncProtocolFromRun, prepareProtocolTransition, transitionProtocol } from "../services/protocols.js";
|
|
30
|
+
import { getPlanStatus, preparePlanItemMutation, updatePlanItem } from "../services/plans.js";
|
|
31
|
+
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
32
|
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";
|
|
33
|
+
import { cleanupApply, cleanupScan, prepareCleanupApply } from "../services/cleanup.js";
|
|
34
|
+
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";
|
|
35
|
+
import { bootstrapWorktree, closeWorktree, createWorktreeRecord, prepareWorktreeCreate, prepareWorktreeBootstrap, prepareWorktreeClose, getWorktreeStatus, planWorktree } from "../services/worktrees.js";
|
|
36
|
+
import { acquireLaneLock, cancelLaneWaiter, prepareLaneWaiterCancellation, checkLaneWorkspace, getLaneWaiters, getLaneStatus, heartbeatLaneLock, releaseLaneLock, setLaneWorkspace, prepareLaneWorkspace, prepareLaneLock, waitAcquireLaneLock, waitForLaneLock } from "../services/lanes.js";
|
|
37
|
+
import { getProjectConfigStatus, prepareProjectConfigValue, setProjectConfigValue } from "../services/config.js";
|
|
38
|
+
import { getCanonStatus, registerCanonRoot, resolveCanonRoot, prepareCanonRegistration } from "../services/canon.js";
|
|
18
39
|
import { getRuntimeStatus } from "../services/status.js";
|
|
19
40
|
import { getCliVersionReport } from "../services/build-info.js";
|
|
20
41
|
import { parseCommandArgs } from "../services/lifecycle-command.js";
|
|
21
|
-
import {
|
|
42
|
+
import { prepareError, prepareJsonFile, prepareJsonSchema, prepareJsonText, prepareTextFile, prepareFileBytes, readBoundedUtf8Stream } from "./input-preparation.js";
|
|
43
|
+
import { autoRefreshDashboards, dashboardData, getCmuxStatus, openDashboard, openGlobalDashboard, openProtocolDashboard, refreshAllDashboards, prepareDashboardRefresh, prepareDashboardRender, prepareGlobalDashboardRender, prepareDashboardOptions, refreshDashboard, refreshGlobalDashboard, renderDashboard, renderGlobalDashboard } from "../services/dashboard.js";
|
|
22
44
|
import { dashboardTargetSummary, preferredDashboardCommand, relatedDashboardCommands, resolveDashboardTarget } from "../services/dashboard-targets.js";
|
|
23
|
-
import { getFlowSessionStatus, registerFlowSession, stopFlowSession, stopMergeWorker, syncFlowSessionUsage } from "../services/sessions.js";
|
|
45
|
+
import { getFlowSessionStatus, prepareFlowSessionPayload, registerFlowSession, stopFlowSession, stopMergeWorker, syncFlowSessionUsage } from "../services/sessions.js";
|
|
24
46
|
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";
|
|
47
|
+
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
48
|
import { previewNextEntityId } from "../services/ids.js";
|
|
27
49
|
import { validateSchema } from "../services/schema-validation.js";
|
|
28
50
|
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";
|
|
51
|
+
import { ingestDroidUsage, ingestAgyUsage, ingestGrokUsage, ingestOpenCodeUsage, ingestZcodeUsage } from "../services/usage.js";
|
|
52
|
+
import { applyMigration, assessMigrationImpact, assertUpgradeRunStorageReady, planMigration, prepareMigrationApply, verifyMigrationReport } from "../services/migrations.js";
|
|
53
|
+
import { PreparedUpgradeStorageReadiness } from "../services/memory-upgrade-storage-readiness.js";
|
|
31
54
|
import { publishProjectSummary } from "../services/project-summary.js";
|
|
32
55
|
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";
|
|
56
|
+
import { resolveProjectRoot, resolveDdFlowHome } from "../storage/paths.js";
|
|
57
|
+
import { doctorEngines, bindRunEngine, prepareRunEngineBind, engineInfo, installCurrentEngine, listEngines, resolveEngine, resolveExplicitProjectRoot, resolveOperationProjectRoot, routeArgsThroughEngine } from "../services/engines.js";
|
|
35
58
|
import { preflightCliCompatibility } from "../services/compatibility-preflight.js";
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import { dispatchVnextPlanReview,
|
|
40
|
-
import { addVnextCodeRepair,
|
|
41
|
-
import {
|
|
42
|
-
import { applyVnextMerge,
|
|
59
|
+
import { getVnextWorkStatus, isVnextSpecifyFlow, isVnextSpecifyRun, prepareVnextSpecifyStart } from "../services/vnext-specify.js";
|
|
60
|
+
import { isVnextProtocolizeRun, prepareProtocolizeResultPath, prepareVnextProtocolizeStart } from "../services/vnext-protocolize.js";
|
|
61
|
+
import { isVnextPlanRun, prepareVnextPlanStart, prepareVnextPlanFinish } from "../services/vnext-plan.js";
|
|
62
|
+
import { dispatchVnextPlanReview, isVnextPlanReviewRun, prepareVnextPlanReviewStart, recordVnextPlanReviewCapacity } from "../services/vnext-plan-review.js";
|
|
63
|
+
import { addVnextCodeRepair, prepareVnextCodeRepair, prepareVnextCodeStart } from "../services/vnext-code.js";
|
|
64
|
+
import { isVnextCodeReviewRun, prepareCodeReviewDecision, prepareVnextCodeReviewRepair, prepareVnextCodeReviewStart } from "../services/vnext-code-review.js";
|
|
65
|
+
import { applyVnextMerge, getVnextMergeRequest, isVnextMergeRun, prepareVnextMergeFinish, prepareVnextMergeStart, repairVnextMerge, routeVnextMergeRequest } from "../services/vnext-merge.js";
|
|
66
|
+
import { sdlcStageHandlers } from "../services/stage-handlers.js";
|
|
67
|
+
import { startMemoryStage, finishMemoryStage, startMemoryUpgradeStage, finishMemoryUpgradeStage } from "../services/memory-stage-dispatch.js";
|
|
68
|
+
import { startDiagnosticStage, finishDiagnosticStage } from "../services/diagnostic-stage-handlers.js";
|
|
69
|
+
import { isDiagnosticFlow } from "../domain/diagnostic-flows.js";
|
|
70
|
+
import { bindStageCoordinatorWork } from "../services/work-registry.js";
|
|
71
|
+
import { registerMemoryCompositionFromStage, publishMemoryUpgradeFromStage } from "../services/memory-compose-handler.js";
|
|
72
|
+
import { runMemoryVerificationChecks } from "../services/memory-verify-handler.js";
|
|
73
|
+
import { registerMemoryReviewFromStage, collectMemoryInitialReviewFromStage } from "../services/memory-review-handler.js";
|
|
74
|
+
import { PreparedRetainedMemoryReviewAuthority } from "../services/memory-retained-review-authority.js";
|
|
75
|
+
import { PreparedRetainedMemoryReviewResults } from "../services/memory-retained-review-results.js";
|
|
76
|
+
import { registerMemoryCorrectionsFromStage, registerMemorySecondCorrectionsFromStage, runMemoryCorrectionChecksFromStage, runMemorySecondCorrectionChecksFromStage, registerCorrectedMemoryReviewFromStage, registerSecondCorrectedMemoryReviewFromStage, collectCorrectedMemoryReviewFromStage, collectSecondCorrectedMemoryReviewFromStage } from "../services/memory-review-handler.js";
|
|
77
|
+
import { integrateMemoryFinishFromStage, registerMemoryFinishEvidenceFromStage } from "../services/memory-finish-handler.js";
|
|
78
|
+
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
79
|
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 {
|
|
80
|
+
import { getVnextFanoutStatus, memoryStageFanoutDescriptor, diagnosticStageFanoutDescriptor, observeVnextNativeChildren, prepareNativeObservations, reconcileVnextFanout } from "../services/vnext-fanout.js";
|
|
81
|
+
import { pauseStageForUser, resumeStageAfterUser, prepareStagePause, prepareStageResume, stagePauseCommand } from "../services/stage-pause.js";
|
|
82
|
+
import { runtimeCommand } from "../services/runtime-command.js";
|
|
83
|
+
import { managedLifecycleCommand } from "../services/lifecycle-invocations.js";
|
|
84
|
+
import { blockStageForRuntime, unblockStageAfterRuntimeRepair, prepareStageRuntimeBlock, prepareStageRuntimeUnblock } from "../services/stage-blocker.js";
|
|
47
85
|
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 {
|
|
54
|
-
import {
|
|
86
|
+
import { prepareWorkerPrompt, renderWorkerPrompt } from "../services/prompts.js";
|
|
87
|
+
import { finishStage, prepareStageFinish, startStage, prepareLegacyStageStart } from "../services/stage-lifecycle.js";
|
|
88
|
+
import { addWorkBatch, assertGenericWorkBatchAllowed, prepareWorkBatch, prepareWorkDeps, prepareWorkDelete, prepareWorkSettlement, prepareWorkRetry, assertStageLifecycleOwner, assertWorkLifecycleOwner, cancelWork, deleteWork, failWork, finishWork, listWorks, mutateWorkDeps, prepareWorkResult, workResultByteLimit, retryWork, showWork, startWork } from "../services/work-registry.js";
|
|
89
|
+
import { launchExternalWork } from "../services/external-work-launch.js";
|
|
90
|
+
import { createEvalBootstrapSnapshot, prepareEvalBootstrapSnapshot, createEvalRunSnapshot, prepareEvalRunSnapshotCreation, prepareVnextSpecifyRun, prepareEvalRunRestore, prepareEvalBootstrapRestore, restoreEvalBootstrapSnapshot, restoreEvalRunSnapshot } from "../services/eval-snapshots.js";
|
|
91
|
+
import { forkRunFromSnapshot, prepareRunFork } from "../services/run-fork.js";
|
|
92
|
+
import { acceptRunRecovery, beginRunRecovery, inspectRunRecovery, prepareRecoveryCapture, prepareRecoveryPayload, prepareRunRecoveryResume, recordRecoveryCapture, resumeRunRecovery, sealRunRecovery } from "../services/run-recovery.js";
|
|
93
|
+
import { loadExternalStageContext, prepareExternalStageContext } from "../services/stage-context.js";
|
|
94
|
+
import { vnextStageDirectory } from "../domain/stage-catalog.js";
|
|
95
|
+
import { prepareCommandRoute } from "./command-inputs.js";
|
|
96
|
+
import { parseLifecycleCommand } from "../services/lifecycle-command.js";
|
|
97
|
+
import { dispatchRuntimeProcess, prepareRuntimeProcess } from "./runtime-process.js";
|
|
98
|
+
import { reconcileFlowRunHealth } from "../services/run-health.js";
|
|
99
|
+
import { fenceRuntimeBudget, runtimeBudgetStatus, prepareRuntimeScopeStatus, prepareRuntimeScopeControl, prepareRuntimeScopeFence } from "../services/runtime-budget.js";
|
|
100
|
+
import { stopRuntimeScope } from "../services/runtime-scope-stop.js";
|
|
101
|
+
import { launchRuntimeScopeControl, launchRuntimeScopeResume, prepareRuntimeScopeReconciliation, prepareRuntimeScopeServing, reconcileRuntimeScopeControl, serveRuntimeScopeControl } from "../services/runtime-scope-worker.js";
|
|
102
|
+
import { prepareRuntimeScopeResumeRequest } from "../services/runtime-scope-resume.js";
|
|
103
|
+
import { launchRunController, prepareRunControllerLaunch, resumeRunController, serveRunController, runControllerStatus, answerRunController, prepareRunControllerAnswer, supplyRunControllerContext, prepareRunControllerContext } from "../services/run-controller.js";
|
|
104
|
+
import { controlLifecycleReply, runControlStatus } from "../services/run-control.js";
|
|
105
|
+
import { reconcileRunControl, requestRunControl, serveRunControl } from "../services/run-control-worker.js";
|
|
106
|
+
import { startRuntimeService, prepareRuntimeService } from "../services/runtime-service.js";
|
|
55
107
|
const defaultIo = {
|
|
56
108
|
stdout: process.stdout,
|
|
57
109
|
stderr: process.stderr,
|
|
@@ -59,9 +111,58 @@ const defaultIo = {
|
|
|
59
111
|
};
|
|
60
112
|
const flagValue = "__dd_flow_flag__";
|
|
61
113
|
export async function runCli(args, io = defaultIo, env = process.env) {
|
|
62
|
-
|
|
114
|
+
return withMaintenanceDiagnosticScope(() => runCliRequest(args, io, env));
|
|
115
|
+
}
|
|
116
|
+
async function runCliRequest(args, io, env) {
|
|
117
|
+
try {
|
|
118
|
+
const help = resolveHelp(args);
|
|
119
|
+
if (help) {
|
|
120
|
+
if (help.json)
|
|
121
|
+
writeJson(io.stdout, help.data);
|
|
122
|
+
else
|
|
123
|
+
io.stdout.write(`${help.text}\n`);
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
const record = errorRecord(error);
|
|
129
|
+
writePreparationError(io, args, record);
|
|
130
|
+
return record.exitCode;
|
|
131
|
+
}
|
|
132
|
+
let output;
|
|
133
|
+
try {
|
|
134
|
+
output = parseOutputOptions(args);
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
const rejection = isAppError(error) ? error : new AppError("usage", error instanceof Error ? error.message : String(error), 2);
|
|
138
|
+
Object.assign(rejection.details, { phase: "prepare", effect: "no_effect", recoverable: true, ...rejection.details });
|
|
139
|
+
try {
|
|
140
|
+
const command = quote(["dd-flow", ...args]);
|
|
141
|
+
const analysis = parseLifecycleCommand(command);
|
|
142
|
+
const event = analysis.kind !== "none" || analysis.auxiliary
|
|
143
|
+
? observedLifecycleNativeEvent(createRouterContext(env), command, env.DD_FLOW_DAEMON_ID) : null;
|
|
144
|
+
if (event) {
|
|
145
|
+
const context = createContext({ ...env }, "hook");
|
|
146
|
+
try {
|
|
147
|
+
observeLifecycleCommand(context, command, event)({ error: rejection });
|
|
148
|
+
}
|
|
149
|
+
finally {
|
|
150
|
+
context.db.close?.();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch (persistence) {
|
|
155
|
+
rejection.details.lifecycle_diagnostic_error = String(persistence);
|
|
156
|
+
}
|
|
157
|
+
writePreparationError(io, args, errorRecord(rejection));
|
|
158
|
+
return rejection.exitCode;
|
|
159
|
+
}
|
|
63
160
|
const progress = progressForCommand(output.args);
|
|
64
161
|
let heartbeat;
|
|
162
|
+
let invocationSettlement;
|
|
163
|
+
let settleHook;
|
|
164
|
+
let diagnosticHome;
|
|
165
|
+
let maintenanceDiagnostics;
|
|
65
166
|
try {
|
|
66
167
|
if (output.args.length === 1 && output.args[0] === "--version") {
|
|
67
168
|
const payload = getCliVersionReport();
|
|
@@ -71,11 +172,60 @@ export async function runCli(args, io = defaultIo, env = process.env) {
|
|
|
71
172
|
io.stdout.write(`dd-flow ${payload.cli.version}\n`);
|
|
72
173
|
return 0;
|
|
73
174
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
175
|
+
if (output.args[0] === "completion")
|
|
176
|
+
return runCompletionCli(args, io);
|
|
177
|
+
if (output.args[0] === "setup")
|
|
178
|
+
return runSetupCli(args, env, io);
|
|
179
|
+
if (output.args[0] === "harnesses")
|
|
180
|
+
return runHarnessesCli(args, env, io);
|
|
181
|
+
if (output.args[0] === "models")
|
|
182
|
+
return runModelsCli(args, io);
|
|
183
|
+
if (output.args[0] === "profiles")
|
|
184
|
+
return runOperatorProfilesCli(args, env, io);
|
|
185
|
+
const suppliedInvocationId = optionalOption(parseArgs(output.args.slice(2)), "invocation-id");
|
|
186
|
+
if (isNativeHookIngress(output.args)) {
|
|
187
|
+
writeJson(io.stdout, await dispatchNativeHook(output.args, await readStdin(io.stdin), env));
|
|
77
188
|
return 0;
|
|
78
189
|
}
|
|
190
|
+
if (output.args.slice(0, 3).join(" ") === "codex hook handle") {
|
|
191
|
+
const parsed = prepareCommandRoute(output.args).parsed;
|
|
192
|
+
const stdin = await readStdin(io.stdin);
|
|
193
|
+
const payload = parseJsonObject(stdin || "{}", "codex hook stdin");
|
|
194
|
+
const event = requiredOption(parsed, "event");
|
|
195
|
+
const isolatedHome = event === "PreToolUse" ? isolatedDdFlowHomeFromHookStdin(stdin) : null;
|
|
196
|
+
const hookEnv = isolatedHome ? { ...env, DD_FLOW_HOME: isolatedHome } : env;
|
|
197
|
+
const proof = await verifyCodexHookDelivery(hookEnv, payload);
|
|
198
|
+
let opened;
|
|
199
|
+
const mode = databaseModeForCommand(output.args, classifyCliOperation(output.args, hookEnv));
|
|
200
|
+
const context = {
|
|
201
|
+
env: hookEnv, ddFlowHome: resolveDdFlowHome(hookEnv), now: () => new Date().toISOString(),
|
|
202
|
+
get db() { return (opened ??= createContext(hookEnv, mode)).db; }
|
|
203
|
+
};
|
|
204
|
+
try {
|
|
205
|
+
// Retired events retain the explicit startup/migration path. PreToolUse
|
|
206
|
+
// only opens its selected prepared store when the handler needs it.
|
|
207
|
+
if (mode !== "hook")
|
|
208
|
+
opened = createContext(hookEnv, mode);
|
|
209
|
+
if (proof)
|
|
210
|
+
context.codexHookProof = proof;
|
|
211
|
+
const result = handleCodexHook(context, { event, projectRoot: optionalOption(parsed, "project-root"), stdin });
|
|
212
|
+
writeJson(io.stdout, result && typeof result === "object" && !Array.isArray(result) && "hookSpecificOutput" in result
|
|
213
|
+
? { hookSpecificOutput: result.hookSpecificOutput } : {});
|
|
214
|
+
return 0;
|
|
215
|
+
}
|
|
216
|
+
finally {
|
|
217
|
+
opened?.db.close?.();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (output.args[0] === "run" && ["drive", "control"].includes(output.args[1] ?? "")) {
|
|
221
|
+
const controlArgs = parseArgs(output.args.slice(2));
|
|
222
|
+
requiredOption(controlArgs, "run");
|
|
223
|
+
requiredOption(controlArgs, "project-root");
|
|
224
|
+
}
|
|
225
|
+
if (io.stdin?.isTTY && ["work", "stage"].includes(output.args[0] ?? "") && output.args[1] === "finish"
|
|
226
|
+
&& hasOption(parseArgs(output.args.slice(2)), "result-stdin")) {
|
|
227
|
+
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);
|
|
228
|
+
}
|
|
79
229
|
const classification = classifyCliOperation(output.args, env);
|
|
80
230
|
if (requiresExplicitUpgradeAuthorization(output.args) && classification.mode !== "mb_upgrade") {
|
|
81
231
|
throw new AppError("upgrade_authorization_required", "This mb-upgrade command requires the explicit mb-upgrade authorization marker and allowlisted target context", 1, {
|
|
@@ -83,26 +233,190 @@ export async function runCli(args, io = defaultIo, env = process.env) {
|
|
|
83
233
|
required_marker: "DD_FLOW_COMPATIBILITY_MODE=mb-upgrade"
|
|
84
234
|
});
|
|
85
235
|
}
|
|
86
|
-
|
|
236
|
+
// Registration may bootstrap the resource registry, but never the unrelated
|
|
237
|
+
// RUN store. Its caller's absolute deadline also bounds that first open.
|
|
238
|
+
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) } : {}) } : {};
|
|
239
|
+
if (output.args[0] === "runtime" && output.args[1] === "scope" && ["fence", "stop"].includes(output.args[2] ?? ""))
|
|
240
|
+
maintenanceOptions.resourceDatabaseMode = "retirement";
|
|
241
|
+
// Keep ordinary CLI JSON/stderr compatibility; bounded maintenance callers
|
|
242
|
+
// provide this request's deadline and consume its structured phase evidence.
|
|
243
|
+
if (maintenanceOptions.databaseDeadlineAt !== undefined)
|
|
244
|
+
maintenanceDiagnostics = runtimeMaintenanceDiagnostics(output.args, maintenanceOptions.databaseDeadlineAt, line => io.stderr.write(line));
|
|
245
|
+
const routerContext = createRouterContext(env, maintenanceOptions.databaseDeadlineAt);
|
|
246
|
+
const publicCommand = quote(["dd-flow", ...output.args]);
|
|
247
|
+
const nativeCommand = quote(["dd-flow", ...args]);
|
|
248
|
+
const lifecycleCommand = parseLifecycleCommand(publicCommand);
|
|
249
|
+
if (lifecycleCommand.kind === "standalone")
|
|
250
|
+
validateLifecyclePathAliases(output.args, lifecycleCommand.invocation.operation);
|
|
251
|
+
const managedScope = lifecycleCommand.kind === "standalone" && env.DD_FLOW_DAEMON_ID ? cliManagedInvocationScope({ ...routerContext, env }) : null;
|
|
252
|
+
const nativeEvent = env.DD_FLOW_DAEMON_ID && lifecycleCommand.kind === "standalone"
|
|
253
|
+
? observedLifecycleNativeEvent(routerContext, nativeCommand, env.DD_FLOW_DAEMON_ID) : null;
|
|
254
|
+
const nativeEventKey = nativeEvent && routerContext.db.get("SELECT event_key FROM hook_events WHERE project_id = ? AND id = ?", [nativeEvent.project_id, nativeEvent.id])?.event_key;
|
|
255
|
+
const observedInvocation = managedScope ? observedLifecycleInvocation(routerContext, publicCommand, managedScope, nativeEventKey ? { eventKey: nativeEventKey } : undefined) : null;
|
|
256
|
+
if (managedScope && suppliedInvocationId
|
|
257
|
+
&& routerContext.db.get("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'lifecycle_invocations'")
|
|
258
|
+
&& routerContext.db.get("SELECT id FROM lifecycle_invocations WHERE id = ?", [suppliedInvocationId])) {
|
|
259
|
+
const suppliedScope = lifecycleInvocationScope({ ...routerContext, env }, suppliedInvocationId);
|
|
260
|
+
if (["projectRoot", "daemonId", "rootSessionId", "runId", "generation"].some(key => suppliedScope[key] !== managedScope[key])) {
|
|
261
|
+
throw new AppError("invocation_scope_mismatch", "Explicit lifecycle assignment differs from the current physical native owner", 1, { effect: "no_effect", recoverable: false });
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const diagnostic = env.DD_FLOW_DAEMON_ID && lifecycleCommand.kind === "standalone"
|
|
265
|
+
? observedLifecycleDiagnostic(routerContext, publicCommand, env.DD_FLOW_DAEMON_ID, observedInvocation?.id) : null;
|
|
266
|
+
if (diagnostic && (!nativeEvent || Number(diagnostic.hook_event_id) > nativeEvent.id)) {
|
|
267
|
+
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 });
|
|
268
|
+
}
|
|
269
|
+
let manualRecoveryHookEventKey;
|
|
270
|
+
if (!observedInvocation && !suppliedInvocationId && env.DD_FLOW_DAEMON_ID && lifecycleCommand.kind === "standalone") {
|
|
271
|
+
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]);
|
|
272
|
+
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;
|
|
273
|
+
if (!manual || JSON.stringify(manual) !== JSON.stringify(managedScope))
|
|
274
|
+
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 });
|
|
275
|
+
assertManualRecoveryInvocationTarget({ ...routerContext, env }, manual, publicCommand);
|
|
276
|
+
manualRecoveryHookEventKey = receipt.event_key;
|
|
277
|
+
}
|
|
278
|
+
const invocationId = observedInvocation?.id ?? suppliedInvocationId;
|
|
279
|
+
let boundInvocationArgs;
|
|
280
|
+
try {
|
|
281
|
+
boundInvocationArgs = observedInvocation
|
|
282
|
+
? resolveObservedLifecycleArgs(routerContext, observedInvocation.id, output.args)
|
|
283
|
+
: invocationId && invocationId !== suppliedInvocationId
|
|
284
|
+
? [...output.args.filter((_, index, values) => values[index - 1] !== "--invocation-id" && values[index] !== "--invocation-id"), "--invocation-id", invocationId]
|
|
285
|
+
: output.args;
|
|
286
|
+
}
|
|
287
|
+
catch (error) {
|
|
288
|
+
if (!(observedInvocation && error instanceof AppError))
|
|
289
|
+
throw error;
|
|
290
|
+
const rejectionContext = createContext({ ...env }, "hook");
|
|
291
|
+
try {
|
|
292
|
+
Object.assign(error.details, settleLifecyclePreparationRejection(rejectionContext, observedInvocation.id, error));
|
|
293
|
+
}
|
|
294
|
+
finally {
|
|
295
|
+
rejectionContext.db.close?.();
|
|
296
|
+
}
|
|
297
|
+
throw error;
|
|
298
|
+
}
|
|
299
|
+
const observedOperation = lifecycleCommand.kind === "standalone" ? lifecycleCommand.invocation.operation : null;
|
|
300
|
+
if (observedInvocation && !observedOperation)
|
|
301
|
+
throw new AppError("invocation_command_invalid", "Observed lifecycle command could not be parsed", 1);
|
|
302
|
+
const invocationScope = invocationId ? lifecycleInvocationScope({ ...routerContext, env }, invocationId) : undefined;
|
|
303
|
+
const expandedInvocationArgs = invocationScope && observedOperation
|
|
304
|
+
? expandLifecycleInvocationArgs(routerContext, boundInvocationArgs, invocationScope, observedOperation)
|
|
305
|
+
: boundInvocationArgs;
|
|
306
|
+
const retainedOutput = observedInvocation ? parseOutputOptions(expandedInvocationArgs) : null;
|
|
307
|
+
if (retainedOutput?.responseFile)
|
|
308
|
+
output.responseFile = retainedOutput.responseFile;
|
|
309
|
+
let invocationArgs = retainedOutput?.args ?? expandedInvocationArgs;
|
|
87
310
|
emitHumanProgress(io, output, progress, "start");
|
|
88
311
|
if (output.progressJsonl) {
|
|
89
312
|
writeProgressJsonl(io, "start", `Starting ${output.args.slice(0, 3).join(" ")}`);
|
|
90
313
|
heartbeat = setInterval(() => writeProgressJsonl(io, "running", "Command is still running"), 15_000);
|
|
91
314
|
}
|
|
92
|
-
|
|
93
|
-
|
|
315
|
+
// Route by retained authority, so a mistyped project path reaches the CLI
|
|
316
|
+
// validator instead of selecting a different engine or failing preflight.
|
|
317
|
+
const scopeProjectRoot = invocationScope?.projectRoot ?? resolveCommandProjectRoot(routerContext, output.args, resolveProjectRootBeforeRouting(output.args, env, maintenanceOptions.databaseDeadlineAt));
|
|
318
|
+
assertAuxiliaryCommandScope({ ...routerContext, env }, publicCommand, scopeProjectRoot);
|
|
319
|
+
invocationArgs = resolveCommandPaths(routerContext, invocationArgs, scopeProjectRoot);
|
|
320
|
+
const routedArgs = output.responseFile ? [...invocationArgs, "--response-file", output.responseFile] : invocationArgs;
|
|
94
321
|
const routed = await routeArgsThroughEngine(routerContext, output.json ? [...routedArgs, "--json", ...(output.progressJsonl ? ["--progress-jsonl"] : [])] : routedArgs, io, io.stdin, env, scopeProjectRoot);
|
|
95
322
|
if (routed?.routed && !routed.inProcess) {
|
|
96
323
|
if (heartbeat)
|
|
97
324
|
clearInterval(heartbeat);
|
|
98
|
-
emitHumanProgress(io, output, progress, "done");
|
|
325
|
+
emitHumanProgress(io, output, progress, routed.exitCode === 0 ? "done" : "failed");
|
|
326
|
+
if (output.progressJsonl && routed.exitCode !== 0)
|
|
327
|
+
writeProgressJsonl(io, "failed", "Routed engine failed", { exit_code: routed.exitCode });
|
|
99
328
|
return routed.exitCode;
|
|
100
329
|
}
|
|
330
|
+
let preparedInput;
|
|
331
|
+
let preparedRoute;
|
|
332
|
+
if (!invocationId) {
|
|
333
|
+
const preparationContext = createContext({ ...env }, "read_existing", undefined, maintenanceOptions);
|
|
334
|
+
try {
|
|
335
|
+
preparedInput = await prepareCliInput(invocationArgs, preparationContext, io, scopeProjectRoot, output.responseFile, undefined, commandProgress(io, output));
|
|
336
|
+
}
|
|
337
|
+
finally {
|
|
338
|
+
preparationContext.db.close?.();
|
|
339
|
+
}
|
|
340
|
+
}
|
|
101
341
|
const dispatchContext = routed?.inProcess
|
|
102
|
-
? createContext({ ...env, DD_FLOW_ENGINE_MODE: "1", DD_FLOW_ENGINE_HOME: "in-process", DD_FLOW_ROUTED_FROM: getCliVersionReport().cli.version }, databaseModeForCommand(
|
|
103
|
-
: createContext(env, databaseModeForCommand(
|
|
104
|
-
|
|
105
|
-
|
|
342
|
+
? 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)
|
|
343
|
+
: createContext({ ...env }, databaseModeForCommand(invocationArgs, classification), undefined, maintenanceOptions);
|
|
344
|
+
diagnosticHome = dispatchContext.ddFlowHome;
|
|
345
|
+
dispatchContext.env.DD_FLOW_INVOCATION_READONLY = classification.mode === "read_only_diagnostics" ? "1" : undefined;
|
|
346
|
+
let dispatchArgs = invocationArgs;
|
|
347
|
+
let retainedOutcome;
|
|
348
|
+
// Issued transports may deliver their receipt asynchronously. Observe only
|
|
349
|
+
// after admission; retained outcomes must not need a fresh hook receipt.
|
|
350
|
+
if (invocationId) {
|
|
351
|
+
const command = quote(["dd-flow", ...invocationArgs]);
|
|
352
|
+
const admission = await awaitLifecycleInvocation(dispatchContext, { id: invocationId, command,
|
|
353
|
+
validateArgs: () => {
|
|
354
|
+
try {
|
|
355
|
+
preparedRoute = prepareCommandRoute(invocationArgs);
|
|
356
|
+
}
|
|
357
|
+
catch (error) {
|
|
358
|
+
if (error instanceof AppError && error.code === "usage" && ["invocation-id", "hook-event-id"].includes(String(error.details.parameter ?? ""))) {
|
|
359
|
+
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 });
|
|
360
|
+
}
|
|
361
|
+
throw error;
|
|
362
|
+
}
|
|
363
|
+
},
|
|
364
|
+
prepare: async () => { preparedInput = await prepareCliInput(invocationArgs, dispatchContext, io, scopeProjectRoot, output.responseFile, preparedRoute, commandProgress(io, output)); },
|
|
365
|
+
assertCurrent: scope => assertLifecycleInvocationCurrent(dispatchContext, scope, command), progress: commandProgress(io, output) });
|
|
366
|
+
if (admission.replay) {
|
|
367
|
+
retainedOutcome = admission.outcome;
|
|
368
|
+
settleHook = undefined;
|
|
369
|
+
delete dispatchContext.observeLifecycleHook;
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
invocationSettlement = { context: dispatchContext, id: invocationId };
|
|
373
|
+
if (observedInvocation)
|
|
374
|
+
retainObservedLifecycleSemantics(dispatchContext, invocationId, invocationArgs);
|
|
375
|
+
dispatchArgs = [...invocationArgs, "--hook-event-id", admission.eventKey];
|
|
376
|
+
preparedInput?.route?.parsed.options.set("hook-event-id", [admission.eventKey]);
|
|
377
|
+
dispatchContext.env.DD_FLOW_INVOCATION_SCOPE = JSON.stringify(lifecycleInvocationScope(dispatchContext, invocationId));
|
|
378
|
+
dispatchContext.env.DD_FLOW_CURRENT_INVOCATION = invocationId;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
if (!invocationId && manualRecoveryHookEventKey) {
|
|
382
|
+
// Keep the physically scoped receipt selected by admission; a service must
|
|
383
|
+
// not rediscover a different daemon's matching command event.
|
|
384
|
+
if (!invocationArgs.includes("--hook-event-id"))
|
|
385
|
+
dispatchArgs = [...invocationArgs, "--hook-event-id", manualRecoveryHookEventKey];
|
|
386
|
+
preparedInput?.route?.parsed.options.set("hook-event-id", [manualRecoveryHookEventKey]);
|
|
387
|
+
}
|
|
388
|
+
if (retainedOutcome?.error) {
|
|
389
|
+
const error = retainedOutcome.error;
|
|
390
|
+
throw new AppError(error.code, error.message, error.exitCode, error.details);
|
|
391
|
+
}
|
|
392
|
+
if (!retainedOutcome && !preparedInput)
|
|
393
|
+
preparedInput = await prepareCliInput(invocationArgs, dispatchContext, io, scopeProjectRoot, output.responseFile, undefined, commandProgress(io, output));
|
|
394
|
+
if (!retainedOutcome)
|
|
395
|
+
await preflightCliCompatibility(dispatchContext, invocationArgs, env, scopeProjectRoot);
|
|
396
|
+
if (!retainedOutcome) {
|
|
397
|
+
const command = !invocationId ? nativeCommand : quote(["dd-flow", ...dispatchArgs]);
|
|
398
|
+
const event = !invocationId ? nativeEvent : null;
|
|
399
|
+
settleHook = observeLifecycleCommand(dispatchContext, command, event ?? undefined);
|
|
400
|
+
}
|
|
401
|
+
const result = preparePromptDelivery(retainedOutcome ? retainedOutcome.result : await dispatch(dispatchArgs, dispatchContext, io, scopeProjectRoot, classification, output, preparedInput));
|
|
402
|
+
if (!retainedOutcome && result && typeof result === "object" && "worker_prompt_markdown" in result && typeof result.worker_prompt_markdown === "string") {
|
|
403
|
+
try {
|
|
404
|
+
bindLifecyclePublishedPacket(dispatchContext, result.worker_prompt_markdown);
|
|
405
|
+
}
|
|
406
|
+
catch (cause) {
|
|
407
|
+
throw new AppError("invocation_packet_binding_failed", "Stage started but its published lifecycle assignments could not be retained", 1, { effect: "committed", cause: String(cause) });
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
const completedHook = settleHook;
|
|
411
|
+
settleHook = undefined;
|
|
412
|
+
completedHook?.({ result });
|
|
413
|
+
if (invocationSettlement) {
|
|
414
|
+
const completed = invocationSettlement;
|
|
415
|
+
invocationSettlement = undefined;
|
|
416
|
+
// A failure to persist an already completed result is an unknown reply,
|
|
417
|
+
// not a failed operation eligible for a new productive retry.
|
|
418
|
+
settleLifecycleInvocation(completed.context, completed.id, { result });
|
|
419
|
+
}
|
|
106
420
|
const refreshWarnings = refreshDashboardsAfterMutation(dispatchContext, output.args, result, scopeProjectRoot);
|
|
107
421
|
const resultWithWarnings = addWarnings(result, refreshWarnings);
|
|
108
422
|
if (heartbeat)
|
|
@@ -111,48 +425,159 @@ export async function runCli(args, io = defaultIo, env = process.env) {
|
|
|
111
425
|
writeProgressJsonl(io, "done", "Command finished");
|
|
112
426
|
emitHumanProgress(io, output, progress, "done");
|
|
113
427
|
const payload = attachProgress(resultWithWarnings, progress, output);
|
|
428
|
+
publishPromptDelivery(payload);
|
|
114
429
|
const visible = writeResponseFile(payload, output);
|
|
430
|
+
const maintenanceReceipt = payload;
|
|
431
|
+
const maintenanceExpiry = maintenanceReceipt?.lease_expires_at ?? maintenanceReceipt?.process?.lease_expires_at;
|
|
432
|
+
maintenanceDiagnostics?.phase("ack", { ...(typeof maintenanceExpiry === "string" && Number.isFinite(Date.parse(maintenanceExpiry)) ? { lease_expires_at: maintenanceExpiry } : {}) });
|
|
115
433
|
if (output.json)
|
|
116
434
|
writeJson(io.stdout, visible);
|
|
117
435
|
else
|
|
118
436
|
io.stdout.write(renderHumanResult(output.args, payload));
|
|
119
437
|
return completedCommandExitCode(payload);
|
|
120
438
|
}
|
|
121
|
-
catch (
|
|
439
|
+
catch (caught) {
|
|
440
|
+
const record = errorRecord(caught);
|
|
441
|
+
maintenanceDiagnostics?.failure(caught);
|
|
442
|
+
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 } : {}) });
|
|
443
|
+
if (!settleHook && !invocationSettlement && error.code !== "prompt_delivery_pending") {
|
|
444
|
+
let earlyContext;
|
|
445
|
+
try {
|
|
446
|
+
const command = quote(["dd-flow", ...args]);
|
|
447
|
+
const analysis = parseLifecycleCommand(command);
|
|
448
|
+
// Maintenance failures must not start an unrelated, unbounded hook
|
|
449
|
+
// lookup after their own request deadline has already expired.
|
|
450
|
+
const router = analysis.kind !== "none" || analysis.auxiliary ? createRouterContext(env) : null;
|
|
451
|
+
const event = router ? observedLifecycleNativeEvent(router, command, env.DD_FLOW_DAEMON_ID) : null;
|
|
452
|
+
// A correctable prepare rejection precedes the terminal command's
|
|
453
|
+
// single-use claim. Correlate its unique hook by the same lifecycle
|
|
454
|
+
// match key used at dispatch, including compound provider tool shells;
|
|
455
|
+
// otherwise a repaired retry sees two fresh, unclaimed events.
|
|
456
|
+
if (router && (event || (error.details.phase === "prepare" && error.details.effect === "no_effect"
|
|
457
|
+
&& fs.existsSync(path.join(router.ddFlowHome, "db.sqlite"))))) {
|
|
458
|
+
earlyContext = createContext({ ...env }, "hook");
|
|
459
|
+
observeLifecycleCommand(earlyContext, command, event ?? undefined)({ error });
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
catch (persistence) {
|
|
463
|
+
error.details.lifecycle_diagnostic_error = String(persistence);
|
|
464
|
+
}
|
|
465
|
+
finally {
|
|
466
|
+
earlyContext?.db.close?.();
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
if (diagnosticHome && (!isAppError(caught) || error.code === "storage_write_failed")) {
|
|
470
|
+
try {
|
|
471
|
+
const directory = path.join(diagnosticHome, "diagnostics", "cli");
|
|
472
|
+
fs.mkdirSync(directory, { recursive: true });
|
|
473
|
+
const artifact = path.join(directory, `${randomUUID()}.json`);
|
|
474
|
+
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 });
|
|
475
|
+
error.details.diagnostic_artifact = artifact;
|
|
476
|
+
}
|
|
477
|
+
catch (secondary) {
|
|
478
|
+
io.stderr.write(`CLI diagnostic persistence failed: ${String(secondary)}\n`);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
122
481
|
if (heartbeat)
|
|
123
482
|
clearInterval(heartbeat);
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
483
|
+
if (invocationSettlement && error.details.effect !== "committed") {
|
|
484
|
+
try {
|
|
485
|
+
if (isAppError(error)) {
|
|
486
|
+
delete error.details.retry_command;
|
|
487
|
+
delete error.details.retry_instruction;
|
|
488
|
+
// The common settlement classifies original validated proof before
|
|
489
|
+
// errorRecord clones diagnostic objects and loses capability identity.
|
|
490
|
+
settleLifecycleRejection(invocationSettlement.context, invocationSettlement.id, error);
|
|
491
|
+
}
|
|
492
|
+
else
|
|
493
|
+
settleLifecycleInvocation(invocationSettlement.context, invocationSettlement.id, { error: { code: "unexpected", message: String(error), exitCode: 1, details: {} } });
|
|
494
|
+
}
|
|
495
|
+
catch (persistenceError) {
|
|
496
|
+
if (isAppError(error))
|
|
497
|
+
error.details.recoverable = false;
|
|
498
|
+
io.stderr.write(`Invocation settlement persistence failed: ${String(persistenceError)}\n`);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
if (settleHook) {
|
|
502
|
+
try {
|
|
503
|
+
settleHook({ error: isAppError(error) ? error : { code: "unexpected", message: String(error), details: {} } });
|
|
504
|
+
}
|
|
505
|
+
catch (persistence) {
|
|
506
|
+
io.stderr.write(`${JSON.stringify({ primary: String(error), lifecycle_persistence_error: String(persistence) })}\n`);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
const payload = { ok: false, error: errorRecord(error) };
|
|
510
|
+
progress.splice(0, progress.length, ...progress.filter(event => event.phase !== "done"));
|
|
511
|
+
if (progress.length)
|
|
512
|
+
progress.push({ phase: "failed", message: payload.error.message, at: new Date().toISOString() });
|
|
127
513
|
emitHumanProgress(io, output, progress, "failed");
|
|
128
514
|
if (output.progressJsonl)
|
|
129
515
|
writeProgressJsonl(io, "failed", payload.error.message, { code: payload.error.code });
|
|
130
516
|
const errorPayload = attachProgress(payload, progress, output);
|
|
131
|
-
|
|
517
|
+
let visible = errorPayload;
|
|
518
|
+
try {
|
|
519
|
+
if (error.details.parameter !== "response-file")
|
|
520
|
+
visible = writeResponseFile(errorPayload, output);
|
|
521
|
+
}
|
|
522
|
+
catch (publication) {
|
|
523
|
+
error.details.response_publication_error = { code: publication?.code ?? "response_publication_failed", message: publication instanceof Error ? publication.message : String(publication) };
|
|
524
|
+
}
|
|
132
525
|
if (output.json && output.progressJsonl)
|
|
133
526
|
writeJson(io.stdout, errorPayload);
|
|
134
527
|
else if (output.json)
|
|
135
528
|
writeJson(io.stderr, visible);
|
|
136
529
|
else
|
|
137
|
-
io.stderr.write(renderHumanError(payload));
|
|
530
|
+
io.stderr.write(renderHumanError(payload, args));
|
|
138
531
|
return isAppError(error) ? error.exitCode : 1;
|
|
139
532
|
}
|
|
533
|
+
finally {
|
|
534
|
+
maintenanceDiagnostics?.close();
|
|
535
|
+
}
|
|
140
536
|
}
|
|
141
|
-
function resolveProjectRootBeforeRouting(args, env) {
|
|
537
|
+
function resolveProjectRootBeforeRouting(args, env, deadlineAt) {
|
|
142
538
|
const explicit = resolveExplicitProjectRoot(args);
|
|
143
539
|
if (explicit)
|
|
144
540
|
return explicit;
|
|
145
541
|
try {
|
|
146
542
|
// Router identity lookup is read-only; it must not initialize or migrate runtime storage.
|
|
147
|
-
|
|
543
|
+
const context = createContext(env, "read_existing", undefined, deadlineAt === undefined ? {} : { databaseDeadlineAt: deadlineAt });
|
|
544
|
+
try {
|
|
545
|
+
return resolveOperationProjectRoot(context, args);
|
|
546
|
+
}
|
|
547
|
+
finally {
|
|
548
|
+
context.db.close?.();
|
|
549
|
+
}
|
|
148
550
|
}
|
|
149
551
|
catch {
|
|
150
552
|
return null;
|
|
151
553
|
}
|
|
152
554
|
}
|
|
153
555
|
function databaseModeForCommand(args, classification) {
|
|
556
|
+
if (isNativeHookIngress(args))
|
|
557
|
+
return "hook";
|
|
558
|
+
if (args[0] === "run" && args[1] === "reconcile")
|
|
559
|
+
return args.includes("--apply") ? "reconcile" : "read_existing";
|
|
560
|
+
if (isRuntimeMaintenance(args))
|
|
561
|
+
return "read_existing";
|
|
154
562
|
const [family, command] = args;
|
|
563
|
+
if (family === "migration" && command === "readiness")
|
|
564
|
+
return "prepared_existing";
|
|
565
|
+
// Managed attempts are issued only from a prepared store. Opening that
|
|
566
|
+
// existing writer must not run migrations before their input is admitted.
|
|
567
|
+
if (parseArgs(args.slice(2)).options.has("invocation-id"))
|
|
568
|
+
return "hook";
|
|
569
|
+
// Native hook ingress must remain synchronous, but it must not run schema
|
|
570
|
+
// migration/VACUUM while the provider is waiting for its decision. Startup
|
|
571
|
+
// prepares the store; the hook mode only verifies the prepared contract and
|
|
572
|
+
// records the receipt.
|
|
573
|
+
if (family === "codex" && command === "hook" && args[2] === "handle") {
|
|
574
|
+
// Only the participating PreToolUse ingress needs the prepared hot-path
|
|
575
|
+
// contract. SessionStart/Stop/PostToolUse are observational no-ops here
|
|
576
|
+
// and retain the ordinary startup path for backwards compatibility.
|
|
577
|
+
return optionalOption(parseArgs(args.slice(3)), "event") === "PreToolUse" ? "hook" : "initialize";
|
|
578
|
+
}
|
|
155
579
|
if (family === "engine" ||
|
|
580
|
+
family === "storage" && command === "migrate-writer" ||
|
|
156
581
|
family === "version" ||
|
|
157
582
|
family === "schema" ||
|
|
158
583
|
family === "canon" && command === "resolve" ||
|
|
@@ -166,86 +591,6 @@ function databaseModeForCommand(args, classification) {
|
|
|
166
591
|
return "read_existing";
|
|
167
592
|
return "initialize";
|
|
168
593
|
}
|
|
169
|
-
function renderHumanError(payload) {
|
|
170
|
-
const lines = [`dd-flow: ${payload.error.message}`];
|
|
171
|
-
const details = recordObject(payload.error.details);
|
|
172
|
-
const compatibility = recordObject(details?.compatibility);
|
|
173
|
-
const remediation = recordObject(details?.remediation);
|
|
174
|
-
if (compatibility) {
|
|
175
|
-
lines.push(`Compatibility: ${String(compatibility.verdict ?? "unknown")} (${String(compatibility.engine_resolution ?? "unknown")})`);
|
|
176
|
-
if (compatibility.blocked_operation)
|
|
177
|
-
lines.push(`Blocked operation: ${String(compatibility.blocked_operation)}`);
|
|
178
|
-
if (compatibility.install_hint)
|
|
179
|
-
lines.push(`Install compatible engine: ${String(compatibility.install_hint)}`);
|
|
180
|
-
}
|
|
181
|
-
if (remediation?.mb_upgrade_mode)
|
|
182
|
-
lines.push(`Upgrade mode: ${String(remediation.mb_upgrade_mode)}`);
|
|
183
|
-
if (details?.preferred_command)
|
|
184
|
-
lines.push(`Preferred command: ${String(details.preferred_command)}`);
|
|
185
|
-
const related = Array.isArray(details?.related_commands) ? details.related_commands.map(String) : [];
|
|
186
|
-
if (related.length > 0) {
|
|
187
|
-
lines.push("", "Related commands:");
|
|
188
|
-
for (const command of related)
|
|
189
|
-
lines.push(` ${command}`);
|
|
190
|
-
}
|
|
191
|
-
return `${lines.join("\n")}\n`;
|
|
192
|
-
}
|
|
193
|
-
function parseOutputOptions(args) {
|
|
194
|
-
const stripped = [];
|
|
195
|
-
let json = false;
|
|
196
|
-
let progressJsonl = false;
|
|
197
|
-
let responseFile = null;
|
|
198
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
199
|
-
const arg = args[index];
|
|
200
|
-
if (arg === undefined)
|
|
201
|
-
continue;
|
|
202
|
-
if (arg === "--json") {
|
|
203
|
-
json = true;
|
|
204
|
-
continue;
|
|
205
|
-
}
|
|
206
|
-
if (arg === "--progress-jsonl") {
|
|
207
|
-
progressJsonl = true;
|
|
208
|
-
continue;
|
|
209
|
-
}
|
|
210
|
-
if (arg === "--response-file") {
|
|
211
|
-
const file = args[++index];
|
|
212
|
-
if (!file)
|
|
213
|
-
throw new AppError("usage", "--response-file requires a path", 2);
|
|
214
|
-
responseFile = file;
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
stripped.push(arg);
|
|
218
|
-
}
|
|
219
|
-
if (responseFile && !json)
|
|
220
|
-
throw new AppError("usage", "--response-file requires --json", 2);
|
|
221
|
-
const hookJsonOnly = stripped[0] === "codex" && stripped[1] === "hook" && stripped[2] === "handle";
|
|
222
|
-
return { json: json || hookJsonOnly, progressJsonl, args: stripped, hookJsonOnly, responseFile };
|
|
223
|
-
}
|
|
224
|
-
/** Persist the complete machine response for harnesses that cap tool output.
|
|
225
|
-
* The visible response keeps routing fields but omits the potentially large
|
|
226
|
-
* rendered prompt; an agent can safely read the named file without rerunning
|
|
227
|
-
* the lifecycle command. */
|
|
228
|
-
function writeResponseFile(payload, output) {
|
|
229
|
-
if (!output.responseFile)
|
|
230
|
-
return payload;
|
|
231
|
-
const file = path.resolve(output.responseFile);
|
|
232
|
-
try {
|
|
233
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
234
|
-
const temporary = `${file}.tmp-${process.pid}-${crypto.randomUUID()}`;
|
|
235
|
-
fs.writeFileSync(temporary, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
236
|
-
fs.renameSync(temporary, file);
|
|
237
|
-
}
|
|
238
|
-
catch (error) {
|
|
239
|
-
// The lifecycle mutation may already be accepted. Do not retry it just
|
|
240
|
-
// because its optional full-response file could not be written.
|
|
241
|
-
return { ...(payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true, result: payload }), response_file: file, response_file_error: String(error) };
|
|
242
|
-
}
|
|
243
|
-
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
244
|
-
return { ok: true, response_file: file };
|
|
245
|
-
const visible = { ...payload, response_file: file };
|
|
246
|
-
delete visible.worker_prompt_markdown;
|
|
247
|
-
return visible;
|
|
248
|
-
}
|
|
249
594
|
function progressForCommand(args) {
|
|
250
595
|
const [family, command, subcommand] = args;
|
|
251
596
|
if (family === "merge-queue" && command === "wait-next") {
|
|
@@ -294,7 +639,12 @@ function writeProgressJsonl(io, phase, message, detail) {
|
|
|
294
639
|
io.stderr.write(`${JSON.stringify({ schema_id: "dd-flow/progress@1", phase, message, at: new Date().toISOString(), ...(detail ? { detail } : {}) })}\n`);
|
|
295
640
|
}
|
|
296
641
|
function commandProgress(io, output) {
|
|
297
|
-
return (message) =>
|
|
642
|
+
return (message) => {
|
|
643
|
+
if (output.progressJsonl)
|
|
644
|
+
writeProgressJsonl(io, "running", message);
|
|
645
|
+
else if (!output.json && !output.hookJsonOnly)
|
|
646
|
+
io.stderr.write(`[running] ${message}\n`);
|
|
647
|
+
};
|
|
298
648
|
}
|
|
299
649
|
function attachProgress(result, progress, output) {
|
|
300
650
|
if (progress.length === 0 || output.hookJsonOnly)
|
|
@@ -501,27 +851,701 @@ function stringValue(value) {
|
|
|
501
851
|
function shortSha(value) {
|
|
502
852
|
return value ? value.slice(0, 12) : "unknown";
|
|
503
853
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
854
|
+
function compositionProposalRetry(context, parsed) {
|
|
855
|
+
return {
|
|
856
|
+
retry_command: runtimeCommand(context, ["memory", "compose", "register", "--run", requiredOption(parsed, "run"),
|
|
857
|
+
"--project-root", requiredOption(parsed, "project-root"), "--request-key", requiredOption(parsed, "request-key"),
|
|
858
|
+
"--result-file", requiredOption(parsed, "result-file"), "--json", "--progress-jsonl"]),
|
|
859
|
+
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."
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
/** All caller supplied file/stdin data is consumed here, before a managed
|
|
863
|
+
* invocation is allowed to claim execution or a stage claims its hook event. */
|
|
864
|
+
async function prepareCliInput(...args) {
|
|
865
|
+
try {
|
|
866
|
+
return await prepareCliInputValue(...args);
|
|
867
|
+
}
|
|
868
|
+
catch (error) {
|
|
869
|
+
return prepareError(error);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
async function prepareCliInputValue(args, context, io, scopeProjectRoot, responseFile = null, route = prepareCommandRoute(args), progress) {
|
|
873
|
+
const { family, command, parsed } = route;
|
|
874
|
+
const prepared = { route };
|
|
875
|
+
if (optionalOption(parsed, "reason-file")) {
|
|
876
|
+
const value = prepareJsonFile(requiredOption(parsed, "reason-file"), "Check recovery evidence").value;
|
|
877
|
+
if (!value || typeof value.reason !== "string" || !value.reason.trim() || Object.keys(value).some(key => key !== "reason"))
|
|
878
|
+
throw new AppError("validation", "Recovery evidence must be a JSON object with one nonempty reason", 2);
|
|
879
|
+
prepared.retryReason = value.reason;
|
|
880
|
+
}
|
|
881
|
+
else if (optionalOption(parsed, "retry-check"))
|
|
882
|
+
prepared.retryReason = optionalOption(parsed, "reason");
|
|
883
|
+
if (route.key === "project register")
|
|
884
|
+
resolveProjectRoot(requiredOption(parsed, "root"));
|
|
885
|
+
if (route.key === "project archive")
|
|
886
|
+
prepareProjectArchive(context, { idOrAlias: parsed.positional[0], root: optionalOption(parsed, "root"), reason: requiredOption(parsed, "reason") });
|
|
887
|
+
if (route.key === "protocol register")
|
|
888
|
+
prepareProtocolRegistration(context, { handshakeId: requiredPosition(parsed, 0, "handshake-id"), projectRoot: requiredOption(parsed, "project-root"), workspacePath: optionalOption(parsed, "workspace-path") });
|
|
889
|
+
if (route.key === "canon register")
|
|
890
|
+
prepared.canonRegistration = prepareCanonRegistration(context, { root: requiredOption(parsed, "root") });
|
|
891
|
+
if (route.key === "run attach-stage")
|
|
892
|
+
prepareFlowRunStageAttachment(context, stageAttachmentInput(parsed));
|
|
893
|
+
if (route.key === "run complete-stage")
|
|
894
|
+
prepareRawFlowRunStageCompletion(context, stageCompletionInput(parsed));
|
|
895
|
+
if (route.key === "run override")
|
|
896
|
+
prepareFlowRunCompletion(context, runOverrideInput(parsed));
|
|
897
|
+
if (route.key === "engine bind-run")
|
|
898
|
+
prepared.engineBind = await prepareRunEngineBind(context, engineBindInput(parsed));
|
|
899
|
+
if (route.key === "worktree create")
|
|
900
|
+
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") });
|
|
901
|
+
if (route.key === "worktree bootstrap")
|
|
902
|
+
prepared.worktreeBootstrap = prepareWorktreeBootstrap(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "worktree bootstrap") });
|
|
903
|
+
if (route.key === "worktree close")
|
|
904
|
+
prepareWorktreeClose(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "worktree close"), mode: requiredOption(parsed, "mode") });
|
|
905
|
+
if (route.key.startsWith("plan item "))
|
|
906
|
+
preparePlanItemMutation(context, planItemInput(parsed, requiredScopeProjectRoot(scopeProjectRoot, route.key)));
|
|
907
|
+
if (route.key === "lane workspace set")
|
|
908
|
+
prepareLaneWorkspace(context, laneWorkspaceInput(parsed));
|
|
909
|
+
if (route.key === "lane waiter cancel")
|
|
910
|
+
prepareLaneWaiterCancellation(context, laneWaiterCancelInput(parsed));
|
|
911
|
+
if (route.key === "merge-queue complete")
|
|
912
|
+
prepareMergeJobCompletion(context, { ...mergeJobTarget(parsed, scopeProjectRoot), summary: requiredOption(parsed, "summary") });
|
|
913
|
+
if (route.key === "merge-queue cancel")
|
|
914
|
+
prepareMergeQueueCancellation(context, mergeCancellationInput(parsed, scopeProjectRoot));
|
|
915
|
+
if (route.key === "merge-queue wait-next")
|
|
916
|
+
prepareMergeQueueWait(context, mergeWaitInput(parsed, scopeProjectRoot));
|
|
917
|
+
if (route.key === "merge-queue next" || route.key === "merge-queue claim")
|
|
918
|
+
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") } : {}) });
|
|
919
|
+
if (route.key === "merge-queue note")
|
|
920
|
+
prepareMergeJobNote(context, { protocolId: requiredPosition(parsed, 0, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue note"), workerId: requiredWorkerId(parsed), summary: requiredOption(parsed, "summary") });
|
|
921
|
+
if (route.key === "merge bundle complete")
|
|
922
|
+
prepareMergeBundleCompletion(context, { projectRoot: requiredOption(parsed, "project-root"), workspacePath: workspacePathOption(parsed), workerId: requiredWorkerId(parsed), summary: requiredOption(parsed, "summary") });
|
|
923
|
+
if (route.key === "merge bundle claim")
|
|
924
|
+
prepareMergeBundleClaim(context, { projectRoot: requiredOption(parsed, "project-root"), workspacePath: workspacePathOption(parsed), workerId: requiredWorkerId(parsed) });
|
|
925
|
+
if (route.key === "merge bundle fail")
|
|
926
|
+
prepareMergeBundleFailure(context, { projectRoot: requiredOption(parsed, "project-root"), workspacePath: workspacePathOption(parsed), workerId: requiredWorkerId(parsed), reason: requiredOption(parsed, "reason"), requeue: parseBoolean(requiredOption(parsed, "requeue"), "requeue") });
|
|
927
|
+
if (route.key === "merge-queue fail")
|
|
928
|
+
prepareMergeJobFailure(context, { ...mergeJobTarget(parsed, scopeProjectRoot), reason: requiredOption(parsed, "reason"), requeue: parseBoolean(requiredOption(parsed, "requeue"), "requeue") });
|
|
929
|
+
if (route.key.startsWith("lane lock ") && route.key !== "lane lock status") {
|
|
930
|
+
const action = requiredPosition(parsed, 0, "lane lock action");
|
|
931
|
+
prepareLaneLock(context, {
|
|
932
|
+
projectRoot: requiredOption(parsed, "project-root"), lane: requiredOption(parsed, "lane"), workerId: requiredOption(parsed, "worker-id"),
|
|
933
|
+
workspacePath: workspacePathOption(parsed), leaseToken: optionalOption(parsed, "lease-token"), reason: optionalOption(parsed, "reason"),
|
|
934
|
+
ttlSeconds: optionalNumber(parsed, "ttl"), timeoutSeconds: optionalNumber(parsed, "timeout"), pollIntervalSeconds: optionalNumber(parsed, "poll-interval")
|
|
935
|
+
}, action);
|
|
936
|
+
}
|
|
937
|
+
if (route.key === "run recovery begin")
|
|
938
|
+
prepared.recoveryPayload = prepareRecoveryPayload(requiredOption(parsed, "interruption-json"), "interruption");
|
|
939
|
+
if (route.key === "run recovery seal")
|
|
940
|
+
prepared.recoveryPayload = prepareRecoveryPayload(requiredOption(parsed, "settlement-json"), "settlement");
|
|
941
|
+
if (route.key === "run recovery capture")
|
|
942
|
+
prepared.recoveryCapture = prepareRecoveryCapture(context, recoveryCaptureInput(parsed));
|
|
943
|
+
if (route.key === "run recovery resume")
|
|
944
|
+
prepared.recoveryResume = prepareRunRecoveryResume(context, recoveryResumeInput(parsed));
|
|
945
|
+
if (route.key === "run drive answer")
|
|
946
|
+
prepared.controllerAnswer = prepareRunControllerAnswer(context, controllerAnswerInput(parsed));
|
|
947
|
+
if (route.key === "run drive launch")
|
|
948
|
+
prepared.controllerLaunch = prepareRunControllerLaunch(context, controllerLaunchInput(parsed));
|
|
949
|
+
if (route.key === "run drive context")
|
|
950
|
+
prepared.controllerContext = prepareRunControllerContext(context, controllerContextInput(parsed));
|
|
951
|
+
if (route.key === "protocol transition" || route.key === "transition")
|
|
952
|
+
prepared.protocolTransition = prepareProtocolTransition(context, protocolTransitionInput(parsed, scopeProjectRoot ?? resolveOperationProjectRoot(context, args)));
|
|
953
|
+
if (route.key === "codex home init")
|
|
954
|
+
prepared.codexHome = prepareCodexHome(context, codexHomeInput(parsed));
|
|
955
|
+
if (route.key === "codex home remove")
|
|
956
|
+
prepareCodexHomeRemoval(context, { projectRoot: requiredOption(parsed, "project-root"), profile: optionalOption(parsed, "profile"), mode: requiredOption(parsed, "mode") });
|
|
957
|
+
if (["codex hooks install", "codex hooks remove"].includes(route.key))
|
|
958
|
+
prepared.codexHooks = prepareCodexHooks(context, { projectRoot: requiredOption(parsed, "project-root"), target: optionalOption(parsed, "target"), profile: optionalOption(parsed, "profile"), yes: hasOption(parsed, "yes") });
|
|
959
|
+
if (route.key === "migration apply")
|
|
960
|
+
prepared.migrationApply = prepareMigrationApply(context, migrationApplyInput(parsed, classifyCliOperation(args, context.env)));
|
|
961
|
+
if (route.key === "migration readiness")
|
|
962
|
+
prepared.upgradeStorageScope = {
|
|
963
|
+
projectRoot: requiredOption(parsed, "project-root"), workspaceRoot: requiredOption(parsed, "workspace-root"), requestId: requiredOption(parsed, "request-id")
|
|
964
|
+
};
|
|
965
|
+
if (route.key === "migration verify")
|
|
966
|
+
prepared.migrationVerification = verifyMigrationReport(context, { file: requiredOption(parsed, "file") });
|
|
967
|
+
if (responseFile)
|
|
968
|
+
prepareResponseDestination(responseFile);
|
|
969
|
+
if (family === "dashboard") {
|
|
970
|
+
const action = dashboardAction(command);
|
|
971
|
+
prepareDashboardOptions({ format: optionalOption(parsed, "format"), viewer: optionalOption(parsed, "viewer"), open: optionalOption(parsed, "open") });
|
|
972
|
+
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") });
|
|
973
|
+
if (action === "refresh" && target.kind !== "global" && optionalOption(parsed, "output") !== undefined) {
|
|
974
|
+
throw new AppError("validation", "--output is supported only for global dashboard refresh; use dashboard render for a custom project/protocol output", 2, { parameter: "output" });
|
|
975
|
+
}
|
|
976
|
+
if (target.kind === "protocol")
|
|
977
|
+
requireProtocol(context, target.protocolId, target.project.id);
|
|
978
|
+
if (action !== "data") {
|
|
979
|
+
if (target.kind === "global" || target.kind === "all_projects") {
|
|
980
|
+
prepareGlobalDashboardRender(context, { output: optionalOption(parsed, "output"), format: optionalOption(parsed, "format") ?? (command?.endsWith("-global") ? undefined : "html") });
|
|
981
|
+
}
|
|
982
|
+
else {
|
|
983
|
+
const format = optionalOption(parsed, "format") ?? (action === "open" || hasOption(parsed, "project") ? "html" : undefined);
|
|
984
|
+
const input = { projectRoot: target.project.root, format, ...(target.kind === "protocol" ? { protocol: target.protocolId } : {}) };
|
|
985
|
+
if (action === "refresh")
|
|
986
|
+
prepareDashboardRefresh(context, { ...input, open: optionalOption(parsed, "open") });
|
|
987
|
+
else if (action === "render")
|
|
988
|
+
prepareDashboardRender(context, { ...input, output: optionalOption(parsed, "output") });
|
|
989
|
+
else if (target.kind === "protocol") {
|
|
990
|
+
if (format !== "html")
|
|
991
|
+
throw new AppError("validation", "Protocol dashboard open supports --format html", 2, { protocol: target.protocolId });
|
|
992
|
+
prepareDashboardRender(context, input);
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
if (route.key === "prompt render") {
|
|
998
|
+
const input = workerPromptInput(parsed);
|
|
999
|
+
prepared.workerPrompt = { input, value: prepareWorkerPrompt(context, input) };
|
|
1000
|
+
}
|
|
1001
|
+
if (route.key === "run snapshot restore" || route.key === "run snapshot bootstrap restore") {
|
|
1002
|
+
try {
|
|
1003
|
+
const input = snapshotRestoreInput(parsed);
|
|
1004
|
+
if (route.key === "run snapshot restore")
|
|
1005
|
+
prepared.snapshotRestore = prepareEvalRunRestore(context, input);
|
|
1006
|
+
else
|
|
1007
|
+
prepared.bootstrapRestore = prepareEvalBootstrapRestore(input);
|
|
1008
|
+
}
|
|
1009
|
+
catch (error) {
|
|
1010
|
+
return prepareError(error, { parameter: "snapshot/project-root" });
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
if (route.key === "run snapshot bootstrap create") {
|
|
1014
|
+
try {
|
|
1015
|
+
prepareEvalBootstrapSnapshot(context, { projectRoot: requiredOption(parsed, "project-root"), output: requiredOption(parsed, "output"), flow: optionalOption(parsed, "flow") });
|
|
1016
|
+
}
|
|
1017
|
+
catch (error) {
|
|
1018
|
+
return prepareError(error, { parameter: "project-root/output" });
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
if (route.key === "run snapshot create") {
|
|
1022
|
+
prepareEvalRunSnapshotCreation(context, snapshotCreateInput(parsed));
|
|
1023
|
+
}
|
|
1024
|
+
if (route.key === "run start") {
|
|
1025
|
+
try {
|
|
1026
|
+
const input = runStartInput(parsed);
|
|
1027
|
+
prepared.runStart = { input, value: prepareFlowRunStart(context, input) };
|
|
1028
|
+
}
|
|
1029
|
+
catch (error) {
|
|
1030
|
+
return prepareError(error, { parameter: "run start" });
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
if (route.key === "run fork") {
|
|
1034
|
+
try {
|
|
1035
|
+
prepared.runFork = prepareRunFork(runForkInput(parsed));
|
|
1036
|
+
}
|
|
1037
|
+
catch (error) {
|
|
1038
|
+
return prepareError(error, { parameter: "from/output" });
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
if (route.key === "runtime scope status")
|
|
1042
|
+
prepareRuntimeScopeStatus(optionalOption(parsed, "scope-id"), optionalOption(parsed, "scope-ids-json"));
|
|
1043
|
+
if (["runtime scope fence", "runtime scope stop"].includes(route.key))
|
|
1044
|
+
prepareRuntimeScopeFence(requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id"));
|
|
1045
|
+
if (route.key === "runtime scope resume")
|
|
1046
|
+
prepareRuntimeScopeResumeRequest(context, runtimeScopeResumeInput(parsed));
|
|
1047
|
+
if (route.key === "runtime scope reconcile")
|
|
1048
|
+
prepareRuntimeScopeReconciliation(context, requiredOption(parsed, "scope-id"), Number(requiredOption(parsed, "generation")), requiredOption(parsed, "request-id"));
|
|
1049
|
+
if (route.key === "runtime scope serve")
|
|
1050
|
+
prepareRuntimeScopeServing(context, requiredOption(parsed, "scope-id"), Number(requiredOption(parsed, "generation")));
|
|
1051
|
+
if (route.key === "runtime scope control") {
|
|
1052
|
+
try {
|
|
1053
|
+
const raw = optionalOption(parsed, "manifest-json");
|
|
1054
|
+
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);
|
|
1055
|
+
}
|
|
1056
|
+
catch (error) {
|
|
1057
|
+
return prepareError(error, { parameter: "scope-id/request-id/manifest-json" });
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
if (family === "runtime" && command === "process")
|
|
1061
|
+
Object.assign(prepared, prepareRuntimeProcess(context, route.key, parsed));
|
|
1062
|
+
if (route.key === "runtime process start") {
|
|
1063
|
+
try {
|
|
1064
|
+
prepareRuntimeService(context, runtimeServiceInput(parsed));
|
|
1065
|
+
}
|
|
1066
|
+
catch (error) {
|
|
1067
|
+
return prepareError(error, { parameter: "runtime process start" });
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
if (family === "work" && command === "retry") {
|
|
1071
|
+
try {
|
|
1072
|
+
prepareWorkRetry(context, requiredPosition(parsed, 0, "work-id"), requiredOption(parsed, "reason"));
|
|
1073
|
+
}
|
|
1074
|
+
catch (error) {
|
|
1075
|
+
return prepareError(error, { parameter: "work-id/reason" });
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
if (family === "work" && (command === "fail" || command === "cancel")) {
|
|
1079
|
+
try {
|
|
1080
|
+
prepareWorkSettlement(context, requiredPosition(parsed, 0, "work-id"), command === "fail" ? "failed" : "cancelled", requiredOption(parsed, "reason"));
|
|
1081
|
+
}
|
|
1082
|
+
catch (error) {
|
|
1083
|
+
return prepareError(error, { parameter: "work-id/reason" });
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
if (family === "work" && (command === "deps" || command === "delete")) {
|
|
1087
|
+
try {
|
|
1088
|
+
if (command === "delete")
|
|
1089
|
+
prepareWorkDelete(context, requiredPosition(parsed, 0, "work-id"));
|
|
1090
|
+
else
|
|
1091
|
+
prepareWorkDeps(context, { workId: requiredPosition(parsed, 1, "work-id"), action: requiredPosition(parsed, 0, "action"), on: parsed.options.get("on") ?? [] });
|
|
1092
|
+
}
|
|
1093
|
+
catch (error) {
|
|
1094
|
+
return prepareError(error, { parameter: "work-id/on" });
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
if (route.key === "cleanup apply") {
|
|
1098
|
+
const planFile = requiredOption(parsed, "plan-file");
|
|
1099
|
+
try {
|
|
1100
|
+
prepared.cleanup = prepareCleanupApply(context, { projectRoot: requiredOption(parsed, "project-root"), planFile, reason: requiredOption(parsed, "reason"), force: hasOption(parsed, "force") });
|
|
1101
|
+
}
|
|
1102
|
+
catch (error) {
|
|
1103
|
+
return prepareError(error, { parameter: "plan-file", file: planFile });
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
if (route.key === "stage block" || route.key === "stage unblock") {
|
|
1107
|
+
const input = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), stage: requiredOption(parsed, "stage"), workId: requiredOption(parsed, "work") };
|
|
1108
|
+
try {
|
|
1109
|
+
if (route.key === "stage block") {
|
|
1110
|
+
const summaryFile = optionalOption(parsed, "summary-file"), summaryStdin = hasOption(parsed, "summary-stdin");
|
|
1111
|
+
if (Boolean(summaryFile) === summaryStdin)
|
|
1112
|
+
throw new AppError("usage", "stage block requires exactly one of --summary-file or --summary-stdin", 2);
|
|
1113
|
+
prepared.interactionText = summaryFile ? prepareTextFile(summaryFile, "Runtime block summary").text : await readStdin(io.stdin);
|
|
1114
|
+
prepareStageRuntimeBlock(context, { ...input, kind: requiredOption(parsed, "kind"), code: requiredOption(parsed, "code"), summary: prepared.interactionText, retryable: hasOption(parsed, "retryable") });
|
|
1115
|
+
}
|
|
1116
|
+
else
|
|
1117
|
+
prepareStageRuntimeUnblock(context, input);
|
|
1118
|
+
}
|
|
1119
|
+
catch (error) {
|
|
1120
|
+
return prepareError(error, { parameter: "stage/work/summary" });
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
if (route.key === "zcode invocation prepare") {
|
|
1124
|
+
const value = prepareJsonText(await readStdin(io.stdin), { file: "<stdin>", label: "invocation issuance" }).value;
|
|
1125
|
+
const payload = value && typeof value === "object" ? value : {};
|
|
1126
|
+
try {
|
|
1127
|
+
prepared.lifecycleIssuance = prepareLifecycleIssuance(payload.command, payload.scope);
|
|
1128
|
+
}
|
|
1129
|
+
catch (error) {
|
|
1130
|
+
if (error instanceof AppError && ["invocation_command_invalid", "invocation_scope_invalid"].includes(error.code))
|
|
1131
|
+
return prepareError(new AppError("usage", error.message, 2, { cause_code: error.code }), { parameter: "stdin" });
|
|
1132
|
+
throw error;
|
|
1133
|
+
}
|
|
1134
|
+
assertLifecycleInvocationCurrent(context, prepared.lifecycleIssuance.scope, prepared.lifecycleIssuance.command);
|
|
1135
|
+
}
|
|
1136
|
+
if (route.key === "work add-batch") {
|
|
1137
|
+
const file = requiredOption(parsed, "file");
|
|
1138
|
+
try {
|
|
1139
|
+
assertGenericWorkBatchAllowed(context, requiredOption(parsed, "parent"));
|
|
1140
|
+
prepared.workBatch = prepareWorkBatch(context, { parentWorkId: requiredOption(parsed, "parent"), file,
|
|
1141
|
+
...(optionalOption(parsed, "stage") ? { stage: optionalOption(parsed, "stage") } : {}),
|
|
1142
|
+
...(optionalOption(parsed, "request-key") ? { requestKey: optionalOption(parsed, "request-key") } : {}) });
|
|
1143
|
+
}
|
|
1144
|
+
catch (error) {
|
|
1145
|
+
return prepareError(error, { parameter: "file/parent", file });
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
if (route.key === "work repair add") {
|
|
1149
|
+
const taskFile = optionalOption(parsed, "task-file"), taskStdin = hasOption(parsed, "task-stdin");
|
|
1150
|
+
if (Boolean(taskFile) === taskStdin)
|
|
1151
|
+
throw new AppError("usage", "work repair add requires exactly one of --task-file or --task-stdin", 2);
|
|
1152
|
+
if (taskStdin && io.stdin?.isTTY)
|
|
1153
|
+
throw new AppError("usage", "--task-stdin requires piped or redirected input", 2);
|
|
1154
|
+
const task = taskFile ? prepareTextFile(taskFile, "Repair task").text : await readStdin(io.stdin);
|
|
1155
|
+
prepared.workRepair = prepareCliWorkRepair(context, parsed, task, scopeProjectRoot);
|
|
1156
|
+
}
|
|
1157
|
+
if (route.key === "run config set" || route.key === "run vars set") {
|
|
1158
|
+
const input = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 1, "run-id"), key: requiredOption(parsed, "key"), value: requiredOption(parsed, "value") };
|
|
1159
|
+
try {
|
|
1160
|
+
if (route.key === "run config set")
|
|
1161
|
+
prepareFlowRunConfig(context, { ...input, reason: requiredOption(parsed, "reason") });
|
|
1162
|
+
else
|
|
1163
|
+
prepareFlowRunVariable(context, input);
|
|
1164
|
+
}
|
|
1165
|
+
catch (error) {
|
|
1166
|
+
return prepareError(error, { parameter: "run config/vars" });
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
if (route.key === "schema validate") {
|
|
1170
|
+
try {
|
|
1171
|
+
prepared.schemaValidation = dispatchSchema(context, command, parsed);
|
|
1172
|
+
}
|
|
1173
|
+
catch (error) {
|
|
1174
|
+
return prepareError(error, { parameter: "file", file: requiredOption(parsed, "file") });
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
if (route.key === "project config set") {
|
|
1178
|
+
try {
|
|
1179
|
+
prepareProjectConfigValue(requiredOption(parsed, "key"), requiredOption(parsed, "value"));
|
|
1180
|
+
}
|
|
1181
|
+
catch (error) {
|
|
1182
|
+
return prepareError(error, { parameter: "key/value" });
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
if (family === "session" && command === "register") {
|
|
1186
|
+
try {
|
|
1187
|
+
prepared.sessionPayload = prepareFlowSessionPayload({ payloadBase64: optionalOption(parsed, "payload-base64"), payloadFile: optionalOption(parsed, "payload-file"), payloadJson: optionalOption(parsed, "payload-json") });
|
|
1188
|
+
}
|
|
1189
|
+
catch (error) {
|
|
1190
|
+
return prepareError(error, { parameter: "session payload" });
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
const run = (projectRoot, runId) => {
|
|
1194
|
+
try {
|
|
1195
|
+
const project = requireProjectByRoot(context, resolveProjectRoot(projectRoot));
|
|
1196
|
+
const row = context.db.get("SELECT run_root, workspace_root, index_json FROM runs WHERE project_id = ? AND id = ?", [project.id, runId]);
|
|
1197
|
+
if (!row)
|
|
1198
|
+
throw new AppError("not_found", "RUN is not registered", 2, { run_id: runId });
|
|
1199
|
+
if (!row.run_root)
|
|
1200
|
+
throw new AppError("runtime_missing", "RUN artifact root is unavailable", 1, { run_id: runId });
|
|
1201
|
+
return { runRoot: row.run_root, workspaceRoot: row.workspace_root, indexJson: row.index_json };
|
|
1202
|
+
}
|
|
1203
|
+
catch (error) {
|
|
1204
|
+
return prepareError(error, { parameter: "run-id", run_id: runId });
|
|
1205
|
+
}
|
|
1206
|
+
};
|
|
1207
|
+
const json = (file, stdin, label, schema) => {
|
|
1208
|
+
if (stdin) {
|
|
1209
|
+
// The caller awaits this helper; the synchronous facade below is only
|
|
1210
|
+
// used for file inputs. Keep stdin handling in the stage branch.
|
|
1211
|
+
throw new AppError("usage", `${label} stdin preparation must be awaited`, 2);
|
|
1212
|
+
}
|
|
1213
|
+
if (!file)
|
|
1214
|
+
throw new AppError("usage", `${label} input is required`, 2);
|
|
1215
|
+
return prepareJsonSchema(file, label, schema);
|
|
1216
|
+
};
|
|
1217
|
+
if (family === "stage" && command === "start") {
|
|
1218
|
+
const stage = requiredOption(parsed, "stage");
|
|
1219
|
+
if (stage !== "specify" && ["bootstrap", "intake-file", "intake-stdin"].some(option => hasOption(parsed, option)))
|
|
1220
|
+
throw new AppError("usage", "--bootstrap and intake inputs are only supported for SPECIFY", 2);
|
|
1221
|
+
requiredOption(parsed, "project-root");
|
|
1222
|
+
if (!hasOption(parsed, "bootstrap"))
|
|
1223
|
+
requiredPosition(parsed, 0, "run-id");
|
|
1224
|
+
if (hasOption(parsed, "session-id"))
|
|
1225
|
+
throw new AppError("usage", "--session-id is managed by the harness lifecycle adapter and is not accepted", 2);
|
|
1226
|
+
const contextFile = optionalOption(parsed, "context-file"), contextSha256 = optionalOption(parsed, "context-sha256");
|
|
1227
|
+
if (Boolean(contextFile) !== Boolean(contextSha256))
|
|
1228
|
+
throw new AppError("usage", "stage start requires --context-file together with --context-sha256", 2);
|
|
1229
|
+
if ([hasOption(parsed, "intake-file"), hasOption(parsed, "intake-stdin"), Boolean(contextFile)].filter(Boolean).length > 1)
|
|
1230
|
+
throw new AppError("usage", "Use exactly one of --intake-file, --intake-stdin or --context-file", 2);
|
|
1231
|
+
if (!hasOption(parsed, "bootstrap")) {
|
|
1232
|
+
const bundle = readFlowRunExecutionBundle(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") });
|
|
1233
|
+
if (bundle)
|
|
1234
|
+
prepared.stageBindingBundle = { stage, bundle, ...(contextSha256 ? { contextSha256 } : {}) };
|
|
1235
|
+
if (bundle && (["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id) || isDiagnosticFlow(bundle.descriptor.descriptor.id))) {
|
|
1236
|
+
bundle.descriptor.stage(stage);
|
|
1237
|
+
if (hasOption(parsed, "dir"))
|
|
1238
|
+
throw new AppError("usage", "Memory stage layout belongs to its frozen descriptor", 2);
|
|
1239
|
+
const loaded = contextFile && contextSha256 ? loadExternalStageContext({ file: contextFile, expectedSha256: contextSha256, stage }) : undefined;
|
|
1240
|
+
if (loaded)
|
|
1241
|
+
prepared.stageStart = { externalContext: loaded };
|
|
1242
|
+
prepared.memoryStage = { operation: "start", bytes: loaded ? Buffer.from(loaded.bytes) : Buffer.alloc(0),
|
|
1243
|
+
flowId: isDiagnosticFlow(bundle.descriptor.descriptor.id) ? bundle.descriptor.descriptor.id : bundle.descriptor.descriptor.id === "mb-upgrade" ? "mb-upgrade" : "mb-init" };
|
|
1244
|
+
return prepared;
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
if (contextFile && contextSha256) {
|
|
1248
|
+
try {
|
|
1249
|
+
prepared.stageStart = { externalContext: loadExternalStageContext({ file: contextFile, expectedSha256: contextSha256, stage }) };
|
|
1250
|
+
}
|
|
1251
|
+
catch (error) {
|
|
1252
|
+
return prepareError(error, { file: contextFile, parameter: "context-file" });
|
|
1253
|
+
}
|
|
1254
|
+
if (!hasOption(parsed, "bootstrap")) {
|
|
1255
|
+
const { runRoot } = run(requiredOption(parsed, "project-root"), requiredPosition(parsed, 0, "run-id"));
|
|
1256
|
+
prepareExternalStageContext({ stageRoot: path.join(runRoot, vnextStageDirectory(stage)), loaded: prepared.stageStart.externalContext });
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
if (stage === "specify") {
|
|
1260
|
+
const intakeFile = optionalOption(parsed, "intake-file");
|
|
1261
|
+
if (intakeFile)
|
|
1262
|
+
prepared.stageStart = { intakeMarkdown: prepareTextFile(intakeFile, "SPECIFY intake").text };
|
|
1263
|
+
else if (hasOption(parsed, "intake-stdin"))
|
|
1264
|
+
prepared.stageStart = { intakeMarkdown: await readStdin(io.stdin) };
|
|
1265
|
+
else if (prepared.stageStart?.externalContext)
|
|
1266
|
+
prepared.stageStart.intakeMarkdown = runnerIntake(prepared.stageStart.externalContext);
|
|
1267
|
+
const projectRoot = requiredOption(parsed, "project-root");
|
|
1268
|
+
const bootstrap = hasOption(parsed, "bootstrap");
|
|
1269
|
+
if (isVnextSpecifyFlow(projectRoot) && (bootstrap || isVnextSpecifyRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") }))) {
|
|
1270
|
+
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") } : {}) });
|
|
1271
|
+
}
|
|
1272
|
+
else if (bootstrap) {
|
|
1273
|
+
if (hasOption(parsed, "intake-stdin") || contextFile)
|
|
1274
|
+
throw new AppError("usage", "Legacy SPECIFY bootstrap supports --intake-file, not stdin or runner context", 2);
|
|
1275
|
+
prepared.legacyStageStart = prepareLegacyStageStart(context, { projectRoot, stage, bootstrap: { subject: requiredOption(parsed, "subject"), ...(intakeFile ? { intakeFile } : {}) }, ...(optionalOption(parsed, "dir") ? { dir: optionalOption(parsed, "dir") } : {}) }, prepared.stageStart?.intakeMarkdown);
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
if (stage === "merge" && isVnextMergeRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1279
|
+
prepared.mergeStart = prepareVnextMergeStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1280
|
+
}
|
|
1281
|
+
if (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1282
|
+
prepared.protocolizeStart = prepareVnextProtocolizeStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1283
|
+
}
|
|
1284
|
+
if (stage === "plan" && isVnextPlanRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1285
|
+
prepared.planStart = prepareVnextPlanStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1286
|
+
}
|
|
1287
|
+
if (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1288
|
+
prepared.planReviewStart = prepareVnextPlanReviewStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1289
|
+
}
|
|
1290
|
+
if (stage === "code" && isVnextPlanRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1291
|
+
prepared.codeStart = prepareVnextCodeStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1292
|
+
}
|
|
1293
|
+
if (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1294
|
+
prepared.codeReviewStart = prepareVnextCodeReviewStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1295
|
+
}
|
|
1296
|
+
if (![prepared.specifyStart, prepared.protocolizeStart, prepared.planStart, prepared.planReviewStart, prepared.codeStart, prepared.codeReviewStart, prepared.mergeStart, prepared.legacyStageStart].some(Boolean)) {
|
|
1297
|
+
if (prepared.stageStart?.externalContext)
|
|
1298
|
+
throw new AppError("usage", "--context-file is currently supported only by vNext flow stages", 2);
|
|
1299
|
+
prepared.legacyStageStart = prepareLegacyStageStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), stage, ...(optionalOption(parsed, "dir") ? { dir: optionalOption(parsed, "dir") } : {}) });
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
if (family === "stage" && (command === "pause" || command === "resume")) {
|
|
1303
|
+
requiredPosition(parsed, 0, "run-id");
|
|
1304
|
+
requiredOption(parsed, "project-root");
|
|
1305
|
+
requiredOption(parsed, "stage");
|
|
1306
|
+
requiredOption(parsed, "work");
|
|
1307
|
+
const fileParameter = command === "pause" ? "question-file" : "answer-file";
|
|
1308
|
+
const stdinParameter = command === "pause" ? "question-stdin" : "answer-stdin";
|
|
1309
|
+
const inputFile = optionalOption(parsed, fileParameter);
|
|
1310
|
+
const fromStdin = hasOption(parsed, stdinParameter);
|
|
1311
|
+
if (Boolean(inputFile) === fromStdin)
|
|
1312
|
+
throw new AppError("usage", `stage ${command} requires exactly one of --${fileParameter} or --${stdinParameter}`, 2);
|
|
1313
|
+
if (fromStdin && io.stdin?.isTTY)
|
|
1314
|
+
throw new AppError("usage", `--${stdinParameter} requires piped or redirected input`, 2);
|
|
1315
|
+
prepared.interactionText = inputFile ? prepareTextFile(inputFile, command === "pause" ? "User question" : "User answer").text : await readStdin(io.stdin);
|
|
1316
|
+
if (!prepared.interactionText.trim())
|
|
1317
|
+
return prepareError(new AppError("validation", `stage ${command} requires non-empty input`, 2), { parameter: inputFile ? fileParameter : stdinParameter });
|
|
1318
|
+
const input = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), stage: requiredOption(parsed, "stage"), workId: requiredOption(parsed, "work") };
|
|
1319
|
+
try {
|
|
1320
|
+
if (command === "pause")
|
|
1321
|
+
prepareStagePause(context, { ...input, question: prepared.interactionText });
|
|
1322
|
+
else
|
|
1323
|
+
prepared.resumeEvidence = prepareStageResume(context, { ...input, answer: prepared.interactionText }).evidence;
|
|
1324
|
+
}
|
|
1325
|
+
catch (error) {
|
|
1326
|
+
return prepareError(error, { parameter: "stage/work" });
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
if (family === "work" && command === "finish") {
|
|
1330
|
+
const retryReceiptId = optionalOption(parsed, "retry-check"), retryReason = prepared.retryReason;
|
|
1331
|
+
if (Boolean(retryReceiptId) !== Boolean(retryReason))
|
|
1332
|
+
throw new AppError("usage", "--retry-check and --reason must be supplied together", 2);
|
|
1333
|
+
const resultFile = optionalOption(parsed, "result-file");
|
|
1334
|
+
const resultStdin = hasOption(parsed, "result-stdin");
|
|
1335
|
+
if (Boolean(resultFile) === resultStdin)
|
|
1336
|
+
throw new AppError("usage", "work finish requires exactly one of --result-file or --result-stdin", 2);
|
|
1337
|
+
const maxBytes = workResultByteLimit(context, requiredPosition(parsed, 0, "work-id"));
|
|
1338
|
+
prepared.workFinish = { result: resultFile ? prepareTextFile(resultFile, "Work result", maxBytes).text : await readStdin(io.stdin, maxBytes) };
|
|
1339
|
+
prepareWorkResult(context, requiredPosition(parsed, 0, "work-id"), prepared.workFinish.result, progress);
|
|
1340
|
+
}
|
|
1341
|
+
if (family === "stage" && command === "fanout" && requiredPosition(parsed, 0, "fanout action") === "reconcile") {
|
|
1342
|
+
prepared.observations = prepareNativeObservations(prepareJsonFile(requiredOption(parsed, "observations-file"), "native observations").value);
|
|
1343
|
+
}
|
|
1344
|
+
if (family === "stage" && command === "native" && requiredPosition(parsed, 0, "native action") === "observe") {
|
|
1345
|
+
prepared.observations = prepareNativeObservations(prepareJsonFile(requiredOption(parsed, "observations-file"), "native observations").value);
|
|
1346
|
+
}
|
|
1347
|
+
if (family === "memory" && (["compose", "corrections"].includes(command ?? "") && parsed.positional[0] === "register"
|
|
1348
|
+
|| command === "repair" && parsed.positional[0] === "publication-reserve")) {
|
|
1349
|
+
try {
|
|
1350
|
+
const bytes = prepareFileBytes(requiredOption(parsed, "result-file"), `Memory ${command} result`, 1024 * 1024).bytes;
|
|
1351
|
+
if (!bytes.length || bytes.length > 1024 * 1024)
|
|
1352
|
+
throw new AppError("usage", "Memory operation result exceeds its byte bound", 2, { phase: "prepare", effect: "no_effect", recoverable: true });
|
|
1353
|
+
prepared.memoryOperationBytes = bytes;
|
|
1354
|
+
}
|
|
1355
|
+
catch (error) {
|
|
1356
|
+
if (command === "compose" && isAppError(error) && error.details.phase === "prepare"
|
|
1357
|
+
&& error.details.effect === "no_effect" && error.details.recoverable === true)
|
|
1358
|
+
Object.assign(error.details, compositionProposalRetry(context, parsed));
|
|
1359
|
+
throw error;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
if (family !== "stage" || command !== "finish")
|
|
1363
|
+
return prepared;
|
|
1364
|
+
const projectRoot = requiredOption(parsed, "project-root");
|
|
1365
|
+
const runId = requiredPosition(parsed, 0, "run-id");
|
|
1366
|
+
const stage = requiredOption(parsed, "stage");
|
|
1367
|
+
if (hasOption(parsed, "status"))
|
|
1368
|
+
throw new AppError("usage", "Stage status belongs in the semantic input file", 2);
|
|
1369
|
+
if (["result-file", "semantic-file", "data", "result-stdin"].filter(key => hasOption(parsed, key)).length > 1)
|
|
1370
|
+
throw new AppError("usage", "Use exactly one result input: --result-file, --semantic-file, --data or --result-stdin", 2);
|
|
1371
|
+
if (Boolean(optionalOption(parsed, "retry-check")) !== Boolean(prepared.retryReason))
|
|
1372
|
+
throw new AppError("usage", "--retry-check requires recovery evidence", 2);
|
|
1373
|
+
const resultStdin = hasOption(parsed, "result-stdin");
|
|
1374
|
+
const semanticFile = optionalOption(parsed, "semantic-file") ?? optionalOption(parsed, "data");
|
|
1375
|
+
const resultFile = optionalOption(parsed, "result-file") ?? semanticFile;
|
|
1376
|
+
const runtime = run(projectRoot, runId);
|
|
1377
|
+
const retainedFlowId = JSON.parse(runtime.indexJson).execution_bundle?.flow_id;
|
|
1378
|
+
if (["mb-init", "mb-upgrade"].includes(retainedFlowId) || isDiagnosticFlow(retainedFlowId)) {
|
|
1379
|
+
const bundle = readFlowRunExecutionBundle(context, { projectRoot, runId });
|
|
1380
|
+
if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id) && !isDiagnosticFlow(bundle.descriptor.descriptor.id))
|
|
1381
|
+
throw new AppError("cli_input_unprepared", "Registered RUN bundle is unavailable", 1);
|
|
1382
|
+
bundle.descriptor.stage(stage);
|
|
1383
|
+
if (["dir", "outcome", "decision-file", "verification-file", "retry-check", "reason", "reason-file", "request", "work"].some(option => hasOption(parsed, option)))
|
|
1384
|
+
throw new AppError("usage", "Memory stage finish requires its registered JSON result, not legacy result options", 2);
|
|
1385
|
+
if (Boolean(resultFile) === resultStdin)
|
|
1386
|
+
throw new AppError("usage", "Memory stage finish requires exactly one result input", 2);
|
|
1387
|
+
const bytes = resultStdin ? Buffer.from(await readStdin(io.stdin, 1024 * 1024)) : prepareFileBytes(resultFile, "Memory stage result", 1024 * 1024).bytes;
|
|
1388
|
+
if (!bytes.length || bytes.length > 1024 * 1024)
|
|
1389
|
+
throw new AppError("usage", "Memory stage result exceeds its byte bound", 2);
|
|
1390
|
+
prepared.memoryStage = { operation: "finish", bytes, flowId: isDiagnosticFlow(bundle.descriptor.descriptor.id) ? bundle.descriptor.descriptor.id : bundle.descriptor.descriptor.id === "mb-upgrade" ? "mb-upgrade" : "mb-init" };
|
|
1391
|
+
return prepared;
|
|
1392
|
+
}
|
|
1393
|
+
const terminalReplay = (name) => {
|
|
1394
|
+
let index;
|
|
1395
|
+
try {
|
|
1396
|
+
index = JSON.parse(runtime.indexJson);
|
|
1397
|
+
}
|
|
1398
|
+
catch (error) {
|
|
1399
|
+
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) });
|
|
1400
|
+
}
|
|
1401
|
+
return index.stage_runs?.some(entry => entry.stage === name && entry.status === "done") === true
|
|
1402
|
+
&& fs.existsSync(path.join(runtime.runRoot, vnextStageDirectory(name), "stage-report.json"));
|
|
1403
|
+
};
|
|
1404
|
+
const acceptStageInputs = (allowed) => {
|
|
1405
|
+
const options = allowed.split(" ");
|
|
1406
|
+
for (const name of ["dir", "semantic-file", "data", "result-file", "result-stdin", "outcome", "decision-file", "verification-file", "retry-check", "reason", "reason-file", "request", "work"]) {
|
|
1407
|
+
if (hasOption(parsed, name) && !options.includes(name === "reason-file" ? "reason" : name))
|
|
1408
|
+
throw new AppError("usage", `stage finish ${stage} does not accept --${name} for this flow`, 2, { parameter: name });
|
|
1409
|
+
}
|
|
1410
|
+
};
|
|
1411
|
+
const fromStdin = async (label, schema) => {
|
|
1412
|
+
const value = prepareJsonText(await readStdin(io.stdin), { file: "<stdin>", label });
|
|
1413
|
+
try {
|
|
1414
|
+
validateSchema({ ...schema, file: value.file, data: value.value });
|
|
1415
|
+
}
|
|
1416
|
+
catch (error) {
|
|
1417
|
+
return prepareError(error, { file: value.file, label });
|
|
1418
|
+
}
|
|
1419
|
+
return value;
|
|
1420
|
+
};
|
|
1421
|
+
if (stage === "specify" && isVnextSpecifyRun(context, { projectRoot, runId })) {
|
|
1422
|
+
acceptStageInputs("result-file semantic-file data result-stdin outcome");
|
|
1423
|
+
const outcome = optionalOption(parsed, "outcome");
|
|
1424
|
+
if (!outcome || !["specified", "failed", "cancelled"].includes(outcome))
|
|
1425
|
+
throw new AppError("usage", "vNext SPECIFY requires --outcome specified|failed|cancelled; use stage pause for a user question", 2);
|
|
1426
|
+
if (Boolean(resultFile) === resultStdin)
|
|
1427
|
+
throw new AppError("usage", "vNext SPECIFY finish requires exactly one result input", 2);
|
|
1428
|
+
const schema = { schemaName: "vnext-specify", projectRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot };
|
|
1429
|
+
const input = resultStdin ? await fromStdin("SPECIFY result", schema) : json(resultFile, false, "SPECIFY result", schema);
|
|
1430
|
+
prepared.stageFinish = { value: input.value, text: input.text };
|
|
1431
|
+
}
|
|
1432
|
+
else if (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId })) {
|
|
1433
|
+
acceptStageInputs("result-file semantic-file data");
|
|
1434
|
+
if (!resultFile || resultStdin || hasOption(parsed, "outcome"))
|
|
1435
|
+
throw new AppError("usage", "vNext PROTOCOLIZE requires exactly --result-file", 2);
|
|
1436
|
+
let absolute;
|
|
1437
|
+
try {
|
|
1438
|
+
absolute = prepareProtocolizeResultPath(runtime.runRoot, resultFile);
|
|
1439
|
+
}
|
|
1440
|
+
catch (error) {
|
|
1441
|
+
return prepareError(error, { file: resultFile });
|
|
1442
|
+
}
|
|
1443
|
+
const input = json(absolute, false, "PROTOCOLIZE result", { schemaName: "vnext-protocolize-result", projectRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot });
|
|
1444
|
+
prepared.stageFinish = { value: input.value, text: input.text };
|
|
1445
|
+
}
|
|
1446
|
+
else if (stage === "plan" && isVnextPlanRun(context, { projectRoot, runId })) {
|
|
1447
|
+
acceptStageInputs("");
|
|
1448
|
+
if (hasOption(parsed, "code-work-batch-file"))
|
|
1449
|
+
throw new AppError("usage", "vNext PLAN derives its CODE batch from accepted semantic plans; do not supply a batch file", 2);
|
|
1450
|
+
try {
|
|
1451
|
+
prepared.planFinish = prepareVnextPlanFinish(context, { projectRoot, runId });
|
|
1452
|
+
}
|
|
1453
|
+
catch (error) {
|
|
1454
|
+
return prepareError(error, { parameter: "PLAN artifacts" });
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
else if (stage === "merge" && isVnextMergeRun(context, { projectRoot, runId })) {
|
|
1458
|
+
acceptStageInputs("request work retry-check reason");
|
|
1459
|
+
if (resultStdin || resultFile || hasOption(parsed, "outcome"))
|
|
1460
|
+
throw new AppError("usage", "vNext MERGE uses the Work result path returned by stage start", 2);
|
|
1461
|
+
requiredOption(parsed, "request");
|
|
1462
|
+
requiredOption(parsed, "work");
|
|
1463
|
+
try {
|
|
1464
|
+
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 } : {}) });
|
|
1465
|
+
}
|
|
1466
|
+
catch (error) {
|
|
1467
|
+
return prepareError(error, { parameter: "MERGE finish" });
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
else if (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId })) {
|
|
1471
|
+
acceptStageInputs("decision-file");
|
|
1472
|
+
if (resultStdin || hasOption(parsed, "outcome"))
|
|
1473
|
+
throw new AppError("usage", "vNext PLAN-REVIEW uses --decision-file", 2);
|
|
1474
|
+
const decision = optionalOption(parsed, "decision-file") ?? path.join(runtime.runRoot, "04-plan-review", "decision.json");
|
|
1475
|
+
const input = json(decision, false, "PLAN-REVIEW decision", { schemaName: "plan-review-decision", projectRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot });
|
|
1476
|
+
prepared.stageFinish = { value: input.value, text: input.text };
|
|
1477
|
+
}
|
|
1478
|
+
else if (stage === "code" && isVnextPlanRun(context, { projectRoot, runId })) {
|
|
1479
|
+
acceptStageInputs("verification-file retry-check reason");
|
|
1480
|
+
if (semanticFile || resultFile || resultStdin || hasOption(parsed, "outcome"))
|
|
1481
|
+
throw new AppError("usage", "vNext CODE finish accepts only --verification-file", 2);
|
|
1482
|
+
const verification = optionalOption(parsed, "verification-file");
|
|
1483
|
+
if (!verification)
|
|
1484
|
+
throw new AppError("usage", "vNext CODE finish requires --verification-file", 2);
|
|
1485
|
+
if (!terminalReplay(stage)) {
|
|
1486
|
+
const input = json(verification, false, "CODE verification", { schemaName: "code-verification", projectRoot: runtime.workspaceRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot });
|
|
1487
|
+
prepared.stageFinish = { value: input.value, text: input.text };
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
else if (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId })) {
|
|
1491
|
+
acceptStageInputs("decision-file retry-check reason");
|
|
1492
|
+
if (resultStdin || semanticFile || resultFile || hasOption(parsed, "outcome"))
|
|
1493
|
+
throw new AppError("usage", "vNext CODE-REVIEW uses --decision-file", 2);
|
|
1494
|
+
const decision = optionalOption(parsed, "decision-file") ?? path.join(runtime.runRoot, "06-code-review", "decision.json");
|
|
1495
|
+
if (!terminalReplay(stage)) {
|
|
1496
|
+
const input = prepareCodeReviewDecision(runtime.runRoot, optionalOption(parsed, "decision-file"));
|
|
1497
|
+
try {
|
|
1498
|
+
validateSchema({ schemaName: "code-review-decision", file: decision, data: input.value, projectRoot: runtime.workspaceRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot });
|
|
1499
|
+
}
|
|
1500
|
+
catch (error) {
|
|
1501
|
+
return prepareError(error, { file: decision, label: "CODE-REVIEW decision" });
|
|
1502
|
+
}
|
|
1503
|
+
prepared.stageFinish = input;
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
else {
|
|
1507
|
+
acceptStageInputs("dir semantic-file data");
|
|
1508
|
+
if (hasOption(parsed, "outcome") || hasOption(parsed, "result-file") || resultStdin)
|
|
1509
|
+
throw new AppError("usage", "--outcome, --result-file and --result-stdin are only available for vNext SPECIFY", 2);
|
|
1510
|
+
prepared.legacyStageFinish = prepareStageFinish(context, { projectRoot, runId, stage, ...(optionalOption(parsed, "dir") ? { dir: optionalOption(parsed, "dir") } : {}), ...(semanticFile ? { semanticFile } : {}) });
|
|
1511
|
+
}
|
|
1512
|
+
return prepared;
|
|
1513
|
+
}
|
|
1514
|
+
async function dispatch(args, context, io, scopeProjectRoot = null, classification = classifyCliOperation(args, context.env), output = parseOutputOptions(args), prepared = {}) {
|
|
1515
|
+
if (!prepared.route)
|
|
1516
|
+
throw new AppError("command_not_prepared", "Command dispatch requires prepared arguments", 1);
|
|
1517
|
+
const { family, command, parsed } = prepared.route;
|
|
1518
|
+
if (family === "zcode" && command === "invocation" && parsed.positional[0] === "prepare") {
|
|
1519
|
+
const payload = prepared.lifecycleIssuance;
|
|
1520
|
+
if (!payload)
|
|
1521
|
+
throw new AppError("command_not_prepared", "Invocation issuance requires prepared input", 1);
|
|
1522
|
+
assertLifecycleInvocationCurrent(context, payload.scope, payload.command);
|
|
1523
|
+
return { ok: true, ...issueLifecycleInvocation(context, payload.command, payload.scope) };
|
|
1524
|
+
}
|
|
1525
|
+
if (family === "storage" && command === "migrate-writer") {
|
|
1526
|
+
if (parsed.positional.length || [...parsed.options.keys()].some(key => !["database", "backup", "offline"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
1527
|
+
throw new AppError("usage", "Usage: dd-flow storage migrate-writer --database ABSOLUTE_DB --backup NEW_ABSOLUTE_FILE --offline", 2);
|
|
1528
|
+
if (parsed.options.get("offline")?.[0] !== flagValue)
|
|
1529
|
+
throw new AppError("usage", "Writer migration requires --offline after draining all local and external runtime owners", 2);
|
|
1530
|
+
return { ok: true, ...migrateStoreWriter(requiredOption(parsed, "database"), requiredOption(parsed, "backup")) };
|
|
1531
|
+
}
|
|
507
1532
|
if (family === "engine") {
|
|
508
|
-
return dispatchEngine(context, command, parsed, classification);
|
|
1533
|
+
return dispatchEngine(context, command, parsed, classification, prepared);
|
|
509
1534
|
}
|
|
510
1535
|
if (family === "status") {
|
|
511
|
-
const rootParsed = parseArgs([command ?? "", ...rest]);
|
|
512
1536
|
return getRuntimeStatus(context, {
|
|
513
|
-
projectRoot: optionalOption(
|
|
514
|
-
checkRegistry: hasOption(
|
|
1537
|
+
projectRoot: optionalOption(parsed, "project-root") ?? optionalOption(parsed, "root"),
|
|
1538
|
+
checkRegistry: hasOption(parsed, "check-registry")
|
|
515
1539
|
});
|
|
516
1540
|
}
|
|
517
1541
|
if (family === "version") {
|
|
518
1542
|
return getCliVersionReport();
|
|
519
1543
|
}
|
|
520
1544
|
if (family === "runtime") {
|
|
521
|
-
return await dispatchRuntime(context, command, parsed, commandProgress(io, output));
|
|
1545
|
+
return await dispatchRuntime(context, command, parsed, commandProgress(io, output), prepared);
|
|
522
1546
|
}
|
|
523
1547
|
if (family === "canon") {
|
|
524
|
-
return dispatchCanon(context, command, parsed);
|
|
1548
|
+
return dispatchCanon(context, command, parsed, prepared);
|
|
525
1549
|
}
|
|
526
1550
|
if (family === "project" && command === "register") {
|
|
527
1551
|
return registerProject(context, { root: requiredOption(parsed, "root") });
|
|
@@ -598,16 +1622,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
598
1622
|
return readyForMerge(context, { protocolId, projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "protocol ready-for-merge") });
|
|
599
1623
|
}
|
|
600
1624
|
if (family === "protocol" && command === "transition") {
|
|
601
|
-
|
|
602
|
-
const reason = optionalOption(parsed, "reason");
|
|
603
|
-
return transitionProtocol(context, {
|
|
604
|
-
protocolId,
|
|
605
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "protocol transition"),
|
|
606
|
-
to: requiredOption(parsed, "to"),
|
|
607
|
-
jsonFile: requiredOptionAlias(parsed, ["payload-file", "json-file"]),
|
|
608
|
-
force: hasOption(parsed, "force"),
|
|
609
|
-
...(reason ? { reason } : {})
|
|
610
|
-
});
|
|
1625
|
+
return transitionProtocol(context, protocolTransitionInput(parsed, scopeProjectRoot), prepared.protocolTransition);
|
|
611
1626
|
}
|
|
612
1627
|
if (family === "protocol" && command === "sync-from-run") {
|
|
613
1628
|
const protocolId = requiredPosition(parsed, 0, "protocol-id");
|
|
@@ -632,17 +1647,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
632
1647
|
});
|
|
633
1648
|
}
|
|
634
1649
|
if (family === "transition") {
|
|
635
|
-
|
|
636
|
-
const protocolId = requiredPosition(transitionParsed, 0, "protocol-id");
|
|
637
|
-
const reason = optionalOption(transitionParsed, "reason");
|
|
638
|
-
return transitionProtocol(context, {
|
|
639
|
-
protocolId,
|
|
640
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "transition"),
|
|
641
|
-
to: requiredOption(transitionParsed, "to"),
|
|
642
|
-
jsonFile: requiredOptionAlias(transitionParsed, ["payload-file", "json-file"]),
|
|
643
|
-
force: hasOption(transitionParsed, "force"),
|
|
644
|
-
...(reason ? { reason } : {})
|
|
645
|
-
});
|
|
1650
|
+
return transitionProtocol(context, protocolTransitionInput(parsed, scopeProjectRoot), prepared.protocolTransition);
|
|
646
1651
|
}
|
|
647
1652
|
if (family === "plan" && command === "status") {
|
|
648
1653
|
const protocolId = requiredPosition(parsed, 0, "protocol-id");
|
|
@@ -656,17 +1661,25 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
656
1661
|
}
|
|
657
1662
|
if (family === "stage" && command === "fanout") {
|
|
658
1663
|
const action = requiredPosition(parsed, 0, "fanout action");
|
|
659
|
-
const runId = requiredPosition(parsed, 1, "run-id");
|
|
660
1664
|
const stage = requiredOption(parsed, "stage");
|
|
661
1665
|
const projectRoot = requiredScopeProjectRoot(scopeProjectRoot, "stage fanout");
|
|
1666
|
+
const project = requireProjectByRoot(context, resolveProjectRoot(projectRoot));
|
|
1667
|
+
const runId = resolveRun(context, project.id, requiredPosition(parsed, 1, "run-id")).id;
|
|
662
1668
|
if (action === "status")
|
|
663
1669
|
return getVnextFanoutStatus(context, { projectRoot, runId, stage });
|
|
1670
|
+
if (action === "reconcile")
|
|
1671
|
+
return reconcileVnextFanout(context, { projectRoot, runId, stage, observations: prepared.observations ?? prepareJsonFile(requiredOption(parsed, "observations-file"), "native observations").value });
|
|
664
1672
|
if (action === "dispatch") {
|
|
665
1673
|
if (stage === "plan-review")
|
|
666
1674
|
return dispatchVnextPlanReview(context, { projectRoot, runId });
|
|
667
1675
|
return getVnextFanoutStatus(context, { projectRoot, runId, stage });
|
|
668
1676
|
}
|
|
669
|
-
throw new AppError("usage", "Use: dd-flow stage fanout <status|dispatch> <RUN> --stage <stage> --project-root <path>", 2);
|
|
1677
|
+
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);
|
|
1678
|
+
}
|
|
1679
|
+
if (family === "stage" && command === "native") {
|
|
1680
|
+
if (requiredPosition(parsed, 0, "native action") !== "observe")
|
|
1681
|
+
throw new AppError("usage", "Use: dd-flow stage native observe <RUN> --stage <stage> --observations-file <native-observations.json> --project-root <path>", 2);
|
|
1682
|
+
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 });
|
|
670
1683
|
}
|
|
671
1684
|
if (family === "run" && command === "capacity") {
|
|
672
1685
|
if (requiredPosition(parsed, 0, "capacity action") !== "record")
|
|
@@ -678,16 +1691,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
678
1691
|
return dispatchPlanItem(context, parsed, requiredScopeProjectRoot(scopeProjectRoot, "plan item"));
|
|
679
1692
|
}
|
|
680
1693
|
if (family === "prompt" && command === "render") {
|
|
681
|
-
|
|
682
|
-
const taskFile = optionalOption(parsed, "task-file");
|
|
683
|
-
return renderWorkerPrompt(context, {
|
|
684
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
685
|
-
runId: requiredOption(parsed, "run"),
|
|
686
|
-
stage: requiredOption(parsed, "stage"),
|
|
687
|
-
profile: requiredOption(parsed, "profile"),
|
|
688
|
-
...(taskFile ? { taskFile } : { planItemId: requiredOption(parsed, "plan-item") }),
|
|
689
|
-
...(workspaceRoot ? { workspaceRoot } : {})
|
|
690
|
-
});
|
|
1694
|
+
return renderWorkerPrompt(context, prepared.workerPrompt?.input ?? workerPromptInput(parsed), prepared.workerPrompt?.value);
|
|
691
1695
|
}
|
|
692
1696
|
if (family === "work" && command === "status") {
|
|
693
1697
|
return getVnextWorkStatus(context, {
|
|
@@ -696,7 +1700,10 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
696
1700
|
});
|
|
697
1701
|
}
|
|
698
1702
|
if (family === "work" && command === "add-batch") {
|
|
699
|
-
|
|
1703
|
+
assertGenericWorkBatchAllowed(context, requiredOption(parsed, "parent"));
|
|
1704
|
+
return addWorkBatch(context, { parentWorkId: requiredOption(parsed, "parent"), file: requiredOption(parsed, "file"), prepared: prepared.workBatch,
|
|
1705
|
+
...(optionalOption(parsed, "stage") ? { stage: optionalOption(parsed, "stage") } : {}),
|
|
1706
|
+
...(optionalOption(parsed, "request-key") ? { requestKey: optionalOption(parsed, "request-key") } : {}) });
|
|
700
1707
|
}
|
|
701
1708
|
if (family === "work" && command === "ls") {
|
|
702
1709
|
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")) } : {}) });
|
|
@@ -704,48 +1711,9 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
704
1711
|
if (family === "work" && command === "show")
|
|
705
1712
|
return showWork(context, requiredPosition(parsed, 0, "work-id"));
|
|
706
1713
|
if (family === "work" && command === "repair" && requiredPosition(parsed, 0, "action") === "add") {
|
|
707
|
-
if (!
|
|
708
|
-
throw new AppError("
|
|
709
|
-
|
|
710
|
-
const findings = parsed.options.get("from-finding") ?? [];
|
|
711
|
-
const unresolved = parsed.options.get("from-unresolved") ?? [];
|
|
712
|
-
if (!origins.length && !findings.length)
|
|
713
|
-
throw new AppError("usage", "work repair add requires --from-check with --origin-work, --from-finding, or --from-unresolved with --origin-work", 2);
|
|
714
|
-
if (findings.length)
|
|
715
|
-
return addVnextCodeReviewRepair(context, {
|
|
716
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "work repair add"),
|
|
717
|
-
runId: requiredOption(parsed, "run"),
|
|
718
|
-
findingIds: findings,
|
|
719
|
-
checkRefsByFinding: Object.fromEntries(findings.map((finding) => [finding, parsed.options.get("check-ref") ?? []])),
|
|
720
|
-
objective: await readStdin(io.stdin)
|
|
721
|
-
});
|
|
722
|
-
if (unresolved.length) {
|
|
723
|
-
if (!origins.length)
|
|
724
|
-
throw new AppError("usage", "semantic repair requires at least one --origin-work", 2);
|
|
725
|
-
const verificationPath = requiredOption(parsed, "verification-file");
|
|
726
|
-
return addVnextCodeRepair(context, {
|
|
727
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "work repair add"),
|
|
728
|
-
runId: requiredOption(parsed, "run"),
|
|
729
|
-
originWorkIds: origins,
|
|
730
|
-
semanticUnresolved: unresolved,
|
|
731
|
-
verificationPath,
|
|
732
|
-
objective: await readStdin(io.stdin)
|
|
733
|
-
});
|
|
734
|
-
}
|
|
735
|
-
if (!hasOption(parsed, "from-check"))
|
|
736
|
-
throw new AppError("usage", "check repair requires --from-check", 2);
|
|
737
|
-
if (!origins.length)
|
|
738
|
-
throw new AppError("usage", "check repair requires at least one --origin-work", 2);
|
|
739
|
-
const receipts = parsed.options.get("from-check") ?? [];
|
|
740
|
-
if (receipts.length !== 1)
|
|
741
|
-
throw new AppError("usage", "check repair accepts exactly one --from-check; create one causal repair Work per failed receipt", 2);
|
|
742
|
-
return addVnextCodeRepair(context, {
|
|
743
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "work repair add"),
|
|
744
|
-
runId: requiredOption(parsed, "run"),
|
|
745
|
-
checkReceiptId: receipts[0],
|
|
746
|
-
originWorkIds: origins,
|
|
747
|
-
objective: await readStdin(io.stdin)
|
|
748
|
-
});
|
|
1714
|
+
if (!prepared.workRepair)
|
|
1715
|
+
throw new AppError("cli_input_unprepared", "Repair input was not prepared", 1);
|
|
1716
|
+
return addVnextCodeRepair(context, prepared.workRepair.input, prepared.workRepair.value);
|
|
749
1717
|
}
|
|
750
1718
|
if (family === "work" && command === "deps") {
|
|
751
1719
|
const on = parsed.options.get("on") ?? [];
|
|
@@ -753,26 +1721,42 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
753
1721
|
}
|
|
754
1722
|
if (family === "work" && command === "delete")
|
|
755
1723
|
return deleteWork(context, requiredPosition(parsed, 0, "work-id"));
|
|
1724
|
+
if (family === "work" && command === "launch")
|
|
1725
|
+
return await launchExternalWork(context, { workId: requiredPosition(parsed, 0, "work-id"), ...(optionalOption(parsed, "stage") ? { stage: optionalOption(parsed, "stage") } : {}), progress: commandProgress(io, output) });
|
|
756
1726
|
if (family === "work" && command === "start") {
|
|
757
1727
|
const hookEventId = optionalOption(parsed, "hook-event-id");
|
|
1728
|
+
const recoveryId = optionalOption(parsed, "recovery-id");
|
|
758
1729
|
const projectRoot = requiredScopeProjectRoot(scopeProjectRoot, "work start");
|
|
759
|
-
return startWork(context, requiredPosition(parsed, 0, "work-id"), { ...(hookEventId ? { hookEventId } : {}), projectRoot });
|
|
1730
|
+
return startWork(context, requiredPosition(parsed, 0, "work-id"), { ...(hookEventId ? { hookEventId } : {}), ...(recoveryId ? { recoveryId } : {}), projectRoot, progress: commandProgress(io, output) });
|
|
760
1731
|
}
|
|
761
1732
|
if (family === "work" && command === "finish") {
|
|
762
1733
|
const resultFile = optionalOption(parsed, "result-file");
|
|
763
1734
|
const resultStdin = hasOption(parsed, "result-stdin");
|
|
764
1735
|
if (Boolean(resultFile) === resultStdin)
|
|
765
1736
|
throw new AppError("usage", "work finish requires exactly one of --result-file or --result-stdin", 2);
|
|
766
|
-
const
|
|
767
|
-
|
|
1737
|
+
const workId = requiredPosition(parsed, 0, "work-id");
|
|
1738
|
+
const projectRoot = requiredScopeProjectRoot(scopeProjectRoot, "work finish");
|
|
1739
|
+
assertWorkLifecycleOwner(context, { workId, projectRoot, operation: "work_finish", ...(optionalOption(parsed, "hook-event-id") ? { hookEventId: optionalOption(parsed, "hook-event-id") } : {}) });
|
|
1740
|
+
const retryReceiptId = optionalOption(parsed, "retry-check"), retryReason = prepared.retryReason;
|
|
1741
|
+
if (Boolean(retryReceiptId) !== Boolean(retryReason))
|
|
1742
|
+
throw new AppError("usage", "--retry-check and --reason must be supplied together", 2);
|
|
1743
|
+
const maxBytes = workResultByteLimit(context, workId);
|
|
1744
|
+
const result = prepared.workFinish?.result ?? (resultFile ? prepareTextFile(resultFile, "Work result", maxBytes).text : await readStdin(io.stdin, maxBytes));
|
|
1745
|
+
return await finishWork(context, workId, result, commandProgress(io, output), retryReceiptId ? { receiptId: retryReceiptId, reason: retryReason } : undefined);
|
|
1746
|
+
}
|
|
1747
|
+
if (family === "work" && command === "fail") {
|
|
1748
|
+
const workId = requiredPosition(parsed, 0, "work-id");
|
|
1749
|
+
const projectRoot = requiredScopeProjectRoot(scopeProjectRoot, "work fail");
|
|
1750
|
+
assertWorkLifecycleOwner(context, { workId, projectRoot, operation: "work_fail", ...(optionalOption(parsed, "hook-event-id") ? { hookEventId: optionalOption(parsed, "hook-event-id") } : {}) });
|
|
1751
|
+
return failWork(context, workId, requiredOption(parsed, "reason"));
|
|
768
1752
|
}
|
|
769
|
-
if (family === "work" && command === "fail")
|
|
770
|
-
return failWork(context, requiredPosition(parsed, 0, "work-id"), requiredOption(parsed, "reason"));
|
|
771
1753
|
if (family === "work" && command === "cancel")
|
|
772
1754
|
return cancelWork(context, requiredPosition(parsed, 0, "work-id"), requiredOption(parsed, "reason"));
|
|
773
1755
|
if (family === "work" && command === "retry")
|
|
774
1756
|
return retryWork(context, requiredPosition(parsed, 0, "work-id"), requiredOption(parsed, "reason"));
|
|
775
1757
|
if (family === "stage" && command === "start") {
|
|
1758
|
+
if (prepared.stageBindingBundle)
|
|
1759
|
+
context = { ...context, stageBindingBundle: prepared.stageBindingBundle };
|
|
776
1760
|
const dir = optionalOption(parsed, "dir");
|
|
777
1761
|
const bootstrap = hasOption(parsed, "bootstrap");
|
|
778
1762
|
const subject = optionalOption(parsed, "subject");
|
|
@@ -789,12 +1773,40 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
789
1773
|
const requireSessionBinding = hasOption(parsed, "require-session-binding");
|
|
790
1774
|
const projectRoot = requiredOption(parsed, "project-root");
|
|
791
1775
|
const stage = requiredOption(parsed, "stage");
|
|
792
|
-
const externalContext =
|
|
1776
|
+
const externalContext = prepared.stageStart?.externalContext;
|
|
1777
|
+
if (prepared.memoryStage?.operation === "start") {
|
|
1778
|
+
const runId = requiredPosition(parsed, 0, "run-id");
|
|
1779
|
+
const event = hookEventId ?? findRecentMatchingHookEvent(context, {
|
|
1780
|
+
projectId: requireProjectByRoot(context, resolveProjectRoot(projectRoot)).id,
|
|
1781
|
+
matchKey: entityReferenceVariants(runId).map(reference => stageStartMatchKey(reference, stage, projectRoot, contextSha256)),
|
|
1782
|
+
errorCode: "trusted_session_binding_required", operation: "Memory stage start"
|
|
1783
|
+
}).eventKey;
|
|
1784
|
+
const work = (isDiagnosticFlow(prepared.memoryStage.flowId) ? startDiagnosticStage : prepared.memoryStage.flowId === "mb-upgrade" ? startMemoryUpgradeStage : startMemoryStage)(context, { projectRoot, runId, stage, bytes: prepared.memoryStage.bytes });
|
|
1785
|
+
try {
|
|
1786
|
+
const binding = bindStageCoordinatorWork(context, { workId: work.work_id, hookEventId: event, stage,
|
|
1787
|
+
promptPath: work.prompt_path, resultPath: work.result_path, ...(contextSha256 ? { contextSha256 } : {}) });
|
|
1788
|
+
// The frozen semantic prompt is also restored by auxiliary owners in
|
|
1789
|
+
// other CLI contexts. Keep native invocation authority in this current
|
|
1790
|
+
// delivery, not in those immutable instruction/custody bytes.
|
|
1791
|
+
const finish = managedLifecycleCommand(context, runtimeCommand(context, ["stage", "finish", runId,
|
|
1792
|
+
"--stage", stage, "--project-root", projectRoot, "--result-file", work.result_path, "--json"]));
|
|
1793
|
+
const pause = stagePauseCommand(context, { projectRoot, runId, stage, workId: work.work_id });
|
|
1794
|
+
const orchestration = memoryStageFanoutDescriptor(context, { projectRoot, runId, stage }) ?? diagnosticStageFanoutDescriptor(context, { projectRoot, runId, stage });
|
|
1795
|
+
const delivery = { ...work, binding, ...(orchestration ? { orchestration } : {}), next: { finish_command: finish, pause_command: pause },
|
|
1796
|
+
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` };
|
|
1797
|
+
bindLifecyclePublishedPacket(context, delivery.worker_prompt_markdown);
|
|
1798
|
+
return controlLifecycleReply(context, { projectRoot, runId }, delivery);
|
|
1799
|
+
}
|
|
1800
|
+
catch (error) {
|
|
1801
|
+
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",
|
|
1802
|
+
cause: error instanceof AppError ? error.code : "coordinator_binding_failure" });
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
793
1805
|
const claimVnextStageStart = (runId) => {
|
|
794
1806
|
registerProject(context, { root: projectRoot });
|
|
795
1807
|
return findRecentMatchingHookEvent(context, {
|
|
796
1808
|
projectId: requireProjectByRoot(context, resolveProjectRoot(projectRoot)).id,
|
|
797
|
-
matchKey: stageStartMatchKey(
|
|
1809
|
+
matchKey: entityReferenceVariants(runId).map((reference) => stageStartMatchKey(reference, stage, projectRoot, contextSha256)),
|
|
798
1810
|
errorCode: "trusted_session_binding_required",
|
|
799
1811
|
operation: `vNext ${stage.toUpperCase()} start`
|
|
800
1812
|
}).eventKey;
|
|
@@ -818,39 +1830,39 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
818
1830
|
})
|
|
819
1831
|
: claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
820
1832
|
}
|
|
821
|
-
const result =
|
|
1833
|
+
const result = sdlcStageHandlers.specify.entry(context, {
|
|
822
1834
|
projectRoot,
|
|
823
|
-
...(externalContext ? { intakeMarkdown: runnerIntake(externalContext) } : intakeFile ? { intakeFile } : { intakeMarkdown: await readStdin(io.stdin) }),
|
|
1835
|
+
...(prepared.stageStart?.intakeMarkdown !== undefined ? { intakeMarkdown: prepared.stageStart.intakeMarkdown } : externalContext ? { intakeMarkdown: runnerIntake(externalContext) } : intakeFile ? { intakeFile } : { intakeMarkdown: await readStdin(io.stdin) }),
|
|
824
1836
|
hookEventId,
|
|
825
1837
|
...(subject ? { slug: subject } : {}),
|
|
826
1838
|
...(externalContext ? { externalContext } : {}),
|
|
827
1839
|
...(!bootstrap ? { runId: requiredPosition(parsed, 0, "run-id") } : {})
|
|
828
|
-
});
|
|
1840
|
+
}, prepared.specifyStart);
|
|
829
1841
|
return attachExternalStageContext(result, externalContext);
|
|
830
1842
|
}
|
|
831
1843
|
if (!bootstrap && stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
832
1844
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
833
|
-
return attachExternalStageContext(
|
|
1845
|
+
return attachExternalStageContext(await sdlcStageHandlers.protocolize.entry(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.protocolizeStart), externalContext);
|
|
834
1846
|
}
|
|
835
1847
|
if (!bootstrap && stage === "plan" && isVnextPlanRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
836
1848
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
837
|
-
return attachExternalStageContext(
|
|
1849
|
+
return attachExternalStageContext(sdlcStageHandlers.plan.entry(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.planStart), externalContext);
|
|
838
1850
|
}
|
|
839
1851
|
if (!bootstrap && stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
840
1852
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
841
|
-
return attachExternalStageContext(
|
|
1853
|
+
return attachExternalStageContext(sdlcStageHandlers["plan-review"].entry(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.planReviewStart), externalContext);
|
|
842
1854
|
}
|
|
843
1855
|
if (!bootstrap && stage === "code" && isVnextPlanRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
844
1856
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
845
|
-
return attachExternalStageContext(await
|
|
1857
|
+
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);
|
|
846
1858
|
}
|
|
847
1859
|
if (!bootstrap && stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
848
1860
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
849
|
-
return attachExternalStageContext(
|
|
1861
|
+
return attachExternalStageContext(sdlcStageHandlers["code-review"].entry(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.codeReviewStart), externalContext);
|
|
850
1862
|
}
|
|
851
1863
|
if (!bootstrap && stage === "merge" && isVnextMergeRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
852
1864
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
853
|
-
return attachExternalStageContext(await
|
|
1865
|
+
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);
|
|
854
1866
|
}
|
|
855
1867
|
if (intakeStdin) {
|
|
856
1868
|
throw new AppError("usage", "--intake-stdin is only available for vNext SPECIFY", 2);
|
|
@@ -865,7 +1877,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
865
1877
|
...(hookEventId ? { hookEventId } : {}),
|
|
866
1878
|
...(requireSessionBinding ? { requireSessionBinding: true } : {}),
|
|
867
1879
|
...(dir ? { dir } : {})
|
|
868
|
-
});
|
|
1880
|
+
}, prepared.legacyStageStart);
|
|
869
1881
|
}
|
|
870
1882
|
if (family === "stage" && command === "finish") {
|
|
871
1883
|
const dir = optionalOption(parsed, "dir");
|
|
@@ -875,9 +1887,34 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
875
1887
|
const projectRoot = requiredOption(parsed, "project-root");
|
|
876
1888
|
const runId = requiredPosition(parsed, 0, "run-id");
|
|
877
1889
|
const stage = requiredOption(parsed, "stage");
|
|
1890
|
+
const controlledReply = (result) => controlLifecycleReply(context, { projectRoot, runId }, result);
|
|
878
1891
|
const semanticFile = optionalOption(parsed, "semantic-file") ?? optionalOption(parsed, "data");
|
|
879
1892
|
const resultFile = optionalOption(parsed, "result-file") ?? semanticFile;
|
|
880
1893
|
const resultStdin = hasOption(parsed, "result-stdin");
|
|
1894
|
+
const retryCheckId = optionalOption(parsed, "retry-check");
|
|
1895
|
+
const retryReason = prepared.retryReason;
|
|
1896
|
+
const hookEventId = optionalOption(parsed, "hook-event-id");
|
|
1897
|
+
if (retryCheckId && !retryReason?.trim())
|
|
1898
|
+
throw new AppError("usage", "--retry-check requires --reason explaining the recovered environment", 2);
|
|
1899
|
+
if (prepared.memoryStage?.operation === "finish") {
|
|
1900
|
+
// Descriptor settlement validates exact coordinator custody and claims
|
|
1901
|
+
// the fresh hook atomically with result acceptance. Do not consume it
|
|
1902
|
+
// through the SDLC lifecycle owner before that settlement.
|
|
1903
|
+
return controlledReply((isDiagnosticFlow(prepared.memoryStage.flowId) ? finishDiagnosticStage : prepared.memoryStage.flowId === "mb-upgrade" ? finishMemoryUpgradeStage : finishMemoryStage)(context, { projectRoot, runId, stage, bytes: prepared.memoryStage.bytes,
|
|
1904
|
+
hookEventId: hookEventId ?? findRecentMatchingHookEvent(context, {
|
|
1905
|
+
projectId: requireProjectByRoot(context, resolveProjectRoot(projectRoot)).id,
|
|
1906
|
+
matchKey: entityReferenceVariants(runId).map(reference => stageLifecycleMatchKey("stage_finish", reference, stage, projectRoot)),
|
|
1907
|
+
errorCode: "trusted_session_binding_required", operation: "Memory stage finish"
|
|
1908
|
+
}).eventKey }));
|
|
1909
|
+
}
|
|
1910
|
+
const protectedLifecycle = (stage === "specify" && isVnextSpecifyRun(context, { projectRoot, runId }))
|
|
1911
|
+
|| (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId }))
|
|
1912
|
+
|| (stage === "plan" && isVnextPlanRun(context, { projectRoot, runId }))
|
|
1913
|
+
|| (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId }))
|
|
1914
|
+
|| (stage === "code" && isVnextPlanRun(context, { projectRoot, runId }))
|
|
1915
|
+
|| (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId }));
|
|
1916
|
+
if (protectedLifecycle)
|
|
1917
|
+
assertStageLifecycleOwner(context, { projectRoot, runId, stage, operation: "stage_finish", ...(hookEventId ? { hookEventId } : {}) });
|
|
881
1918
|
if (stage === "specify" && isVnextSpecifyRun(context, { projectRoot, runId })) {
|
|
882
1919
|
const outcome = optionalOption(parsed, "outcome");
|
|
883
1920
|
if (!outcome || !["specified", "failed", "cancelled"].includes(outcome)) {
|
|
@@ -889,84 +1926,92 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
889
1926
|
if (!resultFile && !resultStdin) {
|
|
890
1927
|
throw new AppError("usage", "vNext SPECIFY finish requires --result-file or --result-stdin", 2);
|
|
891
1928
|
}
|
|
892
|
-
return
|
|
1929
|
+
return controlledReply(await sdlcStageHandlers.specify.finish(context, {
|
|
893
1930
|
projectRoot,
|
|
894
1931
|
runId,
|
|
895
|
-
...(resultFile ? { resultFile } : { resultJson: await readStdin(io.stdin) }),
|
|
1932
|
+
...(resultFile ? { resultFile } : prepared.stageFinish?.value ? {} : { resultJson: await readStdin(io.stdin) }),
|
|
1933
|
+
...(prepared.stageFinish?.value ? { result: prepared.stageFinish.value } : {}),
|
|
896
1934
|
outcome: outcome
|
|
897
|
-
});
|
|
1935
|
+
}));
|
|
898
1936
|
}
|
|
899
1937
|
if (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId })) {
|
|
900
1938
|
if (!resultFile || resultStdin || hasOption(parsed, "outcome")) {
|
|
901
1939
|
throw new AppError("usage", "vNext PROTOCOLIZE requires exactly --result-file", 2);
|
|
902
1940
|
}
|
|
903
|
-
return
|
|
1941
|
+
return controlledReply(await sdlcStageHandlers.protocolize.finish(context, { projectRoot, runId, resultFile, ...(prepared.stageFinish?.value ? { result: prepared.stageFinish.value } : {}) }));
|
|
904
1942
|
}
|
|
905
1943
|
if (stage === "plan" && isVnextPlanRun(context, { projectRoot, runId })) {
|
|
906
1944
|
if (optionalOption(parsed, "code-work-batch-file"))
|
|
907
1945
|
throw new AppError("usage", "vNext PLAN derives its CODE batch from accepted semantic plans; do not supply a batch file", 2);
|
|
908
|
-
return
|
|
1946
|
+
return controlledReply(await sdlcStageHandlers.plan.finish(context, { projectRoot, runId, ...(prepared.planFinish ? { prepared: prepared.planFinish } : {}) }));
|
|
909
1947
|
}
|
|
910
1948
|
if (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId })) {
|
|
911
1949
|
if (resultStdin || hasOption(parsed, "outcome"))
|
|
912
1950
|
throw new AppError("usage", "vNext PLAN-REVIEW uses --decision-file", 2);
|
|
913
1951
|
const decisionFile = optionalOption(parsed, "decision-file");
|
|
914
|
-
return
|
|
1952
|
+
return controlledReply(await sdlcStageHandlers["plan-review"].finish(context, { projectRoot, runId, ...(decisionFile ? { decisionFile } : {}), ...(prepared.stageFinish?.value ? { decision: prepared.stageFinish.value, decisionText: prepared.stageFinish.text } : {}) }));
|
|
915
1953
|
}
|
|
916
1954
|
if (stage === "code" && isVnextPlanRun(context, { projectRoot, runId })) {
|
|
917
1955
|
if (semanticFile || resultFile || resultStdin || hasOption(parsed, "outcome")) {
|
|
918
1956
|
throw new AppError("usage", "vNext CODE finish accepts only --verification-file", 2);
|
|
919
1957
|
}
|
|
920
|
-
return await
|
|
1958
|
+
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) }));
|
|
921
1959
|
}
|
|
922
1960
|
if (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId })) {
|
|
923
1961
|
if (resultStdin || semanticFile || resultFile || hasOption(parsed, "outcome"))
|
|
924
1962
|
throw new AppError("usage", "vNext CODE-REVIEW uses --decision-file", 2);
|
|
925
|
-
return await
|
|
1963
|
+
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) }));
|
|
926
1964
|
}
|
|
927
1965
|
if (stage === "merge" && isVnextMergeRun(context, { projectRoot, runId })) {
|
|
928
1966
|
if (resultStdin || semanticFile || resultFile || hasOption(parsed, "outcome"))
|
|
929
1967
|
throw new AppError("usage", "vNext MERGE uses the Work result path returned by stage start", 2);
|
|
930
|
-
return await
|
|
1968
|
+
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));
|
|
931
1969
|
}
|
|
932
1970
|
if (hasOption(parsed, "outcome") || hasOption(parsed, "result-file") || resultStdin) {
|
|
933
1971
|
throw new AppError("usage", "--outcome, --result-file and --result-stdin are only available for vNext SPECIFY", 2);
|
|
934
1972
|
}
|
|
935
|
-
return finishStage(context, {
|
|
1973
|
+
return controlledReply(await finishStage(context, {
|
|
936
1974
|
projectRoot,
|
|
937
1975
|
runId,
|
|
938
1976
|
stage,
|
|
939
1977
|
...(dir ? { dir } : {}),
|
|
940
1978
|
...(semanticFile ? { semanticFile } : {})
|
|
941
|
-
});
|
|
1979
|
+
}, prepared.legacyStageFinish));
|
|
942
1980
|
}
|
|
943
1981
|
if (family === "stage" && command === "pause") {
|
|
944
|
-
|
|
945
|
-
|
|
1982
|
+
const projectRoot = requiredOption(parsed, "project-root");
|
|
1983
|
+
const runId = requiredPosition(parsed, 0, "run-id");
|
|
1984
|
+
const stage = requiredOption(parsed, "stage");
|
|
1985
|
+
const workId = requiredOption(parsed, "work");
|
|
1986
|
+
const protectedLifecycle = (stage === "specify" && isVnextSpecifyRun(context, { projectRoot, runId }))
|
|
1987
|
+
|| (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId }))
|
|
1988
|
+
|| (stage === "plan" && isVnextPlanRun(context, { projectRoot, runId }))
|
|
1989
|
+
|| (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId }))
|
|
1990
|
+
|| (stage === "code" && isVnextPlanRun(context, { projectRoot, runId }))
|
|
1991
|
+
|| (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId }));
|
|
1992
|
+
if (protectedLifecycle)
|
|
1993
|
+
assertStageLifecycleOwner(context, { projectRoot, runId, stage, workId, operation: "stage_pause", ...(optionalOption(parsed, "hook-event-id") ? { hookEventId: optionalOption(parsed, "hook-event-id") } : {}) });
|
|
946
1994
|
return pauseStageForUser(context, {
|
|
947
|
-
projectRoot
|
|
948
|
-
runId
|
|
949
|
-
stage
|
|
950
|
-
workId
|
|
951
|
-
question: await readStdin(io.stdin)
|
|
1995
|
+
projectRoot,
|
|
1996
|
+
runId,
|
|
1997
|
+
stage,
|
|
1998
|
+
workId,
|
|
1999
|
+
question: prepared.interactionText ?? await readStdin(io.stdin)
|
|
952
2000
|
});
|
|
953
2001
|
}
|
|
954
2002
|
if (family === "stage" && command === "resume") {
|
|
955
|
-
if (!hasOption(parsed, "answer-stdin"))
|
|
956
|
-
throw new AppError("usage", "stage resume requires --answer-stdin", 2);
|
|
957
2003
|
const hookEventId = optionalOption(parsed, "hook-event-id");
|
|
958
2004
|
return resumeStageAfterUser(context, {
|
|
959
2005
|
projectRoot: requiredOption(parsed, "project-root"),
|
|
960
2006
|
runId: requiredPosition(parsed, 0, "run-id"),
|
|
961
2007
|
stage: requiredOption(parsed, "stage"),
|
|
962
2008
|
workId: requiredOption(parsed, "work"),
|
|
963
|
-
answer: await readStdin(io.stdin),
|
|
2009
|
+
answer: prepared.interactionText ?? await readStdin(io.stdin),
|
|
2010
|
+
evidence: prepared.resumeEvidence,
|
|
964
2011
|
...(hookEventId ? { hookEventId } : {})
|
|
965
2012
|
});
|
|
966
2013
|
}
|
|
967
2014
|
if (family === "stage" && command === "block") {
|
|
968
|
-
if (!hasOption(parsed, "summary-stdin"))
|
|
969
|
-
throw new AppError("usage", "stage block requires --summary-stdin", 2);
|
|
970
2015
|
const kind = requiredOption(parsed, "kind");
|
|
971
2016
|
if (kind !== "engine" && kind !== "harness" && kind !== "environment")
|
|
972
2017
|
throw new AppError("usage", "--kind must be engine, harness or environment", 2);
|
|
@@ -977,7 +2022,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
977
2022
|
workId: requiredOption(parsed, "work"),
|
|
978
2023
|
kind,
|
|
979
2024
|
code: requiredOption(parsed, "code"),
|
|
980
|
-
summary: await readStdin(io.stdin),
|
|
2025
|
+
summary: prepared.interactionText ?? await readStdin(io.stdin),
|
|
981
2026
|
retryable: hasOption(parsed, "retryable")
|
|
982
2027
|
});
|
|
983
2028
|
}
|
|
@@ -1002,13 +2047,55 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1002
2047
|
return dispatchMergeWorker(context, command, parsed);
|
|
1003
2048
|
}
|
|
1004
2049
|
if (family === "cleanup") {
|
|
1005
|
-
return dispatchCleanup(context, command, parsed);
|
|
2050
|
+
return dispatchCleanup(context, command, parsed, prepared.cleanup);
|
|
1006
2051
|
}
|
|
1007
2052
|
if (family === "session") {
|
|
1008
|
-
return dispatchSession(context, command, parsed);
|
|
2053
|
+
return dispatchSession(context, command, parsed, prepared.sessionPayload);
|
|
1009
2054
|
}
|
|
1010
2055
|
if (family === "run") {
|
|
1011
|
-
|
|
2056
|
+
if (command === "control") {
|
|
2057
|
+
const action = requiredPosition(parsed, 0, "control action");
|
|
2058
|
+
const scope = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run") };
|
|
2059
|
+
if (action === "status")
|
|
2060
|
+
return runControlStatus(context, scope);
|
|
2061
|
+
if (action === "reconcile") {
|
|
2062
|
+
if ([...parsed.options.keys()].some(key => !["run", "project-root", "control-id", "request-id"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
2063
|
+
throw new AppError("usage", "Use run control reconcile --run ID --project-root PATH --control-id ID --request-id ID", 2);
|
|
2064
|
+
return await reconcileRunControl(context, { ...scope, controlId: requiredOption(parsed, "control-id"), requestId: requiredOption(parsed, "request-id") });
|
|
2065
|
+
}
|
|
2066
|
+
if (action === "resume") {
|
|
2067
|
+
if (["force", "grace-ms", "escalate"].some(option => hasOption(parsed, option)))
|
|
2068
|
+
throw new AppError("usage", "Resume does not accept stop or pause escalation options", 2);
|
|
2069
|
+
return await resumeRunController(context, { ...scope, requestId: requiredOption(parsed, "request-id"), controlId: requiredOption(parsed, "from"), ...(hasOption(parsed, "wait-ms") ? { waitMs: Number(requiredOption(parsed, "wait-ms")) } : {}) });
|
|
2070
|
+
}
|
|
2071
|
+
if (action === "serve")
|
|
2072
|
+
return await serveRunControl(context, { ...scope, controlId: requiredOption(parsed, "control-id") });
|
|
2073
|
+
if (action === "pause" || action === "stop")
|
|
2074
|
+
return await requestRunControl(context, {
|
|
2075
|
+
...scope, action, requestId: requiredOption(parsed, "request-id"),
|
|
2076
|
+
...(hasOption(parsed, "force") ? { force: true } : {}),
|
|
2077
|
+
...(hasOption(parsed, "grace-ms") ? { graceMs: Number(requiredOption(parsed, "grace-ms")) } : {}),
|
|
2078
|
+
...(hasOption(parsed, "escalate") ? { escalate: requiredOption(parsed, "escalate") } : {}),
|
|
2079
|
+
...(hasOption(parsed, "wait-ms") ? { waitMs: Number(requiredOption(parsed, "wait-ms")) } : {})
|
|
2080
|
+
});
|
|
2081
|
+
throw new AppError("usage", "Use: dd-flow run control pause|stop|resume|reconcile|status", 2);
|
|
2082
|
+
}
|
|
2083
|
+
if (command === "drive") {
|
|
2084
|
+
const action = requiredPosition(parsed, 0, "drive action");
|
|
2085
|
+
const scope = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run") };
|
|
2086
|
+
if (action === "status")
|
|
2087
|
+
return runControllerStatus(context, { ...scope, ...(optionalOption(parsed, "after") ? { after: Number(optionalOption(parsed, "after")) } : {}) });
|
|
2088
|
+
if (action === "launch")
|
|
2089
|
+
return await launchRunController(context, controllerLaunchInput(parsed), prepared.controllerLaunch);
|
|
2090
|
+
if (action === "serve")
|
|
2091
|
+
return await serveRunController(context, { ...scope, controllerId: requiredOption(parsed, "controller-id") });
|
|
2092
|
+
if (action === "answer")
|
|
2093
|
+
return answerRunController(context, controllerAnswerInput(parsed), prepared.controllerAnswer);
|
|
2094
|
+
if (action === "context")
|
|
2095
|
+
return supplyRunControllerContext(context, controllerContextInput(parsed), prepared.controllerContext);
|
|
2096
|
+
throw new AppError("usage", "Use: dd-flow run drive launch|status|answer|context", 2);
|
|
2097
|
+
}
|
|
2098
|
+
return dispatchRun(context, command, parsed, prepared);
|
|
1012
2099
|
}
|
|
1013
2100
|
if (family === "stat") {
|
|
1014
2101
|
return dispatchStat(context, command, parsed);
|
|
@@ -1023,22 +2110,26 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1023
2110
|
return dispatchDashboard(context, command, parsed);
|
|
1024
2111
|
}
|
|
1025
2112
|
if (family === "schema") {
|
|
1026
|
-
return
|
|
2113
|
+
return prepared.schemaValidation;
|
|
1027
2114
|
}
|
|
1028
2115
|
if (family === "memory") {
|
|
1029
|
-
return dispatchMemory(command, parsed);
|
|
2116
|
+
return dispatchMemory(context, command, parsed, prepared);
|
|
1030
2117
|
}
|
|
1031
2118
|
if (family === "migration") {
|
|
1032
|
-
return dispatchMigration(context, command, parsed, classification);
|
|
2119
|
+
return dispatchMigration(context, command, parsed, classification, prepared);
|
|
1033
2120
|
}
|
|
1034
2121
|
if (family === "codex" && command === "hooks") {
|
|
1035
|
-
return dispatchCodexHooks(context, parsed);
|
|
2122
|
+
return dispatchCodexHooks(context, parsed, prepared);
|
|
1036
2123
|
}
|
|
1037
2124
|
if (family === "codex" && command === "home") {
|
|
1038
|
-
return dispatchCodexHome(context, parsed);
|
|
2125
|
+
return dispatchCodexHome(context, parsed, prepared);
|
|
1039
2126
|
}
|
|
1040
2127
|
if (family === "codex" && command === "hook") {
|
|
1041
|
-
|
|
2128
|
+
// `handle` is owned by the pre-context ingress above, never generic dispatch.
|
|
2129
|
+
throw new AppError("usage", "Use: dd-flow codex hook handle --event EVENT", 2);
|
|
2130
|
+
}
|
|
2131
|
+
if (family === "zcode" && command === "hook") {
|
|
2132
|
+
return dispatchZcodeHook(context, parsed, await readStdin(io.stdin));
|
|
1042
2133
|
}
|
|
1043
2134
|
if (family === "zcode" && command === "event") {
|
|
1044
2135
|
return dispatchZcodeEvent(context, parsed, await readStdin(io.stdin));
|
|
@@ -1064,6 +2155,16 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1064
2155
|
throw new AppError("usage", "Unknown opencode usage action", 2);
|
|
1065
2156
|
return ingestOpenCodeUsage(context, { projectRoot: requiredOption(parsed, "project-root"), stdin: await readStdin(io.stdin) });
|
|
1066
2157
|
}
|
|
2158
|
+
if (family === "droid" && command === "event") {
|
|
2159
|
+
if (requiredPosition(parsed, 0, "droid event action") !== "handle")
|
|
2160
|
+
throw new AppError("usage", "Unknown droid event action", 2);
|
|
2161
|
+
return handleDroidEvent(context, { projectRoot: requiredOption(parsed, "project-root"), stdin: await readStdin(io.stdin) });
|
|
2162
|
+
}
|
|
2163
|
+
if (family === "droid" && command === "usage") {
|
|
2164
|
+
if (requiredPosition(parsed, 0, "droid usage action") !== "ingest")
|
|
2165
|
+
throw new AppError("usage", "Unknown droid usage action", 2);
|
|
2166
|
+
return ingestDroidUsage(context, { projectRoot: requiredOption(parsed, "project-root"), stdin: await readStdin(io.stdin) });
|
|
2167
|
+
}
|
|
1067
2168
|
if (family === "agy" && command === "event")
|
|
1068
2169
|
return dispatchAgyEvent(context, parsed, await readStdin(io.stdin));
|
|
1069
2170
|
if (family === "agy" && command === "usage") {
|
|
@@ -1072,11 +2173,14 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1072
2173
|
return ingestAgyUsage(context, { projectRoot: requiredOption(parsed, "project-root"), stdin: await readStdin(io.stdin) });
|
|
1073
2174
|
}
|
|
1074
2175
|
if (family === "worktree") {
|
|
1075
|
-
return dispatchWorktree(context, command, parsed, requiredScopeProjectRoot(scopeProjectRoot, `worktree ${command ?? ""}`));
|
|
2176
|
+
return dispatchWorktree(context, command, parsed, requiredScopeProjectRoot(scopeProjectRoot, `worktree ${command ?? ""}`), prepared);
|
|
1076
2177
|
}
|
|
1077
2178
|
throw new AppError("usage", `Unknown command: ${args.join(" ") || "<empty>"}`, 2);
|
|
1078
2179
|
}
|
|
1079
|
-
function
|
|
2180
|
+
function engineBindInput(parsed) {
|
|
2181
|
+
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") };
|
|
2182
|
+
}
|
|
2183
|
+
function dispatchEngine(context, command, parsed, classification, prepared) {
|
|
1080
2184
|
if (command === "install") {
|
|
1081
2185
|
return installCurrentEngine(context, { force: hasOption(parsed, "force") });
|
|
1082
2186
|
}
|
|
@@ -1101,20 +2205,20 @@ function dispatchEngine(context, command, parsed, classification) {
|
|
|
1101
2205
|
});
|
|
1102
2206
|
}
|
|
1103
2207
|
if (command === "bind-run") {
|
|
1104
|
-
return bindRunEngine(context,
|
|
1105
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1106
|
-
runId: requiredPosition(parsed, 0, "run-id"),
|
|
1107
|
-
packageName: optionalOption(parsed, "package"),
|
|
1108
|
-
engineVersion: requiredOption(parsed, "engine-version"),
|
|
1109
|
-
integrityChecksum: optionalOption(parsed, "integrity-checksum"),
|
|
1110
|
-
reason: requiredOption(parsed, "reason")
|
|
1111
|
-
});
|
|
2208
|
+
return bindRunEngine(context, engineBindInput(parsed), prepared.engineBind);
|
|
1112
2209
|
}
|
|
1113
2210
|
throw new AppError("usage", `Unknown engine command: ${command ?? "<empty>"}`, 2);
|
|
1114
2211
|
}
|
|
1115
|
-
function dispatchMigration(context, command, parsed, classification) {
|
|
2212
|
+
function dispatchMigration(context, command, parsed, classification, prepared) {
|
|
1116
2213
|
if (command === "impact")
|
|
1117
2214
|
return assessMigrationImpact(context, { projectRoot: requiredOption(parsed, "project-root"), ...(optionalOption(parsed, "target-version") ? { targetVersion: optionalOption(parsed, "target-version") } : {}), ...(optionalOption(parsed, "mode") ? { mode: optionalOption(parsed, "mode") } : {}) });
|
|
2215
|
+
if (command === "readiness") {
|
|
2216
|
+
if (!prepared.upgradeStorageScope)
|
|
2217
|
+
throw new AppError("validation", "Upgrade storage scope must be prepared before publication", 2);
|
|
2218
|
+
// Unlike the initial readonly syntax pass, this connection is admitted
|
|
2219
|
+
// prepared-existing. Physical writer checks still precede receipt writes.
|
|
2220
|
+
return { ok: true, ...PreparedUpgradeStorageReadiness.prepare(context, prepared.upgradeStorageScope).apply(context) };
|
|
2221
|
+
}
|
|
1118
2222
|
if (command === "plan" || command === "report") {
|
|
1119
2223
|
return planMigration(context, {
|
|
1120
2224
|
projectRoot: requiredOption(parsed, "project-root"),
|
|
@@ -1128,26 +2232,42 @@ function dispatchMigration(context, command, parsed, classification) {
|
|
|
1128
2232
|
});
|
|
1129
2233
|
}
|
|
1130
2234
|
if (command === "verify") {
|
|
1131
|
-
return verifyMigrationReport(context, { file: requiredOption(parsed, "file") });
|
|
2235
|
+
return prepared.migrationVerification ?? verifyMigrationReport(context, { file: requiredOption(parsed, "file") });
|
|
1132
2236
|
}
|
|
1133
2237
|
if (command === "apply") {
|
|
1134
|
-
return applyMigration(context,
|
|
1135
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1136
|
-
sourceVersion: optionalOption(parsed, "source-version"),
|
|
1137
|
-
targetVersion: optionalOption(parsed, "target-version"),
|
|
1138
|
-
runId: optionalOption(parsed, "run"),
|
|
1139
|
-
backupPath: optionalOption(parsed, "backup-path"),
|
|
1140
|
-
backupCreatedAt: optionalOption(parsed, "backup-created-at"),
|
|
1141
|
-
reportFile: optionalOption(parsed, "file"),
|
|
1142
|
-
allowActive: false,
|
|
1143
|
-
classification
|
|
1144
|
-
});
|
|
2238
|
+
return applyMigration(context, migrationApplyInput(parsed, classification), prepared.migrationApply);
|
|
1145
2239
|
}
|
|
1146
2240
|
throw new AppError("usage", `Unknown migration command: ${command ?? "<empty>"}`, 2);
|
|
1147
2241
|
}
|
|
1148
|
-
function
|
|
2242
|
+
function protocolTransitionInput(parsed, scopeProjectRoot) {
|
|
2243
|
+
const reason = optionalOption(parsed, "reason");
|
|
2244
|
+
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 } : {}) };
|
|
2245
|
+
}
|
|
2246
|
+
function controllerAnswerInput(parsed) {
|
|
2247
|
+
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") };
|
|
2248
|
+
}
|
|
2249
|
+
function controllerLaunchInput(parsed) {
|
|
2250
|
+
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") } : {}) };
|
|
2251
|
+
}
|
|
2252
|
+
function controllerContextInput(parsed) {
|
|
2253
|
+
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") };
|
|
2254
|
+
}
|
|
2255
|
+
function migrationApplyInput(parsed, classification) {
|
|
2256
|
+
return {
|
|
2257
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
2258
|
+
sourceVersion: optionalOption(parsed, "source-version"),
|
|
2259
|
+
targetVersion: optionalOption(parsed, "target-version"),
|
|
2260
|
+
runId: optionalOption(parsed, "run"),
|
|
2261
|
+
backupPath: optionalOption(parsed, "backup-path"),
|
|
2262
|
+
backupCreatedAt: optionalOption(parsed, "backup-created-at"),
|
|
2263
|
+
reportFile: optionalOption(parsed, "file"),
|
|
2264
|
+
allowActive: false,
|
|
2265
|
+
classification
|
|
2266
|
+
};
|
|
2267
|
+
}
|
|
2268
|
+
function dispatchCanon(context, command, parsed, prepared) {
|
|
1149
2269
|
if (command === "register") {
|
|
1150
|
-
return registerCanonRoot(context, { root: requiredOption(parsed, "root") });
|
|
2270
|
+
return registerCanonRoot(context, { root: requiredOption(parsed, "root") }, prepared.canonRegistration);
|
|
1151
2271
|
}
|
|
1152
2272
|
if (command === "status") {
|
|
1153
2273
|
return getCanonStatus(context, { root: optionalOption(parsed, "root") });
|
|
@@ -1157,6 +2277,15 @@ function dispatchCanon(context, command, parsed) {
|
|
|
1157
2277
|
}
|
|
1158
2278
|
throw new AppError("usage", `Unknown canon command: ${command ?? "<empty>"}`, 2);
|
|
1159
2279
|
}
|
|
2280
|
+
function mergeJobTarget(parsed, scopeProjectRoot) {
|
|
2281
|
+
return { protocolId: requiredPosition(parsed, 0, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue"), workerId: requiredWorkerId(parsed), workspacePath: workspacePathOption(parsed) };
|
|
2282
|
+
}
|
|
2283
|
+
function mergeCancellationInput(parsed, scopeProjectRoot) {
|
|
2284
|
+
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") };
|
|
2285
|
+
}
|
|
2286
|
+
function mergeWaitInput(parsed, scopeProjectRoot) {
|
|
2287
|
+
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 };
|
|
2288
|
+
}
|
|
1160
2289
|
function dispatchMergeQueue(context, command, parsed, scopeProjectRoot) {
|
|
1161
2290
|
if (hasOption(parsed, "session-id")) {
|
|
1162
2291
|
throw new AppError("usage", "--session-id is no longer accepted on merge-queue commands; use --worker-id", 2);
|
|
@@ -1180,32 +2309,17 @@ function dispatchMergeQueue(context, command, parsed, scopeProjectRoot) {
|
|
|
1180
2309
|
});
|
|
1181
2310
|
}
|
|
1182
2311
|
if (command === "wait-next") {
|
|
1183
|
-
return waitNextMergeJob(context,
|
|
1184
|
-
projectRoot: requiredOptionOrScope(parsed, scopeProjectRoot, "project-root"),
|
|
1185
|
-
workerId: requiredWorkerId(parsed),
|
|
1186
|
-
timeoutSeconds: optionalNumber(parsed, "timeout"),
|
|
1187
|
-
pollIntervalSeconds: optionalNumber(parsed, "poll-interval"),
|
|
1188
|
-
workspacePath: workspacePathOption(parsed),
|
|
1189
|
-
acquireLock: parseOptionalBoolean(optionalOption(parsed, "acquire-lock"), "acquire-lock") ?? false
|
|
1190
|
-
});
|
|
2312
|
+
return waitNextMergeJob(context, mergeWaitInput(parsed, scopeProjectRoot));
|
|
1191
2313
|
}
|
|
1192
2314
|
if (command === "complete") {
|
|
1193
|
-
const protocolId = requiredPosition(parsed, 0, "protocol-id");
|
|
1194
2315
|
return completeMergeJob(context, {
|
|
1195
|
-
|
|
1196
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue complete"),
|
|
1197
|
-
workerId: requiredWorkerId(parsed),
|
|
1198
|
-
workspacePath: workspacePathOption(parsed),
|
|
2316
|
+
...mergeJobTarget(parsed, scopeProjectRoot),
|
|
1199
2317
|
summary: requiredOption(parsed, "summary")
|
|
1200
2318
|
});
|
|
1201
2319
|
}
|
|
1202
2320
|
if (command === "fail") {
|
|
1203
|
-
const protocolId = requiredPosition(parsed, 0, "protocol-id");
|
|
1204
2321
|
return failMergeJob(context, {
|
|
1205
|
-
|
|
1206
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue fail"),
|
|
1207
|
-
workerId: requiredWorkerId(parsed),
|
|
1208
|
-
workspacePath: workspacePathOption(parsed),
|
|
2322
|
+
...mergeJobTarget(parsed, scopeProjectRoot),
|
|
1209
2323
|
reason: requiredOption(parsed, "reason"),
|
|
1210
2324
|
requeue: parseBoolean(requiredOption(parsed, "requeue"), "requeue")
|
|
1211
2325
|
});
|
|
@@ -1220,15 +2334,7 @@ function dispatchMergeQueue(context, command, parsed, scopeProjectRoot) {
|
|
|
1220
2334
|
});
|
|
1221
2335
|
}
|
|
1222
2336
|
if (command === "cancel") {
|
|
1223
|
-
|
|
1224
|
-
return cancelMergeQueueJob(context, {
|
|
1225
|
-
protocolId,
|
|
1226
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue cancel"),
|
|
1227
|
-
reason: requiredOption(parsed, "reason"),
|
|
1228
|
-
workerId: optionalOption(parsed, "worker-id"),
|
|
1229
|
-
workspacePath: optionalOption(parsed, "path"),
|
|
1230
|
-
force: hasOption(parsed, "force")
|
|
1231
|
-
});
|
|
2337
|
+
return cancelMergeQueueJob(context, mergeCancellationInput(parsed, scopeProjectRoot));
|
|
1232
2338
|
}
|
|
1233
2339
|
throw new AppError("usage", `Unknown merge-queue command: ${command ?? "<empty>"}`, 2);
|
|
1234
2340
|
}
|
|
@@ -1327,7 +2433,7 @@ function dispatchMergeWorker(context, command, parsed) {
|
|
|
1327
2433
|
}
|
|
1328
2434
|
throw new AppError("usage", `Unknown merge-worker command: ${command ?? "<empty>"}`, 2);
|
|
1329
2435
|
}
|
|
1330
|
-
function dispatchMemory(command, parsed) {
|
|
2436
|
+
function dispatchMemory(context, command, parsed, prepared) {
|
|
1331
2437
|
const subcommand = requiredPosition(parsed, 0, "memory subcommand");
|
|
1332
2438
|
if (command === "permissions" && subcommand === "preflight") {
|
|
1333
2439
|
return preflightMemoryPermissions({
|
|
@@ -1338,6 +2444,102 @@ function dispatchMemory(command, parsed) {
|
|
|
1338
2444
|
mode: requiredOption(parsed, "mode")
|
|
1339
2445
|
});
|
|
1340
2446
|
}
|
|
2447
|
+
const scope = () => {
|
|
2448
|
+
const projectRoot = requiredOption(parsed, "project-root");
|
|
2449
|
+
const project = requireProjectByRoot(context, resolveProjectRoot(projectRoot));
|
|
2450
|
+
return { projectRoot, runId: resolveRun(context, project.id, requiredOption(parsed, "run")).id };
|
|
2451
|
+
};
|
|
2452
|
+
if (command === "repair") {
|
|
2453
|
+
const wave = requiredOption(parsed, "wave");
|
|
2454
|
+
if (wave !== "1" && wave !== "2")
|
|
2455
|
+
throw new AppError("usage", "Memory repairs support only waves 1 and 2", 2);
|
|
2456
|
+
const work = { ...scope(), workId: requiredOption(parsed, "work") };
|
|
2457
|
+
if (subcommand === "baseline")
|
|
2458
|
+
return (wave === "1" ? registerMemoryRepairBaselineFromStage : registerMemorySecondRepairBaselineFromStage)(context, work);
|
|
2459
|
+
if (subcommand === "copy-reserve")
|
|
2460
|
+
return (wave === "1" ? reserveMemoryRepairWriterCopyFromStage : reserveMemorySecondRepairCopyFromStage)(context, work);
|
|
2461
|
+
if (subcommand === "copy-materialize")
|
|
2462
|
+
return (wave === "1" ? materializeMemoryRepairWriterCopyFromStage : materializeMemorySecondRepairCopyFromStage)(context, work);
|
|
2463
|
+
if (subcommand === "copy-dispatch")
|
|
2464
|
+
return (wave === "1" ? prepareMemoryRepairDispatchFromStage : prepareMemorySecondRepairDispatchFromStage)(context, work);
|
|
2465
|
+
const workspaceRoot = requiredOption(parsed, "workspace-root");
|
|
2466
|
+
if (wave === "2") {
|
|
2467
|
+
const bundle = readFlowRunExecutionBundle(context, work);
|
|
2468
|
+
if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id) || bundle.execution.snapshot().scope.workspace !== workspaceRoot)
|
|
2469
|
+
throw new AppError("memory_repair_scope_invalid", "Repair workspace does not match its frozen memory RUN", 2, { phase: "prepare", effect: "no_effect" });
|
|
2470
|
+
}
|
|
2471
|
+
if (subcommand === "current-view")
|
|
2472
|
+
return wave === "1"
|
|
2473
|
+
? registerMemoryRepairCurrentViewFromStage(context, { ...work, workspaceRoot })
|
|
2474
|
+
: registerMemorySecondRepairCurrentViewFromStage(context, work);
|
|
2475
|
+
const output = { ...work, workspaceRoot, workSessionId: requiredOption(parsed, "work-session") };
|
|
2476
|
+
if (subcommand === "publication-source" && wave === "1")
|
|
2477
|
+
return registerMemoryRepairPublicationSourceFromStage(context, output);
|
|
2478
|
+
if (subcommand === "publication-reserve") {
|
|
2479
|
+
if (!prepared.memoryOperationBytes)
|
|
2480
|
+
throw new AppError("cli_input_unprepared", "Memory repair output bytes were not prepared", 1);
|
|
2481
|
+
return (wave === "1" ? reserveMemoryRepairPublicationAgainstSourceFromStage : reserveMemorySecondRepairPublicationFromStage)(context, { ...output, bytes: prepared.memoryOperationBytes });
|
|
2482
|
+
}
|
|
2483
|
+
if (subcommand === "publication-apply")
|
|
2484
|
+
return (wave === "1" ? applyMemoryRepairPublicationFromStage : applyMemorySecondRepairPublicationFromStage)(context, output);
|
|
2485
|
+
if (subcommand === "publication-finish")
|
|
2486
|
+
return (wave === "1" ? finishMemoryRepairPublicationFromStage : finishMemorySecondRepairPublicationFromStage)(context, output);
|
|
2487
|
+
}
|
|
2488
|
+
if (command === "compose" && subcommand === "register") {
|
|
2489
|
+
if (!prepared.memoryOperationBytes)
|
|
2490
|
+
throw new AppError("cli_input_unprepared", "Memory composition bytes were not prepared", 1);
|
|
2491
|
+
try {
|
|
2492
|
+
return registerMemoryCompositionFromStage(context, { ...scope(), requestKey: requiredOption(parsed, "request-key"), bytes: prepared.memoryOperationBytes });
|
|
2493
|
+
}
|
|
2494
|
+
catch (error) {
|
|
2495
|
+
if (error instanceof AppError && (error.code === "memory_stage_result_invalid"
|
|
2496
|
+
|| error.code === "memory_preflight_handler_invalid" && ["unselected_preflight_source", "proposed_source_sha256_mismatch"].includes(String(error.details.reason))) && error.details.phase === "prepare"
|
|
2497
|
+
&& error.details.effect === "no_effect" && error.details.recoverable === true) {
|
|
2498
|
+
Object.assign(error.details, compositionProposalRetry(context, parsed));
|
|
2499
|
+
}
|
|
2500
|
+
throw error;
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
if (command === "upgrade" && subcommand === "publish")
|
|
2504
|
+
return publishMemoryUpgradeFromStage(context, scope());
|
|
2505
|
+
if (command === "verify" && subcommand === "checks")
|
|
2506
|
+
return runMemoryVerificationChecks(context, scope());
|
|
2507
|
+
if (command === "review" && subcommand === "register")
|
|
2508
|
+
return registerMemoryReviewFromStage(context, scope());
|
|
2509
|
+
if (command === "review" && subcommand === "collect") {
|
|
2510
|
+
const wave = collectMemoryInitialReviewFromStage(context, scope());
|
|
2511
|
+
const selected = scope(), bundle = readFlowRunExecutionBundle(context, selected);
|
|
2512
|
+
if (!bundle)
|
|
2513
|
+
throw new AppError("memory_review_handler_invalid", "Registered Memory bundle required", 2);
|
|
2514
|
+
const project = requireProjectByRoot(context, resolveProjectRoot(selected.projectRoot));
|
|
2515
|
+
const retained = PreparedRetainedMemoryReviewResults.prepare(context, PreparedRetainedMemoryReviewAuthority.prepare(context, { projectId: project.id, runId: selected.runId }, bundle));
|
|
2516
|
+
return { references: wave.references(), projection: wave.resultProjection(),
|
|
2517
|
+
correction_plan_binding: { schema_id: "dd-flow/memory-review-correction-plan@1", run_id: selected.runId, wave: 1, review_sha256: retained.sha256 } };
|
|
2518
|
+
}
|
|
2519
|
+
if (command === "finish" && subcommand === "evidence")
|
|
2520
|
+
return registerMemoryFinishEvidenceFromStage(context, scope());
|
|
2521
|
+
if (command === "finish" && subcommand === "integrate")
|
|
2522
|
+
return integrateMemoryFinishFromStage(context, scope());
|
|
2523
|
+
if (command === "corrections" || command === "review" && ["selective-register", "selective-collect"].includes(subcommand)) {
|
|
2524
|
+
const wave = requiredOption(parsed, "wave");
|
|
2525
|
+
if (wave !== "1" && wave !== "2")
|
|
2526
|
+
throw new AppError("usage", "Memory corrections support only waves 1 and 2", 2);
|
|
2527
|
+
if (command === "corrections" && subcommand === "register") {
|
|
2528
|
+
if (!prepared.memoryOperationBytes)
|
|
2529
|
+
throw new AppError("cli_input_unprepared", "Memory correction plan bytes were not prepared", 1);
|
|
2530
|
+
const register = wave === "1" ? registerMemoryCorrectionsFromStage : registerMemorySecondCorrectionsFromStage;
|
|
2531
|
+
return register(context, { ...scope(), bytes: prepared.memoryOperationBytes });
|
|
2532
|
+
}
|
|
2533
|
+
if (command === "corrections" && subcommand === "checks")
|
|
2534
|
+
return (wave === "1" ? runMemoryCorrectionChecksFromStage : runMemorySecondCorrectionChecksFromStage)(context, scope());
|
|
2535
|
+
if (command === "review" && subcommand === "selective-register")
|
|
2536
|
+
return (wave === "1" ? registerCorrectedMemoryReviewFromStage : registerSecondCorrectedMemoryReviewFromStage)(context, scope());
|
|
2537
|
+
if (command === "review" && subcommand === "selective-collect") {
|
|
2538
|
+
const accepted = (wave === "1" ? collectCorrectedMemoryReviewFromStage : collectSecondCorrectedMemoryReviewFromStage)(context, scope());
|
|
2539
|
+
return { wave: Number(wave), references: accepted.references(), projection: accepted.resultProjection(),
|
|
2540
|
+
...(wave === "1" ? { correction_plan_binding: { schema_id: "dd-flow/memory-review-correction-plan@1", run_id: scope().runId, wave: 2, review_sha256: accepted.sha256 } } : {}) };
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
1341
2543
|
throw new AppError("usage", `Unknown memory command: ${["memory", command, subcommand].filter(Boolean).join(" ")}`, 2);
|
|
1342
2544
|
}
|
|
1343
2545
|
function completedCommandExitCode(payload) {
|
|
@@ -1346,7 +2548,7 @@ function completedCommandExitCode(payload) {
|
|
|
1346
2548
|
const exitCode = payload.exit_code;
|
|
1347
2549
|
return typeof exitCode === "number" && Number.isInteger(exitCode) ? exitCode : 0;
|
|
1348
2550
|
}
|
|
1349
|
-
function dispatchCleanup(context, command, parsed) {
|
|
2551
|
+
function dispatchCleanup(context, command, parsed, prepared) {
|
|
1350
2552
|
if (command === "scan") {
|
|
1351
2553
|
return cleanupScan(context, { projectRoot: requiredOption(parsed, "project-root") });
|
|
1352
2554
|
}
|
|
@@ -1354,6 +2556,7 @@ function dispatchCleanup(context, command, parsed) {
|
|
|
1354
2556
|
return cleanupApply(context, {
|
|
1355
2557
|
projectRoot: requiredOption(parsed, "project-root"),
|
|
1356
2558
|
planFile: requiredOption(parsed, "plan-file"),
|
|
2559
|
+
prepared,
|
|
1357
2560
|
reason: requiredOption(parsed, "reason"),
|
|
1358
2561
|
force: hasOption(parsed, "force")
|
|
1359
2562
|
});
|
|
@@ -1374,12 +2577,14 @@ function dispatchProjectConfig(context, parsed) {
|
|
|
1374
2577
|
}
|
|
1375
2578
|
throw new AppError("usage", `Unknown project config action: ${action}`, 2);
|
|
1376
2579
|
}
|
|
1377
|
-
function dispatchSession(context, command, parsed) {
|
|
2580
|
+
function dispatchSession(context, command, parsed, preparedPayload) {
|
|
1378
2581
|
if (command === "register") {
|
|
1379
2582
|
return registerFlowSession(context, {
|
|
2583
|
+
...(preparedPayload ? { preparedPayload } : {}),
|
|
1380
2584
|
payloadBase64: optionalOption(parsed, "payload-base64"),
|
|
1381
2585
|
payloadJson: optionalOption(parsed, "payload-json"),
|
|
1382
2586
|
payloadFile: optionalOption(parsed, "payload-file"),
|
|
2587
|
+
hookEventId: optionalOption(parsed, "hook-event-id"),
|
|
1383
2588
|
sessionId: optionalOption(parsed, "session-id")
|
|
1384
2589
|
});
|
|
1385
2590
|
}
|
|
@@ -1412,7 +2617,47 @@ function dispatchSession(context, command, parsed) {
|
|
|
1412
2617
|
}
|
|
1413
2618
|
throw new AppError("usage", `Unknown session command: ${command ?? "<empty>"}`, 2);
|
|
1414
2619
|
}
|
|
1415
|
-
function
|
|
2620
|
+
function snapshotCreateInput(parsed) {
|
|
2621
|
+
const recoveryId = optionalOption(parsed, "recovery-id"), stageEntry = optionalOption(parsed, "stage-entry");
|
|
2622
|
+
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 } : {}) };
|
|
2623
|
+
}
|
|
2624
|
+
function snapshotRestoreInput(parsed) {
|
|
2625
|
+
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") } : {}) };
|
|
2626
|
+
}
|
|
2627
|
+
function runStartInput(parsed) {
|
|
2628
|
+
const protocolOverrides = readOptionalJsonFile(optionalOption(parsed, "protocol-override-file"));
|
|
2629
|
+
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")) };
|
|
2630
|
+
}
|
|
2631
|
+
function runForkInput(parsed) {
|
|
2632
|
+
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") } : {}) };
|
|
2633
|
+
}
|
|
2634
|
+
function recoveryCaptureInput(parsed) {
|
|
2635
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 1, "run-id"), recoveryId: requiredOption(parsed, "recovery-id"), capturePath: requiredOption(parsed, "capture-path") };
|
|
2636
|
+
}
|
|
2637
|
+
function recoveryResumeInput(parsed) {
|
|
2638
|
+
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") };
|
|
2639
|
+
}
|
|
2640
|
+
function dispatchRun(context, command, parsed, prepared = {}) {
|
|
2641
|
+
if (command === "reconcile")
|
|
2642
|
+
return reconcileFlowRunHealth(context, { runId: parsed.positional[0], projectRoot: optionalOption(parsed, "project-root"), apply: hasOption(parsed, "apply"), noProgressMs: optionalNumber(parsed, "no-progress-ms") });
|
|
2643
|
+
if (command === "recovery") {
|
|
2644
|
+
const action = requiredPosition(parsed, 0, "recovery action");
|
|
2645
|
+
const projectRoot = requiredOption(parsed, "project-root");
|
|
2646
|
+
const runId = requiredPosition(parsed, 1, "run-id");
|
|
2647
|
+
if (action === "inspect")
|
|
2648
|
+
return inspectRunRecovery(context, { projectRoot, runId });
|
|
2649
|
+
if (action === "begin")
|
|
2650
|
+
return beginRunRecovery(context, { projectRoot, runId, interruption: requiredOption(parsed, "interruption-json") }, prepared.recoveryPayload);
|
|
2651
|
+
if (action === "seal")
|
|
2652
|
+
return sealRunRecovery(context, { projectRoot, runId, recoveryId: requiredOption(parsed, "recovery-id"), settlement: requiredOption(parsed, "settlement-json") }, prepared.recoveryPayload);
|
|
2653
|
+
if (action === "capture")
|
|
2654
|
+
return recordRecoveryCapture(context, recoveryCaptureInput(parsed), prepared.recoveryCapture);
|
|
2655
|
+
if (action === "resume")
|
|
2656
|
+
return resumeRunRecovery(context, recoveryResumeInput(parsed), prepared.recoveryResume);
|
|
2657
|
+
if (action === "accept")
|
|
2658
|
+
return acceptRunRecovery(context, { projectRoot, runId, recoveryId: requiredOption(parsed, "recovery-id"), ...(optionalOption(parsed, "hook-event-id") ? { hookEventId: optionalOption(parsed, "hook-event-id") } : {}) });
|
|
2659
|
+
throw new AppError("usage", "Use: dd-flow run recovery inspect|begin|seal|capture|resume|accept", 2);
|
|
2660
|
+
}
|
|
1416
2661
|
if (command === "snapshot") {
|
|
1417
2662
|
const action = requiredPosition(parsed, 0, "snapshot action");
|
|
1418
2663
|
if (action === "bootstrap") {
|
|
@@ -1420,59 +2665,31 @@ function dispatchRun(context, command, parsed) {
|
|
|
1420
2665
|
if (bootstrapAction === "create")
|
|
1421
2666
|
return createEvalBootstrapSnapshot(context, {
|
|
1422
2667
|
projectRoot: requiredOption(parsed, "project-root"),
|
|
1423
|
-
output: requiredOption(parsed, "output")
|
|
2668
|
+
output: requiredOption(parsed, "output"),
|
|
2669
|
+
flow: optionalOption(parsed, "flow")
|
|
1424
2670
|
});
|
|
1425
2671
|
if (bootstrapAction === "restore")
|
|
1426
|
-
return restoreEvalBootstrapSnapshot(context,
|
|
1427
|
-
snapshot: requiredOption(parsed, "snapshot"),
|
|
1428
|
-
projectRoot: requiredOption(parsed, "project-root")
|
|
1429
|
-
});
|
|
2672
|
+
return restoreEvalBootstrapSnapshot(context, snapshotRestoreInput(parsed), prepared.bootstrapRestore);
|
|
1430
2673
|
throw new AppError("usage", "Use: dd-flow run snapshot bootstrap create|restore", 2);
|
|
1431
2674
|
}
|
|
1432
2675
|
if (action === "create") {
|
|
1433
|
-
|
|
1434
|
-
const stageEntry = optionalOption(parsed, "stage-entry");
|
|
1435
|
-
if (candidate && stageEntry !== undefined)
|
|
1436
|
-
throw new AppError("usage", "Use either --candidate or --stage-entry, not both", 2);
|
|
1437
|
-
if (!candidate && stageEntry === undefined)
|
|
1438
|
-
throw new AppError("usage", "Snapshot create requires --candidate or --stage-entry", 2);
|
|
1439
|
-
const input = {
|
|
1440
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1441
|
-
runId: requiredPosition(parsed, 1, "run-id"),
|
|
1442
|
-
output: requiredOption(parsed, "output")
|
|
1443
|
-
};
|
|
1444
|
-
return candidate
|
|
1445
|
-
? createEvalRunSnapshot(context, { ...input, candidate: true })
|
|
1446
|
-
: createEvalRunSnapshot(context, { ...input, stageEntry: stageEntry });
|
|
2676
|
+
return createEvalRunSnapshot(context, snapshotCreateInput(parsed));
|
|
1447
2677
|
}
|
|
1448
2678
|
if (action === "restore")
|
|
1449
|
-
return restoreEvalRunSnapshot(context,
|
|
1450
|
-
snapshot: requiredOption(parsed, "snapshot"),
|
|
1451
|
-
projectRoot: requiredOption(parsed, "project-root")
|
|
1452
|
-
});
|
|
2679
|
+
return restoreEvalRunSnapshot(context, snapshotRestoreInput(parsed), prepared.snapshotRestore);
|
|
1453
2680
|
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);
|
|
1454
2681
|
}
|
|
2682
|
+
if (command === "fork") {
|
|
2683
|
+
return forkRunFromSnapshot(context, runForkInput(parsed), prepared.runFork);
|
|
2684
|
+
}
|
|
1455
2685
|
if (command === "prepare-vnext-specify") {
|
|
1456
|
-
return prepareVnextSpecifyRun(context, { projectRoot: requiredOption(parsed, "project-root"), slug: requiredOption(parsed, "slug") });
|
|
2686
|
+
return prepareVnextSpecifyRun(context, { projectRoot: requiredOption(parsed, "project-root"), slug: requiredOption(parsed, "slug"), executionRoutingFile: optionalOption(parsed, "execution-routing-file") });
|
|
1457
2687
|
}
|
|
1458
2688
|
if (command === "start") {
|
|
1459
2689
|
if (optionalOption(parsed, "flow-kind") === "mb-upgrade") {
|
|
1460
2690
|
assertUpgradeRunStorageReady(context, requiredOption(parsed, "project-root"));
|
|
1461
2691
|
}
|
|
1462
|
-
|
|
1463
|
-
return startFlowRun(context, {
|
|
1464
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1465
|
-
workspaceRoot: optionalOption(parsed, "workspace-root"),
|
|
1466
|
-
flowKind: requiredOption(parsed, "flow-kind"),
|
|
1467
|
-
subjectType: requiredOption(parsed, "subject-type"),
|
|
1468
|
-
subjectId: requiredOption(parsed, "subject-id"),
|
|
1469
|
-
slug: requiredOption(parsed, "slug"),
|
|
1470
|
-
nextAction: optionalOption(parsed, "next-action"),
|
|
1471
|
-
preset: optionalOption(parsed, "preset"),
|
|
1472
|
-
taskProfile: readOptionalJsonFile(optionalOption(parsed, "task-profile-file")),
|
|
1473
|
-
...(protocolOverrides ? { protocolOverrides } : {}),
|
|
1474
|
-
runOverrides: parseFlowFlagOverrides(allOptions(parsed, "flag"))
|
|
1475
|
-
});
|
|
2692
|
+
return startFlowRun(context, prepared.runStart?.input ?? runStartInput(parsed), prepared.runStart?.value);
|
|
1476
2693
|
}
|
|
1477
2694
|
if (command === "status") {
|
|
1478
2695
|
return getFlowRunStatus(context, {
|
|
@@ -1542,88 +2759,84 @@ function dispatchRun(context, command, parsed) {
|
|
|
1542
2759
|
throw new AppError("usage", `Unknown run flags command: ${action}`, 2);
|
|
1543
2760
|
}
|
|
1544
2761
|
if (command === "attach-stage") {
|
|
1545
|
-
return attachFlowRunStage(context,
|
|
1546
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1547
|
-
runId: requiredPosition(parsed, 0, "run-id"),
|
|
1548
|
-
stage: requiredOption(parsed, "stage"),
|
|
1549
|
-
dir: requiredOption(parsed, "dir"),
|
|
1550
|
-
status: requiredOption(parsed, "status"),
|
|
1551
|
-
dataSchemaId: optionalOption(parsed, "data-schema-id")
|
|
1552
|
-
});
|
|
2762
|
+
return attachFlowRunStage(context, stageAttachmentInput(parsed));
|
|
1553
2763
|
}
|
|
1554
2764
|
if (command === "complete-stage") {
|
|
1555
|
-
const
|
|
1556
|
-
return completeFlowRunStage(context,
|
|
1557
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1558
|
-
runId: requiredPosition(parsed, 0, "run-id"),
|
|
1559
|
-
stage: requiredOption(parsed, "stage"),
|
|
1560
|
-
status: requiredOption(parsed, "status"),
|
|
1561
|
-
stageReport,
|
|
1562
|
-
data: optionalOption(parsed, "data"),
|
|
1563
|
-
dataSchemaId: optionalOption(parsed, "data-schema-id"),
|
|
1564
|
-
report: optionalOption(parsed, "report"),
|
|
1565
|
-
aliases: allOptions(parsed, "alias")
|
|
1566
|
-
});
|
|
2765
|
+
const input = stageCompletionInput(parsed);
|
|
2766
|
+
return completeFlowRunStage(context, input, prepareRawFlowRunStageCompletion(context, input));
|
|
1567
2767
|
}
|
|
1568
2768
|
if (command === "complete") {
|
|
1569
2769
|
throw new AppError("usage", "run complete is retired; only an operator may use run override", 2);
|
|
1570
2770
|
}
|
|
1571
2771
|
if (command === "override") {
|
|
1572
|
-
|
|
1573
|
-
if (!['cancelled', 'failed'].includes(status)) {
|
|
1574
|
-
throw new AppError("usage", "run override status must be cancelled or failed", 2, { status });
|
|
1575
|
-
}
|
|
1576
|
-
return completeFlowRun(context, {
|
|
1577
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1578
|
-
runId: requiredPosition(parsed, 0, "run-id"),
|
|
1579
|
-
status,
|
|
1580
|
-
verdict: optionalOption(parsed, "verdict"),
|
|
1581
|
-
nextAction: optionalOption(parsed, "next-action"),
|
|
1582
|
-
manualOverrideReason: requiredOption(parsed, "reason")
|
|
1583
|
-
});
|
|
2772
|
+
return completeFlowRun(context, runOverrideInput(parsed));
|
|
1584
2773
|
}
|
|
1585
2774
|
throw new AppError("usage", `Unknown run command: ${command ?? "<empty>"}`, 2);
|
|
1586
2775
|
}
|
|
1587
|
-
|
|
2776
|
+
function stageAttachmentInput(parsed) {
|
|
2777
|
+
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") };
|
|
2778
|
+
}
|
|
2779
|
+
function runOverrideInput(parsed) {
|
|
2780
|
+
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") };
|
|
2781
|
+
}
|
|
2782
|
+
function stageCompletionInput(parsed) {
|
|
2783
|
+
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") };
|
|
2784
|
+
}
|
|
2785
|
+
function runtimeServiceInput(parsed) {
|
|
2786
|
+
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 };
|
|
2787
|
+
}
|
|
2788
|
+
function runtimeScopeResumeInput(parsed) {
|
|
2789
|
+
return { scopeId: requiredOption(parsed, "scope-id"), requestId: requiredOption(parsed, "request-id"), generation: Number(requiredOption(parsed, "generation")), captureKey: requiredOption(parsed, "capture-key") };
|
|
2790
|
+
}
|
|
2791
|
+
async function dispatchRuntime(context, command, parsed, progress, prepared = {}) {
|
|
2792
|
+
if (command === "scope") {
|
|
2793
|
+
if (parsed.positional.length === 1 && parsed.positional[0] === "reconcile") {
|
|
2794
|
+
if ([...parsed.options.keys()].some(key => !["scope-id", "request-id", "generation"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
2795
|
+
throw new AppError("usage", "Use runtime scope reconcile --scope-id ID --generation N --request-id ID", 2);
|
|
2796
|
+
return { ok: true, ...await reconcileRuntimeScopeControl(context, requiredOption(parsed, "scope-id"), Number(requiredOption(parsed, "generation")), requiredOption(parsed, "request-id")) };
|
|
2797
|
+
}
|
|
2798
|
+
if (parsed.positional.length === 1 && parsed.positional[0] === "resume") {
|
|
2799
|
+
if ([...parsed.options.keys()].some(key => !["scope-id", "request-id", "generation", "capture-key"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
2800
|
+
throw new AppError("usage", "Use runtime scope resume --scope-id ID --request-id ID --generation N --capture-key SHA256", 2);
|
|
2801
|
+
return { ok: true, ...await launchRuntimeScopeResume(context, runtimeScopeResumeInput(parsed)) };
|
|
2802
|
+
}
|
|
2803
|
+
if (parsed.positional.length === 1 && parsed.positional[0] === "control") {
|
|
2804
|
+
if ([...parsed.options.keys()].some(key => !["scope-id", "request-id", "mode", "manifest-json"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
2805
|
+
throw new AppError("usage", "Use runtime scope control --scope-id ID --request-id ID --mode pause|stop [--manifest-json JSON]", 2);
|
|
2806
|
+
const mode = requiredOption(parsed, "mode");
|
|
2807
|
+
if (mode !== "pause" && mode !== "stop")
|
|
2808
|
+
throw new AppError("usage", "Scope control mode must be pause or stop", 2);
|
|
2809
|
+
return { ok: true, ...await launchRuntimeScopeControl(context, requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id"), mode, undefined, undefined, undefined, prepared.runtimeManifest) };
|
|
2810
|
+
}
|
|
2811
|
+
if (parsed.positional.length === 1 && parsed.positional[0] === "serve") {
|
|
2812
|
+
if ([...parsed.options.keys()].some(key => !["scope-id", "generation"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
2813
|
+
throw new AppError("usage", "Invalid internal scope worker arguments", 2);
|
|
2814
|
+
return { ok: true, ...await serveRuntimeScopeControl(context, requiredOption(parsed, "scope-id"), Number(requiredOption(parsed, "generation"))) };
|
|
2815
|
+
}
|
|
2816
|
+
if (parsed.positional.length === 1 && parsed.positional[0] === "stop") {
|
|
2817
|
+
if ([...parsed.options.keys()].some(key => !["scope-id", "request-id"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
2818
|
+
throw new AppError("usage", "Usage: dd-flow runtime scope stop --scope-id EVAL_ID --request-id ID", 2);
|
|
2819
|
+
return { ok: true, ...await stopRuntimeScope(context, requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id")) };
|
|
2820
|
+
}
|
|
2821
|
+
if (parsed.positional.length === 1 && parsed.positional[0] === "status") {
|
|
2822
|
+
if ([...parsed.options.keys()].some(key => !["scope-id", "scope-ids-json"].includes(key)) || parsed.options.size !== 1 || [...parsed.options.values()].some(values => values.length !== 1))
|
|
2823
|
+
throw new AppError("usage", "Use exactly one of --scope-id or --scope-ids-json", 2);
|
|
2824
|
+
const ids = prepareRuntimeScopeStatus(optionalOption(parsed, "scope-id"), optionalOption(parsed, "scope-ids-json"));
|
|
2825
|
+
if (parsed.options.has("scope-ids-json")) {
|
|
2826
|
+
return { ok: true, scopes: ids.map(id => runtimeBudgetStatus(context, id)) };
|
|
2827
|
+
}
|
|
2828
|
+
return { ok: true, ...runtimeBudgetStatus(context, requiredOption(parsed, "scope-id")) };
|
|
2829
|
+
}
|
|
2830
|
+
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))
|
|
2831
|
+
throw new AppError("usage", "Usage: dd-flow runtime scope fence --scope-id EVAL_ID --request-id ID", 2);
|
|
2832
|
+
return { ok: true, ...fenceRuntimeBudget(context, requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id")) };
|
|
2833
|
+
}
|
|
1588
2834
|
if (command !== "process")
|
|
1589
|
-
throw new AppError("usage", "Usage: dd-flow runtime process
|
|
2835
|
+
throw new AppError("usage", "Usage: dd-flow runtime <scope|process>; see dd-flow runtime scope --help", 2);
|
|
1590
2836
|
const action = requiredPosition(parsed, 0, "runtime process action");
|
|
1591
2837
|
if (action === "start")
|
|
1592
|
-
return await startRuntimeService(context, {
|
|
1593
|
-
|
|
1594
|
-
return { ok: true, process: await stopManagedProcess(context, { id: requiredOption(parsed, "id"), leaseToken: requiredOption(parsed, "lease-token"), ...(optionalPositiveNumber(parsed, "grace-ms", true) ? { graceMs: optionalPositiveNumber(parsed, "grace-ms", true) } : {}) }) };
|
|
1595
|
-
if (action === "register") {
|
|
1596
|
-
const leaseMs = optionalPositiveNumber(parsed, "lease-ms", true);
|
|
1597
|
-
const process = registerManagedProcess(context, {
|
|
1598
|
-
kind: requiredOption(parsed, "kind"), ownerId: requiredOption(parsed, "owner"),
|
|
1599
|
-
ownerPid: optionalPositiveNumber(parsed, "owner-pid", true),
|
|
1600
|
-
projectId: optionalOption(parsed, "project-id") ?? null, runId: optionalOption(parsed, "run") ?? null,
|
|
1601
|
-
workId: optionalOption(parsed, "work") ?? null, checkId: optionalOption(parsed, "check") ?? null,
|
|
1602
|
-
operationId: optionalOption(parsed, "operation") ?? null, stdoutPath: optionalOption(parsed, "stdout") ?? null,
|
|
1603
|
-
stderrPath: optionalOption(parsed, "stderr") ?? null, ...(leaseMs ? { leaseMs } : {})
|
|
1604
|
-
});
|
|
1605
|
-
return { ok: true, process };
|
|
1606
|
-
}
|
|
1607
|
-
if (action === "confirm") {
|
|
1608
|
-
const leaseMs = optionalPositiveNumber(parsed, "lease-ms", true);
|
|
1609
|
-
const processGroupId = optionalPositiveNumber(parsed, "process-group-id", true);
|
|
1610
|
-
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 } : {}) }) };
|
|
1611
|
-
}
|
|
1612
|
-
if (action === "heartbeat") {
|
|
1613
|
-
const leaseMs = optionalPositiveNumber(parsed, "lease-ms", true);
|
|
1614
|
-
return { ok: heartbeatManagedProcess(context, { id: requiredOption(parsed, "id"), leaseToken: requiredOption(parsed, "lease-token"), ...(leaseMs ? { leaseMs } : {}) }) };
|
|
1615
|
-
}
|
|
1616
|
-
if (action === "finish") {
|
|
1617
|
-
const state = requiredOption(parsed, "state");
|
|
1618
|
-
if (state !== "stopped" && state !== "failed")
|
|
1619
|
-
throw new AppError("validation", "--state must be stopped or failed", 2);
|
|
1620
|
-
return { ok: finishManagedProcess(context, { id: requiredOption(parsed, "id"), leaseToken: requiredOption(parsed, "lease-token"), state: state, reason: optionalOption(parsed, "reason") ?? null }) };
|
|
1621
|
-
}
|
|
1622
|
-
if (action === "status")
|
|
1623
|
-
return { ok: true, processes: managedProcessStatus(context) };
|
|
1624
|
-
if (action === "reconcile")
|
|
1625
|
-
return { ok: true, processes: await reconcileExpiredManagedProcesses(context, requiredOption(parsed, "owner"), optionalPositiveNumber(parsed, "grace-ms", true)) };
|
|
1626
|
-
throw new AppError("usage", "Usage: dd-flow runtime process <start|stop|register|confirm|heartbeat|finish|status|reconcile>", 2);
|
|
2838
|
+
return await startRuntimeService(context, { ...runtimeServiceInput(parsed), progress });
|
|
2839
|
+
return await dispatchRuntimeProcess(context, action, parsed, prepared);
|
|
1627
2840
|
}
|
|
1628
2841
|
function dispatchStat(context, command, parsed) {
|
|
1629
2842
|
if (command === "usage") {
|
|
@@ -1657,7 +2870,7 @@ function dispatchCmux(context, parsed) {
|
|
|
1657
2870
|
}
|
|
1658
2871
|
throw new AppError("usage", `Unknown integration cmux action: ${action}`, 2);
|
|
1659
2872
|
}
|
|
1660
|
-
function dispatchDashboard(context, command, parsed) {
|
|
2873
|
+
async function dispatchDashboard(context, command, parsed) {
|
|
1661
2874
|
const action = dashboardAction(command);
|
|
1662
2875
|
if (command === "data") {
|
|
1663
2876
|
const target = resolveDashboardTarget(context, {
|
|
@@ -1692,7 +2905,7 @@ function dispatchDashboard(context, command, parsed) {
|
|
|
1692
2905
|
projectRoot: optionalOption(parsed, "project-root"),
|
|
1693
2906
|
protocol: optionalOption(parsed, "protocol")
|
|
1694
2907
|
});
|
|
1695
|
-
return withDashboardGuidance(action, target, openDashboardForResolvedTarget(context, target, parsed));
|
|
2908
|
+
return withDashboardGuidance(action, target, await openDashboardForResolvedTarget(context, target, parsed));
|
|
1696
2909
|
}
|
|
1697
2910
|
if (command === "refresh") {
|
|
1698
2911
|
const open = optionalOption(parsed, "open");
|
|
@@ -1706,7 +2919,7 @@ function dispatchDashboard(context, command, parsed) {
|
|
|
1706
2919
|
protocol: optionalOption(parsed, "protocol"),
|
|
1707
2920
|
all: hasOption(parsed, "all")
|
|
1708
2921
|
});
|
|
1709
|
-
return withDashboardGuidance(action, target, refreshDashboardForResolvedTarget(context, target, parsed, open));
|
|
2922
|
+
return withDashboardGuidance(action, target, await refreshDashboardForResolvedTarget(context, target, parsed, open));
|
|
1710
2923
|
}
|
|
1711
2924
|
if (command === "refresh-global") {
|
|
1712
2925
|
const target = resolveDashboardTarget(context, { action: "refresh", global: true });
|
|
@@ -1810,7 +3023,9 @@ function dispatchSchema(context, command, parsed) {
|
|
|
1810
3023
|
// searching project directories: run_id is a structural field in the
|
|
1811
3024
|
// submitted artifact and the database owns its materialized run_root.
|
|
1812
3025
|
const explicitRunId = optionalOption(parsed, "run");
|
|
1813
|
-
const
|
|
3026
|
+
const data = prepareJsonFile(file, "schema input").value;
|
|
3027
|
+
const declared = data && typeof data === "object" && "run_id" in data ? data.run_id : undefined;
|
|
3028
|
+
const declaredRunId = explicitRunId ?? (typeof declared === "string" && declared.trim() ? declared : undefined);
|
|
1814
3029
|
const fileRunRoot = path.basename(file) === "run.json" ? path.dirname(path.resolve(file)) : undefined;
|
|
1815
3030
|
const bound = projectRoot
|
|
1816
3031
|
? 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 ?? ""])
|
|
@@ -1818,6 +3033,7 @@ function dispatchSchema(context, command, parsed) {
|
|
|
1818
3033
|
return validateSchema({
|
|
1819
3034
|
schemaName: requiredOption(parsed, "schema"),
|
|
1820
3035
|
file,
|
|
3036
|
+
data,
|
|
1821
3037
|
...(schemaDir ? { schemaDir } : {}),
|
|
1822
3038
|
...(projectRoot ? { projectRoot } : {}),
|
|
1823
3039
|
...(declaredRunId || bound?.id ? { runId: declaredRunId ?? bound.id } : {}),
|
|
@@ -1827,14 +3043,11 @@ function dispatchSchema(context, command, parsed) {
|
|
|
1827
3043
|
}
|
|
1828
3044
|
throw new AppError("usage", `Unknown schema command: ${command ?? "<empty>"}`, 2);
|
|
1829
3045
|
}
|
|
1830
|
-
function
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
}
|
|
1835
|
-
catch {
|
|
1836
|
-
return undefined;
|
|
1837
|
-
}
|
|
3046
|
+
function laneWorkspaceInput(parsed) {
|
|
3047
|
+
return { projectRoot: requiredOption(parsed, "project-root"), lane: requiredOption(parsed, "lane"), workspacePath: requiredOption(parsed, "path"), branch: optionalOption(parsed, "branch") };
|
|
3048
|
+
}
|
|
3049
|
+
function laneWaiterCancelInput(parsed) {
|
|
3050
|
+
return { projectRoot: requiredOption(parsed, "project-root"), lane: requiredOption(parsed, "lane"), workerId: requiredOption(parsed, "worker-id"), reason: requiredOption(parsed, "reason") };
|
|
1838
3051
|
}
|
|
1839
3052
|
async function dispatchLane(context, command, parsed) {
|
|
1840
3053
|
if (command === "status") {
|
|
@@ -1846,24 +3059,14 @@ async function dispatchLane(context, command, parsed) {
|
|
|
1846
3059
|
if (command === "waiter") {
|
|
1847
3060
|
const action = requiredPosition(parsed, 0, "lane waiter action");
|
|
1848
3061
|
if (action === "cancel") {
|
|
1849
|
-
return cancelLaneWaiter(context,
|
|
1850
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1851
|
-
lane: requiredOption(parsed, "lane"),
|
|
1852
|
-
workerId: requiredOption(parsed, "worker-id"),
|
|
1853
|
-
reason: requiredOption(parsed, "reason")
|
|
1854
|
-
});
|
|
3062
|
+
return cancelLaneWaiter(context, laneWaiterCancelInput(parsed));
|
|
1855
3063
|
}
|
|
1856
3064
|
throw new AppError("usage", `Unknown lane waiter action: ${action}`, 2);
|
|
1857
3065
|
}
|
|
1858
3066
|
if (command === "workspace") {
|
|
1859
3067
|
const action = requiredPosition(parsed, 0, "lane workspace action");
|
|
1860
3068
|
if (action === "set") {
|
|
1861
|
-
return setLaneWorkspace(context,
|
|
1862
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1863
|
-
lane: requiredOption(parsed, "lane"),
|
|
1864
|
-
workspacePath: requiredOption(parsed, "path"),
|
|
1865
|
-
branch: optionalOption(parsed, "branch")
|
|
1866
|
-
});
|
|
3069
|
+
return setLaneWorkspace(context, laneWorkspaceInput(parsed));
|
|
1867
3070
|
}
|
|
1868
3071
|
if (action === "check") {
|
|
1869
3072
|
return checkLaneWorkspace(context, {
|
|
@@ -1935,7 +3138,7 @@ async function dispatchLane(context, command, parsed) {
|
|
|
1935
3138
|
}
|
|
1936
3139
|
throw new AppError("usage", `Unknown lane command: ${command ?? "<empty>"}`, 2);
|
|
1937
3140
|
}
|
|
1938
|
-
function dispatchCodexHooks(context, parsed) {
|
|
3141
|
+
function dispatchCodexHooks(context, parsed, prepared) {
|
|
1939
3142
|
const action = requiredPosition(parsed, 0, "codex hooks action");
|
|
1940
3143
|
if (action === "print") {
|
|
1941
3144
|
return printCodexHooks(context, {
|
|
@@ -1957,7 +3160,7 @@ function dispatchCodexHooks(context, parsed) {
|
|
|
1957
3160
|
target: optionalOption(parsed, "target"),
|
|
1958
3161
|
profile: optionalOption(parsed, "profile"),
|
|
1959
3162
|
yes: hasOption(parsed, "yes")
|
|
1960
|
-
});
|
|
3163
|
+
}, prepared.codexHooks);
|
|
1961
3164
|
}
|
|
1962
3165
|
if (action === "remove") {
|
|
1963
3166
|
return removeCodexHooks(context, {
|
|
@@ -1965,11 +3168,14 @@ function dispatchCodexHooks(context, parsed) {
|
|
|
1965
3168
|
target: optionalOption(parsed, "target"),
|
|
1966
3169
|
profile: optionalOption(parsed, "profile"),
|
|
1967
3170
|
yes: hasOption(parsed, "yes")
|
|
1968
|
-
});
|
|
3171
|
+
}, prepared.codexHooks);
|
|
1969
3172
|
}
|
|
1970
3173
|
throw new AppError("usage", `Unknown codex hooks action: ${action}`, 2);
|
|
1971
3174
|
}
|
|
1972
|
-
function
|
|
3175
|
+
function codexHomeInput(parsed) {
|
|
3176
|
+
return { projectRoot: requiredOption(parsed, "project-root"), profile: optionalOption(parsed, "profile"), sourceHome: optionalOption(parsed, "source-home"), targetHome: optionalOption(parsed, "target-home") };
|
|
3177
|
+
}
|
|
3178
|
+
function dispatchCodexHome(context, parsed, prepared) {
|
|
1973
3179
|
const action = requiredPosition(parsed, 0, "codex home action");
|
|
1974
3180
|
if (action === "plan") {
|
|
1975
3181
|
return planCodexHome(context, {
|
|
@@ -1980,12 +3186,7 @@ function dispatchCodexHome(context, parsed) {
|
|
|
1980
3186
|
});
|
|
1981
3187
|
}
|
|
1982
3188
|
if (action === "init") {
|
|
1983
|
-
return initCodexHome(context,
|
|
1984
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1985
|
-
profile: optionalOption(parsed, "profile"),
|
|
1986
|
-
sourceHome: optionalOption(parsed, "source-home"),
|
|
1987
|
-
targetHome: optionalOption(parsed, "target-home")
|
|
1988
|
-
});
|
|
3189
|
+
return initCodexHome(context, codexHomeInput(parsed), prepared.codexHome);
|
|
1989
3190
|
}
|
|
1990
3191
|
if (action === "status") {
|
|
1991
3192
|
return getCodexHomeStatus(context, {
|
|
@@ -2008,23 +3209,6 @@ function dispatchCodexHome(context, parsed) {
|
|
|
2008
3209
|
}
|
|
2009
3210
|
throw new AppError("usage", `Unknown codex home action: ${action}`, 2);
|
|
2010
3211
|
}
|
|
2011
|
-
function dispatchCodexHook(context, parsed, stdin) {
|
|
2012
|
-
const action = requiredPosition(parsed, 0, "codex hook action");
|
|
2013
|
-
if (action === "handle") {
|
|
2014
|
-
const isolatedHome = isolatedDdFlowHomeFromHookStdin(stdin);
|
|
2015
|
-
const hookContext = isolatedHome ? createContext({ ...context.env, DD_FLOW_HOME: isolatedHome }) : context;
|
|
2016
|
-
const result = handleCodexHook(hookContext, {
|
|
2017
|
-
event: requiredOption(parsed, "event"),
|
|
2018
|
-
projectRoot: optionalOption(parsed, "project-root"),
|
|
2019
|
-
stdin
|
|
2020
|
-
});
|
|
2021
|
-
if (result && typeof result === "object" && !Array.isArray(result) && "hookSpecificOutput" in result) {
|
|
2022
|
-
return { hookSpecificOutput: result.hookSpecificOutput };
|
|
2023
|
-
}
|
|
2024
|
-
return {};
|
|
2025
|
-
}
|
|
2026
|
-
throw new AppError("usage", `Unknown codex hook action: ${action}`, 2);
|
|
2027
|
-
}
|
|
2028
3212
|
function dispatchZcodeEvent(context, parsed, stdin) {
|
|
2029
3213
|
const action = requiredPosition(parsed, 0, "zcode event action");
|
|
2030
3214
|
if (action === "handle") {
|
|
@@ -2035,6 +3219,15 @@ function dispatchZcodeEvent(context, parsed, stdin) {
|
|
|
2035
3219
|
}
|
|
2036
3220
|
throw new AppError("usage", `Unknown zcode event action: ${action}`, 2);
|
|
2037
3221
|
}
|
|
3222
|
+
function dispatchZcodeHook(context, parsed, stdin) {
|
|
3223
|
+
if (requiredPosition(parsed, 0, "zcode hook action") !== "handle")
|
|
3224
|
+
throw new AppError("usage", "Unknown zcode hook action", 2);
|
|
3225
|
+
const result = handleZcodeHook(context, { projectRoot: requiredOption(parsed, "project-root"), stdin });
|
|
3226
|
+
if (result && typeof result === "object" && !Array.isArray(result) && "hookSpecificOutput" in result) {
|
|
3227
|
+
return { hookSpecificOutput: result.hookSpecificOutput };
|
|
3228
|
+
}
|
|
3229
|
+
return {};
|
|
3230
|
+
}
|
|
2038
3231
|
function dispatchGrokEvent(context, parsed, stdin) {
|
|
2039
3232
|
const action = requiredPosition(parsed, 0, "grok event action");
|
|
2040
3233
|
if (action === "handle") {
|
|
@@ -2058,7 +3251,7 @@ function dispatchAgyEvent(context, parsed, stdin) {
|
|
|
2058
3251
|
return handleAgyEvent(context, { projectRoot: requiredOption(parsed, "project-root"), stdin });
|
|
2059
3252
|
throw new AppError("usage", `Unknown agy event action: ${action}`, 2);
|
|
2060
3253
|
}
|
|
2061
|
-
function dispatchWorktree(context, command, parsed, scopeProjectRoot) {
|
|
3254
|
+
function dispatchWorktree(context, command, parsed, scopeProjectRoot, prepared) {
|
|
2062
3255
|
if (command === "plan") {
|
|
2063
3256
|
return planWorktree(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: scopeProjectRoot });
|
|
2064
3257
|
}
|
|
@@ -2069,13 +3262,13 @@ function dispatchWorktree(context, command, parsed, scopeProjectRoot) {
|
|
|
2069
3262
|
branch: requiredOption(parsed, "branch"),
|
|
2070
3263
|
base: requiredOption(parsed, "base"),
|
|
2071
3264
|
path: requiredOption(parsed, "path")
|
|
2072
|
-
});
|
|
3265
|
+
}, prepared.worktreeCreate);
|
|
2073
3266
|
}
|
|
2074
3267
|
if (command === "status") {
|
|
2075
3268
|
return getWorktreeStatus(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: scopeProjectRoot });
|
|
2076
3269
|
}
|
|
2077
3270
|
if (command === "bootstrap") {
|
|
2078
|
-
return bootstrapWorktree(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: scopeProjectRoot });
|
|
3271
|
+
return bootstrapWorktree(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: scopeProjectRoot }, prepared.worktreeBootstrap);
|
|
2079
3272
|
}
|
|
2080
3273
|
if (command === "close") {
|
|
2081
3274
|
return closeWorktree(context, {
|
|
@@ -2087,52 +3280,50 @@ function dispatchWorktree(context, command, parsed, scopeProjectRoot) {
|
|
|
2087
3280
|
throw new AppError("usage", `Unknown worktree command: ${command ?? "<empty>"}`, 2);
|
|
2088
3281
|
}
|
|
2089
3282
|
function dispatchPlanItem(context, parsed, projectRoot) {
|
|
3283
|
+
return updatePlanItem(context, planItemInput(parsed, projectRoot));
|
|
3284
|
+
}
|
|
3285
|
+
function planItemInput(parsed, projectRoot) {
|
|
2090
3286
|
const action = requiredPosition(parsed, 0, "plan item action");
|
|
2091
3287
|
const protocolId = requiredPosition(parsed, 1, "protocol-id");
|
|
2092
3288
|
const itemId = requiredPosition(parsed, 2, "item-id");
|
|
2093
3289
|
if (action === "start") {
|
|
2094
|
-
return
|
|
3290
|
+
return { protocolId, projectRoot, itemId, action };
|
|
2095
3291
|
}
|
|
2096
3292
|
if (action === "done") {
|
|
2097
|
-
return
|
|
3293
|
+
return {
|
|
3294
|
+
action,
|
|
2098
3295
|
protocolId,
|
|
2099
3296
|
projectRoot,
|
|
2100
3297
|
itemId,
|
|
2101
3298
|
summary: requiredOption(parsed, "summary"),
|
|
2102
3299
|
evidence: parsed.options.get("evidence") ?? []
|
|
2103
|
-
}
|
|
3300
|
+
};
|
|
2104
3301
|
}
|
|
2105
3302
|
if (action === "block") {
|
|
2106
|
-
return
|
|
3303
|
+
return {
|
|
3304
|
+
action,
|
|
2107
3305
|
protocolId,
|
|
2108
3306
|
projectRoot,
|
|
2109
3307
|
itemId,
|
|
2110
3308
|
reason: requiredOption(parsed, "reason"),
|
|
2111
3309
|
userRequired: parseBoolean(requiredOption(parsed, "user-required"), "user-required")
|
|
2112
|
-
}
|
|
3310
|
+
};
|
|
2113
3311
|
}
|
|
2114
3312
|
if (action === "skip") {
|
|
2115
|
-
return
|
|
3313
|
+
return {
|
|
3314
|
+
action,
|
|
2116
3315
|
protocolId,
|
|
2117
3316
|
projectRoot,
|
|
2118
3317
|
itemId,
|
|
2119
3318
|
reason: requiredOption(parsed, "reason")
|
|
2120
|
-
}
|
|
3319
|
+
};
|
|
2121
3320
|
}
|
|
2122
3321
|
throw new AppError("usage", `Unknown plan item action: ${action}`, 2);
|
|
2123
3322
|
}
|
|
2124
3323
|
function readOptionalJsonFile(file) {
|
|
2125
3324
|
if (!file)
|
|
2126
3325
|
return undefined;
|
|
2127
|
-
|
|
2128
|
-
throw new AppError("not_found", `JSON file does not exist: ${file}`, 1);
|
|
2129
|
-
}
|
|
2130
|
-
try {
|
|
2131
|
-
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
2132
|
-
}
|
|
2133
|
-
catch {
|
|
2134
|
-
throw new AppError("validation", `JSON file is malformed: ${file}`, 2);
|
|
2135
|
-
}
|
|
3326
|
+
return prepareJsonFile(file, "JSON input").value;
|
|
2136
3327
|
}
|
|
2137
3328
|
function parseFlowFlagOverrides(values) {
|
|
2138
3329
|
const result = {};
|
|
@@ -2160,29 +3351,17 @@ function parseArgs(args) {
|
|
|
2160
3351
|
function requiredPosition(parsed, index, label) {
|
|
2161
3352
|
const value = parsed.positional[index];
|
|
2162
3353
|
if (!value) {
|
|
2163
|
-
throw new AppError("usage", `Missing required argument: ${label}`, 2);
|
|
3354
|
+
throw new AppError("usage", `Missing required argument: ${label}`, 2, { parameter: label, reason: "missing_required_argument" });
|
|
2164
3355
|
}
|
|
2165
3356
|
return value;
|
|
2166
3357
|
}
|
|
2167
|
-
function optionalOption(parsed, key) {
|
|
2168
|
-
const values = parsed.options.get(key);
|
|
2169
|
-
const value = values?.[values.length - 1];
|
|
2170
|
-
return value === flagValue ? undefined : value;
|
|
2171
|
-
}
|
|
2172
3358
|
function allOptions(parsed, key) {
|
|
2173
3359
|
return (parsed.options.get(key) ?? []).filter((value) => value !== flagValue);
|
|
2174
3360
|
}
|
|
2175
|
-
function requiredOption(parsed, key) {
|
|
2176
|
-
const value = optionalOption(parsed, key);
|
|
2177
|
-
if (!value) {
|
|
2178
|
-
throw new AppError("usage", `Missing required option: --${key}`, 2);
|
|
2179
|
-
}
|
|
2180
|
-
return value;
|
|
2181
|
-
}
|
|
2182
3361
|
function requiredScopeProjectRoot(root, operation) {
|
|
2183
3362
|
if (root)
|
|
2184
3363
|
return root;
|
|
2185
|
-
throw new AppError("usage", `${operation} requires --project-root or a unique protocol context`, 2);
|
|
3364
|
+
throw new AppError("usage", `${operation} requires --project-root or a unique protocol context`, 2, { parameter: "project-root", reason: "missing_required_option" });
|
|
2186
3365
|
}
|
|
2187
3366
|
function requiredOptionOrScope(parsed, root, key) {
|
|
2188
3367
|
return root ?? requiredOption(parsed, key);
|
|
@@ -2201,7 +3380,7 @@ function requiredOptionAlias(parsed, keys) {
|
|
|
2201
3380
|
if (value)
|
|
2202
3381
|
return value;
|
|
2203
3382
|
}
|
|
2204
|
-
throw new AppError("usage", `Missing required option: ${keys.map((key) => `--${key}`).join(" or ")}`, 2);
|
|
3383
|
+
throw new AppError("usage", `Missing required option: ${keys.map((key) => `--${key}`).join(" or ")}`, 2, { parameter: [...keys].sort(), reason: "missing_required_option" });
|
|
2205
3384
|
}
|
|
2206
3385
|
function hasOption(parsed, key) {
|
|
2207
3386
|
return parsed.options.has(key);
|
|
@@ -2218,26 +3397,6 @@ function parseBoolean(value, key) {
|
|
|
2218
3397
|
function parseOptionalBoolean(value, key) {
|
|
2219
3398
|
return typeof value === "string" ? parseBoolean(value, key) : undefined;
|
|
2220
3399
|
}
|
|
2221
|
-
function optionalNumber(parsed, key) {
|
|
2222
|
-
const value = optionalOption(parsed, key);
|
|
2223
|
-
if (typeof value === "undefined") {
|
|
2224
|
-
return undefined;
|
|
2225
|
-
}
|
|
2226
|
-
const parsedNumber = Number(value);
|
|
2227
|
-
if (!Number.isFinite(parsedNumber)) {
|
|
2228
|
-
throw new AppError("validation", `--${key} must be a number`, 2);
|
|
2229
|
-
}
|
|
2230
|
-
return parsedNumber;
|
|
2231
|
-
}
|
|
2232
|
-
function optionalPositiveNumber(parsed, key, integer = false) {
|
|
2233
|
-
const value = optionalNumber(parsed, key);
|
|
2234
|
-
if (typeof value === "undefined")
|
|
2235
|
-
return undefined;
|
|
2236
|
-
if (value <= 0 || (integer && !Number.isInteger(value))) {
|
|
2237
|
-
throw new AppError("validation", `--${key} must be a positive${integer ? " integer" : ""} number`, 2);
|
|
2238
|
-
}
|
|
2239
|
-
return value;
|
|
2240
|
-
}
|
|
2241
3400
|
function requiredWorkerId(parsed) {
|
|
2242
3401
|
return requiredOption(parsed, "worker-id");
|
|
2243
3402
|
}
|
|
@@ -2251,11 +3410,13 @@ function parseWorktreePolicy(value) {
|
|
|
2251
3410
|
}
|
|
2252
3411
|
throw new AppError("validation", "--worktree must be keep or remove", 2);
|
|
2253
3412
|
}
|
|
2254
|
-
async function readStdin(stream) {
|
|
3413
|
+
async function readStdin(stream, maxBytes) {
|
|
2255
3414
|
if (!stream) {
|
|
2256
3415
|
return "";
|
|
2257
3416
|
}
|
|
2258
3417
|
let data = "";
|
|
3418
|
+
if (maxBytes !== undefined)
|
|
3419
|
+
return readBoundedUtf8Stream(stream, maxBytes);
|
|
2259
3420
|
stream.setEncoding("utf8");
|
|
2260
3421
|
for await (const chunk of stream) {
|
|
2261
3422
|
data += chunk;
|
|
@@ -2271,7 +3432,8 @@ function refreshDashboardsAfterMutation(context, args, result, resolvedScope) {
|
|
|
2271
3432
|
const projectRoot = projectRootForMutation(context, args, result, resolvedScope);
|
|
2272
3433
|
if (!projectRoot)
|
|
2273
3434
|
return [];
|
|
2274
|
-
|
|
3435
|
+
const runId = result && typeof result === "object" && "run_id" in result && typeof result.run_id === "string" ? result.run_id : undefined;
|
|
3436
|
+
autoRefreshDashboards(context, { projectRoot, ...(runId ? { runId } : {}) });
|
|
2275
3437
|
return [];
|
|
2276
3438
|
}
|
|
2277
3439
|
catch (error) {
|
|
@@ -2333,7 +3495,7 @@ function projectRootForMutation(context, args, result, resolvedScope) {
|
|
|
2333
3495
|
return projectRootForMergeQueueMutation(context, command, parsed);
|
|
2334
3496
|
}
|
|
2335
3497
|
if (family === "merge" && ["one-shot", "apply", "repair", "request"].includes(command ?? "")) {
|
|
2336
|
-
return requiredOption(parsed, "project-root");
|
|
3498
|
+
return resolvedScope ?? requiredOption(parsed, "project-root");
|
|
2337
3499
|
}
|
|
2338
3500
|
if (family === "merge-worker" && ["start", "stop"].includes(command ?? "")) {
|
|
2339
3501
|
return requiredOption(parsed, "project-root");
|
|
@@ -2351,7 +3513,7 @@ function projectRootForMutation(context, args, result, resolvedScope) {
|
|
|
2351
3513
|
return requiredOption(parsed, "project-root");
|
|
2352
3514
|
}
|
|
2353
3515
|
if (family === "stage" && ["start", "pause", "resume", "finish"].includes(command ?? "")) {
|
|
2354
|
-
return requiredOption(parsed, "project-root");
|
|
3516
|
+
return resolvedScope ?? requiredOption(parsed, "project-root");
|
|
2355
3517
|
}
|
|
2356
3518
|
if (family === "worktree" && ["create", "bootstrap", "close"].includes(command ?? "")) {
|
|
2357
3519
|
return protocolProjectRoot(context, requiredOption(parsed, "protocol-id"));
|
|
@@ -2365,7 +3527,7 @@ function projectRootForMutation(context, args, result, resolvedScope) {
|
|
|
2365
3527
|
if (family === "opencode" && ((command === "event" && requiredPosition(parsed, 0, "opencode event action") === "handle") || command === "usage")) {
|
|
2366
3528
|
return requiredOption(parsed, "project-root");
|
|
2367
3529
|
}
|
|
2368
|
-
if (family
|
|
3530
|
+
if (["agy", "droid"].includes(family ?? "") && ((command === "event" && requiredPosition(parsed, 0, `${family} event action`) === "handle") || command === "usage"))
|
|
2369
3531
|
return requiredOption(parsed, "project-root");
|
|
2370
3532
|
if (family === "codex" && command === "hooks") {
|
|
2371
3533
|
const action = requiredPosition(parsed, 0, "codex hooks action");
|
|
@@ -2402,14 +3564,36 @@ function projectRootForMergeQueueMutation(context, command, parsed) {
|
|
|
2402
3564
|
function protocolProjectRoot(context, protocolId) {
|
|
2403
3565
|
return requireProtocol(context, protocolId).project_root;
|
|
2404
3566
|
}
|
|
3567
|
+
function prepareCliWorkRepair(context, parsed, objective, scopeProjectRoot) {
|
|
3568
|
+
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") ?? [];
|
|
3569
|
+
if ([findings.length > 0, unresolved.length > 0, receipts.length > 0].filter(Boolean).length !== 1)
|
|
3570
|
+
throw new AppError("usage", "Repair requires exactly one source: --from-check, --from-finding or --from-unresolved", 2);
|
|
3571
|
+
const scope = { projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "work repair add"), runId: requiredOption(parsed, "run"), objective };
|
|
3572
|
+
if (findings.length) {
|
|
3573
|
+
if (origins.length || hasOption(parsed, "verification-file"))
|
|
3574
|
+
throw new AppError("usage", "Finding repair derives its origins and cannot accept --origin-work or --verification-file", 2);
|
|
3575
|
+
return prepareVnextCodeReviewRepair(context, { ...scope, findingIds: findings, checkRefsByFinding: Object.fromEntries(findings.map(finding => [finding, parsed.options.get("check-ref") ?? []])) });
|
|
3576
|
+
}
|
|
3577
|
+
if (!origins.length)
|
|
3578
|
+
throw new AppError("usage", "Repair requires at least one --origin-work", 2);
|
|
3579
|
+
if (hasOption(parsed, "check-ref"))
|
|
3580
|
+
throw new AppError("usage", "--check-ref is only supported with --from-finding", 2);
|
|
3581
|
+
if (receipts.length > 1)
|
|
3582
|
+
throw new AppError("usage", "Create one repair Work per --from-check receipt", 2);
|
|
3583
|
+
if (!unresolved.length && hasOption(parsed, "verification-file"))
|
|
3584
|
+
throw new AppError("usage", "--verification-file is only supported with --from-unresolved", 2);
|
|
3585
|
+
const input = { ...scope, originWorkIds: origins, ...(unresolved.length ? { semanticUnresolved: unresolved, verificationPath: requiredOption(parsed, "verification-file") } : { checkReceiptId: receipts[0] }) };
|
|
3586
|
+
return { input, value: prepareVnextCodeRepair(context, input) };
|
|
3587
|
+
}
|
|
3588
|
+
function workerPromptInput(parsed) {
|
|
3589
|
+
const workspaceRoot = optionalOption(parsed, "workspace-root"), taskFile = optionalOption(parsed, "task-file");
|
|
3590
|
+
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 } : {}) };
|
|
3591
|
+
}
|
|
2405
3592
|
function runnerIntake(context) {
|
|
2406
3593
|
const input = context.value.task_input?.[0];
|
|
2407
3594
|
if (!input?.path)
|
|
2408
3595
|
throw new AppError("validation", "Runner SPECIFY context requires one ordered task_input file", 2);
|
|
2409
|
-
|
|
2410
|
-
if (!fs.existsSync(file) || !fs.statSync(file).isFile())
|
|
2411
|
-
throw new AppError("not_found", "Runner SPECIFY task input is missing", 2, { task_input: file });
|
|
2412
|
-
return fs.readFileSync(file, "utf8");
|
|
3596
|
+
return prepareTextFile(input.path, "Runner SPECIFY task input").text;
|
|
2413
3597
|
}
|
|
2414
3598
|
/** Stage services install context before their lifecycle transition. Keeping
|
|
2415
3599
|
* this check here makes an incomplete service migration fail closed instead of
|