@zhushanwen/subagent-core 0.2.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (347) hide show
  1. package/README.md +45 -2
  2. package/agents/analyst.md +60 -0
  3. package/agents/coder.md +69 -0
  4. package/agents/debugger.md +66 -0
  5. package/agents/doc-reviewer.md +49 -0
  6. package/agents/explorer.md +63 -0
  7. package/agents/general-purpose.md +32 -0
  8. package/agents/orchestrator.md +61 -0
  9. package/agents/planner.md +53 -0
  10. package/agents/researcher.md +67 -0
  11. package/agents/reviewer.md +73 -0
  12. package/dist/chunk-43ONBFZX.js +240 -0
  13. package/dist/chunk-APZY4IME.js +27 -0
  14. package/dist/chunk-V3VHZ2VX.js +59 -0
  15. package/dist/execution/engine/engines/zcode/constants.cjs +62 -9
  16. package/dist/execution/engine/engines/zcode/constants.d.cts +116 -12
  17. package/dist/execution/engine/engines/zcode/constants.d.ts +116 -12
  18. package/dist/execution/engine/engines/zcode/constants.js +39 -7
  19. package/dist/execution/engine/engines/zcode/reader.d.cts +2 -1
  20. package/dist/execution/engine/engines/zcode/reader.d.ts +2 -1
  21. package/dist/execution/engine/engines/zcode/reader.js +4 -234
  22. package/dist/execution/engine/paths.js +7 -19
  23. package/dist/index.cjs +18234 -1277
  24. package/dist/index.d.cts +5646 -673
  25. package/dist/index.d.ts +5646 -673
  26. package/dist/index.js +17828 -1277
  27. package/dist/{types-BxyAidGf.d.cts → types-Dv4QhSJ_.d.cts} +678 -112
  28. package/dist/{types-BxyAidGf.d.ts → types-Dv4QhSJ_.d.ts} +678 -112
  29. package/package.json +7 -4
  30. package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +0 -1
  31. package/src/__tests__/append-system-prompt-assembly.test.ts +8 -7
  32. package/src/__tests__/fr4-get-state-handshake.test.ts +2 -2
  33. package/src/__tests__/m2-append-content-probe.test.ts +0 -1
  34. package/src/__tests__/manifest-store.test.ts +10 -9
  35. package/src/__tests__/record-store-cache.test.ts +0 -2
  36. package/src/__tests__/record-store-index.test.ts +1 -2
  37. package/src/__tests__/review-fix-loop-script.test.ts +93 -3
  38. package/src/__tests__/review-fix-loop-utils.test.ts +31 -10
  39. package/src/__tests__/robustness-low-batch1.test.ts +6 -5
  40. package/src/__tests__/robustness-medium-batch1.test.ts +2 -2
  41. package/src/__tests__/robustness-medium-batch2.test.ts +17 -6
  42. package/src/__tests__/robustness-medium-batch4.test.ts +2 -2
  43. package/src/__tests__/session-runner.test.ts +1 -1
  44. package/src/__tests__/smoke.test.ts +9 -2
  45. package/src/core/__tests__/host-services.test.ts +2 -2
  46. package/src/core/__tests__/logger.test.ts +1 -1
  47. package/src/core/error-message.ts +9 -0
  48. package/src/core/host-services.ts +21 -5
  49. package/src/core/notify-ports.ts +18 -5
  50. package/src/execution/__tests__/agent-profile.test.ts +232 -0
  51. package/src/execution/__tests__/agent-registry.test.ts +370 -0
  52. package/src/execution/__tests__/agent-result-mapper.test.ts +18 -2
  53. package/src/execution/__tests__/agents-assembly.test.ts +219 -0
  54. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +6 -6
  55. package/src/execution/__tests__/chat-engine-routing.test.ts +136 -30
  56. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +5 -4
  57. package/src/execution/__tests__/cold-resurrect.test.ts +283 -0
  58. package/src/execution/__tests__/create-concurrency-pool.test.ts +194 -0
  59. package/src/execution/__tests__/delivery-methods.test.ts +77 -62
  60. package/src/execution/__tests__/descendant-sweep-guards.test.ts +220 -0
  61. package/src/execution/__tests__/descendant-sweep.test.ts +269 -0
  62. package/src/execution/__tests__/dialog-queue.test.ts +199 -1
  63. package/src/execution/__tests__/engine-model-validation.test.ts +741 -0
  64. package/src/execution/__tests__/epipe-fallback.test.ts +31 -24
  65. package/src/execution/__tests__/execute-and-await-worktree.test.ts +21 -91
  66. package/src/execution/__tests__/execute-nesting.test.ts +34 -188
  67. package/src/execution/__tests__/execution-record.test.ts +217 -60
  68. package/src/execution/__tests__/execution-runtime-face.test.ts +272 -0
  69. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +3 -3
  70. package/src/execution/__tests__/finalize-record.test.ts +278 -16
  71. package/src/execution/__tests__/gc-timer.test.ts +57 -2
  72. package/src/execution/__tests__/get-record-for-action-restart.test.ts +14 -12
  73. package/src/execution/__tests__/get-state-handshake.test.ts +127 -0
  74. package/src/execution/__tests__/helpers/mock-extension-api.ts +1 -1
  75. package/src/execution/__tests__/helpers/session-runner-mocks.ts +113 -0
  76. package/src/execution/__tests__/helpers/spawn-mock.ts +150 -3
  77. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +137 -0
  78. package/src/execution/__tests__/keep-alive-no-progress.test.ts +280 -0
  79. package/src/execution/__tests__/kill-all-escalation.test.ts +196 -0
  80. package/src/execution/__tests__/lifecycle-manager-idle-timer-identity.test.ts +117 -0
  81. package/src/execution/__tests__/lifecycle-manager.test.ts +39 -0
  82. package/src/execution/__tests__/lifecycle-predicates.test.ts +1 -2
  83. package/src/execution/__tests__/manifest-parentid.test.ts +4 -4
  84. package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +99 -0
  85. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +113 -0
  86. package/src/execution/__tests__/model-resolver.test.ts +2 -2
  87. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +23 -121
  88. package/src/execution/__tests__/nested-visibility.test.ts +2 -0
  89. package/src/execution/__tests__/notify-ledger.test.ts +1249 -0
  90. package/src/execution/__tests__/output-collector.test.ts +117 -6
  91. package/src/execution/__tests__/pi-invocation.test.ts +1 -1
  92. package/src/execution/__tests__/record-store-orphan-revive.test.ts +159 -0
  93. package/src/execution/__tests__/record-store.test.ts +89 -5
  94. package/src/execution/__tests__/records-cwd-isolation.test.ts +2 -2
  95. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +29 -132
  96. package/src/execution/__tests__/recursive-visibility-env.test.ts +55 -179
  97. package/src/execution/__tests__/rpc-mode.test.ts +2 -2
  98. package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +387 -0
  99. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +19 -9
  100. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +5 -4
  101. package/src/execution/__tests__/run-spawn-edges.test.ts +418 -26
  102. package/src/execution/__tests__/run-spawn-integration.test.ts +3 -3
  103. package/src/execution/__tests__/run-spawn-resume.test.ts +2 -2
  104. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +2 -2
  105. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +16 -8
  106. package/src/execution/__tests__/service-kill-escalation.test.ts +91 -0
  107. package/src/execution/__tests__/session-context-resolver.test.ts +0 -2
  108. package/src/execution/__tests__/session-file-gc.test.ts +35 -0
  109. package/src/execution/__tests__/session-pending.test.ts +471 -0
  110. package/src/execution/__tests__/session-reconstructor.test.ts +366 -15
  111. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +110 -0
  112. package/src/execution/__tests__/session-runner-close-prune.test.ts +181 -0
  113. package/src/execution/__tests__/session-runner-dispatch.test.ts +489 -0
  114. package/src/execution/__tests__/session-runner-epipe.test.ts +4 -3
  115. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +227 -0
  116. package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +290 -0
  117. package/src/execution/__tests__/session-runner-schema-env.test.ts +7 -5
  118. package/src/execution/__tests__/settled-watchdog.test.ts +524 -0
  119. package/src/execution/__tests__/spawn-args.test.ts +11 -10
  120. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +26 -23
  121. package/src/execution/__tests__/spawn-event-adapter.test.ts +1 -1
  122. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +5 -4
  123. package/src/execution/__tests__/spawned-children.test.ts +155 -11
  124. package/src/execution/__tests__/start-sync-model-guard.test.ts +4 -4
  125. package/src/execution/__tests__/stdin-writer.test.ts +6 -4
  126. package/src/execution/__tests__/stream-sink-retirement.test.ts +6 -6
  127. package/src/execution/__tests__/subagent-actions-core.test.ts +1017 -0
  128. package/src/execution/__tests__/subagent-service-message-close.test.ts +99 -35
  129. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +174 -0
  130. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +271 -0
  131. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +33 -29
  132. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +341 -0
  133. package/src/execution/__tests__/subagent-service.test.ts +37 -30
  134. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +88 -12
  135. package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +75 -0
  136. package/src/execution/__tests__/subprocess-agent-runner.test.ts +22 -20
  137. package/src/execution/__tests__/temp-prompt.test.ts +1 -1
  138. package/src/execution/__tests__/timeout-integration.test.ts +13 -12
  139. package/src/execution/__tests__/turn-limiter-semantics.test.ts +1 -1
  140. package/src/execution/__tests__/turn-limiter.test.ts +1 -1
  141. package/src/execution/__tests__/ui-request-handler-factory.test.ts +107 -7
  142. package/src/execution/__tests__/ui-request-handler.test.ts +4 -3
  143. package/src/execution/__tests__/ui-request-queue.test.ts +167 -2
  144. package/src/execution/__tests__/worktree-git-ops.test.ts +571 -0
  145. package/src/execution/__tests__/worktree-manager.test.ts +14 -5
  146. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +4 -3
  147. package/src/execution/__tests__/worktree-reconcile-aging.test.ts +204 -0
  148. package/src/execution/agent-registry.ts +298 -30
  149. package/src/execution/agent-result-mapper.ts +3 -0
  150. package/src/execution/agents-assembly.ts +88 -0
  151. package/src/execution/cold-resurrect.ts +199 -0
  152. package/src/execution/concurrency-pool.ts +71 -9
  153. package/src/execution/dialog-queue.ts +101 -3
  154. package/src/execution/engine/__tests__/common/capability-gate.test.ts +91 -0
  155. package/src/execution/engine/__tests__/common/data-dir.test.ts +1 -1
  156. package/src/execution/engine/__tests__/common/journal-wiring.test.ts +84 -0
  157. package/src/execution/engine/__tests__/common/kill-chain.test.ts +34 -7
  158. package/src/execution/engine/__tests__/common/persona-router.test.ts +4 -5
  159. package/src/execution/engine/__tests__/common/pool-manager.test.ts +211 -51
  160. package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +149 -0
  161. package/src/execution/engine/__tests__/common/session-view-service.test.ts +499 -0
  162. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +56 -99
  163. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +88 -15
  164. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +33 -7
  165. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +1 -1
  166. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +75 -54
  167. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +128 -0
  168. package/src/execution/engine/__tests__/engine-discovery.test.ts +15 -3
  169. package/src/execution/engine/__tests__/model-prompt.test.ts +15 -3
  170. package/src/execution/engine/__tests__/registry.test.ts +94 -3
  171. package/src/execution/engine/__tests__/routing.test.ts +115 -6
  172. package/src/execution/engine/common/capability-gate.ts +92 -0
  173. package/src/execution/engine/common/journal-wiring.ts +97 -0
  174. package/src/execution/engine/common/kill-chain.ts +42 -10
  175. package/src/execution/engine/common/nesting-guard.ts +66 -0
  176. package/src/execution/engine/common/persona-router.ts +18 -8
  177. package/src/execution/engine/common/pool-manager.ts +332 -78
  178. package/src/execution/engine/common/session-view-service.ts +506 -0
  179. package/src/execution/engine/common/session-view-types.ts +125 -0
  180. package/src/execution/engine/engine-discovery.ts +12 -16
  181. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +174 -28
  182. package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +257 -0
  183. package/src/execution/{argv-mirror.ts → engine/engines/pi/argv-mirror.ts} +43 -21
  184. package/src/execution/{get-state-handshake.ts → engine/engines/pi/get-state-handshake.ts} +86 -11
  185. package/src/execution/{output-collector.ts → engine/engines/pi/output-collector.ts} +108 -19
  186. package/src/execution/engine/engines/pi/pi-engine.ts +434 -38
  187. package/src/execution/{pi-invocation.ts → engine/engines/pi/pi-invocation.ts} +2 -2
  188. package/src/execution/engine/engines/pi/reader.ts +11 -0
  189. package/src/execution/engine/engines/pi/registration.ts +4 -2
  190. package/src/execution/engine/engines/pi/session-runner.ts +2840 -0
  191. package/src/execution/{spawn-event-adapter.ts → engine/engines/pi/spawn-event-adapter.ts} +75 -34
  192. package/src/execution/{stdin-writer.ts → engine/engines/pi/stdin-writer.ts} +10 -9
  193. package/src/execution/{temp-prompt.ts → engine/engines/pi/temp-prompt.ts} +2 -2
  194. package/src/execution/{turn-limiter.ts → engine/engines/pi/turn-limiter.ts} +1 -1
  195. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +301 -0
  196. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +36 -0
  197. package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +321 -0
  198. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +473 -0
  199. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +7 -210
  200. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +6 -110
  201. package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +247 -0
  202. package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +451 -0
  203. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +792 -0
  204. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +782 -0
  205. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +168 -0
  206. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +210 -0
  207. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +353 -0
  208. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +282 -0
  209. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +372 -0
  210. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +65 -47
  211. package/src/execution/engine/engines/zcode/appserver-launcher.ts +167 -0
  212. package/src/execution/engine/engines/zcode/connection.ts +598 -0
  213. package/src/execution/engine/engines/zcode/constants.ts +147 -14
  214. package/src/execution/engine/engines/zcode/golden-sample.ts +42 -34
  215. package/src/execution/engine/engines/zcode/parser.ts +12 -346
  216. package/src/execution/engine/engines/zcode/preparer.ts +22 -150
  217. package/src/execution/engine/engines/zcode/session-channel.ts +968 -0
  218. package/src/execution/engine/engines/zcode/zcode-engine.ts +1023 -250
  219. package/src/execution/engine/host-task-spec.ts +32 -36
  220. package/src/execution/engine/model-validation.ts +177 -0
  221. package/src/execution/engine/port.ts +65 -7
  222. package/src/execution/engine/registry.ts +53 -0
  223. package/src/execution/engine/routing.ts +75 -1
  224. package/src/execution/engine/types.ts +30 -103
  225. package/src/execution/execution-record.ts +222 -144
  226. package/src/execution/finalize-record.ts +181 -85
  227. package/src/execution/idle-gc.ts +28 -0
  228. package/src/execution/lifecycle-manager.ts +27 -3
  229. package/src/execution/lifecycle-predicates.ts +1 -1
  230. package/src/execution/manifest-store.ts +53 -62
  231. package/src/execution/notifier.ts +10 -10
  232. package/src/execution/notify-host.ts +212 -0
  233. package/src/execution/notify-ledger.ts +117 -16
  234. package/src/execution/record-entry.ts +8 -2
  235. package/src/execution/record-store.ts +148 -88
  236. package/src/execution/round-settlement.ts +93 -0
  237. package/src/execution/session-file-gc.ts +96 -62
  238. package/src/execution/session-pending.ts +213 -62
  239. package/src/execution/session-reconstructor.ts +256 -143
  240. package/src/execution/sessions-index.ts +127 -102
  241. package/src/execution/settled-watchdog.ts +311 -0
  242. package/src/execution/subagent-actions-core.ts +686 -0
  243. package/src/execution/subagent-service.ts +1106 -907
  244. package/src/execution/subprocess-agent-runner.ts +144 -97
  245. package/src/execution/types.ts +9 -0
  246. package/src/execution/ui-request-handler-factory.ts +126 -75
  247. package/src/execution/ui-request-queue.ts +50 -19
  248. package/src/execution/worktree-git-ops.ts +397 -0
  249. package/src/execution/worktree-manager.ts +72 -10
  250. package/src/execution/worktree-registry.ts +6 -12
  251. package/src/index.ts +542 -9
  252. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +42 -3
  253. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +3 -5
  254. package/src/orchestration/__tests__/agent-call-stream.test.ts +2 -2
  255. package/src/orchestration/__tests__/args-meta.test.ts +358 -0
  256. package/src/orchestration/__tests__/args-validator.test.ts +14 -0
  257. package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +312 -0
  258. package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +384 -0
  259. package/src/orchestration/__tests__/execute-agent-call.test.ts +151 -11
  260. package/src/orchestration/__tests__/file-run-store-prune.test.ts +168 -0
  261. package/src/orchestration/__tests__/file-run-store-throttle.test.ts +168 -0
  262. package/src/orchestration/__tests__/file-run-store.test.ts +390 -0
  263. package/src/orchestration/__tests__/helpers/flush-microtasks.ts +13 -0
  264. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +34 -0
  265. package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +357 -0
  266. package/src/orchestration/__tests__/lifecycle-recover-crashed.test.ts +289 -0
  267. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +3 -3
  268. package/src/orchestration/__tests__/lifecycle.test.ts +84 -5
  269. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +1 -1
  270. package/src/orchestration/__tests__/run-snapshot.test.ts +356 -0
  271. package/src/orchestration/__tests__/script-generate.test.ts +314 -0
  272. package/src/orchestration/__tests__/script-lint.test.ts +17 -0
  273. package/src/orchestration/__tests__/test-mocks.ts +2 -0
  274. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +7 -6
  275. package/src/orchestration/__tests__/worker-message-pump-finalize-run.test.ts +229 -0
  276. package/src/orchestration/__tests__/{error-recovery-handlers.test.ts → worker-message-pump-handlers.test.ts} +28 -27
  277. package/src/orchestration/__tests__/{error-recovery-postmessage-defense.test.ts → worker-message-pump-postmessage-defense.test.ts} +8 -8
  278. package/src/orchestration/__tests__/{error-recovery-serialize-failed-result.test.ts → worker-message-pump-serialize-failed-result.test.ts} +2 -2
  279. package/src/orchestration/__tests__/{error-recovery-workflow-call.test.ts → worker-message-pump-workflow-call.test.ts} +1 -1
  280. package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +275 -0
  281. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +78 -1
  282. package/src/orchestration/__tests__/worker-script-builder.test.ts +1 -1
  283. package/src/orchestration/__tests__/workflow-files.test.ts +186 -0
  284. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +1 -1
  285. package/src/orchestration/__tests__/workflow-run-summary.test.ts +119 -0
  286. package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +124 -0
  287. package/src/orchestration/agent-opts-resolver.ts +8 -8
  288. package/src/orchestration/args-meta.ts +198 -0
  289. package/src/orchestration/args-validator.ts +53 -31
  290. package/src/orchestration/execute-agent-call.ts +25 -79
  291. package/src/orchestration/file-run-store.ts +327 -0
  292. package/src/orchestration/launcher.ts +160 -104
  293. package/src/orchestration/lifecycle.ts +371 -97
  294. package/src/orchestration/models/agent-call.ts +7 -7
  295. package/src/orchestration/models/budget.ts +5 -5
  296. package/src/orchestration/models/ports.ts +4 -4
  297. package/src/orchestration/models/run-runtime.ts +15 -15
  298. package/src/orchestration/models/trace.ts +9 -9
  299. package/src/orchestration/models/types.ts +111 -22
  300. package/src/orchestration/models/workflow-run.ts +28 -28
  301. package/src/orchestration/models/workflow-script.ts +4 -4
  302. package/src/orchestration/run-snapshot.ts +266 -0
  303. package/src/orchestration/script-generate.ts +154 -0
  304. package/src/orchestration/script-lint.ts +123 -90
  305. package/src/orchestration/worker-handle.ts +10 -10
  306. package/src/orchestration/worker-host.ts +10 -10
  307. package/src/orchestration/{error-recovery.ts → worker-message-pump.ts} +498 -221
  308. package/src/orchestration/worker-script-builder.ts +401 -346
  309. package/src/orchestration/workflow-files.ts +37 -11
  310. package/src/orchestration/workflow-run-summary.ts +69 -0
  311. package/src/orchestration/workflow-script-registry-impl.ts +31 -9
  312. package/src/shared/__tests__/agent-ref.test.ts +209 -2
  313. package/src/shared/__tests__/atomic-write.test.ts +267 -0
  314. package/src/shared/__tests__/bounded-serialize.test.ts +236 -0
  315. package/src/shared/__tests__/injection-render.test.ts +518 -0
  316. package/src/shared/__tests__/meta-parser.test.ts +151 -0
  317. package/src/shared/__tests__/resource-discovery-host-roots.test.ts +474 -0
  318. package/src/shared/__tests__/resource-discovery.test.ts +261 -217
  319. package/src/shared/agent-ref.ts +143 -2
  320. package/src/shared/atomic-write.ts +323 -0
  321. package/src/shared/bounded-serialize.ts +154 -0
  322. package/src/shared/injection-render.ts +279 -0
  323. package/src/shared/meta-parser.ts +182 -68
  324. package/src/shared/model-ref.ts +15 -2
  325. package/src/shared/resource-discovery.ts +97 -204
  326. package/src/shared/resource-meta.ts +14 -0
  327. package/src/shared/schema-jsonify.ts +1 -1
  328. package/src/shared/xml-injection.ts +9 -9
  329. package/workflows/README.md +9 -9
  330. package/workflows/chain.js +4 -2
  331. package/workflows/map-reduce.js +5 -3
  332. package/workflows/parallel.js +5 -3
  333. package/workflows/review-fix-loop-utils.cjs +165 -92
  334. package/workflows/review-fix-loop-utils.d.cts +287 -0
  335. package/workflows/review-fix-loop.js +18 -7
  336. package/workflows/scatter-gather.js +4 -2
  337. package/dist/chunk-3VOERJPJ.js +0 -22
  338. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
  339. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  340. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  341. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  342. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  343. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -580
  344. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  345. package/src/execution/execute-options-mapper.ts +0 -115
  346. package/src/execution/session-runner.ts +0 -1789
  347. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -288
@@ -1,9 +1,10 @@
1
1
  // src/execution/__tests__/epipe-fallback.test.ts
2
2
  //
3
- // deliverMessage EPIPE 兜底集成测试——热路径 stdin 写入 EPIPE 时自动转冷路径 resume + 消息重放。
3
+ // deliverChatMessage EPIPE 兜底集成测试——热路径 stdin 写入 EPIPE 时自动转冷路径续轮 + 消息重放
4
+ //(D2 后投递协议知识在 PiEngine.deliverPrompt,编排层经 engine.interactRecord 进入)。
4
5
  //
5
6
  // 场景覆盖:
6
- // 1. 首次 EPIPE → 自动转冷路径 resumeRound(runSpawn 收到 resume 参数 + 原消息)
7
+ // 1. 首次 EPIPE → 自动转冷路径续轮(runSpawn 收到 resume 参数 + 原消息)
7
8
  // 2. 连续 2 次 EPIPE → throw 含恢复指引(防无限循环)
8
9
  // 3. 热路径成功写入后清零 EPIPE 计数(正常路径不受影响)
9
10
 
@@ -22,17 +23,19 @@ vi.mock("../../core/logger.ts", () => ({ getLogger: () => loggerMock }));
22
23
 
23
24
  // mock session-runner:runSpawn 受控 + killAllSpawnedChildren 空实现;
24
25
  // getChildByRecord/spawnedChildren 提供真实 Map 语义。
25
- vi.mock("../session-runner.ts", () => {
26
+ vi.mock("../engine/engines/pi/session-runner.ts", () => {
26
27
  const spawnedChildren = new Map<string, unknown>();
27
28
  return {
28
29
  runSpawn: vi.fn(),
29
30
  killAllSpawnedChildren: vi.fn(),
31
+ registerSpawnedChildForRecord: vi.fn(),
32
+ killRecordChildWithEscalation: vi.fn(),
30
33
  spawnedChildren,
31
34
  getChildByRecord: (id: string): unknown => spawnedChildren.get(id),
32
35
  };
33
36
  });
34
37
 
35
- import { runSpawn, spawnedChildren } from "../session-runner.ts";
38
+ import { runSpawn, spawnedChildren } from "../engine/engines/pi/session-runner.ts";
36
39
  import * as lifecycle from "../lifecycle-manager.ts";
37
40
  import { createRecord } from "../execution-record.ts";
38
41
  import { ModelConfigService } from "../model-config-service.ts";
@@ -48,11 +51,15 @@ function makeTmpAgentDir(): string {
48
51
  }
49
52
 
50
53
  function makePi(): PiLike & {
51
- appendEntry: ReturnType<typeof vi.fn>;
52
- events: { emit: ReturnType<typeof vi.fn> };
53
- sendMessage: ReturnType<typeof vi.fn>;
54
+ appendEntry: ReturnType<typeof vi.fn<(customType: string, data?: unknown) => void>>;
55
+ events: { emit: ReturnType<typeof vi.fn<(channel: string, data: unknown) => void>> };
56
+ sendMessage: ReturnType<typeof vi.fn<(message: Parameters<PiLike["sendMessage"]>[0], options?: Parameters<PiLike["sendMessage"]>[1]) => void>>;
54
57
  } {
55
- return { appendEntry: vi.fn(), events: { emit: vi.fn() }, sendMessage: vi.fn() };
58
+ return {
59
+ appendEntry: vi.fn((customType: string, data?: unknown) => {}),
60
+ events: { emit: vi.fn((channel: string, data: unknown) => {}) },
61
+ sendMessage: vi.fn(() => {}),
62
+ };
56
63
  }
57
64
 
58
65
  function makeResult(success: boolean): AgentResult {
@@ -108,17 +115,17 @@ function makeNormalChild(): ChildProcess {
108
115
  }
109
116
 
110
117
  // ============================================================
111
- // deliverMessage EPIPE 兜底
118
+ // deliverChatMessage EPIPE 兜底
112
119
  // ============================================================
113
120
 
114
- describe("deliverMessage EPIPE 兜底(热路径 stdin EPIPE → 冷路径 resume)", () => {
121
+ describe("deliverChatMessage EPIPE 兜底(热路径 stdin EPIPE → 冷路径续轮)", () => {
115
122
  let agentDir: string;
116
123
  let service: SubagentService;
117
124
  let record: ExecutionRecord;
118
125
 
119
126
  beforeEach(() => {
120
127
  agentDir = makeTmpAgentDir();
121
- const modelService = new ModelConfigService({ agentDir });
128
+ const modelService = new ModelConfigService({ agentDir, cwd: agentDir });
122
129
  service = new SubagentService({ cwd: agentDir, modelService });
123
130
  service.initSession({ pi: makePi(), sessionId: "root-session" });
124
131
  record = makeIdleRecord();
@@ -133,7 +140,7 @@ describe("deliverMessage EPIPE 兜底(热路径 stdin EPIPE → 冷路径 resu
133
140
  service.dispose();
134
141
  spawnedChildren.clear();
135
142
  lifecycle._resetLifecycleState();
136
- fs.rmSync(agentDir, { recursive: true, force: true });
143
+ fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
137
144
  });
138
145
 
139
146
  it("首次 EPIPE → 自动转冷路径 resumeRound + 原消息重放", async () => {
@@ -141,13 +148,13 @@ describe("deliverMessage EPIPE 兜底(热路径 stdin EPIPE → 冷路径 resu
141
148
  // 注册 EPIPE child(热路径会命中但 stdin.write 抛 EPIPE)
142
149
  spawnedChildren.set(record.id, makeEpipeChild());
143
150
 
144
- // deliverMessage throw(首次 EPIPE 走 resume 兜底)
145
- await expect(service.deliverMessage(record, "msg after epipe", false)).resolves.toBeUndefined();
151
+ // 投递不 throw(首次 EPIPE 走续轮兜底)
152
+ await expect(service.chatActions.deliverChatMessage(record, "msg after epipe", false)).resolves.toBeUndefined();
146
153
 
147
154
  // spawnedChildren 中的死进程条目已清理(EPIPE catch 块 delete)
148
155
  expect(spawnedChildren.has(record.id)).toBe(false);
149
156
 
150
- // resumeRound 被触发(冷路径),runSpawn 收到 resume 参数 + 原消息
157
+ // 冷路径续轮被触发,runSpawn 收到 resume 参数 + 原消息
151
158
  await vi.waitFor(() => expect(mockRunSpawn).toHaveBeenCalledTimes(1));
152
159
  const call = mockRunSpawn.mock.calls[0]!;
153
160
  expect(call[1]).toBe("msg after epipe");
@@ -172,7 +179,7 @@ describe("deliverMessage EPIPE 兜底(热路径 stdin EPIPE → 冷路径 resu
172
179
  spawnedChildren.set(record.id, makeEpipeChild());
173
180
 
174
181
  // 第 1 次:EPIPE → resume(不 throw)
175
- await service.deliverMessage(record, "first epipe", false);
182
+ await service.chatActions.deliverChatMessage(record, "first epipe", false);
176
183
  await vi.waitFor(() => expect(mockRunSpawn).toHaveBeenCalledTimes(1));
177
184
 
178
185
  // resume 完成后 record 回 running(v4 B-1:finalizeRoundToIdle 设 running,旧 idle 折入)
@@ -182,10 +189,10 @@ describe("deliverMessage EPIPE 兜底(热路径 stdin EPIPE → 冷路径 resu
182
189
  spawnedChildren.set(record.id, makeEpipeChild());
183
190
 
184
191
  // 第 2 次 EPIPE → throw 含恢复指引(连续 2 次触发 exhaustion)
185
- // 注意:第二 EPIPE 时 count 已为 2,catch 块立即 throw(不经过 resumeRound)。
192
+ // 注意:第二 EPIPE 时 count 已为 2,catch 块立即 throw(不经过续轮)。
186
193
  let thrown: unknown;
187
194
  try {
188
- await service.deliverMessage(record, "second epipe", false);
195
+ await service.chatActions.deliverChatMessage(record, "second epipe", false);
189
196
  } catch (err) {
190
197
  thrown = err;
191
198
  }
@@ -201,13 +208,13 @@ describe("deliverMessage EPIPE 兜底(热路径 stdin EPIPE → 冷路径 resu
201
208
 
202
209
  // 第 1 轮:EPIPE → resume
203
210
  spawnedChildren.set(record.id, makeEpipeChild());
204
- await service.deliverMessage(record, "epipe msg", false);
211
+ await service.chatActions.deliverChatMessage(record, "epipe msg", false);
205
212
  await vi.waitFor(() => expect(mockRunSpawn).toHaveBeenCalledTimes(1));
206
213
  await vi.waitFor(() => expect(record.status).toBe("running"));
207
214
 
208
215
  // 第 2 轮:正常 child,成功写入(清零 EPIPE 计数)
209
216
  spawnedChildren.set(record.id, makeNormalChild());
210
- await service.deliverMessage(record, "normal msg", true);
217
+ await service.chatActions.deliverChatMessage(record, "normal msg", true);
211
218
  // 不 throw,status=running(热路径成功)
212
219
  expect(record.status).toBe("running");
213
220
 
@@ -215,7 +222,7 @@ describe("deliverMessage EPIPE 兜底(热路径 stdin EPIPE → 冷路径 resu
215
222
  // 需要先让 record 回 running(等待续聊态)
216
223
  record.status = "running";
217
224
  spawnedChildren.set(record.id, makeEpipeChild());
218
- await expect(service.deliverMessage(record, "after reset", false)).resolves.toBeUndefined();
225
+ await expect(service.chatActions.deliverChatMessage(record, "after reset", false)).resolves.toBeUndefined();
219
226
  });
220
227
 
221
228
  it("EPIPE 兜底与冷路径共用 in-flight 守卫:resume 在途时 EPIPE 兜底不再二次 spawn(review round2 MF1)", async () => {
@@ -224,14 +231,14 @@ describe("deliverMessage EPIPE 兜底(热路径 stdin EPIPE → 冷路径 resu
224
231
  mockRunSpawn.mockImplementationOnce(
225
232
  () => new Promise<AgentResult>((res) => { releaseFirst = res; }),
226
233
  );
227
- await service.deliverMessage(record, "cold path first", false);
234
+ await service.chatActions.deliverChatMessage(record, "cold path first", false);
228
235
  await vi.waitFor(() => expect(mockRunSpawn).toHaveBeenCalledTimes(1));
229
236
 
230
- // 注册 EPIPE child → deliverMessage 走热路径 → stdin EPIPE → 兜底调 resumeRound
237
+ // 注册 EPIPE child → 投递走热路径 → stdin EPIPE → 兜底调冷路径续轮
231
238
  // → in-flight 命中 → throw(不再走第二次 spawn。EPIPE 计数仍 +1 但未达阈值 2,
232
239
  // exhaustion 分支不触发;throw 由兜底路径的 resumeRound 守卫抛出)
233
240
  spawnedChildren.set(record.id, makeEpipeChild());
234
- await expect(service.deliverMessage(record, "epipe during resume", false)).rejects.toThrow(
241
+ await expect(service.chatActions.deliverChatMessage(record, "epipe during resume", false)).rejects.toThrow(
235
242
  /already starting a new round/,
236
243
  );
237
244
  expect(mockRunSpawn).toHaveBeenCalledTimes(1);
@@ -8,101 +8,31 @@
8
8
  // ── mock 策略 ──
9
9
  //
10
10
  // 复用 execute-nesting.test.ts 的 spawn / node:fs / manifest-store / temp-prompt / alive-store
11
- // / finalized-marker mock 范式(见该文件头部详细注释)。本文件 **不驱动 FakeChild 完成**——
12
- // 被测的两个分支都在 runSpawn 之前抛/收尾(worktree create 在步骤 2.5,runSpawn 在步骤 5),
13
- // 因此 spawn 即使被调也无人驱动,测试在抛错后立即断言即可结束。
11
+ // / finalized-marker mock 范式,已收敛到 ./helpers/subagent-service-mocks.ts 单源(四文件
12
+ // 共享)。本文件 **不驱动 FakeChild 完成**——被测的两个分支都在 runSpawn 之前抛/收尾
13
+ // (worktree create 在步骤 2.5,runSpawn 在步骤 5),因此 spawn 即使被调也无人驱动,
14
+ // 测试在抛错后立即断言即可结束。
14
15
  //
15
16
  // worktreeManager 是 SubagentService 构造时 new 出的私有字段(WorktreeManager 实例,非模块)。
16
17
  // 测试 2 用 vi.spyOn(Reflect.get(service, "worktreeManager"), "create") 注入抛错,无需模块级 mock。
17
18
 
18
- import { PassThrough } from "node:stream";
19
-
20
19
  import { afterEach, describe, expect, it, vi } from "vitest";
21
20
 
22
- // ── mock modules(与 execute-nesting.test.ts 同范式)──
23
-
24
- vi.mock("node:child_process", async () => {
25
- const { EventEmitter } = await import("node:events");
26
- const { PassThrough } = await import("node:stream");
27
-
28
- class FakeChild extends EventEmitter {
29
- pid = 12345;
30
- stdout = new PassThrough();
31
- stderr = new PassThrough();
32
- killed = false;
33
- killSignal: string | undefined;
34
- kill(sig?: string): boolean {
35
- this.killed = true;
36
- this.killSignal = sig;
37
- return true;
38
- }
39
- }
40
-
41
- return {
42
- spawn: vi.fn(() => new FakeChild()),
43
- // buildEnvBlock 的 git branch 调用(execFile 异步):默认 err-first 兜底 → catch → branch=""
44
- execFile: vi.fn(
45
- (
46
- _cmd: string,
47
- _args: readonly string[],
48
- _opts: unknown,
49
- cb: (err: Error | null, stdout?: string, stderr?: string) => void,
50
- ) => cb(new Error("execFile not configured in this test")),
51
- ),
52
- };
53
- });
54
-
55
- vi.mock("node:fs", async () => {
56
- const actual = await import("node:fs");
57
- return {
58
- default: {
59
- ...actual,
60
- mkdirSync: vi.fn(),
61
- existsSync: vi.fn(() => false),
62
- appendFileSync: vi.fn(),
63
- writeFileSync: vi.fn(),
64
- readdirSync: vi.fn(() => []),
65
- },
66
- mkdirSync: vi.fn(),
67
- existsSync: vi.fn(() => false),
68
- appendFileSync: vi.fn(),
69
- writeFileSync: vi.fn(),
70
- readdirSync: vi.fn(() => []),
71
- promises: actual.promises,
72
- };
73
- });
74
-
75
- vi.mock("../alive-store.ts", async (importOriginal) => {
76
- const actual = await importOriginal<typeof import("../alive-store.ts")>();
77
- return {
78
- ...actual,
79
- writeAliveMarker: vi.fn(),
80
- removeAliveMarker: vi.fn(),
81
- };
82
- });
83
-
84
- vi.mock("../finalized-marker.ts", () => ({
85
- writeFinalized: vi.fn(),
86
- readFinalized: vi.fn(() => false),
87
- }));
88
-
89
- vi.mock("../manifest-store.ts", () => {
90
- class FakeManifestStore {
91
- writeManifest = vi.fn(async () => {});
92
- readManifest = vi.fn(async () => null);
93
- listAllSync = vi.fn(() => []);
94
- recoverTmpFiles = vi.fn(async () => []);
95
- }
96
- return { ManifestStore: FakeManifestStore };
97
- });
98
-
99
- vi.mock("../temp-prompt.ts", () => ({
100
- writePromptToTempFile: vi.fn(async (agent: string) => {
101
- const safeName = agent.replace(/[^\w.-]+/g, "_");
102
- return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
103
- }),
104
- cleanupTempPrompt: vi.fn(async () => {}),
105
- }));
21
+ import {
22
+ aliveStoreModule,
23
+ childProcessModule,
24
+ finalizedMarkerModule,
25
+ fsSyncModule,
26
+ manifestStoreModule,
27
+ tempPromptModule,
28
+ } from "./helpers/subagent-service-mocks.ts";
29
+
30
+ vi.mock("node:child_process", () => childProcessModule());
31
+ vi.mock("node:fs", async (importOriginal) => fsSyncModule(await importOriginal<typeof import("node:fs")>()));
32
+ vi.mock("../alive-store.ts", async (importOriginal) => aliveStoreModule(await importOriginal<typeof import("../alive-store.ts")>()));
33
+ vi.mock("../finalized-marker.ts", () => finalizedMarkerModule());
34
+ vi.mock("../manifest-store.ts", () => manifestStoreModule());
35
+ vi.mock("../engine/engines/pi/temp-prompt.ts", () => tempPromptModule());
106
36
 
107
37
  import { ModelConfigService } from "../model-config-service.ts";
108
38
  import type { ModelInfo, ModelRegistryLike } from "../model-resolver.ts";
@@ -127,7 +57,7 @@ interface SetupResult {
127
57
 
128
58
  function setup(): SetupResult {
129
59
  const agentDir = "/tmp/exec-await-worktree-it"; // fs 已 mock,路径不需真实存在
130
- const modelService = new ModelConfigService({ agentDir });
60
+ const modelService = new ModelConfigService({ agentDir, cwd: agentDir });
131
61
  modelService.initModel({
132
62
  modelRegistry: makeEmptyRegistry(),
133
63
  sessionId: "exec-await-worktree-it",
@@ -219,7 +149,7 @@ describe("executeAndAwait worktree 失败收尾", () => {
219
149
  }) as Parameters<WorktreeManager["cleanup"]>[0];
220
150
  let resolveCreate!: (h: unknown) => void;
221
151
  vi.spyOn(worktreeManager, "create").mockImplementation(
222
- () => new Promise((r) => { resolveCreate = r; }) as ReturnType<WorktreeManager["create"]>,
152
+ () => new Promise<unknown>((r) => { resolveCreate = r; }) as ReturnType<WorktreeManager["create"]>,
223
153
  );
224
154
  const cleanupSpy = vi.spyOn(worktreeManager, "cleanup").mockResolvedValue(undefined);
225
155
 
@@ -15,134 +15,36 @@
15
15
  // 因此本文件 mock 的是 node:child_process.spawn(返回 FakeChild),而非 getSdk/fakeSession
16
16
  // (那是对 in-process run() 的旧 mock,在 spawn 改造后是死代码)。
17
17
  //
18
- // mock 模式参考 run-spawn-integration.test.ts(该文件是 spawn 改造后的正确 mock 范式):
19
- // - node:child_process.spawn → FakeChild(EventEmitter + PassThrough),测试控制器
20
- // emit stdout JSON 行(header + SdkEvent)/ stderr / close 时序。
21
- // - node:child_process.execFile → err-first callback 默认兜底(buildEnvBlock 的 git
22
- // branch 调用失败 → catch → branch="",避免副作用)。
23
- // - node:fs 同步方法 → mock(mkdirSync/existsSync/appendFileSync/writeFileSync/readdirSync),
24
- // 避免 sessionDir/sessionFile 触碰真实文件系统。
25
- // - fs.promises.* → 保留真实实现(temp-prompt 整体被 mock,不触发真实 I/O)。
26
- // - temp-prompt → mock(writePromptToTempFile 返回固定路径,消除 fake-timers flaky)。
27
- // - alive-store.writeAliveMarker → mock(避免写 .alive sidecar)。
18
+ // mock 模块工厂已收敛 ./helpers/subagent-service-mocks.ts(四文件共享单源,含
19
+ // spawn → FakeChild / fs 同步方法 / temp-prompt / alive-store / finalized-marker /
20
+ // manifest-store 的完整桩形与动机注释)。
28
21
  //
29
22
  // 所有断言语义不变:它们测的是 SubagentService 的 **编排逻辑**
30
23
  // (pool.acquire / execCtxAls 深度),这些逻辑
31
24
  // 无论事件来自 fakeSession.subscribe 还是 FakeChild.stdout 都一致。
32
25
 
33
- import type { PassThrough } from "node:stream";
34
-
35
26
  import { afterEach, describe, expect, it, vi } from "vitest";
36
27
 
37
- // ── mock modules ──
38
- //
39
- // vitest 会把 vi.mock 提升到文件顶部(早于其他 import / 声明)。mock 工厂若要引用
40
- // FakeChild,需在工厂内部 import(async 工厂可用 await import),而非引用顶部
41
- // 顶层 import(它们在 vi.mock 执行时尚未绑定)。
42
-
43
- vi.mock("node:child_process", async () => {
44
- const { EventEmitter } = await import("node:events");
45
- const { PassThrough } = await import("node:stream");
46
-
47
- // FakeChild:模拟 ChildProcess(EventEmitter + PassThrough streams)。
48
- // 测试通过 lastSpawnedChild() 取回实例,控制 emit stdout JSON 行 / close 时序。
49
- class FakeChild extends EventEmitter {
50
- pid = 12345;
51
- stdout = new PassThrough();
52
- stderr = new PassThrough();
53
- killed = false;
54
- killSignal: string | undefined;
55
- kill(sig?: string): boolean {
56
- this.killed = true;
57
- this.killSignal = sig;
58
- return true;
59
- }
60
- }
61
-
62
- return {
63
- spawn: vi.fn(() => new FakeChild()),
64
- // buildEnvBlock 的 git branch 调用(execFile 异步):默认 err-first 兜底 → catch → branch=""
65
- execFile: vi.fn(
66
- (
67
- _cmd: string,
68
- _args: readonly string[],
69
- _opts: unknown,
70
- cb: (err: Error | null, stdout?: string, stderr?: string) => void,
71
- ) => cb(new Error("execFile not configured in this test")),
72
- ),
73
- };
74
- });
75
-
76
- // node:fs:同步方法 mock(runSpawn 用到的全部),promises 保留真实实现(temp-prompt 用)。
77
- vi.mock("node:fs", async () => {
78
- const actual = await import("node:fs");
79
- return {
80
- default: {
81
- ...actual,
82
- mkdirSync: vi.fn(),
83
- existsSync: vi.fn(() => false),
84
- appendFileSync: vi.fn(),
85
- writeFileSync: vi.fn(),
86
- readdirSync: vi.fn(() => []),
87
- },
88
- // 具名导出与 default 保持一致
89
- mkdirSync: vi.fn(),
90
- existsSync: vi.fn(() => false),
91
- appendFileSync: vi.fn(),
92
- writeFileSync: vi.fn(),
93
- readdirSync: vi.fn(() => []),
94
- // promises 保留真实实现——temp-prompt 已被 mock(见下方 vi.mock),不再触发真实 I/O
95
- promises: actual.promises,
96
- };
97
- });
98
-
99
- // alive-store:mock writeAliveMarker(runSpawn 写 .alive sidecar)+ removeAliveMarker
100
- // (finalizeRecord 收尾删 .alive)。其余导出(readAliveMarker/isProcessAlive)保留真实实现
101
- // (worktree-manager/record-store 用,本组用例不涉及但保留以避免间接报错)。
102
- vi.mock("../alive-store.ts", async (importOriginal) => {
103
- const actual = await importOriginal<typeof import("../alive-store.ts")>();
104
- return {
105
- ...actual,
106
- writeAliveMarker: vi.fn(),
107
- removeAliveMarker: vi.fn(),
108
- };
109
- });
110
-
111
- // finalized-marker mock:避免真实 fs 写 sidecar(测试不关心 finalized 行为)
112
- vi.mock("../finalized-marker.ts", () => ({
113
- writeFinalized: vi.fn(),
114
- readFinalized: vi.fn(() => false),
115
- }));
116
-
117
- // manifest-store mock:writeManifest 用真实 fs.promises 写盘,但本文件 fs 同步方法已 mock
118
- // (目录从不真实创建)→ open/rename/unlink 全 ENOENT → bestEffort 异步 console.debug。
119
- // 这些延迟 console 通过 worker RPC(onUserConsoleLog)回流,与 vitest teardown 形成 race:
120
- // "Closing rpc while onUserConsoleLog was pending" → unhandled rejection / exit 1(flaky)。
121
- // 本组用例测编排逻辑(pool / depth / throttle),不测 manifest 持久化(有 manifest-store.test.ts
122
- // 独立覆盖)。故 mock 成 no-op,消除 teardown race 的根因——异步 console 输出。
123
- vi.mock("../manifest-store.ts", () => {
124
- class FakeManifestStore {
125
- writeManifest = vi.fn(async () => {});
126
- readManifest = vi.fn(async () => null);
127
- listAllSync = vi.fn(() => []);
128
- recoverTmpFiles = vi.fn(async () => []);
129
- }
130
- return { ManifestStore: FakeManifestStore };
131
- });
132
-
133
- // temp-prompt:mock 掉真实 fs.promises I/O,消除 fake-timers 下的 flaky 竞态
134
- // (详见 run-spawn-integration.test.ts 同名 mock 的注释)。
135
- vi.mock("../temp-prompt.ts", () => ({
136
- writePromptToTempFile: vi.fn(async (agent: string) => {
137
- const safeName = agent.replace(/[^\w.-]+/g, "_");
138
- return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
139
- }),
140
- cleanupTempPrompt: vi.fn(async () => {}),
141
- }));
28
+ import {
29
+ aliveStoreModule,
30
+ childProcessModule,
31
+ driveChildToCompletion,
32
+ finalizedMarkerModule,
33
+ fsSyncModule,
34
+ manifestStoreModule,
35
+ tempPromptModule,
36
+ } from "./helpers/subagent-service-mocks.ts";
37
+
38
+ vi.mock("node:child_process", () => childProcessModule());
39
+ vi.mock("node:fs", async (importOriginal) => fsSyncModule(await importOriginal<typeof import("node:fs")>()));
40
+ vi.mock("../alive-store.ts", async (importOriginal) => aliveStoreModule(await importOriginal<typeof import("../alive-store.ts")>()));
41
+ vi.mock("../finalized-marker.ts", () => finalizedMarkerModule());
42
+ vi.mock("../manifest-store.ts", () => manifestStoreModule());
43
+ vi.mock("../engine/engines/pi/temp-prompt.ts", () => tempPromptModule());
142
44
 
143
45
  import { spawn } from "node:child_process";
144
46
 
145
- import { waitForSpawn } from "./helpers/spawn-mock.ts";
47
+ import { waitForSpawn, lastSpawnedChild } from "./helpers/spawn-mock.ts";
146
48
  import { ModelConfigService } from "../model-config-service.ts";
147
49
  import type { ModelInfo, ModelRegistryLike } from "../model-resolver.ts";
148
50
  import { MAX_FORK_DEPTH } from "../session-context-resolver.ts";
@@ -150,63 +52,6 @@ import { SubagentService } from "../subagent-service.ts";
150
52
 
151
53
  const mockSpawn = vi.mocked(spawn);
152
54
 
153
- /**
154
- * spawn mock 返回的 fake child 类型。
155
- * 由于 FakeChild 定义在 vi.mock 工厂内部(作用域隔离),此处用结构子集类型描述,
156
- * 测试代码通过此类型访问 stdout/stderr/kill 等成员。
157
- */
158
- interface FakeChild {
159
- pid: number;
160
- stdout: PassThrough;
161
- stderr: PassThrough;
162
- killed: boolean;
163
- killSignal: string | undefined;
164
- kill(sig?: string): boolean;
165
- emit(event: string, ...args: unknown[]): boolean;
166
- }
167
-
168
- /** 从最近一次 spawn 调用取回返回的 FakeChild(测试控制器)。 */
169
- function lastSpawnedChild(): FakeChild {
170
- const result = mockSpawn.mock.results.at(-1);
171
- if (!result) throw new Error("spawn was not called yet");
172
- return result.value as FakeChild;
173
- }
174
-
175
- // ============================================================
176
- // 辅助:FakeChild stdout 驱动(替代旧 fakeSession.subscribe 的事件注入)
177
- // ============================================================
178
-
179
- /** 构造 session header 行(stdout 首行,runSpawn 据此回填 record.sessionFile)。 */
180
- function sessionHeader(id = "nest-session"): Record<string, unknown> {
181
- return {
182
- type: "session",
183
- id,
184
- timestamp: "2026-07-03T12-00-00-000Z",
185
- cwd: "/tmp/test",
186
- };
187
- }
188
-
189
- /** 向 stdout 写一行 JSON(自动补换行,runSpawn 按 \n split 行)。 */
190
- function emitStdoutLine(child: FakeChild, obj: Record<string, unknown>): void {
191
- child.stdout.write(`${JSON.stringify(obj)}\n`);
192
- }
193
-
194
- /**
195
- * 驱动 FakeChild 完成 session:写 header + 可选事件 + close(0)。
196
- *
197
- * 这是「让 runSpawn 自然 resolve」的标准收尾路径。runSpawn 在 close 后判定 success
198
- * (exitCode=0 → success=true),并跑 identity 补写 + finalizeRecord。
199
- *
200
- * @param events header 之后、close 之前 emit 的 SdkEvent 行(tool/message/turn 等)
201
- */
202
- async function driveChildToCompletion(child: FakeChild, events: Record<string, unknown>[] = []): Promise<void> {
203
- emitStdoutLine(child, sessionHeader());
204
- for (const e of events) emitStdoutLine(child, e);
205
- child.stdout.end();
206
- child.stderr.end();
207
- child.emit("close", 0);
208
- }
209
-
210
55
  // ============================================================
211
56
  // 辅助:service 构造(与旧 setup 等价,但不再装配 fakeSdk)
212
57
  // ============================================================
@@ -225,7 +70,7 @@ interface SetupResult {
225
70
 
226
71
  function setup(): SetupResult {
227
72
  const agentDir = "/tmp/nest-it"; // fs 已 mock,路径不需真实存在
228
- const modelService = new ModelConfigService({ agentDir });
73
+ const modelService = new ModelConfigService({ agentDir, cwd: agentDir });
229
74
  modelService.initModel({
230
75
  modelRegistry: makeEmptyRegistry(),
231
76
  sessionId: "nest-it",
@@ -242,7 +87,8 @@ function setup(): SetupResult {
242
87
 
243
88
  const ctxModel: ModelInfo = { id: "m", name: "M", provider: "p", reasoning: false };
244
89
 
245
- /** execCtxAls.run 的 duck-type(绕过 import AsyncLocalStorage,足够本组用例)。 */
90
+ /** [D3-⑤] execNesting(公共层 ExecutionNestingContext).run 的 duck-type(绕过
91
+ * import AsyncLocalStorage,足够本组用例——私字段经 Reflect 取,机制与旧 execCtxAls 同构)。 */
246
92
  interface ExecCtxAls {
247
93
  run: <T>(store: { recordId: string | undefined; depth: number }, cb: () => T) => T;
248
94
  }
@@ -262,12 +108,12 @@ describe("嵌套护栏 / 并发池 / 节流(D-030~D-033 回归锁)", () => {
262
108
  const pool = Reflect.get(service, "pool") as { acquire: ReturnType<typeof vi.fn>; release: ReturnType<typeof vi.fn> };
263
109
  const acquireSpy = vi.spyOn(pool, "acquire");
264
110
 
265
- const execPromise = service.execute({ task: "bg in pool", ctxModel });
111
+ const execPromise = service.execute({ task: "bg in pool", slug: "test", ctxModel });
266
112
  // detached runAndFinalize → acquire。等 spawn 拿到 child 再驱动完成。
267
113
  await waitForSpawn(mockSpawn);
268
- await driveChildToCompletion(lastSpawnedChild());
114
+ await driveChildToCompletion(lastSpawnedChild(mockSpawn));
269
115
 
270
- // 等 detached promise 链跑完(kickOffBackground 的 .then notify)
116
+ // 等 detached promise 链跑完(kickOffChatRound 的 .then notify)
271
117
  await new Promise<void>((r) => setTimeout(r, 10));
272
118
 
273
119
  expect(acquireSpy).toHaveBeenCalled();
@@ -283,16 +129,16 @@ describe("嵌套护栏 / 并发池 / 节流(D-030~D-033 回归锁)", () => {
283
129
  it("[D-033] execCtxAls depth=MAX 时 execute 抛错(nestingDepth=MAX+1 被拒)", async () => {
284
130
  const { service } = setup();
285
131
 
286
- const execCtxAls = Reflect.get(service, "execCtxAls") as ExecCtxAls;
132
+ const execNesting = Reflect.get(service, "execNesting") as ExecCtxAls;
287
133
 
288
134
  await expect(
289
- execCtxAls.run({ recordId: "parent", depth: MAX_FORK_DEPTH }, () =>
290
- service.execute({ task: "too deep", ctxModel }),
135
+ execNesting.run({ recordId: "parent", depth: MAX_FORK_DEPTH }, () =>
136
+ service.execute({ task: "too deep", slug: "test", ctxModel }),
291
137
  ),
292
138
  ).rejects.toThrow(/nesting depth/);
293
139
 
294
140
  // 无副作用:guard 在 createRecordForMode 之前,record 未创建
295
- expect(service.collectRecords(10)).toHaveLength(0);
141
+ expect(service.queries.collectRecords(10)).toHaveLength(0);
296
142
  // guard 在 spawn 之前——不应 spawn 任何子进程
297
143
  expect(mockSpawn).not.toHaveBeenCalled();
298
144
  });
@@ -300,13 +146,13 @@ describe("嵌套护栏 / 并发池 / 节流(D-030~D-033 回归锁)", () => {
300
146
  it("[D-033] execCtxAls depth=MAX-1 时 execute 不抛(nestingDepth=MAX 允许)", async () => {
301
147
  const { service } = setup();
302
148
 
303
- const execCtxAls = Reflect.get(service, "execCtxAls") as ExecCtxAls;
149
+ const execNesting = Reflect.get(service, "execNesting") as ExecCtxAls;
304
150
 
305
- const execPromise = execCtxAls.run({ recordId: "parent", depth: MAX_FORK_DEPTH - 1 }, () =>
306
- service.execute({ task: "at limit", ctxModel }),
151
+ const execPromise = execNesting.run({ recordId: "parent", depth: MAX_FORK_DEPTH - 1 }, () =>
152
+ service.execute({ task: "at limit", slug: "test", ctxModel }),
307
153
  );
308
154
  await waitForSpawn(mockSpawn);
309
- await driveChildToCompletion(lastSpawnedChild(), [
155
+ await driveChildToCompletion(lastSpawnedChild(mockSpawn), [
310
156
  { type: "turn_end" },
311
157
  { type: "message_end", message: { usage: { input: 1 } } },
312
158
  ]);