@zhushanwen/subagent-core 0.2.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -2
- package/agents/analyst.md +60 -0
- package/agents/coder.md +69 -0
- package/agents/debugger.md +66 -0
- package/agents/doc-reviewer.md +49 -0
- package/agents/explorer.md +63 -0
- package/agents/general-purpose.md +32 -0
- package/agents/orchestrator.md +61 -0
- package/agents/planner.md +53 -0
- package/agents/researcher.md +67 -0
- package/agents/reviewer.md +73 -0
- package/dist/chunk-43ONBFZX.js +240 -0
- package/dist/chunk-APZY4IME.js +27 -0
- package/dist/chunk-V3VHZ2VX.js +59 -0
- package/dist/execution/engine/engines/zcode/constants.cjs +62 -9
- package/dist/execution/engine/engines/zcode/constants.d.cts +116 -12
- package/dist/execution/engine/engines/zcode/constants.d.ts +116 -12
- package/dist/execution/engine/engines/zcode/constants.js +39 -7
- package/dist/execution/engine/engines/zcode/reader.d.cts +2 -1
- package/dist/execution/engine/engines/zcode/reader.d.ts +2 -1
- package/dist/execution/engine/engines/zcode/reader.js +4 -234
- package/dist/execution/engine/paths.js +7 -19
- package/dist/index.cjs +18234 -1277
- package/dist/index.d.cts +5646 -673
- package/dist/index.d.ts +5646 -673
- package/dist/index.js +17828 -1277
- package/dist/{types-BxyAidGf.d.cts → types-Dv4QhSJ_.d.cts} +678 -112
- package/dist/{types-BxyAidGf.d.ts → types-Dv4QhSJ_.d.ts} +678 -112
- package/package.json +7 -4
- package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +0 -1
- package/src/__tests__/append-system-prompt-assembly.test.ts +8 -7
- package/src/__tests__/fr4-get-state-handshake.test.ts +2 -2
- package/src/__tests__/m2-append-content-probe.test.ts +0 -1
- package/src/__tests__/manifest-store.test.ts +10 -9
- package/src/__tests__/record-store-cache.test.ts +0 -2
- package/src/__tests__/record-store-index.test.ts +1 -2
- package/src/__tests__/review-fix-loop-script.test.ts +93 -3
- package/src/__tests__/review-fix-loop-utils.test.ts +31 -10
- package/src/__tests__/robustness-low-batch1.test.ts +6 -5
- package/src/__tests__/robustness-medium-batch1.test.ts +2 -2
- package/src/__tests__/robustness-medium-batch2.test.ts +17 -6
- package/src/__tests__/robustness-medium-batch4.test.ts +2 -2
- package/src/__tests__/session-runner.test.ts +1 -1
- package/src/__tests__/smoke.test.ts +9 -2
- package/src/core/__tests__/host-services.test.ts +2 -2
- package/src/core/__tests__/logger.test.ts +1 -1
- package/src/core/error-message.ts +9 -0
- package/src/core/host-services.ts +21 -5
- package/src/core/notify-ports.ts +18 -5
- package/src/execution/__tests__/agent-profile.test.ts +232 -0
- package/src/execution/__tests__/agent-registry.test.ts +370 -0
- package/src/execution/__tests__/agent-result-mapper.test.ts +18 -2
- package/src/execution/__tests__/agents-assembly.test.ts +219 -0
- package/src/execution/__tests__/ask-user-transit-e2e.test.ts +6 -6
- package/src/execution/__tests__/chat-engine-routing.test.ts +136 -30
- package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +5 -4
- package/src/execution/__tests__/cold-resurrect.test.ts +283 -0
- package/src/execution/__tests__/create-concurrency-pool.test.ts +194 -0
- package/src/execution/__tests__/delivery-methods.test.ts +77 -62
- package/src/execution/__tests__/descendant-sweep-guards.test.ts +220 -0
- package/src/execution/__tests__/descendant-sweep.test.ts +269 -0
- package/src/execution/__tests__/dialog-queue.test.ts +199 -1
- package/src/execution/__tests__/engine-model-validation.test.ts +741 -0
- package/src/execution/__tests__/epipe-fallback.test.ts +31 -24
- package/src/execution/__tests__/execute-and-await-worktree.test.ts +21 -91
- package/src/execution/__tests__/execute-nesting.test.ts +34 -188
- package/src/execution/__tests__/execution-record.test.ts +217 -60
- package/src/execution/__tests__/execution-runtime-face.test.ts +272 -0
- package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +3 -3
- package/src/execution/__tests__/finalize-record.test.ts +278 -16
- package/src/execution/__tests__/gc-timer.test.ts +57 -2
- package/src/execution/__tests__/get-record-for-action-restart.test.ts +14 -12
- package/src/execution/__tests__/get-state-handshake.test.ts +127 -0
- package/src/execution/__tests__/helpers/mock-extension-api.ts +1 -1
- package/src/execution/__tests__/helpers/session-runner-mocks.ts +113 -0
- package/src/execution/__tests__/helpers/spawn-mock.ts +150 -3
- package/src/execution/__tests__/helpers/subagent-service-mocks.ts +137 -0
- package/src/execution/__tests__/keep-alive-no-progress.test.ts +280 -0
- package/src/execution/__tests__/kill-all-escalation.test.ts +196 -0
- package/src/execution/__tests__/lifecycle-manager-idle-timer-identity.test.ts +117 -0
- package/src/execution/__tests__/lifecycle-manager.test.ts +39 -0
- package/src/execution/__tests__/lifecycle-predicates.test.ts +1 -2
- package/src/execution/__tests__/manifest-parentid.test.ts +4 -4
- package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +99 -0
- package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +113 -0
- package/src/execution/__tests__/model-resolver.test.ts +2 -2
- package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +23 -121
- package/src/execution/__tests__/nested-visibility.test.ts +2 -0
- package/src/execution/__tests__/notify-ledger.test.ts +1249 -0
- package/src/execution/__tests__/output-collector.test.ts +117 -6
- package/src/execution/__tests__/pi-invocation.test.ts +1 -1
- package/src/execution/__tests__/record-store-orphan-revive.test.ts +159 -0
- package/src/execution/__tests__/record-store.test.ts +89 -5
- package/src/execution/__tests__/records-cwd-isolation.test.ts +2 -2
- package/src/execution/__tests__/recursive-visibility-baseline.test.ts +29 -132
- package/src/execution/__tests__/recursive-visibility-env.test.ts +55 -179
- package/src/execution/__tests__/rpc-mode.test.ts +2 -2
- package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +387 -0
- package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +19 -9
- package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +5 -4
- package/src/execution/__tests__/run-spawn-edges.test.ts +418 -26
- package/src/execution/__tests__/run-spawn-integration.test.ts +3 -3
- package/src/execution/__tests__/run-spawn-resume.test.ts +2 -2
- package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +2 -2
- package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +16 -8
- package/src/execution/__tests__/service-kill-escalation.test.ts +91 -0
- package/src/execution/__tests__/session-context-resolver.test.ts +0 -2
- package/src/execution/__tests__/session-file-gc.test.ts +35 -0
- package/src/execution/__tests__/session-pending.test.ts +471 -0
- package/src/execution/__tests__/session-reconstructor.test.ts +366 -15
- package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +110 -0
- package/src/execution/__tests__/session-runner-close-prune.test.ts +181 -0
- package/src/execution/__tests__/session-runner-dispatch.test.ts +489 -0
- package/src/execution/__tests__/session-runner-epipe.test.ts +4 -3
- package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +227 -0
- package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +290 -0
- package/src/execution/__tests__/session-runner-schema-env.test.ts +7 -5
- package/src/execution/__tests__/settled-watchdog.test.ts +524 -0
- package/src/execution/__tests__/spawn-args.test.ts +11 -10
- package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +26 -23
- package/src/execution/__tests__/spawn-event-adapter.test.ts +1 -1
- package/src/execution/__tests__/spawn-worktree-guidance.test.ts +5 -4
- package/src/execution/__tests__/spawned-children.test.ts +155 -11
- package/src/execution/__tests__/start-sync-model-guard.test.ts +4 -4
- package/src/execution/__tests__/stdin-writer.test.ts +6 -4
- package/src/execution/__tests__/stream-sink-retirement.test.ts +6 -6
- package/src/execution/__tests__/subagent-actions-core.test.ts +1017 -0
- package/src/execution/__tests__/subagent-service-message-close.test.ts +99 -35
- package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +174 -0
- package/src/execution/__tests__/subagent-service-notify-gate.test.ts +271 -0
- package/src/execution/__tests__/subagent-service-parent-guard.test.ts +33 -29
- package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +341 -0
- package/src/execution/__tests__/subagent-service.test.ts +37 -30
- package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +88 -12
- package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +75 -0
- package/src/execution/__tests__/subprocess-agent-runner.test.ts +22 -20
- package/src/execution/__tests__/temp-prompt.test.ts +1 -1
- package/src/execution/__tests__/timeout-integration.test.ts +13 -12
- package/src/execution/__tests__/turn-limiter-semantics.test.ts +1 -1
- package/src/execution/__tests__/turn-limiter.test.ts +1 -1
- package/src/execution/__tests__/ui-request-handler-factory.test.ts +107 -7
- package/src/execution/__tests__/ui-request-handler.test.ts +4 -3
- package/src/execution/__tests__/ui-request-queue.test.ts +167 -2
- package/src/execution/__tests__/worktree-git-ops.test.ts +571 -0
- package/src/execution/__tests__/worktree-manager.test.ts +14 -5
- package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +4 -3
- package/src/execution/__tests__/worktree-reconcile-aging.test.ts +204 -0
- package/src/execution/agent-registry.ts +298 -30
- package/src/execution/agent-result-mapper.ts +3 -0
- package/src/execution/agents-assembly.ts +88 -0
- package/src/execution/cold-resurrect.ts +199 -0
- package/src/execution/concurrency-pool.ts +71 -9
- package/src/execution/dialog-queue.ts +101 -3
- package/src/execution/engine/__tests__/common/capability-gate.test.ts +91 -0
- package/src/execution/engine/__tests__/common/data-dir.test.ts +1 -1
- package/src/execution/engine/__tests__/common/journal-wiring.test.ts +84 -0
- package/src/execution/engine/__tests__/common/kill-chain.test.ts +34 -7
- package/src/execution/engine/__tests__/common/persona-router.test.ts +4 -5
- package/src/execution/engine/__tests__/common/pool-manager.test.ts +211 -51
- package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +149 -0
- package/src/execution/engine/__tests__/common/session-view-service.test.ts +499 -0
- package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +56 -99
- package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +88 -15
- package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +33 -7
- package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +1 -1
- package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +75 -54
- package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +128 -0
- package/src/execution/engine/__tests__/engine-discovery.test.ts +15 -3
- package/src/execution/engine/__tests__/model-prompt.test.ts +15 -3
- package/src/execution/engine/__tests__/registry.test.ts +94 -3
- package/src/execution/engine/__tests__/routing.test.ts +115 -6
- package/src/execution/engine/common/capability-gate.ts +92 -0
- package/src/execution/engine/common/journal-wiring.ts +97 -0
- package/src/execution/engine/common/kill-chain.ts +42 -10
- package/src/execution/engine/common/nesting-guard.ts +66 -0
- package/src/execution/engine/common/persona-router.ts +18 -8
- package/src/execution/engine/common/pool-manager.ts +332 -78
- package/src/execution/engine/common/session-view-service.ts +506 -0
- package/src/execution/engine/common/session-view-types.ts +125 -0
- package/src/execution/engine/engine-discovery.ts +12 -16
- package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +174 -28
- package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +257 -0
- package/src/execution/{argv-mirror.ts → engine/engines/pi/argv-mirror.ts} +43 -21
- package/src/execution/{get-state-handshake.ts → engine/engines/pi/get-state-handshake.ts} +86 -11
- package/src/execution/{output-collector.ts → engine/engines/pi/output-collector.ts} +108 -19
- package/src/execution/engine/engines/pi/pi-engine.ts +434 -38
- package/src/execution/{pi-invocation.ts → engine/engines/pi/pi-invocation.ts} +2 -2
- package/src/execution/engine/engines/pi/reader.ts +11 -0
- package/src/execution/engine/engines/pi/registration.ts +4 -2
- package/src/execution/engine/engines/pi/session-runner.ts +2840 -0
- package/src/execution/{spawn-event-adapter.ts → engine/engines/pi/spawn-event-adapter.ts} +75 -34
- package/src/execution/{stdin-writer.ts → engine/engines/pi/stdin-writer.ts} +10 -9
- package/src/execution/{temp-prompt.ts → engine/engines/pi/temp-prompt.ts} +2 -2
- package/src/execution/{turn-limiter.ts → engine/engines/pi/turn-limiter.ts} +1 -1
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +301 -0
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +36 -0
- package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +321 -0
- package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +473 -0
- package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +7 -210
- package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +6 -110
- package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +247 -0
- package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +451 -0
- package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +792 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +782 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +168 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +210 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +353 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +282 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +372 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +65 -47
- package/src/execution/engine/engines/zcode/appserver-launcher.ts +167 -0
- package/src/execution/engine/engines/zcode/connection.ts +598 -0
- package/src/execution/engine/engines/zcode/constants.ts +147 -14
- package/src/execution/engine/engines/zcode/golden-sample.ts +42 -34
- package/src/execution/engine/engines/zcode/parser.ts +12 -346
- package/src/execution/engine/engines/zcode/preparer.ts +22 -150
- package/src/execution/engine/engines/zcode/session-channel.ts +968 -0
- package/src/execution/engine/engines/zcode/zcode-engine.ts +1023 -250
- package/src/execution/engine/host-task-spec.ts +32 -36
- package/src/execution/engine/model-validation.ts +177 -0
- package/src/execution/engine/port.ts +65 -7
- package/src/execution/engine/registry.ts +53 -0
- package/src/execution/engine/routing.ts +75 -1
- package/src/execution/engine/types.ts +30 -103
- package/src/execution/execution-record.ts +222 -144
- package/src/execution/finalize-record.ts +181 -85
- package/src/execution/idle-gc.ts +28 -0
- package/src/execution/lifecycle-manager.ts +27 -3
- package/src/execution/lifecycle-predicates.ts +1 -1
- package/src/execution/manifest-store.ts +53 -62
- package/src/execution/notifier.ts +10 -10
- package/src/execution/notify-host.ts +212 -0
- package/src/execution/notify-ledger.ts +117 -16
- package/src/execution/record-entry.ts +8 -2
- package/src/execution/record-store.ts +148 -88
- package/src/execution/round-settlement.ts +93 -0
- package/src/execution/session-file-gc.ts +96 -62
- package/src/execution/session-pending.ts +213 -62
- package/src/execution/session-reconstructor.ts +256 -143
- package/src/execution/sessions-index.ts +127 -102
- package/src/execution/settled-watchdog.ts +311 -0
- package/src/execution/subagent-actions-core.ts +686 -0
- package/src/execution/subagent-service.ts +1106 -907
- package/src/execution/subprocess-agent-runner.ts +144 -97
- package/src/execution/types.ts +9 -0
- package/src/execution/ui-request-handler-factory.ts +126 -75
- package/src/execution/ui-request-queue.ts +50 -19
- package/src/execution/worktree-git-ops.ts +397 -0
- package/src/execution/worktree-manager.ts +72 -10
- package/src/execution/worktree-registry.ts +6 -12
- package/src/index.ts +542 -9
- package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +42 -3
- package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +3 -5
- package/src/orchestration/__tests__/agent-call-stream.test.ts +2 -2
- package/src/orchestration/__tests__/args-meta.test.ts +358 -0
- package/src/orchestration/__tests__/args-validator.test.ts +14 -0
- package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +312 -0
- package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +384 -0
- package/src/orchestration/__tests__/execute-agent-call.test.ts +151 -11
- package/src/orchestration/__tests__/file-run-store-prune.test.ts +168 -0
- package/src/orchestration/__tests__/file-run-store-throttle.test.ts +168 -0
- package/src/orchestration/__tests__/file-run-store.test.ts +390 -0
- package/src/orchestration/__tests__/helpers/flush-microtasks.ts +13 -0
- package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +34 -0
- package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +357 -0
- package/src/orchestration/__tests__/lifecycle-recover-crashed.test.ts +289 -0
- package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +3 -3
- package/src/orchestration/__tests__/lifecycle.test.ts +84 -5
- package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +1 -1
- package/src/orchestration/__tests__/run-snapshot.test.ts +356 -0
- package/src/orchestration/__tests__/script-generate.test.ts +314 -0
- package/src/orchestration/__tests__/script-lint.test.ts +17 -0
- package/src/orchestration/__tests__/test-mocks.ts +2 -0
- package/src/orchestration/__tests__/worker-exit-without-result.test.ts +7 -6
- package/src/orchestration/__tests__/worker-message-pump-finalize-run.test.ts +229 -0
- package/src/orchestration/__tests__/{error-recovery-handlers.test.ts → worker-message-pump-handlers.test.ts} +28 -27
- package/src/orchestration/__tests__/{error-recovery-postmessage-defense.test.ts → worker-message-pump-postmessage-defense.test.ts} +8 -8
- package/src/orchestration/__tests__/{error-recovery-serialize-failed-result.test.ts → worker-message-pump-serialize-failed-result.test.ts} +2 -2
- package/src/orchestration/__tests__/{error-recovery-workflow-call.test.ts → worker-message-pump-workflow-call.test.ts} +1 -1
- package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +275 -0
- package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +78 -1
- package/src/orchestration/__tests__/worker-script-builder.test.ts +1 -1
- package/src/orchestration/__tests__/workflow-files.test.ts +186 -0
- package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +1 -1
- package/src/orchestration/__tests__/workflow-run-summary.test.ts +119 -0
- package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +124 -0
- package/src/orchestration/agent-opts-resolver.ts +8 -8
- package/src/orchestration/args-meta.ts +198 -0
- package/src/orchestration/args-validator.ts +53 -31
- package/src/orchestration/execute-agent-call.ts +25 -79
- package/src/orchestration/file-run-store.ts +327 -0
- package/src/orchestration/launcher.ts +160 -104
- package/src/orchestration/lifecycle.ts +371 -97
- package/src/orchestration/models/agent-call.ts +7 -7
- package/src/orchestration/models/budget.ts +5 -5
- package/src/orchestration/models/ports.ts +4 -4
- package/src/orchestration/models/run-runtime.ts +15 -15
- package/src/orchestration/models/trace.ts +9 -9
- package/src/orchestration/models/types.ts +111 -22
- package/src/orchestration/models/workflow-run.ts +28 -28
- package/src/orchestration/models/workflow-script.ts +4 -4
- package/src/orchestration/run-snapshot.ts +266 -0
- package/src/orchestration/script-generate.ts +154 -0
- package/src/orchestration/script-lint.ts +123 -90
- package/src/orchestration/worker-handle.ts +10 -10
- package/src/orchestration/worker-host.ts +10 -10
- package/src/orchestration/{error-recovery.ts → worker-message-pump.ts} +498 -221
- package/src/orchestration/worker-script-builder.ts +401 -346
- package/src/orchestration/workflow-files.ts +37 -11
- package/src/orchestration/workflow-run-summary.ts +69 -0
- package/src/orchestration/workflow-script-registry-impl.ts +31 -9
- package/src/shared/__tests__/agent-ref.test.ts +209 -2
- package/src/shared/__tests__/atomic-write.test.ts +267 -0
- package/src/shared/__tests__/bounded-serialize.test.ts +236 -0
- package/src/shared/__tests__/injection-render.test.ts +518 -0
- package/src/shared/__tests__/meta-parser.test.ts +151 -0
- package/src/shared/__tests__/resource-discovery-host-roots.test.ts +474 -0
- package/src/shared/__tests__/resource-discovery.test.ts +261 -217
- package/src/shared/agent-ref.ts +143 -2
- package/src/shared/atomic-write.ts +323 -0
- package/src/shared/bounded-serialize.ts +154 -0
- package/src/shared/injection-render.ts +279 -0
- package/src/shared/meta-parser.ts +182 -68
- package/src/shared/model-ref.ts +15 -2
- package/src/shared/resource-discovery.ts +97 -204
- package/src/shared/resource-meta.ts +14 -0
- package/src/shared/schema-jsonify.ts +1 -1
- package/src/shared/xml-injection.ts +9 -9
- package/workflows/README.md +9 -9
- package/workflows/chain.js +4 -2
- package/workflows/map-reduce.js +5 -3
- package/workflows/parallel.js +5 -3
- package/workflows/review-fix-loop-utils.cjs +165 -92
- package/workflows/review-fix-loop-utils.d.cts +287 -0
- package/workflows/review-fix-loop.js +18 -7
- package/workflows/scatter-gather.js +4 -2
- package/dist/chunk-3VOERJPJ.js +0 -22
- package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
- package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
- package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
- package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -580
- package/src/execution/engine/engines/zcode/launcher.ts +0 -161
- package/src/execution/execute-options-mapper.ts +0 -115
- package/src/execution/session-runner.ts +0 -1789
- package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -288
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* worker-message-pump handlers — handleWorkerExit/Error/ScriptError + postBudgetUpdate 测试。
|
|
3
3
|
*
|
|
4
|
-
* 参考
|
|
4
|
+
* 参考 worker-message-pump-workflow-call.test.ts 的 mock 构建。通过 vi.useFakeTimers() 跳过
|
|
5
5
|
* scheduleRebuild 的指数退避(1s/2s/4s)。
|
|
6
6
|
*
|
|
7
7
|
* 覆盖:
|
|
@@ -23,14 +23,15 @@ import {
|
|
|
23
23
|
handleWorkerMessage,
|
|
24
24
|
postBudgetUpdate,
|
|
25
25
|
rebuildRuntime,
|
|
26
|
-
} from "../
|
|
26
|
+
} from "../worker-message-pump.ts";
|
|
27
27
|
import { Budget } from "../models/budget.ts";
|
|
28
28
|
import { RunRuntime } from "../models/run-runtime.ts";
|
|
29
29
|
import { Trace } from "../models/trace.ts";
|
|
30
|
-
import type { AgentResult } from "../models/types.ts";
|
|
30
|
+
import type { AgentResult, DoneReason, RunStatus } from "../models/types.ts";
|
|
31
31
|
import { WorkflowRun } from "../models/workflow-run.ts";
|
|
32
32
|
import type { LifecycleDeps, WorkerHandlers } from "../models/ports.ts";
|
|
33
33
|
import type { WorkerHandle } from "../worker-handle.ts";
|
|
34
|
+
import { flushMicrotasks } from "./helpers/flush-microtasks.ts";
|
|
34
35
|
|
|
35
36
|
// ── helpers ──────────────────────────────────────────────────
|
|
36
37
|
|
|
@@ -70,26 +71,34 @@ function makeRunningRun(opts: {
|
|
|
70
71
|
receivedTerminalMessage: opts.receivedTerminalMessage,
|
|
71
72
|
},
|
|
72
73
|
// transition 副作用——run.state.status 由调用方通过 mock 控制后再次断言
|
|
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;
|
|
81
82
|
}
|
|
82
83
|
|
|
83
|
-
/** LifecycleDeps mock:store/workerHost/runner/eventBus/scheduleTimeBudget 可观察。
|
|
84
|
+
/** LifecycleDeps mock:store/workerHost/runner/eventBus/scheduleTimeBudget 可观察。
|
|
85
|
+
* mock 成员 = 真实签名 & vi.fn 能力(交叉纯 Mock 会丢真实签名,传回被测函数即报错)。 */
|
|
86
|
+
type MockLifecycleDeps = Omit<
|
|
87
|
+
LifecycleDeps,
|
|
88
|
+
"store" | "workerHost" | "runner" | "eventBus" | "onRunDone" | "log"
|
|
89
|
+
> & {
|
|
90
|
+
// 真实类型整体保留(RunStore/WorkerHost 等接口成员完整),仅 mock 方法交叉 vi.fn 能力
|
|
91
|
+
store: LifecycleDeps["store"] & { save: LifecycleDeps["store"]["save"] & ReturnType<typeof vi.fn> };
|
|
92
|
+
workerHost: LifecycleDeps["workerHost"] & { start: LifecycleDeps["workerHost"]["start"] & ReturnType<typeof vi.fn> };
|
|
93
|
+
runner: LifecycleDeps["runner"] & { run: LifecycleDeps["runner"]["run"] & ReturnType<typeof vi.fn> };
|
|
94
|
+
eventBus: NonNullable<LifecycleDeps["eventBus"]> & { emit: NonNullable<LifecycleDeps["eventBus"]>["emit"] & ReturnType<typeof vi.fn> };
|
|
95
|
+
onRunDone: LifecycleDeps["onRunDone"] & ReturnType<typeof vi.fn>;
|
|
96
|
+
log: LifecycleDeps["log"] & ReturnType<typeof vi.fn>;
|
|
97
|
+
};
|
|
98
|
+
|
|
84
99
|
function makeDeps(opts: {
|
|
85
100
|
scheduleTimeBudget?: LifecycleDeps["scheduleTimeBudget"];
|
|
86
|
-
} = {}):
|
|
87
|
-
store: { save: ReturnType<typeof vi.fn> };
|
|
88
|
-
workerHost: { start: ReturnType<typeof vi.fn> };
|
|
89
|
-
eventBus: { emit: ReturnType<typeof vi.fn> };
|
|
90
|
-
onRunDone: ReturnType<typeof vi.fn>;
|
|
91
|
-
log: ReturnType<typeof vi.fn>;
|
|
92
|
-
} {
|
|
101
|
+
} = {}): MockLifecycleDeps {
|
|
93
102
|
return {
|
|
94
103
|
store: { save: vi.fn(async () => {}) },
|
|
95
104
|
workerHost: { start: vi.fn(() => ({ postMessage: vi.fn() })) },
|
|
@@ -343,7 +352,7 @@ describe("rebuildRuntime", () => {
|
|
|
343
352
|
|
|
344
353
|
it("带 budgetTimeMs 时重排 scheduleTimeBudget 计时器", () => {
|
|
345
354
|
const run = makeRunningRun({ budgetTimeMs: 5000 });
|
|
346
|
-
const scheduleTimeBudget = vi.fn(() => undefined);
|
|
355
|
+
const scheduleTimeBudget = vi.fn((_runId: string, _budgetTimeMs: number) => undefined);
|
|
347
356
|
const deps = makeDeps({ scheduleTimeBudget });
|
|
348
357
|
|
|
349
358
|
rebuildRuntime(run, deps, makeHandlers());
|
|
@@ -412,7 +421,7 @@ describe("race-F3: rebuild 时间预算折算", () => {
|
|
|
412
421
|
const run = makeRunningRun({ budgetTimeMs: 5000 });
|
|
413
422
|
// 已耗 70%(3500ms)→ 剩余 1500ms;fake Date 冻结,elapsed 精确
|
|
414
423
|
run.meta.startedAt = new Date(Date.now() - 3500).toISOString();
|
|
415
|
-
const scheduleTimeBudget = vi.fn(() => undefined);
|
|
424
|
+
const scheduleTimeBudget = vi.fn((_runId: string, _budgetTimeMs: number) => undefined);
|
|
416
425
|
const deps = makeDeps({ scheduleTimeBudget });
|
|
417
426
|
|
|
418
427
|
rebuildRuntime(run, deps, makeHandlers());
|
|
@@ -428,7 +437,7 @@ describe("race-F3: rebuild 时间预算折算", () => {
|
|
|
428
437
|
const run = makeRunningRun({ budgetTimeMs: 5000 });
|
|
429
438
|
// 已耗 6000ms > 预算 5000ms(退避 advance 1000ms 后已耗 7000ms,仍耗尽)
|
|
430
439
|
run.meta.startedAt = new Date(Date.now() - 6000).toISOString();
|
|
431
|
-
const scheduleTimeBudget = vi.fn(() => undefined);
|
|
440
|
+
const scheduleTimeBudget = vi.fn((_runId: string, _budgetTimeMs: number) => undefined);
|
|
432
441
|
const deps = makeDeps({ scheduleTimeBudget });
|
|
433
442
|
|
|
434
443
|
const p = handleScriptError(run, "boom", [], deps, makeHandlers());
|
|
@@ -451,7 +460,7 @@ describe("race-F3: rebuild 时间预算折算", () => {
|
|
|
451
460
|
|
|
452
461
|
it("预算未耗尽(fresh run)→ 照常 rebuild,不误转 time_limited(防误伤回归)", async () => {
|
|
453
462
|
const run = makeRunningRun({ budgetTimeMs: 5000 }); // startedAt ≈ now,remaining 满
|
|
454
|
-
const scheduleTimeBudget = vi.fn(() => undefined);
|
|
463
|
+
const scheduleTimeBudget = vi.fn((_runId: string, _budgetTimeMs: number) => undefined);
|
|
455
464
|
const deps = makeDeps({ scheduleTimeBudget });
|
|
456
465
|
|
|
457
466
|
const p = handleScriptError(run, "boom", [], deps, makeHandlers());
|
|
@@ -481,14 +490,6 @@ function createDeferred<T>(): Deferred<T> {
|
|
|
481
490
|
return { promise, resolve, reject };
|
|
482
491
|
}
|
|
483
492
|
|
|
484
|
-
/** flush 微任务队列(dispatch 的 fire-and-forget promise 链推进到稳定态)。 */
|
|
485
|
-
async function flushMicrotasks(ticks = 10): Promise<void> {
|
|
486
|
-
for (let i = 0; i < ticks; i++) {
|
|
487
|
-
// eslint-disable-next-line no-await-in-loop -- 排空微任务队列的固定 tick 循环(fire-and-forget promise 链推进到稳定态),非逐项等待
|
|
488
|
-
await Promise.resolve();
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
|
|
492
493
|
/** 构造真实 WorkflowRun(真实状态机/replaceRuntime/Trace/Budget)+ 初始 RunRuntime。 */
|
|
493
494
|
function makeRealRun(runId: string, opts: { budgetTimeMs?: number } = {}): WorkflowRun {
|
|
494
495
|
const run = new WorkflowRun(
|
|
@@ -752,7 +753,7 @@ describe("rebuildRuntime 可观察性(OB3 日志点)", () => {
|
|
|
752
753
|
|
|
753
754
|
it("U7: 带 budgetTimeMs + scheduleTimeBudget 注入时含 L2(在 L1 之后、L3 之前,payload 含 budgetTimeMs)", () => {
|
|
754
755
|
const run = makeRealRun("wf-rebuild-log-2", { budgetTimeMs: 5000 });
|
|
755
|
-
const scheduleTimeBudget = vi.fn(() => undefined);
|
|
756
|
+
const scheduleTimeBudget = vi.fn((_runId: string, _budgetTimeMs: number) => undefined);
|
|
756
757
|
const deps = makeDeps({ scheduleTimeBudget });
|
|
757
758
|
|
|
758
759
|
rebuildRuntime(run, deps, makeHandlers());
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// src/orchestration/__tests__/
|
|
1
|
+
// src/orchestration/__tests__/worker-message-pump-postmessage-defense.test.ts
|
|
2
2
|
//
|
|
3
3
|
// W2: 主线程层 postMessage 防御测试。
|
|
4
4
|
//
|
|
@@ -35,13 +35,13 @@ vi.mock("../../core/logger.ts", () => ({
|
|
|
35
35
|
getLogger: () => loggerMock,
|
|
36
36
|
}));
|
|
37
37
|
|
|
38
|
-
import { handleWorkerMessage, postBudgetUpdate } from "../
|
|
38
|
+
import { handleWorkerMessage, postBudgetUpdate } from "../worker-message-pump.ts";
|
|
39
39
|
import type { LifecycleDeps, WorkerHandlers } from "../models/ports.ts";
|
|
40
40
|
import type { WorkflowRun } from "../models/workflow-run.ts";
|
|
41
41
|
|
|
42
42
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
43
|
-
const
|
|
44
|
-
join(__dirname, "../
|
|
43
|
+
const WORKER_MESSAGE_PUMP_SRC = readFileSync(
|
|
44
|
+
join(__dirname, "../worker-message-pump.ts"),
|
|
45
45
|
"utf-8",
|
|
46
46
|
);
|
|
47
47
|
|
|
@@ -273,7 +273,7 @@ describe("W2b: dispatchWorkflowCall postResult 防御 DataCloneError", () => {
|
|
|
273
273
|
// 子进程)。利用这一点构造行为测试——往 cached.result 里塞不可克隆值(function),
|
|
274
274
|
// mock postMessage 在首次发送 agent-result 时抛 DataCloneError,验证 fallback 路径。
|
|
275
275
|
//
|
|
276
|
-
// 另外补充:验证
|
|
276
|
+
// 另外补充:验证 worker-message-pump.ts 源码中三处 postMessage 调用点都有 try/catch 包裹,
|
|
277
277
|
// 防止未来重构误删防御(类似 worker-script-builder.test.ts 的源码字符串断言模式)。
|
|
278
278
|
|
|
279
279
|
/** 构造 status="running" 且 calls 已含一个 done 结果(含不可克隆成员)的 mock run。
|
|
@@ -376,7 +376,7 @@ describe("W2c: postAgentResult 防御(源码结构断言)", () => {
|
|
|
376
376
|
}
|
|
377
377
|
|
|
378
378
|
it("postAgentResult 包含 try/catch + fallback result", () => {
|
|
379
|
-
const match =
|
|
379
|
+
const match = WORKER_MESSAGE_PUMP_SRC.match(/function postAgentResult\([\s\S]*?\n\}/);
|
|
380
380
|
expect(match, "postAgentResult 函数定义应存在").toBeTruthy();
|
|
381
381
|
expect(hasTryCatchAroundPostMessage(match![0])).toBe(true);
|
|
382
382
|
// fallback 通过共享工厂 makeSerializeFailedResult 构造纯字符串 result(必可克隆)
|
|
@@ -387,14 +387,14 @@ describe("W2c: postAgentResult 防御(源码结构断言)", () => {
|
|
|
387
387
|
});
|
|
388
388
|
|
|
389
389
|
it("postBudgetUpdate 包含 try/catch", () => {
|
|
390
|
-
const match =
|
|
390
|
+
const match = WORKER_MESSAGE_PUMP_SRC.match(/export function postBudgetUpdate\([\s\S]*?\n\}/);
|
|
391
391
|
expect(match, "postBudgetUpdate 函数定义应存在").toBeTruthy();
|
|
392
392
|
expect(hasTryCatchAroundPostMessage(match![0])).toBe(true);
|
|
393
393
|
});
|
|
394
394
|
|
|
395
395
|
it("dispatchWorkflowCall postResult 闭包包含 try/catch + fallback", () => {
|
|
396
396
|
// postResult 是 const 闭包,匹配到下一个 }; 结尾
|
|
397
|
-
const match =
|
|
397
|
+
const match = WORKER_MESSAGE_PUMP_SRC.match(/const postResult = \(result[\s\S]*?\n \};/);
|
|
398
398
|
expect(match, "postResult 闭包定义应存在").toBeTruthy();
|
|
399
399
|
expect(hasTryCatchAroundPostMessage(match![0])).toBe(true);
|
|
400
400
|
expect(match![0]).toContain("Workflow result serialization failed");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// src/orchestration/__tests__/
|
|
1
|
+
// src/orchestration/__tests__/worker-message-pump-serialize-failed-result.test.ts
|
|
2
2
|
//
|
|
3
3
|
// makeSerializeFailedResult 独立单测(W2 防御关键纯函数)。
|
|
4
4
|
//
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import { describe, expect, it } from "vitest";
|
|
17
17
|
|
|
18
|
-
import { makeSerializeFailedResult } from "../
|
|
18
|
+
import { makeSerializeFailedResult } from "../worker-message-pump.ts";
|
|
19
19
|
|
|
20
20
|
describe("makeSerializeFailedResult", () => {
|
|
21
21
|
it("返回 {content:'', error:'<prefix>: <errMsg>'} 形状(必可克隆 fallback)", () => {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { describe, expect, it, vi } from "vitest";
|
|
12
12
|
|
|
13
|
-
import { handleWorkerMessage } from "../
|
|
13
|
+
import { handleWorkerMessage } from "../worker-message-pump.ts";
|
|
14
14
|
import type { LifecycleDeps, WorkerHandlers } from "../models/ports.ts";
|
|
15
15
|
import type { WorkflowRun } from "../models/workflow-run.ts";
|
|
16
16
|
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [OR-3] worker 侧 pending per-call timeoutMs + abort 广播 测试(真实 Worker 线程,P-T3)。
|
|
3
|
+
*
|
|
4
|
+
* 覆盖(unbounded-wait-audit §4.1 OR-3 / §7.2 T3③ / §7.3 P-T3):
|
|
5
|
+
* - agent() 传 timeoutMs:主线程不回话 → pending 以错误 resolve(不 reject、不放大成
|
|
6
|
+
* 脚本 error)+ 超时 warn 记入 _workerLogs——消息层自己的超时,旧实现零超时永挂
|
|
7
|
+
* - returnMeta 模式超时 resolve {value:"", error}
|
|
8
|
+
* - 主线程不传 timeoutMs(缺省不限):pending 不挂 timer(行为不变)
|
|
9
|
+
* - abort 广播:await 中的 agent() 以 WorkflowAbortedError reject(脚本 catch 可感知
|
|
10
|
+
* 取消)→ 脚本自然收尾 → worker 自然退出(exit 0)
|
|
11
|
+
* - P-T3:abort 广播与 worker 自然退出交错不产 unhandledRejection——广播后立即
|
|
12
|
+
* terminate 的竞态、fire-and-forget 后 return 的自然退出,均不产生主线程
|
|
13
|
+
* unhandledRejection 与 worker 'error' 事件
|
|
14
|
+
*/
|
|
15
|
+
import { Worker } from "node:worker_threads";
|
|
16
|
+
|
|
17
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
18
|
+
|
|
19
|
+
import { buildWorkerScript } from "../worker-script-builder.ts";
|
|
20
|
+
|
|
21
|
+
// ── 判别联合(对齐 worker-script-builder-runtime.test.ts 的守卫形态) ──
|
|
22
|
+
|
|
23
|
+
interface ReturnMsg {
|
|
24
|
+
type: "return";
|
|
25
|
+
result: unknown;
|
|
26
|
+
workerLogs?: Array<{ level: string; message: string }>;
|
|
27
|
+
}
|
|
28
|
+
interface ErrorMsg {
|
|
29
|
+
type: "error";
|
|
30
|
+
error: string;
|
|
31
|
+
workerLogs?: Array<{ level: string; message: string }>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isReturn(m: unknown): m is ReturnMsg {
|
|
35
|
+
return typeof m === "object" && m !== null && (m as { type?: unknown }).type === "return";
|
|
36
|
+
}
|
|
37
|
+
function isError(m: unknown): m is ErrorMsg {
|
|
38
|
+
return typeof m === "object" && m !== null && (m as { type?: unknown }).type === "error";
|
|
39
|
+
}
|
|
40
|
+
function isAgentCall(m: unknown): m is { type: "agent-call"; callId: number } {
|
|
41
|
+
return typeof m === "object" && m !== null && (m as { type?: unknown }).type === "agent-call";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ── harness:主线程「不回话」的可控 Worker 运行器 ──────────────
|
|
45
|
+
|
|
46
|
+
interface RunResult {
|
|
47
|
+
returnValue?: unknown;
|
|
48
|
+
errorMessage?: string;
|
|
49
|
+
/** return/error 消息带回的 workerLogs(超时 warn 通路)。 */
|
|
50
|
+
workerLogs: Array<{ level: string; message: string }>;
|
|
51
|
+
exitCode?: number;
|
|
52
|
+
workerError?: string;
|
|
53
|
+
/** 收到的 agent-call 消息数(确认脚本确实发起了调用)。 */
|
|
54
|
+
agentCallCount: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface RunOptions {
|
|
58
|
+
/** 收到 agent-call 后的主线程动作(默认:不回话——pending 留在 worker 侧)。 */
|
|
59
|
+
onAgentCall?: (msg: { callId: number; opts: Record<string, unknown> }, worker: Worker) => void;
|
|
60
|
+
/** 显式 finish(供 abort 竞态场景手工收尾)。 */
|
|
61
|
+
timeoutMs?: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const createdWorkers: Worker[] = [];
|
|
65
|
+
|
|
66
|
+
afterEach(() => {
|
|
67
|
+
for (const w of createdWorkers.splice(0)) {
|
|
68
|
+
w.terminate().catch(() => {});
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
function runWorker(userScript: string, opts: RunOptions = {}): Promise<RunResult> {
|
|
73
|
+
const timeoutMs = opts.timeoutMs ?? (process.env.CI ? 5000 : 2000);
|
|
74
|
+
return new Promise((resolve) => {
|
|
75
|
+
const worker = new Worker(buildWorkerScript(userScript), {
|
|
76
|
+
eval: true,
|
|
77
|
+
workerData: {
|
|
78
|
+
scriptPath: "test.js",
|
|
79
|
+
args: {},
|
|
80
|
+
workspace: process.cwd(),
|
|
81
|
+
budget: { maxTokens: 0, usedTokens: 0, usedCost: 0 },
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
createdWorkers.push(worker);
|
|
85
|
+
|
|
86
|
+
const result: RunResult = { workerLogs: [], agentCallCount: 0 };
|
|
87
|
+
let settled = false;
|
|
88
|
+
const finish = (): void => {
|
|
89
|
+
if (settled) return;
|
|
90
|
+
settled = true;
|
|
91
|
+
clearTimeout(timer);
|
|
92
|
+
clearTimeout(graceTimer);
|
|
93
|
+
resolve(result);
|
|
94
|
+
};
|
|
95
|
+
// 兜底:永不 settle 时 terminate + 以当前观察收尾
|
|
96
|
+
const timer = setTimeout(() => {
|
|
97
|
+
worker.terminate().catch(() => {});
|
|
98
|
+
finish();
|
|
99
|
+
}, timeoutMs);
|
|
100
|
+
// 收到终态消息后的宽限窗:给潜在 worker 'error' 事件一个到达机会,
|
|
101
|
+
// 然后收尾(parentPort 挂着 message listener 时 worker 线程不自然退出,
|
|
102
|
+
// 与生产一致由 terminate 回收——故不依赖 exit 事件断言)。
|
|
103
|
+
let graceTimer: ReturnType<typeof setTimeout>;
|
|
104
|
+
|
|
105
|
+
worker.on("message", (raw: unknown) => {
|
|
106
|
+
if (isAgentCall(raw)) {
|
|
107
|
+
result.agentCallCount += 1;
|
|
108
|
+
opts.onAgentCall?.(raw as unknown as { callId: number; opts: Record<string, unknown> }, worker);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (isReturn(raw)) {
|
|
112
|
+
result.returnValue = raw.result;
|
|
113
|
+
result.workerLogs = raw.workerLogs ?? [];
|
|
114
|
+
graceTimer = setTimeout(finish, 150);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (isError(raw)) {
|
|
118
|
+
result.errorMessage = raw.error;
|
|
119
|
+
result.workerLogs = raw.workerLogs ?? [];
|
|
120
|
+
graceTimer = setTimeout(finish, 150);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
worker.on("error", (err: Error) => {
|
|
124
|
+
result.workerError = err.message;
|
|
125
|
+
});
|
|
126
|
+
worker.on("exit", (code: number) => {
|
|
127
|
+
result.exitCode = code;
|
|
128
|
+
finish();
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** 收集本测试进程的 unhandledRejection(P-T3 断言)。 */
|
|
134
|
+
function trackUnhandledRejections(): { reasons: unknown[]; dispose: () => void } {
|
|
135
|
+
const reasons: unknown[] = [];
|
|
136
|
+
const handler = (reason: unknown): void => {
|
|
137
|
+
reasons.push(reason);
|
|
138
|
+
};
|
|
139
|
+
process.on("unhandledRejection", handler);
|
|
140
|
+
return { reasons, dispose: () => process.off("unhandledRejection", handler) };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ── [OR-3] per-call timeoutMs(消息层自己的超时) ─────────────
|
|
144
|
+
|
|
145
|
+
describe("[OR-3] agent() pending per-call timeoutMs", () => {
|
|
146
|
+
it("主线程不回话 + timeoutMs=50 → pending 以错误 resolve([B-5] 单值错误消息字符串)", async () => {
|
|
147
|
+
const script = `
|
|
148
|
+
const r = await agent("never answered", { timeoutMs: 50 });
|
|
149
|
+
return { got: r };
|
|
150
|
+
`;
|
|
151
|
+
const res = await runWorker(script);
|
|
152
|
+
|
|
153
|
+
// 脚本正常 return(超时未放大成脚本 error → 不触发 rebuild 矩阵)
|
|
154
|
+
expect(res.errorMessage).toBeUndefined();
|
|
155
|
+
expect(res.workerError).toBeUndefined();
|
|
156
|
+
expect(res.agentCallCount).toBe(1);
|
|
157
|
+
// [B-5] 非 returnMeta 超时 resolve 单值字符串(对齐 agent-result 失败路径
|
|
158
|
+
// resolve _value = parsedOutput ?? content 的形态)——字符串消费型脚本
|
|
159
|
+
// (r.trim() 类)不再 TypeError
|
|
160
|
+
const got = (res.returnValue as { got: string }).got;
|
|
161
|
+
expect(typeof got).toBe("string");
|
|
162
|
+
expect(got).toContain("timed out after 50ms");
|
|
163
|
+
expect(got).toContain("no agent-result received");
|
|
164
|
+
// 超时 warn 记入 _workerLogs(诊断可见)
|
|
165
|
+
expect(res.workerLogs.some((l) => l.level === "warn" && l.message.includes("timed out after 50ms"))).toBe(true);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("returnMeta 模式超时(对象分支透传 returnMeta)→ resolve {value:\"\", error}", async () => {
|
|
169
|
+
const script = `
|
|
170
|
+
const r = await agent({ prompt: "never answered", timeoutMs: 50, returnMeta: true });
|
|
171
|
+
return r;
|
|
172
|
+
`;
|
|
173
|
+
const res = await runWorker(script);
|
|
174
|
+
|
|
175
|
+
expect(res.errorMessage).toBeUndefined();
|
|
176
|
+
expect(res.returnValue).toEqual({ value: "", error: expect.stringContaining("timed out after 50ms") });
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("[B-5] 字符串消费型脚本(r.trim())在超时路径不 TypeError(resolve 单值字符串)", async () => {
|
|
180
|
+
// 旧实现超时 resolve {content:"", error} 对象 → r.trim() 抛 TypeError →
|
|
181
|
+
// 脚本 error → 放大成 rebuild;修复后 resolve 字符串,脚本按普通失败文本消费
|
|
182
|
+
const script = `
|
|
183
|
+
const r = await agent("never answered", { timeoutMs: 40 });
|
|
184
|
+
return { trimmed: r.trim().length > 0 };
|
|
185
|
+
`;
|
|
186
|
+
const res = await runWorker(script);
|
|
187
|
+
|
|
188
|
+
expect(res.errorMessage).toBeUndefined();
|
|
189
|
+
expect(res.workerError).toBeUndefined();
|
|
190
|
+
expect(res.returnValue).toEqual({ trimmed: true });
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("string+secondArg 分支:timeoutMs 透传到 agent-call 消息 opts(此前该分支丢弃该字段)", async () => {
|
|
194
|
+
let seenOpts: Record<string, unknown> | undefined;
|
|
195
|
+
const script = `await agent("with timeout", { timeoutMs: 1234, label: "probe" }); return "unreachable";`;
|
|
196
|
+
await runWorker(script, {
|
|
197
|
+
onAgentCall: (msg, worker) => {
|
|
198
|
+
seenOpts = msg.opts;
|
|
199
|
+
// 回发结果让脚本收尾(透传断言与挂起行为解耦)
|
|
200
|
+
worker.postMessage({ type: "agent-result", callId: msg.callId, result: { content: "ok" }, cached: false });
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
expect(seenOpts?.timeoutMs).toBe(1234);
|
|
205
|
+
expect(seenOpts?.prompt).toBe("with timeout");
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// ── [OR-3] abort 广播(worker 侧接收) ───────────────────────
|
|
210
|
+
|
|
211
|
+
describe("[OR-3] abort 广播接收(P-T3:交错不产 unhandledRejection)", () => {
|
|
212
|
+
it("await 中的 agent() 收到 abort → WorkflowAbortedError → 脚本 catch 后自然 return → exit 0", async () => {
|
|
213
|
+
const script = `
|
|
214
|
+
try {
|
|
215
|
+
await agent("will be aborted");
|
|
216
|
+
return "should not reach";
|
|
217
|
+
} catch (e) {
|
|
218
|
+
return { caught: e.name, reason: e.reason };
|
|
219
|
+
}
|
|
220
|
+
`;
|
|
221
|
+
const res = await runWorker(script, {
|
|
222
|
+
onAgentCall: (msg, worker) => {
|
|
223
|
+
worker.postMessage({ type: "abort", reason: "run aborted by user" });
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
expect(res.workerError).toBeUndefined();
|
|
228
|
+
expect(res.errorMessage).toBeUndefined();
|
|
229
|
+
expect(res.returnValue).toEqual({ caught: "WorkflowAbortedError", reason: "run aborted by user" });
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it("广播后立即 terminate(竞态):无 worker error、无 unhandledRejection", async () => {
|
|
233
|
+
const tracked = trackUnhandledRejections();
|
|
234
|
+
try {
|
|
235
|
+
const script = `return await agent("race with terminate");`;
|
|
236
|
+
const res = await runWorker(script, {
|
|
237
|
+
onAgentCall: (msg, worker) => {
|
|
238
|
+
// 广播与 terminate 同 tick 发出——模拟 abortRun 的广播→transition(terminate) 竞态
|
|
239
|
+
worker.postMessage({ type: "abort", reason: "race" });
|
|
240
|
+
void worker.terminate();
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
// 无论脚本是否来得及收尾:主线程侧无 error 事件、进程无 unhandledRejection
|
|
244
|
+
expect(res.workerError).toBeUndefined();
|
|
245
|
+
// 给潜在异步 unhandledRejection 一个窗口
|
|
246
|
+
await new Promise((r) => { setTimeout(r, 20); });
|
|
247
|
+
expect(tracked.reasons).toEqual([]);
|
|
248
|
+
} finally {
|
|
249
|
+
tracked.dispose();
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it("fire-and-forget agent() 后脚本 return:无 worker error(超时/abort 通路外的孤儿 pending 不产 rejection)", async () => {
|
|
254
|
+
const script = `
|
|
255
|
+
agent("fire and forget");
|
|
256
|
+
return "returned early";
|
|
257
|
+
`;
|
|
258
|
+
const res = await runWorker(script, { timeoutMs: 400 });
|
|
259
|
+
|
|
260
|
+
expect(res.returnValue).toBe("returned early");
|
|
261
|
+
expect(res.workerError).toBeUndefined();
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it("fire-and-forget + timeoutMs:超时 resolve(非 reject)→ 无 worker error 事件", async () => {
|
|
265
|
+
const script = `
|
|
266
|
+
agent("orphan with timeout", { timeoutMs: 40 });
|
|
267
|
+
return "returned before timeout";
|
|
268
|
+
`;
|
|
269
|
+
const res = await runWorker(script, { timeoutMs: 600 });
|
|
270
|
+
|
|
271
|
+
// 超时以 resolve 收口(容错策略)——即使无人消费该 promise 也不产 rejection
|
|
272
|
+
expect(res.returnValue).toBe("returned before timeout");
|
|
273
|
+
expect(res.workerError).toBeUndefined();
|
|
274
|
+
});
|
|
275
|
+
});
|
|
@@ -53,6 +53,12 @@ interface ErrorMsg {
|
|
|
53
53
|
error: string;
|
|
54
54
|
workerLogs?: unknown[];
|
|
55
55
|
}
|
|
56
|
+
/** log 消息:脚本 log() 全局发出的独立诊断消息(协议见 worker-script-builder 头注释)。 */
|
|
57
|
+
interface LogMsg {
|
|
58
|
+
type: "log";
|
|
59
|
+
phase?: string;
|
|
60
|
+
message?: string;
|
|
61
|
+
}
|
|
56
62
|
|
|
57
63
|
// ── 类型守卫:从 unknown 收窄到判别联合 ──
|
|
58
64
|
// 共享 hasType 辅助:避免每个守卫重复 `(m as {type?:string})` 断言(taste/no-unsafe-catch)。
|
|
@@ -74,16 +80,23 @@ function isReturn(m: unknown): m is ReturnMsg {
|
|
|
74
80
|
function isError(m: unknown): m is ErrorMsg {
|
|
75
81
|
return hasType(m, "error");
|
|
76
82
|
}
|
|
83
|
+
function isLog(m: unknown): m is LogMsg {
|
|
84
|
+
return hasType(m, "log");
|
|
85
|
+
}
|
|
77
86
|
|
|
78
87
|
// ── 测试辅助:起一个真实 Worker 跑 buildWorkerScript 产物 ──────────────
|
|
79
88
|
|
|
80
89
|
interface RunResult {
|
|
81
90
|
/** 收到的 return 消息的 result 字段(脚本正常结束时)。 */
|
|
82
91
|
returnValue?: unknown;
|
|
92
|
+
/** return 消息带回的 workerLogs([B-3] 起只含 console.* 捕获条目,log() 不再走此通路)。 */
|
|
93
|
+
returnWorkerLogs?: unknown[];
|
|
83
94
|
/** 收到的 error 消息的 error 字段(脚本 throw 时)。 */
|
|
84
95
|
errorMessage?: string;
|
|
85
96
|
/** error 消息带回的 workerLogs(验证诊断不丢)。 */
|
|
86
97
|
errorWorkerLogs?: unknown[];
|
|
98
|
+
/** 收到的 {type:"log"} 独立消息列表([B-3] 起 log() 的唯一通路)。 */
|
|
99
|
+
logMessages: LogMsg[];
|
|
87
100
|
/** Worker exit code(0=正常,1=崩溃)。 */
|
|
88
101
|
exitCode?: number;
|
|
89
102
|
/** Worker 'error' 事件的错误消息(uncaught exception,正常应为 undefined)。 */
|
|
@@ -94,6 +107,15 @@ interface RunResult {
|
|
|
94
107
|
workflowCalls: WorkflowCallMsg[];
|
|
95
108
|
}
|
|
96
109
|
|
|
110
|
+
/** workerLogs 条目守卫收窄 + message 包含判定(元素类型 unknown,断言前先收窄)。 */
|
|
111
|
+
function someLogMessageContains(logs: unknown[] | undefined, text: string): boolean {
|
|
112
|
+
return (logs ?? []).some((l) => {
|
|
113
|
+
if (typeof l !== "object" || l === null) return false;
|
|
114
|
+
const message = (l as { message?: unknown }).message;
|
|
115
|
+
return typeof message === "string" && message.includes(text);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
97
119
|
interface RunOptions {
|
|
98
120
|
/** $ARGS。 */
|
|
99
121
|
args?: Record<string, unknown>;
|
|
@@ -144,7 +166,7 @@ function runWorker(userScript: string, opts: RunOptions = {}): Promise<RunResult
|
|
|
144
166
|
// S8:创建后立即登记,afterEach 兜底清理(防止 promise 泄漏导致 Worker 未终止)
|
|
145
167
|
createdWorkers.push(worker);
|
|
146
168
|
|
|
147
|
-
const result: RunResult = { agentCalls: [], workflowCalls: [] };
|
|
169
|
+
const result: RunResult = { agentCalls: [], workflowCalls: [], logMessages: [] };
|
|
148
170
|
let agentCallIdx = 0;
|
|
149
171
|
let resolved = false;
|
|
150
172
|
const timer = setTimeout(() => {
|
|
@@ -181,8 +203,13 @@ function runWorker(userScript: string, opts: RunOptions = {}): Promise<RunResult
|
|
|
181
203
|
result.workflowCalls.push(raw);
|
|
182
204
|
const wfResult = opts.handleWorkflowCall ? opts.handleWorkflowCall(raw) : { ok: true };
|
|
183
205
|
worker.postMessage({ type: "workflow-result", callId: raw.callId, result: wfResult });
|
|
206
|
+
} else if (isLog(raw)) {
|
|
207
|
+
// [OR-6/T7④] 独立 log 消息收集(主线程真实 runtime 当前无消费 case,测试
|
|
208
|
+
// harness 收集以断言「协议消息仍发出」通路不被回退)。
|
|
209
|
+
result.logMessages.push(raw);
|
|
184
210
|
} else if (isReturn(raw)) {
|
|
185
211
|
result.returnValue = raw.result;
|
|
212
|
+
result.returnWorkerLogs = raw.workerLogs;
|
|
186
213
|
finish(result);
|
|
187
214
|
} else if (isError(raw)) {
|
|
188
215
|
result.errorMessage = raw.error;
|
|
@@ -382,6 +409,56 @@ describe("buildWorkerScript runtime — 之前缺失的路径覆盖", () => {
|
|
|
382
409
|
});
|
|
383
410
|
});
|
|
384
411
|
|
|
412
|
+
// ── [B-3] log() 单通路可观测性 ─────────────────────────────────────
|
|
413
|
+
// 旧双通路(log() 同时 post 独立消息 + 记入 _workerLogs 随 return/error 带回)让
|
|
414
|
+
// 同一日志在主线程 errorLogs 占两格、TUI 双份。修复后 log() 只发独立 {type:"log"}
|
|
415
|
+
// 消息,主线程 handleWorkerLog 即时消费(error-recovery.ts);崩溃场景丢 log 条目
|
|
416
|
+
// 可接受(log 是 T7 补充可观测)。
|
|
417
|
+
|
|
418
|
+
describe("buildWorkerScript runtime — [B-3] log() 单通路", () => {
|
|
419
|
+
it("log() 发出独立 {type:\"log\"} 消息且携带 phase(主线程即时消费的唯一通路)", async () => {
|
|
420
|
+
const script = `
|
|
421
|
+
phase("build");
|
|
422
|
+
log("hello from script");
|
|
423
|
+
return { ok: true };
|
|
424
|
+
`;
|
|
425
|
+
const res = await runWorker(script);
|
|
426
|
+
expect(res.workerError).toBeUndefined();
|
|
427
|
+
expect(res.logMessages).toEqual([{ type: "log", phase: "build", message: "hello from script" }]);
|
|
428
|
+
expect(res.exitCode).not.toBe(1);
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
it("log() 内容不再随 return 消息的 workerLogs 带回(双份入账已退役)", async () => {
|
|
432
|
+
const script = `
|
|
433
|
+
phase("build");
|
|
434
|
+
log("hello from script");
|
|
435
|
+
return { ok: true };
|
|
436
|
+
`;
|
|
437
|
+
const res = await runWorker(script);
|
|
438
|
+
expect(res.workerError).toBeUndefined();
|
|
439
|
+
// workerLogs 中不得出现 log() 的条目(否则主线程 log case + workerLogs 双份入账)
|
|
440
|
+
expect(someLogMessageContains(res.returnWorkerLogs, "hello from script")).toBe(false);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it("log() 内容不再随 error 消息的 workerLogs 带回(console.* 捕获通路不受影响)", async () => {
|
|
444
|
+
const script = `
|
|
445
|
+
log("before-crash");
|
|
446
|
+
console.error("crash-context");
|
|
447
|
+
throw new Error("boom");
|
|
448
|
+
`;
|
|
449
|
+
const res = await runWorker(script);
|
|
450
|
+
expect(res.errorMessage).toBe("boom");
|
|
451
|
+
// log() 的条目不在 error workerLogs(单通路)
|
|
452
|
+
expect(someLogMessageContains(res.errorWorkerLogs, "before-crash")).toBe(false);
|
|
453
|
+
// console.* 捕获通路保持(workerLogs 仍带回 console.error 条目)
|
|
454
|
+
expect(res.errorWorkerLogs).toEqual(
|
|
455
|
+
expect.arrayContaining([
|
|
456
|
+
expect.objectContaining({ level: "error", message: "crash-context" }),
|
|
457
|
+
]),
|
|
458
|
+
);
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
|
|
385
462
|
// ── W2: agent() returnMeta 模式运行时验证 ───────────────────────────
|
|
386
463
|
// 现有 worker-script-builder.test.ts 的 16 条 returnMeta 断言全是字符串 toContain,
|
|
387
464
|
// 无法验证「真实 Worker 线程执行时 agent({returnMeta:true}) resolve 出对象、
|
|
@@ -225,7 +225,7 @@ describe("buildWorkerScript — W2 agent() returnMeta mode", () => {
|
|
|
225
225
|
// ── thinkingLevel 参数透传(agent() 三分支) ──
|
|
226
226
|
// 验证 thinkingLevel 在 string / task-agent / object.prompt 三个分支均被透传至
|
|
227
227
|
// agent-call 的 opts,且加入 _knownFields 白名单(object.prompt 分支整体透传,
|
|
228
|
-
// 不被 unknown-fields warn 误报)。底层 AgentCallOpts
|
|
228
|
+
// 不被 unknown-fields warn 误报)。底层 AgentCallOpts(D6 合流后 EnginePort 直传,
|
|
229
229
|
// buildSpawnArgs 拼 pi CLI --model provider/modelId:thinkingLevel 已打通,
|
|
230
230
|
// 此处只验入口层 wiring。
|
|
231
231
|
|