@deksden-com/dd-flow-cli 0.9.0-beta.16 → 0.9.0-beta.160
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 +959 -0
- package/README.md +139 -10
- package/dist/build-info.json +8 -8
- package/dist/cli/argument-values.js +38 -0
- package/dist/cli/command-inputs.js +417 -0
- package/dist/cli/command-metadata.js +303 -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/delivery.js +108 -0
- package/dist/cli/diagnostic-invocation.js +62 -0
- package/dist/cli/diagnostic.js +131 -0
- package/dist/cli/error-output.js +62 -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 +521 -19
- package/dist/cli/hook-ingress.js +89 -0
- package/dist/cli/input-preparation.js +193 -0
- package/dist/cli/interactive-invocation.js +33 -0
- package/dist/cli/interactive.js +114 -0
- package/dist/cli/memory-fix-invocation.js +52 -0
- package/dist/cli/memory-fix.js +100 -0
- package/dist/cli/memory-flow-admission.js +54 -0
- package/dist/cli/memory-init-invocation.js +85 -0
- package/dist/cli/memory-init.js +250 -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 +94 -0
- package/dist/cli/native-hook-preparation.js +82 -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 +2020 -555
- 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 +103 -2
- package/dist/domain/delivery-flows.js +8 -0
- package/dist/domain/diagnostic-aspects.js +103 -0
- package/dist/domain/diagnostic-flows.js +8 -0
- package/dist/domain/distill-flow.js +20 -0
- package/dist/domain/interactive-flow.js +1 -0
- package/dist/domain/memory-fix-flow.js +2 -0
- package/dist/domain/review-fix-flow.js +2 -0
- package/dist/domain/source-snapshot-flows.js +4 -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 +71 -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 +50 -0
- package/dist/harness-runtime/bin/dd-opencode.mjs +22 -0
- package/dist/harness-runtime/bin/dd-zcode.mjs +95 -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 +87 -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 +225 -0
- package/dist/harness-runtime/lib/agy-native-inventory.mjs +19 -0
- package/dist/harness-runtime/lib/agy-native_pb.js +117 -0
- package/dist/harness-runtime/lib/agy-terminal-envelope.mjs +74 -0
- package/dist/harness-runtime/lib/agy-transcript-stream.mjs +54 -0
- package/dist/harness-runtime/lib/daemon-observation.d.mts +3 -0
- package/dist/harness-runtime/lib/daemon-observation.mjs +171 -0
- package/dist/harness-runtime/lib/daemon-operations.d.mts +2 -0
- package/dist/harness-runtime/lib/daemon-operations.mjs +512 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.d.mts +7 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +1362 -0
- package/dist/harness-runtime/lib/dd-agy.mjs +105 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.mjs +271 -0
- package/dist/harness-runtime/lib/dd-codex.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex.mjs +986 -0
- package/dist/harness-runtime/lib/dd-droid-daemon.mjs +155 -0
- package/dist/harness-runtime/lib/dd-droid.mjs +739 -0
- package/dist/harness-runtime/lib/dd-grok-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +667 -0
- package/dist/harness-runtime/lib/dd-grok.mjs +182 -0
- package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +772 -0
- package/dist/harness-runtime/lib/dd-opencode.mjs +411 -0
- package/dist/harness-runtime/lib/dd-zcode-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +845 -0
- package/dist/harness-runtime/lib/dd-zcode.mjs +1239 -0
- package/dist/harness-runtime/lib/delegation-instructions.d.mts +16 -0
- package/dist/harness-runtime/lib/delegation-instructions.mjs +179 -0
- package/dist/harness-runtime/lib/delivery-command.mjs +84 -0
- package/dist/harness-runtime/lib/dispatch-fence.mjs +14 -0
- package/dist/harness-runtime/lib/driver-recovery.mjs +121 -0
- package/dist/harness-runtime/lib/droid-observation.mjs +70 -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 +11 -0
- package/dist/harness-runtime/lib/lifecycle-contract.mjs +20 -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 +113 -0
- package/dist/harness-runtime/lib/native-children.d.mts +34 -0
- package/dist/harness-runtime/lib/native-children.mjs +265 -0
- package/dist/harness-runtime/lib/native-hook-command.d.mts +8 -0
- package/dist/harness-runtime/lib/native-hook-command.mjs +306 -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 +38 -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 +112 -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 +140 -0
- package/dist/harness-runtime/lib/operation-progress.d.mts +7 -0
- package/dist/harness-runtime/lib/operation-progress.mjs +118 -0
- package/dist/harness-runtime/lib/process-json.d.mts +5 -0
- package/dist/harness-runtime/lib/process-json.mjs +200 -0
- package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
- package/dist/harness-runtime/lib/provider-overload-policy.d.mts +23 -0
- package/dist/harness-runtime/lib/provider-overload-policy.mjs +242 -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 +238 -0
- package/dist/harness-runtime/lib/zcode-native-hook-ownership.mjs +109 -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/code-work-result.schema.json +2 -1
- package/dist/schemas/delivery-stage-result.schema.json +286 -0
- 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/distill-stage-result.schema.json +667 -0
- package/dist/schemas/distill-work-result.schema.json +428 -0
- package/dist/schemas/execution-bundle.schema.json +215 -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/interactive-stage-result.schema.json +47 -0
- package/dist/schemas/interactive-work-result.schema.json +63 -0
- package/dist/schemas/mb-fix-stage-result.schema.json +93 -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 +2 -2
- package/dist/schemas/release-impact.schema.json +3 -3
- package/dist/schemas/release-manifest.schema.json +291 -0
- package/dist/schemas/review-fix-stage-result.schema.json +210 -0
- package/dist/schemas/run-control-receipt.schema.json +119 -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 +3 -3
- package/dist/services/accepted-intent.js +139 -0
- package/dist/services/assigned-lifecycle.js +382 -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 +344 -0
- package/dist/services/check-runner-proof.js +373 -0
- package/dist/services/cleanup.js +133 -40
- package/dist/services/cli-operation-classifier.js +32 -64
- package/dist/services/cli-upgrade-allowlist.js +27 -0
- package/dist/services/code-checks.js +1426 -121
- package/dist/services/codex-capacity.js +367 -0
- package/dist/services/codex-hook-delivery.js +41 -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 +25 -0
- package/dist/services/controller-child-capacity.js +71 -0
- package/dist/services/controller-child-observer.js +121 -0
- package/dist/services/controller-child-repair.js +117 -0
- package/dist/services/controller-execution.js +97 -0
- package/dist/services/controller-fanout.js +277 -0
- package/dist/services/controller-memory-task.js +45 -0
- package/dist/services/controller-memory-worker.js +51 -0
- package/dist/services/controller-result-authority.js +127 -0
- package/dist/services/controller-work-failure.js +198 -0
- package/dist/services/dashboard.js +145 -58
- package/dist/services/delivery-access.js +123 -0
- package/dist/services/delivery-approval.js +68 -0
- package/dist/services/delivery-artifact.js +86 -0
- package/dist/services/delivery-checks.js +178 -0
- package/dist/services/delivery-execution.js +181 -0
- package/dist/services/delivery-intents.js +212 -0
- package/dist/services/delivery-operation.js +81 -0
- package/dist/services/delivery-policy.js +91 -0
- package/dist/services/delivery-procedures.js +43 -0
- package/dist/services/delivery-request.js +99 -0
- package/dist/services/delivery-result.js +144 -0
- package/dist/services/delivery-stage-commands.js +26 -0
- package/dist/services/delivery-stage-entry.js +56 -0
- package/dist/services/delivery-stage-handlers.js +135 -0
- package/dist/services/delivery-stage-results.js +94 -0
- package/dist/services/descriptor-coordinator-session.js +37 -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 +495 -0
- package/dist/services/descriptor-stage-coordinator.js +318 -0
- package/dist/services/descriptor-stage-settlement.js +268 -0
- package/dist/services/descriptor-stage-start.js +175 -0
- package/dist/services/diagnostic-evidence.js +73 -0
- package/dist/services/diagnostic-repair-intake.js +123 -0
- package/dist/services/diagnostic-repair-source-intake.js +102 -0
- package/dist/services/diagnostic-report-publication.js +72 -0
- package/dist/services/diagnostic-report.js +97 -0
- package/dist/services/diagnostic-run-bundle.js +113 -0
- package/dist/services/diagnostic-snapshot.js +159 -0
- package/dist/services/diagnostic-stage-handlers.js +109 -0
- package/dist/services/diagnostic-stage-results.js +158 -0
- package/dist/services/diagnostic-start.js +134 -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/distill-document-publication.js +280 -0
- package/dist/services/distill-promotion.js +197 -0
- package/dist/services/distill-publication-custody.js +162 -0
- package/dist/services/distill-stage-handlers.js +249 -0
- package/dist/services/distill-stage-results.js +105 -0
- package/dist/services/distill-user-inputs.js +4 -0
- package/dist/services/distill-work-batch.js +42 -0
- package/dist/services/distill-work-collection.js +74 -0
- package/dist/services/distill-work-registration.js +93 -0
- package/dist/services/distill-work-results.js +98 -0
- package/dist/services/engines.js +354 -163
- package/dist/services/eval-snapshots.js +1326 -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 +316 -0
- package/dist/services/flow-catalog.js +54 -0
- package/dist/services/flow-descriptors.js +82 -0
- package/dist/services/frozen-engine.js +404 -0
- package/dist/services/generic-execution.js +165 -0
- package/dist/services/harness-adapter.js +342 -35
- 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 +813 -327
- package/dist/services/hybrid-contracts.js +479 -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 +261 -0
- package/dist/services/interactive-attached-session.js +215 -0
- package/dist/services/interactive-baseline-custody.js +54 -0
- package/dist/services/interactive-before-versions.js +148 -0
- package/dist/services/interactive-bundle-data.js +85 -0
- package/dist/services/interactive-checks.js +179 -0
- package/dist/services/interactive-execution.js +33 -0
- package/dist/services/interactive-finish-integration.js +236 -0
- package/dist/services/interactive-git-baseline.js +230 -0
- package/dist/services/interactive-git-dispatch.js +278 -0
- package/dist/services/interactive-input-delivery.js +82 -0
- package/dist/services/interactive-mutation-scope.js +231 -0
- package/dist/services/interactive-request.js +287 -0
- package/dist/services/interactive-run-bundle.js +109 -0
- package/dist/services/interactive-stage-entry.js +41 -0
- package/dist/services/interactive-stage-handlers.js +289 -0
- package/dist/services/interactive-stage-observation.js +136 -0
- package/dist/services/interactive-stage-results.js +111 -0
- package/dist/services/interactive-user-inputs.js +142 -0
- package/dist/services/interactive-work-batch.js +43 -0
- package/dist/services/interactive-work-collection.js +119 -0
- package/dist/services/interactive-work-completion.js +180 -0
- package/dist/services/interactive-work-context.js +249 -0
- package/dist/services/interactive-work-history.js +112 -0
- package/dist/services/interactive-work-registration.js +124 -0
- package/dist/services/interactive-work-results.js +115 -0
- package/dist/services/interactive-work-start.js +155 -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 +2549 -0
- package/dist/services/managed-daemon-binding.js +129 -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 +380 -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 +491 -0
- package/dist/services/memory-finish-readiness.js +169 -0
- package/dist/services/memory-fix-finish-integration.js +129 -0
- package/dist/services/memory-fix-git-dispatch.js +139 -0
- package/dist/services/memory-fix-intake-custody.js +11 -0
- package/dist/services/memory-fix-intake-settlement.js +127 -0
- package/dist/services/memory-fix-publication.js +196 -0
- package/dist/services/memory-fix-repair-admission.js +116 -0
- package/dist/services/memory-fix-request.js +141 -0
- package/dist/services/memory-fix-run-bundle.js +83 -0
- package/dist/services/memory-fix-scope.js +78 -0
- package/dist/services/memory-fix-sources.js +85 -0
- package/dist/services/memory-fix-stage-entry.js +24 -0
- package/dist/services/memory-fix-stage-handlers.js +282 -0
- package/dist/services/memory-fix-stage-results.js +106 -0
- package/dist/services/memory-fix-workspace.js +127 -0
- package/dist/services/memory-flow-user-inputs.js +88 -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 +181 -0
- package/dist/services/memory-init-controller-start.js +119 -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 +225 -0
- package/dist/services/memory-lint-config.js +194 -0
- package/dist/services/memory-native-review-copy.js +127 -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 +489 -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-flow-source.js +195 -0
- package/dist/services/project-git-integration.js +92 -0
- 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 +463 -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 +29 -0
- package/dist/services/repair-receipt.js +13 -0
- package/dist/services/retained-native-hook.js +274 -0
- package/dist/services/review-copy.js +165 -0
- package/dist/services/review-deferral.js +23 -0
- package/dist/services/review-fix-handoff.js +82 -0
- package/dist/services/review-fix-intake-custody.js +17 -0
- package/dist/services/review-fix-stage-entry.js +43 -0
- package/dist/services/review-fix-stage-handlers.js +176 -0
- package/dist/services/review-fix-stage-results.js +86 -0
- package/dist/services/run-control-receipt.js +56 -0
- package/dist/services/run-control-worker.js +469 -0
- package/dist/services/run-control.js +1109 -0
- package/dist/services/run-controller-adapter.js +434 -0
- package/dist/services/run-controller-capture.js +186 -0
- package/dist/services/run-controller-process.js +226 -0
- package/dist/services/run-controller-recovery.js +335 -0
- package/dist/services/run-controller-state.js +106 -0
- package/dist/services/run-controller.js +1717 -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 +485 -0
- package/dist/services/run-start-custody.js +189 -0
- package/dist/services/run-timeline.js +59 -0
- package/dist/services/runs.js +996 -271
- 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 +37 -91
- package/dist/services/setup.js +427 -0
- package/dist/services/stage-blocker.js +17 -7
- package/dist/services/stage-context.js +240 -18
- package/dist/services/stage-handlers.js +141 -0
- package/dist/services/stage-lifecycle.js +156 -119
- package/dist/services/stage-pause.js +222 -94
- package/dist/services/stage-report-renderer.js +230 -6
- package/dist/services/stage-work-graph.js +52 -0
- package/dist/services/technical-notes.js +31 -0
- package/dist/services/tooling.js +111 -44
- 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 +302 -91
- package/dist/services/vnext-code.js +325 -251
- package/dist/services/vnext-contracts.js +10 -1
- package/dist/services/vnext-execution-profile.js +80 -5
- package/dist/services/vnext-fanout.js +399 -20
- package/dist/services/vnext-merge.js +336 -114
- package/dist/services/vnext-plan-review.js +288 -133
- package/dist/services/vnext-plan.js +230 -106
- package/dist/services/vnext-protocolize.js +534 -96
- package/dist/services/vnext-specify.js +131 -84
- package/dist/services/vnext-workspace-policy.js +7 -2
- package/dist/services/work-registry.js +1950 -215
- package/dist/services/work-retry-context.js +55 -0
- package/dist/services/workspace-bootstrap.js +53 -15
- package/dist/services/workspace-files.js +85 -0
- package/dist/services/worktrees.js +108 -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 +931 -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/dist/verification-components.json +384 -0
- package/package.json +52 -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/tools/verification-jobs.mjs +342 -0
package/dist/cli/run-cli.js
CHANGED
|
@@ -1,57 +1,145 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
+
import { ensureManagedWorktrunk } from "../services/tooling.js";
|
|
3
|
+
import { prepareResponseDestination, writeResponseFile } from "./response-file.js";
|
|
4
|
+
import { preparePromptDelivery, publishPromptDelivery } from "../services/prompt-delivery.js";
|
|
5
|
+
import { requiredOption, optionalOption, optionalNumber, optionalPositiveNumber } from "./argument-values.js";
|
|
6
|
+
import { resolveCommandPaths, resolveCommandProjectRoot } from "../services/command-context.js";
|
|
7
|
+
import { assertAuxiliaryCommandScope, cliManagedInvocationScope, manualRecoveryInvocationScope, assertManualRecoveryInvocationTarget, currentAssignedLifecycle } from "../services/lifecycle-invocations.js";
|
|
2
8
|
import path from "node:path";
|
|
9
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
10
|
+
import { quote } from "shell-quote";
|
|
11
|
+
import { observeLifecycleCommand, observedLifecycleNativeEvent } from "../services/lifecycle-invocations.js";
|
|
12
|
+
import { verifyCodexHookDelivery } from "../services/codex-hook-delivery.js";
|
|
13
|
+
import { assertLifecycleInvocationCurrent, awaitLifecycleInvocation, bindLifecyclePublishedPacket, expandLifecycleInvocationArgs, issueLifecycleInvocation, prepareLifecycleIssuance, lifecycleInvocationScope, observedLifecycleDiagnostic, observedLifecycleInvocation, resolveObservedLifecycleArgs, retainObservedLifecycleSemantics, settleLifecycleInvocation, settleLifecyclePreparationRejection, settleLifecycleRejection, validateLifecyclePathAliases } from "../services/lifecycle-invocations.js";
|
|
14
|
+
import { readAcceptedAssignedWork } from "../services/assigned-lifecycle.js";
|
|
3
15
|
import { createContext, createRouterContext } from "../runtime/context.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
16
|
+
import { dispatchNativeHook, isNativeHookIngress } from "./native-hook-ingress.js";
|
|
17
|
+
import { isRuntimeMaintenance, runtimeMaintenanceDiagnostics, withMaintenanceDiagnosticScope } from "./runtime-maintenance-diagnostics.js";
|
|
18
|
+
import { migrateStoreWriter } from "../storage/writer-migration.js";
|
|
19
|
+
import { resolveHelp } from "./help.js";
|
|
20
|
+
import { renderHumanError, writePreparationError } from "./error-output.js";
|
|
21
|
+
import { runOperatorProfilesCli } from "./operator-profiles.js";
|
|
22
|
+
import { runSetupCli } from "./setup.js";
|
|
23
|
+
import { runCompletionCli } from "./completion-cli.js";
|
|
24
|
+
import { runHarnessesCli } from "./harnesses.js";
|
|
25
|
+
import { runModelsCli } from "./models.js";
|
|
26
|
+
import { parseOutputOptions } from "./output-options.js";
|
|
27
|
+
import { AppError, errorRecord, isAppError } from "../shared/errors.js";
|
|
28
|
+
import { entityReferenceVariants } from "../shared/entity-references.js";
|
|
29
|
+
import { writeJson, parseJsonObject } from "../shared/json.js";
|
|
30
|
+
import { archiveProject, prepareProjectArchive, getProjectStatus, migrateProjectIds, registerProject, resolveProject } from "../services/projects.js";
|
|
31
|
+
import { cancelProtocol, getProtocolBlockers, getProtocolStatus, getReadyProtocols, implementProtocol, readyForMerge, registerProtocol, prepareProtocolRegistration, requireProtocol, syncProtocolFromRun, prepareProtocolTransition, transitionProtocol } from "../services/protocols.js";
|
|
32
|
+
import { getPlanStatus, preparePlanItemMutation, updatePlanItem } from "../services/plans.js";
|
|
33
|
+
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
34
|
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";
|
|
35
|
+
import { cleanupApply, cleanupScan, prepareCleanupApply } from "../services/cleanup.js";
|
|
36
|
+
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";
|
|
37
|
+
import { bootstrapWorktree, closeWorktree, createWorktreeRecord, prepareWorktreeCreate, prepareWorktreeBootstrap, prepareWorktreeClose, getWorktreeStatus, planWorktree } from "../services/worktrees.js";
|
|
38
|
+
import { acquireLaneLock, cancelLaneWaiter, prepareLaneWaiterCancellation, checkLaneWorkspace, getLaneWaiters, getLaneStatus, heartbeatLaneLock, releaseLaneLock, setLaneWorkspace, prepareLaneWorkspace, prepareLaneLock, waitAcquireLaneLock, waitForLaneLock } from "../services/lanes.js";
|
|
39
|
+
import { getProjectConfigStatus, prepareProjectConfigValue, setProjectConfigValue } from "../services/config.js";
|
|
40
|
+
import { getCanonStatus, registerCanonRoot, resolveCanonRoot, prepareCanonRegistration } from "../services/canon.js";
|
|
18
41
|
import { getRuntimeStatus } from "../services/status.js";
|
|
19
42
|
import { getCliVersionReport } from "../services/build-info.js";
|
|
20
43
|
import { parseCommandArgs } from "../services/lifecycle-command.js";
|
|
21
|
-
import {
|
|
44
|
+
import { prepareError, prepareJsonFile, prepareJsonSchema, prepareJsonText, prepareTextFile, prepareFileBytes, readBoundedUtf8Stream, recordConsumedInputDigest, withPreparedInputBinding } from "./input-preparation.js";
|
|
45
|
+
import { autoRefreshDashboards, dashboardData, getCmuxStatus, openDashboard, openGlobalDashboard, openProtocolDashboard, refreshAllDashboards, prepareDashboardRefresh, prepareDashboardRender, prepareGlobalDashboardRender, prepareDashboardOptions, refreshDashboard, refreshGlobalDashboard, renderDashboard, renderGlobalDashboard } from "../services/dashboard.js";
|
|
22
46
|
import { dashboardTargetSummary, preferredDashboardCommand, relatedDashboardCommands, resolveDashboardTarget } from "../services/dashboard-targets.js";
|
|
23
|
-
import { getFlowSessionStatus, registerFlowSession, stopFlowSession, stopMergeWorker, syncFlowSessionUsage } from "../services/sessions.js";
|
|
47
|
+
import { getFlowSessionStatus, prepareFlowSessionPayload, registerFlowSession, stopFlowSession, stopMergeWorker, syncFlowSessionUsage } from "../services/sessions.js";
|
|
24
48
|
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";
|
|
49
|
+
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
50
|
import { previewNextEntityId } from "../services/ids.js";
|
|
27
51
|
import { validateSchema } from "../services/schema-validation.js";
|
|
28
52
|
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";
|
|
53
|
+
import { ingestDroidUsage, ingestAgyUsage, ingestGrokUsage, ingestOpenCodeUsage, ingestZcodeUsage } from "../services/usage.js";
|
|
54
|
+
import { applyMigration, assessMigrationImpact, assertUpgradeRunStorageReady, planMigration, prepareMigrationApply, verifyMigrationReport } from "../services/migrations.js";
|
|
55
|
+
import { PreparedUpgradeStorageReadiness } from "../services/memory-upgrade-storage-readiness.js";
|
|
31
56
|
import { publishProjectSummary } from "../services/project-summary.js";
|
|
32
57
|
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";
|
|
58
|
+
import { resolveProjectRoot, resolveDdFlowHome } from "../storage/paths.js";
|
|
59
|
+
import { doctorEngines, bindRunEngine, prepareRunEngineBind, engineInfo, installCurrentEngine, listEngines, resolveEngine, resolveExplicitProjectRoot, resolveOperationProjectRoot, routeArgsThroughEngine } from "../services/engines.js";
|
|
35
60
|
import { preflightCliCompatibility } from "../services/compatibility-preflight.js";
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import { dispatchVnextPlanReview,
|
|
40
|
-
import { addVnextCodeRepair,
|
|
41
|
-
import {
|
|
42
|
-
import { applyVnextMerge,
|
|
61
|
+
import { getVnextWorkStatus, isVnextSpecifyFlow, isVnextSpecifyRun, prepareVnextSpecifyStart } from "../services/vnext-specify.js";
|
|
62
|
+
import { isVnextProtocolizeRun, prepareProtocolizeResultPath, prepareVnextProtocolizeStart } from "../services/vnext-protocolize.js";
|
|
63
|
+
import { isVnextPlanRun, prepareVnextPlanStart, prepareVnextPlanFinish } from "../services/vnext-plan.js";
|
|
64
|
+
import { dispatchVnextPlanReview, isVnextPlanReviewRun, prepareVnextPlanReviewStart, recordVnextPlanReviewCapacity } from "../services/vnext-plan-review.js";
|
|
65
|
+
import { addVnextCodeRepair, prepareVnextCodeRepair, prepareVnextCodeStart } from "../services/vnext-code.js";
|
|
66
|
+
import { isVnextCodeReviewRun, prepareCodeReviewDecision, prepareVnextCodeReviewRepair, prepareVnextCodeReviewStart } from "../services/vnext-code-review.js";
|
|
67
|
+
import { applyVnextMerge, getVnextMergeRequest, isVnextMergeRun, prepareVnextMergeFinish, prepareVnextMergeStart, repairVnextMerge, routeVnextMergeRequest } from "../services/vnext-merge.js";
|
|
68
|
+
import { sdlcStageHandlers } from "../services/stage-handlers.js";
|
|
69
|
+
import { startMemoryStage, finishMemoryStage, startMemoryUpgradeStage, finishMemoryUpgradeStage } from "../services/memory-stage-dispatch.js";
|
|
70
|
+
import { startDiagnosticStage, finishDiagnosticStage } from "../services/diagnostic-stage-handlers.js";
|
|
71
|
+
import { startDistillStage, finishDistillStage } from "../services/distill-stage-handlers.js";
|
|
72
|
+
import { startMemoryFixStage } from "../services/memory-fix-stage-entry.js";
|
|
73
|
+
import { finishMemoryFixStage } from "../services/memory-fix-stage-handlers.js";
|
|
74
|
+
import { startReviewFixStage } from "../services/review-fix-stage-entry.js";
|
|
75
|
+
import { finishReviewFixStage } from "../services/review-fix-stage-handlers.js";
|
|
76
|
+
import { startInteractiveStage } from "../services/interactive-stage-entry.js";
|
|
77
|
+
import { isDeliveryFlow } from "../domain/delivery-flows.js";
|
|
78
|
+
import { startDeliveryStage, readDeliveryStageScope, assertDeliveryCommandOwner } from "../services/delivery-stage-entry.js";
|
|
79
|
+
import { finishDeliveryStage } from "../services/delivery-stage-handlers.js";
|
|
80
|
+
import { prepareDeliveryChecks } from "../services/delivery-checks.js";
|
|
81
|
+
import { executeDeliveryOperation } from "../services/delivery-operation.js";
|
|
82
|
+
import { observeDeliveryResult } from "../services/delivery-result.js";
|
|
83
|
+
import { prepareDeliveryProjectBinding } from "../services/delivery-policy.js";
|
|
84
|
+
import { deliveryApprovalScope } from "../services/delivery-approval.js";
|
|
85
|
+
import { issueDeliveryStageCommands } from "../services/delivery-stage-commands.js";
|
|
86
|
+
import { readDescriptorStageInput } from "../services/descriptor-stage-coordinator.js";
|
|
87
|
+
import { readAcceptedStageData } from "../services/stage-report-renderer.js";
|
|
88
|
+
import { finishInteractiveStage, registerInteractiveReviewCorrection, registerInteractiveCorrectionReview } from "../services/interactive-stage-handlers.js";
|
|
89
|
+
import { observeInteractiveOriginalGitInputs } from "../services/interactive-finish-integration.js";
|
|
90
|
+
import { readInteractiveUserInputs } from "../services/interactive-user-inputs.js";
|
|
91
|
+
import { deliverInteractiveUserInput } from "../services/interactive-input-delivery.js";
|
|
92
|
+
import { PreparedInteractiveMutationScope } from "../services/interactive-mutation-scope.js";
|
|
93
|
+
import { PreparedInteractiveChecks } from "../services/interactive-checks.js";
|
|
94
|
+
import { registerInteractiveStageGraph } from "../services/interactive-work-registration.js";
|
|
95
|
+
import { readInteractiveWorkGit } from "../services/interactive-work-start.js";
|
|
96
|
+
import { integrateInteractiveFinish } from "../services/interactive-git-dispatch.js";
|
|
97
|
+
import { readMemoryFixSourceIntake } from "../services/memory-fix-intake-custody.js";
|
|
98
|
+
import { readMemoryFixUserInputs } from "../services/diagnostic-repair-intake.js";
|
|
99
|
+
import { prepareMemoryFixScopeEvidence } from "../services/memory-fix-scope.js";
|
|
100
|
+
import { prepareMemoryFixPublication } from "../services/memory-fix-publication.js";
|
|
101
|
+
import { integrateMemoryFixFinish } from "../services/memory-fix-git-dispatch.js";
|
|
102
|
+
import { isDiagnosticFlow } from "../domain/diagnostic-flows.js";
|
|
103
|
+
import { bindStageCoordinatorWork } from "../services/work-registry.js";
|
|
104
|
+
import { registerMemoryCompositionFromStage, publishMemoryUpgradeFromStage } from "../services/memory-compose-handler.js";
|
|
105
|
+
import { publishDistillFromStage } from "../services/distill-promotion.js";
|
|
106
|
+
import { readDistillUserInputs } from "../services/distill-user-inputs.js";
|
|
107
|
+
import { hybridDigest } from "../services/hybrid-contracts.js";
|
|
108
|
+
import { runMemoryVerificationChecks } from "../services/memory-verify-handler.js";
|
|
109
|
+
import { registerMemoryReviewFromStage, collectMemoryInitialReviewFromStage } from "../services/memory-review-handler.js";
|
|
110
|
+
import { PreparedRetainedMemoryReviewAuthority } from "../services/memory-retained-review-authority.js";
|
|
111
|
+
import { PreparedRetainedMemoryReviewResults } from "../services/memory-retained-review-results.js";
|
|
112
|
+
import { registerMemoryCorrectionsFromStage, registerMemorySecondCorrectionsFromStage, runMemoryCorrectionChecksFromStage, runMemorySecondCorrectionChecksFromStage, registerCorrectedMemoryReviewFromStage, registerSecondCorrectedMemoryReviewFromStage, collectCorrectedMemoryReviewFromStage, collectSecondCorrectedMemoryReviewFromStage } from "../services/memory-review-handler.js";
|
|
113
|
+
import { integrateMemoryFinishFromStage, registerMemoryFinishEvidenceFromStage } from "../services/memory-finish-handler.js";
|
|
114
|
+
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
115
|
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 {
|
|
116
|
+
import { getVnextFanoutStatus, registeredStageFanoutDescriptor, observeVnextNativeChildren, prepareNativeObservations, reconcileVnextFanout } from "../services/vnext-fanout.js";
|
|
117
|
+
import { pauseStageForUser, resumeStageAfterUser, prepareStagePause, prepareStageResume, stagePauseCommand } from "../services/stage-pause.js";
|
|
118
|
+
import { runtimeCommand } from "../services/runtime-command.js";
|
|
119
|
+
import { managedLifecycleCommand } from "../services/lifecycle-invocations.js";
|
|
120
|
+
import { blockStageForRuntime, unblockStageAfterRuntimeRepair, prepareStageRuntimeBlock, prepareStageRuntimeUnblock } from "../services/stage-blocker.js";
|
|
47
121
|
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 {
|
|
122
|
+
import { prepareWorkerPrompt, renderWorkerPrompt } from "../services/prompts.js";
|
|
123
|
+
import { finishStage, prepareStageFinish, startStage, prepareLegacyStageStart } from "../services/stage-lifecycle.js";
|
|
124
|
+
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";
|
|
125
|
+
import { launchExternalWork } from "../services/external-work-launch.js";
|
|
126
|
+
import { createEvalBootstrapSnapshot, prepareEvalBootstrapSnapshot, createEvalRunSnapshot, prepareEvalRunSnapshotCreation, prepareVnextSpecifyRun, prepareEvalRunRestore, prepareEvalBootstrapRestore, restoreEvalBootstrapSnapshot, restoreEvalRunSnapshot } from "../services/eval-snapshots.js";
|
|
127
|
+
import { forkRunFromSnapshot, prepareRunFork } from "../services/run-fork.js";
|
|
128
|
+
import { acceptRunRecovery, beginRunRecovery, inspectRunRecovery, prepareRecoveryCapture, prepareRecoveryPayload, prepareRunRecoveryResume, recordRecoveryCapture, resumeRunRecovery, sealRunRecovery } from "../services/run-recovery.js";
|
|
129
|
+
import { loadExternalStageContext, prepareExternalStageContext } from "../services/stage-context.js";
|
|
130
|
+
import { vnextStageDirectory } from "../domain/stage-catalog.js";
|
|
131
|
+
import { prepareCommandRoute } from "./command-inputs.js";
|
|
132
|
+
import { parseLifecycleCommand } from "../services/lifecycle-command.js";
|
|
133
|
+
import { dispatchRuntimeProcess, prepareRuntimeProcess } from "./runtime-process.js";
|
|
134
|
+
import { reconcileFlowRunHealth } from "../services/run-health.js";
|
|
135
|
+
import { fenceRuntimeBudget, runtimeBudgetStatus, prepareRuntimeScopeStatus, prepareRuntimeScopeControl, prepareRuntimeScopeFence } from "../services/runtime-budget.js";
|
|
136
|
+
import { stopRuntimeScope } from "../services/runtime-scope-stop.js";
|
|
137
|
+
import { launchRuntimeScopeControl, launchRuntimeScopeResume, prepareRuntimeScopeReconciliation, prepareRuntimeScopeServing, reconcileRuntimeScopeControl, serveRuntimeScopeControl } from "../services/runtime-scope-worker.js";
|
|
138
|
+
import { prepareRuntimeScopeResumeRequest } from "../services/runtime-scope-resume.js";
|
|
139
|
+
import { launchRunController, prepareRunControllerLaunch, resumeRunController, serveRunController, runControllerStatus, answerRunController, prepareRunControllerAnswer, supplyRunControllerContext, prepareRunControllerContext } from "../services/run-controller.js";
|
|
140
|
+
import { controlLifecycleReply, runControlStatus } from "../services/run-control.js";
|
|
141
|
+
import { reconcileRunControl, requestRunControl, serveRunControl } from "../services/run-control-worker.js";
|
|
142
|
+
import { startRuntimeService, prepareRuntimeService } from "../services/runtime-service.js";
|
|
55
143
|
const defaultIo = {
|
|
56
144
|
stdout: process.stdout,
|
|
57
145
|
stderr: process.stderr,
|
|
@@ -59,9 +147,62 @@ const defaultIo = {
|
|
|
59
147
|
};
|
|
60
148
|
const flagValue = "__dd_flow_flag__";
|
|
61
149
|
export async function runCli(args, io = defaultIo, env = process.env) {
|
|
62
|
-
|
|
150
|
+
return withMaintenanceDiagnosticScope(() => runCliRequest(args, io, env));
|
|
151
|
+
}
|
|
152
|
+
async function runCliRequest(args, io, env) {
|
|
153
|
+
try {
|
|
154
|
+
const help = resolveHelp(args);
|
|
155
|
+
if (help) {
|
|
156
|
+
if (help.json)
|
|
157
|
+
writeJson(io.stdout, help.data);
|
|
158
|
+
else
|
|
159
|
+
io.stdout.write(`${help.text}\n`);
|
|
160
|
+
return 0;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
const record = errorRecord(error);
|
|
165
|
+
writePreparationError(io, args, record);
|
|
166
|
+
return record.exitCode;
|
|
167
|
+
}
|
|
168
|
+
let output;
|
|
169
|
+
try {
|
|
170
|
+
output = parseOutputOptions(args);
|
|
171
|
+
// Expand before routing/context selection, after stripping transport flags.
|
|
172
|
+
// The alias keeps the existing RUN's engine and prepared-store rules.
|
|
173
|
+
if (output.args[0] === "finish")
|
|
174
|
+
output.args = ["interactive", "finish", ...output.args.slice(1)];
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
const rejection = isAppError(error) ? error : new AppError("usage", error instanceof Error ? error.message : String(error), 2);
|
|
178
|
+
Object.assign(rejection.details, { phase: "prepare", effect: "no_effect", recoverable: true, ...rejection.details });
|
|
179
|
+
try {
|
|
180
|
+
const command = quote(["dd-flow", ...args]);
|
|
181
|
+
const analysis = parseLifecycleCommand(command);
|
|
182
|
+
const event = analysis.kind !== "none" || analysis.auxiliary
|
|
183
|
+
? observedLifecycleNativeEvent(createRouterContext(env), command, env.DD_FLOW_DAEMON_ID) : null;
|
|
184
|
+
if (event) {
|
|
185
|
+
const context = createContext({ ...env }, "hook");
|
|
186
|
+
try {
|
|
187
|
+
observeLifecycleCommand(context, command, event)({ error: rejection });
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
context.db.close?.();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
catch (persistence) {
|
|
195
|
+
rejection.details.lifecycle_diagnostic_error = String(persistence);
|
|
196
|
+
}
|
|
197
|
+
writePreparationError(io, args, errorRecord(rejection));
|
|
198
|
+
return rejection.exitCode;
|
|
199
|
+
}
|
|
63
200
|
const progress = progressForCommand(output.args);
|
|
64
201
|
let heartbeat;
|
|
202
|
+
let invocationSettlement;
|
|
203
|
+
let settleHook;
|
|
204
|
+
let diagnosticHome;
|
|
205
|
+
let maintenanceDiagnostics;
|
|
65
206
|
try {
|
|
66
207
|
if (output.args.length === 1 && output.args[0] === "--version") {
|
|
67
208
|
const payload = getCliVersionReport();
|
|
@@ -71,11 +212,72 @@ export async function runCli(args, io = defaultIo, env = process.env) {
|
|
|
71
212
|
io.stdout.write(`dd-flow ${payload.cli.version}\n`);
|
|
72
213
|
return 0;
|
|
73
214
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
215
|
+
if (output.args[0] === "completion")
|
|
216
|
+
return runCompletionCli(args, io);
|
|
217
|
+
if (output.args[0] === "setup")
|
|
218
|
+
return runSetupCli(args, env, io);
|
|
219
|
+
if (output.args[0] === "harnesses")
|
|
220
|
+
return runHarnessesCli(args, env, io);
|
|
221
|
+
if (output.args[0] === "models")
|
|
222
|
+
return runModelsCli(args, io);
|
|
223
|
+
if (output.args[0] === "profiles")
|
|
224
|
+
return runOperatorProfilesCli(args, env, io);
|
|
225
|
+
if (output.args[0] === "tool") {
|
|
226
|
+
prepareCommandRoute(output.args); // Reject unknown tools before filesystem/store effects.
|
|
227
|
+
if (output.responseFile)
|
|
228
|
+
prepareResponseDestination(output.responseFile);
|
|
229
|
+
const tool = await ensureManagedWorktrunk({ ddFlowHome: resolveDdFlowHome(env), env });
|
|
230
|
+
const payload = writeResponseFile({ ok: true, tool: "wt", ...tool, cache_root: path.dirname(tool.bin) }, output);
|
|
231
|
+
if (output.json)
|
|
232
|
+
writeJson(io.stdout, payload);
|
|
233
|
+
else
|
|
234
|
+
io.stdout.write(`${renderHumanResult(output.args, payload)}${tool.bin}\n`);
|
|
235
|
+
return 0;
|
|
236
|
+
}
|
|
237
|
+
const suppliedInvocationId = optionalOption(parseArgs(output.args.slice(2)), "invocation-id");
|
|
238
|
+
if (isNativeHookIngress(output.args)) {
|
|
239
|
+
writeJson(io.stdout, await dispatchNativeHook(output.args, await readStdin(io.stdin), env));
|
|
77
240
|
return 0;
|
|
78
241
|
}
|
|
242
|
+
if (output.args.slice(0, 3).join(" ") === "codex hook handle") {
|
|
243
|
+
const parsed = prepareCommandRoute(output.args).parsed;
|
|
244
|
+
const stdin = await readStdin(io.stdin);
|
|
245
|
+
const payload = parseJsonObject(stdin || "{}", "codex hook stdin");
|
|
246
|
+
const event = requiredOption(parsed, "event");
|
|
247
|
+
const isolatedHome = event === "PreToolUse" ? isolatedDdFlowHomeFromHookStdin(stdin) : null;
|
|
248
|
+
const hookEnv = isolatedHome ? { ...env, DD_FLOW_HOME: isolatedHome } : env;
|
|
249
|
+
const proof = await verifyCodexHookDelivery(hookEnv, payload);
|
|
250
|
+
let opened;
|
|
251
|
+
const mode = databaseModeForCommand(output.args, classifyCliOperation(output.args, hookEnv));
|
|
252
|
+
const context = {
|
|
253
|
+
env: hookEnv, ddFlowHome: resolveDdFlowHome(hookEnv), now: () => new Date().toISOString(),
|
|
254
|
+
get db() { return (opened ??= createContext(hookEnv, mode)).db; }
|
|
255
|
+
};
|
|
256
|
+
try {
|
|
257
|
+
// Retired events retain the explicit startup/migration path. PreToolUse
|
|
258
|
+
// only opens its selected prepared store when the handler needs it.
|
|
259
|
+
if (mode !== "hook")
|
|
260
|
+
opened = createContext(hookEnv, mode);
|
|
261
|
+
if (proof)
|
|
262
|
+
context.codexHookProof = proof;
|
|
263
|
+
const result = handleCodexHook(context, { event, projectRoot: optionalOption(parsed, "project-root"), stdin });
|
|
264
|
+
writeJson(io.stdout, result && typeof result === "object" && !Array.isArray(result) && "hookSpecificOutput" in result
|
|
265
|
+
? { hookSpecificOutput: result.hookSpecificOutput } : {});
|
|
266
|
+
return 0;
|
|
267
|
+
}
|
|
268
|
+
finally {
|
|
269
|
+
opened?.db.close?.();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (output.args[0] === "run" && ["drive", "control"].includes(output.args[1] ?? "")) {
|
|
273
|
+
const controlArgs = parseArgs(output.args.slice(2));
|
|
274
|
+
requiredOption(controlArgs, "run");
|
|
275
|
+
requiredOption(controlArgs, "project-root");
|
|
276
|
+
}
|
|
277
|
+
if (io.stdin?.isTTY && ["work", "stage"].includes(output.args[0] ?? "") && output.args[1] === "finish"
|
|
278
|
+
&& hasOption(parseArgs(output.args.slice(2)), "result-stdin")) {
|
|
279
|
+
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);
|
|
280
|
+
}
|
|
79
281
|
const classification = classifyCliOperation(output.args, env);
|
|
80
282
|
if (requiresExplicitUpgradeAuthorization(output.args) && classification.mode !== "mb_upgrade") {
|
|
81
283
|
throw new AppError("upgrade_authorization_required", "This mb-upgrade command requires the explicit mb-upgrade authorization marker and allowlisted target context", 1, {
|
|
@@ -83,26 +285,205 @@ export async function runCli(args, io = defaultIo, env = process.env) {
|
|
|
83
285
|
required_marker: "DD_FLOW_COMPATIBILITY_MODE=mb-upgrade"
|
|
84
286
|
});
|
|
85
287
|
}
|
|
86
|
-
|
|
288
|
+
// Registration may bootstrap the resource registry, but never the unrelated
|
|
289
|
+
// RUN store. Its caller's absolute deadline also bounds that first open.
|
|
290
|
+
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) } : {}) } : {};
|
|
291
|
+
if (output.args[0] === "runtime" && output.args[1] === "scope" && ["fence", "stop"].includes(output.args[2] ?? ""))
|
|
292
|
+
maintenanceOptions.resourceDatabaseMode = "retirement";
|
|
293
|
+
// Keep ordinary CLI JSON/stderr compatibility; bounded maintenance callers
|
|
294
|
+
// provide this request's deadline and consume its structured phase evidence.
|
|
295
|
+
if (maintenanceOptions.databaseDeadlineAt !== undefined)
|
|
296
|
+
maintenanceDiagnostics = runtimeMaintenanceDiagnostics(output.args, maintenanceOptions.databaseDeadlineAt, line => io.stderr.write(line));
|
|
297
|
+
const routerContext = createRouterContext(env, maintenanceOptions.databaseDeadlineAt);
|
|
298
|
+
const publicCommand = quote(["dd-flow", ...output.args]);
|
|
299
|
+
const nativeCommand = quote(["dd-flow", ...args]);
|
|
300
|
+
const lifecycleCommand = parseLifecycleCommand(publicCommand);
|
|
301
|
+
if (lifecycleCommand.kind === "standalone")
|
|
302
|
+
validateLifecyclePathAliases(output.args, lifecycleCommand.invocation.operation);
|
|
303
|
+
const managedScope = lifecycleCommand.kind === "standalone" && env.DD_FLOW_DAEMON_ID ? cliManagedInvocationScope({ ...routerContext, env }) : null;
|
|
304
|
+
const nativeEvent = env.DD_FLOW_DAEMON_ID && lifecycleCommand.kind === "standalone"
|
|
305
|
+
? observedLifecycleNativeEvent(routerContext, nativeCommand, env.DD_FLOW_DAEMON_ID) : null;
|
|
306
|
+
const nativeEventKey = nativeEvent && routerContext.db.get("SELECT event_key FROM hook_events WHERE project_id = ? AND id = ?", [nativeEvent.project_id, nativeEvent.id])?.event_key;
|
|
307
|
+
const observedInvocation = managedScope ? observedLifecycleInvocation(routerContext, publicCommand, managedScope, nativeEventKey ? { eventKey: nativeEventKey } : undefined) : null;
|
|
308
|
+
if (managedScope && suppliedInvocationId
|
|
309
|
+
&& routerContext.db.get("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'lifecycle_invocations'")
|
|
310
|
+
&& routerContext.db.get("SELECT id FROM lifecycle_invocations WHERE id = ?", [suppliedInvocationId])) {
|
|
311
|
+
const suppliedScope = lifecycleInvocationScope({ ...routerContext, env }, suppliedInvocationId);
|
|
312
|
+
if (["projectRoot", "daemonId", "rootSessionId", "runId", "generation"].some(key => suppliedScope[key] !== managedScope[key])) {
|
|
313
|
+
throw new AppError("invocation_scope_mismatch", "Explicit lifecycle assignment differs from the current physical native owner", 1, { effect: "no_effect", recoverable: false });
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
const diagnostic = env.DD_FLOW_DAEMON_ID && lifecycleCommand.kind === "standalone"
|
|
317
|
+
? observedLifecycleDiagnostic(routerContext, publicCommand, env.DD_FLOW_DAEMON_ID, observedInvocation?.id) : null;
|
|
318
|
+
if (diagnostic && (!nativeEvent || Number(diagnostic.hook_event_id) > nativeEvent.id)) {
|
|
319
|
+
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 });
|
|
320
|
+
}
|
|
321
|
+
let manualRecoveryHookEventKey;
|
|
322
|
+
if (!observedInvocation && !suppliedInvocationId && env.DD_FLOW_DAEMON_ID && lifecycleCommand.kind === "standalone") {
|
|
323
|
+
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]);
|
|
324
|
+
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;
|
|
325
|
+
if (!manual || JSON.stringify(manual) !== JSON.stringify(managedScope))
|
|
326
|
+
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 });
|
|
327
|
+
assertManualRecoveryInvocationTarget({ ...routerContext, env }, manual, publicCommand);
|
|
328
|
+
manualRecoveryHookEventKey = receipt.event_key;
|
|
329
|
+
}
|
|
330
|
+
const invocationId = observedInvocation?.id ?? suppliedInvocationId;
|
|
331
|
+
let boundInvocationArgs;
|
|
332
|
+
try {
|
|
333
|
+
boundInvocationArgs = observedInvocation
|
|
334
|
+
? resolveObservedLifecycleArgs(routerContext, observedInvocation.id, output.args)
|
|
335
|
+
: invocationId && invocationId !== suppliedInvocationId
|
|
336
|
+
? [...output.args.filter((_, index, values) => values[index - 1] !== "--invocation-id" && values[index] !== "--invocation-id"), "--invocation-id", invocationId]
|
|
337
|
+
: output.args;
|
|
338
|
+
}
|
|
339
|
+
catch (error) {
|
|
340
|
+
if (!(observedInvocation && error instanceof AppError))
|
|
341
|
+
throw error;
|
|
342
|
+
const rejectionContext = createContext({ ...env }, "hook");
|
|
343
|
+
try {
|
|
344
|
+
Object.assign(error.details, settleLifecyclePreparationRejection(rejectionContext, observedInvocation.id, error));
|
|
345
|
+
}
|
|
346
|
+
finally {
|
|
347
|
+
rejectionContext.db.close?.();
|
|
348
|
+
}
|
|
349
|
+
throw error;
|
|
350
|
+
}
|
|
351
|
+
const observedOperation = lifecycleCommand.kind === "standalone" ? lifecycleCommand.invocation.operation : null;
|
|
352
|
+
if (observedInvocation && !observedOperation)
|
|
353
|
+
throw new AppError("invocation_command_invalid", "Observed lifecycle command could not be parsed", 1);
|
|
354
|
+
const invocationScope = invocationId ? lifecycleInvocationScope({ ...routerContext, env }, invocationId) : undefined;
|
|
355
|
+
const expandedInvocationArgs = invocationScope && observedOperation
|
|
356
|
+
? expandLifecycleInvocationArgs(routerContext, boundInvocationArgs, invocationScope, observedOperation)
|
|
357
|
+
: boundInvocationArgs;
|
|
358
|
+
const retainedOutput = observedInvocation ? parseOutputOptions(expandedInvocationArgs) : null;
|
|
359
|
+
if (retainedOutput?.json)
|
|
360
|
+
output.json = true;
|
|
361
|
+
if (retainedOutput?.progressJsonl)
|
|
362
|
+
output.progressJsonl = true;
|
|
363
|
+
if (retainedOutput?.responseFile)
|
|
364
|
+
output.responseFile = retainedOutput.responseFile;
|
|
365
|
+
let invocationArgs = retainedOutput?.args ?? expandedInvocationArgs;
|
|
87
366
|
emitHumanProgress(io, output, progress, "start");
|
|
88
367
|
if (output.progressJsonl) {
|
|
89
368
|
writeProgressJsonl(io, "start", `Starting ${output.args.slice(0, 3).join(" ")}`);
|
|
90
369
|
heartbeat = setInterval(() => writeProgressJsonl(io, "running", "Command is still running"), 15_000);
|
|
91
370
|
}
|
|
92
|
-
|
|
93
|
-
|
|
371
|
+
// Route by retained authority, so a mistyped project path reaches the CLI
|
|
372
|
+
// validator instead of selecting a different engine or failing preflight.
|
|
373
|
+
const scopeProjectRoot = invocationScope?.projectRoot ?? resolveCommandProjectRoot(routerContext, output.args, resolveProjectRootBeforeRouting(output.args, env, maintenanceOptions.databaseDeadlineAt));
|
|
374
|
+
assertAuxiliaryCommandScope({ ...routerContext, env }, publicCommand, scopeProjectRoot);
|
|
375
|
+
invocationArgs = resolveCommandPaths(routerContext, invocationArgs, scopeProjectRoot);
|
|
376
|
+
const routedArgs = output.responseFile ? [...invocationArgs, "--response-file", output.responseFile] : invocationArgs;
|
|
94
377
|
const routed = await routeArgsThroughEngine(routerContext, output.json ? [...routedArgs, "--json", ...(output.progressJsonl ? ["--progress-jsonl"] : [])] : routedArgs, io, io.stdin, env, scopeProjectRoot);
|
|
95
378
|
if (routed?.routed && !routed.inProcess) {
|
|
96
379
|
if (heartbeat)
|
|
97
380
|
clearInterval(heartbeat);
|
|
98
|
-
emitHumanProgress(io, output, progress, "done");
|
|
381
|
+
emitHumanProgress(io, output, progress, routed.exitCode === 0 ? "done" : "failed");
|
|
382
|
+
if (output.progressJsonl && routed.exitCode !== 0)
|
|
383
|
+
writeProgressJsonl(io, "failed", "Routed engine failed", { exit_code: routed.exitCode });
|
|
99
384
|
return routed.exitCode;
|
|
100
385
|
}
|
|
386
|
+
let preparedInput;
|
|
387
|
+
let preparedRoute;
|
|
388
|
+
if (!invocationId) {
|
|
389
|
+
const preparationContext = createContext({ ...env }, "read_existing", undefined, maintenanceOptions);
|
|
390
|
+
try {
|
|
391
|
+
preparedInput = await prepareCliInput(invocationArgs, preparationContext, io, scopeProjectRoot, output.responseFile, undefined, commandProgress(io, output));
|
|
392
|
+
}
|
|
393
|
+
finally {
|
|
394
|
+
preparationContext.db.close?.();
|
|
395
|
+
}
|
|
396
|
+
}
|
|
101
397
|
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
|
-
|
|
398
|
+
? 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)
|
|
399
|
+
: createContext({ ...env }, databaseModeForCommand(invocationArgs, classification), undefined, maintenanceOptions);
|
|
400
|
+
diagnosticHome = dispatchContext.ddFlowHome;
|
|
401
|
+
dispatchContext.env.DD_FLOW_INVOCATION_READONLY = classification.mode === "read_only_diagnostics" ? "1" : undefined;
|
|
402
|
+
let dispatchArgs = invocationArgs;
|
|
403
|
+
let retainedOutcome;
|
|
404
|
+
// Issued transports may deliver their receipt asynchronously. Observe only
|
|
405
|
+
// after admission; retained outcomes must not need a fresh hook receipt.
|
|
406
|
+
if (invocationId) {
|
|
407
|
+
const command = quote(["dd-flow", ...invocationArgs]);
|
|
408
|
+
let nextNativeObservation = 0;
|
|
409
|
+
const admission = await awaitLifecycleInvocation(dispatchContext, { id: invocationId, command,
|
|
410
|
+
validateArgs: () => {
|
|
411
|
+
try {
|
|
412
|
+
preparedRoute = prepareCommandRoute(invocationArgs);
|
|
413
|
+
}
|
|
414
|
+
catch (error) {
|
|
415
|
+
if (error instanceof AppError && error.code === "usage" && ["invocation-id", "hook-event-id"].includes(String(error.details.parameter ?? ""))) {
|
|
416
|
+
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 });
|
|
417
|
+
}
|
|
418
|
+
throw error;
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
prepare: async () => { preparedInput = await prepareCliInput(invocationArgs, dispatchContext, io, scopeProjectRoot, output.responseFile, preparedRoute, commandProgress(io, output)); },
|
|
422
|
+
assertCurrent: scope => assertLifecycleInvocationCurrent(dispatchContext, scope, command), progress: commandProgress(io, output),
|
|
423
|
+
...((env.DD_FLOW_RUNTIME_OWNER || env.DD_FLOW_CODEX_STATE_DIR || env.DD_AGY_STATE_DIR || env.DD_GROK_STATE_DIR || env.DD_ZCODE_STATE_DIR) ? { reconcileNativeObservation: async (scope, onProgress) => {
|
|
424
|
+
if (Date.now() < nextNativeObservation)
|
|
425
|
+
return;
|
|
426
|
+
nextNativeObservation = Date.now() + 250;
|
|
427
|
+
const { reconcileRetainedNativeHook } = await import("../services/retained-native-hook.js");
|
|
428
|
+
await reconcileRetainedNativeHook(dispatchContext, { scope, invocationId, onProgress,
|
|
429
|
+
onDiagnostic: value => io.stderr.write(`${JSON.stringify({ kind: "native_hook_diagnostic", ...value })}\n`) });
|
|
430
|
+
} } : {}) });
|
|
431
|
+
if (admission.replay) {
|
|
432
|
+
retainedOutcome = admission.outcome;
|
|
433
|
+
settleHook = undefined;
|
|
434
|
+
delete dispatchContext.observeLifecycleHook;
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
invocationSettlement = { context: dispatchContext, id: invocationId };
|
|
438
|
+
if (observedInvocation)
|
|
439
|
+
retainObservedLifecycleSemantics(dispatchContext, invocationId, invocationArgs);
|
|
440
|
+
if (admission.kind === "native_hook") {
|
|
441
|
+
dispatchArgs = [...invocationArgs, "--hook-event-id", admission.eventKey];
|
|
442
|
+
preparedInput?.route?.parsed.options.set("hook-event-id", [admission.eventKey]);
|
|
443
|
+
}
|
|
444
|
+
dispatchContext.env.DD_FLOW_INVOCATION_SCOPE = JSON.stringify(lifecycleInvocationScope(dispatchContext, invocationId));
|
|
445
|
+
dispatchContext.env.DD_FLOW_CURRENT_INVOCATION = invocationId;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
if (!invocationId && manualRecoveryHookEventKey) {
|
|
449
|
+
// Keep the physically scoped receipt selected by admission; a service must
|
|
450
|
+
// not rediscover a different daemon's matching command event.
|
|
451
|
+
if (!invocationArgs.includes("--hook-event-id"))
|
|
452
|
+
dispatchArgs = [...invocationArgs, "--hook-event-id", manualRecoveryHookEventKey];
|
|
453
|
+
preparedInput?.route?.parsed.options.set("hook-event-id", [manualRecoveryHookEventKey]);
|
|
454
|
+
}
|
|
455
|
+
if (retainedOutcome?.error) {
|
|
456
|
+
const error = retainedOutcome.error;
|
|
457
|
+
throw new AppError(error.code, error.message, error.exitCode, error.details);
|
|
458
|
+
}
|
|
459
|
+
if (!retainedOutcome && !preparedInput)
|
|
460
|
+
preparedInput = await prepareCliInput(invocationArgs, dispatchContext, io, scopeProjectRoot, output.responseFile, undefined, commandProgress(io, output));
|
|
461
|
+
if (!retainedOutcome)
|
|
462
|
+
await preflightCliCompatibility(dispatchContext, invocationArgs, env, scopeProjectRoot);
|
|
463
|
+
if (!retainedOutcome) {
|
|
464
|
+
const command = !invocationId ? nativeCommand : quote(["dd-flow", ...dispatchArgs]);
|
|
465
|
+
const event = !invocationId ? nativeEvent : null;
|
|
466
|
+
settleHook = observeLifecycleCommand(dispatchContext, command, event ?? undefined);
|
|
467
|
+
}
|
|
468
|
+
const result = preparePromptDelivery(retainedOutcome ? retainedOutcome.result : await dispatch(dispatchArgs, dispatchContext, io, scopeProjectRoot, classification, output, preparedInput));
|
|
469
|
+
if (!retainedOutcome && result && typeof result === "object" && "worker_prompt_markdown" in result && typeof result.worker_prompt_markdown === "string") {
|
|
470
|
+
try {
|
|
471
|
+
bindLifecyclePublishedPacket(dispatchContext, result.worker_prompt_markdown);
|
|
472
|
+
}
|
|
473
|
+
catch (cause) {
|
|
474
|
+
throw new AppError("invocation_packet_binding_failed", "Stage started but its published lifecycle assignments could not be retained", 1, { effect: "committed", cause: String(cause) });
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
const completedHook = settleHook;
|
|
478
|
+
settleHook = undefined;
|
|
479
|
+
completedHook?.({ result });
|
|
480
|
+
if (invocationSettlement) {
|
|
481
|
+
const completed = invocationSettlement;
|
|
482
|
+
invocationSettlement = undefined;
|
|
483
|
+
// A failure to persist an already completed result is an unknown reply,
|
|
484
|
+
// not a failed operation eligible for a new productive retry.
|
|
485
|
+
settleLifecycleInvocation(completed.context, completed.id, { result });
|
|
486
|
+
}
|
|
106
487
|
const refreshWarnings = refreshDashboardsAfterMutation(dispatchContext, output.args, result, scopeProjectRoot);
|
|
107
488
|
const resultWithWarnings = addWarnings(result, refreshWarnings);
|
|
108
489
|
if (heartbeat)
|
|
@@ -111,48 +492,179 @@ export async function runCli(args, io = defaultIo, env = process.env) {
|
|
|
111
492
|
writeProgressJsonl(io, "done", "Command finished");
|
|
112
493
|
emitHumanProgress(io, output, progress, "done");
|
|
113
494
|
const payload = attachProgress(resultWithWarnings, progress, output);
|
|
495
|
+
publishPromptDelivery(payload);
|
|
114
496
|
const visible = writeResponseFile(payload, output);
|
|
497
|
+
const maintenanceReceipt = payload;
|
|
498
|
+
const maintenanceExpiry = maintenanceReceipt?.lease_expires_at ?? maintenanceReceipt?.process?.lease_expires_at;
|
|
499
|
+
maintenanceDiagnostics?.phase("ack", { ...(typeof maintenanceExpiry === "string" && Number.isFinite(Date.parse(maintenanceExpiry)) ? { lease_expires_at: maintenanceExpiry } : {}) });
|
|
115
500
|
if (output.json)
|
|
116
501
|
writeJson(io.stdout, visible);
|
|
117
502
|
else
|
|
118
503
|
io.stdout.write(renderHumanResult(output.args, payload));
|
|
119
504
|
return completedCommandExitCode(payload);
|
|
120
505
|
}
|
|
121
|
-
catch (
|
|
506
|
+
catch (caught) {
|
|
507
|
+
const record = errorRecord(caught);
|
|
508
|
+
maintenanceDiagnostics?.failure(caught);
|
|
509
|
+
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 } : {}) });
|
|
510
|
+
if (!settleHook && !invocationSettlement && error.code !== "prompt_delivery_pending") {
|
|
511
|
+
let earlyContext;
|
|
512
|
+
try {
|
|
513
|
+
const command = quote(["dd-flow", ...args]);
|
|
514
|
+
const analysis = parseLifecycleCommand(command);
|
|
515
|
+
// Maintenance failures must not start an unrelated, unbounded hook
|
|
516
|
+
// lookup after their own request deadline has already expired.
|
|
517
|
+
const router = analysis.kind !== "none" || analysis.auxiliary ? createRouterContext(env) : null;
|
|
518
|
+
const event = router ? observedLifecycleNativeEvent(router, command, env.DD_FLOW_DAEMON_ID) : null;
|
|
519
|
+
// A correctable prepare rejection precedes the terminal command's
|
|
520
|
+
// single-use claim. Correlate its unique hook by the same lifecycle
|
|
521
|
+
// match key used at dispatch, including compound provider tool shells;
|
|
522
|
+
// otherwise a repaired retry sees two fresh, unclaimed events.
|
|
523
|
+
if (router && (event || (error.details.phase === "prepare" && error.details.effect === "no_effect"
|
|
524
|
+
&& fs.existsSync(path.join(router.ddFlowHome, "db.sqlite"))))) {
|
|
525
|
+
earlyContext = createContext({ ...env }, "hook");
|
|
526
|
+
observeLifecycleCommand(earlyContext, command, event ?? undefined)({ error });
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
catch (persistence) {
|
|
530
|
+
error.details.lifecycle_diagnostic_error = String(persistence);
|
|
531
|
+
}
|
|
532
|
+
finally {
|
|
533
|
+
earlyContext?.db.close?.();
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
if (diagnosticHome && (!isAppError(caught) || error.code === "storage_write_failed")) {
|
|
537
|
+
try {
|
|
538
|
+
const directory = path.join(diagnosticHome, "diagnostics", "cli");
|
|
539
|
+
fs.mkdirSync(directory, { recursive: true });
|
|
540
|
+
const artifact = path.join(directory, `${randomUUID()}.json`);
|
|
541
|
+
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 });
|
|
542
|
+
error.details.diagnostic_artifact = artifact;
|
|
543
|
+
}
|
|
544
|
+
catch (secondary) {
|
|
545
|
+
io.stderr.write(`CLI diagnostic persistence failed: ${String(secondary)}\n`);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
122
548
|
if (heartbeat)
|
|
123
549
|
clearInterval(heartbeat);
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
550
|
+
if (invocationSettlement && error.details.effect !== "committed") {
|
|
551
|
+
try {
|
|
552
|
+
if (isAppError(error)) {
|
|
553
|
+
delete error.details.retry_command;
|
|
554
|
+
delete error.details.retry_instruction;
|
|
555
|
+
// The common settlement classifies original validated proof before
|
|
556
|
+
// errorRecord clones diagnostic objects and loses capability identity.
|
|
557
|
+
settleLifecycleRejection(invocationSettlement.context, invocationSettlement.id, error);
|
|
558
|
+
}
|
|
559
|
+
else
|
|
560
|
+
settleLifecycleInvocation(invocationSettlement.context, invocationSettlement.id, { error: { code: "unexpected", message: String(error), exitCode: 1, details: {} } });
|
|
561
|
+
}
|
|
562
|
+
catch (persistenceError) {
|
|
563
|
+
if (isAppError(error))
|
|
564
|
+
error.details.recoverable = false;
|
|
565
|
+
io.stderr.write(`Invocation settlement persistence failed: ${String(persistenceError)}\n`);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
if (invocationSettlement && error.details.work_result_accepted === true) {
|
|
569
|
+
const accepted = readAcceptedAssignedWork(invocationSettlement.context, invocationSettlement.id);
|
|
570
|
+
if (accepted) {
|
|
571
|
+
// The Work/WorkSession commit is authoritative. Report its retained
|
|
572
|
+
// answer even if a derivative publication failed after that commit.
|
|
573
|
+
const payload = addWarnings(accepted, [{ code: "work_post_commit_pending", message: "Work result accepted; derivative publication requires reconciliation" }]);
|
|
574
|
+
try {
|
|
575
|
+
writeResponseFile(payload, output);
|
|
576
|
+
}
|
|
577
|
+
catch { /* Accepted SQL reply remains available on stdout/replay. */ }
|
|
578
|
+
if (output.json)
|
|
579
|
+
writeJson(io.stdout, payload);
|
|
580
|
+
else
|
|
581
|
+
io.stdout.write(renderHumanResult(output.args, payload));
|
|
582
|
+
return completedCommandExitCode(payload);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
if (settleHook) {
|
|
586
|
+
try {
|
|
587
|
+
settleHook({ error: isAppError(error) ? error : { code: "unexpected", message: String(error), details: {} } });
|
|
588
|
+
}
|
|
589
|
+
catch (persistence) {
|
|
590
|
+
io.stderr.write(`${JSON.stringify({ primary: String(error), lifecycle_persistence_error: String(persistence) })}\n`);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
const payload = { ok: false, error: errorRecord(error) };
|
|
594
|
+
progress.splice(0, progress.length, ...progress.filter(event => event.phase !== "done"));
|
|
595
|
+
if (progress.length)
|
|
596
|
+
progress.push({ phase: "failed", message: payload.error.message, at: new Date().toISOString() });
|
|
127
597
|
emitHumanProgress(io, output, progress, "failed");
|
|
128
598
|
if (output.progressJsonl)
|
|
129
599
|
writeProgressJsonl(io, "failed", payload.error.message, { code: payload.error.code });
|
|
130
600
|
const errorPayload = attachProgress(payload, progress, output);
|
|
131
|
-
|
|
601
|
+
let visible = errorPayload;
|
|
602
|
+
try {
|
|
603
|
+
if (error.details.parameter !== "response-file")
|
|
604
|
+
visible = writeResponseFile(errorPayload, output);
|
|
605
|
+
}
|
|
606
|
+
catch (publication) {
|
|
607
|
+
error.details.response_publication_error = { code: publication?.code ?? "response_publication_failed", message: publication instanceof Error ? publication.message : String(publication) };
|
|
608
|
+
}
|
|
132
609
|
if (output.json && output.progressJsonl)
|
|
133
610
|
writeJson(io.stdout, errorPayload);
|
|
134
611
|
else if (output.json)
|
|
135
612
|
writeJson(io.stderr, visible);
|
|
136
613
|
else
|
|
137
|
-
io.stderr.write(renderHumanError(payload));
|
|
614
|
+
io.stderr.write(renderHumanError(payload, args));
|
|
138
615
|
return isAppError(error) ? error.exitCode : 1;
|
|
139
616
|
}
|
|
617
|
+
finally {
|
|
618
|
+
maintenanceDiagnostics?.close();
|
|
619
|
+
}
|
|
140
620
|
}
|
|
141
|
-
function resolveProjectRootBeforeRouting(args, env) {
|
|
621
|
+
function resolveProjectRootBeforeRouting(args, env, deadlineAt) {
|
|
142
622
|
const explicit = resolveExplicitProjectRoot(args);
|
|
143
623
|
if (explicit)
|
|
144
624
|
return explicit;
|
|
145
625
|
try {
|
|
146
626
|
// Router identity lookup is read-only; it must not initialize or migrate runtime storage.
|
|
147
|
-
|
|
627
|
+
const context = createContext(env, "read_existing", undefined, deadlineAt === undefined ? {} : { databaseDeadlineAt: deadlineAt });
|
|
628
|
+
try {
|
|
629
|
+
return resolveOperationProjectRoot(context, args);
|
|
630
|
+
}
|
|
631
|
+
finally {
|
|
632
|
+
context.db.close?.();
|
|
633
|
+
}
|
|
148
634
|
}
|
|
149
635
|
catch {
|
|
150
636
|
return null;
|
|
151
637
|
}
|
|
152
638
|
}
|
|
153
639
|
function databaseModeForCommand(args, classification) {
|
|
640
|
+
if (isNativeHookIngress(args))
|
|
641
|
+
return "hook";
|
|
642
|
+
if (args[0] === "run" && args[1] === "reconcile")
|
|
643
|
+
return args.includes("--apply") ? "reconcile" : "read_existing";
|
|
644
|
+
if (isRuntimeMaintenance(args))
|
|
645
|
+
return "read_existing";
|
|
154
646
|
const [family, command] = args;
|
|
647
|
+
// Bound interactive operations never initialize or migrate a RUN store.
|
|
648
|
+
if (family === "interactive" && classification.mode !== "read_only_diagnostics")
|
|
649
|
+
return "prepared_existing";
|
|
650
|
+
if (family === "migration" && command === "readiness")
|
|
651
|
+
return "prepared_existing";
|
|
652
|
+
// Managed attempts are issued only from a prepared store. Opening that
|
|
653
|
+
// existing writer must not run migrations before their input is admitted.
|
|
654
|
+
if (parseArgs(args.slice(2)).options.has("invocation-id"))
|
|
655
|
+
return "hook";
|
|
656
|
+
// Native hook ingress must remain synchronous, but it must not run schema
|
|
657
|
+
// migration/VACUUM while the provider is waiting for its decision. Startup
|
|
658
|
+
// prepares the store; the hook mode only verifies the prepared contract and
|
|
659
|
+
// records the receipt.
|
|
660
|
+
if (family === "codex" && command === "hook" && args[2] === "handle") {
|
|
661
|
+
// Only the participating PreToolUse ingress needs the prepared hot-path
|
|
662
|
+
// contract. SessionStart/Stop/PostToolUse are observational no-ops here
|
|
663
|
+
// and retain the ordinary startup path for backwards compatibility.
|
|
664
|
+
return optionalOption(parseArgs(args.slice(3)), "event") === "PreToolUse" ? "hook" : "initialize";
|
|
665
|
+
}
|
|
155
666
|
if (family === "engine" ||
|
|
667
|
+
family === "storage" && command === "migrate-writer" ||
|
|
156
668
|
family === "version" ||
|
|
157
669
|
family === "schema" ||
|
|
158
670
|
family === "canon" && command === "resolve" ||
|
|
@@ -166,86 +678,6 @@ function databaseModeForCommand(args, classification) {
|
|
|
166
678
|
return "read_existing";
|
|
167
679
|
return "initialize";
|
|
168
680
|
}
|
|
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
681
|
function progressForCommand(args) {
|
|
250
682
|
const [family, command, subcommand] = args;
|
|
251
683
|
if (family === "merge-queue" && command === "wait-next") {
|
|
@@ -294,7 +726,12 @@ function writeProgressJsonl(io, phase, message, detail) {
|
|
|
294
726
|
io.stderr.write(`${JSON.stringify({ schema_id: "dd-flow/progress@1", phase, message, at: new Date().toISOString(), ...(detail ? { detail } : {}) })}\n`);
|
|
295
727
|
}
|
|
296
728
|
function commandProgress(io, output) {
|
|
297
|
-
return (message) =>
|
|
729
|
+
return (message) => {
|
|
730
|
+
if (output.progressJsonl)
|
|
731
|
+
writeProgressJsonl(io, "running", message);
|
|
732
|
+
else if (!output.json && !output.hookJsonOnly)
|
|
733
|
+
io.stderr.write(`[running] ${message}\n`);
|
|
734
|
+
};
|
|
298
735
|
}
|
|
299
736
|
function attachProgress(result, progress, output) {
|
|
300
737
|
if (progress.length === 0 || output.hookJsonOnly)
|
|
@@ -308,7 +745,7 @@ function attachProgress(result, progress, output) {
|
|
|
308
745
|
}
|
|
309
746
|
return { ok: true, result, progress };
|
|
310
747
|
}
|
|
311
|
-
function renderHumanResult(args, result) {
|
|
748
|
+
export function renderHumanResult(args, result) {
|
|
312
749
|
const [family, command] = args;
|
|
313
750
|
const record = result && typeof result === "object" && !Array.isArray(result) ? result : {};
|
|
314
751
|
const ok = record.ok === false ? "failed" : "ok";
|
|
@@ -376,6 +813,16 @@ function renderHumanResult(args, result) {
|
|
|
376
813
|
if (run.run_index_path)
|
|
377
814
|
lines.push(`run_index: ${String(run.run_index_path)}`);
|
|
378
815
|
}
|
|
816
|
+
for (const key of ["outcome", "instruction", "agent_instruction", "next_action", "resume_command", "resume_command_template", "prompt_path"]) {
|
|
817
|
+
if (record[key] !== undefined && typeof record[key] !== "object")
|
|
818
|
+
lines.push(`${key}: ${String(record[key])}`);
|
|
819
|
+
}
|
|
820
|
+
for (const [name, keys] of [["next", ["finish_command"]], ["repair", ["work_id"]], ["prompt_delivery", ["path", "sha256"]], ["pause", ["user_message", "question_path", "resume_command", "resume_command_template"]]]) {
|
|
821
|
+
const value = recordObject(record[name]);
|
|
822
|
+
for (const key of keys)
|
|
823
|
+
if (value?.[key] !== undefined)
|
|
824
|
+
lines.push(`${name}.${key}: ${String(value[key])}`);
|
|
825
|
+
}
|
|
379
826
|
appendFlowGuidanceHuman(lines, record);
|
|
380
827
|
return `${lines.join("\n")}\n`;
|
|
381
828
|
}
|
|
@@ -501,27 +948,759 @@ function stringValue(value) {
|
|
|
501
948
|
function shortSha(value) {
|
|
502
949
|
return value ? value.slice(0, 12) : "unknown";
|
|
503
950
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
951
|
+
function compositionProposalRetry(context, parsed) {
|
|
952
|
+
return {
|
|
953
|
+
retry_command: runtimeCommand(context, ["memory", "compose", "register", "--run", requiredOption(parsed, "run"),
|
|
954
|
+
"--project-root", requiredOption(parsed, "project-root"), "--request-key", requiredOption(parsed, "request-key"),
|
|
955
|
+
"--result-file", requiredOption(parsed, "result-file"), "--json", "--progress-jsonl"]),
|
|
956
|
+
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."
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
/** All caller supplied file/stdin data is consumed here, before a managed
|
|
960
|
+
* invocation is allowed to claim execution or a stage claims its hook event. */
|
|
961
|
+
async function prepareCliInput(...args) {
|
|
962
|
+
try {
|
|
963
|
+
return await withPreparedInputBinding(() => prepareCliInputValue(...args));
|
|
964
|
+
}
|
|
965
|
+
catch (error) {
|
|
966
|
+
return prepareError(error);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
async function prepareCliInputValue(args, context, io, scopeProjectRoot, responseFile = null, route = prepareCommandRoute(args), progress) {
|
|
970
|
+
const { family, command, parsed } = route;
|
|
971
|
+
const prepared = { route };
|
|
972
|
+
if (family === "delivery" && ["checks", "execute"].includes(command ?? "")) {
|
|
973
|
+
const file = requiredOption(parsed, "result-file"), text = prepareTextFile(file, "Delivery input", 1024 * 1024);
|
|
974
|
+
const value = prepareJsonText(text.text, { file, label: "Delivery input" }).value;
|
|
975
|
+
const keys = command === "checks" ? "path,sha256,subject_ref,version" : "basis,expires_at,reason,scope_sha256,user_inputs_sha256";
|
|
976
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).sort().join() !== keys)
|
|
977
|
+
throw new AppError("usage", "Delivery input must match the selected command's exact JSON contract", 2, { phase: "prepare", effect: "no_effect" });
|
|
978
|
+
if (command === "checks")
|
|
979
|
+
prepared.deliveryArtifact = value;
|
|
980
|
+
else
|
|
981
|
+
prepared.deliveryApproval = value;
|
|
982
|
+
}
|
|
983
|
+
if (family === "interactive" && command === "integrate" && hasOption(parsed, "result-file")) {
|
|
984
|
+
const file = requiredOption(parsed, "result-file"), input = prepareTextFile(file, "Interactive Git agreement", 1024 * 1024);
|
|
985
|
+
const value = prepareJsonText(input.text, { file, label: "Interactive Git agreement" }).value;
|
|
986
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).sort().join() !== "basis,include_existing_paths,reason")
|
|
987
|
+
throw new AppError("usage", "Interactive Git agreement requires basis, include_existing_paths and reason", 2, { phase: "prepare", effect: "no_effect" });
|
|
988
|
+
prepared.interactiveGitAgreement = value;
|
|
989
|
+
}
|
|
990
|
+
if (family === "interactive" && command === "checks") {
|
|
991
|
+
const file = requiredOption(parsed, "result-file"), input = prepareTextFile(file, "Interactive check selection", 1024 * 1024);
|
|
992
|
+
const value = prepareJsonText(input.text, { file, label: "Interactive check selection" }).value;
|
|
993
|
+
if (!value || typeof value !== "object" || Array.isArray(value)
|
|
994
|
+
|| !["additionalChecks", "additionalChecks,noChecksReason"].includes(Object.keys(value).sort().join()))
|
|
995
|
+
throw new AppError("usage", "Interactive checks require additionalChecks and optional noChecksReason", 2, { phase: "prepare", effect: "no_effect" });
|
|
996
|
+
prepared.interactiveChecksProposal = value;
|
|
997
|
+
}
|
|
998
|
+
if (family === "interactive" && command === "scope") {
|
|
999
|
+
const file = requiredOption(parsed, "result-file"), input = prepareTextFile(file, "Interactive mutation scope", 1024 * 1024);
|
|
1000
|
+
const value = prepareJsonText(input.text, { file, label: "Interactive mutation scope" }).value;
|
|
1001
|
+
if (!value || typeof value !== "object" || Array.isArray(value)
|
|
1002
|
+
|| Object.keys(value).sort().join() !== "basis,expectedInputsSha256,paths,reason,requestId")
|
|
1003
|
+
throw new AppError("usage", "Interactive scope requires requestId, paths, basis, reason and expectedInputsSha256", 2, { phase: "prepare", effect: "no_effect" });
|
|
1004
|
+
const basis = recordObject(recordObject(value).basis);
|
|
1005
|
+
if (!basis || Object.keys(basis).sort().join() !== "event_id,role,sha256"
|
|
1006
|
+
|| !["request", "turn", "answer", "finish"].includes(String(basis.role))
|
|
1007
|
+
|| typeof basis.event_id !== "number" || !Number.isSafeInteger(basis.event_id) || basis.event_id < 0
|
|
1008
|
+
|| typeof basis.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(basis.sha256))
|
|
1009
|
+
throw new AppError("usage", "Interactive scope basis requires role request|turn|answer|finish, a nonnegative integer event_id and a SHA-256", 2, {
|
|
1010
|
+
reason: "invalid_scope_basis_shape", phase: "prepare", effect: "no_effect", recoverable: true,
|
|
1011
|
+
retry_command: runtimeCommand(context, ["interactive", "scope", "--project-root", requiredOption(parsed, "project-root"),
|
|
1012
|
+
"--run", requiredOption(parsed, "run"), "--result-file", file, "--json", "--progress-jsonl"]),
|
|
1013
|
+
retry_instruction: "Correct only the rejected basis shape. For the original request use role=request, event_id=0 and initial_request.sha256 from interactive inputs. For a later user message copy its exact role, integer event_id and sha256 from messages; a question is not user approval. No scope was retained by this call. Reuse this exact retry_command; valid-shaped but unowned evidence or conflicting accepted scope identities remain refusals."
|
|
1014
|
+
});
|
|
1015
|
+
prepared.interactiveScopeProposal = value;
|
|
1016
|
+
}
|
|
1017
|
+
if (family === "interactive" && (command === "turn" || command === "finish")) {
|
|
1018
|
+
const input = prepareTextFile(requiredOption(parsed, "intake-file"), "Interactive user input", 1024 * 1024);
|
|
1019
|
+
prepared.interactiveInputBytes = Buffer.from(input.text);
|
|
1020
|
+
if (!prepared.interactiveInputBytes.length || !/^[a-f0-9]{64}$/.test(requiredOption(parsed, "expected-inputs-sha256")))
|
|
1021
|
+
throw new AppError("usage", "Interactive delivery requires nonempty UTF-8 input and its current input digest", 2, { phase: "prepare", effect: "no_effect" });
|
|
1022
|
+
}
|
|
1023
|
+
if (optionalOption(parsed, "reason-file")) {
|
|
1024
|
+
const value = prepareJsonFile(requiredOption(parsed, "reason-file"), "Check recovery evidence").value;
|
|
1025
|
+
if (!value || typeof value.reason !== "string" || !value.reason.trim() || Object.keys(value).some(key => key !== "reason"))
|
|
1026
|
+
throw new AppError("validation", "Recovery evidence must be a JSON object with one nonempty reason", 2);
|
|
1027
|
+
prepared.retryReason = value.reason;
|
|
1028
|
+
}
|
|
1029
|
+
else if (optionalOption(parsed, "retry-check"))
|
|
1030
|
+
prepared.retryReason = optionalOption(parsed, "reason");
|
|
1031
|
+
if (route.key === "project register")
|
|
1032
|
+
resolveProjectRoot(requiredOption(parsed, "root"));
|
|
1033
|
+
if (route.key === "project archive")
|
|
1034
|
+
prepareProjectArchive(context, { idOrAlias: parsed.positional[0], root: optionalOption(parsed, "root"), reason: requiredOption(parsed, "reason") });
|
|
1035
|
+
if (route.key === "protocol register")
|
|
1036
|
+
prepareProtocolRegistration(context, { handshakeId: requiredPosition(parsed, 0, "handshake-id"), projectRoot: requiredOption(parsed, "project-root"), workspacePath: optionalOption(parsed, "workspace-path") });
|
|
1037
|
+
if (route.key === "canon register")
|
|
1038
|
+
prepared.canonRegistration = prepareCanonRegistration(context, { root: requiredOption(parsed, "root") });
|
|
1039
|
+
if (route.key === "run attach-stage")
|
|
1040
|
+
prepareFlowRunStageAttachment(context, stageAttachmentInput(parsed));
|
|
1041
|
+
if (route.key === "run complete-stage")
|
|
1042
|
+
prepareRawFlowRunStageCompletion(context, stageCompletionInput(parsed));
|
|
1043
|
+
if (route.key === "run override")
|
|
1044
|
+
prepareFlowRunCompletion(context, runOverrideInput(parsed));
|
|
1045
|
+
if (route.key === "engine bind-run")
|
|
1046
|
+
prepared.engineBind = await prepareRunEngineBind(context, engineBindInput(parsed));
|
|
1047
|
+
if (route.key === "worktree create")
|
|
1048
|
+
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") });
|
|
1049
|
+
if (route.key === "worktree bootstrap")
|
|
1050
|
+
prepared.worktreeBootstrap = prepareWorktreeBootstrap(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "worktree bootstrap") });
|
|
1051
|
+
if (route.key === "worktree close")
|
|
1052
|
+
prepareWorktreeClose(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "worktree close"), mode: requiredOption(parsed, "mode") });
|
|
1053
|
+
if (route.key.startsWith("plan item "))
|
|
1054
|
+
preparePlanItemMutation(context, planItemInput(parsed, requiredScopeProjectRoot(scopeProjectRoot, route.key)));
|
|
1055
|
+
if (route.key === "lane workspace set")
|
|
1056
|
+
prepareLaneWorkspace(context, laneWorkspaceInput(parsed));
|
|
1057
|
+
if (route.key === "lane waiter cancel")
|
|
1058
|
+
prepareLaneWaiterCancellation(context, laneWaiterCancelInput(parsed));
|
|
1059
|
+
if (route.key === "merge-queue complete")
|
|
1060
|
+
prepareMergeJobCompletion(context, { ...mergeJobTarget(parsed, scopeProjectRoot), summary: requiredOption(parsed, "summary") });
|
|
1061
|
+
if (route.key === "merge-queue cancel")
|
|
1062
|
+
prepareMergeQueueCancellation(context, mergeCancellationInput(parsed, scopeProjectRoot));
|
|
1063
|
+
if (route.key === "merge-queue wait-next")
|
|
1064
|
+
prepareMergeQueueWait(context, mergeWaitInput(parsed, scopeProjectRoot));
|
|
1065
|
+
if (route.key === "merge-queue next" || route.key === "merge-queue claim")
|
|
1066
|
+
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") } : {}) });
|
|
1067
|
+
if (route.key === "merge-queue note")
|
|
1068
|
+
prepareMergeJobNote(context, { protocolId: requiredPosition(parsed, 0, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue note"), workerId: requiredWorkerId(parsed), summary: requiredOption(parsed, "summary") });
|
|
1069
|
+
if (route.key === "merge bundle complete")
|
|
1070
|
+
prepareMergeBundleCompletion(context, { projectRoot: requiredOption(parsed, "project-root"), workspacePath: workspacePathOption(parsed), workerId: requiredWorkerId(parsed), summary: requiredOption(parsed, "summary") });
|
|
1071
|
+
if (route.key === "merge bundle claim")
|
|
1072
|
+
prepareMergeBundleClaim(context, { projectRoot: requiredOption(parsed, "project-root"), workspacePath: workspacePathOption(parsed), workerId: requiredWorkerId(parsed) });
|
|
1073
|
+
if (route.key === "merge bundle fail")
|
|
1074
|
+
prepareMergeBundleFailure(context, { projectRoot: requiredOption(parsed, "project-root"), workspacePath: workspacePathOption(parsed), workerId: requiredWorkerId(parsed), reason: requiredOption(parsed, "reason"), requeue: parseBoolean(requiredOption(parsed, "requeue"), "requeue") });
|
|
1075
|
+
if (route.key === "merge-queue fail")
|
|
1076
|
+
prepareMergeJobFailure(context, { ...mergeJobTarget(parsed, scopeProjectRoot), reason: requiredOption(parsed, "reason"), requeue: parseBoolean(requiredOption(parsed, "requeue"), "requeue") });
|
|
1077
|
+
if (route.key.startsWith("lane lock ") && route.key !== "lane lock status") {
|
|
1078
|
+
const action = requiredPosition(parsed, 0, "lane lock action");
|
|
1079
|
+
prepareLaneLock(context, {
|
|
1080
|
+
projectRoot: requiredOption(parsed, "project-root"), lane: requiredOption(parsed, "lane"), workerId: requiredOption(parsed, "worker-id"),
|
|
1081
|
+
workspacePath: workspacePathOption(parsed), leaseToken: optionalOption(parsed, "lease-token"), reason: optionalOption(parsed, "reason"),
|
|
1082
|
+
ttlSeconds: optionalNumber(parsed, "ttl"), timeoutSeconds: optionalNumber(parsed, "timeout"), pollIntervalSeconds: optionalNumber(parsed, "poll-interval")
|
|
1083
|
+
}, action);
|
|
1084
|
+
}
|
|
1085
|
+
if (route.key === "run recovery begin")
|
|
1086
|
+
prepared.recoveryPayload = prepareRecoveryPayload(requiredOption(parsed, "interruption-json"), "interruption");
|
|
1087
|
+
if (route.key === "run recovery seal")
|
|
1088
|
+
prepared.recoveryPayload = prepareRecoveryPayload(requiredOption(parsed, "settlement-json"), "settlement");
|
|
1089
|
+
if (route.key === "run recovery capture")
|
|
1090
|
+
prepared.recoveryCapture = prepareRecoveryCapture(context, recoveryCaptureInput(parsed));
|
|
1091
|
+
if (route.key === "run recovery resume")
|
|
1092
|
+
prepared.recoveryResume = prepareRunRecoveryResume(context, recoveryResumeInput(parsed));
|
|
1093
|
+
if (route.key === "run drive answer")
|
|
1094
|
+
prepared.controllerAnswer = prepareRunControllerAnswer(context, controllerAnswerInput(parsed));
|
|
1095
|
+
if (route.key === "run drive launch")
|
|
1096
|
+
prepared.controllerLaunch = prepareRunControllerLaunch(context, controllerLaunchInput(parsed));
|
|
1097
|
+
if (route.key === "run drive context")
|
|
1098
|
+
prepared.controllerContext = prepareRunControllerContext(context, controllerContextInput(parsed));
|
|
1099
|
+
if (route.key === "protocol transition" || route.key === "transition")
|
|
1100
|
+
prepared.protocolTransition = prepareProtocolTransition(context, protocolTransitionInput(parsed, scopeProjectRoot ?? resolveOperationProjectRoot(context, args)));
|
|
1101
|
+
if (route.key === "codex home init")
|
|
1102
|
+
prepared.codexHome = prepareCodexHome(context, codexHomeInput(parsed));
|
|
1103
|
+
if (route.key === "codex home remove")
|
|
1104
|
+
prepareCodexHomeRemoval(context, { projectRoot: requiredOption(parsed, "project-root"), profile: optionalOption(parsed, "profile"), mode: requiredOption(parsed, "mode") });
|
|
1105
|
+
if (["codex hooks install", "codex hooks remove"].includes(route.key))
|
|
1106
|
+
prepared.codexHooks = prepareCodexHooks(context, { projectRoot: requiredOption(parsed, "project-root"), target: optionalOption(parsed, "target"), profile: optionalOption(parsed, "profile"), yes: hasOption(parsed, "yes") });
|
|
1107
|
+
if (route.key === "migration apply")
|
|
1108
|
+
prepared.migrationApply = prepareMigrationApply(context, migrationApplyInput(parsed, classifyCliOperation(args, context.env)));
|
|
1109
|
+
if (route.key === "migration readiness")
|
|
1110
|
+
prepared.upgradeStorageScope = {
|
|
1111
|
+
projectRoot: requiredOption(parsed, "project-root"), workspaceRoot: requiredOption(parsed, "workspace-root"), requestId: requiredOption(parsed, "request-id")
|
|
1112
|
+
};
|
|
1113
|
+
if (route.key === "migration verify")
|
|
1114
|
+
prepared.migrationVerification = verifyMigrationReport(context, { file: requiredOption(parsed, "file") });
|
|
1115
|
+
if (responseFile)
|
|
1116
|
+
prepareResponseDestination(responseFile);
|
|
1117
|
+
if (family === "dashboard") {
|
|
1118
|
+
const action = dashboardAction(command);
|
|
1119
|
+
prepareDashboardOptions({ format: optionalOption(parsed, "format"), viewer: optionalOption(parsed, "viewer"), open: optionalOption(parsed, "open") });
|
|
1120
|
+
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") });
|
|
1121
|
+
if (action === "refresh" && target.kind !== "global" && optionalOption(parsed, "output") !== undefined) {
|
|
1122
|
+
throw new AppError("validation", "--output is supported only for global dashboard refresh; use dashboard render for a custom project/protocol output", 2, { parameter: "output" });
|
|
1123
|
+
}
|
|
1124
|
+
if (target.kind === "protocol")
|
|
1125
|
+
requireProtocol(context, target.protocolId, target.project.id);
|
|
1126
|
+
if (action !== "data") {
|
|
1127
|
+
if (target.kind === "global" || target.kind === "all_projects") {
|
|
1128
|
+
prepareGlobalDashboardRender(context, { output: optionalOption(parsed, "output"), format: optionalOption(parsed, "format") ?? (command?.endsWith("-global") ? undefined : "html") });
|
|
1129
|
+
}
|
|
1130
|
+
else {
|
|
1131
|
+
const format = optionalOption(parsed, "format") ?? (action === "open" || hasOption(parsed, "project") ? "html" : undefined);
|
|
1132
|
+
const input = { projectRoot: target.project.root, format, ...(target.kind === "protocol" ? { protocol: target.protocolId } : {}) };
|
|
1133
|
+
if (action === "refresh")
|
|
1134
|
+
prepareDashboardRefresh(context, { ...input, open: optionalOption(parsed, "open") });
|
|
1135
|
+
else if (action === "render")
|
|
1136
|
+
prepareDashboardRender(context, { ...input, output: optionalOption(parsed, "output") });
|
|
1137
|
+
else if (target.kind === "protocol") {
|
|
1138
|
+
if (format !== "html")
|
|
1139
|
+
throw new AppError("validation", "Protocol dashboard open supports --format html", 2, { protocol: target.protocolId });
|
|
1140
|
+
prepareDashboardRender(context, input);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
if (route.key === "prompt render") {
|
|
1146
|
+
const input = workerPromptInput(parsed);
|
|
1147
|
+
prepared.workerPrompt = { input, value: prepareWorkerPrompt(context, input) };
|
|
1148
|
+
}
|
|
1149
|
+
if (route.key === "run snapshot restore" || route.key === "run snapshot bootstrap restore") {
|
|
1150
|
+
try {
|
|
1151
|
+
const input = snapshotRestoreInput(parsed);
|
|
1152
|
+
if (route.key === "run snapshot restore")
|
|
1153
|
+
prepared.snapshotRestore = prepareEvalRunRestore(context, input);
|
|
1154
|
+
else
|
|
1155
|
+
prepared.bootstrapRestore = prepareEvalBootstrapRestore(input);
|
|
1156
|
+
}
|
|
1157
|
+
catch (error) {
|
|
1158
|
+
return prepareError(error, { parameter: "snapshot/project-root" });
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
if (route.key === "run snapshot bootstrap create") {
|
|
1162
|
+
try {
|
|
1163
|
+
prepareEvalBootstrapSnapshot(context, { projectRoot: requiredOption(parsed, "project-root"), output: requiredOption(parsed, "output"), flow: optionalOption(parsed, "flow") });
|
|
1164
|
+
}
|
|
1165
|
+
catch (error) {
|
|
1166
|
+
return prepareError(error, { parameter: "project-root/output" });
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
if (route.key === "run snapshot create") {
|
|
1170
|
+
prepareEvalRunSnapshotCreation(context, snapshotCreateInput(parsed));
|
|
1171
|
+
}
|
|
1172
|
+
if (route.key === "run start") {
|
|
1173
|
+
try {
|
|
1174
|
+
const input = runStartInput(parsed);
|
|
1175
|
+
prepared.runStart = { input, value: prepareFlowRunStart(context, input) };
|
|
1176
|
+
}
|
|
1177
|
+
catch (error) {
|
|
1178
|
+
return prepareError(error, { parameter: "run start" });
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
if (route.key === "run fork") {
|
|
1182
|
+
try {
|
|
1183
|
+
prepared.runFork = prepareRunFork(runForkInput(parsed));
|
|
1184
|
+
}
|
|
1185
|
+
catch (error) {
|
|
1186
|
+
return prepareError(error, { parameter: "from/output" });
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
if (route.key === "runtime scope status")
|
|
1190
|
+
prepareRuntimeScopeStatus(optionalOption(parsed, "scope-id"), optionalOption(parsed, "scope-ids-json"));
|
|
1191
|
+
if (["runtime scope fence", "runtime scope stop"].includes(route.key))
|
|
1192
|
+
prepareRuntimeScopeFence(requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id"));
|
|
1193
|
+
if (route.key === "runtime scope resume")
|
|
1194
|
+
prepareRuntimeScopeResumeRequest(context, runtimeScopeResumeInput(parsed));
|
|
1195
|
+
if (route.key === "runtime scope reconcile")
|
|
1196
|
+
prepareRuntimeScopeReconciliation(context, requiredOption(parsed, "scope-id"), Number(requiredOption(parsed, "generation")), requiredOption(parsed, "request-id"));
|
|
1197
|
+
if (route.key === "runtime scope serve")
|
|
1198
|
+
prepareRuntimeScopeServing(context, requiredOption(parsed, "scope-id"), Number(requiredOption(parsed, "generation")));
|
|
1199
|
+
if (route.key === "runtime scope control") {
|
|
1200
|
+
try {
|
|
1201
|
+
const raw = optionalOption(parsed, "manifest-json");
|
|
1202
|
+
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);
|
|
1203
|
+
}
|
|
1204
|
+
catch (error) {
|
|
1205
|
+
return prepareError(error, { parameter: "scope-id/request-id/manifest-json" });
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
if (family === "runtime" && command === "process")
|
|
1209
|
+
Object.assign(prepared, prepareRuntimeProcess(context, route.key, parsed));
|
|
1210
|
+
if (route.key === "runtime process start") {
|
|
1211
|
+
try {
|
|
1212
|
+
prepareRuntimeService(context, runtimeServiceInput(parsed));
|
|
1213
|
+
}
|
|
1214
|
+
catch (error) {
|
|
1215
|
+
return prepareError(error, { parameter: "runtime process start" });
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
if (family === "work" && command === "retry") {
|
|
1219
|
+
try {
|
|
1220
|
+
prepareWorkRetry(context, requiredPosition(parsed, 0, "work-id"), requiredOption(parsed, "reason"));
|
|
1221
|
+
}
|
|
1222
|
+
catch (error) {
|
|
1223
|
+
return prepareError(error, { parameter: "work-id/reason" });
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
if (family === "work" && (command === "fail" || command === "cancel")) {
|
|
1227
|
+
try {
|
|
1228
|
+
prepareWorkSettlement(context, requiredPosition(parsed, 0, "work-id"), command === "fail" ? "failed" : "cancelled", requiredOption(parsed, "reason"));
|
|
1229
|
+
}
|
|
1230
|
+
catch (error) {
|
|
1231
|
+
return prepareError(error, { parameter: "work-id/reason" });
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
if (family === "work" && (command === "deps" || command === "delete")) {
|
|
1235
|
+
try {
|
|
1236
|
+
if (command === "delete")
|
|
1237
|
+
prepareWorkDelete(context, requiredPosition(parsed, 0, "work-id"));
|
|
1238
|
+
else
|
|
1239
|
+
prepareWorkDeps(context, { workId: requiredPosition(parsed, 1, "work-id"), action: requiredPosition(parsed, 0, "action"), on: parsed.options.get("on") ?? [] });
|
|
1240
|
+
}
|
|
1241
|
+
catch (error) {
|
|
1242
|
+
return prepareError(error, { parameter: "work-id/on" });
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
if (route.key === "cleanup apply") {
|
|
1246
|
+
const planFile = requiredOption(parsed, "plan-file");
|
|
1247
|
+
try {
|
|
1248
|
+
prepared.cleanup = prepareCleanupApply(context, { projectRoot: requiredOption(parsed, "project-root"), planFile, reason: requiredOption(parsed, "reason"), force: hasOption(parsed, "force") });
|
|
1249
|
+
}
|
|
1250
|
+
catch (error) {
|
|
1251
|
+
return prepareError(error, { parameter: "plan-file", file: planFile });
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
if (route.key === "stage block" || route.key === "stage unblock") {
|
|
1255
|
+
const input = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), stage: requiredOption(parsed, "stage"), workId: requiredOption(parsed, "work") };
|
|
1256
|
+
try {
|
|
1257
|
+
if (route.key === "stage block") {
|
|
1258
|
+
const summaryFile = optionalOption(parsed, "summary-file"), summaryStdin = hasOption(parsed, "summary-stdin");
|
|
1259
|
+
if (Boolean(summaryFile) === summaryStdin)
|
|
1260
|
+
throw new AppError("usage", "stage block requires exactly one of --summary-file or --summary-stdin", 2);
|
|
1261
|
+
prepared.interactionText = summaryFile ? prepareTextFile(summaryFile, "Runtime block summary").text : await readStdin(io.stdin);
|
|
1262
|
+
prepareStageRuntimeBlock(context, { ...input, kind: requiredOption(parsed, "kind"), code: requiredOption(parsed, "code"), summary: prepared.interactionText, retryable: hasOption(parsed, "retryable") });
|
|
1263
|
+
}
|
|
1264
|
+
else
|
|
1265
|
+
prepareStageRuntimeUnblock(context, input);
|
|
1266
|
+
}
|
|
1267
|
+
catch (error) {
|
|
1268
|
+
return prepareError(error, { parameter: "stage/work/summary" });
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
if (route.key === "zcode invocation prepare") {
|
|
1272
|
+
const value = prepareJsonText(await readStdin(io.stdin), { file: "<stdin>", label: "invocation issuance" }).value;
|
|
1273
|
+
const payload = value && typeof value === "object" ? value : {};
|
|
1274
|
+
try {
|
|
1275
|
+
prepared.lifecycleIssuance = prepareLifecycleIssuance(payload.command, payload.scope);
|
|
1276
|
+
}
|
|
1277
|
+
catch (error) {
|
|
1278
|
+
if (error instanceof AppError && ["invocation_command_invalid", "invocation_scope_invalid"].includes(error.code))
|
|
1279
|
+
return prepareError(new AppError("usage", error.message, 2, { cause_code: error.code }), { parameter: "stdin" });
|
|
1280
|
+
throw error;
|
|
1281
|
+
}
|
|
1282
|
+
assertLifecycleInvocationCurrent(context, prepared.lifecycleIssuance.scope, prepared.lifecycleIssuance.command);
|
|
1283
|
+
}
|
|
1284
|
+
if (route.key === "work add-batch") {
|
|
1285
|
+
const file = requiredOption(parsed, "file");
|
|
1286
|
+
try {
|
|
1287
|
+
assertGenericWorkBatchAllowed(context, requiredOption(parsed, "parent"));
|
|
1288
|
+
prepared.workBatch = prepareWorkBatch(context, { parentWorkId: requiredOption(parsed, "parent"), file,
|
|
1289
|
+
...(optionalOption(parsed, "stage") ? { stage: optionalOption(parsed, "stage") } : {}),
|
|
1290
|
+
...(optionalOption(parsed, "request-key") ? { requestKey: optionalOption(parsed, "request-key") } : {}) });
|
|
1291
|
+
}
|
|
1292
|
+
catch (error) {
|
|
1293
|
+
return prepareError(error, { parameter: "file/parent", file });
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
if (route.key === "work repair add") {
|
|
1297
|
+
const taskFile = optionalOption(parsed, "task-file"), taskStdin = hasOption(parsed, "task-stdin");
|
|
1298
|
+
if (Boolean(taskFile) === taskStdin)
|
|
1299
|
+
throw new AppError("usage", "work repair add requires exactly one of --task-file or --task-stdin", 2);
|
|
1300
|
+
if (taskStdin && io.stdin?.isTTY)
|
|
1301
|
+
throw new AppError("usage", "--task-stdin requires piped or redirected input", 2);
|
|
1302
|
+
const task = taskFile ? prepareTextFile(taskFile, "Repair task").text : await readStdin(io.stdin);
|
|
1303
|
+
prepared.workRepair = prepareCliWorkRepair(context, parsed, task, scopeProjectRoot);
|
|
1304
|
+
}
|
|
1305
|
+
if (route.key === "run config set" || route.key === "run vars set") {
|
|
1306
|
+
const input = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 1, "run-id"), key: requiredOption(parsed, "key"), value: requiredOption(parsed, "value") };
|
|
1307
|
+
try {
|
|
1308
|
+
if (route.key === "run config set")
|
|
1309
|
+
prepareFlowRunConfig(context, { ...input, reason: requiredOption(parsed, "reason") });
|
|
1310
|
+
else
|
|
1311
|
+
prepareFlowRunVariable(context, input);
|
|
1312
|
+
}
|
|
1313
|
+
catch (error) {
|
|
1314
|
+
return prepareError(error, { parameter: "run config/vars" });
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
if (route.key === "schema validate") {
|
|
1318
|
+
try {
|
|
1319
|
+
prepared.schemaValidation = dispatchSchema(context, command, parsed);
|
|
1320
|
+
}
|
|
1321
|
+
catch (error) {
|
|
1322
|
+
return prepareError(error, { parameter: "file", file: requiredOption(parsed, "file") });
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
if (route.key === "project config set") {
|
|
1326
|
+
try {
|
|
1327
|
+
prepareProjectConfigValue(requiredOption(parsed, "key"), requiredOption(parsed, "value"));
|
|
1328
|
+
}
|
|
1329
|
+
catch (error) {
|
|
1330
|
+
return prepareError(error, { parameter: "key/value" });
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
if (family === "session" && command === "register") {
|
|
1334
|
+
try {
|
|
1335
|
+
prepared.sessionPayload = prepareFlowSessionPayload({ payloadBase64: optionalOption(parsed, "payload-base64"), payloadFile: optionalOption(parsed, "payload-file"), payloadJson: optionalOption(parsed, "payload-json") });
|
|
1336
|
+
}
|
|
1337
|
+
catch (error) {
|
|
1338
|
+
return prepareError(error, { parameter: "session payload" });
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
const run = (projectRoot, runId) => {
|
|
1342
|
+
try {
|
|
1343
|
+
const project = requireProjectByRoot(context, resolveProjectRoot(projectRoot));
|
|
1344
|
+
const row = context.db.get("SELECT run_root, workspace_root, index_json FROM runs WHERE project_id = ? AND id = ?", [project.id, runId]);
|
|
1345
|
+
if (!row)
|
|
1346
|
+
throw new AppError("not_found", "RUN is not registered", 2, { run_id: runId });
|
|
1347
|
+
if (!row.run_root)
|
|
1348
|
+
throw new AppError("runtime_missing", "RUN artifact root is unavailable", 1, { run_id: runId });
|
|
1349
|
+
return { runRoot: row.run_root, workspaceRoot: row.workspace_root, indexJson: row.index_json };
|
|
1350
|
+
}
|
|
1351
|
+
catch (error) {
|
|
1352
|
+
return prepareError(error, { parameter: "run-id", run_id: runId });
|
|
1353
|
+
}
|
|
1354
|
+
};
|
|
1355
|
+
const json = (file, stdin, label, schema) => {
|
|
1356
|
+
if (stdin) {
|
|
1357
|
+
// The caller awaits this helper; the synchronous facade below is only
|
|
1358
|
+
// used for file inputs. Keep stdin handling in the stage branch.
|
|
1359
|
+
throw new AppError("usage", `${label} stdin preparation must be awaited`, 2);
|
|
1360
|
+
}
|
|
1361
|
+
if (!file)
|
|
1362
|
+
throw new AppError("usage", `${label} input is required`, 2);
|
|
1363
|
+
return prepareJsonSchema(file, label, schema);
|
|
1364
|
+
};
|
|
1365
|
+
if (family === "stage" && command === "start") {
|
|
1366
|
+
const stage = requiredOption(parsed, "stage");
|
|
1367
|
+
if (stage !== "specify" && ["bootstrap", "intake-file", "intake-stdin"].some(option => hasOption(parsed, option)))
|
|
1368
|
+
throw new AppError("usage", "--bootstrap and intake inputs are only supported for SPECIFY", 2);
|
|
1369
|
+
requiredOption(parsed, "project-root");
|
|
1370
|
+
if (!hasOption(parsed, "bootstrap"))
|
|
1371
|
+
requiredPosition(parsed, 0, "run-id");
|
|
1372
|
+
if (hasOption(parsed, "session-id"))
|
|
1373
|
+
throw new AppError("usage", "--session-id is managed by the harness lifecycle adapter and is not accepted", 2);
|
|
1374
|
+
const contextFile = optionalOption(parsed, "context-file"), contextSha256 = optionalOption(parsed, "context-sha256");
|
|
1375
|
+
if (Boolean(contextFile) !== Boolean(contextSha256))
|
|
1376
|
+
throw new AppError("usage", "stage start requires --context-file together with --context-sha256", 2);
|
|
1377
|
+
if ([hasOption(parsed, "intake-file"), hasOption(parsed, "intake-stdin"), Boolean(contextFile)].filter(Boolean).length > 1)
|
|
1378
|
+
throw new AppError("usage", "Use exactly one of --intake-file, --intake-stdin or --context-file", 2);
|
|
1379
|
+
if (!hasOption(parsed, "bootstrap")) {
|
|
1380
|
+
const bundle = readFlowRunExecutionBundle(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") });
|
|
1381
|
+
if (bundle)
|
|
1382
|
+
prepared.stageBindingBundle = { stage, bundle, ...(contextSha256 ? { contextSha256 } : {}) };
|
|
1383
|
+
if (bundle && (["interactive", "mb-init", "mb-upgrade", "mb-distill", "mb-fix", "review-fix"].includes(bundle.descriptor.descriptor.id) || isDiagnosticFlow(bundle.descriptor.descriptor.id) || isDeliveryFlow(bundle.descriptor.descriptor.id))) {
|
|
1384
|
+
bundle.descriptor.stage(stage);
|
|
1385
|
+
if (hasOption(parsed, "dir"))
|
|
1386
|
+
throw new AppError("usage", "Memory stage layout belongs to its frozen descriptor", 2);
|
|
1387
|
+
const loaded = contextFile && contextSha256 ? loadExternalStageContext({ file: contextFile, expectedSha256: contextSha256, stage }) : undefined;
|
|
1388
|
+
if (loaded)
|
|
1389
|
+
prepared.stageStart = { externalContext: loaded };
|
|
1390
|
+
prepared.memoryStage = { operation: "start", bytes: loaded ? Buffer.from(loaded.bytes) : Buffer.alloc(0),
|
|
1391
|
+
flowId: isDeliveryFlow(bundle.descriptor.descriptor.id) ? bundle.descriptor.descriptor.id : bundle.descriptor.descriptor.id === "interactive" ? "interactive" : bundle.descriptor.descriptor.id === "review-fix" ? "review-fix" : bundle.descriptor.descriptor.id === "mb-fix" ? "mb-fix" : bundle.descriptor.descriptor.id === "mb-distill" ? "mb-distill" : isDiagnosticFlow(bundle.descriptor.descriptor.id) ? bundle.descriptor.descriptor.id : bundle.descriptor.descriptor.id === "mb-upgrade" ? "mb-upgrade" : "mb-init" };
|
|
1392
|
+
return prepared;
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
if (contextFile && contextSha256) {
|
|
1396
|
+
try {
|
|
1397
|
+
prepared.stageStart = { externalContext: loadExternalStageContext({ file: contextFile, expectedSha256: contextSha256, stage }) };
|
|
1398
|
+
}
|
|
1399
|
+
catch (error) {
|
|
1400
|
+
return prepareError(error, { file: contextFile, parameter: "context-file" });
|
|
1401
|
+
}
|
|
1402
|
+
if (!hasOption(parsed, "bootstrap")) {
|
|
1403
|
+
const { runRoot } = run(requiredOption(parsed, "project-root"), requiredPosition(parsed, 0, "run-id"));
|
|
1404
|
+
prepareExternalStageContext({ stageRoot: path.join(runRoot, vnextStageDirectory(stage)), loaded: prepared.stageStart.externalContext });
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
if (stage === "specify") {
|
|
1408
|
+
const intakeFile = optionalOption(parsed, "intake-file");
|
|
1409
|
+
if (intakeFile)
|
|
1410
|
+
prepared.stageStart = { intakeMarkdown: prepareTextFile(intakeFile, "SPECIFY intake").text };
|
|
1411
|
+
else if (hasOption(parsed, "intake-stdin"))
|
|
1412
|
+
prepared.stageStart = { intakeMarkdown: await readStdin(io.stdin) };
|
|
1413
|
+
else if (prepared.stageStart?.externalContext)
|
|
1414
|
+
prepared.stageStart.intakeMarkdown = runnerIntake(prepared.stageStart.externalContext);
|
|
1415
|
+
const projectRoot = requiredOption(parsed, "project-root");
|
|
1416
|
+
const bootstrap = hasOption(parsed, "bootstrap");
|
|
1417
|
+
if (isVnextSpecifyFlow(projectRoot) && (bootstrap || isVnextSpecifyRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") }))) {
|
|
1418
|
+
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") } : {}) });
|
|
1419
|
+
}
|
|
1420
|
+
else if (bootstrap) {
|
|
1421
|
+
if (hasOption(parsed, "intake-stdin") || contextFile)
|
|
1422
|
+
throw new AppError("usage", "Legacy SPECIFY bootstrap supports --intake-file, not stdin or runner context", 2);
|
|
1423
|
+
prepared.legacyStageStart = prepareLegacyStageStart(context, { projectRoot, stage, bootstrap: { subject: requiredOption(parsed, "subject"), ...(intakeFile ? { intakeFile } : {}) }, ...(optionalOption(parsed, "dir") ? { dir: optionalOption(parsed, "dir") } : {}) }, prepared.stageStart?.intakeMarkdown);
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
if (stage === "merge" && isVnextMergeRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1427
|
+
prepared.mergeStart = prepareVnextMergeStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1428
|
+
}
|
|
1429
|
+
if (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1430
|
+
prepared.protocolizeStart = prepareVnextProtocolizeStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1431
|
+
}
|
|
1432
|
+
if (stage === "plan" && isVnextPlanRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1433
|
+
prepared.planStart = prepareVnextPlanStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1434
|
+
}
|
|
1435
|
+
if (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1436
|
+
prepared.planReviewStart = prepareVnextPlanReviewStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1437
|
+
}
|
|
1438
|
+
if (stage === "code" && isVnextPlanRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1439
|
+
prepared.codeStart = prepareVnextCodeStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1440
|
+
}
|
|
1441
|
+
if (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1442
|
+
prepared.codeReviewStart = prepareVnextCodeReviewStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1443
|
+
}
|
|
1444
|
+
if (![prepared.specifyStart, prepared.protocolizeStart, prepared.planStart, prepared.planReviewStart, prepared.codeStart, prepared.codeReviewStart, prepared.mergeStart, prepared.legacyStageStart].some(Boolean)) {
|
|
1445
|
+
if (prepared.stageStart?.externalContext)
|
|
1446
|
+
throw new AppError("usage", "--context-file is currently supported only by vNext flow stages", 2);
|
|
1447
|
+
prepared.legacyStageStart = prepareLegacyStageStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), stage, ...(optionalOption(parsed, "dir") ? { dir: optionalOption(parsed, "dir") } : {}) });
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
if (family === "stage" && (command === "pause" || command === "resume")) {
|
|
1451
|
+
requiredPosition(parsed, 0, "run-id");
|
|
1452
|
+
requiredOption(parsed, "project-root");
|
|
1453
|
+
requiredOption(parsed, "stage");
|
|
1454
|
+
requiredOption(parsed, "work");
|
|
1455
|
+
const fileParameter = command === "pause" ? "question-file" : "answer-file";
|
|
1456
|
+
const stdinParameter = command === "pause" ? "question-stdin" : "answer-stdin";
|
|
1457
|
+
const inputFile = optionalOption(parsed, fileParameter);
|
|
1458
|
+
const fromStdin = hasOption(parsed, stdinParameter);
|
|
1459
|
+
if (Boolean(inputFile) === fromStdin)
|
|
1460
|
+
throw new AppError("usage", `stage ${command} requires exactly one of --${fileParameter} or --${stdinParameter}`, 2);
|
|
1461
|
+
if (fromStdin && io.stdin?.isTTY)
|
|
1462
|
+
throw new AppError("usage", `--${stdinParameter} requires piped or redirected input`, 2);
|
|
1463
|
+
prepared.interactionText = inputFile ? prepareTextFile(inputFile, command === "pause" ? "User question" : "User answer").text : await readStdin(io.stdin);
|
|
1464
|
+
if (!prepared.interactionText.trim())
|
|
1465
|
+
return prepareError(new AppError("validation", `stage ${command} requires non-empty input`, 2), { parameter: inputFile ? fileParameter : stdinParameter });
|
|
1466
|
+
const input = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), stage: requiredOption(parsed, "stage"), workId: requiredOption(parsed, "work") };
|
|
1467
|
+
try {
|
|
1468
|
+
if (command === "pause")
|
|
1469
|
+
prepareStagePause(context, { ...input, question: prepared.interactionText });
|
|
1470
|
+
else
|
|
1471
|
+
prepared.resumeEvidence = prepareStageResume(context, { ...input, answer: prepared.interactionText }).evidence;
|
|
1472
|
+
}
|
|
1473
|
+
catch (error) {
|
|
1474
|
+
return prepareError(error, { parameter: "stage/work" });
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
if (family === "work" && command === "finish") {
|
|
1478
|
+
const retryReceiptId = optionalOption(parsed, "retry-check"), retryReason = prepared.retryReason;
|
|
1479
|
+
if (Boolean(retryReceiptId) !== Boolean(retryReason))
|
|
1480
|
+
throw new AppError("usage", "--retry-check and --reason must be supplied together", 2);
|
|
1481
|
+
const resultFile = optionalOption(parsed, "result-file");
|
|
1482
|
+
const resultStdin = hasOption(parsed, "result-stdin");
|
|
1483
|
+
if (Boolean(resultFile) === resultStdin)
|
|
1484
|
+
throw new AppError("usage", "work finish requires exactly one of --result-file or --result-stdin", 2);
|
|
1485
|
+
const maxBytes = workResultByteLimit(context, requiredPosition(parsed, 0, "work-id"));
|
|
1486
|
+
prepared.workFinish = { result: resultFile ? prepareTextFile(resultFile, "Work result", maxBytes).text : await readStdin(io.stdin, maxBytes) };
|
|
1487
|
+
prepareWorkResult(context, requiredPosition(parsed, 0, "work-id"), prepared.workFinish.result, progress);
|
|
1488
|
+
}
|
|
1489
|
+
if (family === "stage" && command === "fanout" && requiredPosition(parsed, 0, "fanout action") === "reconcile") {
|
|
1490
|
+
prepared.observations = prepareNativeObservations(prepareJsonFile(requiredOption(parsed, "observations-file"), "native observations").value);
|
|
1491
|
+
}
|
|
1492
|
+
if (family === "stage" && command === "native" && requiredPosition(parsed, 0, "native action") === "observe") {
|
|
1493
|
+
prepared.observations = prepareNativeObservations(prepareJsonFile(requiredOption(parsed, "observations-file"), "native observations").value);
|
|
1494
|
+
}
|
|
1495
|
+
if (family === "memory" && (["compose", "corrections"].includes(command ?? "") && parsed.positional[0] === "register"
|
|
1496
|
+
|| command === "repair" && parsed.positional[0] === "publication-reserve"
|
|
1497
|
+
|| command === "distill" && parsed.positional[0] === "publish")) {
|
|
1498
|
+
try {
|
|
1499
|
+
const bytes = prepareFileBytes(requiredOption(parsed, "result-file"), `Memory ${command} result`, 1024 * 1024).bytes;
|
|
1500
|
+
if (!bytes.length || bytes.length > 1024 * 1024)
|
|
1501
|
+
throw new AppError("usage", "Memory operation result exceeds its byte bound", 2, { phase: "prepare", effect: "no_effect", recoverable: true });
|
|
1502
|
+
prepared.memoryOperationBytes = bytes;
|
|
1503
|
+
}
|
|
1504
|
+
catch (error) {
|
|
1505
|
+
if (command === "compose" && isAppError(error) && error.details.phase === "prepare"
|
|
1506
|
+
&& error.details.effect === "no_effect" && error.details.recoverable === true)
|
|
1507
|
+
Object.assign(error.details, compositionProposalRetry(context, parsed));
|
|
1508
|
+
throw error;
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
if (family === "memory" && command === "fix" && ["scope", "publish", "integrate"].includes(parsed.positional[0] ?? "") && hasOption(parsed, "result-file")) {
|
|
1512
|
+
const file = requiredOption(parsed, "result-file"), input = prepareTextFile(file, "Memory repair proposal", 1024 * 1024);
|
|
1513
|
+
prepared.memoryFixProposal = prepareJsonText(input.text, { file, label: "Memory repair proposal" }).value;
|
|
1514
|
+
if (!prepared.memoryFixProposal || typeof prepared.memoryFixProposal !== "object" || Array.isArray(prepared.memoryFixProposal))
|
|
1515
|
+
throw new AppError("usage", "Memory repair proposal must be a JSON object", 2, { phase: "prepare", effect: "no_effect" });
|
|
1516
|
+
}
|
|
1517
|
+
if (family !== "stage" || command !== "finish")
|
|
1518
|
+
return prepared;
|
|
1519
|
+
const projectRoot = requiredOption(parsed, "project-root");
|
|
1520
|
+
const runId = requiredPosition(parsed, 0, "run-id");
|
|
1521
|
+
const stage = requiredOption(parsed, "stage");
|
|
1522
|
+
if (hasOption(parsed, "status"))
|
|
1523
|
+
throw new AppError("usage", "Stage status belongs in the semantic input file", 2);
|
|
1524
|
+
if (["result-file", "semantic-file", "data", "result-stdin"].filter(key => hasOption(parsed, key)).length > 1)
|
|
1525
|
+
throw new AppError("usage", "Use exactly one result input: --result-file, --semantic-file, --data or --result-stdin", 2);
|
|
1526
|
+
if (Boolean(optionalOption(parsed, "retry-check")) !== Boolean(prepared.retryReason))
|
|
1527
|
+
throw new AppError("usage", "--retry-check requires recovery evidence", 2);
|
|
1528
|
+
const resultStdin = hasOption(parsed, "result-stdin");
|
|
1529
|
+
const semanticFile = optionalOption(parsed, "semantic-file") ?? optionalOption(parsed, "data");
|
|
1530
|
+
const resultFile = optionalOption(parsed, "result-file") ?? semanticFile;
|
|
1531
|
+
const runtime = run(projectRoot, runId);
|
|
1532
|
+
const retainedFlowId = JSON.parse(runtime.indexJson).execution_bundle?.flow_id;
|
|
1533
|
+
if (["interactive", "mb-init", "mb-upgrade", "mb-distill", "mb-fix", "review-fix"].includes(retainedFlowId) || isDiagnosticFlow(retainedFlowId) || isDeliveryFlow(retainedFlowId)) {
|
|
1534
|
+
const bundle = readFlowRunExecutionBundle(context, { projectRoot, runId });
|
|
1535
|
+
if (!bundle || !["interactive", "mb-init", "mb-upgrade", "mb-distill", "mb-fix", "review-fix"].includes(bundle.descriptor.descriptor.id) && !isDiagnosticFlow(bundle.descriptor.descriptor.id) && !isDeliveryFlow(bundle.descriptor.descriptor.id))
|
|
1536
|
+
throw new AppError("cli_input_unprepared", "Registered RUN bundle is unavailable", 1);
|
|
1537
|
+
bundle.descriptor.stage(stage);
|
|
1538
|
+
if (["dir", "outcome", "decision-file", "verification-file", "retry-check", "reason", "reason-file", "request", "work"].some(option => hasOption(parsed, option)))
|
|
1539
|
+
throw new AppError("usage", "Memory stage finish requires its registered JSON result, not legacy result options", 2);
|
|
1540
|
+
if (Boolean(resultFile) === resultStdin)
|
|
1541
|
+
throw new AppError("usage", "Memory stage finish requires exactly one result input", 2);
|
|
1542
|
+
const bytes = resultStdin ? Buffer.from(await readStdin(io.stdin, 1024 * 1024)) : prepareFileBytes(resultFile, "Memory stage result", 1024 * 1024).bytes;
|
|
1543
|
+
if (!bytes.length || bytes.length > 1024 * 1024)
|
|
1544
|
+
throw new AppError("usage", "Memory stage result exceeds its byte bound", 2);
|
|
1545
|
+
prepared.memoryStage = { operation: "finish", bytes, flowId: isDeliveryFlow(bundle.descriptor.descriptor.id) ? bundle.descriptor.descriptor.id : bundle.descriptor.descriptor.id === "interactive" ? "interactive" : bundle.descriptor.descriptor.id === "review-fix" ? "review-fix" : bundle.descriptor.descriptor.id === "mb-fix" ? "mb-fix" : bundle.descriptor.descriptor.id === "mb-distill" ? "mb-distill" : isDiagnosticFlow(bundle.descriptor.descriptor.id) ? bundle.descriptor.descriptor.id : bundle.descriptor.descriptor.id === "mb-upgrade" ? "mb-upgrade" : "mb-init" };
|
|
1546
|
+
return prepared;
|
|
1547
|
+
}
|
|
1548
|
+
const terminalReplay = (name) => {
|
|
1549
|
+
let index;
|
|
1550
|
+
try {
|
|
1551
|
+
index = JSON.parse(runtime.indexJson);
|
|
1552
|
+
}
|
|
1553
|
+
catch (error) {
|
|
1554
|
+
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) });
|
|
1555
|
+
}
|
|
1556
|
+
return index.stage_runs?.some(entry => entry.stage === name && entry.status === "done") === true
|
|
1557
|
+
&& fs.existsSync(path.join(runtime.runRoot, vnextStageDirectory(name), "stage-report.json"));
|
|
1558
|
+
};
|
|
1559
|
+
const acceptStageInputs = (allowed) => {
|
|
1560
|
+
const options = allowed.split(" ");
|
|
1561
|
+
for (const name of ["dir", "semantic-file", "data", "result-file", "result-stdin", "outcome", "decision-file", "verification-file", "retry-check", "reason", "reason-file", "request", "work"]) {
|
|
1562
|
+
if (hasOption(parsed, name) && !options.includes(name === "reason-file" ? "reason" : name))
|
|
1563
|
+
throw new AppError("usage", `stage finish ${stage} does not accept --${name} for this flow`, 2, { parameter: name });
|
|
1564
|
+
}
|
|
1565
|
+
};
|
|
1566
|
+
const fromStdin = async (label, schema) => {
|
|
1567
|
+
const value = prepareJsonText(await readStdin(io.stdin), { file: "<stdin>", label });
|
|
1568
|
+
try {
|
|
1569
|
+
validateSchema({ ...schema, file: value.file, data: value.value });
|
|
1570
|
+
}
|
|
1571
|
+
catch (error) {
|
|
1572
|
+
return prepareError(error, { file: value.file, label });
|
|
1573
|
+
}
|
|
1574
|
+
return value;
|
|
1575
|
+
};
|
|
1576
|
+
if (stage === "specify" && isVnextSpecifyRun(context, { projectRoot, runId })) {
|
|
1577
|
+
acceptStageInputs("result-file semantic-file data result-stdin outcome");
|
|
1578
|
+
const outcome = optionalOption(parsed, "outcome");
|
|
1579
|
+
if (!outcome || !["specified", "failed", "cancelled"].includes(outcome))
|
|
1580
|
+
throw new AppError("usage", "vNext SPECIFY requires --outcome specified|failed|cancelled; use stage pause for a user question", 2);
|
|
1581
|
+
if (Boolean(resultFile) === resultStdin)
|
|
1582
|
+
throw new AppError("usage", "vNext SPECIFY finish requires exactly one result input", 2);
|
|
1583
|
+
const schema = { schemaName: "vnext-specify", projectRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot };
|
|
1584
|
+
const input = resultStdin ? await fromStdin("SPECIFY result", schema) : json(resultFile, false, "SPECIFY result", schema);
|
|
1585
|
+
prepared.stageFinish = { value: input.value, text: input.text };
|
|
1586
|
+
}
|
|
1587
|
+
else if (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId })) {
|
|
1588
|
+
acceptStageInputs("result-file semantic-file data");
|
|
1589
|
+
if (!resultFile || resultStdin || hasOption(parsed, "outcome"))
|
|
1590
|
+
throw new AppError("usage", "vNext PROTOCOLIZE requires exactly --result-file", 2);
|
|
1591
|
+
let absolute;
|
|
1592
|
+
try {
|
|
1593
|
+
absolute = prepareProtocolizeResultPath(runtime.runRoot, resultFile);
|
|
1594
|
+
}
|
|
1595
|
+
catch (error) {
|
|
1596
|
+
return prepareError(error, { file: resultFile });
|
|
1597
|
+
}
|
|
1598
|
+
const input = json(absolute, false, "PROTOCOLIZE result", { schemaName: "vnext-protocolize-result", projectRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot });
|
|
1599
|
+
prepared.stageFinish = { value: input.value, text: input.text };
|
|
1600
|
+
}
|
|
1601
|
+
else if (stage === "plan" && isVnextPlanRun(context, { projectRoot, runId })) {
|
|
1602
|
+
acceptStageInputs("");
|
|
1603
|
+
if (hasOption(parsed, "code-work-batch-file"))
|
|
1604
|
+
throw new AppError("usage", "vNext PLAN derives its CODE batch from accepted semantic plans; do not supply a batch file", 2);
|
|
1605
|
+
try {
|
|
1606
|
+
prepared.planFinish = prepareVnextPlanFinish(context, { projectRoot, runId });
|
|
1607
|
+
}
|
|
1608
|
+
catch (error) {
|
|
1609
|
+
return prepareError(error, { parameter: "PLAN artifacts" });
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
else if (stage === "merge" && isVnextMergeRun(context, { projectRoot, runId })) {
|
|
1613
|
+
acceptStageInputs("request work retry-check reason");
|
|
1614
|
+
if (resultStdin || resultFile || hasOption(parsed, "outcome"))
|
|
1615
|
+
throw new AppError("usage", "vNext MERGE uses the Work result path returned by stage start", 2);
|
|
1616
|
+
requiredOption(parsed, "request");
|
|
1617
|
+
requiredOption(parsed, "work");
|
|
1618
|
+
try {
|
|
1619
|
+
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 } : {}) });
|
|
1620
|
+
}
|
|
1621
|
+
catch (error) {
|
|
1622
|
+
return prepareError(error, { parameter: "MERGE finish" });
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
else if (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId })) {
|
|
1626
|
+
acceptStageInputs("decision-file");
|
|
1627
|
+
if (resultStdin || hasOption(parsed, "outcome"))
|
|
1628
|
+
throw new AppError("usage", "vNext PLAN-REVIEW uses --decision-file", 2);
|
|
1629
|
+
const decision = optionalOption(parsed, "decision-file") ?? path.join(runtime.runRoot, "04-plan-review", "decision.json");
|
|
1630
|
+
const input = json(decision, false, "PLAN-REVIEW decision", { schemaName: "plan-review-decision", projectRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot });
|
|
1631
|
+
prepared.stageFinish = { value: input.value, text: input.text };
|
|
1632
|
+
}
|
|
1633
|
+
else if (stage === "code" && isVnextPlanRun(context, { projectRoot, runId })) {
|
|
1634
|
+
acceptStageInputs("verification-file retry-check reason");
|
|
1635
|
+
if (semanticFile || resultFile || resultStdin || hasOption(parsed, "outcome"))
|
|
1636
|
+
throw new AppError("usage", "vNext CODE finish accepts only --verification-file", 2);
|
|
1637
|
+
const verification = optionalOption(parsed, "verification-file");
|
|
1638
|
+
if (!verification)
|
|
1639
|
+
throw new AppError("usage", "vNext CODE finish requires --verification-file", 2);
|
|
1640
|
+
if (!terminalReplay(stage)) {
|
|
1641
|
+
const input = json(verification, false, "CODE verification", { schemaName: "code-verification", projectRoot: runtime.workspaceRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot });
|
|
1642
|
+
prepared.stageFinish = { value: input.value, text: input.text };
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
else if (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId })) {
|
|
1646
|
+
acceptStageInputs("decision-file retry-check reason");
|
|
1647
|
+
if (resultStdin || semanticFile || resultFile || hasOption(parsed, "outcome"))
|
|
1648
|
+
throw new AppError("usage", "vNext CODE-REVIEW uses --decision-file", 2);
|
|
1649
|
+
const decision = optionalOption(parsed, "decision-file") ?? path.join(runtime.runRoot, "06-code-review", "decision.json");
|
|
1650
|
+
if (!terminalReplay(stage)) {
|
|
1651
|
+
const input = prepareCodeReviewDecision(runtime.runRoot, optionalOption(parsed, "decision-file"));
|
|
1652
|
+
try {
|
|
1653
|
+
validateSchema({ schemaName: "code-review-decision", file: decision, data: input.value, projectRoot: runtime.workspaceRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot });
|
|
1654
|
+
}
|
|
1655
|
+
catch (error) {
|
|
1656
|
+
return prepareError(error, { file: decision, label: "CODE-REVIEW decision" });
|
|
1657
|
+
}
|
|
1658
|
+
prepared.stageFinish = input;
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
else {
|
|
1662
|
+
acceptStageInputs("dir semantic-file data");
|
|
1663
|
+
if (hasOption(parsed, "outcome") || hasOption(parsed, "result-file") || resultStdin)
|
|
1664
|
+
throw new AppError("usage", "--outcome, --result-file and --result-stdin are only available for vNext SPECIFY", 2);
|
|
1665
|
+
prepared.legacyStageFinish = prepareStageFinish(context, { projectRoot, runId, stage, ...(optionalOption(parsed, "dir") ? { dir: optionalOption(parsed, "dir") } : {}), ...(semanticFile ? { semanticFile } : {}) });
|
|
1666
|
+
}
|
|
1667
|
+
return prepared;
|
|
1668
|
+
}
|
|
1669
|
+
async function dispatch(args, context, io, scopeProjectRoot = null, classification = classifyCliOperation(args, context.env), output = parseOutputOptions(args), prepared = {}) {
|
|
1670
|
+
if (!prepared.route)
|
|
1671
|
+
throw new AppError("command_not_prepared", "Command dispatch requires prepared arguments", 1);
|
|
1672
|
+
const { family, command, parsed } = prepared.route;
|
|
1673
|
+
if (family === "zcode" && command === "invocation" && parsed.positional[0] === "prepare") {
|
|
1674
|
+
const payload = prepared.lifecycleIssuance;
|
|
1675
|
+
if (!payload)
|
|
1676
|
+
throw new AppError("command_not_prepared", "Invocation issuance requires prepared input", 1);
|
|
1677
|
+
assertLifecycleInvocationCurrent(context, payload.scope, payload.command);
|
|
1678
|
+
return { ok: true, ...issueLifecycleInvocation(context, payload.command, payload.scope) };
|
|
1679
|
+
}
|
|
1680
|
+
if (family === "storage" && command === "migrate-writer") {
|
|
1681
|
+
if (parsed.positional.length || [...parsed.options.keys()].some(key => !["database", "backup", "offline"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
1682
|
+
throw new AppError("usage", "Usage: dd-flow storage migrate-writer --database ABSOLUTE_DB --backup NEW_ABSOLUTE_FILE --offline", 2);
|
|
1683
|
+
if (parsed.options.get("offline")?.[0] !== flagValue)
|
|
1684
|
+
throw new AppError("usage", "Writer migration requires --offline after draining all local and external runtime owners", 2);
|
|
1685
|
+
return { ok: true, ...migrateStoreWriter(requiredOption(parsed, "database"), requiredOption(parsed, "backup")) };
|
|
1686
|
+
}
|
|
507
1687
|
if (family === "engine") {
|
|
508
|
-
return dispatchEngine(context, command, parsed, classification);
|
|
1688
|
+
return dispatchEngine(context, command, parsed, classification, prepared);
|
|
509
1689
|
}
|
|
510
1690
|
if (family === "status") {
|
|
511
|
-
const rootParsed = parseArgs([command ?? "", ...rest]);
|
|
512
1691
|
return getRuntimeStatus(context, {
|
|
513
|
-
projectRoot: optionalOption(
|
|
514
|
-
checkRegistry: hasOption(
|
|
1692
|
+
projectRoot: optionalOption(parsed, "project-root") ?? optionalOption(parsed, "root"),
|
|
1693
|
+
checkRegistry: hasOption(parsed, "check-registry")
|
|
515
1694
|
});
|
|
516
1695
|
}
|
|
517
1696
|
if (family === "version") {
|
|
518
1697
|
return getCliVersionReport();
|
|
519
1698
|
}
|
|
520
1699
|
if (family === "runtime") {
|
|
521
|
-
return await dispatchRuntime(context, command, parsed, commandProgress(io, output));
|
|
1700
|
+
return await dispatchRuntime(context, command, parsed, commandProgress(io, output), prepared);
|
|
522
1701
|
}
|
|
523
1702
|
if (family === "canon") {
|
|
524
|
-
return dispatchCanon(context, command, parsed);
|
|
1703
|
+
return dispatchCanon(context, command, parsed, prepared);
|
|
525
1704
|
}
|
|
526
1705
|
if (family === "project" && command === "register") {
|
|
527
1706
|
return registerProject(context, { root: requiredOption(parsed, "root") });
|
|
@@ -598,16 +1777,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
598
1777
|
return readyForMerge(context, { protocolId, projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "protocol ready-for-merge") });
|
|
599
1778
|
}
|
|
600
1779
|
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
|
-
});
|
|
1780
|
+
return transitionProtocol(context, protocolTransitionInput(parsed, scopeProjectRoot), prepared.protocolTransition);
|
|
611
1781
|
}
|
|
612
1782
|
if (family === "protocol" && command === "sync-from-run") {
|
|
613
1783
|
const protocolId = requiredPosition(parsed, 0, "protocol-id");
|
|
@@ -632,17 +1802,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
632
1802
|
});
|
|
633
1803
|
}
|
|
634
1804
|
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
|
-
});
|
|
1805
|
+
return transitionProtocol(context, protocolTransitionInput(parsed, scopeProjectRoot), prepared.protocolTransition);
|
|
646
1806
|
}
|
|
647
1807
|
if (family === "plan" && command === "status") {
|
|
648
1808
|
const protocolId = requiredPosition(parsed, 0, "protocol-id");
|
|
@@ -656,17 +1816,25 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
656
1816
|
}
|
|
657
1817
|
if (family === "stage" && command === "fanout") {
|
|
658
1818
|
const action = requiredPosition(parsed, 0, "fanout action");
|
|
659
|
-
const runId = requiredPosition(parsed, 1, "run-id");
|
|
660
1819
|
const stage = requiredOption(parsed, "stage");
|
|
661
1820
|
const projectRoot = requiredScopeProjectRoot(scopeProjectRoot, "stage fanout");
|
|
1821
|
+
const project = requireProjectByRoot(context, resolveProjectRoot(projectRoot));
|
|
1822
|
+
const runId = resolveRun(context, project.id, requiredPosition(parsed, 1, "run-id")).id;
|
|
662
1823
|
if (action === "status")
|
|
663
1824
|
return getVnextFanoutStatus(context, { projectRoot, runId, stage });
|
|
1825
|
+
if (action === "reconcile")
|
|
1826
|
+
return reconcileVnextFanout(context, { projectRoot, runId, stage, observations: prepared.observations ?? prepareJsonFile(requiredOption(parsed, "observations-file"), "native observations").value });
|
|
664
1827
|
if (action === "dispatch") {
|
|
665
1828
|
if (stage === "plan-review")
|
|
666
1829
|
return dispatchVnextPlanReview(context, { projectRoot, runId });
|
|
667
1830
|
return getVnextFanoutStatus(context, { projectRoot, runId, stage });
|
|
668
1831
|
}
|
|
669
|
-
throw new AppError("usage", "Use: dd-flow stage fanout <status|dispatch> <RUN> --stage <stage> --project-root <path>", 2);
|
|
1832
|
+
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);
|
|
1833
|
+
}
|
|
1834
|
+
if (family === "stage" && command === "native") {
|
|
1835
|
+
if (requiredPosition(parsed, 0, "native action") !== "observe")
|
|
1836
|
+
throw new AppError("usage", "Use: dd-flow stage native observe <RUN> --stage <stage> --observations-file <native-observations.json> --project-root <path>", 2);
|
|
1837
|
+
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
1838
|
}
|
|
671
1839
|
if (family === "run" && command === "capacity") {
|
|
672
1840
|
if (requiredPosition(parsed, 0, "capacity action") !== "record")
|
|
@@ -678,16 +1846,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
678
1846
|
return dispatchPlanItem(context, parsed, requiredScopeProjectRoot(scopeProjectRoot, "plan item"));
|
|
679
1847
|
}
|
|
680
1848
|
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
|
-
});
|
|
1849
|
+
return renderWorkerPrompt(context, prepared.workerPrompt?.input ?? workerPromptInput(parsed), prepared.workerPrompt?.value);
|
|
691
1850
|
}
|
|
692
1851
|
if (family === "work" && command === "status") {
|
|
693
1852
|
return getVnextWorkStatus(context, {
|
|
@@ -696,7 +1855,10 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
696
1855
|
});
|
|
697
1856
|
}
|
|
698
1857
|
if (family === "work" && command === "add-batch") {
|
|
699
|
-
|
|
1858
|
+
assertGenericWorkBatchAllowed(context, requiredOption(parsed, "parent"));
|
|
1859
|
+
return addWorkBatch(context, { parentWorkId: requiredOption(parsed, "parent"), file: requiredOption(parsed, "file"), prepared: prepared.workBatch,
|
|
1860
|
+
...(optionalOption(parsed, "stage") ? { stage: optionalOption(parsed, "stage") } : {}),
|
|
1861
|
+
...(optionalOption(parsed, "request-key") ? { requestKey: optionalOption(parsed, "request-key") } : {}) });
|
|
700
1862
|
}
|
|
701
1863
|
if (family === "work" && command === "ls") {
|
|
702
1864
|
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 +1866,9 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
704
1866
|
if (family === "work" && command === "show")
|
|
705
1867
|
return showWork(context, requiredPosition(parsed, 0, "work-id"));
|
|
706
1868
|
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
|
-
});
|
|
1869
|
+
if (!prepared.workRepair)
|
|
1870
|
+
throw new AppError("cli_input_unprepared", "Repair input was not prepared", 1);
|
|
1871
|
+
return addVnextCodeRepair(context, prepared.workRepair.input, prepared.workRepair.value);
|
|
749
1872
|
}
|
|
750
1873
|
if (family === "work" && command === "deps") {
|
|
751
1874
|
const on = parsed.options.get("on") ?? [];
|
|
@@ -753,26 +1876,42 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
753
1876
|
}
|
|
754
1877
|
if (family === "work" && command === "delete")
|
|
755
1878
|
return deleteWork(context, requiredPosition(parsed, 0, "work-id"));
|
|
1879
|
+
if (family === "work" && command === "launch")
|
|
1880
|
+
return await launchExternalWork(context, { workId: requiredPosition(parsed, 0, "work-id"), ...(optionalOption(parsed, "stage") ? { stage: optionalOption(parsed, "stage") } : {}), progress: commandProgress(io, output) });
|
|
756
1881
|
if (family === "work" && command === "start") {
|
|
757
1882
|
const hookEventId = optionalOption(parsed, "hook-event-id");
|
|
1883
|
+
const recoveryId = optionalOption(parsed, "recovery-id");
|
|
758
1884
|
const projectRoot = requiredScopeProjectRoot(scopeProjectRoot, "work start");
|
|
759
|
-
return startWork(context, requiredPosition(parsed, 0, "work-id"), { ...(hookEventId ? { hookEventId } : {}), projectRoot });
|
|
1885
|
+
return startWork(context, requiredPosition(parsed, 0, "work-id"), { ...(hookEventId ? { hookEventId } : {}), ...(recoveryId ? { recoveryId } : {}), projectRoot, progress: commandProgress(io, output) });
|
|
760
1886
|
}
|
|
761
1887
|
if (family === "work" && command === "finish") {
|
|
762
1888
|
const resultFile = optionalOption(parsed, "result-file");
|
|
763
1889
|
const resultStdin = hasOption(parsed, "result-stdin");
|
|
764
1890
|
if (Boolean(resultFile) === resultStdin)
|
|
765
1891
|
throw new AppError("usage", "work finish requires exactly one of --result-file or --result-stdin", 2);
|
|
766
|
-
const
|
|
767
|
-
|
|
1892
|
+
const workId = requiredPosition(parsed, 0, "work-id");
|
|
1893
|
+
const projectRoot = requiredScopeProjectRoot(scopeProjectRoot, "work finish");
|
|
1894
|
+
assertWorkLifecycleOwner(context, { workId, projectRoot, operation: "work_finish", ...(optionalOption(parsed, "hook-event-id") ? { hookEventId: optionalOption(parsed, "hook-event-id") } : {}) });
|
|
1895
|
+
const retryReceiptId = optionalOption(parsed, "retry-check"), retryReason = prepared.retryReason;
|
|
1896
|
+
if (Boolean(retryReceiptId) !== Boolean(retryReason))
|
|
1897
|
+
throw new AppError("usage", "--retry-check and --reason must be supplied together", 2);
|
|
1898
|
+
const maxBytes = workResultByteLimit(context, workId);
|
|
1899
|
+
const result = prepared.workFinish?.result ?? (resultFile ? prepareTextFile(resultFile, "Work result", maxBytes).text : await readStdin(io.stdin, maxBytes));
|
|
1900
|
+
return await finishWork(context, workId, result, commandProgress(io, output), retryReceiptId ? { receiptId: retryReceiptId, reason: retryReason } : undefined);
|
|
1901
|
+
}
|
|
1902
|
+
if (family === "work" && command === "fail") {
|
|
1903
|
+
const workId = requiredPosition(parsed, 0, "work-id");
|
|
1904
|
+
const projectRoot = requiredScopeProjectRoot(scopeProjectRoot, "work fail");
|
|
1905
|
+
assertWorkLifecycleOwner(context, { workId, projectRoot, operation: "work_fail", ...(optionalOption(parsed, "hook-event-id") ? { hookEventId: optionalOption(parsed, "hook-event-id") } : {}) });
|
|
1906
|
+
return failWork(context, workId, requiredOption(parsed, "reason"));
|
|
768
1907
|
}
|
|
769
|
-
if (family === "work" && command === "fail")
|
|
770
|
-
return failWork(context, requiredPosition(parsed, 0, "work-id"), requiredOption(parsed, "reason"));
|
|
771
1908
|
if (family === "work" && command === "cancel")
|
|
772
1909
|
return cancelWork(context, requiredPosition(parsed, 0, "work-id"), requiredOption(parsed, "reason"));
|
|
773
1910
|
if (family === "work" && command === "retry")
|
|
774
1911
|
return retryWork(context, requiredPosition(parsed, 0, "work-id"), requiredOption(parsed, "reason"));
|
|
775
1912
|
if (family === "stage" && command === "start") {
|
|
1913
|
+
if (prepared.stageBindingBundle)
|
|
1914
|
+
context = { ...context, stageBindingBundle: prepared.stageBindingBundle };
|
|
776
1915
|
const dir = optionalOption(parsed, "dir");
|
|
777
1916
|
const bootstrap = hasOption(parsed, "bootstrap");
|
|
778
1917
|
const subject = optionalOption(parsed, "subject");
|
|
@@ -789,12 +1928,45 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
789
1928
|
const requireSessionBinding = hasOption(parsed, "require-session-binding");
|
|
790
1929
|
const projectRoot = requiredOption(parsed, "project-root");
|
|
791
1930
|
const stage = requiredOption(parsed, "stage");
|
|
792
|
-
const externalContext =
|
|
1931
|
+
const externalContext = prepared.stageStart?.externalContext;
|
|
1932
|
+
if (prepared.memoryStage?.operation === "start") {
|
|
1933
|
+
const runId = requiredPosition(parsed, 0, "run-id");
|
|
1934
|
+
const event = hookEventId ?? (currentAssignedLifecycle(context) ? undefined : findRecentMatchingHookEvent(context, {
|
|
1935
|
+
projectId: requireProjectByRoot(context, resolveProjectRoot(projectRoot)).id,
|
|
1936
|
+
matchKey: entityReferenceVariants(runId).map(reference => stageStartMatchKey(reference, stage, projectRoot, contextSha256)),
|
|
1937
|
+
errorCode: "trusted_session_binding_required", operation: "Memory stage start"
|
|
1938
|
+
}).eventKey);
|
|
1939
|
+
const work = (isDeliveryFlow(prepared.memoryStage.flowId) ? startDeliveryStage : prepared.memoryStage.flowId === "interactive" ? startInteractiveStage : prepared.memoryStage.flowId === "review-fix" ? startReviewFixStage : prepared.memoryStage.flowId === "mb-fix" ? startMemoryFixStage : prepared.memoryStage.flowId === "mb-distill" ? startDistillStage : isDiagnosticFlow(prepared.memoryStage.flowId) ? startDiagnosticStage : prepared.memoryStage.flowId === "mb-upgrade" ? startMemoryUpgradeStage : startMemoryStage)(context, { projectRoot, runId, stage, bytes: prepared.memoryStage.bytes });
|
|
1940
|
+
try {
|
|
1941
|
+
const binding = bindStageCoordinatorWork(context, { workId: work.work_id, hookEventId: event, stage,
|
|
1942
|
+
promptPath: work.prompt_path, resultPath: work.result_path, ...(contextSha256 ? { contextSha256 } : {}) });
|
|
1943
|
+
// The frozen semantic prompt is also restored by auxiliary owners in
|
|
1944
|
+
// other CLI contexts. Keep native invocation authority in this current
|
|
1945
|
+
// delivery, not in those immutable instruction/custody bytes.
|
|
1946
|
+
const finish = managedLifecycleCommand(context, runtimeCommand(context, ["stage", "finish", runId,
|
|
1947
|
+
"--stage", stage, "--project-root", projectRoot, "--result-file", work.result_path, "--json"]));
|
|
1948
|
+
const pause = stagePauseCommand(context, { projectRoot, runId, stage, workId: work.work_id });
|
|
1949
|
+
// These assignments belong to this native coordinator delivery, not to
|
|
1950
|
+
// the immutable semantic packet restored by auxiliary owners.
|
|
1951
|
+
const deliveryInstructions = isDeliveryFlow(prepared.memoryStage.flowId) ? issueDeliveryStageCommands(context, { flowId: prepared.memoryStage.flowId, projectRoot, runId, stage, stageRoot: path.dirname(work.result_path) }).instructions : "";
|
|
1952
|
+
const orchestration = registeredStageFanoutDescriptor(context, { projectRoot, runId, stage });
|
|
1953
|
+
const delivery = { ...work, binding, ...(orchestration ? { orchestration } : {}), next: { finish_command: finish, pause_command: pause },
|
|
1954
|
+
worker_prompt_markdown: `${work.worker_prompt_markdown}${deliveryInstructions}\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` };
|
|
1955
|
+
bindLifecyclePublishedPacket(context, delivery.worker_prompt_markdown);
|
|
1956
|
+
return controlLifecycleReply(context, { projectRoot, runId }, delivery);
|
|
1957
|
+
}
|
|
1958
|
+
catch (error) {
|
|
1959
|
+
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",
|
|
1960
|
+
cause: error instanceof AppError ? error.code : "coordinator_binding_failure" });
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
793
1963
|
const claimVnextStageStart = (runId) => {
|
|
1964
|
+
if (currentAssignedLifecycle(context)?.operation === "stage_start")
|
|
1965
|
+
return undefined;
|
|
794
1966
|
registerProject(context, { root: projectRoot });
|
|
795
1967
|
return findRecentMatchingHookEvent(context, {
|
|
796
1968
|
projectId: requireProjectByRoot(context, resolveProjectRoot(projectRoot)).id,
|
|
797
|
-
matchKey: stageStartMatchKey(
|
|
1969
|
+
matchKey: entityReferenceVariants(runId).map((reference) => stageStartMatchKey(reference, stage, projectRoot, contextSha256)),
|
|
798
1970
|
errorCode: "trusted_session_binding_required",
|
|
799
1971
|
operation: `vNext ${stage.toUpperCase()} start`
|
|
800
1972
|
}).eventKey;
|
|
@@ -818,39 +1990,39 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
818
1990
|
})
|
|
819
1991
|
: claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
820
1992
|
}
|
|
821
|
-
const result =
|
|
1993
|
+
const result = sdlcStageHandlers.specify.entry(context, {
|
|
822
1994
|
projectRoot,
|
|
823
|
-
...(externalContext ? { intakeMarkdown: runnerIntake(externalContext) } : intakeFile ? { intakeFile } : { intakeMarkdown: await readStdin(io.stdin) }),
|
|
1995
|
+
...(prepared.stageStart?.intakeMarkdown !== undefined ? { intakeMarkdown: prepared.stageStart.intakeMarkdown } : externalContext ? { intakeMarkdown: runnerIntake(externalContext) } : intakeFile ? { intakeFile } : { intakeMarkdown: await readStdin(io.stdin) }),
|
|
824
1996
|
hookEventId,
|
|
825
1997
|
...(subject ? { slug: subject } : {}),
|
|
826
1998
|
...(externalContext ? { externalContext } : {}),
|
|
827
1999
|
...(!bootstrap ? { runId: requiredPosition(parsed, 0, "run-id") } : {})
|
|
828
|
-
});
|
|
2000
|
+
}, prepared.specifyStart);
|
|
829
2001
|
return attachExternalStageContext(result, externalContext);
|
|
830
2002
|
}
|
|
831
2003
|
if (!bootstrap && stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
832
2004
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
833
|
-
return attachExternalStageContext(
|
|
2005
|
+
return attachExternalStageContext(await sdlcStageHandlers.protocolize.entry(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.protocolizeStart), externalContext);
|
|
834
2006
|
}
|
|
835
2007
|
if (!bootstrap && stage === "plan" && isVnextPlanRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
836
2008
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
837
|
-
return attachExternalStageContext(
|
|
2009
|
+
return attachExternalStageContext(sdlcStageHandlers.plan.entry(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.planStart), externalContext);
|
|
838
2010
|
}
|
|
839
2011
|
if (!bootstrap && stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
840
2012
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
841
|
-
return attachExternalStageContext(
|
|
2013
|
+
return attachExternalStageContext(sdlcStageHandlers["plan-review"].entry(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.planReviewStart), externalContext);
|
|
842
2014
|
}
|
|
843
2015
|
if (!bootstrap && stage === "code" && isVnextPlanRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
844
2016
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
845
|
-
return attachExternalStageContext(await
|
|
2017
|
+
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
2018
|
}
|
|
847
2019
|
if (!bootstrap && stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
848
2020
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
849
|
-
return attachExternalStageContext(
|
|
2021
|
+
return attachExternalStageContext(sdlcStageHandlers["code-review"].entry(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.codeReviewStart), externalContext);
|
|
850
2022
|
}
|
|
851
2023
|
if (!bootstrap && stage === "merge" && isVnextMergeRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
852
2024
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
853
|
-
return attachExternalStageContext(await
|
|
2025
|
+
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
2026
|
}
|
|
855
2027
|
if (intakeStdin) {
|
|
856
2028
|
throw new AppError("usage", "--intake-stdin is only available for vNext SPECIFY", 2);
|
|
@@ -865,7 +2037,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
865
2037
|
...(hookEventId ? { hookEventId } : {}),
|
|
866
2038
|
...(requireSessionBinding ? { requireSessionBinding: true } : {}),
|
|
867
2039
|
...(dir ? { dir } : {})
|
|
868
|
-
});
|
|
2040
|
+
}, prepared.legacyStageStart);
|
|
869
2041
|
}
|
|
870
2042
|
if (family === "stage" && command === "finish") {
|
|
871
2043
|
const dir = optionalOption(parsed, "dir");
|
|
@@ -875,13 +2047,34 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
875
2047
|
const projectRoot = requiredOption(parsed, "project-root");
|
|
876
2048
|
const runId = requiredPosition(parsed, 0, "run-id");
|
|
877
2049
|
const stage = requiredOption(parsed, "stage");
|
|
2050
|
+
const controlledReply = (result) => controlLifecycleReply(context, { projectRoot, runId }, result);
|
|
878
2051
|
const semanticFile = optionalOption(parsed, "semantic-file") ?? optionalOption(parsed, "data");
|
|
879
2052
|
const resultFile = optionalOption(parsed, "result-file") ?? semanticFile;
|
|
880
2053
|
const resultStdin = hasOption(parsed, "result-stdin");
|
|
881
2054
|
const retryCheckId = optionalOption(parsed, "retry-check");
|
|
882
|
-
const retryReason =
|
|
2055
|
+
const retryReason = prepared.retryReason;
|
|
2056
|
+
const hookEventId = optionalOption(parsed, "hook-event-id");
|
|
883
2057
|
if (retryCheckId && !retryReason?.trim())
|
|
884
2058
|
throw new AppError("usage", "--retry-check requires --reason explaining the recovered environment", 2);
|
|
2059
|
+
if (prepared.memoryStage?.operation === "finish") {
|
|
2060
|
+
// Descriptor settlement validates exact coordinator custody and claims
|
|
2061
|
+
// the fresh hook atomically with result acceptance. Do not consume it
|
|
2062
|
+
// through the SDLC lifecycle owner before that settlement.
|
|
2063
|
+
return controlledReply((isDeliveryFlow(prepared.memoryStage.flowId) ? finishDeliveryStage : prepared.memoryStage.flowId === "interactive" ? finishInteractiveStage : prepared.memoryStage.flowId === "review-fix" ? finishReviewFixStage : prepared.memoryStage.flowId === "mb-fix" ? finishMemoryFixStage : prepared.memoryStage.flowId === "mb-distill" ? finishDistillStage : isDiagnosticFlow(prepared.memoryStage.flowId) ? finishDiagnosticStage : prepared.memoryStage.flowId === "mb-upgrade" ? finishMemoryUpgradeStage : finishMemoryStage)(context, { projectRoot, runId, stage, bytes: prepared.memoryStage.bytes,
|
|
2064
|
+
hookEventId: hookEventId ?? (currentAssignedLifecycle(context) ? "" : findRecentMatchingHookEvent(context, {
|
|
2065
|
+
projectId: requireProjectByRoot(context, resolveProjectRoot(projectRoot)).id,
|
|
2066
|
+
matchKey: entityReferenceVariants(runId).map(reference => stageLifecycleMatchKey("stage_finish", reference, stage, projectRoot)),
|
|
2067
|
+
errorCode: "trusted_session_binding_required", operation: "Memory stage finish"
|
|
2068
|
+
}).eventKey) }));
|
|
2069
|
+
}
|
|
2070
|
+
const protectedLifecycle = (stage === "specify" && isVnextSpecifyRun(context, { projectRoot, runId }))
|
|
2071
|
+
|| (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId }))
|
|
2072
|
+
|| (stage === "plan" && isVnextPlanRun(context, { projectRoot, runId }))
|
|
2073
|
+
|| (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId }))
|
|
2074
|
+
|| (stage === "code" && isVnextPlanRun(context, { projectRoot, runId }))
|
|
2075
|
+
|| (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId }));
|
|
2076
|
+
if (protectedLifecycle)
|
|
2077
|
+
assertStageLifecycleOwner(context, { projectRoot, runId, stage, operation: "stage_finish", ...(hookEventId ? { hookEventId } : {}) });
|
|
885
2078
|
if (stage === "specify" && isVnextSpecifyRun(context, { projectRoot, runId })) {
|
|
886
2079
|
const outcome = optionalOption(parsed, "outcome");
|
|
887
2080
|
if (!outcome || !["specified", "failed", "cancelled"].includes(outcome)) {
|
|
@@ -893,84 +2086,92 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
893
2086
|
if (!resultFile && !resultStdin) {
|
|
894
2087
|
throw new AppError("usage", "vNext SPECIFY finish requires --result-file or --result-stdin", 2);
|
|
895
2088
|
}
|
|
896
|
-
return
|
|
2089
|
+
return controlledReply(await sdlcStageHandlers.specify.finish(context, {
|
|
897
2090
|
projectRoot,
|
|
898
2091
|
runId,
|
|
899
|
-
...(resultFile ? { resultFile } : { resultJson: await readStdin(io.stdin) }),
|
|
2092
|
+
...(resultFile ? { resultFile } : prepared.stageFinish?.value ? {} : { resultJson: await readStdin(io.stdin) }),
|
|
2093
|
+
...(prepared.stageFinish?.value ? { result: prepared.stageFinish.value } : {}),
|
|
900
2094
|
outcome: outcome
|
|
901
|
-
});
|
|
2095
|
+
}));
|
|
902
2096
|
}
|
|
903
2097
|
if (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId })) {
|
|
904
2098
|
if (!resultFile || resultStdin || hasOption(parsed, "outcome")) {
|
|
905
2099
|
throw new AppError("usage", "vNext PROTOCOLIZE requires exactly --result-file", 2);
|
|
906
2100
|
}
|
|
907
|
-
return
|
|
2101
|
+
return controlledReply(await sdlcStageHandlers.protocolize.finish(context, { projectRoot, runId, resultFile, ...(prepared.stageFinish?.value ? { result: prepared.stageFinish.value } : {}) }));
|
|
908
2102
|
}
|
|
909
2103
|
if (stage === "plan" && isVnextPlanRun(context, { projectRoot, runId })) {
|
|
910
2104
|
if (optionalOption(parsed, "code-work-batch-file"))
|
|
911
2105
|
throw new AppError("usage", "vNext PLAN derives its CODE batch from accepted semantic plans; do not supply a batch file", 2);
|
|
912
|
-
return
|
|
2106
|
+
return controlledReply(await sdlcStageHandlers.plan.finish(context, { projectRoot, runId, ...(prepared.planFinish ? { prepared: prepared.planFinish } : {}) }));
|
|
913
2107
|
}
|
|
914
2108
|
if (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId })) {
|
|
915
2109
|
if (resultStdin || hasOption(parsed, "outcome"))
|
|
916
2110
|
throw new AppError("usage", "vNext PLAN-REVIEW uses --decision-file", 2);
|
|
917
2111
|
const decisionFile = optionalOption(parsed, "decision-file");
|
|
918
|
-
return
|
|
2112
|
+
return controlledReply(await sdlcStageHandlers["plan-review"].finish(context, { projectRoot, runId, ...(decisionFile ? { decisionFile } : {}), ...(prepared.stageFinish?.value ? { decision: prepared.stageFinish.value, decisionText: prepared.stageFinish.text } : {}) }));
|
|
919
2113
|
}
|
|
920
2114
|
if (stage === "code" && isVnextPlanRun(context, { projectRoot, runId })) {
|
|
921
2115
|
if (semanticFile || resultFile || resultStdin || hasOption(parsed, "outcome")) {
|
|
922
2116
|
throw new AppError("usage", "vNext CODE finish accepts only --verification-file", 2);
|
|
923
2117
|
}
|
|
924
|
-
return await
|
|
2118
|
+
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) }));
|
|
925
2119
|
}
|
|
926
2120
|
if (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId })) {
|
|
927
2121
|
if (resultStdin || semanticFile || resultFile || hasOption(parsed, "outcome"))
|
|
928
2122
|
throw new AppError("usage", "vNext CODE-REVIEW uses --decision-file", 2);
|
|
929
|
-
return await
|
|
2123
|
+
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) }));
|
|
930
2124
|
}
|
|
931
2125
|
if (stage === "merge" && isVnextMergeRun(context, { projectRoot, runId })) {
|
|
932
2126
|
if (resultStdin || semanticFile || resultFile || hasOption(parsed, "outcome"))
|
|
933
2127
|
throw new AppError("usage", "vNext MERGE uses the Work result path returned by stage start", 2);
|
|
934
|
-
return await
|
|
2128
|
+
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));
|
|
935
2129
|
}
|
|
936
2130
|
if (hasOption(parsed, "outcome") || hasOption(parsed, "result-file") || resultStdin) {
|
|
937
2131
|
throw new AppError("usage", "--outcome, --result-file and --result-stdin are only available for vNext SPECIFY", 2);
|
|
938
2132
|
}
|
|
939
|
-
return finishStage(context, {
|
|
2133
|
+
return controlledReply(await finishStage(context, {
|
|
940
2134
|
projectRoot,
|
|
941
2135
|
runId,
|
|
942
2136
|
stage,
|
|
943
2137
|
...(dir ? { dir } : {}),
|
|
944
2138
|
...(semanticFile ? { semanticFile } : {})
|
|
945
|
-
});
|
|
2139
|
+
}, prepared.legacyStageFinish));
|
|
946
2140
|
}
|
|
947
2141
|
if (family === "stage" && command === "pause") {
|
|
948
|
-
|
|
949
|
-
|
|
2142
|
+
const projectRoot = requiredOption(parsed, "project-root");
|
|
2143
|
+
const runId = requiredPosition(parsed, 0, "run-id");
|
|
2144
|
+
const stage = requiredOption(parsed, "stage");
|
|
2145
|
+
const workId = requiredOption(parsed, "work");
|
|
2146
|
+
const protectedLifecycle = (stage === "specify" && isVnextSpecifyRun(context, { projectRoot, runId }))
|
|
2147
|
+
|| (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId }))
|
|
2148
|
+
|| (stage === "plan" && isVnextPlanRun(context, { projectRoot, runId }))
|
|
2149
|
+
|| (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId }))
|
|
2150
|
+
|| (stage === "code" && isVnextPlanRun(context, { projectRoot, runId }))
|
|
2151
|
+
|| (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId }));
|
|
2152
|
+
if (protectedLifecycle)
|
|
2153
|
+
assertStageLifecycleOwner(context, { projectRoot, runId, stage, workId, operation: "stage_pause", ...(optionalOption(parsed, "hook-event-id") ? { hookEventId: optionalOption(parsed, "hook-event-id") } : {}) });
|
|
950
2154
|
return pauseStageForUser(context, {
|
|
951
|
-
projectRoot
|
|
952
|
-
runId
|
|
953
|
-
stage
|
|
954
|
-
workId
|
|
955
|
-
question: await readStdin(io.stdin)
|
|
2155
|
+
projectRoot,
|
|
2156
|
+
runId,
|
|
2157
|
+
stage,
|
|
2158
|
+
workId,
|
|
2159
|
+
question: prepared.interactionText ?? await readStdin(io.stdin)
|
|
956
2160
|
});
|
|
957
2161
|
}
|
|
958
2162
|
if (family === "stage" && command === "resume") {
|
|
959
|
-
if (!hasOption(parsed, "answer-stdin"))
|
|
960
|
-
throw new AppError("usage", "stage resume requires --answer-stdin", 2);
|
|
961
2163
|
const hookEventId = optionalOption(parsed, "hook-event-id");
|
|
962
2164
|
return resumeStageAfterUser(context, {
|
|
963
2165
|
projectRoot: requiredOption(parsed, "project-root"),
|
|
964
2166
|
runId: requiredPosition(parsed, 0, "run-id"),
|
|
965
2167
|
stage: requiredOption(parsed, "stage"),
|
|
966
2168
|
workId: requiredOption(parsed, "work"),
|
|
967
|
-
answer: await readStdin(io.stdin),
|
|
2169
|
+
answer: prepared.interactionText ?? await readStdin(io.stdin),
|
|
2170
|
+
evidence: prepared.resumeEvidence,
|
|
968
2171
|
...(hookEventId ? { hookEventId } : {})
|
|
969
2172
|
});
|
|
970
2173
|
}
|
|
971
2174
|
if (family === "stage" && command === "block") {
|
|
972
|
-
if (!hasOption(parsed, "summary-stdin"))
|
|
973
|
-
throw new AppError("usage", "stage block requires --summary-stdin", 2);
|
|
974
2175
|
const kind = requiredOption(parsed, "kind");
|
|
975
2176
|
if (kind !== "engine" && kind !== "harness" && kind !== "environment")
|
|
976
2177
|
throw new AppError("usage", "--kind must be engine, harness or environment", 2);
|
|
@@ -981,7 +2182,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
981
2182
|
workId: requiredOption(parsed, "work"),
|
|
982
2183
|
kind,
|
|
983
2184
|
code: requiredOption(parsed, "code"),
|
|
984
|
-
summary: await readStdin(io.stdin),
|
|
2185
|
+
summary: prepared.interactionText ?? await readStdin(io.stdin),
|
|
985
2186
|
retryable: hasOption(parsed, "retryable")
|
|
986
2187
|
});
|
|
987
2188
|
}
|
|
@@ -1006,13 +2207,55 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1006
2207
|
return dispatchMergeWorker(context, command, parsed);
|
|
1007
2208
|
}
|
|
1008
2209
|
if (family === "cleanup") {
|
|
1009
|
-
return dispatchCleanup(context, command, parsed);
|
|
2210
|
+
return dispatchCleanup(context, command, parsed, prepared.cleanup);
|
|
1010
2211
|
}
|
|
1011
2212
|
if (family === "session") {
|
|
1012
|
-
return dispatchSession(context, command, parsed);
|
|
2213
|
+
return dispatchSession(context, command, parsed, prepared.sessionPayload);
|
|
1013
2214
|
}
|
|
1014
2215
|
if (family === "run") {
|
|
1015
|
-
|
|
2216
|
+
if (command === "control") {
|
|
2217
|
+
const action = requiredPosition(parsed, 0, "control action");
|
|
2218
|
+
const scope = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run") };
|
|
2219
|
+
if (action === "status")
|
|
2220
|
+
return runControlStatus(context, scope);
|
|
2221
|
+
if (action === "reconcile") {
|
|
2222
|
+
if ([...parsed.options.keys()].some(key => !["run", "project-root", "control-id", "request-id"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
2223
|
+
throw new AppError("usage", "Use run control reconcile --run ID --project-root PATH --control-id ID --request-id ID", 2);
|
|
2224
|
+
return await reconcileRunControl(context, { ...scope, controlId: requiredOption(parsed, "control-id"), requestId: requiredOption(parsed, "request-id") });
|
|
2225
|
+
}
|
|
2226
|
+
if (action === "resume") {
|
|
2227
|
+
if (["force", "grace-ms", "escalate"].some(option => hasOption(parsed, option)))
|
|
2228
|
+
throw new AppError("usage", "Resume does not accept stop or pause escalation options", 2);
|
|
2229
|
+
return await resumeRunController(context, { ...scope, requestId: requiredOption(parsed, "request-id"), controlId: requiredOption(parsed, "from"), ...(hasOption(parsed, "wait-ms") ? { waitMs: Number(requiredOption(parsed, "wait-ms")) } : {}) });
|
|
2230
|
+
}
|
|
2231
|
+
if (action === "serve")
|
|
2232
|
+
return await serveRunControl(context, { ...scope, controlId: requiredOption(parsed, "control-id") });
|
|
2233
|
+
if (action === "pause" || action === "stop")
|
|
2234
|
+
return await requestRunControl(context, {
|
|
2235
|
+
...scope, action, requestId: requiredOption(parsed, "request-id"),
|
|
2236
|
+
...(hasOption(parsed, "force") ? { force: true } : {}),
|
|
2237
|
+
...(hasOption(parsed, "grace-ms") ? { graceMs: Number(requiredOption(parsed, "grace-ms")) } : {}),
|
|
2238
|
+
...(hasOption(parsed, "escalate") ? { escalate: requiredOption(parsed, "escalate") } : {}),
|
|
2239
|
+
...(hasOption(parsed, "wait-ms") ? { waitMs: Number(requiredOption(parsed, "wait-ms")) } : {})
|
|
2240
|
+
});
|
|
2241
|
+
throw new AppError("usage", "Use: dd-flow run control pause|stop|resume|reconcile|status", 2);
|
|
2242
|
+
}
|
|
2243
|
+
if (command === "drive") {
|
|
2244
|
+
const action = requiredPosition(parsed, 0, "drive action");
|
|
2245
|
+
const scope = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run") };
|
|
2246
|
+
if (action === "status")
|
|
2247
|
+
return runControllerStatus(context, { ...scope, ...(optionalOption(parsed, "after") ? { after: Number(optionalOption(parsed, "after")) } : {}) });
|
|
2248
|
+
if (action === "launch")
|
|
2249
|
+
return await launchRunController(context, controllerLaunchInput(parsed), prepared.controllerLaunch);
|
|
2250
|
+
if (action === "serve")
|
|
2251
|
+
return await serveRunController(context, { ...scope, controllerId: requiredOption(parsed, "controller-id") });
|
|
2252
|
+
if (action === "answer")
|
|
2253
|
+
return answerRunController(context, controllerAnswerInput(parsed), prepared.controllerAnswer);
|
|
2254
|
+
if (action === "context")
|
|
2255
|
+
return supplyRunControllerContext(context, controllerContextInput(parsed), prepared.controllerContext);
|
|
2256
|
+
throw new AppError("usage", "Use: dd-flow run drive launch|status|answer|context", 2);
|
|
2257
|
+
}
|
|
2258
|
+
return dispatchRun(context, command, parsed, prepared);
|
|
1016
2259
|
}
|
|
1017
2260
|
if (family === "stat") {
|
|
1018
2261
|
return dispatchStat(context, command, parsed);
|
|
@@ -1027,22 +2270,122 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1027
2270
|
return dispatchDashboard(context, command, parsed);
|
|
1028
2271
|
}
|
|
1029
2272
|
if (family === "schema") {
|
|
1030
|
-
return
|
|
2273
|
+
return prepared.schemaValidation;
|
|
2274
|
+
}
|
|
2275
|
+
if (family === "delivery") {
|
|
2276
|
+
const projectRoot = requiredOption(parsed, "project-root"), project = requireProjectByRoot(context, resolveProjectRoot(projectRoot));
|
|
2277
|
+
const runId = resolveRun(context, project.id, requiredPosition(parsed, 0, "run-id")).id, stage = requiredOption(parsed, "stage");
|
|
2278
|
+
const input = { projectRoot, runId, stage, command: quote(["dd-flow", ...args]) };
|
|
2279
|
+
const selected = command === "inputs" ? readDeliveryStageScope(context, input) : assertDeliveryCommandOwner(context, input);
|
|
2280
|
+
if (command === "checks") {
|
|
2281
|
+
if (!prepared.deliveryArtifact)
|
|
2282
|
+
throw new AppError("cli_input_unprepared", "Delivery artifact was not prepared", 1);
|
|
2283
|
+
const checks = prepareDeliveryChecks(context, { ...input, artifact: prepared.deliveryArtifact });
|
|
2284
|
+
await checks.run();
|
|
2285
|
+
return { ok: true, verification: checks.evidence().receipt };
|
|
2286
|
+
}
|
|
2287
|
+
if (command === "execute") {
|
|
2288
|
+
if (!prepared.deliveryApproval)
|
|
2289
|
+
throw new AppError("cli_input_unprepared", "Delivery approval was not prepared", 1);
|
|
2290
|
+
return executeDeliveryOperation(context, { ...input, approval: prepared.deliveryApproval });
|
|
2291
|
+
}
|
|
2292
|
+
const packet = readDescriptorStageInput(context, selected.scope, selected.bundle);
|
|
2293
|
+
const previous = packet.input.predecessor ? readAcceptedStageData(context, { ...selected.scope, stage: packet.input.predecessor.stage }) : null;
|
|
2294
|
+
const details = previous ? JSON.parse(previous.bytes.toString("utf8")).details : null;
|
|
2295
|
+
const operation = details?.decision && details?.artifact ? deliveryApprovalScope(selected.scope, selected.bundle, { operationId: details.decision.operation_id, binding: details.decision.binding,
|
|
2296
|
+
artifact: { subject_ref: details.artifact.subject_ref, version: details.artifact.version, sha256: details.artifact.sha256 } }) : null;
|
|
2297
|
+
if (command === "inputs")
|
|
2298
|
+
return { flow_id: selected.flowId, run_id: runId, stage, user_inputs: selected.users,
|
|
2299
|
+
user_inputs_sha256: selected.userInputsSha256, operation_scope: operation, scope_sha256: operation ? hybridDigest(operation) : null };
|
|
2300
|
+
if (command === "reconcile") {
|
|
2301
|
+
if (!details?.decision)
|
|
2302
|
+
throw new AppError("delivery_stage_invalid", "Original delivery decision is required", 2, { effect: "no_effect" });
|
|
2303
|
+
const policy = prepareDeliveryProjectBinding(selected.bundle.execution.snapshot().scope.workspace, details.decision.policy, details.decision.binding.selector);
|
|
2304
|
+
if (hybridDigest(policy.binding) !== hybridDigest(details.decision.binding))
|
|
2305
|
+
throw new AppError("delivery_stage_invalid", "Original delivery policy changed", 2, { effect: "no_effect" });
|
|
2306
|
+
return observeDeliveryResult(context, { ...selected.scope, binding: policy.binding,
|
|
2307
|
+
assertCurrent() { assertDeliveryCommandOwner(context, input); policy.assertCurrent(); } });
|
|
2308
|
+
}
|
|
2309
|
+
throw new AppError("usage", "Unknown delivery command", 2);
|
|
2310
|
+
}
|
|
2311
|
+
if (family === "interactive") {
|
|
2312
|
+
const projectRoot = requiredOption(parsed, "project-root"), project = requireProjectByRoot(context, resolveProjectRoot(projectRoot));
|
|
2313
|
+
const runId = resolveRun(context, project.id, requiredOption(parsed, "run")).id;
|
|
2314
|
+
if (command === "integrate")
|
|
2315
|
+
return integrateInteractiveFinish(context, { projectRoot, runId,
|
|
2316
|
+
...(prepared.interactiveGitAgreement ? { agreement: prepared.interactiveGitAgreement } : {}) });
|
|
2317
|
+
if (command === "correction")
|
|
2318
|
+
return registerInteractiveReviewCorrection(context, { projectRoot, runId, reviewWorkId: requiredOption(parsed, "work") });
|
|
2319
|
+
if (command === "re-review")
|
|
2320
|
+
return registerInteractiveCorrectionReview(context, { projectRoot, runId, correctionWorkId: requiredOption(parsed, "work") });
|
|
2321
|
+
if (command === "checks") {
|
|
2322
|
+
if (!prepared.interactiveChecksProposal)
|
|
2323
|
+
throw new AppError("cli_input_unprepared", "Interactive check selection was not prepared", 1);
|
|
2324
|
+
let checks;
|
|
2325
|
+
try {
|
|
2326
|
+
checks = PreparedInteractiveChecks.prepare(context, { projectRoot, runId,
|
|
2327
|
+
stage: requiredOption(parsed, "stage"), ...prepared.interactiveChecksProposal });
|
|
2328
|
+
}
|
|
2329
|
+
catch (error) {
|
|
2330
|
+
if (isAppError(error) && error.code === "interactive_checks_invalid"
|
|
2331
|
+
&& error.details.reason === "invalid_check_selection" && error.details.effect === "no_effect") {
|
|
2332
|
+
Object.assign(error.details, { phase: "prepare", recoverable: true,
|
|
2333
|
+
retry_command: runtimeCommand(context, ["interactive", "checks", "--project-root", projectRoot, "--run", runId,
|
|
2334
|
+
"--stage", requiredOption(parsed, "stage"), "--result-file", requiredOption(parsed, "result-file"), "--json", "--progress-jsonl"]),
|
|
2335
|
+
retry_instruction: "Correct only the rejected check selection JSON: additionalChecks contains unique id,command,purpose entries; IDs match ^CHK-[A-Za-z0-9-]{1,64}$ and must not collide with policy checks. No plan or check execution was retained by this call. Keep existing obligations and use this exact retry_command; this does not authorize replay of executed checks or bypass other refusals." });
|
|
2336
|
+
}
|
|
2337
|
+
throw error;
|
|
2338
|
+
}
|
|
2339
|
+
return checks.execute(context).then(receipts => ({ plan_sha256: checks.sha256, plan: checks.value(), receipts }));
|
|
2340
|
+
}
|
|
2341
|
+
if (command === "register") {
|
|
2342
|
+
const bundle = readFlowRunExecutionBundle(context, { projectRoot, runId });
|
|
2343
|
+
if (!bundle || bundle.manifest.flow_id !== "interactive")
|
|
2344
|
+
throw new AppError("interactive_stage_invalid", "Bound interactive RUN required", 2, { effect: "no_effect" });
|
|
2345
|
+
return registerInteractiveStageGraph(context, { projectRoot, runId, stage: requiredOption(parsed, "stage"),
|
|
2346
|
+
gitExecutable: readInteractiveWorkGit(bundle).git_executable });
|
|
2347
|
+
}
|
|
2348
|
+
if (command === "git-inputs")
|
|
2349
|
+
return observeInteractiveOriginalGitInputs(context, { projectRoot, runId, stage: requiredOption(parsed, "stage") });
|
|
2350
|
+
if (command === "inputs" || command === "turn" || command === "finish" || command === "scope") {
|
|
2351
|
+
const bundle = readFlowRunExecutionBundle(context, { projectRoot, runId });
|
|
2352
|
+
if (!bundle || bundle.manifest.flow_id !== "interactive")
|
|
2353
|
+
throw new AppError("interactive_stage_invalid", "Bound interactive RUN required", 2, { effect: "no_effect" });
|
|
2354
|
+
const scope = { projectId: project.id, runId };
|
|
2355
|
+
if (command === "inputs")
|
|
2356
|
+
return readInteractiveUserInputs(context, scope, bundle);
|
|
2357
|
+
if (command === "scope") {
|
|
2358
|
+
if (!prepared.interactiveScopeProposal)
|
|
2359
|
+
throw new AppError("cli_input_unprepared", "Interactive scope proposal was not prepared", 1);
|
|
2360
|
+
const result = PreparedInteractiveMutationScope.prepare(context, scope, bundle, prepared.interactiveScopeProposal).retain(context);
|
|
2361
|
+
return { changed: result.changed, scope_revision: result.scope.revision, user_inputs_sha256: result.scope.inputs.sha256,
|
|
2362
|
+
baseline_sha256: result.scope.baseline.sha256, paths: result.scope.active_paths, needs_scope_refresh: result.scope.needs_scope_refresh };
|
|
2363
|
+
}
|
|
2364
|
+
if (!prepared.interactiveInputBytes)
|
|
2365
|
+
throw new AppError("cli_input_unprepared", "Interactive user bytes were not prepared", 1);
|
|
2366
|
+
return deliverInteractiveUserInput(context, scope, bundle, { projectRoot, intakeFile: requiredOption(parsed, "intake-file"),
|
|
2367
|
+
requestId: requiredOption(parsed, "request-id"), kind: command, bytes: prepared.interactiveInputBytes,
|
|
2368
|
+
expectedInputsSha256: requiredOption(parsed, "expected-inputs-sha256") });
|
|
2369
|
+
}
|
|
1031
2370
|
}
|
|
1032
2371
|
if (family === "memory") {
|
|
1033
|
-
return dispatchMemory(command, parsed);
|
|
2372
|
+
return dispatchMemory(context, command, parsed, prepared);
|
|
1034
2373
|
}
|
|
1035
2374
|
if (family === "migration") {
|
|
1036
|
-
return dispatchMigration(context, command, parsed, classification);
|
|
2375
|
+
return dispatchMigration(context, command, parsed, classification, prepared);
|
|
1037
2376
|
}
|
|
1038
2377
|
if (family === "codex" && command === "hooks") {
|
|
1039
|
-
return dispatchCodexHooks(context, parsed);
|
|
2378
|
+
return dispatchCodexHooks(context, parsed, prepared);
|
|
1040
2379
|
}
|
|
1041
2380
|
if (family === "codex" && command === "home") {
|
|
1042
|
-
return dispatchCodexHome(context, parsed);
|
|
2381
|
+
return dispatchCodexHome(context, parsed, prepared);
|
|
1043
2382
|
}
|
|
1044
2383
|
if (family === "codex" && command === "hook") {
|
|
1045
|
-
|
|
2384
|
+
// `handle` is owned by the pre-context ingress above, never generic dispatch.
|
|
2385
|
+
throw new AppError("usage", "Use: dd-flow codex hook handle --event EVENT", 2);
|
|
2386
|
+
}
|
|
2387
|
+
if (family === "zcode" && command === "hook") {
|
|
2388
|
+
return dispatchZcodeHook(context, parsed, await readStdin(io.stdin));
|
|
1046
2389
|
}
|
|
1047
2390
|
if (family === "zcode" && command === "event") {
|
|
1048
2391
|
return dispatchZcodeEvent(context, parsed, await readStdin(io.stdin));
|
|
@@ -1068,6 +2411,16 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1068
2411
|
throw new AppError("usage", "Unknown opencode usage action", 2);
|
|
1069
2412
|
return ingestOpenCodeUsage(context, { projectRoot: requiredOption(parsed, "project-root"), stdin: await readStdin(io.stdin) });
|
|
1070
2413
|
}
|
|
2414
|
+
if (family === "droid" && command === "event") {
|
|
2415
|
+
if (requiredPosition(parsed, 0, "droid event action") !== "handle")
|
|
2416
|
+
throw new AppError("usage", "Unknown droid event action", 2);
|
|
2417
|
+
return handleDroidEvent(context, { projectRoot: requiredOption(parsed, "project-root"), stdin: await readStdin(io.stdin) });
|
|
2418
|
+
}
|
|
2419
|
+
if (family === "droid" && command === "usage") {
|
|
2420
|
+
if (requiredPosition(parsed, 0, "droid usage action") !== "ingest")
|
|
2421
|
+
throw new AppError("usage", "Unknown droid usage action", 2);
|
|
2422
|
+
return ingestDroidUsage(context, { projectRoot: requiredOption(parsed, "project-root"), stdin: await readStdin(io.stdin) });
|
|
2423
|
+
}
|
|
1071
2424
|
if (family === "agy" && command === "event")
|
|
1072
2425
|
return dispatchAgyEvent(context, parsed, await readStdin(io.stdin));
|
|
1073
2426
|
if (family === "agy" && command === "usage") {
|
|
@@ -1076,11 +2429,14 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1076
2429
|
return ingestAgyUsage(context, { projectRoot: requiredOption(parsed, "project-root"), stdin: await readStdin(io.stdin) });
|
|
1077
2430
|
}
|
|
1078
2431
|
if (family === "worktree") {
|
|
1079
|
-
return dispatchWorktree(context, command, parsed, requiredScopeProjectRoot(scopeProjectRoot, `worktree ${command ?? ""}`));
|
|
2432
|
+
return dispatchWorktree(context, command, parsed, requiredScopeProjectRoot(scopeProjectRoot, `worktree ${command ?? ""}`), prepared);
|
|
1080
2433
|
}
|
|
1081
2434
|
throw new AppError("usage", `Unknown command: ${args.join(" ") || "<empty>"}`, 2);
|
|
1082
2435
|
}
|
|
1083
|
-
function
|
|
2436
|
+
function engineBindInput(parsed) {
|
|
2437
|
+
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") };
|
|
2438
|
+
}
|
|
2439
|
+
function dispatchEngine(context, command, parsed, classification, prepared) {
|
|
1084
2440
|
if (command === "install") {
|
|
1085
2441
|
return installCurrentEngine(context, { force: hasOption(parsed, "force") });
|
|
1086
2442
|
}
|
|
@@ -1105,20 +2461,20 @@ function dispatchEngine(context, command, parsed, classification) {
|
|
|
1105
2461
|
});
|
|
1106
2462
|
}
|
|
1107
2463
|
if (command === "bind-run") {
|
|
1108
|
-
return bindRunEngine(context,
|
|
1109
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1110
|
-
runId: requiredPosition(parsed, 0, "run-id"),
|
|
1111
|
-
packageName: optionalOption(parsed, "package"),
|
|
1112
|
-
engineVersion: requiredOption(parsed, "engine-version"),
|
|
1113
|
-
integrityChecksum: optionalOption(parsed, "integrity-checksum"),
|
|
1114
|
-
reason: requiredOption(parsed, "reason")
|
|
1115
|
-
});
|
|
2464
|
+
return bindRunEngine(context, engineBindInput(parsed), prepared.engineBind);
|
|
1116
2465
|
}
|
|
1117
2466
|
throw new AppError("usage", `Unknown engine command: ${command ?? "<empty>"}`, 2);
|
|
1118
2467
|
}
|
|
1119
|
-
function dispatchMigration(context, command, parsed, classification) {
|
|
2468
|
+
function dispatchMigration(context, command, parsed, classification, prepared) {
|
|
1120
2469
|
if (command === "impact")
|
|
1121
2470
|
return assessMigrationImpact(context, { projectRoot: requiredOption(parsed, "project-root"), ...(optionalOption(parsed, "target-version") ? { targetVersion: optionalOption(parsed, "target-version") } : {}), ...(optionalOption(parsed, "mode") ? { mode: optionalOption(parsed, "mode") } : {}) });
|
|
2471
|
+
if (command === "readiness") {
|
|
2472
|
+
if (!prepared.upgradeStorageScope)
|
|
2473
|
+
throw new AppError("validation", "Upgrade storage scope must be prepared before publication", 2);
|
|
2474
|
+
// Unlike the initial readonly syntax pass, this connection is admitted
|
|
2475
|
+
// prepared-existing. Physical writer checks still precede receipt writes.
|
|
2476
|
+
return { ok: true, ...PreparedUpgradeStorageReadiness.prepare(context, prepared.upgradeStorageScope).apply(context) };
|
|
2477
|
+
}
|
|
1122
2478
|
if (command === "plan" || command === "report") {
|
|
1123
2479
|
return planMigration(context, {
|
|
1124
2480
|
projectRoot: requiredOption(parsed, "project-root"),
|
|
@@ -1132,26 +2488,42 @@ function dispatchMigration(context, command, parsed, classification) {
|
|
|
1132
2488
|
});
|
|
1133
2489
|
}
|
|
1134
2490
|
if (command === "verify") {
|
|
1135
|
-
return verifyMigrationReport(context, { file: requiredOption(parsed, "file") });
|
|
2491
|
+
return prepared.migrationVerification ?? verifyMigrationReport(context, { file: requiredOption(parsed, "file") });
|
|
1136
2492
|
}
|
|
1137
2493
|
if (command === "apply") {
|
|
1138
|
-
return applyMigration(context,
|
|
1139
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1140
|
-
sourceVersion: optionalOption(parsed, "source-version"),
|
|
1141
|
-
targetVersion: optionalOption(parsed, "target-version"),
|
|
1142
|
-
runId: optionalOption(parsed, "run"),
|
|
1143
|
-
backupPath: optionalOption(parsed, "backup-path"),
|
|
1144
|
-
backupCreatedAt: optionalOption(parsed, "backup-created-at"),
|
|
1145
|
-
reportFile: optionalOption(parsed, "file"),
|
|
1146
|
-
allowActive: false,
|
|
1147
|
-
classification
|
|
1148
|
-
});
|
|
2494
|
+
return applyMigration(context, migrationApplyInput(parsed, classification), prepared.migrationApply);
|
|
1149
2495
|
}
|
|
1150
2496
|
throw new AppError("usage", `Unknown migration command: ${command ?? "<empty>"}`, 2);
|
|
1151
2497
|
}
|
|
1152
|
-
function
|
|
2498
|
+
function protocolTransitionInput(parsed, scopeProjectRoot) {
|
|
2499
|
+
const reason = optionalOption(parsed, "reason");
|
|
2500
|
+
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 } : {}) };
|
|
2501
|
+
}
|
|
2502
|
+
function controllerAnswerInput(parsed) {
|
|
2503
|
+
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") };
|
|
2504
|
+
}
|
|
2505
|
+
function controllerLaunchInput(parsed) {
|
|
2506
|
+
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") } : {}) };
|
|
2507
|
+
}
|
|
2508
|
+
function controllerContextInput(parsed) {
|
|
2509
|
+
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") };
|
|
2510
|
+
}
|
|
2511
|
+
function migrationApplyInput(parsed, classification) {
|
|
2512
|
+
return {
|
|
2513
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
2514
|
+
sourceVersion: optionalOption(parsed, "source-version"),
|
|
2515
|
+
targetVersion: optionalOption(parsed, "target-version"),
|
|
2516
|
+
runId: optionalOption(parsed, "run"),
|
|
2517
|
+
backupPath: optionalOption(parsed, "backup-path"),
|
|
2518
|
+
backupCreatedAt: optionalOption(parsed, "backup-created-at"),
|
|
2519
|
+
reportFile: optionalOption(parsed, "file"),
|
|
2520
|
+
allowActive: false,
|
|
2521
|
+
classification
|
|
2522
|
+
};
|
|
2523
|
+
}
|
|
2524
|
+
function dispatchCanon(context, command, parsed, prepared) {
|
|
1153
2525
|
if (command === "register") {
|
|
1154
|
-
return registerCanonRoot(context, { root: requiredOption(parsed, "root") });
|
|
2526
|
+
return registerCanonRoot(context, { root: requiredOption(parsed, "root") }, prepared.canonRegistration);
|
|
1155
2527
|
}
|
|
1156
2528
|
if (command === "status") {
|
|
1157
2529
|
return getCanonStatus(context, { root: optionalOption(parsed, "root") });
|
|
@@ -1161,6 +2533,15 @@ function dispatchCanon(context, command, parsed) {
|
|
|
1161
2533
|
}
|
|
1162
2534
|
throw new AppError("usage", `Unknown canon command: ${command ?? "<empty>"}`, 2);
|
|
1163
2535
|
}
|
|
2536
|
+
function mergeJobTarget(parsed, scopeProjectRoot) {
|
|
2537
|
+
return { protocolId: requiredPosition(parsed, 0, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue"), workerId: requiredWorkerId(parsed), workspacePath: workspacePathOption(parsed) };
|
|
2538
|
+
}
|
|
2539
|
+
function mergeCancellationInput(parsed, scopeProjectRoot) {
|
|
2540
|
+
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") };
|
|
2541
|
+
}
|
|
2542
|
+
function mergeWaitInput(parsed, scopeProjectRoot) {
|
|
2543
|
+
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 };
|
|
2544
|
+
}
|
|
1164
2545
|
function dispatchMergeQueue(context, command, parsed, scopeProjectRoot) {
|
|
1165
2546
|
if (hasOption(parsed, "session-id")) {
|
|
1166
2547
|
throw new AppError("usage", "--session-id is no longer accepted on merge-queue commands; use --worker-id", 2);
|
|
@@ -1184,32 +2565,17 @@ function dispatchMergeQueue(context, command, parsed, scopeProjectRoot) {
|
|
|
1184
2565
|
});
|
|
1185
2566
|
}
|
|
1186
2567
|
if (command === "wait-next") {
|
|
1187
|
-
return waitNextMergeJob(context,
|
|
1188
|
-
projectRoot: requiredOptionOrScope(parsed, scopeProjectRoot, "project-root"),
|
|
1189
|
-
workerId: requiredWorkerId(parsed),
|
|
1190
|
-
timeoutSeconds: optionalNumber(parsed, "timeout"),
|
|
1191
|
-
pollIntervalSeconds: optionalNumber(parsed, "poll-interval"),
|
|
1192
|
-
workspacePath: workspacePathOption(parsed),
|
|
1193
|
-
acquireLock: parseOptionalBoolean(optionalOption(parsed, "acquire-lock"), "acquire-lock") ?? false
|
|
1194
|
-
});
|
|
2568
|
+
return waitNextMergeJob(context, mergeWaitInput(parsed, scopeProjectRoot));
|
|
1195
2569
|
}
|
|
1196
2570
|
if (command === "complete") {
|
|
1197
|
-
const protocolId = requiredPosition(parsed, 0, "protocol-id");
|
|
1198
2571
|
return completeMergeJob(context, {
|
|
1199
|
-
|
|
1200
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue complete"),
|
|
1201
|
-
workerId: requiredWorkerId(parsed),
|
|
1202
|
-
workspacePath: workspacePathOption(parsed),
|
|
2572
|
+
...mergeJobTarget(parsed, scopeProjectRoot),
|
|
1203
2573
|
summary: requiredOption(parsed, "summary")
|
|
1204
2574
|
});
|
|
1205
2575
|
}
|
|
1206
2576
|
if (command === "fail") {
|
|
1207
|
-
const protocolId = requiredPosition(parsed, 0, "protocol-id");
|
|
1208
2577
|
return failMergeJob(context, {
|
|
1209
|
-
|
|
1210
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue fail"),
|
|
1211
|
-
workerId: requiredWorkerId(parsed),
|
|
1212
|
-
workspacePath: workspacePathOption(parsed),
|
|
2578
|
+
...mergeJobTarget(parsed, scopeProjectRoot),
|
|
1213
2579
|
reason: requiredOption(parsed, "reason"),
|
|
1214
2580
|
requeue: parseBoolean(requiredOption(parsed, "requeue"), "requeue")
|
|
1215
2581
|
});
|
|
@@ -1224,15 +2590,7 @@ function dispatchMergeQueue(context, command, parsed, scopeProjectRoot) {
|
|
|
1224
2590
|
});
|
|
1225
2591
|
}
|
|
1226
2592
|
if (command === "cancel") {
|
|
1227
|
-
|
|
1228
|
-
return cancelMergeQueueJob(context, {
|
|
1229
|
-
protocolId,
|
|
1230
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue cancel"),
|
|
1231
|
-
reason: requiredOption(parsed, "reason"),
|
|
1232
|
-
workerId: optionalOption(parsed, "worker-id"),
|
|
1233
|
-
workspacePath: optionalOption(parsed, "path"),
|
|
1234
|
-
force: hasOption(parsed, "force")
|
|
1235
|
-
});
|
|
2593
|
+
return cancelMergeQueueJob(context, mergeCancellationInput(parsed, scopeProjectRoot));
|
|
1236
2594
|
}
|
|
1237
2595
|
throw new AppError("usage", `Unknown merge-queue command: ${command ?? "<empty>"}`, 2);
|
|
1238
2596
|
}
|
|
@@ -1331,7 +2689,7 @@ function dispatchMergeWorker(context, command, parsed) {
|
|
|
1331
2689
|
}
|
|
1332
2690
|
throw new AppError("usage", `Unknown merge-worker command: ${command ?? "<empty>"}`, 2);
|
|
1333
2691
|
}
|
|
1334
|
-
function dispatchMemory(command, parsed) {
|
|
2692
|
+
function dispatchMemory(context, command, parsed, prepared) {
|
|
1335
2693
|
const subcommand = requiredPosition(parsed, 0, "memory subcommand");
|
|
1336
2694
|
if (command === "permissions" && subcommand === "preflight") {
|
|
1337
2695
|
return preflightMemoryPermissions({
|
|
@@ -1342,6 +2700,128 @@ function dispatchMemory(command, parsed) {
|
|
|
1342
2700
|
mode: requiredOption(parsed, "mode")
|
|
1343
2701
|
});
|
|
1344
2702
|
}
|
|
2703
|
+
const scope = () => {
|
|
2704
|
+
const projectRoot = requiredOption(parsed, "project-root");
|
|
2705
|
+
const project = requireProjectByRoot(context, resolveProjectRoot(projectRoot));
|
|
2706
|
+
return { projectRoot, runId: resolveRun(context, project.id, requiredOption(parsed, "run")).id };
|
|
2707
|
+
};
|
|
2708
|
+
if (command === "repair") {
|
|
2709
|
+
const wave = requiredOption(parsed, "wave");
|
|
2710
|
+
if (wave !== "1" && wave !== "2")
|
|
2711
|
+
throw new AppError("usage", "Memory repairs support only waves 1 and 2", 2);
|
|
2712
|
+
const work = { ...scope(), workId: requiredOption(parsed, "work") };
|
|
2713
|
+
if (subcommand === "baseline")
|
|
2714
|
+
return (wave === "1" ? registerMemoryRepairBaselineFromStage : registerMemorySecondRepairBaselineFromStage)(context, work);
|
|
2715
|
+
if (subcommand === "copy-reserve")
|
|
2716
|
+
return (wave === "1" ? reserveMemoryRepairWriterCopyFromStage : reserveMemorySecondRepairCopyFromStage)(context, work);
|
|
2717
|
+
if (subcommand === "copy-materialize")
|
|
2718
|
+
return (wave === "1" ? materializeMemoryRepairWriterCopyFromStage : materializeMemorySecondRepairCopyFromStage)(context, work);
|
|
2719
|
+
if (subcommand === "copy-dispatch")
|
|
2720
|
+
return (wave === "1" ? prepareMemoryRepairDispatchFromStage : prepareMemorySecondRepairDispatchFromStage)(context, work);
|
|
2721
|
+
const workspaceRoot = requiredOption(parsed, "workspace-root");
|
|
2722
|
+
if (wave === "2") {
|
|
2723
|
+
const bundle = readFlowRunExecutionBundle(context, work);
|
|
2724
|
+
if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id) || bundle.execution.snapshot().scope.workspace !== workspaceRoot)
|
|
2725
|
+
throw new AppError("memory_repair_scope_invalid", "Repair workspace does not match its frozen memory RUN", 2, { phase: "prepare", effect: "no_effect" });
|
|
2726
|
+
}
|
|
2727
|
+
if (subcommand === "current-view")
|
|
2728
|
+
return wave === "1"
|
|
2729
|
+
? registerMemoryRepairCurrentViewFromStage(context, { ...work, workspaceRoot })
|
|
2730
|
+
: registerMemorySecondRepairCurrentViewFromStage(context, work);
|
|
2731
|
+
const output = { ...work, workspaceRoot, workSessionId: requiredOption(parsed, "work-session") };
|
|
2732
|
+
if (subcommand === "publication-source" && wave === "1")
|
|
2733
|
+
return registerMemoryRepairPublicationSourceFromStage(context, output);
|
|
2734
|
+
if (subcommand === "publication-reserve") {
|
|
2735
|
+
if (!prepared.memoryOperationBytes)
|
|
2736
|
+
throw new AppError("cli_input_unprepared", "Memory repair output bytes were not prepared", 1);
|
|
2737
|
+
return (wave === "1" ? reserveMemoryRepairPublicationAgainstSourceFromStage : reserveMemorySecondRepairPublicationFromStage)(context, { ...output, bytes: prepared.memoryOperationBytes });
|
|
2738
|
+
}
|
|
2739
|
+
if (subcommand === "publication-apply")
|
|
2740
|
+
return (wave === "1" ? applyMemoryRepairPublicationFromStage : applyMemorySecondRepairPublicationFromStage)(context, output);
|
|
2741
|
+
if (subcommand === "publication-finish")
|
|
2742
|
+
return (wave === "1" ? finishMemoryRepairPublicationFromStage : finishMemorySecondRepairPublicationFromStage)(context, output);
|
|
2743
|
+
}
|
|
2744
|
+
if (command === "compose" && subcommand === "register") {
|
|
2745
|
+
if (!prepared.memoryOperationBytes)
|
|
2746
|
+
throw new AppError("cli_input_unprepared", "Memory composition bytes were not prepared", 1);
|
|
2747
|
+
try {
|
|
2748
|
+
return registerMemoryCompositionFromStage(context, { ...scope(), requestKey: requiredOption(parsed, "request-key"), bytes: prepared.memoryOperationBytes });
|
|
2749
|
+
}
|
|
2750
|
+
catch (error) {
|
|
2751
|
+
if (error instanceof AppError && (error.code === "memory_stage_result_invalid"
|
|
2752
|
+
|| error.code === "memory_preflight_handler_invalid" && ["unselected_preflight_source", "proposed_source_sha256_mismatch"].includes(String(error.details.reason))) && error.details.phase === "prepare"
|
|
2753
|
+
&& error.details.effect === "no_effect" && error.details.recoverable === true) {
|
|
2754
|
+
Object.assign(error.details, compositionProposalRetry(context, parsed));
|
|
2755
|
+
}
|
|
2756
|
+
throw error;
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
if (command === "upgrade" && subcommand === "publish")
|
|
2760
|
+
return publishMemoryUpgradeFromStage(context, scope());
|
|
2761
|
+
if (command === "distill" && subcommand === "inputs") {
|
|
2762
|
+
const inputs = readDistillUserInputs(context, scope());
|
|
2763
|
+
return { ...inputs, user_inputs_sha256: hybridDigest(inputs) };
|
|
2764
|
+
}
|
|
2765
|
+
if (command === "fix" && subcommand === "context") {
|
|
2766
|
+
const intake = readMemoryFixSourceIntake(context, scope()), users = readMemoryFixUserInputs(context, scope());
|
|
2767
|
+
return { ...intake, user_inputs: users, user_inputs_sha256: hybridDigest(users) };
|
|
2768
|
+
}
|
|
2769
|
+
if (command === "fix" && subcommand === "scope") {
|
|
2770
|
+
const proposal = prepared.memoryFixProposal;
|
|
2771
|
+
if (!proposal || Object.keys(proposal).sort().join() !== "routing,write_scope")
|
|
2772
|
+
throw new AppError("usage", "Repair scope proposal requires routing and write_scope", 2, { effect: "no_effect" });
|
|
2773
|
+
return prepareMemoryFixScopeEvidence(context, { ...scope(), routing: proposal.routing, writeScope: proposal.write_scope }).snapshot();
|
|
2774
|
+
}
|
|
2775
|
+
if (command === "fix" && subcommand === "publish") {
|
|
2776
|
+
if (!prepared.memoryFixProposal)
|
|
2777
|
+
throw new AppError("cli_input_unprepared", "Repair proposal was not prepared", 1);
|
|
2778
|
+
return prepareMemoryFixPublication(context, { ...scope(), proposal: prepared.memoryFixProposal }).apply();
|
|
2779
|
+
}
|
|
2780
|
+
if (command === "fix" && subcommand === "integrate")
|
|
2781
|
+
return integrateMemoryFixFinish(context, { ...scope(), ...(prepared.memoryFixProposal ? { agreement: prepared.memoryFixProposal } : {}) });
|
|
2782
|
+
if (command === "distill" && subcommand === "publish") {
|
|
2783
|
+
if (!prepared.memoryOperationBytes)
|
|
2784
|
+
throw new AppError("cli_input_unprepared", "Distillation proposal bytes were not prepared", 1);
|
|
2785
|
+
return publishDistillFromStage(context, { ...scope(), bytes: prepared.memoryOperationBytes });
|
|
2786
|
+
}
|
|
2787
|
+
if (command === "verify" && subcommand === "checks")
|
|
2788
|
+
return runMemoryVerificationChecks(context, scope());
|
|
2789
|
+
if (command === "review" && subcommand === "register")
|
|
2790
|
+
return registerMemoryReviewFromStage(context, scope());
|
|
2791
|
+
if (command === "review" && subcommand === "collect") {
|
|
2792
|
+
const wave = collectMemoryInitialReviewFromStage(context, scope());
|
|
2793
|
+
const selected = scope(), bundle = readFlowRunExecutionBundle(context, selected);
|
|
2794
|
+
if (!bundle)
|
|
2795
|
+
throw new AppError("memory_review_handler_invalid", "Registered Memory bundle required", 2);
|
|
2796
|
+
const project = requireProjectByRoot(context, resolveProjectRoot(selected.projectRoot));
|
|
2797
|
+
const retained = PreparedRetainedMemoryReviewResults.prepare(context, PreparedRetainedMemoryReviewAuthority.prepare(context, { projectId: project.id, runId: selected.runId }, bundle));
|
|
2798
|
+
return { references: wave.references(), projection: wave.resultProjection(),
|
|
2799
|
+
correction_plan_binding: { schema_id: "dd-flow/memory-review-correction-plan@1", run_id: selected.runId, wave: 1, review_sha256: retained.sha256 } };
|
|
2800
|
+
}
|
|
2801
|
+
if (command === "finish" && subcommand === "evidence")
|
|
2802
|
+
return registerMemoryFinishEvidenceFromStage(context, scope());
|
|
2803
|
+
if (command === "finish" && subcommand === "integrate")
|
|
2804
|
+
return integrateMemoryFinishFromStage(context, scope());
|
|
2805
|
+
if (command === "corrections" || command === "review" && ["selective-register", "selective-collect"].includes(subcommand)) {
|
|
2806
|
+
const wave = requiredOption(parsed, "wave");
|
|
2807
|
+
if (wave !== "1" && wave !== "2")
|
|
2808
|
+
throw new AppError("usage", "Memory corrections support only waves 1 and 2", 2);
|
|
2809
|
+
if (command === "corrections" && subcommand === "register") {
|
|
2810
|
+
if (!prepared.memoryOperationBytes)
|
|
2811
|
+
throw new AppError("cli_input_unprepared", "Memory correction plan bytes were not prepared", 1);
|
|
2812
|
+
const register = wave === "1" ? registerMemoryCorrectionsFromStage : registerMemorySecondCorrectionsFromStage;
|
|
2813
|
+
return register(context, { ...scope(), bytes: prepared.memoryOperationBytes });
|
|
2814
|
+
}
|
|
2815
|
+
if (command === "corrections" && subcommand === "checks")
|
|
2816
|
+
return (wave === "1" ? runMemoryCorrectionChecksFromStage : runMemorySecondCorrectionChecksFromStage)(context, scope());
|
|
2817
|
+
if (command === "review" && subcommand === "selective-register")
|
|
2818
|
+
return (wave === "1" ? registerCorrectedMemoryReviewFromStage : registerSecondCorrectedMemoryReviewFromStage)(context, scope());
|
|
2819
|
+
if (command === "review" && subcommand === "selective-collect") {
|
|
2820
|
+
const accepted = (wave === "1" ? collectCorrectedMemoryReviewFromStage : collectSecondCorrectedMemoryReviewFromStage)(context, scope());
|
|
2821
|
+
return { wave: Number(wave), references: accepted.references(), projection: accepted.resultProjection(),
|
|
2822
|
+
...(wave === "1" ? { correction_plan_binding: { schema_id: "dd-flow/memory-review-correction-plan@1", run_id: scope().runId, wave: 2, review_sha256: accepted.sha256 } } : {}) };
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
1345
2825
|
throw new AppError("usage", `Unknown memory command: ${["memory", command, subcommand].filter(Boolean).join(" ")}`, 2);
|
|
1346
2826
|
}
|
|
1347
2827
|
function completedCommandExitCode(payload) {
|
|
@@ -1350,7 +2830,7 @@ function completedCommandExitCode(payload) {
|
|
|
1350
2830
|
const exitCode = payload.exit_code;
|
|
1351
2831
|
return typeof exitCode === "number" && Number.isInteger(exitCode) ? exitCode : 0;
|
|
1352
2832
|
}
|
|
1353
|
-
function dispatchCleanup(context, command, parsed) {
|
|
2833
|
+
function dispatchCleanup(context, command, parsed, prepared) {
|
|
1354
2834
|
if (command === "scan") {
|
|
1355
2835
|
return cleanupScan(context, { projectRoot: requiredOption(parsed, "project-root") });
|
|
1356
2836
|
}
|
|
@@ -1358,6 +2838,7 @@ function dispatchCleanup(context, command, parsed) {
|
|
|
1358
2838
|
return cleanupApply(context, {
|
|
1359
2839
|
projectRoot: requiredOption(parsed, "project-root"),
|
|
1360
2840
|
planFile: requiredOption(parsed, "plan-file"),
|
|
2841
|
+
prepared,
|
|
1361
2842
|
reason: requiredOption(parsed, "reason"),
|
|
1362
2843
|
force: hasOption(parsed, "force")
|
|
1363
2844
|
});
|
|
@@ -1378,12 +2859,14 @@ function dispatchProjectConfig(context, parsed) {
|
|
|
1378
2859
|
}
|
|
1379
2860
|
throw new AppError("usage", `Unknown project config action: ${action}`, 2);
|
|
1380
2861
|
}
|
|
1381
|
-
function dispatchSession(context, command, parsed) {
|
|
2862
|
+
function dispatchSession(context, command, parsed, preparedPayload) {
|
|
1382
2863
|
if (command === "register") {
|
|
1383
2864
|
return registerFlowSession(context, {
|
|
2865
|
+
...(preparedPayload ? { preparedPayload } : {}),
|
|
1384
2866
|
payloadBase64: optionalOption(parsed, "payload-base64"),
|
|
1385
2867
|
payloadJson: optionalOption(parsed, "payload-json"),
|
|
1386
2868
|
payloadFile: optionalOption(parsed, "payload-file"),
|
|
2869
|
+
hookEventId: optionalOption(parsed, "hook-event-id"),
|
|
1387
2870
|
sessionId: optionalOption(parsed, "session-id")
|
|
1388
2871
|
});
|
|
1389
2872
|
}
|
|
@@ -1416,7 +2899,47 @@ function dispatchSession(context, command, parsed) {
|
|
|
1416
2899
|
}
|
|
1417
2900
|
throw new AppError("usage", `Unknown session command: ${command ?? "<empty>"}`, 2);
|
|
1418
2901
|
}
|
|
1419
|
-
function
|
|
2902
|
+
function snapshotCreateInput(parsed) {
|
|
2903
|
+
const recoveryId = optionalOption(parsed, "recovery-id"), stageEntry = optionalOption(parsed, "stage-entry");
|
|
2904
|
+
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 } : {}) };
|
|
2905
|
+
}
|
|
2906
|
+
function snapshotRestoreInput(parsed) {
|
|
2907
|
+
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") } : {}) };
|
|
2908
|
+
}
|
|
2909
|
+
function runStartInput(parsed) {
|
|
2910
|
+
const protocolOverrides = readOptionalJsonFile(optionalOption(parsed, "protocol-override-file"));
|
|
2911
|
+
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")) };
|
|
2912
|
+
}
|
|
2913
|
+
function runForkInput(parsed) {
|
|
2914
|
+
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") } : {}) };
|
|
2915
|
+
}
|
|
2916
|
+
function recoveryCaptureInput(parsed) {
|
|
2917
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 1, "run-id"), recoveryId: requiredOption(parsed, "recovery-id"), capturePath: requiredOption(parsed, "capture-path") };
|
|
2918
|
+
}
|
|
2919
|
+
function recoveryResumeInput(parsed) {
|
|
2920
|
+
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") };
|
|
2921
|
+
}
|
|
2922
|
+
function dispatchRun(context, command, parsed, prepared = {}) {
|
|
2923
|
+
if (command === "reconcile")
|
|
2924
|
+
return reconcileFlowRunHealth(context, { runId: parsed.positional[0], projectRoot: optionalOption(parsed, "project-root"), apply: hasOption(parsed, "apply"), noProgressMs: optionalNumber(parsed, "no-progress-ms") });
|
|
2925
|
+
if (command === "recovery") {
|
|
2926
|
+
const action = requiredPosition(parsed, 0, "recovery action");
|
|
2927
|
+
const projectRoot = requiredOption(parsed, "project-root");
|
|
2928
|
+
const runId = requiredPosition(parsed, 1, "run-id");
|
|
2929
|
+
if (action === "inspect")
|
|
2930
|
+
return inspectRunRecovery(context, { projectRoot, runId });
|
|
2931
|
+
if (action === "begin")
|
|
2932
|
+
return beginRunRecovery(context, { projectRoot, runId, interruption: requiredOption(parsed, "interruption-json") }, prepared.recoveryPayload);
|
|
2933
|
+
if (action === "seal")
|
|
2934
|
+
return sealRunRecovery(context, { projectRoot, runId, recoveryId: requiredOption(parsed, "recovery-id"), settlement: requiredOption(parsed, "settlement-json") }, prepared.recoveryPayload);
|
|
2935
|
+
if (action === "capture")
|
|
2936
|
+
return recordRecoveryCapture(context, recoveryCaptureInput(parsed), prepared.recoveryCapture);
|
|
2937
|
+
if (action === "resume")
|
|
2938
|
+
return resumeRunRecovery(context, recoveryResumeInput(parsed), prepared.recoveryResume);
|
|
2939
|
+
if (action === "accept")
|
|
2940
|
+
return acceptRunRecovery(context, { projectRoot, runId, recoveryId: requiredOption(parsed, "recovery-id"), ...(optionalOption(parsed, "hook-event-id") ? { hookEventId: optionalOption(parsed, "hook-event-id") } : {}) });
|
|
2941
|
+
throw new AppError("usage", "Use: dd-flow run recovery inspect|begin|seal|capture|resume|accept", 2);
|
|
2942
|
+
}
|
|
1420
2943
|
if (command === "snapshot") {
|
|
1421
2944
|
const action = requiredPosition(parsed, 0, "snapshot action");
|
|
1422
2945
|
if (action === "bootstrap") {
|
|
@@ -1424,59 +2947,31 @@ function dispatchRun(context, command, parsed) {
|
|
|
1424
2947
|
if (bootstrapAction === "create")
|
|
1425
2948
|
return createEvalBootstrapSnapshot(context, {
|
|
1426
2949
|
projectRoot: requiredOption(parsed, "project-root"),
|
|
1427
|
-
output: requiredOption(parsed, "output")
|
|
2950
|
+
output: requiredOption(parsed, "output"),
|
|
2951
|
+
flow: optionalOption(parsed, "flow")
|
|
1428
2952
|
});
|
|
1429
2953
|
if (bootstrapAction === "restore")
|
|
1430
|
-
return restoreEvalBootstrapSnapshot(context,
|
|
1431
|
-
snapshot: requiredOption(parsed, "snapshot"),
|
|
1432
|
-
projectRoot: requiredOption(parsed, "project-root")
|
|
1433
|
-
});
|
|
2954
|
+
return restoreEvalBootstrapSnapshot(context, snapshotRestoreInput(parsed), prepared.bootstrapRestore);
|
|
1434
2955
|
throw new AppError("usage", "Use: dd-flow run snapshot bootstrap create|restore", 2);
|
|
1435
2956
|
}
|
|
1436
2957
|
if (action === "create") {
|
|
1437
|
-
|
|
1438
|
-
const stageEntry = optionalOption(parsed, "stage-entry");
|
|
1439
|
-
if (candidate && stageEntry !== undefined)
|
|
1440
|
-
throw new AppError("usage", "Use either --candidate or --stage-entry, not both", 2);
|
|
1441
|
-
if (!candidate && stageEntry === undefined)
|
|
1442
|
-
throw new AppError("usage", "Snapshot create requires --candidate or --stage-entry", 2);
|
|
1443
|
-
const input = {
|
|
1444
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1445
|
-
runId: requiredPosition(parsed, 1, "run-id"),
|
|
1446
|
-
output: requiredOption(parsed, "output")
|
|
1447
|
-
};
|
|
1448
|
-
return candidate
|
|
1449
|
-
? createEvalRunSnapshot(context, { ...input, candidate: true })
|
|
1450
|
-
: createEvalRunSnapshot(context, { ...input, stageEntry: stageEntry });
|
|
2958
|
+
return createEvalRunSnapshot(context, snapshotCreateInput(parsed));
|
|
1451
2959
|
}
|
|
1452
2960
|
if (action === "restore")
|
|
1453
|
-
return restoreEvalRunSnapshot(context,
|
|
1454
|
-
snapshot: requiredOption(parsed, "snapshot"),
|
|
1455
|
-
projectRoot: requiredOption(parsed, "project-root")
|
|
1456
|
-
});
|
|
2961
|
+
return restoreEvalRunSnapshot(context, snapshotRestoreInput(parsed), prepared.snapshotRestore);
|
|
1457
2962
|
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);
|
|
1458
2963
|
}
|
|
2964
|
+
if (command === "fork") {
|
|
2965
|
+
return forkRunFromSnapshot(context, runForkInput(parsed), prepared.runFork);
|
|
2966
|
+
}
|
|
1459
2967
|
if (command === "prepare-vnext-specify") {
|
|
1460
|
-
return prepareVnextSpecifyRun(context, { projectRoot: requiredOption(parsed, "project-root"), slug: requiredOption(parsed, "slug") });
|
|
2968
|
+
return prepareVnextSpecifyRun(context, { projectRoot: requiredOption(parsed, "project-root"), slug: requiredOption(parsed, "slug"), executionRoutingFile: optionalOption(parsed, "execution-routing-file") });
|
|
1461
2969
|
}
|
|
1462
2970
|
if (command === "start") {
|
|
1463
2971
|
if (optionalOption(parsed, "flow-kind") === "mb-upgrade") {
|
|
1464
2972
|
assertUpgradeRunStorageReady(context, requiredOption(parsed, "project-root"));
|
|
1465
2973
|
}
|
|
1466
|
-
|
|
1467
|
-
return startFlowRun(context, {
|
|
1468
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1469
|
-
workspaceRoot: optionalOption(parsed, "workspace-root"),
|
|
1470
|
-
flowKind: requiredOption(parsed, "flow-kind"),
|
|
1471
|
-
subjectType: requiredOption(parsed, "subject-type"),
|
|
1472
|
-
subjectId: requiredOption(parsed, "subject-id"),
|
|
1473
|
-
slug: requiredOption(parsed, "slug"),
|
|
1474
|
-
nextAction: optionalOption(parsed, "next-action"),
|
|
1475
|
-
preset: optionalOption(parsed, "preset"),
|
|
1476
|
-
taskProfile: readOptionalJsonFile(optionalOption(parsed, "task-profile-file")),
|
|
1477
|
-
...(protocolOverrides ? { protocolOverrides } : {}),
|
|
1478
|
-
runOverrides: parseFlowFlagOverrides(allOptions(parsed, "flag"))
|
|
1479
|
-
});
|
|
2974
|
+
return startFlowRun(context, prepared.runStart?.input ?? runStartInput(parsed), prepared.runStart?.value);
|
|
1480
2975
|
}
|
|
1481
2976
|
if (command === "status") {
|
|
1482
2977
|
return getFlowRunStatus(context, {
|
|
@@ -1546,88 +3041,84 @@ function dispatchRun(context, command, parsed) {
|
|
|
1546
3041
|
throw new AppError("usage", `Unknown run flags command: ${action}`, 2);
|
|
1547
3042
|
}
|
|
1548
3043
|
if (command === "attach-stage") {
|
|
1549
|
-
return attachFlowRunStage(context,
|
|
1550
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1551
|
-
runId: requiredPosition(parsed, 0, "run-id"),
|
|
1552
|
-
stage: requiredOption(parsed, "stage"),
|
|
1553
|
-
dir: requiredOption(parsed, "dir"),
|
|
1554
|
-
status: requiredOption(parsed, "status"),
|
|
1555
|
-
dataSchemaId: optionalOption(parsed, "data-schema-id")
|
|
1556
|
-
});
|
|
3044
|
+
return attachFlowRunStage(context, stageAttachmentInput(parsed));
|
|
1557
3045
|
}
|
|
1558
3046
|
if (command === "complete-stage") {
|
|
1559
|
-
const
|
|
1560
|
-
return completeFlowRunStage(context,
|
|
1561
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1562
|
-
runId: requiredPosition(parsed, 0, "run-id"),
|
|
1563
|
-
stage: requiredOption(parsed, "stage"),
|
|
1564
|
-
status: requiredOption(parsed, "status"),
|
|
1565
|
-
stageReport,
|
|
1566
|
-
data: optionalOption(parsed, "data"),
|
|
1567
|
-
dataSchemaId: optionalOption(parsed, "data-schema-id"),
|
|
1568
|
-
report: optionalOption(parsed, "report"),
|
|
1569
|
-
aliases: allOptions(parsed, "alias")
|
|
1570
|
-
});
|
|
3047
|
+
const input = stageCompletionInput(parsed);
|
|
3048
|
+
return completeFlowRunStage(context, input, prepareRawFlowRunStageCompletion(context, input));
|
|
1571
3049
|
}
|
|
1572
3050
|
if (command === "complete") {
|
|
1573
3051
|
throw new AppError("usage", "run complete is retired; only an operator may use run override", 2);
|
|
1574
3052
|
}
|
|
1575
3053
|
if (command === "override") {
|
|
1576
|
-
|
|
1577
|
-
if (!['cancelled', 'failed'].includes(status)) {
|
|
1578
|
-
throw new AppError("usage", "run override status must be cancelled or failed", 2, { status });
|
|
1579
|
-
}
|
|
1580
|
-
return completeFlowRun(context, {
|
|
1581
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1582
|
-
runId: requiredPosition(parsed, 0, "run-id"),
|
|
1583
|
-
status,
|
|
1584
|
-
verdict: optionalOption(parsed, "verdict"),
|
|
1585
|
-
nextAction: optionalOption(parsed, "next-action"),
|
|
1586
|
-
manualOverrideReason: requiredOption(parsed, "reason")
|
|
1587
|
-
});
|
|
3054
|
+
return completeFlowRun(context, runOverrideInput(parsed));
|
|
1588
3055
|
}
|
|
1589
3056
|
throw new AppError("usage", `Unknown run command: ${command ?? "<empty>"}`, 2);
|
|
1590
3057
|
}
|
|
1591
|
-
|
|
3058
|
+
function stageAttachmentInput(parsed) {
|
|
3059
|
+
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") };
|
|
3060
|
+
}
|
|
3061
|
+
function runOverrideInput(parsed) {
|
|
3062
|
+
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") };
|
|
3063
|
+
}
|
|
3064
|
+
function stageCompletionInput(parsed) {
|
|
3065
|
+
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") };
|
|
3066
|
+
}
|
|
3067
|
+
function runtimeServiceInput(parsed) {
|
|
3068
|
+
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 };
|
|
3069
|
+
}
|
|
3070
|
+
function runtimeScopeResumeInput(parsed) {
|
|
3071
|
+
return { scopeId: requiredOption(parsed, "scope-id"), requestId: requiredOption(parsed, "request-id"), generation: Number(requiredOption(parsed, "generation")), captureKey: requiredOption(parsed, "capture-key") };
|
|
3072
|
+
}
|
|
3073
|
+
async function dispatchRuntime(context, command, parsed, progress, prepared = {}) {
|
|
3074
|
+
if (command === "scope") {
|
|
3075
|
+
if (parsed.positional.length === 1 && parsed.positional[0] === "reconcile") {
|
|
3076
|
+
if ([...parsed.options.keys()].some(key => !["scope-id", "request-id", "generation"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
3077
|
+
throw new AppError("usage", "Use runtime scope reconcile --scope-id ID --generation N --request-id ID", 2);
|
|
3078
|
+
return { ok: true, ...await reconcileRuntimeScopeControl(context, requiredOption(parsed, "scope-id"), Number(requiredOption(parsed, "generation")), requiredOption(parsed, "request-id")) };
|
|
3079
|
+
}
|
|
3080
|
+
if (parsed.positional.length === 1 && parsed.positional[0] === "resume") {
|
|
3081
|
+
if ([...parsed.options.keys()].some(key => !["scope-id", "request-id", "generation", "capture-key"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
3082
|
+
throw new AppError("usage", "Use runtime scope resume --scope-id ID --request-id ID --generation N --capture-key SHA256", 2);
|
|
3083
|
+
return { ok: true, ...await launchRuntimeScopeResume(context, runtimeScopeResumeInput(parsed)) };
|
|
3084
|
+
}
|
|
3085
|
+
if (parsed.positional.length === 1 && parsed.positional[0] === "control") {
|
|
3086
|
+
if ([...parsed.options.keys()].some(key => !["scope-id", "request-id", "mode", "manifest-json"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
3087
|
+
throw new AppError("usage", "Use runtime scope control --scope-id ID --request-id ID --mode pause|stop [--manifest-json JSON]", 2);
|
|
3088
|
+
const mode = requiredOption(parsed, "mode");
|
|
3089
|
+
if (mode !== "pause" && mode !== "stop")
|
|
3090
|
+
throw new AppError("usage", "Scope control mode must be pause or stop", 2);
|
|
3091
|
+
return { ok: true, ...await launchRuntimeScopeControl(context, requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id"), mode, undefined, undefined, undefined, prepared.runtimeManifest) };
|
|
3092
|
+
}
|
|
3093
|
+
if (parsed.positional.length === 1 && parsed.positional[0] === "serve") {
|
|
3094
|
+
if ([...parsed.options.keys()].some(key => !["scope-id", "generation"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
3095
|
+
throw new AppError("usage", "Invalid internal scope worker arguments", 2);
|
|
3096
|
+
return { ok: true, ...await serveRuntimeScopeControl(context, requiredOption(parsed, "scope-id"), Number(requiredOption(parsed, "generation"))) };
|
|
3097
|
+
}
|
|
3098
|
+
if (parsed.positional.length === 1 && parsed.positional[0] === "stop") {
|
|
3099
|
+
if ([...parsed.options.keys()].some(key => !["scope-id", "request-id"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
3100
|
+
throw new AppError("usage", "Usage: dd-flow runtime scope stop --scope-id EVAL_ID --request-id ID", 2);
|
|
3101
|
+
return { ok: true, ...await stopRuntimeScope(context, requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id")) };
|
|
3102
|
+
}
|
|
3103
|
+
if (parsed.positional.length === 1 && parsed.positional[0] === "status") {
|
|
3104
|
+
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))
|
|
3105
|
+
throw new AppError("usage", "Use exactly one of --scope-id or --scope-ids-json", 2);
|
|
3106
|
+
const ids = prepareRuntimeScopeStatus(optionalOption(parsed, "scope-id"), optionalOption(parsed, "scope-ids-json"));
|
|
3107
|
+
if (parsed.options.has("scope-ids-json")) {
|
|
3108
|
+
return { ok: true, scopes: ids.map(id => runtimeBudgetStatus(context, id)) };
|
|
3109
|
+
}
|
|
3110
|
+
return { ok: true, ...runtimeBudgetStatus(context, requiredOption(parsed, "scope-id")) };
|
|
3111
|
+
}
|
|
3112
|
+
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))
|
|
3113
|
+
throw new AppError("usage", "Usage: dd-flow runtime scope fence --scope-id EVAL_ID --request-id ID", 2);
|
|
3114
|
+
return { ok: true, ...fenceRuntimeBudget(context, requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id")) };
|
|
3115
|
+
}
|
|
1592
3116
|
if (command !== "process")
|
|
1593
|
-
throw new AppError("usage", "Usage: dd-flow runtime process
|
|
3117
|
+
throw new AppError("usage", "Usage: dd-flow runtime <scope|process>; see dd-flow runtime scope --help", 2);
|
|
1594
3118
|
const action = requiredPosition(parsed, 0, "runtime process action");
|
|
1595
3119
|
if (action === "start")
|
|
1596
|
-
return await startRuntimeService(context, {
|
|
1597
|
-
|
|
1598
|
-
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) } : {}) }) };
|
|
1599
|
-
if (action === "register") {
|
|
1600
|
-
const leaseMs = optionalPositiveNumber(parsed, "lease-ms", true);
|
|
1601
|
-
const process = registerManagedProcess(context, {
|
|
1602
|
-
kind: requiredOption(parsed, "kind"), ownerId: requiredOption(parsed, "owner"),
|
|
1603
|
-
ownerPid: optionalPositiveNumber(parsed, "owner-pid", true),
|
|
1604
|
-
projectId: optionalOption(parsed, "project-id") ?? null, runId: optionalOption(parsed, "run") ?? null,
|
|
1605
|
-
workId: optionalOption(parsed, "work") ?? null, checkId: optionalOption(parsed, "check") ?? null,
|
|
1606
|
-
operationId: optionalOption(parsed, "operation") ?? null, stdoutPath: optionalOption(parsed, "stdout") ?? null,
|
|
1607
|
-
stderrPath: optionalOption(parsed, "stderr") ?? null, ...(leaseMs ? { leaseMs } : {})
|
|
1608
|
-
});
|
|
1609
|
-
return { ok: true, process };
|
|
1610
|
-
}
|
|
1611
|
-
if (action === "confirm") {
|
|
1612
|
-
const leaseMs = optionalPositiveNumber(parsed, "lease-ms", true);
|
|
1613
|
-
const processGroupId = optionalPositiveNumber(parsed, "process-group-id", true);
|
|
1614
|
-
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 } : {}) }) };
|
|
1615
|
-
}
|
|
1616
|
-
if (action === "heartbeat") {
|
|
1617
|
-
const leaseMs = optionalPositiveNumber(parsed, "lease-ms", true);
|
|
1618
|
-
return { ok: heartbeatManagedProcess(context, { id: requiredOption(parsed, "id"), leaseToken: requiredOption(parsed, "lease-token"), ...(leaseMs ? { leaseMs } : {}) }) };
|
|
1619
|
-
}
|
|
1620
|
-
if (action === "finish") {
|
|
1621
|
-
const state = requiredOption(parsed, "state");
|
|
1622
|
-
if (state !== "stopped" && state !== "failed")
|
|
1623
|
-
throw new AppError("validation", "--state must be stopped or failed", 2);
|
|
1624
|
-
return { ok: finishManagedProcess(context, { id: requiredOption(parsed, "id"), leaseToken: requiredOption(parsed, "lease-token"), state: state, reason: optionalOption(parsed, "reason") ?? null }) };
|
|
1625
|
-
}
|
|
1626
|
-
if (action === "status")
|
|
1627
|
-
return { ok: true, processes: managedProcessStatus(context) };
|
|
1628
|
-
if (action === "reconcile")
|
|
1629
|
-
return { ok: true, processes: await reconcileExpiredManagedProcesses(context, requiredOption(parsed, "owner"), optionalPositiveNumber(parsed, "grace-ms", true)) };
|
|
1630
|
-
throw new AppError("usage", "Usage: dd-flow runtime process <start|stop|register|confirm|heartbeat|finish|status|reconcile>", 2);
|
|
3120
|
+
return await startRuntimeService(context, { ...runtimeServiceInput(parsed), progress });
|
|
3121
|
+
return await dispatchRuntimeProcess(context, action, parsed, prepared);
|
|
1631
3122
|
}
|
|
1632
3123
|
function dispatchStat(context, command, parsed) {
|
|
1633
3124
|
if (command === "usage") {
|
|
@@ -1661,7 +3152,7 @@ function dispatchCmux(context, parsed) {
|
|
|
1661
3152
|
}
|
|
1662
3153
|
throw new AppError("usage", `Unknown integration cmux action: ${action}`, 2);
|
|
1663
3154
|
}
|
|
1664
|
-
function dispatchDashboard(context, command, parsed) {
|
|
3155
|
+
async function dispatchDashboard(context, command, parsed) {
|
|
1665
3156
|
const action = dashboardAction(command);
|
|
1666
3157
|
if (command === "data") {
|
|
1667
3158
|
const target = resolveDashboardTarget(context, {
|
|
@@ -1696,7 +3187,7 @@ function dispatchDashboard(context, command, parsed) {
|
|
|
1696
3187
|
projectRoot: optionalOption(parsed, "project-root"),
|
|
1697
3188
|
protocol: optionalOption(parsed, "protocol")
|
|
1698
3189
|
});
|
|
1699
|
-
return withDashboardGuidance(action, target, openDashboardForResolvedTarget(context, target, parsed));
|
|
3190
|
+
return withDashboardGuidance(action, target, await openDashboardForResolvedTarget(context, target, parsed));
|
|
1700
3191
|
}
|
|
1701
3192
|
if (command === "refresh") {
|
|
1702
3193
|
const open = optionalOption(parsed, "open");
|
|
@@ -1710,7 +3201,7 @@ function dispatchDashboard(context, command, parsed) {
|
|
|
1710
3201
|
protocol: optionalOption(parsed, "protocol"),
|
|
1711
3202
|
all: hasOption(parsed, "all")
|
|
1712
3203
|
});
|
|
1713
|
-
return withDashboardGuidance(action, target, refreshDashboardForResolvedTarget(context, target, parsed, open));
|
|
3204
|
+
return withDashboardGuidance(action, target, await refreshDashboardForResolvedTarget(context, target, parsed, open));
|
|
1714
3205
|
}
|
|
1715
3206
|
if (command === "refresh-global") {
|
|
1716
3207
|
const target = resolveDashboardTarget(context, { action: "refresh", global: true });
|
|
@@ -1814,7 +3305,9 @@ function dispatchSchema(context, command, parsed) {
|
|
|
1814
3305
|
// searching project directories: run_id is a structural field in the
|
|
1815
3306
|
// submitted artifact and the database owns its materialized run_root.
|
|
1816
3307
|
const explicitRunId = optionalOption(parsed, "run");
|
|
1817
|
-
const
|
|
3308
|
+
const data = prepareJsonFile(file, "schema input").value;
|
|
3309
|
+
const declared = data && typeof data === "object" && "run_id" in data ? data.run_id : undefined;
|
|
3310
|
+
const declaredRunId = explicitRunId ?? (typeof declared === "string" && declared.trim() ? declared : undefined);
|
|
1818
3311
|
const fileRunRoot = path.basename(file) === "run.json" ? path.dirname(path.resolve(file)) : undefined;
|
|
1819
3312
|
const bound = projectRoot
|
|
1820
3313
|
? 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 ?? ""])
|
|
@@ -1822,6 +3315,7 @@ function dispatchSchema(context, command, parsed) {
|
|
|
1822
3315
|
return validateSchema({
|
|
1823
3316
|
schemaName: requiredOption(parsed, "schema"),
|
|
1824
3317
|
file,
|
|
3318
|
+
data,
|
|
1825
3319
|
...(schemaDir ? { schemaDir } : {}),
|
|
1826
3320
|
...(projectRoot ? { projectRoot } : {}),
|
|
1827
3321
|
...(declaredRunId || bound?.id ? { runId: declaredRunId ?? bound.id } : {}),
|
|
@@ -1831,14 +3325,11 @@ function dispatchSchema(context, command, parsed) {
|
|
|
1831
3325
|
}
|
|
1832
3326
|
throw new AppError("usage", `Unknown schema command: ${command ?? "<empty>"}`, 2);
|
|
1833
3327
|
}
|
|
1834
|
-
function
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
}
|
|
1839
|
-
catch {
|
|
1840
|
-
return undefined;
|
|
1841
|
-
}
|
|
3328
|
+
function laneWorkspaceInput(parsed) {
|
|
3329
|
+
return { projectRoot: requiredOption(parsed, "project-root"), lane: requiredOption(parsed, "lane"), workspacePath: requiredOption(parsed, "path"), branch: optionalOption(parsed, "branch") };
|
|
3330
|
+
}
|
|
3331
|
+
function laneWaiterCancelInput(parsed) {
|
|
3332
|
+
return { projectRoot: requiredOption(parsed, "project-root"), lane: requiredOption(parsed, "lane"), workerId: requiredOption(parsed, "worker-id"), reason: requiredOption(parsed, "reason") };
|
|
1842
3333
|
}
|
|
1843
3334
|
async function dispatchLane(context, command, parsed) {
|
|
1844
3335
|
if (command === "status") {
|
|
@@ -1850,24 +3341,14 @@ async function dispatchLane(context, command, parsed) {
|
|
|
1850
3341
|
if (command === "waiter") {
|
|
1851
3342
|
const action = requiredPosition(parsed, 0, "lane waiter action");
|
|
1852
3343
|
if (action === "cancel") {
|
|
1853
|
-
return cancelLaneWaiter(context,
|
|
1854
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1855
|
-
lane: requiredOption(parsed, "lane"),
|
|
1856
|
-
workerId: requiredOption(parsed, "worker-id"),
|
|
1857
|
-
reason: requiredOption(parsed, "reason")
|
|
1858
|
-
});
|
|
3344
|
+
return cancelLaneWaiter(context, laneWaiterCancelInput(parsed));
|
|
1859
3345
|
}
|
|
1860
3346
|
throw new AppError("usage", `Unknown lane waiter action: ${action}`, 2);
|
|
1861
3347
|
}
|
|
1862
3348
|
if (command === "workspace") {
|
|
1863
3349
|
const action = requiredPosition(parsed, 0, "lane workspace action");
|
|
1864
3350
|
if (action === "set") {
|
|
1865
|
-
return setLaneWorkspace(context,
|
|
1866
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1867
|
-
lane: requiredOption(parsed, "lane"),
|
|
1868
|
-
workspacePath: requiredOption(parsed, "path"),
|
|
1869
|
-
branch: optionalOption(parsed, "branch")
|
|
1870
|
-
});
|
|
3351
|
+
return setLaneWorkspace(context, laneWorkspaceInput(parsed));
|
|
1871
3352
|
}
|
|
1872
3353
|
if (action === "check") {
|
|
1873
3354
|
return checkLaneWorkspace(context, {
|
|
@@ -1939,7 +3420,7 @@ async function dispatchLane(context, command, parsed) {
|
|
|
1939
3420
|
}
|
|
1940
3421
|
throw new AppError("usage", `Unknown lane command: ${command ?? "<empty>"}`, 2);
|
|
1941
3422
|
}
|
|
1942
|
-
function dispatchCodexHooks(context, parsed) {
|
|
3423
|
+
function dispatchCodexHooks(context, parsed, prepared) {
|
|
1943
3424
|
const action = requiredPosition(parsed, 0, "codex hooks action");
|
|
1944
3425
|
if (action === "print") {
|
|
1945
3426
|
return printCodexHooks(context, {
|
|
@@ -1961,7 +3442,7 @@ function dispatchCodexHooks(context, parsed) {
|
|
|
1961
3442
|
target: optionalOption(parsed, "target"),
|
|
1962
3443
|
profile: optionalOption(parsed, "profile"),
|
|
1963
3444
|
yes: hasOption(parsed, "yes")
|
|
1964
|
-
});
|
|
3445
|
+
}, prepared.codexHooks);
|
|
1965
3446
|
}
|
|
1966
3447
|
if (action === "remove") {
|
|
1967
3448
|
return removeCodexHooks(context, {
|
|
@@ -1969,11 +3450,14 @@ function dispatchCodexHooks(context, parsed) {
|
|
|
1969
3450
|
target: optionalOption(parsed, "target"),
|
|
1970
3451
|
profile: optionalOption(parsed, "profile"),
|
|
1971
3452
|
yes: hasOption(parsed, "yes")
|
|
1972
|
-
});
|
|
3453
|
+
}, prepared.codexHooks);
|
|
1973
3454
|
}
|
|
1974
3455
|
throw new AppError("usage", `Unknown codex hooks action: ${action}`, 2);
|
|
1975
3456
|
}
|
|
1976
|
-
function
|
|
3457
|
+
function codexHomeInput(parsed) {
|
|
3458
|
+
return { projectRoot: requiredOption(parsed, "project-root"), profile: optionalOption(parsed, "profile"), sourceHome: optionalOption(parsed, "source-home"), targetHome: optionalOption(parsed, "target-home") };
|
|
3459
|
+
}
|
|
3460
|
+
function dispatchCodexHome(context, parsed, prepared) {
|
|
1977
3461
|
const action = requiredPosition(parsed, 0, "codex home action");
|
|
1978
3462
|
if (action === "plan") {
|
|
1979
3463
|
return planCodexHome(context, {
|
|
@@ -1984,12 +3468,7 @@ function dispatchCodexHome(context, parsed) {
|
|
|
1984
3468
|
});
|
|
1985
3469
|
}
|
|
1986
3470
|
if (action === "init") {
|
|
1987
|
-
return initCodexHome(context,
|
|
1988
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1989
|
-
profile: optionalOption(parsed, "profile"),
|
|
1990
|
-
sourceHome: optionalOption(parsed, "source-home"),
|
|
1991
|
-
targetHome: optionalOption(parsed, "target-home")
|
|
1992
|
-
});
|
|
3471
|
+
return initCodexHome(context, codexHomeInput(parsed), prepared.codexHome);
|
|
1993
3472
|
}
|
|
1994
3473
|
if (action === "status") {
|
|
1995
3474
|
return getCodexHomeStatus(context, {
|
|
@@ -2012,23 +3491,6 @@ function dispatchCodexHome(context, parsed) {
|
|
|
2012
3491
|
}
|
|
2013
3492
|
throw new AppError("usage", `Unknown codex home action: ${action}`, 2);
|
|
2014
3493
|
}
|
|
2015
|
-
function dispatchCodexHook(context, parsed, stdin) {
|
|
2016
|
-
const action = requiredPosition(parsed, 0, "codex hook action");
|
|
2017
|
-
if (action === "handle") {
|
|
2018
|
-
const isolatedHome = isolatedDdFlowHomeFromHookStdin(stdin);
|
|
2019
|
-
const hookContext = isolatedHome ? createContext({ ...context.env, DD_FLOW_HOME: isolatedHome }) : context;
|
|
2020
|
-
const result = handleCodexHook(hookContext, {
|
|
2021
|
-
event: requiredOption(parsed, "event"),
|
|
2022
|
-
projectRoot: optionalOption(parsed, "project-root"),
|
|
2023
|
-
stdin
|
|
2024
|
-
});
|
|
2025
|
-
if (result && typeof result === "object" && !Array.isArray(result) && "hookSpecificOutput" in result) {
|
|
2026
|
-
return { hookSpecificOutput: result.hookSpecificOutput };
|
|
2027
|
-
}
|
|
2028
|
-
return {};
|
|
2029
|
-
}
|
|
2030
|
-
throw new AppError("usage", `Unknown codex hook action: ${action}`, 2);
|
|
2031
|
-
}
|
|
2032
3494
|
function dispatchZcodeEvent(context, parsed, stdin) {
|
|
2033
3495
|
const action = requiredPosition(parsed, 0, "zcode event action");
|
|
2034
3496
|
if (action === "handle") {
|
|
@@ -2039,6 +3501,15 @@ function dispatchZcodeEvent(context, parsed, stdin) {
|
|
|
2039
3501
|
}
|
|
2040
3502
|
throw new AppError("usage", `Unknown zcode event action: ${action}`, 2);
|
|
2041
3503
|
}
|
|
3504
|
+
function dispatchZcodeHook(context, parsed, stdin) {
|
|
3505
|
+
if (requiredPosition(parsed, 0, "zcode hook action") !== "handle")
|
|
3506
|
+
throw new AppError("usage", "Unknown zcode hook action", 2);
|
|
3507
|
+
const result = handleZcodeHook(context, { projectRoot: requiredOption(parsed, "project-root"), stdin });
|
|
3508
|
+
if (result && typeof result === "object" && !Array.isArray(result) && "hookSpecificOutput" in result) {
|
|
3509
|
+
return { hookSpecificOutput: result.hookSpecificOutput };
|
|
3510
|
+
}
|
|
3511
|
+
return {};
|
|
3512
|
+
}
|
|
2042
3513
|
function dispatchGrokEvent(context, parsed, stdin) {
|
|
2043
3514
|
const action = requiredPosition(parsed, 0, "grok event action");
|
|
2044
3515
|
if (action === "handle") {
|
|
@@ -2062,7 +3533,7 @@ function dispatchAgyEvent(context, parsed, stdin) {
|
|
|
2062
3533
|
return handleAgyEvent(context, { projectRoot: requiredOption(parsed, "project-root"), stdin });
|
|
2063
3534
|
throw new AppError("usage", `Unknown agy event action: ${action}`, 2);
|
|
2064
3535
|
}
|
|
2065
|
-
function dispatchWorktree(context, command, parsed, scopeProjectRoot) {
|
|
3536
|
+
function dispatchWorktree(context, command, parsed, scopeProjectRoot, prepared) {
|
|
2066
3537
|
if (command === "plan") {
|
|
2067
3538
|
return planWorktree(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: scopeProjectRoot });
|
|
2068
3539
|
}
|
|
@@ -2073,13 +3544,13 @@ function dispatchWorktree(context, command, parsed, scopeProjectRoot) {
|
|
|
2073
3544
|
branch: requiredOption(parsed, "branch"),
|
|
2074
3545
|
base: requiredOption(parsed, "base"),
|
|
2075
3546
|
path: requiredOption(parsed, "path")
|
|
2076
|
-
});
|
|
3547
|
+
}, prepared.worktreeCreate);
|
|
2077
3548
|
}
|
|
2078
3549
|
if (command === "status") {
|
|
2079
3550
|
return getWorktreeStatus(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: scopeProjectRoot });
|
|
2080
3551
|
}
|
|
2081
3552
|
if (command === "bootstrap") {
|
|
2082
|
-
return bootstrapWorktree(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: scopeProjectRoot });
|
|
3553
|
+
return bootstrapWorktree(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: scopeProjectRoot }, prepared.worktreeBootstrap);
|
|
2083
3554
|
}
|
|
2084
3555
|
if (command === "close") {
|
|
2085
3556
|
return closeWorktree(context, {
|
|
@@ -2091,52 +3562,50 @@ function dispatchWorktree(context, command, parsed, scopeProjectRoot) {
|
|
|
2091
3562
|
throw new AppError("usage", `Unknown worktree command: ${command ?? "<empty>"}`, 2);
|
|
2092
3563
|
}
|
|
2093
3564
|
function dispatchPlanItem(context, parsed, projectRoot) {
|
|
3565
|
+
return updatePlanItem(context, planItemInput(parsed, projectRoot));
|
|
3566
|
+
}
|
|
3567
|
+
function planItemInput(parsed, projectRoot) {
|
|
2094
3568
|
const action = requiredPosition(parsed, 0, "plan item action");
|
|
2095
3569
|
const protocolId = requiredPosition(parsed, 1, "protocol-id");
|
|
2096
3570
|
const itemId = requiredPosition(parsed, 2, "item-id");
|
|
2097
3571
|
if (action === "start") {
|
|
2098
|
-
return
|
|
3572
|
+
return { protocolId, projectRoot, itemId, action };
|
|
2099
3573
|
}
|
|
2100
3574
|
if (action === "done") {
|
|
2101
|
-
return
|
|
3575
|
+
return {
|
|
3576
|
+
action,
|
|
2102
3577
|
protocolId,
|
|
2103
3578
|
projectRoot,
|
|
2104
3579
|
itemId,
|
|
2105
3580
|
summary: requiredOption(parsed, "summary"),
|
|
2106
3581
|
evidence: parsed.options.get("evidence") ?? []
|
|
2107
|
-
}
|
|
3582
|
+
};
|
|
2108
3583
|
}
|
|
2109
3584
|
if (action === "block") {
|
|
2110
|
-
return
|
|
3585
|
+
return {
|
|
3586
|
+
action,
|
|
2111
3587
|
protocolId,
|
|
2112
3588
|
projectRoot,
|
|
2113
3589
|
itemId,
|
|
2114
3590
|
reason: requiredOption(parsed, "reason"),
|
|
2115
3591
|
userRequired: parseBoolean(requiredOption(parsed, "user-required"), "user-required")
|
|
2116
|
-
}
|
|
3592
|
+
};
|
|
2117
3593
|
}
|
|
2118
3594
|
if (action === "skip") {
|
|
2119
|
-
return
|
|
3595
|
+
return {
|
|
3596
|
+
action,
|
|
2120
3597
|
protocolId,
|
|
2121
3598
|
projectRoot,
|
|
2122
3599
|
itemId,
|
|
2123
3600
|
reason: requiredOption(parsed, "reason")
|
|
2124
|
-
}
|
|
3601
|
+
};
|
|
2125
3602
|
}
|
|
2126
3603
|
throw new AppError("usage", `Unknown plan item action: ${action}`, 2);
|
|
2127
3604
|
}
|
|
2128
3605
|
function readOptionalJsonFile(file) {
|
|
2129
3606
|
if (!file)
|
|
2130
3607
|
return undefined;
|
|
2131
|
-
|
|
2132
|
-
throw new AppError("not_found", `JSON file does not exist: ${file}`, 1);
|
|
2133
|
-
}
|
|
2134
|
-
try {
|
|
2135
|
-
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
2136
|
-
}
|
|
2137
|
-
catch {
|
|
2138
|
-
throw new AppError("validation", `JSON file is malformed: ${file}`, 2);
|
|
2139
|
-
}
|
|
3608
|
+
return prepareJsonFile(file, "JSON input").value;
|
|
2140
3609
|
}
|
|
2141
3610
|
function parseFlowFlagOverrides(values) {
|
|
2142
3611
|
const result = {};
|
|
@@ -2164,29 +3633,17 @@ function parseArgs(args) {
|
|
|
2164
3633
|
function requiredPosition(parsed, index, label) {
|
|
2165
3634
|
const value = parsed.positional[index];
|
|
2166
3635
|
if (!value) {
|
|
2167
|
-
throw new AppError("usage", `Missing required argument: ${label}`, 2);
|
|
3636
|
+
throw new AppError("usage", `Missing required argument: ${label}`, 2, { parameter: label, reason: "missing_required_argument" });
|
|
2168
3637
|
}
|
|
2169
3638
|
return value;
|
|
2170
3639
|
}
|
|
2171
|
-
function optionalOption(parsed, key) {
|
|
2172
|
-
const values = parsed.options.get(key);
|
|
2173
|
-
const value = values?.[values.length - 1];
|
|
2174
|
-
return value === flagValue ? undefined : value;
|
|
2175
|
-
}
|
|
2176
3640
|
function allOptions(parsed, key) {
|
|
2177
3641
|
return (parsed.options.get(key) ?? []).filter((value) => value !== flagValue);
|
|
2178
3642
|
}
|
|
2179
|
-
function requiredOption(parsed, key) {
|
|
2180
|
-
const value = optionalOption(parsed, key);
|
|
2181
|
-
if (!value) {
|
|
2182
|
-
throw new AppError("usage", `Missing required option: --${key}`, 2);
|
|
2183
|
-
}
|
|
2184
|
-
return value;
|
|
2185
|
-
}
|
|
2186
3643
|
function requiredScopeProjectRoot(root, operation) {
|
|
2187
3644
|
if (root)
|
|
2188
3645
|
return root;
|
|
2189
|
-
throw new AppError("usage", `${operation} requires --project-root or a unique protocol context`, 2);
|
|
3646
|
+
throw new AppError("usage", `${operation} requires --project-root or a unique protocol context`, 2, { parameter: "project-root", reason: "missing_required_option" });
|
|
2190
3647
|
}
|
|
2191
3648
|
function requiredOptionOrScope(parsed, root, key) {
|
|
2192
3649
|
return root ?? requiredOption(parsed, key);
|
|
@@ -2205,7 +3662,7 @@ function requiredOptionAlias(parsed, keys) {
|
|
|
2205
3662
|
if (value)
|
|
2206
3663
|
return value;
|
|
2207
3664
|
}
|
|
2208
|
-
throw new AppError("usage", `Missing required option: ${keys.map((key) => `--${key}`).join(" or ")}`, 2);
|
|
3665
|
+
throw new AppError("usage", `Missing required option: ${keys.map((key) => `--${key}`).join(" or ")}`, 2, { parameter: [...keys].sort(), reason: "missing_required_option" });
|
|
2209
3666
|
}
|
|
2210
3667
|
function hasOption(parsed, key) {
|
|
2211
3668
|
return parsed.options.has(key);
|
|
@@ -2222,26 +3679,6 @@ function parseBoolean(value, key) {
|
|
|
2222
3679
|
function parseOptionalBoolean(value, key) {
|
|
2223
3680
|
return typeof value === "string" ? parseBoolean(value, key) : undefined;
|
|
2224
3681
|
}
|
|
2225
|
-
function optionalNumber(parsed, key) {
|
|
2226
|
-
const value = optionalOption(parsed, key);
|
|
2227
|
-
if (typeof value === "undefined") {
|
|
2228
|
-
return undefined;
|
|
2229
|
-
}
|
|
2230
|
-
const parsedNumber = Number(value);
|
|
2231
|
-
if (!Number.isFinite(parsedNumber)) {
|
|
2232
|
-
throw new AppError("validation", `--${key} must be a number`, 2);
|
|
2233
|
-
}
|
|
2234
|
-
return parsedNumber;
|
|
2235
|
-
}
|
|
2236
|
-
function optionalPositiveNumber(parsed, key, integer = false) {
|
|
2237
|
-
const value = optionalNumber(parsed, key);
|
|
2238
|
-
if (typeof value === "undefined")
|
|
2239
|
-
return undefined;
|
|
2240
|
-
if (value <= 0 || (integer && !Number.isInteger(value))) {
|
|
2241
|
-
throw new AppError("validation", `--${key} must be a positive${integer ? " integer" : ""} number`, 2);
|
|
2242
|
-
}
|
|
2243
|
-
return value;
|
|
2244
|
-
}
|
|
2245
3682
|
function requiredWorkerId(parsed) {
|
|
2246
3683
|
return requiredOption(parsed, "worker-id");
|
|
2247
3684
|
}
|
|
@@ -2255,16 +3692,21 @@ function parseWorktreePolicy(value) {
|
|
|
2255
3692
|
}
|
|
2256
3693
|
throw new AppError("validation", "--worktree must be keep or remove", 2);
|
|
2257
3694
|
}
|
|
2258
|
-
async function readStdin(stream) {
|
|
3695
|
+
async function readStdin(stream, maxBytes) {
|
|
2259
3696
|
if (!stream) {
|
|
2260
3697
|
return "";
|
|
2261
3698
|
}
|
|
2262
3699
|
let data = "";
|
|
2263
|
-
|
|
3700
|
+
if (maxBytes !== undefined)
|
|
3701
|
+
return readBoundedUtf8Stream(stream, maxBytes);
|
|
3702
|
+
const hash = createHash("sha256"), decoder = new TextDecoder("utf-8", { ignoreBOM: true });
|
|
2264
3703
|
for await (const chunk of stream) {
|
|
2265
|
-
|
|
3704
|
+
const bytes = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
3705
|
+
hash.update(bytes);
|
|
3706
|
+
data += decoder.decode(bytes, { stream: true });
|
|
2266
3707
|
}
|
|
2267
|
-
|
|
3708
|
+
recordConsumedInputDigest(hash.digest("hex"));
|
|
3709
|
+
return data + decoder.decode();
|
|
2268
3710
|
}
|
|
2269
3711
|
function refreshDashboardsAfterMutation(context, args, result, resolvedScope) {
|
|
2270
3712
|
try {
|
|
@@ -2275,7 +3717,8 @@ function refreshDashboardsAfterMutation(context, args, result, resolvedScope) {
|
|
|
2275
3717
|
const projectRoot = projectRootForMutation(context, args, result, resolvedScope);
|
|
2276
3718
|
if (!projectRoot)
|
|
2277
3719
|
return [];
|
|
2278
|
-
|
|
3720
|
+
const runId = result && typeof result === "object" && "run_id" in result && typeof result.run_id === "string" ? result.run_id : undefined;
|
|
3721
|
+
autoRefreshDashboards(context, { projectRoot, ...(runId ? { runId } : {}) });
|
|
2279
3722
|
return [];
|
|
2280
3723
|
}
|
|
2281
3724
|
catch (error) {
|
|
@@ -2337,7 +3780,7 @@ function projectRootForMutation(context, args, result, resolvedScope) {
|
|
|
2337
3780
|
return projectRootForMergeQueueMutation(context, command, parsed);
|
|
2338
3781
|
}
|
|
2339
3782
|
if (family === "merge" && ["one-shot", "apply", "repair", "request"].includes(command ?? "")) {
|
|
2340
|
-
return requiredOption(parsed, "project-root");
|
|
3783
|
+
return resolvedScope ?? requiredOption(parsed, "project-root");
|
|
2341
3784
|
}
|
|
2342
3785
|
if (family === "merge-worker" && ["start", "stop"].includes(command ?? "")) {
|
|
2343
3786
|
return requiredOption(parsed, "project-root");
|
|
@@ -2355,7 +3798,7 @@ function projectRootForMutation(context, args, result, resolvedScope) {
|
|
|
2355
3798
|
return requiredOption(parsed, "project-root");
|
|
2356
3799
|
}
|
|
2357
3800
|
if (family === "stage" && ["start", "pause", "resume", "finish"].includes(command ?? "")) {
|
|
2358
|
-
return requiredOption(parsed, "project-root");
|
|
3801
|
+
return resolvedScope ?? requiredOption(parsed, "project-root");
|
|
2359
3802
|
}
|
|
2360
3803
|
if (family === "worktree" && ["create", "bootstrap", "close"].includes(command ?? "")) {
|
|
2361
3804
|
return protocolProjectRoot(context, requiredOption(parsed, "protocol-id"));
|
|
@@ -2369,7 +3812,7 @@ function projectRootForMutation(context, args, result, resolvedScope) {
|
|
|
2369
3812
|
if (family === "opencode" && ((command === "event" && requiredPosition(parsed, 0, "opencode event action") === "handle") || command === "usage")) {
|
|
2370
3813
|
return requiredOption(parsed, "project-root");
|
|
2371
3814
|
}
|
|
2372
|
-
if (family
|
|
3815
|
+
if (["agy", "droid"].includes(family ?? "") && ((command === "event" && requiredPosition(parsed, 0, `${family} event action`) === "handle") || command === "usage"))
|
|
2373
3816
|
return requiredOption(parsed, "project-root");
|
|
2374
3817
|
if (family === "codex" && command === "hooks") {
|
|
2375
3818
|
const action = requiredPosition(parsed, 0, "codex hooks action");
|
|
@@ -2406,14 +3849,36 @@ function projectRootForMergeQueueMutation(context, command, parsed) {
|
|
|
2406
3849
|
function protocolProjectRoot(context, protocolId) {
|
|
2407
3850
|
return requireProtocol(context, protocolId).project_root;
|
|
2408
3851
|
}
|
|
3852
|
+
function prepareCliWorkRepair(context, parsed, objective, scopeProjectRoot) {
|
|
3853
|
+
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") ?? [];
|
|
3854
|
+
if ([findings.length > 0, unresolved.length > 0, receipts.length > 0].filter(Boolean).length !== 1)
|
|
3855
|
+
throw new AppError("usage", "Repair requires exactly one source: --from-check, --from-finding or --from-unresolved", 2);
|
|
3856
|
+
const scope = { projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "work repair add"), runId: requiredOption(parsed, "run"), objective };
|
|
3857
|
+
if (findings.length) {
|
|
3858
|
+
if (origins.length || hasOption(parsed, "verification-file"))
|
|
3859
|
+
throw new AppError("usage", "Finding repair derives its origins and cannot accept --origin-work or --verification-file", 2);
|
|
3860
|
+
return prepareVnextCodeReviewRepair(context, { ...scope, findingIds: findings, checkRefsByFinding: Object.fromEntries(findings.map(finding => [finding, parsed.options.get("check-ref") ?? []])) });
|
|
3861
|
+
}
|
|
3862
|
+
if (!origins.length)
|
|
3863
|
+
throw new AppError("usage", "Repair requires at least one --origin-work", 2);
|
|
3864
|
+
if (hasOption(parsed, "check-ref"))
|
|
3865
|
+
throw new AppError("usage", "--check-ref is only supported with --from-finding", 2);
|
|
3866
|
+
if (receipts.length > 1)
|
|
3867
|
+
throw new AppError("usage", "Create one repair Work per --from-check receipt", 2);
|
|
3868
|
+
if (!unresolved.length && hasOption(parsed, "verification-file"))
|
|
3869
|
+
throw new AppError("usage", "--verification-file is only supported with --from-unresolved", 2);
|
|
3870
|
+
const input = { ...scope, originWorkIds: origins, ...(unresolved.length ? { semanticUnresolved: unresolved, verificationPath: requiredOption(parsed, "verification-file") } : { checkReceiptId: receipts[0] }) };
|
|
3871
|
+
return { input, value: prepareVnextCodeRepair(context, input) };
|
|
3872
|
+
}
|
|
3873
|
+
function workerPromptInput(parsed) {
|
|
3874
|
+
const workspaceRoot = optionalOption(parsed, "workspace-root"), taskFile = optionalOption(parsed, "task-file");
|
|
3875
|
+
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 } : {}) };
|
|
3876
|
+
}
|
|
2409
3877
|
function runnerIntake(context) {
|
|
2410
3878
|
const input = context.value.task_input?.[0];
|
|
2411
3879
|
if (!input?.path)
|
|
2412
3880
|
throw new AppError("validation", "Runner SPECIFY context requires one ordered task_input file", 2);
|
|
2413
|
-
|
|
2414
|
-
if (!fs.existsSync(file) || !fs.statSync(file).isFile())
|
|
2415
|
-
throw new AppError("not_found", "Runner SPECIFY task input is missing", 2, { task_input: file });
|
|
2416
|
-
return fs.readFileSync(file, "utf8");
|
|
3881
|
+
return prepareTextFile(input.path, "Runner SPECIFY task input").text;
|
|
2417
3882
|
}
|
|
2418
3883
|
/** Stage services install context before their lifecycle transition. Keeping
|
|
2419
3884
|
* this check here makes an incomplete service migration fail closed instead of
|