@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
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
deriveSessionFilePath,
|
|
10
10
|
findSessionFileByHeaderId,
|
|
11
11
|
parseSpawnLine,
|
|
12
|
-
} from "../spawn-event-adapter.ts";
|
|
12
|
+
} from "../engine/engines/pi/spawn-event-adapter.ts";
|
|
13
13
|
|
|
14
14
|
describe("parseSpawnLine", () => {
|
|
15
15
|
describe("空白行", () => {
|
|
@@ -150,14 +150,14 @@ describe("findSessionFileByHeaderId", () => {
|
|
|
150
150
|
fs.writeFileSync(expectedFile, "{}");
|
|
151
151
|
const result = findSessionFileByHeaderId(tmpDir, "sid-456");
|
|
152
152
|
expect(result).toBe(expectedFile);
|
|
153
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
153
|
+
fs.rmSync(tmpDir, { recursive: true, maxRetries: 5, retryDelay: 20 });
|
|
154
154
|
});
|
|
155
155
|
|
|
156
156
|
it("无匹配返回 undefined", () => {
|
|
157
157
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "spawn-adapter-test-"));
|
|
158
158
|
const result = findSessionFileByHeaderId(tmpDir, "nonexistent");
|
|
159
159
|
expect(result).toBeUndefined();
|
|
160
|
-
fs.rmSync(tmpDir, { recursive: true });
|
|
160
|
+
fs.rmSync(tmpDir, { recursive: true, maxRetries: 5, retryDelay: 20 });
|
|
161
161
|
});
|
|
162
162
|
|
|
163
163
|
it("目录不存在返回 undefined(不抛错)", () => {
|
|
@@ -71,7 +71,7 @@ vi.mock("../alive-store.ts", () => ({
|
|
|
71
71
|
writeAliveMarker: vi.fn(),
|
|
72
72
|
}));
|
|
73
73
|
|
|
74
|
-
vi.mock("../temp-prompt.ts", () => ({
|
|
74
|
+
vi.mock("../engine/engines/pi/temp-prompt.ts", () => ({
|
|
75
75
|
writePromptToTempFile: vi.fn(async (agent: string, content: string) => {
|
|
76
76
|
captured.content = content;
|
|
77
77
|
const safeName = agent.replace(/[^\w.-]+/g, "_");
|
|
@@ -83,7 +83,7 @@ vi.mock("../temp-prompt.ts", () => ({
|
|
|
83
83
|
import { spawn } from "node:child_process";
|
|
84
84
|
|
|
85
85
|
import { createRecord } from "../execution-record.ts";
|
|
86
|
-
import { runSpawn, type RunOptions, type SessionRunnerContext } from "../session-runner.ts";
|
|
86
|
+
import { runSpawn, type RunOptions, type SessionRunnerContext } from "../engine/engines/pi/session-runner.ts";
|
|
87
87
|
|
|
88
88
|
const mockSpawn = vi.mocked(spawn);
|
|
89
89
|
|
|
@@ -117,8 +117,9 @@ function makeRecord() {
|
|
|
117
117
|
return createRecord("wt-guidance-1", {
|
|
118
118
|
agent: "general-purpose",
|
|
119
119
|
model: "test/model",
|
|
120
|
-
mode: "
|
|
120
|
+
mode: "background",
|
|
121
121
|
task: "test task",
|
|
122
|
+
slug: "test",
|
|
122
123
|
startedAt: Date.now(),
|
|
123
124
|
rootSessionId: "s1",
|
|
124
125
|
parentRecordId: undefined,
|
|
@@ -128,7 +129,7 @@ function makeRecord() {
|
|
|
128
129
|
|
|
129
130
|
function makeRunOpts(overrides: Partial<RunOptions> = {}): RunOptions {
|
|
130
131
|
return {
|
|
131
|
-
resolved: { model: { provider: "test", id: "model" }, thinkingLevel: undefined },
|
|
132
|
+
resolved: { model: { provider: "test", id: "model", name: "Model", reasoning: false }, thinkingLevel: undefined },
|
|
132
133
|
agentConfig: undefined,
|
|
133
134
|
appendSystemPrompt: undefined,
|
|
134
135
|
skillPath: undefined,
|
|
@@ -29,7 +29,7 @@ vi.mock("../../core/logger.ts", () => ({ getLogger: () => loggerMock }));
|
|
|
29
29
|
|
|
30
30
|
import type { EnginePort } from "../engine/port.ts";
|
|
31
31
|
import { clearEngines, getEngine, registerEngine } from "../engine/registry.ts";
|
|
32
|
-
import { getChildByRecord, killAllSpawnedChildren, spawnedChildren } from "../session-runner.ts";
|
|
32
|
+
import { getChildByRecord, killAllSpawnedChildren, spawnedChildren } from "../engine/engines/pi/session-runner.ts";
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* 假 child:只关心 killed 标记 + kill 调用(killAllSpawnedChildren 遍历用)。
|
|
@@ -63,6 +63,7 @@ function makeDisposingEngine(
|
|
|
63
63
|
resume: "unsupported",
|
|
64
64
|
interrupt: "kill-only",
|
|
65
65
|
permissionMode: "fixed",
|
|
66
|
+
maxTurns: false,
|
|
66
67
|
}),
|
|
67
68
|
probe: () =>
|
|
68
69
|
Promise.resolve({ ok: true, engineVersion: "0.0.0-test", checks: [{ name: "stub", ok: true }] }),
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
// src/execution/__tests__/start-collect-guard.test.ts
|
|
2
|
+
//
|
|
3
|
+
// startHandler 的 collect 语义(subagent-sync-collect U1 foundation):
|
|
4
|
+
// 1. E4 校验(设计 §3.1.5):conversation:true + collect:"sync" → immediate throw,
|
|
5
|
+
// 不产生半启动 record(execute 零调用);文案指引「sync 仅支持 one-shot,
|
|
6
|
+
// 去掉 conversation 或 collect」;
|
|
7
|
+
// 2. 合法组合放行:collect 透传 service.execute(ExecuteOptions.collect);
|
|
8
|
+
// 3. start 响应 collect 段(设计 §3.1.1):仅 resolved=sync 附段(async 响应字节
|
|
9
|
+
// 零变化,G3);pendingSyncCount = 未闭合批 sync 成员数(collectMode=sync 且无
|
|
10
|
+
// batchFinalized;U2 偏差#4 接线后 record 落 collectMode,枚举天然含本条,无 +1)。
|
|
11
|
+
//
|
|
12
|
+
// stub SubagentService 仅实现 handler 触达的方法子集(subagent-actions-core.test.ts
|
|
13
|
+
// 同款形态);真实 service 行为归属 subagent-service 各自测试。
|
|
14
|
+
|
|
15
|
+
import { describe, expect, it, vi, type Mock } from "vitest";
|
|
16
|
+
|
|
17
|
+
import { startHandler } from "../subagent-actions-core.ts";
|
|
18
|
+
// SubagentService 的权威源是 subagent-service.ts(types.ts 只有 record/tool 契约)
|
|
19
|
+
import type { SubagentService } from "../subagent-service.ts";
|
|
20
|
+
import type { StatusFilter } from "../record-store.ts";
|
|
21
|
+
import type {
|
|
22
|
+
ExecutionHandle,
|
|
23
|
+
SubagentRecord,
|
|
24
|
+
SubagentToolDetails,
|
|
25
|
+
} from "../types.ts";
|
|
26
|
+
|
|
27
|
+
function makeDetails(): SubagentToolDetails {
|
|
28
|
+
return {
|
|
29
|
+
status: "running",
|
|
30
|
+
mode: "background",
|
|
31
|
+
agent: "/home/u/agents/worker.md",
|
|
32
|
+
model: "prov/m1",
|
|
33
|
+
thinkingLevel: undefined,
|
|
34
|
+
slug: "src-slug",
|
|
35
|
+
turns: 0,
|
|
36
|
+
totalTokens: 0,
|
|
37
|
+
elapsedSeconds: 0,
|
|
38
|
+
eventLog: [],
|
|
39
|
+
displayItems: [],
|
|
40
|
+
result: undefined,
|
|
41
|
+
error: undefined,
|
|
42
|
+
} as SubagentToolDetails;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function makeHandle(): ExecutionHandle {
|
|
46
|
+
return { mode: "background", subagentId: "sa-new", sessionFile: undefined, details: makeDetails() };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** collectRecords stub 数据行(只含 pendingSyncCount 口径判读所需字段)。 */
|
|
50
|
+
function makeRec(over: Partial<SubagentRecord> = {}): SubagentRecord {
|
|
51
|
+
return {
|
|
52
|
+
id: "sa-1",
|
|
53
|
+
agent: "/home/u/agents/worker.md",
|
|
54
|
+
task: "t",
|
|
55
|
+
slug: "worker",
|
|
56
|
+
status: "running",
|
|
57
|
+
mode: "background",
|
|
58
|
+
startedAt: 1000,
|
|
59
|
+
rootSessionId: "root-A",
|
|
60
|
+
parentRecordId: undefined,
|
|
61
|
+
depth: 0,
|
|
62
|
+
endedAt: undefined,
|
|
63
|
+
turns: 1,
|
|
64
|
+
totalTokens: 42,
|
|
65
|
+
model: "prov/m1",
|
|
66
|
+
thinkingLevel: undefined,
|
|
67
|
+
eventLog: [],
|
|
68
|
+
displayItems: [],
|
|
69
|
+
...over,
|
|
70
|
+
} as SubagentRecord;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Mock<T> 具名签名 + Omit 类原生同名成员:ReturnType<typeof vi.fn>
|
|
74
|
+
// (= Mock<Procedure | Constructable>)与类方法签名交叉会收窄 never
|
|
75
|
+
type ServiceStub = Omit<SubagentService, "execute" | "collectRecords" | "getCollectSyncDefault"> & {
|
|
76
|
+
// 带首参形态:断言消费 execute.mock.calls[0][0](ExecuteOptions),零参签名为空元组取不到
|
|
77
|
+
execute: Mock<(opts: Record<string, unknown>) => Promise<SubagentRecord>>;
|
|
78
|
+
collectRecords: Mock<(limit: number, statusFilter?: StatusFilter) => SubagentRecord[]>;
|
|
79
|
+
getCollectSyncDefault: Mock<() => "async" | "sync">;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/** startHandler 的测试包装:stub → SubagentService 断言收口一处(类私有成员不可结构模拟)。 */
|
|
83
|
+
function sh(svc: ServiceStub, input: Record<string, unknown>, signal?: AbortSignal) {
|
|
84
|
+
return startHandler(
|
|
85
|
+
svc as unknown as SubagentService,
|
|
86
|
+
input as Parameters<typeof startHandler>[1],
|
|
87
|
+
signal ?? undefined,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function makeService(
|
|
92
|
+
collectRecordsReturn: SubagentRecord[] = [],
|
|
93
|
+
collectSyncDefault: "async" | "sync" = "async",
|
|
94
|
+
): ServiceStub {
|
|
95
|
+
const execute = vi.fn(async (_opts: Record<string, unknown>) => makeHandle());
|
|
96
|
+
const collectRecords = vi.fn(() => collectRecordsReturn);
|
|
97
|
+
const getCollectSyncDefault = vi.fn(() => collectSyncDefault);
|
|
98
|
+
// [D4-① 适配] countPendingSyncRecords 经查询面聚合读 collectRecords(dev 重构后
|
|
99
|
+
// service.collectRecords 收窄 private,public 出口 = service.queries.collectRecords)。
|
|
100
|
+
return {
|
|
101
|
+
execute,
|
|
102
|
+
queries: { collectRecords },
|
|
103
|
+
collectRecords,
|
|
104
|
+
getCollectSyncDefault,
|
|
105
|
+
} as unknown as ServiceStub;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const BASE_INPUT = { task: "do things", slug: "do-things" };
|
|
109
|
+
|
|
110
|
+
// ============================================================
|
|
111
|
+
// E4:conversation + collect:"sync" 即拒
|
|
112
|
+
// ============================================================
|
|
113
|
+
|
|
114
|
+
describe("startHandler E4 guard (conversation + collect:sync)", () => {
|
|
115
|
+
it("throws immediately and never calls execute (不产生半启动 record)", async () => {
|
|
116
|
+
const service = makeService();
|
|
117
|
+
await expect(
|
|
118
|
+
sh(service, { ...BASE_INPUT, conversation: true, collect: "sync" }, undefined),
|
|
119
|
+
).rejects.toThrow();
|
|
120
|
+
expect(service.execute).not.toHaveBeenCalled();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("error copy carries the design guidance verbatim (文案锚定)", async () => {
|
|
124
|
+
const service = makeService();
|
|
125
|
+
const err = await sh(service,
|
|
126
|
+
{ ...BASE_INPUT, conversation: true, collect: "sync" },
|
|
127
|
+
undefined,
|
|
128
|
+
).catch((e: unknown) => e as Error);
|
|
129
|
+
expect(err).toBeInstanceOf(Error);
|
|
130
|
+
expect((err as Error).message).toBe(
|
|
131
|
+
'collect:"sync" only supports one-shot subagents — it cannot be combined with conversation:true. ' +
|
|
132
|
+
'Remove either conversation or collect (use collect:"async", or omit it, for conversational subagents).',
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("allows conversation:true + collect:async (async 语义与 conversation 正交)", async () => {
|
|
137
|
+
const service = makeService();
|
|
138
|
+
const result = await sh(service,
|
|
139
|
+
{ ...BASE_INPUT, conversation: true, collect: "async" },
|
|
140
|
+
undefined,
|
|
141
|
+
);
|
|
142
|
+
expect(result.subagentId).toBe("sa-new");
|
|
143
|
+
expect(service.execute).toHaveBeenCalledTimes(1);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("allows collect:sync alone (one-shot sync 是合法主场景)", async () => {
|
|
147
|
+
const service = makeService();
|
|
148
|
+
const result = await sh(service, { ...BASE_INPUT, collect: "sync" }, undefined);
|
|
149
|
+
expect(result.subagentId).toBe("sa-new");
|
|
150
|
+
expect(service.execute).toHaveBeenCalledTimes(1);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("E4 also blocks conversation when config default is sync (偏差#3 接线:resolved 含 config 默认)", async () => {
|
|
154
|
+
const service = makeService([], "sync");
|
|
155
|
+
await expect(
|
|
156
|
+
sh(service, { ...BASE_INPUT, conversation: true }, undefined),
|
|
157
|
+
).rejects.toThrow('collect:"sync" only supports one-shot subagents');
|
|
158
|
+
expect(service.execute).not.toHaveBeenCalled();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("config default=sync applies when collect omitted (缺省读真实 config,U2 偏差#3)", async () => {
|
|
162
|
+
const service = makeService([], "sync");
|
|
163
|
+
const result = await sh(service, { ...BASE_INPUT }, undefined);
|
|
164
|
+
expect(result.response.collect).toBeDefined();
|
|
165
|
+
expect(service.getCollectSyncDefault).toHaveBeenCalledTimes(1);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("reads config default via service accessor when collect omitted (async 默认零附段)", async () => {
|
|
169
|
+
const service = makeService([], "async");
|
|
170
|
+
const result = await sh(service, { ...BASE_INPUT }, undefined);
|
|
171
|
+
expect(result.response.collect).toBeUndefined();
|
|
172
|
+
expect(service.getCollectSyncDefault).toHaveBeenCalledTimes(1);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// ============================================================
|
|
177
|
+
// collect 透传 + 响应 collect 段
|
|
178
|
+
// ============================================================
|
|
179
|
+
|
|
180
|
+
describe("startHandler collect forwarding + response collect segment", () => {
|
|
181
|
+
it("forwards collect to service.execute via ExecuteOptions.collect", async () => {
|
|
182
|
+
const service = makeService();
|
|
183
|
+
await sh(service, { ...BASE_INPUT, collect: "sync" }, undefined);
|
|
184
|
+
const opts = service.execute.mock.calls[0]?.[0] as { collect?: string };
|
|
185
|
+
expect(opts.collect).toBe("sync");
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("omits collect in ExecuteOptions when not provided (resolved≠sync 时保持 undefined,async 缺省语义)", async () => {
|
|
189
|
+
const service = makeService();
|
|
190
|
+
await sh(service, { ...BASE_INPUT }, undefined);
|
|
191
|
+
const opts = service.execute.mock.calls[0]?.[0] as { collect?: string };
|
|
192
|
+
expect(opts.collect).toBeUndefined();
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("B1:config default=sync + 省略 collect → execute 收 collect:\"sync\"(缺省解析作用于 record 本体)", async () => {
|
|
196
|
+
// 修复前:execute 收原始 input.collect=undefined → createRecordForMode 只认
|
|
197
|
+
// ==="sync" → record 走 async 逐条通知而响应声称已入批(设计 §3.1.3/types.ts:689
|
|
198
|
+
// 承诺「缺省 = config 默认」作用于 record)。修复后 resolved=sync 落值。
|
|
199
|
+
const service = makeService([], "sync");
|
|
200
|
+
await sh(service, { ...BASE_INPUT }, undefined);
|
|
201
|
+
const opts = service.execute.mock.calls[0]?.[0] as { collect?: string };
|
|
202
|
+
expect(opts.collect).toBe("sync");
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("B1:config default=sync 时显式 collect:\"async\" 优先(execute 收 \"async\")", async () => {
|
|
206
|
+
const service = makeService([], "sync");
|
|
207
|
+
await sh(service, { ...BASE_INPUT, collect: "async" }, undefined);
|
|
208
|
+
const opts = service.execute.mock.calls[0]?.[0] as { collect?: string };
|
|
209
|
+
expect(opts.collect).toBe("async");
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it("attaches collect segment counting this very record from the enumeration (U2 偏差#4:无 +1 补偿)", async () => {
|
|
213
|
+
// 本条 record(id=sa-new)已由 createRecordForMode 落 collectMode 入枚举:
|
|
214
|
+
// stub 枚举返回本条 + 另一未闭合 sync → count=2(无 +1 补偿即含本条)
|
|
215
|
+
const service = makeService([
|
|
216
|
+
makeRec({ id: "sa-new", collectMode: "sync" }),
|
|
217
|
+
makeRec({ id: "sa-other", collectMode: "sync", status: "running" }),
|
|
218
|
+
makeRec({ id: "sa-gone", collectMode: "sync", batchFinalized: true }),
|
|
219
|
+
]);
|
|
220
|
+
const result = await sh(service, { ...BASE_INPUT, collect: "sync" }, undefined);
|
|
221
|
+
expect(result.response.collect).toEqual({ mode: "sync", pendingSyncCount: 2 });
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it("reports the enumeration verbatim when no sync records exist yet (如实反映,无补偿)", async () => {
|
|
225
|
+
const service = makeService([]);
|
|
226
|
+
const result = await sh(service, { ...BASE_INPUT, collect: "sync" }, undefined);
|
|
227
|
+
expect(result.response.collect).toEqual({ mode: "sync", pendingSyncCount: 0 });
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it("counts pending unmarked sync members from the enumeration (跨轮续累口径)", async () => {
|
|
231
|
+
const service = makeService([
|
|
232
|
+
makeRec({ id: "sa-1", collectMode: "sync" }), // 未闭合(无 batchFinalized)→ 计入
|
|
233
|
+
makeRec({ id: "sa-2", collectMode: "sync", batchFinalized: true }), // 已离场 → 排除
|
|
234
|
+
makeRec({ id: "sa-3" }), // async → 排除
|
|
235
|
+
makeRec({ id: "sa-4", collectMode: "sync", batchFinalized: false }), // 显式 false → 计入
|
|
236
|
+
]);
|
|
237
|
+
const result = await sh(service, { ...BASE_INPUT, collect: "sync" }, undefined);
|
|
238
|
+
expect(result.response.collect).toEqual({ mode: "sync", pendingSyncCount: 2 });
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it("omits the collect segment for async starts (G3: async 响应字节零变化)", async () => {
|
|
242
|
+
for (const input of [{ ...BASE_INPUT }, { ...BASE_INPUT, collect: "async" as const }]) {
|
|
243
|
+
const service = makeService();
|
|
244
|
+
const result = await sh(service, input, undefined);
|
|
245
|
+
expect(result.response.collect).toBeUndefined();
|
|
246
|
+
// G3 字节锚:response 其余字段与既有形态全等
|
|
247
|
+
expect(result.response).toEqual({
|
|
248
|
+
status: "running",
|
|
249
|
+
mode: "background",
|
|
250
|
+
message: result.response.message,
|
|
251
|
+
notifyContract: "ledger+at-least-once",
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
});
|
|
@@ -30,14 +30,14 @@ vi.mock("node:child_process", async () => {
|
|
|
30
30
|
});
|
|
31
31
|
|
|
32
32
|
// buildSpawnArgs 包装为 spy(保留真实实现)——「非全等拒单不触达 spawn 参数组装」的承重断言。
|
|
33
|
-
vi.mock("../session-runner.ts", async (importOriginal) => {
|
|
34
|
-
const actual = await importOriginal<typeof import("../session-runner.ts")>();
|
|
33
|
+
vi.mock("../engine/engines/pi/session-runner.ts", async (importOriginal) => {
|
|
34
|
+
const actual = await importOriginal<typeof import("../engine/engines/pi/session-runner.ts")>();
|
|
35
35
|
return { ...actual, buildSpawnArgs: vi.fn(actual.buildSpawnArgs) };
|
|
36
36
|
});
|
|
37
37
|
|
|
38
38
|
import { ModelConfigService } from "../model-config-service.ts";
|
|
39
39
|
import type { ModelInfo, ModelRegistryLike } from "../model-resolver.ts";
|
|
40
|
-
import { buildSpawnArgs } from "../session-runner.ts";
|
|
40
|
+
import { buildSpawnArgs } from "../engine/engines/pi/session-runner.ts";
|
|
41
41
|
import type { PiLike } from "../subagent-service.ts";
|
|
42
42
|
import { SubagentService } from "../subagent-service.ts";
|
|
43
43
|
|
|
@@ -93,7 +93,7 @@ describe("start 同步期裁决(U1 验收 3:非全等 → isError 且无 spa
|
|
|
93
93
|
|
|
94
94
|
afterEach(() => {
|
|
95
95
|
vi.restoreAllMocks();
|
|
96
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
96
|
+
fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
it("小写变体入参 → execute 同步 reject(问句式报错),buildSpawnArgs/spawn/appendEntry 均未被调", async () => {
|
|
@@ -128,7 +128,7 @@ describe("start 同步期裁决(U1 验收 3:非全等 → isError 且无 spa
|
|
|
128
128
|
});
|
|
129
129
|
|
|
130
130
|
it("全等入参通过裁决(reject 不发生,放行链路触达 spawn 参数组装)——守卫零误伤对照", async () => {
|
|
131
|
-
//
|
|
131
|
+
// 放行路径会进入轮次 kick-off(detached);execute resolve 即视为裁决放行。
|
|
132
132
|
// spawn 本身由 FakeChild 缺失抛错收口——本断言只关心「越过了同步裁决」:
|
|
133
133
|
// execute 不 reject,且 buildSpawnArgs 已被调(或即将被调的 record 已创建)。
|
|
134
134
|
const promise = service.execute({
|
|
@@ -33,7 +33,7 @@ vi.mock("../../core/logger.ts", () => ({
|
|
|
33
33
|
getLogger: () => loggerMock,
|
|
34
34
|
}));
|
|
35
35
|
|
|
36
|
-
import { respond, sendGetStateCommand, sendPromptCommand } from "../stdin-writer.ts";
|
|
36
|
+
import { respond, sendGetStateCommand, sendPromptCommand } from "../engine/engines/pi/stdin-writer.ts";
|
|
37
37
|
|
|
38
38
|
// ── helpers ──
|
|
39
39
|
|
|
@@ -362,8 +362,10 @@ describe("sendGetStateCommand", () => {
|
|
|
362
362
|
describe("writeStdinLine 背压 — write 返回 false 时 warn 不 throw", () => {
|
|
363
363
|
it("child.stdin.write 返回 false → warn 一次,函数正常返回(不 throw)", () => {
|
|
364
364
|
// 构造 write 永远返回 false 的假 stream(模拟内核缓冲满/HWM 到达)
|
|
365
|
+
// 保留 vi.fn 引用(经 PassThrough 视图后 write 的 mock 面不可达)
|
|
366
|
+
const fakeWrite = vi.fn((_chunk: unknown) => false);
|
|
365
367
|
const fakeStdin = {
|
|
366
|
-
write:
|
|
368
|
+
write: fakeWrite,
|
|
367
369
|
destroyed: false,
|
|
368
370
|
} as unknown as PassThrough;
|
|
369
371
|
const child = { stdin: fakeStdin } as unknown as ChildProcess;
|
|
@@ -371,8 +373,8 @@ describe("writeStdinLine 背压 — write 返回 false 时 warn 不 throw", () =
|
|
|
371
373
|
expect(() => respond(child, "req-backpressure", { value: "x" })).not.toThrow();
|
|
372
374
|
|
|
373
375
|
// write 被调一次(写入 command 行)
|
|
374
|
-
expect(
|
|
375
|
-
const writtenArg =
|
|
376
|
+
expect(fakeWrite).toHaveBeenCalledTimes(1);
|
|
377
|
+
const writtenArg = fakeWrite.mock.calls[0]![0] as string;
|
|
376
378
|
expect(writtenArg).toContain("extension_ui_response");
|
|
377
379
|
expect(writtenArg).toContain("req-backpressure");
|
|
378
380
|
expect(writtenArg.endsWith("\n")).toBe(true);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// widget 私货通道退役步骤 2(E 方案 §6.3)测试,两层:
|
|
4
4
|
// 判定层:createBackgroundStream 真实函数(vi.importActual 绕过文件级 mock)——
|
|
5
5
|
// gui+relay 激活不创建;tui / relay 未激活 / headless 原样创建;sink=null 恒降级。
|
|
6
|
-
// 接线层:SubagentService.execute(background →
|
|
6
|
+
// 接线层:SubagentService.execute(background → kickOffChatRound 同步段)透传
|
|
7
7
|
// mode/streamSink 给工厂——mode 传错 = 判定失效,故接线面必须锁定。
|
|
8
8
|
//
|
|
9
9
|
// spawn 层 mock(FakeChild),与 nested-visibility-env-propagation.test.ts 同模式。
|
|
@@ -103,7 +103,7 @@ vi.mock("../manifest-store.ts", () => {
|
|
|
103
103
|
return { ManifestStore: vi.fn(function (_recordsDir: string) { return new FakeManifestStore(); }) };
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
-
vi.mock("../temp-prompt.ts", () => ({
|
|
106
|
+
vi.mock("../engine/engines/pi/temp-prompt.ts", () => ({
|
|
107
107
|
writePromptToTempFile: vi.fn(async (agent: string) => {
|
|
108
108
|
const safeName = agent.replace(/[^\w.-]+/g, "_");
|
|
109
109
|
return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
|
|
@@ -156,7 +156,7 @@ const ctxModel: ModelInfo = { id: "m", name: "M", provider: "p", reasoning: fals
|
|
|
156
156
|
|
|
157
157
|
function setupService(mode: ExtensionMode | undefined, sessionId = "retire-it"): SubagentService {
|
|
158
158
|
const agentDir = "/tmp/stream-retire-it";
|
|
159
|
-
const modelService = new ModelConfigService({ agentDir });
|
|
159
|
+
const modelService = new ModelConfigService({ agentDir, cwd: agentDir });
|
|
160
160
|
modelService.initModel({
|
|
161
161
|
modelRegistry: makeEmptyRegistry(),
|
|
162
162
|
sessionId,
|
|
@@ -176,7 +176,7 @@ function setupService(mode: ExtensionMode | undefined, sessionId = "retire-it"):
|
|
|
176
176
|
return service;
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
-
/** 等
|
|
179
|
+
/** 等 kickOffChatRound 的 detached 轮次执行走到 spawn(stream 创建先于此)。 */
|
|
180
180
|
async function waitForSpawnCall(): Promise<void> {
|
|
181
181
|
const baseline = mockSpawn.mock.calls.length;
|
|
182
182
|
const deadline = Date.now() + 2000;
|
|
@@ -187,7 +187,7 @@ async function waitForSpawnCall(): Promise<void> {
|
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
async function executeBackground(service: SubagentService): Promise<void> {
|
|
190
|
-
await service.execute({ task: "retire test", ctxModel });
|
|
190
|
+
await service.execute({ task: "retire test", slug: "test", ctxModel });
|
|
191
191
|
await waitForSpawnCall();
|
|
192
192
|
// 推进 detached runSpawn 收尾(FakeChild close),防句柄悬挂干扰后续用例
|
|
193
193
|
lastSpawnedChild().emit("close", 0);
|
|
@@ -234,7 +234,7 @@ describe("退役步骤 2 判定层:createBackgroundStream", () => {
|
|
|
234
234
|
|
|
235
235
|
// ── 接线层:service → 工厂的参数透传(mode 传错 = 判定失效)──
|
|
236
236
|
|
|
237
|
-
describe("退役步骤 2 接线层:
|
|
237
|
+
describe("退役步骤 2 接线层:kickOffChatRound 透传 mode/sink 给工厂", () => {
|
|
238
238
|
beforeEach(() => {
|
|
239
239
|
vi.clearAllMocks();
|
|
240
240
|
for (const key of RELAY_ENV_KEYS) delete process.env[key];
|
|
@@ -42,9 +42,9 @@ import type {
|
|
|
42
42
|
ExecutionHandle,
|
|
43
43
|
ExecutionRecord,
|
|
44
44
|
SubagentRecord,
|
|
45
|
-
SubagentService,
|
|
46
45
|
SubagentToolDetails,
|
|
47
46
|
} from "../types.ts";
|
|
47
|
+
import type { SubagentService } from "../subagent-service.ts";
|
|
48
48
|
|
|
49
49
|
// ── 时钟固定(duration 快照确定性,见文件头)──
|
|
50
50
|
const FROZEN_NOW = 1788189209000;
|
|
@@ -132,17 +132,41 @@ function makeRec(over: Partial<SubagentRecord> = {}): SubagentRecord {
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
function makeService(over: Record<string, unknown> = {}): SubagentService {
|
|
135
|
-
|
|
135
|
+
// 聚合面形态对齐 subagent-service.ts D4:queries(读模型)与 chatActions(对话
|
|
136
|
+
// action 面)分组挂载,mock 按同构 key 装配(over 仍传平铺 key,最小化快照用例改动)。
|
|
137
|
+
const m = {
|
|
136
138
|
execute: vi.fn(),
|
|
137
|
-
findRecord: vi.fn(() => undefined),
|
|
138
139
|
cancel: vi.fn(() => false),
|
|
140
|
+
findRecord: vi.fn(() => undefined),
|
|
139
141
|
collectRecords: vi.fn(() => [] as SubagentRecord[]),
|
|
140
142
|
getFullRecord: vi.fn(() => undefined as SubagentRecord | undefined),
|
|
141
143
|
lookupRecordAnyState: vi.fn(() => undefined as SubagentRecord | undefined),
|
|
142
144
|
getRecordForAction: vi.fn(),
|
|
143
145
|
closeSubagent: vi.fn(),
|
|
144
|
-
|
|
146
|
+
deliverChatMessage: vi.fn(),
|
|
147
|
+
// [U2] startHandler 缺省 collect 解析读真实 config(偏差#3 接线):stub 缺省 async
|
|
148
|
+
//(本文件不测 collect 语义,专项见 start-collect-guard.test.ts)。
|
|
149
|
+
getCollectSyncDefault: vi.fn(() => "async" as const),
|
|
145
150
|
...over,
|
|
151
|
+
};
|
|
152
|
+
return {
|
|
153
|
+
execute: m.execute,
|
|
154
|
+
cancel: m.cancel,
|
|
155
|
+
// [U2 偏差#3 接线] startHandler 经平铺访问器读 config 缺省 collect(真实 service
|
|
156
|
+
// 为平铺方法 subagent-service.ts:1786,非 queries 聚合面成员),stub 须同构挂载。
|
|
157
|
+
getCollectSyncDefault: m.getCollectSyncDefault,
|
|
158
|
+
queries: {
|
|
159
|
+
findRecord: m.findRecord,
|
|
160
|
+
lookupRecordAnyState: m.lookupRecordAnyState,
|
|
161
|
+
collectRecords: m.collectRecords,
|
|
162
|
+
getFullRecord: m.getFullRecord,
|
|
163
|
+
onChange: vi.fn(() => () => {}),
|
|
164
|
+
},
|
|
165
|
+
chatActions: {
|
|
166
|
+
getRecordForAction: m.getRecordForAction,
|
|
167
|
+
closeSubagent: m.closeSubagent,
|
|
168
|
+
deliverChatMessage: m.deliverChatMessage,
|
|
169
|
+
},
|
|
146
170
|
} as unknown as SubagentService;
|
|
147
171
|
}
|
|
148
172
|
|
|
@@ -290,7 +314,7 @@ describe("⛔4 startHandler(校验 + 启动,快照 = pi-sw 实测)", () =>
|
|
|
290
314
|
});
|
|
291
315
|
|
|
292
316
|
it("正常启动 → 领域对象全字段 + execute 参数(task/slug trim、拍平透传、ctxModel/signal)", async () => {
|
|
293
|
-
const execute = vi.fn(async (): Promise<ExecutionHandle> => ({
|
|
317
|
+
const execute = vi.fn(async (_opts: { task: string; slug?: string; agent?: string }): Promise<ExecutionHandle> => ({
|
|
294
318
|
mode: "background",
|
|
295
319
|
subagentId: "bg-9-abc",
|
|
296
320
|
sessionFile: "sess-9.jsonl",
|
|
@@ -334,7 +358,7 @@ describe("⛔4 startHandler(校验 + 启动,快照 = pi-sw 实测)", () =>
|
|
|
334
358
|
},
|
|
335
359
|
});
|
|
336
360
|
expect(execute).toHaveBeenCalledTimes(1);
|
|
337
|
-
expect(execute.mock.calls[0][0]).toEqual({
|
|
361
|
+
expect(execute.mock.calls[0]![0]).toEqual({
|
|
338
362
|
task: "long task", // trim 生效
|
|
339
363
|
slug: "long-running",
|
|
340
364
|
agent: "/x/agent.md",
|
|
@@ -489,6 +513,8 @@ describe("⛔4 cancelHandler(守卫 + 归属判定 + CAS 失败映射,快照
|
|
|
489
513
|
expect(
|
|
490
514
|
await errOf(() =>
|
|
491
515
|
cancelHandler(
|
|
516
|
+
// "sync" 是历史已裁撤值(ExecutionMode 现仅 "background")——经 as never
|
|
517
|
+
// 注入非法值专测守卫分支,运行时守卫按 !== "background" 拦截
|
|
492
518
|
makeService({ findRecord: vi.fn(() => makeRec({ id: "bg-1", mode: "sync" as never })) }),
|
|
493
519
|
{ subagentId: "bg-1" },
|
|
494
520
|
),
|
|
@@ -587,11 +613,11 @@ describe("⛔4 messageHandler(守卫 + upgrade + 投递,快照 = pi-sw 实
|
|
|
587
613
|
});
|
|
588
614
|
});
|
|
589
615
|
|
|
590
|
-
it("chatMode record →
|
|
591
|
-
const
|
|
616
|
+
it("chatMode record → deliverChatMessage(text trim + interrupt) + 领域对象", async () => {
|
|
617
|
+
const deliverChatMessage = vi.fn(async () => {});
|
|
592
618
|
const chatRecord = makeExecRecord({ id: "bg-1", chatMode: true, slug: "src-slug" });
|
|
593
619
|
const r = await messageHandler(
|
|
594
|
-
makeService({ getRecordForAction: vi.fn(() => chatRecord),
|
|
620
|
+
makeService({ getRecordForAction: vi.fn(() => chatRecord), deliverChatMessage }),
|
|
595
621
|
{ subagentId: "bg-1", text: " go on ", interrupt: true },
|
|
596
622
|
);
|
|
597
623
|
expect(r).toEqual({
|
|
@@ -600,18 +626,18 @@ describe("⛔4 messageHandler(守卫 + upgrade + 投递,快照 = pi-sw 实
|
|
|
600
626
|
slug: "src-slug",
|
|
601
627
|
response: { delivered: true },
|
|
602
628
|
});
|
|
603
|
-
expect(
|
|
629
|
+
expect(deliverChatMessage).toHaveBeenCalledWith(chatRecord, "go on", true);
|
|
604
630
|
});
|
|
605
631
|
|
|
606
632
|
it("one-shot upgrade:非 chatMode running record 收 message → 置位 chatMode 后投递", async () => {
|
|
607
|
-
const
|
|
633
|
+
const deliverChatMessage = vi.fn(async () => {});
|
|
608
634
|
const upgradeRec = makeExecRecord({ id: "bg-1", chatMode: false, status: "running" });
|
|
609
635
|
await messageHandler(
|
|
610
|
-
makeService({ getRecordForAction: vi.fn(() => upgradeRec),
|
|
636
|
+
makeService({ getRecordForAction: vi.fn(() => upgradeRec), deliverChatMessage }),
|
|
611
637
|
{ subagentId: "bg-1", text: "hi" },
|
|
612
638
|
);
|
|
613
639
|
expect(upgradeRec.chatMode).toBe(true);
|
|
614
|
-
expect(
|
|
640
|
+
expect(deliverChatMessage).toHaveBeenCalledWith(upgradeRec, "hi", false);
|
|
615
641
|
});
|
|
616
642
|
|
|
617
643
|
it("getRecordForAction 拒绝 + 无终态快照 → 原错误透传(文案最准原则)", async () => {
|
|
@@ -774,7 +800,7 @@ describe("⛔4 closeHandler(force 语义透传,快照 = pi-sw 实测)", ()
|
|
|
774
800
|
// forkFromHandler(守卫链 1–6)
|
|
775
801
|
// ============================================================
|
|
776
802
|
describe("⛔4 forkFromHandler(守卫链 + slug 派生 + prompt 包装,快照 = pi-sw 实测)", () => {
|
|
777
|
-
function makeForkService(source: SubagentRecord | undefined, execute = vi.fn(async (): Promise<ExecutionHandle> => ({
|
|
803
|
+
function makeForkService(source: SubagentRecord | undefined, execute = vi.fn(async (_opts: { task: string; slug?: string; agent?: string }): Promise<ExecutionHandle> => ({
|
|
778
804
|
mode: "background",
|
|
779
805
|
subagentId: "bg-new-1",
|
|
780
806
|
sessionFile: "sess-new.jsonl",
|
|
@@ -899,7 +925,7 @@ describe("⛔4 forkFromHandler(守卫链 + slug 派生 + prompt 包装,快
|
|
|
899
925
|
});
|
|
900
926
|
|
|
901
927
|
it("正常:显式 prompt → wrapForkFromPrompt 包装 + slug 派生 'src-slug-resumed' + execute 参数", async () => {
|
|
902
|
-
const execute = vi.fn(async (): Promise<ExecutionHandle> => ({
|
|
928
|
+
const execute = vi.fn(async (_opts: { task: string; slug?: string; agent?: string }): Promise<ExecutionHandle> => ({
|
|
903
929
|
mode: "background",
|
|
904
930
|
subagentId: "bg-new-1",
|
|
905
931
|
sessionFile: "sess-new.jsonl",
|
|
@@ -928,7 +954,7 @@ describe("⛔4 forkFromHandler(守卫链 + slug 派生 + prompt 包装,快
|
|
|
928
954
|
});
|
|
929
955
|
|
|
930
956
|
it("无 prompt → FORK_FROM_DEFAULT_PROMPT 逐字", async () => {
|
|
931
|
-
const execute = vi.fn(async (): Promise<ExecutionHandle> => ({
|
|
957
|
+
const execute = vi.fn(async (_opts: { task: string; slug?: string; agent?: string }): Promise<ExecutionHandle> => ({
|
|
932
958
|
mode: "background",
|
|
933
959
|
subagentId: "bg-new-2",
|
|
934
960
|
sessionFile: "sess-new-2.jsonl",
|
|
@@ -955,7 +981,7 @@ describe("⛔4 forkFromHandler(守卫链 + slug 派生 + prompt 包装,快
|
|
|
955
981
|
});
|
|
956
982
|
|
|
957
983
|
it("slug 派生:34 字符源 slug 截 27 + '-resumed';fallback 链 slug||agent||'resumed'", async () => {
|
|
958
|
-
const execute34 = vi.fn(async (): Promise<ExecutionHandle> => ({
|
|
984
|
+
const execute34 = vi.fn(async (_opts: { task: string; slug?: string; agent?: string }): Promise<ExecutionHandle> => ({
|
|
959
985
|
mode: "background",
|
|
960
986
|
subagentId: "bg-new-3",
|
|
961
987
|
sessionFile: undefined,
|
|
@@ -965,9 +991,9 @@ describe("⛔4 forkFromHandler(守卫链 + slug 派生 + prompt 包装,快
|
|
|
965
991
|
makeForkService(makeRec({ id: "bg-1", slug: "s".repeat(34), sessionFile: "sess-1.jsonl" }), execute34),
|
|
966
992
|
{ sourceSubagentId: "bg-1", prompt: "p" },
|
|
967
993
|
);
|
|
968
|
-
expect(execute34.mock.calls[0][0].slug).toBe(`${"s".repeat(27)}-resumed`);
|
|
994
|
+
expect(execute34.mock.calls[0]![0].slug).toBe(`${"s".repeat(27)}-resumed`);
|
|
969
995
|
|
|
970
|
-
const executeAgent = vi.fn(async (): Promise<ExecutionHandle> => ({
|
|
996
|
+
const executeAgent = vi.fn(async (_opts: { task: string; slug?: string; agent?: string }): Promise<ExecutionHandle> => ({
|
|
971
997
|
mode: "background",
|
|
972
998
|
subagentId: "bg-new-4",
|
|
973
999
|
sessionFile: undefined,
|
|
@@ -980,9 +1006,9 @@ describe("⛔4 forkFromHandler(守卫链 + slug 派生 + prompt 包装,快
|
|
|
980
1006
|
),
|
|
981
1007
|
{ sourceSubagentId: "bg-1", prompt: "p" },
|
|
982
1008
|
);
|
|
983
|
-
expect(executeAgent.mock.calls[0][0].slug).toBe("/home/u/agents/helper.md-resumed");
|
|
1009
|
+
expect(executeAgent.mock.calls[0]![0].slug).toBe("/home/u/agents/helper.md-resumed");
|
|
984
1010
|
|
|
985
|
-
const executeResumed = vi.fn(async (): Promise<ExecutionHandle> => ({
|
|
1011
|
+
const executeResumed = vi.fn(async (_opts: { task: string; slug?: string; agent?: string }): Promise<ExecutionHandle> => ({
|
|
986
1012
|
mode: "background",
|
|
987
1013
|
subagentId: "bg-new-5",
|
|
988
1014
|
sessionFile: undefined,
|
|
@@ -992,6 +1018,6 @@ describe("⛔4 forkFromHandler(守卫链 + slug 派生 + prompt 包装,快
|
|
|
992
1018
|
makeForkService(makeRec({ id: "bg-1", slug: "", agent: "", sessionFile: "sess-1.jsonl" }), executeResumed),
|
|
993
1019
|
{ sourceSubagentId: "bg-1", prompt: "p" },
|
|
994
1020
|
);
|
|
995
|
-
expect(executeResumed.mock.calls[0][0].slug).toBe("resumed-resumed");
|
|
1021
|
+
expect(executeResumed.mock.calls[0]![0].slug).toBe("resumed-resumed");
|
|
996
1022
|
});
|
|
997
1023
|
});
|