@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
@@ -0,0 +1,244 @@
1
+ // src/execution/engine/engine-manifest.ts
2
+ //
3
+ // [W4] 引擎包 manifest 字段级解析(package.json `xyz-agent.subagentEngine` 段)。
4
+ // 设计权威源:docs/design/subagent-engine-protocolization.md §3.4 + impl-plan §2.4。
5
+ //
6
+ // 自 engine-discovery-scan.ts 拆出(max-lines 纪律):manifest 解析是纯「unknown →
7
+ // 强类型」的字段级校验域,与发现主链(搜索路径/装载)无状态耦合;warn 经 core log
8
+ // facade 落宿主日志。字段级契约速览:
9
+ // id / bin / protocol 必需;capabilities 必需(缺键保守值 + warn;未知键忽略 + warn);
10
+ // envPrefixes 可选(非法条目丢弃该前缀 + warn,包仍可用);modelCatalog 可选
11
+ // (缺省 = 不注入,三态语义见 parseModelCatalog);displayName/description 可选。
12
+
13
+ import * as path from "node:path";
14
+
15
+ import type { ModelCatalogEntry } from "@zhushanwen/subagent-engine-sdk";
16
+
17
+ import { getLogger } from "../../core/logger.ts";
18
+ import type { EngineCapabilities } from "./types.ts";
19
+
20
+ const logger = getLogger("subagents");
21
+
22
+ /** 引擎 env 放行前缀的保留字(§2.4/§2.12 L2:引擎 manifest 不得声明宿主命名空间)。 */
23
+ const RESERVED_ENV_PREFIXES = ["XYZ_", "XYZ_AGENT_", "XYZ_SUBAGENT_"];
24
+
25
+ /** env 前缀合法形态(§2.4:^[A-Za-z0-9_]+$;保留前缀比对大小写不敏感)。 */
26
+ const ENV_PREFIX_PATTERN = /^[A-Za-z0-9_]+$/;
27
+
28
+ /**
29
+ * capabilities 全保守值(manifest capabilities 缺失/缺键时的回退——「声明即能力」
30
+ * 模型下,未声明 = 最弱能力:gate 同步拦生成,运行期不会踩引擎不支持的路径)。
31
+ */
32
+ export const CONSERVATIVE_CAPABILITIES: EngineCapabilities = {
33
+ schemaEnforcement: "emulated",
34
+ steer: "unsupported",
35
+ conversation: "unsupported",
36
+ personaInjection: "prompt",
37
+ eventGranularity: "coarse",
38
+ sandbox: "none",
39
+ sessionRead: "outcome-only",
40
+ resume: "unsupported",
41
+ interrupt: "kill-only",
42
+ permissionMode: "ignored",
43
+ maxTurns: false,
44
+ };
45
+
46
+ /** 枚举能力位词表(与 types.ts EngineCapabilities 逐键对应;maxTurns 单独 boolean)。 */
47
+ const CAPABILITY_ENUMS: Record<string, readonly string[]> = {
48
+ schemaEnforcement: ["native", "emulated"],
49
+ steer: ["native", "emulated", "unsupported"],
50
+ conversation: ["native", "unsupported"],
51
+ personaInjection: ["file", "flag", "prompt"],
52
+ eventGranularity: ["stream", "coarse"],
53
+ sandbox: ["native", "emulated", "none"],
54
+ sessionRead: ["full", "partial", "outcome-only"],
55
+ resume: ["native", "cold", "unsupported"],
56
+ interrupt: ["native", "kill-only"],
57
+ permissionMode: ["native", "fixed", "ignored"],
58
+ };
59
+
60
+ /**
61
+ * 解析 manifest bin → 入口文件绝对路径。package.json `bin` 为字符串(单入口形态)
62
+ * 直接用;为 map 时按 manifest bin key 查。查不到返回 undefined(= bin 缺失面)。
63
+ */
64
+ export function resolveManifestBin(
65
+ pkgDir: string,
66
+ pkg: Record<string, unknown>,
67
+ manifestBin: string,
68
+ ): string | undefined {
69
+ const npmBin = pkg["bin"];
70
+ if (typeof npmBin === "string") return path.resolve(pkgDir, npmBin);
71
+ if (typeof npmBin === "object" && npmBin !== null) {
72
+ const rel = (npmBin as Record<string, unknown>)[manifestBin];
73
+ if (typeof rel === "string" && rel.trim() !== "") return path.resolve(pkgDir, rel);
74
+ }
75
+ return undefined;
76
+ }
77
+
78
+ /** capabilities 字段级解析:段缺失 = 全保守 + warn;缺键/坏值 = 该键保守 + warn;未知键忽略 + warn。 */
79
+ export function parseCapabilities(id: string, raw: unknown): EngineCapabilities {
80
+ if (raw === undefined || raw === null) {
81
+ logger.warn(
82
+ `[engine-discovery] engine '${id}': manifest capabilities is required — falling back to most conservative values`,
83
+ );
84
+ return { ...CONSERVATIVE_CAPABILITIES };
85
+ }
86
+ if (typeof raw !== "object" || Array.isArray(raw)) {
87
+ logger.warn(
88
+ `[engine-discovery] engine '${id}': manifest capabilities must be an object — falling back to most conservative values`,
89
+ );
90
+ return { ...CONSERVATIVE_CAPABILITIES };
91
+ }
92
+ const v = raw as Record<string, unknown>;
93
+ const out = {} as Record<string, unknown>;
94
+ for (const [key, allowed] of Object.entries(CAPABILITY_ENUMS)) {
95
+ const value = v[key];
96
+ if (value === undefined) {
97
+ out[key] = CONSERVATIVE_CAPABILITIES[key as keyof EngineCapabilities];
98
+ logger.warn(
99
+ `[engine-discovery] engine '${id}': capabilities.${key} missing — conservative default '${String(out[key])}'`,
100
+ );
101
+ continue;
102
+ }
103
+ if (typeof value === "string" && (allowed as readonly string[]).includes(value)) {
104
+ out[key] = value;
105
+ continue;
106
+ }
107
+ out[key] = CONSERVATIVE_CAPABILITIES[key as keyof EngineCapabilities];
108
+ logger.warn(
109
+ `[engine-discovery] engine '${id}': capabilities.${key}=${describeValue(value)} invalid ` +
110
+ `(expected one of ${allowed.join("|")}) — conservative default '${String(out[key])}'`,
111
+ );
112
+ }
113
+ const maxTurns = v["maxTurns"];
114
+ if (maxTurns === undefined) {
115
+ out["maxTurns"] = false;
116
+ logger.warn(`[engine-discovery] engine '${id}': capabilities.maxTurns missing — conservative default 'false'`);
117
+ } else if (typeof maxTurns !== "boolean") {
118
+ out["maxTurns"] = false;
119
+ logger.warn(
120
+ `[engine-discovery] engine '${id}': capabilities.maxTurns=${describeValue(maxTurns)} invalid (expected boolean) — conservative default 'false'`,
121
+ );
122
+ } else {
123
+ out["maxTurns"] = maxTurns;
124
+ }
125
+ // 未知键:忽略 + warn(一次汇总,防逐键刷屏)
126
+ const knownKeys = new Set([...Object.keys(CAPABILITY_ENUMS), "maxTurns"]);
127
+ const unknownKeys = Object.keys(v).filter((k) => !knownKeys.has(k));
128
+ if (unknownKeys.length > 0) {
129
+ logger.warn(
130
+ `[engine-discovery] engine '${id}': unknown capabilities key(s) ignored: ${unknownKeys.join(", ")}`,
131
+ );
132
+ }
133
+ return out as unknown as EngineCapabilities;
134
+ }
135
+
136
+ /** envPrefixes 字段级解析:非法/保留前缀条目丢弃该前缀 + warn,包仍可用(A12②)。 */
137
+ export function parseEnvPrefixes(id: string, raw: unknown): string[] {
138
+ if (raw === undefined) return [];
139
+ if (!Array.isArray(raw)) {
140
+ logger.warn(
141
+ `[engine-discovery] engine '${id}': envPrefixes must be an array — ignoring (engine remains usable)`,
142
+ );
143
+ return [];
144
+ }
145
+ const out: string[] = [];
146
+ const seen = new Set<string>();
147
+ for (const item of raw) {
148
+ if (typeof item !== "string" || item === "" || item.includes("*") || !ENV_PREFIX_PATTERN.test(item)) {
149
+ logger.warn(
150
+ `[engine-discovery] engine '${id}': envPrefixes entry ${describeValue(item)} rejected ` +
151
+ `(must match ^[A-Za-z0-9_]+$, non-empty, no '*') — dropping the prefix, engine remains usable`,
152
+ );
153
+ continue;
154
+ }
155
+ const upper = item.toUpperCase();
156
+ if (RESERVED_ENV_PREFIXES.some((reserved) => upper.startsWith(reserved))) {
157
+ logger.warn(
158
+ `[engine-discovery] engine '${id}': envPrefixes entry '${item}' rejected (reserved host prefix) — dropping the prefix, engine remains usable`,
159
+ );
160
+ continue;
161
+ }
162
+ if (seen.has(upper)) continue; // 大小写不敏感去重(env 名在 POSIX 侧语义即不区分大小写冲突面)
163
+ seen.add(upper);
164
+ out.push(item);
165
+ }
166
+ return out;
167
+ }
168
+
169
+ /**
170
+ * modelCatalog 字段级解析(三态必写死,§2.4):
171
+ * 缺省 → undefined(不注入——「无枚举面」必须可达);
172
+ * null → null(合法等价省略);
173
+ * 对象 → { dynamic: 缺省 true, models: 条目数组 };models 缺失/非数组 → null + warn
174
+ * (显式声明了 catalog 却无有效 models = 无有效枚举面,取 null 而非 []——[] 是
175
+ * 「有枚举面但空」的作者显式声明,不得代填)。
176
+ */
177
+ export function parseModelCatalog(
178
+ id: string,
179
+ raw: unknown,
180
+ ): { dynamic: boolean; models: ModelCatalogEntry[] } | null | undefined {
181
+ if (raw === undefined) return undefined;
182
+ if (raw === null) return null;
183
+ if (typeof raw !== "object" || Array.isArray(raw)) {
184
+ logger.warn(`[engine-discovery] engine '${id}': modelCatalog must be an object — ignoring (no model catalog)`);
185
+ return undefined;
186
+ }
187
+ const v = raw as Record<string, unknown>;
188
+ const dynamic = parseCatalogDynamic(id, v);
189
+ if (!Array.isArray(v["models"])) {
190
+ logger.warn(
191
+ `[engine-discovery] engine '${id}': modelCatalog.models must be an array — treating catalog as absent (null)`,
192
+ );
193
+ return null;
194
+ }
195
+ const models: ModelCatalogEntry[] = [];
196
+ for (const item of v["models"]) {
197
+ const entry = parseModelCatalogEntry(id, item);
198
+ if (entry === undefined) continue;
199
+ models.push(entry);
200
+ }
201
+ return { dynamic, models };
202
+ }
203
+
204
+ /** modelCatalog.dynamic 解析:缺省 true;非 boolean warn 后仍取缺省。 */
205
+ function parseCatalogDynamic(id: string, v: Record<string, unknown>): boolean {
206
+ const rawDynamic = v["dynamic"];
207
+ if (rawDynamic === undefined) return true;
208
+ if (typeof rawDynamic === "boolean") return rawDynamic;
209
+ logger.warn(
210
+ `[engine-discovery] engine '${id}': modelCatalog.dynamic=${describeValue(rawDynamic)} invalid (expected boolean) — defaulting to true`,
211
+ );
212
+ return true;
213
+ }
214
+
215
+ /** 单条目解析:形态坏/缺 id → undefined(丢弃该条目);非法 aliases 丢弃 aliases 但条目保留。 */
216
+ function parseModelCatalogEntry(id: string, item: unknown): ModelCatalogEntry | undefined {
217
+ if (typeof item !== "object" || item === null || Array.isArray(item)) {
218
+ logger.warn(`[engine-discovery] engine '${id}': modelCatalog entry ${describeValue(item)} is not an object — dropping entry`);
219
+ return undefined;
220
+ }
221
+ const entryId = (item as Record<string, unknown>)["id"];
222
+ if (typeof entryId !== "string" || entryId.trim() === "") {
223
+ logger.warn(`[engine-discovery] engine '${id}': modelCatalog entry missing string id — dropping entry`);
224
+ return undefined;
225
+ }
226
+ const aliases = (item as Record<string, unknown>)["aliases"];
227
+ if (aliases !== undefined && !Array.isArray(aliases)) {
228
+ logger.warn(`[engine-discovery] engine '${id}': modelCatalog entry '${entryId}' aliases must be an array — dropping aliases`);
229
+ }
230
+ return {
231
+ id: entryId,
232
+ ...(Array.isArray(aliases) ? { aliases: aliases.filter((a): a is string => typeof a === "string") } : {}),
233
+ ...((item as Record<string, unknown>)["canonicalRef"] !== undefined
234
+ ? { canonicalRef: (item as Record<string, unknown>)["canonicalRef"] as string }
235
+ : {}),
236
+ };
237
+ }
238
+
239
+ /** unknown 值的短描述(warn 文案;JSON.stringify 对 Symbol/BigInt 返回 undefined 不可靠)。 */
240
+ export function describeValue(value: unknown): string {
241
+ if (typeof value === "string") return JSON.stringify(value);
242
+ if (typeof value === "object" && value !== null) return Array.isArray(value) ? "array" : "object";
243
+ return String(value);
244
+ }
@@ -0,0 +1,123 @@
1
+ // host/askUser 壳侧应答端集成断言(W6 验收,R3 MF-A / impl-plan §2.6 验收补充):
2
+ // fake 引擎经 host/askUser 反向请求可在 core 壳侧收到应答——链路 = fake-engine CLI
3
+ // 帧④ → EngineClient reverse-router(ack 先行,R9-2)→ EngineClientOptions
4
+ // .uiRequestHandler 注入点(discovery portFactory 的接线形态:读壳侧登记处)→
5
+ // SubagentService init.uiRequestHandler 同源的 handler([D4-④] 唯一注入入口的
6
+ // 协议化投影)。应答复用 dialog-queue 的 UiRequest/UiResponse 形状。
7
+
8
+ import { mkdtempSync, rmSync } from "node:fs";
9
+ import { tmpdir } from "node:os";
10
+ import { join } from "node:path";
11
+ import { fileURLToPath } from "node:url";
12
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
13
+
14
+ import { EngineClient } from "../../client/engine-client.ts";
15
+ import {
16
+ _resetHostUiRequestEndpointForTest,
17
+ getHostUiRequestEndpoint,
18
+ setHostUiRequestEndpoint,
19
+ } from "../host-ui-endpoint.ts";
20
+
21
+ const FAKE_ENGINE = fileURLToPath(
22
+ new URL("../../client/__tests__/__fixtures__/fake-engine.mjs", import.meta.url),
23
+ );
24
+
25
+ let dataDir: string;
26
+
27
+ beforeEach(() => {
28
+ dataDir = mkdtempSync(join(tmpdir(), "w6-host-askuser-"));
29
+ });
30
+
31
+ afterEach(() => {
32
+ rmSync(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
33
+ _resetHostUiRequestEndpointForTest();
34
+ });
35
+
36
+ describe("host/askUser 壳侧应答端(W6 R3 MF-A 集成断言)", () => {
37
+ it("fake 引擎经 host/askUser 反向请求 → 壳侧登记 handler 应答(ack 先行 + 结果异步补帧②)", async () => {
38
+ // ① 壳侧登记(生产 = SubagentService init/initSession.uiRequestHandler 写点同步登记)
39
+ const seenRequests: Array<{ method: string; id: string }> = [];
40
+ setHostUiRequestEndpoint(async (req) => {
41
+ seenRequests.push({ method: req.method, id: req.id });
42
+ return { value: "host-answer" };
43
+ });
44
+
45
+ // ② EngineClient 构造(discovery portFactory 同款接线形态:注入点读登记处)
46
+ const client = new EngineClient({
47
+ engineId: "fake",
48
+ command: process.execPath,
49
+ args: [
50
+ FAKE_ENGINE,
51
+ "--run-actions",
52
+ JSON.stringify([
53
+ { op: "askUser", request: { method: "select", id: "q1", title: "pick" } },
54
+ ]),
55
+ ],
56
+ hostKind: "test",
57
+ hostVersion: "test-host-1.0",
58
+ dataDir,
59
+ envPrefixes: [],
60
+ uiRequestHandler: getHostUiRequestEndpoint(),
61
+ });
62
+
63
+ const events: Array<{ type: string; message?: string }> = [];
64
+ const unregister = client.registerRunRoute("run-1", {
65
+ onEvent: (e) => {
66
+ events.push(e as { type: string; message?: string });
67
+ },
68
+ });
69
+ try {
70
+ await client.ensureConnected();
71
+ await client.request("run", {
72
+ runId: "run-1",
73
+ task: { prompt: "p" },
74
+ ctx: { poolKey: "shared", cwd: dataDir },
75
+ });
76
+ } finally {
77
+ unregister();
78
+ await client.dispose();
79
+ }
80
+
81
+ // ③ 壳侧收到请求(形状 = dialog-queue UiRequest)
82
+ expect(seenRequests).toEqual([{ method: "select", id: "q1" }]);
83
+ // ④ 引擎收到应答(fake-engine 把 host/askUser 应答 echo 成 event;ack 先行语义
84
+ // 由 client/__tests__/engine-client.test.ts 的 ack 两阶段用例锁定)
85
+ const echo = events.find((e) => e.message?.startsWith("askUser-result:"));
86
+ expect(echo?.message).toContain('"value":"host-answer"');
87
+ });
88
+
89
+ it("登记处为空(未注入 handler)→ 引擎收 {unsupported:true} 自行降级(不炸链路)", async () => {
90
+ const client = new EngineClient({
91
+ engineId: "fake",
92
+ command: process.execPath,
93
+ args: [
94
+ FAKE_ENGINE,
95
+ "--run-actions",
96
+ JSON.stringify([{ op: "askUser", request: { method: "confirm", id: "q2" } }]),
97
+ ],
98
+ hostKind: "test",
99
+ dataDir,
100
+ envPrefixes: [],
101
+ uiRequestHandler: getHostUiRequestEndpoint(), // undefined
102
+ });
103
+ const events: Array<{ type: string; message?: string }> = [];
104
+ const unregister = client.registerRunRoute("run-1", {
105
+ onEvent: (e) => {
106
+ events.push(e as { type: string; message?: string });
107
+ },
108
+ });
109
+ try {
110
+ await client.ensureConnected();
111
+ await client.request("run", {
112
+ runId: "run-1",
113
+ task: { prompt: "p" },
114
+ ctx: { poolKey: "shared", cwd: dataDir },
115
+ });
116
+ } finally {
117
+ unregister();
118
+ await client.dispose();
119
+ }
120
+ const echo = events.find((e) => e.message?.startsWith("askUser-result:"));
121
+ expect(echo?.message).toContain('"unsupported":true');
122
+ });
123
+ });
@@ -0,0 +1,135 @@
1
+ // HostBridge 契约 + core 实现 单测(W6,impl-plan §2.6 第 2 条 / 设计 §3.8 最小示例 9 方法)。
2
+ // 覆盖:9 方法委托行为、getRecordForAction 异常 → null、cancel 的 void 包装、
3
+ // takeChatRound 未提供 → null、armIdleTimer/disarmIdleTimer 对 lifecycle-manager 的
4
+ // 接线(含超时回调 onIdleTimeout 触发,fake timers)。
5
+
6
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
7
+
8
+ import {
9
+ createHostBridge,
10
+ type HostBridge,
11
+ type HostBridgeServiceFace,
12
+ type HostChatRoundTicket,
13
+ } from "../host-bridge.ts";
14
+ import {
15
+ _resetLifecycleState,
16
+ hasIdleTimer,
17
+ } from "../../../lifecycle-manager.ts";
18
+ import type { ExecutionRecord, SubagentRecord } from "../../../types.ts";
19
+
20
+ /** 最小 record fake(HostBridge 只透传,不读字段)。 */
21
+ function fakeRecord(id: string): ExecutionRecord {
22
+ return { id } as ExecutionRecord;
23
+ }
24
+
25
+ /** 服务面 fake:全部方法 vi.fn,逐方法断言委托。 */
26
+ function makeService(): HostBridgeServiceFace & {
27
+ spies: Record<string, ReturnType<typeof vi.fn>>;
28
+ } {
29
+ const spies = {
30
+ executeAndAwait: vi.fn(async () => ({ content: "ok" })),
31
+ getRecordForAction: vi.fn((id: string) => fakeRecord(id)),
32
+ closeSubagent: vi.fn(async () => {}),
33
+ cancel: vi.fn(() => true),
34
+ collectRecords: vi.fn((): SubagentRecord[] => [{ id: "r1" } as SubagentRecord]),
35
+ takeChatRound: vi.fn(() => undefined),
36
+ reportRecordTransition: vi.fn(() => {}),
37
+ };
38
+ return {
39
+ spies,
40
+ executeAndAwait: spies.executeAndAwait as HostBridgeServiceFace["executeAndAwait"],
41
+ getRecordForAction: spies.getRecordForAction,
42
+ closeSubagent: spies.closeSubagent,
43
+ cancel: spies.cancel,
44
+ collectRecords: spies.collectRecords,
45
+ takeChatRound: spies.takeChatRound as HostBridgeServiceFace["takeChatRound"],
46
+ reportRecordTransition: spies.reportRecordTransition,
47
+ };
48
+ }
49
+
50
+ describe("HostBridge(W6,设计 §3.8 最小示例 9 方法)", () => {
51
+ let service: ReturnType<typeof makeService>;
52
+ let onIdleTimeout: (recordId: string) => void;
53
+ let bridge: HostBridge;
54
+
55
+ beforeEach(() => {
56
+ _resetLifecycleState();
57
+ service = makeService();
58
+ onIdleTimeout = vi.fn();
59
+ bridge = createHostBridge({ service, onIdleTimeout });
60
+ });
61
+
62
+ afterEach(() => {
63
+ _resetLifecycleState();
64
+ });
65
+
66
+ it("executeAndAwait 委托服务面(opts/signal/onEvent/stream 四参透传)", async () => {
67
+ const opts = { task: "t" } as Parameters<HostBridge["executeAndAwait"]>[0];
68
+ const signal = new AbortController().signal;
69
+ const onEvent = () => {};
70
+ const result = await bridge.executeAndAwait(opts, signal, onEvent);
71
+ expect(result).toEqual({ content: "ok" });
72
+ expect(service.spies.executeAndAwait).toHaveBeenCalledWith(opts, signal, onEvent, undefined);
73
+ });
74
+
75
+ it("getRecordForAction:存在 → 返回 record;throw → null(resolveRecord 语义内聚)", () => {
76
+ expect(bridge.getRecordForAction("r1")?.id).toBe("r1");
77
+ (service.spies.getRecordForAction as ReturnType<typeof vi.fn>).mockImplementation(() => {
78
+ throw new Error("not found");
79
+ });
80
+ expect(bridge.getRecordForAction("missing")).toBeNull();
81
+ });
82
+
83
+ it("collectRecords 委托(limit/filter 透传)", () => {
84
+ expect(bridge.collectRecords(10, "all")).toEqual([{ id: "r1" }]);
85
+ expect(service.spies.collectRecords).toHaveBeenCalledWith(10, "all");
86
+ });
87
+
88
+ it("closeSubagent 委托(record/force 透传,Promise 语义保持)", async () => {
89
+ const record = fakeRecord("r1");
90
+ await bridge.closeSubagent(record, true);
91
+ expect(service.spies.closeSubagent).toHaveBeenCalledWith(record, true);
92
+ });
93
+
94
+ it("cancel:服务面 boolean → 契约 void(resolve 语义归引擎侧判 notResumable)", async () => {
95
+ await expect(bridge.cancel("r1")).resolves.toBeUndefined();
96
+ expect(service.spies.cancel).toHaveBeenCalledWith("r1");
97
+ });
98
+
99
+ it("takeChatRound:有票 → 透传;无票 / 服务面未提供 → null", () => {
100
+ expect(bridge.takeChatRound("t1")).toBeNull();
101
+ const ticket: HostChatRoundTicket = {
102
+ record: fakeRecord("r1"),
103
+ opts: {} as HostChatRoundTicket["opts"],
104
+ signal: undefined,
105
+ priority: 1,
106
+ };
107
+ (service.spies.takeChatRound as ReturnType<typeof vi.fn>).mockReturnValue(ticket);
108
+ expect(bridge.takeChatRound("t1")).toBe(ticket);
109
+ });
110
+
111
+ it("reportRecordTransition 委托(record 整体上报,无 patch 形态——见契约头注释)", () => {
112
+ const record = fakeRecord("r1");
113
+ bridge.reportRecordTransition(record);
114
+ expect(service.spies.reportRecordTransition).toHaveBeenCalledWith(record);
115
+ });
116
+
117
+ it("armIdleTimer/disarmIdleTimer 接线 lifecycle-manager(D2 表:idle 定时器归 HostBridge)", () => {
118
+ expect(hasIdleTimer("r1")).toBe(false);
119
+ bridge.armIdleTimer("r1", 60_000);
120
+ expect(hasIdleTimer("r1")).toBe(true);
121
+ bridge.disarmIdleTimer("r1");
122
+ expect(hasIdleTimer("r1")).toBe(false);
123
+ });
124
+
125
+ it("idle timer 超时触发 onIdleTimeout(宿主杀链注入点)", () => {
126
+ vi.useFakeTimers();
127
+ try {
128
+ bridge.armIdleTimer("r1", 1_000);
129
+ vi.advanceTimersByTime(1_000);
130
+ expect(onIdleTimeout).toHaveBeenCalledWith("r1");
131
+ } finally {
132
+ vi.useRealTimers();
133
+ }
134
+ });
135
+ });
@@ -0,0 +1,73 @@
1
+ // core 侧 spawnedChildren 状态镜像单测(W6 建立;[W3 改写] 纯镜像形态)。
2
+ // 覆盖:注册记账、终止意图置死位、镜像整体置死、hasLiveProcessHandleCore 读点。
3
+ // inproc 过渡桥(双写/委托杀/并读)随 inproc pi 引擎目录 删除消亡——实际终止在引擎进程内
4
+ // 经协议 interact cancel/close 承载,本模块只做镜像记账。
5
+
6
+ import { describe, it, expect, beforeEach } from "vitest";
7
+
8
+ import type { ChildProcess } from "node:child_process";
9
+ import {
10
+ _resetCoreSpawnedChildrenMirrorForTest,
11
+ coreSpawnedChildrenMirror,
12
+ hasLiveProcessHandleCore,
13
+ killAllSpawnedChildren,
14
+ killRecordChildWithEscalation,
15
+ registerSpawnedChildForRecord,
16
+ } from "../spawned-children.ts";
17
+
18
+ /** 最小 ChildProcess fake(镜像注册只读 pid/killed)。 */
19
+ function fakeChild(id: string): ChildProcess {
20
+ return { pid: 10_000 + (id.charCodeAt(0) % 1000), killed: false } as unknown as ChildProcess;
21
+ }
22
+
23
+ describe("core 侧 spawnedChildren 状态镜像(W6;W3 纯镜像形态)", () => {
24
+ beforeEach(() => {
25
+ _resetCoreSpawnedChildrenMirrorForTest();
26
+ });
27
+
28
+ it("registerSpawnedChildForRecord:写镜像(host/childSpawned 上报的同款落点)", () => {
29
+ const child = fakeChild("sa-a");
30
+ registerSpawnedChildForRecord("sa-a", child);
31
+ expect(coreSpawnedChildrenMirror().getChildByRecord("sa-a")).toMatchObject({
32
+ pid: child.pid,
33
+ killed: false,
34
+ });
35
+ });
36
+
37
+ it("hasLiveProcessHandleCore:镜像注册后 true;markKilled 后 false", () => {
38
+ registerSpawnedChildForRecord("sa-a", fakeChild("sa-a"));
39
+ expect(hasLiveProcessHandleCore("sa-a")).toBe(true);
40
+ coreSpawnedChildrenMirror().markKilled("sa-a");
41
+ expect(hasLiveProcessHandleCore("sa-a")).toBe(false);
42
+ });
43
+
44
+ it("killRecordChildWithEscalation:终止意图记账(镜像置死位;实际终止经协议承载)", () => {
45
+ registerSpawnedChildForRecord("sa-a", fakeChild("sa-a"));
46
+ killRecordChildWithEscalation("sa-a", "test escalation");
47
+ expect(coreSpawnedChildrenMirror().getChildByRecord("sa-a")?.killed).toBe(true);
48
+ expect(hasLiveProcessHandleCore("sa-a")).toBe(false);
49
+ });
50
+
51
+ it("killAllSpawnedChildren:镜像整体置死(失效语义 2+3),返回清理条目数", () => {
52
+ registerSpawnedChildForRecord("sa-a", fakeChild("sa-a"));
53
+ registerSpawnedChildForRecord("sa-b", fakeChild("sa-b"));
54
+ const killed = killAllSpawnedChildren();
55
+ expect(killed).toBe(2);
56
+ expect(coreSpawnedChildrenMirror().snapshot()).toEqual([]);
57
+ expect(hasLiveProcessHandleCore("sa-a")).toBe(false);
58
+ expect(hasLiveProcessHandleCore("sa-b")).toBe(false);
59
+ });
60
+
61
+ it("不存在的 record killAll:返回 0(幂等)", () => {
62
+ expect(killAllSpawnedChildren()).toBe(0);
63
+ });
64
+
65
+ it("同 recordId 重 spawn:镜像覆盖旧句柄(与引擎侧 Map 覆盖语义同构)", () => {
66
+ registerSpawnedChildForRecord("sa-a", fakeChild("sa-a"));
67
+ killRecordChildWithEscalation("sa-a", "old killed");
68
+ const fresh = fakeChild("sa-b");
69
+ registerSpawnedChildForRecord("sa-a", fresh);
70
+ expect(coreSpawnedChildrenMirror().getChildByRecord("sa-a")?.pid).toBe(fresh.pid);
71
+ expect(hasLiveProcessHandleCore("sa-a")).toBe(true);
72
+ });
73
+ });