@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
@@ -16,102 +16,32 @@
16
16
  // 4. forkDepth 基线:env PI_SUBAGENT_FORK_DEPTH → fork spawn env 递增
17
17
  //
18
18
  // mock 策略与 execute-nesting.test.ts 一致(spawn → FakeChild,fs 同步方法 mock,
19
- // temp-prompt / alive-store / finalized-marker / manifest-store mock)。
20
-
21
- import type { PassThrough } from "node:stream";
19
+ // temp-prompt / alive-store / finalized-marker / manifest-store mock;已收敛
20
+ // ./helpers/subagent-service-mocks.ts 四文件共享单源)。
22
21
 
23
22
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
24
23
 
25
- // ── mock modules(同 execute-nesting.test.ts)──
26
-
27
- vi.mock("node:child_process", async () => {
28
- const { EventEmitter } = await import("node:events");
29
- const { PassThrough } = await import("node:stream");
30
-
31
- class FakeChild extends EventEmitter {
32
- pid = 12345;
33
- stdout = new PassThrough();
34
- stderr = new PassThrough();
35
- killed = false;
36
- killSignal: string | undefined;
37
- kill(sig?: string): boolean {
38
- this.killed = true;
39
- this.killSignal = sig;
40
- return true;
41
- }
42
- }
43
-
44
- return {
45
- spawn: vi.fn(() => new FakeChild()),
46
- // buildEnvBlock 用 execFile 异步取 git branch:默认 err-first 兜底(catch → branch="")
47
- execFile: vi.fn(
48
- (
49
- _cmd: string,
50
- _args: readonly string[],
51
- _opts: unknown,
52
- cb: (err: Error | null, stdout?: string, stderr?: string) => void,
53
- ) => cb(new Error("execFile not configured in this test")),
54
- ),
55
- };
56
- });
24
+ import {
25
+ aliveStoreModule,
26
+ childProcessModule,
27
+ finalizedMarkerModule,
28
+ fsSyncModule,
29
+ manifestStoreModule,
30
+ tempPromptModule,
31
+ } from "./helpers/subagent-service-mocks.ts";
57
32
 
58
- vi.mock("node:fs", async () => {
59
- const actual = await import("node:fs");
60
- return {
61
- default: {
62
- ...actual,
63
- mkdirSync: vi.fn(),
64
- existsSync: vi.fn(() => false),
65
- appendFileSync: vi.fn(),
66
- writeFileSync: vi.fn(),
67
- readdirSync: vi.fn(() => []),
68
- },
69
- mkdirSync: vi.fn(),
70
- existsSync: vi.fn(() => false),
71
- appendFileSync: vi.fn(),
72
- writeFileSync: vi.fn(),
73
- readdirSync: vi.fn(() => []),
74
- promises: actual.promises,
75
- };
76
- });
77
-
78
- vi.mock("../alive-store.ts", async (importOriginal) => {
79
- const actual = await importOriginal<typeof import("../alive-store.ts")>();
80
- return {
81
- ...actual,
82
- writeAliveMarker: vi.fn(),
83
- removeAliveMarker: vi.fn(),
84
- };
85
- });
86
-
87
- vi.mock("../finalized-marker.ts", () => ({
88
- writeFinalized: vi.fn(),
89
- readFinalized: vi.fn(() => false),
90
- }));
91
-
92
- vi.mock("../manifest-store.ts", () => {
93
- class FakeManifestStore {
94
- writeManifest = vi.fn(async () => {});
95
- readManifest = vi.fn(async () => null);
96
- listAllSync = vi.fn(() => []);
97
- recoverTmpFiles = vi.fn(async () => []);
98
- }
99
- // vi.fn 包裹:构造参数(recordsDir)可从 mock.calls 断言([MF-3] 目录统一验证)。
100
- // 注意用普通 function(箭头函数不能被 new 调用)。
101
- return { ManifestStore: vi.fn(function (_recordsDir: string) { return new FakeManifestStore(); }) };
102
- });
33
+ // ── mock modules(同 execute-nesting.test.ts)──
103
34
 
104
- vi.mock("../temp-prompt.ts", () => ({
105
- writePromptToTempFile: vi.fn(async (agent: string) => {
106
- const safeName = agent.replace(/[^\w.-]+/g, "_");
107
- return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
108
- }),
109
- cleanupTempPrompt: vi.fn(async () => {}),
110
- }));
35
+ vi.mock("node:child_process", () => childProcessModule());
36
+ vi.mock("node:fs", async (importOriginal) => fsSyncModule(await importOriginal<typeof import("node:fs")>()));
37
+ vi.mock("../alive-store.ts", async (importOriginal) => aliveStoreModule(await importOriginal<typeof import("../alive-store.ts")>()));
38
+ vi.mock("../finalized-marker.ts", () => finalizedMarkerModule());
39
+ vi.mock("../manifest-store.ts", () => manifestStoreModule());
40
+ vi.mock("../engine/engines/pi/temp-prompt.ts", () => tempPromptModule());
111
41
 
112
42
  import { spawn } from "node:child_process";
113
43
 
114
- import { waitForSpawn } from "./helpers/spawn-mock.ts";
44
+ import { waitForSpawn, lastSpawnedChild } from "./helpers/spawn-mock.ts";
115
45
  import { ModelConfigService } from "../model-config-service.ts";
116
46
  import type { ModelInfo, ModelRegistryLike } from "../model-resolver.ts";
117
47
  import { ManifestStore } from "../manifest-store.ts";
@@ -128,43 +58,10 @@ const ENV_DEPTH = "PI_SUBAGENT_DEPTH";
128
58
  const ENV_ROOT_CWD = "PI_SUBAGENT_ROOT_CWD";
129
59
  const ENV_FORK_DEPTH = "PI_SUBAGENT_FORK_DEPTH";
130
60
 
131
- interface FakeChild {
132
- pid: number;
133
- stdout: PassThrough;
134
- stderr: PassThrough;
135
- killed: boolean;
136
- killSignal: string | undefined;
137
- kill(sig?: string): boolean;
138
- emit(event: string, ...args: unknown[]): boolean;
139
- }
140
-
141
- function lastSpawnedChild(): FakeChild {
142
- const result = mockSpawn.mock.results.at(-1);
143
- if (!result) throw new Error("spawn was not called yet");
144
- return result.value as FakeChild;
145
- }
146
-
147
61
  function getLastSpawnEnv(): Record<string, string | undefined> {
148
62
  return (mockSpawn.mock.calls.at(-1)?.[2]?.env as Record<string, string | undefined>) ?? {};
149
63
  }
150
64
 
151
- function sessionHeader(id = "baseline-session"): Record<string, unknown> {
152
- return { type: "session", id, timestamp: "2026-08-11T00-00-00-000Z", cwd: "/tmp/test" };
153
- }
154
-
155
- function emitStdoutLine(child: FakeChild, obj: Record<string, unknown>): void {
156
- child.stdout.write(`${JSON.stringify(obj)}\n`);
157
- }
158
-
159
- /** 驱动 FakeChild 完成:header + 可选事件 + close(0)(runSpawn 自然 resolve)。 */
160
- async function driveChildToCompletion(child: FakeChild, events: Record<string, unknown>[] = []): Promise<void> {
161
- emitStdoutLine(child, sessionHeader());
162
- for (const e of events) emitStdoutLine(child, e);
163
- child.stdout.end();
164
- child.stderr.end();
165
- child.emit("close", 0);
166
- }
167
-
168
65
  // ── 辅助:service 构造 ──
169
66
 
170
67
  function makeEmptyRegistry(): ModelRegistryLike {
@@ -177,7 +74,7 @@ function makePi() {
177
74
 
178
75
  function setup(env: Record<string, string>): { service: SubagentService; store: RecordStore } {
179
76
  const agentDir = "/tmp/baseline-it";
180
- const modelService = new ModelConfigService({ agentDir });
77
+ const modelService = new ModelConfigService({ agentDir, cwd: agentDir });
181
78
  modelService.initModel({
182
79
  modelRegistry: makeEmptyRegistry(),
183
80
  sessionId: "baseline-it",
@@ -227,7 +124,7 @@ describe("进程级基线兜底(ALS 断裂修复,pi 事件回调模型)",
227
124
  const { service, store } = setup({});
228
125
 
229
126
  // 不在 execCtxAls.run 内调用(模拟 ALS 断裂:事件回调上下文读不到 store)
230
- const handle = await service.execute({ task: "child of parent", ctxModel });
127
+ const handle = await service.execute({ task: "child of parent", slug: "test", ctxModel });
231
128
 
232
129
  const rec = store.collectRecords(10, "all", "root-main").find((r) => r.id === handle.subagentId);
233
130
  expect(rec).toBeDefined();
@@ -239,7 +136,7 @@ describe("进程级基线兜底(ALS 断裂修复,pi 事件回调模型)",
239
136
  // 旧实现传 this.sessionId(子进程自己的 session id ≠ ROOT)→ 子进程内列表恒空。
240
137
  // 子进程的本进程 sessionId 是 "baseline-it"(initSession 注入),而 record 归属
241
138
  // root-main(env 贯穿的真 ROOT)——能查到即证明过滤用的是 sessionRootId。
242
- const viaService = service.collectRecords(10);
139
+ const viaService = service.queries.collectRecords(10);
243
140
  expect(viaService.map((r) => r.id)).toContain(handle.subagentId);
244
141
  expect(viaService[0]!.rootSessionId).toBe("root-main");
245
142
  });
@@ -247,7 +144,7 @@ describe("进程级基线兜底(ALS 断裂修复,pi 事件回调模型)",
247
144
  it("[顶层] 无 env(根进程):parentRecordId undefined / depth 0", async () => {
248
145
  const { service, store } = setup({});
249
146
 
250
- const handle = await service.execute({ task: "top level", ctxModel });
147
+ const handle = await service.execute({ task: "top level", slug: "test", ctxModel });
251
148
 
252
149
  const rec = store.collectRecords(10, "all", "baseline-it").find((r) => r.id === handle.subagentId);
253
150
  expect(rec).toBeDefined();
@@ -261,10 +158,10 @@ describe("进程级基线兜底(ALS 断裂修复,pi 事件回调模型)",
261
158
  process.env[ENV_DEPTH] = "0";
262
159
 
263
160
  const { service, store } = setup({});
264
- const execCtxAls = Reflect.get(service, "execCtxAls") as ExecCtxAls;
161
+ const execNesting = Reflect.get(service, "execNesting") as ExecCtxAls;
265
162
 
266
- const handle = await execCtxAls.run({ recordId: "sa-inline-parent", depth: 3 }, () =>
267
- service.execute({ task: "inline nested", ctxModel }),
163
+ const handle = await execNesting.run({ recordId: "sa-inline-parent", depth: 3 }, () =>
164
+ service.execute({ task: "inline nested", slug: "test", ctxModel }),
268
165
  );
269
166
 
270
167
  const rec = store.collectRecords(10, "all", "root-main").find((r) => r.id === handle.subagentId);
@@ -281,14 +178,14 @@ describe("进程级基线兜底(ALS 断裂修复,pi 事件回调模型)",
281
178
 
282
179
  const { service } = setup({});
283
180
 
284
- const execPromise = service.execute({ task: "fork child", ctxModel, fork: true });
181
+ const execPromise = service.execute({ task: "fork child", slug: "test", ctxModel, fork: true });
285
182
  await waitForSpawn(mockSpawn);
286
183
  const childEnv = getLastSpawnEnv();
287
184
 
288
185
  // 742 行 parentDepth = forkDepthAls.getStore() ?? forkDepthBaseline —— ALS 断裂时基线=1,+1 → 2
289
186
  expect(childEnv.PI_SUBAGENT_FORK_DEPTH).toBe("2");
290
187
 
291
- const child = lastSpawnedChild();
188
+ const child = lastSpawnedChild(mockSpawn);
292
189
  child.emit("close", 0);
293
190
  await execPromise;
294
191
  });
@@ -303,7 +200,7 @@ describe("进程级基线兜底(ALS 断裂修复,pi 事件回调模型)",
303
200
  // MAX_FORK_DEPTH 至少 > 5 才不会被误拒;这里验证基线参与计数的方式是
304
201
  // 用直接深度断言——execute 不抛错说明 nestingDepth=6 未超限,护栏不误伤。
305
202
  // (MAX_FORK_DEPTH 具体值由 session-context-resolver 定义,这里不硬编码。)
306
- await expect(service.execute({ task: "deep but allowed", ctxModel })).resolves.toBeDefined();
203
+ await expect(service.execute({ task: "deep but allowed", slug: "test", ctxModel })).resolves.toBeDefined();
307
204
  });
308
205
 
309
206
  it("[MF-3 回归] 有 ENV_ROOT_CWD(worktree 子进程):sessions 与 records 两套目录统一编码在 ROOT cwd 段", () => {
@@ -314,7 +211,7 @@ describe("进程级基线兜底(ALS 断裂修复,pi 事件回调模型)",
314
211
 
315
212
  const agentDir = "/tmp/baseline-it";
316
213
  const checkoutPath = "/var/folders/worktree/pi-subagents/--root-project--/branch";
317
- const modelService = new ModelConfigService({ agentDir });
214
+ const modelService = new ModelConfigService({ agentDir, cwd: agentDir });
318
215
  modelService.initModel({
319
216
  modelRegistry: makeEmptyRegistry(),
320
217
  sessionId: "baseline-it",
@@ -7,150 +7,46 @@
7
7
  //
8
8
  // 这是场景 1(端到端三层嵌套全树可见)的「env 传递机制」确定性验证——不依赖 LLM 配合,
9
9
  // mock spawn 拦截 childEnv 直接断言。端到端可见性由场景 1(真实 pi CLI + recursive-worker agent)覆盖。
10
-
11
- import type { PassThrough } from "node:stream";
10
+ //
11
+ // mock 块与 fixture 收敛 __tests__/helpers/spawn-mock.ts(vi.mock 工厂 await import 转发,
12
+ // 单源共享);vi.mocked 取回与收尾装配来自 helpers/session-runner-mocks.ts。
12
13
 
13
14
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
14
15
 
15
16
  import { getSubagentSessionDir } from "../path-encoding.ts";
16
17
 
17
- // ── mock modules(同 session-runner-schema-env.test.ts 模式)──
18
-
19
- vi.mock("node:child_process", async () => {
20
- const { EventEmitter } = await import("node:events");
21
- const { PassThrough } = await import("node:stream");
22
-
23
- class FakeChild extends EventEmitter {
24
- pid = 12345;
25
- stdout = new PassThrough();
26
- stdin = new PassThrough(); // runSpawn 注册 stdin EPIPE handler(session-runner),FakeChild 必须提供
27
- stderr = new PassThrough();
28
- killed = false;
29
- killSignal: string | undefined;
30
- kill(sig?: string): boolean {
31
- this.killed = true;
32
- this.killSignal = sig;
33
- return true;
34
- }
35
- }
36
-
37
- return {
38
- spawn: vi.fn(() => new FakeChild()),
39
- // buildEnvBlock execFile 异步取 git branch:默认 err-first 兜底(catch → branch=""),
40
- // 形态同 worktree-manager.test.ts 的 setupExecFile
41
- execFile: vi.fn(
42
- (
43
- _cmd: string,
44
- _args: readonly string[],
45
- _opts: unknown,
46
- cb: (err: Error | null, stdout?: string, stderr?: string) => void,
47
- ) => cb(new Error("execFile not configured in this test")),
48
- ),
49
- };
50
- });
51
-
52
- vi.mock("node:fs", async () => {
53
- const actual = await import("node:fs");
54
- return {
55
- default: {
56
- ...actual,
57
- mkdirSync: vi.fn(),
58
- existsSync: vi.fn(() => false),
59
- appendFileSync: vi.fn(),
60
- writeFileSync: vi.fn(),
61
- readdirSync: vi.fn(() => []),
62
- },
63
- mkdirSync: vi.fn(),
64
- existsSync: vi.fn(() => false),
65
- appendFileSync: vi.fn(),
66
- writeFileSync: vi.fn(),
67
- readdirSync: vi.fn(() => []),
68
- promises: actual.promises,
69
- };
70
- });
71
-
72
- vi.mock("../alive-store.ts", () => ({
73
- writeAliveMarker: vi.fn(),
74
- }));
75
-
76
- vi.mock("../temp-prompt.ts", () => ({
77
- writePromptToTempFile: vi.fn(async (agent: string) => {
78
- const safeName = agent.replace(/[^\w.-]+/g, "_");
79
- return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
80
- }),
81
- cleanupTempPrompt: vi.fn(async () => {}),
82
- }));
83
-
84
- import { spawn } from "node:child_process";
85
- import * as fs from "node:fs";
86
-
87
- import { waitForSpawn } from "./helpers/spawn-mock.ts";
88
- import { createRecord } from "../execution-record.ts";
89
- import { type RunOptions, runSpawn, type SessionRunnerContext } from "../session-runner.ts";
90
-
91
- const mockSpawn = vi.mocked(spawn);
92
- const mockExistsSync = vi.mocked(fs.existsSync);
93
-
94
- interface FakeChild {
95
- pid: number;
96
- stdout: PassThrough;
97
- stderr: PassThrough;
98
- killed: boolean;
99
- killSignal: string | undefined;
100
- kill(sig?: string): boolean;
101
- emit(event: string, ...args: unknown[]): boolean;
102
- }
103
-
104
- function getLastSpawnedChild(): FakeChild {
105
- const result = mockSpawn.mock.results.at(-1);
106
- if (!result) throw new Error("spawn was not called yet");
107
- return result.value as FakeChild;
108
- }
109
-
110
- function getLastSpawnEnv(): Record<string, string | undefined> {
111
- return (mockSpawn.mock.calls.at(-1)?.[2]?.env as Record<string, string | undefined>) ?? {};
112
- }
113
-
114
- // ── fixture ──
115
-
116
- function makeRecord(overrides: { id?: string; depth?: number } = {}) {
117
- return createRecord(overrides.id ?? "sa-test-record", {
118
- agent: "general-purpose",
119
- model: "test/model",
120
- mode: "background",
121
- task: "test task",
122
- startedAt: Date.now(),
123
- rootSessionId: "should-be-overridden-by-sessionRootId-source",
124
- parentRecordId: undefined,
125
- depth: overrides.depth ?? 0,
126
- });
127
- }
128
-
129
- function makeRunOpts(overrides: Partial<RunOptions> = {}): RunOptions {
130
- return {
131
- resolved: { model: { provider: "test", id: "model" }, thinkingLevel: undefined },
132
- agentConfig: undefined,
133
- appendSystemPrompt: undefined,
134
- skillPath: undefined,
135
- schema: undefined,
136
- maxTurns: undefined,
137
- graceTurns: undefined,
138
- signal: undefined,
139
- onEvent: undefined,
140
- ...overrides,
141
- };
142
- }
143
-
144
- function makeCtx(overrides: Partial<SessionRunnerContext> = {}): SessionRunnerContext {
145
- return {
146
- cwd: "/fake/cwd",
147
- agentDir: "/fake/agent",
148
- skillDirs: [],
149
- mainCwd: "/fake/cwd",
150
- sessionRootId: "root-main-session",
151
- rootCwd: "/fake/cwd",
152
- ...overrides,
153
- };
18
+ // ── mock modules(同 session-runner-schema-env.test.ts 模式,工厂单源在 spawn-mock.ts)──
19
+
20
+ vi.mock("node:child_process", async () =>
21
+ (await import("./helpers/spawn-mock.ts")).childProcessModule());
22
+ vi.mock("node:fs", async () => (await import("./helpers/spawn-mock.ts")).fsModule());
23
+ vi.mock("../alive-store.ts", async () =>
24
+ (await import("./helpers/spawn-mock.ts")).aliveStoreModule());
25
+ vi.mock("../engine/engines/pi/temp-prompt.ts", async () =>
26
+ (await import("./helpers/spawn-mock.ts")).tempPromptModule());
27
+
28
+ import { runSpawn } from "../engine/engines/pi/session-runner.ts";
29
+ import {
30
+ getLastSpawnEnv,
31
+ makeRunOpts,
32
+ makeVisibilityCtx,
33
+ makeVisibilityRecord,
34
+ waitForSpawn,
35
+ } from "./helpers/spawn-mock.ts";
36
+ import { closeLastChildAndAwait, takeSessionRunnerMocks } from "./helpers/session-runner-mocks.ts";
37
+
38
+ const { mockSpawn, mockExistsSync } = takeSessionRunnerMocks();
39
+
40
+ /** runSpawn 前奏装配:启动、等 spawn 落位、取回 childEnv(断言留在各用例)。 */
41
+ async function spawnAndCaptureEnv(
42
+ record: ReturnType<typeof makeVisibilityRecord>,
43
+ task: string,
44
+ opts: ReturnType<typeof makeRunOpts>,
45
+ ctx: ReturnType<typeof makeVisibilityCtx>,
46
+ ): Promise<{ resultPromise: Promise<unknown>; childEnv: Record<string, string | undefined> }> {
47
+ const resultPromise = runSpawn(record, task, opts, ctx);
48
+ await waitForSpawn(mockSpawn);
49
+ return { resultPromise, childEnv: getLastSpawnEnv(mockSpawn) };
154
50
  }
155
51
 
156
52
  // ── runSpawn childEnv 身份 env 注入(场景 1b)──
@@ -166,13 +62,11 @@ describe("runSpawn 跨进程身份 env 注入(递归可见性场景 1b)", ()
166
62
  });
167
63
 
168
64
  it("非 fork(fork=false/undefined):无条件注入 4 个身份 env(决策 2)", async () => {
169
- const record = makeRecord({ id: "sa-aaa", depth: 0 });
170
- const ctx = makeCtx({ sessionRootId: "root-main" });
65
+ const record = makeVisibilityRecord({ id: "sa-aaa", depth: 0 });
66
+ const ctx = makeVisibilityCtx({ sessionRootId: "root-main" });
171
67
  const opts = makeRunOpts({ fork: false });
172
68
 
173
- const resultPromise = runSpawn(record, "test task", opts, ctx);
174
- await waitForSpawn(mockSpawn);
175
- const childEnv = getLastSpawnEnv();
69
+ const { resultPromise, childEnv } = await spawnAndCaptureEnv(record, "test task", opts, ctx);
176
70
 
177
71
  expect(childEnv.PI_SUBAGENT_ROOT_SESSION_ID).toBe("root-main");
178
72
  expect(childEnv.PI_SUBAGENT_SELF_RECORD_ID).toBe("sa-aaa");
@@ -182,19 +76,15 @@ describe("runSpawn 跨进程身份 env 注入(递归可见性场景 1b)", ()
182
76
  // fork=false 不注入 fork depth env(与既有行为一致,本测试不改变它)
183
77
  expect(childEnv.PI_SUBAGENT_FORK_DEPTH).toBeUndefined();
184
78
 
185
- const child = getLastSpawnedChild();
186
- child.emit("close", 0);
187
- await resultPromise;
79
+ await closeLastChildAndAwait(resultPromise);
188
80
  });
189
81
 
190
82
  it("fork=true:4 个身份 env 与 fork depth env 共存(决策 2 无条件注入不依赖 fork)", async () => {
191
- const record = makeRecord({ id: "sa-bbb", depth: 2 });
192
- const ctx = makeCtx({ sessionRootId: "root-main" });
83
+ const record = makeVisibilityRecord({ id: "sa-bbb", depth: 2 });
84
+ const ctx = makeVisibilityCtx({ sessionRootId: "root-main" });
193
85
  const opts = makeRunOpts({ fork: true, parentForkDepth: 1 });
194
86
 
195
- const resultPromise = runSpawn(record, "test task", opts, ctx);
196
- await waitForSpawn(mockSpawn);
197
- const childEnv = getLastSpawnEnv();
87
+ const { resultPromise, childEnv } = await spawnAndCaptureEnv(record, "test task", opts, ctx);
198
88
 
199
89
  // 身份 env 无条件存在(决策 2)
200
90
  expect(childEnv.PI_SUBAGENT_ROOT_SESSION_ID).toBe("root-main");
@@ -204,46 +94,36 @@ describe("runSpawn 跨进程身份 env 注入(递归可见性场景 1b)", ()
204
94
  // fork depth env 同时存在(fork=true + parentForkDepth=1 → 2)
205
95
  expect(childEnv.PI_SUBAGENT_FORK_DEPTH).toBe("2");
206
96
 
207
- const child = getLastSpawnedChild();
208
- child.emit("close", 0);
209
- await resultPromise;
97
+ await closeLastChildAndAwait(resultPromise);
210
98
  });
211
99
 
212
100
  it("深层 record(depth=3):DEPTH env = String(record.depth),正确贯穿嵌套层级", async () => {
213
- const record = makeRecord({ id: "sa-deep", depth: 3 });
214
- const ctx = makeCtx({ sessionRootId: "root-topmost" });
101
+ const record = makeVisibilityRecord({ id: "sa-deep", depth: 3 });
102
+ const ctx = makeVisibilityCtx({ sessionRootId: "root-topmost" });
215
103
 
216
- const resultPromise = runSpawn(record, "test task", makeRunOpts(), ctx);
217
- await waitForSpawn(mockSpawn);
218
- const childEnv = getLastSpawnEnv();
104
+ const { resultPromise, childEnv } = await spawnAndCaptureEnv(record, "test task", makeRunOpts(), ctx);
219
105
 
220
106
  expect(childEnv.PI_SUBAGENT_DEPTH).toBe("3");
221
107
  expect(childEnv.PI_SUBAGENT_SELF_RECORD_ID).toBe("sa-deep");
222
108
  expect(childEnv.PI_SUBAGENT_ROOT_SESSION_ID).toBe("root-topmost");
223
109
 
224
- const child = getLastSpawnedChild();
225
- child.emit("close", 0);
226
- await resultPromise;
110
+ await closeLastChildAndAwait(resultPromise);
227
111
  });
228
112
 
229
113
  it("ROOT_SESSION_ID 恒等于 ctx.sessionRootId(贯穿真 ROOT,非 record.rootSessionId)", async () => {
230
114
  // record.rootSessionId 是 createRecord 时写入的值(可能来自旧逻辑),但 env 注入用的是
231
115
  // ctx.sessionRootId(经 buildSessionRunnerContext 从 this.sessionRootId 透传,贯穿真 ROOT)。
232
116
  // 这保证深层 subagent 的子进程仍归顶层 ROOT(设计决策 1/3)。
233
- const record = makeRecord({ id: "sa-ccc" }); // record.rootSessionId = fixture 默认值
234
- const ctx = makeCtx({ sessionRootId: "real-root-session" });
117
+ const record = makeVisibilityRecord({ id: "sa-ccc" }); // record.rootSessionId = fixture 默认值
118
+ const ctx = makeVisibilityCtx({ sessionRootId: "real-root-session" });
235
119
 
236
- const resultPromise = runSpawn(record, "test task", makeRunOpts(), ctx);
237
- await waitForSpawn(mockSpawn);
238
- const childEnv = getLastSpawnEnv();
120
+ const { resultPromise, childEnv } = await spawnAndCaptureEnv(record, "test task", makeRunOpts(), ctx);
239
121
 
240
122
  // env 用 ctx.sessionRootId,不是 record.rootSessionId
241
123
  expect(childEnv.PI_SUBAGENT_ROOT_SESSION_ID).toBe("real-root-session");
242
124
  expect(childEnv.PI_SUBAGENT_ROOT_SESSION_ID).not.toBe(record.rootSessionId);
243
125
 
244
- const child = getLastSpawnedChild();
245
- child.emit("close", 0);
246
- await resultPromise;
126
+ await closeLastChildAndAwait(resultPromise);
247
127
  });
248
128
 
249
129
  it("[MF-3 回归] worktree 模式(mainCwd=checkout ≠ rootCwd):sessionDir 用 ROOT cwd 编码,深层 record 落盘到 ROOT 可扫描段", async () => {
@@ -252,12 +132,10 @@ describe("runSpawn 跨进程身份 env 注入(递归可见性场景 1b)", ()
252
132
  const rootCwd = "/root/project";
253
133
  const checkoutPath = "/var/folders/worktree/pi-subagents/--root-project--/branch";
254
134
  const agentDir = "/fake/agent";
255
- const record = makeRecord({ id: "sa-deep", depth: 2 });
256
- const ctx = makeCtx({ cwd: checkoutPath, mainCwd: checkoutPath, rootCwd, agentDir });
135
+ const record = makeVisibilityRecord({ id: "sa-deep", depth: 2 });
136
+ const ctx = makeVisibilityCtx({ cwd: checkoutPath, mainCwd: checkoutPath, rootCwd, agentDir });
257
137
 
258
- const resultPromise = runSpawn(record, "test task", makeRunOpts(), ctx);
259
- await waitForSpawn(mockSpawn);
260
- const childEnv = getLastSpawnEnv();
138
+ const { resultPromise, childEnv } = await spawnAndCaptureEnv(record, "test task", makeRunOpts(), ctx);
261
139
  const spawnArgs = mockSpawn.mock.calls.at(-1)?.[1] as string[];
262
140
 
263
141
  // 第 4 个 env 贯穿 ROOT cwd
@@ -266,8 +144,6 @@ describe("runSpawn 跨进程身份 env 注入(递归可见性场景 1b)", ()
266
144
  expect(spawnArgs).toContain(getSubagentSessionDir(agentDir, rootCwd));
267
145
  expect(spawnArgs).not.toContain(getSubagentSessionDir(agentDir, checkoutPath));
268
146
 
269
- const child = getLastSpawnedChild();
270
- child.emit("close", 0);
271
- await resultPromise;
147
+ await closeLastChildAndAwait(resultPromise);
272
148
  });
273
149
  });
@@ -6,8 +6,8 @@
6
6
 
7
7
  import { describe, expect, it } from "vitest";
8
8
 
9
- import { buildSpawnArgs } from "../session-runner.ts";
10
- import { parseSpawnLine } from "../spawn-event-adapter.ts";
9
+ import { buildSpawnArgs } from "../engine/engines/pi/session-runner.ts";
10
+ import { parseSpawnLine } from "../engine/engines/pi/spawn-event-adapter.ts";
11
11
 
12
12
  describe("buildSpawnArgs - RPC mode", () => {
13
13
  const baseParams = {