@zhushanwen/subagent-core 0.5.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (318) hide show
  1. package/README.md +1 -1
  2. package/dist/chunk-KXVBIVOU.js +16 -0
  3. package/dist/chunk-T2SYBW3I.js +24 -0
  4. package/dist/chunk-VURUAGMM.js +2305 -0
  5. package/dist/{types-Dv4QhSJ_.d.cts → engine-discovery-scan-ocpM8FmI.d.cts} +835 -668
  6. package/dist/{types-Dv4QhSJ_.d.ts → engine-discovery-scan-ocpM8FmI.d.ts} +835 -668
  7. package/dist/execution/engine/engine-discovery-scan.cjs +2210 -0
  8. package/dist/execution/engine/engine-discovery-scan.d.cts +4 -0
  9. package/dist/execution/engine/engine-discovery-scan.d.ts +4 -0
  10. package/dist/execution/engine/engine-discovery-scan.js +20 -0
  11. package/dist/execution/engine/paths.cjs +6 -23
  12. package/dist/execution/engine/paths.d.cts +1 -8
  13. package/dist/execution/engine/paths.d.ts +1 -8
  14. package/dist/execution/engine/paths.js +1 -1
  15. package/dist/execution/relay-env.cjs +12 -28
  16. package/dist/execution/relay-env.d.cts +1 -33
  17. package/dist/execution/relay-env.d.ts +1 -33
  18. package/dist/execution/relay-env.js +5 -3
  19. package/dist/index.cjs +14123 -15467
  20. package/dist/index.d.cts +1179 -1540
  21. package/dist/index.d.ts +1179 -1540
  22. package/dist/index.js +7694 -10986
  23. package/dist.bundle/index.cjs +33485 -0
  24. package/package.json +20 -26
  25. package/src/__tests__/record-store-last-line.test.ts +16 -7
  26. package/src/core/error-message.ts +7 -6
  27. package/src/core/host-services.ts +8 -2
  28. package/src/core/notify-ports.ts +17 -2
  29. package/src/execution/__tests__/agent-registry.test.ts +1 -1
  30. package/src/execution/__tests__/alive-store.test.ts +1 -1
  31. package/src/execution/__tests__/chat-engine-routing.test.ts +62 -28
  32. package/src/execution/__tests__/chat-round-first-round-watchdog.test.ts +255 -0
  33. package/src/execution/__tests__/collect-budget.test.ts +291 -0
  34. package/src/execution/__tests__/collect-coordinator-service.test.ts +316 -0
  35. package/src/execution/__tests__/collect-coordinator.test.ts +371 -0
  36. package/src/execution/__tests__/collect-mixed-dispatch.test.ts +229 -0
  37. package/src/execution/__tests__/config-collect-sync.test.ts +120 -0
  38. package/src/execution/__tests__/config.test.ts +1 -1
  39. package/src/execution/__tests__/delivery-methods.test.ts +179 -237
  40. package/src/execution/__tests__/execute-and-await-worktree.test.ts +0 -2
  41. package/src/execution/__tests__/execute-nesting.test.ts +20 -13
  42. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +21 -34
  43. package/src/execution/__tests__/finalized-marker.test.ts +1 -1
  44. package/src/execution/__tests__/gc-timer.test.ts +8 -23
  45. package/src/execution/__tests__/get-record-for-action-restart.test.ts +20 -10
  46. package/src/execution/__tests__/helpers/fake-engine-port.ts +187 -0
  47. package/src/execution/__tests__/helpers/spawn-mock.ts +36 -9
  48. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +1 -1
  49. package/src/execution/__tests__/idle-gc.test.ts +200 -0
  50. package/src/execution/__tests__/lifecycle-predicates.test.ts +26 -21
  51. package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +1 -1
  52. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +5 -2
  53. package/src/execution/__tests__/notify-batch.test.ts +500 -0
  54. package/src/execution/__tests__/notify-ledger.test.ts +27 -0
  55. package/src/execution/__tests__/reconcile-sweep.test.ts +200 -0
  56. package/src/execution/__tests__/record-entry-collect.test.ts +102 -0
  57. package/src/execution/__tests__/record-store.test.ts +60 -6
  58. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +20 -13
  59. package/src/execution/__tests__/relay-env.test.ts +12 -1
  60. package/src/execution/__tests__/round-supervisor.test.ts +293 -0
  61. package/src/execution/__tests__/session-file-gc.test.ts +1 -1
  62. package/src/execution/__tests__/session-pending.test.ts +119 -17
  63. package/src/execution/__tests__/start-collect-guard.test.ts +255 -0
  64. package/src/execution/__tests__/stream-sink-retirement.test.ts +10 -10
  65. package/src/execution/__tests__/subagent-actions-core.test.ts +6 -0
  66. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +14 -11
  67. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +33 -30
  68. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +2 -9
  69. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +59 -129
  70. package/src/execution/__tests__/subagent-service.test.ts +2 -2
  71. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +109 -7
  72. package/src/execution/__tests__/subprocess-agent-runner.test.ts +108 -36
  73. package/src/execution/__tests__/sync-collect-recovery.test.ts +1102 -0
  74. package/src/execution/__tests__/tombstone-store.test.ts +1 -1
  75. package/src/execution/__tests__/workflow-state-root.test.ts +232 -0
  76. package/src/execution/__tests__/worktree-git-ops.test.ts +1 -1
  77. package/src/execution/__tests__/worktree-reconcile-aging.test.ts +2 -2
  78. package/src/execution/__tests__/worktree-reconcile.integration.test.ts +1 -1
  79. package/src/execution/__tests__/worktree-registry.test.ts +1 -1
  80. package/src/execution/agent-registry.ts +6 -2
  81. package/src/execution/collect-coordinator.ts +200 -0
  82. package/src/execution/config.ts +77 -5
  83. package/src/execution/dialog-queue.ts +6 -73
  84. package/src/execution/engine/__tests__/common/capability-gate.test.ts +115 -15
  85. package/src/execution/engine/__tests__/common/errors.test.ts +11 -18
  86. package/src/execution/engine/__tests__/common/event-journal.test.ts +1 -1
  87. package/src/execution/engine/__tests__/common/kill-chain.test.ts +7 -0
  88. package/src/execution/engine/__tests__/common/nesting-guard.test.ts +9 -3
  89. package/src/execution/engine/__tests__/common/pool-manager.test.ts +2 -2
  90. package/src/execution/engine/__tests__/common/session-view-service.test.ts +90 -3
  91. package/src/execution/engine/__tests__/conformance/H9-test-disposition.md +116 -0
  92. package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/fake-engine-protocol.mjs +377 -0
  93. package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/recorded.fixture.json +331 -0
  94. package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/smoke-run.fixture.json +213 -0
  95. package/src/execution/engine/__tests__/conformance/chat-round-protocol.test.ts +230 -0
  96. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +69 -42
  97. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +4 -81
  98. package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +67 -44
  99. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +82 -55
  100. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +133 -70
  101. package/src/execution/engine/__tests__/conformance/engine-crash.test.ts +143 -0
  102. package/src/execution/engine/__tests__/conformance/fake-engine-capabilities.ts +19 -0
  103. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +8 -3
  104. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +6 -5
  105. package/src/execution/engine/__tests__/conformance/protocol-blackbox.test.ts +398 -0
  106. package/src/execution/engine/__tests__/conformance/registry-fork-filter.test.ts +221 -0
  107. package/src/execution/engine/__tests__/conformance/round-liveness-supervisor.test.ts +302 -0
  108. package/src/execution/engine/__tests__/conformance/static-spawn-guard.test.ts +58 -0
  109. package/src/execution/engine/__tests__/engine-discovery-scan.test.ts +558 -0
  110. package/src/execution/engine/__tests__/host-task-spec.test.ts +45 -0
  111. package/src/execution/engine/__tests__/model-ref-split.test.ts +74 -0
  112. package/src/execution/engine/__tests__/registry.test.ts +206 -1
  113. package/src/execution/engine/__tests__/routing.test.ts +97 -0
  114. package/src/execution/engine/__tests__/run-failure-worktree-cleanup.test.ts +97 -0
  115. package/src/execution/engine/client/__tests__/__fixtures__/fake-engine.mjs +294 -0
  116. package/src/execution/engine/client/__tests__/engine-client.test.ts +516 -0
  117. package/src/execution/engine/client/__tests__/mirror.test.ts +67 -0
  118. package/src/execution/engine/client/__tests__/pid-file.test.ts +257 -0
  119. package/src/execution/engine/client/__tests__/protocol-closure.test.ts +95 -0
  120. package/src/execution/engine/client/__tests__/remote-engine.test.ts +457 -0
  121. package/src/execution/engine/client/client-options.ts +103 -0
  122. package/src/execution/engine/client/engine-client.ts +687 -0
  123. package/src/execution/engine/client/mirror.ts +167 -0
  124. package/src/execution/engine/client/pid-file.ts +392 -0
  125. package/src/execution/engine/client/reaper.ts +95 -0
  126. package/src/execution/engine/client/remote-engine.ts +441 -0
  127. package/src/execution/engine/client/reverse-router.ts +255 -0
  128. package/src/execution/engine/common/capability-gate.ts +109 -22
  129. package/src/execution/engine/common/errors.ts +9 -25
  130. package/src/execution/engine/common/event-journal.ts +4 -50
  131. package/src/execution/engine/common/journal-replay.ts +3 -1
  132. package/src/execution/engine/common/kill-chain.ts +18 -251
  133. package/src/execution/engine/common/nesting-guard.ts +18 -114
  134. package/src/execution/engine/common/pool-manager.ts +4 -0
  135. package/src/execution/engine/common/session-view-service.ts +56 -84
  136. package/src/execution/engine/config.ts +151 -0
  137. package/src/execution/engine/d8-compat.ts +346 -0
  138. package/src/execution/engine/engine-discovery-roots.ts +107 -0
  139. package/src/execution/engine/engine-discovery-scan.ts +269 -0
  140. package/src/execution/engine/engine-discovery.ts +30 -7
  141. package/src/execution/engine/engine-inspect-package.ts +282 -0
  142. package/src/execution/engine/engine-manifest.ts +244 -0
  143. package/src/execution/engine/host/__tests__/host-askuser-endpoint.test.ts +123 -0
  144. package/src/execution/engine/host/__tests__/host-bridge.test.ts +135 -0
  145. package/src/execution/engine/host/__tests__/spawned-children.test.ts +73 -0
  146. package/src/execution/engine/host/host-bridge.ts +150 -0
  147. package/src/execution/engine/host/host-ui-endpoint.ts +47 -0
  148. package/src/execution/engine/host/pi-host-binding.ts +91 -0
  149. package/src/execution/engine/host/spawned-children.ts +139 -0
  150. package/src/execution/engine/host-task-spec.ts +29 -2
  151. package/src/execution/engine/model-validation.ts +55 -2
  152. package/src/execution/engine/paths.ts +16 -41
  153. package/src/execution/engine/port.ts +48 -2
  154. package/src/execution/engine/registry.ts +153 -34
  155. package/src/execution/engine/routing.ts +225 -52
  156. package/src/execution/engine/types.ts +25 -200
  157. package/src/execution/execution-record.ts +7 -0
  158. package/src/execution/finalize-record.ts +40 -11
  159. package/src/execution/idle-gc.ts +89 -14
  160. package/src/execution/lifecycle-manager.ts +10 -7
  161. package/src/execution/lifecycle-predicates.ts +10 -8
  162. package/src/execution/notifier.ts +234 -25
  163. package/src/execution/notify-host.ts +21 -1
  164. package/src/execution/record-entry.ts +16 -0
  165. package/src/execution/record-store.ts +189 -31
  166. package/src/execution/relay-env.ts +19 -36
  167. package/src/execution/round-supervisor/domain.ts +89 -0
  168. package/src/execution/round-supervisor/index.ts +33 -0
  169. package/src/execution/round-supervisor/notify-accounting.ts +87 -0
  170. package/src/execution/round-supervisor/reconcile-sweep.ts +235 -0
  171. package/src/execution/round-supervisor/service-binding.test.ts +661 -0
  172. package/src/execution/round-supervisor/service-binding.ts +273 -0
  173. package/src/execution/round-supervisor/supervisor.ts +426 -0
  174. package/src/execution/session-pending.ts +68 -74
  175. package/src/execution/settled-watchdog.ts +39 -0
  176. package/src/execution/subagent-actions-core.ts +62 -6
  177. package/src/execution/subagent-service.ts +1161 -406
  178. package/src/execution/subprocess-agent-runner.ts +16 -8
  179. package/src/execution/sync-rebuild.ts +87 -0
  180. package/src/execution/types.ts +104 -117
  181. package/src/execution/ui-channels.ts +13 -214
  182. package/src/execution/ui-request-handler-factory.ts +7 -4
  183. package/src/execution/ui-request-observability.ts +2 -2
  184. package/src/execution/workflow-state-root.ts +67 -0
  185. package/src/execution/worktree-git-ops.ts +5 -0
  186. package/src/execution/worktree-manager.ts +11 -1
  187. package/src/index.ts +55 -26
  188. package/src/orchestration/__tests__/config-loader.test.ts +1 -1
  189. package/src/orchestration/__tests__/execute-agent-call.test.ts +1 -1
  190. package/src/orchestration/__tests__/file-run-store-prune.test.ts +1 -1
  191. package/src/orchestration/__tests__/file-run-store-throttle.test.ts +1 -1
  192. package/src/orchestration/__tests__/review-fix-loop-scriptpath-failfast.test.ts +1 -1
  193. package/src/orchestration/__tests__/script-generate.test.ts +5 -5
  194. package/src/orchestration/__tests__/skill-discovery.test.ts +2 -2
  195. package/src/orchestration/__tests__/workflow-files.test.ts +10 -10
  196. package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +1 -1
  197. package/src/orchestration/execute-agent-call.ts +1 -1
  198. package/src/orchestration/file-run-store.ts +144 -59
  199. package/src/orchestration/models/types.ts +43 -42
  200. package/src/shared/__tests__/atomic-write.test.ts +1 -1
  201. package/src/shared/__tests__/resource-discovery-host-roots.test.ts +5 -5
  202. package/src/shared/zcode-model-ref.ts +31 -0
  203. package/dist/chunk-43ONBFZX.js +0 -240
  204. package/dist/chunk-A4IVZWVX.js +0 -31
  205. package/dist/chunk-APZY4IME.js +0 -27
  206. package/dist/chunk-V3VHZ2VX.js +0 -59
  207. package/dist/execution/engine/engines/zcode/constants.cjs +0 -107
  208. package/dist/execution/engine/engines/zcode/constants.d.cts +0 -135
  209. package/dist/execution/engine/engines/zcode/constants.d.ts +0 -135
  210. package/dist/execution/engine/engines/zcode/constants.js +0 -54
  211. package/dist/execution/engine/engines/zcode/reader.cjs +0 -276
  212. package/dist/execution/engine/engines/zcode/reader.d.cts +0 -21
  213. package/dist/execution/engine/engines/zcode/reader.d.ts +0 -21
  214. package/dist/execution/engine/engines/zcode/reader.js +0 -9
  215. package/src/__tests__/append-system-prompt-assembly.test.ts +0 -243
  216. package/src/__tests__/fr4-get-state-handshake.test.ts +0 -125
  217. package/src/__tests__/session-runner.test.ts +0 -69
  218. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +0 -490
  219. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +0 -191
  220. package/src/execution/__tests__/descendant-sweep-guards.test.ts +0 -220
  221. package/src/execution/__tests__/descendant-sweep.test.ts +0 -269
  222. package/src/execution/__tests__/engine-model-validation.test.ts +0 -741
  223. package/src/execution/__tests__/epipe-fallback.test.ts +0 -248
  224. package/src/execution/__tests__/execution-runtime-face.test.ts +0 -272
  225. package/src/execution/__tests__/get-state-handshake.test.ts +0 -127
  226. package/src/execution/__tests__/helpers/session-runner-mocks.ts +0 -113
  227. package/src/execution/__tests__/keep-alive-no-progress.test.ts +0 -280
  228. package/src/execution/__tests__/kill-all-escalation.test.ts +0 -196
  229. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +0 -113
  230. package/src/execution/__tests__/output-collector.test.ts +0 -469
  231. package/src/execution/__tests__/pi-invocation.test.ts +0 -134
  232. package/src/execution/__tests__/recursive-visibility-env.test.ts +0 -149
  233. package/src/execution/__tests__/rpc-mode.test.ts +0 -89
  234. package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +0 -387
  235. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +0 -277
  236. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +0 -254
  237. package/src/execution/__tests__/run-spawn-edges.test.ts +0 -1079
  238. package/src/execution/__tests__/run-spawn-integration.test.ts +0 -933
  239. package/src/execution/__tests__/run-spawn-resume.test.ts +0 -322
  240. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +0 -196
  241. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +0 -207
  242. package/src/execution/__tests__/service-kill-escalation.test.ts +0 -91
  243. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +0 -110
  244. package/src/execution/__tests__/session-runner-close-prune.test.ts +0 -181
  245. package/src/execution/__tests__/session-runner-dispatch.test.ts +0 -489
  246. package/src/execution/__tests__/session-runner-epipe.test.ts +0 -179
  247. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +0 -227
  248. package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +0 -290
  249. package/src/execution/__tests__/session-runner-schema-env.test.ts +0 -352
  250. package/src/execution/__tests__/settled-watchdog.test.ts +0 -524
  251. package/src/execution/__tests__/spawn-args.test.ts +0 -446
  252. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +0 -192
  253. package/src/execution/__tests__/spawn-event-adapter.test.ts +0 -167
  254. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +0 -208
  255. package/src/execution/__tests__/spawned-children.test.ts +0 -236
  256. package/src/execution/__tests__/start-sync-model-guard.test.ts +0 -150
  257. package/src/execution/__tests__/stdin-writer.test.ts +0 -464
  258. package/src/execution/__tests__/subagent-service-message-close.test.ts +0 -693
  259. package/src/execution/__tests__/temp-prompt.test.ts +0 -53
  260. package/src/execution/__tests__/timeout-integration.test.ts +0 -616
  261. package/src/execution/__tests__/turn-limiter-semantics.test.ts +0 -194
  262. package/src/execution/__tests__/turn-limiter.test.ts +0 -65
  263. package/src/execution/__tests__/ui-request-handler.test.ts +0 -205
  264. package/src/execution/__tests__/ui-request-queue.test.ts +0 -298
  265. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +0 -230
  266. package/src/execution/engine/__tests__/common/schema-emulation.test.ts +0 -128
  267. package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +0 -149
  268. package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +0 -28
  269. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +0 -128
  270. package/src/execution/engine/__tests__/paths.test.ts +0 -39
  271. package/src/execution/engine/common/schema-emulation.ts +0 -189
  272. package/src/execution/engine/common/session-view-projection.ts +0 -51
  273. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +0 -615
  274. package/src/execution/engine/engines/pi/__tests__/reader.test.ts +0 -155
  275. package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +0 -257
  276. package/src/execution/engine/engines/pi/argv-mirror.ts +0 -135
  277. package/src/execution/engine/engines/pi/get-state-handshake.ts +0 -179
  278. package/src/execution/engine/engines/pi/output-collector.ts +0 -317
  279. package/src/execution/engine/engines/pi/pi-engine.ts +0 -811
  280. package/src/execution/engine/engines/pi/pi-invocation.ts +0 -120
  281. package/src/execution/engine/engines/pi/reader.ts +0 -59
  282. package/src/execution/engine/engines/pi/registration.ts +0 -37
  283. package/src/execution/engine/engines/pi/session-runner.ts +0 -2840
  284. package/src/execution/engine/engines/pi/spawn-event-adapter.ts +0 -404
  285. package/src/execution/engine/engines/pi/stdin-writer.ts +0 -199
  286. package/src/execution/engine/engines/pi/temp-prompt.ts +0 -62
  287. package/src/execution/engine/engines/pi/turn-limiter.ts +0 -102
  288. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +0 -301
  289. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +0 -36
  290. package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +0 -321
  291. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +0 -473
  292. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +0 -43
  293. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +0 -124
  294. package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +0 -210
  295. package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +0 -71
  296. package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +0 -247
  297. package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +0 -451
  298. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +0 -792
  299. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +0 -782
  300. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +0 -168
  301. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +0 -210
  302. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +0 -353
  303. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +0 -282
  304. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +0 -372
  305. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +0 -145
  306. package/src/execution/engine/engines/zcode/appserver-launcher.ts +0 -167
  307. package/src/execution/engine/engines/zcode/connection.ts +0 -598
  308. package/src/execution/engine/engines/zcode/constants.ts +0 -176
  309. package/src/execution/engine/engines/zcode/golden-sample.ts +0 -47
  310. package/src/execution/engine/engines/zcode/parser.ts +0 -102
  311. package/src/execution/engine/engines/zcode/preparer.ts +0 -235
  312. package/src/execution/engine/engines/zcode/reader.ts +0 -381
  313. package/src/execution/engine/engines/zcode/registration.ts +0 -37
  314. package/src/execution/engine/engines/zcode/session-channel.ts +0 -968
  315. package/src/execution/engine/engines/zcode/zcode-engine.ts +0 -1431
  316. package/src/execution/round-settlement.ts +0 -93
  317. package/src/execution/ui-request-queue.ts +0 -215
  318. package/src/shared/schema-env.ts +0 -44
@@ -1,167 +0,0 @@
1
- // src/__tests__/spawn-event-adapter.test.ts
2
- import * as fs from "node:fs";
3
- import * as os from "node:os";
4
- import * as path from "node:path";
5
-
6
- import { describe, expect, it } from "vitest";
7
-
8
- import {
9
- deriveSessionFilePath,
10
- findSessionFileByHeaderId,
11
- parseSpawnLine,
12
- } from "../engine/engines/pi/spawn-event-adapter.ts";
13
-
14
- describe("parseSpawnLine", () => {
15
- describe("空白行", () => {
16
- it("空字符串返回 null", () => {
17
- expect(parseSpawnLine("")).toBeNull();
18
- });
19
-
20
- it("纯空白返回 null", () => {
21
- expect(parseSpawnLine(" ")).toBeNull();
22
- expect(parseSpawnLine("\t\t")).toBeNull();
23
- });
24
- });
25
-
26
- describe("header 行", () => {
27
- it("type=session + id 识别为 header", () => {
28
- const line = JSON.stringify({
29
- type: "session",
30
- id: "abc-123",
31
- timestamp: "2026-07-03T12:00:00.000Z",
32
- cwd: "/home/user/project",
33
- });
34
- const result = parseSpawnLine(line);
35
- expect(result?.kind).toBe("header");
36
- if (result?.kind === "header") {
37
- expect(result.header.id).toBe("abc-123");
38
- expect(result.header.cwd).toBe("/home/user/project");
39
- }
40
- });
41
-
42
- it("header 含 parentSession + version", () => {
43
- const line = JSON.stringify({
44
- type: "session",
45
- id: "child-456",
46
- timestamp: "2026-07-03T12:00:00.000Z",
47
- cwd: "/home/user/project",
48
- parentSession: "parent-789",
49
- version: 2,
50
- });
51
- const result = parseSpawnLine(line);
52
- expect(result?.kind).toBe("header");
53
- if (result?.kind === "header") {
54
- expect(result.header.parentSession).toBe("parent-789");
55
- expect(result.header.version).toBe(2);
56
- }
57
- });
58
- });
59
-
60
- describe("事件行", () => {
61
- it("tool_execution_start 识别为 event", () => {
62
- const line = JSON.stringify({
63
- type: "tool_execution_start",
64
- toolName: "bash",
65
- toolCallId: "call-1",
66
- args: { command: "ls" },
67
- });
68
- const result = parseSpawnLine(line);
69
- expect(result?.kind).toBe("event");
70
- if (result?.kind === "event") {
71
- expect(result.event.type).toBe("tool_execution_start");
72
- expect(result.event.toolName).toBe("bash");
73
- }
74
- });
75
-
76
- it("message_end 识别为 event", () => {
77
- const line = JSON.stringify({
78
- type: "message_end",
79
- message: {
80
- role: "assistant",
81
- usage: { input: 100, output: 50 },
82
- },
83
- });
84
- const result = parseSpawnLine(line);
85
- expect(result?.kind).toBe("event");
86
- if (result?.kind === "event") {
87
- expect(result.event.type).toBe("message_end");
88
- }
89
- });
90
-
91
- it("turn_end 识别为 event", () => {
92
- const result = parseSpawnLine(JSON.stringify({ type: "turn_end" }));
93
- expect(result?.kind).toBe("event");
94
- });
95
-
96
- it("未知 type 仍识别为 event(schema 校验由调用方)", () => {
97
- const result = parseSpawnLine(JSON.stringify({ type: "some_future_event" }));
98
- expect(result?.kind).toBe("event");
99
- });
100
- });
101
-
102
- describe("invalid 行", () => {
103
- it("非 JSON 返回 invalid", () => {
104
- const result = parseSpawnLine("not json at all");
105
- expect(result?.kind).toBe("invalid");
106
- if (result?.kind === "invalid") {
107
- expect(result.raw).toBe("not json at all");
108
- expect(result.error).toBeTruthy();
109
- }
110
- });
111
-
112
- it("JSON 但无 type 字段返回 invalid", () => {
113
- const result = parseSpawnLine(JSON.stringify({ foo: "bar" }));
114
- expect(result?.kind).toBe("invalid");
115
- if (result?.kind === "invalid") {
116
- expect(result.error).toContain("type");
117
- }
118
- });
119
-
120
- it("JSON 但 type 非 string 返回 invalid", () => {
121
- const result = parseSpawnLine(JSON.stringify({ type: 123 }));
122
- expect(result?.kind).toBe("invalid");
123
- });
124
-
125
- it("JSON null 返回 invalid", () => {
126
- const result = parseSpawnLine("null");
127
- expect(result?.kind).toBe("invalid");
128
- });
129
- });
130
- });
131
-
132
- describe("deriveSessionFilePath", () => {
133
- it("拼接 sessionDir + fileTimestamp(冒号点转连字符) + id", () => {
134
- const header = {
135
- type: "session" as const,
136
- id: "abc-123",
137
- timestamp: "2026-07-03T12:00:00.000Z",
138
- cwd: "/proj",
139
- };
140
- const path = deriveSessionFilePath(header, "/sessions/dir");
141
- // fileTimestamp = timestamp.replace(/[:.]/g, "-") = "2026-07-03T12-00-00-000Z"
142
- expect(path).toBe("/sessions/dir/2026-07-03T12-00-00-000Z_abc-123.jsonl");
143
- });
144
- });
145
-
146
- describe("findSessionFileByHeaderId", () => {
147
- it("sessionId 后缀匹配返回实际文件路径", () => {
148
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "spawn-adapter-test-"));
149
- const expectedFile = path.join(tmpDir, "2026-07-03T12-00-00-000Z_sid-456.jsonl");
150
- fs.writeFileSync(expectedFile, "{}");
151
- const result = findSessionFileByHeaderId(tmpDir, "sid-456");
152
- expect(result).toBe(expectedFile);
153
- fs.rmSync(tmpDir, { recursive: true });
154
- });
155
-
156
- it("无匹配返回 undefined", () => {
157
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "spawn-adapter-test-"));
158
- const result = findSessionFileByHeaderId(tmpDir, "nonexistent");
159
- expect(result).toBeUndefined();
160
- fs.rmSync(tmpDir, { recursive: true });
161
- });
162
-
163
- it("目录不存在返回 undefined(不抛错)", () => {
164
- const result = findSessionFileByHeaderId("/nonexistent/path/xyz", "sid");
165
- expect(result).toBeUndefined();
166
- });
167
- });
@@ -1,208 +0,0 @@
1
- // src/execution/__tests__/spawn-worktree-guidance.test.ts
2
- //
3
- // worktree 模式认知纠正提示注入测试。
4
- //
5
- // 背景:worktree checkout 放 os.tmpdir()(路径形似临时沙箱),子 agent 无 worktree 语义
6
- // 提示时误判 cwd 为"空隔离目录",主动 cd 别处放弃隔离(wave-agent 事故 session 019ff64c)。
7
- // 修复在 runSpawn 的 appendParts 注入 WORKTREE_GUIDANCE_PROMPT。
8
- //
9
- // 验证:
10
- // - worktree 模式(opts.worktree 传入)→ appendSystemPrompt content 含 WORKTREE_GUIDANCE_PROMPT
11
- // - 非 worktree 模式 → content 不含 worktree 提示
12
- //
13
- // 测试策略:mock writePromptToTempFile 捕获拼接后的 appendParts content(vi.hoisted 防
14
- // vi.mock hoisting 引用未初始化变量),断言含/不含 worktree 提示标识。
15
-
16
- import type { PassThrough } from "node:stream";
17
-
18
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
19
-
20
- // 捕获 writePromptToTempFile 的 content 参数(appendParts.join("\n\n"))。
21
- // vi.hoisted 保证变量在 vi.mock factory hoisting 前已声明可用。
22
- const captured = vi.hoisted(() => ({ content: "" }));
23
-
24
- // ── mock modules(与 session-runner-schema-env.test.ts 同模式)──
25
-
26
- vi.mock("node:child_process", async () => {
27
- const { EventEmitter } = await import("node:events");
28
- const { PassThrough } = await import("node:stream");
29
-
30
- class FakeChild extends EventEmitter {
31
- pid = 12345;
32
- stdout = new PassThrough();
33
- stdin = new PassThrough(); // runSpawn 注册 stdin EPIPE handler(session-runner),FakeChild 必须提供
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
- execFileSync: vi.fn(() => ""),
47
- };
48
- });
49
-
50
- vi.mock("node:fs", async () => {
51
- const actual = await import("node:fs");
52
- return {
53
- default: {
54
- ...actual,
55
- mkdirSync: vi.fn(),
56
- existsSync: vi.fn(() => false),
57
- appendFileSync: vi.fn(),
58
- writeFileSync: vi.fn(),
59
- readdirSync: vi.fn(() => []),
60
- },
61
- mkdirSync: vi.fn(),
62
- existsSync: vi.fn(() => false),
63
- appendFileSync: vi.fn(),
64
- writeFileSync: vi.fn(),
65
- readdirSync: vi.fn(() => []),
66
- promises: actual.promises,
67
- };
68
- });
69
-
70
- vi.mock("../alive-store.ts", () => ({
71
- writeAliveMarker: vi.fn(),
72
- }));
73
-
74
- vi.mock("../engine/engines/pi/temp-prompt.ts", () => ({
75
- writePromptToTempFile: vi.fn(async (agent: string, content: string) => {
76
- captured.content = content;
77
- const safeName = agent.replace(/[^\w.-]+/g, "_");
78
- return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt.md` };
79
- }),
80
- cleanupTempPrompt: vi.fn(async () => {}),
81
- }));
82
-
83
- import { spawn } from "node:child_process";
84
-
85
- import { createRecord } from "../execution-record.ts";
86
- import { runSpawn, type RunOptions, type SessionRunnerContext } from "../engine/engines/pi/session-runner.ts";
87
-
88
- const mockSpawn = vi.mocked(spawn);
89
-
90
- interface FakeChild {
91
- pid: number;
92
- stdout: PassThrough;
93
- stderr: PassThrough;
94
- killed: boolean;
95
- killSignal: string | undefined;
96
- kill(sig?: string): boolean;
97
- emit(event: string, ...args: unknown[]): boolean;
98
- }
99
-
100
- function getLastSpawnedChild(): FakeChild {
101
- const result = mockSpawn.mock.results.at(-1);
102
- if (!result) throw new Error("spawn was not called yet");
103
- return result.value as FakeChild;
104
- }
105
-
106
- async function waitForSpawn(timeoutMs = 1000): Promise<void> {
107
- const start = Date.now();
108
- while (mockSpawn.mock.results.length === 0) {
109
- if (Date.now() - start > timeoutMs) {
110
- throw new Error(`spawn was not called within ${timeoutMs}ms`);
111
- }
112
- await new Promise((r) => setTimeout(r, 5));
113
- }
114
- }
115
-
116
- function makeRecord() {
117
- return createRecord("wt-guidance-1", {
118
- agent: "general-purpose",
119
- model: "test/model",
120
- mode: "background",
121
- task: "test task",
122
- slug: "test",
123
- startedAt: Date.now(),
124
- rootSessionId: "s1",
125
- parentRecordId: undefined,
126
- depth: 0,
127
- });
128
- }
129
-
130
- function makeRunOpts(overrides: Partial<RunOptions> = {}): RunOptions {
131
- return {
132
- resolved: { model: { provider: "test", id: "model", name: "Model", reasoning: false }, thinkingLevel: undefined },
133
- agentConfig: undefined,
134
- appendSystemPrompt: undefined,
135
- skillPath: undefined,
136
- schema: undefined,
137
- maxTurns: undefined,
138
- graceTurns: undefined,
139
- signal: undefined,
140
- onEvent: undefined,
141
- ...overrides,
142
- };
143
- }
144
-
145
- function makeCtx(overrides: Partial<SessionRunnerContext> = {}): SessionRunnerContext {
146
- return {
147
- cwd: "/fake/cwd",
148
- agentDir: "/fake/agent",
149
- skillDirs: [],
150
- mainCwd: "/fake/cwd",
151
- sessionRootId: "root-session-test",
152
- rootCwd: "/fake/cwd",
153
- ...overrides,
154
- };
155
- }
156
-
157
- describe("worktree guidance prompt injection", () => {
158
- beforeEach(() => {
159
- vi.clearAllMocks();
160
- captured.content = "";
161
- });
162
-
163
- afterEach(() => {
164
- vi.restoreAllMocks();
165
- });
166
-
167
- it("worktree 模式 → appendSystemPrompt content 含 worktree 认知纠正提示", async () => {
168
- const record = makeRecord();
169
- const opts = makeRunOpts({
170
- worktree: {
171
- path: "/tmp/pi-subagents/--fake--/pi-sub-sa-wt-guidance-1",
172
- branch: "pi-sub-sa-wt-guidance-1",
173
- baseCommit: "abc123",
174
- mainCwd: "/fake/cwd",
175
- },
176
- });
177
- const ctx = makeCtx();
178
-
179
- const resultPromise = runSpawn(record, "test task", opts, ctx);
180
- await waitForSpawn();
181
-
182
- // content 含 WORKTREE_GUIDANCE_PROMPT 的标题
183
- expect(captured.content).toContain("Git Worktree");
184
- // 含关键纠正信息:cwd 含完整项目代码(非临时沙箱)
185
- expect(captured.content).toContain("complete project source code");
186
- // 含防 cd 别处的行为指引
187
- expect(captured.content).toContain("Do NOT");
188
-
189
- const child = getLastSpawnedChild();
190
- child.emit("close", 0);
191
- await resultPromise;
192
- });
193
-
194
- it("非 worktree 模式 → content 不含 worktree 认知提示", async () => {
195
- const record = makeRecord();
196
- const opts = makeRunOpts(); // 不传 worktree
197
- const ctx = makeCtx();
198
-
199
- const resultPromise = runSpawn(record, "test task", opts, ctx);
200
- await waitForSpawn();
201
-
202
- expect(captured.content).not.toContain("Git Worktree");
203
-
204
- const child = getLastSpawnedChild();
205
- child.emit("close", 0);
206
- await resultPromise;
207
- });
208
- });
@@ -1,236 +0,0 @@
1
- // src/execution/__tests__/spawned-children.test.ts
2
- //
3
- // spawnedChildren Map + getChildByRecord + killAllSpawnedChildren 单元测试(M2-B1)。
4
- //
5
- // M2-B1 把 spawnedChildren 从 Set<ChildProcess> 改为 Map<recordId, ChildProcess>,
6
- // 建立 record→child 映射(busy 投递定位活进程用,设计决策 6)。本文件测 Map API 核心行为:
7
- // - getChildByRecord:set 后能查到;delete 后查不到;未注册返回 undefined
8
- // - killAllSpawnedChildren:遍历 .values() kill + clear;单 child kill 抛错不阻断
9
- // - [R1 D6③] killAllSpawnedChildren 编排扩容:先触发 engine registry dispose(触发不等待),
10
- // 后杀 per-record children——dispose 全部先于任何 kill(D6① 时序)
11
- //
12
- // [T2-⑤ / LC-2] 跳过条件变更(原「跳过 child.killed=true」契约已废止):killed=发过
13
- // kill 请求 ≠ 已死,只有 exitCode/signalCode 非 null(已确认死亡)才跳过;killed 但
14
- // 未确认死亡的 child 补 SIGKILL 升级(SIGTERM 可能被无视)。新契约的完整行为矩阵见
15
- // kill-all-escalation.test.ts(runSpawn 集成面),本文件保留纯 Map 遍历面的正/反例。
16
- //
17
- // 直接操作模块级 spawnedChildren(afterEach clear 防泄漏),不依赖 runSpawn/spawn/fs。
18
-
19
- import type { ChildProcess } from "node:child_process";
20
-
21
- import { afterEach, describe, expect, it, vi } from "vitest";
22
-
23
- const { loggerMock } = vi.hoisted(() => ({
24
- loggerMock: { debug: vi.fn(), warn: vi.fn(), error: vi.fn() },
25
- }));
26
- // mock core logger:session-runner 与 engine/registry 共用同一 facade 模块(两处
27
- // `../../core/logger.ts` 解析到同一文件),本 mock 对二者同时生效。
28
- vi.mock("../../core/logger.ts", () => ({ getLogger: () => loggerMock }));
29
-
30
- import type { EnginePort } from "../engine/port.ts";
31
- import { clearEngines, getEngine, registerEngine } from "../engine/registry.ts";
32
- import { getChildByRecord, killAllSpawnedChildren, spawnedChildren } from "../engine/engines/pi/session-runner.ts";
33
-
34
- /**
35
- * 假 child:只关心 killed 标记 + kill 调用(killAllSpawnedChildren 遍历用)。
36
- * extra 可注入 exitCode/signalCode(模拟已确认死亡的形态)。
37
- */
38
- function makeFakeChild(
39
- killed = false,
40
- extra: Partial<Pick<ChildProcess, "exitCode" | "signalCode">> = {},
41
- ): ChildProcess & { kill: ReturnType<typeof vi.fn> } {
42
- return { killed, kill: vi.fn(), ...extra } as unknown as ChildProcess & {
43
- kill: ReturnType<typeof vi.fn>;
44
- };
45
- }
46
-
47
- /** 带 dispose 的假引擎(编排测试用——dispose 触发顺序记进 events,与 child kill 事件共序)。 */
48
- function makeDisposingEngine(
49
- id: string,
50
- events: string[],
51
- disposeImpl?: () => Promise<void>,
52
- ): EnginePort {
53
- return {
54
- id,
55
- capabilities: () => ({
56
- schemaEnforcement: "native",
57
- steer: "unsupported",
58
- conversation: "unsupported",
59
- personaInjection: "prompt",
60
- eventGranularity: "coarse",
61
- sandbox: "none",
62
- sessionRead: "outcome-only",
63
- resume: "unsupported",
64
- interrupt: "kill-only",
65
- permissionMode: "fixed",
66
- maxTurns: false,
67
- }),
68
- probe: () =>
69
- Promise.resolve({ ok: true, engineVersion: "0.0.0-test", checks: [{ name: "stub", ok: true }] }),
70
- run: () => Promise.reject(new Error("fake engine: run not implemented")),
71
- interact: () => Promise.resolve({ ok: false, code: "engine_capability_unsupported", message: "stub" }),
72
- read: () => Promise.resolve({ engineId: id, turns: [], source: "outcome-only" }),
73
- dispose: disposeImpl ?? (() => {
74
- events.push(`dispose:${id}`);
75
- return Promise.resolve();
76
- }),
77
- };
78
- }
79
-
80
- describe("spawnedChildren Map + getChildByRecord (M2-B1)", () => {
81
- afterEach(() => {
82
- spawnedChildren.clear();
83
- });
84
-
85
- it("set(recordId, child) 后 getChildByRecord 能查到;未注册返回 undefined", () => {
86
- const child = makeFakeChild();
87
- spawnedChildren.set("rec-1", child);
88
- expect(getChildByRecord("rec-1")).toBe(child);
89
- expect(getChildByRecord("rec-2")).toBeUndefined();
90
- });
91
-
92
- it("delete(recordId) 后 getChildByRecord 返回 undefined(close/error 回调语义)", () => {
93
- const child = makeFakeChild();
94
- spawnedChildren.set("rec-1", child);
95
- spawnedChildren.delete("rec-1");
96
- expect(getChildByRecord("rec-1")).toBeUndefined();
97
- });
98
-
99
- it("killAllSpawnedChildren 遍历 .values() 对每个未 killed child kill + clear", () => {
100
- const c1 = makeFakeChild();
101
- const c2 = makeFakeChild();
102
- spawnedChildren.set("rec-a", c1);
103
- spawnedChildren.set("rec-b", c2);
104
-
105
- const n = killAllSpawnedChildren();
106
-
107
- expect(n).toBe(2);
108
- expect(c1.kill).toHaveBeenCalledTimes(1);
109
- expect(c2.kill).toHaveBeenCalledTimes(1);
110
- expect(spawnedChildren.size).toBe(0);
111
- });
112
-
113
- it("[T2-⑤] killed 但未确认死亡 → 不跳过,补 SIGKILL 升级(killed=发过请求≠已死)", () => {
114
- const alive = makeFakeChild(false);
115
- const killedNotClosed = makeFakeChild(true); // SIGTERM 已发、exit/signal 仍 null
116
- spawnedChildren.set("rec-1", alive);
117
- spawnedChildren.set("rec-2", killedNotClosed);
118
-
119
- const n = killAllSpawnedChildren();
120
-
121
- expect(n).toBe(2);
122
- expect(alive.kill).toHaveBeenCalledWith("SIGTERM");
123
- // dispose 是最后兜底,对 killed-not-closed 直接升级 SIGKILL(不再赌 SIGTERM 生效)
124
- expect(killedNotClosed.kill).toHaveBeenCalledWith("SIGKILL");
125
- expect(spawnedChildren.size).toBe(0);
126
- });
127
-
128
- it("[T2-⑤] killed 且已确认死亡(signalCode 非 null)→ 跳过,不重复发信号", () => {
129
- const dead = makeFakeChild(true, { signalCode: "SIGTERM" });
130
- spawnedChildren.set("rec-1", dead);
131
-
132
- const n = killAllSpawnedChildren();
133
-
134
- expect(n).toBe(0);
135
- expect(dead.kill).not.toHaveBeenCalled();
136
- expect(spawnedChildren.size).toBe(0);
137
- });
138
-
139
- it("killAllSpawnedChildren 对单个 child kill 抛错不阻断其他(best-effort catch)", () => {
140
- const ok = makeFakeChild();
141
- const throwy = makeFakeChild();
142
- throwy.kill.mockImplementation(() => {
143
- throw new Error("ESRCH");
144
- });
145
- spawnedChildren.set("rec-1", ok);
146
- spawnedChildren.set("rec-2", throwy);
147
-
148
- const n = killAllSpawnedChildren();
149
-
150
- // throwy 抛错被 catch,不计入 n;ok 正常 kill。两者都调过 kill,Map 已 clear。
151
- expect(n).toBe(1);
152
- expect(ok.kill).toHaveBeenCalledTimes(1);
153
- expect(throwy.kill).toHaveBeenCalledTimes(1);
154
- expect(spawnedChildren.size).toBe(0);
155
- });
156
- });
157
-
158
- describe("killAllSpawnedChildren 编排扩容(R1 D6③:先引擎 dispose 后 per-record children)", () => {
159
- afterEach(() => {
160
- spawnedChildren.clear();
161
- // registry 是进程级全局状态(Symbol.for 槽),防用例间 fake 引擎泄漏串扰
162
- clearEngines();
163
- });
164
-
165
- it("先全部触发已实例化引擎 dispose,后遍历杀 children(D6① 时序:dispose 先于 SIGTERM)", () => {
166
- const events: string[] = [];
167
- registerEngine("e-alpha", () => makeDisposingEngine("e-alpha", events));
168
- registerEngine("e-beta", () => makeDisposingEngine("e-beta", events));
169
- getEngine("e-alpha"); // disposeEngines 只遍历已实例化单例,须先实例化
170
- getEngine("e-beta");
171
-
172
- const c1 = makeFakeChild();
173
- const c2 = makeFakeChild();
174
- c1.kill.mockImplementation(() => {
175
- events.push("kill:rec-a");
176
- });
177
- c2.kill.mockImplementation(() => {
178
- events.push("kill:rec-b");
179
- });
180
- spawnedChildren.set("rec-a", c1);
181
- spawnedChildren.set("rec-b", c2);
182
-
183
- const n = killAllSpawnedChildren();
184
-
185
- expect(n).toBe(2);
186
- // 时序断言(不依赖穿插序):最后一个 dispose 的索引 < 第一个 kill 的索引
187
- const disposeIdx = events
188
- .map((e, i) => (e.startsWith("dispose:") ? i : -1))
189
- .filter((i) => i >= 0);
190
- const firstKill = events.findIndex((e) => e.startsWith("kill:"));
191
- expect(disposeIdx.length).toBe(2);
192
- expect(firstKill).toBeGreaterThan(Math.max(...disposeIdx));
193
- expect(spawnedChildren.size).toBe(0);
194
- });
195
-
196
- it("引擎 dispose 同步 throw 不阻断 children 清理(best-effort 编排,catch 记日志继续)", () => {
197
- const events: string[] = [];
198
- registerEngine(
199
- "throwy",
200
- () =>
201
- makeDisposingEngine("throwy", events, () => {
202
- throw new Error("dispose boom");
203
- }),
204
- );
205
- getEngine("throwy");
206
- const child = makeFakeChild();
207
- child.kill.mockImplementation(() => {
208
- events.push("kill:rec-1");
209
- });
210
- spawnedChildren.set("rec-1", child);
211
-
212
- expect(() => killAllSpawnedChildren()).not.toThrow();
213
- expect(child.kill).toHaveBeenCalledTimes(1);
214
- expect(spawnedChildren.size).toBe(0);
215
- expect(loggerMock.warn).toHaveBeenCalledWith(
216
- expect.stringContaining("[engine-registry] engine 'throwy' dispose threw synchronously"),
217
- );
218
- });
219
-
220
- it("引擎仅注册工厂未实例化 → 不触发 dispose 也不实例化(停机路径不反向创建资源)", () => {
221
- const events: string[] = [];
222
- const engine = makeDisposingEngine("unused", events);
223
- registerEngine("unused", () => engine);
224
- const child = makeFakeChild();
225
- child.kill.mockImplementation(() => {
226
- events.push("kill:rec-1");
227
- });
228
- spawnedChildren.set("rec-1", child);
229
-
230
- const n = killAllSpawnedChildren();
231
-
232
- expect(n).toBe(1);
233
- expect(events).toEqual(["kill:rec-1"]); // 无 dispose 事件混入
234
- expect(child.kill).toHaveBeenCalledTimes(1);
235
- });
236
- });