@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
@@ -0,0 +1,489 @@
1
+ // src/execution/__tests__/session-runner-dispatch.test.ts
2
+ //
3
+ // [S-17a / runSpawn 阶段拆分] 重构提取辅助函数的可观察行为锁定(stdout 分派域)。
4
+ //
5
+ // 背景:session-runner.ts 按执行阶段拆分(文件顶部「runSpawn 的阶段拆分」注释块)后,
6
+ // 新提取的辅助函数(dispatchSdkEventByType / accumulateMessageEnd / handleHeaderLine /
7
+ // setupFreshChild / buildChildEnv / buildSpawnInvocation / startGetStateHandshake /
8
+ // writeAliveMarkerBestEffort)残留分支无断言锁定。本文件经 runSpawn 全链路(FakeChild
9
+ // stdout 行喂入)锁定可观察行为:
10
+ // - stdout 分派:message_end usage 累计 / stopReason=error 错误收口 / compaction_start
11
+ // 事件外发 / 空行不打 invalid 计数 / 末尾残留 invalid 行 close 时聚合留痕;
12
+ // - header 行:worktree pid 补全(onWorktreePid 收到推导 sessionFile)、回调抛错
13
+ // best-effort 不中断解析、alive marker 写失败不中断;
14
+ // - setupFreshChild:worktree pid 注册失败(防御 catch)warn 留痕 spawn 继续;
15
+ // - buildChildEnv:relay 激活时归属 env 注入子进程;
16
+ // - buildSpawnInvocation:resume model 无 / 时 provider 兜底 unknown;
17
+ // - startGetStateHandshake:[F2] get_state 写 stdin 同步 EPIPE → error 留痕 +
18
+ // 握手立即放弃,close 收尾不被阻塞。
19
+ //
20
+ // mock 布局与 session-runner-epipe.test.ts 一致(FakeChild + fs/alive-store/
21
+ // session-pending/temp-prompt mock + 共享 logger mock)。
22
+
23
+ import { spawn } from "node:child_process";
24
+ import type { ChildProcess } from "node:child_process";
25
+
26
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
27
+
28
+ const { loggerMock } = vi.hoisted(() => ({
29
+ loggerMock: { debug: vi.fn(), warn: vi.fn(), error: vi.fn() },
30
+ }));
31
+ vi.mock("../../core/logger.ts", () => ({ getLogger: () => loggerMock }));
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
+ // buildEnvBlock 的 git branch 调用(execFile 异步):默认 err-first 兜底 → branch=""
38
+ execFile: vi.fn(
39
+ (
40
+ _cmd: string,
41
+ _args: readonly string[],
42
+ _opts: unknown,
43
+ cb: (err: Error | null, stdout?: string, stderr?: string) => void,
44
+ ) => cb(new Error("execFile not configured in this test")),
45
+ ),
46
+ };
47
+ });
48
+
49
+ vi.mock("node:fs", async () => {
50
+ const actual = await import("node:fs");
51
+ return {
52
+ default: {
53
+ ...actual,
54
+ mkdirSync: vi.fn(),
55
+ existsSync: vi.fn(() => false),
56
+ appendFileSync: vi.fn(),
57
+ writeFileSync: vi.fn(),
58
+ readdirSync: vi.fn(() => []),
59
+ },
60
+ mkdirSync: vi.fn(),
61
+ existsSync: vi.fn(() => false),
62
+ appendFileSync: vi.fn(),
63
+ writeFileSync: vi.fn(),
64
+ readdirSync: vi.fn(() => []),
65
+ promises: actual.promises,
66
+ };
67
+ });
68
+
69
+ vi.mock("../alive-store.ts", () => ({
70
+ writeAliveMarker: vi.fn(),
71
+ readAliveMarker: vi.fn(() => undefined),
72
+ isProcessAlive: vi.fn(() => false),
73
+ }));
74
+
75
+ vi.mock("../session-pending.ts", () => ({
76
+ readActivePendingFromSessionFile: vi.fn(() => ({ count: 0 })),
77
+ prunePendingCursor: vi.fn(),
78
+ listActivePendingFromSessionFile: vi.fn(() => ({ items: [] })),
79
+ }));
80
+
81
+ vi.mock("../engine/engines/pi/temp-prompt.ts", () => ({
82
+ writePromptToTempFile: vi.fn(async (agent: string) => {
83
+ const safeName = agent.replace(/[^\w.-]+/g, "_");
84
+ return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
85
+ }),
86
+ cleanupTempPrompt: vi.fn(async () => {}),
87
+ }));
88
+
89
+ import { writeAliveMarker } from "../alive-store.ts";
90
+ import { runSpawn, type RunOptions } from "../engine/engines/pi/session-runner.ts";
91
+ import {
92
+ RELAY_ENV_NODE,
93
+ RELAY_ENV_RECORD_ID,
94
+ RELAY_ENV_SCRIPT,
95
+ RELAY_ENV_SESSION_ID,
96
+ RELAY_ENV_SOCKET,
97
+ } from "../relay-env.ts";
98
+ import { _resetLifecycleState } from "../lifecycle-manager.ts";
99
+ import {
100
+ FakeChild,
101
+ lastSpawnedChild as lastSpawnedChildOf,
102
+ makeCtx,
103
+ makeOpts,
104
+ makeRecord,
105
+ sessionHeader,
106
+ waitForSpawn as waitForSpawnOf,
107
+ } from "./helpers/spawn-mock.ts";
108
+ import type { SessionRunnerContext } from "../engine/engines/pi/session-runner.ts";
109
+
110
+ const mockSpawn = vi.mocked(spawn);
111
+ const mockWriteAliveMarker = vi.mocked(writeAliveMarker);
112
+ const lastSpawnedChild = (): FakeChild => lastSpawnedChildOf(mockSpawn);
113
+ const waitForSpawn = (timeoutMs = 1000): Promise<void> => waitForSpawnOf(mockSpawn, timeoutMs);
114
+
115
+ /** FakeChild → spawn mock 实现(结构缺 ChildProcess 私有字段,仅类型层收窄;运行时形状由 runSpawn 消费面界定)。 */
116
+ const fakeSpawnImpl = (): ChildProcess => new FakeChild() as unknown as ChildProcess;
117
+
118
+ /** runSpawn 收尾:end 流 + close(code),await runSpawn 返回 AgentResult。 */
119
+ async function settleRunSpawn(
120
+ child: FakeChild,
121
+ promise: ReturnType<typeof runSpawn>,
122
+ code = 0,
123
+ ): Promise<Awaited<ReturnType<typeof runSpawn>>> {
124
+ child.stdout.end();
125
+ child.stderr.end();
126
+ child.emit("close", code);
127
+ return promise;
128
+ }
129
+
130
+ describe("[S-17a] stdout 分派辅助函数行为(dispatchSdkEventByType / accumulateMessageEnd)", () => {
131
+ beforeEach(() => {
132
+ vi.clearAllMocks();
133
+ // 显式重置 spawn 实现(防止个别用例的 mockImplementation 泄漏到后续用例)
134
+ mockSpawn.mockImplementation(fakeSpawnImpl);
135
+ _resetLifecycleState();
136
+ });
137
+
138
+ afterEach(() => {
139
+ _resetLifecycleState();
140
+ vi.unstubAllEnvs();
141
+ });
142
+
143
+ it("message_end 带 usage → 外发 message_end 事件(cost 从 cost.total 提取)+ totalTokens 累计", async () => {
144
+ const onEvent = vi.fn();
145
+ const record = makeRecord("sa-usage");
146
+ const promise = runSpawn(record, "Task: usage", makeOpts({ onEvent }), makeCtx());
147
+ await waitForSpawn();
148
+ const child = lastSpawnedChild();
149
+
150
+ child.stdout.write(
151
+ `${JSON.stringify({
152
+ type: "message_end",
153
+ message: {
154
+ usage: { input: 10, output: 20, cacheRead: 5, cacheWrite: 0, cost: { total: 0.42 } },
155
+ stopReason: "stop",
156
+ },
157
+ })}\n`,
158
+ );
159
+ await new Promise((r) => setImmediate(r));
160
+
161
+ // 事件外发(AgentEvent 形状):cost 字段拍平自 usage.cost.total
162
+ expect(onEvent).toHaveBeenCalledWith({
163
+ type: "message_end",
164
+ usage: { input: 10, output: 20, cacheRead: 5, cacheWrite: 0, cost: 0.42 },
165
+ });
166
+ // 累计副作用:totalTokens = input+output+cacheRead+cacheWrite
167
+ expect(record.totalTokens).toBe(35);
168
+
169
+ const result = await settleRunSpawn(child, promise);
170
+ expect(result.success).toBe(true);
171
+ });
172
+
173
+ it("message_end stopReason=error → error 事件收口进 record.lastError → runSpawn 结果 failed", async () => {
174
+ const onEvent = vi.fn();
175
+ const record = makeRecord("sa-merr");
176
+ const promise = runSpawn(record, "Task: stop-error", makeOpts({ onEvent }), makeCtx());
177
+ await waitForSpawn();
178
+ const child = lastSpawnedChild();
179
+
180
+ child.stdout.write(
181
+ `${JSON.stringify({
182
+ type: "message_end",
183
+ message: { stopReason: "error", errorMessage: "provider exploded" },
184
+ })}\n`,
185
+ );
186
+ await new Promise((r) => setImmediate(r));
187
+
188
+ // error 事件外发(errorMessage 优先,缺省回退 reason/stopReason)
189
+ expect(onEvent).toHaveBeenCalledWith({ type: "error", message: "provider exploded" });
190
+ expect(record.lastError).toBe("provider exploded");
191
+
192
+ // close 后 outcome 判定:lastError 分支 → success=false + error 原文
193
+ const result = await settleRunSpawn(child, promise, 0);
194
+ expect(result.success).toBe(false);
195
+ expect(result.error).toContain("provider exploded");
196
+ });
197
+
198
+ it("compaction_start → 外发 compaction 事件(record 无数据副作用)", async () => {
199
+ const onEvent = vi.fn();
200
+ const record = makeRecord("sa-compaction");
201
+ const promise = runSpawn(record, "Task: compaction", makeOpts({ onEvent }), makeCtx());
202
+ await waitForSpawn();
203
+ const child = lastSpawnedChild();
204
+
205
+ child.stdout.write(`${JSON.stringify({ type: "compaction_start" })}\n`);
206
+ await new Promise((r) => setImmediate(r));
207
+
208
+ expect(onEvent).toHaveBeenCalledWith({ type: "compaction" });
209
+ expect(record.lastError).toBeUndefined();
210
+
211
+ const result = await settleRunSpawn(child, promise);
212
+ expect(result.success).toBe(true);
213
+ });
214
+
215
+ it("空行不打 invalid 计数;stdout 末尾残留 invalid 行(无换行)close 时聚合 debug 留痕", async () => {
216
+ const record = makeRecord("sa-invalid");
217
+ const promise = runSpawn(record, "Task: invalid-lines", makeOpts(), makeCtx());
218
+ await waitForSpawn();
219
+ const child = lastSpawnedChild();
220
+
221
+ // 空行(parseSpawnLine 返回 null):静默跳过,不算 invalid
222
+ child.stdout.write("\n");
223
+ // 末尾残留半行(无换行,close 前未 flush):非法 JSON → close 路径计入 invalid 统计
224
+ child.stdout.write("this is not json at all");
225
+ await new Promise((r) => setImmediate(r));
226
+
227
+ loggerMock.debug.mockClear();
228
+ const result = await settleRunSpawn(child, promise, 0);
229
+
230
+ expect(result.success).toBe(true);
231
+ // [LC-9/T7②] close 聚合留痕:总数 + 样本(事件行损坏被静默丢弃的排查入口)
232
+ expect(loggerMock.debug).toHaveBeenCalledWith(
233
+ expect.stringContaining("stdout had 1 invalid line(s) dropped in total"),
234
+ );
235
+ });
236
+ });
237
+
238
+ describe("[S-17a] handleHeaderLine:header 行分派 + worktree pid 补全", () => {
239
+ const WORKTREE = {
240
+ path: "/tmp/wt-checkout",
241
+ branch: "wt/dispatch-1",
242
+ baseCommit: "abc123",
243
+ mainCwd: "/tmp/test",
244
+ } as const;
245
+
246
+ beforeEach(() => {
247
+ vi.clearAllMocks();
248
+ mockSpawn.mockImplementation(fakeSpawnImpl);
249
+ _resetLifecycleState();
250
+ });
251
+
252
+ afterEach(() => {
253
+ _resetLifecycleState();
254
+ });
255
+
256
+ it("header 到达 → sessionFile 推导回填 + onWorktreePid 收到 (branch, pid, 推导路径)", async () => {
257
+ const onWorktreePid = vi.fn();
258
+ const ctx = makeCtx({ onWorktreePid } as Partial<SessionRunnerContext>);
259
+ const opts = makeOpts({ worktree: WORKTREE });
260
+ const record = makeRecord("sa-wt-header");
261
+ const promise = runSpawn(record, "Task: wt-header", opts, ctx);
262
+ await waitForSpawn();
263
+ const child = lastSpawnedChild();
264
+
265
+ child.stdout.write(`${JSON.stringify(sessionHeader("sa-sess-wt"))}\n`);
266
+ await new Promise((r) => setImmediate(r));
267
+
268
+ // header 分支的 worktree pid 补全:带推导出的 sessionFile(registry entry 数据源)
269
+ expect(onWorktreePid).toHaveBeenCalledWith(
270
+ "wt/dispatch-1",
271
+ child.pid,
272
+ expect.stringContaining("sa-sess-wt"),
273
+ );
274
+ expect(record.sessionFile).toContain("sa-sess-wt");
275
+
276
+ await settleRunSpawn(child, promise);
277
+ });
278
+
279
+ it("onWorktreePid 回调同步抛错 → best-effort error 留痕,stdout 解析不中断(后续事件照常分派)", async () => {
280
+ const onEvent = vi.fn();
281
+ // setupFreshChild 内两处注册调用先成功,header 分支的第三处抛错
282
+ const onWorktreePid = vi
283
+ .fn()
284
+ .mockReturnValueOnce(undefined)
285
+ .mockReturnValueOnce(undefined)
286
+ .mockImplementation(() => {
287
+ throw new Error("registry lock contention");
288
+ });
289
+ const ctx = makeCtx({ onWorktreePid } as Partial<SessionRunnerContext>);
290
+ const opts = makeOpts({ worktree: WORKTREE });
291
+ const promise = runSpawn(makeRecord("sa-wt-throw"), "Task: wt-throw", makeOpts({ ...opts, onEvent }), ctx);
292
+ await waitForSpawn();
293
+ const child = lastSpawnedChild();
294
+
295
+ child.stdout.write(`${JSON.stringify(sessionHeader("sa-sess-wt2"))}\n`);
296
+ child.stdout.write(`${JSON.stringify({ type: "compaction_start" })}\n`);
297
+ await new Promise((r) => setImmediate(r));
298
+
299
+ // bestEffort 缺省 debug 级(msg + { detail } 两参):同步段异常不阻断 stdout 解析
300
+ expect(loggerMock.debug).toHaveBeenCalledWith(
301
+ expect.stringContaining("onWorktreePid callback (first header) failed"),
302
+ expect.objectContaining({ detail: "registry lock contention" }),
303
+ );
304
+ // 解析继续:header 之后的 compaction 事件照常外发
305
+ expect(onEvent).toHaveBeenCalledWith({ type: "compaction" });
306
+
307
+ await settleRunSpawn(child, promise);
308
+ });
309
+
310
+ it("alive marker 写失败(writeAliveMarker 抛错)→ best-effort debug 留痕,header 回填与解析照常", async () => {
311
+ const onEvent = vi.fn();
312
+ mockWriteAliveMarker.mockImplementation(() => {
313
+ throw new Error("disk full");
314
+ });
315
+ const record = makeRecord("sa-marker-fail");
316
+ const promise = runSpawn(record, "Task: marker-fail", makeOpts({ onEvent }), makeCtx());
317
+ await waitForSpawn();
318
+ const child = lastSpawnedChild();
319
+
320
+ child.stdout.write(`${JSON.stringify(sessionHeader("sa-sess-mf"))}\n`);
321
+ child.stdout.write(`${JSON.stringify({ type: "compaction_start" })}\n`);
322
+ await new Promise((r) => setImmediate(r));
323
+
324
+ // [持久化 C] marker 是崩溃恢复的增强信号,缺失只降低可恢复性,不影响执行主流程
325
+ expect(loggerMock.debug).toHaveBeenCalledWith(
326
+ expect.stringContaining("alive marker write failed (best-effort continue)"),
327
+ );
328
+ expect(record.sessionFile).toContain("sa-sess-mf");
329
+ expect(onEvent).toHaveBeenCalledWith({ type: "compaction" });
330
+
331
+ await settleRunSpawn(child, promise);
332
+ });
333
+ });
334
+
335
+ describe("[S-17a] setupFreshChild:worktree pid 注册防御 catch", () => {
336
+ beforeEach(() => {
337
+ vi.clearAllMocks();
338
+ mockSpawn.mockImplementation(fakeSpawnImpl);
339
+ _resetLifecycleState();
340
+ });
341
+
342
+ afterEach(() => {
343
+ _resetLifecycleState();
344
+ });
345
+
346
+ it("第二处 onWorktreePid 注册抛错(防御 catch)→ warn 留痕,spawn 主流程不被阻断", async () => {
347
+ // 第一处(无保护)成功,第二处(try/catch 保护)抛错
348
+ const onWorktreePid = vi
349
+ .fn()
350
+ .mockReturnValueOnce(undefined)
351
+ .mockImplementation(() => {
352
+ throw new Error("registry write failed");
353
+ });
354
+ const ctx = makeCtx({ onWorktreePid } as Partial<SessionRunnerContext>);
355
+ const opts = makeOpts({
356
+ worktree: { path: "/tmp/wt-checkout", branch: "wt/defensive", baseCommit: "abc", mainCwd: "/tmp/test" },
357
+ });
358
+ const record = makeRecord("sa-wt-defensive");
359
+ const promise = runSpawn(record, "Task: wt-defensive", opts, ctx);
360
+ await waitForSpawn();
361
+ const child = lastSpawnedChild();
362
+
363
+ // [S1] 注册表写失败最坏后果是条目停留 pid=0(reaper 宽限兜底),不阻断 spawn
364
+ expect(loggerMock.warn).toHaveBeenCalledWith(
365
+ expect.stringContaining("worktree pid registration failed (defensive)"),
366
+ expect.objectContaining({ branch: "wt/defensive" }),
367
+ );
368
+ // prompt 命令已照常写入 stdin(spawn 装配未被中断)
369
+ expect(child.stdin.writable).toBe(true);
370
+
371
+ const result = await settleRunSpawn(child, promise);
372
+ expect(result.success).toBe(true);
373
+ });
374
+ });
375
+
376
+ describe("[S-17a] buildChildEnv / buildSpawnInvocation:env 与参数组装", () => {
377
+ beforeEach(() => {
378
+ vi.clearAllMocks();
379
+ mockSpawn.mockImplementation(fakeSpawnImpl);
380
+ _resetLifecycleState();
381
+ });
382
+
383
+ afterEach(() => {
384
+ _resetLifecycleState();
385
+ vi.unstubAllEnvs();
386
+ });
387
+
388
+ it("relay 激活(SOCKET/NODE/SCRIPT 三 env 齐备)→ 子进程 env 注入归属键 SESSION_ID/RECORD_ID", async () => {
389
+ vi.stubEnv(RELAY_ENV_SOCKET, "/tmp/relay.sock");
390
+ vi.stubEnv(RELAY_ENV_NODE, "node-1");
391
+ vi.stubEnv(RELAY_ENV_SCRIPT, "/tmp/relay.mjs");
392
+ const record = makeRecord("sa-relay");
393
+ const promise = runSpawn(record, "Task: relay-env", makeOpts(), makeCtx());
394
+ await waitForSpawn();
395
+
396
+ const call = mockSpawn.mock.calls.at(-1);
397
+ expect(call).toBeDefined();
398
+ const env = call?.[2]?.env as Record<string, string | undefined> | undefined;
399
+ // [E 方案 §5.2-2] tee 帧路由键:SESSION_ID = ctx.sessionRootId、RECORD_ID = record.id
400
+ expect(env?.[RELAY_ENV_SESSION_ID]).toBe("root-session-test");
401
+ expect(env?.[RELAY_ENV_RECORD_ID]).toBe("sa-relay");
402
+
403
+ await settleRunSpawn(lastSpawnedChild(), promise);
404
+ });
405
+
406
+ it("relay 未激活 → 归属 env 不注入(未激活环境下携带 record 值 env 是误导噪声)", async () => {
407
+ const promise = runSpawn(makeRecord("sa-norelay"), "Task: no-relay", makeOpts(), makeCtx());
408
+ await waitForSpawn();
409
+
410
+ const call = mockSpawn.mock.calls.at(-1);
411
+ const env = call?.[2]?.env as Record<string, string | undefined> | undefined;
412
+ expect(env?.[RELAY_ENV_SESSION_ID]).toBeUndefined();
413
+ expect(env?.[RELAY_ENV_RECORD_ID]).toBeUndefined();
414
+
415
+ await settleRunSpawn(lastSpawnedChild(), promise);
416
+ });
417
+
418
+ it("resume model 无 / 分隔(异常形态回显)→ provider 兜底 unknown,--model 仍可执行", async () => {
419
+ const promise = runSpawn(
420
+ makeRecord("sa-resume-noslash"),
421
+ "Task: resume",
422
+ makeOpts() as RunOptions,
423
+ makeCtx(),
424
+ { sessionFile: "/tmp/prev-session.jsonl", model: "legacy-model-no-slash" },
425
+ );
426
+ await waitForSpawn();
427
+
428
+ const call = mockSpawn.mock.calls.at(-1);
429
+ const args = call?.[1] as string[];
430
+ // [U1 D2 豁免] resume 回显不经 registry 断言,无 / 时按 unknown provider 兜底
431
+ //(与 subagent-service record 回读同构)
432
+ const modelIdx = args.indexOf("--model");
433
+ expect(modelIdx).toBeGreaterThan(-1);
434
+ expect(args[modelIdx + 1]).toBe("unknown/legacy-model-no-slash");
435
+ // resume 锁定 sessionFile:--session 参数追加
436
+ const sessionIdx = args.indexOf("--session");
437
+ expect(args[sessionIdx + 1]).toBe("/tmp/prev-session.jsonl");
438
+
439
+ await settleRunSpawn(lastSpawnedChild(), promise);
440
+ });
441
+ });
442
+
443
+ describe("[S-17a] startGetStateHandshake:[F2] 同步写失败兜底", () => {
444
+ beforeEach(() => {
445
+ vi.clearAllMocks();
446
+ _resetLifecycleState();
447
+ });
448
+
449
+ afterEach(() => {
450
+ _resetLifecycleState();
451
+ });
452
+
453
+ it("get_state 写 stdin 同步 EPIPE(stdin 已断)→ error 留痕 + 握手立即放弃,close 收尾不被阻塞", async () => {
454
+ // spawn 即返回 stdin 已断语义的 child:对 get_state 命令的 write 同步抛 EPIPE
455
+ //(prompt 命令写入正常,区分 writeStdinLine 的两条调用链)
456
+ mockSpawn.mockImplementation((): ChildProcess => {
457
+ const c = new FakeChild();
458
+ const realWrite = c.stdin.write.bind(c.stdin);
459
+ c.stdin.write = ((chunk: string | Uint8Array, ...rest: unknown[]) => {
460
+ if (typeof chunk === "string" && chunk.includes('"get_state"')) {
461
+ const err = new Error("write EPIPE") as NodeJS.ErrnoException;
462
+ err.code = "EPIPE";
463
+ throw err;
464
+ }
465
+ return realWrite(chunk as Buffer, ...(rest as []));
466
+ }) as typeof c.stdin.write;
467
+ return c as unknown as ChildProcess;
468
+ });
469
+
470
+ const record = makeRecord("sa-hs-epipe");
471
+ const promise = runSpawn(record, "Task: hs-epipe", makeOpts(), makeCtx());
472
+ await waitForSpawn();
473
+ const child = lastSpawnedChild();
474
+ await new Promise((r) => setImmediate(r));
475
+
476
+ // [F2] unhandledRejection 防线:executor 同步异常转 reject 后被 .catch 接住,
477
+ // 错误消息带 EPIPE 语义与冷路径恢复指引
478
+ expect(loggerMock.error).toHaveBeenCalledWith(
479
+ expect.stringContaining("get_state handshake failed"),
480
+ );
481
+ expect(loggerMock.error).toHaveBeenCalledWith(expect.stringContaining("EPIPE"));
482
+
483
+ // 握手已放弃(abandonHandshake):close 收尾不被阻塞,runSpawn 正常 resolve
484
+ const result = await settleRunSpawn(child, promise, 0);
485
+ expect(result.success).toBe(true);
486
+ // sessionFile 未回填(握手失败形态,收尾由 existsSync + findSessionFileByHeaderId 兜底)
487
+ expect(record.sessionFile).toBeUndefined();
488
+ });
489
+ });
@@ -57,7 +57,7 @@ vi.mock("../session-pending.ts", () => ({
57
57
  prunePendingCursor: vi.fn(),
58
58
  }));
59
59
 
60
- vi.mock("../temp-prompt.ts", () => ({
60
+ vi.mock("../engine/engines/pi/temp-prompt.ts", () => ({
61
61
  writePromptToTempFile: vi.fn(async (agent: string) => {
62
62
  const safeName = agent.replace(/[^\w.-]+/g, "_");
63
63
  return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
@@ -69,8 +69,8 @@ import {
69
69
  EPIPE_FAILURE_THRESHOLD,
70
70
  recordEpipeFailure,
71
71
  resetAllEpipeFailures,
72
- } from "../stdin-writer.ts";
73
- import { runSpawn, spawnedChildren } from "../session-runner.ts";
72
+ } from "../engine/engines/pi/stdin-writer.ts";
73
+ import { runSpawn, spawnedChildren } from "../engine/engines/pi/session-runner.ts";
74
74
  import { _resetLifecycleState } from "../lifecycle-manager.ts";
75
75
  import {
76
76
  FakeChild,
@@ -76,7 +76,7 @@ vi.mock("../session-pending.ts", () => ({
76
76
  listActivePendingFromSessionFile: vi.fn(() => ({ items: [] })),
77
77
  }));
78
78
 
79
- vi.mock("../temp-prompt.ts", () => ({
79
+ vi.mock("../engine/engines/pi/temp-prompt.ts", () => ({
80
80
  writePromptToTempFile: vi.fn(async (agent: string) => {
81
81
  const safeName = agent.replace(/[^\w.-]+/g, "_");
82
82
  return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
@@ -90,7 +90,7 @@ vi.mock("../lifecycle-manager.ts", async (importOriginal) => {
90
90
  });
91
91
 
92
92
  import { DEFAULT_IDLE_TIMEOUT_MS, hasIdleTimer, _resetLifecycleState } from "../lifecycle-manager.ts";
93
- import { runSpawn } from "../session-runner.ts";
93
+ import { runSpawn } from "../engine/engines/pi/session-runner.ts";
94
94
  import {
95
95
  emitStdoutLine,
96
96
  type FakeChild,
@@ -199,8 +199,9 @@ describe("T4② armIdleTimer fail-fast falls back to DEFAULT + warn", () => {
199
199
  );
200
200
 
201
201
  const record = makeRecord("sa-settled-fallback");
202
- record.chatMode = true;
203
- record.idleTimeoutMs = 600_000;
202
+ const mutable = record as { chatMode: boolean; idleTimeoutMs: number };
203
+ mutable.chatMode = true;
204
+ mutable.idleTimeoutMs = 600_000;
204
205
  const promise = runSpawn(record, "Task: settled fallback", makeOpts(), makeCtx());
205
206
  await waitForSpawn();
206
207
  const child = lastSpawnedChild();