@zhushanwen/subagent-core 0.6.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) 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-C3fE3R9x.d.cts → engine-discovery-scan-ocpM8FmI.d.cts} +770 -668
  6. package/dist/{types-C3fE3R9x.d.ts → engine-discovery-scan-ocpM8FmI.d.ts} +770 -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 +14045 -16059
  20. package/dist/index.d.cts +1035 -1579
  21. package/dist/index.d.ts +1035 -1579
  22. package/dist/index.js +9838 -13800
  23. package/dist.bundle/index.cjs +28875 -30323
  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__/chat-engine-routing.test.ts +62 -28
  30. package/src/execution/__tests__/chat-round-first-round-watchdog.test.ts +255 -0
  31. package/src/execution/__tests__/collect-coordinator-service.test.ts +36 -47
  32. package/src/execution/__tests__/collect-mixed-dispatch.test.ts +20 -53
  33. package/src/execution/__tests__/delivery-methods.test.ts +179 -237
  34. package/src/execution/__tests__/execute-and-await-worktree.test.ts +0 -2
  35. package/src/execution/__tests__/execute-nesting.test.ts +20 -13
  36. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +20 -33
  37. package/src/execution/__tests__/gc-timer.test.ts +7 -22
  38. package/src/execution/__tests__/get-record-for-action-restart.test.ts +20 -10
  39. package/src/execution/__tests__/helpers/fake-engine-port.ts +187 -0
  40. package/src/execution/__tests__/helpers/spawn-mock.ts +36 -9
  41. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +1 -1
  42. package/src/execution/__tests__/idle-gc.test.ts +200 -0
  43. package/src/execution/__tests__/lifecycle-predicates.test.ts +26 -21
  44. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +5 -2
  45. package/src/execution/__tests__/reconcile-sweep.test.ts +200 -0
  46. package/src/execution/__tests__/record-store.test.ts +60 -6
  47. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +20 -13
  48. package/src/execution/__tests__/round-supervisor.test.ts +293 -0
  49. package/src/execution/__tests__/session-pending.test.ts +119 -17
  50. package/src/execution/__tests__/stream-sink-retirement.test.ts +10 -10
  51. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +1 -11
  52. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +33 -30
  53. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +2 -9
  54. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +59 -129
  55. package/src/execution/__tests__/subagent-service.test.ts +2 -2
  56. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +109 -7
  57. package/src/execution/__tests__/subprocess-agent-runner.test.ts +108 -36
  58. package/src/execution/__tests__/sync-collect-recovery.test.ts +21 -36
  59. package/src/execution/__tests__/workflow-state-root.test.ts +232 -0
  60. package/src/execution/agent-registry.ts +6 -2
  61. package/src/execution/dialog-queue.ts +6 -73
  62. package/src/execution/engine/__tests__/common/capability-gate.test.ts +115 -15
  63. package/src/execution/engine/__tests__/common/errors.test.ts +11 -18
  64. package/src/execution/engine/__tests__/common/kill-chain.test.ts +7 -0
  65. package/src/execution/engine/__tests__/common/nesting-guard.test.ts +9 -3
  66. package/src/execution/engine/__tests__/common/session-view-service.test.ts +90 -3
  67. package/src/execution/engine/__tests__/conformance/H9-test-disposition.md +116 -0
  68. package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/fake-engine-protocol.mjs +377 -0
  69. package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/recorded.fixture.json +331 -0
  70. package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/smoke-run.fixture.json +213 -0
  71. package/src/execution/engine/__tests__/conformance/chat-round-protocol.test.ts +230 -0
  72. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +69 -42
  73. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +4 -81
  74. package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +67 -44
  75. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +81 -54
  76. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +133 -70
  77. package/src/execution/engine/__tests__/conformance/engine-crash.test.ts +143 -0
  78. package/src/execution/engine/__tests__/conformance/fake-engine-capabilities.ts +19 -0
  79. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +7 -2
  80. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +6 -5
  81. package/src/execution/engine/__tests__/conformance/protocol-blackbox.test.ts +398 -0
  82. package/src/execution/engine/__tests__/conformance/registry-fork-filter.test.ts +221 -0
  83. package/src/execution/engine/__tests__/conformance/round-liveness-supervisor.test.ts +302 -0
  84. package/src/execution/engine/__tests__/conformance/static-spawn-guard.test.ts +58 -0
  85. package/src/execution/engine/__tests__/engine-discovery-scan.test.ts +558 -0
  86. package/src/execution/engine/__tests__/host-task-spec.test.ts +45 -0
  87. package/src/execution/engine/__tests__/model-ref-split.test.ts +74 -0
  88. package/src/execution/engine/__tests__/registry.test.ts +206 -1
  89. package/src/execution/engine/__tests__/routing.test.ts +97 -0
  90. package/src/execution/engine/__tests__/run-failure-worktree-cleanup.test.ts +97 -0
  91. package/src/execution/engine/client/__tests__/__fixtures__/fake-engine.mjs +294 -0
  92. package/src/execution/engine/client/__tests__/engine-client.test.ts +516 -0
  93. package/src/execution/engine/client/__tests__/mirror.test.ts +67 -0
  94. package/src/execution/engine/client/__tests__/pid-file.test.ts +257 -0
  95. package/src/execution/engine/client/__tests__/protocol-closure.test.ts +95 -0
  96. package/src/execution/engine/client/__tests__/remote-engine.test.ts +457 -0
  97. package/src/execution/engine/client/client-options.ts +103 -0
  98. package/src/execution/engine/client/engine-client.ts +687 -0
  99. package/src/execution/engine/client/mirror.ts +167 -0
  100. package/src/execution/engine/client/pid-file.ts +392 -0
  101. package/src/execution/engine/client/reaper.ts +95 -0
  102. package/src/execution/engine/client/remote-engine.ts +441 -0
  103. package/src/execution/engine/client/reverse-router.ts +255 -0
  104. package/src/execution/engine/common/capability-gate.ts +109 -22
  105. package/src/execution/engine/common/errors.ts +9 -25
  106. package/src/execution/engine/common/event-journal.ts +4 -50
  107. package/src/execution/engine/common/journal-replay.ts +3 -1
  108. package/src/execution/engine/common/kill-chain.ts +18 -251
  109. package/src/execution/engine/common/nesting-guard.ts +18 -114
  110. package/src/execution/engine/common/pool-manager.ts +4 -0
  111. package/src/execution/engine/common/session-view-service.ts +56 -84
  112. package/src/execution/engine/config.ts +151 -0
  113. package/src/execution/engine/d8-compat.ts +346 -0
  114. package/src/execution/engine/engine-discovery-roots.ts +107 -0
  115. package/src/execution/engine/engine-discovery-scan.ts +269 -0
  116. package/src/execution/engine/engine-discovery.ts +30 -7
  117. package/src/execution/engine/engine-inspect-package.ts +282 -0
  118. package/src/execution/engine/engine-manifest.ts +244 -0
  119. package/src/execution/engine/host/__tests__/host-askuser-endpoint.test.ts +123 -0
  120. package/src/execution/engine/host/__tests__/host-bridge.test.ts +135 -0
  121. package/src/execution/engine/host/__tests__/spawned-children.test.ts +73 -0
  122. package/src/execution/engine/host/host-bridge.ts +150 -0
  123. package/src/execution/engine/host/host-ui-endpoint.ts +47 -0
  124. package/src/execution/engine/host/pi-host-binding.ts +91 -0
  125. package/src/execution/engine/host/spawned-children.ts +139 -0
  126. package/src/execution/engine/host-task-spec.ts +29 -2
  127. package/src/execution/engine/model-validation.ts +55 -2
  128. package/src/execution/engine/paths.ts +16 -41
  129. package/src/execution/engine/port.ts +48 -2
  130. package/src/execution/engine/registry.ts +153 -34
  131. package/src/execution/engine/routing.ts +225 -52
  132. package/src/execution/engine/types.ts +25 -200
  133. package/src/execution/finalize-record.ts +25 -0
  134. package/src/execution/idle-gc.ts +89 -14
  135. package/src/execution/lifecycle-manager.ts +10 -7
  136. package/src/execution/lifecycle-predicates.ts +10 -8
  137. package/src/execution/record-store.ts +29 -2
  138. package/src/execution/relay-env.ts +19 -36
  139. package/src/execution/round-supervisor/domain.ts +89 -0
  140. package/src/execution/round-supervisor/index.ts +33 -0
  141. package/src/execution/round-supervisor/notify-accounting.ts +87 -0
  142. package/src/execution/round-supervisor/reconcile-sweep.ts +235 -0
  143. package/src/execution/round-supervisor/service-binding.test.ts +661 -0
  144. package/src/execution/round-supervisor/service-binding.ts +273 -0
  145. package/src/execution/round-supervisor/supervisor.ts +426 -0
  146. package/src/execution/session-pending.ts +68 -74
  147. package/src/execution/settled-watchdog.ts +39 -0
  148. package/src/execution/subagent-service.ts +746 -400
  149. package/src/execution/subprocess-agent-runner.ts +16 -8
  150. package/src/execution/types.ts +41 -117
  151. package/src/execution/ui-channels.ts +13 -214
  152. package/src/execution/ui-request-handler-factory.ts +7 -4
  153. package/src/execution/ui-request-observability.ts +2 -2
  154. package/src/execution/workflow-state-root.ts +67 -0
  155. package/src/execution/worktree-git-ops.ts +5 -0
  156. package/src/execution/worktree-manager.ts +11 -1
  157. package/src/index.ts +55 -26
  158. package/src/orchestration/__tests__/execute-agent-call.test.ts +1 -1
  159. package/src/orchestration/execute-agent-call.ts +1 -1
  160. package/src/orchestration/file-run-store.ts +144 -59
  161. package/src/orchestration/models/types.ts +43 -42
  162. package/src/shared/zcode-model-ref.ts +31 -0
  163. package/dist/chunk-43ONBFZX.js +0 -240
  164. package/dist/chunk-A4IVZWVX.js +0 -31
  165. package/dist/chunk-APZY4IME.js +0 -27
  166. package/dist/chunk-V3VHZ2VX.js +0 -59
  167. package/dist/execution/engine/engines/zcode/constants.cjs +0 -107
  168. package/dist/execution/engine/engines/zcode/constants.d.cts +0 -135
  169. package/dist/execution/engine/engines/zcode/constants.d.ts +0 -135
  170. package/dist/execution/engine/engines/zcode/constants.js +0 -54
  171. package/dist/execution/engine/engines/zcode/reader.cjs +0 -276
  172. package/dist/execution/engine/engines/zcode/reader.d.cts +0 -21
  173. package/dist/execution/engine/engines/zcode/reader.d.ts +0 -21
  174. package/dist/execution/engine/engines/zcode/reader.js +0 -9
  175. package/src/__tests__/append-system-prompt-assembly.test.ts +0 -243
  176. package/src/__tests__/fr4-get-state-handshake.test.ts +0 -125
  177. package/src/__tests__/session-runner.test.ts +0 -69
  178. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +0 -490
  179. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +0 -191
  180. package/src/execution/__tests__/descendant-sweep-guards.test.ts +0 -220
  181. package/src/execution/__tests__/descendant-sweep.test.ts +0 -269
  182. package/src/execution/__tests__/engine-model-validation.test.ts +0 -741
  183. package/src/execution/__tests__/epipe-fallback.test.ts +0 -248
  184. package/src/execution/__tests__/execution-runtime-face.test.ts +0 -272
  185. package/src/execution/__tests__/get-state-handshake.test.ts +0 -127
  186. package/src/execution/__tests__/helpers/session-runner-mocks.ts +0 -113
  187. package/src/execution/__tests__/keep-alive-no-progress.test.ts +0 -280
  188. package/src/execution/__tests__/kill-all-escalation.test.ts +0 -196
  189. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +0 -113
  190. package/src/execution/__tests__/output-collector.test.ts +0 -469
  191. package/src/execution/__tests__/pi-invocation.test.ts +0 -166
  192. package/src/execution/__tests__/recursive-visibility-env.test.ts +0 -149
  193. package/src/execution/__tests__/rpc-mode.test.ts +0 -89
  194. package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +0 -387
  195. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +0 -277
  196. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +0 -254
  197. package/src/execution/__tests__/run-spawn-edges.test.ts +0 -1079
  198. package/src/execution/__tests__/run-spawn-integration.test.ts +0 -933
  199. package/src/execution/__tests__/run-spawn-resume.test.ts +0 -322
  200. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +0 -196
  201. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +0 -207
  202. package/src/execution/__tests__/service-kill-escalation.test.ts +0 -91
  203. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +0 -110
  204. package/src/execution/__tests__/session-runner-close-prune.test.ts +0 -181
  205. package/src/execution/__tests__/session-runner-dispatch.test.ts +0 -489
  206. package/src/execution/__tests__/session-runner-epipe.test.ts +0 -179
  207. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +0 -227
  208. package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +0 -290
  209. package/src/execution/__tests__/session-runner-schema-env.test.ts +0 -352
  210. package/src/execution/__tests__/settled-watchdog.test.ts +0 -524
  211. package/src/execution/__tests__/spawn-args.test.ts +0 -446
  212. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +0 -192
  213. package/src/execution/__tests__/spawn-event-adapter.test.ts +0 -167
  214. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +0 -208
  215. package/src/execution/__tests__/spawned-children.test.ts +0 -236
  216. package/src/execution/__tests__/start-sync-model-guard.test.ts +0 -150
  217. package/src/execution/__tests__/stdin-writer.test.ts +0 -464
  218. package/src/execution/__tests__/subagent-service-message-close.test.ts +0 -693
  219. package/src/execution/__tests__/temp-prompt.test.ts +0 -53
  220. package/src/execution/__tests__/timeout-integration.test.ts +0 -616
  221. package/src/execution/__tests__/turn-limiter-semantics.test.ts +0 -194
  222. package/src/execution/__tests__/turn-limiter.test.ts +0 -65
  223. package/src/execution/__tests__/ui-request-handler.test.ts +0 -205
  224. package/src/execution/__tests__/ui-request-queue.test.ts +0 -298
  225. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +0 -230
  226. package/src/execution/engine/__tests__/common/schema-emulation.test.ts +0 -128
  227. package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +0 -149
  228. package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +0 -28
  229. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +0 -128
  230. package/src/execution/engine/__tests__/paths.test.ts +0 -39
  231. package/src/execution/engine/common/schema-emulation.ts +0 -189
  232. package/src/execution/engine/common/session-view-projection.ts +0 -51
  233. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +0 -615
  234. package/src/execution/engine/engines/pi/__tests__/reader.test.ts +0 -155
  235. package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +0 -257
  236. package/src/execution/engine/engines/pi/argv-mirror.ts +0 -135
  237. package/src/execution/engine/engines/pi/get-state-handshake.ts +0 -179
  238. package/src/execution/engine/engines/pi/output-collector.ts +0 -317
  239. package/src/execution/engine/engines/pi/pi-engine.ts +0 -811
  240. package/src/execution/engine/engines/pi/pi-invocation.ts +0 -120
  241. package/src/execution/engine/engines/pi/reader.ts +0 -59
  242. package/src/execution/engine/engines/pi/registration.ts +0 -37
  243. package/src/execution/engine/engines/pi/session-runner.ts +0 -2840
  244. package/src/execution/engine/engines/pi/spawn-event-adapter.ts +0 -404
  245. package/src/execution/engine/engines/pi/stdin-writer.ts +0 -199
  246. package/src/execution/engine/engines/pi/temp-prompt.ts +0 -62
  247. package/src/execution/engine/engines/pi/turn-limiter.ts +0 -102
  248. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +0 -301
  249. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +0 -36
  250. package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +0 -321
  251. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +0 -473
  252. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +0 -43
  253. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +0 -124
  254. package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +0 -210
  255. package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +0 -71
  256. package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +0 -247
  257. package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +0 -451
  258. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +0 -792
  259. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +0 -782
  260. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +0 -168
  261. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +0 -210
  262. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +0 -353
  263. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +0 -282
  264. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +0 -372
  265. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +0 -145
  266. package/src/execution/engine/engines/zcode/appserver-launcher.ts +0 -167
  267. package/src/execution/engine/engines/zcode/connection.ts +0 -598
  268. package/src/execution/engine/engines/zcode/constants.ts +0 -176
  269. package/src/execution/engine/engines/zcode/golden-sample.ts +0 -47
  270. package/src/execution/engine/engines/zcode/parser.ts +0 -102
  271. package/src/execution/engine/engines/zcode/preparer.ts +0 -235
  272. package/src/execution/engine/engines/zcode/reader.ts +0 -381
  273. package/src/execution/engine/engines/zcode/registration.ts +0 -37
  274. package/src/execution/engine/engines/zcode/session-channel.ts +0 -968
  275. package/src/execution/engine/engines/zcode/zcode-engine.ts +0 -1431
  276. package/src/execution/round-settlement.ts +0 -93
  277. package/src/execution/ui-request-queue.ts +0 -215
  278. package/src/shared/schema-env.ts +0 -44
@@ -0,0 +1,230 @@
1
+ // chat-round-protocol.test.ts —— [W6] chat 轮次协议 v1.x 增量的协议黑盒契约。
2
+ //
3
+ // 设计权威源:chat-domain-v1x-liveness-governance.md §3.2 D1-A + §5 W1/W6 行 +
4
+ // 验收 A6。W1 SDK 已交付第 9 反向通道 host/roundLifecycle(settled/idle/failed
5
+ // 三相位,关联键 runId|recordId 互斥分路)与 conversation gate 负向构造器;本套件
6
+ // 在 conformance 层(协议客户端 × fake 引擎 CLI)钉住三条语义,供 W3 chat 改线与
7
+ // A7 live 门回归:
8
+ //
9
+ // ① roundLifecycle 三相位(settled/idle/failed)× 双关联键:数据面回执链闭合
10
+ // (宿主必须确认收到——fake 引擎收到帧②应答后回发 host/log "ack …" 回声,
11
+ // NDJSON 单连接有序保证回声先于 run 应答帧到达);core 不误判引擎故障
12
+ // (run 正常 resolve + 客户端保持 ready,10s 数据面守卫不触发)。
13
+ // ② recordId 键 streamDelta 分路:续聊轮 delta 经 recordId 关联(D1-A 裁定,
14
+ // InteractParams/Result 无 runId)。[W3 接线落地] reverse-router 的 recordId
15
+ // 分支接 EngineClient.recordRoutes 真实消费——本用例钉「宿主确认收到 +
16
+ // delta 投递到 recordId 注册的 chat 轮消费点 + 不误投 runId 路由」。
17
+ // ③ conversation gate 负向(A6):manifest 无 conversation 位的引擎(capabilities
18
+ // 覆写 unsupported)→ assertChatConversationSupported 同步拒——错误码
19
+ // engine_capability_unsupported + 恢复指引含「升级引擎包」文案契约;同一
20
+ // 客户端的 run 域完全正常(gate 只拦 chat 面不伤 run 域)。
21
+ //
22
+ // 帧载荷类型权威 = W1 SDK `HostRoundLifecycleParams`(isHostRoundLifecycleParams
23
+ // 的相位居留形状由 SDK 包内测试覆盖);fake 引擎按该形状发帧,本套件断言消费侧。
24
+
25
+ import { mkdtempSync, rmSync } from "node:fs";
26
+ import { tmpdir } from "node:os";
27
+ import { dirname, join } from "node:path";
28
+ import { fileURLToPath } from "node:url";
29
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
30
+
31
+ import {
32
+ assertChatConversationSupported,
33
+ isHostRoundLifecycleParams,
34
+ type InitializeResult,
35
+ } from "@zhushanwen/subagent-engine-sdk";
36
+
37
+ import { EngineClient } from "../../client/engine-client.ts";
38
+
39
+ const FIXTURE_DIR = join(dirname(fileURLToPath(import.meta.url)), "__fixtures__", "engine-protocol");
40
+ const FAKE_ENGINE = join(FIXTURE_DIR, "fake-engine-protocol.mjs");
41
+ const FIXTURE = join(FIXTURE_DIR, "smoke-run.fixture.json");
42
+
43
+ let dataDir: string;
44
+
45
+ beforeEach(() => {
46
+ dataDir = mkdtempSync(join(tmpdir(), "w6-chat-round-protocol-"));
47
+ });
48
+
49
+ afterEach(() => {
50
+ rmSync(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
51
+ });
52
+
53
+ interface Harness {
54
+ client: EngineClient;
55
+ logs: Array<{ level: string; message: string }>;
56
+ streamDeltas: string[];
57
+ /** [W3] recordId 键 chat 轮路由捕获(续聊轮 delta 消费点)。 */
58
+ recordDeltas: string[];
59
+ recordPhases: Array<Record<string, unknown>>;
60
+ }
61
+
62
+ function makeHarness(extraEnv: Record<string, string> = {}): Harness {
63
+ const h: Harness = {
64
+ client: undefined as unknown as EngineClient,
65
+ logs: [],
66
+ streamDeltas: [],
67
+ recordDeltas: [],
68
+ recordPhases: [],
69
+ };
70
+ h.client = new EngineClient({
71
+ engineId: "fake",
72
+ command: process.execPath,
73
+ args: [FAKE_ENGINE],
74
+ hostKind: "test",
75
+ hostVersion: "w6-chat-round-protocol",
76
+ dataDir,
77
+ envPrefixes: [],
78
+ log: (p) => h.logs.push({ level: p.level, message: p.message }),
79
+ baseEnv: {
80
+ ...process.env,
81
+ FAKE_PROTOCOL_FIXTURE: FIXTURE,
82
+ ...extraEnv,
83
+ },
84
+ });
85
+ const unregister = h.client.registerRunRoute("run-smoke-1", {
86
+ onStreamDelta: (delta) => {
87
+ h.streamDeltas.push(delta);
88
+ },
89
+ });
90
+ void unregister;
91
+ // [W3] recordId 键 chat 轮路由(registerRecordRoute——RemoteEngine.registerChatRoundRoute
92
+ // 的底层实现;conformance 层直接驱动 EngineClient 面)。
93
+ const unregisterRecord = h.client.registerRecordRoute("rec-chat-1", {
94
+ onStreamDelta: (delta) => {
95
+ h.recordDeltas.push(delta);
96
+ },
97
+ onRoundLifecycle: (phase) => {
98
+ h.recordPhases.push(phase as Record<string, unknown>);
99
+ },
100
+ });
101
+ void unregisterRecord;
102
+ return h;
103
+ }
104
+
105
+ describe("[W6] host/roundLifecycle 三相位 × 双关联键(协议黑盒回执链)", () => {
106
+ it("settled/idle/failed 相位 × runId|recordId 键全部被宿主确认(ack 回声)且不判引擎故障", async () => {
107
+ const h = makeHarness({ FAKE_RUN_LIFECYCLE: "1" });
108
+ try {
109
+ await h.client.ensureConnected();
110
+ const result = (await h.client.request("run", {
111
+ runId: "run-smoke-1",
112
+ task: "w6 round lifecycle probe",
113
+ ctx: { poolKey: "shared", cwd: dataDir, model: "fake/fake-1" },
114
+ })) as { outcome: Record<string, unknown> };
115
+ void result;
116
+
117
+ // 回执链闭合:6 帧(三相位 × runId 键 "run-smoke-1" / recordId 键 "rec-chat-1")
118
+ // 的帧②应答全部到达 fake 引擎并回发回声——「宿主必须确认收到」的数据面契约。
119
+ // 回声帧经引擎侧往返(core 帧②应答 → fake 收到 → 回发 log),晚于 run 应答
120
+ // 帧到达——断言轮询等待(NDJSON 单连接有序,run resolve 后毫秒级必达)。
121
+ await expect
122
+ .poll(
123
+ () => h.logs.map((l) => l.message).filter((m) => m.startsWith("ack roundLifecycle ")),
124
+ { timeout: 5_000, interval: 20 },
125
+ )
126
+ .toEqual([
127
+ "ack roundLifecycle run-smoke-1 settled",
128
+ "ack roundLifecycle run-smoke-1 idle",
129
+ "ack roundLifecycle run-smoke-1 failed",
130
+ "ack roundLifecycle rec-chat-1 settled",
131
+ "ack roundLifecycle rec-chat-1 idle",
132
+ "ack roundLifecycle rec-chat-1 failed",
133
+ ]);
134
+
135
+ // [W3 接线契约] recordId 键相位帧投递到 registerRecordRoute 注册的 chat 轮
136
+ // 消费点(settled/idle/failed 三相位按序;宿主编排层据此做交棒/挂 idle timer/
137
+ // 失败分诊——分发层只做键路由)。
138
+ await expect
139
+ .poll(() => h.recordPhases.map((p) => `${p.recordId}:${String(p.phase)}`), { timeout: 5_000, interval: 20 })
140
+ .toEqual([
141
+ "rec-chat-1:settled",
142
+ "rec-chat-1:idle",
143
+ "rec-chat-1:failed",
144
+ ]);
145
+
146
+ // 不误判引擎故障:run 正常 resolve + 客户端保持 ready(10s 数据面应答守卫
147
+ // 未触发杀链——roundLifecycle 是数据面回执语义,不是故障信号)。
148
+ expect(h.client.currentState).toBe("ready");
149
+
150
+ // failed 相位的 error{code,message} 居留形状(isHostRoundLifecycleParams
151
+ // 判据的 failed 分支)——fake 引擎按 W1 载荷类型构造,宿主侧对同形状自证。
152
+ const failedShape = {
153
+ runId: "run-smoke-1",
154
+ phase: "failed",
155
+ error: { code: "engine_run_failed", message: "round failed (scripted)", recovery: "retry" },
156
+ };
157
+ expect(isHostRoundLifecycleParams(failedShape)).toBe(true);
158
+ const malformedShape = { runId: "r", recordId: "c", phase: "settled" };
159
+ expect(isHostRoundLifecycleParams(malformedShape)).toBe(false);
160
+ } finally {
161
+ await h.client.dispose();
162
+ }
163
+ });
164
+ });
165
+
166
+ describe("[W6] recordId 键 streamDelta / roundLifecycle 分路(续聊轮关联键,D1-A)", () => {
167
+ it("recordId 键 delta:宿主确认收到(ack 回声)+ 投递到 recordId 注册的 chat 轮消费点,不误投 runId 路由", async () => {
168
+ const h = makeHarness({ FAKE_RUN_RECORD_DELTA: "1" });
169
+ try {
170
+ await h.client.ensureConnected();
171
+ await h.client.request("run", {
172
+ runId: "run-smoke-1",
173
+ task: "w6 record-key delta probe",
174
+ ctx: { poolKey: "shared", cwd: dataDir, model: "fake/fake-1" },
175
+ });
176
+
177
+ // 宿主确认收到(数据面回执闭合;回声帧晚于 run 应答到达,轮询等待)。
178
+ await expect
179
+ .poll(() => h.logs.map((l) => l.message), { timeout: 5_000, interval: 20 })
180
+ .toContain("ack streamDelta recordId=rec-chat-1");
181
+ // [W3 接线契约] recordId 键投递到 recordRoutes 注册的 chat 轮消费点
182
+ //(W1 偏差 #1 的 no-op 分支已替换为真实分发——reverse-router recordId 分支)。
183
+ expect(h.recordDeltas).toEqual(["chat-delta-not-routed-to-runId"]);
184
+ // 关联键分路互斥:recordId 键不误投 runId 路由(runId 键 fixture delta 不受影响)。
185
+ expect(h.streamDeltas).toEqual(["Hello", " world"]);
186
+ expect(h.client.currentState).toBe("ready");
187
+ } finally {
188
+ await h.client.dispose();
189
+ }
190
+ });
191
+
192
+ });
193
+
194
+ describe("[W6] conversation gate 负向(A6:无 gate 位引擎 chat 请求同步拒,run 域不受影响)", () => {
195
+ it("capabilities.conversation=unsupported → assertChatConversationSupported 同步拒 + 文案契约;同客户端 run 正常", async () => {
196
+ const h = makeHarness({ FAKE_CAPABILITIES_CONVERSATION: "unsupported" });
197
+ try {
198
+ await h.client.ensureConnected();
199
+ const diagnostics = h.client.getInitializeDiagnostics() as InitializeResult;
200
+ expect(diagnostics.capabilities.conversation).toBe("unsupported");
201
+
202
+ // W1 具名构造器(W3 chat 路由接线的判据单源):同步拒、错误码与恢复指引
203
+ // 文案契约——「升级引擎包」恢复指引对齐 core capability-gate conversation
204
+ // 分支(错误 → 权威源 → 重试闭环)。
205
+ let thrown: unknown;
206
+ try {
207
+ assertChatConversationSupported("fake", diagnostics.capabilities);
208
+ } catch (err) {
209
+ thrown = err;
210
+ }
211
+ expect(thrown).toMatchObject({
212
+ name: "EngineSdkError",
213
+ code: "engine_capability_unsupported",
214
+ });
215
+ expect((thrown as Error).message).toContain("conversation");
216
+ expect((thrown as { recovery: string }).recovery).toContain("升级引擎包");
217
+
218
+ // A6 后半:gate 只拦 chat 面——同一客户端的 run 域完全正常。
219
+ const result = (await h.client.request("run", {
220
+ runId: "run-smoke-1",
221
+ task: "w6 gate-negative run-domain probe",
222
+ ctx: { poolKey: "shared", cwd: dataDir, model: "fake/fake-1" },
223
+ })) as { outcome: Record<string, unknown> };
224
+ expect(result.outcome).toBeDefined();
225
+ expect(h.client.currentState).toBe("ready");
226
+ } finally {
227
+ await h.client.dispose();
228
+ }
229
+ });
230
+ });
@@ -1,66 +1,93 @@
1
- // contract.abort.test.ts —— conformance C4(abort 行为):运行中 cancel → 宿主合成
2
- // 终态(exitCode=null)、无悬挂 promise(run 正常 resolve、exited 收口)、错误事件
3
- // 先于终态 emit(不变量 5 的事件面)。fake app-server 注入(不依赖真机)。
1
+ // contract.abort.test.ts —— conformance C4(abort 行为):运行中 cancel → 引擎
2
+ // 3s 窗口内收敛终态、无悬挂 promise、错误语义结构化。
4
3
  //
5
- // 2026-09 重构(共享宿主 HOME):CLI spawn 降级链删除后引擎只剩 app-server 常驻链,
6
- // abort 用例只覆盖常驻形态(fake-appserver.mjs 挂起场景,stop 链优先于杀链——D3)。
7
- // capabilities.interrupt 维持 kill-only 声明(D5——改链路先于改声明,stop 链路经
8
- // conformance 真机验证后再评估升 native)。
4
+ // W10 协议黑盒化:断言对象从内建 ZcodeEngine × fake app-server(inproc,W11 删除)
5
+ // 改为 RemoteEngine × fake 引擎 CLI——abort 走协议 cancel 帧 + 引擎收敛应答
6
+ // (杀链升级面 = W2 EngineClient 套件 3s 收敛窗口用例)。zcode app-server
7
+ // stop 优雅链随 W5 归 zcode-subagent-cli 引擎内测试。
9
8
 
10
- import { describe, expect, it, vi } from "vitest";
9
+ import { mkdtempSync, rmSync } from "node:fs";
10
+ import { tmpdir } from "node:os";
11
+ import { dirname, join } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
11
14
 
12
15
  import type { AgentCallOpts } from "../../../../orchestration/models/types.ts";
16
+ import { EngineClient } from "../../client/engine-client.ts";
17
+ import { RemoteEngine } from "../../client/remote-engine.ts";
13
18
  import type { RunContext } from "../../port.ts";
14
19
  import type { AgentEvent } from "../../types.ts";
15
- import { makeAppserverHarness, sentMethodNames } from "./zcode-appserver-harness.ts";
20
+ import { FAKE_CAPABILITIES } from "./fake-engine-capabilities.ts";
16
21
 
17
- describe("conformance C4:abort 行为(运行中 cancel → 合成终态、无悬挂)", () => {
18
- // app-server 常驻形态:abort 走 D3 链(stop → grace → killChain)。挂起场景
19
- // (只推 state.updated,turn 永不落定)+ stopBehavior 缺省 terminal = stop 优雅
20
- // 生效路径——不杀共享进程,run 正常 resolve(C4 的常驻通道面)。
21
- it("app-server 模式:abort → stop 帧先发 → 优雅收口(run resolve、exitCode=null、error 事件先于终态、kill-only 声明维持)", async () => {
22
- const h = makeAppserverHarness({ hangOnly: true });
23
- try {
24
- // capabilities.interrupt 维持 kill-only(D5——升级序里 interrupt 不动)
25
- expect(h.engine.capabilities().interrupt).toBe("kill-only");
22
+ const FAKE_ENGINE = join(
23
+ dirname(fileURLToPath(import.meta.url)),
24
+ "__fixtures__", "engine-protocol", "fake-engine-protocol.mjs",
25
+ );
26
+ const FIXTURE = join(
27
+ dirname(fileURLToPath(import.meta.url)),
28
+ "__fixtures__", "engine-protocol", "smoke-run.fixture.json",
29
+ );
30
+
31
+ let dataDir: string;
32
+
33
+ beforeEach(() => {
34
+ dataDir = mkdtempSync(join(tmpdir(), "w10-c4-abort-"));
35
+ });
26
36
 
37
+ afterEach(() => {
38
+ rmSync(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
39
+ });
40
+
41
+ function makeEngine(): { engine: RemoteEngine; dispose: () => Promise<void> } {
42
+ const client = new EngineClient({
43
+ engineId: "fake",
44
+ command: process.execPath,
45
+ args: [FAKE_ENGINE],
46
+ hostKind: "test",
47
+ hostVersion: "w10-c4-abort",
48
+ dataDir,
49
+ envPrefixes: [],
50
+ baseEnv: { ...process.env, FAKE_PROTOCOL_FIXTURE: FIXTURE, FAKE_RUN_HANG: "1" },
51
+ });
52
+ const engine = new RemoteEngine({
53
+ engineId: "fake",
54
+ client,
55
+ manifest: { capabilities: FAKE_CAPABILITIES },
56
+ dataDir,
57
+ hostKind: "test",
58
+ });
59
+ return { engine, dispose: () => engine.dispose() };
60
+ }
61
+
62
+ describe("conformance C4:abort 行为(协议黑盒:cancel 帧 + 引擎收敛)", () => {
63
+ it("挂起 run 中 abort → cancel 帧送达 → 引擎窗口内收敛终态,run 正常 resolve(不悬挂)", async () => {
64
+ const { engine, dispose } = makeEngine();
65
+ try {
27
66
  const controller = new AbortController();
28
67
  const events: AgentEvent[] = [];
29
68
  const ctx: RunContext = {
30
- taskId: "sa-c4-appserver",
31
- poolKey: "",
69
+ taskId: "w10-c4-abort",
70
+ poolKey: "shared",
32
71
  signal: controller.signal,
33
72
  onEvent: (e) => events.push(e),
34
73
  };
35
- // D6 合流后单一任务形状 AgentCallOpts(prompt≡原 task、description≡原 slug 源)
36
74
  const task: AgentCallOpts = {
37
75
  prompt: "hang",
38
- description: "abort-appserver",
39
- model: "conformance-provider/m1",
40
- cwd: h.workspace,
76
+ description: "abort-protocol",
77
+ model: "fake/fake-1",
78
+ cwd: dataDir,
41
79
  };
42
- const runP = h.engine.run(task, ctx);
43
- // 推进到 send 已达(create+send 帧落 fake 流水)再 abort——abort 先于 create 会
44
- // pre-aborted 短路面,覆盖不到 stop
45
- await vi.waitFor(
46
- () => expect(sentMethodNames(h.stateFile)).toContain("session/send"),
47
- { timeout: 5_000 },
48
- );
80
+ const runP = engine.run(task, ctx);
81
+ // 推进到 run 受理(fake 引擎进入 hang,poolResolved 反向帧已回)再 abort——
82
+ // abort 先于受理会走 pre-aborted 短路面,覆盖不到 cancel
83
+ await new Promise((r) => setTimeout(r, 300));
49
84
  controller.abort();
50
- const { outcome } = await runP; // 必须正常 resolve(stop 优雅收口,不悬挂)
85
+ const { outcome } = await runP; // 必须正常 resolve(引擎收敛,无需杀链)
51
86
 
52
87
  expect(outcome.exitCode).toBeNull();
53
- expect(outcome.error).toContain("session/stop");
54
- // 错误事件先于终态 emit(不变量 5 的事件面——journal 可重放出失败事实)。abort
55
- // 终态不合成 turn_end(只断言 error 事件到达,且其后无任何非 error 事件流出)
56
- const errorIdx = events.findIndex((e) => e.type === "error");
57
- expect(errorIdx).toBeGreaterThanOrEqual(0);
58
- expect(events.slice(errorIdx + 1).every((e) => e.type === "error")).toBe(true);
59
- // D3 序:stop 帧先于任何杀链动作(优雅路径下进程未被杀)
60
- const methods = sentMethodNames(h.stateFile);
61
- expect(methods.indexOf("session/stop")).toBeGreaterThan(methods.indexOf("session/send"));
88
+ expect(outcome.error).toContain("cancelled");
62
89
  } finally {
63
- await h.dispose();
90
+ await dispose();
64
91
  }
65
92
  }, 20_000);
66
93
  });
@@ -3,7 +3,10 @@
3
3
  // parser」的形态:构造抽掉 message_end / usage 半映射的坏样本流,断言套件转红;
4
4
  // 若断言器检不出破坏则本元测试失败)。
5
5
  //
6
- // 2026-09 重构(共享宿主 HOME):CLI spawn 链删除后引擎只剩 app-server 常驻链,
6
+ // W10 协议化:app-server 常驻全链用例随 zcode 引擎内移(zcode-subagent-cli
7
+ // protocol-e2e/zcode-engine-* 测试覆盖,fake-appserver + golden 同款语料);core 侧
8
+ // 保留断言器正反例与负例自证(协议黑盒套件 protocol-blackbox.test.ts 消费断言器)。
9
+ // 历史注:2026-09 重构(共享宿主 HOME):CLI spawn 链删除后引擎只剩 app-server 常驻链,
7
10
  // C3 的引擎全链覆盖只走 app-server 形态(fake 常驻引擎 + stream 口径,文件末
8
11
  // describe;app-server 设计 §3.4 不变量 1:text_delta 拼接 == read 全文、终态唯一
9
12
  // turn.terminal 权威、message_end.usage 完整、tool 事件不合成——granularity 声明
@@ -12,12 +15,6 @@
12
15
  import { describe, expect, it } from "vitest";
13
16
 
14
17
  import type { AgentEvent } from "../../../types.ts";
15
- import { ZCODE_APPSERVER_GOLDEN } from "../../engines/zcode/golden-sample.ts";
16
- import { ZCODE_SHARED_POOL_KEY } from "../../engines/zcode/constants.ts";
17
- import {
18
- goldenReadFullText,
19
- makeAppserverHarness,
20
- } from "./zcode-appserver-harness.ts";
21
18
  import {
22
19
  assertAgentEventInvariants,
23
20
  checkAgentEventInvariants,
@@ -98,77 +95,3 @@ describe("conformance 负例自证(A12:套件有牙)", () => {
98
95
  expect(findings.some((f) => f.invariant === "2")).toBe(true);
99
96
  });
100
97
  });
101
-
102
- // ============================================================
103
- // [R6] C3 app-server 口径:fake 常驻引擎全链(eventGranularity=stream 形态)
104
- // —— 2026-09 重构后唯一生产形态(CLI spawn 降级链已删,无钉扎/降级双通道)。
105
- // ============================================================
106
-
107
- describe("conformance C3:zcode app-server 常驻通道(stream 口径,fake 常驻全链)", () => {
108
- it("text_delta 拼接 == read 全文(byte 级)+ 终态唯一 + message_end.usage 完整 + tool 事件不合成", async () => {
109
- const h = makeAppserverHarness();
110
- try {
111
- const events: AgentEvent[] = [];
112
- const { outcome } = await h.engine.run(
113
- { prompt: "做点什么", description: "c3-appserver", model: "conformance-provider/m1", cwd: h.workspace },
114
- { taskId: "sa-c3-appserver", poolKey: "", onEvent: (e) => events.push(e) },
115
- );
116
-
117
- // granularity 声明与实际流出一致:声明 stream,流出有 text_delta 实时形态
118
- expect(h.engine.capabilities().eventGranularity).toBe("stream");
119
- expect(events.some((e) => e.type === "text_delta")).toBe(true);
120
-
121
- // 事件形态:三条 delta(golden pushStream)→ message_end(usage) → turn_end
122
- expect(events.map((e) => e.type)).toEqual([
123
- "text_delta", "text_delta", "text_delta", "message_end", "turn_end",
124
- ]);
125
- const messageEnd = events.find((e) => e.type === "message_end") as
126
- | { usage?: { input: number; output: number; cacheRead: number; cacheWrite: number } }
127
- | undefined;
128
- expect(messageEnd?.usage).toEqual({ input: 12599, output: 17, cacheRead: 0, cacheWrite: 0 });
129
-
130
- // tool 事件不合成:首期无逐工具推送帧,流出面不伪造 tool_start/tool_end
131
- // (granularity 声明与实际流出一致即合法——app-server 设计 §3.4 不变量 1)
132
- expect(events.some((e) => e.type === "tool_start" || e.type === "tool_end")).toBe(false);
133
-
134
- // 不变量五条(stream 口径):3a 的 content 用 read 全文(终态权威来源)
135
- const readFullText = goldenReadFullText();
136
- assertAgentEventInvariants(events, { granularity: "stream", content: readFullText });
137
- expect(outcome.content).toBe(readFullText);
138
- } finally {
139
- await h.dispose();
140
- }
141
- }, 20_000);
142
-
143
- it("终态权威 turn.terminal 缺失 → read 兜底收口,事件不变量仍全绿(宽松匹配防洪堤不破坏 C3)", async () => {
144
- const h = makeAppserverHarness({ dropTurnTerminal: true });
145
- try {
146
- const events: AgentEvent[] = [];
147
- const { outcome } = await h.engine.run(
148
- { prompt: "做点什么", description: "c3-no-terminal", model: "conformance-provider/m1", cwd: h.workspace },
149
- { taskId: "sa-c3-no-terminal", poolKey: "", onEvent: (e) => events.push(e) },
150
- );
151
- expect(outcome.error).toBeUndefined();
152
- // 缺 turn.terminal 时收尾帧 + read 兜底仍产出完整终态(session-channel 宽松匹配)
153
- assertAgentEventInvariants(events, { granularity: "stream", content: goldenReadFullText() });
154
- } finally {
155
- await h.dispose();
156
- }
157
- }, 20_000);
158
-
159
- it("handle 携带 shared poolKey(共享宿主 HOME 的 journal 分组锚点,①级读取钥匙随 handle 走)", async () => {
160
- const h = makeAppserverHarness();
161
- try {
162
- const { handle, outcome } = await h.engine.run(
163
- { prompt: "做点什么", description: "c3-anchor", model: "conformance-provider/m1", cwd: h.workspace },
164
- { taskId: "sa-c3-anchor", poolKey: "" },
165
- );
166
- expect(handle.data.poolKey).toBe(ZCODE_SHARED_POOL_KEY);
167
- expect(handle.data.sessionRef["sessionId"]).toBe(outcome.sessionId);
168
- // golden 锚点:sessionId 来自 session.sessionId(projection.sessionId 恒 "unknown" 勿用)
169
- expect(JSON.parse(ZCODE_APPSERVER_GOLDEN.createResponse).session.sessionId).toBe("sess_golden_r3_01");
170
- } finally {
171
- await h.dispose();
172
- }
173
- }, 20_000);
174
- });
@@ -1,21 +1,41 @@
1
1
  // contract.probe.test.ts —— conformance C1(probe 形状):ProbeReport 字段完整;
2
2
  // ok=false 时 error.recovery 非空(§3.3.5——恢复指引是错误闭环「错误→权威源→重试」
3
- // 的载体,空指引 = 拦截了但不知道怎么修)。pi/zcode 双引擎都过(任何 adapter 必过)。
3
+ // 的载体,空指引 = 拦截了但不知道怎么修)。
4
4
  //
5
- // fake 注入(不依赖真机):pi probeVersion fake + 不可解析 invocation 场景;
6
- // zcode 用不存在的 cliPath 构造 binary check 失败。
5
+ // W10 协议黑盒化:断言对象从内建 PiEngine/ZcodeEngine(inproc,W11 删除)改为
6
+ // RemoteEngine × fake 引擎 CLI(协议 probe 帧往返)——成功/失败两形态都经协议面。
7
+ // 引擎内 probe 细节(版本探测、二进制检查)随 W5/W7 归各引擎包测试。
7
8
 
8
- import * as fs from "node:fs";
9
- import * as os from "node:os";
10
- import * as path from "node:path";
9
+ import { mkdtempSync, rmSync } from "node:fs";
10
+ import { tmpdir } from "node:os";
11
+ import { dirname, join } from "node:path";
11
12
  import { fileURLToPath } from "node:url";
13
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
12
14
 
13
- import { describe, expect, it } from "vitest";
14
-
15
- import { PiEngine } from "../../engines/pi/pi-engine.ts";
16
- import { ZcodeEngine } from "../../engines/zcode/zcode-engine.ts";
15
+ import { EngineClient } from "../../client/engine-client.ts";
16
+ import { RemoteEngine } from "../../client/remote-engine.ts";
17
+ import { FAKE_CAPABILITIES } from "./fake-engine-capabilities.ts";
17
18
  import type { ProbeReport } from "../../types.ts";
18
19
 
20
+ const FAKE_ENGINE = join(
21
+ dirname(fileURLToPath(import.meta.url)),
22
+ "__fixtures__", "engine-protocol", "fake-engine-protocol.mjs",
23
+ );
24
+ const FIXTURE = join(
25
+ dirname(fileURLToPath(import.meta.url)),
26
+ "__fixtures__", "engine-protocol", "smoke-run.fixture.json",
27
+ );
28
+
29
+ let dataDir: string;
30
+
31
+ beforeEach(() => {
32
+ dataDir = mkdtempSync(join(tmpdir(), "w10-c1-probe-"));
33
+ });
34
+
35
+ afterEach(() => {
36
+ rmSync(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
37
+ });
38
+
19
39
  function assertProbeShape(report: ProbeReport): void {
20
40
  expect(typeof report.ok).toBe("boolean");
21
41
  expect(typeof report.engineVersion).toBe("string");
@@ -34,44 +54,47 @@ function assertProbeShape(report: ProbeReport): void {
34
54
  }
35
55
  }
36
56
 
37
- describe("conformance C1:probe 形状(ProbeReport 字段完整 + 失败含恢复指引)", () => {
38
- it("pi:成功路径形状(fake 版本探测,不 spawn 真进程)", async () => {
39
- const engine = new PiEngine({ getService: () => null, probeVersion: async () => "0.84.1" });
40
- const report = await engine.probe();
41
- assertProbeShape(report);
42
- expect(report.ok).toBe(true);
43
- expect(report.engineVersion).toBe("0.84.1");
57
+ function makeEngine(extraEnv: Record<string, string> = {}): RemoteEngine {
58
+ const client = new EngineClient({
59
+ engineId: "fake",
60
+ command: process.execPath,
61
+ args: [FAKE_ENGINE],
62
+ hostKind: "test",
63
+ hostVersion: "w10-c1-probe",
64
+ dataDir,
65
+ envPrefixes: [],
66
+ baseEnv: { ...process.env, FAKE_PROTOCOL_FIXTURE: FIXTURE, ...extraEnv },
44
67
  });
45
-
46
- it("pi:失败路径(invocation 不可解析)——error.recovery 非空", async () => {
47
- const engine = new PiEngine({
48
- getService: () => null,
49
- probeVersion: async () => undefined,
50
- });
51
- // PATH 无 pi 时 invocation check 失败;有 pi 时版本 fake 失败——两形态都走断言器
52
- const report = await engine.probe({ force: true });
53
- assertProbeShape(report);
68
+ return new RemoteEngine({
69
+ engineId: "fake",
70
+ client,
71
+ manifest: { capabilities: FAKE_CAPABILITIES },
72
+ dataDir,
73
+ hostKind: "test",
54
74
  });
75
+ }
55
76
 
56
- it("zcode:成功路径形状(存在的 cliPath + fake 版本探测)", async () => {
57
- const engine = new ZcodeEngine({
58
- engineDataDir: () => fs.mkdtempSync(path.join(os.tmpdir(), "probe-zcode-")),
59
- cliPath: fileURLToPath(import.meta.url),
60
- probeVersion: async () => "0.16.5",
61
- });
62
- const report = await engine.probe();
63
- assertProbeShape(report);
64
- expect(report.ok).toBe(true);
77
+ describe("conformance C1:probe 形状(协议黑盒:RemoteEngine × fake 引擎)", () => {
78
+ it("成功路径:probe 帧往返 ProbeReport 形状完整", async () => {
79
+ const engine = makeEngine();
80
+ try {
81
+ const report = await engine.probe();
82
+ assertProbeShape(report);
83
+ expect(report.ok).toBe(true);
84
+ } finally {
85
+ await engine.dispose();
86
+ }
65
87
  });
66
88
 
67
- it("zcode:失败路径(二进制不存在)——error.recovery 非空且含重探指引", async () => {
68
- const engine = new ZcodeEngine({
69
- engineDataDir: () => fs.mkdtempSync(path.join(os.tmpdir(), "probe-zcode-")),
70
- cliPath: "/nonexistent/zcode.cjs",
71
- });
72
- const report = await engine.probe();
73
- assertProbeShape(report);
74
- expect(report.ok).toBe(false);
75
- expect(report.error?.recovery).toContain("--version");
89
+ it("失败路径(FAKE_PROBE_FAIL):ok=false + error.recovery 非空且指向动作", async () => {
90
+ const engine = makeEngine({ FAKE_PROBE_FAIL: "1" });
91
+ try {
92
+ const report = await engine.probe({ force: true });
93
+ assertProbeShape(report);
94
+ expect(report.ok).toBe(false);
95
+ expect(report.error?.recovery).toContain("Reinstall");
96
+ } finally {
97
+ await engine.dispose();
98
+ }
76
99
  });
77
100
  });