@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhushanwen/subagent-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Cross-host shared subagent execution layer and workflow orchestration core (dual-form npm package: TS source for workspace, dist ESM+CJS for npm)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -72,17 +72,19 @@
|
|
|
72
72
|
"default": "./workflows/*"
|
|
73
73
|
}
|
|
74
74
|
},
|
|
75
|
-
"$comment": "D5 公共 API 面定稿(docs/design/subagent-core-package-extraction.md §3.3 D5):公共面 = 主入口 barrel + 4 条语义子入口(./engines/zcode/reader、./engines/zcode/constants、./engine/paths、./relay-env)+ ./workflows/* 资产子入口。exports 面 = semver 契约,收窄不放宽。双形态(D4):workspace TS 消费走 import 条件 -> src(与 extension-protocol 先例同型);npm 消费走 publishConfig 发布面(npm publish 时整体替换 exports):import -> dist ESM、require -> dist CJS。require 条件指向 dist CJS 是本仓首例,D4 依据:zsw 宿主为纯 CJS(node>=20),而 core 依赖的 @xyz-agent/extension-protocol npm dist 仅 ESM(.mjs),node 20 下 require() 加载 ESM 不可靠——因此对 extension-protocol 设 noExternal 防御性 bundle 边界(当前 entry 闭包无其运行时引用、dist 实测零常量命中;未来 entry 一旦引入运行时引用即 bundle),绝不让外部 ESM 依赖出现在 require 链上;require 分支的 types 用 .d.cts 对应。开发态 require 条件即指向 dist:dist 是 require 语义的真实载体,未 build 时 require 失败属预期(build
|
|
75
|
+
"$comment": "D5 公共 API 面定稿(docs/design/subagent-core-package-extraction.md §3.3 D5):公共面 = 主入口 barrel + 4 条语义子入口(./engines/zcode/reader、./engines/zcode/constants、./engine/paths、./relay-env)+ ./workflows/* 资产子入口。exports 面 = semver 契约,收窄不放宽。双形态(D4):workspace TS 消费走 import 条件 -> src(与 extension-protocol 先例同型);npm 消费走 publishConfig 发布面(npm publish 时整体替换 exports):import -> dist ESM、require -> dist CJS。require 条件指向 dist CJS 是本仓首例,D4 依据:zsw 宿主为纯 CJS(node>=20),而 core 依赖的 @xyz-agent/extension-protocol npm dist 仅 ESM(.mjs),node 20 下 require() 加载 ESM 不可靠——因此对 extension-protocol 设 noExternal 防御性 bundle 边界(当前 entry 闭包无其运行时引用、dist 实测零常量命中;未来 entry 一旦引入运行时引用即 bundle),绝不让外部 ESM 依赖出现在 require 链上;require 分支的 types 用 .d.cts 对应。开发态 require 条件即指向 dist:dist 是 require 语义的真实载体,未 build 时 require 失败属预期(build 前置)。D5 豁免终止(post-convergence 设计 §3.2 B-2 / D5-补注,2026-09):曾为此保留的 `./*` -> src 开发态通配已删除——壳侧深路径豁免终止,生产消费收口到主入口 barrel(137 符号,逐名可审)与上列受控子入口,删通配后壳再写深路径 = tsc 编译错误(不再是风格问题);壳测试侧带 .ts 后缀的深路径(from/vi.mock/动态 import)经壳 vitest.config.ts 正则 alias 物理解析到 core src(生产 barrel import 无路径段、子入口 specifier 无 .ts 后缀,均不命中该 alias),不经 exports——生产走 exports / 测试走 alias 的双轨解析对通配删除免疫。发布面从不带该通配,npm 消费面维持上列受控入口不变。workflows/ 资产 .cjs/.js 不编译(D1:src=dist 同字节直发),require/import 条件同径。tsup 多入口保形输出见 tsup.config.ts(dist/<entry> 与 src 同构,d.ts/d.cts 全覆盖)。",
|
|
76
76
|
"files": [
|
|
77
77
|
"src/",
|
|
78
78
|
"workflows/",
|
|
79
|
-
"
|
|
79
|
+
"agents/",
|
|
80
|
+
"dist/",
|
|
81
|
+
"dist.bundle/"
|
|
80
82
|
],
|
|
81
83
|
"dependencies": {
|
|
82
84
|
"ajv": "^8.20.0",
|
|
83
85
|
"proper-lockfile": "^4.1.2",
|
|
84
86
|
"yaml": "^2.9.0",
|
|
85
|
-
"@xyz-agent/extension-protocol": "0.
|
|
87
|
+
"@xyz-agent/extension-protocol": "0.8.1"
|
|
86
88
|
},
|
|
87
89
|
"devDependencies": {
|
|
88
90
|
"@types/node": "^24.0.0",
|
|
@@ -93,6 +95,7 @@
|
|
|
93
95
|
},
|
|
94
96
|
"scripts": {
|
|
95
97
|
"build": "tsup",
|
|
98
|
+
"build:bundle": "tsup",
|
|
96
99
|
"test": "vitest run",
|
|
97
100
|
"typecheck": "tsc --noEmit"
|
|
98
101
|
}
|
|
@@ -66,7 +66,7 @@ vi.mock("../execution/alive-store.ts", () => ({
|
|
|
66
66
|
writeAliveMarker: vi.fn(),
|
|
67
67
|
}));
|
|
68
68
|
|
|
69
|
-
vi.mock("../execution/temp-prompt.ts", () => ({
|
|
69
|
+
vi.mock("../execution/engine/engines/pi/temp-prompt.ts", () => ({
|
|
70
70
|
writePromptToTempFile: vi.fn(async (agent: string, content: string) => {
|
|
71
71
|
captured.calls++;
|
|
72
72
|
captured.content = content;
|
|
@@ -79,7 +79,7 @@ vi.mock("../execution/temp-prompt.ts", () => ({
|
|
|
79
79
|
import { spawn } from "node:child_process";
|
|
80
80
|
|
|
81
81
|
import { createRecord } from "../execution/execution-record.ts";
|
|
82
|
-
import { runSpawn, type RunOptions, type SessionRunnerContext } from "../execution/session-runner.ts";
|
|
82
|
+
import { runSpawn, type RunOptions, type SessionRunnerContext } from "../execution/engine/engines/pi/session-runner.ts";
|
|
83
83
|
|
|
84
84
|
const mockSpawn = vi.mocked(spawn);
|
|
85
85
|
|
|
@@ -116,8 +116,9 @@ function makeRecord() {
|
|
|
116
116
|
return createRecord(`append-asm-${seq}`, {
|
|
117
117
|
agent: "general-purpose",
|
|
118
118
|
model: "test/model",
|
|
119
|
-
mode: "
|
|
119
|
+
mode: "background",
|
|
120
120
|
task: "test task",
|
|
121
|
+
slug: "append-asm",
|
|
121
122
|
startedAt: Date.now(),
|
|
122
123
|
rootSessionId: "s1",
|
|
123
124
|
parentRecordId: undefined,
|
|
@@ -127,7 +128,7 @@ function makeRecord() {
|
|
|
127
128
|
|
|
128
129
|
function makeRunOpts(overrides: Partial<RunOptions> = {}): RunOptions {
|
|
129
130
|
return {
|
|
130
|
-
resolved: { model: { provider: "test", id: "model" }, thinkingLevel: undefined },
|
|
131
|
+
resolved: { model: { provider: "test", id: "model", name: "Model", reasoning: false }, thinkingLevel: undefined },
|
|
131
132
|
agentConfig: undefined,
|
|
132
133
|
appendSystemPrompt: undefined,
|
|
133
134
|
skillPath: undefined,
|
|
@@ -178,7 +179,7 @@ describe("writeAppendSystemPromptFile 组装(经 runSpawn 驱动)", () => {
|
|
|
178
179
|
it("全片段组装:env block → agent body → 调用方片段 → wrap-up hint,顺序稳定", async () => {
|
|
179
180
|
const content = await captureAppendContent(
|
|
180
181
|
makeRunOpts({
|
|
181
|
-
agentConfig: { systemPrompt: "AGENT-BODY-MARKER" },
|
|
182
|
+
agentConfig: { name: "worker", systemPrompt: "AGENT-BODY-MARKER" },
|
|
182
183
|
appendSystemPrompt: ["FRAG-ONE", "FRAG-TWO"],
|
|
183
184
|
maxTurns: 5,
|
|
184
185
|
}),
|
|
@@ -207,7 +208,7 @@ describe("writeAppendSystemPromptFile 组装(经 runSpawn 驱动)", () => {
|
|
|
207
208
|
});
|
|
208
209
|
|
|
209
210
|
it("ask_user RPC 提示 mode 门控:rpc(gui 响应)注入,json(headless)不注入", async () => {
|
|
210
|
-
const opts = makeRunOpts({ agentConfig: { tools: ["ask_user"] } });
|
|
211
|
+
const opts = makeRunOpts({ agentConfig: { name: "worker", systemPrompt: "", tools: ["ask_user"] } });
|
|
211
212
|
const rpcContent = await captureAppendContent(opts, makeCtx({ mode: "rpc" }));
|
|
212
213
|
expect(rpcContent).toContain("ask_user Tool Availability");
|
|
213
214
|
const jsonContent = await captureAppendContent(opts, makeCtx({ mode: "json" }));
|
|
@@ -217,7 +218,7 @@ describe("writeAppendSystemPromptFile 组装(经 runSpawn 驱动)", () => {
|
|
|
217
218
|
|
|
218
219
|
it("mode 响应但 tools 不含 ask_user → 不注入", async () => {
|
|
219
220
|
const content = await captureAppendContent(
|
|
220
|
-
makeRunOpts({ agentConfig: { tools: ["read", "bash"] } }),
|
|
221
|
+
makeRunOpts({ agentConfig: { name: "worker", systemPrompt: "", tools: ["read", "bash"] } }),
|
|
221
222
|
makeCtx({ mode: "rpc" }),
|
|
222
223
|
);
|
|
223
224
|
expect(content).not.toContain("ask_user Tool Availability");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, expect,it } from "vitest";
|
|
2
2
|
|
|
3
|
-
import { parseSpawnLine } from "../execution/spawn-event-adapter";
|
|
4
|
-
import { sendGetStateCommand } from "../execution/stdin-writer";
|
|
3
|
+
import { parseSpawnLine } from "../execution/engine/engines/pi/spawn-event-adapter";
|
|
4
|
+
import { sendGetStateCommand } from "../execution/engine/engines/pi/stdin-writer";
|
|
5
5
|
|
|
6
6
|
describe("FR-4: get_state RPC handshake", () => {
|
|
7
7
|
describe("sendGetStateCommand", () => {
|
|
@@ -19,7 +19,6 @@ describe("M2 append-content probe (design.json TC1)", () => {
|
|
|
19
19
|
schema: { type: "object", properties: { ok: { type: "boolean" } } },
|
|
20
20
|
agent: "/fake/agents/worker.md",
|
|
21
21
|
description: "m2-probe",
|
|
22
|
-
slug: "m2-probe",
|
|
23
22
|
};
|
|
24
23
|
|
|
25
24
|
const result = resolveAgentOpts(opts);
|
|
@@ -58,7 +58,7 @@ describe("ManifestStore", () => {
|
|
|
58
58
|
});
|
|
59
59
|
|
|
60
60
|
afterEach(() => {
|
|
61
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
61
|
+
fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
describe("writeManifest", () => {
|
|
@@ -108,7 +108,7 @@ describe("ManifestStore", () => {
|
|
|
108
108
|
};
|
|
109
109
|
|
|
110
110
|
// Delete the directory to cause write failure
|
|
111
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
111
|
+
fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
112
112
|
|
|
113
113
|
await expect(store.writeManifest(record)).rejects.toThrow();
|
|
114
114
|
});
|
|
@@ -205,7 +205,7 @@ describe("ManifestStore", () => {
|
|
|
205
205
|
describe("listAllSync (A3)", () => {
|
|
206
206
|
it("正常读:返回目录下所有合法 manifest", async () => {
|
|
207
207
|
await store.writeManifest({ id: "a-1", rootSessionId: "s", agentName: "w", status: "running" as const, createdAt: 1 });
|
|
208
|
-
await store.writeManifest({ id: "a-2", rootSessionId: "s", agentName: "w", status: "
|
|
208
|
+
await store.writeManifest({ id: "a-2", rootSessionId: "s", agentName: "w", status: "closed" as const, createdAt: 2 });
|
|
209
209
|
const all = store.listAllSync();
|
|
210
210
|
expect(all.length).toBe(2);
|
|
211
211
|
expect(all.map((r) => r.id).sort()).toEqual(["a-1", "a-2"]);
|
|
@@ -240,8 +240,9 @@ describe("ManifestStore", () => {
|
|
|
240
240
|
});
|
|
241
241
|
});
|
|
242
242
|
|
|
243
|
-
// ── M3:
|
|
244
|
-
|
|
243
|
+
// ── M3: status 枚举(SP-1 后 running/closed/cancelled 三态;crashed 不进 manifest,
|
|
244
|
+
// 历史 completed/failed 由读侧 mapManifestStatus 映射为 closed)──
|
|
245
|
+
describe("status 枚举 (M3)", () => {
|
|
245
246
|
it("cancelled 能写入 + 读回(不再归并 failed)", async () => {
|
|
246
247
|
const record = {
|
|
247
248
|
id: "test-cancelled-4state",
|
|
@@ -257,14 +258,14 @@ describe("ManifestStore", () => {
|
|
|
257
258
|
expect(result?.status).toBe("cancelled");
|
|
258
259
|
});
|
|
259
260
|
|
|
260
|
-
it("listAllSync
|
|
261
|
+
it("listAllSync 接受全部合法态(含 cancelled)", async () => {
|
|
261
262
|
await store.writeManifest({ id: "s-running", rootSessionId: "s", agentName: "w", status: "running" as const, createdAt: 1 });
|
|
262
|
-
await store.writeManifest({ id: "s-completed", rootSessionId: "s", agentName: "w", status: "
|
|
263
|
-
await store.writeManifest({ id: "s-failed", rootSessionId: "s", agentName: "w", status: "
|
|
263
|
+
await store.writeManifest({ id: "s-completed", rootSessionId: "s", agentName: "w", status: "closed" as const, createdAt: 2 });
|
|
264
|
+
await store.writeManifest({ id: "s-failed", rootSessionId: "s", agentName: "w", status: "closed" as const, createdAt: 3 });
|
|
264
265
|
await store.writeManifest({ id: "s-cancelled", rootSessionId: "s", agentName: "w", status: "cancelled" as const, createdAt: 4 });
|
|
265
266
|
const all = store.listAllSync();
|
|
266
267
|
expect(all.length).toBe(4);
|
|
267
|
-
expect(all.map((r) => r.status).sort()).toEqual(["cancelled", "
|
|
268
|
+
expect(all.map((r) => r.status).sort()).toEqual(["cancelled", "closed", "closed", "running"]);
|
|
268
269
|
});
|
|
269
270
|
|
|
270
271
|
it("isValidManifest 拒绝 crashed(crashed 不进 manifest)", async () => {
|
|
@@ -66,7 +66,6 @@ describe("RecordStore per-file cache + light scan [perf]", () => {
|
|
|
66
66
|
agent: opts.agent ?? "worker",
|
|
67
67
|
mode: "background",
|
|
68
68
|
task: "test task",
|
|
69
|
-
slug: "test",
|
|
70
69
|
startedAt: opts.startedAt ?? 1000,
|
|
71
70
|
rootSessionId: opts.rootSessionId ?? "root-1",
|
|
72
71
|
depth: 0,
|
|
@@ -266,7 +265,6 @@ describe("RecordStore per-file cache + light scan [perf]", () => {
|
|
|
266
265
|
const mem = {
|
|
267
266
|
id: "sa-1",
|
|
268
267
|
agent: "worker",
|
|
269
|
-
slug: "test",
|
|
270
268
|
status: "running",
|
|
271
269
|
mode: "background",
|
|
272
270
|
startedAt: 1000,
|
|
@@ -78,11 +78,11 @@ describe("sessions-index 模块(S1TC10-12)", () => {
|
|
|
78
78
|
return {
|
|
79
79
|
mtimeMs: 111,
|
|
80
80
|
size: 222,
|
|
81
|
+
slug: "t",
|
|
81
82
|
id: "sa-1",
|
|
82
83
|
agent: "worker",
|
|
83
84
|
mode: "background",
|
|
84
85
|
task: "test task",
|
|
85
|
-
slug: "test",
|
|
86
86
|
startedAt: 1000,
|
|
87
87
|
rootSessionId: "root-1",
|
|
88
88
|
parentRecordId: undefined,
|
|
@@ -208,7 +208,6 @@ describe("RecordStore 索引接入 [perf L-1](S1TC1-9/13)", () => {
|
|
|
208
208
|
agent: opts.agent ?? "worker",
|
|
209
209
|
mode: "background",
|
|
210
210
|
task: "test task",
|
|
211
|
-
slug: "test",
|
|
212
211
|
startedAt: opts.startedAt ?? 1000,
|
|
213
212
|
rootSessionId: opts.rootSessionId ?? "root-1",
|
|
214
213
|
depth: 0,
|
|
@@ -12,11 +12,14 @@
|
|
|
12
12
|
// (旧实现不排除 error 分支,agent 失败被误诊为引擎透传未上线并烧掉 once 名额)
|
|
13
13
|
// - A11:model 缺省回退 "(default)"(请求时参数语义)
|
|
14
14
|
// - A12:promptMode=null(aggregator/fixer)必须保持 null,不被 || 误转 "full"
|
|
15
|
-
import {
|
|
15
|
+
import { execFile } from "node:child_process";
|
|
16
|
+
import { copyFileSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
17
|
+
import { tmpdir } from "node:os";
|
|
16
18
|
import { join } from "node:path";
|
|
19
|
+
import { promisify } from "node:util";
|
|
17
20
|
import vm from "node:vm";
|
|
18
21
|
|
|
19
|
-
import { describe, expect, it } from "vitest";
|
|
22
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
20
23
|
|
|
21
24
|
const WORKFLOW_SOURCE = readFileSync(
|
|
22
25
|
join(__dirname, "..", "..", "workflows", "review-fix-loop.js"),
|
|
@@ -283,6 +286,93 @@ describe("review-fix-loop.js 模块形态约束", () => {
|
|
|
283
286
|
// 本 import 语句同时是静态依赖边:让依赖分析把该 workflow 文件纳入测试可达域
|
|
284
287
|
// (CRAP 覆盖估算)。顶层 return / require 决定了 import 必然 reject——这是
|
|
285
288
|
// workflow 脚本的固有形态,抽取测试法(本文件)因此是唯一可行的直测途径。
|
|
286
|
-
|
|
289
|
+
// `as string` 使 import() 参数脱离字面量模块解析(该文件顶层 return,非合法模块,
|
|
290
|
+
// 本就无类型可锚);esbuild 转译剥除断言后运行时与字面量形态完全一致
|
|
291
|
+
await expect(import("../../workflows/review-fix-loop.js" as string)).rejects.toThrow();
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// ── RX2-F2:fixAgent=fallow-scan 显式拒收(脚本顶层参数校验,非函数段) ──
|
|
296
|
+
// 拒收逻辑位于脚本顶层(FIX_AGENT_RAW 解析后、resolveAgentDefs 前),函数段抽取法
|
|
297
|
+
// 覆盖不到;改用 review-fix-loop-scriptpath-failfast.test.ts 同款「AsyncFunction 包装
|
|
298
|
+
// + node -e 真实子进程」探针跑整脚本顶层副本。拒收点在 RUN_ROOT 落盘 / lockReviewBase
|
|
299
|
+
// 之前,探针无文件系统副作用、恒非零退出。
|
|
300
|
+
describe("review-fix-loop.js fixAgent=fallow-scan 显式拒收(RX2-F2)", () => {
|
|
301
|
+
const run = promisify(execFile);
|
|
302
|
+
|
|
303
|
+
/** execFile reject 侧的最小形状(Node ExecException 子集)。 */
|
|
304
|
+
interface ExecFailure extends Error {
|
|
305
|
+
code?: number | string;
|
|
306
|
+
stderr?: string;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function isExecFailure(e: unknown): e is ExecFailure {
|
|
310
|
+
return e instanceof Error;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
let sandboxDir = "";
|
|
314
|
+
|
|
315
|
+
beforeEach(() => {
|
|
316
|
+
sandboxDir = mkdtempSync(join(tmpdir(), "rfl-fixagent-guard-"));
|
|
317
|
+
const workflowsDir = join(__dirname, "..", "..", "workflows");
|
|
318
|
+
// 副本以 .cjs 落 sandbox(require 解析形态对齐 worker 宿主);utils 锚定加载经
|
|
319
|
+
// workerData.scriptPath(副本同目录),白名单校验前即需要它
|
|
320
|
+
copyFileSync(join(workflowsDir, "review-fix-loop.js"), join(sandboxDir, "review-fix-loop.cjs"));
|
|
321
|
+
copyFileSync(join(workflowsDir, "review-fix-loop-utils.cjs"), join(sandboxDir, "review-fix-loop-utils.cjs"));
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
afterEach(() => {
|
|
325
|
+
rmSync(sandboxDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
/** -e 探针体:AsyncFunction 复刻 worker 模板宿主形态(workerData/$ARGS/log 注入)。 */
|
|
329
|
+
function probeCode(argsJson: string): string {
|
|
330
|
+
const copyPath = join(sandboxDir, "review-fix-loop.cjs");
|
|
331
|
+
return [
|
|
332
|
+
"const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;",
|
|
333
|
+
"const src = require('fs').readFileSync(" + JSON.stringify(copyPath) + ", 'utf8');",
|
|
334
|
+
"const runner = new AsyncFunction('workerData', '$ARGS', 'require', 'log', src + '\\n');",
|
|
335
|
+
"const workerData = { scriptPath: " + JSON.stringify(join(sandboxDir, "review-fix-loop.cjs")) + " };",
|
|
336
|
+
"const $ARGS = " + argsJson + ";",
|
|
337
|
+
// $MODEL 是 worker 模板全局(主模型注入)——对照用例会推进到 fixAgent 解析之后
|
|
338
|
+
// 的 MODEL = $MODEL 行(:204),缺席会 ReferenceError 而非到达批次校验
|
|
339
|
+
"const $MODEL = 'probe-model';",
|
|
340
|
+
"runner(workerData, $ARGS, require, function () {}).catch(function (e) {",
|
|
341
|
+
" require('fs').writeSync(2, String((e && e.message) || e) + '\\n');",
|
|
342
|
+
" process.exit(1);",
|
|
343
|
+
"});",
|
|
344
|
+
].join("\n");
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
async function runProbeExpectFailure(argsJson: string): Promise<string> {
|
|
348
|
+
const outcome = await run(process.execPath, ["-e", probeCode(argsJson)], {
|
|
349
|
+
cwd: sandboxDir,
|
|
350
|
+
timeout: 30_000,
|
|
351
|
+
}).then(
|
|
352
|
+
(): ExecFailure | null => null,
|
|
353
|
+
(e: unknown): ExecFailure | null => (isExecFailure(e) ? e : null),
|
|
354
|
+
);
|
|
355
|
+
if (outcome === null) {
|
|
356
|
+
throw new Error("expected non-zero exit, but node exited 0 with args: " + argsJson);
|
|
357
|
+
}
|
|
358
|
+
return String(outcome.stderr ?? "");
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
it("fixAgent=fallow-scan → 非零退出 + 保留字错误 + fallowScan=true 恢复指引", async () => {
|
|
362
|
+
const stderr = await runProbeExpectFailure(
|
|
363
|
+
'{ targetType: "file", target: "probe", fixAgent: "fallow-scan" }',
|
|
364
|
+
);
|
|
365
|
+
expect(stderr).toContain("内部保留字");
|
|
366
|
+
expect(stderr).toContain("fallowScan=true");
|
|
367
|
+
// 旧实现静默映射 FALLOW_DEF(fix 派发退化为通用 subagent)——不会到达此处报错
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it("对照:合法形态 fixAgent(.md 路径)不触发保留字拒收(后续批次校验照常 fail)", async () => {
|
|
371
|
+
const stderr = await runProbeExpectFailure(
|
|
372
|
+
'{ targetType: "file", target: "probe", fixAgent: "/tmp/rx2-f2-fake-agent.md" }',
|
|
373
|
+
);
|
|
374
|
+
// 推进到批次解析才失败(缺批次参数)——证明拒收只对字面值 fallow-scan 触发
|
|
375
|
+
expect(stderr).toContain("缺少批次参数");
|
|
376
|
+
expect(stderr).not.toContain("内部保留字");
|
|
287
377
|
});
|
|
288
378
|
});
|
|
@@ -450,7 +450,7 @@ describe("reconcileIssues", () => {
|
|
|
450
450
|
const r = reconcileIssues(prev, { seenIds: new Set(), escalateIds: new Set(), round: 2, stuckThreshold: 3 });
|
|
451
451
|
expect(r.issues["MF-1"].status).toBe("fixed");
|
|
452
452
|
expect(r.issues["MF-2"].status).toBe("fixed");
|
|
453
|
-
expect(r.issues["MF-2"].history
|
|
453
|
+
expect(r.issues["MF-2"].history!.at(-1)!.status).toBe("fixed");
|
|
454
454
|
expect(r.stuck).toBe(false);
|
|
455
455
|
expect(r.knownRemaining).toEqual([]);
|
|
456
456
|
});
|
|
@@ -495,7 +495,7 @@ describe("reconcileIssues", () => {
|
|
|
495
495
|
expect(r.issues["MF-1"].status).toBe("regressed");
|
|
496
496
|
expect(r.issues["MF-1"].fixAttempts).toBe(2);
|
|
497
497
|
expect(r.issues["MF-1"].openStreak).toBe(1);
|
|
498
|
-
expect(r.issues["MF-1"].history
|
|
498
|
+
expect(r.issues["MF-1"].history!.at(-1)!.status).toBe("regressed");
|
|
499
499
|
expect(r.stuck).toBe(false);
|
|
500
500
|
});
|
|
501
501
|
it("fixed 未再被报告(未 seen)→ 保持 fixed,不误转 regressed(MF-2)", () => {
|
|
@@ -616,7 +616,7 @@ describe("reconcileIssues escalate + fixAttempts 起点", () => {
|
|
|
616
616
|
{ seenIds: [], escalateIds: ["S-1"], round: 3, stuckThreshold: 3 },
|
|
617
617
|
);
|
|
618
618
|
expect(rec.issues["S-1"].status).toBe("open");
|
|
619
|
-
expect(rec.issues["S-1"].history
|
|
619
|
+
expect(rec.issues["S-1"].history!.at(-1)!.status).toBe("escalated");
|
|
620
620
|
expect(rec.knownRemaining).toEqual([]);
|
|
621
621
|
});
|
|
622
622
|
it("deferred 无 escalate → 留 known-remaining", () => {
|
|
@@ -1140,6 +1140,27 @@ describe("findIssueKey — 归一化键空间查找", () => {
|
|
|
1140
1140
|
expect(findIssueKey(issues, "MF-1")).toBe("MF-1 (fixed)");
|
|
1141
1141
|
expect(findIssueKey(issues, "mf-1 (by design)")).toBe("MF-1 (fixed)");
|
|
1142
1142
|
});
|
|
1143
|
+
|
|
1144
|
+
it("原型链键(__proto__/toString/constructor)→ 不命中(Object.hasOwn 首查,zsw 侧同款修复上收)", () => {
|
|
1145
|
+
// 旧 truthy 查表(issues[issueId])会让 Object.prototype 上的成员误命中:
|
|
1146
|
+
// issues["__proto__"] 取到原型对象(truthy)→ 返回 "__proto__" 当台账键 →
|
|
1147
|
+
// 调用方读 issues["__proto__"].severity 得 undefined/函数污染 fix 对账。
|
|
1148
|
+
// ID 来自 LLM 产出,原型链键是真实攻击面而非理论边界。
|
|
1149
|
+
const issues = { "MF-1": { severity: "major" } };
|
|
1150
|
+
expect(findIssueKey(issues, "__proto__")).toBeUndefined();
|
|
1151
|
+
expect(findIssueKey(issues, "toString")).toBeUndefined();
|
|
1152
|
+
expect(findIssueKey(issues, "constructor")).toBeUndefined();
|
|
1153
|
+
expect(findIssueKey(issues, "hasOwnProperty")).toBeUndefined();
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1156
|
+
it("own 键且值 falsy(null)→ 仍命中该键(hasOwn 与 truthy 查表的等价性由归一化回退段兜底)", () => {
|
|
1157
|
+
// 旧 truthy 查表在值 falsy 时不直接命中,但归一化回退段(Object.keys 只枚举
|
|
1158
|
+
// own 键)会命中同一键——两形态最终返回一致,hasOwn 只是路径更短、且对原型链
|
|
1159
|
+
// 键正确拒绝。断言钉住该等价性,防未来有人「顺手」改回 truthy 形态。
|
|
1160
|
+
const issues = { "MF-1": null };
|
|
1161
|
+
expect(findIssueKey(issues, "MF-1")).toBe("MF-1");
|
|
1162
|
+
expect(findIssueKey(issues, "mf-1")).toBe("MF-1");
|
|
1163
|
+
});
|
|
1143
1164
|
});
|
|
1144
1165
|
|
|
1145
1166
|
describe("T-2 端到端:normIssueId/findIssueKey 在 validateFixResult 的真实消费", () => {
|
|
@@ -1173,8 +1194,8 @@ describe("T-2 端到端:normIssueId/findIssueKey 在 validateFixResult 的真
|
|
|
1173
1194
|
|
|
1174
1195
|
// ── T-3:reconcile 新 ID severity:"unknown" 守卫分支 ──────────────────────
|
|
1175
1196
|
// validateFixResult 的 tracked severity 交叉核对:trackedIssues 中某 ID 的 severity
|
|
1176
|
-
// 为 "unknown"(reconcileIssues 给新发现 ID 的默认值,review-fix-loop-utils.cjs:
|
|
1177
|
-
// `severity: "unknown"`)时不覆盖 fix agent 自报 severity——守卫在 cjs:
|
|
1197
|
+
// 为 "unknown"(reconcileIssues 给新发现 ID 的默认值,review-fix-loop-utils.cjs:935
|
|
1198
|
+
// `severity: "unknown"`)时不覆盖 fix agent 自报 severity——守卫在 cjs:347-355(仅认
|
|
1178
1199
|
// critical/major/minor/trivial 为真实等级)。现有 MF-4 测试覆盖了 major/critical/minor
|
|
1179
1200
|
// tracked + 无此 ID,缺 "unknown" 分支。守卫目的:新发现 ID 的 severity 尚未由 reviewer
|
|
1180
1201
|
// 结构化确认(reconcile 默认 unknown),不应凭默认值把合法 minor deferral 误升级为违规。
|
|
@@ -1230,7 +1251,7 @@ describe("A5 resolveRunRoot: 存储根解析(git slug / 非 git cwd / home 不
|
|
|
1230
1251
|
expect(root).toBe(join(home, ".review-fix-loop", "Users-x-proj-my-repo", "wf-test-1"));
|
|
1231
1252
|
expect(made).toEqual([root]);
|
|
1232
1253
|
} finally {
|
|
1233
|
-
rmSync(home, { recursive: true, force: true });
|
|
1254
|
+
rmSync(home, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
1234
1255
|
}
|
|
1235
1256
|
});
|
|
1236
1257
|
|
|
@@ -1248,7 +1269,7 @@ describe("A5 resolveRunRoot: 存储根解析(git slug / 非 git cwd / home 不
|
|
|
1248
1269
|
expect(slug).toBe("tmp-plain-docs");
|
|
1249
1270
|
expect(root).toBe(join(home, ".review-fix-loop", "tmp-plain-docs", "run-42"));
|
|
1250
1271
|
} finally {
|
|
1251
|
-
rmSync(home, { recursive: true, force: true });
|
|
1272
|
+
rmSync(home, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
1252
1273
|
}
|
|
1253
1274
|
});
|
|
1254
1275
|
|
|
@@ -1273,7 +1294,7 @@ describe("A5 resolveRunRoot: 存储根解析(git slug / 非 git cwd / home 不
|
|
|
1273
1294
|
expect(root).toBe(join(fallbackTmp, "review-fix-loop", "wf-test-2"));
|
|
1274
1295
|
expect(made).toEqual([root]);
|
|
1275
1296
|
} finally {
|
|
1276
|
-
rmSync(fallbackTmp, { recursive: true, force: true });
|
|
1297
|
+
rmSync(fallbackTmp, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
1277
1298
|
}
|
|
1278
1299
|
});
|
|
1279
1300
|
|
|
@@ -1292,7 +1313,7 @@ describe("A5 resolveRunRoot: 存储根解析(git slug / 非 git cwd / home 不
|
|
|
1292
1313
|
expect(existsSync(root)).toBe(true);
|
|
1293
1314
|
expect(root).toContain(join(home, ".review-fix-loop", "Users-x-a b-c", "wf-real-1"));
|
|
1294
1315
|
} finally {
|
|
1295
|
-
rmSync(home, { recursive: true, force: true });
|
|
1316
|
+
rmSync(home, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
1296
1317
|
}
|
|
1297
1318
|
});
|
|
1298
1319
|
});
|
|
@@ -1770,7 +1791,7 @@ describe("filterDormantFromRecon + applyCleanRoundBackfill 的 dormant 分区",
|
|
|
1770
1791
|
const out = applyCleanRoundBackfill(state, { reconSeen: new Set(["MF-D1"]), reconEscalate: new Set(), round: 2, stuckThreshold: 3 });
|
|
1771
1792
|
// 过滤生效:MF-D1 不经对账通道建 issue(复活唯一入口 = 聚合活跃重报)
|
|
1772
1793
|
expect(out.state.issues["MF-D1"]).toBeUndefined();
|
|
1773
|
-
expect(out.state.dormant[0].revived).toBe(false);
|
|
1794
|
+
expect(out.state.dormant![0].revived).toBe(false);
|
|
1774
1795
|
});
|
|
1775
1796
|
});
|
|
1776
1797
|
|
|
@@ -7,14 +7,15 @@
|
|
|
7
7
|
//(run.state.errorLogs = ...),每次重试的诊断日志都被最后一次覆盖丢失。
|
|
8
8
|
// 修复:改为 push + 截断(MAX_ERROR_LOGS=500 防无界增长)。
|
|
9
9
|
//
|
|
10
|
-
// 测试搭建方式参照
|
|
10
|
+
// 测试搭建方式参照 worker-message-pump-handlers.test.ts(mock LifecycleDeps + WorkflowRun)。
|
|
11
11
|
// - handleScriptError 是 export 的 async function,直接 import 调用
|
|
12
12
|
// - handleReturn 不是 export 的——通过 handleWorkerMessage 发 { type: "return", ... } 触发
|
|
13
13
|
|
|
14
14
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
15
15
|
|
|
16
|
-
import { handleScriptError, handleWorkerMessage } from "../orchestration/
|
|
16
|
+
import { handleScriptError, handleWorkerMessage } from "../orchestration/worker-message-pump.ts";
|
|
17
17
|
import type { LifecycleDeps, WorkerHandlers } from "../orchestration/models/ports.ts";
|
|
18
|
+
import type { DoneReason, RunStatus } from "../orchestration/models/types.ts";
|
|
18
19
|
import type { WorkflowRun } from "../orchestration/models/workflow-run.ts";
|
|
19
20
|
|
|
20
21
|
// ── helpers ──────────────────────────────────────────────────
|
|
@@ -52,15 +53,15 @@ function makeRunningRun(): WorkflowRun & { resetRunning(): void } {
|
|
|
52
53
|
runtime: {
|
|
53
54
|
worker: { postMessage: vi.fn() },
|
|
54
55
|
},
|
|
55
|
-
transition(target:
|
|
56
|
+
transition(this: WorkflowRun, target: RunStatus, reason?: DoneReason): void {
|
|
56
57
|
this.state.status = target;
|
|
57
58
|
if (target === "done") this.state.reason = reason;
|
|
58
59
|
},
|
|
59
|
-
replaceRuntime(rt:
|
|
60
|
+
replaceRuntime(this: WorkflowRun, rt: NonNullable<WorkflowRun["runtime"]>): void {
|
|
60
61
|
this.runtime = rt;
|
|
61
62
|
},
|
|
62
63
|
// 多次触发 handleReturn 时把状态从 done 重置回 running
|
|
63
|
-
resetRunning(): void {
|
|
64
|
+
resetRunning(this: WorkflowRun): void {
|
|
64
65
|
this.state.status = "running";
|
|
65
66
|
this.state.reason = undefined;
|
|
66
67
|
},
|
|
@@ -20,7 +20,7 @@ function readSrc(relPath: string): string {
|
|
|
20
20
|
// ── M4: node.live cleared before stale guard ─────────────────
|
|
21
21
|
|
|
22
22
|
describe("M4: dispatchAgentCall .then() clears node.live before stale guard", () => {
|
|
23
|
-
const src = readSrc(join("src", "orchestration", "
|
|
23
|
+
const src = readSrc(join("src", "orchestration", "worker-message-pump.ts"));
|
|
24
24
|
|
|
25
25
|
it("node.live = undefined appears before the stale guard return", () => {
|
|
26
26
|
// 找到 .then 回调中的 stale guard 和 node.live 清理的相对顺序
|
|
@@ -41,7 +41,7 @@ describe("M4: dispatchAgentCall .then() clears node.live before stale guard", ()
|
|
|
41
41
|
// ── M7: handleWorkerMessage shape validation ─────────────────
|
|
42
42
|
|
|
43
43
|
describe("M7: handleWorkerMessage validates msg before dereferencing", () => {
|
|
44
|
-
const src = readSrc(join("src", "orchestration", "
|
|
44
|
+
const src = readSrc(join("src", "orchestration", "worker-message-pump.ts"));
|
|
45
45
|
|
|
46
46
|
it("handleWorkerMessage has shape guard for msg before dereferencing opts", () => {
|
|
47
47
|
// handleWorkerMessage 中 `raw as WorkerMsg` 后应有 typeof/形状校验
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
// M6: worktree cleanup not gated by patchOk (decoupled)
|
|
4
4
|
// M9: store.save not fire-and-forget (has .catch)
|
|
5
5
|
// M12: budget-done transition and onRunDone in separate try blocks
|
|
6
|
+
// (D5-② coda 收敛后语义不变、落点转移:budget-done 分支改调 finalizeRun 单写点,
|
|
7
|
+
// transition / unregister+onRunDone 的错误分离在 finalizeRun 函数体内断言)
|
|
6
8
|
// [u1-move] M10(notifyDone 序列化守卫,读壳侧 interface/helpers.ts 源文本)随壳件
|
|
7
9
|
// 留守 pi extension 包:src/__tests__/robustness-medium-batch2-interface.test.ts。
|
|
8
10
|
|
|
@@ -37,7 +39,7 @@ describe("M6: worktree cleanup not gated by patchOk", () => {
|
|
|
37
39
|
// ── M9: store.save has .catch (not fire-and-forget) ──────────
|
|
38
40
|
|
|
39
41
|
describe("M9: store.save not fire-and-forget in dispatchAgentCall", () => {
|
|
40
|
-
const src = readSrc(join("src", "orchestration", "
|
|
42
|
+
const src = readSrc(join("src", "orchestration", "worker-message-pump.ts"));
|
|
41
43
|
|
|
42
44
|
it("no bare `void deps.store.save` without .catch", () => {
|
|
43
45
|
// 查找所有 void deps.store.save(run) 的出现
|
|
@@ -48,18 +50,27 @@ describe("M9: store.save not fire-and-forget in dispatchAgentCall", () => {
|
|
|
48
50
|
});
|
|
49
51
|
});
|
|
50
52
|
|
|
51
|
-
// ── M12: budget-done
|
|
53
|
+
// ── M12: budget-done coda 经 finalizeRun 单写点(错误分离语义不变) ──
|
|
52
54
|
|
|
53
55
|
describe("M12: budget-done separates transition and onRunDone error handling", () => {
|
|
54
|
-
const src = readSrc(join("src", "orchestration", "
|
|
56
|
+
const src = readSrc(join("src", "orchestration", "worker-message-pump.ts"));
|
|
55
57
|
|
|
56
|
-
it("budget-done
|
|
58
|
+
it("budget-done branch delegates to finalizeRun (D5-② single coda write point)", () => {
|
|
57
59
|
// 找到 budget isExceeded 块
|
|
58
60
|
const budgetMatch = src.match(/budget\.isExceeded\(\)[\s\S]*?\}\s*\}\s*\)/);
|
|
59
61
|
expect(budgetMatch).toBeTruthy();
|
|
60
62
|
const budgetBlock = budgetMatch![0];
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
+
// coda 收敛后:budget-done 分支只调 finalizeRun(transition/save/unregister/onRunDone
|
|
64
|
+
// 四步不再内联复制)
|
|
65
|
+
expect(budgetBlock).toContain("finalizeRun(run, deps, \"budget_limited\"");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("finalizeRun body separates transition and onRunDone error handling (>= 2 catch)", () => {
|
|
69
|
+
// finalizeRun 函数体:transition 的 try/catch 与 unregister/onRunDone 的 try/catch
|
|
70
|
+
// 分离(M12 语义锚定不变,随 coda 收敛转移至单写点内)
|
|
71
|
+
const fnMatch = src.match(/export async function finalizeRun\([\s\S]*?\n\}/);
|
|
72
|
+
expect(fnMatch, "finalizeRun 函数定义应存在").toBeTruthy();
|
|
73
|
+
const catchCount = (fnMatch![0].match(/\bcatch\b/g) || []).length;
|
|
63
74
|
expect(catchCount).toBeGreaterThanOrEqual(2);
|
|
64
75
|
});
|
|
65
76
|
});
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
//
|
|
7
7
|
// 通过 handleWorkerMessage 触发 dispatchAgentCall / dispatchWorkflowCall(内部函数不 export)。
|
|
8
8
|
// Mock 构造模式参考 orchestration/__tests__/agent-call-catch-fallback.test.ts 和
|
|
9
|
-
// orchestration/__tests__/
|
|
9
|
+
// orchestration/__tests__/worker-message-pump-workflow-call.test.ts。
|
|
10
10
|
|
|
11
11
|
import { describe, expect, it, vi } from "vitest";
|
|
12
12
|
|
|
@@ -22,7 +22,7 @@ vi.mock("../core/logger.ts", () => ({
|
|
|
22
22
|
getLogger: () => loggerMock,
|
|
23
23
|
}));
|
|
24
24
|
|
|
25
|
-
import { handleWorkerMessage } from "../orchestration/
|
|
25
|
+
import { handleWorkerMessage } from "../orchestration/worker-message-pump.ts";
|
|
26
26
|
import type { LifecycleDeps, WorkerHandlers } from "../orchestration/models/ports.ts";
|
|
27
27
|
import { Trace } from "../orchestration/models/trace.ts";
|
|
28
28
|
import type { AgentResult } from "../orchestration/models/types.ts";
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* 因带 delta 字段被误判为 text_delta。正向判定(只放行 text_delta)根治。
|
|
13
13
|
*/
|
|
14
14
|
import { describe, it, expect } from "vitest";
|
|
15
|
-
import { mapAssistantMessageDelta } from "../execution/session-runner.js";
|
|
15
|
+
import { mapAssistantMessageDelta } from "../execution/engine/engines/pi/session-runner.js";
|
|
16
16
|
|
|
17
17
|
describe("mapAssistantMessageDelta: assistantMessageEvent → streaming 事件分流", () => {
|
|
18
18
|
it("text_delta → forward 为 text_delta", () => {
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
1
2
|
import { describe, expect, it } from "vitest";
|
|
2
3
|
|
|
3
4
|
import { CORE_PACKAGE_VERSION } from "../index";
|
|
4
5
|
|
|
6
|
+
// 对 package.json version 断言(单一事实源):本测试是 src/index.ts 字面量与
|
|
7
|
+
// package.json 双源同步的执法者——任何一侧 bump 后未同步另一侧立即红,
|
|
8
|
+
// 替代旧硬编码 "0.1.0" 字面量(版本 bump 时必忘改的第三处双源)。
|
|
5
9
|
describe("subagent-core scaffold smoke", () => {
|
|
6
|
-
it("exposes CORE_PACKAGE_VERSION", () => {
|
|
7
|
-
|
|
10
|
+
it("exposes CORE_PACKAGE_VERSION synced with package.json", () => {
|
|
11
|
+
const pkgVersion = JSON.parse(
|
|
12
|
+
readFileSync(new URL("../../package.json", import.meta.url), "utf8"),
|
|
13
|
+
).version;
|
|
14
|
+
expect(CORE_PACKAGE_VERSION).toBe(pkgVersion);
|
|
8
15
|
});
|
|
9
16
|
});
|
|
@@ -62,13 +62,13 @@ describe("getHostServices(未配置 → NULL_HOST)", () => {
|
|
|
62
62
|
|
|
63
63
|
host.log("warn", "comp-a", "w-message", { k: 1 });
|
|
64
64
|
expect(warnSpy).toHaveBeenCalledTimes(1);
|
|
65
|
-
expect(warnSpy.mock.calls[0][0]).toBe("[comp-a] w-message");
|
|
65
|
+
expect(warnSpy.mock.calls[0]![0]).toBe("[comp-a] w-message");
|
|
66
66
|
expect(warnSpy.mock.calls[0][1]).toEqual({ k: 1 });
|
|
67
67
|
expect(errorSpy).not.toHaveBeenCalled();
|
|
68
68
|
|
|
69
69
|
host.log("error", "comp-a", "e-message");
|
|
70
70
|
expect(errorSpy).toHaveBeenCalledTimes(1);
|
|
71
|
-
expect(errorSpy.mock.calls[0][0]).toBe("[comp-a] e-message");
|
|
71
|
+
expect(errorSpy.mock.calls[0]![0]).toBe("[comp-a] e-message");
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
it("data 缺省时省略第二参数(node console 会把显式 undefined 打成尾巴)", () => {
|