@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
@@ -47,7 +47,7 @@
47
47
  // };
48
48
  // });
49
49
  // vi.mock("../alive-store.ts", () => ({ writeAliveMarker: vi.fn() }));
50
- // vi.mock("../temp-prompt.ts", () => ({
50
+ // vi.mock("../engine/engines/pi/temp-prompt.ts", () => ({
51
51
  // writePromptToTempFile: vi.fn(async (agent: string) => {
52
52
  // const safeName = agent.replace(/[^\w.-]+/g, "_");
53
53
  // return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
@@ -60,8 +60,10 @@ import type { ChildProcess } from "node:child_process";
60
60
  import { EventEmitter } from "node:events";
61
61
  import { PassThrough } from "node:stream";
62
62
 
63
+ import { vi } from "vitest";
64
+
63
65
  import { createRecord } from "../../execution-record.ts";
64
- import type { RunOptions, SessionRunnerContext } from "../../session-runner.ts";
66
+ import type { RunOptions, SessionRunnerContext } from "../../engine/engines/pi/session-runner.ts";
65
67
 
66
68
  /** FakeChild 的假 pid(满足 ChildProcess.pid 形状,无真实进程语义)。 */
67
69
  const FAKE_PID = 12345;
@@ -194,8 +196,9 @@ export function makeRecord(id = "run-1") {
194
196
  return createRecord(id, {
195
197
  agent: "general-purpose",
196
198
  model: "test-model",
197
- mode: "sync",
199
+ mode: "background",
198
200
  task: "do something",
201
+ slug: "spawn-mock",
199
202
  startedAt: 1_000_000,
200
203
  rootSessionId: "root-session",
201
204
  parentRecordId: undefined,
@@ -240,3 +243,147 @@ export function makeCtx(overrides: Partial<SessionRunnerContext> = {}): SessionR
240
243
  ...overrides,
241
244
  };
242
245
  }
246
+
247
+ // ── mock 模块工厂(session-runner 系测试共享,从 keep-alive-no-progress /
248
+ // recursive-visibility-env 的逐字重复 mock 块收敛为单源)──
249
+ //
250
+ // 使用模式(vi.mock 工厂内 await import 本文件,免疫 hoist 时序):
251
+ // vi.mock("node:child_process", async () =>
252
+ // (await import("./helpers/spawn-mock.ts")).childProcessModule());
253
+ //
254
+ // 本段保持「无 session-runner / alive-store / session-pending 值依赖」——本文件会被
255
+ // 各 vi.mock 工厂 await import(求值时机 = 被 mock 模块首次请求),若再值依赖
256
+ // session-runner 会构成「mock 工厂 → 本文件 → session-runner → node:child_process
257
+ // (mock 求值中)」循环。需要值依赖这些模块的测试文件侧装配放
258
+ // session-runner-mocks.ts(只被测试文件顶层静态 import)。
259
+
260
+ /** 共享 logger mock 单例(复核失败 warn 留痕断言用;vi.clearAllMocks 统一重置)。 */
261
+ export const loggerMock = { debug: vi.fn(), warn: vi.fn(), error: vi.fn() };
262
+
263
+ /** "../../core/logger.ts" mock 工厂。 */
264
+ export function coreLoggerModule() {
265
+ return { getLogger: () => loggerMock };
266
+ }
267
+
268
+ /** "node:child_process" mock 工厂(spawn 返回 FakeChild + execFile err-first 兜底)。 */
269
+ export function childProcessModule() {
270
+ return {
271
+ spawn: vi.fn(() => new FakeChild()),
272
+ // buildEnvBlock 用 execFile 异步取 git branch:默认 err-first 兜底(catch → branch=""),
273
+ // 形态同 worktree-manager.test.ts 的 setupExecFile
274
+ execFile: vi.fn(
275
+ (
276
+ _cmd: string,
277
+ _args: readonly string[],
278
+ _opts: unknown,
279
+ cb: (err: Error | null, stdout?: string, stderr?: string) => void,
280
+ ) => cb(new Error("execFile not configured in this test")),
281
+ ),
282
+ };
283
+ }
284
+
285
+ /** "node:fs" mock 工厂(fs 侧写方法 vi.fn 化,promises 保留 actual)。 */
286
+ export async function fsModule(): Promise<Record<string, unknown>> {
287
+ const actual = await vi.importActual<typeof import("node:fs")>("node:fs");
288
+ return {
289
+ default: {
290
+ ...actual,
291
+ mkdirSync: vi.fn(),
292
+ existsSync: vi.fn(() => false),
293
+ appendFileSync: vi.fn(),
294
+ writeFileSync: vi.fn(),
295
+ readdirSync: vi.fn(() => []),
296
+ },
297
+ mkdirSync: vi.fn(),
298
+ existsSync: vi.fn(() => false),
299
+ appendFileSync: vi.fn(),
300
+ writeFileSync: vi.fn(),
301
+ readdirSync: vi.fn(() => []),
302
+ promises: actual.promises,
303
+ };
304
+ }
305
+
306
+ /** "../alive-store.ts" mock 工厂(keep-alive 系超集:marker 读写 + pid 存活判定兜底)。 */
307
+ export function aliveStoreModule() {
308
+ return {
309
+ writeAliveMarker: vi.fn(),
310
+ // [T5②] keep-alive 心跳刷新读取现有 marker id(缺失兜底 record.id)
311
+ readAliveMarker: vi.fn(() => undefined),
312
+ isProcessAlive: vi.fn(() => false),
313
+ };
314
+ }
315
+
316
+ /** "../session-pending.ts" mock 工厂(keep-alive 判定统一 count>0:有活跃后代 → keep-alive 分支)。 */
317
+ export function sessionPendingModule() {
318
+ return {
319
+ readActivePendingFromSessionFile: vi.fn(() => ({ count: 1, recentUnregister: false })),
320
+ prunePendingCursor: vi.fn(),
321
+ listActivePendingFromSessionFile: vi.fn(() => ({ items: [] })),
322
+ };
323
+ }
324
+
325
+ /** "../engine/engines/pi/temp-prompt.ts" mock 工厂(prompt 落 /tmp/fake-<agent>,收尾 no-op)。 */
326
+ export function tempPromptModule() {
327
+ return {
328
+ writePromptToTempFile: vi.fn(async (agent: string) => {
329
+ const safeName = agent.replace(/[^\w.-]+/g, "_");
330
+ return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
331
+ }),
332
+ cleanupTempPrompt: vi.fn(async () => {}),
333
+ };
334
+ }
335
+
336
+ // ── recursive-visibility / schema-env 族 fixture(env 断言族专用,与上方 makeRecord 系
337
+ // 默认值不同:/fake/* 路径 + test/model 形态参与 PI_SUBAGENT_* 断言,逐字保留原值)──
338
+
339
+ /** 构造 env 注入断言族的 ExecutionRecord(可 override id/depth)。 */
340
+ export function makeVisibilityRecord(overrides: { id?: string; depth?: number } = {}) {
341
+ return createRecord(overrides.id ?? "sa-test-record", {
342
+ agent: "general-purpose",
343
+ model: "test/model",
344
+ mode: "background",
345
+ slug: "t",
346
+ task: "test task",
347
+ startedAt: Date.now(),
348
+ rootSessionId: "should-be-overridden-by-sessionRootId-source",
349
+ parentRecordId: undefined,
350
+ depth: overrides.depth ?? 0,
351
+ });
352
+ }
353
+
354
+ /** 构造 env 注入断言族的 RunOptions(可 override 关键字段)。 */
355
+ export function makeRunOpts(overrides: Partial<RunOptions> = {}): RunOptions {
356
+ return {
357
+ resolved: { model: { provider: "test", id: "model", name: "Model", reasoning: false }, thinkingLevel: undefined },
358
+ agentConfig: undefined,
359
+ appendSystemPrompt: undefined,
360
+ skillPath: undefined,
361
+ schema: undefined,
362
+ maxTurns: undefined,
363
+ graceTurns: undefined,
364
+ signal: undefined,
365
+ onEvent: undefined,
366
+ ...overrides,
367
+ };
368
+ }
369
+
370
+ /** 构造 env 注入断言族的 SessionRunnerContext(可 override 关键字段)。 */
371
+ export function makeVisibilityCtx(overrides: Partial<SessionRunnerContext> = {}): SessionRunnerContext {
372
+ return {
373
+ cwd: "/fake/cwd",
374
+ agentDir: "/fake/agent",
375
+ skillDirs: [],
376
+ mainCwd: "/fake/cwd",
377
+ sessionRootId: "root-main-session",
378
+ rootCwd: "/fake/cwd",
379
+ ...overrides,
380
+ };
381
+ }
382
+
383
+ /** 取最近一次 spawn 调用传入的 env(跨进程身份注入断言用)。 */
384
+ export function getLastSpawnEnv<T extends { mock: { calls: unknown[][] } }>(
385
+ mockSpawn: T,
386
+ ): Record<string, string | undefined> {
387
+ const opts = mockSpawn.mock.calls.at(-1)?.[2] as { env?: Record<string, string | undefined> } | undefined;
388
+ return opts?.env ?? {};
389
+ }
@@ -0,0 +1,137 @@
1
+ // src/execution/__tests__/helpers/subagent-service-mocks.ts
2
+ //
3
+ // SubagentService 编排层测试(execute-and-await-worktree / execute-nesting /
4
+ // nested-visibility-env-propagation / recursive-visibility-baseline 四文件)共享的
5
+ // spawn 链 mock 模块工厂。原四文件各自内联 ~85 行逐字重复的 vi.mock 工厂体(fallow
6
+ // duplication 告警),收敛到本 module 单源——桩形变更改此文件,全量消费方同步生效。
7
+ //
8
+ // 消费方式(与 __tests__/helpers/system-page-mount.ts、spawn-mock.ts 同款先例):
9
+ // vi.mock 调用留在测试文件(mock 注册是文件作用域),import 本 module 的语句写在
10
+ // vi.mock 调用之前(工厂执行发生在被测模块链请求被 mock 模块时,届时本 module 已加载)。
11
+ //
12
+ // node:fs / ../alive-store.ts 两个工厂需要真实模块做基底(...actual 展开):真实实现
13
+ // 经工厂的 importOriginal 参数由测试文件传入——本 module 不能顶层静态 import 它们
14
+ // (测试文件的 vi.mock 注册先于一切 import,本 module 属其依赖图,静态 import 会拿到
15
+ // mock 版自身 → 展开循环)。
16
+ //
17
+ // FakeChild 复用 ./spawn-mock.ts 的 class(结构超集:多 stdin / exitCode / signalCode
18
+ // 字段,四文件原内联 FakeChild 只访问 stdout/stderr/kill/emit,行为等价)。
19
+
20
+ import { vi } from "vitest";
21
+
22
+
23
+
24
+ import { FakeChild, emitStdoutLine, sessionHeader } from "./spawn-mock.ts";
25
+
26
+ /**
27
+ * node:child_process mock 模块。
28
+ * spawn → FakeChild(测试经 mockSpawn.mock.results 取回控制器);
29
+ * execFile → err-first 兜底(buildEnvBlock 的 git branch 调用失败 → catch → branch="")。
30
+ */
31
+ export function childProcessModule() {
32
+ return {
33
+ spawn: vi.fn(() => new FakeChild()),
34
+ execFile: vi.fn(
35
+ (
36
+ _cmd: string,
37
+ _args: readonly string[],
38
+ _opts: unknown,
39
+ cb: (err: Error | null, stdout?: string, stderr?: string) => void,
40
+ ) => cb(new Error("execFile not configured in this test")),
41
+ ),
42
+ };
43
+ }
44
+
45
+ /**
46
+ * node:fs mock 模块:同步方法 mock(runSpawn 用到的全部),promises 保留真实实现
47
+ * (temp-prompt 已整体 mock,不触发真实 I/O)。default 与顶层具名是两套独立 vi.fn
48
+ * 实例(与收敛前四文件内联形态同构)。
49
+ */
50
+ // 显式返回类型(typeof import 表达式可命名)——否则推导的返回类型引用 fs 内部
51
+ // 未导出的 ReadStreamOptions/WriteStreamOptions,触发 TS4058 可命名性检查
52
+ export function fsSyncModule(
53
+ actual: typeof import("node:fs"),
54
+ ): {
55
+ /** 真实 fs + 同步 mock 的合并视图(vi.mock 工厂消费面宽松) */
56
+ default: unknown;
57
+ promises: typeof import("node:fs")["promises"];
58
+ mkdirSync: ReturnType<typeof vi.fn>;
59
+ existsSync: ReturnType<typeof vi.fn>;
60
+ appendFileSync: ReturnType<typeof vi.fn>;
61
+ writeFileSync: ReturnType<typeof vi.fn>;
62
+ readdirSync: ReturnType<typeof vi.fn>;
63
+ } {
64
+ const syncMocks = () => ({
65
+ mkdirSync: vi.fn(),
66
+ existsSync: vi.fn(() => false),
67
+ appendFileSync: vi.fn(),
68
+ writeFileSync: vi.fn(),
69
+ readdirSync: vi.fn(() => []),
70
+ });
71
+ return {
72
+ default: { ...actual, ...syncMocks() },
73
+ ...syncMocks(),
74
+ promises: actual.promises,
75
+ };
76
+ }
77
+
78
+ /**
79
+ * ../alive-store.ts mock 模块:writeAliveMarker(runSpawn 写 .alive sidecar)+
80
+ * removeAliveMarker(finalizeRecord 收尾删 .alive)mock;其余导出保留真实实现
81
+ * (worktree-manager / record-store 消费,本组用例不涉及但保留以避免间接报错)。
82
+ */
83
+ export function aliveStoreModule(actual: typeof import("../../alive-store.ts")) {
84
+ return {
85
+ ...actual,
86
+ writeAliveMarker: vi.fn(),
87
+ removeAliveMarker: vi.fn(),
88
+ };
89
+ }
90
+
91
+ /** ../finalized-marker.ts mock 模块(避免真实 fs 写 sidecar)。 */
92
+ export function finalizedMarkerModule() {
93
+ return {
94
+ writeFinalized: vi.fn(),
95
+ readFinalized: vi.fn(() => false),
96
+ };
97
+ }
98
+
99
+ /**
100
+ * ../manifest-store.ts mock 模块。vi.fn 包裹构造器(普通 function,箭头函数不能被 new):
101
+ * 构造参数 recordsDir 可从 mock.calls 断言([MF-3] 目录统一验证用)。
102
+ * no-op 化的根因:writeManifest 真实链路的异步 console 经 worker RPC 回流,与 vitest
103
+ * teardown 形成 race(unhandled rejection / exit 1)——本组用例测编排逻辑不测持久化。
104
+ */
105
+ export function manifestStoreModule() {
106
+ class FakeManifestStore {
107
+ writeManifest = vi.fn(async () => {});
108
+ readManifest = vi.fn(async () => null);
109
+ listAllSync = vi.fn(() => []);
110
+ recoverTmpFiles = vi.fn(async () => []);
111
+ }
112
+ return { ManifestStore: vi.fn(function (_recordsDir: string) { return new FakeManifestStore(); }) };
113
+ }
114
+
115
+ /** ../engine/engines/pi/temp-prompt.ts mock 模块(固定路径,消除 fake-timers 下 flaky 竞态)。 */
116
+ export function tempPromptModule() {
117
+ return {
118
+ writePromptToTempFile: vi.fn(async (agent: string) => {
119
+ const safeName = agent.replace(/[^\w.-]+/g, "_");
120
+ return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
121
+ }),
122
+ cleanupTempPrompt: vi.fn(async () => {}),
123
+ };
124
+ }
125
+
126
+ /**
127
+ * 驱动 FakeChild 完成 session:写 header + 可选事件 + close(0)。
128
+ * runSpawn 在 close 后判定 success(exitCode=0),并跑 identity 补写 + finalizeRecord
129
+ * ——「让 runSpawn 自然 resolve」的标准收尾路径。
130
+ */
131
+ export async function driveChildToCompletion(child: FakeChild, events: Record<string, unknown>[] = []): Promise<void> {
132
+ emitStdoutLine(child, sessionHeader());
133
+ for (const e of events) emitStdoutLine(child, e);
134
+ child.stdout.end();
135
+ child.stderr.end();
136
+ child.emit("close", 0);
137
+ }
@@ -13,102 +13,52 @@
13
13
  // 语义保留,A1-1:显式 maxTurns<=0 与 fail-fast 降级也不挂)。
14
14
  //
15
15
  // mock 布局与 run-spawn-edges.test.ts 一致(FakeChild + mock session-pending)。
16
+ // mock 工厂与 loggerMock 单源收敛 helpers/spawn-mock.ts;vi.mocked 取回与
17
+ // spawn/keep-alive 前奏收敛 helpers/session-runner-mocks.ts(分工见后者头注释)。
16
18
 
17
- import { spawn } from "node:child_process";
18
- import * as fs from "node:fs";
19
-
20
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
19
+ import { describe, expect, it, vi } from "vitest";
21
20
 
22
21
  // [A1-2③] Mock 共享 logger:复核失败的 warn 留痕可被断言(对齐 run-spawn-edges 模式)。
23
- const { loggerMock } = vi.hoisted(() => ({
24
- loggerMock: { debug: vi.fn(), warn: vi.fn(), error: vi.fn() },
25
- }));
26
- vi.mock("../../core/logger.ts", () => ({
27
- getLogger: () => loggerMock,
28
- }));
29
-
30
- vi.mock("node:child_process", async () => {
31
- const { FakeChild } = await import("./helpers/spawn-mock.ts");
32
- return {
33
- spawn: vi.fn(() => new FakeChild()),
34
- execFile: vi.fn(
35
- (
36
- _cmd: string,
37
- _args: readonly string[],
38
- _opts: unknown,
39
- cb: (err: Error | null, stdout?: string, stderr?: string) => void,
40
- ) => cb(new Error("execFile not configured in this test")),
41
- ),
42
- };
43
- });
22
+ vi.mock("../../core/logger.ts", async () =>
23
+ (await import("./helpers/spawn-mock.ts")).coreLoggerModule());
44
24
 
45
- vi.mock("node:fs", async () => {
46
- const actual = await import("node:fs");
47
- return {
48
- default: {
49
- ...actual,
50
- mkdirSync: vi.fn(),
51
- existsSync: vi.fn(() => false),
52
- appendFileSync: vi.fn(),
53
- writeFileSync: vi.fn(),
54
- readdirSync: vi.fn(() => []),
55
- },
56
- mkdirSync: vi.fn(),
57
- existsSync: vi.fn(() => false),
58
- appendFileSync: vi.fn(),
59
- writeFileSync: vi.fn(),
60
- readdirSync: vi.fn(() => []),
61
- promises: actual.promises,
62
- };
63
- });
64
-
65
- vi.mock("../alive-store.ts", () => ({
66
- writeAliveMarker: vi.fn(),
67
- // [T5②] keep-alive 心跳刷新读取现有 marker id(缺失兜底 record.id)
68
- readAliveMarker: vi.fn(() => undefined),
69
- isProcessAlive: vi.fn(() => false),
70
- }));
25
+ vi.mock("node:child_process", async () =>
26
+ (await import("./helpers/spawn-mock.ts")).childProcessModule());
27
+ vi.mock("node:fs", async () => (await import("./helpers/spawn-mock.ts")).fsModule());
28
+ vi.mock("../alive-store.ts", async () =>
29
+ (await import("./helpers/spawn-mock.ts")).aliveStoreModule());
71
30
 
72
31
  // keep-alive 判定:本文件统一 count>0(有活跃后代 → keep-alive 分支)。
73
- vi.mock("../session-pending.ts", () => ({
74
- readActivePendingFromSessionFile: vi.fn(() => ({ count: 1, recentUnregister: false })),
75
- prunePendingCursor: vi.fn(),
76
- listActivePendingFromSessionFile: vi.fn(() => ({ items: [] })),
77
- }));
78
-
79
- vi.mock("../temp-prompt.ts", () => ({
80
- writePromptToTempFile: vi.fn(async (agent: string) => {
81
- const safeName = agent.replace(/[^\w.-]+/g, "_");
82
- return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
83
- }),
84
- cleanupTempPrompt: vi.fn(async () => {}),
85
- }));
32
+ vi.mock("../session-pending.ts", async () =>
33
+ (await import("./helpers/spawn-mock.ts")).sessionPendingModule());
34
+
35
+ vi.mock("../engine/engines/pi/temp-prompt.ts", async () =>
36
+ (await import("./helpers/spawn-mock.ts")).tempPromptModule());
86
37
 
87
38
  import {
88
39
  KEEP_ALIVE_NO_PROGRESS_TIMEOUT_MS,
89
40
  maxTurnsToWatchdogMs,
90
- runSpawn,
91
41
  SPAWN_WATCHDOG_ENV,
92
- } from "../session-runner.ts";
93
- import { listActivePendingFromSessionFile, readActivePendingFromSessionFile } from "../session-pending.ts";
94
- import { isProcessAlive, readAliveMarker, writeAliveMarker } from "../alive-store.ts";
42
+ } from "../engine/engines/pi/session-runner.ts";
95
43
  import {
96
44
  emitStdoutLine,
97
45
  type FakeChild,
98
46
  lastSpawnedChild as lastSpawnedChildOf,
99
- makeCtx,
47
+ loggerMock,
100
48
  makeOpts,
101
- makeRecord,
102
49
  waitForSpawn as waitForSpawnOf,
103
50
  } from "./helpers/spawn-mock.ts";
104
-
105
- const mockSpawn = vi.mocked(spawn);
106
- const mockPending = vi.mocked(readActivePendingFromSessionFile);
107
- const mockListPending = vi.mocked(listActivePendingFromSessionFile);
108
- const mockReadAliveMarker = vi.mocked(readAliveMarker);
109
- const mockWriteAliveMarker = vi.mocked(writeAliveMarker);
110
- const mockIsProcessAlive = vi.mocked(isProcessAlive);
111
- const mockReaddirSync = vi.mocked(fs.readdirSync);
51
+ import { keepAliveTestHooks, spawnAndReachKeepAlive, takeSessionRunnerMocks } from "./helpers/session-runner-mocks.ts";
52
+
53
+ const {
54
+ mockSpawn,
55
+ mockPending,
56
+ mockListPending,
57
+ mockReadAliveMarker,
58
+ mockWriteAliveMarker,
59
+ mockIsProcessAlive,
60
+ mockReaddirSync,
61
+ } = takeSessionRunnerMocks();
112
62
 
113
63
  /** A1-2 复核用例的后代 pid(存/死两形态,对齐 descendant-sweep.test.ts 数值风格)。 */
114
64
  const LIVE_DESCENDANT_PID = 424_242;
@@ -117,48 +67,22 @@ const DEAD_DESCENDANT_PID = 999_999;
117
67
  const lastSpawnedChild = (): FakeChild => lastSpawnedChildOf(mockSpawn);
118
68
  const waitForSpawn = (timeoutMs = 1000): Promise<void> => waitForSpawnOf(mockSpawn, timeoutMs);
119
69
 
120
- /** chatMode 层主 + fake timers + agent_end keep-alive 落位的公共前奏。 */
121
- async function spawnAndReachKeepAlive(opts = makeOpts()): Promise<{
122
- child: FakeChild;
123
- finish: (code?: number) => Promise<Awaited<ReturnType<typeof runSpawn>>>;
124
- }> {
125
- const record = makeRecord();
126
- const promise = runSpawn(record, "Task: keep-alive no-progress", opts, makeCtx());
127
- await waitForSpawn();
128
- const child = lastSpawnedChild();
129
- // fake timers 必须在 emit agent_end 之前启用(keep-alive timer 新建于 agent_end
130
- // 处理器内;不 fake setImmediate——stream flush 靠真实事件循环交付,见 MF-3 先例)。
131
- vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout", "Date"] });
132
- emitStdoutLine(child, sessionHeaderLine());
133
- emitStdoutLine(child, { type: "agent_end", messages: [], willRetry: false });
134
- await new Promise((r) => setImmediate(r));
135
- return {
136
- child,
137
- finish: (code = 143) => {
138
- child.stdout.end();
139
- child.stderr.end();
140
- child.emit("close", code);
141
- return promise as Promise<Awaited<ReturnType<typeof runSpawn>>>;
142
- },
143
- };
144
- }
145
-
146
- function sessionHeaderLine(): Record<string, unknown> {
147
- return { type: "session", id: "sess-ka", timestamp: "2026-09-01T00:00:00.000Z", cwd: "/tmp/test" };
70
+ /** A1-2 复核用例的差集后代装配:pending + session 文件名 + marker + pid 存活形态。 */
71
+ function mockDescendant(sessionId: string, pid: number, alive: boolean): void {
72
+ mockListPending.mockReturnValue({
73
+ items: [{ id: "bg-1", sessionId, type: "session" }],
74
+ });
75
+ mockReaddirSync.mockReturnValue([`20260901T000000-000_${sessionId}.jsonl`]);
76
+ mockReadAliveMarker.mockImplementation((f: unknown) =>
77
+ String(f).endsWith(`_${sessionId}.jsonl`)
78
+ ? { pid, id: sessionId, startedAt: 0 }
79
+ : undefined,
80
+ );
81
+ mockIsProcessAlive.mockImplementation((p: number) => (p === pid ? alive : false));
148
82
  }
149
83
 
150
84
  describe("[T2-①] keep-alive 裸缺省无进展检测上界(P-T2 降级路径 B)", () => {
151
- beforeEach(() => {
152
- vi.clearAllMocks();
153
- vi.stubEnv(SPAWN_WATCHDOG_ENV, "");
154
- mockPending.mockReturnValue({ count: 1, recentUnregister: false });
155
- });
156
-
157
- afterEach(() => {
158
- vi.restoreAllMocks();
159
- vi.unstubAllEnvs();
160
- vi.useRealTimers();
161
- });
85
+ keepAliveTestHooks(mockPending);
162
86
 
163
87
  it("裸缺省:agent_end keep-alive 挂无进展 timer,连续静默 30min 触发 SIGTERM 处置", async () => {
164
88
  const { child, finish } = await spawnAndReachKeepAlive();
@@ -235,16 +159,7 @@ describe("[T2-①] keep-alive 裸缺省无进展检测上界(P-T2 降级路径
235
159
  mockPending
236
160
  .mockReturnValueOnce({ count: 1, recentUnregister: false })
237
161
  .mockReturnValueOnce({ count: 0, recentUnregister: false });
238
- const record = makeRecord();
239
- const promise = runSpawn(record, "Task: keep-then-final", makeOpts(), makeCtx());
240
- await waitForSpawn();
241
- const child = lastSpawnedChild();
242
-
243
- vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout", "Date"] });
244
- emitStdoutLine(child, sessionHeaderLine());
245
- // 第一次 agent_end:keep-alive(挂无进展 timer)
246
- emitStdoutLine(child, { type: "agent_end", messages: [], willRetry: false });
247
- await new Promise((r) => setImmediate(r));
162
+ const { child, promise } = await spawnAndReachKeepAlive(undefined, "Task: keep-then-final");
248
163
  expect(child.killed).toBe(false);
249
164
 
250
165
  // 第二次 agent_end:后代已完成 → final kill(keep-alive 结束,无进展 timer 同撤)
@@ -253,10 +168,15 @@ describe("[T2-①] keep-alive 裸缺省无进展检测上界(P-T2 降级路径
253
168
  expect(child.killed).toBe(true);
254
169
 
255
170
  // 反证 timer 已撤:advance 30min 不再有任何残留 kill 副作用(进程已 close 收尾)
171
+ // 真实 ChildProcess 语义:被 SIGTERM 杀死 → 先 exit(null,'SIGTERM') 再 close。只发
172
+ // close 不发 exit 时 killChain 升级窗口不结算(waitForExit 挂 exit 事件),下方
173
+ // advance 30min+ 会越过 30s grace 误触发 SIGKILL 升级——补 exit 忠实模拟进程已死。
174
+ child.signalCode = "SIGTERM";
175
+ child.emit("exit", null, "SIGTERM");
256
176
  child.stdout.end();
257
177
  child.stderr.end();
258
178
  child.emit("close", 143);
259
- const result = (await promise) as Awaited<ReturnType<typeof runSpawn>>;
179
+ const result = await promise;
260
180
  expect(result.success).toBe(true);
261
181
  await vi.advanceTimersByTimeAsync(KEEP_ALIVE_NO_PROGRESS_TIMEOUT_MS + 60_000);
262
182
  expect(child.killSignal).toBe("SIGTERM"); // 仅 final kill 一次,无二次信号
@@ -296,16 +216,7 @@ describe("[T2-①] keep-alive 裸缺省无进展检测上界(P-T2 降级路径
296
216
 
297
217
  it("A1-2①: fire 复核有存活活跃后代 → 视为有进展重挂(30min 固定复核节奏),不处置层主", async () => {
298
218
  // 后代 pending 差集非空 + pid 存活(与 descendant sweep 同源判据)
299
- mockListPending.mockReturnValue({
300
- items: [{ id: "bg-1", sessionId: "ka-desc-sess", type: "session" }],
301
- });
302
- mockReaddirSync.mockReturnValue(["20260901T000000-000_ka-desc-sess.jsonl"]);
303
- mockReadAliveMarker.mockImplementation((f: unknown) =>
304
- String(f).endsWith("_ka-desc-sess.jsonl")
305
- ? { pid: LIVE_DESCENDANT_PID, id: "ka-desc-sess", startedAt: 0 }
306
- : undefined,
307
- );
308
- mockIsProcessAlive.mockImplementation((pid: number) => pid === LIVE_DESCENDANT_PID);
219
+ mockDescendant("ka-desc-sess", LIVE_DESCENDANT_PID, true);
309
220
 
310
221
  const { child, finish } = await spawnAndReachKeepAlive();
311
222
  expect(child.killed).toBe(false);
@@ -336,16 +247,7 @@ describe("[T2-①] keep-alive 裸缺省无进展检测上界(P-T2 降级路径
336
247
  });
337
248
 
338
249
  it("A1-2②: fire 复核后代差集非空但 pid 全部已死 → 真静默,执行处置", async () => {
339
- mockListPending.mockReturnValue({
340
- items: [{ id: "bg-1", sessionId: "ka-dead-sess", type: "session" }],
341
- });
342
- mockReaddirSync.mockReturnValue(["20260901T000000-000_ka-dead-sess.jsonl"]);
343
- mockReadAliveMarker.mockImplementation((f: unknown) =>
344
- String(f).endsWith("_ka-dead-sess.jsonl")
345
- ? { pid: DEAD_DESCENDANT_PID, id: "ka-dead-sess", startedAt: 0 }
346
- : undefined,
347
- );
348
- mockIsProcessAlive.mockReturnValue(false); // 全部已死
250
+ mockDescendant("ka-dead-sess", DEAD_DESCENDANT_PID, false); // 差集非空但 pid 全部已死
349
251
 
350
252
  const { child, finish } = await spawnAndReachKeepAlive();
351
253
 
@@ -56,7 +56,7 @@ vi.mock("../session-pending.ts", () => ({
56
56
  prunePendingCursor: vi.fn(),
57
57
  listActivePendingFromSessionFile: vi.fn(() => ({ items: [] })),
58
58
  }));
59
- vi.mock("../temp-prompt.ts", () => ({
59
+ vi.mock("../engine/engines/pi/temp-prompt.ts", () => ({
60
60
  writePromptToTempFile: vi.fn(async (agent: string) => {
61
61
  const safeName = agent.replace(/[^\w.-]+/g, "_");
62
62
  return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
@@ -64,7 +64,7 @@ vi.mock("../temp-prompt.ts", () => ({
64
64
  cleanupTempPrompt: vi.fn(async () => {}),
65
65
  }));
66
66
 
67
- import { killAllSpawnedChildren, runSpawn, spawnedChildren } from "../session-runner.ts";
67
+ import { killAllSpawnedChildren, runSpawn, spawnedChildren } from "../engine/engines/pi/session-runner.ts";
68
68
  import {
69
69
  emitStdoutLine,
70
70
  type FakeChild,
@@ -10,7 +10,7 @@ import {
10
10
  disarmIdleTimer,
11
11
  _resetLifecycleState,
12
12
  } from "../lifecycle-manager.ts";
13
- import { spawnedChildren } from "../session-runner.ts";
13
+ import { spawnedChildren } from "../engine/engines/pi/session-runner.ts";
14
14
  import type { ChildProcess } from "node:child_process";
15
15
  import type { ExecutionRecord } from "../types.ts";
16
16
 
@@ -25,7 +25,6 @@ function makeRecord(overrides: Partial<ExecutionRecord> = {}): ExecutionRecord {
25
25
  thinkingLevel: undefined,
26
26
  mode: "background",
27
27
  task: "test",
28
- slug: "test",
29
28
  startedAt: Date.now(),
30
29
  rootSessionId: "root",
31
30
  parentRecordId: undefined,
@@ -22,7 +22,7 @@ function makeBaseManifest(overrides: Partial<ManifestRecord> = {}): ManifestReco
22
22
  id: "rec-test",
23
23
  rootSessionId: "session-main",
24
24
  agentName: "worker",
25
- status: "completed",
25
+ status: "closed",
26
26
  createdAt: 1000,
27
27
  ...overrides,
28
28
  };
@@ -38,7 +38,7 @@ describe("ManifestStore — parentRecordId 落盘 (M3a)", () => {
38
38
  });
39
39
 
40
40
  afterEach(() => {
41
- fs.rmSync(tmpDir, { recursive: true, force: true });
41
+ fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
42
42
  });
43
43
 
44
44
  // ── TC-m3a-new-record-parentid ──
@@ -71,7 +71,7 @@ describe("ManifestStore — parentRecordId 落盘 (M3a)", () => {
71
71
  id: "rec-old",
72
72
  rootSessionId: "session-main",
73
73
  agentName: "worker",
74
- status: "completed",
74
+ status: "closed",
75
75
  createdAt: 2000,
76
76
  // 无 parentRecordId —— 旧版本写的
77
77
  };
@@ -104,7 +104,7 @@ describe("ManifestStore — parentRecordId 落盘 (M3a)", () => {
104
104
  id: "rec-minimal",
105
105
  rootSessionId: "session-main",
106
106
  agentName: "worker",
107
- status: "completed",
107
+ status: "closed",
108
108
  createdAt: 3000,
109
109
  };
110
110
  fs.writeFileSync(
@@ -54,7 +54,7 @@ describe("T5④ recoverTmpFiles per-file tolerance", () => {
54
54
 
55
55
  afterEach(() => {
56
56
  vi.restoreAllMocks();
57
- fs.rmSync(dir, { recursive: true, force: true });
57
+ fs.rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
58
58
  });
59
59
 
60
60
  it("processes remaining tmp files when one unlink fails mid-loop", async () => {
@@ -23,7 +23,7 @@ import {
23
23
  maxTurnsToWatchdogMs,
24
24
  resolveSpawnWatchdogMs,
25
25
  SPAWN_WATCHDOG_ENV,
26
- } from "../session-runner.ts";
26
+ } from "../engine/engines/pi/session-runner.ts";
27
27
 
28
28
  const MIN_MS = 60_000;
29
29
  /** watchdog floor(session-runner WATCHDOG_FLOOR_MINUTES=30 的毫秒值)。 */