@zhushanwen/subagent-core 0.2.0 → 0.5.1

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 (347) hide show
  1. package/README.md +45 -2
  2. package/agents/analyst.md +60 -0
  3. package/agents/coder.md +69 -0
  4. package/agents/debugger.md +66 -0
  5. package/agents/doc-reviewer.md +49 -0
  6. package/agents/explorer.md +63 -0
  7. package/agents/general-purpose.md +32 -0
  8. package/agents/orchestrator.md +61 -0
  9. package/agents/planner.md +53 -0
  10. package/agents/researcher.md +67 -0
  11. package/agents/reviewer.md +73 -0
  12. package/dist/chunk-43ONBFZX.js +240 -0
  13. package/dist/chunk-APZY4IME.js +27 -0
  14. package/dist/chunk-V3VHZ2VX.js +59 -0
  15. package/dist/execution/engine/engines/zcode/constants.cjs +62 -9
  16. package/dist/execution/engine/engines/zcode/constants.d.cts +116 -12
  17. package/dist/execution/engine/engines/zcode/constants.d.ts +116 -12
  18. package/dist/execution/engine/engines/zcode/constants.js +39 -7
  19. package/dist/execution/engine/engines/zcode/reader.d.cts +2 -1
  20. package/dist/execution/engine/engines/zcode/reader.d.ts +2 -1
  21. package/dist/execution/engine/engines/zcode/reader.js +4 -234
  22. package/dist/execution/engine/paths.js +7 -19
  23. package/dist/index.cjs +18234 -1277
  24. package/dist/index.d.cts +5646 -673
  25. package/dist/index.d.ts +5646 -673
  26. package/dist/index.js +17828 -1277
  27. package/dist/{types-BxyAidGf.d.cts → types-Dv4QhSJ_.d.cts} +678 -112
  28. package/dist/{types-BxyAidGf.d.ts → types-Dv4QhSJ_.d.ts} +678 -112
  29. package/package.json +7 -4
  30. package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +0 -1
  31. package/src/__tests__/append-system-prompt-assembly.test.ts +8 -7
  32. package/src/__tests__/fr4-get-state-handshake.test.ts +2 -2
  33. package/src/__tests__/m2-append-content-probe.test.ts +0 -1
  34. package/src/__tests__/manifest-store.test.ts +10 -9
  35. package/src/__tests__/record-store-cache.test.ts +0 -2
  36. package/src/__tests__/record-store-index.test.ts +1 -2
  37. package/src/__tests__/review-fix-loop-script.test.ts +93 -3
  38. package/src/__tests__/review-fix-loop-utils.test.ts +31 -10
  39. package/src/__tests__/robustness-low-batch1.test.ts +6 -5
  40. package/src/__tests__/robustness-medium-batch1.test.ts +2 -2
  41. package/src/__tests__/robustness-medium-batch2.test.ts +17 -6
  42. package/src/__tests__/robustness-medium-batch4.test.ts +2 -2
  43. package/src/__tests__/session-runner.test.ts +1 -1
  44. package/src/__tests__/smoke.test.ts +9 -2
  45. package/src/core/__tests__/host-services.test.ts +2 -2
  46. package/src/core/__tests__/logger.test.ts +1 -1
  47. package/src/core/error-message.ts +9 -0
  48. package/src/core/host-services.ts +21 -5
  49. package/src/core/notify-ports.ts +18 -5
  50. package/src/execution/__tests__/agent-profile.test.ts +232 -0
  51. package/src/execution/__tests__/agent-registry.test.ts +370 -0
  52. package/src/execution/__tests__/agent-result-mapper.test.ts +18 -2
  53. package/src/execution/__tests__/agents-assembly.test.ts +219 -0
  54. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +6 -6
  55. package/src/execution/__tests__/chat-engine-routing.test.ts +136 -30
  56. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +5 -4
  57. package/src/execution/__tests__/cold-resurrect.test.ts +283 -0
  58. package/src/execution/__tests__/create-concurrency-pool.test.ts +194 -0
  59. package/src/execution/__tests__/delivery-methods.test.ts +77 -62
  60. package/src/execution/__tests__/descendant-sweep-guards.test.ts +220 -0
  61. package/src/execution/__tests__/descendant-sweep.test.ts +269 -0
  62. package/src/execution/__tests__/dialog-queue.test.ts +199 -1
  63. package/src/execution/__tests__/engine-model-validation.test.ts +741 -0
  64. package/src/execution/__tests__/epipe-fallback.test.ts +31 -24
  65. package/src/execution/__tests__/execute-and-await-worktree.test.ts +21 -91
  66. package/src/execution/__tests__/execute-nesting.test.ts +34 -188
  67. package/src/execution/__tests__/execution-record.test.ts +217 -60
  68. package/src/execution/__tests__/execution-runtime-face.test.ts +272 -0
  69. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +3 -3
  70. package/src/execution/__tests__/finalize-record.test.ts +278 -16
  71. package/src/execution/__tests__/gc-timer.test.ts +57 -2
  72. package/src/execution/__tests__/get-record-for-action-restart.test.ts +14 -12
  73. package/src/execution/__tests__/get-state-handshake.test.ts +127 -0
  74. package/src/execution/__tests__/helpers/mock-extension-api.ts +1 -1
  75. package/src/execution/__tests__/helpers/session-runner-mocks.ts +113 -0
  76. package/src/execution/__tests__/helpers/spawn-mock.ts +150 -3
  77. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +137 -0
  78. package/src/execution/__tests__/keep-alive-no-progress.test.ts +280 -0
  79. package/src/execution/__tests__/kill-all-escalation.test.ts +196 -0
  80. package/src/execution/__tests__/lifecycle-manager-idle-timer-identity.test.ts +117 -0
  81. package/src/execution/__tests__/lifecycle-manager.test.ts +39 -0
  82. package/src/execution/__tests__/lifecycle-predicates.test.ts +1 -2
  83. package/src/execution/__tests__/manifest-parentid.test.ts +4 -4
  84. package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +99 -0
  85. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +113 -0
  86. package/src/execution/__tests__/model-resolver.test.ts +2 -2
  87. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +23 -121
  88. package/src/execution/__tests__/nested-visibility.test.ts +2 -0
  89. package/src/execution/__tests__/notify-ledger.test.ts +1249 -0
  90. package/src/execution/__tests__/output-collector.test.ts +117 -6
  91. package/src/execution/__tests__/pi-invocation.test.ts +1 -1
  92. package/src/execution/__tests__/record-store-orphan-revive.test.ts +159 -0
  93. package/src/execution/__tests__/record-store.test.ts +89 -5
  94. package/src/execution/__tests__/records-cwd-isolation.test.ts +2 -2
  95. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +29 -132
  96. package/src/execution/__tests__/recursive-visibility-env.test.ts +55 -179
  97. package/src/execution/__tests__/rpc-mode.test.ts +2 -2
  98. package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +387 -0
  99. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +19 -9
  100. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +5 -4
  101. package/src/execution/__tests__/run-spawn-edges.test.ts +418 -26
  102. package/src/execution/__tests__/run-spawn-integration.test.ts +3 -3
  103. package/src/execution/__tests__/run-spawn-resume.test.ts +2 -2
  104. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +2 -2
  105. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +16 -8
  106. package/src/execution/__tests__/service-kill-escalation.test.ts +91 -0
  107. package/src/execution/__tests__/session-context-resolver.test.ts +0 -2
  108. package/src/execution/__tests__/session-file-gc.test.ts +35 -0
  109. package/src/execution/__tests__/session-pending.test.ts +471 -0
  110. package/src/execution/__tests__/session-reconstructor.test.ts +366 -15
  111. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +110 -0
  112. package/src/execution/__tests__/session-runner-close-prune.test.ts +181 -0
  113. package/src/execution/__tests__/session-runner-dispatch.test.ts +489 -0
  114. package/src/execution/__tests__/session-runner-epipe.test.ts +4 -3
  115. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +227 -0
  116. package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +290 -0
  117. package/src/execution/__tests__/session-runner-schema-env.test.ts +7 -5
  118. package/src/execution/__tests__/settled-watchdog.test.ts +524 -0
  119. package/src/execution/__tests__/spawn-args.test.ts +11 -10
  120. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +26 -23
  121. package/src/execution/__tests__/spawn-event-adapter.test.ts +1 -1
  122. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +5 -4
  123. package/src/execution/__tests__/spawned-children.test.ts +155 -11
  124. package/src/execution/__tests__/start-sync-model-guard.test.ts +4 -4
  125. package/src/execution/__tests__/stdin-writer.test.ts +6 -4
  126. package/src/execution/__tests__/stream-sink-retirement.test.ts +6 -6
  127. package/src/execution/__tests__/subagent-actions-core.test.ts +1017 -0
  128. package/src/execution/__tests__/subagent-service-message-close.test.ts +99 -35
  129. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +174 -0
  130. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +271 -0
  131. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +33 -29
  132. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +341 -0
  133. package/src/execution/__tests__/subagent-service.test.ts +37 -30
  134. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +88 -12
  135. package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +75 -0
  136. package/src/execution/__tests__/subprocess-agent-runner.test.ts +22 -20
  137. package/src/execution/__tests__/temp-prompt.test.ts +1 -1
  138. package/src/execution/__tests__/timeout-integration.test.ts +13 -12
  139. package/src/execution/__tests__/turn-limiter-semantics.test.ts +1 -1
  140. package/src/execution/__tests__/turn-limiter.test.ts +1 -1
  141. package/src/execution/__tests__/ui-request-handler-factory.test.ts +107 -7
  142. package/src/execution/__tests__/ui-request-handler.test.ts +4 -3
  143. package/src/execution/__tests__/ui-request-queue.test.ts +167 -2
  144. package/src/execution/__tests__/worktree-git-ops.test.ts +571 -0
  145. package/src/execution/__tests__/worktree-manager.test.ts +14 -5
  146. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +4 -3
  147. package/src/execution/__tests__/worktree-reconcile-aging.test.ts +204 -0
  148. package/src/execution/agent-registry.ts +298 -30
  149. package/src/execution/agent-result-mapper.ts +3 -0
  150. package/src/execution/agents-assembly.ts +88 -0
  151. package/src/execution/cold-resurrect.ts +199 -0
  152. package/src/execution/concurrency-pool.ts +71 -9
  153. package/src/execution/dialog-queue.ts +101 -3
  154. package/src/execution/engine/__tests__/common/capability-gate.test.ts +91 -0
  155. package/src/execution/engine/__tests__/common/data-dir.test.ts +1 -1
  156. package/src/execution/engine/__tests__/common/journal-wiring.test.ts +84 -0
  157. package/src/execution/engine/__tests__/common/kill-chain.test.ts +34 -7
  158. package/src/execution/engine/__tests__/common/persona-router.test.ts +4 -5
  159. package/src/execution/engine/__tests__/common/pool-manager.test.ts +211 -51
  160. package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +149 -0
  161. package/src/execution/engine/__tests__/common/session-view-service.test.ts +499 -0
  162. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +56 -99
  163. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +88 -15
  164. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +33 -7
  165. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +1 -1
  166. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +75 -54
  167. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +128 -0
  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 +94 -3
  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/engine-discovery.ts +12 -16
  181. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +174 -28
  182. package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +257 -0
  183. package/src/execution/{argv-mirror.ts → engine/engines/pi/argv-mirror.ts} +43 -21
  184. package/src/execution/{get-state-handshake.ts → engine/engines/pi/get-state-handshake.ts} +86 -11
  185. package/src/execution/{output-collector.ts → engine/engines/pi/output-collector.ts} +108 -19
  186. package/src/execution/engine/engines/pi/pi-engine.ts +434 -38
  187. package/src/execution/{pi-invocation.ts → engine/engines/pi/pi-invocation.ts} +2 -2
  188. package/src/execution/engine/engines/pi/reader.ts +11 -0
  189. package/src/execution/engine/engines/pi/registration.ts +4 -2
  190. package/src/execution/engine/engines/pi/session-runner.ts +2840 -0
  191. package/src/execution/{spawn-event-adapter.ts → engine/engines/pi/spawn-event-adapter.ts} +75 -34
  192. package/src/execution/{stdin-writer.ts → engine/engines/pi/stdin-writer.ts} +10 -9
  193. package/src/execution/{temp-prompt.ts → engine/engines/pi/temp-prompt.ts} +2 -2
  194. package/src/execution/{turn-limiter.ts → engine/engines/pi/turn-limiter.ts} +1 -1
  195. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +301 -0
  196. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +36 -0
  197. package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +321 -0
  198. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +473 -0
  199. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +7 -210
  200. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +6 -110
  201. package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +247 -0
  202. package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +451 -0
  203. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +792 -0
  204. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +782 -0
  205. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +168 -0
  206. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +210 -0
  207. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +353 -0
  208. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +282 -0
  209. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +372 -0
  210. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +65 -47
  211. package/src/execution/engine/engines/zcode/appserver-launcher.ts +167 -0
  212. package/src/execution/engine/engines/zcode/connection.ts +598 -0
  213. package/src/execution/engine/engines/zcode/constants.ts +147 -14
  214. package/src/execution/engine/engines/zcode/golden-sample.ts +42 -34
  215. package/src/execution/engine/engines/zcode/parser.ts +12 -346
  216. package/src/execution/engine/engines/zcode/preparer.ts +22 -150
  217. package/src/execution/engine/engines/zcode/session-channel.ts +968 -0
  218. package/src/execution/engine/engines/zcode/zcode-engine.ts +1023 -250
  219. package/src/execution/engine/host-task-spec.ts +32 -36
  220. package/src/execution/engine/model-validation.ts +177 -0
  221. package/src/execution/engine/port.ts +65 -7
  222. package/src/execution/engine/registry.ts +53 -0
  223. package/src/execution/engine/routing.ts +75 -1
  224. package/src/execution/engine/types.ts +30 -103
  225. package/src/execution/execution-record.ts +222 -144
  226. package/src/execution/finalize-record.ts +181 -85
  227. package/src/execution/idle-gc.ts +28 -0
  228. package/src/execution/lifecycle-manager.ts +27 -3
  229. package/src/execution/lifecycle-predicates.ts +1 -1
  230. package/src/execution/manifest-store.ts +53 -62
  231. package/src/execution/notifier.ts +10 -10
  232. package/src/execution/notify-host.ts +212 -0
  233. package/src/execution/notify-ledger.ts +117 -16
  234. package/src/execution/record-entry.ts +8 -2
  235. package/src/execution/record-store.ts +148 -88
  236. package/src/execution/round-settlement.ts +93 -0
  237. package/src/execution/session-file-gc.ts +96 -62
  238. package/src/execution/session-pending.ts +213 -62
  239. package/src/execution/session-reconstructor.ts +256 -143
  240. package/src/execution/sessions-index.ts +127 -102
  241. package/src/execution/settled-watchdog.ts +311 -0
  242. package/src/execution/subagent-actions-core.ts +686 -0
  243. package/src/execution/subagent-service.ts +1106 -907
  244. package/src/execution/subprocess-agent-runner.ts +144 -97
  245. package/src/execution/types.ts +9 -0
  246. package/src/execution/ui-request-handler-factory.ts +126 -75
  247. package/src/execution/ui-request-queue.ts +50 -19
  248. package/src/execution/worktree-git-ops.ts +397 -0
  249. package/src/execution/worktree-manager.ts +72 -10
  250. package/src/execution/worktree-registry.ts +6 -12
  251. package/src/index.ts +542 -9
  252. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +42 -3
  253. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +3 -5
  254. package/src/orchestration/__tests__/agent-call-stream.test.ts +2 -2
  255. package/src/orchestration/__tests__/args-meta.test.ts +358 -0
  256. package/src/orchestration/__tests__/args-validator.test.ts +14 -0
  257. package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +312 -0
  258. package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +384 -0
  259. package/src/orchestration/__tests__/execute-agent-call.test.ts +151 -11
  260. package/src/orchestration/__tests__/file-run-store-prune.test.ts +168 -0
  261. package/src/orchestration/__tests__/file-run-store-throttle.test.ts +168 -0
  262. package/src/orchestration/__tests__/file-run-store.test.ts +390 -0
  263. package/src/orchestration/__tests__/helpers/flush-microtasks.ts +13 -0
  264. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +34 -0
  265. package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +357 -0
  266. package/src/orchestration/__tests__/lifecycle-recover-crashed.test.ts +289 -0
  267. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +3 -3
  268. package/src/orchestration/__tests__/lifecycle.test.ts +84 -5
  269. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +1 -1
  270. package/src/orchestration/__tests__/run-snapshot.test.ts +356 -0
  271. package/src/orchestration/__tests__/script-generate.test.ts +314 -0
  272. package/src/orchestration/__tests__/script-lint.test.ts +17 -0
  273. package/src/orchestration/__tests__/test-mocks.ts +2 -0
  274. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +7 -6
  275. package/src/orchestration/__tests__/worker-message-pump-finalize-run.test.ts +229 -0
  276. package/src/orchestration/__tests__/{error-recovery-handlers.test.ts → worker-message-pump-handlers.test.ts} +28 -27
  277. package/src/orchestration/__tests__/{error-recovery-postmessage-defense.test.ts → worker-message-pump-postmessage-defense.test.ts} +8 -8
  278. package/src/orchestration/__tests__/{error-recovery-serialize-failed-result.test.ts → worker-message-pump-serialize-failed-result.test.ts} +2 -2
  279. package/src/orchestration/__tests__/{error-recovery-workflow-call.test.ts → worker-message-pump-workflow-call.test.ts} +1 -1
  280. package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +275 -0
  281. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +78 -1
  282. package/src/orchestration/__tests__/worker-script-builder.test.ts +1 -1
  283. package/src/orchestration/__tests__/workflow-files.test.ts +186 -0
  284. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +1 -1
  285. package/src/orchestration/__tests__/workflow-run-summary.test.ts +119 -0
  286. package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +124 -0
  287. package/src/orchestration/agent-opts-resolver.ts +8 -8
  288. package/src/orchestration/args-meta.ts +198 -0
  289. package/src/orchestration/args-validator.ts +53 -31
  290. package/src/orchestration/execute-agent-call.ts +25 -79
  291. package/src/orchestration/file-run-store.ts +327 -0
  292. package/src/orchestration/launcher.ts +160 -104
  293. package/src/orchestration/lifecycle.ts +371 -97
  294. package/src/orchestration/models/agent-call.ts +7 -7
  295. package/src/orchestration/models/budget.ts +5 -5
  296. package/src/orchestration/models/ports.ts +4 -4
  297. package/src/orchestration/models/run-runtime.ts +15 -15
  298. package/src/orchestration/models/trace.ts +9 -9
  299. package/src/orchestration/models/types.ts +111 -22
  300. package/src/orchestration/models/workflow-run.ts +28 -28
  301. package/src/orchestration/models/workflow-script.ts +4 -4
  302. package/src/orchestration/run-snapshot.ts +266 -0
  303. package/src/orchestration/script-generate.ts +154 -0
  304. package/src/orchestration/script-lint.ts +123 -90
  305. package/src/orchestration/worker-handle.ts +10 -10
  306. package/src/orchestration/worker-host.ts +10 -10
  307. package/src/orchestration/{error-recovery.ts → worker-message-pump.ts} +498 -221
  308. package/src/orchestration/worker-script-builder.ts +401 -346
  309. package/src/orchestration/workflow-files.ts +37 -11
  310. package/src/orchestration/workflow-run-summary.ts +69 -0
  311. package/src/orchestration/workflow-script-registry-impl.ts +31 -9
  312. package/src/shared/__tests__/agent-ref.test.ts +209 -2
  313. package/src/shared/__tests__/atomic-write.test.ts +267 -0
  314. package/src/shared/__tests__/bounded-serialize.test.ts +236 -0
  315. package/src/shared/__tests__/injection-render.test.ts +518 -0
  316. package/src/shared/__tests__/meta-parser.test.ts +151 -0
  317. package/src/shared/__tests__/resource-discovery-host-roots.test.ts +474 -0
  318. package/src/shared/__tests__/resource-discovery.test.ts +261 -217
  319. package/src/shared/agent-ref.ts +143 -2
  320. package/src/shared/atomic-write.ts +323 -0
  321. package/src/shared/bounded-serialize.ts +154 -0
  322. package/src/shared/injection-render.ts +279 -0
  323. package/src/shared/meta-parser.ts +182 -68
  324. package/src/shared/model-ref.ts +15 -2
  325. package/src/shared/resource-discovery.ts +97 -204
  326. package/src/shared/resource-meta.ts +14 -0
  327. package/src/shared/schema-jsonify.ts +1 -1
  328. package/src/shared/xml-injection.ts +9 -9
  329. package/workflows/README.md +9 -9
  330. package/workflows/chain.js +4 -2
  331. package/workflows/map-reduce.js +5 -3
  332. package/workflows/parallel.js +5 -3
  333. package/workflows/review-fix-loop-utils.cjs +165 -92
  334. package/workflows/review-fix-loop-utils.d.cts +287 -0
  335. package/workflows/review-fix-loop.js +18 -7
  336. package/workflows/scatter-gather.js +4 -2
  337. package/dist/chunk-3VOERJPJ.js +0 -22
  338. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
  339. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  340. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  341. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  342. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  343. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -580
  344. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  345. package/src/execution/execute-options-mapper.ts +0 -115
  346. package/src/execution/session-runner.ts +0 -1789
  347. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -288
@@ -0,0 +1,524 @@
1
+ // src/execution/__tests__/settled-watchdog.test.ts
2
+ //
3
+ // [T2-③ / D9 两段式] chatMode settled 等待守护:中段无进展检测 + 收尾段固定上界。
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 先例。
9
+ //
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
+ // 不误杀。
19
+ // 3. 清除点:settled 到达(含 resolveRun 同点)/ close 任一发生即清——挂载点消费
20
+ // 同一导出常量(advance 常量值可触发 = 同源断言)。
21
+ //
22
+ // [时序约束] runSpawn 内部的前置 await 均为微任务级(mock),集成用例在调用 runSpawn
23
+ // **之前**启用 fake timers,再用 advanceTimersByTimeAsync(1) 循环 flush 到 spawn 被调
24
+ // ——settled watchdog 挂载于 spawn 时,先 waitForSpawn(真实 setTimeout 轮询)后
25
+ // useFakeTimers 的话 timer 已挂在真实时钟上,fake 时钟 advance 推不到(MF-3 先例
26
+ // 注释的同一原理)。
27
+
28
+ import { spawn } from "node:child_process";
29
+ import * as fs from "node:fs";
30
+
31
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
32
+
33
+ vi.mock("node:child_process", async () => {
34
+ const { FakeChild } = await import("./helpers/spawn-mock.ts");
35
+ return {
36
+ spawn: vi.fn(() => new FakeChild()),
37
+ execFile: vi.fn(
38
+ (
39
+ _cmd: string,
40
+ _args: readonly string[],
41
+ _opts: unknown,
42
+ cb: (err: Error | null, stdout?: string, stderr?: string) => void,
43
+ ) => cb(new Error("execFile not configured in this test")),
44
+ ),
45
+ };
46
+ });
47
+
48
+ vi.mock("node:fs", async () => {
49
+ const actual = await import("node:fs");
50
+ return {
51
+ default: {
52
+ ...actual,
53
+ mkdirSync: vi.fn(),
54
+ existsSync: vi.fn(() => false),
55
+ appendFileSync: vi.fn(),
56
+ writeFileSync: vi.fn(),
57
+ readdirSync: vi.fn(() => []),
58
+ },
59
+ mkdirSync: vi.fn(),
60
+ existsSync: vi.fn(() => false),
61
+ appendFileSync: vi.fn(),
62
+ writeFileSync: vi.fn(),
63
+ readdirSync: vi.fn(() => []),
64
+ promises: actual.promises,
65
+ };
66
+ });
67
+
68
+ vi.mock("../alive-store.ts", () => ({ writeAliveMarker: vi.fn() }));
69
+
70
+ // chatMode agent_end 早返回(continue),不读 session-pending;统一 count=0 防干扰。
71
+ vi.mock("../session-pending.ts", () => ({
72
+ readActivePendingFromSessionFile: vi.fn(() => ({ count: 0, recentUnregister: false })),
73
+ prunePendingCursor: vi.fn(),
74
+ listActivePendingFromSessionFile: vi.fn(() => ({ items: [] })),
75
+ }));
76
+
77
+ vi.mock("../engine/engines/pi/temp-prompt.ts", () => ({
78
+ writePromptToTempFile: vi.fn(async (agent: string) => {
79
+ const safeName = agent.replace(/[^\w.-]+/g, "_");
80
+ return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
81
+ }),
82
+ cleanupTempPrompt: vi.fn(async () => {}),
83
+ }));
84
+
85
+ import { runSpawn } from "../engine/engines/pi/session-runner.ts";
86
+ import {
87
+ _resetSettledWatchdogsForTest,
88
+ armMidRoundNoProgress,
89
+ armSettledWatchdog,
90
+ disarmSettledWatchdog,
91
+ getSettledWatchdogPhase,
92
+ getSettledWatchdogTimeoutMs,
93
+ handoverMidRoundToSettled,
94
+ hasSettledWatchdog,
95
+ isSettledWatchdogDisabled,
96
+ refreshMidRoundNoProgress,
97
+ SETTLED_MID_ROUND_NO_PROGRESS_MS,
98
+ SETTLED_WATCHDOG_ENV,
99
+ SETTLED_WATCHDOG_TIMEOUT_MS,
100
+ type SettledWatchdogFireInfo,
101
+ } from "../settled-watchdog.ts";
102
+ import { _resetLifecycleState } from "../lifecycle-manager.ts";
103
+ import { createRecord } from "../execution-record.ts";
104
+ import type { ExecutionRecord } from "../types.ts";
105
+ import {
106
+ emitStdoutLine,
107
+ type FakeChild,
108
+ lastSpawnedChild as lastSpawnedChildOf,
109
+ makeCtx,
110
+ makeOpts,
111
+ makeRecord,
112
+ } from "./helpers/spawn-mock.ts";
113
+
114
+ const mockSpawn = vi.mocked(spawn);
115
+ const lastSpawnedChild = (): FakeChild => lastSpawnedChildOf(mockSpawn);
116
+
117
+ function sessionHeaderLine(): Record<string, unknown> {
118
+ return { type: "session", id: "sess-sw", timestamp: "2026-09-01T00:00:00.000Z", cwd: "/tmp/test" };
119
+ }
120
+
121
+ /** 构造 chatMode record(idleTimeoutMs 拉长到 1h,防 idle timer 干扰两段窗口断言)。 */
122
+ function makeChatModeRecord(id = "sa-chat-sw"): ExecutionRecord {
123
+ const record = createRecord(id, {
124
+ agent: "general-purpose",
125
+ model: "test-model",
126
+ mode: "background",
127
+ task: "chat task",
128
+ slug: "chat",
129
+ startedAt: 1_000_000,
130
+ rootSessionId: "root-session",
131
+ parentRecordId: undefined,
132
+ depth: 0,
133
+ chatMode: true,
134
+ });
135
+ (record as { idleTimeoutMs: number }).idleTimeoutMs = 3_600_000;
136
+ return record;
137
+ }
138
+
139
+ /**
140
+ * fake timers 下的 spawn 等待:advanceTimersByTimeAsync(1) 循环 flush 微任务链
141
+ *(runSpawn 前置 await 均为 mock 的微任务级),直到 spawn 被调。
142
+ */
143
+ async function waitForSpawnUnderFakeTimers(baseline: number, maxIters = 50): Promise<void> {
144
+ for (let i = 0; i < maxIters && mockSpawn.mock.results.length <= baseline; i++) {
145
+ await vi.advanceTimersByTimeAsync(1);
146
+ }
147
+ if (mockSpawn.mock.results.length <= baseline) {
148
+ throw new Error("spawn was not called under fake timers");
149
+ }
150
+ }
151
+
152
+ // ============================================================
153
+ // 原语单测(两段式,验收 ①-⑤)
154
+ // ============================================================
155
+
156
+ describe("[T2-③ / D9] settled-watchdog 两段式原语", () => {
157
+ beforeEach(() => {
158
+ vi.clearAllMocks();
159
+ vi.unstubAllEnvs();
160
+ _resetSettledWatchdogsForTest();
161
+ });
162
+
163
+ afterEach(() => {
164
+ _resetSettledWatchdogsForTest();
165
+ vi.unstubAllEnvs();
166
+ vi.useRealTimers();
167
+ });
168
+
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);
174
+ });
175
+
176
+ it("验收①:agent_end 前静默超时按第一段语义触发(mid-round 回调 + 段信息;收尾回调不触发)", async () => {
177
+ vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout"] });
178
+ const onMidTimeout = vi.fn();
179
+ const onSettleTimeout = vi.fn();
180
+ armMidRoundNoProgress("sa-1", { onMidTimeout, onSettleTimeout });
181
+ expect(hasSettledWatchdog("sa-1")).toBe(true);
182
+ expect(getSettledWatchdogPhase("sa-1")).toBe("mid-round");
183
+
184
+ await vi.advanceTimersByTimeAsync(SETTLED_MID_ROUND_NO_PROGRESS_MS - 1);
185
+ expect(onMidTimeout).not.toHaveBeenCalled();
186
+ await vi.advanceTimersByTimeAsync(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();
191
+ // 到期回调先自删条目(对齐 armIdleTimer 语义)
192
+ expect(hasSettledWatchdog("sa-1")).toBe(false);
193
+
194
+ // disarm 已触发/不存在的 record:幂等 no-op
195
+ disarmSettledWatchdog("sa-1");
196
+ disarmSettledWatchdog("never-armed");
197
+ expect(onMidTimeout).toHaveBeenCalledTimes(1);
198
+ });
199
+
200
+ it("验收②:agent_end 交棒后切换到收尾段(phase=settled;收尾段从交棒点独立计时)", async () => {
201
+ vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout"] });
202
+ const onMidTimeout = vi.fn();
203
+ const onSettleTimeout = vi.fn();
204
+ armMidRoundNoProgress("sa-2", { onMidTimeout, onSettleTimeout });
205
+
206
+ handoverMidRoundToSettled("sa-2");
207
+ expect(getSettledWatchdogPhase("sa-2")).toBe("settled");
208
+ expect(hasSettledWatchdog("sa-2")).toBe(true);
209
+
210
+ // 交棒后满收尾段窗长才触发,触发的是收尾回调(段信息 settled + 600s)
211
+ await vi.advanceTimersByTimeAsync(SETTLED_WATCHDOG_TIMEOUT_MS - 1);
212
+ expect(onSettleTimeout).not.toHaveBeenCalled();
213
+ await vi.advanceTimersByTimeAsync(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);
372
+ });
373
+ });
374
+
375
+ // ============================================================
376
+ // runSpawn 首轮挂载集成(chatMode)
377
+ // ============================================================
378
+
379
+ describe("[T2-③ / D9] runSpawn 首轮挂载(chatMode)", () => {
380
+ beforeEach(() => {
381
+ vi.clearAllMocks();
382
+ vi.stubEnv("XYZ_SUBAGENT_SPAWN_WATCHDOG_MS", "");
383
+ _resetLifecycleState();
384
+ _resetSettledWatchdogsForTest();
385
+ vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout", "Date"] });
386
+ });
387
+
388
+ afterEach(() => {
389
+ vi.restoreAllMocks();
390
+ vi.unstubAllEnvs();
391
+ vi.useRealTimers();
392
+ _resetLifecycleState();
393
+ _resetSettledWatchdogsForTest();
394
+ });
395
+
396
+ it("验收①挂载面:agent_end 前静默超 30min(中段语义)→ kill 层主 → runSpawn 错误返回含段标注 + 恢复指引", async () => {
397
+ const record = makeChatModeRecord();
398
+ const baseline = mockSpawn.mock.results.length;
399
+ const promise = runSpawn(record, "Chat: mid-round wedge", makeOpts(), makeCtx());
400
+ await waitForSpawnUnderFakeTimers(baseline);
401
+ const child = lastSpawnedChild();
402
+
403
+ // prompt 发出后挂中段:advance 到中段窗长触发(原 10min 全程窗已不存在——
404
+ // advance 10min 静默不再误杀,是对 P0-4 的直接回归断言)
405
+ emitStdoutLine(child, sessionHeaderLine());
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);
409
+ expect(child.killed).toBe(false);
410
+ // [余量] fake timers 对 Date mock 有 ms 级舍入,断言边界留 1s 余量(对阈值语义无损)
411
+ await vi.advanceTimersByTimeAsync(2_000);
412
+ expect(child.killed).toBe(true);
413
+ expect(child.killSignal).toBe("SIGTERM");
414
+
415
+ // kill → close:runSpawn 以错误返回(设计 §6.2 首轮窗口形态)
416
+ child.stdout.end();
417
+ child.stderr.end();
418
+ child.emit("close", 143);
419
+ const result = await promise;
420
+ expect(result.success).toBe(false);
421
+ expect(result.error).toContain("settled watchdog");
422
+ expect(result.error).toContain("agent_settled");
423
+ expect(result.error).toContain("mid-round no-progress");
424
+ // S-B 验收判据:错误消息含恢复指引
425
+ expect(result.error).toContain("Recovery:");
426
+ expect(result.error).toContain("action:'list'");
427
+ });
428
+
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 () => {
461
+ const record = makeChatModeRecord();
462
+ const baseline = mockSpawn.mock.results.length;
463
+ const promise = runSpawn(record, "Chat: normal", makeOpts(), makeCtx());
464
+ await waitForSpawnUnderFakeTimers(baseline);
465
+ const child = lastSpawnedChild();
466
+
467
+ emitStdoutLine(child, sessionHeaderLine());
468
+ emitStdoutLine(child, { type: "agent_end", messages: [], willRetry: false });
469
+ emitStdoutLine(child, { type: "agent_settled" });
470
+ await new Promise((r) => setImmediate(r));
471
+
472
+ // 首轮窗口结束(resolveRun 同点清除两段守护);idle timer 被拉长到 1h 不干扰
473
+ expect(hasSettledWatchdog(record.id)).toBe(false);
474
+ await vi.advanceTimersByTimeAsync(SETTLED_MID_ROUND_NO_PROGRESS_MS + SETTLED_WATCHDOG_TIMEOUT_MS + 60_000);
475
+ // settled watchdog 已清:子进程未被本守护 kill(若回归未清,此处 killed=true)
476
+ expect(child.killed).toBe(false);
477
+
478
+ // 收尾
479
+ child.stdout.end();
480
+ child.stderr.end();
481
+ child.emit("close", 0);
482
+ const result = await promise;
483
+ expect(result.success).toBe(true);
484
+ expect(result.error).toBeUndefined();
485
+ });
486
+
487
+ it("挂载面限定 chatMode:非 chatMode 首轮不挂 settled 守护(中段/收尾段均无)", async () => {
488
+ const record = makeRecord(); // 非 chatMode
489
+ const baseline = mockSpawn.mock.results.length;
490
+ const promise = runSpawn(record, "Task: not chat", makeOpts(), makeCtx());
491
+ await waitForSpawnUnderFakeTimers(baseline);
492
+ const child = lastSpawnedChild();
493
+
494
+ emitStdoutLine(child, sessionHeaderLine());
495
+ await new Promise((r) => setImmediate(r));
496
+ expect(hasSettledWatchdog(record.id)).toBe(false);
497
+
498
+ // 非 chatMode:即使静默超过中段窗长也不被 settled 守护 kill(spawn watchdog 同为
499
+ // 未挂载——裸缺省 + env 清),agent_end 之前的执行期不受 T2-③ 触及
500
+ await vi.advanceTimersByTimeAsync(SETTLED_MID_ROUND_NO_PROGRESS_MS + 60_000);
501
+ expect(child.killed).toBe(false);
502
+
503
+ // agent_end(count=0)→ final kill(既有语义)
504
+ emitStdoutLine(child, { type: "agent_end", messages: [], willRetry: false });
505
+ await new Promise((r) => setImmediate(r));
506
+ expect(child.killed).toBe(true);
507
+
508
+ child.stdout.end();
509
+ child.stderr.end();
510
+ child.emit("close", 143);
511
+ const result = await promise;
512
+ expect(result.success).toBe(true);
513
+ });
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