@zhushanwen/subagent-core 0.4.0 → 0.6.0
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/dist/{chunk-4KN4TTG7.js → chunk-43ONBFZX.js} +1 -1
- package/dist/{chunk-X7SZ5HLQ.js → chunk-V3VHZ2VX.js} +20 -27
- package/dist/execution/engine/engines/zcode/constants.cjs +29 -41
- package/dist/execution/engine/engines/zcode/constants.d.cts +65 -53
- package/dist/execution/engine/engines/zcode/constants.d.ts +65 -53
- package/dist/execution/engine/engines/zcode/constants.js +17 -27
- 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 +2 -2
- package/dist/index.cjs +17100 -14082
- package/dist/index.d.cts +5336 -4033
- package/dist/index.d.ts +5336 -4033
- package/dist/index.js +17020 -14055
- package/dist/{types-DpUO16pj.d.cts → types-C3fE3R9x.d.cts} +280 -112
- package/dist/{types-DpUO16pj.d.ts → types-C3fE3R9x.d.ts} +280 -112
- package/dist.bundle/index.cjs +34933 -0
- package/package.json +3 -3
- 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 +4 -2
- package/src/__tests__/review-fix-loop-utils.test.ts +10 -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/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 +4 -4
- 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 +2 -2
- package/src/execution/__tests__/alive-store.test.ts +1 -1
- package/src/execution/__tests__/ask-user-transit-e2e.test.ts +6 -6
- package/src/execution/__tests__/chat-engine-routing.test.ts +68 -33
- package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +4 -4
- package/src/execution/__tests__/cold-resurrect.test.ts +283 -0
- package/src/execution/__tests__/collect-budget.test.ts +291 -0
- package/src/execution/__tests__/collect-coordinator-service.test.ts +327 -0
- package/src/execution/__tests__/collect-coordinator.test.ts +371 -0
- package/src/execution/__tests__/collect-mixed-dispatch.test.ts +262 -0
- package/src/execution/__tests__/config-collect-sync.test.ts +120 -0
- package/src/execution/__tests__/config.test.ts +1 -1
- package/src/execution/__tests__/delivery-methods.test.ts +75 -62
- package/src/execution/__tests__/descendant-sweep-guards.test.ts +220 -0
- package/src/execution/__tests__/descendant-sweep.test.ts +6 -6
- package/src/execution/__tests__/dialog-queue.test.ts +2 -2
- package/src/execution/__tests__/engine-model-validation.test.ts +741 -0
- package/src/execution/__tests__/epipe-fallback.test.ts +29 -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 +5 -5
- package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +4 -4
- package/src/execution/__tests__/finalize-record.test.ts +173 -17
- package/src/execution/__tests__/finalized-marker.test.ts +1 -1
- package/src/execution/__tests__/gc-timer.test.ts +56 -3
- package/src/execution/__tests__/get-record-for-action-restart.test.ts +12 -12
- package/src/execution/__tests__/get-state-handshake.test.ts +1 -1
- 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 +50 -148
- package/src/execution/__tests__/kill-all-escalation.test.ts +2 -2
- 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 +1 -1
- package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +1 -1
- 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-batch.test.ts +500 -0
- package/src/execution/__tests__/notify-ledger.test.ts +1000 -18
- package/src/execution/__tests__/output-collector.test.ts +117 -6
- package/src/execution/__tests__/pi-invocation.test.ts +36 -4
- package/src/execution/__tests__/record-entry-collect.test.ts +102 -0
- package/src/execution/__tests__/record-store.test.ts +10 -9
- 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__/relay-env.test.ts +12 -1
- 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 +15 -9
- package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +4 -4
- package/src/execution/__tests__/run-spawn-edges.test.ts +17 -15
- package/src/execution/__tests__/run-spawn-integration.test.ts +2 -2
- 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 +4 -4
- package/src/execution/__tests__/service-kill-escalation.test.ts +7 -5
- package/src/execution/__tests__/session-context-resolver.test.ts +0 -2
- package/src/execution/__tests__/session-file-gc.test.ts +36 -1
- package/src/execution/__tests__/session-pending.test.ts +257 -14
- package/src/execution/__tests__/session-reconstructor.test.ts +366 -15
- package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +1 -1
- package/src/execution/__tests__/session-runner-close-prune.test.ts +2 -2
- package/src/execution/__tests__/session-runner-dispatch.test.ts +489 -0
- package/src/execution/__tests__/session-runner-epipe.test.ts +3 -3
- package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +5 -4
- 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 +282 -47
- 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 +3 -3
- package/src/execution/__tests__/spawn-worktree-guidance.test.ts +5 -4
- package/src/execution/__tests__/spawned-children.test.ts +2 -1
- package/src/execution/__tests__/start-collect-guard.test.ts +255 -0
- package/src/execution/__tests__/start-sync-model-guard.test.ts +5 -5
- 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 +48 -22
- package/src/execution/__tests__/subagent-service-message-close.test.ts +29 -26
- package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +21 -8
- package/src/execution/__tests__/subagent-service-notify-gate.test.ts +27 -22
- package/src/execution/__tests__/subagent-service-parent-guard.test.ts +30 -29
- package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +45 -20
- 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__/sync-collect-recovery.test.ts +1117 -0
- package/src/execution/__tests__/temp-prompt.test.ts +1 -1
- package/src/execution/__tests__/timeout-integration.test.ts +6 -5
- package/src/execution/__tests__/tombstone-store.test.ts +1 -1
- 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 +12 -6
- package/src/execution/__tests__/ui-request-handler.test.ts +4 -3
- package/src/execution/__tests__/ui-request-queue.test.ts +142 -2
- package/src/execution/__tests__/worktree-git-ops.test.ts +1 -1
- 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 +2 -2
- package/src/execution/__tests__/worktree-reconcile.integration.test.ts +1 -1
- package/src/execution/__tests__/worktree-registry.test.ts +1 -1
- package/src/execution/agent-registry.ts +74 -30
- package/src/execution/agent-result-mapper.ts +3 -0
- package/src/execution/cold-resurrect.ts +199 -0
- package/src/execution/collect-coordinator.ts +200 -0
- package/src/execution/config.ts +77 -5
- 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/event-journal.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 +213 -53
- 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 +22 -112
- package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +15 -28
- package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +1 -1
- package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +12 -12
- package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +2 -2
- package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +21 -82
- package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +4 -7
- 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 +4 -2
- 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/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} +1 -1
- 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/{session-runner.ts → engine/engines/pi/session-runner.ts} +617 -412
- 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 +54 -27
- package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +321 -0
- package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +13 -12
- 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 +20 -8
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +77 -110
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +26 -320
- 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 +48 -148
- package/src/execution/engine/engines/zcode/appserver-launcher.ts +167 -0
- package/src/execution/engine/engines/zcode/connection.ts +24 -11
- package/src/execution/engine/engines/zcode/constants.ts +75 -71
- package/src/execution/engine/engines/zcode/golden-sample.ts +8 -46
- package/src/execution/engine/engines/zcode/parser.ts +12 -346
- package/src/execution/engine/engines/zcode/preparer.ts +12 -143
- package/src/execution/engine/engines/zcode/session-channel.ts +351 -39
- package/src/execution/engine/engines/zcode/zcode-engine.ts +665 -760
- 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 +24 -6
- package/src/execution/engine/routing.ts +75 -1
- package/src/execution/engine/types.ts +30 -103
- package/src/execution/execution-record.ts +229 -144
- package/src/execution/finalize-record.ts +141 -99
- package/src/execution/idle-gc.ts +28 -0
- package/src/execution/lifecycle-predicates.ts +1 -1
- package/src/execution/notifier.ts +234 -25
- package/src/execution/notify-host.ts +232 -0
- package/src/execution/record-entry.ts +16 -0
- package/src/execution/record-store.ts +280 -103
- package/src/execution/round-settlement.ts +93 -0
- package/src/execution/session-file-gc.ts +96 -62
- package/src/execution/session-reconstructor.ts +256 -143
- package/src/execution/sessions-index.ts +117 -47
- package/src/execution/settled-watchdog.ts +264 -52
- package/src/execution/subagent-actions-core.ts +84 -28
- package/src/execution/subagent-service.ts +1225 -964
- package/src/execution/subprocess-agent-runner.ts +144 -97
- package/src/execution/sync-rebuild.ts +87 -0
- package/src/execution/types.ts +72 -0
- package/src/execution/ui-request-handler-factory.ts +102 -74
- package/src/execution/ui-request-queue.ts +50 -19
- package/src/index.ts +442 -316
- package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +1 -1
- package/src/orchestration/__tests__/agent-call-stream.test.ts +2 -2
- package/src/orchestration/__tests__/args-validator.test.ts +14 -0
- package/src/orchestration/__tests__/config-loader.test.ts +1 -1
- package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +1 -1
- package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +11 -9
- package/src/orchestration/__tests__/execute-agent-call.test.ts +151 -11
- package/src/orchestration/__tests__/file-run-store-prune.test.ts +1 -1
- package/src/orchestration/__tests__/file-run-store-throttle.test.ts +1 -1
- package/src/orchestration/__tests__/file-run-store.test.ts +7 -6
- package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +2 -2
- package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +1 -1
- package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +3 -3
- package/src/orchestration/__tests__/lifecycle.test.ts +7 -5
- package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +1 -1
- package/src/orchestration/__tests__/review-fix-loop-scriptpath-failfast.test.ts +1 -1
- package/src/orchestration/__tests__/run-snapshot.test.ts +8 -5
- package/src/orchestration/__tests__/script-generate.test.ts +5 -5
- package/src/orchestration/__tests__/script-lint.test.ts +17 -0
- package/src/orchestration/__tests__/skill-discovery.test.ts +2 -2
- 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} +27 -19
- 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 +1 -1
- package/src/orchestration/__tests__/worker-script-builder.test.ts +1 -1
- package/src/orchestration/__tests__/workflow-files.test.ts +10 -10
- package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +1 -1
- package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +1 -1
- package/src/orchestration/agent-opts-resolver.ts +1 -1
- package/src/orchestration/args-validator.ts +53 -31
- package/src/orchestration/execute-agent-call.ts +19 -73
- package/src/orchestration/launcher.ts +128 -81
- package/src/orchestration/lifecycle.ts +43 -45
- package/src/orchestration/models/ports.ts +4 -4
- package/src/orchestration/models/run-runtime.ts +2 -2
- package/src/orchestration/models/trace.ts +1 -1
- package/src/orchestration/models/types.ts +111 -22
- package/src/orchestration/models/workflow-run.ts +1 -1
- package/src/orchestration/script-lint.ts +91 -58
- package/src/orchestration/{error-recovery.ts → worker-message-pump.ts} +157 -150
- package/src/shared/__tests__/atomic-write.test.ts +1 -1
- package/src/shared/__tests__/meta-parser.test.ts +151 -0
- package/src/shared/__tests__/resource-discovery-host-roots.test.ts +5 -5
- package/src/shared/__tests__/resource-discovery.test.ts +258 -215
- package/src/shared/atomic-write.ts +12 -9
- package/src/shared/meta-parser.ts +158 -84
- package/src/shared/model-ref.ts +15 -2
- package/src/shared/resource-discovery.ts +9 -183
- package/src/shared/schema-jsonify.ts +1 -1
- package/workflows/review-fix-loop-utils.cjs +151 -89
- package/workflows/review-fix-loop-utils.d.cts +287 -0
- 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__/preparer-appserver.test.ts +0 -387
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -710
- package/src/execution/engine/engines/zcode/appserver-home.ts +0 -442
- package/src/execution/engine/engines/zcode/appserver-probe.ts +0 -141
- package/src/execution/engine/engines/zcode/launcher.ts +0 -161
- package/src/execution/execute-options-mapper.ts +0 -115
- package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -288
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
// };
|
|
48
48
|
// });
|
|
49
49
|
// vi.mock("../alive-store.ts", () => ({ writeAliveMarker: vi.fn() }));
|
|
50
|
-
// vi.mock("../temp-prompt.ts", () => ({
|
|
50
|
+
// vi.mock("../engine/engines/pi/temp-prompt.ts", () => ({
|
|
51
51
|
// writePromptToTempFile: vi.fn(async (agent: string) => {
|
|
52
52
|
// const safeName = agent.replace(/[^\w.-]+/g, "_");
|
|
53
53
|
// return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
|
|
@@ -60,8 +60,10 @@ import type { ChildProcess } from "node:child_process";
|
|
|
60
60
|
import { EventEmitter } from "node:events";
|
|
61
61
|
import { PassThrough } from "node:stream";
|
|
62
62
|
|
|
63
|
+
import { vi } from "vitest";
|
|
64
|
+
|
|
63
65
|
import { createRecord } from "../../execution-record.ts";
|
|
64
|
-
import type { RunOptions, SessionRunnerContext } from "../../session-runner.ts";
|
|
66
|
+
import type { RunOptions, SessionRunnerContext } from "../../engine/engines/pi/session-runner.ts";
|
|
65
67
|
|
|
66
68
|
/** FakeChild 的假 pid(满足 ChildProcess.pid 形状,无真实进程语义)。 */
|
|
67
69
|
const FAKE_PID = 12345;
|
|
@@ -194,8 +196,9 @@ export function makeRecord(id = "run-1") {
|
|
|
194
196
|
return createRecord(id, {
|
|
195
197
|
agent: "general-purpose",
|
|
196
198
|
model: "test-model",
|
|
197
|
-
mode: "
|
|
199
|
+
mode: "background",
|
|
198
200
|
task: "do something",
|
|
201
|
+
slug: "spawn-mock",
|
|
199
202
|
startedAt: 1_000_000,
|
|
200
203
|
rootSessionId: "root-session",
|
|
201
204
|
parentRecordId: undefined,
|
|
@@ -240,3 +243,147 @@ export function makeCtx(overrides: Partial<SessionRunnerContext> = {}): SessionR
|
|
|
240
243
|
...overrides,
|
|
241
244
|
};
|
|
242
245
|
}
|
|
246
|
+
|
|
247
|
+
// ── mock 模块工厂(session-runner 系测试共享,从 keep-alive-no-progress /
|
|
248
|
+
// recursive-visibility-env 的逐字重复 mock 块收敛为单源)──
|
|
249
|
+
//
|
|
250
|
+
// 使用模式(vi.mock 工厂内 await import 本文件,免疫 hoist 时序):
|
|
251
|
+
// vi.mock("node:child_process", async () =>
|
|
252
|
+
// (await import("./helpers/spawn-mock.ts")).childProcessModule());
|
|
253
|
+
//
|
|
254
|
+
// 本段保持「无 session-runner / alive-store / session-pending 值依赖」——本文件会被
|
|
255
|
+
// 各 vi.mock 工厂 await import(求值时机 = 被 mock 模块首次请求),若再值依赖
|
|
256
|
+
// session-runner 会构成「mock 工厂 → 本文件 → session-runner → node:child_process
|
|
257
|
+
// (mock 求值中)」循环。需要值依赖这些模块的测试文件侧装配放
|
|
258
|
+
// session-runner-mocks.ts(只被测试文件顶层静态 import)。
|
|
259
|
+
|
|
260
|
+
/** 共享 logger mock 单例(复核失败 warn 留痕断言用;vi.clearAllMocks 统一重置)。 */
|
|
261
|
+
export const loggerMock = { debug: vi.fn(), warn: vi.fn(), error: vi.fn() };
|
|
262
|
+
|
|
263
|
+
/** "../../core/logger.ts" mock 工厂。 */
|
|
264
|
+
export function coreLoggerModule() {
|
|
265
|
+
return { getLogger: () => loggerMock };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** "node:child_process" mock 工厂(spawn 返回 FakeChild + execFile err-first 兜底)。 */
|
|
269
|
+
export function childProcessModule() {
|
|
270
|
+
return {
|
|
271
|
+
spawn: vi.fn(() => new FakeChild()),
|
|
272
|
+
// buildEnvBlock 用 execFile 异步取 git branch:默认 err-first 兜底(catch → branch=""),
|
|
273
|
+
// 形态同 worktree-manager.test.ts 的 setupExecFile
|
|
274
|
+
execFile: vi.fn(
|
|
275
|
+
(
|
|
276
|
+
_cmd: string,
|
|
277
|
+
_args: readonly string[],
|
|
278
|
+
_opts: unknown,
|
|
279
|
+
cb: (err: Error | null, stdout?: string, stderr?: string) => void,
|
|
280
|
+
) => cb(new Error("execFile not configured in this test")),
|
|
281
|
+
),
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** "node:fs" mock 工厂(fs 侧写方法 vi.fn 化,promises 保留 actual)。 */
|
|
286
|
+
export async function fsModule(): Promise<Record<string, unknown>> {
|
|
287
|
+
const actual = await vi.importActual<typeof import("node:fs")>("node:fs");
|
|
288
|
+
return {
|
|
289
|
+
default: {
|
|
290
|
+
...actual,
|
|
291
|
+
mkdirSync: vi.fn(),
|
|
292
|
+
existsSync: vi.fn(() => false),
|
|
293
|
+
appendFileSync: vi.fn(),
|
|
294
|
+
writeFileSync: vi.fn(),
|
|
295
|
+
readdirSync: vi.fn(() => []),
|
|
296
|
+
},
|
|
297
|
+
mkdirSync: vi.fn(),
|
|
298
|
+
existsSync: vi.fn(() => false),
|
|
299
|
+
appendFileSync: vi.fn(),
|
|
300
|
+
writeFileSync: vi.fn(),
|
|
301
|
+
readdirSync: vi.fn(() => []),
|
|
302
|
+
promises: actual.promises,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/** "../alive-store.ts" mock 工厂(keep-alive 系超集:marker 读写 + pid 存活判定兜底)。 */
|
|
307
|
+
export function aliveStoreModule() {
|
|
308
|
+
return {
|
|
309
|
+
writeAliveMarker: vi.fn(),
|
|
310
|
+
// [T5②] keep-alive 心跳刷新读取现有 marker id(缺失兜底 record.id)
|
|
311
|
+
readAliveMarker: vi.fn(() => undefined),
|
|
312
|
+
isProcessAlive: vi.fn(() => false),
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/** "../session-pending.ts" mock 工厂(keep-alive 判定统一 count>0:有活跃后代 → keep-alive 分支)。 */
|
|
317
|
+
export function sessionPendingModule() {
|
|
318
|
+
return {
|
|
319
|
+
readActivePendingFromSessionFile: vi.fn(() => ({ count: 1, recentUnregister: false })),
|
|
320
|
+
prunePendingCursor: vi.fn(),
|
|
321
|
+
listActivePendingFromSessionFile: vi.fn(() => ({ items: [] })),
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/** "../engine/engines/pi/temp-prompt.ts" mock 工厂(prompt 落 /tmp/fake-<agent>,收尾 no-op)。 */
|
|
326
|
+
export function tempPromptModule() {
|
|
327
|
+
return {
|
|
328
|
+
writePromptToTempFile: vi.fn(async (agent: string) => {
|
|
329
|
+
const safeName = agent.replace(/[^\w.-]+/g, "_");
|
|
330
|
+
return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
|
|
331
|
+
}),
|
|
332
|
+
cleanupTempPrompt: vi.fn(async () => {}),
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// ── recursive-visibility / schema-env 族 fixture(env 断言族专用,与上方 makeRecord 系
|
|
337
|
+
// 默认值不同:/fake/* 路径 + test/model 形态参与 PI_SUBAGENT_* 断言,逐字保留原值)──
|
|
338
|
+
|
|
339
|
+
/** 构造 env 注入断言族的 ExecutionRecord(可 override id/depth)。 */
|
|
340
|
+
export function makeVisibilityRecord(overrides: { id?: string; depth?: number } = {}) {
|
|
341
|
+
return createRecord(overrides.id ?? "sa-test-record", {
|
|
342
|
+
agent: "general-purpose",
|
|
343
|
+
model: "test/model",
|
|
344
|
+
mode: "background",
|
|
345
|
+
slug: "t",
|
|
346
|
+
task: "test task",
|
|
347
|
+
startedAt: Date.now(),
|
|
348
|
+
rootSessionId: "should-be-overridden-by-sessionRootId-source",
|
|
349
|
+
parentRecordId: undefined,
|
|
350
|
+
depth: overrides.depth ?? 0,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/** 构造 env 注入断言族的 RunOptions(可 override 关键字段)。 */
|
|
355
|
+
export function makeRunOpts(overrides: Partial<RunOptions> = {}): RunOptions {
|
|
356
|
+
return {
|
|
357
|
+
resolved: { model: { provider: "test", id: "model", name: "Model", reasoning: false }, thinkingLevel: undefined },
|
|
358
|
+
agentConfig: undefined,
|
|
359
|
+
appendSystemPrompt: undefined,
|
|
360
|
+
skillPath: undefined,
|
|
361
|
+
schema: undefined,
|
|
362
|
+
maxTurns: undefined,
|
|
363
|
+
graceTurns: undefined,
|
|
364
|
+
signal: undefined,
|
|
365
|
+
onEvent: undefined,
|
|
366
|
+
...overrides,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** 构造 env 注入断言族的 SessionRunnerContext(可 override 关键字段)。 */
|
|
371
|
+
export function makeVisibilityCtx(overrides: Partial<SessionRunnerContext> = {}): SessionRunnerContext {
|
|
372
|
+
return {
|
|
373
|
+
cwd: "/fake/cwd",
|
|
374
|
+
agentDir: "/fake/agent",
|
|
375
|
+
skillDirs: [],
|
|
376
|
+
mainCwd: "/fake/cwd",
|
|
377
|
+
sessionRootId: "root-main-session",
|
|
378
|
+
rootCwd: "/fake/cwd",
|
|
379
|
+
...overrides,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/** 取最近一次 spawn 调用传入的 env(跨进程身份注入断言用)。 */
|
|
384
|
+
export function getLastSpawnEnv<T extends { mock: { calls: unknown[][] } }>(
|
|
385
|
+
mockSpawn: T,
|
|
386
|
+
): Record<string, string | undefined> {
|
|
387
|
+
const opts = mockSpawn.mock.calls.at(-1)?.[2] as { env?: Record<string, string | undefined> } | undefined;
|
|
388
|
+
return opts?.env ?? {};
|
|
389
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// src/execution/__tests__/helpers/subagent-service-mocks.ts
|
|
2
|
+
//
|
|
3
|
+
// SubagentService 编排层测试(execute-and-await-worktree / execute-nesting /
|
|
4
|
+
// nested-visibility-env-propagation / recursive-visibility-baseline 四文件)共享的
|
|
5
|
+
// spawn 链 mock 模块工厂。原四文件各自内联 ~85 行逐字重复的 vi.mock 工厂体(fallow
|
|
6
|
+
// duplication 告警),收敛到本 module 单源——桩形变更改此文件,全量消费方同步生效。
|
|
7
|
+
//
|
|
8
|
+
// 消费方式(与 __tests__/helpers/system-page-mount.ts、spawn-mock.ts 同款先例):
|
|
9
|
+
// vi.mock 调用留在测试文件(mock 注册是文件作用域),import 本 module 的语句写在
|
|
10
|
+
// vi.mock 调用之前(工厂执行发生在被测模块链请求被 mock 模块时,届时本 module 已加载)。
|
|
11
|
+
//
|
|
12
|
+
// node:fs / ../alive-store.ts 两个工厂需要真实模块做基底(...actual 展开):真实实现
|
|
13
|
+
// 经工厂的 importOriginal 参数由测试文件传入——本 module 不能顶层静态 import 它们
|
|
14
|
+
// (测试文件的 vi.mock 注册先于一切 import,本 module 属其依赖图,静态 import 会拿到
|
|
15
|
+
// mock 版自身 → 展开循环)。
|
|
16
|
+
//
|
|
17
|
+
// FakeChild 复用 ./spawn-mock.ts 的 class(结构超集:多 stdin / exitCode / signalCode
|
|
18
|
+
// 字段,四文件原内联 FakeChild 只访问 stdout/stderr/kill/emit,行为等价)。
|
|
19
|
+
|
|
20
|
+
import { vi } from "vitest";
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
import { FakeChild, emitStdoutLine, sessionHeader } from "./spawn-mock.ts";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* node:child_process mock 模块。
|
|
28
|
+
* spawn → FakeChild(测试经 mockSpawn.mock.results 取回控制器);
|
|
29
|
+
* execFile → err-first 兜底(buildEnvBlock 的 git branch 调用失败 → catch → branch="")。
|
|
30
|
+
*/
|
|
31
|
+
export function childProcessModule() {
|
|
32
|
+
return {
|
|
33
|
+
spawn: vi.fn(() => new FakeChild()),
|
|
34
|
+
execFile: vi.fn(
|
|
35
|
+
(
|
|
36
|
+
_cmd: string,
|
|
37
|
+
_args: readonly string[],
|
|
38
|
+
_opts: unknown,
|
|
39
|
+
cb: (err: Error | null, stdout?: string, stderr?: string) => void,
|
|
40
|
+
) => cb(new Error("execFile not configured in this test")),
|
|
41
|
+
),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* node:fs mock 模块:同步方法 mock(runSpawn 用到的全部),promises 保留真实实现
|
|
47
|
+
* (temp-prompt 已整体 mock,不触发真实 I/O)。default 与顶层具名是两套独立 vi.fn
|
|
48
|
+
* 实例(与收敛前四文件内联形态同构)。
|
|
49
|
+
*/
|
|
50
|
+
// 显式返回类型(typeof import 表达式可命名)——否则推导的返回类型引用 fs 内部
|
|
51
|
+
// 未导出的 ReadStreamOptions/WriteStreamOptions,触发 TS4058 可命名性检查
|
|
52
|
+
export function fsSyncModule(
|
|
53
|
+
actual: typeof import("node:fs"),
|
|
54
|
+
): {
|
|
55
|
+
/** 真实 fs + 同步 mock 的合并视图(vi.mock 工厂消费面宽松) */
|
|
56
|
+
default: unknown;
|
|
57
|
+
promises: typeof import("node:fs")["promises"];
|
|
58
|
+
mkdirSync: ReturnType<typeof vi.fn>;
|
|
59
|
+
existsSync: ReturnType<typeof vi.fn>;
|
|
60
|
+
appendFileSync: ReturnType<typeof vi.fn>;
|
|
61
|
+
writeFileSync: ReturnType<typeof vi.fn>;
|
|
62
|
+
readdirSync: ReturnType<typeof vi.fn>;
|
|
63
|
+
} {
|
|
64
|
+
const syncMocks = () => ({
|
|
65
|
+
mkdirSync: vi.fn(),
|
|
66
|
+
existsSync: vi.fn(() => false),
|
|
67
|
+
appendFileSync: vi.fn(),
|
|
68
|
+
writeFileSync: vi.fn(),
|
|
69
|
+
readdirSync: vi.fn(() => []),
|
|
70
|
+
});
|
|
71
|
+
return {
|
|
72
|
+
default: { ...actual, ...syncMocks() },
|
|
73
|
+
...syncMocks(),
|
|
74
|
+
promises: actual.promises,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* ../alive-store.ts mock 模块:writeAliveMarker(runSpawn 写 .alive sidecar)+
|
|
80
|
+
* removeAliveMarker(finalizeRecord 收尾删 .alive)mock;其余导出保留真实实现
|
|
81
|
+
* (worktree-manager / record-store 消费,本组用例不涉及但保留以避免间接报错)。
|
|
82
|
+
*/
|
|
83
|
+
export function aliveStoreModule(actual: typeof import("../../alive-store.ts")) {
|
|
84
|
+
return {
|
|
85
|
+
...actual,
|
|
86
|
+
writeAliveMarker: vi.fn(),
|
|
87
|
+
removeAliveMarker: vi.fn(),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** ../finalized-marker.ts mock 模块(避免真实 fs 写 sidecar)。 */
|
|
92
|
+
export function finalizedMarkerModule() {
|
|
93
|
+
return {
|
|
94
|
+
writeFinalized: vi.fn(),
|
|
95
|
+
readFinalized: vi.fn(() => false),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* ../manifest-store.ts mock 模块。vi.fn 包裹构造器(普通 function,箭头函数不能被 new):
|
|
101
|
+
* 构造参数 recordsDir 可从 mock.calls 断言([MF-3] 目录统一验证用)。
|
|
102
|
+
* no-op 化的根因:writeManifest 真实链路的异步 console 经 worker RPC 回流,与 vitest
|
|
103
|
+
* teardown 形成 race(unhandled rejection / exit 1)——本组用例测编排逻辑不测持久化。
|
|
104
|
+
*/
|
|
105
|
+
export function manifestStoreModule() {
|
|
106
|
+
class FakeManifestStore {
|
|
107
|
+
writeManifest = vi.fn(async () => {});
|
|
108
|
+
readManifest = vi.fn(async () => null);
|
|
109
|
+
listAllSync = vi.fn(() => []);
|
|
110
|
+
recoverTmpFiles = vi.fn(async () => []);
|
|
111
|
+
}
|
|
112
|
+
return { ManifestStore: vi.fn(function (_recordsDir: string) { return new FakeManifestStore(); }) };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** ../engine/engines/pi/temp-prompt.ts mock 模块(固定路径,消除 fake-timers 下 flaky 竞态)。 */
|
|
116
|
+
export function tempPromptModule() {
|
|
117
|
+
return {
|
|
118
|
+
writePromptToTempFile: vi.fn(async (agent: string) => {
|
|
119
|
+
const safeName = agent.replace(/[^\w.-]+/g, "_");
|
|
120
|
+
return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
|
|
121
|
+
}),
|
|
122
|
+
cleanupTempPrompt: vi.fn(async () => {}),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 驱动 FakeChild 完成 session:写 header + 可选事件 + close(0)。
|
|
128
|
+
* runSpawn 在 close 后判定 success(exitCode=0),并跑 identity 补写 + finalizeRecord
|
|
129
|
+
* ——「让 runSpawn 自然 resolve」的标准收尾路径。
|
|
130
|
+
*/
|
|
131
|
+
export async function driveChildToCompletion(child: FakeChild, events: Record<string, unknown>[] = []): Promise<void> {
|
|
132
|
+
emitStdoutLine(child, sessionHeader());
|
|
133
|
+
for (const e of events) emitStdoutLine(child, e);
|
|
134
|
+
child.stdout.end();
|
|
135
|
+
child.stderr.end();
|
|
136
|
+
child.emit("close", 0);
|
|
137
|
+
}
|
|
@@ -13,102 +13,52 @@
|
|
|
13
13
|
// 语义保留,A1-1:显式 maxTurns<=0 与 fail-fast 降级也不挂)。
|
|
14
14
|
//
|
|
15
15
|
// mock 布局与 run-spawn-edges.test.ts 一致(FakeChild + mock session-pending)。
|
|
16
|
+
// mock 工厂与 loggerMock 单源收敛 helpers/spawn-mock.ts;vi.mocked 取回与
|
|
17
|
+
// spawn/keep-alive 前奏收敛 helpers/session-runner-mocks.ts(分工见后者头注释)。
|
|
16
18
|
|
|
17
|
-
import {
|
|
18
|
-
import * as fs from "node:fs";
|
|
19
|
-
|
|
20
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
19
|
+
import { describe, expect, it, vi } from "vitest";
|
|
21
20
|
|
|
22
21
|
// [A1-2③] Mock 共享 logger:复核失败的 warn 留痕可被断言(对齐 run-spawn-edges 模式)。
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}));
|
|
26
|
-
vi.mock("../../core/logger.ts", () => ({
|
|
27
|
-
getLogger: () => loggerMock,
|
|
28
|
-
}));
|
|
29
|
-
|
|
30
|
-
vi.mock("node:child_process", async () => {
|
|
31
|
-
const { FakeChild } = await import("./helpers/spawn-mock.ts");
|
|
32
|
-
return {
|
|
33
|
-
spawn: vi.fn(() => new FakeChild()),
|
|
34
|
-
execFile: vi.fn(
|
|
35
|
-
(
|
|
36
|
-
_cmd: string,
|
|
37
|
-
_args: readonly string[],
|
|
38
|
-
_opts: unknown,
|
|
39
|
-
cb: (err: Error | null, stdout?: string, stderr?: string) => void,
|
|
40
|
-
) => cb(new Error("execFile not configured in this test")),
|
|
41
|
-
),
|
|
42
|
-
};
|
|
43
|
-
});
|
|
22
|
+
vi.mock("../../core/logger.ts", async () =>
|
|
23
|
+
(await import("./helpers/spawn-mock.ts")).coreLoggerModule());
|
|
44
24
|
|
|
45
|
-
vi.mock("node:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
mkdirSync: vi.fn(),
|
|
51
|
-
existsSync: vi.fn(() => false),
|
|
52
|
-
appendFileSync: vi.fn(),
|
|
53
|
-
writeFileSync: vi.fn(),
|
|
54
|
-
readdirSync: vi.fn(() => []),
|
|
55
|
-
},
|
|
56
|
-
mkdirSync: vi.fn(),
|
|
57
|
-
existsSync: vi.fn(() => false),
|
|
58
|
-
appendFileSync: vi.fn(),
|
|
59
|
-
writeFileSync: vi.fn(),
|
|
60
|
-
readdirSync: vi.fn(() => []),
|
|
61
|
-
promises: actual.promises,
|
|
62
|
-
};
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
vi.mock("../alive-store.ts", () => ({
|
|
66
|
-
writeAliveMarker: vi.fn(),
|
|
67
|
-
// [T5②] keep-alive 心跳刷新读取现有 marker id(缺失兜底 record.id)
|
|
68
|
-
readAliveMarker: vi.fn(() => undefined),
|
|
69
|
-
isProcessAlive: vi.fn(() => false),
|
|
70
|
-
}));
|
|
25
|
+
vi.mock("node:child_process", async () =>
|
|
26
|
+
(await import("./helpers/spawn-mock.ts")).childProcessModule());
|
|
27
|
+
vi.mock("node:fs", async () => (await import("./helpers/spawn-mock.ts")).fsModule());
|
|
28
|
+
vi.mock("../alive-store.ts", async () =>
|
|
29
|
+
(await import("./helpers/spawn-mock.ts")).aliveStoreModule());
|
|
71
30
|
|
|
72
31
|
// keep-alive 判定:本文件统一 count>0(有活跃后代 → keep-alive 分支)。
|
|
73
|
-
vi.mock("../session-pending.ts", () =>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
vi.mock("../temp-prompt.ts", () => ({
|
|
80
|
-
writePromptToTempFile: vi.fn(async (agent: string) => {
|
|
81
|
-
const safeName = agent.replace(/[^\w.-]+/g, "_");
|
|
82
|
-
return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
|
|
83
|
-
}),
|
|
84
|
-
cleanupTempPrompt: vi.fn(async () => {}),
|
|
85
|
-
}));
|
|
32
|
+
vi.mock("../session-pending.ts", async () =>
|
|
33
|
+
(await import("./helpers/spawn-mock.ts")).sessionPendingModule());
|
|
34
|
+
|
|
35
|
+
vi.mock("../engine/engines/pi/temp-prompt.ts", async () =>
|
|
36
|
+
(await import("./helpers/spawn-mock.ts")).tempPromptModule());
|
|
86
37
|
|
|
87
38
|
import {
|
|
88
39
|
KEEP_ALIVE_NO_PROGRESS_TIMEOUT_MS,
|
|
89
40
|
maxTurnsToWatchdogMs,
|
|
90
|
-
runSpawn,
|
|
91
41
|
SPAWN_WATCHDOG_ENV,
|
|
92
|
-
} from "../session-runner.ts";
|
|
93
|
-
import { listActivePendingFromSessionFile, readActivePendingFromSessionFile } from "../session-pending.ts";
|
|
94
|
-
import { isProcessAlive, readAliveMarker, writeAliveMarker } from "../alive-store.ts";
|
|
42
|
+
} from "../engine/engines/pi/session-runner.ts";
|
|
95
43
|
import {
|
|
96
44
|
emitStdoutLine,
|
|
97
45
|
type FakeChild,
|
|
98
46
|
lastSpawnedChild as lastSpawnedChildOf,
|
|
99
|
-
|
|
47
|
+
loggerMock,
|
|
100
48
|
makeOpts,
|
|
101
|
-
makeRecord,
|
|
102
49
|
waitForSpawn as waitForSpawnOf,
|
|
103
50
|
} from "./helpers/spawn-mock.ts";
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
51
|
+
import { keepAliveTestHooks, spawnAndReachKeepAlive, takeSessionRunnerMocks } from "./helpers/session-runner-mocks.ts";
|
|
52
|
+
|
|
53
|
+
const {
|
|
54
|
+
mockSpawn,
|
|
55
|
+
mockPending,
|
|
56
|
+
mockListPending,
|
|
57
|
+
mockReadAliveMarker,
|
|
58
|
+
mockWriteAliveMarker,
|
|
59
|
+
mockIsProcessAlive,
|
|
60
|
+
mockReaddirSync,
|
|
61
|
+
} = takeSessionRunnerMocks();
|
|
112
62
|
|
|
113
63
|
/** A1-2 复核用例的后代 pid(存/死两形态,对齐 descendant-sweep.test.ts 数值风格)。 */
|
|
114
64
|
const LIVE_DESCENDANT_PID = 424_242;
|
|
@@ -117,48 +67,22 @@ const DEAD_DESCENDANT_PID = 999_999;
|
|
|
117
67
|
const lastSpawnedChild = (): FakeChild => lastSpawnedChildOf(mockSpawn);
|
|
118
68
|
const waitForSpawn = (timeoutMs = 1000): Promise<void> => waitForSpawnOf(mockSpawn, timeoutMs);
|
|
119
69
|
|
|
120
|
-
/**
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
emitStdoutLine(child, sessionHeaderLine());
|
|
133
|
-
emitStdoutLine(child, { type: "agent_end", messages: [], willRetry: false });
|
|
134
|
-
await new Promise((r) => setImmediate(r));
|
|
135
|
-
return {
|
|
136
|
-
child,
|
|
137
|
-
finish: (code = 143) => {
|
|
138
|
-
child.stdout.end();
|
|
139
|
-
child.stderr.end();
|
|
140
|
-
child.emit("close", code);
|
|
141
|
-
return promise as Promise<Awaited<ReturnType<typeof runSpawn>>>;
|
|
142
|
-
},
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function sessionHeaderLine(): Record<string, unknown> {
|
|
147
|
-
return { type: "session", id: "sess-ka", timestamp: "2026-09-01T00:00:00.000Z", cwd: "/tmp/test" };
|
|
70
|
+
/** A1-2 复核用例的差集后代装配:pending 项 + session 文件名 + marker + pid 存活形态。 */
|
|
71
|
+
function mockDescendant(sessionId: string, pid: number, alive: boolean): void {
|
|
72
|
+
mockListPending.mockReturnValue({
|
|
73
|
+
items: [{ id: "bg-1", sessionId, type: "session" }],
|
|
74
|
+
});
|
|
75
|
+
mockReaddirSync.mockReturnValue([`20260901T000000-000_${sessionId}.jsonl`]);
|
|
76
|
+
mockReadAliveMarker.mockImplementation((f: unknown) =>
|
|
77
|
+
String(f).endsWith(`_${sessionId}.jsonl`)
|
|
78
|
+
? { pid, id: sessionId, startedAt: 0 }
|
|
79
|
+
: undefined,
|
|
80
|
+
);
|
|
81
|
+
mockIsProcessAlive.mockImplementation((p: number) => (p === pid ? alive : false));
|
|
148
82
|
}
|
|
149
83
|
|
|
150
84
|
describe("[T2-①] keep-alive 裸缺省无进展检测上界(P-T2 降级路径 B)", () => {
|
|
151
|
-
|
|
152
|
-
vi.clearAllMocks();
|
|
153
|
-
vi.stubEnv(SPAWN_WATCHDOG_ENV, "");
|
|
154
|
-
mockPending.mockReturnValue({ count: 1, recentUnregister: false });
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
afterEach(() => {
|
|
158
|
-
vi.restoreAllMocks();
|
|
159
|
-
vi.unstubAllEnvs();
|
|
160
|
-
vi.useRealTimers();
|
|
161
|
-
});
|
|
85
|
+
keepAliveTestHooks(mockPending);
|
|
162
86
|
|
|
163
87
|
it("裸缺省:agent_end keep-alive 挂无进展 timer,连续静默 30min 触发 SIGTERM 处置", async () => {
|
|
164
88
|
const { child, finish } = await spawnAndReachKeepAlive();
|
|
@@ -235,16 +159,7 @@ describe("[T2-①] keep-alive 裸缺省无进展检测上界(P-T2 降级路径
|
|
|
235
159
|
mockPending
|
|
236
160
|
.mockReturnValueOnce({ count: 1, recentUnregister: false })
|
|
237
161
|
.mockReturnValueOnce({ count: 0, recentUnregister: false });
|
|
238
|
-
const
|
|
239
|
-
const promise = runSpawn(record, "Task: keep-then-final", makeOpts(), makeCtx());
|
|
240
|
-
await waitForSpawn();
|
|
241
|
-
const child = lastSpawnedChild();
|
|
242
|
-
|
|
243
|
-
vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout", "Date"] });
|
|
244
|
-
emitStdoutLine(child, sessionHeaderLine());
|
|
245
|
-
// 第一次 agent_end:keep-alive(挂无进展 timer)
|
|
246
|
-
emitStdoutLine(child, { type: "agent_end", messages: [], willRetry: false });
|
|
247
|
-
await new Promise((r) => setImmediate(r));
|
|
162
|
+
const { child, promise } = await spawnAndReachKeepAlive(undefined, "Task: keep-then-final");
|
|
248
163
|
expect(child.killed).toBe(false);
|
|
249
164
|
|
|
250
165
|
// 第二次 agent_end:后代已完成 → final kill(keep-alive 结束,无进展 timer 同撤)
|
|
@@ -253,10 +168,15 @@ describe("[T2-①] keep-alive 裸缺省无进展检测上界(P-T2 降级路径
|
|
|
253
168
|
expect(child.killed).toBe(true);
|
|
254
169
|
|
|
255
170
|
// 反证 timer 已撤:advance 30min 不再有任何残留 kill 副作用(进程已 close 收尾)
|
|
171
|
+
// 真实 ChildProcess 语义:被 SIGTERM 杀死 → 先 exit(null,'SIGTERM') 再 close。只发
|
|
172
|
+
// close 不发 exit 时 killChain 升级窗口不结算(waitForExit 挂 exit 事件),下方
|
|
173
|
+
// advance 30min+ 会越过 30s grace 误触发 SIGKILL 升级——补 exit 忠实模拟进程已死。
|
|
174
|
+
child.signalCode = "SIGTERM";
|
|
175
|
+
child.emit("exit", null, "SIGTERM");
|
|
256
176
|
child.stdout.end();
|
|
257
177
|
child.stderr.end();
|
|
258
178
|
child.emit("close", 143);
|
|
259
|
-
const result =
|
|
179
|
+
const result = await promise;
|
|
260
180
|
expect(result.success).toBe(true);
|
|
261
181
|
await vi.advanceTimersByTimeAsync(KEEP_ALIVE_NO_PROGRESS_TIMEOUT_MS + 60_000);
|
|
262
182
|
expect(child.killSignal).toBe("SIGTERM"); // 仅 final kill 一次,无二次信号
|
|
@@ -296,16 +216,7 @@ describe("[T2-①] keep-alive 裸缺省无进展检测上界(P-T2 降级路径
|
|
|
296
216
|
|
|
297
217
|
it("A1-2①: fire 复核有存活活跃后代 → 视为有进展重挂(30min 固定复核节奏),不处置层主", async () => {
|
|
298
218
|
// 后代 pending 差集非空 + pid 存活(与 descendant sweep 同源判据)
|
|
299
|
-
|
|
300
|
-
items: [{ id: "bg-1", sessionId: "ka-desc-sess", type: "session" }],
|
|
301
|
-
});
|
|
302
|
-
mockReaddirSync.mockReturnValue(["20260901T000000-000_ka-desc-sess.jsonl"]);
|
|
303
|
-
mockReadAliveMarker.mockImplementation((f: unknown) =>
|
|
304
|
-
String(f).endsWith("_ka-desc-sess.jsonl")
|
|
305
|
-
? { pid: LIVE_DESCENDANT_PID, id: "ka-desc-sess", startedAt: 0 }
|
|
306
|
-
: undefined,
|
|
307
|
-
);
|
|
308
|
-
mockIsProcessAlive.mockImplementation((pid: number) => pid === LIVE_DESCENDANT_PID);
|
|
219
|
+
mockDescendant("ka-desc-sess", LIVE_DESCENDANT_PID, true);
|
|
309
220
|
|
|
310
221
|
const { child, finish } = await spawnAndReachKeepAlive();
|
|
311
222
|
expect(child.killed).toBe(false);
|
|
@@ -336,16 +247,7 @@ describe("[T2-①] keep-alive 裸缺省无进展检测上界(P-T2 降级路径
|
|
|
336
247
|
});
|
|
337
248
|
|
|
338
249
|
it("A1-2②: fire 复核后代差集非空但 pid 全部已死 → 真静默,执行处置", async () => {
|
|
339
|
-
|
|
340
|
-
items: [{ id: "bg-1", sessionId: "ka-dead-sess", type: "session" }],
|
|
341
|
-
});
|
|
342
|
-
mockReaddirSync.mockReturnValue(["20260901T000000-000_ka-dead-sess.jsonl"]);
|
|
343
|
-
mockReadAliveMarker.mockImplementation((f: unknown) =>
|
|
344
|
-
String(f).endsWith("_ka-dead-sess.jsonl")
|
|
345
|
-
? { pid: DEAD_DESCENDANT_PID, id: "ka-dead-sess", startedAt: 0 }
|
|
346
|
-
: undefined,
|
|
347
|
-
);
|
|
348
|
-
mockIsProcessAlive.mockReturnValue(false); // 全部已死
|
|
250
|
+
mockDescendant("ka-dead-sess", DEAD_DESCENDANT_PID, false); // 差集非空但 pid 全部已死
|
|
349
251
|
|
|
350
252
|
const { child, finish } = await spawnAndReachKeepAlive();
|
|
351
253
|
|
|
@@ -56,7 +56,7 @@ vi.mock("../session-pending.ts", () => ({
|
|
|
56
56
|
prunePendingCursor: vi.fn(),
|
|
57
57
|
listActivePendingFromSessionFile: vi.fn(() => ({ items: [] })),
|
|
58
58
|
}));
|
|
59
|
-
vi.mock("../temp-prompt.ts", () => ({
|
|
59
|
+
vi.mock("../engine/engines/pi/temp-prompt.ts", () => ({
|
|
60
60
|
writePromptToTempFile: vi.fn(async (agent: string) => {
|
|
61
61
|
const safeName = agent.replace(/[^\w.-]+/g, "_");
|
|
62
62
|
return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
|
|
@@ -64,7 +64,7 @@ vi.mock("../temp-prompt.ts", () => ({
|
|
|
64
64
|
cleanupTempPrompt: vi.fn(async () => {}),
|
|
65
65
|
}));
|
|
66
66
|
|
|
67
|
-
import { killAllSpawnedChildren, runSpawn, spawnedChildren } from "../session-runner.ts";
|
|
67
|
+
import { killAllSpawnedChildren, runSpawn, spawnedChildren } from "../engine/engines/pi/session-runner.ts";
|
|
68
68
|
import {
|
|
69
69
|
emitStdoutLine,
|
|
70
70
|
type FakeChild,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
disarmIdleTimer,
|
|
11
11
|
_resetLifecycleState,
|
|
12
12
|
} from "../lifecycle-manager.ts";
|
|
13
|
-
import { spawnedChildren } from "../session-runner.ts";
|
|
13
|
+
import { spawnedChildren } from "../engine/engines/pi/session-runner.ts";
|
|
14
14
|
import type { ChildProcess } from "node:child_process";
|
|
15
15
|
import type { ExecutionRecord } from "../types.ts";
|
|
16
16
|
|
|
@@ -25,7 +25,6 @@ function makeRecord(overrides: Partial<ExecutionRecord> = {}): ExecutionRecord {
|
|
|
25
25
|
thinkingLevel: undefined,
|
|
26
26
|
mode: "background",
|
|
27
27
|
task: "test",
|
|
28
|
-
slug: "test",
|
|
29
28
|
startedAt: Date.now(),
|
|
30
29
|
rootSessionId: "root",
|
|
31
30
|
parentRecordId: undefined,
|
|
@@ -22,7 +22,7 @@ function makeBaseManifest(overrides: Partial<ManifestRecord> = {}): ManifestReco
|
|
|
22
22
|
id: "rec-test",
|
|
23
23
|
rootSessionId: "session-main",
|
|
24
24
|
agentName: "worker",
|
|
25
|
-
status: "
|
|
25
|
+
status: "closed",
|
|
26
26
|
createdAt: 1000,
|
|
27
27
|
...overrides,
|
|
28
28
|
};
|
|
@@ -38,7 +38,7 @@ describe("ManifestStore — parentRecordId 落盘 (M3a)", () => {
|
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
afterEach(() => {
|
|
41
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
41
|
+
fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
// ── TC-m3a-new-record-parentid ──
|
|
@@ -71,7 +71,7 @@ describe("ManifestStore — parentRecordId 落盘 (M3a)", () => {
|
|
|
71
71
|
id: "rec-old",
|
|
72
72
|
rootSessionId: "session-main",
|
|
73
73
|
agentName: "worker",
|
|
74
|
-
status: "
|
|
74
|
+
status: "closed",
|
|
75
75
|
createdAt: 2000,
|
|
76
76
|
// 无 parentRecordId —— 旧版本写的
|
|
77
77
|
};
|
|
@@ -104,7 +104,7 @@ describe("ManifestStore — parentRecordId 落盘 (M3a)", () => {
|
|
|
104
104
|
id: "rec-minimal",
|
|
105
105
|
rootSessionId: "session-main",
|
|
106
106
|
agentName: "worker",
|
|
107
|
-
status: "
|
|
107
|
+
status: "closed",
|
|
108
108
|
createdAt: 3000,
|
|
109
109
|
};
|
|
110
110
|
fs.writeFileSync(
|
|
@@ -54,7 +54,7 @@ describe("T5④ recoverTmpFiles per-file tolerance", () => {
|
|
|
54
54
|
|
|
55
55
|
afterEach(() => {
|
|
56
56
|
vi.restoreAllMocks();
|
|
57
|
-
fs.rmSync(dir, { recursive: true, force: true });
|
|
57
|
+
fs.rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
58
58
|
});
|
|
59
59
|
|
|
60
60
|
it("processes remaining tmp files when one unlink fails mid-loop", async () => {
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
maxTurnsToWatchdogMs,
|
|
24
24
|
resolveSpawnWatchdogMs,
|
|
25
25
|
SPAWN_WATCHDOG_ENV,
|
|
26
|
-
} from "../session-runner.ts";
|
|
26
|
+
} from "../engine/engines/pi/session-runner.ts";
|
|
27
27
|
|
|
28
28
|
const MIN_MS = 60_000;
|
|
29
29
|
/** watchdog floor(session-runner WATCHDOG_FLOOR_MINUTES=30 的毫秒值)。 */
|