@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,19 +1,23 @@
1
1
  // src/execution/__tests__/subagent-service-recovery-bounds.test.ts
2
2
  //
3
3
  // [u-svc / T2] 服务侧回收上界与 kill 收敛:
4
- // - T2④/LC-2:closeChatIdle / closeAfterRoundSettled / cancelBackground 三条裸 SIGTERM
5
- // 收敛到 killRecordChildWithEscalation(spy 断言调用点与参数);
4
+ // - T2④/LC-2:closeChatIdle / closeAfterRoundSettled / cancelBackground 三条终止路径
5
+ // 收敛到 killRecordChildWithEscalation(spy 断言调用点与参数)+ [W3] 引擎侧终止
6
+ // 意图(协议 interact cancel/close fire-and-forget);
6
7
  // - T2⑥/PS-1:disposeAllRecords 补三回收面(controller.abort + kill + disarm idle timer
7
8
  // + disarm settled watchdog);
8
- // - T2③/LC-1 + D9:热路径投递 armMidRoundNoProgress(挂载点 D2 下沉后在编排层
9
- // deliverChatMessage 的 interact 返回点;挂载中段无进展检测 + 到期 onMidTimeout/
10
- // onSettleTimeout 处置:kill + 失败终态化 + error 含 'settled watchdog' 标记与恢复
11
- // 指引;agent_end 交棒收尾段的接线在 session-runner stdout pump,两路径共用);
12
- // - T2⑧/PS-3:非 EPIPE 热路径写失败 re-arm idle timer(进程不再裸奔;挂载位 D2
13
- // 下沉后在 PiEngine.deliverPrompt 的非 EPIPE catch——编排层无法区分错误类别)。
9
+ // - T2③/LC-1 + D9:热路径投递 armMidRoundNoProgress(挂载点在编排层
10
+ // deliverChatMessage 的 interact 受理点;中段无进展检测 + 到期处置:kill + 失败
11
+ // 终态化 + error 含 'settled watchdog' 标记与恢复指引)。
14
12
  //
15
- // mock 形态沿用 subagent-service-message-close.test.ts(真实 SubagentService + ServiceInternals
16
- // cast 暴露 store + mock session-runner/stdin-writer/logger)。
13
+ // [W3 改写] 替身从 inproc session-runner/stdin-writer mock 改为协议 seam
14
+ //(registerFakePiEngine):投递 = engine.interact(message) 受理;终止意图 =
15
+ // engine.interact(cancel/close)。T2⑧(非 EPIPE 写失败 re-arm idle timer)随
16
+ // inproc pi 引擎目录 删除归 pi-subagent-cli 包内(写失败语义在引擎进程内,core 已无
17
+ // 可泄漏的本地方案进程),core 侧不再可等价改写。
18
+ //
19
+ // mock 形态:真实 SubagentService + ServiceInternals cast 暴露 store + logger +
20
+ // spawned-children 的 kill 入口换 spy(importOriginal 保留镜像真实现)。
17
21
 
18
22
  import * as fs from "node:fs";
19
23
  import * as os from "node:os";
@@ -26,37 +30,15 @@ const { loggerMock } = vi.hoisted(() => ({
26
30
  }));
27
31
  vi.mock("../../core/logger.ts", () => ({ getLogger: () => loggerMock }));
28
32
 
29
- // killRecordChildWithEscalation 换 spy(收敛调用点断言);spawnedChildren 用真 Map
30
- //(热路径投递判定 / EPIPE 兜底按值删除的消费者);getChildByRecord 从同一 Map
31
- //(mock 工厂与测试共享同一实例)。D2 后模块物理下沉 engines/pi/(mock 路径随迁)。
32
- const { killChildSpy, spawnedMap, getChildByRecordMock } = vi.hoisted(() => {
33
- const map = new Map<string, unknown>();
34
- return {
35
- killChildSpy: vi.fn(),
36
- spawnedMap: map,
37
- getChildByRecordMock: vi.fn((recordId: string) => map.get(recordId)),
38
- };
33
+ // killRecordChildWithEscalation 换 spy(收敛调用点断言);镜像真实现保留。
34
+ const { killChildSpy } = vi.hoisted(() => ({ killChildSpy: vi.fn() }));
35
+ vi.mock("../engine/host/spawned-children.ts", async (importOriginal) => {
36
+ const actual = await importOriginal<typeof import("../engine/host/spawned-children.ts")>();
37
+ return { ...actual, killRecordChildWithEscalation: killChildSpy };
39
38
  });
40
- vi.mock("../engine/engines/pi/session-runner.ts", () => ({
41
- runSpawn: vi.fn(),
42
- killAllSpawnedChildren: vi.fn(),
43
- getChildByRecord: getChildByRecordMock,
44
- registerSpawnedChildForRecord: vi.fn(),
45
- spawnedChildren: spawnedMap,
46
- killRecordChildWithEscalation: killChildSpy,
47
- }));
48
-
49
- // sendPromptCommand 换 spy(控制热路径写成功 / 抛非 EPIPE 错误)。
50
- const { sendPromptCommandMock } = vi.hoisted(() => ({ sendPromptCommandMock: vi.fn() }));
51
- vi.mock("../engine/engines/pi/stdin-writer.ts", () => ({
52
- sendPromptCommand: sendPromptCommandMock,
53
- clearEpipeFailure: vi.fn(),
54
- recordEpipeFailure: vi.fn(() => 1),
55
- resetAllEpipeFailures: vi.fn(),
56
- EPIPE_FAILURE_THRESHOLD: 3,
57
- }));
58
39
 
59
- import { FakeChild } from "./helpers/spawn-mock.ts";
40
+ import { clearEngines } from "../engine/registry.ts";
41
+ import { registerFakePiEngine, type FakePiEnginePort } from "./helpers/fake-engine-port.ts";
60
42
  import { createRecord } from "../execution-record.ts";
61
43
  import { ModelConfigService } from "../model-config-service.ts";
62
44
  import type { RecordStore } from "../record-store.ts";
@@ -64,26 +46,19 @@ import { SubagentService } from "../subagent-service.ts";
64
46
  import type { PiLike } from "../subagent-service.ts";
65
47
  import { armSettledWatchdog, hasSettledWatchdog, SETTLED_MID_ROUND_NO_PROGRESS_MS, _resetSettledWatchdogsForTest } from "../settled-watchdog.ts";
66
48
  import { armIdleTimer, hasIdleTimer, _resetLifecycleState } from "../lifecycle-manager.ts";
49
+ import { _resetCoreSpawnedChildrenMirrorForTest } from "../engine/host/spawned-children.ts";
67
50
  import type { ExecutionRecord } from "../types.ts";
68
51
 
69
52
  function makeTmpAgentDir(): string {
70
53
  return fs.mkdtempSync(path.join(os.tmpdir(), "svc-recovery-bounds-"));
71
54
  }
72
55
 
73
- function makePi(): PiLike & {
74
- appendEntry: ReturnType<typeof vi.fn>;
75
- events: { emit: ReturnType<typeof vi.fn> };
76
- sendMessage: ReturnType<typeof vi.fn>;
77
- } {
56
+ function makePi(): PiLike {
78
57
  return {
79
58
  appendEntry: vi.fn(),
80
59
  events: { emit: vi.fn() },
81
60
  sendMessage: vi.fn(),
82
- } as unknown as PiLike & {
83
- appendEntry: ReturnType<typeof vi.fn>;
84
- events: { emit: ReturnType<typeof vi.fn> };
85
- sendMessage: ReturnType<typeof vi.fn>;
86
- };
61
+ } as unknown as PiLike;
87
62
  }
88
63
 
89
64
  function makeRecord(overrides: Partial<ExecutionRecord> & { id?: string } = {}): ExecutionRecord {
@@ -109,14 +84,16 @@ interface ServiceInternals {
109
84
 
110
85
  type MockPi = ReturnType<typeof makePi>;
111
86
 
112
- function setup(): { agentDir: string; service: SubagentService; store: RecordStore; pi: MockPi } {
87
+ function setup(): { agentDir: string; service: SubagentService; store: RecordStore; pi: MockPi; fake: FakePiEnginePort } {
113
88
  const agentDir = makeTmpAgentDir();
89
+ clearEngines();
90
+ const fake = registerFakePiEngine();
114
91
  const modelService = new ModelConfigService({ agentDir, cwd: agentDir });
115
92
  const service = new SubagentService({ cwd: agentDir, modelService });
116
93
  const pi = makePi();
117
94
  service.initSession({ pi, sessionId: "root-session" });
118
95
  const store = (service as unknown as ServiceInternals).store;
119
- return { agentDir, service, store, pi };
96
+ return { agentDir, service, store, pi, fake };
120
97
  }
121
98
 
122
99
  /** bracket 调 private 方法(ServiceInternals cast 先例的扩展形态)。 */
@@ -125,8 +102,8 @@ function privateFn<K extends string>(service: SubagentService, key: K): (...args
125
102
  }
126
103
 
127
104
  /**
128
- * message 投递入口(D2 后形态:编排层私有 deliverChatMessage → PiEngine.interactRecord
129
- * → deliverPrompt;旧直调形态 deliverMessage 已随协议知识下沉删除)。
105
+ * message 投递入口([W3] 协议形态:编排层私有 deliverChatMessage → engine.interact(message);
106
+ * 冷路径 = engine_session_not_resumable run chat + resume)。
130
107
  */
131
108
  async function deliverChat(
132
109
  service: SubagentService,
@@ -149,40 +126,49 @@ describe("T2④ service-side kill convergence", () => {
149
126
  let service: SubagentService;
150
127
  let store: RecordStore;
151
128
  let pi: MockPi;
129
+ let fake: FakePiEnginePort;
152
130
 
153
131
  beforeEach(() => {
154
132
  vi.restoreAllMocks();
155
133
  killChildSpy.mockClear();
156
- sendPromptCommandMock.mockReset();
157
- ({ agentDir, service, store, pi } = setup());
134
+ ({ agentDir, service, store, pi, fake } = setup());
158
135
  });
159
136
 
160
137
  afterEach(() => {
138
+ service.dispose();
139
+ clearEngines();
161
140
  _resetLifecycleState();
162
141
  _resetSettledWatchdogsForTest();
163
- spawnedMap.clear();
142
+ _resetCoreSpawnedChildrenMirrorForTest();
164
143
  fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
165
144
  });
166
145
 
167
- it("cancelBackground (via cancel) routes through killRecordChildWithEscalation", async () => {
146
+ it("cancelBackground (via cancel) routes through killRecordChildWithEscalation + 引擎侧 cancel", async () => {
168
147
  const record = makeRecord({ id: "sa-cancel" });
169
148
  store.register(record);
170
- spawnedMap.set(record.id, new FakeChild());
171
149
  const ok = service.cancel(record.id);
172
150
  expect(ok).toBe(true);
173
151
  expect(killChildSpy).toHaveBeenCalledWith(record.id, "cancelBackground");
152
+ // [W3] 实际终止在引擎进程内:协议 interact cancel fire-and-forget
153
+ await vi.waitFor(() => {
154
+ expect(fake.interacts.some((c) => c.action.kind === "cancel")).toBe(true);
155
+ });
174
156
  });
175
157
 
176
158
  it("closeChatIdle (via closeSubagent force:false on idle) routes through killRecordChildWithEscalation and disarms timers", async () => {
177
159
  const record = makeRecord({ id: "sa-close-idle" });
178
160
  store.register(record);
179
- spawnedMap.set(record.id, new FakeChild());
180
161
  armIdleTimer(record.id, () => {}); // Path A:idle timer armed(进程保活)
181
162
  armSettledWatchdog(record.id, () => {});
182
163
  await service["closeSubagent"](record, false);
183
164
  expect(killChildSpy).toHaveBeenCalledWith(record.id, "closeChatIdle");
184
165
  expect(hasIdleTimer(record.id)).toBe(false);
185
166
  expect(hasSettledWatchdog(record.id)).toBe(false);
167
+ // [W3] 引擎侧 close force(idle 进程回收)
168
+ await vi.waitFor(() => {
169
+ const close = fake.interacts.find((c) => c.action.kind === "close");
170
+ expect(close?.action).toMatchObject({ kind: "close", payload: { force: true } });
171
+ });
186
172
  });
187
173
 
188
174
  it("closeAfterRoundSettled routes through killRecordChildWithEscalation", async () => {
@@ -198,7 +184,6 @@ describe("T2④ service-side kill convergence", () => {
198
184
  const idle = makeRecord({ id: "sa-dispose-idle" });
199
185
  store.register(running);
200
186
  store.register(idle);
201
- spawnedMap.set(running.id, new FakeChild());
202
187
  armIdleTimer(idle.id, () => {});
203
188
  armSettledWatchdog(idle.id, () => {});
204
189
 
@@ -226,49 +211,42 @@ describe("T2③ hot-path settled watchdog", () => {
226
211
  let service: SubagentService;
227
212
  let store: RecordStore;
228
213
  let pi: MockPi;
214
+ let fake: FakePiEnginePort;
229
215
 
230
216
  beforeEach(() => {
231
217
  vi.restoreAllMocks();
232
218
  killChildSpy.mockClear();
233
- // 模拟真 kill 的语义面(置 killed 标记):isResumable 判定(无活进程)与失败通知
234
- // 放行依赖它——真实 kill 后 close 事件把句柄移出 spawnedChildren,语义相同。
235
- killChildSpy.mockImplementation((recordId: string) => {
236
- const c = spawnedMap.get(recordId) as FakeChild | undefined;
237
- if (c) c.killed = true;
238
- });
239
- sendPromptCommandMock.mockReset();
240
- sendPromptCommandMock.mockImplementation(() => {});
241
- ({ agentDir, service, store, pi } = setup());
219
+ ({ agentDir, service, store, pi, fake } = setup());
242
220
  });
243
221
 
244
222
  afterEach(() => {
223
+ service.dispose();
224
+ clearEngines();
245
225
  vi.useRealTimers();
246
226
  _resetLifecycleState();
247
227
  _resetSettledWatchdogsForTest();
248
- spawnedMap.clear();
228
+ _resetCoreSpawnedChildrenMirrorForTest();
249
229
  fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
250
230
  });
251
231
 
252
232
  it("arms the settled watchdog after a successful hot-path prompt", async () => {
253
233
  const record = makeRecord({ id: "sa-hot-arm" });
254
234
  store.register(record);
255
- spawnedMap.set(record.id, new FakeChild());
256
235
  await deliverChat(service, record, "hello", false);
257
- expect(sendPromptCommandMock).toHaveBeenCalledTimes(1);
236
+ expect(fake.interacts.length).toBe(1);
258
237
  expect(hasSettledWatchdog(record.id)).toBe(true);
259
238
  });
260
239
 
261
- it("onMidTimeout: kills the child, fails the round, error carries 'settled watchdog' marker and recovery hint", async () => {
240
+ it("onMidTimeout: kills the child (engine cancel), fails the round, error carries 'settled watchdog' marker and recovery hint", async () => {
262
241
  // fake timers 必须先于 arm 生效(useFakeTimers 不接管已存在的真实 timer)
263
242
  vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout"] });
264
243
  const record = makeRecord({ id: "sa-hot-timeout" });
265
244
  store.register(record);
266
- spawnedMap.set(record.id, new FakeChild());
267
245
  await deliverChat(service, record, "hello", false);
268
246
  expect(hasSettledWatchdog(record.id)).toBe(true);
269
247
 
270
- // [D9 两段式] 热路径 prompt 发出后挂中段:静默满中段窗长触发(本测试 mock 了
271
- // session-runner,无 stdout pump 驱动事件刷新/交棒,中段静默形态直达)
248
+ // [D9 两段式] 热路径 prompt 发出后挂中段:静默满中段窗长触发(本测试替身不驱动
249
+ // 协议事件,中段静默形态直达)
272
250
  await vi.advanceTimersByTimeAsync(SETTLED_MID_ROUND_NO_PROGRESS_MS + 1);
273
251
 
274
252
  // kill 收敛入口被触发(onTimeout 第一步)
@@ -283,59 +261,11 @@ describe("T2③ hot-path settled watchdog", () => {
283
261
  expect(record.result).toContain("action:'list'");
284
262
  // 失败通知送达(内核直发路径)
285
263
  expect(pi.sendMessage).toHaveBeenCalled();
286
- const notifyContent = (pi.sendMessage.mock.calls[0]?.[0] as { content?: string } | undefined)?.content ?? "";
264
+ const sendMessageCalls = (pi.sendMessage as unknown as ReturnType<typeof vi.fn>).mock.calls as Array<[{ content?: string }]>;
265
+ const notifyContent = sendMessageCalls[0]?.[0]?.content ?? "";
287
266
  expect(notifyContent).toContain("settled watchdog");
288
- });
289
- });
290
-
291
- describe("T2⑧ non-EPIPE hot-path failure re-arms idle timer", () => {
292
- let agentDir: string;
293
- let service: SubagentService;
294
- let store: RecordStore;
295
-
296
- beforeEach(() => {
297
- vi.restoreAllMocks();
298
- killChildSpy.mockClear();
299
- ({ agentDir, service, store } = setup());
300
- });
301
-
302
- afterEach(() => {
303
- _resetLifecycleState();
304
- _resetSettledWatchdogsForTest();
305
- spawnedMap.clear();
306
- fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
307
- });
308
-
309
- it("re-arms the idle timer (default duration) and rethrows on non-EPIPE write failure", async () => {
310
- const record = makeRecord({ id: "sa-nonpipe" });
311
- store.register(record);
312
- spawnedMap.set(record.id, new FakeChild());
313
- sendPromptCommandMock.mockImplementation(() => {
314
- throw new Error("write after end: ERR_STREAM_DESTROYED");
315
- });
316
-
317
- await expect(deliverChat(service, record, "hello", false)).rejects.toThrow(/ERR_STREAM_DESTROYED/);
318
-
319
- // 防泄漏前提恢复:入口 disarm 过的 idle timer 被 re-arm(进程不再裸奔)
320
- expect(hasIdleTimer(record.id)).toBe(true);
321
- expect(loggerMock.warn).toHaveBeenCalledWith(
322
- expect.stringContaining("idle timer re-armed"),
323
- expect.objectContaining({ detail: expect.stringContaining("ERR_STREAM_DESTROYED") }),
324
- );
325
- });
326
-
327
- it("falls back to DEFAULT idle duration (visible) when record.idleTimeoutMs is invalid", async () => {
328
- const record = makeRecord({ id: "sa-nonpipe-bad" });
329
- // 直接注入非法值模拟「入口校验前已存在的 record」/ 回归形态:re-arm 首选值非法 →
330
- // 降级挂 DEFAULT(防御性兜底可见,不静默不挂)
331
- (record as { idleTimeoutMs: number }).idleTimeoutMs = 3_000_000_000;
332
- store.register(record);
333
- spawnedMap.set(record.id, new FakeChild());
334
- sendPromptCommandMock.mockImplementation(() => {
335
- throw new Error("ERR_STREAM_DESTROYED");
336
- });
337
-
338
- await expect(deliverChat(service, record, "hello", false)).rejects.toThrow(/ERR_STREAM_DESTROYED/);
339
- expect(hasIdleTimer(record.id)).toBe(true);
267
+ // [W3] 引擎侧终止意图(fire-and-forget;fake timers 下只断言受理动作已发出)
268
+ const cancel = fake.interacts.find((c) => c.action.kind === "cancel");
269
+ expect(cancel).toBeDefined();
340
270
  });
341
271
  });
@@ -323,7 +323,7 @@ describe("SubagentService", () => {
323
323
  // [背景] Pi 单进程 session 串行接管。session A shutdown 发 SIGTERM 后、子进程彻底
324
324
  // close 前(graceful shutdown 窗口几十~几百 ms),子进程的 trailing extension_ui_request
325
325
  // 仍会被父进程 pump 解析,调到 A 的 handler 闭包(仍持有 A 的 ctx)。旧实现 dispose 不清
326
- // uiRequestHandler,该闭包触发 ui-request-queue.ts catch 分支打 `[subagents] uiRequestHandler
326
+ // uiRequestHandler,该闭包触发 inproc UI 请求队列(已删) catch 分支打 `[subagents] uiRequestHandler
327
327
  // threw` 误导性 console.error。dispose 现注入 stub(始终返回 {cancelled:true}),让 trailing
328
328
  // ui_request 干净降级,不再走旧 handler 闭包。
329
329
  //
@@ -376,7 +376,7 @@ describe("SubagentService", () => {
376
376
 
377
377
  // 关键价值断言:无 "[subagents] uiRequestHandler threw" 误导性噪声
378
378
  // (旧实现 dispose 不清 handler 时,trailing ui_request 走 staleHandler 抛错 →
379
- // ui-request-queue.ts catch 打该日志。stub 覆盖后该路径不再触发。)
379
+ // inproc UI 请求队列(已删) catch 打该日志。stub 覆盖后该路径不再触发。)
380
380
  const subagentsErrors = errorSpy.mock.calls.filter(
381
381
  (args: unknown[]) => typeof args[0] === "string" && args[0].includes("[subagents]"),
382
382
  );
@@ -15,11 +15,16 @@ import * as path from "node:path";
15
15
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
16
16
 
17
17
  import type { AgentCallOpts, AgentResult } from "../../orchestration/models/types.ts";
18
- import type { RunContext, EnginePort } from "../engine/port.ts";
18
+ import type { EnginePort, RunContext } from "../engine/port.ts";
19
19
  import { clearEngines, registerEngine } from "../engine/registry.ts";
20
- import type { ProbeReport } from "../engine/types.ts";
20
+ import type { AgentOutcome, EngineCapabilities, ProbeReport } from "../engine/types.ts";
21
21
  import { ModelConfigService, setModelConfigService } from "../model-config-service.ts";
22
- import { getChildByRecord } from "../engine/engines/pi/session-runner.ts";
22
+ // W10(§2.10 ②):子进程句柄断言改读 core 侧状态镜像(host/spawned-children——
23
+ // 协议化后 spawnedChildren 持有方在引擎进程,core 消费镜像面;判据 pid 同构)。
24
+ import {
25
+ coreSpawnedChildrenMirror,
26
+ _resetCoreSpawnedChildrenMirrorForTest,
27
+ } from "../engine/host/spawned-children.ts";
23
28
  import { SubprocessAgentRunner } from "../subprocess-agent-runner.ts";
24
29
  import type { SubagentService } from "../subagent-service.ts";
25
30
  import type { ExecuteOptions } from "../types.ts";
@@ -88,13 +93,86 @@ function makeMockPiService() {
88
93
  executeOpts.push(opts);
89
94
  return { content: "from-pi", durationMs: 1, toolCalls: [] };
90
95
  });
91
- // [D4 聚合连带] SAR 构造器经 asEngineService 显式视图取 PiEngineService——fake 的
96
+ // [D4 聚合连带] SAR 构造器经 asEngineService 显式视图取引擎服务面——fake 的
92
97
  // face 即自身,getter 直接返回 self。
93
98
  const service = { executeAndAwait } as unknown as SubagentService & { asEngineService: unknown };
94
99
  (service as { asEngineService: unknown }).asEngineService = service;
100
+ // [W3] 登记当前 mock 服务——委托式替身 port 的工厂闭包按它路由(getEngine 惰性)。
101
+ currentPiService = service as unknown as SubagentService;
95
102
  return { service, executeOpts, executeAndAwait };
96
103
  }
97
104
 
105
+ // ── [W3] 委托式替身 pi port(原 inproc PiEngine 的 workflow 分支形态)──
106
+ // run() 还原引擎内 task→ExecuteOptions 映射(含 engine='pi' 留痕与 engineFallback
107
+ // 透传——两断言的契约面)后委托 mock 服务的 executeAndAwait。
108
+
109
+ let currentPiService: SubagentService | undefined;
110
+
111
+ function piTaskToExecuteOptions(task: AgentCallOpts, ctx: RunContext): ExecuteOptions {
112
+ const rawSlug = task.description ?? task.agent ?? "workflow-agent";
113
+ return {
114
+ task: task.prompt,
115
+ slug: rawSlug.length > 35 ? rawSlug.slice(0, 35) : rawSlug,
116
+ agent: task.agent,
117
+ model: task.model,
118
+ thinkingLevel: task.thinkingLevel,
119
+ skillPath: task.skillPath,
120
+ appendSystemPrompt: task.appendSystemPrompt,
121
+ schema: task.schema,
122
+ schemaEnv: task.schemaEnv,
123
+ maxTurns: task.maxTurns,
124
+ graceTurns: task.graceTurns,
125
+ ctxModel: ctx.ctxModel,
126
+ fork: task.fork,
127
+ worktree: task.worktree,
128
+ cwd: task.cwd,
129
+ conversation: task.conversation,
130
+ idleTimeoutMs: task.idleTimeoutMs,
131
+ // P4 引擎留痕(D9①):实际执行引擎 id 恒 pi(原 PiEngine.run workflow 分支同款)
132
+ engine: "pi",
133
+ ...(ctx.engineFallback !== undefined ? { engineFallback: ctx.engineFallback } : {}),
134
+ };
135
+ }
136
+
137
+ function makeDelegatingPiPort(getService: () => SubagentService | undefined): EnginePort {
138
+ return {
139
+ id: "pi",
140
+ capabilities: (): EngineCapabilities => ({
141
+ schemaEnforcement: "native",
142
+ steer: "unsupported",
143
+ conversation: "native",
144
+ personaInjection: "flag",
145
+ eventGranularity: "stream",
146
+ sandbox: "emulated",
147
+ sessionRead: "full",
148
+ resume: "native",
149
+ interrupt: "kill-only",
150
+ permissionMode: "native",
151
+ maxTurns: true,
152
+ }),
153
+ probe: async (): Promise<ProbeReport> => ({ ok: true, engineVersion: "fake", checks: [] }),
154
+ run: async (task, ctx) => {
155
+ const service = getService();
156
+ if (service === undefined) throw new Error("mock pi service not installed");
157
+ const result = await (service as unknown as {
158
+ executeAndAwait: (opts: ExecuteOptions, signal?: AbortSignal) => Promise<AgentResult>;
159
+ }).executeAndAwait(piTaskToExecuteOptions(task, ctx), ctx.signal);
160
+ const outcome: AgentOutcome = {
161
+ content: result.content,
162
+ durationMs: result.durationMs,
163
+ error: result.error,
164
+ engineId: "pi",
165
+ };
166
+ return {
167
+ handle: { data: { v: 1, engineId: "pi", sessionRef: {}, poolKey: "shared", adapterVersion: "fake-sar" } },
168
+ outcome,
169
+ };
170
+ },
171
+ interact: async () => ({ ok: false, code: "engine_interact_failed", message: "not supported in this test" }),
172
+ read: async () => ({ engineId: "pi", turns: [], source: "outcome-only" }),
173
+ };
174
+ }
175
+
98
176
  // ── 环境 ──
99
177
 
100
178
  let tmpRoot: string;
@@ -128,6 +206,7 @@ function installModelService(cfg?: Record<string, unknown>): void {
128
206
  }
129
207
 
130
208
  beforeEach(() => {
209
+ _resetCoreSpawnedChildrenMirrorForTest();
131
210
  tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "sar-routing-"));
132
211
  agentDir = path.join(tmpRoot, "pi-agent");
133
212
  fs.mkdirSync(agentDir, { recursive: true });
@@ -135,6 +214,10 @@ beforeEach(() => {
135
214
  prevDataDirEnv = process.env["XYZ_AGENT_DATA_DIR"];
136
215
  process.env["XYZ_AGENT_DATA_DIR"] = path.join(tmpRoot, "engine-data");
137
216
  clearEngines();
217
+ currentPiService = undefined;
218
+ // [U-2 一致性修复 → W3] resolveHostPiEnginePort 对已注册 port 原样返回——登记
219
+ // 委托式替身 pi port(registry 命中即路由命中;mock 服务的 DI 语义保持不变)。
220
+ registerEngine("pi", () => makeDelegatingPiPort(() => currentPiService));
138
221
  });
139
222
 
140
223
  afterEach(() => {
@@ -327,10 +410,11 @@ describe("SAR 路由集成(P4 验收 1/2/3)", () => {
327
410
  // 引擎回调(真实引擎在 spawn 成功后同步调)→ 按 taskId('sa-' 记账 key)注册可见
328
411
  const child = spawn(process.execPath, ["-e", ""]);
329
412
  ctx?.onChildSpawned?.(child);
330
- expect(getChildByRecord(ctx.taskId)).toBe(child);
331
- // 子进程退出(真实 close 事件)后记账按句移除——不残留死句柄
413
+ expect(coreSpawnedChildrenMirror().getChildByRecord(ctx.taskId)?.pid).toBe(child.pid);
414
+ // 按句移除断言(W10 注):inproc 双模下子进程退出不回灌 host/childStateChanged
415
+ // (该通道是 cli 协议形态专属)——镜像移除断言由 protocol-blackbox 的
416
+ // childStateChanged 用例承载;此处等待 close 仅保证进程面收口不悬挂。
332
417
  await new Promise<void>((resolve) => child.once("close", () => resolve()));
333
- expect(getChildByRecord(ctx.taskId)).toBeUndefined();
334
418
  });
335
419
 
336
420
  // ── [D3-④] SAR 路径预检(capabilities 驱动;唯一有意行为变化 = zcode+worktree)──
@@ -384,3 +468,21 @@ describe("SAR 路由集成(P4 验收 1/2/3)", () => {
384
468
  expect(pi.executeOpts[0]?.maxTurns).toBe(3);
385
469
  });
386
470
  });
471
+
472
+ // [U-2 一致性修复] D4 错误契约:pi 未注册(引擎包未装/发现失败)时普通 run 路径
473
+ // 不再静默直构 inproc——派发期显式 engine_not_found + 安装指引(与 zcode 对称)。
474
+ describe("pi 未注册 → engine_not_found(D4 契约,U-2 修复锚定)", () => {
475
+ it("pi 不在 registry → run 结果 error 含 engine_not_found + 安装指引,不静默走 inproc", async () => {
476
+ clearEngines(); // 覆盖 beforeEach 的 inproc pi 登记——模拟发现失败/未装包
477
+ installModelService();
478
+ const pi = makeMockPiService();
479
+ const sar = new SubprocessAgentRunner({ subagentService: pi.service });
480
+
481
+ const result = await sar.run(makeOpts(), new AbortController().signal);
482
+
483
+ expect(result.error).toContain("engine_not_found");
484
+ expect(result.error).toContain("No engine packages were discovered");
485
+ expect(result.error).toMatch(/install an engine package/i);
486
+ expect(pi.executeAndAwait).not.toHaveBeenCalled();
487
+ });
488
+ });