@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
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
// src/execution/__tests__/settled-watchdog.test.ts
|
|
2
2
|
//
|
|
3
|
-
// [T2-③ /
|
|
3
|
+
// [T2-③ / D9 两段式] chatMode settled 等待守护:中段无进展检测 + 收尾段固定上界。
|
|
4
4
|
//
|
|
5
|
-
// 设计:docs/design/
|
|
6
|
-
//
|
|
5
|
+
// 设计:docs/design/timeout-zcode-turn-and-settled-watchdog.md §6-D9 / §7(两段式
|
|
6
|
+
// 重锚定,P0-4 核心);收尾段 600s 定值由 P-T2c 已执行探针定案(probe/p-t2c-report.md,
|
|
7
|
+
// 收尾段 <2ms、compact 30 万 tokens 40.1s、P99×10=401s<600s);中段 30min 对齐
|
|
8
|
+
// KEEP_ALIVE_NO_PROGRESS_TIMEOUT_MS 先例。
|
|
7
9
|
//
|
|
8
|
-
//
|
|
9
|
-
// 1. 原语单测(settled-watchdog.ts
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
10
|
+
// 覆盖五面(对应单元验收 ①-⑤):
|
|
11
|
+
// 1. 原语单测(settled-watchdog.ts):两常量值;①中段静默超时按第一段语义触发;
|
|
12
|
+
// ②agent_end 交棒后切换收尾段;③收尾段 settle 到达 disarm 不误杀;④两段各自
|
|
13
|
+
// 独立计时(中段已过时间不继承——收尾段从 agent_end 起满 600s 才 fire);
|
|
14
|
+
// ⑤env XYZ_SUBAGENT_SETTLED_WATCHDOG_MS 覆盖 / ≤0 双段关闭 / 非法回落。
|
|
15
|
+
// 2. 首轮挂载集成(runSpawn chatMode):prompt 发出后挂中段;静默 30min 触发 kill
|
|
16
|
+
// → close → runSpawn 以错误返回且错误含段标注 + 恢复指引(S-B 验收判据);
|
|
17
|
+
// agent_end 交棒后收尾段 600s 独立计时;正常轮转(agent_end+agent_settled)
|
|
18
|
+
// 不误杀。
|
|
14
19
|
// 3. 清除点:settled 到达(含 resolveRun 同点)/ close 任一发生即清——挂载点消费
|
|
15
20
|
// 同一导出常量(advance 常量值可触发 = 同源断言)。
|
|
16
21
|
//
|
|
@@ -69,7 +74,7 @@ vi.mock("../session-pending.ts", () => ({
|
|
|
69
74
|
listActivePendingFromSessionFile: vi.fn(() => ({ items: [] })),
|
|
70
75
|
}));
|
|
71
76
|
|
|
72
|
-
vi.mock("../temp-prompt.ts", () => ({
|
|
77
|
+
vi.mock("../engine/engines/pi/temp-prompt.ts", () => ({
|
|
73
78
|
writePromptToTempFile: vi.fn(async (agent: string) => {
|
|
74
79
|
const safeName = agent.replace(/[^\w.-]+/g, "_");
|
|
75
80
|
return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
|
|
@@ -77,13 +82,22 @@ vi.mock("../temp-prompt.ts", () => ({
|
|
|
77
82
|
cleanupTempPrompt: vi.fn(async () => {}),
|
|
78
83
|
}));
|
|
79
84
|
|
|
80
|
-
import { runSpawn } from "../session-runner.ts";
|
|
85
|
+
import { runSpawn } from "../engine/engines/pi/session-runner.ts";
|
|
81
86
|
import {
|
|
82
87
|
_resetSettledWatchdogsForTest,
|
|
88
|
+
armMidRoundNoProgress,
|
|
83
89
|
armSettledWatchdog,
|
|
84
90
|
disarmSettledWatchdog,
|
|
91
|
+
getSettledWatchdogPhase,
|
|
92
|
+
getSettledWatchdogTimeoutMs,
|
|
93
|
+
handoverMidRoundToSettled,
|
|
85
94
|
hasSettledWatchdog,
|
|
95
|
+
isSettledWatchdogDisabled,
|
|
96
|
+
refreshMidRoundNoProgress,
|
|
97
|
+
SETTLED_MID_ROUND_NO_PROGRESS_MS,
|
|
98
|
+
SETTLED_WATCHDOG_ENV,
|
|
86
99
|
SETTLED_WATCHDOG_TIMEOUT_MS,
|
|
100
|
+
type SettledWatchdogFireInfo,
|
|
87
101
|
} from "../settled-watchdog.ts";
|
|
88
102
|
import { _resetLifecycleState } from "../lifecycle-manager.ts";
|
|
89
103
|
import { createRecord } from "../execution-record.ts";
|
|
@@ -104,12 +118,12 @@ function sessionHeaderLine(): Record<string, unknown> {
|
|
|
104
118
|
return { type: "session", id: "sess-sw", timestamp: "2026-09-01T00:00:00.000Z", cwd: "/tmp/test" };
|
|
105
119
|
}
|
|
106
120
|
|
|
107
|
-
/** 构造 chatMode record(idleTimeoutMs 拉长到 1h,防 idle timer
|
|
121
|
+
/** 构造 chatMode record(idleTimeoutMs 拉长到 1h,防 idle timer 干扰两段窗口断言)。 */
|
|
108
122
|
function makeChatModeRecord(id = "sa-chat-sw"): ExecutionRecord {
|
|
109
123
|
const record = createRecord(id, {
|
|
110
124
|
agent: "general-purpose",
|
|
111
125
|
model: "test-model",
|
|
112
|
-
mode: "
|
|
126
|
+
mode: "background",
|
|
113
127
|
task: "chat task",
|
|
114
128
|
slug: "chat",
|
|
115
129
|
startedAt: 1_000_000,
|
|
@@ -118,7 +132,7 @@ function makeChatModeRecord(id = "sa-chat-sw"): ExecutionRecord {
|
|
|
118
132
|
depth: 0,
|
|
119
133
|
chatMode: true,
|
|
120
134
|
});
|
|
121
|
-
record.idleTimeoutMs = 3_600_000;
|
|
135
|
+
(record as { idleTimeoutMs: number }).idleTimeoutMs = 3_600_000;
|
|
122
136
|
return record;
|
|
123
137
|
}
|
|
124
138
|
|
|
@@ -135,57 +149,234 @@ async function waitForSpawnUnderFakeTimers(baseline: number, maxIters = 50): Pro
|
|
|
135
149
|
}
|
|
136
150
|
}
|
|
137
151
|
|
|
138
|
-
|
|
152
|
+
// ============================================================
|
|
153
|
+
// 原语单测(两段式,验收 ①-⑤)
|
|
154
|
+
// ============================================================
|
|
155
|
+
|
|
156
|
+
describe("[T2-③ / D9] settled-watchdog 两段式原语", () => {
|
|
139
157
|
beforeEach(() => {
|
|
140
158
|
vi.clearAllMocks();
|
|
159
|
+
vi.unstubAllEnvs();
|
|
141
160
|
_resetSettledWatchdogsForTest();
|
|
142
161
|
});
|
|
143
162
|
|
|
144
163
|
afterEach(() => {
|
|
145
164
|
_resetSettledWatchdogsForTest();
|
|
165
|
+
vi.unstubAllEnvs();
|
|
146
166
|
vi.useRealTimers();
|
|
147
167
|
});
|
|
148
168
|
|
|
149
|
-
it("
|
|
150
|
-
expect(
|
|
169
|
+
it("两常量值:中段 30min(对齐 keep-alive 先例)+ 收尾段 600s(P-T2c 定案,值不变)", () => {
|
|
170
|
+
expect(SETTLED_MID_ROUND_NO_PROGRESS_MS).toBe(30 * 60 * 1000);
|
|
171
|
+
expect(SETTLED_WATCHDOG_TIMEOUT_MS).toBe(600_000);
|
|
172
|
+
expect(getSettledWatchdogTimeoutMs()).toBe(600_000);
|
|
173
|
+
expect(isSettledWatchdogDisabled()).toBe(false);
|
|
151
174
|
});
|
|
152
175
|
|
|
153
|
-
it("
|
|
176
|
+
it("验收①:agent_end 前静默超时按第一段语义触发(mid-round 回调 + 段信息;收尾回调不触发)", async () => {
|
|
154
177
|
vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout"] });
|
|
155
|
-
const
|
|
156
|
-
|
|
178
|
+
const onMidTimeout = vi.fn();
|
|
179
|
+
const onSettleTimeout = vi.fn();
|
|
180
|
+
armMidRoundNoProgress("sa-1", { onMidTimeout, onSettleTimeout });
|
|
157
181
|
expect(hasSettledWatchdog("sa-1")).toBe(true);
|
|
182
|
+
expect(getSettledWatchdogPhase("sa-1")).toBe("mid-round");
|
|
158
183
|
|
|
159
|
-
await vi.advanceTimersByTimeAsync(
|
|
160
|
-
expect(
|
|
184
|
+
await vi.advanceTimersByTimeAsync(SETTLED_MID_ROUND_NO_PROGRESS_MS - 1);
|
|
185
|
+
expect(onMidTimeout).not.toHaveBeenCalled();
|
|
161
186
|
await vi.advanceTimersByTimeAsync(1);
|
|
162
|
-
expect(
|
|
187
|
+
expect(onMidTimeout).toHaveBeenCalledTimes(1);
|
|
188
|
+
expect(onMidTimeout).toHaveBeenCalledWith({ phase: "mid-round", waitedMs: SETTLED_MID_ROUND_NO_PROGRESS_MS });
|
|
189
|
+
// 第一段语义:只触发中段回调,收尾回调不参与
|
|
190
|
+
expect(onSettleTimeout).not.toHaveBeenCalled();
|
|
163
191
|
// 到期回调先自删条目(对齐 armIdleTimer 语义)
|
|
164
192
|
expect(hasSettledWatchdog("sa-1")).toBe(false);
|
|
165
193
|
|
|
166
194
|
// disarm 已触发/不存在的 record:幂等 no-op
|
|
167
195
|
disarmSettledWatchdog("sa-1");
|
|
168
196
|
disarmSettledWatchdog("never-armed");
|
|
169
|
-
expect(
|
|
197
|
+
expect(onMidTimeout).toHaveBeenCalledTimes(1);
|
|
170
198
|
});
|
|
171
199
|
|
|
172
|
-
it("
|
|
200
|
+
it("验收②:agent_end 交棒后切换到收尾段(phase=settled;收尾段从交棒点独立计时)", async () => {
|
|
173
201
|
vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout"] });
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
// 重挂(新一轮 prompt):旧窗口必须作废
|
|
178
|
-
armSettledWatchdog("sa-2", onTimeout);
|
|
202
|
+
const onMidTimeout = vi.fn();
|
|
203
|
+
const onSettleTimeout = vi.fn();
|
|
204
|
+
armMidRoundNoProgress("sa-2", { onMidTimeout, onSettleTimeout });
|
|
179
205
|
|
|
180
|
-
|
|
206
|
+
handoverMidRoundToSettled("sa-2");
|
|
207
|
+
expect(getSettledWatchdogPhase("sa-2")).toBe("settled");
|
|
208
|
+
expect(hasSettledWatchdog("sa-2")).toBe(true);
|
|
209
|
+
|
|
210
|
+
// 交棒后满收尾段窗长才触发,触发的是收尾回调(段信息 settled + 600s)
|
|
181
211
|
await vi.advanceTimersByTimeAsync(SETTLED_WATCHDOG_TIMEOUT_MS - 1);
|
|
182
|
-
expect(
|
|
212
|
+
expect(onSettleTimeout).not.toHaveBeenCalled();
|
|
183
213
|
await vi.advanceTimersByTimeAsync(1);
|
|
184
|
-
expect(
|
|
214
|
+
expect(onSettleTimeout).toHaveBeenCalledTimes(1);
|
|
215
|
+
expect(onSettleTimeout).toHaveBeenCalledWith({ phase: "settled", waitedMs: SETTLED_WATCHDOG_TIMEOUT_MS });
|
|
216
|
+
expect(onMidTimeout).not.toHaveBeenCalled();
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it("验收③:收尾段内 settle 到达(disarm)则正常解除不误杀", async () => {
|
|
220
|
+
vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout"] });
|
|
221
|
+
const onMidTimeout = vi.fn();
|
|
222
|
+
const onSettleTimeout = vi.fn();
|
|
223
|
+
armMidRoundNoProgress("sa-3", { onMidTimeout, onSettleTimeout });
|
|
224
|
+
handoverMidRoundToSettled("sa-3");
|
|
225
|
+
|
|
226
|
+
// settled 到达(生产接线:handleSdkEvent / close / 收尾统一 disarm,两段一并清)
|
|
227
|
+
disarmSettledWatchdog("sa-3");
|
|
228
|
+
expect(hasSettledWatchdog("sa-3")).toBe(false);
|
|
229
|
+
|
|
230
|
+
await vi.advanceTimersByTimeAsync(SETTLED_WATCHDOG_TIMEOUT_MS + 60_000);
|
|
231
|
+
expect(onSettleTimeout).not.toHaveBeenCalled();
|
|
232
|
+
expect(onMidTimeout).not.toHaveBeenCalled();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it("验收④:两段各自独立计时——中段已过时间不继承进收尾段(D9 交棒语义,构造性断言)", async () => {
|
|
236
|
+
vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout"] });
|
|
237
|
+
const onMidTimeout = vi.fn();
|
|
238
|
+
const onSettleTimeout = vi.fn();
|
|
239
|
+
armMidRoundNoProgress("sa-4", { onMidTimeout, onSettleTimeout });
|
|
240
|
+
|
|
241
|
+
// 中段跑了 29min(差 1min 触发)时 agent_end 到达交棒
|
|
242
|
+
await vi.advanceTimersByTimeAsync(SETTLED_MID_ROUND_NO_PROGRESS_MS - 60_000);
|
|
243
|
+
handoverMidRoundToSettled("sa-4");
|
|
244
|
+
|
|
245
|
+
// 若收尾段继承中段已过时间,agent_end 后约 10min 即触发;独立计时应满 600s
|
|
246
|
+
await vi.advanceTimersByTimeAsync(SETTLED_WATCHDOG_TIMEOUT_MS - 60_000);
|
|
247
|
+
expect(onSettleTimeout).not.toHaveBeenCalled();
|
|
248
|
+
expect(onMidTimeout).not.toHaveBeenCalled();
|
|
249
|
+
await vi.advanceTimersByTimeAsync(60_000);
|
|
250
|
+
expect(onSettleTimeout).toHaveBeenCalledTimes(1);
|
|
251
|
+
expect(onSettleTimeout).toHaveBeenCalledWith({ phase: "settled", waitedMs: SETTLED_WATCHDOG_TIMEOUT_MS });
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it("中段刷新语义:有效事件行重挂同窗(连续静默才触发);交棒后刷新 no-op(收尾段不刷新)", async () => {
|
|
255
|
+
vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout"] });
|
|
256
|
+
const onMidTimeout = vi.fn();
|
|
257
|
+
const onSettleTimeout = vi.fn();
|
|
258
|
+
armMidRoundNoProgress("sa-5", { onMidTimeout, onSettleTimeout });
|
|
259
|
+
|
|
260
|
+
// 静默 29min → 事件刷新 → 再 29min 不触发(若未刷新早已触发)
|
|
261
|
+
await vi.advanceTimersByTimeAsync(SETTLED_MID_ROUND_NO_PROGRESS_MS - 60_000);
|
|
262
|
+
refreshMidRoundNoProgress("sa-5");
|
|
263
|
+
await vi.advanceTimersByTimeAsync(SETTLED_MID_ROUND_NO_PROGRESS_MS - 60_000);
|
|
264
|
+
expect(onMidTimeout).not.toHaveBeenCalled();
|
|
265
|
+
// 刷新后再满 1min 静默触发
|
|
266
|
+
await vi.advanceTimersByTimeAsync(60_000);
|
|
267
|
+
expect(onMidTimeout).toHaveBeenCalledTimes(1);
|
|
268
|
+
|
|
269
|
+
// 收尾段不刷新(D9 被否 (b) 方案的否决理由:刷新让收尾段失去唯一可收敛形态)
|
|
270
|
+
const onSettleOnly = vi.fn();
|
|
271
|
+
armSettledWatchdog("sa-5b", onSettleOnly);
|
|
272
|
+
refreshMidRoundNoProgress("sa-5b");
|
|
273
|
+
await vi.advanceTimersByTimeAsync(SETTLED_WATCHDOG_TIMEOUT_MS);
|
|
274
|
+
expect(onSettleOnly).toHaveBeenCalledTimes(1);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it("handover 幂等:未挂载 / 重复交棒 / 收尾段后再交棒均 no-op(pump 每轮 agent_end 都调)", async () => {
|
|
278
|
+
vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout"] });
|
|
279
|
+
// 未挂载:no-op
|
|
280
|
+
handoverMidRoundToSettled("sa-6-none");
|
|
281
|
+
expect(hasSettledWatchdog("sa-6-none")).toBe(false);
|
|
282
|
+
|
|
283
|
+
const onMidTimeout = vi.fn();
|
|
284
|
+
const onSettleTimeout = vi.fn();
|
|
285
|
+
armMidRoundNoProgress("sa-6", { onMidTimeout, onSettleTimeout });
|
|
286
|
+
// 重复交棒:第二次 no-op(不会叠加/重置收尾段计时)
|
|
287
|
+
handoverMidRoundToSettled("sa-6");
|
|
288
|
+
await vi.advanceTimersByTimeAsync(SETTLED_WATCHDOG_TIMEOUT_MS / 2);
|
|
289
|
+
handoverMidRoundToSettled("sa-6");
|
|
290
|
+
await vi.advanceTimersByTimeAsync(SETTLED_WATCHDOG_TIMEOUT_MS / 2);
|
|
291
|
+
expect(onSettleTimeout).toHaveBeenCalledTimes(1);
|
|
292
|
+
expect(onMidTimeout).not.toHaveBeenCalled();
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it("重复 arm 以新窗口覆盖旧计时(新一轮 prompt:旧中段/残留收尾段一并作废)", async () => {
|
|
296
|
+
vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout"] });
|
|
297
|
+
const onMidTimeout = vi.fn();
|
|
298
|
+
const onSettleTimeout = vi.fn();
|
|
299
|
+
armMidRoundNoProgress("sa-7", { onMidTimeout, onSettleTimeout });
|
|
300
|
+
await vi.advanceTimersByTimeAsync(SETTLED_MID_ROUND_NO_PROGRESS_MS / 2);
|
|
301
|
+
// 新一轮 prompt 重新 arm:旧窗口必须作废(含异常形态下残留的收尾段)
|
|
302
|
+
armMidRoundNoProgress("sa-7", { onMidTimeout, onSettleTimeout });
|
|
303
|
+
expect(getSettledWatchdogPhase("sa-7")).toBe("mid-round");
|
|
304
|
+
|
|
305
|
+
await vi.advanceTimersByTimeAsync(SETTLED_MID_ROUND_NO_PROGRESS_MS - 1);
|
|
306
|
+
expect(onMidTimeout).not.toHaveBeenCalled();
|
|
307
|
+
await vi.advanceTimersByTimeAsync(1);
|
|
308
|
+
expect(onMidTimeout).toHaveBeenCalledTimes(1);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// ── 验收⑤:env XYZ_SUBAGENT_SETTLED_WATCHDOG_MS ──
|
|
312
|
+
|
|
313
|
+
it("验收⑤a:env >0 覆盖收尾段定值(生效窗长随 fire 信息回传)", async () => {
|
|
314
|
+
vi.stubEnv(SETTLED_WATCHDOG_ENV, "12345");
|
|
315
|
+
_resetSettledWatchdogsForTest(); // 清 env 解析缓存使新 env 生效
|
|
316
|
+
vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout"] });
|
|
317
|
+
expect(getSettledWatchdogTimeoutMs()).toBe(12_345);
|
|
318
|
+
expect(isSettledWatchdogDisabled()).toBe(false);
|
|
319
|
+
|
|
320
|
+
const onSettleTimeout = vi.fn();
|
|
321
|
+
armSettledWatchdog("sa-8", onSettleTimeout);
|
|
322
|
+
await vi.advanceTimersByTimeAsync(12_345 - 1);
|
|
323
|
+
expect(onSettleTimeout).not.toHaveBeenCalled();
|
|
324
|
+
await vi.advanceTimersByTimeAsync(1);
|
|
325
|
+
expect(onSettleTimeout).toHaveBeenCalledWith({ phase: "settled", waitedMs: 12_345 });
|
|
326
|
+
|
|
327
|
+
// 交棒路径同样消费覆盖值
|
|
328
|
+
const onSettle2 = vi.fn();
|
|
329
|
+
armMidRoundNoProgress("sa-8b", { onMidTimeout: vi.fn(), onSettleTimeout: onSettle2 });
|
|
330
|
+
handoverMidRoundToSettled("sa-8b");
|
|
331
|
+
await vi.advanceTimersByTimeAsync(12_345);
|
|
332
|
+
expect(onSettle2).toHaveBeenCalledWith({ phase: "settled", waitedMs: 12_345 });
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it("验收⑤b:env ≤0 关闭两段(arm no-op + warn 明示三无窗口后果)", async () => {
|
|
336
|
+
vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout"] });
|
|
337
|
+
vi.stubEnv(SETTLED_WATCHDOG_ENV, "0");
|
|
338
|
+
_resetSettledWatchdogsForTest();
|
|
339
|
+
expect(isSettledWatchdogDisabled()).toBe(true);
|
|
340
|
+
|
|
341
|
+
const onMidTimeout = vi.fn();
|
|
342
|
+
const onSettleTimeout = vi.fn();
|
|
343
|
+
armMidRoundNoProgress("sa-9", { onMidTimeout, onSettleTimeout });
|
|
344
|
+
// 两段都不挂载(关闭语义:回到「三无窗口」,warn 已明示)
|
|
345
|
+
expect(hasSettledWatchdog("sa-9")).toBe(false);
|
|
346
|
+
handoverMidRoundToSettled("sa-9");
|
|
347
|
+
refreshMidRoundNoProgress("sa-9");
|
|
348
|
+
armSettledWatchdog("sa-9", onSettleTimeout);
|
|
349
|
+
expect(hasSettledWatchdog("sa-9")).toBe(false);
|
|
350
|
+
|
|
351
|
+
await vi.advanceTimersByTimeAsync(SETTLED_MID_ROUND_NO_PROGRESS_MS + SETTLED_WATCHDOG_TIMEOUT_MS);
|
|
352
|
+
expect(onMidTimeout).not.toHaveBeenCalled();
|
|
353
|
+
expect(onSettleTimeout).not.toHaveBeenCalled();
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it("验收⑤c:env 非法(非数字)回落默认 + warn 留痕(LC-7 教训:非法回落必须可见)", () => {
|
|
357
|
+
vi.stubEnv(SETTLED_WATCHDOG_ENV, "not-a-number");
|
|
358
|
+
_resetSettledWatchdogsForTest();
|
|
359
|
+
expect(getSettledWatchdogTimeoutMs()).toBe(SETTLED_WATCHDOG_TIMEOUT_MS);
|
|
360
|
+
expect(isSettledWatchdogDisabled()).toBe(false);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it("验收⑤d:env 未设/空串 = 默认 600s(规则 19:用户显式指定才生效)", () => {
|
|
364
|
+
delete process.env[SETTLED_WATCHDOG_ENV];
|
|
365
|
+
_resetSettledWatchdogsForTest();
|
|
366
|
+
expect(getSettledWatchdogTimeoutMs()).toBe(600_000);
|
|
367
|
+
|
|
368
|
+
vi.stubEnv(SETTLED_WATCHDOG_ENV, " ");
|
|
369
|
+
_resetSettledWatchdogsForTest();
|
|
370
|
+
expect(getSettledWatchdogTimeoutMs()).toBe(600_000);
|
|
371
|
+
expect(isSettledWatchdogDisabled()).toBe(false);
|
|
185
372
|
});
|
|
186
373
|
});
|
|
187
374
|
|
|
188
|
-
|
|
375
|
+
// ============================================================
|
|
376
|
+
// runSpawn 首轮挂载集成(chatMode)
|
|
377
|
+
// ============================================================
|
|
378
|
+
|
|
379
|
+
describe("[T2-③ / D9] runSpawn 首轮挂载(chatMode)", () => {
|
|
189
380
|
beforeEach(() => {
|
|
190
381
|
vi.clearAllMocks();
|
|
191
382
|
vi.stubEnv("XYZ_SUBAGENT_SPAWN_WATCHDOG_MS", "");
|
|
@@ -202,19 +393,21 @@ describe("[T2-③] runSpawn 首轮挂载(chatMode)", () => {
|
|
|
202
393
|
_resetSettledWatchdogsForTest();
|
|
203
394
|
});
|
|
204
395
|
|
|
205
|
-
it("
|
|
396
|
+
it("验收①挂载面:agent_end 前静默超 30min(中段语义)→ kill 层主 → runSpawn 错误返回含段标注 + 恢复指引", async () => {
|
|
206
397
|
const record = makeChatModeRecord();
|
|
207
398
|
const baseline = mockSpawn.mock.results.length;
|
|
208
|
-
const promise = runSpawn(record, "Chat: wedge", makeOpts(), makeCtx());
|
|
399
|
+
const promise = runSpawn(record, "Chat: mid-round wedge", makeOpts(), makeCtx());
|
|
209
400
|
await waitForSpawnUnderFakeTimers(baseline);
|
|
210
401
|
const child = lastSpawnedChild();
|
|
211
402
|
|
|
212
|
-
//
|
|
213
|
-
//
|
|
214
|
-
// 断言边界留 1s 余量——对 10min 上限语义无损,对计时器实现细节稳健。
|
|
403
|
+
// prompt 发出后挂中段:advance 到中段窗长触发(原 10min 全程窗已不存在——
|
|
404
|
+
// advance 10min 静默不再误杀,是对 P0-4 的直接回归断言)
|
|
215
405
|
emitStdoutLine(child, sessionHeaderLine());
|
|
216
|
-
await vi.advanceTimersByTimeAsync(
|
|
406
|
+
await vi.advanceTimersByTimeAsync(10 * 60 * 1000);
|
|
407
|
+
expect(child.killed).toBe(false);
|
|
408
|
+
await vi.advanceTimersByTimeAsync(SETTLED_MID_ROUND_NO_PROGRESS_MS - 10 * 60 * 1000 - 1_000);
|
|
217
409
|
expect(child.killed).toBe(false);
|
|
410
|
+
// [余量] fake timers 对 Date mock 有 ms 级舍入,断言边界留 1s 余量(对阈值语义无损)
|
|
218
411
|
await vi.advanceTimersByTimeAsync(2_000);
|
|
219
412
|
expect(child.killed).toBe(true);
|
|
220
413
|
expect(child.killSignal).toBe("SIGTERM");
|
|
@@ -227,12 +420,44 @@ describe("[T2-③] runSpawn 首轮挂载(chatMode)", () => {
|
|
|
227
420
|
expect(result.success).toBe(false);
|
|
228
421
|
expect(result.error).toContain("settled watchdog");
|
|
229
422
|
expect(result.error).toContain("agent_settled");
|
|
423
|
+
expect(result.error).toContain("mid-round no-progress");
|
|
230
424
|
// S-B 验收判据:错误消息含恢复指引
|
|
231
425
|
expect(result.error).toContain("Recovery:");
|
|
232
426
|
expect(result.error).toContain("action:'list'");
|
|
233
427
|
});
|
|
234
428
|
|
|
235
|
-
it("
|
|
429
|
+
it("验收②挂载面:agent_end 交棒后收尾段 600s 独立计时——中段 30min 不再适用,600s 到期 kill + settled 段文案", async () => {
|
|
430
|
+
const record = makeChatModeRecord();
|
|
431
|
+
const baseline = mockSpawn.mock.results.length;
|
|
432
|
+
const promise = runSpawn(record, "Chat: settle never arrives", makeOpts(), makeCtx());
|
|
433
|
+
await waitForSpawnUnderFakeTimers(baseline);
|
|
434
|
+
const child = lastSpawnedChild();
|
|
435
|
+
|
|
436
|
+
emitStdoutLine(child, sessionHeaderLine());
|
|
437
|
+
// agent_end 到达:交棒收尾段(中段静默 30min 判定随之失效)
|
|
438
|
+
emitStdoutLine(child, { type: "agent_end", messages: [], willRetry: false });
|
|
439
|
+
await new Promise((r) => setImmediate(r));
|
|
440
|
+
|
|
441
|
+
// 收尾段从 agent_end 起独立计时:满 600s 前不触发(600s < 中段 30min——本推进
|
|
442
|
+
// 段内无论收尾段还是残留中段都不该 fire)
|
|
443
|
+
await vi.advanceTimersByTimeAsync(SETTLED_WATCHDOG_TIMEOUT_MS - 2_000);
|
|
444
|
+
expect(child.killed).toBe(false);
|
|
445
|
+
await vi.advanceTimersByTimeAsync(4_000);
|
|
446
|
+
expect(child.killed).toBe(true);
|
|
447
|
+
expect(child.killSignal).toBe("SIGTERM");
|
|
448
|
+
|
|
449
|
+
child.stdout.end();
|
|
450
|
+
child.stderr.end();
|
|
451
|
+
child.emit("close", 143);
|
|
452
|
+
const result = await promise;
|
|
453
|
+
expect(result.success).toBe(false);
|
|
454
|
+
expect(result.error).toContain("settled watchdog");
|
|
455
|
+
expect(result.error).toContain("settled phase");
|
|
456
|
+
expect(result.error).toContain("after agent_end");
|
|
457
|
+
expect(result.error).toContain("Recovery:");
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
it("验收③挂载面:agent_end + agent_settled 正常轮转即清——advance 两段窗长合计不误杀已正常轮转的层主", async () => {
|
|
236
461
|
const record = makeChatModeRecord();
|
|
237
462
|
const baseline = mockSpawn.mock.results.length;
|
|
238
463
|
const promise = runSpawn(record, "Chat: normal", makeOpts(), makeCtx());
|
|
@@ -244,10 +469,10 @@ describe("[T2-③] runSpawn 首轮挂载(chatMode)", () => {
|
|
|
244
469
|
emitStdoutLine(child, { type: "agent_settled" });
|
|
245
470
|
await new Promise((r) => setImmediate(r));
|
|
246
471
|
|
|
247
|
-
// 首轮窗口结束(resolveRun
|
|
472
|
+
// 首轮窗口结束(resolveRun 同点清除两段守护);idle timer 被拉长到 1h 不干扰
|
|
248
473
|
expect(hasSettledWatchdog(record.id)).toBe(false);
|
|
249
|
-
await vi.advanceTimersByTimeAsync(SETTLED_WATCHDOG_TIMEOUT_MS + 60_000);
|
|
250
|
-
// settled watchdog
|
|
474
|
+
await vi.advanceTimersByTimeAsync(SETTLED_MID_ROUND_NO_PROGRESS_MS + SETTLED_WATCHDOG_TIMEOUT_MS + 60_000);
|
|
475
|
+
// settled watchdog 已清:子进程未被本守护 kill(若回归未清,此处 killed=true)
|
|
251
476
|
expect(child.killed).toBe(false);
|
|
252
477
|
|
|
253
478
|
// 收尾
|
|
@@ -259,7 +484,7 @@ describe("[T2-③] runSpawn 首轮挂载(chatMode)", () => {
|
|
|
259
484
|
expect(result.error).toBeUndefined();
|
|
260
485
|
});
|
|
261
486
|
|
|
262
|
-
it("挂载面限定 chatMode:非 chatMode 首轮不挂 settled
|
|
487
|
+
it("挂载面限定 chatMode:非 chatMode 首轮不挂 settled 守护(中段/收尾段均无)", async () => {
|
|
263
488
|
const record = makeRecord(); // 非 chatMode
|
|
264
489
|
const baseline = mockSpawn.mock.results.length;
|
|
265
490
|
const promise = runSpawn(record, "Task: not chat", makeOpts(), makeCtx());
|
|
@@ -270,9 +495,9 @@ describe("[T2-③] runSpawn 首轮挂载(chatMode)", () => {
|
|
|
270
495
|
await new Promise((r) => setImmediate(r));
|
|
271
496
|
expect(hasSettledWatchdog(record.id)).toBe(false);
|
|
272
497
|
|
|
273
|
-
// 非 chatMode
|
|
498
|
+
// 非 chatMode:即使静默超过中段窗长也不被 settled 守护 kill(spawn watchdog 同为
|
|
274
499
|
// 未挂载——裸缺省 + env 清),agent_end 之前的执行期不受 T2-③ 触及
|
|
275
|
-
await vi.advanceTimersByTimeAsync(
|
|
500
|
+
await vi.advanceTimersByTimeAsync(SETTLED_MID_ROUND_NO_PROGRESS_MS + 60_000);
|
|
276
501
|
expect(child.killed).toBe(false);
|
|
277
502
|
|
|
278
503
|
// agent_end(count=0)→ final kill(既有语义)
|
|
@@ -287,3 +512,13 @@ describe("[T2-③] runSpawn 首轮挂载(chatMode)", () => {
|
|
|
287
512
|
expect(result.success).toBe(true);
|
|
288
513
|
});
|
|
289
514
|
});
|
|
515
|
+
|
|
516
|
+
// fire info 类型形状守卫(挂载点回调签名消费;编译期校验的运行时锚定)
|
|
517
|
+
describe("[T2-③ / D9] SettledWatchdogFireInfo 形状", () => {
|
|
518
|
+
it("fire 信息的段与窗长字段齐全(挂载点文案分叉的契约面)", () => {
|
|
519
|
+
const mid: SettledWatchdogFireInfo = { phase: "mid-round", waitedMs: SETTLED_MID_ROUND_NO_PROGRESS_MS };
|
|
520
|
+
const settled: SettledWatchdogFireInfo = { phase: "settled", waitedMs: SETTLED_WATCHDOG_TIMEOUT_MS };
|
|
521
|
+
expect(mid.phase).toBe("mid-round");
|
|
522
|
+
expect(settled.phase).toBe("settled");
|
|
523
|
+
});
|
|
524
|
+
});
|
|
@@ -6,20 +6,21 @@ import * as path from "node:path";
|
|
|
6
6
|
|
|
7
7
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
8
8
|
|
|
9
|
-
import { mirrorMainProcessFlags } from "../argv-mirror.ts";
|
|
9
|
+
import { mirrorMainProcessFlags } from "../engine/engines/pi/argv-mirror.ts";
|
|
10
10
|
import { MAX_FORK_DEPTH } from "../session-context-resolver.ts";
|
|
11
|
-
import { buildEnvBlock, buildSpawnArgs } from "../session-runner.ts";
|
|
11
|
+
import { buildEnvBlock, buildSpawnArgs } from "../engine/engines/pi/session-runner.ts";
|
|
12
12
|
|
|
13
13
|
describe("buildSpawnArgs", () => {
|
|
14
14
|
// [U1 D2] modelRef 为必填(spawn 前置守卫:未经裁决的裸字符串类型层面不可达)。
|
|
15
|
-
|
|
15
|
+
// 类型直接锚定 buildSpawnArgs 入参(thinkingLevel 是 ThinkingLevel 枚举,非裸 string)
|
|
16
|
+
const baseParams: Parameters<typeof buildSpawnArgs>[0] = {
|
|
16
17
|
modelRef: { provider: "openai", id: "gpt-4o" },
|
|
17
|
-
thinkingLevel: undefined
|
|
18
|
-
agentTools: undefined
|
|
19
|
-
appendSystemPromptPath: undefined
|
|
18
|
+
thinkingLevel: undefined,
|
|
19
|
+
agentTools: undefined,
|
|
20
|
+
appendSystemPromptPath: undefined,
|
|
20
21
|
sessionDir: "/sessions/dir",
|
|
21
|
-
forkSource: undefined
|
|
22
|
-
skillPaths: undefined
|
|
22
|
+
forkSource: undefined,
|
|
23
|
+
skillPaths: undefined,
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
it("基础参数:--mode rpc --session-dir + --model provider/id,不含 -p 也不含 task(task 经 stdin 传)", () => {
|
|
@@ -372,7 +373,7 @@ describe("buildEnvBlock", () => {
|
|
|
372
373
|
});
|
|
373
374
|
|
|
374
375
|
afterEach(() => {
|
|
375
|
-
fs.rmSync(tmpGitRepo, { recursive: true, force: true });
|
|
376
|
+
fs.rmSync(tmpGitRepo, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
376
377
|
});
|
|
377
378
|
|
|
378
379
|
it("注入 cwd(Working directory 行)", async () => {
|
|
@@ -429,7 +430,7 @@ describe("buildEnvBlock", () => {
|
|
|
429
430
|
// 但仍含 working directory(环境块始终输出)
|
|
430
431
|
expect(block).toContain(`Working directory: ${nonGitDir}`);
|
|
431
432
|
} finally {
|
|
432
|
-
fs.rmSync(nonGitDir, { recursive: true, force: true });
|
|
433
|
+
fs.rmSync(nonGitDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
433
434
|
}
|
|
434
435
|
});
|
|
435
436
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
//
|
|
3
3
|
// W1 红灯测试:spawn-event-adapter.ts 协议层重写 — Pi 原生格式 + method 平铺。
|
|
4
4
|
//
|
|
5
|
-
// 测试对象:
|
|
5
|
+
// 测试对象:packages/subagent-core/src/execution/engine/engines/pi/spawn-event-adapter.ts(改造)
|
|
6
6
|
// 契约来源:.fix-plans/00-master-summary.md §二(统一接口契约)+ §一冲突 2/判定顺序
|
|
7
7
|
//
|
|
8
8
|
// 核心改动:
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
import { describe, expect, it } from "vitest";
|
|
20
20
|
|
|
21
|
-
import { parseSpawnLine } from "../spawn-event-adapter.ts";
|
|
21
|
+
import { parseSpawnLine } from "../engine/engines/pi/spawn-event-adapter.ts";
|
|
22
22
|
|
|
23
23
|
// ── Pi 原生协议样本构造(来源:rpc-types.ts L230-265 真实类型) ──────────
|
|
24
24
|
// 10 种 method,全部 {type:"extension_ui_request", id, method, ...平铺字段}。
|
|
@@ -28,6 +28,17 @@ function piLine(methodFields: Record<string, unknown>): string {
|
|
|
28
28
|
return JSON.stringify({ type: "extension_ui_request", id: "req-001", ...methodFields });
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
|
|
32
|
+
/** 判别收窄辅助:string-fallback 变体(method: string)使 !== 收窄失效,
|
|
33
|
+
* 用 Extract 条件类型(可赋值方向)精确取变体。 */
|
|
34
|
+
function asVariant<M extends string>(
|
|
35
|
+
req: import("../engine/engines/pi/spawn-event-adapter.ts").ExtensionUiRequest,
|
|
36
|
+
method: M,
|
|
37
|
+
): Extract<import("../engine/engines/pi/spawn-event-adapter.ts").ExtensionUiRequest, { method: M }> {
|
|
38
|
+
if (req.method !== method) throw new Error(`request.method 应为 ${method}`);
|
|
39
|
+
return req as Extract<import("../engine/engines/pi/spawn-event-adapter.ts").ExtensionUiRequest, { method: M }>;
|
|
40
|
+
}
|
|
41
|
+
|
|
31
42
|
describe("parseSpawnLine — 10 种 method 真实样本分类(Pi 原生格式)", () => {
|
|
32
43
|
describe("dialog 类(select/confirm/input/editor)", () => {
|
|
33
44
|
it("select → kind=extension_ui_request + request.method=select + title/options 平铺", () => {
|
|
@@ -37,9 +48,7 @@ describe("parseSpawnLine — 10 种 method 真实样本分类(Pi 原生格式
|
|
|
37
48
|
expect(result?.kind).toBe("extension_ui_request");
|
|
38
49
|
if (result?.kind !== "extension_ui_request") return;
|
|
39
50
|
expect(result.id).toBe("req-001");
|
|
40
|
-
|
|
41
|
-
expect(result.request.title).toBe("Pick one");
|
|
42
|
-
expect(result.request.options).toEqual(["a", "b"]);
|
|
51
|
+
const req = asVariant(result.request, "select");
|
|
43
52
|
});
|
|
44
53
|
|
|
45
54
|
it("confirm → request.method=confirm + title/message 平铺", () => {
|
|
@@ -48,9 +57,7 @@ describe("parseSpawnLine — 10 种 method 真实样本分类(Pi 原生格式
|
|
|
48
57
|
}));
|
|
49
58
|
expect(result?.kind).toBe("extension_ui_request");
|
|
50
59
|
if (result?.kind !== "extension_ui_request") return;
|
|
51
|
-
|
|
52
|
-
expect(result.request.title).toBe("Sure?");
|
|
53
|
-
expect(result.request.message).toBe("Proceed?");
|
|
60
|
+
const req = asVariant(result.request, "confirm");
|
|
54
61
|
});
|
|
55
62
|
|
|
56
63
|
it("input → request.method=input + title + placeholder", () => {
|
|
@@ -59,8 +66,7 @@ describe("parseSpawnLine — 10 种 method 真实样本分类(Pi 原生格式
|
|
|
59
66
|
}));
|
|
60
67
|
expect(result?.kind).toBe("extension_ui_request");
|
|
61
68
|
if (result?.kind !== "extension_ui_request") return;
|
|
62
|
-
|
|
63
|
-
expect(result.request.placeholder).toBe("type here");
|
|
69
|
+
const req = asVariant(result.request, "input");
|
|
64
70
|
});
|
|
65
71
|
|
|
66
72
|
it("editor → request.method=editor + title + prefill", () => {
|
|
@@ -69,8 +75,7 @@ describe("parseSpawnLine — 10 种 method 真实样本分类(Pi 原生格式
|
|
|
69
75
|
}));
|
|
70
76
|
expect(result?.kind).toBe("extension_ui_request");
|
|
71
77
|
if (result?.kind !== "extension_ui_request") return;
|
|
72
|
-
|
|
73
|
-
expect(result.request.prefill).toBe("initial text");
|
|
78
|
+
const req = asVariant(result.request, "editor");
|
|
74
79
|
});
|
|
75
80
|
});
|
|
76
81
|
|
|
@@ -81,8 +86,7 @@ describe("parseSpawnLine — 10 种 method 真实样本分类(Pi 原生格式
|
|
|
81
86
|
}));
|
|
82
87
|
expect(result?.kind).toBe("extension_ui_request");
|
|
83
88
|
if (result?.kind !== "extension_ui_request") return;
|
|
84
|
-
|
|
85
|
-
expect(result.request.message).toBe("hi");
|
|
89
|
+
const req = asVariant(result.request, "notify");
|
|
86
90
|
});
|
|
87
91
|
|
|
88
92
|
it("setStatus → request.method=setStatus + statusKey/statusText", () => {
|
|
@@ -91,8 +95,8 @@ describe("parseSpawnLine — 10 种 method 真实样本分类(Pi 原生格式
|
|
|
91
95
|
}));
|
|
92
96
|
expect(result?.kind).toBe("extension_ui_request");
|
|
93
97
|
if (result?.kind !== "extension_ui_request") return;
|
|
94
|
-
|
|
95
|
-
expect(
|
|
98
|
+
const req = asVariant(result.request, "setStatus");
|
|
99
|
+
expect(req.statusKey).toBe("k");
|
|
96
100
|
});
|
|
97
101
|
|
|
98
102
|
it("setWidget → request.method=setWidget + widgetKey/widgetLines/widgetPlacement", () => {
|
|
@@ -102,25 +106,24 @@ describe("parseSpawnLine — 10 种 method 真实样本分类(Pi 原生格式
|
|
|
102
106
|
}));
|
|
103
107
|
expect(result?.kind).toBe("extension_ui_request");
|
|
104
108
|
if (result?.kind !== "extension_ui_request") return;
|
|
105
|
-
|
|
106
|
-
expect(
|
|
107
|
-
expect(
|
|
109
|
+
const req = asVariant(result.request, "setWidget");
|
|
110
|
+
expect(req.widgetKey).toBe("w1");
|
|
111
|
+
expect(req.widgetPlacement).toBe("aboveEditor");
|
|
108
112
|
});
|
|
109
113
|
|
|
110
114
|
it("setTitle → request.method=setTitle + title", () => {
|
|
111
115
|
const result = parseSpawnLine(piLine({ method: "setTitle", title: "My Title" }));
|
|
112
116
|
expect(result?.kind).toBe("extension_ui_request");
|
|
113
117
|
if (result?.kind !== "extension_ui_request") return;
|
|
114
|
-
|
|
115
|
-
expect(
|
|
118
|
+
const req = asVariant(result.request, "setTitle");
|
|
119
|
+
expect(req.title).toBe("My Title");
|
|
116
120
|
});
|
|
117
121
|
|
|
118
122
|
it("set_editor_text → request.method=set_editor_text + text", () => {
|
|
119
123
|
const result = parseSpawnLine(piLine({ method: "set_editor_text", text: "body" }));
|
|
120
124
|
expect(result?.kind).toBe("extension_ui_request");
|
|
121
125
|
if (result?.kind !== "extension_ui_request") return;
|
|
122
|
-
|
|
123
|
-
expect(result.request.text).toBe("body");
|
|
126
|
+
const req = asVariant(result.request, "set_editor_text");
|
|
124
127
|
});
|
|
125
128
|
});
|
|
126
129
|
});
|