@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
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
// src/execution/__tests__/collect-coordinator-service.test.ts
|
|
2
|
+
//
|
|
3
|
+
// collectCoordinator 的 service 集成面(subagent-sync-collect U2):
|
|
4
|
+
// 1. 偏差#4 落点:execute({collect:"sync"}) 的 record 落 collectMode="sync"
|
|
5
|
+
// (观察者形态:subagent-record entry 落盘产物断言,非 mock record);
|
|
6
|
+
// 2. notifyComplete 统一路由:sync 成员终态 → 协调器缓冲 → 单成员闭合 → 降级
|
|
7
|
+
// flush → notifier.notify 收到该成员(观察点:替换 notifier.notify 为 spy,
|
|
8
|
+
// 闭包经 this 运行时读取,替换生效);
|
|
9
|
+
// 3. async record(无 collect)直通同一 notifier.notify(现状路径字节不变);
|
|
10
|
+
// 4. 偏差#3 接线:getCollectSyncDefault 读真实 config(缺省 async / 配置 sync +
|
|
11
|
+
// reloadGlobalConfig 后生效);
|
|
12
|
+
// 5. flush 屏障时序:manifest 写完成先于 notifyBatch 写账(通知可达 ⇒ 索引就位
|
|
13
|
+
// 的构造性保证,时序竞态修复)。
|
|
14
|
+
//
|
|
15
|
+
// mock 手法对齐 ended-message-and-fork-from.test.ts:[W3 改写] 替身从
|
|
16
|
+
// vi.mock session-runner(不 spawn 真子进程)改为协议 seam(registerFakePiEngine 替身,
|
|
17
|
+
// 测试显式 settle 应答)+ logger;record-store / config 走真实实现(tmpdir 自建自删,红线)。
|
|
18
|
+
|
|
19
|
+
import * as fs from "node:fs";
|
|
20
|
+
import * as os from "node:os";
|
|
21
|
+
import * as path from "node:path";
|
|
22
|
+
|
|
23
|
+
import { afterEach, beforeEach, describe, expect, it, vi, type Mock } from "vitest";
|
|
24
|
+
|
|
25
|
+
const { loggerMock } = vi.hoisted(() => ({
|
|
26
|
+
loggerMock: { debug: vi.fn(), warn: vi.fn(), error: vi.fn(), info: vi.fn() },
|
|
27
|
+
}));
|
|
28
|
+
vi.mock("../../core/logger.ts", () => ({ getLogger: () => loggerMock }));
|
|
29
|
+
|
|
30
|
+
// [W3] execute 链经 executeViaEngine → kickOffChatRound → engine.run(协议替身)。
|
|
31
|
+
// 替身 run 挂起不自动 settle:测试取 fake.runs.at(-1) 显式 settle(受控时序——
|
|
32
|
+
// 原 runSpawnMock 的 resolve 时序控制等价面)。
|
|
33
|
+
import { clearEngines } from "../engine/registry.ts";
|
|
34
|
+
import { registerFakePiEngine, type FakePiEnginePort } from "./helpers/fake-engine-port.ts";
|
|
35
|
+
|
|
36
|
+
import { getSubagentSessionDir, getSubagentRecordsDir } from "../path-encoding.ts";
|
|
37
|
+
import { ModelConfigService } from "../model-config-service.ts";
|
|
38
|
+
import type { ModelRegistryLike } from "../model-resolver.ts";
|
|
39
|
+
import { SubagentService } from "../subagent-service.ts";
|
|
40
|
+
|
|
41
|
+
const IDENTITY_ENV_KEYS = [
|
|
42
|
+
"PI_SUBAGENT_ROOT_SESSION_ID",
|
|
43
|
+
"PI_SUBAGENT_SELF_RECORD_ID",
|
|
44
|
+
"PI_SUBAGENT_DEPTH",
|
|
45
|
+
"PI_SUBAGENT_ROOT_CWD",
|
|
46
|
+
"PI_SUBAGENT_FORK_DEPTH",
|
|
47
|
+
] as const;
|
|
48
|
+
|
|
49
|
+
function makePi() {
|
|
50
|
+
return {
|
|
51
|
+
appendEntry: vi.fn(),
|
|
52
|
+
events: { emit: vi.fn() },
|
|
53
|
+
sendMessage: vi.fn(),
|
|
54
|
+
on: vi.fn(),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface NotifierSpy {
|
|
59
|
+
// Mock<T> 而非纯函数签名:测试断言消费 .mock.calls,需保留 mock 元数据
|
|
60
|
+
notify: Mock<(record: unknown) => void>;
|
|
61
|
+
/** U3:sync 批投递 spy。mock 返回 true(flushBatch 接线的落标 accepted 分支可达)。 */
|
|
62
|
+
notifyBatch: Mock<(records: unknown, budget?: unknown) => boolean>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** 替换 service 私有 notifier 为仅覆盖 notify/notifyBatch 的 spy(保留其余方法;
|
|
66
|
+
* 协调器 deps 闭包经 this 运行时读取,替换生效)。 */
|
|
67
|
+
function spyNotifier(service: SubagentService): NotifierSpy {
|
|
68
|
+
// [D4-① 适配] notifier 实例已封装进 notifyHost(createNotifyHost),spy 改为包装
|
|
69
|
+
// host 的 notify/notifyBatch 出口(其余方法经 ...host 保留真实现);协调器 deps
|
|
70
|
+
// 闭包经 this 运行时读取 service.notifyHost,字段替换即生效。
|
|
71
|
+
const host = (service as unknown as { notifyHost: object }).notifyHost;
|
|
72
|
+
const spy: NotifierSpy = { notify: vi.fn(), notifyBatch: vi.fn(() => true) };
|
|
73
|
+
(service as unknown as { notifyHost: unknown }).notifyHost = {
|
|
74
|
+
...host,
|
|
75
|
+
notify: (record: unknown) => spy.notify(record),
|
|
76
|
+
notifyBatch: (records: unknown, budget?: unknown) => spy.notifyBatch(records, budget),
|
|
77
|
+
};
|
|
78
|
+
return spy;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 异步链等待:手写短轮询(真实 timers)。
|
|
83
|
+
* 为什么不用 vi.waitFor:vitest 4.1.8 本包环境下 vi.waitFor 对 falsy callback
|
|
84
|
+
* 立即 resolve 不轮询不超时(sanity 用例实证:waitFor(()=>false,{timeout:100})
|
|
85
|
+
* resolve(false) 而非 reject)——属测试基建缺陷,与被测链路无关,此处绕开。
|
|
86
|
+
*/
|
|
87
|
+
async function until(cond: () => boolean, timeoutMs = 3000): Promise<void> {
|
|
88
|
+
const start = Date.now();
|
|
89
|
+
while (!cond()) {
|
|
90
|
+
if (Date.now() - start > timeoutMs) {
|
|
91
|
+
throw new Error(`collect-coordinator-service: condition not met within ${timeoutMs}ms`);
|
|
92
|
+
}
|
|
93
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
describe("collectCoordinator service integration (U2)", () => {
|
|
98
|
+
let agentDir: string;
|
|
99
|
+
let service: SubagentService;
|
|
100
|
+
let pi: ReturnType<typeof makePi>;
|
|
101
|
+
let fake: FakePiEnginePort;
|
|
102
|
+
|
|
103
|
+
beforeEach(() => {
|
|
104
|
+
for (const k of IDENTITY_ENV_KEYS) delete process.env[k];
|
|
105
|
+
clearEngines();
|
|
106
|
+
fake = registerFakePiEngine();
|
|
107
|
+
agentDir = fs.mkdtempSync(path.join(os.tmpdir(), "core-collect-coord-"));
|
|
108
|
+
fs.mkdirSync(getSubagentSessionDir(agentDir, agentDir), { recursive: true });
|
|
109
|
+
pi = makePi();
|
|
110
|
+
service = makeService(pi);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
afterEach(() => {
|
|
114
|
+
service.dispose();
|
|
115
|
+
clearEngines();
|
|
116
|
+
fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
function makeService(piInstance: ReturnType<typeof makePi>): SubagentService {
|
|
120
|
+
const modelService = new ModelConfigService({ agentDir, cwd: agentDir });
|
|
121
|
+
const modelRegistry: ModelRegistryLike = {
|
|
122
|
+
getAvailable: () => [],
|
|
123
|
+
find: () => undefined,
|
|
124
|
+
hasConfiguredAuth: () => false,
|
|
125
|
+
};
|
|
126
|
+
modelService.initModel({
|
|
127
|
+
sessionId: "root-session-cur",
|
|
128
|
+
ctxModel: { id: "m", name: "M", provider: "p", reasoning: false },
|
|
129
|
+
modelRegistry,
|
|
130
|
+
});
|
|
131
|
+
const created = new SubagentService({ cwd: agentDir, modelService });
|
|
132
|
+
created.initSession({ pi: piInstance, sessionId: "root-session-cur" });
|
|
133
|
+
return created;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
it("stamps collectMode on the in-memory record visible to the coordinator routing (偏差#4 落点)", async () => {
|
|
137
|
+
// 注:entry 落盘观察者断言(subagent-record entry 含 collectMode)依赖
|
|
138
|
+
// record-store.recordToSubagent 投影扩展——U5 领地(偏差登记);本用例锁
|
|
139
|
+
// 内存 record 经协调器路由的可见行为:sync 成员终态 → 单成员闭合 → notifyBatch
|
|
140
|
+
// 批投递(下方用例),async 成员直通。本条记录 execute 链零异常完成。
|
|
141
|
+
const spy = spyNotifier(service);
|
|
142
|
+
const handle = await service.execute({ task: "collect me", slug: "collect-me", collect: "sync" });
|
|
143
|
+
await until(() => fake.runs.length >= 1);
|
|
144
|
+
await settleLast();
|
|
145
|
+
await until(() => spy.notifyBatch.mock.calls.length > 0);
|
|
146
|
+
expect(handle.subagentId).toMatch(/^sa-/);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("routes a finished sync member into a single-member batch (U3 接线:单成员闭合 → notifyBatch)", async () => {
|
|
150
|
+
const spy = spyNotifier(service);
|
|
151
|
+
const handle = await service.execute({ task: "collect me", slug: "collect-me", collect: "sync" });
|
|
152
|
+
await until(() => fake.runs.length >= 1);
|
|
153
|
+
await settleLast();
|
|
154
|
+
await until(() => spy.notifyBatch.mock.calls.length > 0);
|
|
155
|
+
// U3 接线后:sync 成员走单条批投递(不再逐条降级 notify)
|
|
156
|
+
expect(spy.notify).not.toHaveBeenCalled();
|
|
157
|
+
const batches = spy.notifyBatch.mock.calls.map((c) => c[0] as { id: string }[]);
|
|
158
|
+
expect(batches).toHaveLength(1);
|
|
159
|
+
expect(batches[0]!.map((m) => m.id)).toEqual([handle.subagentId]);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("flushBatch accepted=false(幂等拒绝)时不落标:零带 batchFinalized 的 entry", async () => {
|
|
163
|
+
const spy = spyNotifier(service);
|
|
164
|
+
spy.notifyBatch.mockReturnValue(false); // 模拟同成员集批已在账(E1 重建重发形态)
|
|
165
|
+
const handle = await service.execute({ task: "collect me", slug: "collect-me", collect: "sync" });
|
|
166
|
+
await until(() => fake.runs.length >= 1);
|
|
167
|
+
await settleLast();
|
|
168
|
+
await until(() => spy.notifyBatch.mock.calls.length > 0);
|
|
169
|
+
expect(handle.subagentId).toMatch(/^sa-/);
|
|
170
|
+
// 落标出口①绑「写账成功」:accepted=false → 无带 batchFinalized 标记的 subagent-record
|
|
171
|
+
// entry(register/archive 常规 entry 经 recordToSubagent 投影携带 batchFinalized=
|
|
172
|
+
// undefined——未离场成员,非 true;U5 投影扩展后缺省键不落值,断言语义不变)
|
|
173
|
+
const recordEntries = pi.appendEntry.mock.calls
|
|
174
|
+
.filter((c) => c[0] === "subagent-record")
|
|
175
|
+
.map((c) => c[1] as Record<string, unknown>);
|
|
176
|
+
expect(recordEntries.length).toBeGreaterThan(0); // execute 链自身的 register/archive 在场
|
|
177
|
+
expect(recordEntries.some((d) => d["batchFinalized"] === true)).toBe(false);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("routes an async member through the same notifyAsync path (现状直通)", async () => {
|
|
181
|
+
const spy = spyNotifier(service);
|
|
182
|
+
const handle = await service.execute({ task: "plain", slug: "plain" });
|
|
183
|
+
await until(() => fake.runs.length >= 1);
|
|
184
|
+
await settleLast();
|
|
185
|
+
await until(() => spy.notify.mock.calls.length > 0);
|
|
186
|
+
const notified = spy.notify.mock.calls.map((c) => c[0] as { id: string });
|
|
187
|
+
expect(notified.some((n) => n.id === handle.subagentId)).toBe(true);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("getCollectSyncDefault reads real config (偏差#3 接线)", () => {
|
|
191
|
+
// 无 config.json → 缺省 async
|
|
192
|
+
expect(service.getCollectSyncDefault()).toBe("async");
|
|
193
|
+
// 写入 collectSync.default=sync + reload → sync 生效
|
|
194
|
+
const configPath = path.join(agentDir, "subagents", "config.json");
|
|
195
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
196
|
+
fs.writeFileSync(
|
|
197
|
+
configPath,
|
|
198
|
+
JSON.stringify({ version: 1, maxConcurrent: 6, collectSync: { default: "sync" } }),
|
|
199
|
+
"utf-8",
|
|
200
|
+
);
|
|
201
|
+
const modelService = (service as unknown as { modelService: ModelConfigService }).modelService;
|
|
202
|
+
modelService.reloadGlobalConfig();
|
|
203
|
+
expect(service.getCollectSyncDefault()).toBe("sync");
|
|
204
|
+
// E5 坏值回默认:非枚举 → async 兜底
|
|
205
|
+
fs.writeFileSync(
|
|
206
|
+
configPath,
|
|
207
|
+
JSON.stringify({ version: 1, maxConcurrent: 6, collectSync: { default: "immediate" } }),
|
|
208
|
+
"utf-8",
|
|
209
|
+
);
|
|
210
|
+
modelService.reloadGlobalConfig();
|
|
211
|
+
expect(service.getCollectSyncDefault()).toBe("async");
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
/** 伪造子 session 文件(record-store.test.ts 同款:header + identity + subagent-record
|
|
215
|
+
* entry)——落标通路 getFullRecord 冷路径重建的真实文件数据源(mock runSpawn 不产子文件)。 */
|
|
216
|
+
function writeChildSessionFile(recordId: string, agent: string, result: string): void {
|
|
217
|
+
const sessionsDir = getSubagentSessionDir(agentDir, agentDir);
|
|
218
|
+
const ts = new Date(1000).toISOString();
|
|
219
|
+
const header = JSON.stringify({
|
|
220
|
+
type: "session", version: 3, id: `sess-${recordId}`, timestamp: ts, cwd: agentDir,
|
|
221
|
+
});
|
|
222
|
+
const identity = JSON.stringify({
|
|
223
|
+
type: "custom", id: "id-1", parentId: null, timestamp: ts,
|
|
224
|
+
customType: "subagent-identity",
|
|
225
|
+
data: {
|
|
226
|
+
id: recordId, agent, mode: "background", task: "collect me", startedAt: 1000,
|
|
227
|
+
rootSessionId: "root-session-cur", depth: 0,
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
const recordEntry = JSON.stringify({
|
|
231
|
+
type: "custom", id: "id-2", parentId: "id-1", timestamp: ts,
|
|
232
|
+
customType: "subagent-record",
|
|
233
|
+
data: {
|
|
234
|
+
v: 1, id: recordId, agent, task: "collect me", slug: "collect-me",
|
|
235
|
+
status: "closed", mode: "background", startedAt: 1000,
|
|
236
|
+
rootSessionId: "root-session-cur", parentRecordId: undefined, depth: 0,
|
|
237
|
+
endedAt: 2000, turns: 1, totalTokens: 10, model: "prov/m1", thinkingLevel: undefined,
|
|
238
|
+
eventLog: [], displayItems: [], result,
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
fs.writeFileSync(path.join(sessionsDir, `${recordId}.jsonl`), `${header}\n${identity}\n${recordEntry}\n`, "utf-8");
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** 受控替身 settle:等替身 run 到位(execute 返回后的异步链内发起),按需 settle。
|
|
245
|
+
* 原 runSpawnMock 受控 promise 的等价面(content "ok" 对齐原 AgentResult.text)。 */
|
|
246
|
+
const settleLast = async (): Promise<void> => {
|
|
247
|
+
await until(() => fake.runs.length >= 1);
|
|
248
|
+
fake.runs[fake.runs.length - 1]!.settle({ content: "ok" });
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
it("跨轮续累(D2 service 层):两轮派 2 sync → 闭合时单批 2 成员", async () => {
|
|
252
|
+
const spy = spyNotifier(service);
|
|
253
|
+
const h1 = await service.execute({ task: "t1", slug: "one", collect: "sync" });
|
|
254
|
+
const h2 = await service.execute({ task: "t2", slug: "two", collect: "sync" });
|
|
255
|
+
// engine.run 在 execute 返回后的异步链内才被发起——等两个替身 run 都到位
|
|
256
|
+
await until(() => fake.runs.length >= 2);
|
|
257
|
+
|
|
258
|
+
// 第一台终态:第二台仍 running → 缓冲不闭合(零投递)
|
|
259
|
+
fake.runs[0]!.settle({ content: "ok" });
|
|
260
|
+
await new Promise((resolve) => setTimeout(resolve, 50)); // microtask 链排空
|
|
261
|
+
expect(spy.notifyBatch.mock.calls).toHaveLength(0);
|
|
262
|
+
|
|
263
|
+
// 第二台终态:闭合 → 单批 2 成员(含第一台的快照,跨轮续累不丢)
|
|
264
|
+
fake.runs[1]!.settle({ content: "ok" });
|
|
265
|
+
await until(() => spy.notifyBatch.mock.calls.length > 0);
|
|
266
|
+
expect(spy.notifyBatch.mock.calls).toHaveLength(1);
|
|
267
|
+
const batch = spy.notifyBatch.mock.calls[0]![0] as { id: string }[];
|
|
268
|
+
expect(batch.map((m) => m.id).sort()).toEqual([h1.subagentId, h2.subagentId].sort());
|
|
269
|
+
expect(spy.notify).not.toHaveBeenCalled();
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it("batchFinalized 落标(真文件通路):闭合 flush 写账成功后末条 entry 带标记(E1 排除判据)", async () => {
|
|
273
|
+
const spy = spyNotifier(service);
|
|
274
|
+
const handle = await service.execute({ task: "collect me", slug: "collect-me", collect: "sync" });
|
|
275
|
+
await until(() => fake.runs.length >= 1); // 替身 run 已到位
|
|
276
|
+
// 子 session 文件在闭合判定/flush 前就位(真实生产链中引擎轮次已写好 identity)
|
|
277
|
+
writeChildSessionFile(handle.subagentId, "/agents/worker.md", "ok");
|
|
278
|
+
await settleLast();
|
|
279
|
+
await until(() => spy.notifyBatch.mock.calls.length > 0);
|
|
280
|
+
|
|
281
|
+
// 观察者形态:主 session 落盘的末条 subagent-record entry 带 batchFinalized=true +
|
|
282
|
+
// collectMode=sync(reportSubagentRecord 直投影 SubagentRecord,不经 recordToSubagent)。
|
|
283
|
+
// status 不在此锁:one-shot 成功链走 SP-5 resumable 回退(record 留内存 running 态,
|
|
284
|
+
// 真实形态),E1 排除判据只依赖 collectMode+batchFinalized 两字段。
|
|
285
|
+
const marked = pi.appendEntry.mock.calls
|
|
286
|
+
.filter((c) => c[0] === "subagent-record")
|
|
287
|
+
.map((c) => c[1] as Record<string, unknown>)
|
|
288
|
+
.filter((d) => d["id"] === handle.subagentId && d["batchFinalized"] === true);
|
|
289
|
+
expect(marked).toHaveLength(1);
|
|
290
|
+
expect(marked[0]?.["collectMode"]).toBe("sync");
|
|
291
|
+
expect(marked[0]?.["result"]).toBe("ok");
|
|
292
|
+
expect(marked[0]?.["resumable"]).toBe(true); // SP-5 成功回退态(真链形态保真)
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it("flush 屏障:manifest 写完成先于 notifyBatch 写账(通知可达 ⇒ 索引就位,构造性保证)", async () => {
|
|
296
|
+
// 时序竞态修复(v2 D1 修订):批通知路径的 manifest 写从「写账后 fire-and-forget」
|
|
297
|
+
// 前移为「写账前 await 全部落盘」屏障——「通知可达 ⇒ 索引就位」从大概率成立升为
|
|
298
|
+
// 构造性保证(by construction)。断言打在 notifyBatch(写账入口)被调用的时刻:
|
|
299
|
+
// 磁盘上 records/<sa-id>.json 必已存在(修复前该时刻大概率 false,探针实测 mtime
|
|
300
|
+
// 相对 notify entry ±2/3ms 方向不定)。
|
|
301
|
+
const spy = spyNotifier(service);
|
|
302
|
+
const handle = await service.execute({ task: "collect me", slug: "collect-me", collect: "sync" });
|
|
303
|
+
await until(() => fake.runs.length >= 1); // 替身 run 已到位
|
|
304
|
+
writeChildSessionFile(handle.subagentId, "/agents/worker.md", "ok");
|
|
305
|
+
const manifestPath = path.join(getSubagentRecordsDir(agentDir, agentDir), `${handle.subagentId}.json`);
|
|
306
|
+
// 写账时刻钩子:mock 内联捕获 manifest 存在性(此刻即指针行消费可用性)
|
|
307
|
+
let manifestExistsAtLedgerWrite = false;
|
|
308
|
+
spy.notifyBatch.mockImplementation(() => {
|
|
309
|
+
manifestExistsAtLedgerWrite = fs.existsSync(manifestPath);
|
|
310
|
+
return true;
|
|
311
|
+
});
|
|
312
|
+
await settleLast();
|
|
313
|
+
await until(() => spy.notifyBatch.mock.calls.length > 0);
|
|
314
|
+
expect(manifestExistsAtLedgerWrite).toBe(true);
|
|
315
|
+
});
|
|
316
|
+
});
|