@zhushanwen/subagent-core 0.2.0 → 0.5.1
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/README.md +45 -2
- package/agents/analyst.md +60 -0
- package/agents/coder.md +69 -0
- package/agents/debugger.md +66 -0
- package/agents/doc-reviewer.md +49 -0
- package/agents/explorer.md +63 -0
- package/agents/general-purpose.md +32 -0
- package/agents/orchestrator.md +61 -0
- package/agents/planner.md +53 -0
- package/agents/researcher.md +67 -0
- package/agents/reviewer.md +73 -0
- package/dist/chunk-43ONBFZX.js +240 -0
- package/dist/chunk-APZY4IME.js +27 -0
- package/dist/chunk-V3VHZ2VX.js +59 -0
- package/dist/execution/engine/engines/zcode/constants.cjs +62 -9
- package/dist/execution/engine/engines/zcode/constants.d.cts +116 -12
- package/dist/execution/engine/engines/zcode/constants.d.ts +116 -12
- package/dist/execution/engine/engines/zcode/constants.js +39 -7
- package/dist/execution/engine/engines/zcode/reader.d.cts +2 -1
- package/dist/execution/engine/engines/zcode/reader.d.ts +2 -1
- package/dist/execution/engine/engines/zcode/reader.js +4 -234
- package/dist/execution/engine/paths.js +7 -19
- package/dist/index.cjs +18234 -1277
- package/dist/index.d.cts +5646 -673
- package/dist/index.d.ts +5646 -673
- package/dist/index.js +17828 -1277
- package/dist/{types-BxyAidGf.d.cts → types-Dv4QhSJ_.d.cts} +678 -112
- package/dist/{types-BxyAidGf.d.ts → types-Dv4QhSJ_.d.ts} +678 -112
- package/package.json +7 -4
- package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +0 -1
- package/src/__tests__/append-system-prompt-assembly.test.ts +8 -7
- package/src/__tests__/fr4-get-state-handshake.test.ts +2 -2
- package/src/__tests__/m2-append-content-probe.test.ts +0 -1
- package/src/__tests__/manifest-store.test.ts +10 -9
- package/src/__tests__/record-store-cache.test.ts +0 -2
- package/src/__tests__/record-store-index.test.ts +1 -2
- package/src/__tests__/review-fix-loop-script.test.ts +93 -3
- package/src/__tests__/review-fix-loop-utils.test.ts +31 -10
- package/src/__tests__/robustness-low-batch1.test.ts +6 -5
- package/src/__tests__/robustness-medium-batch1.test.ts +2 -2
- package/src/__tests__/robustness-medium-batch2.test.ts +17 -6
- package/src/__tests__/robustness-medium-batch4.test.ts +2 -2
- package/src/__tests__/session-runner.test.ts +1 -1
- package/src/__tests__/smoke.test.ts +9 -2
- package/src/core/__tests__/host-services.test.ts +2 -2
- package/src/core/__tests__/logger.test.ts +1 -1
- package/src/core/error-message.ts +9 -0
- package/src/core/host-services.ts +21 -5
- package/src/core/notify-ports.ts +18 -5
- package/src/execution/__tests__/agent-profile.test.ts +232 -0
- package/src/execution/__tests__/agent-registry.test.ts +370 -0
- package/src/execution/__tests__/agent-result-mapper.test.ts +18 -2
- package/src/execution/__tests__/agents-assembly.test.ts +219 -0
- package/src/execution/__tests__/ask-user-transit-e2e.test.ts +6 -6
- package/src/execution/__tests__/chat-engine-routing.test.ts +136 -30
- package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +5 -4
- package/src/execution/__tests__/cold-resurrect.test.ts +283 -0
- package/src/execution/__tests__/create-concurrency-pool.test.ts +194 -0
- package/src/execution/__tests__/delivery-methods.test.ts +77 -62
- package/src/execution/__tests__/descendant-sweep-guards.test.ts +220 -0
- package/src/execution/__tests__/descendant-sweep.test.ts +269 -0
- package/src/execution/__tests__/dialog-queue.test.ts +199 -1
- package/src/execution/__tests__/engine-model-validation.test.ts +741 -0
- package/src/execution/__tests__/epipe-fallback.test.ts +31 -24
- package/src/execution/__tests__/execute-and-await-worktree.test.ts +21 -91
- package/src/execution/__tests__/execute-nesting.test.ts +34 -188
- package/src/execution/__tests__/execution-record.test.ts +217 -60
- package/src/execution/__tests__/execution-runtime-face.test.ts +272 -0
- package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +3 -3
- package/src/execution/__tests__/finalize-record.test.ts +278 -16
- package/src/execution/__tests__/gc-timer.test.ts +57 -2
- package/src/execution/__tests__/get-record-for-action-restart.test.ts +14 -12
- package/src/execution/__tests__/get-state-handshake.test.ts +127 -0
- package/src/execution/__tests__/helpers/mock-extension-api.ts +1 -1
- package/src/execution/__tests__/helpers/session-runner-mocks.ts +113 -0
- package/src/execution/__tests__/helpers/spawn-mock.ts +150 -3
- package/src/execution/__tests__/helpers/subagent-service-mocks.ts +137 -0
- package/src/execution/__tests__/keep-alive-no-progress.test.ts +280 -0
- package/src/execution/__tests__/kill-all-escalation.test.ts +196 -0
- package/src/execution/__tests__/lifecycle-manager-idle-timer-identity.test.ts +117 -0
- package/src/execution/__tests__/lifecycle-manager.test.ts +39 -0
- package/src/execution/__tests__/lifecycle-predicates.test.ts +1 -2
- package/src/execution/__tests__/manifest-parentid.test.ts +4 -4
- package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +99 -0
- package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +113 -0
- package/src/execution/__tests__/model-resolver.test.ts +2 -2
- package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +23 -121
- package/src/execution/__tests__/nested-visibility.test.ts +2 -0
- package/src/execution/__tests__/notify-ledger.test.ts +1249 -0
- package/src/execution/__tests__/output-collector.test.ts +117 -6
- package/src/execution/__tests__/pi-invocation.test.ts +1 -1
- package/src/execution/__tests__/record-store-orphan-revive.test.ts +159 -0
- package/src/execution/__tests__/record-store.test.ts +89 -5
- package/src/execution/__tests__/records-cwd-isolation.test.ts +2 -2
- package/src/execution/__tests__/recursive-visibility-baseline.test.ts +29 -132
- package/src/execution/__tests__/recursive-visibility-env.test.ts +55 -179
- package/src/execution/__tests__/rpc-mode.test.ts +2 -2
- package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +387 -0
- package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +19 -9
- package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +5 -4
- package/src/execution/__tests__/run-spawn-edges.test.ts +418 -26
- package/src/execution/__tests__/run-spawn-integration.test.ts +3 -3
- package/src/execution/__tests__/run-spawn-resume.test.ts +2 -2
- package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +2 -2
- package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +16 -8
- package/src/execution/__tests__/service-kill-escalation.test.ts +91 -0
- package/src/execution/__tests__/session-context-resolver.test.ts +0 -2
- package/src/execution/__tests__/session-file-gc.test.ts +35 -0
- package/src/execution/__tests__/session-pending.test.ts +471 -0
- package/src/execution/__tests__/session-reconstructor.test.ts +366 -15
- package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +110 -0
- package/src/execution/__tests__/session-runner-close-prune.test.ts +181 -0
- package/src/execution/__tests__/session-runner-dispatch.test.ts +489 -0
- package/src/execution/__tests__/session-runner-epipe.test.ts +4 -3
- package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +227 -0
- package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +290 -0
- package/src/execution/__tests__/session-runner-schema-env.test.ts +7 -5
- package/src/execution/__tests__/settled-watchdog.test.ts +524 -0
- package/src/execution/__tests__/spawn-args.test.ts +11 -10
- package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +26 -23
- package/src/execution/__tests__/spawn-event-adapter.test.ts +1 -1
- package/src/execution/__tests__/spawn-worktree-guidance.test.ts +5 -4
- package/src/execution/__tests__/spawned-children.test.ts +155 -11
- package/src/execution/__tests__/start-sync-model-guard.test.ts +4 -4
- package/src/execution/__tests__/stdin-writer.test.ts +6 -4
- package/src/execution/__tests__/stream-sink-retirement.test.ts +6 -6
- package/src/execution/__tests__/subagent-actions-core.test.ts +1017 -0
- package/src/execution/__tests__/subagent-service-message-close.test.ts +99 -35
- package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +174 -0
- package/src/execution/__tests__/subagent-service-notify-gate.test.ts +271 -0
- package/src/execution/__tests__/subagent-service-parent-guard.test.ts +33 -29
- package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +341 -0
- package/src/execution/__tests__/subagent-service.test.ts +37 -30
- package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +88 -12
- package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +75 -0
- package/src/execution/__tests__/subprocess-agent-runner.test.ts +22 -20
- package/src/execution/__tests__/temp-prompt.test.ts +1 -1
- package/src/execution/__tests__/timeout-integration.test.ts +13 -12
- package/src/execution/__tests__/turn-limiter-semantics.test.ts +1 -1
- package/src/execution/__tests__/turn-limiter.test.ts +1 -1
- package/src/execution/__tests__/ui-request-handler-factory.test.ts +107 -7
- package/src/execution/__tests__/ui-request-handler.test.ts +4 -3
- package/src/execution/__tests__/ui-request-queue.test.ts +167 -2
- package/src/execution/__tests__/worktree-git-ops.test.ts +571 -0
- package/src/execution/__tests__/worktree-manager.test.ts +14 -5
- package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +4 -3
- package/src/execution/__tests__/worktree-reconcile-aging.test.ts +204 -0
- package/src/execution/agent-registry.ts +298 -30
- package/src/execution/agent-result-mapper.ts +3 -0
- package/src/execution/agents-assembly.ts +88 -0
- package/src/execution/cold-resurrect.ts +199 -0
- package/src/execution/concurrency-pool.ts +71 -9
- package/src/execution/dialog-queue.ts +101 -3
- package/src/execution/engine/__tests__/common/capability-gate.test.ts +91 -0
- package/src/execution/engine/__tests__/common/data-dir.test.ts +1 -1
- package/src/execution/engine/__tests__/common/journal-wiring.test.ts +84 -0
- package/src/execution/engine/__tests__/common/kill-chain.test.ts +34 -7
- package/src/execution/engine/__tests__/common/persona-router.test.ts +4 -5
- package/src/execution/engine/__tests__/common/pool-manager.test.ts +211 -51
- package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +149 -0
- package/src/execution/engine/__tests__/common/session-view-service.test.ts +499 -0
- package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +56 -99
- package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +88 -15
- package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +33 -7
- package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +1 -1
- package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +75 -54
- package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +128 -0
- package/src/execution/engine/__tests__/engine-discovery.test.ts +15 -3
- package/src/execution/engine/__tests__/model-prompt.test.ts +15 -3
- package/src/execution/engine/__tests__/registry.test.ts +94 -3
- package/src/execution/engine/__tests__/routing.test.ts +115 -6
- package/src/execution/engine/common/capability-gate.ts +92 -0
- package/src/execution/engine/common/journal-wiring.ts +97 -0
- package/src/execution/engine/common/kill-chain.ts +42 -10
- package/src/execution/engine/common/nesting-guard.ts +66 -0
- package/src/execution/engine/common/persona-router.ts +18 -8
- package/src/execution/engine/common/pool-manager.ts +332 -78
- package/src/execution/engine/common/session-view-service.ts +506 -0
- package/src/execution/engine/common/session-view-types.ts +125 -0
- package/src/execution/engine/engine-discovery.ts +12 -16
- package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +174 -28
- package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +257 -0
- package/src/execution/{argv-mirror.ts → engine/engines/pi/argv-mirror.ts} +43 -21
- package/src/execution/{get-state-handshake.ts → engine/engines/pi/get-state-handshake.ts} +86 -11
- package/src/execution/{output-collector.ts → engine/engines/pi/output-collector.ts} +108 -19
- package/src/execution/engine/engines/pi/pi-engine.ts +434 -38
- package/src/execution/{pi-invocation.ts → engine/engines/pi/pi-invocation.ts} +2 -2
- package/src/execution/engine/engines/pi/reader.ts +11 -0
- package/src/execution/engine/engines/pi/registration.ts +4 -2
- package/src/execution/engine/engines/pi/session-runner.ts +2840 -0
- package/src/execution/{spawn-event-adapter.ts → engine/engines/pi/spawn-event-adapter.ts} +75 -34
- package/src/execution/{stdin-writer.ts → engine/engines/pi/stdin-writer.ts} +10 -9
- package/src/execution/{temp-prompt.ts → engine/engines/pi/temp-prompt.ts} +2 -2
- package/src/execution/{turn-limiter.ts → engine/engines/pi/turn-limiter.ts} +1 -1
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +301 -0
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +36 -0
- package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +321 -0
- package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +473 -0
- package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +7 -210
- package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +6 -110
- package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +247 -0
- package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +451 -0
- package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +792 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +782 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +168 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +210 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +353 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +282 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +372 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +65 -47
- package/src/execution/engine/engines/zcode/appserver-launcher.ts +167 -0
- package/src/execution/engine/engines/zcode/connection.ts +598 -0
- package/src/execution/engine/engines/zcode/constants.ts +147 -14
- package/src/execution/engine/engines/zcode/golden-sample.ts +42 -34
- package/src/execution/engine/engines/zcode/parser.ts +12 -346
- package/src/execution/engine/engines/zcode/preparer.ts +22 -150
- package/src/execution/engine/engines/zcode/session-channel.ts +968 -0
- package/src/execution/engine/engines/zcode/zcode-engine.ts +1023 -250
- package/src/execution/engine/host-task-spec.ts +32 -36
- package/src/execution/engine/model-validation.ts +177 -0
- package/src/execution/engine/port.ts +65 -7
- package/src/execution/engine/registry.ts +53 -0
- package/src/execution/engine/routing.ts +75 -1
- package/src/execution/engine/types.ts +30 -103
- package/src/execution/execution-record.ts +222 -144
- package/src/execution/finalize-record.ts +181 -85
- package/src/execution/idle-gc.ts +28 -0
- package/src/execution/lifecycle-manager.ts +27 -3
- package/src/execution/lifecycle-predicates.ts +1 -1
- package/src/execution/manifest-store.ts +53 -62
- package/src/execution/notifier.ts +10 -10
- package/src/execution/notify-host.ts +212 -0
- package/src/execution/notify-ledger.ts +117 -16
- package/src/execution/record-entry.ts +8 -2
- package/src/execution/record-store.ts +148 -88
- package/src/execution/round-settlement.ts +93 -0
- package/src/execution/session-file-gc.ts +96 -62
- package/src/execution/session-pending.ts +213 -62
- package/src/execution/session-reconstructor.ts +256 -143
- package/src/execution/sessions-index.ts +127 -102
- package/src/execution/settled-watchdog.ts +311 -0
- package/src/execution/subagent-actions-core.ts +686 -0
- package/src/execution/subagent-service.ts +1106 -907
- package/src/execution/subprocess-agent-runner.ts +144 -97
- package/src/execution/types.ts +9 -0
- package/src/execution/ui-request-handler-factory.ts +126 -75
- package/src/execution/ui-request-queue.ts +50 -19
- package/src/execution/worktree-git-ops.ts +397 -0
- package/src/execution/worktree-manager.ts +72 -10
- package/src/execution/worktree-registry.ts +6 -12
- package/src/index.ts +542 -9
- package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +42 -3
- package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +3 -5
- package/src/orchestration/__tests__/agent-call-stream.test.ts +2 -2
- package/src/orchestration/__tests__/args-meta.test.ts +358 -0
- package/src/orchestration/__tests__/args-validator.test.ts +14 -0
- package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +312 -0
- package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +384 -0
- package/src/orchestration/__tests__/execute-agent-call.test.ts +151 -11
- package/src/orchestration/__tests__/file-run-store-prune.test.ts +168 -0
- package/src/orchestration/__tests__/file-run-store-throttle.test.ts +168 -0
- package/src/orchestration/__tests__/file-run-store.test.ts +390 -0
- package/src/orchestration/__tests__/helpers/flush-microtasks.ts +13 -0
- package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +34 -0
- package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +357 -0
- package/src/orchestration/__tests__/lifecycle-recover-crashed.test.ts +289 -0
- package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +3 -3
- package/src/orchestration/__tests__/lifecycle.test.ts +84 -5
- package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +1 -1
- package/src/orchestration/__tests__/run-snapshot.test.ts +356 -0
- package/src/orchestration/__tests__/script-generate.test.ts +314 -0
- package/src/orchestration/__tests__/script-lint.test.ts +17 -0
- package/src/orchestration/__tests__/test-mocks.ts +2 -0
- package/src/orchestration/__tests__/worker-exit-without-result.test.ts +7 -6
- package/src/orchestration/__tests__/worker-message-pump-finalize-run.test.ts +229 -0
- package/src/orchestration/__tests__/{error-recovery-handlers.test.ts → worker-message-pump-handlers.test.ts} +28 -27
- package/src/orchestration/__tests__/{error-recovery-postmessage-defense.test.ts → worker-message-pump-postmessage-defense.test.ts} +8 -8
- package/src/orchestration/__tests__/{error-recovery-serialize-failed-result.test.ts → worker-message-pump-serialize-failed-result.test.ts} +2 -2
- package/src/orchestration/__tests__/{error-recovery-workflow-call.test.ts → worker-message-pump-workflow-call.test.ts} +1 -1
- package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +275 -0
- package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +78 -1
- package/src/orchestration/__tests__/worker-script-builder.test.ts +1 -1
- package/src/orchestration/__tests__/workflow-files.test.ts +186 -0
- package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +1 -1
- package/src/orchestration/__tests__/workflow-run-summary.test.ts +119 -0
- package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +124 -0
- package/src/orchestration/agent-opts-resolver.ts +8 -8
- package/src/orchestration/args-meta.ts +198 -0
- package/src/orchestration/args-validator.ts +53 -31
- package/src/orchestration/execute-agent-call.ts +25 -79
- package/src/orchestration/file-run-store.ts +327 -0
- package/src/orchestration/launcher.ts +160 -104
- package/src/orchestration/lifecycle.ts +371 -97
- package/src/orchestration/models/agent-call.ts +7 -7
- package/src/orchestration/models/budget.ts +5 -5
- package/src/orchestration/models/ports.ts +4 -4
- package/src/orchestration/models/run-runtime.ts +15 -15
- package/src/orchestration/models/trace.ts +9 -9
- package/src/orchestration/models/types.ts +111 -22
- package/src/orchestration/models/workflow-run.ts +28 -28
- package/src/orchestration/models/workflow-script.ts +4 -4
- package/src/orchestration/run-snapshot.ts +266 -0
- package/src/orchestration/script-generate.ts +154 -0
- package/src/orchestration/script-lint.ts +123 -90
- package/src/orchestration/worker-handle.ts +10 -10
- package/src/orchestration/worker-host.ts +10 -10
- package/src/orchestration/{error-recovery.ts → worker-message-pump.ts} +498 -221
- package/src/orchestration/worker-script-builder.ts +401 -346
- package/src/orchestration/workflow-files.ts +37 -11
- package/src/orchestration/workflow-run-summary.ts +69 -0
- package/src/orchestration/workflow-script-registry-impl.ts +31 -9
- package/src/shared/__tests__/agent-ref.test.ts +209 -2
- package/src/shared/__tests__/atomic-write.test.ts +267 -0
- package/src/shared/__tests__/bounded-serialize.test.ts +236 -0
- package/src/shared/__tests__/injection-render.test.ts +518 -0
- package/src/shared/__tests__/meta-parser.test.ts +151 -0
- package/src/shared/__tests__/resource-discovery-host-roots.test.ts +474 -0
- package/src/shared/__tests__/resource-discovery.test.ts +261 -217
- package/src/shared/agent-ref.ts +143 -2
- package/src/shared/atomic-write.ts +323 -0
- package/src/shared/bounded-serialize.ts +154 -0
- package/src/shared/injection-render.ts +279 -0
- package/src/shared/meta-parser.ts +182 -68
- package/src/shared/model-ref.ts +15 -2
- package/src/shared/resource-discovery.ts +97 -204
- package/src/shared/resource-meta.ts +14 -0
- package/src/shared/schema-jsonify.ts +1 -1
- package/src/shared/xml-injection.ts +9 -9
- package/workflows/README.md +9 -9
- package/workflows/chain.js +4 -2
- package/workflows/map-reduce.js +5 -3
- package/workflows/parallel.js +5 -3
- package/workflows/review-fix-loop-utils.cjs +165 -92
- package/workflows/review-fix-loop-utils.d.cts +287 -0
- package/workflows/review-fix-loop.js +18 -7
- package/workflows/scatter-gather.js +4 -2
- package/dist/chunk-3VOERJPJ.js +0 -22
- package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
- package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
- package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
- package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -580
- package/src/execution/engine/engines/zcode/launcher.ts +0 -161
- package/src/execution/execute-options-mapper.ts +0 -115
- package/src/execution/session-runner.ts +0 -1789
- package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -288
|
@@ -1,28 +1,43 @@
|
|
|
1
1
|
// src/execution/engine/engines/zcode/zcode-engine.ts
|
|
2
2
|
//
|
|
3
|
-
// ZcodeEngine
|
|
4
|
-
// docs/architecture/subagent-engine-abstraction.md D10
|
|
5
|
-
//
|
|
3
|
+
// ZcodeEngine:zcode 的 EnginePort 实现(2026-09 起单一 app-server 形态)。
|
|
4
|
+
// 设计权威源:docs/architecture/subagent-engine-abstraction.md D10 / §3.3.4 /
|
|
5
|
+
// §3.3.5;docs/design/zcode-engine-appserver-resident.md §3.3 D1(每引擎实例一条
|
|
6
|
+
// 连接)/ D3(abort 链)/ D4(会话自包含)/ D5(capabilities)/ D6(停机面)。
|
|
6
7
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
8
|
+
// 2026-09 breaking 重构(用户拍板,理由与代价见设计文档修订节):
|
|
9
|
+
// - **删除 CLI spawn 降级链**(原 XYZ_ZCODE_MODE=spawn 定向 / probe 冒烟门控 /
|
|
10
|
+
// protocol-drift 首败降级):zcode 无公开契约,协议漂移不再降级保底,直接报
|
|
11
|
+
// 可操作错误(提示核对版本 / 重启 / 改用 engine: pi)。
|
|
12
|
+
// - **删除 HOME 池化,共享宿主 HOME**:spawn env 不覆写 HOME,app-server 共享
|
|
13
|
+
// 宿主 ~/.zcode/(会话 db 与 GUI 共写同一 SQLite,WAL 并发安全;凭据经
|
|
14
|
+
// appserver-launcher fs 拦截注入——cli config 读取重定向为「真实文件 + v2
|
|
15
|
+
// provider」合并,同 id 时 v2 优先,机制与漂移面见该文件头注;已接受代价:
|
|
16
|
+
// GUI 会话列表可见 headless 会话、登录态轮换后常驻连接需引擎进程重启才用
|
|
17
|
+
// 新凭据)。HOME 依赖副作用(如 pnpm store 路径随 HOME 翻转)随之消失。
|
|
18
|
+
// - journal 分组 key 固定 'shared'(与 pi 引擎 PI_POOL_KEY 同构):journal 落
|
|
19
|
+
// engineDataDir/engines/zcode/shared/journal-<taskId>.jsonl;handle.dbPath
|
|
20
|
+
// 为绝对路径(宿主 ~/.zcode/cli/db/db.sqlite)。
|
|
10
21
|
//
|
|
11
22
|
// run 错误语义(设计 §3.3.5):
|
|
12
|
-
// ① prepare 期错误(credential_missing / model_not_available
|
|
13
|
-
//
|
|
23
|
+
// ① prepare 期错误(credential_missing / model_not_available)在进程创建前
|
|
24
|
+
// reject,不产生 handle;
|
|
14
25
|
// ② 运行中失败不 reject——合成 engine_run_failed outcome + 正常 handle 返回
|
|
15
26
|
// (record 必须收尾);
|
|
16
|
-
// ③ abort
|
|
17
|
-
//
|
|
27
|
+
// ③ abort:D3 链(session/stop → grace → killChain 连坐共享进程)——终态
|
|
28
|
+
// exitCode=null + 杀链标记。
|
|
29
|
+
// [D3-④] capability 拒绝(fork/conversation/maxTurns/worktree)不再在本引擎:
|
|
30
|
+
// 上提到宿主调用前预检(common/capability-gate,两调用点 = chat 域
|
|
31
|
+
// executeViaEngine 同步段 + SAR run 前)——拒绝语义不变
|
|
32
|
+
// (engine_capability_unsupported + 无进程创建)。
|
|
18
33
|
//
|
|
19
|
-
// schema 仿真接线(D4 emulated 侧):common/schema-emulation.ts
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
// 已接线(P4 对齐点①:common/journal-replay 复用 live reducer)。
|
|
34
|
+
// schema 仿真接线(D4 emulated 侧):common/schema-emulation.ts——prompt 拼仿真段、
|
|
35
|
+
// 终态后三级容错提取 + ajv 校验、失败强化重试一次、仍失败报 schema_emulation_failed。
|
|
36
|
+
// read 第②级 journal 降级已接线(common/journal-replay 复用 live reducer)。
|
|
23
37
|
|
|
24
38
|
import { execFile } from "node:child_process";
|
|
25
39
|
import * as fs from "node:fs";
|
|
40
|
+
import * as os from "node:os";
|
|
26
41
|
import * as path from "node:path";
|
|
27
42
|
|
|
28
43
|
import { getLogger } from "../../../../core/logger.ts";
|
|
@@ -34,11 +49,11 @@ import {
|
|
|
34
49
|
import { HOST_TIMEOUT_ABORT_REASON, synthesizeTimeoutOutcome } from "../../common/kill-chain.ts";
|
|
35
50
|
import { engineTimeoutDetail } from "../../common/errors.ts";
|
|
36
51
|
import { replayJournalToSessionView } from "../../common/journal-replay.ts";
|
|
52
|
+
import type { AgentCallOpts } from "../../../../orchestration/models/types.ts";
|
|
37
53
|
import type { EnginePort, EngineRunResult, RunContext } from "../../port.ts";
|
|
38
54
|
import type {
|
|
39
55
|
AgentEvent,
|
|
40
56
|
AgentOutcome,
|
|
41
|
-
AgentTaskSpec,
|
|
42
57
|
EngineCapabilities,
|
|
43
58
|
EngineHandle,
|
|
44
59
|
InteractAction,
|
|
@@ -49,41 +64,66 @@ import type {
|
|
|
49
64
|
import { resolvePoolDir } from "../../paths.ts";
|
|
50
65
|
import {
|
|
51
66
|
ZCODE_ADAPTER_VERSION,
|
|
67
|
+
ZCODE_APPSERVER_ABORT_GRACE_MS,
|
|
68
|
+
ZCODE_APPSERVER_ERR_BUSY_SESSION,
|
|
69
|
+
ZCODE_APPSERVER_ERR_MODEL_CONFIG_MISSING,
|
|
70
|
+
ZCODE_APPSERVER_HARVEST_GRACE_MS,
|
|
71
|
+
ZCODE_APPSERVER_STOP_TIMEOUT_MS,
|
|
52
72
|
ZCODE_CLI_DEFAULT_PATH,
|
|
53
73
|
ZCODE_ENGINE_ID,
|
|
54
74
|
ZCODE_ERROR_TAIL_CHARS,
|
|
75
|
+
ZCODE_HOST_DB_SUFFIX,
|
|
55
76
|
ZCODE_KILL_GRACE_MS,
|
|
56
|
-
|
|
77
|
+
ZCODE_SHARED_POOL_KEY,
|
|
78
|
+
ZCODE_TURN_MAX_TIMEOUT_ENV,
|
|
79
|
+
parseZcodeTurnTimeoutEnv,
|
|
57
80
|
} from "./constants.ts";
|
|
58
|
-
import { ZCODE_GOLDEN_STDOUT } from "./golden-sample.ts";
|
|
59
81
|
import {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
assertZcodeArgvBudget,
|
|
63
|
-
launchZcodeProcess,
|
|
64
|
-
type ZcodeLaunchedProcess,
|
|
65
|
-
} from "./launcher.ts";
|
|
66
|
-
import {
|
|
67
|
-
buildRunFailedMessage,
|
|
68
|
-
collectZcodeOutput,
|
|
69
|
-
parseZcodeTerminal,
|
|
82
|
+
mapZcodeOutcomeUsage,
|
|
83
|
+
mapZcodeUsage,
|
|
70
84
|
synthesizeCoarseEvents,
|
|
71
|
-
type ZcodeCollectedOutput,
|
|
72
85
|
type ZcodeTerminalPayload,
|
|
73
86
|
} from "./parser.ts";
|
|
74
|
-
import {
|
|
87
|
+
import {
|
|
88
|
+
defaultV2ConfigPath,
|
|
89
|
+
listZcodeModels,
|
|
90
|
+
resolveZcodeModelRef,
|
|
91
|
+
splitZcodeModelRef,
|
|
92
|
+
type ZcodeSourcePaths,
|
|
93
|
+
} from "./preparer.ts";
|
|
94
|
+
import { ensureAppServerLauncher } from "./appserver-launcher.ts";
|
|
75
95
|
import { readZcodeSessionView } from "./reader.ts";
|
|
96
|
+
import { AppServerConnection, buildAppServerEnv, isAppServerRpcError } from "./connection.ts";
|
|
97
|
+
import {
|
|
98
|
+
SessionChannel,
|
|
99
|
+
TurnTimeoutError,
|
|
100
|
+
type SessionCreateParams,
|
|
101
|
+
type SessionTurnResult,
|
|
102
|
+
} from "./session-channel.ts";
|
|
103
|
+
import { toErrorMessage } from "../../../../core/error-message.ts";
|
|
76
104
|
|
|
77
105
|
const logger = getLogger("subagents");
|
|
78
106
|
|
|
79
107
|
/** probe 的版本探测超时(ms)——二进制无响应按探针失败处理,不静默挂死。 */
|
|
80
108
|
const PROBE_VERSION_TIMEOUT_MS = 15_000;
|
|
81
109
|
|
|
110
|
+
/**
|
|
111
|
+
* [RX2-F1] 常见 thoughtLevel 档位(仅作提示基准,非权威值域):pi 全 7 档
|
|
112
|
+
* (off/minimal/low/medium/high/xhigh/max)恒等透传不拦截——core 引擎层不掌握各模型
|
|
113
|
+
* 真实值域,禁止硬编码枚举做拒收或映射;不在此列的档位只触发一行提示(warnThoughtLevelUncommon)。
|
|
114
|
+
*/
|
|
115
|
+
const COMMON_THOUGHT_LEVELS: readonly string[] = ["low", "high", "max"];
|
|
116
|
+
|
|
117
|
+
/** 宿主 HOME 下 zcode 会话 db 的绝对路径(共享 HOME 形态的 read/handle 锚点)。 */
|
|
118
|
+
export function hostZcodeDbPath(): string {
|
|
119
|
+
return path.join(os.homedir(), ...ZCODE_HOST_DB_SUFFIX);
|
|
120
|
+
}
|
|
121
|
+
|
|
82
122
|
/** ZcodeEngine 构造依赖(全部可注入——测试不依赖真机 CLI/真凭据)。 */
|
|
83
123
|
export interface ZcodeEngineDeps {
|
|
84
124
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
125
|
+
* 引擎数据目录(journal 分组根 <dir>/engines/zcode/shared/ 与 stderr 日志落点)。
|
|
126
|
+
* 来源通道(宿主 dataDir)见 registration.ts 缺省解析。
|
|
87
127
|
*/
|
|
88
128
|
engineDataDir: () => string;
|
|
89
129
|
/** zcode CLI 路径;缺省 ZCODE_CLI_DEFAULT_PATH。 */
|
|
@@ -92,54 +132,69 @@ export interface ZcodeEngineDeps {
|
|
|
92
132
|
sources?: ZcodeSourcePaths;
|
|
93
133
|
/** 版本探测执行器(probe check "version";测试注入 fake 防真实子进程)。 */
|
|
94
134
|
probeVersion?: (cliPath: string) => Promise<string | undefined>;
|
|
95
|
-
/**
|
|
96
|
-
launch?: (opts: { cliPath: string; args: string[]; env: NodeJS.ProcessEnv }) => ZcodeLaunchedProcess;
|
|
97
|
-
/** env 基底(测试注入;缺省 process.env)。 */
|
|
135
|
+
/** env 基底(测试注入;缺省 process.env——app-server env 组装经它)。 */
|
|
98
136
|
processEnv?: NodeJS.ProcessEnv;
|
|
99
137
|
}
|
|
100
138
|
|
|
139
|
+
/** 常驻运行时(每引擎实例一份;dispose 时整件丢弃)。 */
|
|
140
|
+
interface AppServerRuntime {
|
|
141
|
+
conn: AppServerConnection;
|
|
142
|
+
channel: SessionChannel;
|
|
143
|
+
/** 在途会话登记(dispose 时 fire session/close 的目标集;settle 后移除)。 */
|
|
144
|
+
activeSessions: Set<string>;
|
|
145
|
+
}
|
|
146
|
+
|
|
101
147
|
/** zcode 引擎适配器。 */
|
|
102
148
|
export class ZcodeEngine implements EnginePort {
|
|
103
149
|
readonly id = ZCODE_ENGINE_ID;
|
|
104
150
|
|
|
105
151
|
private readonly deps: ZcodeEngineDeps;
|
|
106
152
|
private probeCache: ProbeReport | undefined;
|
|
153
|
+
private appserverRuntime: AppServerRuntime | undefined;
|
|
154
|
+
/**
|
|
155
|
+
* [P0-1 U4] 引擎停机标志(dispose 置位,不重置——dispose 后首个 run 走重建路径
|
|
156
|
+
* 不受影响):瞬时重试判定据此排除 dispose 收割引发的崩溃形态——停机后的重试轮
|
|
157
|
+
* 会经 ensureAppServerRuntime 惰性重建进程(复活已停机引擎),违背 dispose 防泄漏
|
|
158
|
+
* 语义。
|
|
159
|
+
*/
|
|
160
|
+
private disposed = false;
|
|
107
161
|
|
|
108
162
|
constructor(deps: ZcodeEngineDeps) {
|
|
109
163
|
this.deps = deps;
|
|
110
164
|
}
|
|
111
165
|
|
|
112
166
|
/**
|
|
113
|
-
* zcode
|
|
114
|
-
* common 层后仍为 emulated;app-server 常驻化后 eventGranularity 升 stream)必须
|
|
115
|
-
* 先改链路再改声明。
|
|
167
|
+
* zcode 链路实际接通的能力(D3 链路口径。声明升级必须先改链路再改声明(C4 原则)。
|
|
116
168
|
*/
|
|
117
169
|
capabilities(): EngineCapabilities {
|
|
118
170
|
return {
|
|
119
171
|
// 无 --json-schema 类通道;公共 schema 仿真层(prompt 约定 + 容错提取 + ajv)
|
|
120
172
|
schemaEnforcement: "emulated",
|
|
121
|
-
//
|
|
173
|
+
// send-while-running 恒 -32010 硬错误(旧实测)——app-server 常驻化不改变此判据
|
|
122
174
|
steer: "unsupported",
|
|
123
|
-
// 无同进程 idle
|
|
175
|
+
// 无同进程 idle 复用(D4:每任务自包含 create→run→close)
|
|
124
176
|
conversation: "unsupported",
|
|
125
177
|
// 无 --append-system-prompt flag(实测拒收)——persona 只能拼进 prompt
|
|
126
178
|
personaInjection: "prompt",
|
|
127
|
-
//
|
|
128
|
-
eventGranularity: "
|
|
179
|
+
// app-server 推送流实时流出(session/event payload.delta → text_delta)
|
|
180
|
+
eventGranularity: "stream",
|
|
129
181
|
// 首期未接 worktree 隔离(公共层 worktree-manager 接入后升 emulated)
|
|
130
182
|
sandbox: "none",
|
|
131
183
|
// sqlite 三级 JOIN 完整重建 turns(reader 实测)
|
|
132
184
|
sessionRead: "full",
|
|
133
185
|
// --resume 冷启动可用(实测)
|
|
134
186
|
resume: "cold",
|
|
135
|
-
//
|
|
187
|
+
// abort 走 D3 链(stop→grace→killChain)但声明维持 kill-only 不升级(改链路
|
|
188
|
+
// 先于改声明;stop 链路经 conformance 真机验证后再评估升 native)
|
|
136
189
|
interrupt: "kill-only",
|
|
137
190
|
// --mode build/edit/plan/yolo 原生权限档位
|
|
138
191
|
permissionMode: "native",
|
|
192
|
+
// [D3-④] 无 turn_end 语义,轮数上限不可兑现(预检 gate 据此同步拒绝)
|
|
193
|
+
maxTurns: false,
|
|
139
194
|
};
|
|
140
195
|
}
|
|
141
196
|
|
|
142
|
-
/** 探针(D7):二进制存在 +
|
|
197
|
+
/** 探针(D7):二进制存在 + 版本解析(zcode 无公开契约,版本漂移的入口信号)。 */
|
|
143
198
|
async probe(opts?: { force?: boolean }): Promise<ProbeReport> {
|
|
144
199
|
if (!opts?.force && this.probeCache) return this.probeCache;
|
|
145
200
|
|
|
@@ -156,9 +211,6 @@ export class ZcodeEngine implements EnginePort {
|
|
|
156
211
|
engineVersion = version.engineVersion;
|
|
157
212
|
}
|
|
158
213
|
|
|
159
|
-
// check 3:golden 样本干跑回归(parser 对实录样本解析——格式漂移入口拦截)
|
|
160
|
-
checks.push(this.probeGoldenCheck());
|
|
161
|
-
|
|
162
214
|
const ok = checks.every((c) => c.ok);
|
|
163
215
|
const report: ProbeReport = {
|
|
164
216
|
ok,
|
|
@@ -197,94 +249,484 @@ export class ZcodeEngine implements EnginePort {
|
|
|
197
249
|
};
|
|
198
250
|
}
|
|
199
251
|
|
|
200
|
-
/** check 3:golden 样本干跑(parser 对实录样本解析——stdout 格式漂移的入口拦截)。 */
|
|
201
|
-
private probeGoldenCheck(): ProbeReport["checks"][number] {
|
|
202
|
-
const golden = parseZcodeTerminal(ZCODE_GOLDEN_STDOUT);
|
|
203
|
-
const goldenOk = golden.ok && golden.payload.sessionId !== undefined && golden.payload.usage !== undefined;
|
|
204
|
-
return {
|
|
205
|
-
name: "golden-regression",
|
|
206
|
-
ok: goldenOk,
|
|
207
|
-
detail: goldenOk
|
|
208
|
-
? "parser 对 0.16.5 实录样本回归通过(sessionId/response/usage 形状完整)"
|
|
209
|
-
: `解析实录样本失败:${golden.ok ? "字段缺失(sessionId/usage)" : golden.reason}`,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
252
|
/** 探针失败的恢复指引(§3.3.3 终态四:版本确认命令 + 探针重跑 + 调研文档路径)。 */
|
|
214
253
|
private probeFailureRecovery(cliPath: string): NonNullable<ProbeReport["error"]> {
|
|
215
254
|
return {
|
|
216
255
|
code: "engine_probe_failed",
|
|
217
256
|
recovery:
|
|
218
257
|
`Run \`node ${cliPath} --version\` 确认 zcode CLI 可用且版本未漂移,然后重跑探针(重新初始化引擎或 probe({force:true}))。` +
|
|
219
|
-
`若
|
|
258
|
+
`若 app-server 协议已漂移(RPC 错误),重启 ZCode 或固定 zcode 版本后重试。` +
|
|
220
259
|
`参照 docs/research/agent-engine-zcode.md。`,
|
|
221
260
|
};
|
|
222
261
|
}
|
|
223
262
|
|
|
224
|
-
/** D1
|
|
225
|
-
async run(task:
|
|
263
|
+
/** D1 主语义:唯一通道 = app-server 常驻连接(spawn 降级链已删除)。 */
|
|
264
|
+
async run(task: AgentCallOpts, ctx: RunContext): Promise<EngineRunResult> {
|
|
265
|
+
// [D3-④] fork/conversation/maxTurns 的能力拒绝已上提到宿主调用前预检
|
|
266
|
+
//(common/capability-gate,capabilities.maxTurns 扩位承载)——引擎内不再做
|
|
267
|
+
// shape 检查(拦截逻辑单点化,重演双轨根因的形态被删除)。
|
|
268
|
+
return this.runViaAppServer(task, ctx);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// ============================================================
|
|
272
|
+
// app-server 常驻路径(D1/D3/D4)
|
|
273
|
+
// ============================================================
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* 常驻路径主编排:模型解析(v2 单源校验)→ 惰性连接 + runTurn(事件时序前移:
|
|
277
|
+
* text_delta 流式、终态后 message_end/turn_end)→ schema 仿真重试 → outcome/handle。
|
|
278
|
+
* poolKey 固定 'shared'(共享宿主 HOME,无池),onPoolResolved 在 prepare 期、
|
|
279
|
+
* onHandleReady 在 create 应答后(§3.4 不变量 3)。
|
|
280
|
+
*/
|
|
281
|
+
private async runViaAppServer(task: AgentCallOpts, ctx: RunContext): Promise<EngineRunResult> {
|
|
226
282
|
const startedAt = Date.now();
|
|
227
|
-
|
|
283
|
+
// pre-aborted 短路:取消先于启动——不创建会话、不触发连接惰性启动(防误杀共享
|
|
284
|
+
// 进程殃及在途任务)
|
|
285
|
+
if (ctx.signal?.aborted === true) {
|
|
286
|
+
return this.abortedAppServerRun(task, ctx, startedAt);
|
|
287
|
+
}
|
|
228
288
|
|
|
229
|
-
// ① prepare 期:模型解析(provider
|
|
289
|
+
// ① prepare 期:模型解析(provider 体系校验——错误语义为进程创建前 reject)
|
|
230
290
|
const modelRef = resolveZcodeModelRef(task.model, this.deps.sources);
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
// 的路径——单一权威,宿主不再两边推导);须在首个事件 emit 前(终态后合成,天然满足)
|
|
238
|
-
ctx.onPoolResolved?.(prepared.poolKey);
|
|
239
|
-
logger.debug("[zcode-engine] isolated home prepared", {
|
|
240
|
-
poolKey: prepared.poolKey,
|
|
241
|
-
wroteConfig: prepared.wroteConfig,
|
|
242
|
-
modelRef,
|
|
243
|
-
taskId: ctx.taskId,
|
|
244
|
-
});
|
|
291
|
+
this.warnIgnoredCtxModel(task, ctx, modelRef);
|
|
292
|
+
// [RX2-F1] 非常见档位出声一行(不拦截透传);放主编排而非 attemptAppServerTurn——
|
|
293
|
+
// schema 重试轮会二次进 attempt,warn 只应随任务出声一次
|
|
294
|
+
this.warnThoughtLevelUncommon(task, ctx);
|
|
295
|
+
// 对齐点③(不变量 3):poolKey 在 prepare 期声明,早于连接建立与首个事件
|
|
296
|
+
ctx.onPoolResolved?.(ZCODE_SHARED_POOL_KEY);
|
|
245
297
|
|
|
246
298
|
const cwd = task.cwd ?? process.cwd();
|
|
247
299
|
const schema = isPlainObject(task.schema) ? task.schema : undefined;
|
|
248
300
|
const basePrompt = this.buildPrompt(task, schema);
|
|
249
301
|
|
|
250
|
-
//
|
|
251
|
-
// structured-output 的重试语义对齐,设计 §3.3.3 schema_emulation_failed 行)。
|
|
252
|
-
// 重试轮产生的新 session 是独立 LLM 调用:token 计入 outcome.usage 总量,
|
|
253
|
-
// 事件只在最终轮终态后一次性合成(不变量 5:事件 emit 完成先于 run resolve)。
|
|
302
|
+
// ② 首轮执行 + schema 仿真重试(重试语义与不变量注释见 runAppServerAttemptsWithRetry)
|
|
254
303
|
const usageAcc = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, has: false };
|
|
255
|
-
|
|
304
|
+
const final = await this.runAppServerAttemptsWithRetry(task, ctx, modelRef, cwd, basePrompt, schema, usageAcc);
|
|
305
|
+
|
|
306
|
+
const outcome = this.finalizeOutcome(task, ctx, final, usageAcc, startedAt);
|
|
307
|
+
return { handle: this.appServerHandle(outcome), outcome };
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/** pre-aborted 短路收口:合成中止 outcome + 'shared' 锚定 handle。 */
|
|
311
|
+
private abortedAppServerRun(task: AgentCallOpts, ctx: RunContext, startedAt: number): EngineRunResult {
|
|
312
|
+
// 对齐点③(不变量 3):onPoolResolved 必须先于首个事件 emit——本分支
|
|
313
|
+
// finalizeOutcome 经 applyAbortedOutcome emit error 事件
|
|
314
|
+
ctx.onPoolResolved?.(ZCODE_SHARED_POOL_KEY);
|
|
315
|
+
const outcome = this.finalizeOutcome(
|
|
316
|
+
task,
|
|
317
|
+
ctx,
|
|
318
|
+
abortedAppServerAttempt(ctx),
|
|
319
|
+
{ input: 0, output: 0, cacheRead: 0, cacheWrite: 0, has: false },
|
|
320
|
+
startedAt,
|
|
321
|
+
);
|
|
322
|
+
return { handle: this.appServerHandle(outcome), outcome };
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* 首轮执行 + 双重试编排(常驻路径):
|
|
327
|
+
* - **schema 仿真重试**(既有语义):parsed 但校验失败时重试一次(强化 JSON 输出
|
|
328
|
+
* 指令——与 structured-output 的重试语义对齐)。
|
|
329
|
+
* - **瞬时失败自动重试一次**([P0-1 U4/D6]):末次 attempt 为 timeout 类(idle/
|
|
330
|
+
* ceiling)或连接崩溃类失败且非用户 abort → 用新会话重跑一次(attempt 本就每次
|
|
331
|
+
* 新建会话)。重试轮 prompt 用 basePrompt 原样重跑(失败形态非 schema),文案补
|
|
332
|
+
* 「已自动重试一次」句(retried 标记仅对真实发生的重试生效)。
|
|
333
|
+
*
|
|
334
|
+
* 两次重试一次封顶各自独立(D6 被否①:多次重试/指数退避不做——重跑一轮=整任务
|
|
335
|
+
* 重算,一次封顶)。组合序:瞬时重试在前、schema 重试在后——瞬时重试轮 parsed 且
|
|
336
|
+
* 校验失败时仍进 schema 重试(末次 attempt 语义,schema 重试编排保持现状不动)。
|
|
337
|
+
*
|
|
338
|
+
* 重试轮是独立会话的独立 LLM 调用:token 计入 outcome.usage 总量;事件面
|
|
339
|
+
* text_delta 按实际流出(含失败轮——journal 记录真实流水),message_end/turn_end
|
|
340
|
+
* 只在最终轮终态后合成(不变量 2/5)。
|
|
341
|
+
*/
|
|
342
|
+
private async runAppServerAttemptsWithRetry(
|
|
343
|
+
task: AgentCallOpts,
|
|
344
|
+
ctx: RunContext,
|
|
345
|
+
modelRef: string,
|
|
346
|
+
cwd: string,
|
|
347
|
+
basePrompt: string,
|
|
348
|
+
schema: JsonSchemaObject | undefined,
|
|
349
|
+
usageAcc: { input: number; output: number; cacheRead: number; cacheWrite: number; has: boolean },
|
|
350
|
+
): Promise<AttemptResult> {
|
|
351
|
+
const attemptStartedAt = Date.now();
|
|
352
|
+
let final = await this.attemptAppServerTurn(task, ctx, modelRef, cwd, basePrompt);
|
|
256
353
|
accumulateUsage(usageAcc, final);
|
|
354
|
+
// [P0-1 U4/D6] 瞬时失败自动重试一次:判据 = run-failed 且 transient 形态标记
|
|
355
|
+
// (类型化,不经字符串反推;RPC 错误/status=error 终态等有应答的精确归类形态
|
|
356
|
+
// 构造处即无 transient——含协议漂移类,漂移不再降级 spawn、直接报错)+ 非用户
|
|
357
|
+
// 已取消 + 非引擎停机(dispose 收割引发的崩溃不重试——停机后惰性重建 = 复活
|
|
358
|
+
// 进程,违背 dispose 防泄漏语义)。
|
|
359
|
+
// 预算继承(P-Z4):显式总上界预算下重试轮上界 = 剩余(总 − 已耗尽),剩余不足
|
|
360
|
+
// 最小下限不重试直接终态化;重试轮启动即 journal 出声(D6:重试事实记入 journal)。
|
|
361
|
+
if (
|
|
362
|
+
final.kind === "run-failed" &&
|
|
363
|
+
final.transient !== undefined &&
|
|
364
|
+
ctx.signal?.aborted !== true &&
|
|
365
|
+
!this.disposed
|
|
366
|
+
) {
|
|
367
|
+
const budget = resolveTransientRetryBudget(explicitTurnBudgetMs(), Date.now() - attemptStartedAt);
|
|
368
|
+
if (budget.state === "depleted") {
|
|
369
|
+
logger.warn(
|
|
370
|
+
`[zcode-engine] 末次 attempt 瞬时失败(${final.transient})——显式总上界预算剩余不足 ${ZCODE_TURN_RETRY_MIN_BUDGET_MS}ms,不重试直接终态化(预算继承:重试不重置总预算)`,
|
|
371
|
+
);
|
|
372
|
+
} else {
|
|
373
|
+
logger.warn(
|
|
374
|
+
`[zcode-engine] 末次 attempt 瞬时失败(${final.transient})——止损链已终局,新会话自动重试一次` +
|
|
375
|
+
(budget.state === "inherit"
|
|
376
|
+
? `(预算继承:重试轮总上界=剩余 ${budget.remainingMs}ms,不重置总预算)`
|
|
377
|
+
: "(无显式总上界预算,重试轮走 env/默认上界)"),
|
|
378
|
+
);
|
|
379
|
+
const retry = await this.attemptAppServerTurn(task, ctx, modelRef, cwd, basePrompt, {
|
|
380
|
+
// 预算继承传递点(D2 内部传参面):显式预算 → 剩余值;无显式预算 → 缺省
|
|
381
|
+
// (channel 侧走同一 env/默认,与首轮行为一致)
|
|
382
|
+
...(budget.state === "inherit" ? { turnTimeoutMs: budget.remainingMs } : {}),
|
|
383
|
+
// 重试事实进文案:「已自动重试一次」句仅对真实发生的重试生效(未重试形态
|
|
384
|
+
// 不含——与行为一致,§5.2 F-1/F-4)
|
|
385
|
+
retried: true,
|
|
386
|
+
});
|
|
387
|
+
accumulateUsage(usageAcc, retry);
|
|
388
|
+
final = retry;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
257
391
|
if (final.kind === "parsed" && final.schemaResult !== undefined && !final.schemaResult.ok && schema !== undefined) {
|
|
258
392
|
const retryPrompt = appendSchemaRetryDirective(basePrompt, final.schemaResult.error);
|
|
259
|
-
const retry = await this.
|
|
393
|
+
const retry = await this.attemptAppServerTurn(task, ctx, modelRef, cwd, retryPrompt);
|
|
260
394
|
accumulateUsage(usageAcc, retry);
|
|
261
395
|
final = retry;
|
|
262
396
|
}
|
|
397
|
+
return final;
|
|
398
|
+
}
|
|
263
399
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
400
|
+
/** 常驻路径的 handle 合成(poolKey 固定 'shared';dbPath = 宿主 HOME 绝对路径)。 */
|
|
401
|
+
private appServerHandle(outcome: AgentOutcome): EngineHandle {
|
|
402
|
+
return {
|
|
267
403
|
data: {
|
|
268
404
|
v: 1,
|
|
269
405
|
engineId: ZCODE_ENGINE_ID,
|
|
270
406
|
sessionRef: {
|
|
271
|
-
|
|
272
|
-
dbPath: ZCODE_POOL_DB_RELATIVE_PATH,
|
|
407
|
+
dbPath: hostZcodeDbPath(),
|
|
273
408
|
...(outcome.sessionId !== undefined ? { sessionId: outcome.sessionId } : {}),
|
|
274
409
|
},
|
|
275
|
-
poolKey:
|
|
410
|
+
poolKey: ZCODE_SHARED_POOL_KEY,
|
|
276
411
|
...(this.probeCache?.engineVersion !== undefined && this.probeCache.engineVersion !== ""
|
|
277
412
|
? { engineVersion: this.probeCache.engineVersion }
|
|
278
413
|
: {}),
|
|
279
414
|
adapterVersion: ZCODE_ADAPTER_VERSION,
|
|
280
415
|
},
|
|
281
416
|
};
|
|
282
|
-
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* 单轮常驻执行:runTurn 组合面 + D3 abort 链 + 事件前移(text_delta 实时流出;
|
|
421
|
+
* 终态数据经 read 兜底收口后才 resolve——不变量 1/2)。
|
|
422
|
+
*
|
|
423
|
+
* @param opts turnTimeoutMs:显式总上界传参面(D2 内部传参点)——U4/D6 预算继承
|
|
424
|
+
* 向重试轮传剩余值;缺省不传(channel 走 env→默认,首轮行为)。
|
|
425
|
+
* retried:瞬时重试轮标记——失败文案补「已自动重试一次」句(F-1/F-4)。
|
|
426
|
+
*/
|
|
427
|
+
private async attemptAppServerTurn(
|
|
428
|
+
task: AgentCallOpts,
|
|
429
|
+
ctx: RunContext,
|
|
430
|
+
modelRef: string,
|
|
431
|
+
cwd: string,
|
|
432
|
+
prompt: string,
|
|
433
|
+
opts: { turnTimeoutMs?: number; retried?: boolean } = {},
|
|
434
|
+
): Promise<AttemptResult> {
|
|
435
|
+
const rt = this.ensureAppServerRuntime();
|
|
436
|
+
const { providerId, modelId } = splitZcodeModelRef(modelRef);
|
|
437
|
+
const createParams = buildAppServerCreateParams(task, providerId, modelId, cwd);
|
|
438
|
+
|
|
439
|
+
let currentSessionId: string | undefined;
|
|
440
|
+
let signalSessionCreated: (() => void) | undefined;
|
|
441
|
+
const sessionCreated = new Promise<void>((resolve) => {
|
|
442
|
+
signalSessionCreated = resolve;
|
|
443
|
+
});
|
|
444
|
+
const turn = rt.channel.runTurn(createParams, prompt, {
|
|
445
|
+
// 事件时序前移:payload.delta → text_delta 实时流出(stream 粒度,D5)
|
|
446
|
+
onTextDelta: (delta) => ctx.onEvent?.({ type: "text_delta", delta }),
|
|
447
|
+
onSessionCreated: (sessionId) => {
|
|
448
|
+
currentSessionId = sessionId;
|
|
449
|
+
rt.activeSessions.add(sessionId);
|
|
450
|
+
signalSessionCreated?.();
|
|
451
|
+
// §3.4 不变量 3:create 应答后立即回填(早于 subscribe/send/终态/run resolve)
|
|
452
|
+
ctx.onHandleReady?.({
|
|
453
|
+
sessionRef: { dbPath: hostZcodeDbPath(), sessionId },
|
|
454
|
+
poolKey: ZCODE_SHARED_POOL_KEY,
|
|
455
|
+
});
|
|
456
|
+
},
|
|
457
|
+
// 显式总上界传参(D2/U4:缺省缺席——channel 侧 resolveTurnTimerMs 走 env→默认)
|
|
458
|
+
...(opts.turnTimeoutMs !== undefined ? { turnTimeoutMs: opts.turnTimeoutMs } : {}),
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
// D3 abort 链:signal abort → ① session/stop {sessionId} ② grace 窗口确认终态
|
|
462
|
+
// ③ stop 失败/超时 → killChain 杀共享进程(接受连坐——协议已不可信)→ 在途
|
|
463
|
+
// 其他任务走崩溃路径。capabilities.interrupt 维持 kill-only 不升级(C4)。
|
|
464
|
+
// [P0-1 U2] 用户取消入口 = escalateOn:"turn-settled"(grace 窗口确认 turn 落定,
|
|
465
|
+
// 现状语义零改动);channel 超时判死(TurnTimeoutError)走 catch 分流的
|
|
466
|
+
// escalateOn:"stop-outcome" 入口(stop 应答三态裁决)。
|
|
467
|
+
const onAbort = (): void => {
|
|
468
|
+
void this.appServerAbortChain(rt, turn, () => currentSessionId, sessionCreated, { escalateOn: "turn-settled" });
|
|
469
|
+
};
|
|
470
|
+
if (ctx.signal !== undefined) {
|
|
471
|
+
if (ctx.signal.aborted) onAbort();
|
|
472
|
+
else ctx.signal.addEventListener("abort", onAbort, { once: true });
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
try {
|
|
476
|
+
const r = await turn;
|
|
477
|
+
// stop 优雅生效(终态在 grace 内到达):宿主已取消——按中止终态收口(record
|
|
478
|
+
// 终态迁移由编排层 CAS 决定)
|
|
479
|
+
if (ctx.signal?.aborted === true) return abortedAppServerAttempt(ctx);
|
|
480
|
+
return parsedAppServerAttempt(task, r);
|
|
481
|
+
} catch (err) {
|
|
482
|
+
if (ctx.signal?.aborted === true) return abortedAppServerAttempt(ctx);
|
|
483
|
+
// [P0-1 U2] 超时入口(D3 v1.1):turn 已被 channel 判死 reject——升级判据不能
|
|
484
|
+
// 再挂在 turn 落定上(race 恒真,killChain 结构性不可达的 v1 击穿点),改以
|
|
485
|
+
// stop 应答三态裁决。await 链终局(非 fire-and-forget):outcome 止损文案与
|
|
486
|
+
// 重试时序(D6,u-z4)都依赖链终局信号——止损完成前不合成终态。
|
|
487
|
+
if (err instanceof TurnTimeoutError) {
|
|
488
|
+
const stopPath = await this.appServerAbortChain(
|
|
489
|
+
rt,
|
|
490
|
+
turn,
|
|
491
|
+
() => currentSessionId,
|
|
492
|
+
sessionCreated,
|
|
493
|
+
{ escalateOn: "stop-outcome" },
|
|
494
|
+
);
|
|
495
|
+
// [P0-1 U4] timeout 类(idle/ceiling 都算)是 D6 明文的可重试形态——结构化
|
|
496
|
+
// 标记(TurnTimeoutError 类型化判据,不经字符串匹配,D4 同精神)
|
|
497
|
+
return timeoutAppServerAttempt(err, currentSessionId, stopPath, { retried: opts.retried });
|
|
498
|
+
}
|
|
499
|
+
// [P0-1 U4] 连接崩溃收割形态(failAllTurns 的错误,D6 第二可重试形态)判据:
|
|
500
|
+
// 非 RPC error(服务端无明确应答——有应答即精确错误归类,非瞬时崩溃面)且
|
|
501
|
+
// conn 不存活。时序可靠性:catch 时刻紧随 onClose 收割,连接重建仅由
|
|
502
|
+
// conn.request 惰性触发——本链路中 runTurn finally 的 closeSession 对死连接
|
|
503
|
+
// 短路(channel 侧 !alive 守卫)、stop 只属 abort/超时入口(前者已被
|
|
504
|
+
// signal.aborted 短路、后者走上一分支)——此刻无 request 可重建,判据可靠。
|
|
505
|
+
if (!isAppServerRpcError(err) && !rt.conn.alive) {
|
|
506
|
+
return failedAppServerAttempt(err, currentSessionId, { retried: opts.retried, transient: "conn-closed" });
|
|
507
|
+
}
|
|
508
|
+
return failedAppServerAttempt(err, currentSessionId, { retried: opts.retried });
|
|
509
|
+
} finally {
|
|
510
|
+
if (currentSessionId !== undefined) rt.activeSessions.delete(currentSessionId);
|
|
511
|
+
if (ctx.signal !== undefined) ctx.signal.removeEventListener("abort", onAbort);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* killChain 后等待连接 finalize 实际完成(child 置空 + onClose 广播)再宣告链终局:
|
|
517
|
+
* shutdown resolve 于 `exit` 事件,而 finalize 挂 `close`(stdio 排空)——两者之间的
|
|
518
|
+
* 事件窗口内 conn.child 仍非 null,紧接的下一任务 request 会复用垂死进程(写入成功
|
|
519
|
+
* 但必败,走崩溃路径)而非触发重建。与 shutdownRuntimeAndDisposeChannel 的
|
|
520
|
+
* HARVEST_GRACE 同款 race 形态(close 永不到达不挂死)。超时入口的 await 链终局
|
|
521
|
+
* 语义(D6 重试时序依据)因此是「进程收割确认完成」而非「SIGTERM 已发出」。
|
|
522
|
+
*/
|
|
523
|
+
private async awaitConnFinalized(rt: AppServerRuntime): Promise<void> {
|
|
524
|
+
if (!rt.conn.alive) return;
|
|
525
|
+
await new Promise<void>((resolve) => {
|
|
526
|
+
const timer = setTimeout(() => finish(), ZCODE_APPSERVER_HARVEST_GRACE_MS);
|
|
527
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
528
|
+
const off = rt.conn.onClose(() => finish());
|
|
529
|
+
function finish(): void {
|
|
530
|
+
clearTimeout(timer);
|
|
531
|
+
off(); // Set.delete 幂等——并发触发无副作用
|
|
532
|
+
resolve();
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* D3 abort 链执行体(双入口分岔,P0-1 U2 参数化——设计
|
|
539
|
+
* timeout-zcode-turn-and-settled-watchdog.md §6 D3 v1.1):
|
|
540
|
+
* - **用户取消入口**(`escalateOn: "turn-settled"`,现状语义零改动;fire-and-forget——
|
|
541
|
+
* 与 turn promise 并行推进):stop 帧(超时 ZCODE_APPSERVER_STOP_TIMEOUT_MS)→
|
|
542
|
+
* grace 窗口内 turn 落定即止(不杀共享进程)→ 超窗 killChain(conn.shutdown 全序:
|
|
543
|
+
* SIGTERM→grace→SIGKILL)。turn 的最终落定由 attempt 主路径 await 收口,本链不
|
|
544
|
+
* 直接产出终态。abort 与 create 竞态(signal 先到、session 未建立):等会话建立
|
|
545
|
+
* (带上限)再发 stop——否则 stop 永远发不出,直接连坐杀共享进程。
|
|
546
|
+
* - **超时入口**(`escalateOn: "stop-outcome"`,channel 判死后由 catch await 链终局):
|
|
547
|
+
* turn 已 reject,对它 race 恒真不可用(v1 击穿点)——升级判据改挂在 **stop 应答
|
|
548
|
+
* 三态**:①成功应答 → 服务端接受停 turn,止损确认,链终止;②协议性 error 应答
|
|
549
|
+
* (有 error 帧即控制面活的证据,多因 runTurn finally 的 closeSession 先行关会话,
|
|
550
|
+
* 健康形态竞态)→ 链终止**不升级**(止损由 close 回收 + 服务端自治承担;把
|
|
551
|
+
* 「stop 报错」一律升级会误杀健康共享进程并连坐并发任务);③超时/写入失败/进程
|
|
552
|
+
* 死等连接级失败(控制面死)→ killChain 升级。判据实现依据:error 应答帧 reject
|
|
553
|
+
* 携带 number code(isAppServerRpcError);连接级失败是无 code 的新 Error
|
|
554
|
+
* (connection.request 三态 reject 形态)。返回值即止损路径(超时入口的 outcome
|
|
555
|
+
* 文案素材——D3 强制可观测面)。
|
|
556
|
+
*/
|
|
557
|
+
private async appServerAbortChain(
|
|
558
|
+
rt: AppServerRuntime,
|
|
559
|
+
turn: Promise<SessionTurnResult>,
|
|
560
|
+
getSessionId: () => string | undefined,
|
|
561
|
+
sessionCreated: Promise<void>,
|
|
562
|
+
entry: { escalateOn: "turn-settled" | "stop-outcome" },
|
|
563
|
+
): Promise<AbortChainStopPath> {
|
|
564
|
+
const graceRaceThenKill = async (): Promise<AbortChainStopPath> => {
|
|
565
|
+
const settled = await Promise.race([
|
|
566
|
+
turn.then(
|
|
567
|
+
() => true,
|
|
568
|
+
() => true,
|
|
569
|
+
),
|
|
570
|
+
delayResolved(ZCODE_APPSERVER_ABORT_GRACE_MS, false),
|
|
571
|
+
]);
|
|
572
|
+
if (settled) return "settled-in-grace"; // stop 生效:终态在 grace 窗口内到达,共享进程不杀
|
|
573
|
+
logger.warn(
|
|
574
|
+
`[zcode-engine] abort grace 窗口内未见终态——killChain 收割共享进程(接受连坐,在途任务走崩溃路径)`,
|
|
575
|
+
);
|
|
576
|
+
await rt.conn.shutdown({ graceMs: ZCODE_KILL_GRACE_MS });
|
|
577
|
+
await this.awaitConnFinalized(rt);
|
|
578
|
+
return "escalated-kill";
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
let sessionId = getSessionId();
|
|
582
|
+
if (sessionId === undefined) {
|
|
583
|
+
await Promise.race([sessionCreated, delayResolved(ZCODE_APPSERVER_STOP_TIMEOUT_MS, undefined)]);
|
|
584
|
+
sessionId = getSessionId();
|
|
585
|
+
if (sessionId === undefined) {
|
|
586
|
+
// 会话始终未建立:超时入口实际不可达(TurnTimeoutError 只能发生在 openTurn
|
|
587
|
+
// 挂 timer 后,彼时 create 已成功),防御分支——无会话即无在途任务可止损;
|
|
588
|
+
// 用户取消入口保持既有语义:跳过 stop,grace race 兜底(create 竞态挂死形态)
|
|
589
|
+
if (entry.escalateOn === "stop-outcome") return "no-session";
|
|
590
|
+
return graceRaceThenKill();
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
// [u-z2 修复轮] alive 守卫(与 closeSession 的 `!conn.alive` return 同款防御,
|
|
594
|
+
// 对称补齐):进程在 turn 判死/abort 与本链发 stop 之间 finalize 完成的微窗口内,
|
|
595
|
+
// request 首行 ensureStarted 会惰性 spawn 新一代进程再写 stop 帧——凭空拉起无人
|
|
596
|
+
// 使用的进程,且 stop-outcome 入口会拿到新进程的「成功应答」误报止损确认。
|
|
597
|
+
// 不 alive = 连接级失败形态(进程已死即已收割):超时入口直接落杀链终局(等同
|
|
598
|
+
// 三态的连接级失败分支);用户取消入口跳过 stop 落回 grace race(turn 已被
|
|
599
|
+
// failAllTurns 收割则立即 settled,语义零变化)。
|
|
600
|
+
if (!rt.conn.alive) {
|
|
601
|
+
if (entry.escalateOn === "stop-outcome") return "stop-unreachable-killed";
|
|
602
|
+
return graceRaceThenKill();
|
|
603
|
+
}
|
|
604
|
+
try {
|
|
605
|
+
await rt.conn.request("session/stop", { sessionId }, { timeoutMs: ZCODE_APPSERVER_STOP_TIMEOUT_MS });
|
|
606
|
+
} catch (err) {
|
|
607
|
+
if (entry.escalateOn === "stop-outcome") {
|
|
608
|
+
if (isAppServerRpcError(err)) {
|
|
609
|
+
// ② 协议性 error:控制面活、会话已被回收(健康形态竞态)——不升级
|
|
610
|
+
logger.debug(
|
|
611
|
+
`[zcode-engine] session/stop 报协议性 error(${errMessage(err)})——会话已回收,控制面存活,不升级杀链`,
|
|
612
|
+
);
|
|
613
|
+
return "stop-rejected";
|
|
614
|
+
}
|
|
615
|
+
// ③ 连接级失败(请求超时/写入失败/进程死):控制面死,只有杀进程能止损
|
|
616
|
+
logger.warn(
|
|
617
|
+
`[zcode-engine] session/stop 无应答(${errMessage(err)})——升级 killChain 收割共享进程(超时入口,接受连坐)`,
|
|
618
|
+
);
|
|
619
|
+
await rt.conn.shutdown({ graceMs: ZCODE_KILL_GRACE_MS });
|
|
620
|
+
await this.awaitConnFinalized(rt);
|
|
621
|
+
return "stop-unreachable-killed";
|
|
622
|
+
}
|
|
623
|
+
logger.debug(
|
|
624
|
+
`[zcode-engine] session/stop 失败(${errMessage(err)})——grace 后走 killChain 兜底`,
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
if (entry.escalateOn === "stop-outcome") return "stop-acked"; // ① 成功应答:止损确认,链终止
|
|
628
|
+
return graceRaceThenKill();
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// ── 常驻运行时管理(D1/D6)──────────────────────
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* 惰性获取常驻运行时(D1:每引擎实例一条连接,全任务共享;连接自身的崩溃重建在
|
|
635
|
+
* connection 层内部完成——同一条代码路径,§3.4 不变量 4)。常驻进程不进宿主
|
|
636
|
+
* spawnedChildren、不调 onChildSpawned(D6——生命周期归 dispose)。进程级 --cwd
|
|
637
|
+
* 用引擎数据目录(连接跨任务共享的中性位置,工作区由 create 的
|
|
638
|
+
* workspace.workspacePath 按任务传递——D10 基线不预设任务级进程 cwd)。
|
|
639
|
+
* spawn 经 fs 拦截 wrapper(appserver-launcher:cli config 读取重定向为
|
|
640
|
+
* 「真实文件 + v2 provider 注入」——CLI 形态 app-server 在共享宿主 HOME 下的
|
|
641
|
+
* 唯一凭据供数通路,机制与漂移面见该文件头注)。
|
|
642
|
+
*/
|
|
643
|
+
private ensureAppServerRuntime(): AppServerRuntime {
|
|
644
|
+
if (this.appserverRuntime !== undefined) return this.appserverRuntime;
|
|
645
|
+
const cliPath = this.deps.cliPath ?? ZCODE_CLI_DEFAULT_PATH;
|
|
646
|
+
const engineDataDir = this.deps.engineDataDir();
|
|
647
|
+
const launcherScript = ensureAppServerLauncher(engineDataDir);
|
|
648
|
+
const env = buildAppServerEnv(this.deps.processEnv ?? process.env);
|
|
649
|
+
env.ZCODE_ENG_CLI_PATH = cliPath;
|
|
650
|
+
env.ZCODE_ENG_V2_CONFIG = this.deps.sources?.v2ConfigPath ?? defaultV2ConfigPath();
|
|
651
|
+
const conn = new AppServerConnection({
|
|
652
|
+
cliPath,
|
|
653
|
+
cwd: engineDataDir,
|
|
654
|
+
env,
|
|
655
|
+
launcherScript,
|
|
656
|
+
stderrLogPath: path.join(engineDataDir, "logs", "zcode-appserver-stderr.log"),
|
|
657
|
+
});
|
|
658
|
+
const rt: AppServerRuntime = {
|
|
659
|
+
conn,
|
|
660
|
+
channel: new SessionChannel(conn),
|
|
661
|
+
activeSessions: new Set<string>(),
|
|
662
|
+
};
|
|
663
|
+
this.appserverRuntime = rt;
|
|
664
|
+
return rt;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* [R5 修复 R4 既有竞态] shutdown → 等崩溃收割实际发生 → channel 退订。killChain 在
|
|
669
|
+
* `exit` 事件 resolve,而连接 finalize(onClose → channel 的 failAllTurns)挂
|
|
670
|
+
* `close` 事件——两者之间有一个事件循环窗口:shutdown resolve 后立即退订,在途
|
|
671
|
+
* turn 会错过收割、挂到 turnTimeoutMs(300s)。退订前等 onClose 触发(本方法先于
|
|
672
|
+
* shutdown 订阅;channel 的订阅在构造期更早——其 failAllTurns 先于本 promise
|
|
673
|
+
* resolve 执行);ZCODE_APPSERVER_HARVEST_GRACE_MS 兜底防 `close` 永不到达时挂死。
|
|
674
|
+
* [P0-1 U5/D7] grace race 输掉(close 迟到/永不到达——stdio 被孙进程持有排空不
|
|
675
|
+
* 尽等病态形态)时,channel.dispose() 内置的 dispose 收割(failAllTurns 先于退订,
|
|
676
|
+
* SessionChannel.dispose)兜底在途 turn——退化终点从「挂满 turn 自身 idle/总上界
|
|
677
|
+
* 预算」收敛为「grace 窗口内明确失败」(设计 §3.4 退化路径闭合);race 窗口与
|
|
678
|
+
* awaitConnFinalized 同源同量级(ZCODE_APPSERVER_HARVEST_GRACE_MS)。正常 close
|
|
679
|
+
* 先到时 onClose 收割先行,dispose 收割幂等 no-op(零回归)。
|
|
680
|
+
*/
|
|
681
|
+
private async shutdownRuntimeAndDisposeChannel(rt: AppServerRuntime): Promise<void> {
|
|
682
|
+
const harvested = new Promise<void>((resolve) => {
|
|
683
|
+
const off = rt.conn.onClose(() => {
|
|
684
|
+
off();
|
|
685
|
+
resolve();
|
|
686
|
+
});
|
|
687
|
+
});
|
|
688
|
+
await rt.conn.shutdown({ graceMs: ZCODE_KILL_GRACE_MS });
|
|
689
|
+
// close 未在 grace 内到达 → 输掉 race → channel.dispose() 的内置收割兜底
|
|
690
|
+
await Promise.race([harvested, delayResolved(ZCODE_APPSERVER_HARVEST_GRACE_MS, undefined)]);
|
|
691
|
+
rt.channel.dispose();
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* [R1 D6 主体] 引擎停机面:①fire 全部在途会话的 session/close 帧(不等待
|
|
696
|
+
* 应答——D6① 顺序规定:close 帧必须先于 SIGTERM,否则对面来不及处理即被杀)→
|
|
697
|
+
* ②同步 SIGTERM(conn.shutdown 调用内 killChain 前缀同步执行——同步面在返回
|
|
698
|
+
* Promise 前完成)→ ③grace → SIGKILL(异步面,Promise resolve 于进程退出)。
|
|
699
|
+
* 幂等:运行时字段取走即置空,二次调用零副作用;dispose 后首个 run 经
|
|
700
|
+
* ensureAppServerRuntime 自动重建(与崩溃重建同一代码路径,不变量 4)。
|
|
701
|
+
*/
|
|
702
|
+
async dispose(): Promise<void> {
|
|
703
|
+
// [P0-1 U4] 停机标志先行:在途任务的瞬时重试判定据此短路(dispose 收割引发的
|
|
704
|
+
// 崩溃不再触发重试轮——重试轮会经惰性重建复活已停机引擎)
|
|
705
|
+
this.disposed = true;
|
|
706
|
+
const rt = this.appserverRuntime;
|
|
707
|
+
if (rt === undefined) return;
|
|
708
|
+
this.appserverRuntime = undefined;
|
|
709
|
+
// ①fire 全部在途会话的 session/close 帧(不等待应答——D6① 顺序规定:close 帧
|
|
710
|
+
// 必须先于 SIGTERM,否则对面来不及处理即被杀)。进程已死(child=null,如崩溃
|
|
711
|
+
// 收割与 activeSessions 清理之间的微拍)则整体跳过:post 会经 ensureStarted 惰性
|
|
712
|
+
// 拉起新进程再被同次 dispose 杀掉——无意义的 spawn+kill 循环(D6 dispose=防泄漏
|
|
713
|
+
// 语义不制造新进程)。同步循环内 alive 不会中途翻转(进程退出是异步事件,本轮
|
|
714
|
+
// 循环不可重入)
|
|
715
|
+
if (rt.conn.alive) {
|
|
716
|
+
for (const sessionId of [...rt.activeSessions]) {
|
|
717
|
+
rt.conn.post("session/close", { sessionId });
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
// ②③ 同步 SIGTERM(killChain 前缀在 shutdown 调用内同步执行)→ grace → SIGKILL
|
|
721
|
+
//(异步面,resolve 于进程退出)。channel 退订放在崩溃收割之后(exit→close 窗口
|
|
722
|
+
//竞态的修复体,见 shutdownRuntimeAndDisposeChannel——先退订会让在途 turn 挂到
|
|
723
|
+
//turnTimeoutMs)
|
|
724
|
+
await this.shutdownRuntimeAndDisposeChannel(rt);
|
|
283
725
|
}
|
|
284
726
|
|
|
285
727
|
/** 终态合成(extension-conventions 函数 80 行上限,从 run 提取):aborted / run-failed / parsed 三分支。 */
|
|
286
728
|
private finalizeOutcome(
|
|
287
|
-
task:
|
|
729
|
+
task: AgentCallOpts,
|
|
288
730
|
ctx: RunContext,
|
|
289
731
|
final: AttemptResult,
|
|
290
732
|
usageAcc: { input: number; output: number; cacheRead: number; cacheWrite: number; has: boolean },
|
|
@@ -316,31 +758,37 @@ export class ZcodeEngine implements EnginePort {
|
|
|
316
758
|
/** abort 合成终态:exitCode=null(record 正常收尾,不留僵尸)。 */
|
|
317
759
|
private applyAbortedOutcome(
|
|
318
760
|
outcome: AgentOutcome,
|
|
319
|
-
task:
|
|
761
|
+
task: AgentCallOpts,
|
|
320
762
|
ctx: RunContext,
|
|
321
763
|
final: Extract<AttemptResult, { kind: "aborted" }>,
|
|
322
764
|
emit: (event: AgentEvent) => void,
|
|
323
765
|
): void {
|
|
324
766
|
outcome.exitCode = null;
|
|
325
767
|
// 对齐点④:宿主超时(mergeTimeoutSignal 的 timeout abort)统一走公共合成终态
|
|
326
|
-
// (common/kill-chain.synthesizeTimeoutOutcome——engine_timeout 文案 SSOT
|
|
327
|
-
//
|
|
328
|
-
//
|
|
768
|
+
// (common/kill-chain.synthesizeTimeoutOutcome——engine_timeout 文案 SSOT + 「可用
|
|
769
|
+
// engine: pi 重跑」建议);用户主动 cancel 维持 engine_run_failed 中止标记(非超时
|
|
770
|
+
// 语义,不冒充超时)。?? 兜底是类型收窄(合成器恒写 error)。
|
|
329
771
|
outcome.error = isHostTimeoutAbort(ctx)
|
|
330
772
|
? synthesizeTimeoutOutcome(task, final.output.stdoutText, ZCODE_ENGINE_ID).error ??
|
|
331
773
|
engineTimeoutDetail(final.output.stdoutText)
|
|
332
|
-
:
|
|
333
|
-
`
|
|
774
|
+
: final.abortMessage ??
|
|
775
|
+
`engine_run_failed: zcode 任务被中止(app-server abort 链收口,宿主合成终态)。` +
|
|
776
|
+
`输出尾部: ${final.output.stdoutText.slice(-ZCODE_ERROR_TAIL_CHARS)}`;
|
|
334
777
|
emit({ type: "error", message: outcome.error });
|
|
335
778
|
}
|
|
336
779
|
|
|
337
|
-
/**
|
|
780
|
+
/**
|
|
781
|
+
* run-failed 合成终态:错误信息由 buildAppServerRunFailedMessage 产出(已含恢复
|
|
782
|
+
* 指引)直接透传;附带的会话 id 落 outcome.sessionId(错误规格表 -32004 行「含会话
|
|
783
|
+
* id」——appServerHandle 据此写 handle.sessionRef,run-failed 不再恒缺)。
|
|
784
|
+
*/
|
|
338
785
|
private applyRunFailedOutcome(
|
|
339
786
|
outcome: AgentOutcome,
|
|
340
787
|
final: Extract<AttemptResult, { kind: "run-failed" }>,
|
|
341
788
|
emit: (event: AgentEvent) => void,
|
|
342
789
|
): void {
|
|
343
790
|
outcome.exitCode = final.output.exitCode;
|
|
791
|
+
if (final.sessionId !== undefined) outcome.sessionId = final.sessionId;
|
|
344
792
|
outcome.error = final.message;
|
|
345
793
|
emit({ type: "error", message: outcome.error });
|
|
346
794
|
}
|
|
@@ -386,97 +834,6 @@ export class ZcodeEngine implements EnginePort {
|
|
|
386
834
|
for (const ev of synthesizeCoarseEvents(payload.response, payload.usage)) emit(ev);
|
|
387
835
|
}
|
|
388
836
|
|
|
389
|
-
/**
|
|
390
|
-
* 单轮执行(launch → collect → parse → schema 校验)。产出三态之一给 run 编排:
|
|
391
|
-
* aborted(我方杀链)/ run-failed(非零退出或解析失败)/ parsed(含 schema 校验结果)。
|
|
392
|
-
*/
|
|
393
|
-
private async attemptOnce(
|
|
394
|
-
task: AgentTaskSpec,
|
|
395
|
-
ctx: RunContext,
|
|
396
|
-
prepared: ReturnType<typeof prepareZcodeHome>,
|
|
397
|
-
cwd: string,
|
|
398
|
-
prompt: string,
|
|
399
|
-
): Promise<AttemptResult> {
|
|
400
|
-
// ② argv 组装 + 字节估算(超限抛 prompt_too_large——进程创建前;公共预算权威)
|
|
401
|
-
const args = buildZcodeArgv({ cwd, prompt, denyTools: task.denyTools });
|
|
402
|
-
const cliPath = this.deps.cliPath ?? ZCODE_CLI_DEFAULT_PATH;
|
|
403
|
-
assertZcodeArgvBudget("node", cliPath, args);
|
|
404
|
-
|
|
405
|
-
// ③ launch:spawn 单轮(stdin ignore;HOME=池目录;嵌套标记经公共 nesting-guard)
|
|
406
|
-
const launch = this.deps.launch ?? launchZcodeProcess;
|
|
407
|
-
const env = buildZcodeEnv(prepared.homeDir, this.deps.processEnv ?? process.env);
|
|
408
|
-
let proc: ZcodeLaunchedProcess;
|
|
409
|
-
try {
|
|
410
|
-
proc = launch({ cliPath, args, env });
|
|
411
|
-
} catch (err) {
|
|
412
|
-
// spawn 同步失败(node 缺失等):进程未创建,按 prepare 期语义 reject
|
|
413
|
-
throw new Error(
|
|
414
|
-
`[engine_run_failed] 无法启动 zcode CLI(${cliPath}):${err instanceof Error ? err.message : String(err)}。` +
|
|
415
|
-
`恢复指引:确认 node 在 PATH 且 ${cliPath} 存在(probe 可探测),或改用 engine: pi。`,
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// [U0 D10] 终止链路径①:spawn 成功后同步注册子进程句柄进宿主 spawnedChildren
|
|
420
|
-
// 记账(port.ts 契约「spawn 成功后同步回调」)——cancelBackground SIGTERM /
|
|
421
|
-
// dispose killAll 收割兜底对引擎 record 生效,防 controller 丢失/竞态场景下孤儿进程
|
|
422
|
-
ctx.onChildSpawned?.(proc.child);
|
|
423
|
-
|
|
424
|
-
// abort 分级(D1):zcode 无原生中断(interrupt: kill-only),AbortSignal 直通
|
|
425
|
-
// SIGTERM → grace → SIGKILL 杀链,终态由宿主合成
|
|
426
|
-
const onAbort = (): void => {
|
|
427
|
-
void proc.abort(ZCODE_KILL_GRACE_MS);
|
|
428
|
-
};
|
|
429
|
-
if (ctx.signal !== undefined) {
|
|
430
|
-
if (ctx.signal.aborted) onAbort();
|
|
431
|
-
else ctx.signal.addEventListener("abort", onAbort, { once: true });
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
// ④ parser:有界收集 + 终 JSON
|
|
435
|
-
const output = await collectZcodeOutput(proc);
|
|
436
|
-
if (ctx.signal !== undefined) ctx.signal.removeEventListener("abort", onAbort);
|
|
437
|
-
|
|
438
|
-
if (proc.killedByUs()) return { kind: "aborted", output };
|
|
439
|
-
if (output.exitCode !== 0) {
|
|
440
|
-
return {
|
|
441
|
-
kind: "run-failed",
|
|
442
|
-
output,
|
|
443
|
-
message: buildRunFailedMessage({
|
|
444
|
-
cliPath,
|
|
445
|
-
exitCode: output.exitCode,
|
|
446
|
-
stdoutTail: output.stdoutText,
|
|
447
|
-
stderrTail: output.stderrTail,
|
|
448
|
-
modelRef: prepared.modelRef,
|
|
449
|
-
configPath: prepared.configPath,
|
|
450
|
-
}),
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
const terminal = parseZcodeTerminal(output.stdoutText);
|
|
454
|
-
if (!terminal.ok) {
|
|
455
|
-
return {
|
|
456
|
-
kind: "run-failed",
|
|
457
|
-
output,
|
|
458
|
-
message: buildRunFailedMessage({
|
|
459
|
-
cliPath,
|
|
460
|
-
exitCode: output.exitCode,
|
|
461
|
-
stdoutTail: output.stdoutText,
|
|
462
|
-
stderrTail: output.stderrTail,
|
|
463
|
-
parseReason: terminal.reason,
|
|
464
|
-
modelRef: prepared.modelRef,
|
|
465
|
-
configPath: prepared.configPath,
|
|
466
|
-
}),
|
|
467
|
-
};
|
|
468
|
-
}
|
|
469
|
-
const schema = isPlainObject(task.schema) ? task.schema : undefined;
|
|
470
|
-
return {
|
|
471
|
-
kind: "parsed",
|
|
472
|
-
output,
|
|
473
|
-
payload: terminal.payload,
|
|
474
|
-
...(schema !== undefined
|
|
475
|
-
? { schemaResult: extractAndValidateStructuredOutput(terminal.payload.response, schema) }
|
|
476
|
-
: {}),
|
|
477
|
-
};
|
|
478
|
-
}
|
|
479
|
-
|
|
480
837
|
/**
|
|
481
838
|
* D1 可选面:zcode 首期不支持 conversation(capabilities 声明)——同步拒绝、
|
|
482
839
|
* 不创建进程,文案给可操作建议(A11)。
|
|
@@ -487,22 +844,36 @@ export class ZcodeEngine implements EnginePort {
|
|
|
487
844
|
code: "engine_capability_unsupported",
|
|
488
845
|
message:
|
|
489
846
|
"zcode 引擎不支持 conversation 交互控制面(capabilities.conversation = 'unsupported'," +
|
|
490
|
-
"
|
|
847
|
+
"每任务自包含会话,无同进程 idle 复用)。恢复指引:改用单次 subagent 调用重新派发任务," +
|
|
491
848
|
"或使用 engine: 'pi'(chatMode idle 复用,支持 message/close/cancel)。",
|
|
492
849
|
};
|
|
493
850
|
}
|
|
494
851
|
|
|
495
|
-
/**
|
|
496
|
-
* D6 read 三级降级:①sqlite 原生读取 → ②宿主 event journal 重放(对齐点①接线:
|
|
497
|
-
* replayJournalToSessionView 复用 live reducer,重放等价性见 §3.3.6)→ ③outcome-only。
|
|
498
|
-
* sessionId 缺失(解析失败的 run 无法在共享池 db 内定位 session)跳过①级;②级
|
|
499
|
-
* 依赖 handle.journalPath(宿主 run 后回填)。
|
|
500
|
-
*/
|
|
501
852
|
/** [U7] 模型可发现性:v2 桌面登录态聚合(带凭据 provider × models),失败安全返回清单本身可能为空。 */
|
|
502
853
|
listModels(): Array<{ id: string; name?: string }> {
|
|
503
854
|
return listZcodeModels(this.deps.sources);
|
|
504
855
|
}
|
|
505
856
|
|
|
857
|
+
/**
|
|
858
|
+
* [u-h2 D2-2] 派发同步期 model 校验:委托 resolveZcodeModelRef(与 run prepare 期
|
|
859
|
+
* 同一函数——canonicalRef 归一化、短名缺省 provider、凭据与清单校验单一权威,无双实现)。
|
|
860
|
+
* modelRef undefined = 返回引擎缺省模型 canonical 全名(ZCODE_FALLBACK_DEFAULT_MODEL,
|
|
861
|
+
* D2-1 ctxModel 不透传的承接面)。校验失败原样抛 ZcodePrepareError,由编排层
|
|
862
|
+
* (engine/model-validation.ts)包装成「引擎与模型不配套」文案。
|
|
863
|
+
*/
|
|
864
|
+
validateModel(modelRef: string | undefined): { canonicalRef: string } {
|
|
865
|
+
return { canonicalRef: resolveZcodeModelRef(modelRef, this.deps.sources) };
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
/**
|
|
869
|
+
* D6 read 三级降级:①sqlite 原生读取 → ②宿主 event journal 重放(对齐点①接线:
|
|
870
|
+
* replayJournalToSessionView 复用 live reducer,重放等价性见 §3.3.6)→ ③outcome-only。
|
|
871
|
+
* sessionId 缺失(解析失败的 run 无法定位 session)跳过①级;②级依赖
|
|
872
|
+
* handle.journalPath(宿主 run 后回填)。dbPath:新 handle 恒绝对路径(宿主
|
|
873
|
+
* ~/.zcode/cli/db/db.sqlite,tier1 精确匹配白名单见方法体);旧 records(池时代)
|
|
874
|
+
* 的相对路径仍按 poolKey 锚定解析(read 兼容旧数据,池目录不存在时自然落②级
|
|
875
|
+
* journal 降级)。
|
|
876
|
+
*/
|
|
506
877
|
async read(handle: EngineHandle): Promise<SessionView> {
|
|
507
878
|
if (handle.data.engineId !== ZCODE_ENGINE_ID) {
|
|
508
879
|
return { engineId: ZCODE_ENGINE_ID, turns: [], source: "outcome-only" };
|
|
@@ -510,18 +881,33 @@ export class ZcodeEngine implements EnginePort {
|
|
|
510
881
|
const sessionId = handle.data.sessionRef["sessionId"];
|
|
511
882
|
const dbPathRaw = handle.data.sessionRef["dbPath"];
|
|
512
883
|
if (typeof sessionId === "string" && typeof dbPathRaw === "string") {
|
|
513
|
-
//
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
884
|
+
// 绝对路径 tier1 白名单(与 runtime subagent-engine-history 同判):handle/
|
|
885
|
+
// record 来自 append-only JSONL(不可信面),仅放行宿主真实 db 的精确匹配,
|
|
886
|
+
// 其余绝对路径拒绝 ①级 sqlite 读取、降 journal 重放——防任意文件读
|
|
887
|
+
let dbPath: string | undefined;
|
|
888
|
+
if (path.isAbsolute(dbPathRaw)) {
|
|
889
|
+
if (dbPathRaw === hostZcodeDbPath()) dbPath = dbPathRaw;
|
|
890
|
+
else {
|
|
891
|
+
logger.warn("[zcode-engine] record dbPath 非宿主 db 绝对路径,拒绝 ①级读取降 journal", {
|
|
892
|
+
dbPath: dbPathRaw,
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
} else {
|
|
896
|
+
dbPath = path.join(
|
|
897
|
+
resolvePoolDir(this.deps.engineDataDir(), ZCODE_ENGINE_ID, handle.data.poolKey),
|
|
898
|
+
dbPathRaw,
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
if (dbPath !== undefined) {
|
|
902
|
+
try {
|
|
903
|
+
return await readZcodeSessionView(dbPath, sessionId);
|
|
904
|
+
} catch (err) {
|
|
905
|
+
logger.warn("[zcode-engine] native session read failed, degrade to journal replay", {
|
|
906
|
+
dbPath,
|
|
907
|
+
sessionId,
|
|
908
|
+
reason: toErrorMessage(err),
|
|
909
|
+
});
|
|
910
|
+
}
|
|
525
911
|
}
|
|
526
912
|
}
|
|
527
913
|
// ②级:journal 重放(journalPath 缺省 / 文件不存在 / 无事件 → undefined 落③级)
|
|
@@ -533,44 +919,53 @@ export class ZcodeEngine implements EnginePort {
|
|
|
533
919
|
// ── 内部 ──
|
|
534
920
|
|
|
535
921
|
/**
|
|
536
|
-
*
|
|
537
|
-
*
|
|
538
|
-
*
|
|
539
|
-
*
|
|
540
|
-
*
|
|
541
|
-
*
|
|
922
|
+
* [RX2-F1] appserver 路径的非常见档位提示:thinkingLevel → thoughtLevel 恒等透传
|
|
923
|
+
* (F15a),全 7 档放行不拦截——但部分档位(off/minimal/medium/xhigh 等)不在部分
|
|
924
|
+
* 模型的合法值域内(如 GLM-5.3 仅接受 low/high/max),app-server 侧对不支持的档位
|
|
925
|
+
* warn-skip(会话照常但档位静默失效),调用方无从察觉。此处仅对
|
|
926
|
+
* COMMON_THOUGHT_LEVELS 之外的档位出声一行提示(措辞是「若不支持将被忽略/回落」的
|
|
927
|
+
* 或然警告,非无效断言);是否真不支持由目标模型决定,core 不做权威校验(引擎层
|
|
928
|
+
* 不掌握各模型值域)。
|
|
542
929
|
*/
|
|
543
|
-
private
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
)
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
930
|
+
private warnThoughtLevelUncommon(task: AgentCallOpts, ctx: RunContext): void {
|
|
931
|
+
const thoughtLevel = task.thinkingLevel?.trim();
|
|
932
|
+
if (thoughtLevel === undefined || thoughtLevel === "") return;
|
|
933
|
+
if (COMMON_THOUGHT_LEVELS.includes(thoughtLevel)) return;
|
|
934
|
+
logger.warn(
|
|
935
|
+
`[zcode-engine] thinkingLevel=${thoughtLevel} 已透传为 thoughtLevel(非常见档位):若目标模型不支持该档位将被忽略/回落到模型缺省推理档位(常见档位:${COMMON_THOUGHT_LEVELS.join("/")});档位是否生效以模型实际行为为准`,
|
|
936
|
+
{ taskId: ctx.taskId },
|
|
937
|
+
);
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
/**
|
|
941
|
+
* [F16b] ctxModel 忽略留痕:ctxModel 是 pi 链路的第三层兜底(port.ts 契约——
|
|
942
|
+
* 依赖 pi resolveModel 链的引擎才消费它),zcode 自带 provider 体系与缺省模型
|
|
943
|
+
* (resolveZcodeModelRef:requested > ZCODE_FALLBACK_DEFAULT_MODEL),不消费
|
|
944
|
+
* ctxModel。「调用方给了 ctxModel 但 task.model 未显式指定」时出声一行,说明
|
|
945
|
+
* 实际落引擎缺省模型(含实际 model id)——防静默降档无据可查。只在「ctx 有模型
|
|
946
|
+
* 但被忽略」场景输出:显式 task.model 走正常解析链、ctx 本就无模型属预期缺省,
|
|
947
|
+
* 均不出声(避免噪音)。
|
|
948
|
+
*/
|
|
949
|
+
private warnIgnoredCtxModel(task: AgentCallOpts, ctx: RunContext, modelRef: string): void {
|
|
950
|
+
if (ctx.ctxModel === undefined) return;
|
|
951
|
+
const requested = task.model?.trim();
|
|
952
|
+
if (requested !== undefined && requested !== "") return;
|
|
953
|
+
logger.warn(
|
|
954
|
+
`[zcode-engine] ctx.ctxModel(${ctx.ctxModel.id})被忽略——ctxModel 是 pi 链路兜底,zcode 不消费;` +
|
|
955
|
+
`task.model 未显式指定,实际使用引擎缺省模型 ${modelRef}`,
|
|
956
|
+
{ taskId: ctx.taskId },
|
|
957
|
+
);
|
|
564
958
|
}
|
|
565
959
|
|
|
566
960
|
/**
|
|
567
961
|
* persona 拼接后的完整 prompt(personaInjection: 'prompt'——zcode 无 flag 通道):
|
|
568
|
-
* appendSystemPrompt
|
|
569
|
-
*
|
|
962
|
+
* appendSystemPrompt 段在前(人设/约束语境——D6 合流后 persona≡skillPath+
|
|
963
|
+
* appendSystemPrompt 平铺,由上游解析进 appendSystemPrompt),task 正文居中,
|
|
964
|
+
* schema 仿真段尾置(common/schema-emulation 公共层产出)。
|
|
570
965
|
*/
|
|
571
|
-
private buildPrompt(task:
|
|
572
|
-
const segments: string[] = [...(task.
|
|
573
|
-
segments.push(task.
|
|
966
|
+
private buildPrompt(task: AgentCallOpts, schema: JsonSchemaObject | undefined): string {
|
|
967
|
+
const segments: string[] = [...(task.appendSystemPrompt ?? [])];
|
|
968
|
+
segments.push(task.prompt);
|
|
574
969
|
if (schema !== undefined) segments.push(buildSchemaEmulationSegment(schema));
|
|
575
970
|
return segments.join("\n\n");
|
|
576
971
|
}
|
|
@@ -578,22 +973,180 @@ export class ZcodeEngine implements EnginePort {
|
|
|
578
973
|
|
|
579
974
|
// ── 模块级辅助(run 的重试编排件) ──
|
|
580
975
|
|
|
581
|
-
/**
|
|
976
|
+
/** app-server 路径的合成 output 形态(stdoutText 恒空——失败素材在 message 内)。 */
|
|
977
|
+
interface AttemptOutput {
|
|
978
|
+
exitCode: number | null;
|
|
979
|
+
stdoutText: string;
|
|
980
|
+
stderrTail: string;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* attempt 的三态产物(run 按序编排重试与终态合成)。
|
|
985
|
+
*/
|
|
582
986
|
type AttemptResult =
|
|
583
|
-
| { kind: "aborted"; output:
|
|
584
|
-
| {
|
|
987
|
+
| { kind: "aborted"; output: AttemptOutput; abortMessage?: string }
|
|
988
|
+
| {
|
|
989
|
+
kind: "run-failed";
|
|
990
|
+
output: AttemptOutput;
|
|
991
|
+
message: string;
|
|
992
|
+
/** appserver 路径失败时已建立的会话 id(错误规格表 -32004 行:按任务失败上报含会话 id)。 */
|
|
993
|
+
sessionId?: string;
|
|
994
|
+
/**
|
|
995
|
+
* [P0-1 U4/D6] 瞬时失败形态标记(重试判定判据——类型化字段,不经字符串
|
|
996
|
+
* 反推):timeout = channel 判死(TurnTimeoutError,idle/ceiling 都算——D6
|
|
997
|
+
* 明文两类均可重试);conn-closed = 连接崩溃收割(failAllTurns 形态,判据 =
|
|
998
|
+
* 非 RPC error 且 conn 不存活——catch 时刻重建仅由 conn.request 惰性触发,
|
|
999
|
+
* 此前无 request,判据可靠)。缺席 = 非瞬时形态(RPC 错误/status=error 终态/
|
|
1000
|
+
* send 未送达等),不参与重试(D6 被否③:status='error' 终态 v1 不重试)。
|
|
1001
|
+
*/
|
|
1002
|
+
transient?: "timeout" | "conn-closed";
|
|
1003
|
+
}
|
|
585
1004
|
| {
|
|
586
1005
|
kind: "parsed";
|
|
587
|
-
output:
|
|
1006
|
+
output: AttemptOutput;
|
|
588
1007
|
payload: ZcodeTerminalPayload;
|
|
589
1008
|
schemaResult?: { ok: true; parsed: unknown } | { ok: false; error: string; tail: string };
|
|
590
1009
|
};
|
|
591
1010
|
|
|
592
|
-
/**
|
|
593
|
-
|
|
1011
|
+
/**
|
|
1012
|
+
* [P0-1 U4/D6] 瞬时失败自动重试的最小剩余预算下限(ms):显式总上界预算下,剩余
|
|
1013
|
+
* 低于此值不再重试直接终态化(D6「剩余不足一个最小下限(如 5min)」——重跑一轮
|
|
1014
|
+
* 整任务的最小耗时估计,剩余更小的重试注定再被上界回收,白烧一轮 token)。
|
|
1015
|
+
* 单消费方(本文件重试编排),故为模块常量不进 constants.ts(跨文件共享才上移)。
|
|
1016
|
+
*/
|
|
1017
|
+
export const ZCODE_TURN_RETRY_MIN_BUDGET_MS = 300_000;
|
|
1018
|
+
|
|
1019
|
+
/** resolveTransientRetryBudget 的判定结果(可判别联合——inherit 分支剩余值必有)。 */
|
|
1020
|
+
export type ZcodeTurnRetryBudget =
|
|
1021
|
+
| { state: "inherit"; remainingMs: number }
|
|
1022
|
+
| { state: "depleted" }
|
|
1023
|
+
| { state: "unbounded" };
|
|
1024
|
+
|
|
1025
|
+
/**
|
|
1026
|
+
* [P0-1 U4/D6 预算继承] 重试轮预算判定(纯函数,P-Z4 探针「显式预算下重试轮不
|
|
1027
|
+
* 重置总预算」的数学本体——剩余 = 总预算 − 已耗尽):显式总上界预算存在时重试轮
|
|
1028
|
+
* 上界收窄为剩余(不重置),剩余不足最小下限则不重试;非显式(env 未设/非法/
|
|
1029
|
+
* ≤0 显式关闭)为 unbounded——无「总预算」可言,重试轮走 env/默认全新上界(与
|
|
1030
|
+
* 首轮同源,行为一致),不受预算门禁。
|
|
1031
|
+
*/
|
|
1032
|
+
export function resolveTransientRetryBudget(
|
|
1033
|
+
totalBudgetMs: number | undefined,
|
|
1034
|
+
consumedMs: number,
|
|
1035
|
+
): ZcodeTurnRetryBudget {
|
|
1036
|
+
if (totalBudgetMs === undefined) return { state: "unbounded" };
|
|
1037
|
+
const remainingMs = totalBudgetMs - consumedMs;
|
|
1038
|
+
return remainingMs >= ZCODE_TURN_RETRY_MIN_BUDGET_MS
|
|
1039
|
+
? { state: "inherit", remainingMs }
|
|
1040
|
+
: { state: "depleted" };
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
/**
|
|
1044
|
+
* 显式总上界预算读取(P-Z4 门禁的「显式」判定——D6「显式设置了 turnTimeoutMs
|
|
1045
|
+
* (env 或内部传参)」在引擎侧的唯一来源是 env;引擎内部传参点只用于向重试轮传
|
|
1046
|
+
* 剩余值):env 设置为正数 → 显式预算;未设/非法(走默认)与 ≤0(显式关闭上界)
|
|
1047
|
+
* 均非显式预算(undefined → unbounded)。读 process.env 直连(与 session-channel
|
|
1048
|
+
* 的 resolveTurnTimerMs 同源同通道,vi.stubEnv 可测——D2 env 通道一致性)。
|
|
1049
|
+
*/
|
|
1050
|
+
function explicitTurnBudgetMs(): number | undefined {
|
|
1051
|
+
const parsed = parseZcodeTurnTimeoutEnv(process.env[ZCODE_TURN_MAX_TIMEOUT_ENV]);
|
|
1052
|
+
return parsed.state === "valid" && parsed.ms > 0 ? parsed.ms : undefined;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
/**
|
|
1056
|
+
* task.schema 的最小 JSON Schema 形状(S13:替代裸 object——序列化边界上表达
|
|
1057
|
+
* 「ajv 可消费的 schema 对象」;具体关键字(type/properties/required…)由
|
|
1058
|
+
* schema-emulation 层解释,此处只约束对象形态)。
|
|
1059
|
+
*/
|
|
1060
|
+
type JsonSchemaObject = Readonly<Record<string, unknown>>
|
|
1061
|
+
|
|
1062
|
+
/** Record 形状 guard(task.schema 的运行时窄化——JsonSchemaObject 不满足 ajv 的 object 入参)。 */
|
|
1063
|
+
function isPlainObject(v: unknown): v is JsonSchemaObject {
|
|
594
1064
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
595
1065
|
}
|
|
596
1066
|
|
|
1067
|
+
/** 错误/日志出声用的 message 提取(非 Error 值不抛二次异常)。 */
|
|
1068
|
+
function errMessage(err: unknown): string {
|
|
1069
|
+
return toErrorMessage(err);
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/** ms 后 resolve 指定值(abort 链 grace 窗口的 race 材料;unref 不阻塞进程退出)。 */
|
|
1073
|
+
function delayResolved<T>(ms: number, value: T): Promise<T> {
|
|
1074
|
+
return new Promise<T>((resolve) => {
|
|
1075
|
+
const t = setTimeout(() => resolve(value), ms);
|
|
1076
|
+
if (typeof t.unref === "function") t.unref();
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
/** appserver 路径的合成 output(无 stdout 可收集——exitCode 语义:0=正常轮;null=中止/连接级失败)。 */
|
|
1081
|
+
function syntheticAppServerOutput(exitCode: number | null): AttemptOutput {
|
|
1082
|
+
return { exitCode, stdoutText: "", stderrTail: "" };
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
/** appserver 路径的 aborted 三态(abortMessage 描述 D3 链形态)。 */
|
|
1086
|
+
function abortedAppServerAttempt(ctx: RunContext): AttemptResult {
|
|
1087
|
+
void ctx;
|
|
1088
|
+
return {
|
|
1089
|
+
kind: "aborted",
|
|
1090
|
+
output: syntheticAppServerOutput(null),
|
|
1091
|
+
abortMessage:
|
|
1092
|
+
`engine_run_failed: zcode 任务被中止(app-server abort 链:session/stop → ${ZCODE_APPSERVER_ABORT_GRACE_MS}ms grace 确认 → ` +
|
|
1093
|
+
`超时 killChain SIGTERM→${ZCODE_KILL_GRACE_MS}ms→SIGKILL 收割共享进程,在途任务走崩溃路径)。`,
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
/** runTurn 终态 → 载荷形态(usage 映射:parser.mapZcodeUsage 一族)。 */
|
|
1098
|
+
function turnResultToPayload(r: SessionTurnResult): ZcodeTerminalPayload {
|
|
1099
|
+
return {
|
|
1100
|
+
response: r.response,
|
|
1101
|
+
sessionId: r.sessionId,
|
|
1102
|
+
...(mapZcodeUsage(r.usage) !== undefined ? { usage: mapZcodeUsage(r.usage) } : {}),
|
|
1103
|
+
...(mapZcodeOutcomeUsage(r.usage, undefined) !== undefined
|
|
1104
|
+
? { outcomeUsage: mapZcodeOutcomeUsage(r.usage, undefined) }
|
|
1105
|
+
: {}),
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
/**
|
|
1110
|
+
* appserver 路径运行中失败的结构化文案(错误规格表):-32603 "Model config is
|
|
1111
|
+
* missing" → engine_credential_missing(共享宿主 HOME——凭据在 ZCode 桌面端管理);
|
|
1112
|
+
* -32010 → 单会话一任务是结构保证(busy 不排队不打断),文案引导附带 sessionId/state
|
|
1113
|
+
* 流水报告;其余(连接崩溃/会话失败/协议漂移)→ engine_run_failed + 恢复指引
|
|
1114
|
+
* (漂移不再降级 spawn——直接报错)。sessionId 已建立时随文案透出。
|
|
1115
|
+
*
|
|
1116
|
+
* retried([P0-1 U4] §5.2 F-4「已重试 1 次」):仅在瞬时重试真实发生后为 true——
|
|
1117
|
+
* 兜底行恢复指引补「已自动重试一次仍失败」句(未重试形态不含,与行为一致);专属
|
|
1118
|
+
* 归类行(credential/busy)有独立恢复指引,不掺重试事实(错误规格表行的归类语义优先)。
|
|
1119
|
+
*/
|
|
1120
|
+
function buildAppServerRunFailedMessage(err: unknown, sessionId?: string, retried = false): string {
|
|
1121
|
+
if (
|
|
1122
|
+
isAppServerRpcError(err) &&
|
|
1123
|
+
err.code === ZCODE_APPSERVER_ERR_MODEL_CONFIG_MISSING &&
|
|
1124
|
+
/Model config is missing/.test(err.message)
|
|
1125
|
+
) {
|
|
1126
|
+
return (
|
|
1127
|
+
`engine_credential_missing: app-server 报 "Model config is missing"(宿主 HOME 的 zcode 配置无可用模型)。` +
|
|
1128
|
+
`恢复指引:在 ZCode 桌面端登录并配置 provider 凭据后重跑本任务(常驻连接在引擎进程重启后生效新凭据)。`
|
|
1129
|
+
);
|
|
1130
|
+
}
|
|
1131
|
+
if (isAppServerRpcError(err) && err.code === ZCODE_APPSERVER_ERR_BUSY_SESSION) {
|
|
1132
|
+
const sid = sessionId !== undefined ? `(会话 id: ${sessionId})` : "";
|
|
1133
|
+
return (
|
|
1134
|
+
`engine_run_failed: app-server 报 -32010${sid}(send 时该会话已有轮在跑,busy 不排队不打断)。` +
|
|
1135
|
+
`单会话一任务是结构保证,出现即 bug;请附带 sessionId 与 state 流水(连接/会话事件日志)上报问题。`
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1138
|
+
const code = isAppServerRpcError(err) && err.code !== undefined ? `(code ${err.code})` : "";
|
|
1139
|
+
const sid = sessionId !== undefined ? `(会话 ${sessionId})` : "";
|
|
1140
|
+
const retryNote = retried
|
|
1141
|
+
? `恢复指引:直接重跑本任务(瞬时故障已自动重试一次仍失败;重试用的是崩溃后自动重建的新会话)。`
|
|
1142
|
+
: `恢复指引:直接重跑本任务(连接崩溃后自动重建进程);`;
|
|
1143
|
+
return (
|
|
1144
|
+
`engine_run_failed: app-server 会话执行失败${code}${sid}: ${errMessage(err).slice(-ZCODE_ERROR_TAIL_CHARS)}。` +
|
|
1145
|
+
`${retryNote}若持续失败(疑似 zcode 升级后协议漂移——` +
|
|
1146
|
+
`-32601/-32602 类错误),重启 ZCode 或固定 zcode 版本后重试,或改用 engine: pi。`
|
|
1147
|
+
);
|
|
1148
|
+
}
|
|
1149
|
+
|
|
597
1150
|
/** 跨重试轮累计 token 用量(重试的 LLM 调用真实发生)。 */
|
|
598
1151
|
function accumulateUsage(acc: { input: number; output: number; cacheRead: number; cacheWrite: number; has: boolean }, r: AttemptResult): void {
|
|
599
1152
|
if (r.kind !== "parsed") return;
|
|
@@ -617,8 +1170,228 @@ function appendSchemaRetryDirective(basePrompt: string, validationError: string)
|
|
|
617
1170
|
);
|
|
618
1171
|
}
|
|
619
1172
|
|
|
620
|
-
/**
|
|
621
|
-
|
|
1173
|
+
/** create 参数组装(A.2 ① strict 键集:空白 thoughtLevel / 空 deny 清单不设键)。 */
|
|
1174
|
+
function buildAppServerCreateParams(
|
|
1175
|
+
task: AgentCallOpts,
|
|
1176
|
+
providerId: string,
|
|
1177
|
+
modelId: string,
|
|
1178
|
+
cwd: string,
|
|
1179
|
+
): SessionCreateParams {
|
|
1180
|
+
const denyTools = (task.denyTools ?? []).filter((t) => typeof t === "string" && t.trim() !== "");
|
|
1181
|
+
// thinkingLevel → thoughtLevel(A.2 ① 键集内):空白串归一为不设键——strict 对象下
|
|
1182
|
+
// 空值键位无语义且防 -32602 变形拒收(与 denyTools 空清单不设键同款纪律)
|
|
1183
|
+
const thoughtLevel = task.thinkingLevel?.trim();
|
|
1184
|
+
return {
|
|
1185
|
+
workspacePath: cwd,
|
|
1186
|
+
mode: "yolo",
|
|
1187
|
+
// per-session model(G3):create 参数透传(A.2 ① strict 对象)——同进程任务
|
|
1188
|
+
// 各用各的模型,互不干扰
|
|
1189
|
+
model: { providerId, modelId },
|
|
1190
|
+
...(thoughtLevel !== undefined && thoughtLevel !== "" ? { thoughtLevel } : {}),
|
|
1191
|
+
...(denyTools.length > 0 ? { toolDenylist: denyTools } : {}),
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
/**
|
|
1196
|
+
* 权威终态 status 提取(P0-1 U3/D5② + ⛔P-Z2 降级路径约束「只消费
|
|
1197
|
+
* source="turn.terminal" 的 status」):turn.terminal 到达(先到/迟到)时 u-z1 的
|
|
1198
|
+
* lastTerminalStatus 必有记录(channel 无条件先记再 settle/不改写落定);缺席说明
|
|
1199
|
+
* 终态仅由 final-frame 宽松判定落定(恒 settle success,不可信)——无权威 status
|
|
1200
|
+
* 可消费,不据此判失败。
|
|
1201
|
+
*/
|
|
1202
|
+
function authoritativeTerminalStatus(r: SessionTurnResult): string | undefined {
|
|
1203
|
+
if (r.lastTerminalStatus !== undefined) return r.lastTerminalStatus;
|
|
1204
|
+
return r.terminal.source === "turn.terminal" ? r.terminal.status : undefined;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
/**
|
|
1208
|
+
* 失败终态判据(⛔P-Z2 门修正):真实 status 枚举 = ["success","interrupted",
|
|
1209
|
+
* "failed"](app-server dist schema f.enum 实证,**无 "error"**——v1 判据
|
|
1210
|
+
* `=== "error"` 对真实 failed 终态漏分流即假成功,本修复轮根修)。裁决:
|
|
1211
|
+
* - "failed" → run-failed(模型/服务端真实失败——§5.2 F-3);
|
|
1212
|
+
* - "interrupted" → 不分流(用户中断,不属引擎失败——随宿主 abort 主路径收口,
|
|
1213
|
+
* 引擎侧不抢先把它终态化为失败);
|
|
1214
|
+
* - "error" → 保留为容错分支(非真实枚举,防协议漂移/旧版本形态再滑入假成功;
|
|
1215
|
+
* 假成功代价 >> 误报失败代价,取并集防御)。
|
|
1216
|
+
*/
|
|
1217
|
+
function isFailedTerminalStatus(status: string | undefined): boolean {
|
|
1218
|
+
return status === "failed" || status === "error";
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
/**
|
|
1222
|
+
* [P0-1 U3/D5②] appserver 轮成功收口的 parsed 三态(read 兜底后的 response + schema
|
|
1223
|
+
* 校验)。失败终态(isFailedTerminalStatus,"interrupted" 不在其中——不误判失败)
|
|
1224
|
+
* 先分流(§3.2 缺陷 B 不再假成功;schema 校验对失败形态无意义——失败终态的
|
|
1225
|
+
* response 是错误尾部,非结构化输出候选)。
|
|
1226
|
+
*/
|
|
1227
|
+
function parsedAppServerAttempt(task: AgentCallOpts, r: SessionTurnResult): AttemptResult {
|
|
1228
|
+
if (isFailedTerminalStatus(authoritativeTerminalStatus(r))) {
|
|
1229
|
+
return failedTerminalAppServerAttempt(r);
|
|
1230
|
+
}
|
|
1231
|
+
const schema = isPlainObject(task.schema) ? task.schema : undefined;
|
|
1232
|
+
return {
|
|
1233
|
+
kind: "parsed",
|
|
1234
|
+
output: syntheticAppServerOutput(0),
|
|
1235
|
+
payload: turnResultToPayload(r),
|
|
1236
|
+
...(schema !== undefined
|
|
1237
|
+
? { schemaResult: extractAndValidateStructuredOutput(r.response, schema) }
|
|
1238
|
+
: {}),
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
/**
|
|
1243
|
+
* [P0-1 U3/D5② + ⛔P-Z2 门修正] failed 终态的 run-failed 合成(§5.2 F-3 文案):
|
|
1244
|
+
* exitCode=null 异常终态、sessionId 留痕同 failedAppServerAttempt。错误详情优先级:
|
|
1245
|
+
* terminal 帧 errorCode/errorMessage(⛔P-Z2 实证——真实 failed 终态的错误详情只在
|
|
1246
|
+
* terminal 帧,read/delta 携带不了)> read 兜底/delta 聚合尾部(F-3 原文案,降级为
|
|
1247
|
+
* 兜底)> 「无返回内容」(P-Z2 降级形态:final-frame 先到且 read 无错误信息——不
|
|
1248
|
+
* 伪造错误详情,覆盖面收窄但不假成功)。
|
|
1249
|
+
*/
|
|
1250
|
+
function failedTerminalAppServerAttempt(r: SessionTurnResult): AttemptResult {
|
|
1251
|
+
const status = authoritativeTerminalStatus(r);
|
|
1252
|
+
const detail = r.lastTerminalError;
|
|
1253
|
+
const detailParts: string[] = [];
|
|
1254
|
+
if (detail?.code !== undefined) detailParts.push(`errorCode: ${detail.code}`);
|
|
1255
|
+
if (detail?.message !== undefined) detailParts.push(detail.message);
|
|
1256
|
+
// 错误详情优先级(⛔P-Z2):terminal 帧详情 > read 兜底/delta 聚合尾部 > 无返回内容
|
|
1257
|
+
let body: string;
|
|
1258
|
+
if (detailParts.length > 0) {
|
|
1259
|
+
body = `服务端错误:${detailParts.join(":")}。`;
|
|
1260
|
+
} else {
|
|
1261
|
+
const tail = r.response.trim();
|
|
1262
|
+
body =
|
|
1263
|
+
tail !== ""
|
|
1264
|
+
? `服务端返回尾部:${tail.slice(-ZCODE_ERROR_TAIL_CHARS)}。`
|
|
1265
|
+
: "服务端无返回内容(read 兜底/delta 聚合均为空)。";
|
|
1266
|
+
}
|
|
1267
|
+
return {
|
|
1268
|
+
kind: "run-failed",
|
|
1269
|
+
output: syntheticAppServerOutput(null),
|
|
1270
|
+
message:
|
|
1271
|
+
`engine_run_failed: app-server 终态 status=${status}(会话 ${r.sessionId})。${body}\n` +
|
|
1272
|
+
`👉 恢复指引:错误内容来自模型/服务端;直接重跑,若持续出现核对 ZCode 桌面端凭据与模型配置(engine_credential_missing 同族排查)。`,
|
|
1273
|
+
sessionId: r.sessionId,
|
|
1274
|
+
};
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
/** appserver 轮失败收口的 run-failed 三态(结构化文案 + 会话 id 留痕)。 */
|
|
1278
|
+
function failedAppServerAttempt(
|
|
1279
|
+
err: unknown,
|
|
1280
|
+
currentSessionId: string | undefined,
|
|
1281
|
+
opts: { retried?: boolean; transient?: "timeout" | "conn-closed" } = {},
|
|
1282
|
+
): AttemptResult {
|
|
1283
|
+
return {
|
|
1284
|
+
kind: "run-failed",
|
|
1285
|
+
output: syntheticAppServerOutput(null),
|
|
1286
|
+
message: buildAppServerRunFailedMessage(err, currentSessionId, opts.retried === true),
|
|
1287
|
+
// [P0-1 U4/D6] 瞬时失败形态标记(重试判定判据——timeout 形态走
|
|
1288
|
+
// timeoutAppServerAttempt,此处只承载 conn-closed)
|
|
1289
|
+
...(opts.transient !== undefined ? { transient: opts.transient } : {}),
|
|
1290
|
+
// 错误规格表 -32004 行「按任务失败上报(含会话 id)」:create 成功后运行中失败
|
|
1291
|
+
// (-32004/-32010 等)时留痕会话 id——经 applyRunFailedOutcome 落 outcome.sessionId
|
|
1292
|
+
// 与 handle.sessionRef(create 阶段失败无会话,缺省不带)
|
|
1293
|
+
...(currentSessionId !== undefined ? { sessionId: currentSessionId } : {}),
|
|
1294
|
+
};
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
/**
|
|
1298
|
+
* D3 abort 链的止损路径终局(P0-1 U2):超时入口的 outcome 文案素材(D3 强制可观测
|
|
1299
|
+
* 面——r3 SG-4,A2/A11 验收断言「outcome 止损路径为 stop 已送达 / 升级杀链」的载体);
|
|
1300
|
+
* settled-in-grace / escalated-kill 两值只由用户取消入口产生,超时入口不可达(保留
|
|
1301
|
+
* 联合完整供文案兜底)。
|
|
1302
|
+
*/
|
|
1303
|
+
type AbortChainStopPath =
|
|
1304
|
+
| "stop-acked"
|
|
1305
|
+
| "stop-rejected"
|
|
1306
|
+
| "stop-unreachable-killed"
|
|
1307
|
+
| "no-session"
|
|
1308
|
+
| "settled-in-grace"
|
|
1309
|
+
| "escalated-kill";
|
|
1310
|
+
|
|
1311
|
+
/**
|
|
1312
|
+
* [P0-1 U2] channel 判死(TurnTimeoutError)后的收口三态:engine_timeout 前缀
|
|
1313
|
+
* (D4——与 engine_run_failed 分流,下游按前缀分流不经字符串反推超时语义),走
|
|
1314
|
+
* run-failed kind 承载(exitCode=null 异常终态口径与杀链超时合成终态一致)。sessionId
|
|
1315
|
+
* 留痕同 failedAppServerAttempt。
|
|
1316
|
+
* [P0-1 U4/D6] timeout 类(idle/ceiling 都算)恒标 transient——D6 明文的可重试形态;
|
|
1317
|
+
* retried 时文案补「已自动重试一次」句(F-1)。
|
|
1318
|
+
*/
|
|
1319
|
+
function timeoutAppServerAttempt(
|
|
1320
|
+
err: TurnTimeoutError,
|
|
1321
|
+
currentSessionId: string | undefined,
|
|
1322
|
+
stopPath: AbortChainStopPath,
|
|
1323
|
+
opts: { retried?: boolean } = {},
|
|
1324
|
+
): AttemptResult {
|
|
1325
|
+
return {
|
|
1326
|
+
kind: "run-failed",
|
|
1327
|
+
output: syntheticAppServerOutput(null),
|
|
1328
|
+
message: buildAppServerTimeoutMessage(err, currentSessionId, stopPath, opts.retried === true),
|
|
1329
|
+
transient: "timeout",
|
|
1330
|
+
...(currentSessionId !== undefined ? { sessionId: currentSessionId } : {}),
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
/** 止损路径的可观测文案(§5.2 F-1:stop 已送达 / stop 无应答已升级杀链两分支各具名)。 */
|
|
1335
|
+
function stopPathText(stopPath: AbortChainStopPath): string {
|
|
1336
|
+
switch (stopPath) {
|
|
1337
|
+
case "stop-acked":
|
|
1338
|
+
return "session/stop 已送达(服务端接受停 turn)";
|
|
1339
|
+
case "stop-rejected":
|
|
1340
|
+
return "session/stop 报协议性 error(会话已被回收,控制面存活——止损由会话回收承担,不升级杀链)";
|
|
1341
|
+
case "stop-unreachable-killed":
|
|
1342
|
+
return `session/stop 无应答已升级杀链(SIGTERM→${ZCODE_KILL_GRACE_MS}ms→SIGKILL 收割共享进程)`;
|
|
1343
|
+
case "no-session":
|
|
1344
|
+
return "会话未建立(任务未开始执行,无在途消耗)";
|
|
1345
|
+
default:
|
|
1346
|
+
// settled-in-grace / escalated-kill 只属用户取消入口;超时入口不可达,防御兜底
|
|
1347
|
+
return "grace 窗口内终态落定或已走杀链";
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
/**
|
|
1352
|
+
* 超时族 outcome 文案(D4 + §5.2 F-1/F-2,两形态有别):idle 主判定静默时长 + 最后
|
|
1353
|
+
* 事件时刻(诊断面);ceiling 总上界判死附 env 自救通道(XYZ_ZCODE_TURN_MAX_TIMEOUT_MS
|
|
1354
|
+
* 可调/0 关闭——§2 目标 5 的用户可见出口)。恢复指引共段:重跑 + 连通性排查 + engine: pi。
|
|
1355
|
+
* retried([P0-1 U4] §5.2 F-1 样例句,u-z2 留的补句义务):仅在瞬时重试真实发生后
|
|
1356
|
+
* 为 true——恢复指引补「瞬时故障已自动重试一次仍超时;重试在止损链终局后启动,无
|
|
1357
|
+
* 新旧任务双跑窗」句(未重试形态不含,与行为一致)。
|
|
1358
|
+
*/
|
|
1359
|
+
function buildAppServerTimeoutMessage(
|
|
1360
|
+
err: TurnTimeoutError,
|
|
1361
|
+
sessionId: string | undefined,
|
|
1362
|
+
stopPath: AbortChainStopPath,
|
|
1363
|
+
retried = false,
|
|
1364
|
+
): string {
|
|
1365
|
+
const sid = sessionId !== undefined ? `(会话 ${sessionId})` : "";
|
|
1366
|
+
const lastEventText =
|
|
1367
|
+
err.lastEventAt !== undefined
|
|
1368
|
+
? `,最后事件 ${new Date(err.lastEventAt).toISOString()}`
|
|
1369
|
+
: ",整轮未观察到任何事件(进程假死/协议静默形态)";
|
|
1370
|
+
const head =
|
|
1371
|
+
err.kind === "idle"
|
|
1372
|
+
? `engine_timeout: zcode turn 连续静默 ${err.thresholdMs}ms(idle 判定${lastEventText},总耗时 ${err.elapsed}ms)${sid}。`
|
|
1373
|
+
: `engine_timeout: zcode turn 总上界 ${err.thresholdMs}ms 内未观察到终态(chatty-wedge 判定——事件流仍活跃而终态未到达,总耗时 ${err.elapsed}ms)${sid}。`;
|
|
1374
|
+
const selfHelp =
|
|
1375
|
+
err.kind === "ceiling"
|
|
1376
|
+
? `若本任务属合法超长任务(预期超过 ${err.thresholdMs}ms),重跑前设 ${ZCODE_TURN_MAX_TIMEOUT_ENV} 为更大毫秒值或 0 关闭总上界(关闭后 chatty 形态不再自动回收,静默 wedged 仍由 idle 层兜底——自行权衡)。`
|
|
1377
|
+
: "";
|
|
1378
|
+
const rerunGuide = retried
|
|
1379
|
+
? `👉 恢复指引:直接重跑本任务(瞬时故障已自动重试一次仍超时;重试在止损链终局后启动,无新旧任务双跑窗);`
|
|
1380
|
+
: `👉 恢复指引:直接重跑本任务;`;
|
|
1381
|
+
return (
|
|
1382
|
+
`${head}止损路径:${stopPathText(stopPath)}。\n` +
|
|
1383
|
+
`${rerunGuide}${selfHelp}若持续出现,检查 ZCode 桌面端模型连通性或改用 engine: pi。`
|
|
1384
|
+
);
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
/**
|
|
1388
|
+
* prepare 期能力拒绝的历史载体(code 进 message 前缀,调用方可程序化分流)。
|
|
1389
|
+
* [U10① D6] execution 运行时面错误族成员:export 供宿主 instanceof 分流。
|
|
1390
|
+
* [D3-④ 合并注] 引擎内 shape 拒绝已上提 common/capability-gate(EngineError 承载),
|
|
1391
|
+
* 本引擎不再抛出;保留 export 维持错误族面兼容(execution-runtime-face.test 消费),
|
|
1392
|
+
* 待波 2 收口时随 export 面清理一并裁决。
|
|
1393
|
+
*/
|
|
1394
|
+
export class ZcodeTaskShapeError extends Error {
|
|
622
1395
|
readonly code: string;
|
|
623
1396
|
|
|
624
1397
|
constructor(code: string, message: string) {
|
|
@@ -650,7 +1423,7 @@ async function defaultProbeVersion(cliPath: string): Promise<string | undefined>
|
|
|
650
1423
|
} catch (err) {
|
|
651
1424
|
logger.debug(
|
|
652
1425
|
`[zcode-engine] probe version check failed (best-effort continue): ${
|
|
653
|
-
|
|
1426
|
+
toErrorMessage(err)
|
|
654
1427
|
}`,
|
|
655
1428
|
);
|
|
656
1429
|
return undefined;
|