@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
|
@@ -0,0 +1,845 @@
|
|
|
1
|
+
import { managedFlowEnv } from './managed-flow-env.mjs';
|
|
2
|
+
import { SETUP_STARTUP_MS, startupObserver, observationDetails } from './setup-progress.mjs';
|
|
3
|
+
import { callDaemonObserved } from './daemon-observation.mjs';
|
|
4
|
+
import { admitNativeRequestBuffer } from "./native-streams.mjs";
|
|
5
|
+
import { ObservationClock } from "./observation-clock.mjs";
|
|
6
|
+
import { prepareDaemonRequest } from "./native-request-proof.mjs";
|
|
7
|
+
import { spawn } from "node:child_process";
|
|
8
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
9
|
+
import { chmod, lstat, mkdir, open, readFile, rename, unlink, writeFile } from "node:fs/promises";
|
|
10
|
+
import net from "node:net";
|
|
11
|
+
import { durableDaemonDispatch, completeDaemonShutdown, commitDaemonShutdown, assertDaemonDispatch, assertDaemonRestartable, trackDaemonConnections, retireDaemonEndpoint, retireFailedDaemon } from "./daemon-operations.mjs";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
import { assertStartupChild, prepareRuntimeOwner, cleanupFailedDaemonStart, confirmDaemonProcess, confirmDaemonStopped, finishDaemonProcess, scheduleDaemonProcessHeartbeat, registerDaemonProcess, startManagedBridge, stopProcessGroup } from "./managed-daemon.mjs";
|
|
14
|
+
import { acceptNativeHookRequest, readNativeHookFailures, writeNativeHookFailure } from "./native-hook-command.mjs";
|
|
15
|
+
import { parseLifecycleCommand, continuationCommand, commandOption } from "./lifecycle-command.mjs";
|
|
16
|
+
import { errorRecord, isNativeHookObservationLoss, isProvenShellCorrection, reportedError } from "./operation-errors.mjs";
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
AcpBridge,
|
|
20
|
+
cancelSessionWithBridge,
|
|
21
|
+
cancelChildWithBridge,
|
|
22
|
+
createSessionWithBridge,
|
|
23
|
+
doctor,
|
|
24
|
+
forkSessionWithBridge,
|
|
25
|
+
inspectSessionWithBridge,
|
|
26
|
+
promptSessionWithBridge,
|
|
27
|
+
zcodeChildSessionIds,
|
|
28
|
+
observeClosedZcodeTree,
|
|
29
|
+
readZcodeSubagents,
|
|
30
|
+
} from "./dd-zcode.mjs";
|
|
31
|
+
|
|
32
|
+
const REQUEST_SCHEMA = "dd-zcode/daemon-request@1";
|
|
33
|
+
import { settledNativeTree, nativeExecutionReceipt } from "./native-children.mjs";
|
|
34
|
+
const RESPONSE_SCHEMA = "dd-zcode/daemon-response@1";
|
|
35
|
+
const STATE_SCHEMA = "dd-zcode/daemon-state@1";
|
|
36
|
+
// ACP silence starts diagnostics; it is not a provider-failure deadline.
|
|
37
|
+
export const DEFAULT_LIVENESS_TIMEOUT_MS = 600_000;
|
|
38
|
+
export const DEFAULT_REQUEST_TIMEOUT_MS = null;
|
|
39
|
+
|
|
40
|
+
function positiveDuration(value, name, fallback) {
|
|
41
|
+
const duration = value ?? fallback;
|
|
42
|
+
if (!Number.isFinite(duration) || duration <= 0) throw new DaemonError("invalid_duration", `${name} must be a positive finite duration`, false, { name, value: duration });
|
|
43
|
+
return duration;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function optionalDuration(value, name, fallback) {
|
|
47
|
+
if (value === null) return null;
|
|
48
|
+
if (value === undefined && fallback === null) return null;
|
|
49
|
+
return positiveDuration(value, name, fallback);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
class DaemonError extends Error {
|
|
53
|
+
constructor(code, message, retryable = false, details) {
|
|
54
|
+
super(message); this.code = code; this.retryable = retryable; this.details = details;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function absolute(value, label) {
|
|
59
|
+
if (!value || !path.isAbsolute(value)) throw new DaemonError("invalid_path", `${label} must be an absolute path`);
|
|
60
|
+
return path.resolve(value);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function locations(stateDir) {
|
|
64
|
+
const dir = absolute(stateDir, "--state-dir");
|
|
65
|
+
const localSocket = path.join(dir, "daemon.sock");
|
|
66
|
+
const socket = Buffer.byteLength(localSocket) < 100
|
|
67
|
+
? localSocket
|
|
68
|
+
: `/tmp/dd-zcode-${createHash("sha256").update(dir).digest("hex").slice(0, 24)}.sock`;
|
|
69
|
+
return { dir, socket, state: path.join(dir, "daemon.json"), log: path.join(dir, "daemon.log") };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function readState(stateDir) {
|
|
73
|
+
const file = locations(stateDir).state;
|
|
74
|
+
try { return JSON.parse(await readFile(file, "utf8")); }
|
|
75
|
+
catch (error) { if (error.code === "ENOENT") return null; throw error; }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function writeState(file, value) {
|
|
79
|
+
const temporary = `${file}.${process.pid}.tmp`;
|
|
80
|
+
await writeFile(temporary, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
|
|
81
|
+
await rename(temporary, file);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function removeSocket(socket, dir) {
|
|
85
|
+
if (socket !== locations(dir).socket) throw new DaemonError("unsafe_socket", "daemon socket does not match the exact state directory");
|
|
86
|
+
try {
|
|
87
|
+
const info = await lstat(socket);
|
|
88
|
+
if (!info.isSocket()) throw new DaemonError("unsafe_socket", "refusing to remove a non-socket daemon path");
|
|
89
|
+
await unlink(socket);
|
|
90
|
+
} catch (error) { if (error.code !== "ENOENT") throw error; }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const errorPayload = errorRecord;
|
|
94
|
+
|
|
95
|
+
export async function callDaemon(stateDir, operation, params = {}, timeoutMs = 30_000) {
|
|
96
|
+
const request = prepareDaemonRequest({ schema_id: REQUEST_SCHEMA, id: process.env.DD_EVAL_OPERATION_ID ?? randomUUID(), operation, params });
|
|
97
|
+
return await callDaemonObserved({ stateDir, socket: locations(stateDir).socket, request, responseSchema: RESPONSE_SCHEMA, harness: 'zcode', timeoutMs });
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const ZCODE_ROUTING_ENV = ["ZCODE_HOME", "ZCODE_STORAGE_DIR", "ZCODE_SESSION_DB_PATH"];
|
|
101
|
+
|
|
102
|
+
function nativeRoutingEnv() {
|
|
103
|
+
return Object.fromEntries(ZCODE_ROUTING_ENV.filter(key => process.env[key] !== undefined)
|
|
104
|
+
.map(key => [key, absolute(process.env[key], key)]));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function sameNativeRouting(previous, config) {
|
|
108
|
+
return ZCODE_ROUTING_ENV.every(key => (previous?.env?.[key] ?? null) === (config.env?.[key] ?? null));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function sameConfig(state, config) {
|
|
112
|
+
return sameNativeRouting(state.config, config) && ["cwd", "journal", "bin", "zcodePath", "ddFlowBin", "ddFlowHome", "projectRoot", "resourceHome", "livenessTimeoutMs", "requestTimeoutMs"].every((key) => (state.config?.[key] ?? null) === (config[key] ?? null));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export async function startDaemon(options) {
|
|
116
|
+
if (options.noFlow) options = { ...options, ddFlowHome: undefined, ddFlowBin: undefined, projectRoot: undefined };
|
|
117
|
+
const paths = locations(options.stateDir);
|
|
118
|
+
const hasFlowContext = Boolean(options.ddFlowHome || options.projectRoot || options.ddFlowBin);
|
|
119
|
+
if (hasFlowContext && (!options.ddFlowHome || !options.projectRoot || !options.ddFlowBin)) {
|
|
120
|
+
throw new DaemonError("flow_context_incomplete", "--dd-flow-home, --dd-flow-bin and --project-root must be provided together for ZCode lifecycle forwarding");
|
|
121
|
+
}
|
|
122
|
+
const config = {
|
|
123
|
+
cwd: absolute(options.cwd, "--cwd"),
|
|
124
|
+
journal: absolute(options.journal, "--journal"),
|
|
125
|
+
bin: options.bin ?? null,
|
|
126
|
+
zcodePath: options.zcodePath ?? null,
|
|
127
|
+
ddFlowBin: hasFlowContext ? absolute(options.ddFlowBin, "--dd-flow-bin") : null,
|
|
128
|
+
ddFlowHome: options.ddFlowHome ? absolute(options.ddFlowHome, "--dd-flow-home") : null,
|
|
129
|
+
projectRoot: options.projectRoot ? absolute(options.projectRoot, "--project-root") : null,
|
|
130
|
+
resourceHome: !options.noFlow && process.env.DD_FLOW_RESOURCE_HOME ? absolute(process.env.DD_FLOW_RESOURCE_HOME, "DD_FLOW_RESOURCE_HOME") : null,
|
|
131
|
+
env: { ...nativeRoutingEnv(), ...(options.ddFlowHome ? { DD_FLOW_HOME: absolute(options.ddFlowHome, "--dd-flow-home"), ...(options.ddFlowBin ? { DD_FLOW_BIN: absolute(options.ddFlowBin, "--dd-flow-bin") } : {}) } : {}) },
|
|
132
|
+
livenessTimeoutMs: positiveDuration(options.livenessTimeoutMs, "livenessTimeoutMs", DEFAULT_LIVENESS_TIMEOUT_MS),
|
|
133
|
+
requestTimeoutMs: optionalDuration(options.requestTimeoutMs, "requestTimeoutMs", DEFAULT_REQUEST_TIMEOUT_MS),
|
|
134
|
+
};
|
|
135
|
+
await prepareRuntimeOwner(config, paths.dir);
|
|
136
|
+
await mkdir(paths.dir, { recursive: true, mode: 0o700 });
|
|
137
|
+
await chmod(paths.dir, 0o700);
|
|
138
|
+
let previous = await readState(paths.dir); assertDaemonRestartable(previous);
|
|
139
|
+
try {
|
|
140
|
+
const status = await callDaemon(paths.dir, "daemon.status", {}, 1000);
|
|
141
|
+
if (!sameConfig(status, config)) throw new DaemonError("daemon_config_mismatch", "a daemon is already running with different configuration");
|
|
142
|
+
return { ...status, already_running: true };
|
|
143
|
+
} catch (error) { if (error.code !== "daemon_not_running" && error.code !== "daemon_timeout") throw error; }
|
|
144
|
+
|
|
145
|
+
previous = await readState(paths.dir); assertDaemonRestartable(previous);
|
|
146
|
+
if (previous?.shutdown_state === "clean") {
|
|
147
|
+
if (!sameNativeRouting(previous.config, config)) throw new DaemonError("daemon_config_mismatch", "retained ZCode native storage routing changed");
|
|
148
|
+
await authorizeRetainedDaemonResume(paths.dir, previous, options.sessionId, config);
|
|
149
|
+
}
|
|
150
|
+
if (previous?.shutdown_state === "running" && previous.active_tree) {
|
|
151
|
+
throw new DaemonError("invalid_harness_crash", "the previous daemon died with an active or unproven Session tree", false, { daemon_id: previous.daemon_id });
|
|
152
|
+
}
|
|
153
|
+
await removeSocket(paths.socket, paths.dir);
|
|
154
|
+
const versions = await doctor({ bin: config.bin, zcodePath: config.zcodePath });
|
|
155
|
+
if (hasFlowContext && versions.compatible !== true) {
|
|
156
|
+
throw new DaemonError("zcode_lifecycle_unqualified", "ZCode native event identity is not qualified for managed CLI admission; inspect lifecycle_qualification before starting E2E.", false, { startup_resources: "not_created", observed_runtime: versions.observed_runtime, lifecycle_qualification: versions.lifecycle_qualification });
|
|
157
|
+
}
|
|
158
|
+
const resourceProcess = await registerDaemonProcess(config, {
|
|
159
|
+
kind: "zcode-daemon", owner: `zcode:${path.basename(paths.dir)}`, operation: `zcode-daemon:${path.basename(paths.dir)}`,
|
|
160
|
+
stdout: paths.log, stderr: paths.log,
|
|
161
|
+
});
|
|
162
|
+
const state = {
|
|
163
|
+
schema_id: STATE_SCHEMA,
|
|
164
|
+
daemon_id: randomUUID(),
|
|
165
|
+
pid: null,
|
|
166
|
+
socket: paths.socket,
|
|
167
|
+
cwd: config.cwd,
|
|
168
|
+
journal: config.journal,
|
|
169
|
+
started_at: new Date().toISOString(),
|
|
170
|
+
shutdown_state: "starting",
|
|
171
|
+
active_tree: false,
|
|
172
|
+
recovery_status: previous?.shutdown_state === "running" ? "recovered_idle" : "clean_start",
|
|
173
|
+
versions: versions.versions,
|
|
174
|
+
config,
|
|
175
|
+
sessions: previous?.sessions ?? [],
|
|
176
|
+
resource_process: resourceProcess,
|
|
177
|
+
};
|
|
178
|
+
await writeState(paths.state, state);
|
|
179
|
+
const log = await open(paths.log, "a", 0o600);
|
|
180
|
+
const child = spawn(process.execPath, [absolute(options.entryPath, "dd-zcode entry"), "daemon", "serve", "--state-dir", paths.dir], {
|
|
181
|
+
cwd: config.cwd, detached: true, stdio: ["ignore", log.fd, log.fd],
|
|
182
|
+
});
|
|
183
|
+
child.unref();
|
|
184
|
+
await log.close();
|
|
185
|
+
try {
|
|
186
|
+
await confirmDaemonProcess(config, resourceProcess, child);
|
|
187
|
+
const startupClock = new ObservationClock({ timeoutMs: SETUP_STARTUP_MS }); const startupProgress = startupObserver(paths.dir, state.daemon_id);
|
|
188
|
+
while (!startupClock.sample(await startupProgress())) {
|
|
189
|
+
assertStartupChild(child, state, await readState(paths.dir), paths.log);
|
|
190
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
191
|
+
try { return await callDaemon(paths.dir, "daemon.status", {}, 1000); }
|
|
192
|
+
catch (error) { if (error.code !== "daemon_not_running" && error.code !== "daemon_timeout") throw error; }
|
|
193
|
+
}
|
|
194
|
+
throw Object.assign(new DaemonError("daemon_start_failed", "daemon did not become ready", true), { details: observationDetails(startupClock, 'daemon-start', child.pid) });
|
|
195
|
+
} catch (error) {
|
|
196
|
+
await cleanupFailedDaemonStart(config, resourceProcess, child, error, () => readState(paths.dir), state.daemon_id);
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export async function stopDaemon(options) {
|
|
202
|
+
const timeoutMs = options.timeoutMs ?? 30_000;
|
|
203
|
+
const { socket } = locations(options.stateDir);
|
|
204
|
+
return await confirmDaemonStopped(() => callDaemon(options.stateDir, "daemon.stop", { cancelTree: options.cancelTree === true }, timeoutMs), socket, timeoutMs, { readState: () => readState(options.stateDir) });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export class DaemonRuntime {
|
|
208
|
+
constructor(paths, state, bridge, initialized) {
|
|
209
|
+
this.paths = paths; this.state = state; this.bridge = bridge; this.initialized = initialized;
|
|
210
|
+
this.state.capabilities = { ...(this.state.capabilities ?? {}), retained_child_observation: true };
|
|
211
|
+
this.sessions = new Map((state.sessions ?? []).map((item) => [item.provider_session_id, item]));
|
|
212
|
+
this.liveAdapters = new Set();
|
|
213
|
+
this.activeProductive = null; this.server = null; this.persisting = Promise.resolve();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
persist(patch = {}) {
|
|
217
|
+
Object.assign(this.state, patch, { pid: process.pid, updated_at: new Date().toISOString(), sessions: [...this.sessions.values()] });
|
|
218
|
+
const snapshot = structuredClone(this.state);
|
|
219
|
+
this.persisting = this.persisting.catch(() => {}).then(() => writeState(this.paths.state, snapshot));
|
|
220
|
+
return this.persisting;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
options(params) {
|
|
224
|
+
const known = this.sessions.get(params.sessionId);
|
|
225
|
+
const adapter = params.adapterSessionId ?? known?.adapter_session_id ?? params.sessionId;
|
|
226
|
+
const generation = this.activeGeneration ?? this.controlGeneration ?? 0;
|
|
227
|
+
return {
|
|
228
|
+
...this.state.config, ...params, operationId: this.state.active_request?.request_id, nativeHookOwner: this.nativeHookOperation ?? null, knownChildSessionIds: known?.known_child_session_ids ?? [], adapterSessionId: adapter,
|
|
229
|
+
neverPromptedRootProof: this.neverPromptedRootProof(params.sessionId),
|
|
230
|
+
daemonId: this.state.daemon_id, allowBackground: true, liveSession: this.liveAdapters.has(adapter),
|
|
231
|
+
assertDispatch: () => { if (generation !== (this.controlGeneration ?? 0)) throw new DaemonError("operation_cancelled", "ZCode prompt was cancelled before native dispatch"); },
|
|
232
|
+
onActivity: async ({ sessionId, requestId, progress }) => {
|
|
233
|
+
const request = this.state.active_request;
|
|
234
|
+
if (!request || request.adapter_session_id !== sessionId || this.activeGeneration !== request.generation) return;
|
|
235
|
+
await this.persist({ active_request: { ...request, observation: { state: "productive", request_id: requestId, progress } } });
|
|
236
|
+
},
|
|
237
|
+
// The bridge owns the quiet timer; the daemon owns the durable status.
|
|
238
|
+
// Do not infer provider state or cancel here.
|
|
239
|
+
onInactivity: async ({ requestId, sessionId, lastActivityAt, idleTimeoutMs, cursor }) => {
|
|
240
|
+
const request = this.state.active_request;
|
|
241
|
+
// `sessionId` is ACP identity; the durable request's `session_id` is
|
|
242
|
+
// native/provider identity. These strings can coincide but are not
|
|
243
|
+
// interchangeable.
|
|
244
|
+
if (!request || request.adapter_session_id !== sessionId) return;
|
|
245
|
+
const adapterSessionId = request.adapter_session_id;
|
|
246
|
+
let diagnostics;
|
|
247
|
+
try {
|
|
248
|
+
// Both are non-mutating native observations. In particular, do not
|
|
249
|
+
// inspect/resume a running child: some ZCode children are known to
|
|
250
|
+
// be non-resident and unsubscribable while their request is alive.
|
|
251
|
+
const [resident, topology] = await Promise.all([
|
|
252
|
+
this.bridge.request("zcode/session/resident", { sessionId: adapterSessionId }, 5_000),
|
|
253
|
+
// childSessionIds is complete on every page. This diagnostic reads
|
|
254
|
+
// identities only, not ended outcomes or a settlement verdict.
|
|
255
|
+
this.bridge.request("zcode/session/retainedSubagents", { sessionId: adapterSessionId }, 5_000),
|
|
256
|
+
]);
|
|
257
|
+
diagnostics = {
|
|
258
|
+
observed_at: new Date().toISOString(), adapter_session_id: adapterSessionId,
|
|
259
|
+
resident: resident?.resident === true,
|
|
260
|
+
child_session_ids: zcodeChildSessionIds(topology?.topology ?? topology),
|
|
261
|
+
};
|
|
262
|
+
} catch (error) {
|
|
263
|
+
diagnostics = { observed_at: new Date().toISOString(), adapter_session_id: adapterSessionId, observation_error: errorPayload(error) };
|
|
264
|
+
}
|
|
265
|
+
// The probe is asynchronous. Do not recreate a completed or
|
|
266
|
+
// reconnected request with a stale callback snapshot.
|
|
267
|
+
const current = this.state.active_request;
|
|
268
|
+
if (!current || current.request_id !== request.request_id || current.adapter_session_id !== adapterSessionId || this.activeGeneration !== request.generation
|
|
269
|
+
|| cursor !== undefined && this.bridge.pending?.get(requestId)?.progress?.cursor !== cursor && cursor !== 0) return;
|
|
270
|
+
await this.persist({ active_request: {
|
|
271
|
+
...current,
|
|
272
|
+
observation: { state: "quiet", request_id: requestId, detected_at: new Date().toISOString(), last_activity_at: new Date(lastActivityAt).toISOString(), quiet_threshold_ms: idleTimeoutMs, diagnostics },
|
|
273
|
+
} });
|
|
274
|
+
},
|
|
275
|
+
// Native create publishes its identities before post-create work. Keep
|
|
276
|
+
// that allocation in the same durable request record so recovery can
|
|
277
|
+
// distinguish "no Session allocated" from "allocated, later step lost".
|
|
278
|
+
onSessionAliasAllocated: async (identity) => {
|
|
279
|
+
const current = this.state.active_request;
|
|
280
|
+
await this.persist({
|
|
281
|
+
active_request: {
|
|
282
|
+
...(current ?? {
|
|
283
|
+
request_id: randomUUID(),
|
|
284
|
+
session_id: params.sessionId ?? null,
|
|
285
|
+
adapter_session_id: identity.adapter_session_id,
|
|
286
|
+
generation: this.controlGeneration ?? 0,
|
|
287
|
+
dispatched_at: new Date().toISOString(),
|
|
288
|
+
}),
|
|
289
|
+
allocation: {
|
|
290
|
+
adapter_session_id: identity.adapter_session_id,
|
|
291
|
+
provider_session_id: null,
|
|
292
|
+
cwd: identity.cwd,
|
|
293
|
+
allocated_at: new Date().toISOString(),
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
},
|
|
298
|
+
onSessionAllocated: async (identity) => {
|
|
299
|
+
const current = this.state.active_request;
|
|
300
|
+
// A late identity is evidence, not permission to resurrect a completed
|
|
301
|
+
// request or overwrite another operation's ownership.
|
|
302
|
+
this.track(identity);
|
|
303
|
+
const allocated = this.sessions.get(identity.provider_session_id);
|
|
304
|
+
if (allocated && this.activeProductive === "session.create" && current?.allocation?.adapter_session_id === identity.adapter_session_id
|
|
305
|
+
&& allocated.prompt_dispatched === undefined) {
|
|
306
|
+
allocated.prompt_dispatched = false;
|
|
307
|
+
allocated.created_by_daemon_id = this.state.daemon_id;
|
|
308
|
+
}
|
|
309
|
+
if (!current || current.allocation?.adapter_session_id !== identity.adapter_session_id) {
|
|
310
|
+
await this.persist({});
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
await this.persist({
|
|
314
|
+
active_request: {
|
|
315
|
+
...(current ?? {
|
|
316
|
+
request_id: randomUUID(),
|
|
317
|
+
session_id: params.sessionId ?? null,
|
|
318
|
+
adapter_session_id: params.adapterSessionId ?? params.sessionId ?? null,
|
|
319
|
+
generation: this.controlGeneration ?? 0,
|
|
320
|
+
dispatched_at: new Date().toISOString(),
|
|
321
|
+
}),
|
|
322
|
+
allocation: {
|
|
323
|
+
adapter_session_id: identity.adapter_session_id,
|
|
324
|
+
provider_session_id: identity.provider_session_id,
|
|
325
|
+
cwd: identity.cwd,
|
|
326
|
+
allocated_at: new Date().toISOString(),
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
});
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
track(result, topology = result?.evidence?.subagents) {
|
|
335
|
+
if (result?.provider_session_id) { const existing = this.sessions.get(result.provider_session_id); const suppliedParent = result.parent_provider_session_id;
|
|
336
|
+
if (existing && suppliedParent !== undefined && suppliedParent !== existing.parent_provider_session_id) {
|
|
337
|
+
throw new DaemonError("session_ancestry_conflict", "ZCode Session parent changed after it was recorded", false, { session_id: result.provider_session_id, expected_parent: existing.parent_provider_session_id, actual_parent: suppliedParent });
|
|
338
|
+
}
|
|
339
|
+
const parent = suppliedParent ?? existing?.parent_provider_session_id ?? null;
|
|
340
|
+
const parentRoot = parent ? this.sessions.get(parent)?.root_provider_session_id ?? null : null;
|
|
341
|
+
this.sessions.set(result.provider_session_id, {
|
|
342
|
+
provider_session_id: result.provider_session_id,
|
|
343
|
+
adapter_session_id: result.adapter_session_id ?? existing?.adapter_session_id ?? result.provider_session_id,
|
|
344
|
+
parent_provider_session_id: parent,
|
|
345
|
+
root_provider_session_id: existing?.root_provider_session_id ?? (parent ? parentRoot : result.provider_session_id),
|
|
346
|
+
topology: topology ?? existing?.topology ?? null,
|
|
347
|
+
known_child_session_ids: [...new Set([...(existing?.known_child_session_ids ?? zcodeChildSessionIds(existing?.topology)), ...zcodeChildSessionIds(topology)])],
|
|
348
|
+
completed_turns: existing?.completed_turns ?? 0,
|
|
349
|
+
prompt_dispatched: existing?.prompt_dispatched,
|
|
350
|
+
created_by_daemon_id: existing?.created_by_daemon_id,
|
|
351
|
+
close_receipt: result.close?.closed === true ? result : existing?.close_receipt ?? null,
|
|
352
|
+
}); }
|
|
353
|
+
if (result?.adapter_session_id) this.liveAdapters.add(result.adapter_session_id);
|
|
354
|
+
for (const child of topology?.running ?? []) {
|
|
355
|
+
const adapter = child.childSessionId ?? child.adapterSessionId ?? child.sessionId;
|
|
356
|
+
if (typeof adapter === "string" && adapter) this.liveAdapters.add(adapter);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
treeRunning(result) {
|
|
361
|
+
const closed = this.sessions.get(result?.provider_session_id)?.close_receipt;
|
|
362
|
+
if (closed) return closed.settled !== true;
|
|
363
|
+
const evidence = result?.evidence ?? result;
|
|
364
|
+
const children = evidence?.subagents?.running;
|
|
365
|
+
return evidence?.read?.projection?.status !== "idle" || !Array.isArray(children) || children.length > 0 || Boolean(evidence?.subagents?.observation_error);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
async productive(name, task, request = null) {
|
|
369
|
+
if (this.activeProductive) throw new DaemonError("operation_busy", `${this.activeProductive} is already running`, true);
|
|
370
|
+
this.activeProductive = name;
|
|
371
|
+
this.activeGeneration = this.controlGeneration ?? 0;
|
|
372
|
+
const current = this.nativeHookOperation = { operationId: request?.operation_id, rootProviderSessionId: request?.session_id, generation: this.activeGeneration, requests: new Map() };
|
|
373
|
+
current.valid = () => current.generation === (this.controlGeneration ?? 0);
|
|
374
|
+
let rejectHook;
|
|
375
|
+
const hookFailed = new Promise((_, reject) => { rejectHook = reject; });
|
|
376
|
+
void hookFailed.catch(() => {}); // A hook can fail while the dispatch receipt is still persisting.
|
|
377
|
+
current.reject = error => { if (this.nativeHookOperation !== current || current.error) return; current.error = error; this.bridge.lifecycleFailure ??= error; this.bridge.notificationError = this.bridge.lifecycleFailure; this.bridge.rejectAll(this.bridge.lifecycleFailure); rejectHook(this.bridge.lifecycleFailure); };
|
|
378
|
+
const timer = setInterval(() => { void this.observeHookFailures(current).catch(error => current.reject(error)); }, 100);
|
|
379
|
+
try {
|
|
380
|
+
if (name === "session.prompt") this.state.last_native_failure = null;
|
|
381
|
+
await this.persist({ active_tree: true, active_operation: name, ...(request ? { active_request: request } : {}) });
|
|
382
|
+
const result = await Promise.race([task(), hookFailed]);
|
|
383
|
+
// The native tool cannot finish before its hook returns. Drain any
|
|
384
|
+
// already-written failure before discarding this operation's requests.
|
|
385
|
+
await this.observeHookFailures(current);
|
|
386
|
+
if (current.error) throw current.error;
|
|
387
|
+
const hookDiagnostics = (this.bridge.lifecycleDiagnostics ?? []).filter(item => item.operation_id === current.operationId && item.generation === current.generation);
|
|
388
|
+
if (hookDiagnostics.length) result.native_hook_diagnostics = [...new Map([...(result.native_hook_diagnostics ?? []), ...hookDiagnostics].map(item => [JSON.stringify(item), item])).values()];
|
|
389
|
+
this.track(result);
|
|
390
|
+
const running = this.treeRunning(result);
|
|
391
|
+
await this.persist({ active_tree: running, active_operation: null, active_request: null });
|
|
392
|
+
return { ...result, settled: !running };
|
|
393
|
+
} catch (error) {
|
|
394
|
+
// Keep an allocated identity visible until the controller seals the
|
|
395
|
+
// outcome. Clearing it here was the source of `provider_identity_unknown`
|
|
396
|
+
// after a late/partial create: the native Session existed, but the
|
|
397
|
+
// daemon state no longer carried the only correlation to it.
|
|
398
|
+
const active = this.state?.active_request;
|
|
399
|
+
const response = error?.details?.terminal_response;
|
|
400
|
+
if (name === "session.prompt" && response?.method === "session/prompt" && error.details.provider_session_id)
|
|
401
|
+
this.state.last_native_failure = { provider_session_id: error.details.provider_session_id, request_id: response.request_id };
|
|
402
|
+
try {
|
|
403
|
+
await this.persist({
|
|
404
|
+
active_operation: null,
|
|
405
|
+
active_request: active?.allocation
|
|
406
|
+
? { ...active, outcome: "unknown", error: errorPayload(error) }
|
|
407
|
+
: null,
|
|
408
|
+
});
|
|
409
|
+
} catch (cleanupError) {
|
|
410
|
+
// Preserve the operation's primary failure if durable cleanup itself
|
|
411
|
+
// is unavailable. The caller will retain the outcome as unknown and
|
|
412
|
+
// the next recovery pass can retry the state write.
|
|
413
|
+
if (error && typeof error === "object") error.cleanup_error = errorPayload(cleanupError);
|
|
414
|
+
}
|
|
415
|
+
throw error;
|
|
416
|
+
} finally { clearInterval(timer); if (!current.retainNativeHooks) current.requests.clear(); if (this.nativeHookOperation === current) this.nativeHookOperation = null; this.activeProductive = null; this.activeGeneration = null; }
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
async observeHookFailures(current = this.nativeHookOperation) {
|
|
420
|
+
if (!current || this.nativeHookOperation !== current) return;
|
|
421
|
+
while (current.reading) {
|
|
422
|
+
await current.reading;
|
|
423
|
+
if (this.nativeHookOperation !== current) return;
|
|
424
|
+
}
|
|
425
|
+
const reading = readNativeHookFailures(this.paths.dir, current.requests, current, error => this.acceptHookFailure(current, error));
|
|
426
|
+
current.reading = reading;
|
|
427
|
+
try { await reading; } finally { if (current.reading === reading) current.reading = null; }
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
acceptHookFailure(current, error) {
|
|
431
|
+
if (this.nativeHookOperation !== current) return;
|
|
432
|
+
const unconfirmed = error.details?.native_hook_binding?.native_creator === "unconfirmed";
|
|
433
|
+
if (!unconfirmed && isProvenShellCorrection(error)) return;
|
|
434
|
+
if (unconfirmed || isNativeHookObservationLoss(error)) {
|
|
435
|
+
const diagnostic = { ...errorRecord(error), session_id: this.state.active_request?.adapter_session_id ?? null,
|
|
436
|
+
operation_id: current.operationId, generation: current.generation, delivery_stage: "hook_observation", completeness: "incomplete" };
|
|
437
|
+
this.bridge.lifecycleDiagnostics ??= [];
|
|
438
|
+
this.bridge.lifecycleDiagnostics.push(diagnostic);
|
|
439
|
+
try { void Promise.resolve(this.bridge.journal?.write("native_hook_observation_failure", diagnostic)).catch(() => {}); } catch { /* diagnostic is optional */ }
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
current.reject(error);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
hookOwner(binding) {
|
|
446
|
+
if (!binding?.request_id) return null;
|
|
447
|
+
const turns = this.bridge.nativeHookOwnership?.turns?.values() ?? [];
|
|
448
|
+
const owners = new Set([this.nativeHookOperation, ...[...turns].filter(turn => !turn.invalid).map(turn => turn.owner)]);
|
|
449
|
+
for (const owner of owners) {
|
|
450
|
+
if (!owner || owner.operationId !== binding.operation_id || owner.generation !== binding.turn_generation || owner.generation !== (this.controlGeneration ?? 0)) continue;
|
|
451
|
+
const accepted = owner.requests.get(binding.request_id);
|
|
452
|
+
if (accepted?.current !== owner || JSON.stringify(accepted.binding) !== JSON.stringify(binding)) continue;
|
|
453
|
+
if (binding.source_harness === "zcode-acp") {
|
|
454
|
+
const proof = this.bridge.nativeHookOwnership?.fact(binding.provider_session_id, binding.tool_call_id, binding.native_command);
|
|
455
|
+
if (!proof || proof.owner !== owner || proof.parent !== binding.parent_provider_session_id) continue;
|
|
456
|
+
}
|
|
457
|
+
return owner;
|
|
458
|
+
}
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
async requireSettled(params) {
|
|
463
|
+
if (this.sessions.get(params.sessionId)?.close_receipt) throw new DaemonError("session_closed", "Closed Session requires explicit recovery");
|
|
464
|
+
// A freshly created ZCode Session has no prior Turn and is not yet exposed
|
|
465
|
+
// by the provider's subagent index. Its first prompt is therefore safe.
|
|
466
|
+
if (this.neverPromptedRootProof(params.sessionId)) return null;
|
|
467
|
+
const observed = await inspectSessionWithBridge(this.bridge, this.options(params));
|
|
468
|
+
const runningChildren = observed.subagents?.running ?? [];
|
|
469
|
+
if (this.treeRunning(observed)) {
|
|
470
|
+
throw new DaemonError("tree_not_settled", "the prior ZCode agent tree is not fully settled", false, { session_id: params.sessionId, running_children: runningChildren.map((child) => child.sessionId ?? child.childSessionId ?? null) });
|
|
471
|
+
}
|
|
472
|
+
this.track(observed, observed.subagents);
|
|
473
|
+
return observed;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
neverPromptedRootProof(sessionId) {
|
|
477
|
+
const known = this.sessions.get(sessionId);
|
|
478
|
+
return known?.prompt_dispatched === false && known.created_by_daemon_id === this.state.daemon_id
|
|
479
|
+
&& known.parent_provider_session_id === null && known.root_provider_session_id === sessionId
|
|
480
|
+
&& Array.isArray(known.known_child_session_ids) && known.known_child_session_ids.length === 0
|
|
481
|
+
? { source: "owned_daemon_create_without_prompt", daemon_id: this.state.daemon_id, provider_session_id: sessionId }
|
|
482
|
+
: null;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
async markPromptDispatched(sessionId) {
|
|
486
|
+
const known = this.sessions.get(sessionId);
|
|
487
|
+
if (known) known.prompt_dispatched = true;
|
|
488
|
+
await this.persist();
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
async dispatch(operation, params, operationId = null) {
|
|
492
|
+
if (["session.create", "session.prompt", "session.start", "session.fork", "session.resume"].includes(operation)) assertDaemonDispatch(this.paths.dir);
|
|
493
|
+
if (operation === "session.observe-children") {
|
|
494
|
+
assertDaemonDispatch(this.paths.dir);
|
|
495
|
+
const root = this.sessions.get(params.sessionId);
|
|
496
|
+
if (!root || root.close_receipt) throw new DaemonError("session_identity_mismatch", "Retained observation requires a registered open root Session");
|
|
497
|
+
const generation = this.controlGeneration ?? 0;
|
|
498
|
+
const active = this.state.active_request;
|
|
499
|
+
const activeIdentity = value => JSON.stringify([value?.operation_id ?? null, value?.session_id ?? null, value?.adapter_session_id ?? null, value?.generation ?? null]);
|
|
500
|
+
const capturedIdentity = activeIdentity(active);
|
|
501
|
+
if (active && active.session_id !== params.sessionId) throw new DaemonError("session_identity_mismatch", "Retained observation has a foreign active root");
|
|
502
|
+
const cacheScope = `${params.sessionId}:${generation}`;
|
|
503
|
+
if (this.childObservationScope !== cacheScope) { this.childObservationScope = cacheScope; this.childTerminalCache = new Map(); this.childObservationKeys = new Set(); }
|
|
504
|
+
const subagents = await readZcodeSubagents(this.bridge, root.adapter_session_id, 30_000, true, this.childTerminalCache, root.provider_session_id);
|
|
505
|
+
if (generation !== (this.controlGeneration ?? 0)) throw new DaemonError("operation_cancelled", "Observation generation was retired");
|
|
506
|
+
if (activeIdentity(this.state.active_request) !== capturedIdentity) throw new DaemonError("operation_cancelled", "Observation belongs to a retired active Turn");
|
|
507
|
+
assertDaemonDispatch(this.paths.dir);
|
|
508
|
+
const receipt = { schema_id: "dd-zcode/child-observation@1", daemon_id: this.state.daemon_id, provider_session_id: params.sessionId,
|
|
509
|
+
generation, active_operation_id: active?.operation_id ?? null, subagents };
|
|
510
|
+
const failed = (subagents.ended?.items ?? subagents.completed ?? []).filter(child => child.status === "failed" && child.terminal_turn_id);
|
|
511
|
+
const fresh = failed.filter(child => !this.childObservationKeys.has(`${child.childSessionId}:${child.terminal_turn_id}`));
|
|
512
|
+
if (fresh.length) {
|
|
513
|
+
await this.bridge.journal.write("native_child_observation", { ...receipt, subagents: { running: [], ended: { total: fresh.length, items: fresh } } });
|
|
514
|
+
await this.bridge.journal.flush();
|
|
515
|
+
if (this.state.config?.journal) {
|
|
516
|
+
const journal = await open(this.state.config.journal, "a");
|
|
517
|
+
try { await journal.sync(); } finally { await journal.close(); }
|
|
518
|
+
}
|
|
519
|
+
for (const child of fresh) this.childObservationKeys.add(`${child.childSessionId}:${child.terminal_turn_id}`);
|
|
520
|
+
}
|
|
521
|
+
if (generation !== (this.controlGeneration ?? 0)) throw new DaemonError("operation_cancelled", "Observation generation was retired before ACK");
|
|
522
|
+
if (activeIdentity(this.state.active_request) !== capturedIdentity) throw new DaemonError("operation_cancelled", "Observation Turn changed before ACK");
|
|
523
|
+
assertDaemonDispatch(this.paths.dir);
|
|
524
|
+
return receipt;
|
|
525
|
+
}
|
|
526
|
+
if (operation === "daemon.status") return {
|
|
527
|
+
daemon_id: this.state.daemon_id, pid: process.pid, socket: this.paths.socket,
|
|
528
|
+
cwd: this.state.cwd, journal: this.state.journal, versions: this.state.versions,
|
|
529
|
+
started_at: this.state.started_at, updated_at: this.state.updated_at,
|
|
530
|
+
shutdown_state: this.state.shutdown_state, recovery_status: this.state.recovery_status,
|
|
531
|
+
active_tree: this.state.active_tree, active_operation: this.activeProductive, active_request: this.state.active_request ?? null,
|
|
532
|
+
sessions: [...this.sessions.values()], config: this.state.config, capabilities: { retained_child_observation: true },
|
|
533
|
+
};
|
|
534
|
+
if (operation === "hook.resolve") {
|
|
535
|
+
const sessionId = String(params.sessionId ?? "");
|
|
536
|
+
const payload = params.payload, command = payload?.tool_input?.command ?? payload?.tool_input?.cmd;
|
|
537
|
+
const tool = payload?.tool_use_id ?? payload?.tool_call_id;
|
|
538
|
+
const source = this.bridge.nativeHookOwnership;
|
|
539
|
+
let proof = source?.fact(sessionId, tool, command);
|
|
540
|
+
const tracked = this.sessions.get(sessionId);
|
|
541
|
+
if (!proof && (!tracked || tracked.parent_provider_session_id) && source && tool && typeof command === "string") {
|
|
542
|
+
await source.wait(sessionId, tool, command, params.hookDeadlineAt);
|
|
543
|
+
proof = source.fact(sessionId, tool, command);
|
|
544
|
+
}
|
|
545
|
+
const session = proof ? { root_provider_session_id: proof.root, parent_provider_session_id: proof.parent } : tracked;
|
|
546
|
+
if (!session?.root_provider_session_id) throw new DaemonError("hook_identity_unknown", "ZCode hook Session is not a tracked controlled Session", false, { session_id: sessionId });
|
|
547
|
+
const identity = { daemonId: this.state.daemon_id, rootProviderSessionId: session.root_provider_session_id, parentProviderSessionId: session.parent_provider_session_id ?? null, observedProfile: this.bridge.modelProfiles?.get(sessionId) ?? {} };
|
|
548
|
+
if (payload?.hook_event_name === "PreToolUse" && typeof command === "string" && (["lifecycle", "auxiliary"].includes(parseLifecycleCommand(command).participation) || continuationCommand(command))) {
|
|
549
|
+
// A retained child can only be attributed through the exact observed
|
|
550
|
+
// native turn and tool. Otherwise preserve the legacy handler without
|
|
551
|
+
// guessing that the currently active prompt created it.
|
|
552
|
+
if (sessionId !== session.root_provider_session_id && !proof) return identity;
|
|
553
|
+
const current = proof?.owner ?? this.nativeHookOperation, active = this.state.active_request;
|
|
554
|
+
const pending = [...(this.bridge.pending?.values() ?? [])].some(request => request.method === "session/prompt" && request.sessionId === active?.adapter_session_id);
|
|
555
|
+
const root = active?.session_id ?? active?.allocation?.provider_session_id;
|
|
556
|
+
if (!current || current.generation !== (this.controlGeneration ?? 0) || !tool || !proof && (!active || !pending || session.root_provider_session_id !== root)) throw new DaemonError("native_hook_unproven", "Native hook has no exact owned Session operation");
|
|
557
|
+
assertDaemonDispatch(this.paths.dir);
|
|
558
|
+
const nativePayload = proof ? { ...proof.message, _meta: { ...(proof.message._meta ?? {}), ddZcode: { daemonId: this.state.daemon_id, rootProviderSessionId: proof.root, parentProviderSessionId: proof.parent } } } : null;
|
|
559
|
+
const binding = await acceptNativeHookRequest(this.paths.dir, { daemon_id: this.state.daemon_id, operation_id: current.operationId, turn_generation: current.generation, provider_session_id: sessionId, root_provider_session_id: session.root_provider_session_id, parent_provider_session_id: session.parent_provider_session_id ?? null, tool_call_id: tool, event: "PreToolUse", command_sha256: createHash("sha256").update(command).digest("hex"), native_cwd: payload.cwd ?? null, native_creator: proof ? "confirmed" : "unconfirmed", ...(proof ? { source_harness: "zcode-acp", native_command: command, native_payload: nativePayload } : {}) }, current);
|
|
560
|
+
if (current.generation !== (this.controlGeneration ?? 0) || proof && source.fact(sessionId, tool, command) !== proof || !proof && this.nativeHookOperation !== current) throw new DaemonError("native_hook_unproven", "Native hook operation changed during acceptance");
|
|
561
|
+
current.requests.set(binding.request_id, { current, binding }); identity.nativeHookBinding = binding;
|
|
562
|
+
const parsed = parseLifecycleCommand(command);
|
|
563
|
+
if (proof && parsed.kind === "standalone" && parsed.invocation.operation === "work_start" && commandOption(parsed.invocation, "invocation-id")) identity.fastAck = true;
|
|
564
|
+
}
|
|
565
|
+
return identity;
|
|
566
|
+
}
|
|
567
|
+
if (operation === "hook.verify") {
|
|
568
|
+
if (params.daemonId !== this.state.daemon_id) throw new DaemonError("native_hook_unproven", "Native hook daemon identity differs");
|
|
569
|
+
const owner = this.hookOwner(params.binding);
|
|
570
|
+
if (!owner) throw new DaemonError("native_hook_unproven", "Native hook has no matching retained operation");
|
|
571
|
+
assertDaemonDispatch(this.paths.dir);
|
|
572
|
+
return { verified: true, nativeHookBinding: params.binding };
|
|
573
|
+
}
|
|
574
|
+
if (operation === "hook.failed") {
|
|
575
|
+
if (params.binding !== undefined || params.failure !== undefined) {
|
|
576
|
+
const binding = params.binding, failure = params.failure, current = this.hookOwner(binding);
|
|
577
|
+
// Join an in-flight file observation before checking the request. A
|
|
578
|
+
// direct fallback and its receipt must not be delivered twice.
|
|
579
|
+
while (current?.reading) await current.reading;
|
|
580
|
+
const accepted = current?.requests.get(binding?.request_id);
|
|
581
|
+
if (!current || this.hookOwner(binding) !== current || accepted?.current !== current
|
|
582
|
+
|| JSON.stringify(accepted.binding) !== JSON.stringify(binding) || typeof failure?.code !== "string" || typeof failure?.message !== "string")
|
|
583
|
+
throw new DaemonError("native_hook_unproven", "Hook failure has no exact current owned delivery");
|
|
584
|
+
assertDaemonDispatch(this.paths.dir);
|
|
585
|
+
if (this.nativeHookOperation !== current) {
|
|
586
|
+
await writeNativeHookFailure(this.paths.dir, binding, reportedError(failure, "Native hook failed"));
|
|
587
|
+
return { observed: true, retained: true };
|
|
588
|
+
}
|
|
589
|
+
current.requests.delete(binding.request_id);
|
|
590
|
+
const error = reportedError(failure, "Native hook failed");
|
|
591
|
+
error.details = { ...error.details, native_hook_binding: binding };
|
|
592
|
+
this.acceptHookFailure(current, error);
|
|
593
|
+
return { observed: true };
|
|
594
|
+
}
|
|
595
|
+
await this.observeHookFailures(); return { observed: true };
|
|
596
|
+
}
|
|
597
|
+
if (operation === "session.create") {
|
|
598
|
+
const request = {
|
|
599
|
+
operation_id: operationId,
|
|
600
|
+
request_id: randomUUID(),
|
|
601
|
+
session_id: params.sessionId ?? null,
|
|
602
|
+
adapter_session_id: params.adapterSessionId ?? params.sessionId ?? null,
|
|
603
|
+
generation: this.controlGeneration ?? 0,
|
|
604
|
+
dispatched_at: new Date().toISOString(),
|
|
605
|
+
request_timeout_ms: this.state.config.requestTimeoutMs,
|
|
606
|
+
};
|
|
607
|
+
return await this.productive(operation, async () => await createSessionWithBridge(this.bridge, this.options(params), this.initialized), request);
|
|
608
|
+
}
|
|
609
|
+
if (operation === "session.prompt") {
|
|
610
|
+
if (this.sessions.get(params.sessionId)?.close_receipt) throw new DaemonError("session_closed", "Closed Session requires explicit recovery before another prompt");
|
|
611
|
+
const known = this.sessions.get(params.sessionId);
|
|
612
|
+
const request = {
|
|
613
|
+
operation_id: operationId,
|
|
614
|
+
request_id: randomUUID(),
|
|
615
|
+
session_id: params.sessionId,
|
|
616
|
+
adapter_session_id: params.adapterSessionId ?? known?.adapter_session_id ?? params.sessionId,
|
|
617
|
+
generation: this.controlGeneration ?? 0,
|
|
618
|
+
dispatched_at: new Date().toISOString(),
|
|
619
|
+
request_timeout_ms: this.state.config.requestTimeoutMs,
|
|
620
|
+
};
|
|
621
|
+
const result = await this.productive(operation, async () => { await this.requireSettled(params); await this.markPromptDispatched(params.sessionId); return await promptSessionWithBridge(this.bridge, { ...this.options(params), timeoutMs: request.request_timeout_ms }); }, request);
|
|
622
|
+
const session = this.sessions.get(result.provider_session_id); if (session) session.completed_turns = (session.completed_turns ?? 0) + 1;
|
|
623
|
+
await this.persist(); return result;
|
|
624
|
+
}
|
|
625
|
+
if (operation === "session.fork") return await this.productive(operation, async () => { await this.requireSettled(params); await this.markPromptDispatched(params.sessionId); return await forkSessionWithBridge(this.bridge, this.options(params)); });
|
|
626
|
+
if (operation === "session.inspect" || operation === "session.resume") {
|
|
627
|
+
const closed = this.sessions.get(params.sessionId)?.close_receipt;
|
|
628
|
+
if (closed) {
|
|
629
|
+
if (operation === "session.resume") throw new DaemonError("session_closed", "Closed Session requires explicit recovery");
|
|
630
|
+
const topology = closed.topology;
|
|
631
|
+
if (topology?.root_session_id !== params.sessionId || !Array.isArray(topology.child_session_ids) || topology.child_session_ids.some(id => typeof id !== "string" || !id)) return { ...closed, settled: false, observation_error: { code: "topology_incomplete", message: "closed Session has no native topology identity" } };
|
|
632
|
+
const previous = this.state.settlement_observations?.[params.sessionId];
|
|
633
|
+
// A confirmed closed tree cannot resume through this daemon. Retain
|
|
634
|
+
// that terminal evidence even after its bridge has been retired.
|
|
635
|
+
for (const receipt of [previous, closed]) {
|
|
636
|
+
if (receipt?.close?.closed === true && receipt.settled === true
|
|
637
|
+
&& receipt.topology?.complete === true && receipt.topology.root_session_id === params.sessionId
|
|
638
|
+
&& Array.isArray(receipt.topology.child_session_ids)) return nativeExecutionReceipt(receipt, { daemonId: this.state.daemon_id, generation: this.controlGeneration ?? 0 });
|
|
639
|
+
}
|
|
640
|
+
const known = [...new Set([...topology.child_session_ids, ...(previous?.topology?.child_session_ids ?? [])])];
|
|
641
|
+
const emptyRoot = this.neverPromptedRootProof(params.sessionId);
|
|
642
|
+
const observation = { ...closed, ...await observeClosedZcodeTree(this.bridge, params.sessionId, known, emptyRoot && closed.close?.closed === true ? {...emptyRoot,close_confirmed:true} : null), observed_at: new Date().toISOString() };
|
|
643
|
+
// Observations are not ownership inventory: their timestamps must not
|
|
644
|
+
// invalidate a drain fence on every read.
|
|
645
|
+
this.state.settlement_observations ??= {};
|
|
646
|
+
this.state.settlement_observations[params.sessionId] = observation;
|
|
647
|
+
await this.persist();
|
|
648
|
+
return nativeExecutionReceipt(observation, { daemonId: this.state.daemon_id, generation: this.controlGeneration ?? 0 });
|
|
649
|
+
}
|
|
650
|
+
const adapter = params.adapterSessionId ?? params.sessionId;
|
|
651
|
+
if (!this.liveAdapters.has(adapter) && this.activeProductive) {
|
|
652
|
+
throw new DaemonError(
|
|
653
|
+
"child_inspection_unavailable",
|
|
654
|
+
"cannot inspect an unregistered child while a daemon-owned turn is running; inspection would resume and mutate it",
|
|
655
|
+
true,
|
|
656
|
+
{ session_id: params.sessionId },
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
const result = await inspectSessionWithBridge(this.bridge, this.options(params));
|
|
660
|
+
// A child can be inspected through its parent's live bridge, but is not
|
|
661
|
+
// an independently daemon-owned root Session. Do not promote it into
|
|
662
|
+
// `sessions`: daemon shutdown would then treat the child as a second
|
|
663
|
+
// root and could reject an otherwise settled parent tree.
|
|
664
|
+
if (this.sessions.has(result.provider_session_id)) this.track(result, result.subagents);
|
|
665
|
+
else if (result.adapter_session_id) this.liveAdapters.add(result.adapter_session_id);
|
|
666
|
+
await this.persist({ active_tree: this.treeRunning(result) || Boolean(this.activeProductive) });
|
|
667
|
+
const settled = !this.treeRunning(result) && !this.activeProductive;
|
|
668
|
+
const failure = this.state.last_native_failure;
|
|
669
|
+
const nativeChildren = (result.evidence ?? result).subagents;
|
|
670
|
+
const childIds = [...new Set([...(this.sessions.get(result.provider_session_id)?.known_child_session_ids ?? []), ...zcodeChildSessionIds(nativeChildren)])];
|
|
671
|
+
const childStates = new Map([...(nativeChildren?.ended?.items ?? nativeChildren?.completed ?? []), ...(nativeChildren?.running ?? []).map(child => ({ ...child, status: "running" }))].map(child => [child.childSessionId ?? child.sessionId, child.status]));
|
|
672
|
+
const rootSettled = !this.activeProductive && failure?.provider_session_id === result.provider_session_id && (result.evidence ?? result).read?.projection?.status === "idle";
|
|
673
|
+
return nativeExecutionReceipt({ ...result, settled, ...settledNativeTree(result.provider_session_id, childIds.map(childSessionId => ({ childSessionId, status: childStates.get(childSessionId) ?? "unknown" })), settled, rootSettled),
|
|
674
|
+
...(failure?.provider_session_id === result.provider_session_id ? { terminal_turn_id: String(failure.request_id) } : {}) }, { daemonId: this.state.daemon_id, generation: this.controlGeneration ?? 0 });
|
|
675
|
+
}
|
|
676
|
+
if (operation === "session.cancel") {
|
|
677
|
+
if (!this.sessions.has(params.sessionId)) throw new DaemonError("session_identity_mismatch", "cancellation Session is not owned by this daemon");
|
|
678
|
+
const closed = this.sessions.get(params.sessionId)?.close_receipt;
|
|
679
|
+
if (closed) return closed;
|
|
680
|
+
this.controlGeneration = (this.controlGeneration ?? 0) + 1;
|
|
681
|
+
const result = await cancelSessionWithBridge(this.bridge, this.options(params));
|
|
682
|
+
this.track(result, result.after);
|
|
683
|
+
await this.persist({ active_tree: result.settled !== true && (this.treeRunning(result) || Boolean(this.activeProductive)) });
|
|
684
|
+
return result;
|
|
685
|
+
}
|
|
686
|
+
if (operation === "session.cancel-child") {
|
|
687
|
+
const result = await cancelChildWithBridge(this.bridge, this.options(params));
|
|
688
|
+
this.track(result, result.after);
|
|
689
|
+
await this.persist({ active_tree: this.treeRunning(result) || Boolean(this.activeProductive) });
|
|
690
|
+
return result;
|
|
691
|
+
}
|
|
692
|
+
if (operation === "daemon.stop") return await completeDaemonShutdown(this.paths.dir, this.state, patch => this.persist(patch), () => this.prepareStop(params.cancelTree === true), [
|
|
693
|
+
["provider", () => this.bridge.close?.()],
|
|
694
|
+
["provider_resource", () => finishDaemonProcess(this.state.config, this.state.provider_process)],
|
|
695
|
+
["daemon_resource", () => finishDaemonProcess(this.state.config, this.state.resource_process)]
|
|
696
|
+
], undefined, { cancelTree: params.cancelTree === true });
|
|
697
|
+
throw new DaemonError("unknown_operation", `unknown daemon operation: ${operation}`);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
async prepareStop(cancel) {
|
|
701
|
+
if (this.activeProductive && !cancel) throw new DaemonError("tree_not_settled", "productive operation is running");
|
|
702
|
+
if (cancel) this.controlGeneration = (this.controlGeneration ?? 0) + 1;
|
|
703
|
+
if (!this.state.native_stop_proof || this.state.native_stop_proof.daemon_id !== this.state.daemon_id) {
|
|
704
|
+
const unsettled = [];
|
|
705
|
+
for (const session of this.sessions.values()) {
|
|
706
|
+
if (session.close_receipt) {
|
|
707
|
+
const observed = await this.dispatch("session.inspect", { sessionId: session.provider_session_id });
|
|
708
|
+
if (observed.settled !== true) unsettled.push(session.provider_session_id);
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
const options = this.options({ sessionId: session.provider_session_id, adapterSessionId: session.adapter_session_id });
|
|
712
|
+
let observed;
|
|
713
|
+
// A daemon-owned allocation with no productive dispatch cannot have
|
|
714
|
+
// children, and native ZCode has no durable read index for it yet. Close
|
|
715
|
+
// that resident directly; do not send cancellation for default stop.
|
|
716
|
+
const emptyRoot = !cancel && !this.activeProductive ? this.neverPromptedRootProof(session.provider_session_id) : null;
|
|
717
|
+
if (emptyRoot) {
|
|
718
|
+
const close = await this.bridge.request("zcode/session/close", { sessionId: session.adapter_session_id });
|
|
719
|
+
const observation = close?.closed === true
|
|
720
|
+
? await observeClosedZcodeTree(this.bridge, session.provider_session_id, [], { ...emptyRoot, close_confirmed: true })
|
|
721
|
+
: { settled: false, topology: { root_session_id: session.provider_session_id, child_session_ids: [], complete: false } };
|
|
722
|
+
this.track({ provider_session_id: session.provider_session_id, adapter_session_id: session.adapter_session_id, cancellation_requested: false, close_requested: true, close, ...observation });
|
|
723
|
+
await this.persist({ active_tree: observation.settled !== true });
|
|
724
|
+
if (observation.settled !== true) unsettled.push(session.provider_session_id);
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
if (!cancel) observed = await inspectSessionWithBridge(this.bridge, options);
|
|
728
|
+
if (cancel || !observed || this.treeRunning(observed)) {
|
|
729
|
+
if (!cancel) unsettled.push(session.provider_session_id);
|
|
730
|
+
else {
|
|
731
|
+
let cancelled;
|
|
732
|
+
try { cancelled = await cancelSessionWithBridge(this.bridge, options); }
|
|
733
|
+
catch (error) { unsettled.push(session.provider_session_id); continue; }
|
|
734
|
+
this.track(cancelled, cancelled.after);
|
|
735
|
+
await this.persist({ active_tree: cancelled.settled !== true });
|
|
736
|
+
if (cancelled.close?.closed === true) {
|
|
737
|
+
if (!cancelled.settled) unsettled.push(session.provider_session_id);
|
|
738
|
+
continue;
|
|
739
|
+
}
|
|
740
|
+
// A cooperative cancel needs a final read. A close receipt is
|
|
741
|
+
// terminal itself: native session/read correctly rejects it as no
|
|
742
|
+
// longer resident, so treating that rejection as unsettled would
|
|
743
|
+
// turn a successful close into an immortal daemon.
|
|
744
|
+
if (!cancelled.settled) {
|
|
745
|
+
try { observed = await inspectSessionWithBridge(this.bridge, options); }
|
|
746
|
+
catch (error) { unsettled.push(session.provider_session_id); continue; }
|
|
747
|
+
if (this.treeRunning(observed)) unsettled.push(session.provider_session_id);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
if (unsettled.length) throw new DaemonError("tree_not_settled", "daemon still owns running Sessions", false, { sessions: unsettled });
|
|
753
|
+
commitDaemonShutdown(this.paths.dir);
|
|
754
|
+
await this.persist({ native_stop_proof: { daemon_id: this.state.daemon_id, sessions: [...this.sessions.keys()], observed_at: new Date().toISOString() } });
|
|
755
|
+
}
|
|
756
|
+
commitDaemonShutdown(this.paths.dir);
|
|
757
|
+
if (!cancel) {
|
|
758
|
+
// The native tree has been observed idle. Publish that execution proof
|
|
759
|
+
// before attempting to close the independent bridge process. A close
|
|
760
|
+
// failure is cleanup debt and must not erase the already observed turn.
|
|
761
|
+
await this.persist({ active_tree: false, shutdown_state: "stopping", active_operation: null, active_request: null });
|
|
762
|
+
return { daemon_id: this.state.daemon_id, clean: true, settled: true, native_settled: true };
|
|
763
|
+
}
|
|
764
|
+
// A cooperative close can make the native Session non-resident while its
|
|
765
|
+
// original ACP prompt is still pending. For an explicitly whole-RUN
|
|
766
|
+
// cancellation this daemon owns the bridge process group, so retire that
|
|
767
|
+
// group now rather than reporting a clean-looking but still live tree.
|
|
768
|
+
// A per-child cancellation never takes this path.
|
|
769
|
+
let bridgeClosed = false;
|
|
770
|
+
if (this.activeProductive && cancel) {
|
|
771
|
+
try {
|
|
772
|
+
if (typeof this.bridge.close === "function") await this.bridge.close();
|
|
773
|
+
bridgeClosed = true;
|
|
774
|
+
} catch (error) {
|
|
775
|
+
throw new DaemonError(error.code ?? "provider_process_stop_incomplete", error.message ?? String(error), false, error.details);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
const deadline = Date.now() + 5000;
|
|
779
|
+
while (this.activeProductive && Date.now() < deadline) await new Promise((resolve) => setTimeout(resolve, 50));
|
|
780
|
+
if (this.activeProductive) throw new DaemonError("tree_not_settled", "productive operation did not settle after cancellation");
|
|
781
|
+
// The owned bridge process group is the physical stop barrier. A native
|
|
782
|
+
// closed/resident receipt only proves API state and cannot seal recovery.
|
|
783
|
+
// A bridge without a close method occurs only in in-memory diagnostics: it
|
|
784
|
+
// owns no OS process boundary. Production AcpBridge always has one.
|
|
785
|
+
try { if (!bridgeClosed && typeof this.bridge.close === "function") await this.bridge.close(); }
|
|
786
|
+
catch (error) { throw new DaemonError(error.code ?? "provider_process_stop_incomplete", error.message ?? String(error), false, error.details); }
|
|
787
|
+
await this.persist({ active_tree: false, shutdown_state: "stopping", active_operation: null, active_request: null, physical_settlement: { confirmed_at: new Date().toISOString(), bridge_pid: this.bridge.child?.pid ?? null } });
|
|
788
|
+
return { daemon_id: this.state.daemon_id, stopped: true, clean: true, settled: true, native_settled: true, physical_settlement: this.state.physical_settlement, _shutdown: true };
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
export function zcodeBridgeEnv(config, daemonId) {
|
|
794
|
+
return { ...config.env, ...managedFlowEnv(config, daemonId), DD_ZCODE_STATE_DIR: !config.noFlow && daemonId ? config.runtime_owner?.state_dir : undefined };
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
export async function serveDaemon(stateDir) {
|
|
798
|
+
const paths = locations(stateDir);
|
|
799
|
+
const state = await readState(paths.dir);
|
|
800
|
+
if (!state || state.schema_id !== STATE_SCHEMA) throw new DaemonError("daemon_state_missing", "daemon state is missing or incompatible");
|
|
801
|
+
const bridge = new AcpBridge({ ...state.config, env: zcodeBridgeEnv(state.config, state.daemon_id) });
|
|
802
|
+
const initialized = await startManagedBridge({ config: state.config, state, bridge, kind: "zcode-provider", publish: snapshot => writeState(paths.state, snapshot), log: paths.log });
|
|
803
|
+
const runtime = new DaemonRuntime(paths, state, bridge, initialized);
|
|
804
|
+
scheduleDaemonProcessHeartbeat(state.config, state.resource_process);
|
|
805
|
+
await removeSocket(paths.socket, paths.dir);
|
|
806
|
+
const server = net.createServer((connection) => {
|
|
807
|
+
connection.setEncoding("utf8"); let buffer = "", consumed = false;
|
|
808
|
+
connection.on("data", (chunk) => {
|
|
809
|
+
if (consumed) return;
|
|
810
|
+
buffer += chunk; if (!admitNativeRequestBuffer(buffer, connection, RESPONSE_SCHEMA)) { consumed = true; return; }
|
|
811
|
+
const newline = buffer.indexOf("\n");
|
|
812
|
+
if (newline < 0) return;
|
|
813
|
+
consumed = true;
|
|
814
|
+
const line = buffer.slice(0, newline); buffer = buffer.slice(newline + 1);
|
|
815
|
+
void (async () => {
|
|
816
|
+
let request;
|
|
817
|
+
try {
|
|
818
|
+
request = JSON.parse(line);
|
|
819
|
+
if (request.schema_id !== REQUEST_SCHEMA || !request.id || !request.operation) throw new DaemonError("daemon_protocol_mismatch", "invalid daemon request");
|
|
820
|
+
const result = await durableDaemonDispatch(paths.dir, request, () => runtime.dispatch(request.operation, request.params ?? {}, request.id), () => runtime.dispatch("session.inspect", request.params ?? {}));
|
|
821
|
+
if (result?._shutdown) retireDaemonEndpoint(server);
|
|
822
|
+
connection.end(`${JSON.stringify({ schema_id: RESPONSE_SCHEMA, id: request.id, ok: true, result: { ...result, _shutdown: undefined } })}\n`);
|
|
823
|
+
} catch (error) {
|
|
824
|
+
await retireFailedDaemon(server, state, error, patch => runtime.persist(patch));
|
|
825
|
+
connection.end(`${JSON.stringify({ schema_id: RESPONSE_SCHEMA, id: request?.id ?? null, ok: false, error: errorPayload(error) })}\n`);
|
|
826
|
+
}
|
|
827
|
+
})();
|
|
828
|
+
});
|
|
829
|
+
});
|
|
830
|
+
runtime.server = server;
|
|
831
|
+
trackDaemonConnections(server);
|
|
832
|
+
await new Promise((resolve, reject) => { server.once("error", reject); server.listen(paths.socket, resolve); });
|
|
833
|
+
await chmod(paths.socket, 0o600);
|
|
834
|
+
await runtime.persist({ shutdown_state: "running", active_tree: false });
|
|
835
|
+
let shuttingDown = false;
|
|
836
|
+
const onSignal = () => {
|
|
837
|
+
if (shuttingDown) return;
|
|
838
|
+
shuttingDown = true;
|
|
839
|
+
void runtime.shutdown().catch(async () => { await runtime.persist({ shutdown_state: "running" }); }).finally(() => process.exit(1));
|
|
840
|
+
};
|
|
841
|
+
process.on("SIGTERM", onSignal);
|
|
842
|
+
process.on("SIGINT", onSignal);
|
|
843
|
+
return await new Promise(() => {});
|
|
844
|
+
}
|
|
845
|
+
import { authorizeRetainedDaemonResume } from "./driver-recovery.mjs";
|