@zhushanwen/subagent-core 0.2.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (347) hide show
  1. package/README.md +45 -2
  2. package/agents/analyst.md +60 -0
  3. package/agents/coder.md +69 -0
  4. package/agents/debugger.md +66 -0
  5. package/agents/doc-reviewer.md +49 -0
  6. package/agents/explorer.md +63 -0
  7. package/agents/general-purpose.md +32 -0
  8. package/agents/orchestrator.md +61 -0
  9. package/agents/planner.md +53 -0
  10. package/agents/researcher.md +67 -0
  11. package/agents/reviewer.md +73 -0
  12. package/dist/chunk-43ONBFZX.js +240 -0
  13. package/dist/chunk-APZY4IME.js +27 -0
  14. package/dist/chunk-V3VHZ2VX.js +59 -0
  15. package/dist/execution/engine/engines/zcode/constants.cjs +62 -9
  16. package/dist/execution/engine/engines/zcode/constants.d.cts +116 -12
  17. package/dist/execution/engine/engines/zcode/constants.d.ts +116 -12
  18. package/dist/execution/engine/engines/zcode/constants.js +39 -7
  19. package/dist/execution/engine/engines/zcode/reader.d.cts +2 -1
  20. package/dist/execution/engine/engines/zcode/reader.d.ts +2 -1
  21. package/dist/execution/engine/engines/zcode/reader.js +4 -234
  22. package/dist/execution/engine/paths.js +7 -19
  23. package/dist/index.cjs +18234 -1277
  24. package/dist/index.d.cts +5646 -673
  25. package/dist/index.d.ts +5646 -673
  26. package/dist/index.js +17828 -1277
  27. package/dist/{types-BxyAidGf.d.cts → types-Dv4QhSJ_.d.cts} +678 -112
  28. package/dist/{types-BxyAidGf.d.ts → types-Dv4QhSJ_.d.ts} +678 -112
  29. package/package.json +7 -4
  30. package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +0 -1
  31. package/src/__tests__/append-system-prompt-assembly.test.ts +8 -7
  32. package/src/__tests__/fr4-get-state-handshake.test.ts +2 -2
  33. package/src/__tests__/m2-append-content-probe.test.ts +0 -1
  34. package/src/__tests__/manifest-store.test.ts +10 -9
  35. package/src/__tests__/record-store-cache.test.ts +0 -2
  36. package/src/__tests__/record-store-index.test.ts +1 -2
  37. package/src/__tests__/review-fix-loop-script.test.ts +93 -3
  38. package/src/__tests__/review-fix-loop-utils.test.ts +31 -10
  39. package/src/__tests__/robustness-low-batch1.test.ts +6 -5
  40. package/src/__tests__/robustness-medium-batch1.test.ts +2 -2
  41. package/src/__tests__/robustness-medium-batch2.test.ts +17 -6
  42. package/src/__tests__/robustness-medium-batch4.test.ts +2 -2
  43. package/src/__tests__/session-runner.test.ts +1 -1
  44. package/src/__tests__/smoke.test.ts +9 -2
  45. package/src/core/__tests__/host-services.test.ts +2 -2
  46. package/src/core/__tests__/logger.test.ts +1 -1
  47. package/src/core/error-message.ts +9 -0
  48. package/src/core/host-services.ts +21 -5
  49. package/src/core/notify-ports.ts +18 -5
  50. package/src/execution/__tests__/agent-profile.test.ts +232 -0
  51. package/src/execution/__tests__/agent-registry.test.ts +370 -0
  52. package/src/execution/__tests__/agent-result-mapper.test.ts +18 -2
  53. package/src/execution/__tests__/agents-assembly.test.ts +219 -0
  54. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +6 -6
  55. package/src/execution/__tests__/chat-engine-routing.test.ts +136 -30
  56. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +5 -4
  57. package/src/execution/__tests__/cold-resurrect.test.ts +283 -0
  58. package/src/execution/__tests__/create-concurrency-pool.test.ts +194 -0
  59. package/src/execution/__tests__/delivery-methods.test.ts +77 -62
  60. package/src/execution/__tests__/descendant-sweep-guards.test.ts +220 -0
  61. package/src/execution/__tests__/descendant-sweep.test.ts +269 -0
  62. package/src/execution/__tests__/dialog-queue.test.ts +199 -1
  63. package/src/execution/__tests__/engine-model-validation.test.ts +741 -0
  64. package/src/execution/__tests__/epipe-fallback.test.ts +31 -24
  65. package/src/execution/__tests__/execute-and-await-worktree.test.ts +21 -91
  66. package/src/execution/__tests__/execute-nesting.test.ts +34 -188
  67. package/src/execution/__tests__/execution-record.test.ts +217 -60
  68. package/src/execution/__tests__/execution-runtime-face.test.ts +272 -0
  69. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +3 -3
  70. package/src/execution/__tests__/finalize-record.test.ts +278 -16
  71. package/src/execution/__tests__/gc-timer.test.ts +57 -2
  72. package/src/execution/__tests__/get-record-for-action-restart.test.ts +14 -12
  73. package/src/execution/__tests__/get-state-handshake.test.ts +127 -0
  74. package/src/execution/__tests__/helpers/mock-extension-api.ts +1 -1
  75. package/src/execution/__tests__/helpers/session-runner-mocks.ts +113 -0
  76. package/src/execution/__tests__/helpers/spawn-mock.ts +150 -3
  77. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +137 -0
  78. package/src/execution/__tests__/keep-alive-no-progress.test.ts +280 -0
  79. package/src/execution/__tests__/kill-all-escalation.test.ts +196 -0
  80. package/src/execution/__tests__/lifecycle-manager-idle-timer-identity.test.ts +117 -0
  81. package/src/execution/__tests__/lifecycle-manager.test.ts +39 -0
  82. package/src/execution/__tests__/lifecycle-predicates.test.ts +1 -2
  83. package/src/execution/__tests__/manifest-parentid.test.ts +4 -4
  84. package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +99 -0
  85. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +113 -0
  86. package/src/execution/__tests__/model-resolver.test.ts +2 -2
  87. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +23 -121
  88. package/src/execution/__tests__/nested-visibility.test.ts +2 -0
  89. package/src/execution/__tests__/notify-ledger.test.ts +1249 -0
  90. package/src/execution/__tests__/output-collector.test.ts +117 -6
  91. package/src/execution/__tests__/pi-invocation.test.ts +1 -1
  92. package/src/execution/__tests__/record-store-orphan-revive.test.ts +159 -0
  93. package/src/execution/__tests__/record-store.test.ts +89 -5
  94. package/src/execution/__tests__/records-cwd-isolation.test.ts +2 -2
  95. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +29 -132
  96. package/src/execution/__tests__/recursive-visibility-env.test.ts +55 -179
  97. package/src/execution/__tests__/rpc-mode.test.ts +2 -2
  98. package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +387 -0
  99. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +19 -9
  100. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +5 -4
  101. package/src/execution/__tests__/run-spawn-edges.test.ts +418 -26
  102. package/src/execution/__tests__/run-spawn-integration.test.ts +3 -3
  103. package/src/execution/__tests__/run-spawn-resume.test.ts +2 -2
  104. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +2 -2
  105. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +16 -8
  106. package/src/execution/__tests__/service-kill-escalation.test.ts +91 -0
  107. package/src/execution/__tests__/session-context-resolver.test.ts +0 -2
  108. package/src/execution/__tests__/session-file-gc.test.ts +35 -0
  109. package/src/execution/__tests__/session-pending.test.ts +471 -0
  110. package/src/execution/__tests__/session-reconstructor.test.ts +366 -15
  111. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +110 -0
  112. package/src/execution/__tests__/session-runner-close-prune.test.ts +181 -0
  113. package/src/execution/__tests__/session-runner-dispatch.test.ts +489 -0
  114. package/src/execution/__tests__/session-runner-epipe.test.ts +4 -3
  115. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +227 -0
  116. package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +290 -0
  117. package/src/execution/__tests__/session-runner-schema-env.test.ts +7 -5
  118. package/src/execution/__tests__/settled-watchdog.test.ts +524 -0
  119. package/src/execution/__tests__/spawn-args.test.ts +11 -10
  120. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +26 -23
  121. package/src/execution/__tests__/spawn-event-adapter.test.ts +1 -1
  122. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +5 -4
  123. package/src/execution/__tests__/spawned-children.test.ts +155 -11
  124. package/src/execution/__tests__/start-sync-model-guard.test.ts +4 -4
  125. package/src/execution/__tests__/stdin-writer.test.ts +6 -4
  126. package/src/execution/__tests__/stream-sink-retirement.test.ts +6 -6
  127. package/src/execution/__tests__/subagent-actions-core.test.ts +1017 -0
  128. package/src/execution/__tests__/subagent-service-message-close.test.ts +99 -35
  129. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +174 -0
  130. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +271 -0
  131. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +33 -29
  132. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +341 -0
  133. package/src/execution/__tests__/subagent-service.test.ts +37 -30
  134. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +88 -12
  135. package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +75 -0
  136. package/src/execution/__tests__/subprocess-agent-runner.test.ts +22 -20
  137. package/src/execution/__tests__/temp-prompt.test.ts +1 -1
  138. package/src/execution/__tests__/timeout-integration.test.ts +13 -12
  139. package/src/execution/__tests__/turn-limiter-semantics.test.ts +1 -1
  140. package/src/execution/__tests__/turn-limiter.test.ts +1 -1
  141. package/src/execution/__tests__/ui-request-handler-factory.test.ts +107 -7
  142. package/src/execution/__tests__/ui-request-handler.test.ts +4 -3
  143. package/src/execution/__tests__/ui-request-queue.test.ts +167 -2
  144. package/src/execution/__tests__/worktree-git-ops.test.ts +571 -0
  145. package/src/execution/__tests__/worktree-manager.test.ts +14 -5
  146. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +4 -3
  147. package/src/execution/__tests__/worktree-reconcile-aging.test.ts +204 -0
  148. package/src/execution/agent-registry.ts +298 -30
  149. package/src/execution/agent-result-mapper.ts +3 -0
  150. package/src/execution/agents-assembly.ts +88 -0
  151. package/src/execution/cold-resurrect.ts +199 -0
  152. package/src/execution/concurrency-pool.ts +71 -9
  153. package/src/execution/dialog-queue.ts +101 -3
  154. package/src/execution/engine/__tests__/common/capability-gate.test.ts +91 -0
  155. package/src/execution/engine/__tests__/common/data-dir.test.ts +1 -1
  156. package/src/execution/engine/__tests__/common/journal-wiring.test.ts +84 -0
  157. package/src/execution/engine/__tests__/common/kill-chain.test.ts +34 -7
  158. package/src/execution/engine/__tests__/common/persona-router.test.ts +4 -5
  159. package/src/execution/engine/__tests__/common/pool-manager.test.ts +211 -51
  160. package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +149 -0
  161. package/src/execution/engine/__tests__/common/session-view-service.test.ts +499 -0
  162. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +56 -99
  163. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +88 -15
  164. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +33 -7
  165. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +1 -1
  166. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +75 -54
  167. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +128 -0
  168. package/src/execution/engine/__tests__/engine-discovery.test.ts +15 -3
  169. package/src/execution/engine/__tests__/model-prompt.test.ts +15 -3
  170. package/src/execution/engine/__tests__/registry.test.ts +94 -3
  171. package/src/execution/engine/__tests__/routing.test.ts +115 -6
  172. package/src/execution/engine/common/capability-gate.ts +92 -0
  173. package/src/execution/engine/common/journal-wiring.ts +97 -0
  174. package/src/execution/engine/common/kill-chain.ts +42 -10
  175. package/src/execution/engine/common/nesting-guard.ts +66 -0
  176. package/src/execution/engine/common/persona-router.ts +18 -8
  177. package/src/execution/engine/common/pool-manager.ts +332 -78
  178. package/src/execution/engine/common/session-view-service.ts +506 -0
  179. package/src/execution/engine/common/session-view-types.ts +125 -0
  180. package/src/execution/engine/engine-discovery.ts +12 -16
  181. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +174 -28
  182. package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +257 -0
  183. package/src/execution/{argv-mirror.ts → engine/engines/pi/argv-mirror.ts} +43 -21
  184. package/src/execution/{get-state-handshake.ts → engine/engines/pi/get-state-handshake.ts} +86 -11
  185. package/src/execution/{output-collector.ts → engine/engines/pi/output-collector.ts} +108 -19
  186. package/src/execution/engine/engines/pi/pi-engine.ts +434 -38
  187. package/src/execution/{pi-invocation.ts → engine/engines/pi/pi-invocation.ts} +2 -2
  188. package/src/execution/engine/engines/pi/reader.ts +11 -0
  189. package/src/execution/engine/engines/pi/registration.ts +4 -2
  190. package/src/execution/engine/engines/pi/session-runner.ts +2840 -0
  191. package/src/execution/{spawn-event-adapter.ts → engine/engines/pi/spawn-event-adapter.ts} +75 -34
  192. package/src/execution/{stdin-writer.ts → engine/engines/pi/stdin-writer.ts} +10 -9
  193. package/src/execution/{temp-prompt.ts → engine/engines/pi/temp-prompt.ts} +2 -2
  194. package/src/execution/{turn-limiter.ts → engine/engines/pi/turn-limiter.ts} +1 -1
  195. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +301 -0
  196. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +36 -0
  197. package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +321 -0
  198. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +473 -0
  199. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +7 -210
  200. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +6 -110
  201. package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +247 -0
  202. package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +451 -0
  203. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +792 -0
  204. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +782 -0
  205. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +168 -0
  206. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +210 -0
  207. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +353 -0
  208. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +282 -0
  209. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +372 -0
  210. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +65 -47
  211. package/src/execution/engine/engines/zcode/appserver-launcher.ts +167 -0
  212. package/src/execution/engine/engines/zcode/connection.ts +598 -0
  213. package/src/execution/engine/engines/zcode/constants.ts +147 -14
  214. package/src/execution/engine/engines/zcode/golden-sample.ts +42 -34
  215. package/src/execution/engine/engines/zcode/parser.ts +12 -346
  216. package/src/execution/engine/engines/zcode/preparer.ts +22 -150
  217. package/src/execution/engine/engines/zcode/session-channel.ts +968 -0
  218. package/src/execution/engine/engines/zcode/zcode-engine.ts +1023 -250
  219. package/src/execution/engine/host-task-spec.ts +32 -36
  220. package/src/execution/engine/model-validation.ts +177 -0
  221. package/src/execution/engine/port.ts +65 -7
  222. package/src/execution/engine/registry.ts +53 -0
  223. package/src/execution/engine/routing.ts +75 -1
  224. package/src/execution/engine/types.ts +30 -103
  225. package/src/execution/execution-record.ts +222 -144
  226. package/src/execution/finalize-record.ts +181 -85
  227. package/src/execution/idle-gc.ts +28 -0
  228. package/src/execution/lifecycle-manager.ts +27 -3
  229. package/src/execution/lifecycle-predicates.ts +1 -1
  230. package/src/execution/manifest-store.ts +53 -62
  231. package/src/execution/notifier.ts +10 -10
  232. package/src/execution/notify-host.ts +212 -0
  233. package/src/execution/notify-ledger.ts +117 -16
  234. package/src/execution/record-entry.ts +8 -2
  235. package/src/execution/record-store.ts +148 -88
  236. package/src/execution/round-settlement.ts +93 -0
  237. package/src/execution/session-file-gc.ts +96 -62
  238. package/src/execution/session-pending.ts +213 -62
  239. package/src/execution/session-reconstructor.ts +256 -143
  240. package/src/execution/sessions-index.ts +127 -102
  241. package/src/execution/settled-watchdog.ts +311 -0
  242. package/src/execution/subagent-actions-core.ts +686 -0
  243. package/src/execution/subagent-service.ts +1106 -907
  244. package/src/execution/subprocess-agent-runner.ts +144 -97
  245. package/src/execution/types.ts +9 -0
  246. package/src/execution/ui-request-handler-factory.ts +126 -75
  247. package/src/execution/ui-request-queue.ts +50 -19
  248. package/src/execution/worktree-git-ops.ts +397 -0
  249. package/src/execution/worktree-manager.ts +72 -10
  250. package/src/execution/worktree-registry.ts +6 -12
  251. package/src/index.ts +542 -9
  252. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +42 -3
  253. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +3 -5
  254. package/src/orchestration/__tests__/agent-call-stream.test.ts +2 -2
  255. package/src/orchestration/__tests__/args-meta.test.ts +358 -0
  256. package/src/orchestration/__tests__/args-validator.test.ts +14 -0
  257. package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +312 -0
  258. package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +384 -0
  259. package/src/orchestration/__tests__/execute-agent-call.test.ts +151 -11
  260. package/src/orchestration/__tests__/file-run-store-prune.test.ts +168 -0
  261. package/src/orchestration/__tests__/file-run-store-throttle.test.ts +168 -0
  262. package/src/orchestration/__tests__/file-run-store.test.ts +390 -0
  263. package/src/orchestration/__tests__/helpers/flush-microtasks.ts +13 -0
  264. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +34 -0
  265. package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +357 -0
  266. package/src/orchestration/__tests__/lifecycle-recover-crashed.test.ts +289 -0
  267. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +3 -3
  268. package/src/orchestration/__tests__/lifecycle.test.ts +84 -5
  269. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +1 -1
  270. package/src/orchestration/__tests__/run-snapshot.test.ts +356 -0
  271. package/src/orchestration/__tests__/script-generate.test.ts +314 -0
  272. package/src/orchestration/__tests__/script-lint.test.ts +17 -0
  273. package/src/orchestration/__tests__/test-mocks.ts +2 -0
  274. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +7 -6
  275. package/src/orchestration/__tests__/worker-message-pump-finalize-run.test.ts +229 -0
  276. package/src/orchestration/__tests__/{error-recovery-handlers.test.ts → worker-message-pump-handlers.test.ts} +28 -27
  277. package/src/orchestration/__tests__/{error-recovery-postmessage-defense.test.ts → worker-message-pump-postmessage-defense.test.ts} +8 -8
  278. package/src/orchestration/__tests__/{error-recovery-serialize-failed-result.test.ts → worker-message-pump-serialize-failed-result.test.ts} +2 -2
  279. package/src/orchestration/__tests__/{error-recovery-workflow-call.test.ts → worker-message-pump-workflow-call.test.ts} +1 -1
  280. package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +275 -0
  281. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +78 -1
  282. package/src/orchestration/__tests__/worker-script-builder.test.ts +1 -1
  283. package/src/orchestration/__tests__/workflow-files.test.ts +186 -0
  284. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +1 -1
  285. package/src/orchestration/__tests__/workflow-run-summary.test.ts +119 -0
  286. package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +124 -0
  287. package/src/orchestration/agent-opts-resolver.ts +8 -8
  288. package/src/orchestration/args-meta.ts +198 -0
  289. package/src/orchestration/args-validator.ts +53 -31
  290. package/src/orchestration/execute-agent-call.ts +25 -79
  291. package/src/orchestration/file-run-store.ts +327 -0
  292. package/src/orchestration/launcher.ts +160 -104
  293. package/src/orchestration/lifecycle.ts +371 -97
  294. package/src/orchestration/models/agent-call.ts +7 -7
  295. package/src/orchestration/models/budget.ts +5 -5
  296. package/src/orchestration/models/ports.ts +4 -4
  297. package/src/orchestration/models/run-runtime.ts +15 -15
  298. package/src/orchestration/models/trace.ts +9 -9
  299. package/src/orchestration/models/types.ts +111 -22
  300. package/src/orchestration/models/workflow-run.ts +28 -28
  301. package/src/orchestration/models/workflow-script.ts +4 -4
  302. package/src/orchestration/run-snapshot.ts +266 -0
  303. package/src/orchestration/script-generate.ts +154 -0
  304. package/src/orchestration/script-lint.ts +123 -90
  305. package/src/orchestration/worker-handle.ts +10 -10
  306. package/src/orchestration/worker-host.ts +10 -10
  307. package/src/orchestration/{error-recovery.ts → worker-message-pump.ts} +498 -221
  308. package/src/orchestration/worker-script-builder.ts +401 -346
  309. package/src/orchestration/workflow-files.ts +37 -11
  310. package/src/orchestration/workflow-run-summary.ts +69 -0
  311. package/src/orchestration/workflow-script-registry-impl.ts +31 -9
  312. package/src/shared/__tests__/agent-ref.test.ts +209 -2
  313. package/src/shared/__tests__/atomic-write.test.ts +267 -0
  314. package/src/shared/__tests__/bounded-serialize.test.ts +236 -0
  315. package/src/shared/__tests__/injection-render.test.ts +518 -0
  316. package/src/shared/__tests__/meta-parser.test.ts +151 -0
  317. package/src/shared/__tests__/resource-discovery-host-roots.test.ts +474 -0
  318. package/src/shared/__tests__/resource-discovery.test.ts +261 -217
  319. package/src/shared/agent-ref.ts +143 -2
  320. package/src/shared/atomic-write.ts +323 -0
  321. package/src/shared/bounded-serialize.ts +154 -0
  322. package/src/shared/injection-render.ts +279 -0
  323. package/src/shared/meta-parser.ts +182 -68
  324. package/src/shared/model-ref.ts +15 -2
  325. package/src/shared/resource-discovery.ts +97 -204
  326. package/src/shared/resource-meta.ts +14 -0
  327. package/src/shared/schema-jsonify.ts +1 -1
  328. package/src/shared/xml-injection.ts +9 -9
  329. package/workflows/README.md +9 -9
  330. package/workflows/chain.js +4 -2
  331. package/workflows/map-reduce.js +5 -3
  332. package/workflows/parallel.js +5 -3
  333. package/workflows/review-fix-loop-utils.cjs +165 -92
  334. package/workflows/review-fix-loop-utils.d.cts +287 -0
  335. package/workflows/review-fix-loop.js +18 -7
  336. package/workflows/scatter-gather.js +4 -2
  337. package/dist/chunk-3VOERJPJ.js +0 -22
  338. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
  339. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  340. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  341. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  342. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  343. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -580
  344. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  345. package/src/execution/execute-options-mapper.ts +0 -115
  346. package/src/execution/session-runner.ts +0 -1789
  347. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -288
@@ -0,0 +1,387 @@
1
+ // src/execution/__tests__/run-and-finalize-anchoring.test.ts
2
+ //
3
+ // runAndFinalize 特征锚定基线(第二批复杂度债务 U04:锚定测试先行)。
4
+ //
5
+ // 既有 run-and-finalize-chatmode.test.ts 覆盖 settle 阶段的 chatMode × closeAfterRound
6
+ // 分流;本文件补直接分支缺口,重构前后行为逐字节对照(错误文案 / 终态写盘 / 事件顺序):
7
+ // G1 pooled acquire 成功 → acquire → runSpawn → release 调用次序 + 轮次回退事件(running)
8
+ // G2 排队 acquire 被中止(signal.aborted)→ finalizeAborted("cancelled by user")
9
+ // G3 acquire 非中止失败 → finalizeFailed 合成 "aborted" 文案(原始错误被吞——现状怪癖锚定)
10
+ // G4 非 chatMode + runSpawn 抛错 → finalizeFailed:closed/gc + archive + manifest 落盘
11
+ // G5 chatMode + runSpawn 抛错 → MF-6 回退:record.result="round did not complete: <msg>"
12
+ // G6 chatMode + 抛错 + CAS 输(record 已 closed)→ 不回退、不 archive、原样返回合成 result
13
+ // G7 chatMode + idle timer armed → early return:settle 不执行(round/closeAfterRound 不动)
14
+ // G8 settle 阶段 CAS 输(record 已 closed)→ 跳过收尾、closeAfterRound 不消费
15
+ // G9 非 chatMode + 失败 result → else 分支:closed/gc + archive + manifest 落盘 + endedAt
16
+ // G10 运行中 abort → closedReason="cancelled" 终态化,result 原样返回(不改写为 cancelled 形态)
17
+ //
18
+ // harness 与 run-and-finalize-chatmode.test.ts 同形态:mock session-runner.runSpawn,
19
+ // 走 SubagentService 真实 runAndFinalize 分流逻辑,断言「最终可观察状态」。
20
+
21
+ import * as fs from "node:fs";
22
+ import * as os from "node:os";
23
+ import * as path from "node:path";
24
+
25
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
26
+
27
+ // mock logger(doFinalizeRecord 的 manifest 写入降级路径用 logger.error)
28
+ const { loggerMock } = vi.hoisted(() => ({
29
+ loggerMock: { debug: vi.fn(), error: vi.fn(), warn: vi.fn() },
30
+ }));
31
+ vi.mock("../../core/logger.ts", () => ({ getLogger: () => loggerMock }));
32
+
33
+ // mock session-runner:runSpawn 受控返回 result / 抛错,killAllSpawnedChildren 空实现。
34
+ // 注意:必须在 import SubagentService 之前 mock(vi.mock 提升到顶部)。
35
+ vi.mock("../engine/engines/pi/session-runner.ts", () => ({
36
+ runSpawn: vi.fn(),
37
+ killAllSpawnedChildren: vi.fn(),
38
+ getChildByRecord: vi.fn(() => undefined),
39
+ registerSpawnedChildForRecord: vi.fn(),
40
+ killRecordChildWithEscalation: vi.fn(),
41
+ spawnedChildren: new Map(),
42
+ }));
43
+
44
+ import { runSpawn } from "../engine/engines/pi/session-runner.ts";
45
+ import type { SessionRunnerContext } from "../engine/engines/pi/session-runner.ts";
46
+ import { armIdleTimer, disarmIdleTimer } from "../lifecycle-manager.ts";
47
+ import { createRecord } from "../execution-record.ts";
48
+ import { ModelConfigService } from "../model-config-service.ts";
49
+ import type { ModelInfo } from "../model-resolver.ts";
50
+ import { getSubagentRecordsDir } from "../path-encoding.ts";
51
+ import { RecordStore } from "../record-store.ts";
52
+ import type { ConcurrencyPool } from "../concurrency-pool.ts";
53
+ import { SubagentService } from "../subagent-service.ts";
54
+ import type { PiLike } from "../subagent-service.ts";
55
+ import type {
56
+ AgentResult,
57
+ ExecutionRecord,
58
+ ExecuteOptions,
59
+ } from "../types.ts";
60
+
61
+ const mockRunSpawn = vi.mocked(runSpawn);
62
+
63
+ // ── 最小合法 ModelInfo(resolveModel 未注入 registry 会抛错,故 stub;runSpawn 被 mock 不消费)──
64
+ const STUB_MODEL: ModelInfo = {
65
+ id: "test-model",
66
+ name: "Test",
67
+ provider: "test",
68
+ reasoning: false,
69
+ };
70
+
71
+ function makeTmpAgentDir(): string {
72
+ return fs.mkdtempSync(path.join(os.tmpdir(), "raf-anchor-test-"));
73
+ }
74
+
75
+ function makePi(): PiLike & {
76
+ appendEntry: ReturnType<typeof vi.fn<(customType: string, data?: unknown) => void>>;
77
+ events: { emit: ReturnType<typeof vi.fn<(channel: string, data: unknown) => void>> };
78
+ sendMessage: ReturnType<typeof vi.fn<(message: Parameters<PiLike["sendMessage"]>[0], options?: Parameters<PiLike["sendMessage"]>[1]) => void>>;
79
+ } {
80
+ return {
81
+ appendEntry: vi.fn((customType: string, data?: unknown) => {}),
82
+ events: { emit: vi.fn((channel: string, data: unknown) => {}) },
83
+ sendMessage: vi.fn(() => {}),
84
+ };
85
+ }
86
+
87
+ function makeResult(success: boolean): AgentResult {
88
+ return {
89
+ text: success ? "done" : "err",
90
+ turns: 1,
91
+ durationMs: 100,
92
+ success,
93
+ error: success ? undefined : "boom",
94
+ sessionId: "sess-1",
95
+ toolCalls: [],
96
+ };
97
+ }
98
+
99
+ function makeRecord(chatMode: boolean, id: string): ExecutionRecord {
100
+ return createRecord(id, {
101
+ agent: "general-purpose",
102
+ model: "test-model",
103
+ mode: "background",
104
+ task: "do something",
105
+ slug: "test",
106
+ startedAt: 1000,
107
+ rootSessionId: "root-session",
108
+ chatMode,
109
+ });
110
+ }
111
+
112
+ /** 暴露 runAndFinalize + store + pool 的私有访问接口(测试专用 cast)。 */
113
+ interface ServiceInternals {
114
+ store: RecordStore;
115
+ pool: ConcurrencyPool;
116
+ runAndFinalize: (
117
+ record: ExecutionRecord,
118
+ opts: ExecuteOptions,
119
+ ctx: SessionRunnerContext,
120
+ identity: {
121
+ agent: string;
122
+ agentConfig: unknown;
123
+ resolved: { model: ModelInfo; thinkingLevel: string | undefined };
124
+ },
125
+ signal: AbortSignal | undefined,
126
+ priority: number,
127
+ ) => Promise<AgentResult>;
128
+ }
129
+
130
+ describe("runAndFinalize 特征锚定基线(U04 重构前后行为对照)", () => {
131
+ let agentDir: string;
132
+ let modelService: ModelConfigService;
133
+ let service: SubagentService;
134
+ let internals: ServiceInternals;
135
+ let pi: ReturnType<typeof makePi>;
136
+ let armedTimerRecordIds: string[];
137
+
138
+ beforeEach(() => {
139
+ // rootCwd 推导基线:PI_SUBAGENT_ROOT_CWD 若被宿主 env 污染会改写 recordsDir 编码段,
140
+ // manifest 落盘断言(G4/G9)依赖「rootCwd = init.cwd」的未设基线。
141
+ delete process.env.PI_SUBAGENT_ROOT_CWD;
142
+ agentDir = makeTmpAgentDir();
143
+ modelService = new ModelConfigService({ agentDir, cwd: agentDir });
144
+ service = new SubagentService({ cwd: agentDir, modelService });
145
+ pi = makePi();
146
+ service.initSession({ pi, sessionId: "root-session" });
147
+ internals = service as unknown as ServiceInternals;
148
+ armedTimerRecordIds = [];
149
+ mockRunSpawn.mockReset();
150
+ });
151
+
152
+ afterEach(() => {
153
+ for (const id of armedTimerRecordIds) disarmIdleTimer(id);
154
+ service.dispose();
155
+ fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
156
+ });
157
+
158
+ /** 直接调私有 runAndFinalize(mock runSpawn 后走完整分流逻辑)。 */
159
+ async function callRunAndFinalize(
160
+ record: ExecutionRecord,
161
+ mockImpl: () => void,
162
+ signal?: AbortSignal,
163
+ ): Promise<AgentResult> {
164
+ mockImpl();
165
+ const opts: ExecuteOptions = { task: "do something", slug: "test" };
166
+ const ctx: SessionRunnerContext = {
167
+ cwd: agentDir,
168
+ agentDir,
169
+ skillDirs: [],
170
+ mainCwd: agentDir,
171
+ sessionRootId: "s-root",
172
+ rootCwd: agentDir,
173
+ };
174
+ const identity = {
175
+ agent: "general-purpose",
176
+ agentConfig: undefined,
177
+ resolved: { model: STUB_MODEL, thinkingLevel: undefined },
178
+ };
179
+ return internals.runAndFinalize(record, opts, ctx, identity, signal, 0);
180
+ }
181
+
182
+ function manifestPath(recordId: string): string {
183
+ return path.join(getSubagentRecordsDir(agentDir, agentDir), `${recordId}.json`);
184
+ }
185
+
186
+ it("G1: pooled acquire 成功 → acquire → runSpawn → release 次序锚定 + 轮次回退事件(running)", async () => {
187
+ const record = makeRecord(false, "sa-anchor-g1");
188
+ internals.store.register(record);
189
+ const pool = internals.pool;
190
+ const acquireSpy = vi.spyOn(pool, "acquire");
191
+ const releaseSpy = vi.spyOn(pool, "release");
192
+
193
+ const result = await callRunAndFinalize(record, () => mockRunSpawn.mockResolvedValueOnce(makeResult(true)));
194
+
195
+ expect(result.text).toBe("done");
196
+ // 事件顺序:acquire 先于 runSpawn,release(finally 收口)最后。
197
+ expect(acquireSpy).toHaveBeenCalledTimes(1);
198
+ expect(mockRunSpawn).toHaveBeenCalledTimes(1);
199
+ expect(releaseSpy).toHaveBeenCalledTimes(1);
200
+ const order = acquireSpy.mock.invocationCallOrder[0];
201
+ const spawnOrder = mockRunSpawn.mock.invocationCallOrder[0];
202
+ const releaseOrder = releaseSpy.mock.invocationCallOrder[0];
203
+ expect(order).toBeLessThan(spawnOrder);
204
+ expect(spawnOrder).toBeLessThan(releaseOrder);
205
+ // one-shot 成功无 closeAfterRound → finalizeRoundToIdle:留内存 + 进程死注销事件(running)。
206
+ expect(record.status).toBe("running");
207
+ expect(internals.store.getMutable(record.id)).toBe(record);
208
+ expect(pi.events.emit).toHaveBeenCalledWith("pending:unregister", { id: record.id, reason: "running" });
209
+ });
210
+
211
+ it("G2: 排队 acquire 被中止(signal.aborted)→ finalizeAborted:'cancelled by user' + closed/cancelled + archive", async () => {
212
+ const record = makeRecord(false, "sa-anchor-g2");
213
+ internals.store.register(record);
214
+ const releaseSpy = vi.spyOn(internals.pool, "release");
215
+ const controller = new AbortController();
216
+ controller.abort(); // pre-aborted:signal.aborted = true
217
+ // 真实 ConcurrencyPool.acquire 对 pre-aborted signal 并不保证 reject——
218
+ // 显式 mock 排队中止(acquire reject + signal.aborted)以触发 catch 的 S1 分支。
219
+ vi.spyOn(internals.pool, "acquire").mockRejectedValueOnce(new Error("AbortError: queued acquire aborted"));
220
+
221
+ const result = await callRunAndFinalize(record, () => mockRunSpawn.mockResolvedValueOnce(makeResult(true)), controller.signal);
222
+
223
+ // 错误路径文案逐字节:S1 对齐已运行被 abort 的 cancel 语义。
224
+ expect(result.error).toBe("cancelled by user");
225
+ expect(result.success).toBe(false);
226
+ // 终态:closed + cancelled + archive(写盘收尾走完),runSpawn 未触达。
227
+ expect(record.status).toBe("closed");
228
+ expect(record.closedReason).toBe("cancelled");
229
+ expect(record.endedAt).toBeDefined();
230
+ expect(internals.store.getMutable(record.id)).toBeUndefined();
231
+ expect(mockRunSpawn).not.toHaveBeenCalled();
232
+ // acquire 失败早退(try/finally 之前)→ 池槽不归还(acquired=false 语义)。
233
+ expect(releaseSpy).not.toHaveBeenCalled();
234
+ expect(pi.events.emit).toHaveBeenCalledWith("pending:unregister", { id: record.id, reason: "closed" });
235
+ });
236
+
237
+ it("G3: acquire 非中止失败 → finalizeFailed 合成 'aborted' 文案(原始错误被吞——现状怪癖锚定)", async () => {
238
+ const record = makeRecord(false, "sa-anchor-g3");
239
+ internals.store.register(record);
240
+ const releaseSpy = vi.spyOn(internals.pool, "release");
241
+ vi.spyOn(internals.pool, "acquire").mockRejectedValueOnce(new Error("pool exploded"));
242
+
243
+ const result = await callRunAndFinalize(record, () => mockRunSpawn.mockResolvedValueOnce(makeResult(true)));
244
+
245
+ // 现状:catch 吞掉原始错误,统一合成 new Error("aborted") → toErrorMessage = "aborted"。
246
+ // "pool exploded" 不透传——重构必须保持该文案(行为逐字节不变)。
247
+ expect(result.error).toBe("aborted");
248
+ expect(result.success).toBe(false);
249
+ expect(record.status).toBe("closed");
250
+ expect(record.closedReason).toBe("gc");
251
+ expect(internals.store.getMutable(record.id)).toBeUndefined();
252
+ expect(mockRunSpawn).not.toHaveBeenCalled();
253
+ expect(releaseSpy).not.toHaveBeenCalled();
254
+ });
255
+
256
+ it("G4: 非 chatMode + runSpawn 抛错 → finalizeFailed:error 透传 + closed/gc + archive + manifest 落盘", async () => {
257
+ const record = makeRecord(false, "sa-anchor-g4");
258
+ internals.store.register(record);
259
+
260
+ const result = await callRunAndFinalize(record, () => mockRunSpawn.mockRejectedValueOnce(new Error("spawn exploded")));
261
+
262
+ // 错误文案:toErrorMessage(Error) → .message 逐字节透传。
263
+ expect(result.error).toBe("spawn exploded");
264
+ expect(result.success).toBe(false);
265
+ // 终态写盘:closed + gc + archive + manifest json 落盘。
266
+ expect(record.status).toBe("closed");
267
+ expect(record.closedReason).toBe("gc");
268
+ expect(record.endedAt).toBeDefined();
269
+ expect(internals.store.getMutable(record.id)).toBeUndefined();
270
+ expect(fs.existsSync(manifestPath(record.id))).toBe(true);
271
+ expect(pi.events.emit).toHaveBeenCalledWith("pending:unregister", { id: record.id, reason: "closed" });
272
+ });
273
+
274
+ it("G5: chatMode + runSpawn 抛错 → MF-6 回退:'round did not complete: <msg>' + running + round+1 + 留内存", async () => {
275
+ const record = makeRecord(true, "sa-anchor-g5");
276
+ internals.store.register(record);
277
+
278
+ const result = await callRunAndFinalize(record, () => mockRunSpawn.mockRejectedValueOnce(new Error("spawn creation failed")));
279
+
280
+ // 合成 failed result 错误文案透传(swallow,不 re-throw)。
281
+ expect(result.error).toBe("spawn creation failed");
282
+ // MF-6:不销毁对话——finalizeRoundToIdle 回退 running-resumable(留内存可重试 message/close)。
283
+ expect(record.status).toBe("running");
284
+ expect(record.closedReason).toBeUndefined(); // [S10] 不残留 gc
285
+ expect(record.round).toBe(1);
286
+ expect(internals.store.getMutable(record.id)).toBe(record);
287
+ // MF-2 写点:失败轮 error 优先 → record.result 固定前缀文案(notify 正文逐字节)。
288
+ expect(record.result).toBe("round did not complete: spawn creation failed");
289
+ // 事件:进程死注销 reason=running(非 closed——record 留内存非终态)。
290
+ expect(pi.events.emit).toHaveBeenCalledWith("pending:unregister", { id: record.id, reason: "running" });
291
+ });
292
+
293
+ it("G6: chatMode + 抛错 + CAS 输(record 已 closed)→ 不回退不 archive,原样返回合成 result", async () => {
294
+ const record = makeRecord(true, "sa-anchor-g6");
295
+ record.status = "closed"; // cancel/收尾抢先:tryTransition(closed, gc) 必败
296
+ internals.store.register(record);
297
+
298
+ const result = await callRunAndFinalize(record, () => mockRunSpawn.mockRejectedValueOnce(new Error("spawn creation failed")));
299
+
300
+ expect(result.error).toBe("spawn creation failed");
301
+ // CAS 输:无任何收尾副作用——状态/轮次不动、留内存、无注销事件。
302
+ expect(record.status).toBe("closed");
303
+ expect(record.round).toBe(0);
304
+ expect(internals.store.getMutable(record.id)).toBe(record);
305
+ expect(pi.events.emit).not.toHaveBeenCalled();
306
+ });
307
+
308
+ it("G7: chatMode + idle timer armed → early return:settle 不执行(round/closeAfterRound 不动)", async () => {
309
+ const record = makeRecord(true, "sa-anchor-g7");
310
+ record.status = "running";
311
+ record.round = 1;
312
+ internals.store.register(record);
313
+ // V2 决策 2/3 形态:onRoundSettled 已 arm idle timer(agent_settled 提前闭环)。
314
+ armIdleTimer(record.id, () => {}, 60_000);
315
+ armedTimerRecordIds.push(record.id);
316
+
317
+ const result = await callRunAndFinalize(record, () => mockRunSpawn.mockResolvedValueOnce(makeResult(true)));
318
+
319
+ // early return:原样返回 runSpawn result,settle 阶段未执行。
320
+ expect(result.text).toBe("done");
321
+ expect(record.status).toBe("running");
322
+ expect(record.round).toBe(1); // 未 +1(round+1 归 onRoundSettled,非本函数)
323
+ expect(record.closeAfterRound).toBeUndefined();
324
+ expect(internals.store.getMutable(record.id)).toBe(record);
325
+ expect(pi.events.emit).not.toHaveBeenCalled();
326
+ });
327
+
328
+ it("G8: settle 阶段 CAS 输(record 已 closed)→ 跳过收尾、closeAfterRound 不消费", async () => {
329
+ const record = makeRecord(false, "sa-anchor-g8");
330
+ record.status = "closed"; // cancel 抢先(A2-1 形态)
331
+ record.closeAfterRound = true;
332
+ internals.store.register(record);
333
+
334
+ const result = await callRunAndFinalize(record, () => mockRunSpawn.mockResolvedValueOnce(makeResult(true)));
335
+
336
+ // 原样返回(不改写为 cancelled 形态);收尾全跳过。
337
+ expect(result.text).toBe("done");
338
+ expect(result.success).toBe(true);
339
+ expect(record.status).toBe("closed");
340
+ expect(record.closeAfterRound).toBe(true); // 标志不被本路径消费
341
+ expect(record.endedAt).toBeUndefined();
342
+ expect(internals.store.getMutable(record.id)).toBe(record);
343
+ expect(pi.events.emit).not.toHaveBeenCalled();
344
+ });
345
+
346
+ it("G9: 非 chatMode + 失败 result → else 分支:closed/gc + archive + manifest 落盘 + endedAt", async () => {
347
+ const record = makeRecord(false, "sa-anchor-g9");
348
+ internals.store.register(record);
349
+
350
+ const result = await callRunAndFinalize(record, () => mockRunSpawn.mockResolvedValueOnce(makeResult(false)));
351
+
352
+ // 失败 result 原样返回(错误文案不经本函数改写)。
353
+ expect(result.success).toBe(false);
354
+ expect(result.error).toBe("boom");
355
+ // 非 chatMode 失败 = 一次性销毁(archive + manifest 落盘)。
356
+ expect(record.status).toBe("closed");
357
+ expect(record.closedReason).toBe("gc");
358
+ expect(record.endedAt).toBeDefined();
359
+ expect(internals.store.getMutable(record.id)).toBeUndefined();
360
+ expect(fs.existsSync(manifestPath(record.id))).toBe(true);
361
+ expect(pi.events.emit).toHaveBeenCalledWith("pending:unregister", { id: record.id, reason: "closed" });
362
+ });
363
+
364
+ it("G10: 运行中 abort → closedReason='cancelled' 终态化,result 原样返回(不改写)", async () => {
365
+ const record = makeRecord(false, "sa-anchor-g10");
366
+ internals.store.register(record);
367
+ const controller = new AbortController();
368
+ // acquire(signal 未中止 → 放行)后、runSpawn resolve 前中止。
369
+ mockRunSpawn.mockImplementationOnce(async () => {
370
+ await new Promise((r) => { setTimeout(r, 15); });
371
+ return makeResult(true);
372
+ });
373
+ setTimeout(() => controller.abort(), 5);
374
+
375
+ const result = await callRunAndFinalize(record, () => {}, controller.signal);
376
+
377
+ // result 原样返回(success true 不被改写为 cancelled 形态——现状锚定)。
378
+ expect(result.success).toBe(true);
379
+ expect(result.text).toBe("done");
380
+ // 终态:closed + cancelled(abort 折入 closed)+ archive。
381
+ expect(record.status).toBe("closed");
382
+ expect(record.closedReason).toBe("cancelled");
383
+ expect(record.endedAt).toBeDefined();
384
+ expect(internals.store.getMutable(record.id)).toBeUndefined();
385
+ expect(pi.events.emit).toHaveBeenCalledWith("pending:unregister", { id: record.id, reason: "closed" });
386
+ });
387
+ });
@@ -25,13 +25,17 @@ vi.mock("../../core/logger.ts", () => ({ getLogger: () => loggerMock }));
25
25
 
26
26
  // mock session-runner:runSpawn 受控返回 result,killAllSpawnedChildren 空实现。
27
27
  // 注意:必须在 import SubagentService 之前 mock(vi.mock 提升到顶部)。
28
- vi.mock("../session-runner.ts", () => ({
28
+ vi.mock("../engine/engines/pi/session-runner.ts", () => ({
29
29
  runSpawn: vi.fn(),
30
30
  killAllSpawnedChildren: vi.fn(),
31
+ getChildByRecord: vi.fn(() => undefined),
32
+ registerSpawnedChildForRecord: vi.fn(),
33
+ killRecordChildWithEscalation: vi.fn(),
34
+ spawnedChildren: new Map(),
31
35
  }));
32
36
 
33
- import { runSpawn } from "../session-runner.ts";
34
- import type { SessionRunnerContext } from "../session-runner.ts";
37
+ import { runSpawn } from "../engine/engines/pi/session-runner.ts";
38
+ import type { SessionRunnerContext } from "../engine/engines/pi/session-runner.ts";
35
39
  import { createRecord } from "../execution-record.ts";
36
40
  import { ModelConfigService } from "../model-config-service.ts";
37
41
  import type { ModelInfo } from "../model-resolver.ts";
@@ -59,11 +63,15 @@ function makeTmpAgentDir(): string {
59
63
  }
60
64
 
61
65
  function makePi(): PiLike & {
62
- appendEntry: ReturnType<typeof vi.fn>;
63
- events: { emit: ReturnType<typeof vi.fn> };
64
- sendMessage: ReturnType<typeof vi.fn>;
66
+ appendEntry: ReturnType<typeof vi.fn<(customType: string, data?: unknown) => void>>;
67
+ events: { emit: ReturnType<typeof vi.fn<(channel: string, data: unknown) => void>> };
68
+ sendMessage: ReturnType<typeof vi.fn<(message: Parameters<PiLike["sendMessage"]>[0], options?: Parameters<PiLike["sendMessage"]>[1]) => void>>;
65
69
  } {
66
- return { appendEntry: vi.fn(), events: { emit: vi.fn() }, sendMessage: vi.fn() };
70
+ return {
71
+ appendEntry: vi.fn((customType: string, data?: unknown) => {}),
72
+ events: { emit: vi.fn((channel: string, data: unknown) => {}) },
73
+ sendMessage: vi.fn(() => {}),
74
+ };
67
75
  }
68
76
 
69
77
  function makeResult(success: boolean): AgentResult {
@@ -116,7 +124,7 @@ describe("runAndFinalize chatMode idle 分流 (M2-A)", () => {
116
124
 
117
125
  beforeEach(() => {
118
126
  agentDir = makeTmpAgentDir();
119
- modelService = new ModelConfigService({ agentDir });
127
+ modelService = new ModelConfigService({ agentDir, cwd: agentDir });
120
128
  service = new SubagentService({ cwd: agentDir, modelService });
121
129
  service.initSession({ pi: makePi(), sessionId: "root-session" });
122
130
  internals = service as unknown as ServiceInternals;
@@ -125,7 +133,7 @@ describe("runAndFinalize chatMode idle 分流 (M2-A)", () => {
125
133
 
126
134
  afterEach(() => {
127
135
  service.dispose();
128
- fs.rmSync(agentDir, { recursive: true, force: true });
136
+ fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
129
137
  });
130
138
 
131
139
  /** 直接调私有 runAndFinalize(mock runSpawn 后走完整分流逻辑)。 */
@@ -137,6 +145,8 @@ describe("runAndFinalize chatMode idle 分流 (M2-A)", () => {
137
145
  agentDir,
138
146
  skillDirs: [],
139
147
  mainCwd: agentDir,
148
+ sessionRootId: "s-root",
149
+ rootCwd: agentDir,
140
150
  };
141
151
  const identity = {
142
152
  agent: "general-purpose",
@@ -60,9 +60,10 @@ vi.mock("../alive-store.ts", () => ({
60
60
  // 非 chatMode 用例 ④ 的 agent_end 走正常 kill 分支(count=0 → SIGTERM)。
61
61
  vi.mock("../session-pending.ts", () => ({
62
62
  readActivePendingFromSessionFile: vi.fn(() => ({ count: 0 })),
63
+ prunePendingCursor: vi.fn(),
63
64
  }));
64
65
 
65
- vi.mock("../temp-prompt.ts", () => ({
66
+ vi.mock("../engine/engines/pi/temp-prompt.ts", () => ({
66
67
  writePromptToTempFile: vi.fn(async (agent: string) => {
67
68
  const safeName = agent.replace(/[^\w.-]+/g, "_");
68
69
  return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
@@ -70,8 +71,8 @@ vi.mock("../temp-prompt.ts", () => ({
70
71
  cleanupTempPrompt: vi.fn(async () => {}),
71
72
  }));
72
73
 
73
- import { runSpawn } from "../session-runner.ts";
74
- import type { SessionRunnerContext } from "../session-runner.ts";
74
+ import { runSpawn } from "../engine/engines/pi/session-runner.ts";
75
+ import type { SessionRunnerContext } from "../engine/engines/pi/session-runner.ts";
75
76
  import {
76
77
  DEFAULT_IDLE_TIMEOUT_MS,
77
78
  hasIdleTimer,
@@ -100,7 +101,7 @@ function makeChatModeRecord(id = "sa-chat"): ExecutionRecord {
100
101
  return createRecord(id, {
101
102
  agent: "general-purpose",
102
103
  model: "test-model",
103
- mode: "sync",
104
+ mode: "background",
104
105
  task: "chat task",
105
106
  slug: "chat",
106
107
  startedAt: 1_000_000,