@zhushanwen/subagent-core 0.5.1 → 0.7.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/README.md +1 -1
- package/dist/chunk-KXVBIVOU.js +16 -0
- package/dist/chunk-T2SYBW3I.js +24 -0
- package/dist/chunk-VURUAGMM.js +2305 -0
- package/dist/{types-Dv4QhSJ_.d.cts → engine-discovery-scan-ocpM8FmI.d.cts} +835 -668
- package/dist/{types-Dv4QhSJ_.d.ts → engine-discovery-scan-ocpM8FmI.d.ts} +835 -668
- package/dist/execution/engine/engine-discovery-scan.cjs +2210 -0
- package/dist/execution/engine/engine-discovery-scan.d.cts +4 -0
- package/dist/execution/engine/engine-discovery-scan.d.ts +4 -0
- package/dist/execution/engine/engine-discovery-scan.js +20 -0
- package/dist/execution/engine/paths.cjs +6 -23
- package/dist/execution/engine/paths.d.cts +1 -8
- package/dist/execution/engine/paths.d.ts +1 -8
- package/dist/execution/engine/paths.js +1 -1
- package/dist/execution/relay-env.cjs +12 -28
- package/dist/execution/relay-env.d.cts +1 -33
- package/dist/execution/relay-env.d.ts +1 -33
- package/dist/execution/relay-env.js +5 -3
- package/dist/index.cjs +14123 -15467
- package/dist/index.d.cts +1179 -1540
- package/dist/index.d.ts +1179 -1540
- package/dist/index.js +7694 -10986
- package/dist.bundle/index.cjs +33485 -0
- package/package.json +20 -26
- package/src/__tests__/record-store-last-line.test.ts +16 -7
- package/src/core/error-message.ts +7 -6
- package/src/core/host-services.ts +8 -2
- package/src/core/notify-ports.ts +17 -2
- package/src/execution/__tests__/agent-registry.test.ts +1 -1
- package/src/execution/__tests__/alive-store.test.ts +1 -1
- package/src/execution/__tests__/chat-engine-routing.test.ts +62 -28
- package/src/execution/__tests__/chat-round-first-round-watchdog.test.ts +255 -0
- package/src/execution/__tests__/collect-budget.test.ts +291 -0
- package/src/execution/__tests__/collect-coordinator-service.test.ts +316 -0
- package/src/execution/__tests__/collect-coordinator.test.ts +371 -0
- package/src/execution/__tests__/collect-mixed-dispatch.test.ts +229 -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 +179 -237
- package/src/execution/__tests__/execute-and-await-worktree.test.ts +0 -2
- package/src/execution/__tests__/execute-nesting.test.ts +20 -13
- package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +21 -34
- package/src/execution/__tests__/finalized-marker.test.ts +1 -1
- package/src/execution/__tests__/gc-timer.test.ts +8 -23
- package/src/execution/__tests__/get-record-for-action-restart.test.ts +20 -10
- package/src/execution/__tests__/helpers/fake-engine-port.ts +187 -0
- package/src/execution/__tests__/helpers/spawn-mock.ts +36 -9
- package/src/execution/__tests__/helpers/subagent-service-mocks.ts +1 -1
- package/src/execution/__tests__/idle-gc.test.ts +200 -0
- package/src/execution/__tests__/lifecycle-predicates.test.ts +26 -21
- package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +1 -1
- package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +5 -2
- package/src/execution/__tests__/notify-batch.test.ts +500 -0
- package/src/execution/__tests__/notify-ledger.test.ts +27 -0
- package/src/execution/__tests__/reconcile-sweep.test.ts +200 -0
- package/src/execution/__tests__/record-entry-collect.test.ts +102 -0
- package/src/execution/__tests__/record-store.test.ts +60 -6
- package/src/execution/__tests__/recursive-visibility-baseline.test.ts +20 -13
- package/src/execution/__tests__/relay-env.test.ts +12 -1
- package/src/execution/__tests__/round-supervisor.test.ts +293 -0
- package/src/execution/__tests__/session-file-gc.test.ts +1 -1
- package/src/execution/__tests__/session-pending.test.ts +119 -17
- package/src/execution/__tests__/start-collect-guard.test.ts +255 -0
- package/src/execution/__tests__/stream-sink-retirement.test.ts +10 -10
- package/src/execution/__tests__/subagent-actions-core.test.ts +6 -0
- package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +14 -11
- package/src/execution/__tests__/subagent-service-notify-gate.test.ts +33 -30
- package/src/execution/__tests__/subagent-service-parent-guard.test.ts +2 -9
- package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +59 -129
- package/src/execution/__tests__/subagent-service.test.ts +2 -2
- package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +109 -7
- package/src/execution/__tests__/subprocess-agent-runner.test.ts +108 -36
- package/src/execution/__tests__/sync-collect-recovery.test.ts +1102 -0
- package/src/execution/__tests__/tombstone-store.test.ts +1 -1
- package/src/execution/__tests__/workflow-state-root.test.ts +232 -0
- package/src/execution/__tests__/worktree-git-ops.test.ts +1 -1
- 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 +6 -2
- package/src/execution/collect-coordinator.ts +200 -0
- package/src/execution/config.ts +77 -5
- package/src/execution/dialog-queue.ts +6 -73
- package/src/execution/engine/__tests__/common/capability-gate.test.ts +115 -15
- package/src/execution/engine/__tests__/common/errors.test.ts +11 -18
- package/src/execution/engine/__tests__/common/event-journal.test.ts +1 -1
- package/src/execution/engine/__tests__/common/kill-chain.test.ts +7 -0
- package/src/execution/engine/__tests__/common/nesting-guard.test.ts +9 -3
- package/src/execution/engine/__tests__/common/pool-manager.test.ts +2 -2
- package/src/execution/engine/__tests__/common/session-view-service.test.ts +90 -3
- package/src/execution/engine/__tests__/conformance/H9-test-disposition.md +116 -0
- package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/fake-engine-protocol.mjs +377 -0
- package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/recorded.fixture.json +331 -0
- package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/smoke-run.fixture.json +213 -0
- package/src/execution/engine/__tests__/conformance/chat-round-protocol.test.ts +230 -0
- package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +69 -42
- package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +4 -81
- package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +67 -44
- package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +82 -55
- package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +133 -70
- package/src/execution/engine/__tests__/conformance/engine-crash.test.ts +143 -0
- package/src/execution/engine/__tests__/conformance/fake-engine-capabilities.ts +19 -0
- package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +8 -3
- package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +6 -5
- package/src/execution/engine/__tests__/conformance/protocol-blackbox.test.ts +398 -0
- package/src/execution/engine/__tests__/conformance/registry-fork-filter.test.ts +221 -0
- package/src/execution/engine/__tests__/conformance/round-liveness-supervisor.test.ts +302 -0
- package/src/execution/engine/__tests__/conformance/static-spawn-guard.test.ts +58 -0
- package/src/execution/engine/__tests__/engine-discovery-scan.test.ts +558 -0
- package/src/execution/engine/__tests__/host-task-spec.test.ts +45 -0
- package/src/execution/engine/__tests__/model-ref-split.test.ts +74 -0
- package/src/execution/engine/__tests__/registry.test.ts +206 -1
- package/src/execution/engine/__tests__/routing.test.ts +97 -0
- package/src/execution/engine/__tests__/run-failure-worktree-cleanup.test.ts +97 -0
- package/src/execution/engine/client/__tests__/__fixtures__/fake-engine.mjs +294 -0
- package/src/execution/engine/client/__tests__/engine-client.test.ts +516 -0
- package/src/execution/engine/client/__tests__/mirror.test.ts +67 -0
- package/src/execution/engine/client/__tests__/pid-file.test.ts +257 -0
- package/src/execution/engine/client/__tests__/protocol-closure.test.ts +95 -0
- package/src/execution/engine/client/__tests__/remote-engine.test.ts +457 -0
- package/src/execution/engine/client/client-options.ts +103 -0
- package/src/execution/engine/client/engine-client.ts +687 -0
- package/src/execution/engine/client/mirror.ts +167 -0
- package/src/execution/engine/client/pid-file.ts +392 -0
- package/src/execution/engine/client/reaper.ts +95 -0
- package/src/execution/engine/client/remote-engine.ts +441 -0
- package/src/execution/engine/client/reverse-router.ts +255 -0
- package/src/execution/engine/common/capability-gate.ts +109 -22
- package/src/execution/engine/common/errors.ts +9 -25
- package/src/execution/engine/common/event-journal.ts +4 -50
- package/src/execution/engine/common/journal-replay.ts +3 -1
- package/src/execution/engine/common/kill-chain.ts +18 -251
- package/src/execution/engine/common/nesting-guard.ts +18 -114
- package/src/execution/engine/common/pool-manager.ts +4 -0
- package/src/execution/engine/common/session-view-service.ts +56 -84
- package/src/execution/engine/config.ts +151 -0
- package/src/execution/engine/d8-compat.ts +346 -0
- package/src/execution/engine/engine-discovery-roots.ts +107 -0
- package/src/execution/engine/engine-discovery-scan.ts +269 -0
- package/src/execution/engine/engine-discovery.ts +30 -7
- package/src/execution/engine/engine-inspect-package.ts +282 -0
- package/src/execution/engine/engine-manifest.ts +244 -0
- package/src/execution/engine/host/__tests__/host-askuser-endpoint.test.ts +123 -0
- package/src/execution/engine/host/__tests__/host-bridge.test.ts +135 -0
- package/src/execution/engine/host/__tests__/spawned-children.test.ts +73 -0
- package/src/execution/engine/host/host-bridge.ts +150 -0
- package/src/execution/engine/host/host-ui-endpoint.ts +47 -0
- package/src/execution/engine/host/pi-host-binding.ts +91 -0
- package/src/execution/engine/host/spawned-children.ts +139 -0
- package/src/execution/engine/host-task-spec.ts +29 -2
- package/src/execution/engine/model-validation.ts +55 -2
- package/src/execution/engine/paths.ts +16 -41
- package/src/execution/engine/port.ts +48 -2
- package/src/execution/engine/registry.ts +153 -34
- package/src/execution/engine/routing.ts +225 -52
- package/src/execution/engine/types.ts +25 -200
- package/src/execution/execution-record.ts +7 -0
- package/src/execution/finalize-record.ts +40 -11
- package/src/execution/idle-gc.ts +89 -14
- package/src/execution/lifecycle-manager.ts +10 -7
- package/src/execution/lifecycle-predicates.ts +10 -8
- package/src/execution/notifier.ts +234 -25
- package/src/execution/notify-host.ts +21 -1
- package/src/execution/record-entry.ts +16 -0
- package/src/execution/record-store.ts +189 -31
- package/src/execution/relay-env.ts +19 -36
- package/src/execution/round-supervisor/domain.ts +89 -0
- package/src/execution/round-supervisor/index.ts +33 -0
- package/src/execution/round-supervisor/notify-accounting.ts +87 -0
- package/src/execution/round-supervisor/reconcile-sweep.ts +235 -0
- package/src/execution/round-supervisor/service-binding.test.ts +661 -0
- package/src/execution/round-supervisor/service-binding.ts +273 -0
- package/src/execution/round-supervisor/supervisor.ts +426 -0
- package/src/execution/session-pending.ts +68 -74
- package/src/execution/settled-watchdog.ts +39 -0
- package/src/execution/subagent-actions-core.ts +62 -6
- package/src/execution/subagent-service.ts +1161 -406
- package/src/execution/subprocess-agent-runner.ts +16 -8
- package/src/execution/sync-rebuild.ts +87 -0
- package/src/execution/types.ts +104 -117
- package/src/execution/ui-channels.ts +13 -214
- package/src/execution/ui-request-handler-factory.ts +7 -4
- package/src/execution/ui-request-observability.ts +2 -2
- package/src/execution/workflow-state-root.ts +67 -0
- package/src/execution/worktree-git-ops.ts +5 -0
- package/src/execution/worktree-manager.ts +11 -1
- package/src/index.ts +55 -26
- package/src/orchestration/__tests__/config-loader.test.ts +1 -1
- package/src/orchestration/__tests__/execute-agent-call.test.ts +1 -1
- 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__/review-fix-loop-scriptpath-failfast.test.ts +1 -1
- package/src/orchestration/__tests__/script-generate.test.ts +5 -5
- package/src/orchestration/__tests__/skill-discovery.test.ts +2 -2
- package/src/orchestration/__tests__/workflow-files.test.ts +10 -10
- package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +1 -1
- package/src/orchestration/execute-agent-call.ts +1 -1
- package/src/orchestration/file-run-store.ts +144 -59
- package/src/orchestration/models/types.ts +43 -42
- package/src/shared/__tests__/atomic-write.test.ts +1 -1
- package/src/shared/__tests__/resource-discovery-host-roots.test.ts +5 -5
- package/src/shared/zcode-model-ref.ts +31 -0
- package/dist/chunk-43ONBFZX.js +0 -240
- package/dist/chunk-A4IVZWVX.js +0 -31
- package/dist/chunk-APZY4IME.js +0 -27
- package/dist/chunk-V3VHZ2VX.js +0 -59
- package/dist/execution/engine/engines/zcode/constants.cjs +0 -107
- package/dist/execution/engine/engines/zcode/constants.d.cts +0 -135
- package/dist/execution/engine/engines/zcode/constants.d.ts +0 -135
- package/dist/execution/engine/engines/zcode/constants.js +0 -54
- package/dist/execution/engine/engines/zcode/reader.cjs +0 -276
- package/dist/execution/engine/engines/zcode/reader.d.cts +0 -21
- package/dist/execution/engine/engines/zcode/reader.d.ts +0 -21
- package/dist/execution/engine/engines/zcode/reader.js +0 -9
- package/src/__tests__/append-system-prompt-assembly.test.ts +0 -243
- package/src/__tests__/fr4-get-state-handshake.test.ts +0 -125
- package/src/__tests__/session-runner.test.ts +0 -69
- package/src/execution/__tests__/ask-user-transit-e2e.test.ts +0 -490
- package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +0 -191
- package/src/execution/__tests__/descendant-sweep-guards.test.ts +0 -220
- package/src/execution/__tests__/descendant-sweep.test.ts +0 -269
- package/src/execution/__tests__/engine-model-validation.test.ts +0 -741
- package/src/execution/__tests__/epipe-fallback.test.ts +0 -248
- package/src/execution/__tests__/execution-runtime-face.test.ts +0 -272
- package/src/execution/__tests__/get-state-handshake.test.ts +0 -127
- package/src/execution/__tests__/helpers/session-runner-mocks.ts +0 -113
- package/src/execution/__tests__/keep-alive-no-progress.test.ts +0 -280
- package/src/execution/__tests__/kill-all-escalation.test.ts +0 -196
- package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +0 -113
- package/src/execution/__tests__/output-collector.test.ts +0 -469
- package/src/execution/__tests__/pi-invocation.test.ts +0 -134
- package/src/execution/__tests__/recursive-visibility-env.test.ts +0 -149
- package/src/execution/__tests__/rpc-mode.test.ts +0 -89
- package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +0 -387
- package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +0 -277
- package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +0 -254
- package/src/execution/__tests__/run-spawn-edges.test.ts +0 -1079
- package/src/execution/__tests__/run-spawn-integration.test.ts +0 -933
- package/src/execution/__tests__/run-spawn-resume.test.ts +0 -322
- package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +0 -196
- package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +0 -207
- package/src/execution/__tests__/service-kill-escalation.test.ts +0 -91
- package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +0 -110
- package/src/execution/__tests__/session-runner-close-prune.test.ts +0 -181
- package/src/execution/__tests__/session-runner-dispatch.test.ts +0 -489
- package/src/execution/__tests__/session-runner-epipe.test.ts +0 -179
- package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +0 -227
- package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +0 -290
- package/src/execution/__tests__/session-runner-schema-env.test.ts +0 -352
- package/src/execution/__tests__/settled-watchdog.test.ts +0 -524
- package/src/execution/__tests__/spawn-args.test.ts +0 -446
- package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +0 -192
- package/src/execution/__tests__/spawn-event-adapter.test.ts +0 -167
- package/src/execution/__tests__/spawn-worktree-guidance.test.ts +0 -208
- package/src/execution/__tests__/spawned-children.test.ts +0 -236
- package/src/execution/__tests__/start-sync-model-guard.test.ts +0 -150
- package/src/execution/__tests__/stdin-writer.test.ts +0 -464
- package/src/execution/__tests__/subagent-service-message-close.test.ts +0 -693
- package/src/execution/__tests__/temp-prompt.test.ts +0 -53
- package/src/execution/__tests__/timeout-integration.test.ts +0 -616
- package/src/execution/__tests__/turn-limiter-semantics.test.ts +0 -194
- package/src/execution/__tests__/turn-limiter.test.ts +0 -65
- package/src/execution/__tests__/ui-request-handler.test.ts +0 -205
- package/src/execution/__tests__/ui-request-queue.test.ts +0 -298
- package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +0 -230
- package/src/execution/engine/__tests__/common/schema-emulation.test.ts +0 -128
- package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +0 -149
- package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +0 -28
- package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +0 -128
- package/src/execution/engine/__tests__/paths.test.ts +0 -39
- package/src/execution/engine/common/schema-emulation.ts +0 -189
- package/src/execution/engine/common/session-view-projection.ts +0 -51
- package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +0 -615
- package/src/execution/engine/engines/pi/__tests__/reader.test.ts +0 -155
- package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +0 -257
- package/src/execution/engine/engines/pi/argv-mirror.ts +0 -135
- package/src/execution/engine/engines/pi/get-state-handshake.ts +0 -179
- package/src/execution/engine/engines/pi/output-collector.ts +0 -317
- package/src/execution/engine/engines/pi/pi-engine.ts +0 -811
- package/src/execution/engine/engines/pi/pi-invocation.ts +0 -120
- package/src/execution/engine/engines/pi/reader.ts +0 -59
- package/src/execution/engine/engines/pi/registration.ts +0 -37
- package/src/execution/engine/engines/pi/session-runner.ts +0 -2840
- package/src/execution/engine/engines/pi/spawn-event-adapter.ts +0 -404
- package/src/execution/engine/engines/pi/stdin-writer.ts +0 -199
- package/src/execution/engine/engines/pi/temp-prompt.ts +0 -62
- package/src/execution/engine/engines/pi/turn-limiter.ts +0 -102
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +0 -301
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +0 -36
- package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +0 -321
- package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +0 -473
- package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +0 -43
- package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +0 -124
- package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +0 -210
- package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +0 -71
- package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +0 -247
- package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +0 -451
- package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +0 -792
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +0 -782
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +0 -168
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +0 -210
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +0 -353
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +0 -282
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +0 -372
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +0 -145
- package/src/execution/engine/engines/zcode/appserver-launcher.ts +0 -167
- package/src/execution/engine/engines/zcode/connection.ts +0 -598
- package/src/execution/engine/engines/zcode/constants.ts +0 -176
- package/src/execution/engine/engines/zcode/golden-sample.ts +0 -47
- package/src/execution/engine/engines/zcode/parser.ts +0 -102
- package/src/execution/engine/engines/zcode/preparer.ts +0 -235
- package/src/execution/engine/engines/zcode/reader.ts +0 -381
- package/src/execution/engine/engines/zcode/registration.ts +0 -37
- package/src/execution/engine/engines/zcode/session-channel.ts +0 -968
- package/src/execution/engine/engines/zcode/zcode-engine.ts +0 -1431
- package/src/execution/round-settlement.ts +0 -93
- package/src/execution/ui-request-queue.ts +0 -215
- package/src/shared/schema-env.ts +0 -44
|
@@ -32,7 +32,6 @@ import {
|
|
|
32
32
|
finalizedMarkerModule,
|
|
33
33
|
fsSyncModule,
|
|
34
34
|
manifestStoreModule,
|
|
35
|
-
tempPromptModule,
|
|
36
35
|
} from "./helpers/subagent-service-mocks.ts";
|
|
37
36
|
|
|
38
37
|
vi.mock("node:child_process", () => childProcessModule());
|
|
@@ -40,11 +39,11 @@ vi.mock("node:fs", async (importOriginal) => fsSyncModule(await importOriginal<t
|
|
|
40
39
|
vi.mock("../alive-store.ts", async (importOriginal) => aliveStoreModule(await importOriginal<typeof import("../alive-store.ts")>()));
|
|
41
40
|
vi.mock("../finalized-marker.ts", () => finalizedMarkerModule());
|
|
42
41
|
vi.mock("../manifest-store.ts", () => manifestStoreModule());
|
|
43
|
-
vi.mock("../engine/engines/pi/temp-prompt.ts", () => tempPromptModule());
|
|
44
42
|
|
|
45
43
|
import { spawn } from "node:child_process";
|
|
46
44
|
|
|
47
|
-
import {
|
|
45
|
+
import { registerFakePiEngine, type FakePiEnginePort } from "./helpers/fake-engine-port.ts";
|
|
46
|
+
import { clearEngines } from "../engine/registry.ts";
|
|
48
47
|
import { ModelConfigService } from "../model-config-service.ts";
|
|
49
48
|
import type { ModelInfo, ModelRegistryLike } from "../model-resolver.ts";
|
|
50
49
|
import { MAX_FORK_DEPTH } from "../session-context-resolver.ts";
|
|
@@ -85,6 +84,15 @@ function setup(): SetupResult {
|
|
|
85
84
|
return { service };
|
|
86
85
|
}
|
|
87
86
|
|
|
87
|
+
/** [W3] 协议替身注册 + 受控 settle(原 driveChildToCompletion 的等价驱动面):
|
|
88
|
+
* execute 发起的 engine.run 由测试显式应答(轮终语义对齐 driveChild 的 close(0))。 */
|
|
89
|
+
function setupWithFakeEngine(): SetupResult & { fake: FakePiEnginePort } {
|
|
90
|
+
const base = setup();
|
|
91
|
+
clearEngines();
|
|
92
|
+
const fake = registerFakePiEngine();
|
|
93
|
+
return { ...base, fake };
|
|
94
|
+
}
|
|
95
|
+
|
|
88
96
|
const ctxModel: ModelInfo = { id: "m", name: "M", provider: "p", reasoning: false };
|
|
89
97
|
|
|
90
98
|
/** [D3-⑤] execNesting(公共层 ExecutionNestingContext).run 的 duck-type(绕过
|
|
@@ -103,15 +111,15 @@ describe("嵌套护栏 / 并发池 / 节流(D-030~D-033 回归锁)", () => {
|
|
|
103
111
|
// ============================================================
|
|
104
112
|
|
|
105
113
|
it("[D-032] background execute 调 pool.acquire(进池限流)", async () => {
|
|
106
|
-
const { service } =
|
|
114
|
+
const { service, fake } = setupWithFakeEngine();
|
|
107
115
|
|
|
108
116
|
const pool = Reflect.get(service, "pool") as { acquire: ReturnType<typeof vi.fn>; release: ReturnType<typeof vi.fn> };
|
|
109
117
|
const acquireSpy = vi.spyOn(pool, "acquire");
|
|
110
118
|
|
|
111
119
|
const execPromise = service.execute({ task: "bg in pool", slug: "test", ctxModel });
|
|
112
|
-
// detached
|
|
113
|
-
await
|
|
114
|
-
|
|
120
|
+
// detached kickOffChatRound → acquire。等替身 run 到位再驱动应答。
|
|
121
|
+
await vi.waitFor(() => expect(fake.runs.length).toBe(1));
|
|
122
|
+
fake.runs[0]!.settle({ content: "ok" });
|
|
115
123
|
|
|
116
124
|
// 等 detached promise 链跑完(kickOffChatRound 的 .then notify)
|
|
117
125
|
await new Promise<void>((r) => setTimeout(r, 10));
|
|
@@ -120,6 +128,7 @@ describe("嵌套护栏 / 并发池 / 节流(D-030~D-033 回归锁)", () => {
|
|
|
120
128
|
// background execute 立即返回 handle(不等完成)
|
|
121
129
|
const handle = await execPromise;
|
|
122
130
|
expect(handle.mode).toBe("background");
|
|
131
|
+
clearEngines();
|
|
123
132
|
});
|
|
124
133
|
|
|
125
134
|
// ============================================================
|
|
@@ -144,21 +153,19 @@ describe("嵌套护栏 / 并发池 / 节流(D-030~D-033 回归锁)", () => {
|
|
|
144
153
|
});
|
|
145
154
|
|
|
146
155
|
it("[D-033] execCtxAls depth=MAX-1 时 execute 不抛(nestingDepth=MAX 允许)", async () => {
|
|
147
|
-
const { service } =
|
|
156
|
+
const { service, fake } = setupWithFakeEngine();
|
|
148
157
|
|
|
149
158
|
const execNesting = Reflect.get(service, "execNesting") as ExecCtxAls;
|
|
150
159
|
|
|
151
160
|
const execPromise = execNesting.run({ recordId: "parent", depth: MAX_FORK_DEPTH - 1 }, () =>
|
|
152
161
|
service.execute({ task: "at limit", slug: "test", ctxModel }),
|
|
153
162
|
);
|
|
154
|
-
await
|
|
155
|
-
|
|
156
|
-
{ type: "turn_end" },
|
|
157
|
-
{ type: "message_end", message: { usage: { input: 1 } } },
|
|
158
|
-
]);
|
|
163
|
+
await vi.waitFor(() => expect(fake.runs.length).toBe(1));
|
|
164
|
+
fake.runs[0]!.settle({ content: "ok" });
|
|
159
165
|
const result = await execPromise;
|
|
160
166
|
|
|
161
167
|
expect(result.mode).toBe("background");
|
|
168
|
+
clearEngines();
|
|
162
169
|
});
|
|
163
170
|
|
|
164
171
|
// ============================================================
|
|
@@ -7,46 +7,31 @@
|
|
|
7
7
|
// 错误含 <available_subagents> 恢复指引,对齐 workflow name not found 反馈风格)。
|
|
8
8
|
//
|
|
9
9
|
// 本文件锁住:
|
|
10
|
-
// 1. 裸名/相对路径 agent ref → execute 同步 reject(Invalid agent ref
|
|
11
|
-
//
|
|
10
|
+
// 1. 裸名/相对路径 agent ref → execute 同步 reject(Invalid agent ref),无引擎
|
|
11
|
+
// 派发、无子 session、record 不落盘(engine.run/appendEntry 零触达)
|
|
12
12
|
// 2. 绝对路径但文件不存在 → reject(not found or unreadable)
|
|
13
13
|
// 3. 对照:不传 agent(默认 general-purpose)与合法绝对路径 ref 均不误伤
|
|
14
14
|
//
|
|
15
|
+
// [W3 改写] 原 vi.mock(inproc pi 引擎目录/session-runner)(buildSpawnArgs spy)随删件消亡
|
|
16
|
+
// ——「spawn 链路零触达」的结构性证据改经协议替身(engine.run 零调用)+ appendEntry
|
|
17
|
+
// 零触达承载;守卫位于同步裁决期、引擎派发前置的结构不变。
|
|
18
|
+
//
|
|
15
19
|
// harness 复用 start-sync-model-guard.test.ts(真实 SubagentService + tmpdir +
|
|
16
|
-
//
|
|
20
|
+
// 协议替身),保证守卫位于同步裁决期、引擎派发前置的结构性证据可断言。
|
|
17
21
|
|
|
18
|
-
import { spawn } from "node:child_process";
|
|
19
22
|
import * as fs from "node:fs";
|
|
20
23
|
import * as os from "node:os";
|
|
21
24
|
import * as path from "node:path";
|
|
22
25
|
|
|
23
26
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return {
|
|
28
|
-
...actual,
|
|
29
|
-
spawn: vi.fn(() => {
|
|
30
|
-
throw new Error("spawn must not be called on agent-ref rejection");
|
|
31
|
-
}),
|
|
32
|
-
};
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
// buildSpawnArgs 包装为 spy(保留真实实现)——「显式 ref 拒单不触达 spawn 参数组装」的承重断言。
|
|
36
|
-
vi.mock("../engine/engines/pi/session-runner.ts", async (importOriginal) => {
|
|
37
|
-
const actual = await importOriginal<typeof import("../engine/engines/pi/session-runner.ts")>();
|
|
38
|
-
return { ...actual, buildSpawnArgs: vi.fn(actual.buildSpawnArgs) };
|
|
39
|
-
});
|
|
40
|
-
|
|
28
|
+
import { registerFakePiEngine, type FakePiEnginePort } from "./helpers/fake-engine-port.ts";
|
|
29
|
+
import { clearEngines } from "../engine/registry.ts";
|
|
41
30
|
import { ModelConfigService } from "../model-config-service.ts";
|
|
42
31
|
import type { ModelInfo, ModelRegistryLike } from "../model-resolver.ts";
|
|
43
|
-
import { buildSpawnArgs } from "../engine/engines/pi/session-runner.ts";
|
|
44
32
|
import type { PiLike } from "../subagent-service.ts";
|
|
45
33
|
import { SubagentService } from "../subagent-service.ts";
|
|
46
34
|
|
|
47
|
-
const mockSpawn = vi.mocked(spawn);
|
|
48
|
-
const mockBuildSpawnArgs = vi.mocked(buildSpawnArgs);
|
|
49
|
-
|
|
50
35
|
// ── 工具(start-sync-model-guard 同款)──
|
|
51
36
|
|
|
52
37
|
function makeModel(over: Partial<ModelInfo> = {}): ModelInfo {
|
|
@@ -79,9 +64,12 @@ describe("显式 agent ref 失败报错(非静默降级 general-purpose)", (
|
|
|
79
64
|
let tmpDir: string;
|
|
80
65
|
let service: SubagentService;
|
|
81
66
|
let pi: PiLike;
|
|
67
|
+
let fake: FakePiEnginePort;
|
|
82
68
|
|
|
83
69
|
beforeEach(() => {
|
|
84
70
|
vi.clearAllMocks();
|
|
71
|
+
clearEngines();
|
|
72
|
+
fake = registerFakePiEngine();
|
|
85
73
|
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "sw-agent-ref-guard-"));
|
|
86
74
|
const modelService = new ModelConfigService({ agentDir: tmpDir, cwd: tmpDir });
|
|
87
75
|
modelService.initModel({
|
|
@@ -95,20 +83,20 @@ describe("显式 agent ref 失败报错(非静默降级 general-purpose)", (
|
|
|
95
83
|
|
|
96
84
|
afterEach(() => {
|
|
97
85
|
vi.restoreAllMocks();
|
|
98
|
-
|
|
86
|
+
clearEngines();
|
|
87
|
+
fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
99
88
|
});
|
|
100
89
|
|
|
101
|
-
// ── 拒绝路径:显式 ref 失败 → 同步 reject +
|
|
90
|
+
// ── 拒绝路径:显式 ref 失败 → 同步 reject + 引擎派发零触达 ──
|
|
102
91
|
|
|
103
92
|
it("裸名 agent ref('worker')→ execute 同步 reject(Invalid agent ref + 恢复指引)", async () => {
|
|
104
93
|
await expect(
|
|
105
94
|
service.execute({ task: "probe task", slug: "ref-guard", agent: "worker" }),
|
|
106
95
|
).rejects.toThrow(/Invalid agent ref: worker.*<available_subagents>/s);
|
|
107
96
|
|
|
108
|
-
//
|
|
109
|
-
// 旧行为(静默 undefined)会继续走 record 创建(appendEntry
|
|
110
|
-
expect(
|
|
111
|
-
expect(mockSpawn).not.toHaveBeenCalled();
|
|
97
|
+
// 引擎派发零触达 + record 未创建——非静默降级的结构性证据:
|
|
98
|
+
// 旧行为(静默 undefined)会继续走 record 创建(appendEntry 被调)后派发引擎。
|
|
99
|
+
expect(fake.runs.length).toBe(0);
|
|
112
100
|
expect(pi.appendEntry).not.toHaveBeenCalled();
|
|
113
101
|
});
|
|
114
102
|
|
|
@@ -117,7 +105,7 @@ describe("显式 agent ref 失败报错(非静默降级 general-purpose)", (
|
|
|
117
105
|
service.execute({ task: "probe task", slug: "ref-guard", agent: "./agents/worker.md" }),
|
|
118
106
|
).rejects.toThrow(/Invalid agent ref/);
|
|
119
107
|
|
|
120
|
-
expect(
|
|
108
|
+
expect(fake.runs.length).toBe(0);
|
|
121
109
|
expect(pi.appendEntry).not.toHaveBeenCalled();
|
|
122
110
|
});
|
|
123
111
|
|
|
@@ -127,8 +115,7 @@ describe("显式 agent ref 失败报错(非静默降级 general-purpose)", (
|
|
|
127
115
|
service.execute({ task: "probe task", slug: "ref-guard", agent: missing }),
|
|
128
116
|
).rejects.toThrow(/not found or unreadable.*<available_subagents>/s);
|
|
129
117
|
|
|
130
|
-
expect(
|
|
131
|
-
expect(mockSpawn).not.toHaveBeenCalled();
|
|
118
|
+
expect(fake.runs.length).toBe(0);
|
|
132
119
|
expect(pi.appendEntry).not.toHaveBeenCalled();
|
|
133
120
|
});
|
|
134
121
|
|
|
@@ -140,7 +127,7 @@ describe("显式 agent ref 失败报错(非静默降级 general-purpose)", (
|
|
|
140
127
|
slug: "ref-default-ok",
|
|
141
128
|
ctxModel: makeModel(),
|
|
142
129
|
});
|
|
143
|
-
// 默认语义下 identity 解析必须放行(execute 可能因 mock
|
|
130
|
+
// 默认语义下 identity 解析必须放行(execute 可能因 mock 环境引擎收尾而 reject,
|
|
144
131
|
// 但错误不能是 agent ref 拒绝);record 已创建(appendEntry 发生)。
|
|
145
132
|
try {
|
|
146
133
|
await promise;
|
|
@@ -20,7 +20,7 @@ describe("finalized-marker", () => {
|
|
|
20
20
|
sessionFile = path.join(tmpDir, "2026-01-01_uuid.jsonl");
|
|
21
21
|
});
|
|
22
22
|
afterEach(() => {
|
|
23
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
23
|
+
fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
describe("write → read 往返", () => {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// [W3 改写] 原 vi.mock(inproc pi 引擎目录/session-runner)(runSpawn 占位 + getChildByRecord
|
|
2
|
+
// 活进程判定)随 inproc pi 引擎目录 删除消亡——「有活进程」形态改经 core 侧 spawnedChildren
|
|
3
|
+
// 镜像注入(host/spawned-children.ts,isResumable 的 hasLiveProcessHandle 唯一读点)。
|
|
1
4
|
// src/execution/__tests__/gc-timer.test.ts
|
|
2
5
|
//
|
|
3
6
|
// [M8] idle record GC 定时器测试(subagent-service.ts startGcTimer,L485-507)。
|
|
@@ -12,7 +15,6 @@
|
|
|
12
15
|
// session-runner(import 链依赖 + isResumable 的 hasLiveProcessHandle 查询点)与 logger。
|
|
13
16
|
// timer 全部 vi.useFakeTimers,不真实等待。
|
|
14
17
|
|
|
15
|
-
import type { ChildProcess } from "node:child_process";
|
|
16
18
|
import * as fs from "node:fs";
|
|
17
19
|
import * as os from "node:os";
|
|
18
20
|
import * as path from "node:path";
|
|
@@ -24,17 +26,6 @@ const { loggerMock } = vi.hoisted(() => ({
|
|
|
24
26
|
}));
|
|
25
27
|
vi.mock("../../core/logger.ts", () => ({ getLogger: () => loggerMock }));
|
|
26
28
|
|
|
27
|
-
// mock session-runner:runSpawn/killAllSpawnedChildren 占位(import 链需要),
|
|
28
|
-
// getChildByRecord 默认返回 undefined(无活进程 → isResumable=true),用例内按需覆盖。
|
|
29
|
-
vi.mock("../engine/engines/pi/session-runner.ts", () => ({
|
|
30
|
-
runSpawn: vi.fn(),
|
|
31
|
-
killAllSpawnedChildren: vi.fn(),
|
|
32
|
-
getChildByRecord: vi.fn(() => undefined),
|
|
33
|
-
registerSpawnedChildForRecord: vi.fn(),
|
|
34
|
-
killRecordChildWithEscalation: vi.fn(),
|
|
35
|
-
spawnedChildren: new Map(),
|
|
36
|
-
}));
|
|
37
|
-
|
|
38
29
|
// [D8] idle-gc 归档时释放引擎池引用(releasePoolRef 经 getEngineDataDir 解析
|
|
39
30
|
// dataDir)——测试钉到模块级 holder(vi.mock factory 闭包只能引用模块级变量),
|
|
40
31
|
// beforeEach 刷新为当前 tmp agentDir,防触碰真实数据目录。
|
|
@@ -44,15 +35,13 @@ vi.mock("../engine/common/data-dir.ts", () => ({
|
|
|
44
35
|
}));
|
|
45
36
|
|
|
46
37
|
import { acquirePool } from "../engine/common/pool-manager.ts";
|
|
47
|
-
import {
|
|
38
|
+
import { coreSpawnedChildrenMirror, _resetCoreSpawnedChildrenMirrorForTest } from "../engine/host/spawned-children.ts";
|
|
48
39
|
import { createRecord } from "../execution-record.ts";
|
|
49
40
|
import { ModelConfigService } from "../model-config-service.ts";
|
|
50
41
|
import { RecordStore } from "../record-store.ts";
|
|
51
42
|
import { SubagentService } from "../subagent-service.ts";
|
|
52
43
|
import type { ExecutionRecord } from "../types.ts";
|
|
53
44
|
|
|
54
|
-
const mockGetChildByRecord = vi.mocked(getChildByRecord);
|
|
55
|
-
|
|
56
45
|
/** 与 startGcTimer 内部常量一致(1h 扫描 / 30 天 TTL)。 */
|
|
57
46
|
const GC_INTERVAL_MS = 60 * 60 * 1000;
|
|
58
47
|
const IDLE_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
@@ -111,15 +100,14 @@ describe("[M8] idle record GC 定时器(startGcTimer)", () => {
|
|
|
111
100
|
service = new SubagentService({ cwd: agentDir, modelService });
|
|
112
101
|
service.initSession({ pi: makePi(), sessionId: "root-session" });
|
|
113
102
|
store = (service as unknown as ServiceInternals).store;
|
|
114
|
-
mockGetChildByRecord.mockReset();
|
|
115
103
|
// 默认无活进程(isResumable=true)——Path B:进程已回收、可冷路径 resume 的等待续聊态
|
|
116
|
-
|
|
104
|
+
_resetCoreSpawnedChildrenMirrorForTest();
|
|
117
105
|
loggerMock.warn.mockClear();
|
|
118
106
|
});
|
|
119
107
|
|
|
120
108
|
afterEach(() => {
|
|
121
109
|
service.dispose();
|
|
122
|
-
fs.rmSync(agentDir, { recursive: true, force: true });
|
|
110
|
+
fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
123
111
|
vi.useRealTimers();
|
|
124
112
|
});
|
|
125
113
|
|
|
@@ -144,12 +132,9 @@ describe("[M8] idle record GC 定时器(startGcTimer)", () => {
|
|
|
144
132
|
|
|
145
133
|
it("有活进程的 record 不归档(isResumable=false,即使 idleSince 超 TTL)", () => {
|
|
146
134
|
const fireAt = Date.now() + GC_INTERVAL_MS;
|
|
147
|
-
// Path A:进程保活等待续聊(idle timer armed
|
|
135
|
+
// Path A:进程保活等待续聊(idle timer armed、镜像登记活句柄)——归档会让活进程失管
|
|
148
136
|
store.register(makeIdleRecord("sa-live", fireAt - IDLE_TTL_MS - 1));
|
|
149
|
-
|
|
150
|
-
(id: string): ChildProcess | undefined =>
|
|
151
|
-
id === "sa-live" ? ({ killed: false } as unknown as ChildProcess) : undefined,
|
|
152
|
-
);
|
|
137
|
+
coreSpawnedChildrenMirror().register("sa-live", { pid: 4242, killed: false });
|
|
153
138
|
|
|
154
139
|
service.startGcTimer();
|
|
155
140
|
vi.advanceTimersByTime(GC_INTERVAL_MS);
|
|
@@ -25,18 +25,13 @@ const { loggerMock } = vi.hoisted(() => ({
|
|
|
25
25
|
}));
|
|
26
26
|
vi.mock("../../core/logger.ts", () => ({ getLogger: () => loggerMock }));
|
|
27
27
|
|
|
28
|
-
// mock session-runner
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
runSpawn: vi.fn(),
|
|
32
|
-
killAllSpawnedChildren: vi.fn(),
|
|
33
|
-
getChildByRecord: vi.fn(() => undefined),
|
|
34
|
-
registerSpawnedChildForRecord: vi.fn(),
|
|
35
|
-
killRecordChildWithEscalation: vi.fn(),
|
|
36
|
-
spawnedChildren: new Map(),
|
|
37
|
-
}));
|
|
28
|
+
// [W3 改写] 原 vi.mock(inproc pi 引擎目录/session-runner) 随删件消亡——deliverChatMessage 走
|
|
29
|
+
// 协议 seam(registerFakePiEngine 替身;跨重启无活进程 = 引擎冷路径拒绝,续聊落
|
|
30
|
+
// resumeColdRound 守卫链)。
|
|
38
31
|
|
|
39
32
|
import { writeFinalized } from "../finalized-marker.ts";
|
|
33
|
+
import { registerFakePiEngine } from "./helpers/fake-engine-port.ts";
|
|
34
|
+
import { clearEngines } from "../engine/registry.ts";
|
|
40
35
|
import { ModelConfigService } from "../model-config-service.ts";
|
|
41
36
|
import { getSubagentSessionDir } from "../path-encoding.ts";
|
|
42
37
|
import { RecordStore } from "../record-store.ts";
|
|
@@ -224,6 +219,14 @@ describe("[M10] getRecordForAction 跨重启磁盘重建(S3 回归场景)",
|
|
|
224
219
|
// 不可序列化、重建后恒缺失,若无守卫 → 冷路径 resume 的 spawn cwd 静默回落主 repo
|
|
225
220
|
//(隔离失效,正是 worktree 要防的并发写冲突场景)。
|
|
226
221
|
it("[review round2] worktree record 跨重启重建 → hadWorktree 标记 + 续聊被拒(行动语言)", async () => {
|
|
222
|
+
clearEngines();
|
|
223
|
+
const fake = registerFakePiEngine();
|
|
224
|
+
// 跨重启无活进程 → 引擎冷路径拒绝 → 续聊落 resumeColdRound 守卫链
|
|
225
|
+
fake.interactMessageResult = {
|
|
226
|
+
ok: false,
|
|
227
|
+
code: "engine_session_not_resumable",
|
|
228
|
+
message: "no live process (cold path)",
|
|
229
|
+
};
|
|
227
230
|
writeSessionJsonl(sessionsDir, {
|
|
228
231
|
id: "sa-wt",
|
|
229
232
|
rootSessionId: "root-session",
|
|
@@ -243,6 +246,13 @@ describe("[M10] getRecordForAction 跨重启磁盘重建(S3 回归场景)",
|
|
|
243
246
|
});
|
|
244
247
|
|
|
245
248
|
it("[review round2] 非 worktree record 跨重启重建 → 续聊不受 worktree 守卫拦截(向后兼容)", async () => {
|
|
249
|
+
clearEngines();
|
|
250
|
+
const fake = registerFakePiEngine();
|
|
251
|
+
fake.interactMessageResult = {
|
|
252
|
+
ok: false,
|
|
253
|
+
code: "engine_session_not_resumable",
|
|
254
|
+
message: "no live process (cold path)",
|
|
255
|
+
};
|
|
246
256
|
// identity entry 无 worktree 字段(旧文件)→ found.worktree undefined → hadWorktree false
|
|
247
257
|
writeSessionJsonl(sessionsDir, { id: "sa-nowt", rootSessionId: "root-session" });
|
|
248
258
|
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// src/execution/__tests__/helpers/fake-engine-port.ts
|
|
2
|
+
//
|
|
3
|
+
// [W3 chat 域收口] SubagentService 编排层测试共享的 pi EnginePort 替身。
|
|
4
|
+
//
|
|
5
|
+
// 背景:chat 域执行链路自 W3 起经协议客户端(registry 'pi' 的 cli 形态 port——
|
|
6
|
+
// RemoteEngine)发往 pi-subagent-cli 引擎进程;原 inproc 链路的测试替身
|
|
7
|
+
//(vi.mock session-runner / FakeChild stdout pump)随 inproc pi 引擎目录 删除消亡。
|
|
8
|
+
// 本 helper 是协议 seam 的替身单源:测试经 registerEngine("pi", fake) 注入,
|
|
9
|
+
// 拿到 fake 实例后驱动 run/interact/roundLifecycle,断言协议交互。
|
|
10
|
+
//
|
|
11
|
+
// 消费方式(与 spawn-mock.ts 同款先例):
|
|
12
|
+
// const fake = registerFakePiEngine();
|
|
13
|
+
// await service.execute({ task: "...", conversation: true });
|
|
14
|
+
// fake.runs[0].settle({ content: "round text" }); // 模拟首轮 agent_settled 应答
|
|
15
|
+
// fake.interacts[0] // interact 受理断言
|
|
16
|
+
//
|
|
17
|
+
// 形态对照(协议语义,见 pi-subagent-cli chat-session.ts):
|
|
18
|
+
// - run 应答时点 = 首轮 agent_settled(outcome = 本轮内容非会话终态);
|
|
19
|
+
// - interact message 受理 = { ok: true, delivered: true };冷路径 = engine_session_not_resumable;
|
|
20
|
+
// - interact cancel = { ok: true, delivered: true }(引擎侧 SIGTERM → settle → 杀链);
|
|
21
|
+
// - roundLifecycle 相位由测试显式驱动(idle 帧先于 run 应答帧)。
|
|
22
|
+
|
|
23
|
+
import type { EnginePort, EngineRunResult, RunContext, ChatRoundRoute } from "../../../execution/engine/port.ts";
|
|
24
|
+
import type { AgentCallOpts } from "../../../orchestration/models/types.ts";
|
|
25
|
+
import type {
|
|
26
|
+
AgentOutcome,
|
|
27
|
+
EngineCapabilities,
|
|
28
|
+
EngineHandle,
|
|
29
|
+
InteractAction,
|
|
30
|
+
InteractResult,
|
|
31
|
+
ProbeReport,
|
|
32
|
+
SessionView,
|
|
33
|
+
} from "../../../execution/engine/types.ts";
|
|
34
|
+
import { registerEngine } from "../../../execution/engine/registry.ts";
|
|
35
|
+
|
|
36
|
+
/** 一次 run 调用的捕获(task + ctx + 结算控制面)。 */
|
|
37
|
+
export class FakeRun {
|
|
38
|
+
readonly task: AgentCallOpts;
|
|
39
|
+
readonly ctx: RunContext;
|
|
40
|
+
private resolve!: (r: EngineRunResult) => void;
|
|
41
|
+
private reject!: (err: unknown) => void;
|
|
42
|
+
readonly promise: Promise<EngineRunResult>;
|
|
43
|
+
|
|
44
|
+
constructor(task: AgentCallOpts, ctx: RunContext) {
|
|
45
|
+
this.task = task;
|
|
46
|
+
this.ctx = ctx;
|
|
47
|
+
this.promise = new Promise<EngineRunResult>((res, rej) => {
|
|
48
|
+
this.resolve = res;
|
|
49
|
+
this.reject = rej;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** 模拟 run 正常应答(首轮 agent_settled 口径;outcome 缺省 = 成功空轮)。 */
|
|
54
|
+
settle(outcome: Partial<AgentOutcome> = {}): void {
|
|
55
|
+
this.resolve({
|
|
56
|
+
handle: {
|
|
57
|
+
data: {
|
|
58
|
+
v: 1,
|
|
59
|
+
engineId: "pi",
|
|
60
|
+
sessionRef: { recordId: this.ctx.taskId },
|
|
61
|
+
poolKey: "shared",
|
|
62
|
+
adapterVersion: "fake-engine-port",
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
outcome: {
|
|
66
|
+
content: "",
|
|
67
|
+
engineId: "pi",
|
|
68
|
+
...outcome,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** 模拟 run 期失败(prepare 期 reject / 引擎进程死亡)。 */
|
|
74
|
+
fail(err: unknown): void {
|
|
75
|
+
this.reject(err);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** 模拟引擎反向事件通知(协议 event 通知形态)。 */
|
|
79
|
+
emitEvent(event: unknown): void {
|
|
80
|
+
this.ctx.onEvent?.(event as never);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** 模拟 host/streamDelta(runId 键——首轮)。 */
|
|
84
|
+
emitDelta(delta: string): void {
|
|
85
|
+
this.ctx.stream?.onDelta(delta);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** 模拟 host/roundLifecycle(runId 键——首轮)。 */
|
|
89
|
+
emitLifecycle(phase: Record<string, unknown>): void {
|
|
90
|
+
this.ctx.onRoundLifecycle?.({ runId: this.ctx.taskId, ...phase } as never);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** 模拟 host/handleReady(运行中句柄回填)。 */
|
|
94
|
+
emitHandleReady(sessionRef: Record<string, string>, poolKey = "shared"): void {
|
|
95
|
+
this.ctx.onHandleReady?.({ sessionRef, poolKey });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** 一次 interact 调用的捕获(action 原样记录,供断言)。 */
|
|
100
|
+
export interface FakeInteractCall {
|
|
101
|
+
handle: EngineHandle;
|
|
102
|
+
action: InteractAction;
|
|
103
|
+
result: InteractResult | Promise<InteractResult>;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export class FakePiEnginePort implements EnginePort {
|
|
107
|
+
readonly id = "pi";
|
|
108
|
+
|
|
109
|
+
/** run 调用捕获(按序)。 */
|
|
110
|
+
readonly runs: FakeRun[] = [];
|
|
111
|
+
/** interact 调用捕获(按序;result = 受理时返回值)。 */
|
|
112
|
+
readonly interacts: FakeInteractCall[] = [];
|
|
113
|
+
/** recordId 键路由注册捕获(recordId → 注销函数)。 */
|
|
114
|
+
readonly chatRoutes = new Map<string, ChatRoundRoute>();
|
|
115
|
+
readonly chatRouteUnregisters: string[] = [];
|
|
116
|
+
|
|
117
|
+
/** interact message 的应答形态(默认受理;测试改写为冷路径拒绝等)。 */
|
|
118
|
+
interactMessageResult: InteractResult = { ok: true, delivered: true };
|
|
119
|
+
/** interact cancel / close 的应答形态。 */
|
|
120
|
+
interactControlResult: InteractResult = { ok: true, delivered: true };
|
|
121
|
+
|
|
122
|
+
capabilities(): EngineCapabilities {
|
|
123
|
+
// 与 pi-subagent-cli manifest 逐位一致(gate 同步面放行 pi 全参数——V4⑤ 反向守护)。
|
|
124
|
+
return {
|
|
125
|
+
schemaEnforcement: "native",
|
|
126
|
+
steer: "unsupported",
|
|
127
|
+
conversation: "native",
|
|
128
|
+
personaInjection: "flag",
|
|
129
|
+
eventGranularity: "stream",
|
|
130
|
+
sandbox: "emulated",
|
|
131
|
+
sessionRead: "full",
|
|
132
|
+
resume: "native",
|
|
133
|
+
interrupt: "kill-only",
|
|
134
|
+
permissionMode: "native",
|
|
135
|
+
maxTurns: true,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async probe(): Promise<ProbeReport> {
|
|
140
|
+
return { ok: true, engineVersion: "fake-1", checks: [{ name: "invocation", ok: true, detail: "fake" }] };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
run(task: AgentCallOpts, ctx: RunContext): Promise<EngineRunResult> {
|
|
144
|
+
const captured = new FakeRun(task, ctx);
|
|
145
|
+
this.runs.push(captured);
|
|
146
|
+
return captured.promise;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async interact(handle: EngineHandle, action: InteractAction): Promise<InteractResult> {
|
|
150
|
+
const result = action.kind === "message" ? this.interactMessageResult : this.interactControlResult;
|
|
151
|
+
this.interacts.push({ handle, action, result });
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async read(handle: EngineHandle): Promise<SessionView> {
|
|
156
|
+
return { engineId: "pi", turns: [], source: "outcome-only" };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
registerChatRoundRoute(recordId: string, route: ChatRoundRoute): () => void {
|
|
160
|
+
this.chatRoutes.set(recordId, route);
|
|
161
|
+
return () => {
|
|
162
|
+
if (this.chatRoutes.get(recordId) === route) {
|
|
163
|
+
this.chatRoutes.delete(recordId);
|
|
164
|
+
this.chatRouteUnregisters.push(recordId);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ── 测试驱动面(recordId 键路由——interact 续聊轮的协议回流)──
|
|
170
|
+
|
|
171
|
+
/** 向指定 record 的 chat 路由发 recordId 键 delta。 */
|
|
172
|
+
emitRecordDelta(recordId: string, delta: string): void {
|
|
173
|
+
void this.chatRoutes.get(recordId)?.onStreamDelta?.(delta);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** 向指定 record 的 chat 路由发 recordId 键 roundLifecycle 相位。 */
|
|
177
|
+
emitRecordLifecycle(recordId: string, phase: Record<string, unknown>): void {
|
|
178
|
+
void this.chatRoutes.get(recordId)?.onRoundLifecycle?.({ recordId, ...phase } as never);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** 注册替身 pi 引擎进 registry(覆盖同 id 既有注册——测试隔离自负责清理)。 */
|
|
183
|
+
export function registerFakePiEngine(): FakePiEnginePort {
|
|
184
|
+
const fake = new FakePiEnginePort();
|
|
185
|
+
registerEngine("pi", () => fake);
|
|
186
|
+
return fake;
|
|
187
|
+
}
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
// };
|
|
48
48
|
// });
|
|
49
49
|
// vi.mock("../alive-store.ts", () => ({ writeAliveMarker: vi.fn() }));
|
|
50
|
-
// vi.mock("../engine/
|
|
50
|
+
// vi.mock("../engine/inproc temp-prompt(已删)", () => ({
|
|
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` };
|
|
@@ -63,7 +63,11 @@ import { PassThrough } from "node:stream";
|
|
|
63
63
|
import { vi } from "vitest";
|
|
64
64
|
|
|
65
65
|
import { createRecord } from "../../execution-record.ts";
|
|
66
|
-
|
|
66
|
+
|
|
67
|
+
// [W3 改写] RunOptions / SessionRunnerContext 类型源(inproc session-runner(已删))
|
|
68
|
+
// 随删件消亡——makeOpts/makeCtx 改用结构化最小类型(消费方为 keep-alive 系历史
|
|
69
|
+
// fixture 形态;引擎侧等价类型在 pi-subagent-cli)。core 侧剩余消费面为
|
|
70
|
+
// recursive-visibility-baseline 的 spawn 断言工具(waitForSpawn/lastSpawnedChild)。
|
|
67
71
|
|
|
68
72
|
/** FakeChild 的假 pid(满足 ChildProcess.pid 形状,无真实进程语义)。 */
|
|
69
73
|
const FAKE_PID = 12345;
|
|
@@ -82,7 +86,7 @@ const SPAWN_POLL_INTERVAL_MS = 5;
|
|
|
82
86
|
* 导出 class(而非只在工厂内部定义)是为了:
|
|
83
87
|
* 1. vi.mock 工厂内 `await import("./helpers/spawn-mock.ts")` 后 `new FakeChild()` 与
|
|
84
88
|
* 测试侧 `instanceof FakeChild` / 类型断言用同一个 class。
|
|
85
|
-
* 2. C13 e2e 测试可直接 `new FakeChild()` 手动构造 child 喂给
|
|
89
|
+
* 2. C13 e2e 测试可直接 `new FakeChild()` 手动构造 child 喂给 inproc UI 请求队列(已删)(不经 spawn)。
|
|
86
90
|
*/
|
|
87
91
|
export class FakeChild extends EventEmitter {
|
|
88
92
|
pid = FAKE_PID;
|
|
@@ -206,8 +210,21 @@ export function makeRecord(id = "run-1") {
|
|
|
206
210
|
});
|
|
207
211
|
}
|
|
208
212
|
|
|
213
|
+
/** RunOptions 的结构化最小形态(原 inproc pi 引擎目录 RunOptions 同构;测试 fixture 用)。 */
|
|
214
|
+
export interface RunOptionsLike {
|
|
215
|
+
resolved: { model: { id: string; name: string; provider: string; reasoning: boolean }; thinkingLevel: string | undefined };
|
|
216
|
+
agentConfig: undefined;
|
|
217
|
+
appendSystemPrompt?: string[];
|
|
218
|
+
skillPath?: string;
|
|
219
|
+
schema?: Record<string, unknown>;
|
|
220
|
+
maxTurns?: number;
|
|
221
|
+
graceTurns?: number;
|
|
222
|
+
signal?: AbortSignal;
|
|
223
|
+
onEvent?: ((event: unknown) => void) | undefined;
|
|
224
|
+
}
|
|
225
|
+
|
|
209
226
|
/** 构造最小合法的 RunOptions(runSpawn 入参,可 override 关键字段)。 */
|
|
210
|
-
export function makeOpts(overrides: Partial<
|
|
227
|
+
export function makeOpts(overrides: Partial<RunOptionsLike> = {}): RunOptionsLike {
|
|
211
228
|
return {
|
|
212
229
|
resolved: {
|
|
213
230
|
model: {
|
|
@@ -230,8 +247,19 @@ export function makeOpts(overrides: Partial<RunOptions> = {}): RunOptions {
|
|
|
230
247
|
};
|
|
231
248
|
}
|
|
232
249
|
|
|
250
|
+
/** SessionRunnerContext 的结构化最小形态(core 侧消费字段面)。 */
|
|
251
|
+
export interface SessionRunnerContextLike {
|
|
252
|
+
cwd: string;
|
|
253
|
+
agentDir: string;
|
|
254
|
+
skillDirs: string[];
|
|
255
|
+
mainCwd: string;
|
|
256
|
+
mainSessionFile?: string;
|
|
257
|
+
sessionRootId: string;
|
|
258
|
+
rootCwd: string;
|
|
259
|
+
}
|
|
260
|
+
|
|
233
261
|
/** 构造最小合法的 SessionRunnerContext(runSpawn 入参,可 override 关键字段)。 */
|
|
234
|
-
export function makeCtx(overrides: Partial<
|
|
262
|
+
export function makeCtx(overrides: Partial<SessionRunnerContextLike> = {}): SessionRunnerContextLike {
|
|
235
263
|
return {
|
|
236
264
|
cwd: "/tmp/test",
|
|
237
265
|
agentDir: "/tmp/test/agents",
|
|
@@ -318,11 +346,10 @@ export function sessionPendingModule() {
|
|
|
318
346
|
return {
|
|
319
347
|
readActivePendingFromSessionFile: vi.fn(() => ({ count: 1, recentUnregister: false })),
|
|
320
348
|
prunePendingCursor: vi.fn(),
|
|
321
|
-
listActivePendingFromSessionFile: vi.fn(() => ({ items: [] })),
|
|
322
349
|
};
|
|
323
350
|
}
|
|
324
351
|
|
|
325
|
-
/** "../engine/
|
|
352
|
+
/** "../engine/inproc temp-prompt(已删)" mock 工厂(prompt 落 /tmp/fake-<agent>,收尾 no-op)。 */
|
|
326
353
|
export function tempPromptModule() {
|
|
327
354
|
return {
|
|
328
355
|
writePromptToTempFile: vi.fn(async (agent: string) => {
|
|
@@ -352,7 +379,7 @@ export function makeVisibilityRecord(overrides: { id?: string; depth?: number }
|
|
|
352
379
|
}
|
|
353
380
|
|
|
354
381
|
/** 构造 env 注入断言族的 RunOptions(可 override 关键字段)。 */
|
|
355
|
-
export function makeRunOpts(overrides: Partial<
|
|
382
|
+
export function makeRunOpts(overrides: Partial<RunOptionsLike> = {}): RunOptionsLike {
|
|
356
383
|
return {
|
|
357
384
|
resolved: { model: { provider: "test", id: "model", name: "Model", reasoning: false }, thinkingLevel: undefined },
|
|
358
385
|
agentConfig: undefined,
|
|
@@ -368,7 +395,7 @@ export function makeRunOpts(overrides: Partial<RunOptions> = {}): RunOptions {
|
|
|
368
395
|
}
|
|
369
396
|
|
|
370
397
|
/** 构造 env 注入断言族的 SessionRunnerContext(可 override 关键字段)。 */
|
|
371
|
-
export function makeVisibilityCtx(overrides: Partial<
|
|
398
|
+
export function makeVisibilityCtx(overrides: Partial<SessionRunnerContextLike> = {}): SessionRunnerContextLike {
|
|
372
399
|
return {
|
|
373
400
|
cwd: "/fake/cwd",
|
|
374
401
|
agentDir: "/fake/agent",
|
|
@@ -112,7 +112,7 @@ export function manifestStoreModule() {
|
|
|
112
112
|
return { ManifestStore: vi.fn(function (_recordsDir: string) { return new FakeManifestStore(); }) };
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
/**
|
|
115
|
+
/** temp-prompt mock 模块(固定路径,消除 fake-timers 下 flaky 竞态)。[W3] 被 mock 的 inproc temp-prompt 模块已删——本工厂仅余存量测试文件引用。 */
|
|
116
116
|
export function tempPromptModule() {
|
|
117
117
|
return {
|
|
118
118
|
writePromptToTempFile: vi.fn(async (agent: string) => {
|