@deksden-com/dd-flow-cli 0.9.0-beta.14 → 0.9.0-beta.141
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +740 -0
- package/README.md +143 -1
- package/dist/build-info.json +8 -8
- package/dist/cli/argument-values.js +38 -0
- package/dist/cli/command-inputs.js +376 -0
- package/dist/cli/command-metadata.js +197 -0
- package/dist/cli/command-option-descriptions.js +191 -0
- package/dist/cli/completion-cli.js +70 -0
- package/dist/cli/completion.js +57 -0
- package/dist/cli/error-output.js +52 -0
- package/dist/cli/executor-onboarding.js +76 -0
- package/dist/cli/flows.js +29 -0
- package/dist/cli/harnesses.js +84 -0
- package/dist/cli/help.js +328 -18
- package/dist/cli/hook-ingress.js +78 -0
- package/dist/cli/input-preparation.js +165 -0
- package/dist/cli/memory-flow-admission.js +44 -0
- package/dist/cli/memory-init-invocation.js +78 -0
- package/dist/cli/memory-init.js +240 -0
- package/dist/cli/memory-storage-admission.js +53 -0
- package/dist/cli/models.js +39 -0
- package/dist/cli/native-hook-ingress.js +80 -0
- package/dist/cli/operator-profiles.js +61 -0
- package/dist/cli/output-options.js +35 -0
- package/dist/cli/response-file.js +71 -0
- package/dist/cli/run-cli.js +1689 -545
- package/dist/cli/runtime-maintenance-diagnostics.js +46 -0
- package/dist/cli/runtime-maintenance-ingress.js +52 -0
- package/dist/cli/runtime-process.js +153 -0
- package/dist/cli/setup.js +149 -0
- package/dist/cli.js +71 -2
- package/dist/domain/stage-catalog.js +1 -1
- package/dist/domain/stage-handler-identities.js +20 -0
- package/dist/harness-runtime/DD-ZCODE.md +112 -0
- package/dist/harness-runtime/OPENCODE-LICENSE.txt +24 -0
- package/dist/harness-runtime/bin/dd-agy.mjs +56 -0
- package/dist/harness-runtime/bin/dd-codex.mjs +27 -0
- package/dist/harness-runtime/bin/dd-droid.mjs +34 -0
- package/dist/harness-runtime/bin/dd-grok.mjs +40 -0
- package/dist/harness-runtime/bin/dd-opencode.mjs +22 -0
- package/dist/harness-runtime/bin/dd-zcode.mjs +87 -0
- package/dist/harness-runtime/lib/adapter-timeouts.d.mts +5 -0
- package/dist/harness-runtime/lib/adapter-timeouts.mjs +34 -0
- package/dist/harness-runtime/lib/agy-hooks.d.mts +1 -0
- package/dist/harness-runtime/lib/agy-hooks.mjs +9 -0
- package/dist/harness-runtime/lib/daemon-observation.d.mts +2 -0
- package/dist/harness-runtime/lib/daemon-observation.mjs +163 -0
- package/dist/harness-runtime/lib/daemon-operations.d.mts +1 -0
- package/dist/harness-runtime/lib/daemon-operations.mjs +371 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.d.mts +6 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +728 -0
- package/dist/harness-runtime/lib/dd-agy.mjs +70 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.mjs +196 -0
- package/dist/harness-runtime/lib/dd-codex.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex.mjs +817 -0
- package/dist/harness-runtime/lib/dd-droid-daemon.mjs +140 -0
- package/dist/harness-runtime/lib/dd-droid.mjs +584 -0
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +498 -0
- package/dist/harness-runtime/lib/dd-grok.mjs +177 -0
- package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +557 -0
- package/dist/harness-runtime/lib/dd-opencode.mjs +363 -0
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +751 -0
- package/dist/harness-runtime/lib/dd-zcode.mjs +1145 -0
- package/dist/harness-runtime/lib/delegation-instructions.d.mts +14 -0
- package/dist/harness-runtime/lib/delegation-instructions.mjs +157 -0
- package/dist/harness-runtime/lib/dispatch-fence.mjs +14 -0
- package/dist/harness-runtime/lib/driver-recovery.mjs +139 -0
- package/dist/harness-runtime/lib/droid-observation.mjs +66 -0
- package/dist/harness-runtime/lib/lease-renewal.d.mts +8 -0
- package/dist/harness-runtime/lib/lease-renewal.mjs +37 -0
- package/dist/harness-runtime/lib/lifecycle-command.d.mts +20 -0
- package/dist/harness-runtime/lib/lifecycle-command.mjs +273 -0
- package/dist/harness-runtime/lib/lifecycle-contract.d.mts +10 -0
- package/dist/harness-runtime/lib/lifecycle-contract.mjs +19 -0
- package/dist/harness-runtime/lib/managed-daemon.d.mts +16 -0
- package/dist/harness-runtime/lib/managed-daemon.mjs +683 -0
- package/dist/harness-runtime/lib/managed-flow-env.mjs +15 -0
- package/dist/harness-runtime/lib/model-observations.mjs +101 -0
- package/dist/harness-runtime/lib/native-children.d.mts +16 -0
- package/dist/harness-runtime/lib/native-children.mjs +113 -0
- package/dist/harness-runtime/lib/native-hook-command.d.mts +7 -0
- package/dist/harness-runtime/lib/native-hook-command.mjs +251 -0
- package/dist/harness-runtime/lib/native-request-proof.d.mts +2 -0
- package/dist/harness-runtime/lib/native-request-proof.mjs +65 -0
- package/dist/harness-runtime/lib/native-streams.mjs +45 -0
- package/dist/harness-runtime/lib/observation-clock.d.mts +18 -0
- package/dist/harness-runtime/lib/observation-clock.mjs +92 -0
- package/dist/harness-runtime/lib/opencode-plugin.mjs +85 -0
- package/dist/harness-runtime/lib/operation-context.mjs +5 -0
- package/dist/harness-runtime/lib/operation-errors.d.mts +16 -0
- package/dist/harness-runtime/lib/operation-errors.mjs +127 -0
- package/dist/harness-runtime/lib/operation-progress.d.mts +7 -0
- package/dist/harness-runtime/lib/operation-progress.mjs +114 -0
- package/dist/harness-runtime/lib/process-json.d.mts +5 -0
- package/dist/harness-runtime/lib/process-json.mjs +201 -0
- package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
- package/dist/harness-runtime/lib/provider-overload-policy.d.mts +13 -0
- package/dist/harness-runtime/lib/provider-overload-policy.mjs +138 -0
- package/dist/harness-runtime/lib/runner-events.mjs +200 -0
- package/dist/harness-runtime/lib/runner-lock.mjs +42 -0
- package/dist/harness-runtime/lib/session-settlement.mjs +30 -0
- package/dist/harness-runtime/lib/setup-progress.d.mts +9 -0
- package/dist/harness-runtime/lib/setup-progress.mjs +51 -0
- package/dist/harness-runtime/lib/tool-observations.d.mts +21 -0
- package/dist/harness-runtime/lib/tool-observations.mjs +228 -0
- package/dist/harness-runtime/lib/zcode-usage.mjs +55 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-1y4a697q.js +81 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-2ny4vxct.js +92 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-6mptkzbc.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-7x67cf1q.js +52 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9rqn6x4v.js +16 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9vdz0zwn.js +1661 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ecs5ftka.js +1466 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-frbwqjmf.js +145 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gne7h7mq.js +50 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gx9wnkzr.js +105 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-m2eywc8w.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-nwcpakzx.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-q4g3afnw.js +623 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-qav6mfdz.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-sxt88q69.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-t1xa31vm.js +39 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ve4aws36.js +103 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vmytr4q8.js +4 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vt9jbch8.js +36 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vyas3vgt.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-wjyeq25y.js +30 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-xgnj2w4v.js +20 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-y2etg5bs.js +9 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ydhz1zrh.js +238 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-z6qdwcsf.js +62 -0
- package/dist/harness-runtime/vendor/opencode-client/contract.d.ts +1 -0
- package/dist/harness-runtime/vendor/opencode-client/contract.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api/api.d.ts +2806 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api/api.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api.d.ts +4 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/client.d.ts +2530 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/client.js +15 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.d.ts +7 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.js +6 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.d.ts +2529 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.js +8 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.d.ts +2 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/index.d.ts +35 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/index.js +75 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/rpc.d.ts +20 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/rpc.js +11 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/service.d.ts +90 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/service.js +187 -0
- package/dist/harness-runtime/vendor/opencode-client/package.json +84 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/api.d.ts +21 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/api.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/client.d.ts +252 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/client.js +14 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.d.ts +8 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.js +6 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.d.ts +259 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.js +7 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.d.ts +3 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.js +79 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.d.ts +8932 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.js +70 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/index.d.ts +6 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/index.js +83 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/rpc.d.ts +32 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/rpc.js +7 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/service.d.ts +24 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/service.js +187 -0
- package/dist/harness-runtime/vendor/opencode-client/pty-handoff.d.ts +12 -0
- package/dist/harness-runtime/vendor/opencode-client/pty-handoff.js +14 -0
- package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.d.ts +20 -0
- package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.js +14 -0
- package/dist/harness-runtime/vendor/opencode-client/service-contender.d.ts +37 -0
- package/dist/harness-runtime/vendor/opencode-client/service-contender.js +10 -0
- package/dist/harness-runtime/vendor/opencode-client/service-probe.d.ts +70 -0
- package/dist/harness-runtime/vendor/opencode-client/service-probe.js +16 -0
- package/dist/harness-runtime/vendor/opencode-client/service-timing.d.ts +12 -0
- package/dist/harness-runtime/vendor/opencode-client/service-timing.js +10 -0
- package/dist/harness-runtime/vendor/opencode-client/service-version.d.ts +2 -0
- package/dist/harness-runtime/vendor/opencode-client/service-version.js +6 -0
- package/dist/harness-runtime/vendor/opencode-client/service.d.ts +52 -0
- package/dist/harness-runtime/vendor/opencode-client/service.js +1 -0
- package/dist/harness-runtime/vendor/opencode-client/shared-events.d.ts +11 -0
- package/dist/harness-runtime/vendor/opencode-client/shared-events.js +8 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/connection.d.ts +37 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/connection.js +10 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/data.d.ts +211 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/data.js +18 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/index.d.ts +3 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/index.js +32 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/pty.d.ts +22 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/pty.js +8 -0
- package/dist/harness-runtime/vendor/opencode-plugin.mjs +3 -0
- package/dist/protocol/local-files.js +18 -9
- package/dist/runtime/context.js +8 -4
- package/dist/schemas/agent-profile.schema.json +1 -1
- package/dist/schemas/code-review-result.schema.json +1 -1
- package/dist/schemas/code-verification.schema.json +7 -5
- package/dist/schemas/code-work-batch.schema.json +2 -2
- package/dist/schemas/discovery-result.schema.json +184 -0
- package/dist/schemas/execution-bundle.schema.json +205 -0
- package/dist/schemas/execution-settings.schema.json +162 -0
- package/dist/schemas/flow-descriptor.schema.json +54 -0
- package/dist/schemas/flow-run.schema.json +28 -0
- package/dist/schemas/harness-catalog.schema.json +80 -0
- package/dist/schemas/mb-init-compose-result.schema.json +457 -0
- package/dist/schemas/mb-init-discover-result.schema.json +500 -0
- package/dist/schemas/mb-init-finish-result.schema.json +422 -0
- package/dist/schemas/mb-init-preflight-result.schema.json +367 -0
- package/dist/schemas/mb-init-review-result.schema.json +396 -0
- package/dist/schemas/mb-init-verify-result.schema.json +391 -0
- package/dist/schemas/memory-bootstrap-receipt-v2.schema.json +67 -0
- package/dist/schemas/memory-bootstrap-receipt.schema.json +354 -0
- package/dist/schemas/memory-composition-work-result.schema.json +38 -0
- package/dist/schemas/memory-repair-work-result.schema.json +49 -0
- package/dist/schemas/memory-review-correction-plan.schema.json +30 -0
- package/dist/schemas/memory-review-work-result.schema.json +43 -0
- package/dist/schemas/model-catalog.schema.json +153 -0
- package/dist/schemas/plan-review-decision.schema.json +3 -3
- package/dist/schemas/plan-review-result.schema.json +2 -2
- package/dist/schemas/profile-settings.schema.json +122 -0
- package/dist/schemas/release-manifest.schema.json +291 -0
- package/dist/schemas/run-control-receipt.schema.json +118 -0
- package/dist/schemas/run-control-request.schema.json +36 -0
- package/dist/schemas/run-engine-binding.schema.json +1 -1
- package/dist/schemas/vnext-protocol-plan.schema.json +2 -2
- package/dist/services/branch-context.js +3 -3
- package/dist/services/canon.js +11 -8
- package/dist/services/check-runner-evidence.js +250 -0
- package/dist/services/cleanup.js +133 -40
- package/dist/services/cli-operation-classifier.js +25 -62
- package/dist/services/cli-upgrade-allowlist.js +27 -0
- package/dist/services/code-checks.js +1168 -107
- package/dist/services/codex-hook-delivery.js +32 -0
- package/dist/services/command-context.js +80 -0
- package/dist/services/compatibility-preflight.js +13 -3
- package/dist/services/config.js +11 -8
- package/dist/services/continuation-outcome.js +20 -0
- package/dist/services/controller-child-capacity.js +13 -0
- package/dist/services/controller-child-observer.js +115 -0
- package/dist/services/controller-execution.js +88 -0
- package/dist/services/controller-fanout.js +202 -0
- package/dist/services/dashboard.js +143 -58
- package/dist/services/descriptor-coordinator-session.js +35 -0
- package/dist/services/descriptor-root-work.js +53 -0
- package/dist/services/descriptor-run-settlement.js +105 -0
- package/dist/services/descriptor-run-start.js +449 -0
- package/dist/services/descriptor-stage-coordinator.js +197 -0
- package/dist/services/descriptor-stage-settlement.js +222 -0
- package/dist/services/descriptor-stage-start.js +139 -0
- package/dist/services/discovery/agy-discovery.js +2 -0
- package/dist/services/discovery/codex-discovery.js +2 -0
- package/dist/services/discovery/droid-discovery.js +2 -0
- package/dist/services/discovery/grok-discovery.js +2 -0
- package/dist/services/discovery/opencode-discovery.js +2 -0
- package/dist/services/discovery/static.js +31 -0
- package/dist/services/discovery/zcode-discovery.js +2 -0
- package/dist/services/engines.js +294 -153
- package/dist/services/eval-snapshots.js +1215 -116
- package/dist/services/execution-bundles.js +263 -0
- package/dist/services/execution-policy.js +185 -0
- package/dist/services/executor-onboarding.js +56 -0
- package/dist/services/external-work-launch.js +308 -0
- package/dist/services/flow-catalog.js +21 -0
- package/dist/services/flow-descriptors.js +82 -0
- package/dist/services/frozen-engine.js +250 -0
- package/dist/services/generic-execution.js +164 -0
- package/dist/services/harness-adapter.js +355 -36
- package/dist/services/harness-config.js +2 -0
- package/dist/services/harness-discovery.js +29 -0
- package/dist/services/harness-doctor.js +75 -0
- package/dist/services/harness-probe-effects.js +51 -0
- package/dist/services/harness-version-probe.js +100 -0
- package/dist/services/hooks.js +771 -325
- package/dist/services/hybrid-contracts.js +483 -0
- package/dist/services/ids.js +29 -1
- package/dist/services/installation-lock.js +95 -0
- package/dist/services/installed-flow-source.js +183 -0
- package/dist/services/lanes.js +78 -65
- package/dist/services/lifecycle-command.js +1 -288
- package/dist/services/lifecycle-contract.js +1 -0
- package/dist/services/lifecycle-invocations.js +2007 -0
- package/dist/services/managed-daemon-binding.js +38 -0
- package/dist/services/managed-lease-monitor.js +133 -0
- package/dist/services/managed-processes.js +475 -55
- package/dist/services/memory-accepted-composition.js +77 -0
- package/dist/services/memory-baseline-preservation.js +65 -0
- package/dist/services/memory-completed-correction-wave.js +320 -0
- package/dist/services/memory-compose-handler.js +258 -0
- package/dist/services/memory-composition-acceptance.js +199 -0
- package/dist/services/memory-composition-graph.js +134 -0
- package/dist/services/memory-composition-registration.js +146 -0
- package/dist/services/memory-composition-work-custody.js +151 -0
- package/dist/services/memory-composition-work-results.js +96 -0
- package/dist/services/memory-corrected-review-acceptance.js +129 -0
- package/dist/services/memory-corrected-review-batch.js +30 -0
- package/dist/services/memory-corrected-review-findings.js +106 -0
- package/dist/services/memory-corrected-review-inputs.js +210 -0
- package/dist/services/memory-corrected-review-projection.js +78 -0
- package/dist/services/memory-corrected-review-receipt.js +170 -0
- package/dist/services/memory-corrected-review-registration.js +191 -0
- package/dist/services/memory-correction-check-receipt.js +219 -0
- package/dist/services/memory-correction-input-owner.js +6 -0
- package/dist/services/memory-discover-handler.js +104 -0
- package/dist/services/memory-finish-evidence.js +435 -0
- package/dist/services/memory-finish-handler.js +215 -0
- package/dist/services/memory-finish-integration.js +280 -0
- package/dist/services/memory-finish-readiness.js +122 -0
- package/dist/services/memory-git-integration.js +151 -0
- package/dist/services/memory-historical-corrected-review.js +206 -0
- package/dist/services/memory-historical-original-review.js +130 -0
- package/dist/services/memory-historical-repair-registration.js +99 -0
- package/dist/services/memory-historical-second-review.js +128 -0
- package/dist/services/memory-init-bootstrap-readiness.js +210 -0
- package/dist/services/memory-init-contexts.js +89 -0
- package/dist/services/memory-init-controller-start.js +84 -0
- package/dist/services/memory-init-engine-release.js +223 -0
- package/dist/services/memory-init-git-policy.js +136 -0
- package/dist/services/memory-init-lint-capability.js +185 -0
- package/dist/services/memory-init-native-capacity.js +296 -0
- package/dist/services/memory-init-preflight.js +330 -0
- package/dist/services/memory-init-project-bootstrap.js +599 -0
- package/dist/services/memory-init-request-candidate.js +157 -0
- package/dist/services/memory-init-request-input.js +66 -0
- package/dist/services/memory-init-resource-storage.js +125 -0
- package/dist/services/memory-init-run-allocation.js +81 -0
- package/dist/services/memory-init-run-bundle.js +98 -0
- package/dist/services/memory-init-start.js +181 -0
- package/dist/services/memory-init-startup-custody.js +80 -0
- package/dist/services/memory-init-startup-input.js +240 -0
- package/dist/services/memory-init-storage.js +81 -0
- package/dist/services/memory-init-stores.js +359 -0
- package/dist/services/memory-init-worktrunk-capability.js +178 -0
- package/dist/services/memory-layer-publication.js +611 -0
- package/dist/services/memory-layer.js +156 -0
- package/dist/services/memory-lint-config.js +85 -0
- package/dist/services/memory-native-review-copy.js +165 -0
- package/dist/services/memory-normal-flow-entry.js +75 -0
- package/dist/services/memory-ordinary-defaults.js +112 -0
- package/dist/services/memory-preflight-bootstrap-custody.js +115 -0
- package/dist/services/memory-preflight-handler.js +259 -0
- package/dist/services/memory-repair-current-view.js +299 -0
- package/dist/services/memory-repair-document-delta.js +451 -0
- package/dist/services/memory-repair-history.js +491 -0
- package/dist/services/memory-repair-physical-binding.js +312 -0
- package/dist/services/memory-repair-publication-files.js +152 -0
- package/dist/services/memory-repair-publication-intent.js +27 -0
- package/dist/services/memory-repair-publication-plan.js +160 -0
- package/dist/services/memory-repair-publication-source.js +225 -0
- package/dist/services/memory-repair-publication.js +794 -0
- package/dist/services/memory-repair-registration.js +239 -0
- package/dist/services/memory-repair-work-baseline.js +413 -0
- package/dist/services/memory-repair-work-completion.js +97 -0
- package/dist/services/memory-repair-work-custody.js +73 -0
- package/dist/services/memory-repair-work-results.js +89 -0
- package/dist/services/memory-repair-workspace.js +252 -0
- package/dist/services/memory-repair-writer-copy.js +735 -0
- package/dist/services/memory-result-binding.js +173 -0
- package/dist/services/memory-retained-review-authority.js +129 -0
- package/dist/services/memory-retained-review-results.js +70 -0
- package/dist/services/memory-retained-second-baseline.js +102 -0
- package/dist/services/memory-retained-second-repairs.js +156 -0
- package/dist/services/memory-review-acceptance.js +214 -0
- package/dist/services/memory-review-correction-plan.js +171 -0
- package/dist/services/memory-review-handler.js +585 -0
- package/dist/services/memory-review-inputs.js +200 -0
- package/dist/services/memory-review-receipt.js +180 -0
- package/dist/services/memory-review-registration.js +108 -0
- package/dist/services/memory-review-work-custody.js +124 -0
- package/dist/services/memory-review-work-input.js +72 -0
- package/dist/services/memory-review-work-results.js +73 -0
- package/dist/services/memory-second-corrected-review-inputs.js +165 -0
- package/dist/services/memory-second-correction-plan.js +76 -0
- package/dist/services/memory-second-repair-baseline.js +91 -0
- package/dist/services/memory-second-repair-launch.js +614 -0
- package/dist/services/memory-second-repair-registration.js +88 -0
- package/dist/services/memory-second-repair-source.js +187 -0
- package/dist/services/memory-second-review-acceptance.js +115 -0
- package/dist/services/memory-source-observations.js +117 -0
- package/dist/services/memory-stage-dispatch.js +28 -0
- package/dist/services/memory-stage-results.js +199 -0
- package/dist/services/memory-verification-acceptance.js +135 -0
- package/dist/services/memory-verification-checks.js +252 -0
- package/dist/services/memory-verification-receipt.js +159 -0
- package/dist/services/memory-verify-handler.js +135 -0
- package/dist/services/memory-work-result-preparation.js +63 -0
- package/dist/services/merge-queue.js +228 -149
- package/dist/services/merge-server.js +161 -35
- package/dist/services/migrations.js +37 -16
- package/dist/services/model-catalog.js +109 -0
- package/dist/services/model-discovery.js +46 -0
- package/dist/services/native-daemon-history.js +49 -0
- package/dist/services/native-session-control.js +39 -0
- package/dist/services/operator-profiles.js +236 -0
- package/dist/services/plan-runtime.js +26 -11
- package/dist/services/plans.js +28 -25
- package/dist/services/projects.js +37 -17
- package/dist/services/prompts.js +28 -17
- package/dist/services/protocols.js +163 -87
- package/dist/services/provider-capacity.js +385 -0
- package/dist/services/recovery-observation-budget.js +121 -0
- package/dist/services/recovery-snapshot-database.js +560 -0
- package/dist/services/release-archive-producer.js +52 -0
- package/dist/services/release-archive.js +143 -0
- package/dist/services/release-cache.js +142 -0
- package/dist/services/release-compatibility.js +18 -0
- package/dist/services/release-download.js +67 -0
- package/dist/services/release-envelope.js +104 -0
- package/dist/services/release-manifest.js +45 -0
- package/dist/services/release-payload.js +114 -0
- package/dist/services/release-producer.js +63 -0
- package/dist/services/release-provenance.js +160 -0
- package/dist/services/release-registry.js +66 -0
- package/dist/services/release-source.js +194 -0
- package/dist/services/repair-intents.js +42 -0
- package/dist/services/review-copy.js +165 -0
- package/dist/services/review-deferral.js +23 -0
- package/dist/services/run-control-receipt.js +56 -0
- package/dist/services/run-control-worker.js +411 -0
- package/dist/services/run-control.js +1056 -0
- package/dist/services/run-controller-adapter.js +399 -0
- package/dist/services/run-controller-capture.js +186 -0
- package/dist/services/run-controller-process.js +208 -0
- package/dist/services/run-controller-recovery.js +289 -0
- package/dist/services/run-controller-state.js +97 -0
- package/dist/services/run-controller.js +1205 -0
- package/dist/services/run-engine-bindings.js +1 -1
- package/dist/services/run-fork.js +147 -0
- package/dist/services/run-health.js +149 -0
- package/dist/services/run-observations.js +152 -0
- package/dist/services/run-projection.js +22 -64
- package/dist/services/run-recovery-guard.js +77 -0
- package/dist/services/run-recovery-runtime.js +112 -0
- package/dist/services/run-recovery.js +489 -0
- package/dist/services/run-start-custody.js +177 -0
- package/dist/services/runs.js +1019 -247
- package/dist/services/runtime-budget.js +430 -0
- package/dist/services/runtime-command.js +43 -0
- package/dist/services/runtime-scope-capture.js +52 -0
- package/dist/services/runtime-scope-control.js +468 -0
- package/dist/services/runtime-scope-resume.js +599 -0
- package/dist/services/runtime-scope-stop.js +210 -0
- package/dist/services/runtime-scope-worker.js +287 -0
- package/dist/services/runtime-service.js +96 -0
- package/dist/services/schema-validation.js +10 -8
- package/dist/services/scope-writer-cleanup.js +27 -0
- package/dist/services/sessions.js +35 -69
- package/dist/services/setup.js +377 -0
- package/dist/services/stage-blocker.js +17 -7
- package/dist/services/stage-context.js +193 -19
- package/dist/services/stage-handlers.js +74 -0
- package/dist/services/stage-lifecycle.js +152 -116
- package/dist/services/stage-pause.js +89 -75
- package/dist/services/stage-report-renderer.js +234 -6
- package/dist/services/stage-work-graph.js +52 -0
- package/dist/services/tooling.js +41 -30
- package/dist/services/usage.js +49 -101
- package/dist/services/verification-matrix.js +375 -0
- package/dist/services/vnext-code-review.js +223 -88
- package/dist/services/vnext-code.js +327 -226
- package/dist/services/vnext-contracts.js +10 -1
- package/dist/services/vnext-execution-profile.js +80 -5
- package/dist/services/vnext-fanout.js +311 -19
- package/dist/services/vnext-merge.js +332 -111
- package/dist/services/vnext-plan-review.js +279 -127
- package/dist/services/vnext-plan.js +228 -104
- package/dist/services/vnext-protocolize.js +531 -95
- package/dist/services/vnext-specify.js +119 -81
- package/dist/services/vnext-workspace-policy.js +7 -2
- package/dist/services/work-registry.js +1627 -199
- package/dist/services/workspace-bootstrap.js +52 -15
- package/dist/services/workspace-files.js +85 -0
- package/dist/services/worktrees.js +105 -48
- package/dist/shared/entity-references.js +8 -0
- package/dist/shared/errors.js +27 -0
- package/dist/storage/agent-output.js +26 -0
- package/dist/storage/database.js +891 -39
- package/dist/storage/foreign-key-transaction.js +39 -0
- package/dist/storage/immutable-input.js +45 -0
- package/dist/storage/import-guard.js +102 -0
- package/dist/storage/native-cleanup.js +52 -0
- package/dist/storage/paths.js +17 -4
- package/dist/storage/projection-lock.js +73 -0
- package/dist/storage/work-references.js +23 -0
- package/dist/storage/writer-contract.js +89 -0
- package/dist/storage/writer-migration.js +85 -0
- package/package.json +43 -13
- package/tools/create-canon-release.mjs +33 -0
- package/tools/produce-hybrid-release.mjs +41 -0
- package/tools/repair-paused-run-status.mjs +84 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Async iteration backpressures the native pipe while its existing evidence
|
|
2
|
+
// sink drains. Bound one JSON reply, never lifetime diagnostics.
|
|
3
|
+
export const MAX_NATIVE_LINE_BYTES = 8 * 1024 * 1024;
|
|
4
|
+
export async function readNativeLines(stream, onLine, onError) {
|
|
5
|
+
let buffer = "";
|
|
6
|
+
stream.setEncoding("utf8");
|
|
7
|
+
try {
|
|
8
|
+
for await (const chunk of stream.iterator({ destroyOnReturn: false })) {
|
|
9
|
+
buffer += chunk;
|
|
10
|
+
let at;
|
|
11
|
+
while ((at = buffer.indexOf("\n")) >= 0) {
|
|
12
|
+
const line = buffer.slice(0, at); buffer = buffer.slice(at + 1);
|
|
13
|
+
assertLine(line);
|
|
14
|
+
if (line.trim()) await onLine(line);
|
|
15
|
+
}
|
|
16
|
+
assertLine(buffer);
|
|
17
|
+
}
|
|
18
|
+
if (buffer.trim()) { assertLine(buffer); await onLine(buffer); }
|
|
19
|
+
} catch (cause) { stream.pause(); await observationFailure(cause, onError); }
|
|
20
|
+
}
|
|
21
|
+
export function assertNativeLine(line, phase = "reply") {
|
|
22
|
+
if (Buffer.byteLength(line) > MAX_NATIVE_LINE_BYTES) throw Object.assign(new Error(`Native JSON ${phase} exceeds the bounded capture limit`), { code: phase === "request" ? "native_request_limit" : "operation_observation_lost", details: { reason: `native_${phase}_limit`, limit_bytes: MAX_NATIVE_LINE_BYTES, ...(phase === "request" ? { effect: "no_effect" } : { native_outcome: "unknown" }) } });
|
|
23
|
+
}
|
|
24
|
+
const assertLine = assertNativeLine;
|
|
25
|
+
export function admitNativeRequestBuffer(buffer, socket, schema) {
|
|
26
|
+
try { assertNativeLine(buffer, "request"); return true; }
|
|
27
|
+
catch (error) { socket.pause(); socket.end(`${JSON.stringify({ schema_id: schema, id: null, ok: false, error: { code: error.code, message: error.message, details: error.details } })}\n`, () => socket.destroy()); return false; }
|
|
28
|
+
}
|
|
29
|
+
export function retainNativeReplyBuffer(buffer, finish) {
|
|
30
|
+
try { assertNativeLine(buffer); return true; }
|
|
31
|
+
catch (error) { finish(error); return false; }
|
|
32
|
+
}
|
|
33
|
+
export async function streamNativeDiagnostics(stream, onChunk, onError) {
|
|
34
|
+
stream.setEncoding("utf8");
|
|
35
|
+
try { for await (const chunk of stream.iterator({ destroyOnReturn: false })) await onChunk(String(chunk)); }
|
|
36
|
+
catch (cause) { stream.pause(); await observationFailure(cause, onError); }
|
|
37
|
+
}
|
|
38
|
+
async function observationFailure(cause, onError) {
|
|
39
|
+
const error = nativeObservationError(cause);
|
|
40
|
+
try { await onError(error); }
|
|
41
|
+
catch (publication) { error.cleanup_error ??= { code: publication.code ?? null, message: publication.message }; }
|
|
42
|
+
}
|
|
43
|
+
export function nativeObservationError(cause, details = {}) {
|
|
44
|
+
return !cause.code || /^[A-Z_]+$/.test(cause.code) ? Object.assign(new Error("Native evidence reader failed", { cause }), { code: "operation_observation_lost", details: { native_outcome: "unknown", observation_error: { code: cause.code ?? null, message: cause.message }, ...details } }) : cause;
|
|
45
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const OBSERVATION_CLOCK_CONTRACT_VERSION: "operation-progress@1";
|
|
2
|
+
export function validateObservationDuration(value: number, label?: string): number;
|
|
3
|
+
export interface ObservationState {
|
|
4
|
+
policy: string; timeout_ms: number; started_at: number; cursor?: unknown;
|
|
5
|
+
observed_at: number | null; elapsed_ms: number; uncertainty_elapsed_ms: number;
|
|
6
|
+
gaps: number; observation_lost: boolean;
|
|
7
|
+
}
|
|
8
|
+
export class ObservationClock {
|
|
9
|
+
constructor(options: { timeoutMs: number; gapMs?: number; uncertaintyMs?: number;
|
|
10
|
+
wall?: () => number; monotonic?: () => number; onGap?: (value: Record<string, unknown>) => void;
|
|
11
|
+
saved?: ObservationState | null });
|
|
12
|
+
observationLost: boolean;
|
|
13
|
+
sample(progress?: unknown, progressAt?: number): boolean;
|
|
14
|
+
state(): ObservationState;
|
|
15
|
+
}
|
|
16
|
+
export function observedTimeout(callback: (clock: ObservationClock) => void, timeoutMs: number,
|
|
17
|
+
options?: { gapMs?: number; uncertaintyMs?: number; wall?: () => number; monotonic?: () => number;
|
|
18
|
+
onGap?: (value: Record<string, unknown>) => void; progress?: () => unknown; saved?: ObservationState | null }): ReturnType<typeof setInterval>;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
export const OBSERVATION_CLOCK_CONTRACT_VERSION = "operation-progress@1";
|
|
2
|
+
export function validateObservationDuration(value, label = "observation duration") {
|
|
3
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > 2_147_483_647)
|
|
4
|
+
throw new RangeError(`${label} must be integer milliseconds in 1..2147483647`);
|
|
5
|
+
return value;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** Only observed monotonic time is silence. A clock gap is uncertainty, never
|
|
9
|
+
* progress or proof of provider failure. Durable snapshots retain that episode. */
|
|
10
|
+
export class ObservationClock {
|
|
11
|
+
constructor({ timeoutMs, gapMs = 60_000, uncertaintyMs = 120_000, wall = Date.now,
|
|
12
|
+
monotonic = () => performance.now(), onGap = () => {}, saved = null }) {
|
|
13
|
+
validateObservationDuration(timeoutMs); validateObservationDuration(gapMs, "gap duration");
|
|
14
|
+
validateObservationDuration(uncertaintyMs, "observation recovery duration");
|
|
15
|
+
Object.assign(this, { timeoutMs, gapMs, uncertaintyMs, wall, monotonic, onGap });
|
|
16
|
+
this.lastWall = wall(); this.lastMono = monotonic(); this.startedWall = this.lastWall;
|
|
17
|
+
this.elapsed = 0; this.progress = undefined; this.progressAt = null;
|
|
18
|
+
this.uncertain = false; this.uncertaintyElapsed = 0; this.gaps = 0; this.observationLost = false;
|
|
19
|
+
if (saved) {
|
|
20
|
+
if (saved.policy !== OBSERVATION_CLOCK_CONTRACT_VERSION || saved.timeout_ms !== timeoutMs
|
|
21
|
+
|| !Number.isFinite(saved.started_at) || !Number.isSafeInteger(saved.gaps) || saved.gaps < 0
|
|
22
|
+
|| saved.observed_at !== null && !Number.isFinite(saved.observed_at)
|
|
23
|
+
|| typeof saved.observation_lost !== "boolean"
|
|
24
|
+
|| !Number.isFinite(saved.elapsed_ms) || saved.elapsed_ms < 0
|
|
25
|
+
|| !Number.isFinite(saved.uncertainty_elapsed_ms) || saved.uncertainty_elapsed_ms < 0)
|
|
26
|
+
throw new RangeError("invalid retained observation clock");
|
|
27
|
+
this.startedWall = saved.started_at; this.elapsed = saved.elapsed_ms;
|
|
28
|
+
this.progress = saved.cursor; this.progressAt = saved.observed_at;
|
|
29
|
+
// A reconstructed observer cannot prove silence during its absence.
|
|
30
|
+
this.uncertain = true; this.gaps = saved.gaps + 1;
|
|
31
|
+
this.uncertaintyElapsed = saved.uncertainty_elapsed_ms;
|
|
32
|
+
this.observationLost = saved.observation_lost || this.gaps > 1 || this.uncertaintyElapsed >= uncertaintyMs;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
sample(progress, progressAt) {
|
|
36
|
+
if (progress && typeof progress === "object" && Object.hasOwn(progress, "cursor")) {
|
|
37
|
+
progressAt = progress.observed_at; progress = progress.cursor;
|
|
38
|
+
}
|
|
39
|
+
const wall = this.wall(), mono = this.monotonic();
|
|
40
|
+
const delta = mono - this.lastMono, wallDelta = wall - this.lastWall;
|
|
41
|
+
const gap = !Number.isFinite(delta) || !Number.isFinite(wallDelta)
|
|
42
|
+
|| delta > this.gapMs || wallDelta > this.gapMs || delta < 0 || wallDelta < 0;
|
|
43
|
+
if (gap) {
|
|
44
|
+
this.uncertain = true; this.gaps++;
|
|
45
|
+
this.onGap({ source: "observer_clock", classification: "observation_gap", confirmed_sleep: false,
|
|
46
|
+
started_at: Number.isFinite(this.lastWall) ? this.lastWall : null, observed_at: Number.isFinite(wall) ? wall : null,
|
|
47
|
+
wall_delta_ms: wallDelta, monotonic_delta_ms: delta });
|
|
48
|
+
} else {
|
|
49
|
+
this.elapsed += delta;
|
|
50
|
+
if (this.uncertain) this.uncertaintyElapsed += delta;
|
|
51
|
+
}
|
|
52
|
+
this.lastWall = wall; this.lastMono = mono;
|
|
53
|
+
const validCursor = typeof progress === "string" || typeof progress === "boolean"
|
|
54
|
+
|| typeof progress === "number" && Number.isFinite(progress);
|
|
55
|
+
const changed = validCursor && progress !== this.progress;
|
|
56
|
+
const timestamped = progressAt !== undefined;
|
|
57
|
+
const validTime = !timestamped ? wall >= this.startedWall && (this.progressAt === null || wall >= this.progressAt)
|
|
58
|
+
: Number.isFinite(progressAt) && progressAt >= this.startedWall
|
|
59
|
+
&& progressAt <= wall && (this.progressAt === null || progressAt >= this.progressAt);
|
|
60
|
+
if (changed && validTime && !this.observationLost) {
|
|
61
|
+
const eventAt = timestamped ? progressAt : wall;
|
|
62
|
+
// A delayed pre-gap event cannot establish that observation has recovered.
|
|
63
|
+
const recovers = !this.uncertain || eventAt >= wall - Math.max(0, gap ? 0 : delta);
|
|
64
|
+
this.progress = progress; this.progressAt = eventAt;
|
|
65
|
+
this.elapsed = Math.max(0, wall - eventAt);
|
|
66
|
+
if (recovers) { this.uncertain = false; this.gaps = 0; this.uncertaintyElapsed = 0; }
|
|
67
|
+
}
|
|
68
|
+
if (changed && timestamped && Number.isFinite(progressAt) && !validTime && progressAt > wall) {
|
|
69
|
+
if (!this.uncertain) this.onGap({ source: "observer_clock", classification: "clock_skew",
|
|
70
|
+
confirmed_sleep: false, observed_at: wall, signal_at: progressAt });
|
|
71
|
+
this.uncertain = true;
|
|
72
|
+
}
|
|
73
|
+
if (this.uncertain && (this.gaps > 1 || this.uncertaintyElapsed >= this.uncertaintyMs)) this.observationLost = true;
|
|
74
|
+
return this.observationLost || !this.uncertain && this.elapsed >= this.timeoutMs;
|
|
75
|
+
}
|
|
76
|
+
state() {
|
|
77
|
+
return { policy: OBSERVATION_CLOCK_CONTRACT_VERSION, timeout_ms: this.timeoutMs,
|
|
78
|
+
started_at: this.startedWall, cursor: this.progress, observed_at: this.progressAt,
|
|
79
|
+
elapsed_ms: this.elapsed, uncertainty_elapsed_ms: this.uncertaintyElapsed,
|
|
80
|
+
gaps: this.gaps, observation_lost: this.observationLost };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Node timer handle, compatible with existing clearTimeout cleanup. */
|
|
85
|
+
export function observedTimeout(callback, timeoutMs, options = {}) {
|
|
86
|
+
const clock = new ObservationClock({ timeoutMs, ...options });
|
|
87
|
+
const timer = setInterval(() => {
|
|
88
|
+
const snapshot = options.progress?.();
|
|
89
|
+
if (clock.sample(snapshot)) { clearInterval(timer); callback(clock); }
|
|
90
|
+
}, Math.max(1, Math.min(1_000, timeoutMs / 4)));
|
|
91
|
+
return timer;
|
|
92
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { managedFlowEnv } from './managed-flow-env.mjs';
|
|
3
|
+
import * as Plugin from "../vendor/opencode-plugin.mjs";
|
|
4
|
+
import { hookBudget, invokeNativeHook, writeNativeHookFailure } from "./native-hook-command.mjs";
|
|
5
|
+
import { callDaemon } from "./dd-opencode-daemon.mjs";
|
|
6
|
+
import { errorRecord } from "./operation-errors.mjs";
|
|
7
|
+
|
|
8
|
+
/** Launch-bound server plugin; never uses a process-global 'last Session'. */
|
|
9
|
+
export function makeOpenCodePlugin(binding, invoke = invokeNativeHook, resolve = callDaemon) {
|
|
10
|
+
return Plugin.define({ id: binding.ddFlowBin ? "dd-flow" : "dd-flow-security", async setup(ctx) {
|
|
11
|
+
const participating = new Map();
|
|
12
|
+
const key = event => JSON.stringify([binding.daemonId, event.sessionID, event.messageID, event.id]);
|
|
13
|
+
const envelope = async (phase, event, budget, outcome) => {
|
|
14
|
+
const input = event.input;
|
|
15
|
+
if (!input || typeof input !== "object" || typeof input.command !== "string") throw new Error("OpenCode shell input is unavailable");
|
|
16
|
+
const identity = await budget.run(() => resolve(binding.stateDir, "hook.resolve", { sessionId: event.sessionID,
|
|
17
|
+
payload: { phase, message_id: event.messageID, tool_call_id: event.id, input } }, budget.remaining("identity")), "identity");
|
|
18
|
+
const info = identity?.nativeSession;
|
|
19
|
+
if (!identity?.nativeHookBinding || info?.id !== event.sessionID || !info.directory) throw Object.assign(new Error("OpenCode Session identity is unavailable"), { code: "native_hook_unproven" });
|
|
20
|
+
const cwd = path.resolve(info.directory, input.workdir ?? ".");
|
|
21
|
+
return { schema_id: "dd-flow/opencode-tool-event@1", native_protocol: 2,
|
|
22
|
+
native_hook_binding: identity.nativeHookBinding,
|
|
23
|
+
event_id: [binding.daemonId, event.sessionID, event.messageID, event.id].join(":"), phase,
|
|
24
|
+
daemon_id: binding.daemonId, server_version: binding.version, plugin_sha256: process.env.DD_OPENCODE_PLUGIN_SHA256,
|
|
25
|
+
session: { provider_session_id: info.id, parent_provider_session_id: info.parentID ?? null,
|
|
26
|
+
agent_id: event.agent, directory: cwd, project_id: info.projectID },
|
|
27
|
+
message_id: event.messageID, tool_call_id: event.id, tool: "shell", native_tool: event.tool,
|
|
28
|
+
profile: { provider: info.model?.providerID, model: info.model?.id, variant: info.model?.variant ?? "default", agent: event.agent },
|
|
29
|
+
input: { ...input, workdir: cwd }, ...(outcome ? { outcome } : {}) };
|
|
30
|
+
};
|
|
31
|
+
const deliver = async (payload, budget) => {
|
|
32
|
+
const identity = { nativeHookBinding: payload.native_hook_binding };
|
|
33
|
+
try {
|
|
34
|
+
const result = await invoke({ bin: binding.ddFlowBin, nodeBin: binding.nodeBin,
|
|
35
|
+
args: ["opencode", "event", "handle", "--project-root", binding.projectRoot, "--json"],
|
|
36
|
+
payload: { ...payload, native_hook_binding: identity.nativeHookBinding }, home: binding.ddFlowHome, timeoutMs: budget.remaining("admission", 15000) });
|
|
37
|
+
await budget.run(() => resolve(binding.stateDir, "hook.verify", { binding: identity.nativeHookBinding }, budget.remaining("generation")), "generation");
|
|
38
|
+
if (result?.permissionDecision === "deny" || result?.hookSpecificOutput?.permissionDecision === "deny") throw Object.assign(new Error("dd-flow denied native shell admission"), { code: "native_hook_rejected" });
|
|
39
|
+
return result;
|
|
40
|
+
} catch (error) {
|
|
41
|
+
error.details = { ...error.details, native_hook_binding: identity.nativeHookBinding, ...(payload.phase === "after" ? { effect: "unknown", tool_outcome: payload.outcome } : {}) };
|
|
42
|
+
try { await writeNativeHookFailure(binding.stateDir, identity.nativeHookBinding, error, hookBudget(1_000, null)); await resolve(binding.stateDir, "hook.failed", {}, 1_000); }
|
|
43
|
+
catch (storage) {
|
|
44
|
+
error.details.receipt_error = { code: storage.code ?? "hook_receipt_write_failed", message: storage.message };
|
|
45
|
+
try { await resolve(binding.stateDir, "hook.failed", { binding: identity.nativeHookBinding, failure: errorRecord(error) }, 1_000); }
|
|
46
|
+
catch (notify) { error.details.receipt_error.notification_error = notify.code ?? "hook_notification_failed"; }
|
|
47
|
+
}
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
await ctx.shell.hook("create.before", event => {
|
|
52
|
+
Object.assign(event.env, managedFlowEnv(binding));
|
|
53
|
+
delete event.env.OPENCODE_PASSWORD; delete event.env.OPENCODE_SERVER_PASSWORD;
|
|
54
|
+
});
|
|
55
|
+
await ctx.tool.hook("execute.before", async event => {
|
|
56
|
+
if (event.tool !== "shell") return;
|
|
57
|
+
if (event.input.timeout === undefined) event.input.timeout = 0;
|
|
58
|
+
if (!binding.ddFlowBin) return;
|
|
59
|
+
const budget = hookBudget(), payload = await envelope("before", event, budget);
|
|
60
|
+
event.input = { ...event.input, workdir: payload.session.directory };
|
|
61
|
+
const result = await deliver({ ...payload, input: event.input }, budget);
|
|
62
|
+
if (result?.observed) {
|
|
63
|
+
participating.set(key(event), payload);
|
|
64
|
+
await ctx.storage.set(key(event), JSON.parse(JSON.stringify(payload)));
|
|
65
|
+
// The common observer, not OpenCode's fixed 120-second cutoff, owns
|
|
66
|
+
// flow CLI inactivity. No lifecycle process is detached here.
|
|
67
|
+
event.input = { ...event.input, ...(result.hookSpecificOutput?.updatedInput ?? {}), timeout: 0, background: false };
|
|
68
|
+
} else if (event.input.timeout === undefined) event.input.timeout = 0;
|
|
69
|
+
});
|
|
70
|
+
await ctx.tool.hook("execute.after", async event => {
|
|
71
|
+
if (event.tool !== "shell") return;
|
|
72
|
+
const budget = hookBudget(), original = participating.get(key(event)) ?? await ctx.storage.get(key(event));
|
|
73
|
+
if (!original) return; // Ordinary shell tools do not create lifecycle receipts.
|
|
74
|
+
const output = event.result?.output;
|
|
75
|
+
const success = event.status === "completed" && output?.status !== "running" && output?.exit === 0 && !output?.timeout && !output?.signal;
|
|
76
|
+
const outcome = { status: success ? "completed" : output?.status === "running" ? "running" : "failed",
|
|
77
|
+
exit_code: output?.exit ?? null, signal: output?.signal ?? null, shell_id: output?.shellID ?? null };
|
|
78
|
+
const payload = await envelope("after", event, budget, outcome);
|
|
79
|
+
await deliver({ ...payload, ...(original ? { event_id: original.event_id } : {}) }, budget);
|
|
80
|
+
participating.delete(key(event));
|
|
81
|
+
await ctx.storage.remove(key(event));
|
|
82
|
+
});
|
|
83
|
+
return () => participating.clear();
|
|
84
|
+
} });
|
|
85
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
|
|
3
|
+
// The existing runner operation owns its child driver requests, including calls
|
|
4
|
+
// nested in Judge/HITL/fanout. Async context keeps parallel executions separate.
|
|
5
|
+
export const operationContext = new AsyncLocalStorage();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const OPERATION_ERROR_CONTRACT_VERSION: "operation-errors@2";
|
|
2
|
+
export interface ErrorRecord {
|
|
3
|
+
code: string;
|
|
4
|
+
message: string;
|
|
5
|
+
retryable?: boolean;
|
|
6
|
+
exitCode?: number;
|
|
7
|
+
details?: unknown;
|
|
8
|
+
cause?: ErrorRecord;
|
|
9
|
+
cleanup_error?: ErrorRecord;
|
|
10
|
+
}
|
|
11
|
+
export function errorRecord(error: unknown, depth?: number): ErrorRecord;
|
|
12
|
+
export function redactDiagnostic(value: string): string;
|
|
13
|
+
export function diagnosticJson(value: unknown): string;
|
|
14
|
+
export function isObservationLoss(error: unknown): boolean;
|
|
15
|
+
export function isProvenShellCorrection(error: unknown): boolean;
|
|
16
|
+
export function reportedError(value: Record<string, unknown>, fallback: string): Error & ErrorRecord;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// An observer losing its response does not establish that a provider Turn failed.
|
|
2
|
+
export const OPERATION_ERROR_CONTRACT_VERSION = "operation-errors@2";
|
|
3
|
+
export function redactDiagnostic(value) {
|
|
4
|
+
return value.replace(/\b(Bearer|Basic)\s+[^\s"']+/gi, "$1 <redacted>")
|
|
5
|
+
.replace(/(^|[^\w-])([\w-]+)(["']?\s*[=:]\s*["']?)([^\s"',;&}]+)/g,
|
|
6
|
+
(whole, prefix, key, separator) => /token|secret|password|api[_-]?key|authorization/i.test(key)
|
|
7
|
+
? `${prefix}${key}${separator}<redacted>` : whole)
|
|
8
|
+
.replace(/(\b[A-Za-z][A-Za-z0-9+.-]{0,31}:\/\/)[^\s/:@]+:[^\s/@]+@/g, "$1<redacted>@");
|
|
9
|
+
}
|
|
10
|
+
export function diagnosticJson(value) {
|
|
11
|
+
return JSON.stringify(value, (key, item) => /token|secret|password|api[_-]?key|authorization/i.test(key)
|
|
12
|
+
? "<redacted>" : typeof item === "string" ? redactDiagnostic(item) : item, 2);
|
|
13
|
+
}
|
|
14
|
+
// Only use after the native delivery/operation owner has been verified.
|
|
15
|
+
export function isProvenShellCorrection(error) {
|
|
16
|
+
const details = error?.details;
|
|
17
|
+
return error?.code === "lifecycle_shell_syntax_invalid" && details?.phase === "prepare"
|
|
18
|
+
&& details.effect === "no_effect" && details.recoverable === true
|
|
19
|
+
&& details.disposition === "pre_cli_shell_refusal" && details.cli_admission_barrier === true
|
|
20
|
+
&& typeof details.issued_invocation_id === "string" && details.issued_invocation_id.trim().length > 0
|
|
21
|
+
&& typeof details.event_key === "string" && details.event_key.trim().length > 0;
|
|
22
|
+
}
|
|
23
|
+
export function isObservationLoss(error) {
|
|
24
|
+
return ["rpc_timeout", "native_timeout", "native_outcome_unknown", "native_backend_dead", "native_backend_pipe_broken", "bridge_exited", "bridge_pipe_broken", "daemon_timeout", "turn_timeout", "operation_observation_lost", "daemon_connection_closed", "harness_adapter_timeout", "harness_adapter_output_limit", "harness_adapter_invalid", "subject_liveness_timeout", "command_observation_lost", "command_output_limit", "operation_output_limit"].includes(error?.code);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function field(value, key) { try { return value?.[key]; } catch { return "[unreadable]"; } }
|
|
28
|
+
function text(value) { try { return String(value); } catch { return "[unreadable]"; } }
|
|
29
|
+
function boundedText(value, budget, limit = 16000) {
|
|
30
|
+
let result = value.slice(0, limit);
|
|
31
|
+
if (Buffer.byteLength(JSON.stringify(result)) > budget.remaining) result = result.slice(0, Math.max(0, Math.floor(budget.remaining / 6)));
|
|
32
|
+
budget.remaining -= Buffer.byteLength(JSON.stringify(result));
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
// Lifecycle controls are not optional diagnostics. Reserve them before bulk
|
|
36
|
+
// messages/details, including nested typed causes, without copying authority
|
|
37
|
+
// that was truncated into a different command or continuation receipt.
|
|
38
|
+
const operationalDetailKeys = ["phase", "effect", "recoverable", "retryable_no_effect", "handoff_required", "storage_retry", "repeated_without_progress", "cleanup_unconfirmed", "native_dispatch_started", "disposition", "cli_admission_barrier", "native_pre_cli_refusal", "issued_invocation_id", "invocation_id", "event_key", "refusal_event_key", "hook_event_id", "work_id", "hook_retry_signature", "rejection_signature", "retry_command", "repair_command", "continuation", "retry_instruction", "recovery_instruction", "authority_truncated"];
|
|
39
|
+
function operationalDetails(value, budget) {
|
|
40
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
41
|
+
const entries = [];
|
|
42
|
+
let authorityTruncated = false;
|
|
43
|
+
for (const key of operationalDetailKeys) {
|
|
44
|
+
try { if (!Object.hasOwn(value, key)) continue; } catch { continue; }
|
|
45
|
+
const raw = field(value, key), reserved = { ...budget };
|
|
46
|
+
const name = boundedText(key, reserved), result = jsonDiagnostic(raw, 0, new Set(), reserved);
|
|
47
|
+
if (name !== key || typeof raw === "string" && result !== raw
|
|
48
|
+
|| key === "continuation" && JSON.stringify(result).includes("[truncated]")) {
|
|
49
|
+
authorityTruncated ||= ["retry_command", "repair_command", "continuation"].includes(key);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
Object.assign(budget, reserved);
|
|
53
|
+
entries.push([key, result]);
|
|
54
|
+
}
|
|
55
|
+
return { ...Object.fromEntries(entries), ...(authorityTruncated ? { recoverable: false, authority_truncated: true } : {}) };
|
|
56
|
+
}
|
|
57
|
+
function jsonDiagnostic(value, depth, seen, budget, excludedKeys = []) {
|
|
58
|
+
if (depth >= 12 || budget.remaining <= 0 || budget.nodes-- <= 0) return "[truncated]";
|
|
59
|
+
if (typeof value === "string") { const result = boundedText(value, budget); return result.length === value.length ? result : `${result}[truncated]`; }
|
|
60
|
+
if (value === null || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) { budget.remaining -= String(value).length + 1; return value; }
|
|
61
|
+
if (typeof value !== "object") return "[unavailable]";
|
|
62
|
+
if (seen.has(value)) return "[circular]";
|
|
63
|
+
seen.add(value); budget.remaining -= 2;
|
|
64
|
+
let result;
|
|
65
|
+
try {
|
|
66
|
+
if (Array.isArray(value)) {
|
|
67
|
+
result = [];
|
|
68
|
+
for (let i = 0; i < Math.min(value.length, 256); i++) {
|
|
69
|
+
if (budget.remaining <= 0 || budget.nodes <= 0) { result.push("[truncated]"); break; }
|
|
70
|
+
result.push(jsonDiagnostic(field(value, i), depth + 1, seen, budget));
|
|
71
|
+
}
|
|
72
|
+
if (value.length > 256) result.push("[truncated]");
|
|
73
|
+
} else {
|
|
74
|
+
const entries = [];
|
|
75
|
+
// Error.message and the standard Error.cause are non-enumerable.
|
|
76
|
+
// Keep the original identity and shared bounds for nested diagnostics.
|
|
77
|
+
const errorFields = value instanceof Error ? ["message", "cause"].filter(key => field(value, key) !== undefined) : [];
|
|
78
|
+
for (const key of errorFields) {
|
|
79
|
+
if (budget.remaining <= 0 || budget.nodes <= 0) { entries.push(["[truncated]", true]); break; }
|
|
80
|
+
entries.push([boundedText(key, budget), jsonDiagnostic(field(value, key), depth + 1, seen, budget)]);
|
|
81
|
+
}
|
|
82
|
+
for (const key in value) {
|
|
83
|
+
if (!Object.hasOwn(value, key) || errorFields.includes(key) || excludedKeys.includes(key)) continue;
|
|
84
|
+
if (entries.length >= 256 || budget.remaining <= 0 || budget.nodes <= 0) { entries.push(["[truncated]", true]); break; }
|
|
85
|
+
const name = boundedText(key, budget);
|
|
86
|
+
entries.push([name, jsonDiagnostic(field(value, key), depth + 1, seen, budget)]);
|
|
87
|
+
}
|
|
88
|
+
result = Object.fromEntries(entries);
|
|
89
|
+
}
|
|
90
|
+
} catch { result = "[unreadable]"; }
|
|
91
|
+
seen.delete(value); return result;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function record(error, depth, seen, budget, pending) {
|
|
95
|
+
if (error && typeof error === "object") {
|
|
96
|
+
if (seen.has(error)) return { code: "diagnostic_cycle", message: "[circular]" };
|
|
97
|
+
seen.add(error);
|
|
98
|
+
}
|
|
99
|
+
const value = Object.fromEntries(["code", "message", "retryable", "exitCode", "details", "cause", "cleanup_error"].map(key => [key, field(error, key)]));
|
|
100
|
+
const result = {
|
|
101
|
+
code: boundedText(typeof value.code === "string" ? value.code : "operation_failed", budget),
|
|
102
|
+
...(typeof value.retryable === "boolean" ? { retryable: value.retryable } : {}),
|
|
103
|
+
...(typeof value.exitCode === "number" && Number.isFinite(value.exitCode) ? { exitCode: value.exitCode } : {}),
|
|
104
|
+
};
|
|
105
|
+
pending.push([result, typeof value.message === "string" ? value.message : text(error), value.details]);
|
|
106
|
+
if (value.cause && value.cause !== "[unreadable]" && depth < 3) result.cause = record(value.cause, depth + 1, new Set(seen), budget, pending);
|
|
107
|
+
if (value.cleanup_error && value.cleanup_error !== "[unreadable]" && depth < 3) result.cleanup_error = record(value.cleanup_error, depth + 1, new Set(seen), budget, pending);
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function errorRecord(error, depth = 0, seen = new Set()) {
|
|
112
|
+
const budget = { remaining: 65536, nodes: 2048 }, pending = [];
|
|
113
|
+
// Reserve every typed code before messages and optional bulk details.
|
|
114
|
+
const result = record(error, depth, seen, budget, pending);
|
|
115
|
+
const controls = pending.map(([, , value]) => operationalDetails(value, budget));
|
|
116
|
+
for (const [target, message] of pending) target.message = boundedText(message, budget);
|
|
117
|
+
for (const [index, [target, , value]] of pending.entries()) if (value !== undefined) {
|
|
118
|
+
const details = jsonDiagnostic(value, 0, new Set(), budget, controls[index] ? operationalDetailKeys : []);
|
|
119
|
+
target.details = controls[index] ? { ...controls[index], ...(details && typeof details === "object" ? details : { "[truncated]": true }) } : details;
|
|
120
|
+
}
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function reportedError(value, fallback) {
|
|
125
|
+
const record = errorRecord({ ...value, message: value.message ?? fallback });
|
|
126
|
+
return Object.assign(new Error(record.message), record);
|
|
127
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const OPERATION_PROGRESS_CONTRACT: "dd-flow/operation-progress@1";
|
|
2
|
+
export function operationDirectory(root: string, id: string): string;
|
|
3
|
+
export function withOperationProgress<T>(root: string, id: string, action: () => T): T;
|
|
4
|
+
export function readOperationRecord(root: string, id: string, name: string): Promise<Record<string, any> | null>;
|
|
5
|
+
export function readOperationProgress(root: string, id: string, expected?: Record<string, unknown>): Promise<(Record<string, unknown> & { sequence: number; observed_at: string }) | null>;
|
|
6
|
+
export function readOperationWaiting(root: string, id: string, expected?: Record<string, unknown>): Promise<boolean>;
|
|
7
|
+
export function suspendOperationClock(clock: import("./observation-clock.mjs").ObservationClock): void;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { open, readFile } from 'node:fs/promises';
|
|
4
|
+
import { constants } from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { writeJsonAtomic } from './runner-events.mjs';
|
|
7
|
+
|
|
8
|
+
export const OPERATION_PROGRESS_CONTRACT = 'dd-flow/operation-progress@1';
|
|
9
|
+
const context = new AsyncLocalStorage(), flights = new Map();
|
|
10
|
+
const fields = ['operation_id', 'daemon_id', 'operation', 'params_sha256', 'owner_id', 'generation'];
|
|
11
|
+
const phases = new Set(['accepted','ownership_confirmed','admission_confirmed','native_dispatched','native_progress','native_outcome_retained','settlement_started','settlement_completed','settlement_pending']);
|
|
12
|
+
export function operationDirectory(root,id) {
|
|
13
|
+
if (typeof root!=='string' || !path.isAbsolute(root) || typeof id!=='string' || !id || id.length>1_024) throw Object.assign(new Error('invalid operation observation binding'),{code:'operation_progress_invalid'});
|
|
14
|
+
return path.join(root, 'operations', createHash('sha256').update(id).digest('hex'));
|
|
15
|
+
}
|
|
16
|
+
export async function readOperationRecord(root, id, name) {
|
|
17
|
+
try {
|
|
18
|
+
const handle = await open(path.join(operationDirectory(root, id), name), constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
19
|
+
try {
|
|
20
|
+
const stat = await handle.stat();
|
|
21
|
+
if (!stat.isFile() || name === 'progress.json' && stat.size > 16_384) throw Object.assign(new Error('invalid operation record file'), {code:'operation_progress_invalid'});
|
|
22
|
+
const bytes = name === 'progress.json'
|
|
23
|
+
? await handle.read({buffer:Buffer.alloc(16_385),position:0}).then(({buffer,bytesRead})=>buffer.subarray(0,bytesRead))
|
|
24
|
+
: await handle.readFile();
|
|
25
|
+
if (name === 'progress.json' && bytes.length > 16_384) throw Object.assign(new Error('invalid operation record file'), {code:'operation_progress_invalid'});
|
|
26
|
+
try {
|
|
27
|
+
const value = JSON.parse(bytes.toString('utf8'));
|
|
28
|
+
if (name === 'progress.json' && (!value || typeof value !== 'object' || Array.isArray(value))) throw new Error('operation progress must be an object');
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
catch (cause) { if (name !== 'progress.json') throw cause; throw Object.assign(new Error('invalid operation progress JSON'), {code:'operation_progress_invalid',cause}); }
|
|
32
|
+
} finally { await handle.close(); }
|
|
33
|
+
} catch (error) { if (error.code === 'ENOENT') return null; throw error; }
|
|
34
|
+
}
|
|
35
|
+
export async function readOperationProgress(root, id, expected = {}) {
|
|
36
|
+
const requested = await readOperationRecord(root, id, 'requested.json');
|
|
37
|
+
const progress = await readOperationRecord(root, id, 'progress.json');
|
|
38
|
+
if (!progress) return null;
|
|
39
|
+
const invalid = () => Object.assign(new Error('operation progress binding is invalid'), { code: 'operation_progress_invalid', details: { operation_id: id } });
|
|
40
|
+
if (!requested || progress.schema_id !== OPERATION_PROGRESS_CONTRACT || progress.operation_id !== id
|
|
41
|
+
|| typeof progress.operation!=='string' || !progress.operation || progress.operation.length>128
|
|
42
|
+
|| !/^[a-f0-9]{64}$/.test(progress.params_sha256 ?? '')
|
|
43
|
+
|| ['daemon_id','owner_id'].some(field=>progress[field]!==null && (typeof progress[field]!=='string' || !progress[field] || progress[field].length>1_024))
|
|
44
|
+
|| progress.generation!==null && (!Number.isSafeInteger(progress.generation) || progress.generation<0)
|
|
45
|
+
|| !Number.isSafeInteger(progress.sequence) || progress.sequence < 1 || !phases.has(progress.phase)
|
|
46
|
+
|| !Number.isFinite(Date.parse(requested.requested_at))
|
|
47
|
+
|| !Number.isFinite(Date.parse(progress.observed_at)) || Date.parse(progress.observed_at) > Date.now()
|
|
48
|
+
|| Date.parse(progress.observed_at) < Date.parse(requested.requested_at)
|
|
49
|
+
|| fields.some(field => (progress[field] ?? null) !== (requested[field] ?? null))) throw invalid();
|
|
50
|
+
assertExpectedBinding(requested, expected, id);
|
|
51
|
+
return progress;
|
|
52
|
+
}
|
|
53
|
+
function assertExpectedBinding(requested, expected, id) {
|
|
54
|
+
if (fields.some(field => Object.hasOwn(expected, field) && (expected[field] ?? null) !== (requested[field] ?? null))) {
|
|
55
|
+
throw Object.assign(new Error('retained operation differs from dispatched request'), {code:'operation_conflict',details:{operation_id:id}});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function publishOperationProgress(root, id, phase, metadata = {}) {
|
|
59
|
+
if (!phases.has(phase)) return Promise.reject(Object.assign(new Error('unknown operation progress phase'), {code:'operation_progress_invalid'}));
|
|
60
|
+
const key = `${root}\0${id}`;
|
|
61
|
+
const next = (flights.get(key) ?? Promise.resolve()).catch(() => {}).then(async () => {
|
|
62
|
+
const requested = await readOperationRecord(root, id, 'requested.json');
|
|
63
|
+
if (!requested) return null;
|
|
64
|
+
const prior = await readOperationProgress(root, id);
|
|
65
|
+
const observedAt = metadata.observed_at ?? new Date().toISOString();
|
|
66
|
+
if (prior && Date.parse(observedAt)<Date.parse(prior.observed_at)) return prior;
|
|
67
|
+
// A later successful ownership gate is fresh liveness evidence while admission waits.
|
|
68
|
+
if (prior && phase === prior.phase && phase !== 'native_progress'
|
|
69
|
+
&& (phase !== 'ownership_confirmed' || Date.parse(observedAt) <= Date.parse(prior.observed_at))) return prior;
|
|
70
|
+
if (prior && phase === 'native_progress' && Date.parse(observedAt) - Date.parse(prior.observed_at) < 1_000) return prior;
|
|
71
|
+
const value = { schema_id: OPERATION_PROGRESS_CONTRACT,
|
|
72
|
+
...Object.fromEntries(fields.map(field => [field, requested[field] ?? null])),
|
|
73
|
+
sequence: (prior?.sequence ?? 0) + 1, phase, observed_at: observedAt,
|
|
74
|
+
...Object.fromEntries(['native_request_id', 'session_id'].filter(field => typeof metadata[field] === 'string').map(field => [field, metadata[field].slice(0, 256)])) };
|
|
75
|
+
if (!Number.isFinite(Date.parse(observedAt)) || Date.parse(observedAt) > Date.now() || Date.parse(observedAt) < Date.parse(requested.requested_at)) throw Object.assign(new Error('invalid progress event timestamp'), { code: 'operation_progress_invalid' });
|
|
76
|
+
await writeJsonAtomic(path.join(operationDirectory(root, id), 'progress.json'), value);
|
|
77
|
+
return value;
|
|
78
|
+
});
|
|
79
|
+
flights.set(key, next);
|
|
80
|
+
void next.finally(() => { if (flights.get(key) === next) flights.delete(key); }).catch(() => {});
|
|
81
|
+
return next;
|
|
82
|
+
}
|
|
83
|
+
export const withOperationProgress = (root, id, action) => context.run({ root, id }, action);
|
|
84
|
+
export function operationProgressReporter() {
|
|
85
|
+
const binding = context.getStore();
|
|
86
|
+
return (phase, metadata) => binding ? publishOperationProgress(binding.root, binding.id, phase, metadata) : Promise.resolve(null);
|
|
87
|
+
}
|
|
88
|
+
export const noteOperationProgress = (phase, metadata) => operationProgressReporter()(phase, metadata);
|
|
89
|
+
|
|
90
|
+
/** Confirmed admission waiting is ownership evidence, not native activity. */
|
|
91
|
+
export async function publishOperationWaiting(root,id,receipt) {
|
|
92
|
+
const requested = await readOperationRecord(root,id,'requested.json');
|
|
93
|
+
if (!requested) return;
|
|
94
|
+
const prior = await readOperationRecord(root,id,'admission-wait.json');
|
|
95
|
+
if (prior?.state === (receipt.admitted ? 'admitted' : 'provider_capacity') && Date.now()-Date.parse(prior.observed_at)<1_000) return;
|
|
96
|
+
const value = { schema_id:'dd-flow/operation-waiting@1', ...Object.fromEntries(fields.map(field=>[field,requested[field] ?? null])), observed_at:new Date().toISOString(), lease_expires_at:receipt.lease_expires_at, process_id:receipt.process_id, state:receipt.admitted ? 'admitted' : 'provider_capacity' };
|
|
97
|
+
await writeJsonAtomic(path.join(operationDirectory(root,id),'admission-wait.json'),value);
|
|
98
|
+
}
|
|
99
|
+
export async function readOperationWaiting(root,id,expected={}) {
|
|
100
|
+
const requested = await readOperationRecord(root,id,'requested.json'), waiting = await readOperationRecord(root,id,'admission-wait.json');
|
|
101
|
+
if (!waiting || waiting.state!=='provider_capacity') return false;
|
|
102
|
+
if (!requested || waiting.schema_id!=='dd-flow/operation-waiting@1' || fields.some(field=>(waiting[field] ?? null)!==(requested[field] ?? null))) throw Object.assign(new Error('admission waiting binding differs'),{code:'operation_progress_invalid'});
|
|
103
|
+
assertExpectedBinding(requested, expected, id);
|
|
104
|
+
if (!Number.isFinite(Date.parse(waiting.observed_at)) || Date.parse(waiting.observed_at)>Date.now() || Date.parse(waiting.observed_at)<Date.parse(requested.requested_at) || typeof waiting.process_id!=='string' || !waiting.process_id) throw Object.assign(new Error('invalid admission observation'),{code:'operation_progress_invalid'});
|
|
105
|
+
if (Date.now()-Date.parse(waiting.observed_at)>30_000) return false;
|
|
106
|
+
if (!Number.isFinite(Date.parse(waiting.lease_expires_at)) || Date.parse(waiting.lease_expires_at)<=Date.now()) return false;
|
|
107
|
+
const daemon = JSON.parse(await readFile(path.join(root,'daemon.json'),'utf8'));
|
|
108
|
+
if ((daemon.daemon_id ?? daemon.config?.daemonId)!==waiting.daemon_id || daemon.config?.runtime_owner?.owner_id!==waiting.owner_id || ['stopping','clean','unclean','cleanup_failed','failed'].includes(daemon.shutdown_state)) return false;
|
|
109
|
+
if (waiting.process_id !== daemon.resource_process?.id) return false;
|
|
110
|
+
try { const fence = JSON.parse(await readFile(path.join(root,'operation-fence.json'),'utf8')); if (fence.daemon_id===waiting.daemon_id && fence.generation>waiting.generation) return false; } catch(error) { if(error.code!=='ENOENT') throw error; }
|
|
111
|
+
try { process.kill(daemon.pid,0); } catch { return false; }
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
export function suspendOperationClock(clock) { clock.lastWall = clock.wall(); clock.lastMono = clock.monotonic(); }
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
type Options = { cwd?: string; env?: NodeJS.ProcessEnv; input?: string | null; onProgress?: (value: unknown) => void; progress?: () => number; startupMs?: number | null; timeoutMs?: number | null; phase?: string; signal?: AbortSignal; onDiagnostic?: (data: string, stream: string) => void };
|
|
2
|
+
export function reportedFailure(stdout: string, stderr: string, fallback: string, fallbackCode?: string, retainedError?: Record<string, unknown> | null): Error & import('./operation-errors.mjs').ErrorRecord;
|
|
3
|
+
export function commandJson(bin: string, args: string[], options?: Options): Promise<Record<string, unknown>>;
|
|
4
|
+
export function commandText(bin: string, args: string[], options?: Options): Promise<string>;
|
|
5
|
+
export function commandResult(bin: string, args: string[], options?: Options): Promise<{exit_code: number | null; signal: string | null; stdout: string; stderr: string}>;
|