@deksden-com/dd-flow-cli 0.9.0-beta.15 → 0.9.0-beta.150
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +895 -0
- package/README.md +140 -6
- package/dist/build-info.json +8 -8
- package/dist/cli/argument-values.js +38 -0
- package/dist/cli/command-inputs.js +386 -0
- package/dist/cli/command-metadata.js +232 -0
- package/dist/cli/command-option-descriptions.js +192 -0
- package/dist/cli/completion-cli.js +70 -0
- package/dist/cli/completion.js +57 -0
- package/dist/cli/diagnostic-invocation.js +46 -0
- package/dist/cli/diagnostic.js +120 -0
- package/dist/cli/error-output.js +52 -0
- package/dist/cli/executor-onboarding.js +76 -0
- package/dist/cli/flows.js +29 -0
- package/dist/cli/harnesses.js +84 -0
- package/dist/cli/help.js +396 -19
- package/dist/cli/hook-ingress.js +102 -0
- package/dist/cli/input-preparation.js +165 -0
- package/dist/cli/memory-flow-admission.js +50 -0
- package/dist/cli/memory-init-invocation.js +85 -0
- package/dist/cli/memory-init.js +249 -0
- package/dist/cli/memory-storage-admission.js +54 -0
- package/dist/cli/memory-storage-qualification.js +105 -0
- package/dist/cli/memory-storage-transition.js +123 -0
- package/dist/cli/memory-upgrade.js +189 -0
- package/dist/cli/models.js +39 -0
- package/dist/cli/native-hook-ingress.js +90 -0
- package/dist/cli/native-hook-preparation.js +55 -0
- package/dist/cli/operator-profiles.js +61 -0
- package/dist/cli/output-options.js +35 -0
- package/dist/cli/response-file.js +72 -0
- package/dist/cli/run-cli.js +1734 -550
- package/dist/cli/runtime-maintenance-diagnostics.js +46 -0
- package/dist/cli/runtime-maintenance-ingress.js +52 -0
- package/dist/cli/runtime-process.js +153 -0
- package/dist/cli/setup.js +150 -0
- package/dist/cli.js +89 -2
- package/dist/domain/diagnostic-aspects.js +103 -0
- package/dist/domain/diagnostic-flows.js +8 -0
- package/dist/domain/stage-catalog.js +1 -1
- package/dist/domain/stage-handler-identities.js +28 -0
- package/dist/harness-runtime/DD-ZCODE.md +112 -0
- package/dist/harness-runtime/OPENCODE-LICENSE.txt +24 -0
- package/dist/harness-runtime/bin/dd-agy.mjs +56 -0
- package/dist/harness-runtime/bin/dd-codex.mjs +27 -0
- package/dist/harness-runtime/bin/dd-droid.mjs +34 -0
- package/dist/harness-runtime/bin/dd-grok.mjs +40 -0
- package/dist/harness-runtime/bin/dd-opencode.mjs +22 -0
- package/dist/harness-runtime/bin/dd-zcode.mjs +87 -0
- package/dist/harness-runtime/lib/adapter-timeouts.d.mts +5 -0
- package/dist/harness-runtime/lib/adapter-timeouts.mjs +34 -0
- package/dist/harness-runtime/lib/agy-error-boundary.mjs +58 -0
- package/dist/harness-runtime/lib/agy-hooks.d.mts +1 -0
- package/dist/harness-runtime/lib/agy-hooks.mjs +9 -0
- package/dist/harness-runtime/lib/agy-native-inspection.mjs +216 -0
- package/dist/harness-runtime/lib/agy-native_pb.js +117 -0
- package/dist/harness-runtime/lib/daemon-observation.d.mts +2 -0
- package/dist/harness-runtime/lib/daemon-observation.mjs +163 -0
- package/dist/harness-runtime/lib/daemon-operations.d.mts +2 -0
- package/dist/harness-runtime/lib/daemon-operations.mjs +462 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.d.mts +6 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +1096 -0
- package/dist/harness-runtime/lib/dd-agy.mjs +86 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.mjs +231 -0
- package/dist/harness-runtime/lib/dd-codex.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex.mjs +954 -0
- package/dist/harness-runtime/lib/dd-droid-daemon.mjs +141 -0
- package/dist/harness-runtime/lib/dd-droid.mjs +626 -0
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +555 -0
- package/dist/harness-runtime/lib/dd-grok.mjs +180 -0
- package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +622 -0
- package/dist/harness-runtime/lib/dd-opencode.mjs +390 -0
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +752 -0
- package/dist/harness-runtime/lib/dd-zcode.mjs +1175 -0
- package/dist/harness-runtime/lib/delegation-instructions.d.mts +14 -0
- package/dist/harness-runtime/lib/delegation-instructions.mjs +157 -0
- package/dist/harness-runtime/lib/dispatch-fence.mjs +14 -0
- package/dist/harness-runtime/lib/driver-recovery.mjs +140 -0
- package/dist/harness-runtime/lib/droid-observation.mjs +66 -0
- package/dist/harness-runtime/lib/entity-references.d.mts +2 -0
- package/dist/harness-runtime/lib/entity-references.mjs +9 -0
- package/dist/harness-runtime/lib/lease-renewal.d.mts +8 -0
- package/dist/harness-runtime/lib/lease-renewal.mjs +37 -0
- package/dist/harness-runtime/lib/lifecycle-command.d.mts +20 -0
- package/dist/harness-runtime/lib/lifecycle-command.mjs +273 -0
- package/dist/harness-runtime/lib/lifecycle-contract.d.mts +10 -0
- package/dist/harness-runtime/lib/lifecycle-contract.mjs +19 -0
- package/dist/harness-runtime/lib/managed-daemon.d.mts +16 -0
- package/dist/harness-runtime/lib/managed-daemon.mjs +698 -0
- package/dist/harness-runtime/lib/managed-flow-env.mjs +15 -0
- package/dist/harness-runtime/lib/model-observations.mjs +101 -0
- package/dist/harness-runtime/lib/native-children.d.mts +23 -0
- package/dist/harness-runtime/lib/native-children.mjs +219 -0
- package/dist/harness-runtime/lib/native-hook-command.d.mts +7 -0
- package/dist/harness-runtime/lib/native-hook-command.mjs +251 -0
- package/dist/harness-runtime/lib/native-request-proof.d.mts +2 -0
- package/dist/harness-runtime/lib/native-request-proof.mjs +65 -0
- package/dist/harness-runtime/lib/native-streams.mjs +45 -0
- package/dist/harness-runtime/lib/observation-clock.d.mts +18 -0
- package/dist/harness-runtime/lib/observation-clock.mjs +95 -0
- package/dist/harness-runtime/lib/opencode-plugin.mjs +85 -0
- package/dist/harness-runtime/lib/operation-context.mjs +5 -0
- package/dist/harness-runtime/lib/operation-errors.d.mts +16 -0
- package/dist/harness-runtime/lib/operation-errors.mjs +127 -0
- package/dist/harness-runtime/lib/operation-progress.d.mts +7 -0
- package/dist/harness-runtime/lib/operation-progress.mjs +114 -0
- package/dist/harness-runtime/lib/process-json.d.mts +5 -0
- package/dist/harness-runtime/lib/process-json.mjs +201 -0
- package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
- package/dist/harness-runtime/lib/provider-overload-policy.d.mts +15 -0
- package/dist/harness-runtime/lib/provider-overload-policy.mjs +151 -0
- package/dist/harness-runtime/lib/runner-events.mjs +202 -0
- package/dist/harness-runtime/lib/runner-lock.mjs +42 -0
- package/dist/harness-runtime/lib/session-settlement.mjs +30 -0
- package/dist/harness-runtime/lib/setup-progress.d.mts +9 -0
- package/dist/harness-runtime/lib/setup-progress.mjs +53 -0
- package/dist/harness-runtime/lib/tool-observations.d.mts +21 -0
- package/dist/harness-runtime/lib/tool-observations.mjs +228 -0
- package/dist/harness-runtime/lib/zcode-usage.mjs +55 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-1y4a697q.js +81 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-2ny4vxct.js +92 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-6mptkzbc.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-7x67cf1q.js +52 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9rqn6x4v.js +16 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9vdz0zwn.js +1661 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ecs5ftka.js +1466 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-frbwqjmf.js +145 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gne7h7mq.js +50 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gx9wnkzr.js +105 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-m2eywc8w.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-nwcpakzx.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-q4g3afnw.js +623 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-qav6mfdz.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-sxt88q69.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-t1xa31vm.js +39 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ve4aws36.js +103 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vmytr4q8.js +4 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vt9jbch8.js +36 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vyas3vgt.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-wjyeq25y.js +30 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-xgnj2w4v.js +20 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-y2etg5bs.js +9 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ydhz1zrh.js +238 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-z6qdwcsf.js +62 -0
- package/dist/harness-runtime/vendor/opencode-client/contract.d.ts +1 -0
- package/dist/harness-runtime/vendor/opencode-client/contract.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api/api.d.ts +2806 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api/api.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api.d.ts +4 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/client.d.ts +2530 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/client.js +15 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.d.ts +7 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.js +6 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.d.ts +2529 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.js +8 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.d.ts +2 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/index.d.ts +35 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/index.js +75 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/rpc.d.ts +20 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/rpc.js +11 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/service.d.ts +90 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/service.js +187 -0
- package/dist/harness-runtime/vendor/opencode-client/package.json +84 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/api.d.ts +21 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/api.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/client.d.ts +252 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/client.js +14 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.d.ts +8 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.js +6 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.d.ts +259 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.js +7 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.d.ts +3 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.js +79 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.d.ts +8932 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.js +70 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/index.d.ts +6 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/index.js +83 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/rpc.d.ts +32 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/rpc.js +7 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/service.d.ts +24 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/service.js +187 -0
- package/dist/harness-runtime/vendor/opencode-client/pty-handoff.d.ts +12 -0
- package/dist/harness-runtime/vendor/opencode-client/pty-handoff.js +14 -0
- package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.d.ts +20 -0
- package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.js +14 -0
- package/dist/harness-runtime/vendor/opencode-client/service-contender.d.ts +37 -0
- package/dist/harness-runtime/vendor/opencode-client/service-contender.js +10 -0
- package/dist/harness-runtime/vendor/opencode-client/service-probe.d.ts +70 -0
- package/dist/harness-runtime/vendor/opencode-client/service-probe.js +16 -0
- package/dist/harness-runtime/vendor/opencode-client/service-timing.d.ts +12 -0
- package/dist/harness-runtime/vendor/opencode-client/service-timing.js +10 -0
- package/dist/harness-runtime/vendor/opencode-client/service-version.d.ts +2 -0
- package/dist/harness-runtime/vendor/opencode-client/service-version.js +6 -0
- package/dist/harness-runtime/vendor/opencode-client/service.d.ts +52 -0
- package/dist/harness-runtime/vendor/opencode-client/service.js +1 -0
- package/dist/harness-runtime/vendor/opencode-client/shared-events.d.ts +11 -0
- package/dist/harness-runtime/vendor/opencode-client/shared-events.js +8 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/connection.d.ts +37 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/connection.js +10 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/data.d.ts +211 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/data.js +18 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/index.d.ts +3 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/index.js +32 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/pty.d.ts +22 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/pty.js +8 -0
- package/dist/harness-runtime/vendor/opencode-plugin.mjs +3 -0
- package/dist/protocol/local-files.js +18 -9
- package/dist/runtime/context.js +8 -4
- package/dist/schemas/agent-profile.schema.json +1 -1
- package/dist/schemas/code-review-decision.schema.json +9 -0
- package/dist/schemas/code-review-result.schema.json +1 -1
- package/dist/schemas/code-verification.schema.json +7 -5
- package/dist/schemas/code-work-batch.schema.json +2 -2
- package/dist/schemas/diagnostic-stage-result.schema.json +354 -0
- package/dist/schemas/diagnostic-work-result.schema.json +296 -0
- package/dist/schemas/discovery-result.schema.json +184 -0
- package/dist/schemas/execution-bundle.schema.json +211 -0
- package/dist/schemas/execution-settings.schema.json +162 -0
- package/dist/schemas/flow-descriptor.schema.json +54 -0
- package/dist/schemas/flow-run.schema.json +29 -0
- package/dist/schemas/harness-catalog.schema.json +80 -0
- package/dist/schemas/mb-init-compose-result.schema.json +457 -0
- package/dist/schemas/mb-init-discover-result.schema.json +500 -0
- package/dist/schemas/mb-init-finish-result.schema.json +422 -0
- package/dist/schemas/mb-init-preflight-result.schema.json +367 -0
- package/dist/schemas/mb-init-review-result.schema.json +396 -0
- package/dist/schemas/mb-init-verify-result.schema.json +391 -0
- package/dist/schemas/mb-upgrade-stage-result.schema.json +863 -0
- package/dist/schemas/memory-bootstrap-receipt-v2.schema.json +67 -0
- package/dist/schemas/memory-bootstrap-receipt.schema.json +354 -0
- package/dist/schemas/memory-composition-work-result.schema.json +38 -0
- package/dist/schemas/memory-repair-work-result.schema.json +56 -0
- package/dist/schemas/memory-review-correction-plan.schema.json +30 -0
- package/dist/schemas/memory-review-work-result.schema.json +43 -0
- package/dist/schemas/model-catalog.schema.json +153 -0
- package/dist/schemas/plan-review-decision.schema.json +12 -3
- package/dist/schemas/plan-review-result.schema.json +2 -2
- package/dist/schemas/profile-settings.schema.json +122 -0
- package/dist/schemas/project-flow-pack-manifest.schema.json +1 -1
- package/dist/schemas/release-impact.schema.json +3 -3
- package/dist/schemas/release-manifest.schema.json +291 -0
- package/dist/schemas/run-control-receipt.schema.json +118 -0
- package/dist/schemas/run-control-request.schema.json +36 -0
- package/dist/schemas/run-engine-binding.schema.json +1 -1
- package/dist/schemas/stage-start-response.schema.json +4 -0
- package/dist/schemas/vnext-protocol-plan.schema.json +2 -2
- package/dist/services/accepted-intent.js +139 -0
- package/dist/services/branch-context.js +3 -3
- package/dist/services/canon.js +54 -15
- package/dist/services/check-environment.js +21 -0
- package/dist/services/check-runner-evidence.js +562 -0
- package/dist/services/cleanup.js +133 -40
- package/dist/services/cli-operation-classifier.js +26 -63
- package/dist/services/cli-upgrade-allowlist.js +27 -0
- package/dist/services/code-checks.js +1398 -119
- package/dist/services/codex-hook-delivery.js +32 -0
- package/dist/services/command-context.js +80 -0
- package/dist/services/compatibility-preflight.js +16 -3
- package/dist/services/config.js +11 -8
- package/dist/services/continuation-outcome.js +20 -0
- package/dist/services/controller-child-capacity.js +13 -0
- package/dist/services/controller-child-observer.js +115 -0
- package/dist/services/controller-execution.js +94 -0
- package/dist/services/controller-fanout.js +226 -0
- package/dist/services/controller-memory-task.js +45 -0
- package/dist/services/controller-memory-worker.js +49 -0
- package/dist/services/dashboard.js +143 -58
- package/dist/services/descriptor-coordinator-session.js +35 -0
- package/dist/services/descriptor-root-work.js +53 -0
- package/dist/services/descriptor-run-settlement.js +107 -0
- package/dist/services/descriptor-run-start.js +475 -0
- package/dist/services/descriptor-stage-coordinator.js +250 -0
- package/dist/services/descriptor-stage-settlement.js +223 -0
- package/dist/services/descriptor-stage-start.js +175 -0
- package/dist/services/diagnostic-evidence.js +73 -0
- package/dist/services/diagnostic-report-publication.js +72 -0
- package/dist/services/diagnostic-report.js +67 -0
- package/dist/services/diagnostic-run-bundle.js +74 -0
- package/dist/services/diagnostic-snapshot.js +159 -0
- package/dist/services/diagnostic-stage-handlers.js +108 -0
- package/dist/services/diagnostic-stage-results.js +132 -0
- package/dist/services/diagnostic-start.js +84 -0
- package/dist/services/diagnostic-work-batch.js +34 -0
- package/dist/services/diagnostic-work-collection.js +97 -0
- package/dist/services/diagnostic-work-registration.js +101 -0
- package/dist/services/diagnostic-work-results.js +93 -0
- package/dist/services/discovery/agy-discovery.js +2 -0
- package/dist/services/discovery/codex-discovery.js +2 -0
- package/dist/services/discovery/droid-discovery.js +2 -0
- package/dist/services/discovery/grok-discovery.js +2 -0
- package/dist/services/discovery/opencode-discovery.js +2 -0
- package/dist/services/discovery/static.js +31 -0
- package/dist/services/discovery/zcode-discovery.js +2 -0
- package/dist/services/engines.js +354 -163
- package/dist/services/eval-snapshots.js +1316 -150
- package/dist/services/execution-bundles.js +275 -0
- package/dist/services/execution-policy.js +185 -0
- package/dist/services/executor-onboarding.js +56 -0
- package/dist/services/external-work-launch.js +310 -0
- package/dist/services/flow-catalog.js +30 -0
- package/dist/services/flow-descriptors.js +82 -0
- package/dist/services/frozen-engine.js +387 -0
- package/dist/services/generic-execution.js +164 -0
- package/dist/services/harness-adapter.js +355 -36
- package/dist/services/harness-config.js +2 -0
- package/dist/services/harness-discovery.js +29 -0
- package/dist/services/harness-doctor.js +75 -0
- package/dist/services/harness-probe-effects.js +51 -0
- package/dist/services/harness-version-probe.js +100 -0
- package/dist/services/hook-memory-routing.js +188 -0
- package/dist/services/hook-recovery-guard.js +14 -0
- package/dist/services/hook-session-observation.js +24 -0
- package/dist/services/hooks.js +791 -326
- package/dist/services/hybrid-contracts.js +474 -0
- package/dist/services/hybrid-digest.js +14 -0
- package/dist/services/ids.js +29 -1
- package/dist/services/installation-lock.js +95 -0
- package/dist/services/installed-flow-source.js +226 -0
- package/dist/services/lanes.js +78 -65
- package/dist/services/lifecycle-command.js +1 -288
- package/dist/services/lifecycle-contract.js +1 -0
- package/dist/services/lifecycle-invocations.js +2148 -0
- package/dist/services/managed-daemon-binding.js +38 -0
- package/dist/services/managed-lease-monitor.js +133 -0
- package/dist/services/managed-processes.js +449 -69
- package/dist/services/memory-accepted-composition.js +78 -0
- package/dist/services/memory-baseline-preservation.js +65 -0
- package/dist/services/memory-completed-correction-wave.js +328 -0
- package/dist/services/memory-compose-handler.js +400 -0
- package/dist/services/memory-composition-acceptance.js +375 -0
- package/dist/services/memory-composition-graph.js +165 -0
- package/dist/services/memory-composition-registration.js +161 -0
- package/dist/services/memory-composition-work-custody.js +156 -0
- package/dist/services/memory-composition-work-results.js +108 -0
- package/dist/services/memory-corrected-review-acceptance.js +129 -0
- package/dist/services/memory-corrected-review-batch.js +37 -0
- package/dist/services/memory-corrected-review-findings.js +108 -0
- package/dist/services/memory-corrected-review-inputs.js +215 -0
- package/dist/services/memory-corrected-review-projection.js +78 -0
- package/dist/services/memory-corrected-review-receipt.js +178 -0
- package/dist/services/memory-corrected-review-registration.js +192 -0
- package/dist/services/memory-correction-check-receipt.js +235 -0
- package/dist/services/memory-correction-input-owner.js +6 -0
- package/dist/services/memory-custody-snapshot.js +36 -0
- package/dist/services/memory-discover-handler.js +104 -0
- package/dist/services/memory-finish-evidence.js +457 -0
- package/dist/services/memory-finish-handler.js +229 -0
- package/dist/services/memory-finish-integration.js +450 -0
- package/dist/services/memory-finish-readiness.js +169 -0
- package/dist/services/memory-git-integration.js +220 -0
- package/dist/services/memory-historical-corrected-review.js +212 -0
- package/dist/services/memory-historical-original-review.js +143 -0
- package/dist/services/memory-historical-repair-registration.js +105 -0
- package/dist/services/memory-historical-second-review.js +147 -0
- package/dist/services/memory-init-bootstrap-readiness.js +210 -0
- package/dist/services/memory-init-contexts.js +131 -0
- package/dist/services/memory-init-controller-start.js +103 -0
- package/dist/services/memory-init-engine-release.js +223 -0
- package/dist/services/memory-init-git-policy.js +165 -0
- package/dist/services/memory-init-lint-capability.js +203 -0
- package/dist/services/memory-init-native-capacity.js +309 -0
- package/dist/services/memory-init-preflight.js +334 -0
- package/dist/services/memory-init-project-bootstrap.js +599 -0
- package/dist/services/memory-init-request-candidate.js +213 -0
- package/dist/services/memory-init-request-input.js +82 -0
- package/dist/services/memory-init-resource-storage.js +125 -0
- package/dist/services/memory-init-run-allocation.js +81 -0
- package/dist/services/memory-init-run-bundle.js +135 -0
- package/dist/services/memory-init-start.js +181 -0
- package/dist/services/memory-init-startup-custody.js +80 -0
- package/dist/services/memory-init-startup-input.js +240 -0
- package/dist/services/memory-init-storage.js +91 -0
- package/dist/services/memory-init-stores.js +359 -0
- package/dist/services/memory-init-worktrunk-capability.js +174 -0
- package/dist/services/memory-layer-publication.js +611 -0
- package/dist/services/memory-layer.js +221 -0
- package/dist/services/memory-lint-config.js +194 -0
- package/dist/services/memory-native-review-copy.js +103 -0
- package/dist/services/memory-normal-flow-entry.js +87 -0
- package/dist/services/memory-ordinary-defaults.js +129 -0
- package/dist/services/memory-preflight-bootstrap-custody.js +115 -0
- package/dist/services/memory-preflight-handler.js +274 -0
- package/dist/services/memory-repair-current-view.js +299 -0
- package/dist/services/memory-repair-document-delta.js +488 -0
- package/dist/services/memory-repair-history.js +496 -0
- package/dist/services/memory-repair-physical-binding.js +194 -0
- package/dist/services/memory-repair-publication-files.js +152 -0
- package/dist/services/memory-repair-publication-intent.js +27 -0
- package/dist/services/memory-repair-publication-plan.js +162 -0
- package/dist/services/memory-repair-publication-source.js +225 -0
- package/dist/services/memory-repair-publication.js +795 -0
- package/dist/services/memory-repair-registration.js +240 -0
- package/dist/services/memory-repair-work-baseline.js +415 -0
- package/dist/services/memory-repair-work-completion.js +117 -0
- package/dist/services/memory-repair-work-custody.js +73 -0
- package/dist/services/memory-repair-work-results.js +108 -0
- package/dist/services/memory-repair-workspace.js +261 -0
- package/dist/services/memory-repair-writer-copy.js +736 -0
- package/dist/services/memory-result-binding.js +239 -0
- package/dist/services/memory-retained-review-authority.js +138 -0
- package/dist/services/memory-retained-review-results.js +70 -0
- package/dist/services/memory-retained-second-baseline.js +102 -0
- package/dist/services/memory-retained-second-repairs.js +162 -0
- package/dist/services/memory-review-acceptance.js +214 -0
- package/dist/services/memory-review-correction-plan.js +206 -0
- package/dist/services/memory-review-handler.js +585 -0
- package/dist/services/memory-review-inputs.js +246 -0
- package/dist/services/memory-review-limits.js +10 -0
- package/dist/services/memory-review-receipt.js +180 -0
- package/dist/services/memory-review-registration.js +117 -0
- package/dist/services/memory-review-work-custody.js +126 -0
- package/dist/services/memory-review-work-input.js +91 -0
- package/dist/services/memory-review-work-results.js +95 -0
- package/dist/services/memory-second-corrected-review-inputs.js +167 -0
- package/dist/services/memory-second-correction-plan.js +77 -0
- package/dist/services/memory-second-repair-baseline.js +91 -0
- package/dist/services/memory-second-repair-launch.js +614 -0
- package/dist/services/memory-second-repair-registration.js +88 -0
- package/dist/services/memory-second-repair-source.js +187 -0
- package/dist/services/memory-second-review-acceptance.js +115 -0
- package/dist/services/memory-source-observations.js +117 -0
- package/dist/services/memory-stage-dispatch.js +65 -0
- package/dist/services/memory-stage-results.js +290 -0
- package/dist/services/memory-tool-probe.js +25 -0
- package/dist/services/memory-upgrade-analysis-handler.js +243 -0
- package/dist/services/memory-upgrade-bootstrap-readiness.js +251 -0
- package/dist/services/memory-upgrade-evidence.js +100 -0
- package/dist/services/memory-upgrade-inventory.js +442 -0
- package/dist/services/memory-upgrade-publication-custody.js +73 -0
- package/dist/services/memory-upgrade-publication.js +413 -0
- package/dist/services/memory-upgrade-selection.js +430 -0
- package/dist/services/memory-upgrade-start.js +60 -0
- package/dist/services/memory-upgrade-storage-readiness.js +340 -0
- package/dist/services/memory-verification-acceptance.js +156 -0
- package/dist/services/memory-verification-checks.js +317 -0
- package/dist/services/memory-verification-receipt.js +160 -0
- package/dist/services/memory-verify-handler.js +137 -0
- package/dist/services/memory-work-result-json.js +12 -0
- package/dist/services/memory-work-result-preparation.js +64 -0
- package/dist/services/merge-queue.js +228 -149
- package/dist/services/merge-server.js +163 -35
- package/dist/services/migrations.js +164 -114
- package/dist/services/model-catalog.js +109 -0
- package/dist/services/model-discovery.js +46 -0
- package/dist/services/native-daemon-history.js +50 -0
- package/dist/services/native-session-control.js +39 -0
- package/dist/services/operator-profiles.js +236 -0
- package/dist/services/physical-startup.js +45 -0
- package/dist/services/plan-runtime.js +26 -11
- package/dist/services/plans.js +28 -25
- package/dist/services/project-registry.js +97 -0
- package/dist/services/project-summary.js +1 -1
- package/dist/services/projects.js +17 -87
- package/dist/services/prompt-delivery.js +71 -0
- package/dist/services/prompts.js +28 -17
- package/dist/services/protocols.js +163 -87
- package/dist/services/provider-capacity.js +455 -0
- package/dist/services/recovery-observation-budget.js +121 -0
- package/dist/services/recovery-snapshot-database.js +588 -0
- package/dist/services/release-archive-producer.js +52 -0
- package/dist/services/release-archive.js +143 -0
- package/dist/services/release-cache.js +150 -0
- package/dist/services/release-compatibility.js +18 -0
- package/dist/services/release-download.js +67 -0
- package/dist/services/release-envelope.js +104 -0
- package/dist/services/release-manifest.js +45 -0
- package/dist/services/release-payload.js +114 -0
- package/dist/services/release-producer.js +63 -0
- package/dist/services/release-provenance.js +160 -0
- package/dist/services/release-registry.js +66 -0
- package/dist/services/release-source.js +194 -0
- package/dist/services/repair-intents.js +31 -0
- package/dist/services/repair-receipt.js +13 -0
- package/dist/services/review-copy.js +165 -0
- package/dist/services/review-deferral.js +23 -0
- package/dist/services/run-control-receipt.js +56 -0
- package/dist/services/run-control-worker.js +428 -0
- package/dist/services/run-control.js +1092 -0
- package/dist/services/run-controller-adapter.js +400 -0
- package/dist/services/run-controller-capture.js +186 -0
- package/dist/services/run-controller-process.js +205 -0
- package/dist/services/run-controller-recovery.js +295 -0
- package/dist/services/run-controller-state.js +97 -0
- package/dist/services/run-controller.js +1326 -0
- package/dist/services/run-engine-bindings.js +1 -1
- package/dist/services/run-fork.js +147 -0
- package/dist/services/run-health.js +149 -0
- package/dist/services/run-observations.js +152 -0
- package/dist/services/run-projection.js +22 -64
- package/dist/services/run-recovery-guard.js +107 -0
- package/dist/services/run-recovery-runtime.js +112 -0
- package/dist/services/run-recovery.js +456 -0
- package/dist/services/run-start-custody.js +189 -0
- package/dist/services/runs.js +1023 -246
- package/dist/services/runtime-budget.js +446 -0
- package/dist/services/runtime-command.js +43 -0
- package/dist/services/runtime-scope-capture.js +52 -0
- package/dist/services/runtime-scope-control.js +468 -0
- package/dist/services/runtime-scope-resume.js +605 -0
- package/dist/services/runtime-scope-stop.js +211 -0
- package/dist/services/runtime-scope-worker.js +314 -0
- package/dist/services/runtime-service.js +19 -14
- package/dist/services/schema-validation.js +10 -8
- package/dist/services/scope-writer-cleanup.js +27 -0
- package/dist/services/sessions.js +36 -91
- package/dist/services/setup.js +427 -0
- package/dist/services/stage-blocker.js +17 -7
- package/dist/services/stage-context.js +193 -19
- package/dist/services/stage-handlers.js +89 -0
- package/dist/services/stage-lifecycle.js +152 -116
- package/dist/services/stage-pause.js +202 -91
- package/dist/services/stage-report-renderer.js +234 -6
- package/dist/services/stage-work-graph.js +52 -0
- package/dist/services/tooling.js +41 -30
- package/dist/services/usage.js +49 -101
- package/dist/services/verification-matrix.js +393 -0
- package/dist/services/version-status.js +1 -1
- package/dist/services/vnext-code-review.js +235 -88
- package/dist/services/vnext-code.js +327 -226
- package/dist/services/vnext-contracts.js +10 -1
- package/dist/services/vnext-execution-profile.js +80 -5
- package/dist/services/vnext-fanout.js +355 -21
- package/dist/services/vnext-merge.js +332 -111
- package/dist/services/vnext-plan-review.js +287 -132
- package/dist/services/vnext-plan.js +228 -104
- package/dist/services/vnext-protocolize.js +533 -95
- package/dist/services/vnext-specify.js +123 -81
- package/dist/services/vnext-workspace-policy.js +7 -2
- package/dist/services/work-registry.js +1706 -200
- package/dist/services/workspace-bootstrap.js +53 -15
- package/dist/services/workspace-files.js +85 -0
- package/dist/services/worktrees.js +105 -48
- package/dist/shared/entity-references.js +1 -0
- package/dist/shared/errors.js +27 -0
- package/dist/storage/agent-output.js +26 -0
- package/dist/storage/data-preservation.js +65 -0
- package/dist/storage/database.js +930 -40
- package/dist/storage/foreign-key-transaction.js +39 -0
- package/dist/storage/immutable-input.js +45 -0
- package/dist/storage/import-guard.js +159 -0
- package/dist/storage/memory-upgrade-schema-bootstrap.js +38 -0
- package/dist/storage/native-cleanup.js +52 -0
- package/dist/storage/paired-writer-migration.js +424 -0
- package/dist/storage/paths.js +17 -4
- package/dist/storage/projection-lock.js +73 -0
- package/dist/storage/work-references.js +23 -0
- package/dist/storage/writer-contract.js +96 -0
- package/dist/storage/writer-migration.js +346 -0
- package/dist/storage/writer-restore.js +217 -0
- package/package.json +50 -13
- package/tools/create-canon-release.mjs +33 -0
- package/tools/probe-codex-history.mjs +37 -0
- package/tools/produce-hybrid-release.mjs +41 -0
- package/tools/repair-paused-run-status.mjs +84 -0
|
@@ -0,0 +1,1175 @@
|
|
|
1
|
+
import { ObservationClock, observedTimeout, validateObservationDuration } from "./observation-clock.mjs";
|
|
2
|
+
import { ToolObservations, acpToolObservation, zcodePhysicalSession } from "./tool-observations.mjs";
|
|
3
|
+
import { hookBudget, invokeNativeHook } from "./native-hook-command.mjs";
|
|
4
|
+
import { parseLifecycleCommand, continuationCommand, commandOption } from "./lifecycle-command.mjs";
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
6
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
7
|
+
import { createReadStream } from 'node:fs';
|
|
8
|
+
import { publishSetup, SETUP_STARTUP_MS } from './setup-progress.mjs';
|
|
9
|
+
import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { closeBridgeProcess } from "./managed-daemon.mjs";
|
|
12
|
+
import { errorRecord, isProvenShellCorrection } from "./operation-errors.mjs";
|
|
13
|
+
import { checkObservedProfile, observeModel, observedFields } from "./model-observations.mjs";
|
|
14
|
+
import { commandText } from "./process-json.mjs";
|
|
15
|
+
import { readNativeLines, streamNativeDiagnostics, nativeObservationError } from "./native-streams.mjs";
|
|
16
|
+
import { operationProgressReporter } from "./operation-progress.mjs";
|
|
17
|
+
import { safeNativeFailure } from "./native-children.mjs";
|
|
18
|
+
import { classifyProviderError, safeProviderError } from "./provider-overload-policy.mjs";
|
|
19
|
+
|
|
20
|
+
import { readZcodeUsage } from "./zcode-usage.mjs";
|
|
21
|
+
const ZCODE_HARNESS_CONTRACT = "dd-zcode-harness@1";
|
|
22
|
+
export const supportedZcodeHarnessContract = contract => [ZCODE_HARNESS_CONTRACT, "dd-zcode-harness@2"].includes(contract);
|
|
23
|
+
// A deadline is an observation boundary, not permission to replay an ACP
|
|
24
|
+
// request. Keep the original correlation slot briefly so a slow native
|
|
25
|
+
// response (especially cold-start/session creation) can still settle it.
|
|
26
|
+
const LATE_RESPONSE_GRACE_MS = 5_000;
|
|
27
|
+
// Only the owned lifecycle transport can attest a persisted CLI barrier.
|
|
28
|
+
// Model text and arbitrary callback error.details cannot grant correction.
|
|
29
|
+
const correctableLifecycleErrors = new WeakSet();
|
|
30
|
+
|
|
31
|
+
function fail(message, code) { const error = new Error(message); if (code) error.code = code; throw error; }
|
|
32
|
+
function absolute(value, label) { if (!value || !path.isAbsolute(value)) fail(`${label} must be an absolute path`); return path.resolve(value); }
|
|
33
|
+
function text(value, label) { if (typeof value !== "string" || !value.trim()) fail(`${label} is required`); return value.trim(); }
|
|
34
|
+
function now() { return new Date().toISOString(); }
|
|
35
|
+
function responseSummary(message, method = null) {
|
|
36
|
+
const result = message?.result;
|
|
37
|
+
return { request_id: message?.id ?? null, method, has_result: result !== undefined,
|
|
38
|
+
result_keys: result && typeof result === "object" && !Array.isArray(result) ? Object.keys(result).slice(0, 32) : [], error: message?.error ?? null };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function executable(command, args = []) {
|
|
42
|
+
const resolved = command ?? process.env.DD_ZCODE_ACP_BIN ?? "zcode-acp";
|
|
43
|
+
return /\.[cm]?js$/.test(resolved) ? { command: process.execPath, args: [resolved, ...args] } : { command: resolved, args };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function commandOutput(command, args) {
|
|
47
|
+
const target = executable(command, args);
|
|
48
|
+
return await commandText(target.command, target.args, { phase: "native-version" });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function runWithStdin(command, args, stdin, env = {}) {
|
|
52
|
+
const target = executable(command, args);
|
|
53
|
+
return await commandText(target.command, target.args, { input: stdin, env, phase: "usage-ingest" });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
class Journal {
|
|
57
|
+
constructor(file) {
|
|
58
|
+
this.file = file ? absolute(file, "--journal") : null;
|
|
59
|
+
this.pending = this.file ? mkdir(path.dirname(this.file), { recursive: true }) : Promise.resolve();
|
|
60
|
+
this.order = 0;
|
|
61
|
+
this.artifacts = new Set();
|
|
62
|
+
}
|
|
63
|
+
write(kind, payload) {
|
|
64
|
+
if (!this.file) return;
|
|
65
|
+
const line = JSON.stringify({ order: ++this.order, observed_at: now(), kind, payload });
|
|
66
|
+
this.pending = this.pending.then(() => appendFile(this.file, `${line}\n`));
|
|
67
|
+
void this.pending.catch(() => {});
|
|
68
|
+
return this.pending;
|
|
69
|
+
}
|
|
70
|
+
writeInbound(payload, method) {
|
|
71
|
+
if (!this.file || method !== "zcode/session/read" || !payload?.result || typeof payload.result !== "object") return this.write("inbound", payload);
|
|
72
|
+
const raw = JSON.stringify(payload.result);
|
|
73
|
+
const sha256 = createHash("sha256").update(raw).digest("hex");
|
|
74
|
+
const artifactDir = `${this.file}.artifacts`, artifact = path.join(artifactDir, `${sha256}.json`);
|
|
75
|
+
const blobs = new Map();
|
|
76
|
+
const messages = Array.isArray(payload.result.messages) ? payload.result.messages.map(message => {
|
|
77
|
+
const bytes = Buffer.from(JSON.stringify(message));
|
|
78
|
+
const chunks = [];
|
|
79
|
+
for (let offset = 0; offset < bytes.length; offset += 65536) {
|
|
80
|
+
const chunk = bytes.subarray(offset, offset + 65536);
|
|
81
|
+
const hash = createHash("sha256").update(chunk).digest("hex");
|
|
82
|
+
const file = path.join(artifactDir, `${hash}.chunk`);
|
|
83
|
+
blobs.set(file, chunk); chunks.push({ path: path.basename(file), sha256: hash, bytes: chunk.length });
|
|
84
|
+
}
|
|
85
|
+
return { chunks };
|
|
86
|
+
}) : null;
|
|
87
|
+
const manifest = JSON.stringify({ encoding: "dd-zcode/session-read@1", result: { ...payload.result, ...(messages ? { messages } : {}) } });
|
|
88
|
+
const result = { ...payload.result };
|
|
89
|
+
if (Array.isArray(result.messages)) result.messages = { omitted_count: result.messages.length };
|
|
90
|
+
const compact = { ...payload, result, raw_result: { sha256, bytes: Buffer.byteLength(raw), path: artifact, encoding: "dd-zcode/session-read@1" } };
|
|
91
|
+
const line = JSON.stringify({ order: ++this.order, observed_at: now(), kind: "inbound", payload: compact });
|
|
92
|
+
this.pending = this.pending.then(async () => {
|
|
93
|
+
await mkdir(artifactDir, { recursive: true });
|
|
94
|
+
for (const [file, bytes] of [...blobs, [artifact, manifest]]) {
|
|
95
|
+
if (this.artifacts.has(file)) continue;
|
|
96
|
+
try { await writeFile(file, bytes, { flag: "wx" }); }
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (error?.code !== "EEXIST") throw error;
|
|
99
|
+
if (!Buffer.from(await readFile(file)).equals(Buffer.from(bytes))) throw new Error(`Journal artifact conflict: ${file}`);
|
|
100
|
+
}
|
|
101
|
+
this.artifacts.add(file);
|
|
102
|
+
}
|
|
103
|
+
await appendFile(this.file, `${line}\n`);
|
|
104
|
+
});
|
|
105
|
+
void this.pending.catch(() => {});
|
|
106
|
+
return this.pending;
|
|
107
|
+
}
|
|
108
|
+
async flush() { await this.pending; }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Reconstruct a retained read, verifying every chunk and the original result. */
|
|
112
|
+
export async function readSessionJournalArtifact(reference) {
|
|
113
|
+
const stored = JSON.parse(await readFile(reference.path, "utf8"));
|
|
114
|
+
const result = reference.encoding === "dd-zcode/session-read@1" ? stored.result : stored;
|
|
115
|
+
if (reference.encoding === "dd-zcode/session-read@1" && Array.isArray(result.messages)) {
|
|
116
|
+
const messages = [];
|
|
117
|
+
for (const message of result.messages) {
|
|
118
|
+
const parts = [];
|
|
119
|
+
for (const chunk of message.chunks) {
|
|
120
|
+
if (chunk.path !== `${chunk.sha256}.chunk` || !/^[a-f0-9]{64}$/.test(chunk.sha256)) throw new Error("Invalid journal chunk reference");
|
|
121
|
+
const bytes = await readFile(path.join(path.dirname(reference.path), chunk.path));
|
|
122
|
+
if (bytes.length !== chunk.bytes || createHash("sha256").update(bytes).digest("hex") !== chunk.sha256) throw new Error("Journal chunk integrity mismatch");
|
|
123
|
+
parts.push(bytes);
|
|
124
|
+
}
|
|
125
|
+
messages.push(JSON.parse(Buffer.concat(parts).toString("utf8")));
|
|
126
|
+
}
|
|
127
|
+
result.messages = messages;
|
|
128
|
+
}
|
|
129
|
+
const raw = JSON.stringify(result);
|
|
130
|
+
if (Buffer.byteLength(raw) !== reference.bytes || createHash("sha256").update(raw).digest("hex") !== reference.sha256) throw new Error("Journal result integrity mismatch");
|
|
131
|
+
return result;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export class AcpBridge {
|
|
135
|
+
constructor(options) {
|
|
136
|
+
this.options = options;
|
|
137
|
+
this.nextId = 1;
|
|
138
|
+
this.pending = new Map();
|
|
139
|
+
this.allocationObservers = new Map();
|
|
140
|
+
this.lateResponses = new Map();
|
|
141
|
+
this.expiredRequests = new Map();
|
|
142
|
+
this.sessionActivity = new Map();
|
|
143
|
+
this.notifications = Promise.resolve();
|
|
144
|
+
this.terminalOutcomes = Promise.resolve();
|
|
145
|
+
this.terminalRequests = new Map();
|
|
146
|
+
this.connectionId = randomUUID();
|
|
147
|
+
this.receivedSequence = 0;
|
|
148
|
+
this.lifecycleNotifications = new Set();
|
|
149
|
+
this.notificationError = null;
|
|
150
|
+
this.bridgeStderr = "";
|
|
151
|
+
this.toolObservations = new ToolObservations();
|
|
152
|
+
this.assistantText = new Map();
|
|
153
|
+
this.journal = new Journal(options.journal);
|
|
154
|
+
this.modelSessions = new Map(); this.modelProfiles = new Map();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
configure(options) { this.options = { ...this.options, ...options }; }
|
|
158
|
+
registerModelSession(adapterId, sessionId, requested, parentSessionId = null, armed = true, bindCurrentRequest = false) {
|
|
159
|
+
const parentAdapter = parentSessionId ? [...this.modelSessions].find(([key, value]) => key === parentSessionId || value.sessionId === parentSessionId)?.[0] : null;
|
|
160
|
+
const parentRequest = this.activityOwner(parentAdapter);
|
|
161
|
+
const prior = this.modelSessions.get(adapterId);
|
|
162
|
+
this.modelSessions.set(adapterId, { sessionId, requested, parentSessionId, armed, parentRequestId: prior && !bindCurrentRequest ? prior.parentRequestId ?? null : parentRequest?.id ?? null });
|
|
163
|
+
}
|
|
164
|
+
async recordModel(adapterId, observed, source) {
|
|
165
|
+
const identity = this.modelSessions.get(adapterId); if (!identity) return;
|
|
166
|
+
this.modelProfiles.set(identity.sessionId, observed);
|
|
167
|
+
await observeModel({ journal: this.options.journal, harness: this.options.clientInfo?.name === "dd-grok" ? "grok-acp" : "zcode-acp",
|
|
168
|
+
sessionId: identity.sessionId, parentSessionId: identity.parentSessionId, requested: identity.requested, observed, source: typeof source === "string" ? { channel: source, scope: "configured", raw_model: observed.raw_model ?? observed.model, raw_provider: observed.provider } : source });
|
|
169
|
+
}
|
|
170
|
+
async observeModelUpdate(message) {
|
|
171
|
+
const update = message.params?.update;
|
|
172
|
+
if (update?.sessionUpdate !== "config_option_update" || !Array.isArray(update.configOptions)) return;
|
|
173
|
+
const value = category => update.configOptions.find(option => option.type === "select" && (option.category === category || option.id === category))?.currentValue ?? null;
|
|
174
|
+
const raw = value("model");
|
|
175
|
+
const model = this.options.clientInfo?.name === "dd-grok" ? { model: raw } : zcodeModelSelector(raw);
|
|
176
|
+
const observed = { ...model, reasoning: value("thought_level"), mode: value("mode") };
|
|
177
|
+
const identity = this.modelSessions.get(message.params.sessionId); if (!identity?.armed) return;
|
|
178
|
+
checkObservedProfile(identity.requested, observed);
|
|
179
|
+
await this.recordModel(message.params.sessionId, observed, "acp.config_option_update");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async start() {
|
|
183
|
+
const target = executable(this.options.bin, this.options.commandArgs ?? ["server"]);
|
|
184
|
+
const cwd = this.options.cwd ? absolute(this.options.cwd, "--cwd") : process.cwd();
|
|
185
|
+
this.child = spawn(target.command, target.args, { cwd, detached: process.platform !== "win32", stdio: ["pipe", "pipe", "pipe"], env: { ...process.env, ...(this.options.env ?? {}) } });
|
|
186
|
+
this.diagnostics = streamNativeDiagnostics(this.child.stderr, async chunk => { this.bridgeStderr = (this.bridgeStderr + chunk).slice(-65_536); await this.journal.write("bridge_stderr", { text: chunk }); }, error => this.rejectAll(error));
|
|
187
|
+
this.child.stdin?.on("error", (error) => {
|
|
188
|
+
error.details = { system_code: error.code ?? null };
|
|
189
|
+
error.code = "bridge_pipe_broken";
|
|
190
|
+
this.rejectAll(error);
|
|
191
|
+
});
|
|
192
|
+
this.child.on("error", (error) => { error.code ??= "bridge_exited"; this.rejectAll(error); });
|
|
193
|
+
this.child.on("close", code => { void Promise.all([this.nativeDrain, this.diagnostics]).then(() => { const error = new Error(`zcode-acp exited ${code}${this.bridgeStderr.trim() ? `: ${this.bridgeStderr.trim()}` : ""}`); error.code = "bridge_exited"; this.rejectAll(error); }); });
|
|
194
|
+
this.nativeDrain = readNativeLines(this.child.stdout, async line => { this.receive(line); await this.journal.flush(); }, error => this.rejectAll(error));
|
|
195
|
+
if (this.options.onSpawn) await this.options.onSpawn(this.child);
|
|
196
|
+
publishSetup('probe_started', 'native-initialize');
|
|
197
|
+
const initialized = await this.request("initialize", {
|
|
198
|
+
protocolVersion: 1,
|
|
199
|
+
clientCapabilities: { fs: { readTextFile: false, writeTextFile: false }, elicitation: { form: {} } },
|
|
200
|
+
clientInfo: this.options.clientInfo ?? { name: "dd-zcode", version: "0.1.0" },
|
|
201
|
+
}, this.options.initializeTimeoutMs ?? SETUP_STARTUP_MS);
|
|
202
|
+
publishSetup('native_initialized', 'native-initialize');
|
|
203
|
+
return initialized;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
receive(line) {
|
|
207
|
+
let message;
|
|
208
|
+
try { message = JSON.parse(line); } catch { this.journal.write("malformed", { line }); return; }
|
|
209
|
+
const observation = { connection_id: this.connectionId, sequence: ++this.receivedSequence, productive: isProductiveSessionNotification(message) };
|
|
210
|
+
const request = message.id !== undefined ? this.pending.get(message.id) : null;
|
|
211
|
+
const expired = message.id !== undefined ? this.expiredRequests.get(message.id) : null;
|
|
212
|
+
const isResponse = message.id !== undefined && !message.method;
|
|
213
|
+
const rejected = isResponse && this.rejectedHookRequests?.get(message.id);
|
|
214
|
+
if (rejected || isResponse && (request?.method === "session/prompt" || this.terminalRequests.has(message.id))) {
|
|
215
|
+
const owner = rejected ?? request ?? this.terminalRequests.get(message.id);
|
|
216
|
+
const terminal = { request_id: message.id, method: owner.method, session_id: owner.sessionId, terminal_response: message,
|
|
217
|
+
connection_id: this.connectionId, operation_id: owner.operationId ?? null, generation: owner.generation ?? null, native_sequence: observation.sequence };
|
|
218
|
+
this.terminalOutcomes = this.terminalOutcomes.then(async () => {
|
|
219
|
+
await this.journal.write(rejected ? "native_terminal_after_hook_failure" : "native_terminal_outcome", terminal);
|
|
220
|
+
await this.journal.flush();
|
|
221
|
+
await this.options.onTerminalOutcome?.(terminal);
|
|
222
|
+
this.terminalRequests.set(message.id, owner);
|
|
223
|
+
while (this.terminalRequests.size > 128) this.terminalRequests.delete(this.terminalRequests.keys().next().value);
|
|
224
|
+
if (rejected) this.rejectedHookRequests.delete(message.id);
|
|
225
|
+
});
|
|
226
|
+
void this.terminalOutcomes.catch(storage => { this.terminalProjectionError ??= storage; if (rejected) rejected.error.cleanup_error ??= errorRecord(storage); });
|
|
227
|
+
if (rejected) {
|
|
228
|
+
rejected.error.details = { ...rejected.error.details, native_terminal_after_hook_failure: terminal };
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (!isResponse || request) this.journal.writeInbound(message, request?.method);
|
|
232
|
+
if (typeof message.params?.sessionId === "string" && isProductiveSessionNotification(message)) this.observeActivity(message.params.sessionId, message.params.update);
|
|
233
|
+
const update = message.params?.update;
|
|
234
|
+
// ZCode emits this as `session/update`; Grok Build wraps the same ACP
|
|
235
|
+
// payload in `_x.ai/session_notification`. Preserve either provider's
|
|
236
|
+
// native error before its generic JSON-RPC terminal response arrives.
|
|
237
|
+
if (typeof message.params?.sessionId === "string" && update?.sessionUpdate === "retry_state") {
|
|
238
|
+
for (const pending of this.pending.values()) {
|
|
239
|
+
if (pending.method === "session/prompt" && pending.sessionId === message.params.sessionId) {
|
|
240
|
+
pending.nativeRetryActive = update.type !== "failed";
|
|
241
|
+
if (update.type === "failed") pending.providerError = { ...update, observed_at: now(), provider_session_id: pending.sessionId, source: message.method };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (message.method === "session/update" && message.params?.update?.sessionUpdate === "agent_message_chunk" && message.params.update.content?.type === "text") {
|
|
246
|
+
const sessionId = message.params.sessionId;
|
|
247
|
+
this.assistantText.set(sessionId, `${this.assistantText.get(sessionId) ?? ""}${message.params.update.content.text ?? ""}`);
|
|
248
|
+
}
|
|
249
|
+
if (message.method === "zcode/session/allocated") {
|
|
250
|
+
const identity = message.params;
|
|
251
|
+
const observer = this.allocationObservers.get(identity?.adapter_session_id);
|
|
252
|
+
if (observer && typeof identity?.provider_session_id === "string" && identity.provider_session_id.length > 0) {
|
|
253
|
+
this.allocationObservers.delete(identity.adapter_session_id);
|
|
254
|
+
this.notifications = this.notifications.then(() => observer(identity));
|
|
255
|
+
void this.notifications.catch(error => { this.notificationError ??= error; });
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
this.observeToolCall(message);
|
|
259
|
+
if (message.method === "session/update" && this.options.onLifecycleNotification) {
|
|
260
|
+
const observer = this.options.onLifecycleNotification;
|
|
261
|
+
const pending = Promise.resolve().then(() => observer(message)).catch(async error => {
|
|
262
|
+
// A validated no-effect rejection is returned through the invocation
|
|
263
|
+
// ledger. It is evidence, not a poisoned ACP Session.
|
|
264
|
+
const current = this.options.onLifecycleNotification === observer;
|
|
265
|
+
const correctable = current && correctableLifecycleErrors.has(error);
|
|
266
|
+
if (current && !correctable) {
|
|
267
|
+
this.lifecycleFailure ??= this.notificationError?.code === "profile_integrity_violation" ? this.notificationError : error;
|
|
268
|
+
this.notificationError = this.lifecycleFailure;
|
|
269
|
+
}
|
|
270
|
+
// A missing/foreign authority is not a recoverable CLI validation
|
|
271
|
+
// result. Stop admitting productive requests immediately; the managed
|
|
272
|
+
// controller still owns physical stop/drain and evidence capture.
|
|
273
|
+
if (current && !correctable) this.rejectAll(this.notificationError);
|
|
274
|
+
try {
|
|
275
|
+
await hookBudget(1_000, null).run(() => this.journal.write("notification_failure", { ...errorRecord(error),
|
|
276
|
+
session_id: message.params?.sessionId ?? null, tool_call_id: update?.toolCallId ?? null, event_time: now(), delivery_stage: "lifecycle_receipt", completeness: "incomplete" }), "failure_persistence");
|
|
277
|
+
} catch (persistenceError) {
|
|
278
|
+
if (current) this.notificationError ??= persistenceError;
|
|
279
|
+
process.stderr.write(`${JSON.stringify({ code: error.code, message: error.message, persistence_error: persistenceError.message })}\n`);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
this.lifecycleNotifications.add(pending);
|
|
283
|
+
void pending.then(() => this.lifecycleNotifications.delete(pending), () => this.lifecycleNotifications.delete(pending));
|
|
284
|
+
}
|
|
285
|
+
if (message.id !== undefined && message.method) { void this.answer(message).catch((error) => this.rejectAll(error)); return; }
|
|
286
|
+
if (message.id !== undefined) {
|
|
287
|
+
const pending = request ?? this.pending.get(message.id);
|
|
288
|
+
if (!pending) {
|
|
289
|
+
this.lateResponses.set(message.id, message);
|
|
290
|
+
while (this.lateResponses.size > 128) this.lateResponses.delete(this.lateResponses.keys().next().value);
|
|
291
|
+
this.journal.write("late_response", responseSummary(message, expired?.method ?? null));
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
clearTimeout(pending.timer); if (pending.idleTimer) clearInterval(pending.idleTimer); this.pending.delete(message.id);
|
|
295
|
+
if (pending.timedOut) this.journal.write("late_response", { ...responseSummary(message, pending.method), grace_ms: LATE_RESPONSE_GRACE_MS });
|
|
296
|
+
if (message.error) {
|
|
297
|
+
const data = message.error.data && typeof message.error.data === "object" && !Array.isArray(message.error.data) ? message.error.data : null;
|
|
298
|
+
const details = data?.details ?? data?.message ?? data ?? null;
|
|
299
|
+
const provider = pending.providerError;
|
|
300
|
+
const providerDetails = provider?.message ?? null;
|
|
301
|
+
const messageText = [message.error.message, details, providerDetails].filter(Boolean).map(value => typeof value === "string" ? value : JSON.stringify(value)).join(": ") || JSON.stringify(message.error);
|
|
302
|
+
const error = new Error(messageText);
|
|
303
|
+
const nativeCode = typeof data?.code === "string" ? data.code : typeof data?.native_code === "string" ? data.native_code : null;
|
|
304
|
+
const unknown = ["native_timeout", "native_backend_dead", "native_backend_pipe_broken", "native_outcome_unknown"].includes(nativeCode);
|
|
305
|
+
const nativeFailure = { ...(provider ?? {}), ...(data ?? {}),
|
|
306
|
+
code: [provider?.code, data?.code, data?.native_code].filter(value => typeof value === "string").join(" "), message: messageText };
|
|
307
|
+
const category = pending.method === "session/prompt" ? classifyProviderError(nativeFailure) : "provider_failed";
|
|
308
|
+
error.code = unknown ? nativeCode : category === "provider_failed" ? "acp_request_failed" : category;
|
|
309
|
+
error.retryable = ["provider_rate_limited", "provider_capacity_transient"].includes(error.code);
|
|
310
|
+
error.details = { acp_code: message.error.code ?? null,
|
|
311
|
+
observed_at: provider?.observed_at ?? now(), provider_session_id: pending.sessionId ?? null, source: "acp.error_response", request_id: message.id,
|
|
312
|
+
terminal_response: { method: pending.method, session_id: pending.sessionId, request_id: message.id },
|
|
313
|
+
...(pending.method === "session/prompt" ? { native_source: "acp.error_response", terminal_status: "failed", turn_id: String(message.id), native_turn_id: String(message.id), provider_error: safeProviderError(nativeFailure),
|
|
314
|
+
native_retry_active: pending.nativeRetryActive === true, will_retry: nativeFailure.willRetry === true,
|
|
315
|
+
native_turn_items: { observed: this.toolSummary(pending.sessionId).completeness === "complete", possible_effects: this.toolSummary(pending.sessionId).total > 0, pending: this.toolSummary(pending.sessionId).pending > 0 } } : {}),
|
|
316
|
+
...(data ? { native: data } : {}), ...(details ? { provider_details: details } : {}), ...(providerDetails ? { provider_update: provider } : {}) };
|
|
317
|
+
pending.reject(error);
|
|
318
|
+
}
|
|
319
|
+
else { pending.resolve(message.result ?? {}); }
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
if (message.method) this.notifications = this.notifications
|
|
323
|
+
.then(async () => { await this.observeModelUpdate(message); await this.options.onNotification?.(message, observation); })
|
|
324
|
+
.catch(async (error) => {
|
|
325
|
+
const failure = { code: error.code ?? "notification_observation_failed", message: error.message, ...(error.details ? { details: error.details } : {}), session_id: message.params?.sessionId ?? null, tool_call_id: message.params?.update?.toolCallId ?? null, event_time: now(), delivery_stage: "acp_evidence", completeness: "incomplete" };
|
|
326
|
+
try { await this.journal.write("notification_failure", failure); }
|
|
327
|
+
catch (persistenceError) { process.stderr.write(`${JSON.stringify({ ...failure, persistence_error: persistenceError.message })}\n`); }
|
|
328
|
+
// Evidence loss does not retroactively revoke a committed native hook.
|
|
329
|
+
// Permission/profile integrity is a separate, still-fatal contract.
|
|
330
|
+
if (error.code === "profile_integrity_violation") { this.notificationError ??= error; this.rejectAll(error); }
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
observeToolCall(message) {
|
|
335
|
+
const rootSessionId = this.modelSessions.get(message.params?.sessionId)?.sessionId;
|
|
336
|
+
this.toolObservations.add(acpToolObservation(message, { harness: this.options.observationHarness ?? "zcode-acp", rootSessionId }));
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
observeActivity(sessionId, update) {
|
|
340
|
+
const observedAt = Date.now(), seen = new Set();
|
|
341
|
+
let adapterId = sessionId;
|
|
342
|
+
// ACP has no Turn ID: scope direct notifications to the active request on
|
|
343
|
+
// this connection, and descendants to their parent's dispatch generation.
|
|
344
|
+
const owner = this.activityOwner(adapterId);
|
|
345
|
+
if (!owner) return;
|
|
346
|
+
if (["tool_call", "tool_call_update"].includes(update?.sessionUpdate)) {
|
|
347
|
+
const toolId = update.toolCallId;
|
|
348
|
+
if (!toolId) return;
|
|
349
|
+
owner.toolProgress ??= new Map();
|
|
350
|
+
const signature = JSON.stringify({ status: update.status, kind: update.kind, title: update.title, content: update.content, rawInput: update.rawInput, rawOutput: update.rawOutput });
|
|
351
|
+
const key = `${sessionId}:${toolId}`, previous = owner.toolProgress.get(key);
|
|
352
|
+
if (previous === signature) return;
|
|
353
|
+
// An empty update is only a replay/metadata snapshot, not tool work.
|
|
354
|
+
if (update.sessionUpdate === "tool_call_update" && !update.status && !update.content?.length && update.rawInput === undefined && update.rawOutput === undefined) return;
|
|
355
|
+
if (owner.toolProgress.size >= 1_024 && !owner.toolProgress.has(key)) return;
|
|
356
|
+
owner.toolProgress.set(key, signature);
|
|
357
|
+
}
|
|
358
|
+
while (adapterId && !seen.has(adapterId)) {
|
|
359
|
+
seen.add(adapterId);
|
|
360
|
+
const pending = [...this.pending.values()].find(value => value.method === "session/prompt" && value.sessionId === adapterId);
|
|
361
|
+
if (pending) {
|
|
362
|
+
pending.progress = { cursor: (pending.progress?.cursor ?? 0) + 1, observed_at: observedAt };
|
|
363
|
+
const rejectObservation = cause => {
|
|
364
|
+
if (this.pending.get(pending.id) !== pending) return;
|
|
365
|
+
clearTimeout(pending.timer); if (pending.idleTimer) clearInterval(pending.idleTimer);
|
|
366
|
+
this.pending.delete(pending.id); pending.reject(nativeObservationError(cause));
|
|
367
|
+
};
|
|
368
|
+
void pending.publish("native_progress", { native_request_id: String(pending.id), session_id: adapterId, observed_at: new Date(observedAt).toISOString() }).catch(rejectObservation);
|
|
369
|
+
this.sessionActivity.set(adapterId, observedAt);
|
|
370
|
+
void Promise.resolve(this.options.onActivity?.({ sessionId: adapterId, requestId: pending.id, progress: pending.progress })).catch(rejectObservation);
|
|
371
|
+
}
|
|
372
|
+
const child = this.modelSessions.get(adapterId), parent = child?.parentSessionId;
|
|
373
|
+
const next = parent ? [...this.modelSessions].find(([key, value]) => key === parent || value.sessionId === parent)?.[0] : null;
|
|
374
|
+
const parentPending = this.activityOwner(next);
|
|
375
|
+
adapterId = parentPending?.id === child?.parentRequestId ? next : null;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
activityOwner(sessionId) {
|
|
380
|
+
const path = [], seen = new Set();
|
|
381
|
+
while (sessionId && !seen.has(sessionId)) {
|
|
382
|
+
seen.add(sessionId);
|
|
383
|
+
const pending = [...this.pending.values()].find(value => value.method === "session/prompt" && value.sessionId === sessionId);
|
|
384
|
+
if (pending) return path.every(child => child.parentRequestId === pending.id) ? pending : null;
|
|
385
|
+
const identity = this.modelSessions.get(sessionId);
|
|
386
|
+
if (!identity?.parentSessionId) return null;
|
|
387
|
+
path.push(identity);
|
|
388
|
+
sessionId = [...this.modelSessions].find(([key, value]) => key === identity.parentSessionId || value.sessionId === identity.parentSessionId)?.[0];
|
|
389
|
+
}
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
toolSummary(sessionId) {
|
|
394
|
+
return this.toolObservations.summary(this.modelSessions.get(sessionId)?.sessionId ?? sessionId);
|
|
395
|
+
}
|
|
396
|
+
toolCursor() { return this.toolObservations.cursor(); }
|
|
397
|
+
assistantTextCursor(sessionId) { return (this.assistantText.get(sessionId) ?? "").length; }
|
|
398
|
+
assistantTextSince(sessionId, cursor) { return (this.assistantText.get(sessionId) ?? "").slice(cursor); }
|
|
399
|
+
toolSummarySince(cursor, sessionId) {
|
|
400
|
+
return this.toolObservations.summary(this.modelSessions.get(sessionId)?.sessionId ?? sessionId, cursor);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
async answer(message) {
|
|
404
|
+
await this.notifications;
|
|
405
|
+
let result;
|
|
406
|
+
if (message.method === "session/request_permission") {
|
|
407
|
+
const options = message.params?.options ?? [];
|
|
408
|
+
const selected = this.options.permission === "allow" && !this.notificationError
|
|
409
|
+
? options.find((item) => item.optionId === "allow_once") ?? options[0]
|
|
410
|
+
: options.find((item) => item.optionId === "deny");
|
|
411
|
+
result = selected ? { outcome: { outcome: "selected", optionId: selected.optionId } } : { outcome: { outcome: "cancelled" } };
|
|
412
|
+
} else if (message.method === "elicitation/create") {
|
|
413
|
+
result = this.options.answers ? { action: "accept", content: this.options.answers } : { action: "decline", reason: "dd-zcode has no declared answers" };
|
|
414
|
+
} else {
|
|
415
|
+
this.send({ jsonrpc: "2.0", id: message.id, error: { code: -32601, message: `unsupported client request: ${message.method}` } });
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
this.journal.write("interaction", { method: message.method, params: message.params, result });
|
|
419
|
+
this.send({ jsonrpc: "2.0", id: message.id, result });
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
request(method, params = {}, timeoutMs = 30_000, { idleSessionId = null, idleTimeoutMs = null, inactivityMode = "reject" } = {}) {
|
|
423
|
+
if (method === "session/prompt" && (this.rejectedHookRequests?.size ?? 0) + [...this.pending.values()].filter(request => request.method === "session/prompt").length >= 32) return Promise.reject(Object.assign(new Error("Unreconciled native terminal evidence limit reached"), { code: "native_terminal_evidence_pending" }));
|
|
424
|
+
if (timeoutMs !== null) validateObservationDuration(timeoutMs);
|
|
425
|
+
if (idleTimeoutMs !== null) validateObservationDuration(idleTimeoutMs);
|
|
426
|
+
if ((this.lifecycleFailure || this.notificationError?.code === "profile_integrity_violation") && ["session/new", "session/prompt", "session/fork"].includes(method)) return Promise.reject(this.lifecycleFailure ?? this.notificationError);
|
|
427
|
+
if (this.inactivityError && ["session/new", "session/prompt", "session/fork"].includes(method)) return Promise.reject(this.inactivityError);
|
|
428
|
+
const id = this.nextId++;
|
|
429
|
+
return new Promise((resolve, reject) => {
|
|
430
|
+
const finish = (error) => {
|
|
431
|
+
const pending = this.pending.get(id); if (!pending) return;
|
|
432
|
+
clearTimeout(pending.timer); if (pending.idleTimer) clearInterval(pending.idleTimer); this.pending.delete(id); reject(error);
|
|
433
|
+
};
|
|
434
|
+
// Productive Turns use the sliding session-activity timeout below. A
|
|
435
|
+
// second wall-clock timeout would kill an active, long-running Turn.
|
|
436
|
+
const timer = Number.isFinite(timeoutMs) && timeoutMs > 0
|
|
437
|
+
? observedTimeout(() => {
|
|
438
|
+
const pending = this.pending.get(id);
|
|
439
|
+
if (!pending || pending.timedOut) return;
|
|
440
|
+
pending.timedOut = true;
|
|
441
|
+
const timeoutError = Object.assign(new Error(`${method} timed out`), {
|
|
442
|
+
code: "rpc_timeout",
|
|
443
|
+
details: { method, request_id: id, session_id: params.sessionId ?? null, timeout_ms: timeoutMs, grace_ms: LATE_RESPONSE_GRACE_MS },
|
|
444
|
+
});
|
|
445
|
+
this.journal.write("request_deadline", timeoutError.details);
|
|
446
|
+
pending.timer = observedTimeout(() => {
|
|
447
|
+
this.expiredRequests.set(id, { method, sessionId: params.sessionId ?? null });
|
|
448
|
+
while (this.expiredRequests.size > 128) this.expiredRequests.delete(this.expiredRequests.keys().next().value);
|
|
449
|
+
finish(timeoutError);
|
|
450
|
+
}, LATE_RESPONSE_GRACE_MS, { progress: () => pending.progress });
|
|
451
|
+
}, timeoutMs, { progress: () => this.pending.get(id)?.progress })
|
|
452
|
+
: null;
|
|
453
|
+
const validIdleTimeout = Number.isFinite(idleTimeoutMs) && idleTimeoutMs > 0 ? idleTimeoutMs : null;
|
|
454
|
+
if (idleSessionId) this.sessionActivity.set(idleSessionId, Date.now());
|
|
455
|
+
const clock = validIdleTimeout ? new ObservationClock({ timeoutMs: validIdleTimeout,
|
|
456
|
+
onGap: gap => this.journal.write("observation_gap", { ...gap, method, request_id: id, session_id: idleSessionId }) }) : null;
|
|
457
|
+
let checking = false, quietCursor = null;
|
|
458
|
+
const checkIdle = async () => {
|
|
459
|
+
const pending = this.pending.get(id);
|
|
460
|
+
if (!pending || checking || !clock.sample(pending.progress)) return;
|
|
461
|
+
const cursor = pending.progress?.cursor ?? 0;
|
|
462
|
+
if (quietCursor === cursor) return;
|
|
463
|
+
quietCursor = cursor; checking = true;
|
|
464
|
+
const lastActivity = this.sessionActivity.get(idleSessionId) ?? pending.dispatchedAt;
|
|
465
|
+
try {
|
|
466
|
+
this.journal.write("inactivity_detected", { method, request_id: id, session_id: idleSessionId, last_activity_at: new Date(lastActivity).toISOString(), idle_timeout_ms: validIdleTimeout, observed_at: now() });
|
|
467
|
+
if (inactivityMode === "diagnostic") await this.options.onInactivity?.({ method, requestId: id, sessionId: idleSessionId, lastActivityAt: lastActivity, idleTimeoutMs: validIdleTimeout, cursor });
|
|
468
|
+
const current = this.pending.get(id);
|
|
469
|
+
if (current !== pending || (current.progress?.cursor ?? 0) !== cursor || !clock.sample(current.progress)) return;
|
|
470
|
+
this.expiredRequests.set(id, { method, sessionId: pending.sessionId });
|
|
471
|
+
finish(Object.assign(new Error(`${method} native outcome cannot be confirmed after ${validIdleTimeout}ms of silence`), { code: clock.observationLost ? "operation_observation_lost" : "subject_liveness_timeout", details: { request_id: id, session_id: idleSessionId, last_activity_at: new Date(lastActivity).toISOString(), idle_timeout_ms: validIdleTimeout, native_outcome: "unknown" } }));
|
|
472
|
+
} catch (error) {
|
|
473
|
+
const current = this.pending.get(id);
|
|
474
|
+
if (current === pending && (current.progress?.cursor ?? 0) === cursor) finish(Object.assign(new Error("ACP inactivity reconciliation could not confirm the native outcome"), { code: "operation_observation_lost", details: { request_id: id, session_id: idleSessionId, native_outcome: "unknown", observation_error: errorRecord(error) }, cause: error }));
|
|
475
|
+
} finally { checking = false; }
|
|
476
|
+
};
|
|
477
|
+
const idleTimer = validIdleTimeout ? setInterval(() => {
|
|
478
|
+
void checkIdle().catch((error) => {
|
|
479
|
+
// A timer callback is outside the request promise. Preserve the
|
|
480
|
+
// failure for the owning daemon/flush path instead of creating an
|
|
481
|
+
// unhandled rejection that loses the primary request evidence.
|
|
482
|
+
this.inactivityError ??= error;
|
|
483
|
+
void this.journal.write("inactivity_observation_failure", { method, request_id: id, session_id: idleSessionId, error: { code: error?.code ?? "inactivity_observation_failed", message: error?.message ?? String(error) } }).catch(() => {});
|
|
484
|
+
});
|
|
485
|
+
}, Math.max(10, Math.min(1_000, Math.floor(validIdleTimeout / 4)))) : null;
|
|
486
|
+
this.pending.set(id, { id, resolve, reject, timer, idleTimer, method, sessionId: params.sessionId ?? null, operationId: this.dispatchOperationId ?? process.env.DD_EVAL_OPERATION_ID ?? null, generation: this.dispatchGeneration ?? null, dispatchedAt: Date.now(), publish: operationProgressReporter() });
|
|
487
|
+
if (method === "session/prompt") {
|
|
488
|
+
void (async () => {
|
|
489
|
+
const dispatch = { operation_id: this.pending.get(id)?.operationId, session_id: params.sessionId, native_request_id: id,
|
|
490
|
+
connection_id: this.connectionId, generation: this.pending.get(id)?.generation };
|
|
491
|
+
await this.journal.write("native_dispatch_prepared", dispatch);
|
|
492
|
+
await this.journal.flush();
|
|
493
|
+
await this.options.onPromptDispatch?.(dispatch);
|
|
494
|
+
if (!this.pending.has(id) || this.pending.get(id).timedOut || this.lifecycleFailure) return;
|
|
495
|
+
this.send({ jsonrpc: "2.0", id, method, params });
|
|
496
|
+
await this.journal.write("native_dispatch", dispatch);
|
|
497
|
+
})().catch(finish);
|
|
498
|
+
} else this.send({ jsonrpc: "2.0", id, method, params });
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
notify(method, params = {}) { this.send({ jsonrpc: "2.0", method, params }); }
|
|
503
|
+
send(message) {
|
|
504
|
+
this.journal.write("outbound", message);
|
|
505
|
+
const stdin = this.child?.stdin;
|
|
506
|
+
if (!stdin || stdin.destroyed) {
|
|
507
|
+
const error = Object.assign(new Error("zcode-acp stdin is closed"), { code: "bridge_pipe_broken" });
|
|
508
|
+
this.rejectAll(error);
|
|
509
|
+
throw error;
|
|
510
|
+
}
|
|
511
|
+
try { stdin.write(`${JSON.stringify(message)}\n`); }
|
|
512
|
+
catch (error) { error.details = { system_code: error.code ?? null }; error.code = "bridge_pipe_broken"; this.rejectAll(error); throw error; }
|
|
513
|
+
}
|
|
514
|
+
rejectAll(error) {
|
|
515
|
+
for (const [id, pending] of this.pending) {
|
|
516
|
+
if (this.notificationError === error && pending.method === "session/prompt") {
|
|
517
|
+
this.rejectedHookRequests ??= new Map(); this.rejectedHookRequests.set(id, { method: pending.method, sessionId: pending.sessionId, operationId: pending.operationId, generation: pending.generation, error });
|
|
518
|
+
}
|
|
519
|
+
clearTimeout(pending.timer); if (pending.idleTimer) clearInterval(pending.idleTimer); pending.reject(error);
|
|
520
|
+
}
|
|
521
|
+
this.pending.clear();
|
|
522
|
+
}
|
|
523
|
+
async flush() {
|
|
524
|
+
await this.terminalOutcomes;
|
|
525
|
+
if (this.notificationError) throw this.notificationError;
|
|
526
|
+
try { await Promise.all([...this.lifecycleNotifications]); await this.notifications; await this.journal.flush(); }
|
|
527
|
+
catch (error) { throw this.notificationError ?? error; }
|
|
528
|
+
if (this.notificationError) throw this.notificationError;
|
|
529
|
+
if (this.inactivityError) throw this.inactivityError;
|
|
530
|
+
}
|
|
531
|
+
async close() {
|
|
532
|
+
// Productive notification failures belong to flush()/the original Turn.
|
|
533
|
+
// Draining them must not relabel a successful physical shutdown as failed.
|
|
534
|
+
return await closeBridgeProcess(this, async () => {
|
|
535
|
+
await Promise.all([this.nativeDrain, this.diagnostics]);
|
|
536
|
+
await this.terminalOutcomes;
|
|
537
|
+
await Promise.allSettled([...this.lifecycleNotifications, this.notifications]);
|
|
538
|
+
if (this.notificationError) await hookBudget(1_000, null).run(() => this.journal.flush(), "failure_drain");
|
|
539
|
+
else await this.journal.flush();
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function isProductiveSessionNotification(message) {
|
|
545
|
+
if (!["session/update", "_x.ai/session_notification"].includes(message.method)) return false;
|
|
546
|
+
const update = message.params?.update;
|
|
547
|
+
// Transport state and the bridge's cosmetic thinking hint are not native
|
|
548
|
+
// progress. The latter is identifiable without leaking a ZCode-specific
|
|
549
|
+
// synthetic flag through the ACP wire contract.
|
|
550
|
+
if (update?.sessionUpdate === "heartbeat" || update?.type === "heartbeat") return false;
|
|
551
|
+
if (update?.sessionUpdate === "agent_thought_chunk" && typeof update?.messageId === "string" && update.messageId.startsWith("thinking_")) return false;
|
|
552
|
+
if (["agent_message_chunk", "agent_thought_chunk"].includes(update?.sessionUpdate)) return update.content?.type === "text" && typeof update.content.text === "string" && update.content.text.length > 0;
|
|
553
|
+
return ["agent_message_chunk", "agent_thought_chunk", "tool_call", "tool_call_update"].includes(update?.sessionUpdate);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// The qualified bridge encodes exactly providerId\\modelId; no other suffix or
|
|
557
|
+
// slash normalization is authorized. Bare IDs do not prove account routing.
|
|
558
|
+
export function zcodeModelSelector(raw, provider = null) {
|
|
559
|
+
if (typeof raw !== "string") return { model: null, provider, raw_model: raw };
|
|
560
|
+
const parts = raw.split("\\");
|
|
561
|
+
if (parts.length === 1) return { model: raw || null, provider, raw_model: raw };
|
|
562
|
+
if (parts.length !== 2 || !parts.every(Boolean) || provider && provider !== parts[0]) return { model: null, provider, raw_model: raw };
|
|
563
|
+
return { model: parts[1], provider: parts[0], raw_model: raw };
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
export function observedProfile(read) {
|
|
567
|
+
const model = read?.settings?.model?.current ?? {};
|
|
568
|
+
return {
|
|
569
|
+
...zcodeModelSelector(model.modelId, typeof model.providerId === "string" ? model.providerId : null),
|
|
570
|
+
reasoning: read?.settings?.thoughtLevel?.current ?? null,
|
|
571
|
+
mode: read?.settings?.mode?.current ?? read?.projection?.mode ?? null,
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
export function assertProfile(requested, observed) { return checkObservedProfile(requested, observed); }
|
|
576
|
+
|
|
577
|
+
async function resume(bridge, sessionId, cwd) {
|
|
578
|
+
await bridge.request("session/resume", { sessionId: text(sessionId, "--session-id"), cwd: absolute(cwd, "--cwd"), mcpServers: [] });
|
|
579
|
+
return sessionId;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/** Ended children are paginated by the native API (20 by default). A partial
|
|
583
|
+
* directory is never settlement evidence, including during teardown. */
|
|
584
|
+
export async function readZcodeSubagents(bridge, sessionId, timeoutMs = 30_000, retained = false, terminalCache = new Map()) {
|
|
585
|
+
const clock = new ObservationClock({ timeoutMs });
|
|
586
|
+
const cursors = new Set(), ended = new Map();
|
|
587
|
+
let first, cursor;
|
|
588
|
+
for (;;) {
|
|
589
|
+
if (clock.sample()) fail("ZCode child observation is unavailable", "operation_observation_lost");
|
|
590
|
+
const response = await bridge.request(retained ? "zcode/session/retainedSubagents" : "zcode/session/subagents", { sessionId, ...(cursor ? { endedCursor: cursor } : {}) }, timeoutMs);
|
|
591
|
+
if (retained && response?.sessionId !== sessionId) fail("Retained ZCode child directory has a foreign Session identity", "native_topology_incomplete");
|
|
592
|
+
const page = retained ? response.topology : response;
|
|
593
|
+
if (!page || !Array.isArray(page.running)) fail("ZCode child directory is unavailable", "native_topology_incomplete");
|
|
594
|
+
if (!first) first = page;
|
|
595
|
+
if (!page.ended) {
|
|
596
|
+
if (cursor) fail("ZCode child page omitted ended outcomes", "native_topology_incomplete");
|
|
597
|
+
return page; // Older qualified bridges expose an unpaginated completed array.
|
|
598
|
+
}
|
|
599
|
+
if (!Array.isArray(page.ended.items) || !Number.isSafeInteger(page.ended.total) || page.ended.total < 0
|
|
600
|
+
|| page.revision !== first.revision || page.ended.total !== first.ended.total) fail("ZCode child directory changed or is malformed during pagination", "native_topology_incomplete");
|
|
601
|
+
for (const child of page.ended.items) {
|
|
602
|
+
if (typeof child.childSessionId !== "string" || !child.childSessionId) fail("ZCode ended child has no identity", "native_topology_incomplete");
|
|
603
|
+
const prior = ended.get(child.childSessionId);
|
|
604
|
+
if (prior && JSON.stringify(prior) !== JSON.stringify(child)) fail("ZCode child pages contain conflicting outcomes", "native_topology_incomplete");
|
|
605
|
+
ended.set(child.childSessionId, child);
|
|
606
|
+
}
|
|
607
|
+
cursor = page.ended.nextCursor;
|
|
608
|
+
if (!cursor) {
|
|
609
|
+
if (ended.size !== first.ended.total) fail("ZCode child directory is incomplete", "native_topology_incomplete");
|
|
610
|
+
const items = [];
|
|
611
|
+
for (const child of ended.values()) {
|
|
612
|
+
if (child.status !== "failed" || !Number.isSafeInteger(child.startedAt) || child.startedAt <= 0
|
|
613
|
+
|| !Number.isSafeInteger(child.endedAt) || child.endedAt < child.startedAt || child.endedAt > 8.64e15) { items.push(child); continue; }
|
|
614
|
+
// Closed children must never be loaded merely to explain their failure.
|
|
615
|
+
const cacheKey = JSON.stringify([child.childSessionId, child.startedAt, child.endedAt]);
|
|
616
|
+
if (terminalCache.has(cacheKey)) { items.push(terminalCache.get(cacheKey)); continue; }
|
|
617
|
+
let terminal;
|
|
618
|
+
try { terminal = await bridge.request("zcode/session/retainedTerminal", { sessionId: child.childSessionId, startedAt: child.startedAt, endedAt: child.endedAt }, timeoutMs); }
|
|
619
|
+
catch (error) { items.push({ ...child, terminal_metadata_unavailable: error.code ?? "terminal_metadata_unavailable" }); continue; }
|
|
620
|
+
if (terminal?.available !== true) { items.push({ ...child, terminal_metadata_unavailable: "terminal_metadata_unavailable" }); continue; }
|
|
621
|
+
const observedAt = Date.parse(terminal.terminal_observed_at);
|
|
622
|
+
if (terminal.sessionId !== child.childSessionId || typeof terminal.terminal_turn_id !== "string" || !terminal.terminal_turn_id
|
|
623
|
+
|| terminal.startedAt !== child.startedAt || terminal.endedAt !== child.endedAt
|
|
624
|
+
|| !Number.isFinite(observedAt) || observedAt < child.startedAt || observedAt > child.endedAt) fail("ZCode terminal failure metadata has foreign identity", "native_child_turn_conflict");
|
|
625
|
+
const providerError = safeNativeFailure(terminal.provider_error);
|
|
626
|
+
const item = { ...child, terminal_turn_id: terminal.terminal_turn_id, terminal_observed_at: terminal.terminal_observed_at,
|
|
627
|
+
...(providerError ? { provider_error: providerError } : {}) };
|
|
628
|
+
terminalCache.set(cacheKey, item);
|
|
629
|
+
items.push(item);
|
|
630
|
+
clock.sample(child.childSessionId);
|
|
631
|
+
}
|
|
632
|
+
return { ...first, ended: { total: first.ended.total, items } };
|
|
633
|
+
}
|
|
634
|
+
if (typeof cursor !== "string" || cursors.has(cursor) || !Number.isSafeInteger(first.revision)) fail("ZCode child pagination has no advancing, versioned cursor", "native_topology_incomplete");
|
|
635
|
+
cursors.add(cursor);
|
|
636
|
+
if (cursors.size > 1_024) fail("ZCode child pagination exceeds its bounded directory limit", "native_topology_incomplete");
|
|
637
|
+
clock.sample(cursor);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
async function inspect(bridge, sessionId) {
|
|
642
|
+
const [read, subagents] = await Promise.all([
|
|
643
|
+
bridge.request("zcode/session/read", { sessionId }),
|
|
644
|
+
readZcodeSubagents(bridge, sessionId),
|
|
645
|
+
]);
|
|
646
|
+
let usage = null, usage_observation_error = null;
|
|
647
|
+
try { usage = await readZcodeUsage(bridge, sessionId); }
|
|
648
|
+
catch (error) { usage_observation_error = { code: error.code ?? "usage_observation_failed", message: error.message ?? String(error) }; }
|
|
649
|
+
const observed = observedProfile(read);
|
|
650
|
+
await bridge.recordModel?.(sessionId, observed, "zcode/session/read");
|
|
651
|
+
return { read, subagents, usage, ...(usage_observation_error ? { usage_observation_error } : {}), observed_profile: observed };
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// The journal is the immutable transcript. CLI receipts only need the
|
|
655
|
+
// operational projection; returning every message makes a routine status
|
|
656
|
+
// command enormous and obscures the useful facts.
|
|
657
|
+
function publicEvidence(evidence) {
|
|
658
|
+
return {
|
|
659
|
+
...evidence,
|
|
660
|
+
read: {
|
|
661
|
+
projection: evidence.read?.projection ?? null,
|
|
662
|
+
session: evidence.read?.session ?? null,
|
|
663
|
+
settings: evidence.read?.settings ?? null,
|
|
664
|
+
},
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// ACP returns the terminal turn separately from its visible transcript. The
|
|
669
|
+
// probe contract needs the literal terminal answer, so extract only text parts
|
|
670
|
+
// from the last assistant message rather than asking a model to interpret it.
|
|
671
|
+
export function latestAssistantText(read) {
|
|
672
|
+
const messages = Array.isArray(read?.messages) ? read.messages : [];
|
|
673
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
674
|
+
const message = messages[index];
|
|
675
|
+
if (message?.info?.role !== "assistant") continue;
|
|
676
|
+
const text = (Array.isArray(message.parts) ? message.parts : [])
|
|
677
|
+
.filter((part) => part?.type === "text" && typeof part.text === "string")
|
|
678
|
+
.map((part) => part.text)
|
|
679
|
+
.join("")
|
|
680
|
+
.trim();
|
|
681
|
+
if (text) return text;
|
|
682
|
+
}
|
|
683
|
+
return null;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
export function zcodeChildSessionIds(snapshot) {
|
|
687
|
+
return [...new Set([
|
|
688
|
+
...(Array.isArray(snapshot?.childSessionIds) ? snapshot.childSessionIds : []),
|
|
689
|
+
...(Array.isArray(snapshot?.running) ? snapshot.running : []).map(child => child?.childSessionId),
|
|
690
|
+
...(Array.isArray(snapshot?.ended?.items) ? snapshot.ended.items : []).map(child => child?.childSessionId),
|
|
691
|
+
].filter(id => typeof id === "string" && id))];
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// Read residency BEFORE each durable topology snapshot. An inactive node cannot
|
|
695
|
+
// add descendants; a resident node never proves settlement, even when idle.
|
|
696
|
+
// No resolve/read/subagents extension here: those may implicitly resume.
|
|
697
|
+
export async function observeClosedZcodeTree(bridge, root, retainedChildren = [], neverPromptedRootProof = null) {
|
|
698
|
+
const pending = new Set([root, ...retainedChildren]), residents = [], snapshots = [];
|
|
699
|
+
const clock = new ObservationClock({ timeoutMs: 30_000 });
|
|
700
|
+
try {
|
|
701
|
+
for (const sessionId of pending) {
|
|
702
|
+
if (pending.size > 1024 || clock.sample()) throw new Error("Closed tree observation limit exceeded");
|
|
703
|
+
const resident = await bridge.request("zcode/session/resident", { sessionId }, 5_000);
|
|
704
|
+
if (resident?.sessionId !== sessionId || typeof resident.resident !== "boolean") throw new Error("Invalid residency identity or state");
|
|
705
|
+
residents.push(resident);
|
|
706
|
+
// A newly allocated root is not durable in ZCode until its first prompt.
|
|
707
|
+
// This is an explicit owned-daemon ledger fact, never error→empty inference.
|
|
708
|
+
if (sessionId === root && resident.resident === false && pending.size === 1
|
|
709
|
+
&& neverPromptedRootProof?.source === "owned_daemon_create_without_prompt"
|
|
710
|
+
&& neverPromptedRootProof.close_confirmed === true
|
|
711
|
+
&& neverPromptedRootProof.provider_session_id === root && typeof neverPromptedRootProof.daemon_id === "string") {
|
|
712
|
+
snapshots.push({ sessionId, childSessionIds: [], running: [], ended: {total:0,items:[]}, provenance: neverPromptedRootProof });
|
|
713
|
+
continue;
|
|
714
|
+
}
|
|
715
|
+
const tree = await readZcodeSubagents(bridge, sessionId, 30_000, true);
|
|
716
|
+
if (!Array.isArray(tree?.childSessionIds)
|
|
717
|
+
|| tree.childSessionIds.some(id => typeof id !== "string" || !id || id === sessionId)
|
|
718
|
+
|| !Array.isArray(tree?.running) || !Array.isArray(tree?.ended?.items)
|
|
719
|
+
|| !Number.isSafeInteger(tree.ended.total) || tree.ended.total < 0
|
|
720
|
+
|| tree.ended.total > tree.childSessionIds.length
|
|
721
|
+
|| [...tree.running, ...tree.ended.items].some(child => !tree.childSessionIds.includes(child?.childSessionId))) throw new Error("Incomplete retained native topology");
|
|
722
|
+
snapshots.push({ sessionId, ...tree });
|
|
723
|
+
clock.sample(sessionId);
|
|
724
|
+
for (const child of tree.childSessionIds) pending.add(child);
|
|
725
|
+
}
|
|
726
|
+
return { topology: { root_session_id: root, child_session_ids: [...pending].filter(id => id !== root), complete: true }, residents, snapshots, settled: residents.every(item => item.resident === false) };
|
|
727
|
+
} catch (error) {
|
|
728
|
+
return { topology: { root_session_id: root, child_session_ids: [...pending].filter(id => id !== root), complete: false }, residents, snapshots, settled: false, observation_error: errorRecord(error) };
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
async function cancelTree(bridge, sessionId, before, providerSessionId, retainedChildren = [], neverPromptedRootProof = null) {
|
|
733
|
+
const knownChildren = new Set(retainedChildren);
|
|
734
|
+
const remember = snapshot => {
|
|
735
|
+
for (const id of zcodeChildSessionIds(snapshot)) knownChildren.add(id);
|
|
736
|
+
};
|
|
737
|
+
remember(before);
|
|
738
|
+
const cancellations = [];
|
|
739
|
+
for (const child of before.running ?? []) {
|
|
740
|
+
const taskId = child.taskId ?? child.agentId;
|
|
741
|
+
if (taskId) {
|
|
742
|
+
try { cancellations.push(await bridge.request("session/cancelBackgroundTask", { sessionId, taskId })); }
|
|
743
|
+
catch (error) { cancellations.push({ taskId, cancelled: false, error: errorRecord(error) }); }
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
// ZCode's stop is deliberately idempotent. A stop acknowledgement is not a
|
|
747
|
+
// terminal receipt: native turns (and a root without children) can remain
|
|
748
|
+
// active after it. Observe a bounded drain, then close this owned Session.
|
|
749
|
+
bridge.notify("session/cancel", { sessionId });
|
|
750
|
+
let after = before, read = null, root_status = null;
|
|
751
|
+
const deadline = Date.now() + 6000;
|
|
752
|
+
for (let attempt = 0; attempt < 6 && Date.now() < deadline; attempt += 1) {
|
|
753
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
754
|
+
const timeout = Math.max(1, deadline - Date.now());
|
|
755
|
+
[after, read] = await Promise.all([
|
|
756
|
+
readZcodeSubagents(bridge, sessionId, timeout).catch(error => ({ observation_error: errorRecord(error) })),
|
|
757
|
+
bridge.request("zcode/session/read", { sessionId }, timeout).catch(error => ({ observation_error: errorRecord(error) })),
|
|
758
|
+
]);
|
|
759
|
+
root_status = read?.projection?.status ?? read?.session?.status ?? null;
|
|
760
|
+
remember(after);
|
|
761
|
+
if (after.observation_error || read?.observation_error) break;
|
|
762
|
+
if (Array.isArray(after.running) && !after.running.length && !(read?.projection?.activeToolCalls ?? []).length && ["idle", "completed", "cancelled", "failed", "stopped"].includes(root_status)) {
|
|
763
|
+
// Idle is only a turn-level observation. Close the owned resident before
|
|
764
|
+
// proving the transitive tree cannot create another child.
|
|
765
|
+
break;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
let close = null;
|
|
769
|
+
try { close = await bridge.request("zcode/session/close", { sessionId }); }
|
|
770
|
+
catch (error) { close = { closed: false, error: errorRecord(error) }; }
|
|
771
|
+
const observation = close?.closed === true ? await observeClosedZcodeTree(bridge, providerSessionId, [...knownChildren], neverPromptedRootProof ? {...neverPromptedRootProof,close_confirmed:true} : null)
|
|
772
|
+
: { topology: { root_session_id: providerSessionId, child_session_ids: [...knownChildren], complete: false }, settled: false };
|
|
773
|
+
return { cancellations, before, after, root_status, cancellation_requested: true, close_requested: true, close, ...observation };
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// Cancel one known child without cancelling its parent turn. The parent keeps
|
|
777
|
+
// its ACP listener, so later child lifecycle events still reach dd-flow.
|
|
778
|
+
export async function cancelChildWithBridge(bridge, options) {
|
|
779
|
+
requireJournal(options);
|
|
780
|
+
const identity = await controlledIdentity(bridge, options);
|
|
781
|
+
const childSessionId = text(options.childSessionId, "--child-session-id");
|
|
782
|
+
const before = await readZcodeSubagents(bridge, identity.adapterSessionId);
|
|
783
|
+
const child = (before.running ?? []).find((item) => item.childSessionId === childSessionId);
|
|
784
|
+
if (!child) fail(`ZCode has no running child ${childSessionId}`, "child_not_running");
|
|
785
|
+
const taskId = child.taskId ?? child.agentId;
|
|
786
|
+
if (!taskId) fail(`ZCode child ${childSessionId} has no cancellation identifier`, "child_cancel_unsupported");
|
|
787
|
+
const cancellation = await bridge.request("session/cancelBackgroundTask", { sessionId: identity.adapterSessionId, taskId });
|
|
788
|
+
let after = before;
|
|
789
|
+
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
790
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
791
|
+
after = await readZcodeSubagents(bridge, identity.adapterSessionId);
|
|
792
|
+
if (!(after.running ?? []).some((item) => item.childSessionId === childSessionId)) break;
|
|
793
|
+
}
|
|
794
|
+
if ((after.running ?? []).some((item) => item.childSessionId === childSessionId)) {
|
|
795
|
+
fail(`ZCode child ${childSessionId} did not stop`, "partial_cancellation");
|
|
796
|
+
}
|
|
797
|
+
return receipt(options, {
|
|
798
|
+
harness: "zcode-acp",
|
|
799
|
+
provider_session_id: identity.providerSessionId,
|
|
800
|
+
adapter_session_id: identity.adapterSessionId,
|
|
801
|
+
child_session_id: childSessionId,
|
|
802
|
+
cancellation,
|
|
803
|
+
before,
|
|
804
|
+
after,
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
export async function cancelChild(options) {
|
|
809
|
+
return await withBridge(options, async (bridge) => await cancelChildWithBridge(bridge, options));
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
async function controlledIdentity(bridge, options) {
|
|
813
|
+
const providerSessionId = text(options.sessionId, "--session-id");
|
|
814
|
+
const adapterSessionId = options.adapterSessionId ?? providerSessionId;
|
|
815
|
+
if (!options.liveSession) await resume(bridge, adapterSessionId, options.cwd);
|
|
816
|
+
const resolved = await bridge.request("zcode/session/resolve", { sessionId: adapterSessionId });
|
|
817
|
+
if (resolved.providerSessionId !== providerSessionId) fail(`ZCode Session identity mismatch: expected ${providerSessionId}, observed ${resolved.providerSessionId}`, "session_identity_mismatch");
|
|
818
|
+
bridge.registerModelSession?.(adapterSessionId, providerSessionId, profile(options));
|
|
819
|
+
return { providerSessionId, adapterSessionId };
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
async function applyProfile(bridge, sessionId, requested, assertDispatch = () => {}) {
|
|
823
|
+
const identity = bridge.modelSessions?.get(sessionId);
|
|
824
|
+
if (identity) identity.armed = false;
|
|
825
|
+
assertDispatch();
|
|
826
|
+
if (requested.mode) await bridge.request("session/set_mode", { sessionId, modeId: requested.mode });
|
|
827
|
+
assertDispatch();
|
|
828
|
+
if (requested.reasoning) await bridge.request("session/setThoughtLevel", { sessionId, thoughtLevel: requested.reasoning });
|
|
829
|
+
assertDispatch();
|
|
830
|
+
if (requested.model) await bridge.request("session/setModel", { sessionId, modelId: requested.provider ? `${requested.provider}\\${requested.model}` : requested.model });
|
|
831
|
+
assertDispatch();
|
|
832
|
+
const read = await bridge.request("zcode/session/read", { sessionId });
|
|
833
|
+
await bridge.notifications;
|
|
834
|
+
const observed = observedProfile(read);
|
|
835
|
+
await bridge.recordModel?.(sessionId, observed, "zcode/session/read.applied");
|
|
836
|
+
const receipt = assertProfile(requested, observed);
|
|
837
|
+
if (identity) identity.armed = true;
|
|
838
|
+
return receipt;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
function profile(options) { return { provider: options.provider ?? null, model: options.model ?? null, reasoning: options.reasoning ?? null, mode: options.mode ?? null }; }
|
|
842
|
+
|
|
843
|
+
function requiredProfile(options) {
|
|
844
|
+
const requested = profile(options);
|
|
845
|
+
for (const key of ["provider", "model", "reasoning", "mode"]) text(requested[key], `--${key}`);
|
|
846
|
+
return requested;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
function requireJournal(options) { absolute(options.journal, "--journal"); }
|
|
850
|
+
|
|
851
|
+
function receipt(options, value) { return options.daemonId ? { daemon_id: options.daemonId, ...value } : value; }
|
|
852
|
+
|
|
853
|
+
async function withBridge(options, operation) {
|
|
854
|
+
const bridge = new AcpBridge(options); let primary, result;
|
|
855
|
+
try { const initialized = await bridge.start(); result = await operation(bridge, initialized); await bridge.flush(); return result; }
|
|
856
|
+
catch (error) { primary = result === undefined ? error : nativeObservationError(error, { result }); throw primary; }
|
|
857
|
+
finally { try { await bridge.close(); } catch (error) { if (!primary) throw nativeObservationError(error, result === undefined ? {} : { result }); primary.cleanup_error = errorRecord(error); primary.details = { ...primary.details, cleanup_error: primary.cleanup_error }; } }
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
function flowForwarder(options, rootProviderSessionId, bridge) {
|
|
861
|
+
return async (notification) => {
|
|
862
|
+
if (notification.method !== "session/update") return;
|
|
863
|
+
const update = notification.params?.update;
|
|
864
|
+
const command = update?.sessionUpdate === "tool_call" ? update.rawInput?.command ?? update.rawInput?.cmd : null;
|
|
865
|
+
if (typeof command !== "string" || !command.includes("dd-flow")) return;
|
|
866
|
+
const childId = notification.params?.update?._meta?.zcodeRuntime?.childSessionId;
|
|
867
|
+
const observed = bridge?.modelProfiles?.get(childId ?? rootProviderSessionId) ?? {};
|
|
868
|
+
if (childId && !observed.model) await observeModel({ journal: options.journal, harness: "zcode-acp", sessionId: childId, parentSessionId: rootProviderSessionId, requested: profile(options), observed, source: "zcode.child_tool_event", evidence: "unavailable" });
|
|
869
|
+
// This serialized path is model evidence only. The independent observer
|
|
870
|
+
// below owns qualified ACP admission; do not mint a second receipt here.
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/** Preserve native tool ownership independently of slow secondary evidence.
|
|
875
|
+
* Rehydration uses this same session's retained inbound journal, not model text. */
|
|
876
|
+
export async function zcodeInvocationObserver(options, rootProviderSessionId, send = invokeNativeHook) {
|
|
877
|
+
if (!options.ddFlowBin) return async () => {};
|
|
878
|
+
const owners = new Map();
|
|
879
|
+
const remember = (message, mandatory = false) => {
|
|
880
|
+
const update = message.params?.update;
|
|
881
|
+
if (message.method !== "session/update" || update?.sessionUpdate !== "tool_call") return null;
|
|
882
|
+
if (options.adapterSessionId && message.params?.sessionId !== options.adapterSessionId) return null;
|
|
883
|
+
const native = update._meta?.zcodeRuntime ?? {};
|
|
884
|
+
const session = zcodePhysicalSession(update, rootProviderSessionId);
|
|
885
|
+
if (!session) {
|
|
886
|
+
if (mandatory) throw Object.assign(new Error("Native subagent event has no physical child identity"), { code: "zcode_child_identity_missing" });
|
|
887
|
+
return null;
|
|
888
|
+
}
|
|
889
|
+
const key = update.toolCallId;
|
|
890
|
+
if (!key || typeof session !== "string") return null;
|
|
891
|
+
if (owners.has(key) && owners.get(key) !== session) {
|
|
892
|
+
if (mandatory) throw Object.assign(new Error("Native tool id has conflicting Session ownership"), { code: "zcode_tool_identity_conflict" });
|
|
893
|
+
return null;
|
|
894
|
+
}
|
|
895
|
+
owners.set(key, session);
|
|
896
|
+
return { update, native, session };
|
|
897
|
+
};
|
|
898
|
+
if (options.journal) {
|
|
899
|
+
try {
|
|
900
|
+
const contents = await readFile(options.journal, "utf8");
|
|
901
|
+
for (const line of contents.split("\n").filter(Boolean)) {
|
|
902
|
+
const event = JSON.parse(line);
|
|
903
|
+
if (event.kind === "inbound") remember(event.payload);
|
|
904
|
+
}
|
|
905
|
+
} catch (error) { if (error.code !== "ENOENT") throw error; }
|
|
906
|
+
}
|
|
907
|
+
// The same native Session can host several Turns. Retained tool IDs belong
|
|
908
|
+
// to earlier Turns; a delayed duplicate cannot reject a new prompt waiter.
|
|
909
|
+
const priorTools = new Set(owners.keys());
|
|
910
|
+
return async message => {
|
|
911
|
+
if (priorTools.has(message.params?.update?.toolCallId)) return;
|
|
912
|
+
const candidate = message.params?.update, command = candidate?.rawInput?.command ?? candidate?.rawInput?.cmd;
|
|
913
|
+
const shell = ["Bash", "bash", "execute"].includes(candidate?._meta?.claudeCode?.toolName) && typeof command === "string";
|
|
914
|
+
const analysis = shell ? parseLifecycleCommand(command) : null;
|
|
915
|
+
const mandatory = shell && (analysis.kind !== "none" || ["lifecycle", "auxiliary"].includes(analysis.participation) || continuationCommand(command));
|
|
916
|
+
const facts = remember(message, mandatory);
|
|
917
|
+
if (!facts || !options.ddFlowBin) return;
|
|
918
|
+
const { native, session } = facts;
|
|
919
|
+
if (!mandatory) return;
|
|
920
|
+
const parent = session === rootProviderSessionId ? null : owners.get(native.parentToolCallId);
|
|
921
|
+
if (session !== rootProviderSessionId && !parent) throw Object.assign(new Error("Native child has no confirmed immediate parent tool owner"), { code: "zcode_parent_identity_missing" });
|
|
922
|
+
try {
|
|
923
|
+
await send({ bin: options.ddFlowBin, home: options.ddFlowHome,
|
|
924
|
+
args: ["zcode", "event", "handle", "--project-root", options.projectRoot ?? options.cwd, "--json"],
|
|
925
|
+
payload: { ...message, _meta: { ddZcode: { daemonId: options.daemonId, rootProviderSessionId, parentProviderSessionId: parent } } },
|
|
926
|
+
});
|
|
927
|
+
} catch (error) {
|
|
928
|
+
const details = error.details;
|
|
929
|
+
// Issued-ID commands cannot cross the CLI CAS without the receipt.
|
|
930
|
+
// This proof concerns lifecycle effects only, never shell-prefix replay.
|
|
931
|
+
if (analysis.kind !== "none" && commandOption(analysis.invocation, "invocation-id")
|
|
932
|
+
&& isProvenShellCorrection(error)
|
|
933
|
+
&& details.issued_invocation_id === commandOption(analysis.invocation, "invocation-id")) correctableLifecycleErrors.add(error);
|
|
934
|
+
throw error;
|
|
935
|
+
}
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
export function zcodeLifecycleEnvelope(notification, options, rootProviderSessionId) {
|
|
940
|
+
return { ...notification, _meta: { ddZcode: {
|
|
941
|
+
rootProviderSessionId,
|
|
942
|
+
requestedProfile: profile(options),
|
|
943
|
+
observedProfile: observedFields(options.observedProfile),
|
|
944
|
+
...(options.daemonId ? { daemonId: options.daemonId } : {}),
|
|
945
|
+
} } };
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
async function forwardUsage(options, providerSessionId, usage, toolCalls) {
|
|
949
|
+
if (!options.ddFlowBin || !usage) return;
|
|
950
|
+
const projectRoot = absolute(options.projectRoot, "--project-root");
|
|
951
|
+
const payload = {
|
|
952
|
+
provider_session_id: providerSessionId,
|
|
953
|
+
daemon_id: options.daemonId ?? null,
|
|
954
|
+
observed_at: new Date().toISOString(),
|
|
955
|
+
usage,
|
|
956
|
+
// ZCode exposes counters for the addressed physical session only. State
|
|
957
|
+
// this explicitly so dd-flow never has to infer an aggregation model.
|
|
958
|
+
usage_scope: "physical_session",
|
|
959
|
+
completeness: usage?.usageIsIncomplete ? "partial" : "complete",
|
|
960
|
+
...(toolCalls ? { tool_calls: toolCalls } : {}),
|
|
961
|
+
};
|
|
962
|
+
await runWithStdin(options.ddFlowBin, ["zcode", "usage", "ingest", "--project-root", projectRoot, "--json"], `${JSON.stringify(payload)}\n`, options.ddFlowHome ? { DD_FLOW_HOME: absolute(options.ddFlowHome, "--dd-flow-home") } : {});
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
async function forwardUsageBestEffort(options, providerSessionId, usage, toolCalls) {
|
|
966
|
+
try { await forwardUsage(options, providerSessionId, usage, toolCalls); return null; }
|
|
967
|
+
catch (error) { return { code: error.code ?? "usage_ingest_failed", message: error.message ?? String(error) }; }
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
export function zcodeLifecycleQualification(sha256, bridgeCommit, harnessContract) {
|
|
971
|
+
// The native app bundle is an auto-updated runtime. Its whole-file SHA is
|
|
972
|
+
// evidence for provenance, not a protocol identity: an unchanged bridge
|
|
973
|
+
// must not reject every normal ZCode patch release. The bridge contract is
|
|
974
|
+
// the compatibility boundary; native event identity is checked by the
|
|
975
|
+
// lifecycle observer and receipts during the bounded qualification probe.
|
|
976
|
+
const qualifiedBridgeCommits = new Map([
|
|
977
|
+
["e0600fe3c46e215695f257e65aa6f674ced998d8", ZCODE_HARNESS_CONTRACT],
|
|
978
|
+
["60af0d31e13076a313d9770f10aa70f7c94742cf", ZCODE_HARNESS_CONTRACT],
|
|
979
|
+
["8c0a893f3c26a0b96cb138acf762db84c800298b", ZCODE_HARNESS_CONTRACT],
|
|
980
|
+
["f4d47b517073f9bdb8d4eba3d0d59ce66c6a2833", "dd-zcode-harness@2"],
|
|
981
|
+
["5a8c89ab716a4b2ccc50df81cb4d204ff5c06368", "dd-zcode-harness@2"],
|
|
982
|
+
["91a1470650f5fd6105f9863fd31d1cad7b90a109", "dd-zcode-harness@2"],
|
|
983
|
+
["b9e0f5106dddb439b76186278bd9be5f45dbf3ef", "dd-zcode-harness@2"],
|
|
984
|
+
["636b14181d5c9a95476055079836fd6b58169f58", "dd-zcode-harness@2"],
|
|
985
|
+
["4621fabd3f517ecdd349dfebebae9077c01d2f2b", "dd-zcode-harness@2"],
|
|
986
|
+
["9c5ca6cc81c4eb5065e43f8b7ab6b7cc5ab7ae47", "dd-zcode-harness@2"],
|
|
987
|
+
]);
|
|
988
|
+
const qualified = qualifiedBridgeCommits.get(bridgeCommit) === harnessContract;
|
|
989
|
+
return { status: qualified ? "qualified" : "unqualified", mechanism: "cli-invocation@1", sha256,
|
|
990
|
+
capabilities: qualified ? ["root", "concurrent_children", "child_continuation"] : [],
|
|
991
|
+
native_nested_children: false, reason: qualified ? null : "native_event_contract_unqualified" };
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
let doctorDigestScanOrdinal = 0;
|
|
995
|
+
export async function doctor(options = {}) {
|
|
996
|
+
const zcodeAcp = await commandOutput(options.bin, ["--version"]);
|
|
997
|
+
const zcodeAcpCommit = await commandOutput(options.bin, ["--dd-harness-commit"]);
|
|
998
|
+
const ddHarness = await commandOutput(options.bin, ["--dd-harness-version"]);
|
|
999
|
+
const zcodePath = options.zcodePath ?? process.env.ZCODE_BIN ?? process.env.ZCODE_PATH ?? "/Applications/ZCode.app/Contents/Resources/glm/zcode.cjs";
|
|
1000
|
+
const zcode = await commandOutput(zcodePath, ["--version"]);
|
|
1001
|
+
const hash = createHash('sha256'); let bytes = 0;
|
|
1002
|
+
const scan = `zcode-digest:${zcodePath}:scan-${++doctorDigestScanOrdinal}`;
|
|
1003
|
+
publishSetup('probe_started', scan);
|
|
1004
|
+
for await (const chunk of createReadStream(zcodePath)) {
|
|
1005
|
+
hash.update(chunk); bytes += chunk.length;
|
|
1006
|
+
if (bytes % (4 * 1024 * 1024) < chunk.length) publishSetup('identity_digest_progress', `${scan}:${bytes}`);
|
|
1007
|
+
}
|
|
1008
|
+
const sha256 = hash.digest('hex'); publishSetup('identity_digest_completed', scan);
|
|
1009
|
+
const qualification = zcodeLifecycleQualification(sha256, zcodeAcpCommit, ddHarness);
|
|
1010
|
+
return { compatible: qualification.status === "qualified", contract_supported: supportedZcodeHarnessContract(ddHarness), lifecycle_qualification: qualification, observed_runtime: { zcode, zcode_acp: zcodeAcp, zcode_acp_commit: zcodeAcpCommit, dd_harness_contract: ddHarness } };
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
export async function createSession(options) {
|
|
1014
|
+
requireJournal(options);
|
|
1015
|
+
return await withBridge(options, async (bridge, initialized) => await createSessionWithBridge(bridge, options, initialized));
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
export async function createSessionWithBridge(bridge, options, initialized) {
|
|
1019
|
+
requireJournal(options);
|
|
1020
|
+
const cwd = absolute(options.cwd, "--cwd"); const requested = requiredProfile(options);
|
|
1021
|
+
let forward;
|
|
1022
|
+
bridge.configure({ ...options, onNotification: async (event) => { await forward?.(event); await options.onNotification?.(event); } });
|
|
1023
|
+
const created = await bridge.request("session/new", { cwd, mcpServers: [] });
|
|
1024
|
+
// The ACP alias is the first durable identity available. Persist it before
|
|
1025
|
+
// resolving the native Session so a later resolve timeout cannot erase the
|
|
1026
|
+
// fact that allocation was already admitted.
|
|
1027
|
+
await options.onSessionAliasAllocated?.({ adapter_session_id: created.sessionId, cwd });
|
|
1028
|
+
bridge.allocationObservers?.set(created.sessionId, async identity => {
|
|
1029
|
+
// ZCode 0.46.x emits adapter/provider identities on this notification but
|
|
1030
|
+
// does not repeat the workspace. The cwd from session/new is authoritative
|
|
1031
|
+
// for this live bridge; reject only an explicitly conflicting workspace.
|
|
1032
|
+
if (identity.cwd !== undefined && identity.cwd !== cwd) fail("ZCode allocation workspace mismatch", "session_identity_mismatch");
|
|
1033
|
+
await options.onSessionAllocated?.({ ...identity, cwd });
|
|
1034
|
+
});
|
|
1035
|
+
// Two provider syncs (10s each) and native create (90s) run inside resolve.
|
|
1036
|
+
const resolved = await bridge.request("zcode/session/resolve", { sessionId: created.sessionId }, 150_000);
|
|
1037
|
+
const providerSessionId = resolved.providerSessionId;
|
|
1038
|
+
// Expose both identities before profile/usage bookkeeping. The owning
|
|
1039
|
+
// daemon can persist this allocation even when a later step fails.
|
|
1040
|
+
await options.onSessionAllocated?.({
|
|
1041
|
+
adapter_session_id: created.sessionId,
|
|
1042
|
+
provider_session_id: providerSessionId,
|
|
1043
|
+
cwd,
|
|
1044
|
+
});
|
|
1045
|
+
bridge.registerModelSession?.(created.sessionId, providerSessionId, requested, null, false);
|
|
1046
|
+
const profile_receipt = await applyProfile(bridge, created.sessionId, requested);
|
|
1047
|
+
forward = flowForwarder(options, providerSessionId, bridge);
|
|
1048
|
+
let usage = null, usage_observation_error = null;
|
|
1049
|
+
try { usage = await readZcodeUsage(bridge, created.sessionId); }
|
|
1050
|
+
catch (error) { usage_observation_error = { code: error.code ?? "usage_observation_failed", message: error.message ?? String(error) }; }
|
|
1051
|
+
const usage_ingest_error = await forwardUsageBestEffort(options, providerSessionId, usage, bridge.toolSummary(created.sessionId));
|
|
1052
|
+
await bridge.flush();
|
|
1053
|
+
// ZCode materializes `subagents` only after its first prompt. Session
|
|
1054
|
+
// creation is intentionally nonproductive, so querying that topology here
|
|
1055
|
+
// would both fail on real ZCode and turn the common create contract into an
|
|
1056
|
+
// implicit first turn. The first prompt/inspect records full evidence.
|
|
1057
|
+
const evidence = { read: null, subagents: { running: [], completed: [] }, usage, ...(usage_observation_error ? { usage_observation_error } : {}), ...(usage_ingest_error ? { usage_ingest_error } : {}), observed_profile: profile_receipt.observed, tool_calls: bridge.toolSummary(created.sessionId) };
|
|
1058
|
+
return receipt(options, { harness: "zcode-acp", runtime_family: "zcode", adapter_session_id: created.sessionId, provider_session_id: providerSessionId, cwd, initialized, profile: profile_receipt, evidence: publicEvidence(evidence) });
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
export async function promptSession(options) {
|
|
1062
|
+
requireJournal(options);
|
|
1063
|
+
return await withBridge(options, async (bridge) => await promptSessionWithBridge(bridge, options));
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
export async function promptSessionWithBridge(bridge, options) {
|
|
1067
|
+
requireJournal(options);
|
|
1068
|
+
const cwd = absolute(options.cwd, "--cwd"); const prompt = text(options.prompt, "prompt"); const requested = requiredProfile(options);
|
|
1069
|
+
let forward;
|
|
1070
|
+
bridge.configure({ ...options, onNotification: async (event) => { await forward?.(event); await options.onNotification?.(event); } });
|
|
1071
|
+
const identity = await controlledIdentity(bridge, { ...options, cwd }); forward = flowForwarder(options, identity.providerSessionId, bridge);
|
|
1072
|
+
await bridge.journal.flush();
|
|
1073
|
+
bridge.configure({ onLifecycleNotification: await zcodeInvocationObserver({ ...options, adapterSessionId: identity.adapterSessionId }, identity.providerSessionId) });
|
|
1074
|
+
const profile_receipt = await applyProfile(bridge, identity.adapterSessionId, requested, options.assertDispatch);
|
|
1075
|
+
options.assertDispatch?.();
|
|
1076
|
+
let initial_usage_error = null;
|
|
1077
|
+
try { initial_usage_error = await forwardUsageBestEffort(options, identity.providerSessionId, await readZcodeUsage(bridge, identity.adapterSessionId), bridge.toolSummary(identity.adapterSessionId)); }
|
|
1078
|
+
catch (error) { initial_usage_error = { code: error.code ?? "usage_observation_failed", message: error.message ?? String(error) }; }
|
|
1079
|
+
// This is the request budget. ACP silence is observed separately and must
|
|
1080
|
+
// never turn a long native child request into a fabricated provider failure.
|
|
1081
|
+
const timeoutMs = null;
|
|
1082
|
+
const textCursor = bridge.assistantTextCursor(identity.adapterSessionId);
|
|
1083
|
+
options.assertDispatch?.();
|
|
1084
|
+
bridge.dispatchOperationId = options.operationId;
|
|
1085
|
+
const turn = await bridge.request("session/prompt", { sessionId: identity.adapterSessionId, prompt: [{ type: "text", text: prompt }] }, timeoutMs, {
|
|
1086
|
+
idleSessionId: identity.adapterSessionId,
|
|
1087
|
+
idleTimeoutMs: options.livenessTimeoutMs ?? 600_000,
|
|
1088
|
+
inactivityMode: "diagnostic",
|
|
1089
|
+
}).catch(error => {
|
|
1090
|
+
if (error.details?.terminal_response?.session_id === identity.adapterSessionId) error.details.provider_session_id = identity.providerSessionId;
|
|
1091
|
+
throw error;
|
|
1092
|
+
});
|
|
1093
|
+
const stopReason = typeof turn?.stopReason === "string" ? turn.stopReason : null;
|
|
1094
|
+
const completionEvidence = turn?._meta?.zcodeCompletionEvidence;
|
|
1095
|
+
if (completionEvidence === "inferred" || completionEvidence === "failed") {
|
|
1096
|
+
let observationError = null;
|
|
1097
|
+
try { await bridge.flush(); } catch (error) { observationError = errorRecord(error); }
|
|
1098
|
+
const error = new Error(`ZCode completion is ${completionEvidence}, not a confirmed successful native turn`);
|
|
1099
|
+
error.code = completionEvidence === "inferred" ? "native_outcome_unknown" : "zcode_turn_not_completed";
|
|
1100
|
+
error.retryable = false;
|
|
1101
|
+
error.details = { completion_evidence: completionEvidence, provider_session_id: identity.providerSessionId, adapter_session_id: identity.adapterSessionId, ...(observationError ? { observation_error: observationError } : {}) };
|
|
1102
|
+
throw error;
|
|
1103
|
+
}
|
|
1104
|
+
if (stopReason && stopReason !== "end_turn") {
|
|
1105
|
+
// A close can settle the pending prompt before its terminal event. Do not
|
|
1106
|
+
// collect live evidence first: ordinary inspection is allowed to recover
|
|
1107
|
+
// an evicted Session, which would undo the just-confirmed close.
|
|
1108
|
+
let observationError = null;
|
|
1109
|
+
try { await bridge.flush(); } catch (error) { observationError = errorRecord(error); }
|
|
1110
|
+
const error = new Error(`ZCode ended the provider request with ${stopReason}, not end_turn`);
|
|
1111
|
+
error.code = stopReason === "cancelled" ? "zcode_turn_cancelled" : "zcode_turn_not_completed";
|
|
1112
|
+
error.details = { stop_reason: stopReason, provider_session_id: identity.providerSessionId, adapter_session_id: identity.adapterSessionId, ...(observationError ? { observation_error: observationError } : {}) };
|
|
1113
|
+
throw error;
|
|
1114
|
+
}
|
|
1115
|
+
let evidence;
|
|
1116
|
+
try { await bridge.flush(); evidence = await inspect(bridge, identity.adapterSessionId); }
|
|
1117
|
+
catch (cause) {
|
|
1118
|
+
const error = new Error("ZCode completed the native turn, but post-turn observation failed");
|
|
1119
|
+
error.code = "native_outcome_observation_failed";
|
|
1120
|
+
error.retryable = false;
|
|
1121
|
+
error.cause = cause;
|
|
1122
|
+
error.details = { native_outcome: { status: "completed", stop_reason: stopReason ?? "end_turn", completion_evidence: completionEvidence ?? "native" }, provider_session_id: identity.providerSessionId, adapter_session_id: identity.adapterSessionId, observation_error: errorRecord(cause) };
|
|
1123
|
+
throw error;
|
|
1124
|
+
}
|
|
1125
|
+
evidence.tool_calls = bridge.toolSummary(identity.adapterSessionId);
|
|
1126
|
+
const usage_ingest_error = await forwardUsageBestEffort(options, identity.providerSessionId, evidence.usage, evidence.tool_calls);
|
|
1127
|
+
if (initial_usage_error) evidence.initial_usage_error = initial_usage_error;
|
|
1128
|
+
if (usage_ingest_error) evidence.usage_ingest_error = usage_ingest_error;
|
|
1129
|
+
if (!options.allowBackground && (evidence.subagents.running ?? []).length) {
|
|
1130
|
+
await cancelTree(bridge, identity.adapterSessionId, evidence.subagents, identity.providerSessionId);
|
|
1131
|
+
fail("background ZCode subagents cannot outlive the one-shot dd-zcode connection; the live child tree was cancelled");
|
|
1132
|
+
}
|
|
1133
|
+
return receipt(options, { harness: "zcode-acp", provider_session_id: identity.providerSessionId, adapter_session_id: identity.adapterSessionId, assistant_text: bridge.assistantTextSince(identity.adapterSessionId, textCursor), turn, profile: profile_receipt, evidence: publicEvidence(evidence) });
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
export async function inspectSession(options) {
|
|
1137
|
+
requireJournal(options);
|
|
1138
|
+
return await withBridge(options, async (bridge) => await inspectSessionWithBridge(bridge, options));
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
export async function inspectSessionWithBridge(bridge, options) {
|
|
1142
|
+
requireJournal(options);
|
|
1143
|
+
const identity = await controlledIdentity(bridge, options);
|
|
1144
|
+
return receipt(options, { harness: "zcode-acp", provider_session_id: identity.providerSessionId, adapter_session_id: identity.adapterSessionId, ...publicEvidence(await inspect(bridge, identity.adapterSessionId)) });
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
export async function cancelSession(options) {
|
|
1148
|
+
requireJournal(options);
|
|
1149
|
+
return await withBridge(options, async (bridge) => await cancelSessionWithBridge(bridge, options));
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
export async function cancelSessionWithBridge(bridge, options) {
|
|
1153
|
+
requireJournal(options);
|
|
1154
|
+
const identity = await controlledIdentity(bridge, options);
|
|
1155
|
+
const before = await readZcodeSubagents(bridge, identity.adapterSessionId).catch(error => ({ observation_error: errorRecord(error) }));
|
|
1156
|
+
return receipt(options, { harness: "zcode-acp", provider_session_id: identity.providerSessionId, adapter_session_id: identity.adapterSessionId, ...await cancelTree(bridge, identity.adapterSessionId, before, identity.providerSessionId, options.knownChildSessionIds, options.neverPromptedRootProof) });
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
export async function forkSession(options) {
|
|
1160
|
+
requireJournal(options);
|
|
1161
|
+
return await withBridge(options, async (bridge) => await forkSessionWithBridge(bridge, options));
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
export async function forkSessionWithBridge(bridge, options) {
|
|
1165
|
+
requireJournal(options);
|
|
1166
|
+
if (!options.target || typeof options.target !== "object") fail("explicit --target-json is required");
|
|
1167
|
+
const identity = await controlledIdentity(bridge, options);
|
|
1168
|
+
const topology = await readZcodeSubagents(bridge, identity.adapterSessionId);
|
|
1169
|
+
if ((topology.running ?? []).length) fail("cannot fork while background children are running");
|
|
1170
|
+
const result = await bridge.request("session/fork", { sessionId: identity.adapterSessionId, target: options.target });
|
|
1171
|
+
if (!result.forkedSessionId) fail("ZCode fork returned no provider Session ID");
|
|
1172
|
+
return receipt(options, { harness: "zcode-acp", parent_provider_session_id: identity.providerSessionId, parent_adapter_session_id: identity.adapterSessionId, provider_session_id: result.forkedSessionId, adapter_session_id: result.forkedSessionId, target: options.target });
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
export async function promptFromFile(file) { return await readFile(absolute(file, "--prompt-file"), "utf8"); }
|