@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
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [OR-4] 终态收尾 emit/onRunDone M12 同款围栏 + [OR-6] handleWorkerMessage log case /
|
|
3
|
+
* default 留痕 + [OR-8] run done 时 in-flight call 收口 cancelled 测试。
|
|
4
|
+
*
|
|
5
|
+
* 覆盖(unbounded-wait-audit §4.1 OR-4 / §4.3 OR-6、OR-8 / §7.2 T3④⑤、T7④):
|
|
6
|
+
* - OR-4:eventBus.emit 或 onRunDone 同步抛错时,五个终态路径(return / worker error
|
|
7
|
+
* 超限 / script error 超限 / exit 无终态消息 / time_limited)promise 均 resolve——
|
|
8
|
+
* 错误被围栏捕获 error 留痕,不再经 worker-host 的 `void handlers.onXxx(...)`
|
|
9
|
+
* 变 unhandledRejection 崩宿主
|
|
10
|
+
* - OR-6 主线程半边:{type:"log"} 计入 errorLogs + deps.log debug 留痕(含上限裁剪);
|
|
11
|
+
* 未知 type warn 留痕(协议漂移防线)
|
|
12
|
+
* - OR-8:脚本 return 时残留 fire-and-forget in-flight call 收口为取消终态
|
|
13
|
+
* (call done + trace failed + Cancelled 文案 + completedAt + live 清除),
|
|
14
|
+
* 先收口再落盘——done 快照不含 running 节点
|
|
15
|
+
*/
|
|
16
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
17
|
+
|
|
18
|
+
import { getLogger } from "../../core/logger.ts";
|
|
19
|
+
import {
|
|
20
|
+
closeOutInFlightCalls,
|
|
21
|
+
handleScriptError,
|
|
22
|
+
handleWorkerError,
|
|
23
|
+
handleWorkerExit,
|
|
24
|
+
handleWorkerMessage,
|
|
25
|
+
} from "../worker-message-pump.ts";
|
|
26
|
+
import { Budget } from "../models/budget.ts";
|
|
27
|
+
import { RunRuntime } from "../models/run-runtime.ts";
|
|
28
|
+
import { toRunSnapshot } from "../run-snapshot.ts";
|
|
29
|
+
import { Trace } from "../models/trace.ts";
|
|
30
|
+
import type { AgentResult, WorkerLogEntry } from "../models/types.ts";
|
|
31
|
+
import { WorkflowRun } from "../models/workflow-run.ts";
|
|
32
|
+
import type { LifecycleDeps, WorkerHandlers } from "../models/ports.ts";
|
|
33
|
+
import type { WorkerHandle } from "../worker-handle.ts";
|
|
34
|
+
import { flushMicrotasks } from "./helpers/flush-microtasks.ts";
|
|
35
|
+
|
|
36
|
+
// ── helpers ──────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
const MAX_ERROR_LOGS = 500;
|
|
39
|
+
|
|
40
|
+
function makeRealRun(runId: string, opts: { budgetTimeMs?: number } = {}): WorkflowRun {
|
|
41
|
+
const run = new WorkflowRun(
|
|
42
|
+
runId,
|
|
43
|
+
{
|
|
44
|
+
scriptName: "test-wf",
|
|
45
|
+
scriptSource: "agent('hi')",
|
|
46
|
+
args: {},
|
|
47
|
+
scriptPath: "/tmp/test-wf.js",
|
|
48
|
+
budgetTimeMs: opts.budgetTimeMs,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
status: "running",
|
|
52
|
+
budget: new Budget(),
|
|
53
|
+
calls: new Map(),
|
|
54
|
+
trace: new Trace(),
|
|
55
|
+
errorLogs: [],
|
|
56
|
+
},
|
|
57
|
+
{ startedAt: new Date().toISOString() },
|
|
58
|
+
);
|
|
59
|
+
const initialWorker = {
|
|
60
|
+
postMessage: vi.fn(),
|
|
61
|
+
terminate: vi.fn(async () => {}),
|
|
62
|
+
} as unknown as WorkerHandle;
|
|
63
|
+
run.assignRuntime(new RunRuntime(initialWorker, new AbortController()));
|
|
64
|
+
return run;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function makeDeps(opts: {
|
|
68
|
+
/** eventBus.emit 同步抛错(模拟 listener 异常)。 */
|
|
69
|
+
emitThrows?: boolean;
|
|
70
|
+
/** onRunDone 同步抛错(模拟 evictDoneRunsBeyondCap 等收尾异常)。 */
|
|
71
|
+
onRunDoneThrows?: boolean;
|
|
72
|
+
} = {}): Omit<LifecycleDeps, "store" | "workerHost" | "runner" | "eventBus" | "onRunDone" | "log"> & {
|
|
73
|
+
// 真实类型整体保留(接口成员完整),仅 mock 方法交叉 vi.fn 能力
|
|
74
|
+
store: LifecycleDeps["store"] & { save: LifecycleDeps["store"]["save"] & ReturnType<typeof vi.fn> };
|
|
75
|
+
workerHost: LifecycleDeps["workerHost"] & { start: LifecycleDeps["workerHost"]["start"] & ReturnType<typeof vi.fn> };
|
|
76
|
+
runner: LifecycleDeps["runner"] & { run: LifecycleDeps["runner"]["run"] & ReturnType<typeof vi.fn> };
|
|
77
|
+
eventBus: NonNullable<LifecycleDeps["eventBus"]> & { emit: NonNullable<LifecycleDeps["eventBus"]>["emit"] & ReturnType<typeof vi.fn> };
|
|
78
|
+
onRunDone: LifecycleDeps["onRunDone"] & ReturnType<typeof vi.fn>;
|
|
79
|
+
log: LifecycleDeps["log"] & ReturnType<typeof vi.fn>;
|
|
80
|
+
} {
|
|
81
|
+
return {
|
|
82
|
+
store: { save: vi.fn(async () => {}) },
|
|
83
|
+
workerHost: {
|
|
84
|
+
start: vi.fn(
|
|
85
|
+
() => ({ postMessage: vi.fn(), terminate: vi.fn(async () => {}) }) as unknown as WorkerHandle,
|
|
86
|
+
),
|
|
87
|
+
},
|
|
88
|
+
runner: { run: vi.fn(async () => ({}) as AgentResult) },
|
|
89
|
+
runs: new Map(),
|
|
90
|
+
eventBus: {
|
|
91
|
+
emit: opts.emitThrows
|
|
92
|
+
? vi.fn(() => {
|
|
93
|
+
throw new Error("listener exploded");
|
|
94
|
+
})
|
|
95
|
+
: vi.fn(),
|
|
96
|
+
},
|
|
97
|
+
onRunDone: opts.onRunDoneThrows
|
|
98
|
+
? vi.fn(() => {
|
|
99
|
+
throw new Error("onRunDone exploded");
|
|
100
|
+
})
|
|
101
|
+
: vi.fn(),
|
|
102
|
+
log: vi.fn(),
|
|
103
|
+
} as unknown as ReturnType<typeof makeDeps>;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function makeHandlers(): WorkerHandlers {
|
|
107
|
+
return {
|
|
108
|
+
onMessage: vi.fn(async () => {}),
|
|
109
|
+
onError: vi.fn(async () => {}),
|
|
110
|
+
onExit: vi.fn(async () => {}),
|
|
111
|
+
} as unknown as WorkerHandlers;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function makeAgentCallMsg(callId: number): unknown {
|
|
115
|
+
return {
|
|
116
|
+
type: "agent-call",
|
|
117
|
+
callId,
|
|
118
|
+
opts: { prompt: "test task", agent: "worker", description: "test-slug" },
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
beforeEach(() => {
|
|
123
|
+
vi.useFakeTimers();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
afterEach(() => {
|
|
127
|
+
vi.useRealTimers();
|
|
128
|
+
vi.restoreAllMocks();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// ── [OR-4] 终态收尾围栏 ──────────────────────────────────────
|
|
132
|
+
|
|
133
|
+
describe("[OR-4] 终态收尾 emit/onRunDone 围栏(不产 unhandledRejection)", () => {
|
|
134
|
+
it("handleReturn:eventBus.emit 同步抛错 → promise resolve + error 留痕(save 已发生)", async () => {
|
|
135
|
+
const run = makeRealRun("wf-fence-1");
|
|
136
|
+
const deps = makeDeps({ emitThrows: true });
|
|
137
|
+
|
|
138
|
+
await expect(
|
|
139
|
+
handleWorkerMessage(run, { type: "return", result: { ok: true } }, deps, makeHandlers()),
|
|
140
|
+
).resolves.toBeUndefined();
|
|
141
|
+
|
|
142
|
+
expect(run.state.status).toBe("done");
|
|
143
|
+
expect(deps.store.save).toHaveBeenCalledTimes(1);
|
|
144
|
+
// [B-4] 独立围栏:emit 故障不再跳过 onRunDone(旧实现同一 try 会跳过)
|
|
145
|
+
expect(deps.onRunDone).toHaveBeenCalledTimes(1);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("handleReturn:onRunDone 同步抛错 → promise resolve + error 留痕", async () => {
|
|
149
|
+
const run = makeRealRun("wf-fence-2");
|
|
150
|
+
const deps = makeDeps({ onRunDoneThrows: true });
|
|
151
|
+
|
|
152
|
+
await expect(
|
|
153
|
+
handleWorkerMessage(run, { type: "return", result: { ok: true } }, deps, makeHandlers()),
|
|
154
|
+
).resolves.toBeUndefined();
|
|
155
|
+
|
|
156
|
+
expect(run.state.status).toBe("done");
|
|
157
|
+
expect(deps.store.save).toHaveBeenCalledTimes(1);
|
|
158
|
+
expect(deps.eventBus.emit).toHaveBeenCalledTimes(1); // emit 正常发出后才抛
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("handleWorkerError 超限:emit 抛错 → resolve(旧实现裸调 → unhandledRejection)", async () => {
|
|
162
|
+
const run = makeRealRun("wf-fence-3");
|
|
163
|
+
(run.meta as { workerErrorCount?: number }).workerErrorCount = 3; // count=4 > MAX
|
|
164
|
+
const deps = makeDeps({ emitThrows: true });
|
|
165
|
+
|
|
166
|
+
await expect(handleWorkerError(run, new Error("boom"), deps, makeHandlers())).resolves.toBeUndefined();
|
|
167
|
+
expect(run.state.reason).toBe("failed");
|
|
168
|
+
expect(deps.store.save).toHaveBeenCalledTimes(1);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("handleScriptError 超限:onRunDone 抛错 → resolve", async () => {
|
|
172
|
+
const run = makeRealRun("wf-fence-4");
|
|
173
|
+
(run.meta as { scriptErrorCount?: number }).scriptErrorCount = 3;
|
|
174
|
+
const deps = makeDeps({ onRunDoneThrows: true });
|
|
175
|
+
|
|
176
|
+
await expect(handleScriptError(run, "boom", [], deps, makeHandlers())).resolves.toBeUndefined();
|
|
177
|
+
expect(run.state.reason).toBe("failed");
|
|
178
|
+
expect(deps.store.save).toHaveBeenCalledTimes(1);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("handleWorkerExit 无终态消息路径:emit 抛错 → resolve", async () => {
|
|
182
|
+
const run = makeRealRun("wf-fence-5");
|
|
183
|
+
const deps = makeDeps({ emitThrows: true });
|
|
184
|
+
const handle = { isCurrent: true } as unknown as WorkerHandle;
|
|
185
|
+
|
|
186
|
+
await expect(handleWorkerExit(run, 0, handle, deps, makeHandlers())).resolves.toBeUndefined();
|
|
187
|
+
expect(run.state.reason).toBe("failed");
|
|
188
|
+
expect(deps.store.save).toHaveBeenCalledTimes(1);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("time_limited 路径(预算耗尽 + 超限重试):emit 抛错 → resolve", async () => {
|
|
192
|
+
const run = makeRealRun("wf-fence-6", { budgetTimeMs: 5000 });
|
|
193
|
+
// 已耗 6000ms > 预算 5000ms(fake timers 冻结 Date)
|
|
194
|
+
run.meta.startedAt = new Date(Date.now() - 6000).toISOString();
|
|
195
|
+
const deps = makeDeps({ emitThrows: true });
|
|
196
|
+
|
|
197
|
+
const p = handleScriptError(run, "boom", [], deps, makeHandlers());
|
|
198
|
+
await vi.advanceTimersByTimeAsync(1000); // 退避
|
|
199
|
+
await expect(p).resolves.toBeUndefined();
|
|
200
|
+
|
|
201
|
+
expect(run.state.reason).toBe("time_limited");
|
|
202
|
+
expect(deps.store.save).toHaveBeenCalledTimes(1);
|
|
203
|
+
// [B-4] 独立围栏:emit 故障不再跳过 onRunDone(旧实现同一 try 会跳过)
|
|
204
|
+
expect(deps.onRunDone).toHaveBeenCalledTimes(1);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("围栏捕获后 error 留痕(shared logger)", async () => {
|
|
208
|
+
const errorSpy = vi.spyOn(getLogger("subagents"), "error");
|
|
209
|
+
const run = makeRealRun("wf-fence-7");
|
|
210
|
+
const deps = makeDeps({ onRunDoneThrows: true });
|
|
211
|
+
|
|
212
|
+
await handleWorkerMessage(run, { type: "return", result: 1 }, deps, makeHandlers());
|
|
213
|
+
|
|
214
|
+
const errLogs = errorSpy.mock.calls.map((c) => String(c[0]));
|
|
215
|
+
expect(errLogs.some((m) => m.includes("onRunDone failed") && m.includes("onRunDone exploded"))).toBe(true);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it("[B-4] emit 抛错 → onRunDone 仍执行(独立围栏,完成回调不被通知总线故障吞掉)", async () => {
|
|
219
|
+
const errorSpy = vi.spyOn(getLogger("subagents"), "error");
|
|
220
|
+
const run = makeRealRun("wf-fence-8");
|
|
221
|
+
const deps = makeDeps({ emitThrows: true });
|
|
222
|
+
|
|
223
|
+
await handleWorkerMessage(run, { type: "return", result: 1 }, deps, makeHandlers());
|
|
224
|
+
|
|
225
|
+
expect(run.state.reason).toBe("completed");
|
|
226
|
+
expect(deps.onRunDone).toHaveBeenCalledTimes(1); // 旧实现同一 try:emit 抛错会跳过
|
|
227
|
+
const errLogs = errorSpy.mock.calls.map((c) => String(c[0]));
|
|
228
|
+
expect(errLogs.some((m) => m.includes("pending:unregister emit failed") && m.includes("listener exploded"))).toBe(true);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// ── [OR-6] handleWorkerMessage log case + default 留痕 ───────
|
|
233
|
+
|
|
234
|
+
describe("[OR-6] log 消息消费 + 未知类型 default 留痕", () => {
|
|
235
|
+
it("{type:\"log\"} 计入 run.state.errorLogs + deps.log debug 留痕(不触发终态)", async () => {
|
|
236
|
+
const run = makeRealRun("wf-log-1");
|
|
237
|
+
const deps = makeDeps();
|
|
238
|
+
|
|
239
|
+
await handleWorkerMessage(run, { type: "log", phase: "build", message: "step 1 done" }, deps, makeHandlers());
|
|
240
|
+
|
|
241
|
+
expect(run.state.errorLogs).toEqual([{ level: "log", message: "step 1 done" }]);
|
|
242
|
+
expect(run.state.status).toBe("running"); // 不触发终态
|
|
243
|
+
expect(deps.store.save).not.toHaveBeenCalled();
|
|
244
|
+
expect(deps.log).toHaveBeenCalledWith("debug", "workflow:worker-message-pump", "worker log", {
|
|
245
|
+
runId: "wf-log-1",
|
|
246
|
+
phase: "build",
|
|
247
|
+
message: "step 1 done",
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it("log 超上限(MAX_ERROR_LOGS)时裁剪保留尾部(与 workerLogs 通路同语义)", async () => {
|
|
252
|
+
const run = makeRealRun("wf-log-2");
|
|
253
|
+
const deps = makeDeps();
|
|
254
|
+
const prefill: WorkerLogEntry[] = Array.from({ length: MAX_ERROR_LOGS }, (_, i) => ({
|
|
255
|
+
level: "log",
|
|
256
|
+
message: `pre-${i}`,
|
|
257
|
+
}));
|
|
258
|
+
run.state.errorLogs.push(...prefill);
|
|
259
|
+
|
|
260
|
+
await handleWorkerMessage(run, { type: "log", message: "the-latest" }, deps, makeHandlers());
|
|
261
|
+
|
|
262
|
+
expect(run.state.errorLogs).toHaveLength(MAX_ERROR_LOGS);
|
|
263
|
+
expect(run.state.errorLogs.at(-1)).toEqual({ level: "log", message: "the-latest" });
|
|
264
|
+
expect(run.state.errorLogs[0]).toEqual({ level: "log", message: "pre-1" }); // 头部被裁
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it("[B-3] log 单通路:log 消息即时入账 + return workerLogs(仅 console.* 条目)→ 同一日志恰一份", async () => {
|
|
268
|
+
// worker 侧 [B-3] 后的产出形态(worker-script-builder-runtime.test.ts 锁定):
|
|
269
|
+
// log() 只发独立 {type:"log"} 消息,return 的 workerLogs 只含 console.* 捕获条目。
|
|
270
|
+
// 主线程两条消息各走各的入账 → 同一日志在 errorLogs 恰一份(旧双通路为 2 份)。
|
|
271
|
+
const run = makeRealRun("wf-log-dup");
|
|
272
|
+
const deps = makeDeps();
|
|
273
|
+
|
|
274
|
+
await handleWorkerMessage(run, { type: "log", phase: "build", message: "step-1" }, deps, makeHandlers());
|
|
275
|
+
await handleWorkerMessage(
|
|
276
|
+
run,
|
|
277
|
+
{ type: "return", result: { ok: true }, workerLogs: [{ level: "error", message: "console-entry" }] },
|
|
278
|
+
deps,
|
|
279
|
+
makeHandlers(),
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
expect(run.state.errorLogs.filter((l) => l.message === "step-1")).toHaveLength(1);
|
|
283
|
+
expect(run.state.errorLogs.filter((l) => l.message === "console-entry")).toHaveLength(1);
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it("未知消息类型:warn 留痕后丢弃(协议漂移防线),不写状态不终态", async () => {
|
|
287
|
+
const warnSpy = vi.spyOn(getLogger("subagents"), "warn");
|
|
288
|
+
const run = makeRealRun("wf-log-3");
|
|
289
|
+
const deps = makeDeps();
|
|
290
|
+
|
|
291
|
+
await handleWorkerMessage(run, { type: "future-unknown-type", payload: 1 }, deps, makeHandlers());
|
|
292
|
+
|
|
293
|
+
expect(warnSpy.mock.calls.map((c) => String(c[0])).some((m) => m.includes("unknown worker message type") && m.includes("future-unknown-type"))).toBe(true);
|
|
294
|
+
expect(deps.log).toHaveBeenCalledWith("warn", "workflow:worker-message-pump", "unknown worker message type", {
|
|
295
|
+
runId: "wf-log-3",
|
|
296
|
+
type: "future-unknown-type",
|
|
297
|
+
});
|
|
298
|
+
expect(run.state.status).toBe("running");
|
|
299
|
+
expect(deps.store.save).not.toHaveBeenCalled();
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// ── [OR-8] run done 时 in-flight call 收口 cancelled ─────────
|
|
304
|
+
|
|
305
|
+
describe("[OR-8] run done 时残留 in-flight call 收口 cancelled", () => {
|
|
306
|
+
it("fire-and-forget agent() 后 return:call 收口 done + trace failed(Cancelled 文案),快照无 running 节点", async () => {
|
|
307
|
+
const run = makeRealRun("wf-or8-1");
|
|
308
|
+
const deps = makeDeps();
|
|
309
|
+
// fire-and-forget dispatch:runner 永不 settle(脚本不 await,模拟在飞子进程)
|
|
310
|
+
deps.runner.run.mockImplementation(() => new Promise<AgentResult>(() => {}));
|
|
311
|
+
await handleWorkerMessage(run, makeAgentCallMsg(1), deps, makeHandlers());
|
|
312
|
+
await flushMicrotasks();
|
|
313
|
+
expect(run.state.calls.get(1)?.status).toBe("running");
|
|
314
|
+
|
|
315
|
+
// 脚本 return(fire-and-forget call 仍在飞)
|
|
316
|
+
await handleWorkerMessage(run, { type: "return", result: "done early" }, deps, makeHandlers());
|
|
317
|
+
|
|
318
|
+
expect(run.state.status).toBe("done");
|
|
319
|
+
const call = run.state.calls.get(1);
|
|
320
|
+
// call 收口为 done + 取消文案(不删除条目——保留调用痕迹)
|
|
321
|
+
expect(call?.status).toBe("done");
|
|
322
|
+
expect(call?.result?.error).toContain("Cancelled");
|
|
323
|
+
// trace 节点 failed + completedAt + live 清除(快照/GUI 不再显示 running 步骤)
|
|
324
|
+
const node = run.state.trace.find(1);
|
|
325
|
+
expect(node?.status).toBe("failed");
|
|
326
|
+
expect(node?.error).toContain("Cancelled");
|
|
327
|
+
expect(node?.completedAt).toBeDefined();
|
|
328
|
+
expect(node?.live).toBeUndefined();
|
|
329
|
+
// 持久化快照(收口先于 save)不含 running 形态
|
|
330
|
+
const snap = toRunSnapshot(run);
|
|
331
|
+
expect(snap.state.calls.map((c) => c.status)).toEqual(["done"]);
|
|
332
|
+
expect(snap.state.trace.map((n) => n.status)).toEqual(["failed"]);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it("handleWorkerError 超限路径同样收口(failed 快照一致)", async () => {
|
|
336
|
+
const run = makeRealRun("wf-or8-2");
|
|
337
|
+
const deps = makeDeps();
|
|
338
|
+
deps.runner.run.mockImplementation(() => new Promise<AgentResult>(() => {}));
|
|
339
|
+
await handleWorkerMessage(run, makeAgentCallMsg(2), deps, makeHandlers());
|
|
340
|
+
await flushMicrotasks();
|
|
341
|
+
|
|
342
|
+
// 预置计数已达上限:count=4 > MAX → 无退避直接 failed(in-flight call 一并收口)
|
|
343
|
+
(run.meta as { workerErrorCount?: number }).workerErrorCount = 3;
|
|
344
|
+
await handleWorkerError(run, new Error("final boom"), deps, makeHandlers());
|
|
345
|
+
|
|
346
|
+
expect(run.state.reason).toBe("failed");
|
|
347
|
+
expect(run.state.calls.get(2)?.status).toBe("done");
|
|
348
|
+
expect(run.state.calls.get(2)?.result?.error).toContain("Cancelled");
|
|
349
|
+
expect(run.state.trace.find(2)?.status).toBe("failed");
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it("closeOutInFlightCalls 对 pending 状态 call 补齐状态机(markRunning→markDone)", () => {
|
|
353
|
+
const run = makeRealRun("wf-or8-3");
|
|
354
|
+
const deps = makeDeps();
|
|
355
|
+
deps.runner.run.mockImplementation(() => new Promise<AgentResult>(() => {}));
|
|
356
|
+
void handleWorkerMessage(run, makeAgentCallMsg(3), deps, makeHandlers());
|
|
357
|
+
// 直接构造 pending 形态(防御 pending 分支可达性)
|
|
358
|
+
const call = run.state.calls.get(3)!;
|
|
359
|
+
(call as { status: string }).status = "pending";
|
|
360
|
+
|
|
361
|
+
const closed = closeOutInFlightCalls(run);
|
|
362
|
+
|
|
363
|
+
expect(closed).toEqual([3]);
|
|
364
|
+
expect(call.status).toBe("done");
|
|
365
|
+
expect(call.result?.error).toContain("Cancelled");
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it("无 in-flight call 时收口为 no-op(正常完成的 call 不受影响)", async () => {
|
|
369
|
+
const run = makeRealRun("wf-or8-4");
|
|
370
|
+
const deps = makeDeps();
|
|
371
|
+
deps.runner.run.mockImplementation(
|
|
372
|
+
async () => ({ content: "ok", durationMs: 1, error: undefined, toolCalls: [] }) as AgentResult,
|
|
373
|
+
);
|
|
374
|
+
await handleWorkerMessage(run, makeAgentCallMsg(4), deps, makeHandlers());
|
|
375
|
+
await flushMicrotasks(20);
|
|
376
|
+
expect(run.state.calls.get(4)?.status).toBe("done");
|
|
377
|
+
|
|
378
|
+
await handleWorkerMessage(run, { type: "return", result: "ok" }, deps, makeHandlers());
|
|
379
|
+
|
|
380
|
+
// 已完成 call 的结果原样保留(不被取消文案覆盖)
|
|
381
|
+
expect(run.state.calls.get(4)?.result?.content).toBe("ok");
|
|
382
|
+
expect(run.state.trace.find(4)?.status).toBe("completed");
|
|
383
|
+
});
|
|
384
|
+
});
|
|
@@ -6,16 +6,17 @@
|
|
|
6
6
|
import { describe, expect, it, vi } from "vitest";
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
|
+
classifyFailureKind,
|
|
10
|
+
describeMissingParsedOutput,
|
|
9
11
|
DETERMINISTIC_SCHEMA_FAILURE_PREFIX,
|
|
10
|
-
executeAgentCall,
|
|
11
12
|
isDeterministicSchemaFailureMsg,
|
|
12
13
|
isStaleContextErrorMsg,
|
|
13
14
|
STALE_CONTEXT_PATTERNS,
|
|
14
|
-
} from "
|
|
15
|
+
} from "../../execution/engine/engines/pi/output-collector.ts";
|
|
16
|
+
import { executeAgentCall } from "../execute-agent-call.ts";
|
|
15
17
|
import { AgentCall } from "../models/agent-call.ts";
|
|
16
18
|
import { Budget } from "../models/budget.ts";
|
|
17
19
|
import type { AgentRunner } from "../models/ports.ts";
|
|
18
|
-
import { describeMissingParsedOutput } from "../../execution/output-collector.ts";
|
|
19
20
|
import type { ToolCall } from "../../execution/types.ts";
|
|
20
21
|
import { Trace } from "../models/trace.ts";
|
|
21
22
|
import type { ExecutionTraceNode } from "../models/types.ts";
|
|
@@ -67,6 +68,15 @@ function createMockRunner(impl?: ReturnType<typeof vi.fn>): AgentRunner & { run:
|
|
|
67
68
|
return { run } as unknown as AgentRunner & { run: ReturnType<typeof vi.fn> };
|
|
68
69
|
}
|
|
69
70
|
|
|
71
|
+
/**
|
|
72
|
+
* [D5-③] 构造带真实分诊的失败 result——failureKind 经产出侧 classifyFailureKind
|
|
73
|
+
* 分类(复刻 collectResult → mapper 透传后的消费侧视角),避免手写字面量与
|
|
74
|
+
* 产出侧词表脱钩。
|
|
75
|
+
*/
|
|
76
|
+
function makeFailedResult(error: string): AgentResult {
|
|
77
|
+
return makeMockResult({ error, failureKind: classifyFailureKind(error) });
|
|
78
|
+
}
|
|
79
|
+
|
|
70
80
|
// ── U2: executeAgentCall 透传 stream 给 runner.run ──
|
|
71
81
|
|
|
72
82
|
describe("U2: executeAgentCall 透传 stream", () => {
|
|
@@ -201,8 +211,9 @@ describe("isOrphaned 守卫", () => {
|
|
|
201
211
|
it("U2: 谓词 true + stale-context 失败路径 → trace.update 0 次,markDone 保留(覆盖 stale finalize 调用点)", async () => {
|
|
202
212
|
const { call, trace } = makeAgentCallAndTrace();
|
|
203
213
|
const updateSpy = vi.spyOn(trace, "update");
|
|
214
|
+
// "context canceled" 经产出侧词表分类为 stale_context(D5-③ 后消费侧读字段)
|
|
204
215
|
const runner = createMockRunner(
|
|
205
|
-
vi.fn().mockResolvedValue(
|
|
216
|
+
vi.fn().mockResolvedValue(makeFailedResult("context canceled")),
|
|
206
217
|
);
|
|
207
218
|
const budget = new Budget();
|
|
208
219
|
|
|
@@ -297,9 +308,11 @@ describe("W4b: stale 分诊对齐 pi 真实文案", () => {
|
|
|
297
308
|
});
|
|
298
309
|
|
|
299
310
|
it("真实文案 → executeAgentCall 不重试(runner.run 恰 1 次)+ markDone failed", async () => {
|
|
311
|
+
// [D5-③] 全链路锁:产出侧 classifyFailureKind(真实文案 → stale_context)→
|
|
312
|
+
// 消费侧读 failureKind 字段分诊(不扫文案)
|
|
300
313
|
const { call, trace } = makeAgentCallAndTrace();
|
|
301
314
|
const runner = createMockRunner(
|
|
302
|
-
vi.fn().mockResolvedValue(
|
|
315
|
+
vi.fn().mockResolvedValue(makeFailedResult(PI_REAL_STALE_MESSAGE)),
|
|
303
316
|
);
|
|
304
317
|
const budget = new Budget();
|
|
305
318
|
|
|
@@ -351,7 +364,7 @@ describe("MF-1: 确定性 schema 失败不重试", () => {
|
|
|
351
364
|
|
|
352
365
|
const { call, trace } = makeAgentCallAndTrace();
|
|
353
366
|
const runner = createMockRunner(
|
|
354
|
-
vi.fn().mockResolvedValue(
|
|
367
|
+
vi.fn().mockResolvedValue(makeFailedResult(attribution)),
|
|
355
368
|
);
|
|
356
369
|
const budget = new Budget();
|
|
357
370
|
|
|
@@ -368,7 +381,7 @@ describe("MF-1: 确定性 schema 失败不重试", () => {
|
|
|
368
381
|
const attribution = describeMissingParsedOutput([])!;
|
|
369
382
|
const { call, trace } = makeAgentCallAndTrace();
|
|
370
383
|
const runner = createMockRunner(
|
|
371
|
-
vi.fn().mockResolvedValue(
|
|
384
|
+
vi.fn().mockResolvedValue(makeFailedResult(attribution)),
|
|
372
385
|
);
|
|
373
386
|
const budget = new Budget();
|
|
374
387
|
|
|
@@ -384,7 +397,11 @@ describe("MF-1: 确定性 schema 失败不重试", () => {
|
|
|
384
397
|
const { call, trace } = makeAgentCallAndTrace();
|
|
385
398
|
const runner = createMockRunner(
|
|
386
399
|
vi.fn().mockResolvedValue(
|
|
387
|
-
makeMockResult({
|
|
400
|
+
makeMockResult({
|
|
401
|
+
error: attribution,
|
|
402
|
+
failureKind: "schema_deterministic",
|
|
403
|
+
usage: { input: 100, output: 50, cacheRead: 0, cacheWrite: 0, cost: 0.01, contextTokens: 0, turns: 0 },
|
|
404
|
+
}),
|
|
388
405
|
),
|
|
389
406
|
);
|
|
390
407
|
const budget = new Budget();
|
|
@@ -402,7 +419,7 @@ describe("MF-1: 确定性 schema 失败不重试", () => {
|
|
|
402
419
|
const attribution = state2Attribution();
|
|
403
420
|
const { call, trace } = makeAgentCallAndTrace();
|
|
404
421
|
const runner = createMockRunner(
|
|
405
|
-
vi.fn().mockResolvedValue(
|
|
422
|
+
vi.fn().mockResolvedValue(makeFailedResult(attribution)),
|
|
406
423
|
);
|
|
407
424
|
const budget = new Budget();
|
|
408
425
|
|
|
@@ -414,7 +431,7 @@ describe("MF-1: 确定性 schema 失败不重试", () => {
|
|
|
414
431
|
it("态③(no details,无标记)→ 照常重试(可重试语义保留)", async () => {
|
|
415
432
|
vi.useFakeTimers();
|
|
416
433
|
try {
|
|
417
|
-
// 态③真实文案(不带确定性标记)——矩阵执行面锁定:无标记 = 可重试
|
|
434
|
+
// 态③真实文案(不带确定性标记)——矩阵执行面锁定:无标记 = unknown = 可重试
|
|
418
435
|
const msg = describeMissingParsedOutput([
|
|
419
436
|
{ toolName: "structured-output", result: { content: [] } },
|
|
420
437
|
])!;
|
|
@@ -423,7 +440,7 @@ describe("MF-1: 确定性 schema 失败不重试", () => {
|
|
|
423
440
|
const { call, trace } = makeAgentCallAndTrace();
|
|
424
441
|
const runner = createMockRunner(
|
|
425
442
|
vi.fn()
|
|
426
|
-
.mockResolvedValueOnce(
|
|
443
|
+
.mockResolvedValueOnce(makeFailedResult(msg))
|
|
427
444
|
.mockResolvedValueOnce(makeMockResult()),
|
|
428
445
|
);
|
|
429
446
|
const budget = new Budget();
|
|
@@ -449,3 +466,126 @@ describe("MF-1: 确定性 schema 失败不重试", () => {
|
|
|
449
466
|
expect(isDeterministicSchemaFailureMsg(DETERMINISTIC_SCHEMA_FAILURE_PREFIX)).toBe(true);
|
|
450
467
|
});
|
|
451
468
|
});
|
|
469
|
+
|
|
470
|
+
// ── D5-③: failureKind 三态结构化分诊(V5③④ 验收的执行面锁定) ──
|
|
471
|
+
//
|
|
472
|
+
// 语义守恒(r1 MF4 钉正,最高优先约束):unknown(含字段缺省)= 可重试——保持
|
|
473
|
+
// 收敛前(子串分诊时代)的默认重试语义;仅 stale_context(不重试、换参重发由
|
|
474
|
+
// 上层编排)与 schema_deterministic 维持特判。词表识别已收敛到产出侧
|
|
475
|
+
// output-collector.classifyFailureKind(消费侧只读字段)。
|
|
476
|
+
|
|
477
|
+
describe("D5-③: failureKind 三态分诊", () => {
|
|
478
|
+
it("stale_context → 不退避不重试:runner.run 恰 1 次 + 终态 failed(V5③)", async () => {
|
|
479
|
+
const { call, trace } = makeAgentCallAndTrace();
|
|
480
|
+
const runner = createMockRunner(
|
|
481
|
+
vi.fn().mockResolvedValue(makeMockResult({
|
|
482
|
+
error: PI_REAL_STALE_MESSAGE,
|
|
483
|
+
failureKind: "stale_context",
|
|
484
|
+
})),
|
|
485
|
+
);
|
|
486
|
+
const budget = new Budget();
|
|
487
|
+
|
|
488
|
+
await executeAgentCall(call, runner, budget, new AbortController().signal, trace);
|
|
489
|
+
|
|
490
|
+
expect(runner.run).toHaveBeenCalledTimes(1);
|
|
491
|
+
expect(call.attempts).toBe(1);
|
|
492
|
+
expect(call.status).toBe("done");
|
|
493
|
+
expect(call.result?.error).toBe(PI_REAL_STALE_MESSAGE);
|
|
494
|
+
expect(trace.find(0)?.status).toBe("failed");
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
it("schema_deterministic → 不重试特判维持:runner.run 恰 1 次(V5③ 同族)", async () => {
|
|
498
|
+
const attribution = state2Attribution();
|
|
499
|
+
const { call, trace } = makeAgentCallAndTrace();
|
|
500
|
+
const runner = createMockRunner(
|
|
501
|
+
vi.fn().mockResolvedValue(makeMockResult({
|
|
502
|
+
error: attribution,
|
|
503
|
+
failureKind: "schema_deterministic",
|
|
504
|
+
})),
|
|
505
|
+
);
|
|
506
|
+
const budget = new Budget();
|
|
507
|
+
|
|
508
|
+
await executeAgentCall(call, runner, budget, new AbortController().signal, trace);
|
|
509
|
+
|
|
510
|
+
expect(runner.run).toHaveBeenCalledTimes(1);
|
|
511
|
+
expect(trace.find(0)?.status).toBe("failed");
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
it("unknown → 默认退避重试:瞬态错误(模拟 provider 5xx)退避后第二次成功(V5④ 正向)", async () => {
|
|
515
|
+
vi.useFakeTimers();
|
|
516
|
+
try {
|
|
517
|
+
const { call, trace } = makeAgentCallAndTrace();
|
|
518
|
+
const runner = createMockRunner(
|
|
519
|
+
vi.fn()
|
|
520
|
+
.mockResolvedValueOnce(makeMockResult({
|
|
521
|
+
error: "provider 503 service unavailable",
|
|
522
|
+
failureKind: "unknown",
|
|
523
|
+
}))
|
|
524
|
+
.mockResolvedValueOnce(makeMockResult()),
|
|
525
|
+
);
|
|
526
|
+
const budget = new Budget();
|
|
527
|
+
|
|
528
|
+
const promise = executeAgentCall(call, runner, budget, new AbortController().signal, trace);
|
|
529
|
+
await vi.advanceTimersByTimeAsync(2000);
|
|
530
|
+
await promise;
|
|
531
|
+
|
|
532
|
+
expect(runner.run).toHaveBeenCalledTimes(2);
|
|
533
|
+
expect(call.status).toBe("done");
|
|
534
|
+
expect(call.result?.error).toBeUndefined();
|
|
535
|
+
expect(trace.find(0)?.status).toBe("completed");
|
|
536
|
+
} finally {
|
|
537
|
+
vi.useRealTimers();
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
it("failureKind 缺省(旧链路/上游未写)→ 等同 unknown 默认退避重试(V5④ 语义守恒核心断言)", async () => {
|
|
542
|
+
vi.useFakeTimers();
|
|
543
|
+
try {
|
|
544
|
+
// 字段缺省是可重试而非「保守不重试」——反转会把一切未标注路径(瞬态 provider
|
|
545
|
+
// 错误、spawn 失败)静默丢进不重试(r1 MF4 击穿反例)
|
|
546
|
+
const { call, trace } = makeAgentCallAndTrace();
|
|
547
|
+
const runner = createMockRunner(
|
|
548
|
+
vi.fn()
|
|
549
|
+
.mockResolvedValueOnce(makeMockResult({ error: "spawn EAGAIN transient" }))
|
|
550
|
+
.mockResolvedValueOnce(makeMockResult()),
|
|
551
|
+
);
|
|
552
|
+
const budget = new Budget();
|
|
553
|
+
|
|
554
|
+
const promise = executeAgentCall(call, runner, budget, new AbortController().signal, trace);
|
|
555
|
+
await vi.advanceTimersByTimeAsync(2000);
|
|
556
|
+
await promise;
|
|
557
|
+
|
|
558
|
+
expect(runner.run).toHaveBeenCalledTimes(2);
|
|
559
|
+
expect(call.status).toBe("done");
|
|
560
|
+
} finally {
|
|
561
|
+
vi.useRealTimers();
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
it("词表漂移失效模式:未知错误文案 → classifyFailureKind=unknown → 走退避重试(安全默认)", async () => {
|
|
566
|
+
vi.useFakeTimers();
|
|
567
|
+
try {
|
|
568
|
+
// pi 升级改写 stale 文案后,旧词表对新文案零命中——分诊降级 unknown、
|
|
569
|
+
// 保守重试(可能多耗一次调用),而不是静默漏诊挂死在不重试
|
|
570
|
+
const futurePiError = "extension runtime was superseded by a newer orchestration epoch";
|
|
571
|
+
expect(classifyFailureKind(futurePiError)).toBe("unknown");
|
|
572
|
+
|
|
573
|
+
const { call, trace } = makeAgentCallAndTrace();
|
|
574
|
+
const runner = createMockRunner(
|
|
575
|
+
vi.fn()
|
|
576
|
+
.mockResolvedValueOnce(makeFailedResult(futurePiError))
|
|
577
|
+
.mockResolvedValueOnce(makeMockResult()),
|
|
578
|
+
);
|
|
579
|
+
const budget = new Budget();
|
|
580
|
+
|
|
581
|
+
const promise = executeAgentCall(call, runner, budget, new AbortController().signal, trace);
|
|
582
|
+
await vi.advanceTimersByTimeAsync(2000);
|
|
583
|
+
await promise;
|
|
584
|
+
|
|
585
|
+
expect(runner.run).toHaveBeenCalledTimes(2);
|
|
586
|
+
expect(call.status).toBe("done");
|
|
587
|
+
} finally {
|
|
588
|
+
vi.useRealTimers();
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
});
|