@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,314 @@
|
|
|
1
|
+
// src/orchestration/__tests__/script-generate.test.ts
|
|
2
|
+
//
|
|
3
|
+
// C4-core-script-pipeline(convergence W4)单元测试:generate 校验管线下沉的
|
|
4
|
+
// 行为锁定。覆盖 §4.2 W4 门全部条目:
|
|
5
|
+
// - 五道闸逐字文案(期望串按 pi 现版 actionGenerate 源码逻辑手工构造——
|
|
6
|
+
// pi 侧行为不变是 CA2 前提,文案改动必须同步两处);
|
|
7
|
+
// - round-trip 失败报错含 YAML 诊断与 line/col(parseResourceMetaDetailed
|
|
8
|
+
// 真实调用不 mock,与 pi 测试 TC2/TC5 同原则);
|
|
9
|
+
// - 合法样本落 tmp:目录参数注入生效(真实落盘断言,非 mock fs);
|
|
10
|
+
// - 缺省目录 = pi 布局(相对 cwd resolve,pi 现行为不变);
|
|
11
|
+
// - barrel 逐名探针(红线 9)。
|
|
12
|
+
// 设计权威源:docs/design/subagent-core-convergence.md §3.2 D-6。
|
|
13
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
|
+
import { join, resolve } from "node:path";
|
|
16
|
+
|
|
17
|
+
import { describe, expect, it, vi } from "vitest";
|
|
18
|
+
|
|
19
|
+
import type {
|
|
20
|
+
GenerateWorkflowScriptOptions,
|
|
21
|
+
GenerateWorkflowScriptResult,
|
|
22
|
+
WorkflowDirOptions,
|
|
23
|
+
} from "../../index.ts";
|
|
24
|
+
import * as subagentCore from "../../index.ts";
|
|
25
|
+
import { parseResourceMetaDetailed } from "../../shared/meta-parser.ts";
|
|
26
|
+
import { generateWorkflowScript } from "../script-generate.ts";
|
|
27
|
+
|
|
28
|
+
// ============================================================
|
|
29
|
+
// 样本(构造逻辑对齐 pi 侧 tool-workflow-script-generate.test.ts)
|
|
30
|
+
// ============================================================
|
|
31
|
+
|
|
32
|
+
const PI_META_VALID = `/* @pi-meta
|
|
33
|
+
name: test-wf
|
|
34
|
+
description: valid new format
|
|
35
|
+
phases: [a, b]
|
|
36
|
+
parameters:
|
|
37
|
+
type: object
|
|
38
|
+
properties:
|
|
39
|
+
task: { type: string }
|
|
40
|
+
required: [task]
|
|
41
|
+
*/
|
|
42
|
+
const agent = require("./agent");
|
|
43
|
+
agent("worker", { task: $ARGS.task });
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
const PI_META_MALFORMED = `/* @pi-meta
|
|
47
|
+
name: test-wf
|
|
48
|
+
description: bad
|
|
49
|
+
broken: indent
|
|
50
|
+
phases: [a]
|
|
51
|
+
*/
|
|
52
|
+
const agent = require("./agent");
|
|
53
|
+
agent("w");
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
const PI_META_REGEX_SINGLE_BS = `/* @pi-meta
|
|
57
|
+
name: test-wf
|
|
58
|
+
description: regex
|
|
59
|
+
phases: [A]
|
|
60
|
+
parameters:
|
|
61
|
+
type: object
|
|
62
|
+
patternProperties:
|
|
63
|
+
"^batch\\d+$": { type: string }
|
|
64
|
+
*/
|
|
65
|
+
const agent = require("./agent");
|
|
66
|
+
agent("w");
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
const LEGACY_CONST_META = `const meta = {
|
|
70
|
+
name: "legacy-wf",
|
|
71
|
+
description: "legacy format",
|
|
72
|
+
phases: ["a"]
|
|
73
|
+
};
|
|
74
|
+
const agent = require("./agent");
|
|
75
|
+
agent("w");
|
|
76
|
+
`;
|
|
77
|
+
|
|
78
|
+
const NO_META = `const agent = require("./agent");
|
|
79
|
+
agent("w");
|
|
80
|
+
`;
|
|
81
|
+
|
|
82
|
+
const ESM_IMPORT = `/* @pi-meta
|
|
83
|
+
name: x
|
|
84
|
+
description: d
|
|
85
|
+
phases: [a]
|
|
86
|
+
*/
|
|
87
|
+
import { foo } from "bar";
|
|
88
|
+
const agent = require("./agent");
|
|
89
|
+
agent("w");
|
|
90
|
+
`;
|
|
91
|
+
|
|
92
|
+
const ESM_EXPORT_NON_META = `/* @pi-meta
|
|
93
|
+
name: x
|
|
94
|
+
description: d
|
|
95
|
+
phases: [a]
|
|
96
|
+
*/
|
|
97
|
+
const agent = require("./agent");
|
|
98
|
+
export const foo = 1;
|
|
99
|
+
agent("w");
|
|
100
|
+
`;
|
|
101
|
+
|
|
102
|
+
const NO_AGENT = `/* @pi-meta
|
|
103
|
+
name: x
|
|
104
|
+
description: d
|
|
105
|
+
phases: [a]
|
|
106
|
+
*/
|
|
107
|
+
const x = 1;
|
|
108
|
+
`;
|
|
109
|
+
|
|
110
|
+
const SYNTAX_ERROR = `/* @pi-meta
|
|
111
|
+
name: x
|
|
112
|
+
description: d
|
|
113
|
+
phases: [a]
|
|
114
|
+
*/
|
|
115
|
+
const agent = require("./agent");
|
|
116
|
+
agent("w";
|
|
117
|
+
`;
|
|
118
|
+
|
|
119
|
+
// ============================================================
|
|
120
|
+
// 期望串(pi 现版 actionGenerate 文案逐字手工构造)
|
|
121
|
+
// ============================================================
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* round-trip 报错文案模板——pi 版 tool-workflow-script.ts actionGenerate
|
|
125
|
+
* 的模板逐字复制(含源码字面 `(\\d not \d)` 的历史形态:模板字符串里 `\\d`
|
|
126
|
+
* 渲染 `\d`、`\d` 渲染 `d`;两处字符级一致即运行时逐字一致)。
|
|
127
|
+
*/
|
|
128
|
+
function expectedRoundtripError(detailed: {
|
|
129
|
+
error: string;
|
|
130
|
+
linePos?: { line: number; col: number };
|
|
131
|
+
}): string {
|
|
132
|
+
const loc = "linePos" in detailed && detailed.linePos
|
|
133
|
+
? ` (line ${detailed.linePos.line}, col ${detailed.linePos.col})`
|
|
134
|
+
: "";
|
|
135
|
+
return `Generated /* @pi-meta */ YAML cannot be parsed${loc}: ${detailed.error}. Common causes: YAML indent errors, patternProperties regex must use double backslash (\\d not \d), or a stray star-slash inside the YAML body. Fix the meta block and retry.`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** 非.ok 分支的错误文案(窄化辅助:ok 分支直接 fail)。 */
|
|
139
|
+
function errorOf(r: GenerateWorkflowScriptResult): string {
|
|
140
|
+
if (r.ok) throw new Error("expected failure result, got success");
|
|
141
|
+
return r.error;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function makeTmpRoot(): string {
|
|
145
|
+
return mkdtempSync(join(tmpdir(), "subagent-core-gen-"));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ============================================================
|
|
149
|
+
// 五道闸(文案逐字对齐 pi 现版)
|
|
150
|
+
// ============================================================
|
|
151
|
+
|
|
152
|
+
describe("generateWorkflowScript 校验闸(文案逐字对齐 pi 现版)", () => {
|
|
153
|
+
it("缺 name/script → 拒(防御性文案逐字)", () => {
|
|
154
|
+
expect(errorOf(generateWorkflowScript("", "x"))).toBe(
|
|
155
|
+
"generate requires 'name' and 'script' parameters",
|
|
156
|
+
);
|
|
157
|
+
expect(errorOf(generateWorkflowScript("x", ""))).toBe(
|
|
158
|
+
"generate requires 'name' and 'script' parameters",
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("ESM import → 拒(文案逐字)", () => {
|
|
163
|
+
const tmpRoot = makeTmpRoot();
|
|
164
|
+
try {
|
|
165
|
+
const r = generateWorkflowScript("esm-wf", ESM_IMPORT, { tmpDir: tmpRoot });
|
|
166
|
+
expect(errorOf(r)).toBe(
|
|
167
|
+
"Script uses ESM 'import' syntax. Workflow scripts run in a CJS Worker — use require() instead.",
|
|
168
|
+
);
|
|
169
|
+
expect(existsSync(join(tmpRoot, "esm-wf.js"))).toBe(false);
|
|
170
|
+
} finally {
|
|
171
|
+
rmSync(tmpRoot, { recursive: true, force: true });
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("ESM export(非 meta)→ 拒(文案逐字)", () => {
|
|
176
|
+
const r = generateWorkflowScript("exp-wf", ESM_EXPORT_NON_META);
|
|
177
|
+
expect(errorOf(r)).toBe(
|
|
178
|
+
"Script uses ESM 'export' (non-meta). Use 'const meta = {...}' at top level instead.",
|
|
179
|
+
);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("无 meta 声明 → 拒(文案逐字)", () => {
|
|
183
|
+
const r = generateWorkflowScript("nometa-wf", NO_META);
|
|
184
|
+
expect(errorOf(r)).toBe(
|
|
185
|
+
"Script must contain a meta declaration: a /* @pi-meta */ YAML block comment (preferred) or legacy const meta = { ... }. The block has the form: a block comment starting with /* @pi-meta followed by YAML (name/description/phases/parameters?/usage?), closed by */ on its own line.",
|
|
186
|
+
);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("无 agent() 调用 → 拒(文案逐字)", () => {
|
|
190
|
+
const r = generateWorkflowScript("noagent-wf", NO_AGENT);
|
|
191
|
+
expect(errorOf(r)).toBe(
|
|
192
|
+
"Script does not contain any agent() calls. A workflow must call agent() at least once.",
|
|
193
|
+
);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("语法错误 → 拒(前缀逐字 + V8 诊断非空)", () => {
|
|
197
|
+
const r = generateWorkflowScript("synerr-wf", SYNTAX_ERROR);
|
|
198
|
+
const err = errorOf(r);
|
|
199
|
+
expect(err.startsWith("Syntax error in script: ")).toBe(true);
|
|
200
|
+
expect(err.length > "Syntax error in script: ".length).toBe(true);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it("malformed @pi-meta YAML → round-trip 拒,报错含 line/col + 未落盘", () => {
|
|
204
|
+
const tmpRoot = makeTmpRoot();
|
|
205
|
+
try {
|
|
206
|
+
const r = generateWorkflowScript("bad-wf", PI_META_MALFORMED, { tmpDir: tmpRoot });
|
|
207
|
+
const err = errorOf(r);
|
|
208
|
+
// 行列信息保留(ERR4:LLM 自纠正依赖)
|
|
209
|
+
expect(err).toMatch(/ \(line \d+, col \d+\)/);
|
|
210
|
+
// 期望串 = 同一 parser 实测值 + pi 源码模板手工展开 → 逐字一致
|
|
211
|
+
const detailed = parseResourceMetaDetailed(PI_META_MALFORMED, "workflow");
|
|
212
|
+
expect(detailed.ok).toBe(false);
|
|
213
|
+
if (!detailed.ok) {
|
|
214
|
+
expect(detailed.linePos).toBeDefined();
|
|
215
|
+
expect(err).toBe(expectedRoundtripError(detailed));
|
|
216
|
+
}
|
|
217
|
+
expect(existsSync(join(tmpRoot, "bad-wf.js"))).toBe(false);
|
|
218
|
+
} finally {
|
|
219
|
+
rmSync(tmpRoot, { recursive: true, force: true });
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("patternProperties 单反斜杠正则 → round-trip 拒(LLM 高频错,含行列)", () => {
|
|
224
|
+
const r = generateWorkflowScript("regex-wf", PI_META_REGEX_SINGLE_BS);
|
|
225
|
+
const err = errorOf(r);
|
|
226
|
+
expect(err).toMatch(/ \(line \d+, col \d+\)/);
|
|
227
|
+
const detailed = parseResourceMetaDetailed(PI_META_REGEX_SINGLE_BS, "workflow");
|
|
228
|
+
expect(detailed.ok).toBe(false);
|
|
229
|
+
if (!detailed.ok) {
|
|
230
|
+
expect(err).toBe(expectedRoundtripError(detailed));
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// ============================================================
|
|
236
|
+
// 合法样本落 tmp(目录参数注入生效)
|
|
237
|
+
// ============================================================
|
|
238
|
+
|
|
239
|
+
describe("generateWorkflowScript 合法样本落 tmp", () => {
|
|
240
|
+
it("注入 tmpDir:真实落盘指定目录,内容逐字节一致", () => {
|
|
241
|
+
const tmpRoot = makeTmpRoot();
|
|
242
|
+
try {
|
|
243
|
+
const r = generateWorkflowScript("my-wf", PI_META_VALID, { tmpDir: tmpRoot });
|
|
244
|
+
expect(r.ok).toBe(true);
|
|
245
|
+
if (!r.ok) throw new Error(r.error);
|
|
246
|
+
expect(r.path).toBe(resolve(tmpRoot, "my-wf.js"));
|
|
247
|
+
expect(existsSync(r.path)).toBe(true);
|
|
248
|
+
expect(readFileSync(r.path, "utf-8")).toBe(PI_META_VALID);
|
|
249
|
+
} finally {
|
|
250
|
+
rmSync(tmpRoot, { recursive: true, force: true });
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it("legacy const meta(过渡期)→ 过闸落盘(无 @pi-meta 跳过 round-trip)", () => {
|
|
255
|
+
const tmpRoot = makeTmpRoot();
|
|
256
|
+
try {
|
|
257
|
+
const r = generateWorkflowScript("legacy-wf", LEGACY_CONST_META, { tmpDir: tmpRoot });
|
|
258
|
+
expect(r.ok).toBe(true);
|
|
259
|
+
if (!r.ok) throw new Error(r.error);
|
|
260
|
+
expect(existsSync(r.path)).toBe(true);
|
|
261
|
+
} finally {
|
|
262
|
+
rmSync(tmpRoot, { recursive: true, force: true });
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("缺省 tmpDir = pi 布局 <cwd>/.pi/workflows/.tmp(pi 现行为不变)", () => {
|
|
267
|
+
const fakeCwd = mkdtempSync(join(tmpdir(), "subagent-core-cwd-"));
|
|
268
|
+
const cwdSpy = vi.spyOn(process, "cwd").mockReturnValue(fakeCwd);
|
|
269
|
+
try {
|
|
270
|
+
const r = generateWorkflowScript("def-wf", PI_META_VALID);
|
|
271
|
+
expect(r.ok).toBe(true);
|
|
272
|
+
if (!r.ok) throw new Error(r.error);
|
|
273
|
+
expect(r.path).toBe(join(fakeCwd, ".pi", "workflows", ".tmp", "def-wf.js"));
|
|
274
|
+
expect(existsSync(r.path)).toBe(true);
|
|
275
|
+
} finally {
|
|
276
|
+
cwdSpy.mockRestore();
|
|
277
|
+
rmSync(fakeCwd, { recursive: true, force: true });
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// ============================================================
|
|
283
|
+
// barrel 逐名探针(红线 9:新增导出必须进 barrel,逐名列出)
|
|
284
|
+
// ============================================================
|
|
285
|
+
|
|
286
|
+
describe("barrel exports probe", () => {
|
|
287
|
+
it("值导出逐名可达(typeof function)", () => {
|
|
288
|
+
const valueExports = [
|
|
289
|
+
"generateWorkflowScript",
|
|
290
|
+
"saveWorkflow",
|
|
291
|
+
"deleteWorkflow",
|
|
292
|
+
] as const;
|
|
293
|
+
const barrel = subagentCore as Record<string, unknown>;
|
|
294
|
+
for (const name of valueExports) {
|
|
295
|
+
expect(typeof barrel[name], `barrel 缺值导出: ${name}`).toBe("function");
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it("缺省目录常量经 barrel 可达(值 = pi 布局)", () => {
|
|
300
|
+
const barrel = subagentCore as Record<string, unknown>;
|
|
301
|
+
expect(barrel["DEFAULT_WORKFLOW_TMP_DIR"]).toBe(".pi/workflows/.tmp");
|
|
302
|
+
expect(barrel["DEFAULT_WORKFLOW_SAVED_DIR"]).toBe(".pi/workflows");
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it("类型导出经 barrel 可引用(typecheck 即探针)", () => {
|
|
306
|
+
const genOpts: GenerateWorkflowScriptOptions = { tmpDir: "/tmp/x" };
|
|
307
|
+
const dirOpts: WorkflowDirOptions = { tmpDir: "/tmp/x", savedDir: "/tmp/y" };
|
|
308
|
+
let result: GenerateWorkflowScriptResult = { ok: true, path: "/p" };
|
|
309
|
+
result = { ok: false, error: "e" };
|
|
310
|
+
expect(genOpts.tmpDir).toBe("/tmp/x");
|
|
311
|
+
expect(dirOpts.savedDir).toBe("/tmp/y");
|
|
312
|
+
expect(result.ok).toBe(false);
|
|
313
|
+
});
|
|
314
|
+
});
|
|
@@ -16,6 +16,7 @@ import { readFileSync } from "node:fs";
|
|
|
16
16
|
import { join } from "node:path";
|
|
17
17
|
|
|
18
18
|
import { lintAgentMeta, lintScript } from "../script-lint.ts";
|
|
19
|
+
import type { LintFinding } from "../script-lint.ts";
|
|
19
20
|
import { parseResourceMeta } from "../../shared/meta-parser.ts";
|
|
20
21
|
|
|
21
22
|
const WORKFLOWS_DIR = join(__dirname, "../../../workflows");
|
|
@@ -491,6 +492,22 @@ describe("MF-4 回归:非字面量实参 agent(callVar) / agent(expr()) 跳过
|
|
|
491
492
|
expect(descWarnings).toHaveLength(1);
|
|
492
493
|
expect(descWarnings[0].line).toBe(2);
|
|
493
494
|
});
|
|
495
|
+
|
|
496
|
+
it("`agent(` 换行 `{` 的多行字面量调用保留追踪(argTail 为空分支)", () => {
|
|
497
|
+
// agent( 与 { 分离两行:argTail 为空不跳过,范围跨三行闭合——缺 description
|
|
498
|
+
// 报 1 条且行号落在调用起点(锚定 matchAgentCallHead 的空 argTail 保留路径)
|
|
499
|
+
const src = [
|
|
500
|
+
`await agent(`,
|
|
501
|
+
` { prompt: "x" }`,
|
|
502
|
+
`);`,
|
|
503
|
+
``,
|
|
504
|
+
].join("\n");
|
|
505
|
+
const result = lintScript(src);
|
|
506
|
+
|
|
507
|
+
const descWarnings = warnings(result.findings).filter((w) => /description.*unnamed/i.test(w.message));
|
|
508
|
+
expect(descWarnings).toHaveLength(1);
|
|
509
|
+
expect(descWarnings[0].line).toBe(1);
|
|
510
|
+
});
|
|
494
511
|
});
|
|
495
512
|
|
|
496
513
|
// ── MF-4 回归:checkAgentDescription 的两个跳过分支 ────────────────
|
|
@@ -68,6 +68,7 @@ export function mkCtx(
|
|
|
68
68
|
ui: mkUi(),
|
|
69
69
|
// pi 在 xyz-agent 下以 rpc 子进程模式运行;rpc 模式 hasUI 为 true(SDK 契约注释)
|
|
70
70
|
mode: "rpc",
|
|
71
|
+
scopedModels: [],
|
|
71
72
|
hasUI: true,
|
|
72
73
|
cwd: "/tmp",
|
|
73
74
|
sessionManager: {
|
|
@@ -168,6 +169,7 @@ export function mkPi(
|
|
|
168
169
|
registerTool: vi.fn(),
|
|
169
170
|
registerCommand: vi.fn(),
|
|
170
171
|
registerShortcut: vi.fn(),
|
|
172
|
+
registerMarkdownTransformer: vi.fn(),
|
|
171
173
|
registerFlag: vi.fn(),
|
|
172
174
|
getFlag: vi.fn(() => undefined),
|
|
173
175
|
registerMessageRenderer: vi.fn(),
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* `void handlers.onXxx(...)` 无人接 → unhandledRejection + pending:unregister / onRunDone
|
|
19
19
|
* 不执行(pending 通知幽灵注销)。修复后 save 失败仅 logger.error,状态机继续推进。
|
|
20
20
|
*
|
|
21
|
-
* mock 构建参考
|
|
21
|
+
* mock 构建参考 worker-message-pump-handlers.test.ts(plain-object WorkflowRun mock)。
|
|
22
22
|
*/
|
|
23
23
|
import { describe, expect, it, vi } from "vitest";
|
|
24
24
|
|
|
@@ -27,16 +27,17 @@ import {
|
|
|
27
27
|
handleWorkerMessage,
|
|
28
28
|
handleScriptError,
|
|
29
29
|
handleWorkerError,
|
|
30
|
-
} from "../
|
|
30
|
+
} from "../worker-message-pump.ts";
|
|
31
31
|
import type { LifecycleDeps, WorkerHandlers } from "../models/ports.ts";
|
|
32
|
+
import type { DoneReason, RunStatus } from "../models/types.ts";
|
|
32
33
|
import type { WorkflowRun } from "../models/workflow-run.ts";
|
|
33
34
|
import type { WorkerHandle } from "../worker-handle.ts";
|
|
34
35
|
|
|
35
|
-
/** [F1] 归因文案——与
|
|
36
|
+
/** [F1] 归因文案——与 worker-message-pump.ts 常量一致(不直接 import 常量以锚定对外文案)。 */
|
|
36
37
|
const EXITED_WITHOUT_RESULT_MSG =
|
|
37
38
|
"worker exited before delivering a result (return value may not be structured-cloneable)";
|
|
38
39
|
|
|
39
|
-
// ── helpers(对齐
|
|
40
|
+
// ── helpers(对齐 worker-message-pump-handlers.test.ts)─────────────────
|
|
40
41
|
|
|
41
42
|
interface RunMockOpts {
|
|
42
43
|
workerErrorCount?: number;
|
|
@@ -70,11 +71,11 @@ function makeRunningRun(opts: RunMockOpts = {}): WorkflowRun {
|
|
|
70
71
|
worker: { postMessage: vi.fn() },
|
|
71
72
|
receivedTerminalMessage: opts.receivedTerminalMessage,
|
|
72
73
|
},
|
|
73
|
-
transition(target:
|
|
74
|
+
transition(this: WorkflowRun, target: RunStatus, reason?: DoneReason): void {
|
|
74
75
|
this.state.status = target;
|
|
75
76
|
if (target === "done") this.state.reason = reason;
|
|
76
77
|
},
|
|
77
|
-
replaceRuntime(rt:
|
|
78
|
+
replaceRuntime(this: WorkflowRun, rt: NonNullable<WorkflowRun["runtime"]>): void {
|
|
78
79
|
this.runtime = rt;
|
|
79
80
|
},
|
|
80
81
|
} as unknown as WorkflowRun;
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* finalizeRun — D5-② 终态 coda 单写点测试。
|
|
3
|
+
*
|
|
4
|
+
* 「transition → save → pending:unregister → onRunDone」四步终态序列的唯一
|
|
5
|
+
* 定义点(worker-message-pump.ts)。收敛前 8 处逐字复制(本文件 6 处 + lifecycle
|
|
6
|
+
* 2 处)——本文件锁定:
|
|
7
|
+
* 1. 四步恰好一次且有序(transition 先于 save 先于 unregister 先于 onRunDone)
|
|
8
|
+
* 2. notifyDone:false 真差异承载(terminateRunningRuns 不发 onRunDone、unregister 仍发)
|
|
9
|
+
* 3. transition 让位(并发终态化)→ 后三步全不执行
|
|
10
|
+
* 4. save best-effort(SW-DATA-3)→ unregister/onRunDone 不被落盘失败短路
|
|
11
|
+
*
|
|
12
|
+
* 其余终态路径的「恰好一次四步」行为断言(save×1 + unregister + onRunDone×1)
|
|
13
|
+
* 已由既有测试覆盖:handleWorkerError/handleScriptError/handleWorkerExit 超限
|
|
14
|
+
* (worker-message-pump-handlers / worker-exit-without-result)、time_limited
|
|
15
|
+
* (worker-message-pump-handlers race-F3 describe)、abortRun(lifecycle.test)、
|
|
16
|
+
* handleReturn(worker-exit-without-result SW-DATA-3 describe)。本文件补齐
|
|
17
|
+
* budget_limited 路径(dispatchAgentCall coda)与 notifyDone:false 路径。
|
|
18
|
+
*/
|
|
19
|
+
import { describe, expect, it, vi } from "vitest";
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
finalizeRun,
|
|
23
|
+
handleWorkerMessage,
|
|
24
|
+
} from "../worker-message-pump.ts";
|
|
25
|
+
import { Budget } from "../models/budget.ts";
|
|
26
|
+
import { RunRuntime } from "../models/run-runtime.ts";
|
|
27
|
+
import { Trace } from "../models/trace.ts";
|
|
28
|
+
import type { AgentResult } from "../models/types.ts";
|
|
29
|
+
import { WorkflowRun } from "../models/workflow-run.ts";
|
|
30
|
+
import type { LifecycleDeps, WorkerHandlers } from "../models/ports.ts";
|
|
31
|
+
import type { WorkerHandle } from "../worker-handle.ts";
|
|
32
|
+
|
|
33
|
+
// ── helpers ──────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
/** 构造真实 WorkflowRun(真实状态机 transition)+ 初始 runtime。 */
|
|
36
|
+
function makeRealRun(runId: string): WorkflowRun {
|
|
37
|
+
const run = new WorkflowRun(
|
|
38
|
+
runId,
|
|
39
|
+
{
|
|
40
|
+
scriptName: "test-wf",
|
|
41
|
+
scriptSource: "agent('hi')",
|
|
42
|
+
args: {},
|
|
43
|
+
scriptPath: "/tmp/test-wf.js",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
status: "running",
|
|
47
|
+
budget: new Budget(),
|
|
48
|
+
calls: new Map(),
|
|
49
|
+
trace: new Trace(),
|
|
50
|
+
errorLogs: [],
|
|
51
|
+
},
|
|
52
|
+
{ startedAt: new Date().toISOString() },
|
|
53
|
+
);
|
|
54
|
+
const worker = {
|
|
55
|
+
postMessage: vi.fn(),
|
|
56
|
+
terminate: vi.fn(async () => {}),
|
|
57
|
+
} as unknown as WorkerHandle;
|
|
58
|
+
run.assignRuntime(new RunRuntime(worker, new AbortController()));
|
|
59
|
+
return run;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** deps mock:副作用打点进单一顺序数组(断言四步顺序)。 */
|
|
63
|
+
function makeTracingDeps(): LifecycleDeps & {
|
|
64
|
+
order: string[];
|
|
65
|
+
store: { save: ReturnType<typeof vi.fn> };
|
|
66
|
+
} {
|
|
67
|
+
const order: string[] = [];
|
|
68
|
+
return {
|
|
69
|
+
order,
|
|
70
|
+
store: {
|
|
71
|
+
save: vi.fn(async () => {
|
|
72
|
+
order.push("save");
|
|
73
|
+
}),
|
|
74
|
+
},
|
|
75
|
+
workerHost: { start: vi.fn(() => ({ postMessage: vi.fn() })) },
|
|
76
|
+
runner: { run: vi.fn(async () => ({}) as AgentResult) },
|
|
77
|
+
runs: new Map(),
|
|
78
|
+
eventBus: {
|
|
79
|
+
emit: vi.fn((event: string) => {
|
|
80
|
+
order.push(`emit:${event}`);
|
|
81
|
+
}),
|
|
82
|
+
},
|
|
83
|
+
onRunDone: vi.fn(() => {
|
|
84
|
+
order.push("onRunDone");
|
|
85
|
+
}),
|
|
86
|
+
log: vi.fn(),
|
|
87
|
+
} as unknown as ReturnType<typeof makeTracingDeps>;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** flush 微任务队列(void finalizeRun 的 async 链推进到稳定态)。 */
|
|
91
|
+
async function flushMicrotasks(ticks = 20): Promise<void> {
|
|
92
|
+
for (let i = 0; i < ticks; i++) {
|
|
93
|
+
// eslint-disable-next-line no-await-in-loop -- 排空微任务队列的固定 tick 循环,非逐项等待
|
|
94
|
+
await Promise.resolve();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ── finalizeRun 直测 ─────────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
describe("finalizeRun(D5-② 单写点直测)", () => {
|
|
101
|
+
it("四步恰好一次且有序:transition → save → pending:unregister → onRunDone", async () => {
|
|
102
|
+
const run = makeRealRun("wf-fin-1");
|
|
103
|
+
const deps = makeTracingDeps();
|
|
104
|
+
|
|
105
|
+
const transitionSpy = vi.spyOn(run, "transition");
|
|
106
|
+
const ok = await finalizeRun(run, deps, "completed", { context: "test" });
|
|
107
|
+
|
|
108
|
+
expect(ok).toBe(true);
|
|
109
|
+
expect(run.state.status).toBe("done");
|
|
110
|
+
expect(run.state.reason).toBe("completed");
|
|
111
|
+
// 四步各恰好一次
|
|
112
|
+
expect(transitionSpy).toHaveBeenCalledTimes(1);
|
|
113
|
+
expect(deps.store.save).toHaveBeenCalledTimes(1);
|
|
114
|
+
expect(deps.eventBus!.emit).toHaveBeenCalledTimes(1);
|
|
115
|
+
expect(deps.onRunDone).toHaveBeenCalledTimes(1);
|
|
116
|
+
// 顺序:save → unregister → onRunDone(transition 已同步先行)
|
|
117
|
+
expect(deps.order).toEqual(["save", "emit:pending:unregister", "onRunDone"]);
|
|
118
|
+
// unregister reason = transition 后的 state.reason
|
|
119
|
+
expect(deps.eventBus!.emit).toHaveBeenCalledWith("pending:unregister", {
|
|
120
|
+
id: "wf-fin-1",
|
|
121
|
+
reason: "completed",
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("notifyDone:false → onRunDone 不调、unregister 仍发(terminateRunningRuns 真差异经参数承载)", async () => {
|
|
126
|
+
const run = makeRealRun("wf-fin-2");
|
|
127
|
+
const deps = makeTracingDeps();
|
|
128
|
+
|
|
129
|
+
const ok = await finalizeRun(run, deps, "failed", {
|
|
130
|
+
context: "terminateRunningRuns",
|
|
131
|
+
notifyDone: false,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
expect(ok).toBe(true);
|
|
135
|
+
expect(run.state.reason).toBe("failed");
|
|
136
|
+
expect(deps.eventBus!.emit).toHaveBeenCalledTimes(1);
|
|
137
|
+
expect(deps.eventBus!.emit).toHaveBeenCalledWith("pending:unregister", {
|
|
138
|
+
id: "wf-fin-2",
|
|
139
|
+
reason: "failed",
|
|
140
|
+
});
|
|
141
|
+
expect(deps.onRunDone).not.toHaveBeenCalled();
|
|
142
|
+
expect(deps.order).toEqual(["save", "emit:pending:unregister"]);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("transition 抛错(并发 abort 抢先终态化)→ 返回 false,后三步全不执行", async () => {
|
|
146
|
+
const run = makeRealRun("wf-fin-3");
|
|
147
|
+
// 抢先终态化——此后 running→done 转移抛 illegal-transition
|
|
148
|
+
run.transition("done", "aborted");
|
|
149
|
+
const deps = makeTracingDeps();
|
|
150
|
+
|
|
151
|
+
const ok = await finalizeRun(run, deps, "failed", { context: "test" });
|
|
152
|
+
|
|
153
|
+
expect(ok).toBe(false);
|
|
154
|
+
// 抢先方已兑现 unregister/onRunDone——本路径让位,不重复执行
|
|
155
|
+
expect(deps.store.save).not.toHaveBeenCalled();
|
|
156
|
+
expect(deps.eventBus!.emit).not.toHaveBeenCalled();
|
|
157
|
+
expect(deps.onRunDone).not.toHaveBeenCalled();
|
|
158
|
+
expect(run.state.reason).toBe("aborted");
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("save 抛错(ENOSPC)→ best-effort:unregister + onRunDone 照常(SW-DATA-3 统一)", async () => {
|
|
162
|
+
const run = makeRealRun("wf-fin-4");
|
|
163
|
+
const deps = makeTracingDeps();
|
|
164
|
+
deps.store.save = vi.fn(async () => {
|
|
165
|
+
throw new Error("ENOSPC: no space left on device");
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const ok = await finalizeRun(run, deps, "failed", { context: "test" });
|
|
169
|
+
|
|
170
|
+
expect(ok).toBe(true);
|
|
171
|
+
expect(run.state.status).toBe("done");
|
|
172
|
+
expect(deps.eventBus!.emit).toHaveBeenCalledTimes(1);
|
|
173
|
+
expect(deps.onRunDone).toHaveBeenCalledTimes(1);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("onRunDone 抛错 → 被捕获记日志(M12:真实副作用错误不静默吞、不上抛)", async () => {
|
|
177
|
+
const run = makeRealRun("wf-fin-5");
|
|
178
|
+
const deps = makeTracingDeps();
|
|
179
|
+
deps.onRunDone = vi.fn(() => {
|
|
180
|
+
throw new Error("interface notify blew up");
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
await expect(
|
|
184
|
+
finalizeRun(run, deps, "completed", { context: "test" }),
|
|
185
|
+
).resolves.toBe(true);
|
|
186
|
+
expect(deps.eventBus!.emit).toHaveBeenCalledTimes(1);
|
|
187
|
+
expect(run.state.status).toBe("done");
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// ── budget_limited 路径(dispatchAgentCall coda)四步恰好一次 ────────────
|
|
192
|
+
|
|
193
|
+
describe("budget_limited 终态路径(dispatchAgentCall → finalizeRun)", () => {
|
|
194
|
+
it("agent call 后预算超限 → 四步恰好一次(unregister reason=budget_limited + onRunDone)", async () => {
|
|
195
|
+
const run = makeRealRun("wf-budget-1");
|
|
196
|
+
const deps = makeTracingDeps();
|
|
197
|
+
// budget.isExceeded 恒 true——runner.run 成功返回后命中 C-2 coda
|
|
198
|
+
(run.state.budget as unknown as { isExceeded: () => boolean }).isExceeded =
|
|
199
|
+
() => true;
|
|
200
|
+
deps.runner.run = vi.fn(async () =>
|
|
201
|
+
({ content: "ok", durationMs: 1, error: undefined, toolCalls: [] }) as AgentResult,
|
|
202
|
+
);
|
|
203
|
+
const handlers: WorkerHandlers = {
|
|
204
|
+
onMessage: vi.fn(async () => {}),
|
|
205
|
+
onError: vi.fn(async () => {}),
|
|
206
|
+
onExit: vi.fn(async () => {}),
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
await handleWorkerMessage(
|
|
210
|
+
run,
|
|
211
|
+
{ type: "agent-call", callId: 1, opts: { prompt: "p", description: "d" } },
|
|
212
|
+
deps,
|
|
213
|
+
handlers,
|
|
214
|
+
);
|
|
215
|
+
await flushMicrotasks();
|
|
216
|
+
|
|
217
|
+
expect(run.state.status).toBe("done");
|
|
218
|
+
expect(run.state.reason).toBe("budget_limited");
|
|
219
|
+
// save 2 次 = call 完成快照(dispatchAgentCall .then 的常规持久化)+ budget
|
|
220
|
+
// 终态快照(finalizeRun 内)——两次语义不同,收敛前后一致
|
|
221
|
+
expect(deps.store.save).toHaveBeenCalledTimes(2);
|
|
222
|
+
expect(deps.eventBus!.emit).toHaveBeenCalledTimes(1);
|
|
223
|
+
expect(deps.eventBus!.emit).toHaveBeenCalledWith("pending:unregister", {
|
|
224
|
+
id: "wf-budget-1",
|
|
225
|
+
reason: "budget_limited",
|
|
226
|
+
});
|
|
227
|
+
expect(deps.onRunDone).toHaveBeenCalledTimes(1);
|
|
228
|
+
});
|
|
229
|
+
});
|