@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.
Files changed (309) hide show
  1. package/dist/{chunk-4KN4TTG7.js → chunk-43ONBFZX.js} +1 -1
  2. package/dist/{chunk-X7SZ5HLQ.js → chunk-V3VHZ2VX.js} +20 -27
  3. package/dist/execution/engine/engines/zcode/constants.cjs +29 -41
  4. package/dist/execution/engine/engines/zcode/constants.d.cts +65 -53
  5. package/dist/execution/engine/engines/zcode/constants.d.ts +65 -53
  6. package/dist/execution/engine/engines/zcode/constants.js +17 -27
  7. package/dist/execution/engine/engines/zcode/reader.d.cts +2 -1
  8. package/dist/execution/engine/engines/zcode/reader.d.ts +2 -1
  9. package/dist/execution/engine/engines/zcode/reader.js +2 -2
  10. package/dist/index.cjs +17100 -14082
  11. package/dist/index.d.cts +5336 -4033
  12. package/dist/index.d.ts +5336 -4033
  13. package/dist/index.js +17020 -14055
  14. package/dist/{types-DpUO16pj.d.cts → types-C3fE3R9x.d.cts} +280 -112
  15. package/dist/{types-DpUO16pj.d.ts → types-C3fE3R9x.d.ts} +280 -112
  16. package/dist.bundle/index.cjs +34933 -0
  17. package/package.json +3 -3
  18. package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +0 -1
  19. package/src/__tests__/append-system-prompt-assembly.test.ts +8 -7
  20. package/src/__tests__/fr4-get-state-handshake.test.ts +2 -2
  21. package/src/__tests__/m2-append-content-probe.test.ts +0 -1
  22. package/src/__tests__/manifest-store.test.ts +10 -9
  23. package/src/__tests__/record-store-cache.test.ts +0 -2
  24. package/src/__tests__/record-store-index.test.ts +1 -2
  25. package/src/__tests__/review-fix-loop-script.test.ts +4 -2
  26. package/src/__tests__/review-fix-loop-utils.test.ts +10 -10
  27. package/src/__tests__/robustness-low-batch1.test.ts +6 -5
  28. package/src/__tests__/robustness-medium-batch1.test.ts +2 -2
  29. package/src/__tests__/robustness-medium-batch2.test.ts +17 -6
  30. package/src/__tests__/robustness-medium-batch4.test.ts +2 -2
  31. package/src/__tests__/session-runner.test.ts +1 -1
  32. package/src/core/__tests__/host-services.test.ts +2 -2
  33. package/src/core/__tests__/logger.test.ts +1 -1
  34. package/src/core/error-message.ts +9 -0
  35. package/src/core/host-services.ts +21 -5
  36. package/src/core/notify-ports.ts +18 -5
  37. package/src/execution/__tests__/agent-profile.test.ts +4 -4
  38. package/src/execution/__tests__/agent-registry.test.ts +370 -0
  39. package/src/execution/__tests__/agent-result-mapper.test.ts +18 -2
  40. package/src/execution/__tests__/agents-assembly.test.ts +2 -2
  41. package/src/execution/__tests__/alive-store.test.ts +1 -1
  42. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +6 -6
  43. package/src/execution/__tests__/chat-engine-routing.test.ts +68 -33
  44. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +4 -4
  45. package/src/execution/__tests__/cold-resurrect.test.ts +283 -0
  46. package/src/execution/__tests__/collect-budget.test.ts +291 -0
  47. package/src/execution/__tests__/collect-coordinator-service.test.ts +327 -0
  48. package/src/execution/__tests__/collect-coordinator.test.ts +371 -0
  49. package/src/execution/__tests__/collect-mixed-dispatch.test.ts +262 -0
  50. package/src/execution/__tests__/config-collect-sync.test.ts +120 -0
  51. package/src/execution/__tests__/config.test.ts +1 -1
  52. package/src/execution/__tests__/delivery-methods.test.ts +75 -62
  53. package/src/execution/__tests__/descendant-sweep-guards.test.ts +220 -0
  54. package/src/execution/__tests__/descendant-sweep.test.ts +6 -6
  55. package/src/execution/__tests__/dialog-queue.test.ts +2 -2
  56. package/src/execution/__tests__/engine-model-validation.test.ts +741 -0
  57. package/src/execution/__tests__/epipe-fallback.test.ts +29 -24
  58. package/src/execution/__tests__/execute-and-await-worktree.test.ts +21 -91
  59. package/src/execution/__tests__/execute-nesting.test.ts +34 -188
  60. package/src/execution/__tests__/execution-record.test.ts +217 -60
  61. package/src/execution/__tests__/execution-runtime-face.test.ts +5 -5
  62. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +4 -4
  63. package/src/execution/__tests__/finalize-record.test.ts +173 -17
  64. package/src/execution/__tests__/finalized-marker.test.ts +1 -1
  65. package/src/execution/__tests__/gc-timer.test.ts +56 -3
  66. package/src/execution/__tests__/get-record-for-action-restart.test.ts +12 -12
  67. package/src/execution/__tests__/get-state-handshake.test.ts +1 -1
  68. package/src/execution/__tests__/helpers/mock-extension-api.ts +1 -1
  69. package/src/execution/__tests__/helpers/session-runner-mocks.ts +113 -0
  70. package/src/execution/__tests__/helpers/spawn-mock.ts +150 -3
  71. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +137 -0
  72. package/src/execution/__tests__/keep-alive-no-progress.test.ts +50 -148
  73. package/src/execution/__tests__/kill-all-escalation.test.ts +2 -2
  74. package/src/execution/__tests__/lifecycle-predicates.test.ts +1 -2
  75. package/src/execution/__tests__/manifest-parentid.test.ts +4 -4
  76. package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +1 -1
  77. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +1 -1
  78. package/src/execution/__tests__/model-resolver.test.ts +2 -2
  79. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +23 -121
  80. package/src/execution/__tests__/nested-visibility.test.ts +2 -0
  81. package/src/execution/__tests__/notify-batch.test.ts +500 -0
  82. package/src/execution/__tests__/notify-ledger.test.ts +1000 -18
  83. package/src/execution/__tests__/output-collector.test.ts +117 -6
  84. package/src/execution/__tests__/pi-invocation.test.ts +36 -4
  85. package/src/execution/__tests__/record-entry-collect.test.ts +102 -0
  86. package/src/execution/__tests__/record-store.test.ts +10 -9
  87. package/src/execution/__tests__/records-cwd-isolation.test.ts +2 -2
  88. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +29 -132
  89. package/src/execution/__tests__/recursive-visibility-env.test.ts +55 -179
  90. package/src/execution/__tests__/relay-env.test.ts +12 -1
  91. package/src/execution/__tests__/rpc-mode.test.ts +2 -2
  92. package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +387 -0
  93. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +15 -9
  94. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +4 -4
  95. package/src/execution/__tests__/run-spawn-edges.test.ts +17 -15
  96. package/src/execution/__tests__/run-spawn-integration.test.ts +2 -2
  97. package/src/execution/__tests__/run-spawn-resume.test.ts +2 -2
  98. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +2 -2
  99. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +4 -4
  100. package/src/execution/__tests__/service-kill-escalation.test.ts +7 -5
  101. package/src/execution/__tests__/session-context-resolver.test.ts +0 -2
  102. package/src/execution/__tests__/session-file-gc.test.ts +36 -1
  103. package/src/execution/__tests__/session-pending.test.ts +257 -14
  104. package/src/execution/__tests__/session-reconstructor.test.ts +366 -15
  105. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +1 -1
  106. package/src/execution/__tests__/session-runner-close-prune.test.ts +2 -2
  107. package/src/execution/__tests__/session-runner-dispatch.test.ts +489 -0
  108. package/src/execution/__tests__/session-runner-epipe.test.ts +3 -3
  109. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +5 -4
  110. package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +290 -0
  111. package/src/execution/__tests__/session-runner-schema-env.test.ts +7 -5
  112. package/src/execution/__tests__/settled-watchdog.test.ts +282 -47
  113. package/src/execution/__tests__/spawn-args.test.ts +11 -10
  114. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +26 -23
  115. package/src/execution/__tests__/spawn-event-adapter.test.ts +3 -3
  116. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +5 -4
  117. package/src/execution/__tests__/spawned-children.test.ts +2 -1
  118. package/src/execution/__tests__/start-collect-guard.test.ts +255 -0
  119. package/src/execution/__tests__/start-sync-model-guard.test.ts +5 -5
  120. package/src/execution/__tests__/stdin-writer.test.ts +6 -4
  121. package/src/execution/__tests__/stream-sink-retirement.test.ts +6 -6
  122. package/src/execution/__tests__/subagent-actions-core.test.ts +48 -22
  123. package/src/execution/__tests__/subagent-service-message-close.test.ts +29 -26
  124. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +21 -8
  125. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +27 -22
  126. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +30 -29
  127. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +45 -20
  128. package/src/execution/__tests__/subagent-service.test.ts +37 -30
  129. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +88 -12
  130. package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +75 -0
  131. package/src/execution/__tests__/subprocess-agent-runner.test.ts +22 -20
  132. package/src/execution/__tests__/sync-collect-recovery.test.ts +1117 -0
  133. package/src/execution/__tests__/temp-prompt.test.ts +1 -1
  134. package/src/execution/__tests__/timeout-integration.test.ts +6 -5
  135. package/src/execution/__tests__/tombstone-store.test.ts +1 -1
  136. package/src/execution/__tests__/turn-limiter-semantics.test.ts +1 -1
  137. package/src/execution/__tests__/turn-limiter.test.ts +1 -1
  138. package/src/execution/__tests__/ui-request-handler-factory.test.ts +12 -6
  139. package/src/execution/__tests__/ui-request-handler.test.ts +4 -3
  140. package/src/execution/__tests__/ui-request-queue.test.ts +142 -2
  141. package/src/execution/__tests__/worktree-git-ops.test.ts +1 -1
  142. package/src/execution/__tests__/worktree-manager.test.ts +14 -5
  143. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +4 -3
  144. package/src/execution/__tests__/worktree-reconcile-aging.test.ts +2 -2
  145. package/src/execution/__tests__/worktree-reconcile.integration.test.ts +1 -1
  146. package/src/execution/__tests__/worktree-registry.test.ts +1 -1
  147. package/src/execution/agent-registry.ts +74 -30
  148. package/src/execution/agent-result-mapper.ts +3 -0
  149. package/src/execution/cold-resurrect.ts +199 -0
  150. package/src/execution/collect-coordinator.ts +200 -0
  151. package/src/execution/config.ts +77 -5
  152. package/src/execution/engine/__tests__/common/capability-gate.test.ts +91 -0
  153. package/src/execution/engine/__tests__/common/data-dir.test.ts +1 -1
  154. package/src/execution/engine/__tests__/common/event-journal.test.ts +1 -1
  155. package/src/execution/engine/__tests__/common/journal-wiring.test.ts +84 -0
  156. package/src/execution/engine/__tests__/common/kill-chain.test.ts +34 -7
  157. package/src/execution/engine/__tests__/common/persona-router.test.ts +4 -5
  158. package/src/execution/engine/__tests__/common/pool-manager.test.ts +213 -53
  159. package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +149 -0
  160. package/src/execution/engine/__tests__/common/session-view-service.test.ts +499 -0
  161. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +22 -112
  162. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +15 -28
  163. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +1 -1
  164. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +12 -12
  165. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +2 -2
  166. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +21 -82
  167. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +4 -7
  168. package/src/execution/engine/__tests__/engine-discovery.test.ts +15 -3
  169. package/src/execution/engine/__tests__/model-prompt.test.ts +15 -3
  170. package/src/execution/engine/__tests__/registry.test.ts +4 -2
  171. package/src/execution/engine/__tests__/routing.test.ts +115 -6
  172. package/src/execution/engine/common/capability-gate.ts +92 -0
  173. package/src/execution/engine/common/journal-wiring.ts +97 -0
  174. package/src/execution/engine/common/kill-chain.ts +42 -10
  175. package/src/execution/engine/common/nesting-guard.ts +66 -0
  176. package/src/execution/engine/common/persona-router.ts +18 -8
  177. package/src/execution/engine/common/pool-manager.ts +332 -78
  178. package/src/execution/engine/common/session-view-service.ts +506 -0
  179. package/src/execution/engine/common/session-view-types.ts +125 -0
  180. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +174 -28
  181. package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +257 -0
  182. package/src/execution/{argv-mirror.ts → engine/engines/pi/argv-mirror.ts} +43 -21
  183. package/src/execution/{get-state-handshake.ts → engine/engines/pi/get-state-handshake.ts} +1 -1
  184. package/src/execution/{output-collector.ts → engine/engines/pi/output-collector.ts} +108 -19
  185. package/src/execution/engine/engines/pi/pi-engine.ts +434 -38
  186. package/src/execution/{pi-invocation.ts → engine/engines/pi/pi-invocation.ts} +2 -2
  187. package/src/execution/engine/engines/pi/reader.ts +11 -0
  188. package/src/execution/engine/engines/pi/registration.ts +4 -2
  189. package/src/execution/{session-runner.ts → engine/engines/pi/session-runner.ts} +617 -412
  190. package/src/execution/{spawn-event-adapter.ts → engine/engines/pi/spawn-event-adapter.ts} +75 -34
  191. package/src/execution/{stdin-writer.ts → engine/engines/pi/stdin-writer.ts} +10 -9
  192. package/src/execution/{temp-prompt.ts → engine/engines/pi/temp-prompt.ts} +2 -2
  193. package/src/execution/{turn-limiter.ts → engine/engines/pi/turn-limiter.ts} +1 -1
  194. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +54 -27
  195. package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +321 -0
  196. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +13 -12
  197. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +7 -210
  198. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +6 -110
  199. package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +247 -0
  200. package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +451 -0
  201. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +20 -8
  202. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +77 -110
  203. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +26 -320
  204. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +210 -0
  205. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +353 -0
  206. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +282 -0
  207. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +372 -0
  208. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +48 -148
  209. package/src/execution/engine/engines/zcode/appserver-launcher.ts +167 -0
  210. package/src/execution/engine/engines/zcode/connection.ts +24 -11
  211. package/src/execution/engine/engines/zcode/constants.ts +75 -71
  212. package/src/execution/engine/engines/zcode/golden-sample.ts +8 -46
  213. package/src/execution/engine/engines/zcode/parser.ts +12 -346
  214. package/src/execution/engine/engines/zcode/preparer.ts +12 -143
  215. package/src/execution/engine/engines/zcode/session-channel.ts +351 -39
  216. package/src/execution/engine/engines/zcode/zcode-engine.ts +665 -760
  217. package/src/execution/engine/host-task-spec.ts +32 -36
  218. package/src/execution/engine/model-validation.ts +177 -0
  219. package/src/execution/engine/port.ts +24 -6
  220. package/src/execution/engine/routing.ts +75 -1
  221. package/src/execution/engine/types.ts +30 -103
  222. package/src/execution/execution-record.ts +229 -144
  223. package/src/execution/finalize-record.ts +141 -99
  224. package/src/execution/idle-gc.ts +28 -0
  225. package/src/execution/lifecycle-predicates.ts +1 -1
  226. package/src/execution/notifier.ts +234 -25
  227. package/src/execution/notify-host.ts +232 -0
  228. package/src/execution/record-entry.ts +16 -0
  229. package/src/execution/record-store.ts +280 -103
  230. package/src/execution/round-settlement.ts +93 -0
  231. package/src/execution/session-file-gc.ts +96 -62
  232. package/src/execution/session-reconstructor.ts +256 -143
  233. package/src/execution/sessions-index.ts +117 -47
  234. package/src/execution/settled-watchdog.ts +264 -52
  235. package/src/execution/subagent-actions-core.ts +84 -28
  236. package/src/execution/subagent-service.ts +1225 -964
  237. package/src/execution/subprocess-agent-runner.ts +144 -97
  238. package/src/execution/sync-rebuild.ts +87 -0
  239. package/src/execution/types.ts +72 -0
  240. package/src/execution/ui-request-handler-factory.ts +102 -74
  241. package/src/execution/ui-request-queue.ts +50 -19
  242. package/src/index.ts +442 -316
  243. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +1 -1
  244. package/src/orchestration/__tests__/agent-call-stream.test.ts +2 -2
  245. package/src/orchestration/__tests__/args-validator.test.ts +14 -0
  246. package/src/orchestration/__tests__/config-loader.test.ts +1 -1
  247. package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +1 -1
  248. package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +11 -9
  249. package/src/orchestration/__tests__/execute-agent-call.test.ts +151 -11
  250. package/src/orchestration/__tests__/file-run-store-prune.test.ts +1 -1
  251. package/src/orchestration/__tests__/file-run-store-throttle.test.ts +1 -1
  252. package/src/orchestration/__tests__/file-run-store.test.ts +7 -6
  253. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +2 -2
  254. package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +1 -1
  255. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +3 -3
  256. package/src/orchestration/__tests__/lifecycle.test.ts +7 -5
  257. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +1 -1
  258. package/src/orchestration/__tests__/review-fix-loop-scriptpath-failfast.test.ts +1 -1
  259. package/src/orchestration/__tests__/run-snapshot.test.ts +8 -5
  260. package/src/orchestration/__tests__/script-generate.test.ts +5 -5
  261. package/src/orchestration/__tests__/script-lint.test.ts +17 -0
  262. package/src/orchestration/__tests__/skill-discovery.test.ts +2 -2
  263. package/src/orchestration/__tests__/test-mocks.ts +2 -0
  264. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +7 -6
  265. package/src/orchestration/__tests__/worker-message-pump-finalize-run.test.ts +229 -0
  266. package/src/orchestration/__tests__/{error-recovery-handlers.test.ts → worker-message-pump-handlers.test.ts} +27 -19
  267. package/src/orchestration/__tests__/{error-recovery-postmessage-defense.test.ts → worker-message-pump-postmessage-defense.test.ts} +8 -8
  268. package/src/orchestration/__tests__/{error-recovery-serialize-failed-result.test.ts → worker-message-pump-serialize-failed-result.test.ts} +2 -2
  269. package/src/orchestration/__tests__/{error-recovery-workflow-call.test.ts → worker-message-pump-workflow-call.test.ts} +1 -1
  270. package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +1 -1
  271. package/src/orchestration/__tests__/worker-script-builder.test.ts +1 -1
  272. package/src/orchestration/__tests__/workflow-files.test.ts +10 -10
  273. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +1 -1
  274. package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +1 -1
  275. package/src/orchestration/agent-opts-resolver.ts +1 -1
  276. package/src/orchestration/args-validator.ts +53 -31
  277. package/src/orchestration/execute-agent-call.ts +19 -73
  278. package/src/orchestration/launcher.ts +128 -81
  279. package/src/orchestration/lifecycle.ts +43 -45
  280. package/src/orchestration/models/ports.ts +4 -4
  281. package/src/orchestration/models/run-runtime.ts +2 -2
  282. package/src/orchestration/models/trace.ts +1 -1
  283. package/src/orchestration/models/types.ts +111 -22
  284. package/src/orchestration/models/workflow-run.ts +1 -1
  285. package/src/orchestration/script-lint.ts +91 -58
  286. package/src/orchestration/{error-recovery.ts → worker-message-pump.ts} +157 -150
  287. package/src/shared/__tests__/atomic-write.test.ts +1 -1
  288. package/src/shared/__tests__/meta-parser.test.ts +151 -0
  289. package/src/shared/__tests__/resource-discovery-host-roots.test.ts +5 -5
  290. package/src/shared/__tests__/resource-discovery.test.ts +258 -215
  291. package/src/shared/atomic-write.ts +12 -9
  292. package/src/shared/meta-parser.ts +158 -84
  293. package/src/shared/model-ref.ts +15 -2
  294. package/src/shared/resource-discovery.ts +9 -183
  295. package/src/shared/schema-jsonify.ts +1 -1
  296. package/workflows/review-fix-loop-utils.cjs +151 -89
  297. package/workflows/review-fix-loop-utils.d.cts +287 -0
  298. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
  299. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  300. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  301. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  302. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  303. package/src/execution/engine/engines/zcode/__tests__/preparer-appserver.test.ts +0 -387
  304. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -710
  305. package/src/execution/engine/engines/zcode/appserver-home.ts +0 -442
  306. package/src/execution/engine/engines/zcode/appserver-probe.ts +0 -141
  307. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  308. package/src/execution/execute-options-mapper.ts +0 -115
  309. 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-③ / LC-1] chatMode settled 等待固定硬上限。
3
+ // [T2-③ / D9 两段式] chatMode settled 等待守护:中段无进展检测 + 收尾段固定上界。
4
4
  //
5
- // 设计:docs/design/subagent-core-unbounded-wait-audit.md §7.2 T2-③;10min 默认值
6
- // P-T2c 探针定案(probe/p-t2c-report.md,正常形态 ≈0ms、余量 4 个数量级)。
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):唯一常量值 + arm 到期触发 + disarm 清除 +
10
- // 重复 arm 以新窗口覆盖旧计时(旧窗不清则提前触发,断言可证伪)。
11
- // 2. 首轮挂载集成(runSpawn chatMode):prompt 发出后挂载;advance
12
- // SETTLED_WATCHDOG_TIMEOUT_MS 触发 kill close runSpawn 以错误返回且错误含
13
- // 恢复指引(S-B 验收判据)。
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 干扰 10min 窗口断言)。 */
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: "sync",
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
- describe("[T2-③] settled-watchdog 原语", () => {
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("唯一常量值 = 10min(P-T2c 定案,双挂载点共享此导出)", () => {
150
- expect(SETTLED_WATCHDOG_TIMEOUT_MS).toBe(10 * 60 * 1000);
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("arm advance 10min 触发 onTimeout(到期自删记账);disarm 后不再触发", async () => {
176
+ it("验收①:agent_end 前静默超时按第一段语义触发(mid-round 回调 + 段信息;收尾回调不触发)", async () => {
154
177
  vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout"] });
155
- const onTimeout = vi.fn();
156
- armSettledWatchdog("sa-1", onTimeout);
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(SETTLED_WATCHDOG_TIMEOUT_MS - 1);
160
- expect(onTimeout).not.toHaveBeenCalled();
184
+ await vi.advanceTimersByTimeAsync(SETTLED_MID_ROUND_NO_PROGRESS_MS - 1);
185
+ expect(onMidTimeout).not.toHaveBeenCalled();
161
186
  await vi.advanceTimersByTimeAsync(1);
162
- expect(onTimeout).toHaveBeenCalledTimes(1);
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(onTimeout).toHaveBeenCalledTimes(1);
197
+ expect(onMidTimeout).toHaveBeenCalledTimes(1);
170
198
  });
171
199
 
172
- it("重复 arm 以新窗口覆盖旧计时——旧窗口不清会提前触发(断言可证伪)", async () => {
200
+ it("验收②:agent_end 交棒后切换到收尾段(phase=settled;收尾段从交棒点独立计时)", async () => {
173
201
  vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout"] });
174
- const onTimeout = vi.fn();
175
- armSettledWatchdog("sa-2", onTimeout);
176
- await vi.advanceTimersByTimeAsync(SETTLED_WATCHDOG_TIMEOUT_MS / 2);
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
- // 若旧窗未清:旧 timer 会在重挂后 5min 触发(此处 advance 内)→ 断言失败
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(onTimeout).not.toHaveBeenCalled();
212
+ expect(onSettleTimeout).not.toHaveBeenCalled();
183
213
  await vi.advanceTimersByTimeAsync(1);
184
- expect(onTimeout).toHaveBeenCalledTimes(1);
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
- describe("[T2-③] runSpawn 首轮挂载(chatMode)", () => {
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("settled 永不到达:10min 硬上限到期 kill 层主 → runSpawn 以错误返回且含恢复指引", async () => {
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
- // 挂载点消费同一导出常量:advance 常量值量级可触发即同源断言。
213
- // [余量] fake timers 对 Date mock 有 ms 级舍入(实测 advance(SET-1) 会触发),
214
- // 断言边界留 1s 余量——对 10min 上限语义无损,对计时器实现细节稳健。
403
+ // prompt 发出后挂中段:advance 到中段窗长触发(原 10min 全程窗已不存在——
404
+ // advance 10min 静默不再误杀,是对 P0-4 的直接回归断言)
215
405
  emitStdoutLine(child, sessionHeaderLine());
216
- await vi.advanceTimersByTimeAsync(SETTLED_WATCHDOG_TIMEOUT_MS - 1_000);
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("settled 到达:硬上限即清(含 resolveRun 同点)——advance 10min 不误杀已正常轮转的层主", async () => {
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 同点清除硬上限);idle timer 被拉长到 1h 不干扰本窗口
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 已清:子进程未被本上界 kill(若回归未清,此处 killed=true)
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 上界", async () => {
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:即使静默超过 10min 也不被 settled 上界 kill(spawn watchdog 同为
498
+ // 非 chatMode:即使静默超过中段窗长也不被 settled 守护 kill(spawn watchdog 同为
274
499
  // 未挂载——裸缺省 + env 清),agent_end 之前的执行期不受 T2-③ 触及
275
- await vi.advanceTimersByTimeAsync(SETTLED_WATCHDOG_TIMEOUT_MS + 60_000);
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
- const baseParams = {
15
+ // 类型直接锚定 buildSpawnArgs 入参(thinkingLevel 是 ThinkingLevel 枚举,非裸 string)
16
+ const baseParams: Parameters<typeof buildSpawnArgs>[0] = {
16
17
  modelRef: { provider: "openai", id: "gpt-4o" },
17
- thinkingLevel: undefined as string | undefined,
18
- agentTools: undefined as string[] | undefined,
19
- appendSystemPromptPath: undefined as string | undefined,
18
+ thinkingLevel: undefined,
19
+ agentTools: undefined,
20
+ appendSystemPromptPath: undefined,
20
21
  sessionDir: "/sessions/dir",
21
- forkSource: undefined as string | undefined,
22
- skillPaths: undefined as string[] | 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
- // 测试对象:extensions/universal/subagent-workflow/src/execution/spawn-event-adapter.ts(改造)
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
- expect(result.request.method).toBe("select");
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
- expect(result.request.method).toBe("confirm");
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
- expect(result.request.method).toBe("input");
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
- expect(result.request.method).toBe("editor");
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
- expect(result.request.method).toBe("notify");
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
- expect(result.request.method).toBe("setStatus");
95
- expect(result.request.statusKey).toBe("k");
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
- expect(result.request.method).toBe("setWidget");
106
- expect(result.request.widgetKey).toBe("w1");
107
- expect(result.request.widgetPlacement).toBe("aboveEditor");
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
- expect(result.request.method).toBe("setTitle");
115
- expect(result.request.title).toBe("My Title");
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
- expect(result.request.method).toBe("set_editor_text");
123
- expect(result.request.text).toBe("body");
126
+ const req = asVariant(result.request, "set_editor_text");
124
127
  });
125
128
  });
126
129
  });