@deksden-com/dd-flow-cli 0.9.0-beta.16 → 0.9.0-beta.161
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 +965 -0
- package/README.md +139 -10
- package/dist/build-info.json +8 -8
- package/dist/cli/argument-values.js +38 -0
- package/dist/cli/command-inputs.js +417 -0
- package/dist/cli/command-metadata.js +303 -0
- package/dist/cli/command-option-descriptions.js +192 -0
- package/dist/cli/completion-cli.js +70 -0
- package/dist/cli/completion.js +57 -0
- package/dist/cli/delivery.js +108 -0
- package/dist/cli/diagnostic-invocation.js +62 -0
- package/dist/cli/diagnostic.js +131 -0
- package/dist/cli/error-output.js +62 -0
- package/dist/cli/executor-onboarding.js +76 -0
- package/dist/cli/flows.js +29 -0
- package/dist/cli/harnesses.js +84 -0
- package/dist/cli/help.js +521 -19
- package/dist/cli/hook-ingress.js +89 -0
- package/dist/cli/input-preparation.js +193 -0
- package/dist/cli/interactive-invocation.js +33 -0
- package/dist/cli/interactive.js +114 -0
- package/dist/cli/memory-fix-invocation.js +52 -0
- package/dist/cli/memory-fix.js +100 -0
- package/dist/cli/memory-flow-admission.js +54 -0
- package/dist/cli/memory-init-invocation.js +85 -0
- package/dist/cli/memory-init.js +250 -0
- package/dist/cli/memory-storage-admission.js +54 -0
- package/dist/cli/memory-storage-qualification.js +105 -0
- package/dist/cli/memory-storage-transition.js +123 -0
- package/dist/cli/memory-upgrade.js +189 -0
- package/dist/cli/models.js +39 -0
- package/dist/cli/native-hook-ingress.js +94 -0
- package/dist/cli/native-hook-preparation.js +82 -0
- package/dist/cli/operator-profiles.js +61 -0
- package/dist/cli/output-options.js +35 -0
- package/dist/cli/response-file.js +72 -0
- package/dist/cli/run-cli.js +2020 -555
- package/dist/cli/runtime-maintenance-diagnostics.js +46 -0
- package/dist/cli/runtime-maintenance-ingress.js +52 -0
- package/dist/cli/runtime-process.js +153 -0
- package/dist/cli/setup.js +150 -0
- package/dist/cli.js +103 -2
- package/dist/domain/delivery-flows.js +8 -0
- package/dist/domain/diagnostic-aspects.js +103 -0
- package/dist/domain/diagnostic-flows.js +8 -0
- package/dist/domain/distill-flow.js +20 -0
- package/dist/domain/interactive-flow.js +1 -0
- package/dist/domain/memory-fix-flow.js +2 -0
- package/dist/domain/review-fix-flow.js +2 -0
- package/dist/domain/source-snapshot-flows.js +4 -0
- package/dist/domain/stage-catalog.js +1 -1
- package/dist/domain/stage-handler-identities.js +28 -0
- package/dist/harness-runtime/DD-ZCODE.md +112 -0
- package/dist/harness-runtime/OPENCODE-LICENSE.txt +24 -0
- package/dist/harness-runtime/bin/dd-agy.mjs +71 -0
- package/dist/harness-runtime/bin/dd-codex.mjs +27 -0
- package/dist/harness-runtime/bin/dd-droid.mjs +34 -0
- package/dist/harness-runtime/bin/dd-grok.mjs +50 -0
- package/dist/harness-runtime/bin/dd-opencode.mjs +22 -0
- package/dist/harness-runtime/bin/dd-zcode.mjs +95 -0
- package/dist/harness-runtime/lib/adapter-timeouts.d.mts +5 -0
- package/dist/harness-runtime/lib/adapter-timeouts.mjs +34 -0
- package/dist/harness-runtime/lib/agy-error-boundary.mjs +87 -0
- package/dist/harness-runtime/lib/agy-hooks.d.mts +1 -0
- package/dist/harness-runtime/lib/agy-hooks.mjs +9 -0
- package/dist/harness-runtime/lib/agy-native-inspection.mjs +225 -0
- package/dist/harness-runtime/lib/agy-native-inventory.mjs +19 -0
- package/dist/harness-runtime/lib/agy-native_pb.js +117 -0
- package/dist/harness-runtime/lib/agy-terminal-envelope.mjs +74 -0
- package/dist/harness-runtime/lib/agy-transcript-stream.mjs +54 -0
- package/dist/harness-runtime/lib/daemon-observation.d.mts +3 -0
- package/dist/harness-runtime/lib/daemon-observation.mjs +171 -0
- package/dist/harness-runtime/lib/daemon-operations.d.mts +2 -0
- package/dist/harness-runtime/lib/daemon-operations.mjs +512 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.d.mts +7 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +1362 -0
- package/dist/harness-runtime/lib/dd-agy.mjs +105 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.mjs +271 -0
- package/dist/harness-runtime/lib/dd-codex.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex.mjs +986 -0
- package/dist/harness-runtime/lib/dd-droid-daemon.mjs +155 -0
- package/dist/harness-runtime/lib/dd-droid.mjs +739 -0
- package/dist/harness-runtime/lib/dd-grok-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +667 -0
- package/dist/harness-runtime/lib/dd-grok.mjs +182 -0
- package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +772 -0
- package/dist/harness-runtime/lib/dd-opencode.mjs +411 -0
- package/dist/harness-runtime/lib/dd-zcode-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +845 -0
- package/dist/harness-runtime/lib/dd-zcode.mjs +1239 -0
- package/dist/harness-runtime/lib/delegation-instructions.d.mts +16 -0
- package/dist/harness-runtime/lib/delegation-instructions.mjs +179 -0
- package/dist/harness-runtime/lib/delivery-command.mjs +84 -0
- package/dist/harness-runtime/lib/dispatch-fence.mjs +14 -0
- package/dist/harness-runtime/lib/driver-recovery.mjs +121 -0
- package/dist/harness-runtime/lib/droid-observation.mjs +70 -0
- package/dist/harness-runtime/lib/entity-references.d.mts +2 -0
- package/dist/harness-runtime/lib/entity-references.mjs +9 -0
- package/dist/harness-runtime/lib/lease-renewal.d.mts +8 -0
- package/dist/harness-runtime/lib/lease-renewal.mjs +37 -0
- package/dist/harness-runtime/lib/lifecycle-command.d.mts +20 -0
- package/dist/harness-runtime/lib/lifecycle-command.mjs +273 -0
- package/dist/harness-runtime/lib/lifecycle-contract.d.mts +11 -0
- package/dist/harness-runtime/lib/lifecycle-contract.mjs +20 -0
- package/dist/harness-runtime/lib/managed-daemon.d.mts +16 -0
- package/dist/harness-runtime/lib/managed-daemon.mjs +698 -0
- package/dist/harness-runtime/lib/managed-flow-env.mjs +15 -0
- package/dist/harness-runtime/lib/model-observations.mjs +113 -0
- package/dist/harness-runtime/lib/native-children.d.mts +34 -0
- package/dist/harness-runtime/lib/native-children.mjs +265 -0
- package/dist/harness-runtime/lib/native-hook-command.d.mts +8 -0
- package/dist/harness-runtime/lib/native-hook-command.mjs +306 -0
- package/dist/harness-runtime/lib/native-request-proof.d.mts +2 -0
- package/dist/harness-runtime/lib/native-request-proof.mjs +65 -0
- package/dist/harness-runtime/lib/native-streams.mjs +38 -0
- package/dist/harness-runtime/lib/observation-clock.d.mts +18 -0
- package/dist/harness-runtime/lib/observation-clock.mjs +95 -0
- package/dist/harness-runtime/lib/opencode-plugin.mjs +112 -0
- package/dist/harness-runtime/lib/operation-context.mjs +5 -0
- package/dist/harness-runtime/lib/operation-errors.d.mts +16 -0
- package/dist/harness-runtime/lib/operation-errors.mjs +140 -0
- package/dist/harness-runtime/lib/operation-progress.d.mts +7 -0
- package/dist/harness-runtime/lib/operation-progress.mjs +118 -0
- package/dist/harness-runtime/lib/process-json.d.mts +5 -0
- package/dist/harness-runtime/lib/process-json.mjs +200 -0
- package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
- package/dist/harness-runtime/lib/provider-overload-policy.d.mts +23 -0
- package/dist/harness-runtime/lib/provider-overload-policy.mjs +242 -0
- package/dist/harness-runtime/lib/runner-events.mjs +202 -0
- package/dist/harness-runtime/lib/runner-lock.mjs +42 -0
- package/dist/harness-runtime/lib/session-settlement.mjs +30 -0
- package/dist/harness-runtime/lib/setup-progress.d.mts +9 -0
- package/dist/harness-runtime/lib/setup-progress.mjs +53 -0
- package/dist/harness-runtime/lib/tool-observations.d.mts +21 -0
- package/dist/harness-runtime/lib/tool-observations.mjs +238 -0
- package/dist/harness-runtime/lib/zcode-native-hook-ownership.mjs +109 -0
- package/dist/harness-runtime/lib/zcode-usage.mjs +55 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-1y4a697q.js +81 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-2ny4vxct.js +92 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-6mptkzbc.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-7x67cf1q.js +52 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9rqn6x4v.js +16 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-9vdz0zwn.js +1661 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ecs5ftka.js +1466 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-frbwqjmf.js +145 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gne7h7mq.js +50 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-gx9wnkzr.js +105 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-m2eywc8w.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-nwcpakzx.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-q4g3afnw.js +623 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-qav6mfdz.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-sxt88q69.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-t1xa31vm.js +39 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ve4aws36.js +103 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vmytr4q8.js +4 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vt9jbch8.js +36 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-vyas3vgt.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-wjyeq25y.js +30 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-xgnj2w4v.js +20 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-y2etg5bs.js +9 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-ydhz1zrh.js +238 -0
- package/dist/harness-runtime/vendor/opencode-client/chunks/service-probe-z6qdwcsf.js +62 -0
- package/dist/harness-runtime/vendor/opencode-client/contract.d.ts +1 -0
- package/dist/harness-runtime/vendor/opencode-client/contract.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api/api.d.ts +2806 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api/api.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api.d.ts +4 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/api.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/client.d.ts +2530 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/client.js +15 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.d.ts +7 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client-error.js +6 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.d.ts +2529 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/client.js +8 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.d.ts +2 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/generated/index.js +12 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/index.d.ts +35 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/index.js +75 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/rpc.d.ts +20 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/rpc.js +11 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/service.d.ts +90 -0
- package/dist/harness-runtime/vendor/opencode-client/effect/service.js +187 -0
- package/dist/harness-runtime/vendor/opencode-client/package.json +84 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/api.d.ts +21 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/api.js +0 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/client.d.ts +252 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/client.js +14 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.d.ts +8 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client-error.js +6 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.d.ts +259 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/client.js +7 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.d.ts +3 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/index.js +79 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.d.ts +8932 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/generated/types.js +70 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/index.d.ts +6 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/index.js +83 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/rpc.d.ts +32 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/rpc.js +7 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/service.d.ts +24 -0
- package/dist/harness-runtime/vendor/opencode-client/promise/service.js +187 -0
- package/dist/harness-runtime/vendor/opencode-client/pty-handoff.d.ts +12 -0
- package/dist/harness-runtime/vendor/opencode-client/pty-handoff.js +14 -0
- package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.d.ts +20 -0
- package/dist/harness-runtime/vendor/opencode-client/rpc-runtime.js +14 -0
- package/dist/harness-runtime/vendor/opencode-client/service-contender.d.ts +37 -0
- package/dist/harness-runtime/vendor/opencode-client/service-contender.js +10 -0
- package/dist/harness-runtime/vendor/opencode-client/service-probe.d.ts +70 -0
- package/dist/harness-runtime/vendor/opencode-client/service-probe.js +16 -0
- package/dist/harness-runtime/vendor/opencode-client/service-timing.d.ts +12 -0
- package/dist/harness-runtime/vendor/opencode-client/service-timing.js +10 -0
- package/dist/harness-runtime/vendor/opencode-client/service-version.d.ts +2 -0
- package/dist/harness-runtime/vendor/opencode-client/service-version.js +6 -0
- package/dist/harness-runtime/vendor/opencode-client/service.d.ts +52 -0
- package/dist/harness-runtime/vendor/opencode-client/service.js +1 -0
- package/dist/harness-runtime/vendor/opencode-client/shared-events.d.ts +11 -0
- package/dist/harness-runtime/vendor/opencode-client/shared-events.js +8 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/connection.d.ts +37 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/connection.js +10 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/data.d.ts +211 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/data.js +18 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/index.d.ts +3 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/index.js +32 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/pty.d.ts +22 -0
- package/dist/harness-runtime/vendor/opencode-client/solid/pty.js +8 -0
- package/dist/harness-runtime/vendor/opencode-plugin.mjs +3 -0
- package/dist/protocol/local-files.js +18 -9
- package/dist/runtime/context.js +8 -4
- package/dist/schemas/agent-profile.schema.json +1 -1
- package/dist/schemas/code-review-decision.schema.json +9 -0
- package/dist/schemas/code-review-result.schema.json +1 -1
- package/dist/schemas/code-verification.schema.json +7 -5
- package/dist/schemas/code-work-batch.schema.json +2 -2
- package/dist/schemas/code-work-result.schema.json +2 -1
- package/dist/schemas/delivery-stage-result.schema.json +286 -0
- package/dist/schemas/diagnostic-stage-result.schema.json +354 -0
- package/dist/schemas/diagnostic-work-result.schema.json +296 -0
- package/dist/schemas/discovery-result.schema.json +184 -0
- package/dist/schemas/distill-stage-result.schema.json +667 -0
- package/dist/schemas/distill-work-result.schema.json +428 -0
- package/dist/schemas/execution-bundle.schema.json +215 -0
- package/dist/schemas/execution-settings.schema.json +162 -0
- package/dist/schemas/flow-descriptor.schema.json +54 -0
- package/dist/schemas/flow-run.schema.json +29 -0
- package/dist/schemas/harness-catalog.schema.json +80 -0
- package/dist/schemas/interactive-stage-result.schema.json +47 -0
- package/dist/schemas/interactive-work-result.schema.json +63 -0
- package/dist/schemas/mb-fix-stage-result.schema.json +93 -0
- package/dist/schemas/mb-init-compose-result.schema.json +457 -0
- package/dist/schemas/mb-init-discover-result.schema.json +500 -0
- package/dist/schemas/mb-init-finish-result.schema.json +422 -0
- package/dist/schemas/mb-init-preflight-result.schema.json +367 -0
- package/dist/schemas/mb-init-review-result.schema.json +396 -0
- package/dist/schemas/mb-init-verify-result.schema.json +391 -0
- package/dist/schemas/mb-upgrade-stage-result.schema.json +863 -0
- package/dist/schemas/memory-bootstrap-receipt-v2.schema.json +67 -0
- package/dist/schemas/memory-bootstrap-receipt.schema.json +354 -0
- package/dist/schemas/memory-composition-work-result.schema.json +38 -0
- package/dist/schemas/memory-repair-work-result.schema.json +56 -0
- package/dist/schemas/memory-review-correction-plan.schema.json +30 -0
- package/dist/schemas/memory-review-work-result.schema.json +43 -0
- package/dist/schemas/model-catalog.schema.json +153 -0
- package/dist/schemas/plan-review-decision.schema.json +12 -3
- package/dist/schemas/plan-review-result.schema.json +2 -2
- package/dist/schemas/profile-settings.schema.json +122 -0
- package/dist/schemas/project-flow-pack-manifest.schema.json +2 -2
- package/dist/schemas/release-impact.schema.json +3 -3
- package/dist/schemas/release-manifest.schema.json +291 -0
- package/dist/schemas/review-fix-stage-result.schema.json +210 -0
- package/dist/schemas/run-control-receipt.schema.json +119 -0
- package/dist/schemas/run-control-request.schema.json +36 -0
- package/dist/schemas/run-engine-binding.schema.json +1 -1
- package/dist/schemas/stage-start-response.schema.json +4 -0
- package/dist/schemas/vnext-protocol-plan.schema.json +3 -3
- package/dist/services/accepted-intent.js +139 -0
- package/dist/services/assigned-lifecycle.js +382 -0
- package/dist/services/branch-context.js +3 -3
- package/dist/services/canon.js +54 -15
- package/dist/services/check-environment.js +21 -0
- package/dist/services/check-runner-evidence.js +344 -0
- package/dist/services/check-runner-proof.js +373 -0
- package/dist/services/cleanup.js +133 -40
- package/dist/services/cli-operation-classifier.js +32 -64
- package/dist/services/cli-upgrade-allowlist.js +27 -0
- package/dist/services/code-checks.js +1426 -121
- package/dist/services/codex-capacity.js +367 -0
- package/dist/services/codex-hook-delivery.js +41 -0
- package/dist/services/command-context.js +80 -0
- package/dist/services/compatibility-preflight.js +16 -3
- package/dist/services/config.js +11 -8
- package/dist/services/continuation-outcome.js +25 -0
- package/dist/services/controller-child-capacity.js +71 -0
- package/dist/services/controller-child-observer.js +121 -0
- package/dist/services/controller-child-repair.js +117 -0
- package/dist/services/controller-execution.js +97 -0
- package/dist/services/controller-fanout.js +277 -0
- package/dist/services/controller-memory-task.js +45 -0
- package/dist/services/controller-memory-worker.js +51 -0
- package/dist/services/controller-result-authority.js +127 -0
- package/dist/services/controller-work-failure.js +198 -0
- package/dist/services/dashboard.js +145 -58
- package/dist/services/delivery-access.js +123 -0
- package/dist/services/delivery-approval.js +68 -0
- package/dist/services/delivery-artifact.js +86 -0
- package/dist/services/delivery-checks.js +178 -0
- package/dist/services/delivery-execution.js +181 -0
- package/dist/services/delivery-intents.js +212 -0
- package/dist/services/delivery-operation.js +81 -0
- package/dist/services/delivery-policy.js +91 -0
- package/dist/services/delivery-procedures.js +43 -0
- package/dist/services/delivery-request.js +99 -0
- package/dist/services/delivery-result.js +144 -0
- package/dist/services/delivery-stage-commands.js +26 -0
- package/dist/services/delivery-stage-entry.js +56 -0
- package/dist/services/delivery-stage-handlers.js +135 -0
- package/dist/services/delivery-stage-results.js +94 -0
- package/dist/services/descriptor-coordinator-session.js +37 -0
- package/dist/services/descriptor-root-work.js +53 -0
- package/dist/services/descriptor-run-settlement.js +107 -0
- package/dist/services/descriptor-run-start.js +495 -0
- package/dist/services/descriptor-stage-coordinator.js +318 -0
- package/dist/services/descriptor-stage-settlement.js +268 -0
- package/dist/services/descriptor-stage-start.js +175 -0
- package/dist/services/diagnostic-evidence.js +73 -0
- package/dist/services/diagnostic-repair-intake.js +123 -0
- package/dist/services/diagnostic-repair-source-intake.js +102 -0
- package/dist/services/diagnostic-report-publication.js +72 -0
- package/dist/services/diagnostic-report.js +97 -0
- package/dist/services/diagnostic-run-bundle.js +113 -0
- package/dist/services/diagnostic-snapshot.js +159 -0
- package/dist/services/diagnostic-stage-handlers.js +109 -0
- package/dist/services/diagnostic-stage-results.js +158 -0
- package/dist/services/diagnostic-start.js +134 -0
- package/dist/services/diagnostic-work-batch.js +34 -0
- package/dist/services/diagnostic-work-collection.js +97 -0
- package/dist/services/diagnostic-work-registration.js +101 -0
- package/dist/services/diagnostic-work-results.js +93 -0
- package/dist/services/discovery/agy-discovery.js +2 -0
- package/dist/services/discovery/codex-discovery.js +2 -0
- package/dist/services/discovery/droid-discovery.js +2 -0
- package/dist/services/discovery/grok-discovery.js +2 -0
- package/dist/services/discovery/opencode-discovery.js +2 -0
- package/dist/services/discovery/static.js +31 -0
- package/dist/services/discovery/zcode-discovery.js +2 -0
- package/dist/services/distill-document-publication.js +280 -0
- package/dist/services/distill-promotion.js +197 -0
- package/dist/services/distill-publication-custody.js +162 -0
- package/dist/services/distill-stage-handlers.js +249 -0
- package/dist/services/distill-stage-results.js +105 -0
- package/dist/services/distill-user-inputs.js +4 -0
- package/dist/services/distill-work-batch.js +42 -0
- package/dist/services/distill-work-collection.js +74 -0
- package/dist/services/distill-work-registration.js +93 -0
- package/dist/services/distill-work-results.js +98 -0
- package/dist/services/engines.js +354 -163
- package/dist/services/eval-snapshots.js +1326 -150
- package/dist/services/execution-bundles.js +275 -0
- package/dist/services/execution-policy.js +185 -0
- package/dist/services/executor-onboarding.js +56 -0
- package/dist/services/external-work-launch.js +316 -0
- package/dist/services/flow-catalog.js +54 -0
- package/dist/services/flow-descriptors.js +82 -0
- package/dist/services/frozen-engine.js +404 -0
- package/dist/services/generic-execution.js +165 -0
- package/dist/services/harness-adapter.js +342 -35
- package/dist/services/harness-config.js +2 -0
- package/dist/services/harness-discovery.js +29 -0
- package/dist/services/harness-doctor.js +75 -0
- package/dist/services/harness-probe-effects.js +51 -0
- package/dist/services/harness-version-probe.js +100 -0
- package/dist/services/hook-memory-routing.js +188 -0
- package/dist/services/hook-recovery-guard.js +14 -0
- package/dist/services/hook-session-observation.js +24 -0
- package/dist/services/hooks.js +813 -327
- package/dist/services/hybrid-contracts.js +479 -0
- package/dist/services/hybrid-digest.js +14 -0
- package/dist/services/ids.js +29 -1
- package/dist/services/installation-lock.js +95 -0
- package/dist/services/installed-flow-source.js +261 -0
- package/dist/services/interactive-attached-session.js +215 -0
- package/dist/services/interactive-baseline-custody.js +54 -0
- package/dist/services/interactive-before-versions.js +148 -0
- package/dist/services/interactive-bundle-data.js +85 -0
- package/dist/services/interactive-checks.js +179 -0
- package/dist/services/interactive-execution.js +33 -0
- package/dist/services/interactive-finish-integration.js +236 -0
- package/dist/services/interactive-git-baseline.js +230 -0
- package/dist/services/interactive-git-dispatch.js +278 -0
- package/dist/services/interactive-input-delivery.js +82 -0
- package/dist/services/interactive-mutation-scope.js +231 -0
- package/dist/services/interactive-request.js +287 -0
- package/dist/services/interactive-run-bundle.js +109 -0
- package/dist/services/interactive-stage-entry.js +41 -0
- package/dist/services/interactive-stage-handlers.js +289 -0
- package/dist/services/interactive-stage-observation.js +136 -0
- package/dist/services/interactive-stage-results.js +111 -0
- package/dist/services/interactive-user-inputs.js +142 -0
- package/dist/services/interactive-work-batch.js +43 -0
- package/dist/services/interactive-work-collection.js +119 -0
- package/dist/services/interactive-work-completion.js +180 -0
- package/dist/services/interactive-work-context.js +249 -0
- package/dist/services/interactive-work-history.js +112 -0
- package/dist/services/interactive-work-registration.js +124 -0
- package/dist/services/interactive-work-results.js +115 -0
- package/dist/services/interactive-work-start.js +155 -0
- package/dist/services/lanes.js +78 -65
- package/dist/services/lifecycle-command.js +1 -288
- package/dist/services/lifecycle-contract.js +1 -0
- package/dist/services/lifecycle-invocations.js +2549 -0
- package/dist/services/managed-daemon-binding.js +129 -0
- package/dist/services/managed-lease-monitor.js +133 -0
- package/dist/services/managed-processes.js +449 -69
- package/dist/services/memory-accepted-composition.js +78 -0
- package/dist/services/memory-baseline-preservation.js +65 -0
- package/dist/services/memory-completed-correction-wave.js +328 -0
- package/dist/services/memory-compose-handler.js +380 -0
- package/dist/services/memory-composition-acceptance.js +375 -0
- package/dist/services/memory-composition-graph.js +165 -0
- package/dist/services/memory-composition-registration.js +161 -0
- package/dist/services/memory-composition-work-custody.js +156 -0
- package/dist/services/memory-composition-work-results.js +108 -0
- package/dist/services/memory-corrected-review-acceptance.js +129 -0
- package/dist/services/memory-corrected-review-batch.js +37 -0
- package/dist/services/memory-corrected-review-findings.js +108 -0
- package/dist/services/memory-corrected-review-inputs.js +215 -0
- package/dist/services/memory-corrected-review-projection.js +78 -0
- package/dist/services/memory-corrected-review-receipt.js +178 -0
- package/dist/services/memory-corrected-review-registration.js +192 -0
- package/dist/services/memory-correction-check-receipt.js +235 -0
- package/dist/services/memory-correction-input-owner.js +6 -0
- package/dist/services/memory-custody-snapshot.js +36 -0
- package/dist/services/memory-discover-handler.js +104 -0
- package/dist/services/memory-finish-evidence.js +457 -0
- package/dist/services/memory-finish-handler.js +229 -0
- package/dist/services/memory-finish-integration.js +491 -0
- package/dist/services/memory-finish-readiness.js +169 -0
- package/dist/services/memory-fix-finish-integration.js +129 -0
- package/dist/services/memory-fix-git-dispatch.js +139 -0
- package/dist/services/memory-fix-intake-custody.js +11 -0
- package/dist/services/memory-fix-intake-settlement.js +127 -0
- package/dist/services/memory-fix-publication.js +196 -0
- package/dist/services/memory-fix-repair-admission.js +116 -0
- package/dist/services/memory-fix-request.js +141 -0
- package/dist/services/memory-fix-run-bundle.js +83 -0
- package/dist/services/memory-fix-scope.js +78 -0
- package/dist/services/memory-fix-sources.js +85 -0
- package/dist/services/memory-fix-stage-entry.js +24 -0
- package/dist/services/memory-fix-stage-handlers.js +282 -0
- package/dist/services/memory-fix-stage-results.js +106 -0
- package/dist/services/memory-fix-workspace.js +127 -0
- package/dist/services/memory-flow-user-inputs.js +88 -0
- package/dist/services/memory-git-integration.js +220 -0
- package/dist/services/memory-historical-corrected-review.js +212 -0
- package/dist/services/memory-historical-original-review.js +143 -0
- package/dist/services/memory-historical-repair-registration.js +105 -0
- package/dist/services/memory-historical-second-review.js +147 -0
- package/dist/services/memory-init-bootstrap-readiness.js +210 -0
- package/dist/services/memory-init-contexts.js +181 -0
- package/dist/services/memory-init-controller-start.js +119 -0
- package/dist/services/memory-init-engine-release.js +223 -0
- package/dist/services/memory-init-git-policy.js +165 -0
- package/dist/services/memory-init-lint-capability.js +203 -0
- package/dist/services/memory-init-native-capacity.js +309 -0
- package/dist/services/memory-init-preflight.js +334 -0
- package/dist/services/memory-init-project-bootstrap.js +599 -0
- package/dist/services/memory-init-request-candidate.js +213 -0
- package/dist/services/memory-init-request-input.js +82 -0
- package/dist/services/memory-init-resource-storage.js +125 -0
- package/dist/services/memory-init-run-allocation.js +81 -0
- package/dist/services/memory-init-run-bundle.js +135 -0
- package/dist/services/memory-init-start.js +181 -0
- package/dist/services/memory-init-startup-custody.js +80 -0
- package/dist/services/memory-init-startup-input.js +240 -0
- package/dist/services/memory-init-storage.js +91 -0
- package/dist/services/memory-init-stores.js +359 -0
- package/dist/services/memory-init-worktrunk-capability.js +174 -0
- package/dist/services/memory-layer-publication.js +611 -0
- package/dist/services/memory-layer.js +225 -0
- package/dist/services/memory-lint-config.js +194 -0
- package/dist/services/memory-native-review-copy.js +127 -0
- package/dist/services/memory-normal-flow-entry.js +87 -0
- package/dist/services/memory-ordinary-defaults.js +129 -0
- package/dist/services/memory-preflight-bootstrap-custody.js +115 -0
- package/dist/services/memory-preflight-handler.js +274 -0
- package/dist/services/memory-repair-current-view.js +299 -0
- package/dist/services/memory-repair-document-delta.js +489 -0
- package/dist/services/memory-repair-history.js +496 -0
- package/dist/services/memory-repair-physical-binding.js +194 -0
- package/dist/services/memory-repair-publication-files.js +152 -0
- package/dist/services/memory-repair-publication-intent.js +27 -0
- package/dist/services/memory-repair-publication-plan.js +162 -0
- package/dist/services/memory-repair-publication-source.js +225 -0
- package/dist/services/memory-repair-publication.js +795 -0
- package/dist/services/memory-repair-registration.js +240 -0
- package/dist/services/memory-repair-work-baseline.js +415 -0
- package/dist/services/memory-repair-work-completion.js +117 -0
- package/dist/services/memory-repair-work-custody.js +73 -0
- package/dist/services/memory-repair-work-results.js +108 -0
- package/dist/services/memory-repair-workspace.js +261 -0
- package/dist/services/memory-repair-writer-copy.js +736 -0
- package/dist/services/memory-result-binding.js +239 -0
- package/dist/services/memory-retained-review-authority.js +138 -0
- package/dist/services/memory-retained-review-results.js +70 -0
- package/dist/services/memory-retained-second-baseline.js +102 -0
- package/dist/services/memory-retained-second-repairs.js +162 -0
- package/dist/services/memory-review-acceptance.js +214 -0
- package/dist/services/memory-review-correction-plan.js +206 -0
- package/dist/services/memory-review-handler.js +585 -0
- package/dist/services/memory-review-inputs.js +246 -0
- package/dist/services/memory-review-limits.js +10 -0
- package/dist/services/memory-review-receipt.js +180 -0
- package/dist/services/memory-review-registration.js +117 -0
- package/dist/services/memory-review-work-custody.js +126 -0
- package/dist/services/memory-review-work-input.js +91 -0
- package/dist/services/memory-review-work-results.js +95 -0
- package/dist/services/memory-second-corrected-review-inputs.js +167 -0
- package/dist/services/memory-second-correction-plan.js +77 -0
- package/dist/services/memory-second-repair-baseline.js +91 -0
- package/dist/services/memory-second-repair-launch.js +614 -0
- package/dist/services/memory-second-repair-registration.js +88 -0
- package/dist/services/memory-second-repair-source.js +187 -0
- package/dist/services/memory-second-review-acceptance.js +115 -0
- package/dist/services/memory-source-observations.js +117 -0
- package/dist/services/memory-stage-dispatch.js +65 -0
- package/dist/services/memory-stage-results.js +290 -0
- package/dist/services/memory-tool-probe.js +25 -0
- package/dist/services/memory-upgrade-analysis-handler.js +243 -0
- package/dist/services/memory-upgrade-bootstrap-readiness.js +251 -0
- package/dist/services/memory-upgrade-evidence.js +100 -0
- package/dist/services/memory-upgrade-inventory.js +442 -0
- package/dist/services/memory-upgrade-publication-custody.js +73 -0
- package/dist/services/memory-upgrade-publication.js +413 -0
- package/dist/services/memory-upgrade-selection.js +430 -0
- package/dist/services/memory-upgrade-start.js +60 -0
- package/dist/services/memory-upgrade-storage-readiness.js +340 -0
- package/dist/services/memory-verification-acceptance.js +156 -0
- package/dist/services/memory-verification-checks.js +317 -0
- package/dist/services/memory-verification-receipt.js +160 -0
- package/dist/services/memory-verify-handler.js +137 -0
- package/dist/services/memory-work-result-json.js +12 -0
- package/dist/services/memory-work-result-preparation.js +64 -0
- package/dist/services/merge-queue.js +228 -149
- package/dist/services/merge-server.js +163 -35
- package/dist/services/migrations.js +164 -114
- package/dist/services/model-catalog.js +109 -0
- package/dist/services/model-discovery.js +46 -0
- package/dist/services/native-daemon-history.js +50 -0
- package/dist/services/native-session-control.js +39 -0
- package/dist/services/operator-profiles.js +236 -0
- package/dist/services/physical-startup.js +45 -0
- package/dist/services/plan-runtime.js +26 -11
- package/dist/services/plans.js +28 -25
- package/dist/services/project-flow-source.js +195 -0
- package/dist/services/project-git-integration.js +92 -0
- package/dist/services/project-registry.js +97 -0
- package/dist/services/project-summary.js +1 -1
- package/dist/services/projects.js +17 -87
- package/dist/services/prompt-delivery.js +71 -0
- package/dist/services/prompts.js +28 -17
- package/dist/services/protocols.js +163 -87
- package/dist/services/provider-capacity.js +463 -0
- package/dist/services/recovery-observation-budget.js +121 -0
- package/dist/services/recovery-snapshot-database.js +588 -0
- package/dist/services/release-archive-producer.js +52 -0
- package/dist/services/release-archive.js +143 -0
- package/dist/services/release-cache.js +150 -0
- package/dist/services/release-compatibility.js +18 -0
- package/dist/services/release-download.js +67 -0
- package/dist/services/release-envelope.js +104 -0
- package/dist/services/release-manifest.js +45 -0
- package/dist/services/release-payload.js +114 -0
- package/dist/services/release-producer.js +63 -0
- package/dist/services/release-provenance.js +160 -0
- package/dist/services/release-registry.js +66 -0
- package/dist/services/release-source.js +194 -0
- package/dist/services/repair-intents.js +29 -0
- package/dist/services/repair-receipt.js +13 -0
- package/dist/services/retained-native-hook.js +274 -0
- package/dist/services/review-copy.js +165 -0
- package/dist/services/review-deferral.js +23 -0
- package/dist/services/review-fix-handoff.js +82 -0
- package/dist/services/review-fix-intake-custody.js +17 -0
- package/dist/services/review-fix-stage-entry.js +43 -0
- package/dist/services/review-fix-stage-handlers.js +176 -0
- package/dist/services/review-fix-stage-results.js +86 -0
- package/dist/services/run-control-receipt.js +56 -0
- package/dist/services/run-control-worker.js +469 -0
- package/dist/services/run-control.js +1109 -0
- package/dist/services/run-controller-adapter.js +434 -0
- package/dist/services/run-controller-capture.js +186 -0
- package/dist/services/run-controller-process.js +226 -0
- package/dist/services/run-controller-recovery.js +335 -0
- package/dist/services/run-controller-state.js +106 -0
- package/dist/services/run-controller.js +1717 -0
- package/dist/services/run-engine-bindings.js +1 -1
- package/dist/services/run-fork.js +147 -0
- package/dist/services/run-health.js +149 -0
- package/dist/services/run-observations.js +152 -0
- package/dist/services/run-projection.js +22 -64
- package/dist/services/run-recovery-guard.js +107 -0
- package/dist/services/run-recovery-runtime.js +112 -0
- package/dist/services/run-recovery.js +485 -0
- package/dist/services/run-start-custody.js +189 -0
- package/dist/services/run-timeline.js +59 -0
- package/dist/services/runs.js +996 -271
- package/dist/services/runtime-budget.js +446 -0
- package/dist/services/runtime-command.js +43 -0
- package/dist/services/runtime-scope-capture.js +52 -0
- package/dist/services/runtime-scope-control.js +468 -0
- package/dist/services/runtime-scope-resume.js +605 -0
- package/dist/services/runtime-scope-stop.js +211 -0
- package/dist/services/runtime-scope-worker.js +314 -0
- package/dist/services/runtime-service.js +19 -14
- package/dist/services/schema-validation.js +10 -8
- package/dist/services/scope-writer-cleanup.js +27 -0
- package/dist/services/sessions.js +37 -91
- package/dist/services/setup.js +427 -0
- package/dist/services/stage-blocker.js +17 -7
- package/dist/services/stage-context.js +240 -18
- package/dist/services/stage-handlers.js +141 -0
- package/dist/services/stage-lifecycle.js +156 -119
- package/dist/services/stage-pause.js +222 -94
- package/dist/services/stage-report-renderer.js +230 -6
- package/dist/services/stage-work-graph.js +52 -0
- package/dist/services/technical-notes.js +31 -0
- package/dist/services/tooling.js +111 -44
- package/dist/services/usage.js +49 -101
- package/dist/services/verification-matrix.js +393 -0
- package/dist/services/version-status.js +1 -1
- package/dist/services/vnext-code-review.js +302 -91
- package/dist/services/vnext-code.js +325 -251
- package/dist/services/vnext-contracts.js +10 -1
- package/dist/services/vnext-execution-profile.js +80 -5
- package/dist/services/vnext-fanout.js +399 -20
- package/dist/services/vnext-merge.js +336 -114
- package/dist/services/vnext-plan-review.js +288 -133
- package/dist/services/vnext-plan.js +257 -110
- package/dist/services/vnext-protocolize.js +534 -96
- package/dist/services/vnext-specify.js +131 -84
- package/dist/services/vnext-workspace-policy.js +7 -2
- package/dist/services/work-registry.js +1950 -215
- package/dist/services/work-retry-context.js +55 -0
- package/dist/services/workspace-bootstrap.js +53 -15
- package/dist/services/workspace-files.js +85 -0
- package/dist/services/worktrees.js +108 -48
- package/dist/shared/entity-references.js +1 -0
- package/dist/shared/errors.js +27 -0
- package/dist/storage/agent-output.js +26 -0
- package/dist/storage/data-preservation.js +65 -0
- package/dist/storage/database.js +931 -40
- package/dist/storage/foreign-key-transaction.js +39 -0
- package/dist/storage/immutable-input.js +45 -0
- package/dist/storage/import-guard.js +159 -0
- package/dist/storage/memory-upgrade-schema-bootstrap.js +38 -0
- package/dist/storage/native-cleanup.js +52 -0
- package/dist/storage/paired-writer-migration.js +424 -0
- package/dist/storage/paths.js +17 -4
- package/dist/storage/projection-lock.js +73 -0
- package/dist/storage/work-references.js +23 -0
- package/dist/storage/writer-contract.js +96 -0
- package/dist/storage/writer-migration.js +346 -0
- package/dist/storage/writer-restore.js +217 -0
- package/dist/verification-components.json +384 -0
- package/package.json +52 -13
- package/tools/create-canon-release.mjs +33 -0
- package/tools/probe-codex-history.mjs +37 -0
- package/tools/produce-hybrid-release.mjs +41 -0
- package/tools/repair-paused-run-status.mjs +84 -0
- package/tools/verification-jobs.mjs +342 -0
|
@@ -0,0 +1,667 @@
|
|
|
1
|
+
import { managedFlowEnv } from './managed-flow-env.mjs';
|
|
2
|
+
import { SETUP_STARTUP_MS, startupObserver, observationDetails } from './setup-progress.mjs';
|
|
3
|
+
import { callDaemonObserved } from './daemon-observation.mjs';
|
|
4
|
+
import { canonicalNativeOperation } from './adapter-timeouts.mjs';
|
|
5
|
+
import { admitNativeRequestBuffer } from "./native-streams.mjs";
|
|
6
|
+
import { prepareDaemonRequest } from "./native-request-proof.mjs";
|
|
7
|
+
import { captureDispatchGuard, cancelPendingDispatch } from "./dispatch-fence.mjs";
|
|
8
|
+
import { errorRecord, isNativeHookObservationLoss, isProvenShellCorrection, reportedError } from "./operation-errors.mjs";
|
|
9
|
+
import { ToolObservations, acpToolObservation } from "./tool-observations.mjs";
|
|
10
|
+
import { acceptNativeHookRequest, readNativeHookFailures, writeNativeHookFailure } from "./native-hook-command.mjs";
|
|
11
|
+
import { parseLifecycleCommand, continuationCommand, commandOption } from "./lifecycle-command.mjs";
|
|
12
|
+
import { ObservationClock } from "./observation-clock.mjs";
|
|
13
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
14
|
+
import { chmod, copyFile, cp, lstat, mkdir, open, readFile, readdir, rename, unlink, writeFile } from "node:fs/promises";
|
|
15
|
+
import { spawn } from "node:child_process";
|
|
16
|
+
import net from "node:net";
|
|
17
|
+
import { durableDaemonDispatch as dispatchOperation, completeDaemonShutdown, trackDaemonConnections, retireDaemonEndpoint, retireFailedDaemon, assertDaemonDispatch, assertDaemonRestartable } from "./daemon-operations.mjs";
|
|
18
|
+
import { waitForSettlement } from "./session-settlement.mjs";
|
|
19
|
+
import os from "node:os";
|
|
20
|
+
import path from "node:path";
|
|
21
|
+
|
|
22
|
+
import { AcpBridge } from "./dd-zcode.mjs";
|
|
23
|
+
import { safeProviderError } from "./provider-overload-policy.mjs";
|
|
24
|
+
import { settledNativeTree, nativeExecutionReceipt } from "./native-children.mjs";
|
|
25
|
+
import { observeModel, readModelObservations } from "./model-observations.mjs";
|
|
26
|
+
import { cancelSessionWithBridge, createSessionWithBridge, doctor, forkSessionWithBridge, inspectSessionWithBridge, promptSessionWithBridge } from "./dd-grok.mjs";
|
|
27
|
+
import { prepareRuntimeOwner, cleanupFailedDaemonStart, confirmDaemonProcess, confirmDaemonStopped, finishDaemonProcess, scheduleDaemonProcessHeartbeat, registerDaemonProcess, startManagedBridge, stopProcessGroup } from "./managed-daemon.mjs";
|
|
28
|
+
|
|
29
|
+
const REQUEST_SCHEMA = "dd-grok/daemon-request@1";
|
|
30
|
+
const RESPONSE_SCHEMA = "dd-grok/daemon-response@1";
|
|
31
|
+
const STATE_SCHEMA = "dd-grok/daemon-state@1";
|
|
32
|
+
// Normalize raw socket callers too, before the shared layer reserves capacity.
|
|
33
|
+
export function durableDaemonDispatch(root, request, action, observe) {
|
|
34
|
+
return dispatchOperation(root, { ...request, operation: canonicalNativeOperation("grok", request.operation) }, action, observe);
|
|
35
|
+
}
|
|
36
|
+
class DaemonError extends Error { constructor(code, message, retryable = false, details) { super(message); this.code = code; this.retryable = retryable; this.details = details; } }
|
|
37
|
+
function absolute(value, label) { if (!value || !path.isAbsolute(value)) throw new DaemonError("invalid_path", `${label} must be an absolute path`); return path.resolve(value); }
|
|
38
|
+
function locations(stateDir) { const dir = absolute(stateDir, "--state-dir"); const localSocket = path.join(dir, "daemon.sock"); return { dir, socket: Buffer.byteLength(localSocket) < 100 ? localSocket : `/tmp/dd-grok-${createHash("sha256").update(dir).digest("hex").slice(0, 24)}.sock`, state: path.join(dir, "daemon.json"), log: path.join(dir, "daemon.log"), home: path.join(dir, "grok-home") }; }
|
|
39
|
+
async function readState(dir) { try { return JSON.parse(await readFile(locations(dir).state, "utf8")); } catch (error) { if (error.code === "ENOENT") return null; throw error; } }
|
|
40
|
+
async function writeState(file, value) { const temporary = `${file}.${process.pid}.tmp`; await writeFile(temporary, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 }); await rename(temporary, file); }
|
|
41
|
+
async function removeSocket(socket, dir) { if (socket !== locations(dir).socket) throw new DaemonError("unsafe_socket", "daemon socket does not match its state directory"); try { const info = await lstat(socket); if (!info.isSocket()) throw new DaemonError("unsafe_socket", "refusing to remove a non-socket daemon path"); await unlink(socket); } catch (error) { if (error.code !== "ENOENT") throw error; } }
|
|
42
|
+
const errorPayload = errorRecord;
|
|
43
|
+
function shellQuote(value) { return `'${String(value).replace(/'/g, "'\\''")}'`; }
|
|
44
|
+
|
|
45
|
+
function executable(command, args = []) { const resolved = command ?? process.env.DD_GROK_BIN ?? "grok"; return /\.[cm]?js$/.test(resolved) ? { command: process.execPath, args: [resolved, ...args] } : { command: resolved, args }; }
|
|
46
|
+
export function isolatedEnv(config) { return { ...process.env, HOME: config.grokHome, GROK_HOME: config.grokHome, ...(config.authPath ? { GROK_AUTH_PATH: config.authPath } : {}), ...managedFlowEnv(config),
|
|
47
|
+
DD_GROK_STATE_DIR: !config.noFlow && config.daemonId ? config.runtime_owner?.state_dir : undefined }; }
|
|
48
|
+
function within(candidate, root) { const resolved = path.resolve(candidate); const parent = path.resolve(root); return resolved === parent || resolved.startsWith(`${parent}${path.sep}`); }
|
|
49
|
+
|
|
50
|
+
async function writeIsolatedConfig(paths) {
|
|
51
|
+
const disabled = ["skills", "rules", "agents", "mcps", "hooks", "sessions"].map((key) => `${key} = false`).join("\n");
|
|
52
|
+
await writeFile(path.join(paths.home, "config.toml"), `# Managed by dd-grok. Do not add user configuration here.\n[cli]\nauto_update = false\n\n[compat.claude]\n${disabled}\n\n[compat.cursor]\n${disabled}\n\n[compat.codex]\nsessions = false\n`, { mode: 0o600 });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function inspectIsolatedConfig(config) {
|
|
56
|
+
const target = executable(config.bin, ["inspect", "--json"]);
|
|
57
|
+
const raw = await commandText(target.command, target.args, { cwd: config.cwd, env: isolatedEnv(config), phase: "native-inspect" });
|
|
58
|
+
let report; try { report = JSON.parse(raw); } catch (error) { throw new DaemonError("grok_inspect_invalid", `grok inspect did not return JSON: ${error.message}`); }
|
|
59
|
+
const foreign = [];
|
|
60
|
+
const checkSource = (kind, value, roots) => { const candidate = value?.source?.path ?? value?.path ?? value?.filePath; if (candidate && !roots.some((root) => within(candidate, root))) foreign.push({ kind, path: candidate }); };
|
|
61
|
+
for (const layer of report.configSources?.layers ?? []) checkSource("config", layer, [config.grokHome]);
|
|
62
|
+
for (const hook of report.hooks ?? []) checkSource("hook", hook, [config.grokHome]);
|
|
63
|
+
for (const skill of report.skills ?? []) checkSource("skill", skill, [config.grokHome]);
|
|
64
|
+
for (const agent of report.agents ?? []) checkSource("agent", agent, [config.grokHome]);
|
|
65
|
+
for (const instruction of report.projectInstructions ?? []) checkSource("instruction", instruction, [config.cwd, config.projectRoot]);
|
|
66
|
+
const compatEnabled = (report.externalCompat?.cells ?? []).filter((cell) => ["claude", "cursor", "codex"].includes(cell.vendor) && cell.enabled).map((cell) => `${cell.vendor}.${cell.surface}`);
|
|
67
|
+
const violations = [
|
|
68
|
+
...(foreign.length ? [{ kind: "foreign_sources", foreign }] : []),
|
|
69
|
+
...((report.plugins ?? []).length ? [{ kind: "plugins_loaded", count: report.plugins.length }] : []),
|
|
70
|
+
...((report.mcpServers ?? []).length ? [{ kind: "mcp_loaded", count: report.mcpServers.length }] : []),
|
|
71
|
+
...((report.permissions?.sources ?? []).length ? [{ kind: "permission_sources", count: report.permissions.sources.length }] : []),
|
|
72
|
+
...(report.externalCompat?.remoteSettingsLoaded ? [{ kind: "remote_compat" }] : []),
|
|
73
|
+
...(compatEnabled.length ? [{ kind: "compat_enabled", values: compatEnabled }] : []),
|
|
74
|
+
...((report.configWarnings ?? []).length ? [{ kind: "config_warnings", warnings: report.configWarnings }] : [])
|
|
75
|
+
];
|
|
76
|
+
if (violations.length) throw new DaemonError("grok_config_not_isolated", "Grok Build loaded configuration outside the managed isolation boundary", false, { violations });
|
|
77
|
+
return { checked_at: new Date().toISOString(), config_path: path.join(config.grokHome, "config.toml"), hooks: (report.hooks ?? []).length, skills: (report.skills ?? []).length, agents: (report.agents ?? []).length, config_layers: (report.configSources?.layers ?? []).map((layer) => layer.path), compatibility: "disabled" };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function writeHook(paths, config, entryPath) {
|
|
81
|
+
const hookDir = path.join(paths.home, "hooks"); await mkdir(hookDir, { recursive: true, mode: 0o700 });
|
|
82
|
+
const command = [shellQuote(process.execPath), shellQuote(entryPath), "hook", "handle", "--state-dir", shellQuote(paths.dir), "--project-root", shellQuote(config.projectRoot), "--dd-flow-bin", shellQuote(config.ddFlowBin), "--dd-flow-home", shellQuote(config.ddFlowHome)].join(" ");
|
|
83
|
+
const hook = { type: "command", command, timeout: 30 };
|
|
84
|
+
// ACP exposes the native terminal as run_terminal_command; interactive
|
|
85
|
+
// Grok clients still use Bash. Register both rather than assuming one UI.
|
|
86
|
+
await writeFile(path.join(hookDir, "dd-flow.json"), `${JSON.stringify({ hooks: { PreToolUse: ["Bash", "run_terminal_command"].map((matcher) => ({ matcher, hooks: [hook] })) } }, null, 2)}\n`, { mode: 0o600 });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function observeAcpToolCall(config, identity, message) {
|
|
90
|
+
// Grok's synchronous native hook owns admission. This ACP mirror is
|
|
91
|
+
// observational only; forwarding it used to create a second receipt after
|
|
92
|
+
// the command had already run.
|
|
93
|
+
void config; void identity; void message;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function copyAuth(paths, source) {
|
|
97
|
+
const target = path.join(paths.home, "auth.json");
|
|
98
|
+
try { await copyFile(source, target); await chmod(target, 0o600); return target; }
|
|
99
|
+
catch (error) { if (error.code === "ENOENT") return null; throw error; }
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const ARCHIVE_SCHEMA = "dd-grok/session-archive@1";
|
|
103
|
+
function sessionDirectory(home, sourceCwd, sessionId) {
|
|
104
|
+
if (!/^[A-Za-z0-9-]+$/.test(String(sessionId))) throw new DaemonError("invalid_session_id", "Grok Build session id is unsafe");
|
|
105
|
+
return path.join(home, "sessions", encodeURIComponent(absolute(sourceCwd, "Grok Build source cwd")), String(sessionId));
|
|
106
|
+
}
|
|
107
|
+
async function privateTree(root) {
|
|
108
|
+
const info = await lstat(root);
|
|
109
|
+
if (info.isSymbolicLink()) throw new DaemonError("unsafe_session_archive", "session archive must not contain symbolic links");
|
|
110
|
+
if (info.isDirectory()) {
|
|
111
|
+
await chmod(root, 0o700);
|
|
112
|
+
for (const entry of await readdir(root)) await privateTree(path.join(root, entry));
|
|
113
|
+
} else if (info.isFile()) await chmod(root, 0o600);
|
|
114
|
+
else throw new DaemonError("unsafe_session_archive", "session archive contains an unsupported filesystem entry");
|
|
115
|
+
}
|
|
116
|
+
async function mustNotExist(target, code, message) {
|
|
117
|
+
try { await lstat(target); throw new DaemonError(code, message); }
|
|
118
|
+
catch (error) { if (error.code === "ENOENT") return; throw error; }
|
|
119
|
+
}
|
|
120
|
+
async function exportSessionArchive(paths, sessionId, sourceCwd, archiveDir, versions) {
|
|
121
|
+
const destination = absolute(archiveDir, "--archive-dir");
|
|
122
|
+
await mustNotExist(destination, "archive_exists", "session archive directory already exists");
|
|
123
|
+
const source = sessionDirectory(paths.home, sourceCwd, sessionId);
|
|
124
|
+
const sourceInfo = await lstat(source);
|
|
125
|
+
if (!sourceInfo.isDirectory()) throw new DaemonError("session_archive_missing", "Grok Build session files are unavailable for export");
|
|
126
|
+
await privateTree(source);
|
|
127
|
+
await mkdir(destination, { recursive: false, mode: 0o700 });
|
|
128
|
+
await cp(source, path.join(destination, "session"), { recursive: true, errorOnExist: true });
|
|
129
|
+
const manifest = { schema_id: ARCHIVE_SCHEMA, provider_session_id: sessionId, source_cwd: sourceCwd, grok_version: versions.grok, exported_at: new Date().toISOString() };
|
|
130
|
+
await writeFile(path.join(destination, "manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`, { mode: 0o600 });
|
|
131
|
+
await privateTree(destination);
|
|
132
|
+
return { archive_dir: destination, ...manifest };
|
|
133
|
+
}
|
|
134
|
+
async function readSessionArchiveManifest(archiveDir) {
|
|
135
|
+
const source = absolute(archiveDir, "--session-archive");
|
|
136
|
+
let manifest;
|
|
137
|
+
try { manifest = JSON.parse(await readFile(path.join(source, "manifest.json"), "utf8")); }
|
|
138
|
+
catch (error) { throw new DaemonError("invalid_session_archive", `cannot read session archive manifest: ${error.message}`); }
|
|
139
|
+
if (manifest?.schema_id !== ARCHIVE_SCHEMA || !manifest.provider_session_id || !manifest.source_cwd) throw new DaemonError("invalid_session_archive", "session archive manifest is incompatible");
|
|
140
|
+
return { source, manifest };
|
|
141
|
+
}
|
|
142
|
+
async function materializeSessionArchive(paths, archiveDir) {
|
|
143
|
+
if (!archiveDir) return null;
|
|
144
|
+
const { source, manifest } = await readSessionArchiveManifest(archiveDir);
|
|
145
|
+
const target = sessionDirectory(paths.home, manifest.source_cwd, manifest.provider_session_id);
|
|
146
|
+
await mustNotExist(target, "session_archive_collision", "managed Grok home already contains this session");
|
|
147
|
+
const archiveSession = path.join(source, "session");
|
|
148
|
+
const sourceInfo = await lstat(archiveSession);
|
|
149
|
+
if (!sourceInfo.isDirectory()) throw new DaemonError("invalid_session_archive", "session archive payload is missing");
|
|
150
|
+
await privateTree(archiveSession);
|
|
151
|
+
await mkdir(path.dirname(target), { recursive: true, mode: 0o700 });
|
|
152
|
+
await cp(archiveSession, target, { recursive: true, errorOnExist: true });
|
|
153
|
+
await privateTree(target);
|
|
154
|
+
return { schema_id: ARCHIVE_SCHEMA, provider_session_id: manifest.provider_session_id, source_cwd: manifest.source_cwd, grok_version: manifest.grok_version ?? null, archive_dir: source };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export async function callDaemon(stateDir, operation, params = {}, timeoutMs = 30_000) {
|
|
158
|
+
operation = canonicalNativeOperation('grok', operation);
|
|
159
|
+
const request = prepareDaemonRequest({ schema_id: REQUEST_SCHEMA, id: process.env.DD_EVAL_OPERATION_ID ?? randomUUID(), operation, params });
|
|
160
|
+
return await callDaemonObserved({ stateDir, socket: locations(stateDir).socket, request, responseSchema: RESPONSE_SCHEMA, harness: 'grok', timeoutMs });
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function sameConfig(state, config) { return ["cwd", "journal", "bin", "authSource", "model", "reasoning", "mode", "ddFlowBin", "ddFlowHome", "projectRoot", "sessionArchive", "resourceHome", "noFlow"].every((key) => (state.config?.[key] ?? null) === (config[key] ?? null)); }
|
|
164
|
+
export function grokStartupFailure(state) {
|
|
165
|
+
if (!["failed", "cleanup_failed"].includes(state?.shutdown_state) || !state.startup_error) return null;
|
|
166
|
+
const failure = state.startup_error;
|
|
167
|
+
return new DaemonError(failure.code ?? "daemon_start_failed", failure.message ?? "Grok daemon failed before becoming ready", failure.retryable === true, failure.details);
|
|
168
|
+
}
|
|
169
|
+
export async function startDaemon(options) {
|
|
170
|
+
const paths = locations(options.stateDir); const archive = options.sessionArchive ? await readSessionArchiveManifest(options.sessionArchive) : null; const archiveCwd = archive ? absolute(archive.manifest.source_cwd, "Grok Build archive source cwd") : null; const cwd = options.cwd ? absolute(options.cwd, "--cwd") : archiveCwd; if (!cwd) throw new DaemonError("invalid_path", "--cwd must be an absolute path when no session archive is supplied"); if (archiveCwd && cwd !== archiveCwd) throw new DaemonError("session_archive_cwd_mismatch", "Grok Build session archives must start from their recorded source cwd", false, { archive_cwd: archiveCwd, requested_cwd: cwd }); const noFlow = options.noFlow === true; const authSource = options.authPath ? absolute(options.authPath, "--auth-path") : path.join(os.homedir(), ".grok", "auth.json"); const config = { cwd, journal: absolute(options.journal, "--journal"), bin: options.bin ?? process.env.DD_GROK_BIN ?? "grok", authSource, authPath: path.join(paths.home, "auth.json"), model: String(options.model ?? ""), reasoning: String(options.reasoning ?? ""), mode: options.mode ?? "bypassPermissions", ddFlowBin: noFlow ? null : absolute(options.ddFlowBin, "--dd-flow-bin"), ddFlowHome: noFlow ? null : absolute(options.ddFlowHome, "--dd-flow-home"), projectRoot: noFlow ? cwd : absolute(options.projectRoot, "--project-root"), resourceHome: noFlow ? null : (process.env.DD_FLOW_RESOURCE_HOME ? absolute(process.env.DD_FLOW_RESOURCE_HOME, "DD_FLOW_RESOURCE_HOME") : null), env: noFlow ? {} : { DD_FLOW_HOME: absolute(options.ddFlowHome, "--dd-flow-home") }, sessionArchive: archive?.source ?? null, grokHome: paths.home, noFlow };
|
|
171
|
+
await prepareRuntimeOwner(config, paths.dir);
|
|
172
|
+
if (!config.model || !config.reasoning) throw new DaemonError("profile_required", "--model and --reasoning are required");
|
|
173
|
+
await mkdir(paths.dir, { recursive: true, mode: 0o700 }); await chmod(paths.dir, 0o700);
|
|
174
|
+
let previous = await readState(paths.dir); assertDaemonRestartable(previous);
|
|
175
|
+
try { const status = await callDaemon(paths.dir, "daemon.ready", {}, 5000); if (!sameConfig(status, config)) throw new DaemonError("daemon_config_mismatch", "a daemon is already running with different configuration"); if (!status.ready) throw new DaemonError("daemon_start_in_progress", "an existing daemon is still starting", true); return { ...status, already_running: true }; } catch (error) { if (error.code !== "daemon_not_running") throw error; }
|
|
176
|
+
previous = await readState(paths.dir); assertDaemonRestartable(previous);
|
|
177
|
+
if (previous?.shutdown_state === "clean") await authorizeRetainedDaemonResume(paths.dir, previous, options.sessionId, config); if (previous?.shutdown_state === "running" && previous.active_tree) throw new DaemonError("invalid_harness_crash", "previous daemon died with an active or unproven Session tree", false, { daemon_id: previous.daemon_id });
|
|
178
|
+
await removeSocket(paths.socket, paths.dir); await mkdir(paths.home, { recursive: true, mode: 0o700 }); const importedSession = await materializeSessionArchive(paths, config.sessionArchive); const copiedAuth = await copyAuth(paths, authSource); await writeIsolatedConfig(paths); if (!noFlow) await writeHook(paths, config, absolute(options.entryPath, "dd-grok entry")); const versions = await doctor({ bin: config.bin }); if (importedSession && importedSession.grok_version !== versions.versions.grok) throw new DaemonError("session_archive_version_mismatch", "Grok Build session archive was created by a different CLI version", false, { archive_version: importedSession.grok_version, current_version: versions.versions.grok }); const configIsolation = await inspectIsolatedConfig(config);
|
|
179
|
+
const resourceProcess = await registerDaemonProcess(config, { kind: "grok-daemon", owner: `grok:${path.basename(paths.dir)}`, operation: `grok-daemon:${path.basename(paths.dir)}`, stdout: paths.log, stderr: paths.log });
|
|
180
|
+
const daemonId = randomUUID();
|
|
181
|
+
const state = { descendants: previous?.descendants ?? [], schema_id: STATE_SCHEMA, daemon_id: daemonId, pid: null, socket: paths.socket, started_at: new Date().toISOString(), shutdown_state: "starting", active_tree: false, recovery_status: previous?.shutdown_state === "running" ? "recovered_idle" : "clean_start", auth_status: copiedAuth ? "copied" : "absent", ...(importedSession ? { imported_session: importedSession } : {}), versions: versions.versions, config_isolation: configIsolation, config: { ...config, daemonId }, sessions: importedSession ? [{ provider_session_id: importedSession.provider_session_id, adapter_session_id: importedSession.provider_session_id, parent_provider_session_id: null, root_provider_session_id: importedSession.provider_session_id, cwd: importedSession.source_cwd }] : (previous?.sessions ?? []), resource_process: resourceProcess };
|
|
182
|
+
await writeState(paths.state, state); const log = await open(paths.log, "a", 0o600); const child = spawn(process.execPath, [absolute(options.entryPath, "dd-grok entry"), "daemon", "serve", "--state-dir", paths.dir], { cwd: config.cwd, env: isolatedEnv(state.config), detached: true, stdio: ["ignore", log.fd, log.fd] }); child.unref(); await log.close();
|
|
183
|
+
try { await confirmDaemonProcess(config, resourceProcess, child); const startupClock = new ObservationClock({ timeoutMs: SETUP_STARTUP_MS }); const startupProgress = startupObserver(paths.dir, state.daemon_id); while (!startupClock.sample(await startupProgress())) { await new Promise((resolve) => setTimeout(resolve, 100)); const failure = grokStartupFailure(await readState(paths.dir)); if (failure) throw failure; try { const status = await callDaemon(paths.dir, "daemon.ready", {}, 5000); if (status.ready) return status; } catch (error) { if (error.code !== "daemon_not_running" && error.code !== "daemon_timeout") throw error; } } throw Object.assign(new DaemonError("daemon_start_failed", "daemon did not become ready", true), { details: observationDetails(startupClock, 'daemon-start', child.pid) }); }
|
|
184
|
+
catch (error) { await cleanupFailedDaemonStart(config, resourceProcess, child, error, () => readState(paths.dir), daemonId); throw error; }
|
|
185
|
+
}
|
|
186
|
+
export async function stopDaemon(options) { const timeoutMs = options.timeoutMs ?? 30_000; return await confirmDaemonStopped(() => callDaemon(options.stateDir, "daemon.stop", { cancelTree: options.cancelTree === true }, timeoutMs), locations(options.stateDir).socket, timeoutMs, { readState: () => readState(options.stateDir) }); }
|
|
187
|
+
|
|
188
|
+
export class Runtime {
|
|
189
|
+
constructor(paths, state, bridge, initialized) { this.paths = paths; this.state = state; this.bridge = bridge; this.initialized = initialized; this.sessions = new Map((state.sessions ?? []).map((item) => [item.provider_session_id, item])); this.descendants = new Map((state.descendants ?? []).map(child => [child.provider_session_id, child])); for (const session of this.sessions.values()) if (session.parent_provider_session_id && !this.descendants.has(session.provider_session_id)) this.descendants.set(session.provider_session_id, { provider_session_id: session.provider_session_id, parent_provider_session_id: session.parent_provider_session_id, status: "unknown", source: "legacy.daemon_state", outcome_error: { code: "native_child_outcome_unavailable", message: "Retained Grok child has no durable outcome receipt" } }); this.toolUsage = new Map(); this.promptCreators = new Map(); this.childCreators = new Map(); this.childSpawnWaiters = new Map(); this.loadedSessionId = null; this.active = null; this.server = null; this.ready = false; this.persisting = Promise.resolve(); }
|
|
190
|
+
persist(patch = {}) { Object.assign(this.state, patch, { pid: process.pid, updated_at: new Date().toISOString(), sessions: [...this.sessions.values()], descendants: [...this.descendants.values()] }); const snapshot = structuredClone(this.state); this.persisting = this.persisting.catch(() => {}).then(() => writeState(this.paths.state, snapshot)); return this.persisting; }
|
|
191
|
+
async reduceTerminal(outcome) {
|
|
192
|
+
const session = this.sessions.get(outcome.session_id), turn = session?.native_prompt_operation;
|
|
193
|
+
if (outcome.method !== "session/prompt" || !turn || typeof outcome.connection_id !== "string" || !outcome.connection_id
|
|
194
|
+
|| !Number.isSafeInteger(outcome.request_id) || outcome.request_id < 1 || !Number.isSafeInteger(outcome.native_sequence) || outcome.native_sequence < 1 || turn.daemon_id !== this.state.daemon_id
|
|
195
|
+
|| turn.connection_id !== outcome.connection_id || turn.operation_id !== outcome.operation_id || turn.generation !== outcome.generation
|
|
196
|
+
|| turn.native_request_id !== outcome.request_id) return;
|
|
197
|
+
const message = outcome.terminal_response;
|
|
198
|
+
const kind = message?.error ? "failed" : message?.result?.stopReason;
|
|
199
|
+
if (!["failed", "end_turn", "cancelled"].includes(kind)) return;
|
|
200
|
+
const receipt = { kind, ...turn, request_id: outcome.request_id, native_sequence: outcome.native_sequence };
|
|
201
|
+
if (session.native_root_receipt && (session.native_root_receipt.kind !== kind || session.native_root_receipt.request_id !== outcome.request_id)) {
|
|
202
|
+
this.state.observation_error = { code: "native_terminal_outcome_conflict", message: "Conflicting native terminal outcomes for the same prompt" };
|
|
203
|
+
await this.persist();
|
|
204
|
+
throw new DaemonError(this.state.observation_error.code, this.state.observation_error.message);
|
|
205
|
+
}
|
|
206
|
+
if (session.native_root_receipt) return;
|
|
207
|
+
if (session.native_activity && session.native_activity.connection_id === outcome.connection_id && session.native_activity.sequence > outcome.native_sequence) {
|
|
208
|
+
return await this.invalidateTerminal(session);
|
|
209
|
+
}
|
|
210
|
+
session.native_root_receipt = receipt;
|
|
211
|
+
await this.persist();
|
|
212
|
+
}
|
|
213
|
+
async invalidateTerminal(session) {
|
|
214
|
+
session.native_root_receipt = null;
|
|
215
|
+
this.state.observation_error = { code: "native_activity_after_terminal", message: "Productive root activity followed its native terminal outcome" };
|
|
216
|
+
await this.persist();
|
|
217
|
+
}
|
|
218
|
+
async observeRootActivity(message, observation) {
|
|
219
|
+
const session = this.sessions.get(message?.params?.sessionId), turn = session?.native_prompt_operation;
|
|
220
|
+
if (!observation?.productive || !turn || session.parent_provider_session_id || turn.connection_id !== observation.connection_id) return;
|
|
221
|
+
if (session.native_activity?.connection_id !== observation.connection_id || session.native_activity.sequence < observation.sequence) session.native_activity = { connection_id: observation.connection_id, sequence: observation.sequence };
|
|
222
|
+
const receipt = session.native_root_receipt;
|
|
223
|
+
if (receipt?.connection_id === observation.connection_id && observation.sequence > receipt.native_sequence) await this.invalidateTerminal(session);
|
|
224
|
+
}
|
|
225
|
+
async drainTerminal() { await this.bridge.terminalOutcomes; await this.persisting; if (this.bridge.terminalProjectionError) throw this.bridge.terminalProjectionError; }
|
|
226
|
+
settledRoot(id) { return !this.active && !this.rootRunning({ provider_session_id: id }) && this.topology(id).every(child => !this.rootRunning({ provider_session_id: child.provider_session_id }) && !child.outcome_error && !child.activity_status); }
|
|
227
|
+
async restoreTerminals() {
|
|
228
|
+
if (!this.state.config?.journal) return;
|
|
229
|
+
let contents;
|
|
230
|
+
try { contents = await readFile(this.state.config.journal, "utf8"); } catch (error) { if (error.code === "ENOENT") return; throw error; }
|
|
231
|
+
for (const line of contents.split("\n").filter(Boolean)) {
|
|
232
|
+
const event = JSON.parse(line);
|
|
233
|
+
if (["native_terminal_outcome", "native_terminal_after_hook_failure"].includes(event.kind)) await this.reduceTerminal(event.payload);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
options(params) { const sessionId = params.sessionId; const session = sessionId ? this.sessions.get(sessionId) : null; const root = session?.root_provider_session_id ?? sessionId; return { ...this.state.config, ...params, cwd: params.cwd ?? session?.cwd ?? this.state.config.cwd, initialized: this.initialized, daemonId: this.state.daemon_id, rootProviderSessionId: root, toolUsage: this.toolUsage, allowBackground: true, rootSettled: id => this.settledRoot(id), liveSession: params.liveSession ?? Boolean(sessionId && sessionId === this.loadedSessionId) }; }
|
|
237
|
+
track(result, before = null) { if (!result?.provider_session_id) return; const session = this.sessions.get(result.provider_session_id); const root = result.parent_provider_session_id ? (this.sessions.get(result.parent_provider_session_id)?.root_provider_session_id ?? result.parent_provider_session_id) : (session?.root_provider_session_id ?? result.provider_session_id); this.sessions.set(result.provider_session_id, { ...session, native_root_receipt: session?.native_root_receipt ?? null, provider_session_id: result.provider_session_id, adapter_session_id: result.adapter_session_id ?? result.provider_session_id, parent_provider_session_id: result.parent_provider_session_id ?? session?.parent_provider_session_id ?? null, root_provider_session_id: root, cwd: result.cwd ?? result.target?.newCwd ?? result.info?.cwd ?? session?.cwd ?? null }); for (const child of this.running(result)) this.recordDescendant({ ...child, stale_activity: Boolean(before && before.get(child.sessionId ?? child.subagentId ?? child.provider_session_id) !== this.descendants.get(child.sessionId ?? child.subagentId ?? child.provider_session_id)) }, root, "x.ai/subagent/list_running", "running"); for (const child of result.descendants ?? []) this.recordDescendant(child, root, child.source ?? "x.ai/subagent/event", child.status ?? "unknown"); }
|
|
238
|
+
recordDescendant(value, root, source, fallback, nativeSpawn = false) {
|
|
239
|
+
const id = value?.provider_session_id ?? value?.session_id ?? value?.sessionId ?? value?.childSessionId ?? value?.subagentId ?? value?.child_session_id ?? value?.subagent_id;
|
|
240
|
+
const parent = value?.parent_provider_session_id ?? value?.parentSessionId ?? value?.parent_session_id ?? root;
|
|
241
|
+
if (typeof id !== "string" || !id || typeof parent !== "string" || !parent) return;
|
|
242
|
+
if (id === parent) throw new DaemonError("session_parent_conflict", "Grok child Session cannot be its own native parent", false, { provider_session_id: id, observed_parent_provider_session_id: parent });
|
|
243
|
+
const prior = this.sessions.get(id);
|
|
244
|
+
if (prior?.parent_provider_session_id && prior.parent_provider_session_id !== parent) {
|
|
245
|
+
throw new DaemonError("session_parent_conflict", "Grok child Session was observed with conflicting native parents", false, {
|
|
246
|
+
provider_session_id: id,
|
|
247
|
+
retained_parent_provider_session_id: prior.parent_provider_session_id,
|
|
248
|
+
observed_parent_provider_session_id: parent
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
const parentSession = this.sessions.get(parent);
|
|
252
|
+
const rootProviderSessionId = prior?.root_provider_session_id
|
|
253
|
+
?? parentSession?.root_provider_session_id
|
|
254
|
+
?? root;
|
|
255
|
+
const cwd = value?.cwd ?? prior?.cwd ?? parentSession?.cwd ?? this.state.config?.cwd ?? null;
|
|
256
|
+
// A descendant is a managed native Session, not merely a liveness item.
|
|
257
|
+
// Register it in the same registry used by inspect/hook.resolve so a
|
|
258
|
+
// parentless inspection cannot downgrade it into a second root.
|
|
259
|
+
const descendant = this.descendants.get(id);
|
|
260
|
+
const attempt = value.attempt_id;
|
|
261
|
+
const retired = descendant?.retired_attempt_ids ?? [];
|
|
262
|
+
if (attempt && retired.includes(attempt)) return;
|
|
263
|
+
const spawned = value.sessionUpdate === "subagent_spawned";
|
|
264
|
+
const newAttempt = spawned && typeof attempt === "string" && attempt !== descendant?.attempt_id;
|
|
265
|
+
const terminal = status => ["completed", "failed", "cancelled"].includes(status);
|
|
266
|
+
const status = value?.status ?? value?.state ?? (fallback === "unknown" ? descendant?.status ?? fallback : fallback);
|
|
267
|
+
if (attempt && descendant?.attempt_id && attempt !== descendant.attempt_id && !newAttempt) throw new DaemonError("native_child_execution_ambiguous", "Grok child event has no matching native spawn attempt", false, { session_id: id, attempt_id: attempt });
|
|
268
|
+
if (source === "x.ai/subagent/event" && terminal(status) && descendant?.attempt_id && !attempt) throw new DaemonError("native_child_execution_ambiguous", "Grok terminal event omits the retained child attempt identity", false, { session_id: id });
|
|
269
|
+
if (!newAttempt && terminal(descendant?.status) && status === "running") {
|
|
270
|
+
if (value.stale_activity || attempt === descendant.attempt_id && attempt) return;
|
|
271
|
+
const error = { code: "native_child_execution_ambiguous", message: "Grok child activity follows a terminal result without native new-attempt evidence", details: { session_id: id } };
|
|
272
|
+
this.descendants.set(id, { ...descendant, activity_status: "running", outcome_error: error });
|
|
273
|
+
if (spawned) throw new DaemonError(error.code, error.message, false, error.details);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
if (!newAttempt && terminal(descendant?.status) && terminal(status) && descendant.status !== status) throw new DaemonError("native_child_outcome_conflict", "Grok child has contradictory terminal outcomes", false, { session_id: id, previous: descendant.status, observed: status });
|
|
277
|
+
if (nativeSpawn && spawned && typeof attempt === "string" && attempt && source === "x.ai/subagent/event") {
|
|
278
|
+
const promptId = value.parent_prompt_id;
|
|
279
|
+
const creator = typeof promptId === "string" && promptId ? this.promptCreators.get(JSON.stringify([parent, promptId])) : null;
|
|
280
|
+
const old = this.childCreators.get(id);
|
|
281
|
+
if (old && old.attempt === attempt && (old.parent !== parent || typeof promptId === "string" && promptId && old.promptId !== promptId)) throw new DaemonError("native_child_execution_ambiguous", "Grok child attempt has conflicting native creator", false, { session_id: id });
|
|
282
|
+
if (creator) this.childCreators.set(id, { parent, promptId, attempt, creator });
|
|
283
|
+
else if (newAttempt) this.childCreators.delete(id);
|
|
284
|
+
}
|
|
285
|
+
this.sessions.set(id, {
|
|
286
|
+
...prior,
|
|
287
|
+
provider_session_id: id,
|
|
288
|
+
adapter_session_id: prior?.adapter_session_id ?? id,
|
|
289
|
+
parent_provider_session_id: parent,
|
|
290
|
+
root_provider_session_id: rootProviderSessionId,
|
|
291
|
+
cwd,
|
|
292
|
+
native_root_receipt: prior?.native_root_receipt ?? null
|
|
293
|
+
});
|
|
294
|
+
this.bridge.registerModelSession?.(id, id, this.state.config, parent, true, newAttempt);
|
|
295
|
+
this.descendants.set(id, {
|
|
296
|
+
provider_session_id: id,
|
|
297
|
+
parent_provider_session_id: parent,
|
|
298
|
+
status,
|
|
299
|
+
source,
|
|
300
|
+
...(terminal(status) && typeof attempt === "string" && attempt ? { terminal_turn_id: attempt,
|
|
301
|
+
...(typeof value.observed_at === "string" ? { terminal_observed_at: value.observed_at } : {}),
|
|
302
|
+
...(value.provider_error && typeof value.provider_error === "object" ? { provider_error: safeProviderError(value.provider_error), native_source: "grok.subagent.event" } : {}),
|
|
303
|
+
...(value.native_turn_items && typeof value.native_turn_items === "object" ? { native_turn_items: value.native_turn_items } : {}) } : {}),
|
|
304
|
+
...(attempt || descendant?.attempt_id ? { attempt_id: attempt ?? descendant.attempt_id } : {}),
|
|
305
|
+
...(newAttempt && descendant?.attempt_id ? { retired_attempt_ids: [...retired, descendant.attempt_id] } : retired.length ? { retired_attempt_ids: retired } : {}),
|
|
306
|
+
...(!newAttempt && !terminal(status) && descendant?.outcome_error ? { outcome_error: descendant.outcome_error } : {})
|
|
307
|
+
});
|
|
308
|
+
if (this.childCreators.has(id)) for (const wake of this.childSpawnWaiters.get(id) ?? []) wake();
|
|
309
|
+
if (spawned && typeof value.model === "string" && value.model.trim()) {
|
|
310
|
+
return { sessionId: id, parentSessionId: parent, attemptId: attempt ?? null, parentPromptId: value.parent_prompt_id ?? null, model: value.model.trim() };
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
observeSubagentEvent(message) {
|
|
314
|
+
const root = message?.params?.sessionId, update = message?.params?.update;
|
|
315
|
+
const kind = String(update?.sessionUpdate ?? update?.type ?? message?.method ?? "");
|
|
316
|
+
if (!root) return [];
|
|
317
|
+
const modelFacts = [];
|
|
318
|
+
if (/subagent/i.test(kind)) {
|
|
319
|
+
for (const value of [update?.subagent, update?.subagentInfo, update?.child, update?.result, update]) {
|
|
320
|
+
const fact = this.recordDescendant(value, root, "x.ai/subagent/event", /fail|error/i.test(kind) ? "failed" : /cancel/i.test(kind) ? "cancelled" : /complete|finish|done/i.test(kind) ? "completed" : "running", value === update);
|
|
321
|
+
if (fact) modelFacts.push(fact);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
// list_running omits completed children. Retain native tool receipts, never
|
|
325
|
+
// extract identities from the model's prose or from arbitrary shell output.
|
|
326
|
+
this.subagentCalls ??= new Map();
|
|
327
|
+
const key = `${root}:${update?.toolCallId}`;
|
|
328
|
+
const tool = update?._meta?.["x.ai/tool"]?.name ?? (kind === "tool_call" ? update.title : null);
|
|
329
|
+
if (["spawn_subagent", "get_command_or_subagent_output"].includes(tool) && !this.subagentCalls.has(key)) this.subagentCalls.set(key, { name: tool, input: update.rawInput, attempts: new Map([...this.descendants].map(([id, child]) => [id, child.attempt_id])) });
|
|
330
|
+
const call = this.subagentCalls.get(key), name = call?.name, output = update?.rawOutput;
|
|
331
|
+
if (call && update.rawInput) call.input = update.rawInput;
|
|
332
|
+
if (kind !== "tool_call_update" || update.status !== "completed") return modelFacts;
|
|
333
|
+
if (name === "spawn_subagent" && output?.type === "Text") {
|
|
334
|
+
const id = /^subagent_id:\s*(\S+)\s*$/m.exec(output.text ?? "")?.[1];
|
|
335
|
+
if (id) this.recordDescendant({ session_id: id, stale_activity: !call.attempts.has(id) || call.attempts.get(id) !== this.descendants.get(id)?.attempt_id }, root, "x.ai/tool/spawn_subagent", "running");
|
|
336
|
+
}
|
|
337
|
+
if (name === "get_command_or_subagent_output" && output?.type === "TaskOutput") {
|
|
338
|
+
const results = output.Result ? [output.Result] : output.MultiResult?.results ?? [];
|
|
339
|
+
if (!Array.isArray(results)) throw new DaemonError("native_child_outcome_unavailable", "Grok TaskOutput has a malformed result list");
|
|
340
|
+
// TaskNotFound is prose in the native contract; identity comes from the
|
|
341
|
+
// matching tool input, never by scraping its diagnostic message.
|
|
342
|
+
if (output.TaskNotFound && typeof call.input?.task_id === "string") results.push({ task_id: call.input.task_id, status: "not_found" });
|
|
343
|
+
let firstError;
|
|
344
|
+
for (const result of results) {
|
|
345
|
+
const child = this.descendants.get(result?.task_id);
|
|
346
|
+
if (!child || call.attempts.get(result.task_id) !== child.attempt_id) continue;
|
|
347
|
+
if (result.status === "not_found") {
|
|
348
|
+
const error = new DaemonError("native_child_outcome_unavailable", "Grok cannot retrieve the owned child's native outcome", false, { session_id: result.task_id, tool_call_id: update.toolCallId });
|
|
349
|
+
this.descendants.set(result.task_id, { ...child, outcome_error: errorPayload(error) });
|
|
350
|
+
firstError ??= error;
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
if (!["running", "completed", "failed", "cancelled"].includes(result.status)) throw new DaemonError("native_child_outcome_unavailable", "Grok child returned an unknown native outcome", false, { session_id: result.task_id, status: result.status });
|
|
354
|
+
this.recordDescendant({ session_id: result.task_id, status: result.status, attempt_id: child.attempt_id }, root, "x.ai/tool/TaskOutput", "unknown");
|
|
355
|
+
}
|
|
356
|
+
if (firstError) throw firstError;
|
|
357
|
+
}
|
|
358
|
+
return modelFacts;
|
|
359
|
+
}
|
|
360
|
+
topology(root) { if (this.state.observation_error) { const error = this.state.observation_error; throw new DaemonError(error.code, error.message, false, error.details); } return [...this.descendants.values()].filter((child) => child.parent_provider_session_id === root || this.sessions.get(child.provider_session_id)?.root_provider_session_id === root); }
|
|
361
|
+
observeToolCall(message) { this.toolObservations ??= new ToolObservations(); this.toolObservations.add(acpToolObservation(message, { harness: "grok-acp" })); const sessionId = message?.params?.sessionId; if (sessionId) this.toolUsage.set(sessionId, this.toolObservations.summary(sessionId)); }
|
|
362
|
+
running(result) { return result?.evidence?.subagents?.subagents ?? result?.subagents?.subagents ?? []; }
|
|
363
|
+
treeRunning(result) {
|
|
364
|
+
const evidence = result?.evidence ?? result;
|
|
365
|
+
const children = evidence?.subagents?.subagents;
|
|
366
|
+
return this.rootRunning({ ...result, ...evidence }) || !Array.isArray(children) || children.length > 0 || Boolean(evidence?.subagents?.observation_error)
|
|
367
|
+
|| this.topology(result?.provider_session_id).some(child => this.rootRunning({ provider_session_id: child.provider_session_id }) || child.outcome_error || child.activity_status);
|
|
368
|
+
}
|
|
369
|
+
rootRunning(result) {
|
|
370
|
+
const status = String(result?.info?.status ?? result?.info?.state ?? result?.info?.data?.status ?? "unknown").toLowerCase();
|
|
371
|
+
const child = this.descendants.get(result?.provider_session_id);
|
|
372
|
+
// Grok may retire a completed child Session before session/info can be
|
|
373
|
+
// inspected. A retained TaskOutput for that exact attempt is native
|
|
374
|
+
// terminal proof; missing session/info alone is not.
|
|
375
|
+
if (status === "unknown" && ["x.ai/tool/TaskOutput", "x.ai/subagent/event"].includes(child?.source) && child.attempt_id && !child.outcome_error && !child.activity_status
|
|
376
|
+
&& ["completed", "failed", "cancelled"].includes(child.status)) return false;
|
|
377
|
+
// Grok 1.0.17 omits status from session/info. Only a durable native
|
|
378
|
+
// creation/end_turn receipt can then establish an idle root. Dispatch
|
|
379
|
+
// clears that receipt before sending any subsequent productive request.
|
|
380
|
+
const receipt = this.sessions.get(result?.provider_session_id)?.native_root_receipt;
|
|
381
|
+
if (status === "unknown" && ["created", "end_turn", "cancelled", "failed"].includes(receipt?.kind)) return false;
|
|
382
|
+
return !["completed", "failed", "cancelled", "stopped", "idle"].includes(status);
|
|
383
|
+
}
|
|
384
|
+
async refreshTree(cancel = false) {
|
|
385
|
+
await this.drainTerminal();
|
|
386
|
+
const running = [];
|
|
387
|
+
for (const session of [...this.sessions.values()].filter(item => !item.parent_provider_session_id)) {
|
|
388
|
+
const id = session.provider_session_id;
|
|
389
|
+
const before = new Map(this.descendants);
|
|
390
|
+
let observed = await inspectSessionWithBridge(this.bridge, this.options({ sessionId: id, liveSession: true }));
|
|
391
|
+
this.track(observed, before);
|
|
392
|
+
const rootActive = Boolean(this.active);
|
|
393
|
+
if (cancel && (rootActive || this.treeRunning(observed))) {
|
|
394
|
+
await cancelSessionWithBridge(this.bridge, this.options({ sessionId: id, liveSession: true }));
|
|
395
|
+
const beforeCancelledInspection = new Map(this.descendants);
|
|
396
|
+
observed = await inspectSessionWithBridge(this.bridge, this.options({ sessionId: id, liveSession: true }));
|
|
397
|
+
this.track(observed, beforeCancelledInspection);
|
|
398
|
+
}
|
|
399
|
+
if (this.active || this.treeRunning(observed)) running.push(id);
|
|
400
|
+
}
|
|
401
|
+
await this.persist({ active_tree: running.length > 0 || Boolean(this.active) });
|
|
402
|
+
return running;
|
|
403
|
+
}
|
|
404
|
+
async requireSettled() { const running = await this.refreshTree(); if (running.length) throw new DaemonError("tree_not_settled", "daemon still owns a running Session tree", false, { sessions: running }); }
|
|
405
|
+
async productive(name, task, sessionId, operationId = null) {
|
|
406
|
+
assertDaemonDispatch(this.paths?.dir);
|
|
407
|
+
if (this.active) throw new DaemonError("operation_busy", `${this.active} is already running`, true);
|
|
408
|
+
this.active = name;
|
|
409
|
+
const current = this.nativeHookOperation = { operationId, sessionId, generation: (this.hookGeneration = (this.hookGeneration ?? 0) + 1), requests: new Map(), guard: captureDispatchGuard(this) };
|
|
410
|
+
let rejectHook;
|
|
411
|
+
const hookFailed = new Promise((_, reject) => { rejectHook = reject; });
|
|
412
|
+
void hookFailed.catch(() => {});
|
|
413
|
+
current.reject = error => { if (this.nativeHookOperation !== current || current.error) return; current.error = error; this.bridge.lifecycleFailure ??= error; this.bridge.notificationError = this.bridge.lifecycleFailure; this.bridge.rejectAll(this.bridge.lifecycleFailure); rejectHook(this.bridge.lifecycleFailure); };
|
|
414
|
+
const timer = setInterval(() => { void this.observeHookFailures(current).catch(error => current.reject(error)); }, 100);
|
|
415
|
+
// Only a new native turn invalidates its own root proof. Creation, fork
|
|
416
|
+
// and local archive export must not erase an unrelated Session's receipt.
|
|
417
|
+
if (name === "session.prompt" && this.sessions.has(sessionId)) {
|
|
418
|
+
const session = this.sessions.get(sessionId);
|
|
419
|
+
if (typeof operationId === "string" && operationId) {
|
|
420
|
+
current.nativePromptId = randomUUID();
|
|
421
|
+
// Retain only for this daemon lifetime: a late native spawn or hook
|
|
422
|
+
// cannot be safely distinguished from an expired one by a timer.
|
|
423
|
+
// After restart the in-memory correlation is deliberately unavailable.
|
|
424
|
+
this.promptCreators.set(JSON.stringify([sessionId, current.nativePromptId]), current);
|
|
425
|
+
}
|
|
426
|
+
session.native_root_receipt = null;
|
|
427
|
+
this.bridge.dispatchGeneration = current.generation;
|
|
428
|
+
session.native_prompt_operation = { daemon_id: this.state.daemon_id, connection_id: this.bridge.connectionId, operation_id: operationId, generation: current.generation,
|
|
429
|
+
...(current.nativePromptId ? { native_prompt_id: current.nativePromptId } : {}) };
|
|
430
|
+
}
|
|
431
|
+
try {
|
|
432
|
+
await this.persist({ active_tree: true, active_operation: name });
|
|
433
|
+
assertDaemonDispatch(this.paths?.dir);
|
|
434
|
+
const before = new Map(this.descendants);
|
|
435
|
+
const result = await Promise.race([task(), hookFailed]);
|
|
436
|
+
await this.observeHookFailures(current);
|
|
437
|
+
if (current.error) throw current.error;
|
|
438
|
+
const hookDiagnostics = (this.bridge.lifecycleDiagnostics ?? []).filter(item => item.operation_id === current.operationId && item.generation === current.generation);
|
|
439
|
+
if (hookDiagnostics.length) result.native_hook_diagnostics = [...new Map([...(result.native_hook_diagnostics ?? []), ...hookDiagnostics].map(item => [JSON.stringify(item), item])).values()];
|
|
440
|
+
await this.drainTerminal();
|
|
441
|
+
this.track(result, before);
|
|
442
|
+
const session = this.sessions.get(result.provider_session_id);
|
|
443
|
+
if (session && ["session.create", "session.fork"].includes(name)) session.native_root_receipt = { kind: "created", daemon_id: this.state.daemon_id };
|
|
444
|
+
// Archive requires an idle tree before entering this exclusive section
|
|
445
|
+
// and performs no native work. Its file receipt is not tree evidence.
|
|
446
|
+
const running = name === "session.archive" ? false : this.treeRunning(result);
|
|
447
|
+
await this.persist({ active_tree: running, active_operation: null });
|
|
448
|
+
return { ...result, descendants: this.topology(result.provider_session_id), settled: !running };
|
|
449
|
+
} catch (error) {
|
|
450
|
+
// A matched JSON-RPC error completes this native prompt request. It is
|
|
451
|
+
// root-turn proof, not proof that any descendants have stopped. A lost
|
|
452
|
+
// response, bridge error, or failure before dispatch earns no receipt.
|
|
453
|
+
try { await this.drainTerminal(); } catch (storage) { error.cleanup_error ??= errorRecord(storage); }
|
|
454
|
+
try { await this.persist({ active_tree: true, active_operation: null }); }
|
|
455
|
+
catch (storage) { error.cleanup_error = errorRecord(storage); }
|
|
456
|
+
throw error;
|
|
457
|
+
}
|
|
458
|
+
finally { clearInterval(timer); if (!current.nativePromptId) current.requests.clear(); if (this.nativeHookOperation === current) this.nativeHookOperation = null; this.active = null; }
|
|
459
|
+
}
|
|
460
|
+
async observeHookFailures(current = this.nativeHookOperation) {
|
|
461
|
+
if (!current || this.nativeHookOperation !== current) return;
|
|
462
|
+
while (current.reading) {
|
|
463
|
+
await current.reading;
|
|
464
|
+
if (this.nativeHookOperation !== current) return;
|
|
465
|
+
}
|
|
466
|
+
const reading = readNativeHookFailures(this.paths.dir, current.requests, current, error => this.acceptHookFailure(current, error));
|
|
467
|
+
current.reading = reading;
|
|
468
|
+
try { await reading; } finally { if (current.reading === reading) current.reading = null; }
|
|
469
|
+
}
|
|
470
|
+
acceptHookFailure(current, error) {
|
|
471
|
+
if (this.nativeHookOperation !== current) return;
|
|
472
|
+
const unconfirmed = error.details?.native_hook_binding?.native_creator === "unconfirmed";
|
|
473
|
+
if (!unconfirmed && isProvenShellCorrection(error)) return;
|
|
474
|
+
if (unconfirmed || isNativeHookObservationLoss(error)) {
|
|
475
|
+
const diagnostic = { ...errorRecord(error), session_id: current.sessionId, operation_id: current.operationId,
|
|
476
|
+
generation: current.generation, delivery_stage: "hook_observation", completeness: "incomplete" };
|
|
477
|
+
this.bridge.lifecycleDiagnostics ??= [];
|
|
478
|
+
this.bridge.lifecycleDiagnostics.push(diagnostic);
|
|
479
|
+
try { void Promise.resolve(this.bridge.journal?.write("native_hook_observation_failure", diagnostic)).catch(() => {}); } catch { /* diagnostic is optional */ }
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
current.reject(error);
|
|
483
|
+
}
|
|
484
|
+
async acceptHook(identity, sessionId, payload) {
|
|
485
|
+
const input = payload?.tool_input ?? payload?.toolInput;
|
|
486
|
+
const command = input?.command ?? input?.cmd;
|
|
487
|
+
if ((payload?.hook_event_name ?? payload?.hookEventName) !== "PreToolUse" || typeof command !== "string" || (!["lifecycle", "auxiliary"].includes(parseLifecycleCommand(command).participation) && !continuationCommand(command))) return identity;
|
|
488
|
+
const active = this.nativeHookOperation, tool = payload.tool_use_id ?? payload.toolUseId ?? payload.tool_call_id;
|
|
489
|
+
const root = identity.rootProviderSessionId;
|
|
490
|
+
const source = sessionId !== root ? this.childCreators.get(sessionId) : null;
|
|
491
|
+
// An older Grok may not surface creator metadata. Preserve its ordinary
|
|
492
|
+
// stateful handler, but never turn the current prompt into guessed proof.
|
|
493
|
+
if (sessionId !== root && !source) return identity;
|
|
494
|
+
const current = sessionId !== root ? source?.creator : active;
|
|
495
|
+
const pending = [...(this.bridge.pending?.values() ?? [])].some(request => request.method === "session/prompt" && request.sessionId === root);
|
|
496
|
+
const nativePrompt = payload.prompt_id ?? payload.promptId;
|
|
497
|
+
if (!current || current.sessionId !== root || !tool || sessionId === root && (!pending || active !== current)
|
|
498
|
+
|| sessionId === root && typeof nativePrompt === "string" && nativePrompt && nativePrompt !== current.nativePromptId
|
|
499
|
+
|| sessionId !== root && (source.parent !== identity.parentProviderSessionId || this.promptCreators.get(JSON.stringify([source.parent, source.promptId])) !== current))
|
|
500
|
+
throw new DaemonError("native_hook_unproven", "Native hook has no exact source-bound Session operation");
|
|
501
|
+
current.guard(); assertDaemonDispatch(this.paths.dir);
|
|
502
|
+
const nativePayload = { ...payload, _meta: { ...(payload._meta ?? {}), ddGrok: { ...identity } } };
|
|
503
|
+
const binding = await acceptNativeHookRequest(this.paths.dir, { daemon_id: this.state.daemon_id, operation_id: current.operationId, turn_generation: current.generation,
|
|
504
|
+
provider_session_id: sessionId, root_provider_session_id: root, parent_provider_session_id: identity.parentProviderSessionId ?? null,
|
|
505
|
+
tool_call_id: tool, event: "PreToolUse", command_sha256: createHash("sha256").update(command).digest("hex"), native_cwd: payload.cwd ?? null,
|
|
506
|
+
native_creator: source || typeof nativePrompt === "string" && nativePrompt && nativePrompt === current.nativePromptId ? "confirmed" : "unconfirmed",
|
|
507
|
+
...(source ? { source_harness: "grok-acp", native_command: command, native_payload: nativePayload } : {}) }, current);
|
|
508
|
+
current.guard();
|
|
509
|
+
if (sessionId === root && this.nativeHookOperation !== current || sessionId !== root && this.childCreators.get(sessionId) !== source)
|
|
510
|
+
throw new DaemonError("native_hook_unproven", "Native hook operation changed during acceptance");
|
|
511
|
+
current.requests.set(binding.request_id, { current, binding });
|
|
512
|
+
const parsed = parseLifecycleCommand(command);
|
|
513
|
+
const fastAck = Boolean(source && parsed.kind === "standalone" && parsed.invocation.operation === "work_start" && commandOption(parsed.invocation, "invocation-id"));
|
|
514
|
+
return { ...identity, nativeHookBinding: binding, ...(fastAck ? { fastAck: true } : {}) };
|
|
515
|
+
}
|
|
516
|
+
async waitForChildCreator(sessionId, deadlineAt) {
|
|
517
|
+
const remaining = deadlineAt - Date.now();
|
|
518
|
+
if (this.childCreators.has(sessionId) || !Number.isSafeInteger(deadlineAt) || remaining <= 0 || remaining > 30_000) return;
|
|
519
|
+
// Leave most of the existing host budget to the legacy handler if this
|
|
520
|
+
// provider omits the optional spawn correlation; do not poll the provider.
|
|
521
|
+
const waitMs = Math.floor(remaining / 3);
|
|
522
|
+
if (!waitMs) return;
|
|
523
|
+
await new Promise(resolve => {
|
|
524
|
+
const waiters = this.childSpawnWaiters.get(sessionId) ?? new Set();
|
|
525
|
+
const wake = () => { clearTimeout(timer); waiters.delete(wake); if (!waiters.size) this.childSpawnWaiters.delete(sessionId); resolve(); };
|
|
526
|
+
const timer = setTimeout(wake, waitMs);
|
|
527
|
+
waiters.add(wake); this.childSpawnWaiters.set(sessionId, waiters);
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
hookOwner(binding) {
|
|
531
|
+
if (!binding?.request_id) return null;
|
|
532
|
+
const owners = new Set([this.nativeHookOperation, ...this.promptCreators.values()]);
|
|
533
|
+
for (const owner of owners) {
|
|
534
|
+
if (!owner || owner.operationId !== binding.operation_id || owner.generation !== binding.turn_generation) continue;
|
|
535
|
+
const accepted = owner.requests.get(binding.request_id);
|
|
536
|
+
if (accepted?.current === owner && JSON.stringify(accepted.binding) === JSON.stringify(binding)) return owner;
|
|
537
|
+
}
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
async dispatch(operation, params, operationId = null) {
|
|
541
|
+
const assertDispatch = captureDispatchGuard(this);
|
|
542
|
+
if (["session.create", "session.prompt", "session.fork", "session.archive"].includes(operation)) assertDaemonDispatch(this.paths.dir);
|
|
543
|
+
if (operation === "daemon.ready") return { ready: this.ready, daemon_id: this.state.daemon_id, pid: process.pid, socket: this.paths.socket, versions: this.state.versions, config_isolation: this.state.config_isolation, shutdown_state: this.state.shutdown_state, recovery_status: this.state.recovery_status, auth_status: this.state.auth_status, active_tree: this.state.active_tree, active_operation: this.active, sessions: [...this.sessions.values()], config: this.state.config };
|
|
544
|
+
if (operation === "daemon.status") { let observationError; if (!this.active && !this.state.shutdown?.phases?.execution && !this.state.shutdown?.physical_only) try { await this.refreshTree(); } catch (error) { observationError = errorPayload(error); } return { ...await this.dispatch("daemon.ready", {}), observation_error: observationError ?? this.state.observation_error ?? null }; }
|
|
545
|
+
if (operation === "hook.resolve") { const sessionId = String(params.sessionId ?? ""); if (!sessionId) throw new DaemonError("hook_identity_missing", "hook has no sessionId"); let session = this.sessions.get(sessionId); if ((!session || session.parent_provider_session_id) && !this.childCreators.has(sessionId) && this.promptCreators.size) { await this.waitForChildCreator(sessionId, params.hookDeadlineAt); session = this.sessions.get(sessionId); } if (!session) { const roots = [...this.sessions.values()].filter((item) => !item.parent_provider_session_id); if (roots.length !== 1) throw new DaemonError("hook_identity_unknown", "unknown hook Session has no unique root"); const observed = await inspectSessionWithBridge(this.bridge, this.options({ sessionId, liveSession: true })); this.track(observed); session = this.sessions.get(sessionId); if (!session || session.root_provider_session_id !== roots[0].provider_session_id) throw new DaemonError("hook_identity_unknown", "hook Session is not a member of the retained native tree"); await this.persist(); } return await this.acceptHook({ daemonId: this.state.daemon_id, rootProviderSessionId: session.root_provider_session_id, parentProviderSessionId: session.parent_provider_session_id ?? null, observedProfile: this.bridge.modelProfiles?.get(sessionId) ?? {} }, sessionId, params.payload); }
|
|
546
|
+
if (operation === "hook.verify") {
|
|
547
|
+
if (params.daemonId !== this.state.daemon_id) throw new DaemonError("native_hook_unproven", "Native hook daemon identity differs");
|
|
548
|
+
const owner = this.hookOwner(params.binding);
|
|
549
|
+
if (!owner) throw new DaemonError("native_hook_unproven", "Native hook has no matching retained operation");
|
|
550
|
+
owner.guard(); assertDaemonDispatch(this.paths.dir);
|
|
551
|
+
return { verified: true, nativeHookBinding: params.binding };
|
|
552
|
+
}
|
|
553
|
+
if (operation === "hook.failed") {
|
|
554
|
+
if (params.binding !== undefined || params.failure !== undefined) {
|
|
555
|
+
const binding = params.binding, failure = params.failure;
|
|
556
|
+
const current = this.hookOwner(binding);
|
|
557
|
+
while (current?.reading) await current.reading;
|
|
558
|
+
const accepted = current?.requests.get(binding?.request_id);
|
|
559
|
+
if (!current || this.hookOwner(binding) !== current || accepted?.current !== current || JSON.stringify(accepted.binding) !== JSON.stringify(binding)
|
|
560
|
+
|| typeof failure?.code !== "string" || typeof failure?.message !== "string")
|
|
561
|
+
throw new DaemonError("native_hook_unproven", "Hook failure has no exact current owned delivery");
|
|
562
|
+
current.guard(); assertDaemonDispatch(this.paths.dir);
|
|
563
|
+
if (this.nativeHookOperation !== current) {
|
|
564
|
+
// The creator has returned. Retain the bound failure under its
|
|
565
|
+
// original operation, without rejecting the newer productive turn.
|
|
566
|
+
await writeNativeHookFailure(this.paths.dir, binding, reportedError(failure, "Native hook failed"));
|
|
567
|
+
return { observed: true, retained: true };
|
|
568
|
+
}
|
|
569
|
+
current.requests.delete(binding.request_id);
|
|
570
|
+
const error = reportedError(failure, "Native hook failed");
|
|
571
|
+
error.details = { ...error.details, native_hook_binding: binding };
|
|
572
|
+
this.acceptHookFailure(current, error);
|
|
573
|
+
return { observed: true };
|
|
574
|
+
}
|
|
575
|
+
await this.observeHookFailures(); return { observed: true };
|
|
576
|
+
}
|
|
577
|
+
if (operation === "session.create") return await this.productive(operation, async () => { const result = await createSessionWithBridge(this.bridge, { ...this.options(params), onSessionCreated: async (session) => { this.track(session); this.loadedSessionId = session.provider_session_id; await this.persist(); } }, this.initialized); this.loadedSessionId = result.provider_session_id; return result; }, null, operationId);
|
|
578
|
+
if (operation === "session.prompt") { await this.requireSettled(); return await this.productive(operation, async () => { const result = await promptSessionWithBridge(this.bridge, { ...this.options(params), assertDispatch, operationId, nativePromptId: this.nativeHookOperation?.nativePromptId }); this.loadedSessionId = params.sessionId; return result; }, params.sessionId, operationId); }
|
|
579
|
+
if (operation === "session.fork") { await this.requireSettled(); return await this.productive(operation, async () => { const result = await forkSessionWithBridge(this.bridge, this.options(params)); this.loadedSessionId = params.sessionId; return result; }); }
|
|
580
|
+
if (operation === "session.inspect" || operation === "session.resume") { const before = new Map(this.descendants); const result = await inspectSessionWithBridge(this.bridge, this.options({ ...params, liveSession: true })); this.track(result, before); await this.persist(); const settled = !this.active && !this.treeRunning(result); const descendants = this.topology(result.provider_session_id); const terminal = this.sessions.get(result.provider_session_id)?.native_root_receipt; return nativeExecutionReceipt({ ...result, settled, descendants, ...settledNativeTree(result.provider_session_id, descendants, settled, !this.active && !this.rootRunning(result)), ...(terminal?.kind === "failed" && terminal.request_id !== undefined ? { terminal_turn_id: String(terminal.request_id) } : {}) }, { daemonId: this.state.daemon_id, generation: this.controlGeneration ?? 0 }); }
|
|
581
|
+
if (operation === "session.cancel") { if (!this.sessions.has(params.sessionId)) throw new DaemonError("session_identity_mismatch", "cancellation Session is not owned by this daemon"); cancelPendingDispatch(this); const result = await cancelSessionWithBridge(this.bridge, this.options({ ...params, liveSession: true })); await this.persist({ active_tree: Boolean(this.active) || !result.settled }); return result; }
|
|
582
|
+
if (operation === "session.archive") { await this.requireSettled(); return await this.productive(operation, async () => { let session = this.sessions.get(params.sessionId); if (!session?.cwd) { const result = await inspectSessionWithBridge(this.bridge, this.options({ ...params, liveSession: true })); this.track(result); session = this.sessions.get(result.provider_session_id); } return await exportSessionArchive(this.paths, session.provider_session_id, absolute(session.cwd, "Grok Build session cwd"), params.archiveDir, this.state.versions); }); }
|
|
583
|
+
if (operation === "daemon.stop") {
|
|
584
|
+
return await completeDaemonShutdown(this.paths.dir, this.state, patch => this.persist(patch), async () => {
|
|
585
|
+
if (params.cancelTree) cancelPendingDispatch(this);
|
|
586
|
+
await waitForSettlement({
|
|
587
|
+
knownSessions: () => [...this.sessions.values()].filter(s => !s.parent_provider_session_id).map(s => s.provider_session_id),
|
|
588
|
+
observe: async () => ({ sessions: await this.refreshTree(), active: Boolean(this.active), active_operation: this.active }),
|
|
589
|
+
...(params.cancelTree ? { cancel: id => cancelSessionWithBridge(this.bridge, this.options({ sessionId: id, liveSession: true })) } : {})
|
|
590
|
+
});
|
|
591
|
+
return { clean: true, settled: true, native_settled: true };
|
|
592
|
+
}, [
|
|
593
|
+
["provider", () => this.bridge.close()],
|
|
594
|
+
["provider_resource", () => finishDaemonProcess(this.state.config, this.state.provider_process)],
|
|
595
|
+
["auth", () => unlink(path.join(this.paths.home, "auth.json")).catch(error => { if (error.code !== "ENOENT") throw error; })],
|
|
596
|
+
["daemon_resource", () => finishDaemonProcess(this.state.config, this.state.resource_process)]
|
|
597
|
+
], undefined, { cancelTree: params.cancelTree === true });
|
|
598
|
+
}
|
|
599
|
+
throw new DaemonError("unknown_operation", `unknown daemon operation: ${operation}`);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
export async function serveDaemon(stateDir) {
|
|
604
|
+
const paths = locations(stateDir); const state = await readState(paths.dir); if (!state || state.schema_id !== STATE_SCHEMA) throw new DaemonError("daemon_state_missing", "daemon state is missing or incompatible"); let runtime;
|
|
605
|
+
const bridge = new AcpBridge({ ...state.config, initializeTimeoutMs: 60_000, commandArgs: ["agent", "--no-leader", "--always-approve", "--model", state.config.model, "--reasoning-effort", state.config.reasoning, "stdio"], env: isolatedEnv(state.config), clientInfo: { name: "dd-grok", version: "0.1.0" }, permission: "allow", onNotification: async (message, observation) => {
|
|
606
|
+
const sessionId = message?.params?.sessionId;
|
|
607
|
+
if (!runtime || !sessionId) return;
|
|
608
|
+
try {
|
|
609
|
+
await runtime.observeRootActivity(message, observation);
|
|
610
|
+
runtime.observeToolCall(message);
|
|
611
|
+
for (const fact of runtime.observeSubagentEvent(message)) {
|
|
612
|
+
const source = { channel: "x.ai/subagent_spawned", scope: "configured", attempt_id: fact.attemptId, parent_prompt_id: fact.parentPromptId,
|
|
613
|
+
event_sha256: createHash("sha256").update(JSON.stringify(fact)).digest("hex") };
|
|
614
|
+
await observeModel({ journal: state.config.journal, harness: "grok-acp", sessionId: fact.sessionId, parentSessionId: fact.parentSessionId,
|
|
615
|
+
requested: state.config, observed: { model: fact.model }, source, evidence: "configured" });
|
|
616
|
+
bridge.modelProfiles?.set(fact.sessionId, { model: fact.model });
|
|
617
|
+
}
|
|
618
|
+
} catch (error) {
|
|
619
|
+
// Native notifications are fire-and-forget. Persist topology conflicts
|
|
620
|
+
// for the next inspect/status call instead of creating an unhandled
|
|
621
|
+
// rejection that can kill the daemon without a causal receipt.
|
|
622
|
+
try { await runtime.persist({ observation_error: errorPayload(error) }); } catch { /* status/inspect will report the original native failure */ }
|
|
623
|
+
}
|
|
624
|
+
const session = runtime.sessions.get(sessionId);
|
|
625
|
+
const rootProviderSessionId = session?.root_provider_session_id ?? sessionId;
|
|
626
|
+
await observeAcpToolCall(state.config, { daemonId: state.daemon_id, rootProviderSessionId, parentProviderSessionId: session?.parent_provider_session_id ?? null, observedProfile: bridge.modelProfiles?.get(sessionId) ?? {} }, message);
|
|
627
|
+
} }); const initialized = await startManagedBridge({ config: state.config, state, bridge, kind: "grok-provider", publish: snapshot => writeState(paths.state, snapshot), log: paths.log }); runtime = new Runtime(paths, state, bridge, initialized);
|
|
628
|
+
bridge.configure({ onTerminalOutcome: outcome => runtime.reduceTerminal(outcome), onPromptDispatch: async dispatch => {
|
|
629
|
+
const turn = runtime.sessions.get(dispatch.session_id)?.native_prompt_operation;
|
|
630
|
+
if (!turn || turn.operation_id !== dispatch.operation_id || turn.connection_id !== dispatch.connection_id || turn.generation !== dispatch.generation) throw new DaemonError("native_dispatch_binding_mismatch", "Native prompt dispatch differs from retained operation");
|
|
631
|
+
turn.native_request_id = dispatch.native_request_id;
|
|
632
|
+
await runtime.persist();
|
|
633
|
+
} });
|
|
634
|
+
await runtime.restoreTerminals();
|
|
635
|
+
for (const fact of await readModelObservations(state.config.journal)) {
|
|
636
|
+
if (fact.harness === "grok-acp" && fact.source?.channel === "x.ai/subagent_spawned" && runtime.sessions.has(fact.session_id)
|
|
637
|
+
&& (runtime.descendants.get(fact.session_id)?.attempt_id ?? null) === (fact.source.attempt_id ?? null) && fact.observed?.model) {
|
|
638
|
+
bridge.modelProfiles?.set(fact.session_id, { model: fact.observed.model });
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
scheduleDaemonProcessHeartbeat(state.config, state.resource_process);
|
|
642
|
+
await removeSocket(paths.socket, paths.dir);
|
|
643
|
+
const server = net.createServer(connection => {
|
|
644
|
+
connection.setEncoding("utf8"); let buffer = "", consumed = false;
|
|
645
|
+
connection.on("data", chunk => {
|
|
646
|
+
if (consumed) return;
|
|
647
|
+
buffer += chunk; if (!admitNativeRequestBuffer(buffer, connection, RESPONSE_SCHEMA)) { consumed = true; return; } const at = buffer.indexOf("\n"); if (at < 0) return; consumed = true;
|
|
648
|
+
void (async () => {
|
|
649
|
+
let request;
|
|
650
|
+
try {
|
|
651
|
+
request = JSON.parse(buffer.slice(0, at));
|
|
652
|
+
if (request.schema_id !== REQUEST_SCHEMA || !request.id || !request.operation) throw new DaemonError("daemon_protocol_mismatch", "invalid daemon request");
|
|
653
|
+
const result = await durableDaemonDispatch(paths.dir, request, () => runtime.dispatch(request.operation, request.params ?? {}, request.id), () => runtime.dispatch("session.inspect", request.params ?? {}));
|
|
654
|
+
if (result._shutdown) retireDaemonEndpoint(server);
|
|
655
|
+
connection.end(`${JSON.stringify({ schema_id: RESPONSE_SCHEMA, id: request.id, ok: true, result: { ...result, _shutdown: undefined } })}\n`);
|
|
656
|
+
} catch (error) {
|
|
657
|
+
await retireFailedDaemon(server, state, error, patch => runtime.persist(patch));
|
|
658
|
+
connection.end(`${JSON.stringify({ schema_id: RESPONSE_SCHEMA, id: request?.id ?? null, ok: false, error: errorPayload(error) })}\n`);
|
|
659
|
+
}
|
|
660
|
+
})();
|
|
661
|
+
});
|
|
662
|
+
});
|
|
663
|
+
trackDaemonConnections(server); runtime.server = server;
|
|
664
|
+
await new Promise((resolve, reject) => { server.once("error", reject); server.listen(paths.socket, resolve); }); await chmod(paths.socket, 0o600); await runtime.persist({ shutdown_state: "running", active_tree: false }); runtime.ready = true; return await new Promise(() => {});
|
|
665
|
+
}
|
|
666
|
+
import { authorizeRetainedDaemonResume } from "./driver-recovery.mjs";
|
|
667
|
+
import { commandText } from "./process-json.mjs";
|