@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,792 +0,0 @@
1
- // session-channel.test.ts —— R3 会话层测试:全部跑 __fixtures__/fake-appserver.mjs
2
- // 子进程(scenario 注入会话场景),绝不 spawn 真 zcode.cjs。覆盖验收条款:
3
- // A.2 帧序列逐字断言(create 参数键集与值形态 / subscribe deliveryKind 必填 /
4
- // send 字段是 content 不是 text)/ sessionId 从 result.session.sessionId 提取且不用
5
- // projection / 终态判定(turn.terminal 权威 + 缺失时收尾帧宽松判定 + error status
6
- // 亦终态)/ read 兜底全文拼接 == delta 拼接(不变量 1,四方双来源)/ usage 收尾帧
7
- // 提取并随 resolve 返回(step-finish tokens 宽容兜底)/ close 成功与失败路径都被
8
- // 调用(try/finally)/ -32601/-32602 结构化透传(R5 降级链地基)/ resolve 严格晚于
9
- // 末事件回调(不变量 2)/ golden 双副本 diff + 帧序列语料四类样本全链回放。
10
-
11
- import { createHash } from "node:crypto";
12
- import { mkdtempSync, readFileSync, writeFileSync } from "node:fs";
13
- import { tmpdir } from "node:os";
14
- import { join } from "node:path";
15
- import { fileURLToPath } from "node:url";
16
-
17
- import { afterEach, describe, expect, it, vi } from "vitest";
18
-
19
- import {
20
- AppServerConnection,
21
- buildAppServerEnv,
22
- isAppServerRpcError,
23
- } from "../connection.ts";
24
- import { ZCODE_APPSERVER_GOLDEN } from "../golden-sample.ts";
25
- import {
26
- SessionChannel,
27
- SUBSCRIBE_DELIVERY_KIND,
28
- TurnTimeoutError,
29
- extractAssistantText,
30
- extractCreatedSessionId,
31
- extractReadUsage,
32
- stableWorkspaceKey,
33
- } from "../session-channel.ts";
34
-
35
- const TMP = mkdtempSync(join(tmpdir(), "zcode-sess-"));
36
- const FAKE_CLI = fileURLToPath(
37
- new URL("./__fixtures__/fake-appserver.mjs", import.meta.url)
38
- );
39
-
40
- // golden fixture(采集 SSOT)——双副本 diff 断言的数据源
41
- const GOLDEN_FIXTURE = JSON.parse(
42
- readFileSync(
43
- fileURLToPath(
44
- new URL("./__fixtures__/zcode-golden-appserver.json", import.meta.url)
45
- ),
46
- "utf8"
47
- )
48
- ) as { frames: typeof ZCODE_APPSERVER_GOLDEN };
49
-
50
- const GOLDEN_SESSION_ID = "sess_golden_r3_01";
51
- const GOLDEN_FULL_TEXT = "你好,任务完成";
52
- const GOLDEN_FINAL_USAGE = { inputTokens: 12599, outputTokens: 17 };
53
- const GOLDEN_READ_TOKENS = {
54
- input: 12599,
55
- output: 17,
56
- reasoning: 0,
57
- cache: 512,
58
- total: 12616,
59
- };
60
-
61
- /** 任意 RPC error 注入形态(fake scenario 的 error 三件套)。 */
62
- interface RpcErrorSpec {
63
- code: number;
64
- message: string;
65
- data?: unknown;
66
- }
67
-
68
- interface ScenarioOverrides {
69
- /** 终态帧组去掉 turn.terminal(宽松判定场景)。 */
70
- dropTurnTerminal?: boolean;
71
- /** 终态帧组去掉收尾帧(delta 聚合 / read tokens 兜底场景)。 */
72
- dropFinalFrame?: boolean;
73
- /** 整组替换推送帧(完全自定义帧序)。 */
74
- replaceSendPushes?: string[];
75
- /** 在默认帧序之后追加帧(迟到帧注入)。 */
76
- extraSendPushes?: string[];
77
- createResult?: unknown;
78
- createError?: RpcErrorSpec;
79
- sendError?: RpcErrorSpec;
80
- sendResult?: unknown;
81
- readError?: RpcErrorSpec;
82
- readResult?: unknown;
83
- }
84
-
85
- interface ChannelHandle {
86
- ch: SessionChannel;
87
- conn: AppServerConnection;
88
- stateFile: string;
89
- workspacePath: string;
90
- }
91
-
92
- const CONNECTIONS: AppServerConnection[] = [];
93
- let connSeq = 0;
94
-
95
- /** NDJSON 行 / 帧对象统一规范化为帧对象(golden 语料是 NDJSON 行,fake 需要对象逐字回放)。 */
96
- function toFrame(f: string | Record<string, unknown>): Record<string, unknown> {
97
- return typeof f === "string" ? (JSON.parse(f) as Record<string, unknown>) : f;
98
- }
99
-
100
- /** 建一条连到 fake 的连接 + 会话通道(scenario 文件随 env 固化进子进程)。 */
101
- function makeChannel(overrides: ScenarioOverrides = {}): ChannelHandle {
102
- connSeq += 1;
103
- const stateFile = join(TMP, `state-${connSeq}.jsonl`);
104
- const scenarioFile = join(TMP, `scenario-${connSeq}.json`);
105
- const workspacePath = join(TMP, `ws-${connSeq}`);
106
- const pushes = (
107
- overrides.replaceSendPushes ?? [
108
- ...ZCODE_APPSERVER_GOLDEN.pushStream,
109
- ...(overrides.dropTurnTerminal
110
- ? []
111
- : [ZCODE_APPSERVER_GOLDEN.terminal[0]]),
112
- ...(overrides.dropFinalFrame ? [] : [ZCODE_APPSERVER_GOLDEN.terminal[1]]),
113
- ]
114
- ).map(toFrame);
115
- const scenario: Record<string, unknown> = {
116
- createResult:
117
- overrides.createResult ??
118
- JSON.parse(ZCODE_APPSERVER_GOLDEN.createResponse),
119
- sendPushes: overrides.extraSendPushes
120
- ? [...pushes, ...overrides.extraSendPushes.map(toFrame)]
121
- : pushes,
122
- readResult:
123
- overrides.readResult ?? JSON.parse(ZCODE_APPSERVER_GOLDEN.readResponse),
124
- ...(overrides.createError !== undefined
125
- ? { createError: overrides.createError }
126
- : {}),
127
- ...(overrides.sendError !== undefined
128
- ? { sendError: overrides.sendError }
129
- : {}),
130
- ...(overrides.sendResult !== undefined
131
- ? { sendResult: overrides.sendResult }
132
- : {}),
133
- ...(overrides.readError !== undefined
134
- ? { readError: overrides.readError }
135
- : {}),
136
- ...(overrides.readResult !== undefined
137
- ? { readResult: overrides.readResult }
138
- : {}),
139
- };
140
- writeFileSync(scenarioFile, JSON.stringify(scenario));
141
- // buildAppServerEnv 2026-09 共享宿主 HOME 改版后只收单参 baseEnv(不再覆写 HOME);
142
- // 旧两参形态 (homeDir, extraEnv) 的第一参被展开成字符索引对象——PATH 进不了子 env,
143
- // spawn("node") 无 PATH 可查 → ENOENT。此处传 env 对象本体(与 zcode-engine-*
144
- // 引擎测试的 deps.processEnv 同模式)。
145
- const conn = new AppServerConnection({
146
- cliPath: FAKE_CLI,
147
- cwd: workspacePath,
148
- env: buildAppServerEnv({
149
- PATH: process.env.PATH ?? "",
150
- FAKE_STATE_FILE: stateFile,
151
- FAKE_SESSION_SCENARIO: scenarioFile,
152
- }),
153
- stderrLogPath: join(TMP, `stderr-${connSeq}.log`),
154
- });
155
- CONNECTIONS.push(conn);
156
- return { ch: new SessionChannel(conn), conn, stateFile, workspacePath };
157
- }
158
-
159
- afterEach(async () => {
160
- const conns = CONNECTIONS.splice(0);
161
- for (const conn of conns) conn.close(); // 同步 SIGTERM 面快速收割;shutdown 幂等收尸
162
- await Promise.allSettled(conns.map((c) => c.shutdown({ graceMs: 1_000 })));
163
- });
164
-
165
- // ------------------------------------------- 流水读取 helpers(fake 侧 recv 帧)
166
-
167
- interface StateEvent {
168
- seq: number;
169
- ev: string;
170
- [key: string]: unknown;
171
- }
172
-
173
- function readState(file: string): StateEvent[] {
174
- try {
175
- return readFileSync(file, "utf8")
176
- .trim()
177
- .split("\n")
178
- .filter(Boolean)
179
- .map((l) => JSON.parse(l) as StateEvent);
180
- } catch {
181
- return [];
182
- }
183
- }
184
-
185
- function isRecord(v: unknown): v is Record<string, unknown> {
186
- return typeof v === "object" && v !== null && !Array.isArray(v);
187
- }
188
-
189
- interface SentFrame {
190
- id: number;
191
- method: string;
192
- params: Record<string, unknown>;
193
- }
194
-
195
- function sentFrames(stateFile: string, method: string): SentFrame[] {
196
- return readState(stateFile)
197
- .map((e) => e.frame)
198
- .filter(
199
- (f): f is SentFrame =>
200
- isRecord(f) &&
201
- f.method === method &&
202
- isRecord(f.params) &&
203
- typeof f.id === "number"
204
- );
205
- }
206
-
207
- /** 客户端请求帧的 method 序列(我方反向应答帧无 method 不计入)。 */
208
- function sentMethods(stateFile: string): string[] {
209
- return readState(stateFile)
210
- .map((e) => e.frame)
211
- .filter(
212
- (f): f is Record<string, unknown> =>
213
- isRecord(f) && typeof f.method === "string"
214
- )
215
- .map((f) => f.method as string);
216
- }
217
-
218
- // ============================================================
219
- // A.2 帧序列逐字断言(出站精确键集——对面 zod strict 拒未知键)
220
- // ============================================================
221
-
222
- describe("A.2 帧序列逐字断言", () => {
223
- it("create 帧:参数键集恰 {workspace, mode, persistence};workspaceKey='ws-'+sha256 前 16;persistence:'immediate'", async () => {
224
- const { ch, stateFile, workspacePath } = makeChannel();
225
- await ch.runTurn({ workspacePath, mode: "yolo" }, "做点什么");
226
- const frames = sentFrames(stateFile, "session/create");
227
- expect(frames).toHaveLength(1);
228
- // 帧级键集 {id, method, params}——无 jsonrpc、无未知键
229
- expect(Object.keys(frames[0]).sort()).toEqual(["id", "method", "params"]);
230
- const params = frames[0].params as Record<string, { workspaceKey?: string; workspacePath?: string; [k: string]: unknown }>;
231
- expect(Object.keys(params).sort()).toEqual([
232
- "mode",
233
- "persistence",
234
- "workspace",
235
- ]);
236
- expect(params.mode).toBe("yolo");
237
- expect(params.persistence).toBe("immediate");
238
- expect(Object.keys(params.workspace).sort()).toEqual([
239
- "workspaceKey",
240
- "workspacePath",
241
- ]);
242
- expect(params.workspace).toEqual({
243
- workspacePath,
244
- workspaceKey:
245
- "ws-" +
246
- createHash("sha256").update(workspacePath).digest("hex").slice(0, 16),
247
- });
248
- expect(stableWorkspaceKey(workspacePath)).toBe(
249
- params.workspace.workspaceKey
250
- );
251
- }, 10_000);
252
-
253
- it("create 可选键:model 是 strict 对象(providerId/modelId/variant);空 toolAllowlist 不设键;toolDenylist 传则带", async () => {
254
- const { ch, stateFile, workspacePath } = makeChannel();
255
- await ch.runTurn(
256
- {
257
- workspacePath,
258
- mode: "yolo",
259
- model: { providerId: "zai", modelId: "glm-5.3", variant: "plan" },
260
- thoughtLevel: "high",
261
- toolAllowlist: [],
262
- toolDenylist: ["Bash", "Write"],
263
- },
264
- "做点什么"
265
- );
266
- const params = sentFrames(stateFile, "session/create")[0].params as Record<string, { workspaceKey?: string; workspacePath?: string; [k: string]: unknown }>;
267
- expect(Object.keys(params).sort()).toEqual([
268
- "mode",
269
- "model",
270
- "persistence",
271
- "thoughtLevel",
272
- "toolDenylist",
273
- "workspace",
274
- ]);
275
- // strict 对象逐字段(A.2 ①:字符串 model 会被 -32602 拒收)
276
- expect(params.model).toEqual({
277
- providerId: "zai",
278
- modelId: "glm-5.3",
279
- variant: "plan",
280
- });
281
- expect(params.thoughtLevel).toBe("high");
282
- expect(params.toolDenylist).toEqual(["Bash", "Write"]);
283
- }, 10_000);
284
-
285
- it("subscribe 帧:参数恰 {sessionId, deliveryKind:'desktop-continuous'}(deliveryKind 必填)", async () => {
286
- const { ch, stateFile, workspacePath } = makeChannel();
287
- await ch.runTurn({ workspacePath, mode: "yolo" }, "做点什么");
288
- const frames = sentFrames(stateFile, "session/subscribe");
289
- expect(frames).toHaveLength(1);
290
- expect(frames[0].params).toEqual({
291
- sessionId: GOLDEN_SESSION_ID,
292
- deliveryKind: "desktop-continuous",
293
- });
294
- expect(Object.keys(frames[0].params).sort()).toEqual([
295
- "deliveryKind",
296
- "sessionId",
297
- ]);
298
- expect(SUBSCRIBE_DELIVERY_KIND).toBe("desktop-continuous");
299
- }, 10_000);
300
-
301
- it("send 帧:字段是 content 不是 text——参数恰 {sessionId, content}", async () => {
302
- const { ch, stateFile, workspacePath } = makeChannel();
303
- await ch.runTurn({ workspacePath, mode: "yolo" }, "做点什么");
304
- const frames = sentFrames(stateFile, "session/send");
305
- expect(frames).toHaveLength(1);
306
- expect(frames[0].params).toEqual({
307
- sessionId: GOLDEN_SESSION_ID,
308
- content: "做点什么",
309
- });
310
- expect(Object.keys(frames[0].params).sort()).toEqual([
311
- "content",
312
- "sessionId",
313
- ]);
314
- expect("text" in frames[0].params).toBe(false);
315
- }, 10_000);
316
-
317
- it("read/close 帧参数恰 {sessionId}", async () => {
318
- const { ch, stateFile, workspacePath } = makeChannel();
319
- await ch.runTurn({ workspacePath, mode: "yolo" }, "做点什么");
320
- expect(sentFrames(stateFile, "session/read")).toHaveLength(1);
321
- expect(sentFrames(stateFile, "session/read")[0].params).toEqual({
322
- sessionId: GOLDEN_SESSION_ID,
323
- });
324
- expect(sentFrames(stateFile, "session/close")).toHaveLength(1);
325
- expect(sentFrames(stateFile, "session/close")[0].params).toEqual({
326
- sessionId: GOLDEN_SESSION_ID,
327
- });
328
- }, 10_000);
329
- });
330
-
331
- // ============================================================
332
- // sessionId 提取(projection.sessionId 恒 "unknown" 勿用)
333
- // ============================================================
334
-
335
- describe("sessionId 提取", () => {
336
- it("golden create 应答:从 result.session.sessionId 提取,不用 projection.sessionId(恒 'unknown')", async () => {
337
- const { ch, workspacePath } = makeChannel();
338
- const sid = await ch.createSession({ workspacePath, mode: "yolo" });
339
- expect(sid).toBe(GOLDEN_SESSION_ID);
340
- expect(sid).not.toBe("unknown");
341
- // 提取函数对 golden 应答的单点断言(conformance/golden 回放层可复用)
342
- expect(
343
- extractCreatedSessionId(JSON.parse(ZCODE_APPSERVER_GOLDEN.createResponse))
344
- ).toBe(GOLDEN_SESSION_ID);
345
- }, 10_000);
346
-
347
- it("无 sessionId 的 create 应答 → 显式错误(含应答摘要,不落 'unknown')", async () => {
348
- const { ch, workspacePath } = makeChannel({
349
- createResult: { projection: { sessionId: "unknown" } },
350
- });
351
- await expect(
352
- ch.createSession({ workspacePath, mode: "yolo" })
353
- ).rejects.toThrow(/未返回 sessionId/);
354
- }, 10_000);
355
-
356
- it("宽松兜底链:session.id / 顶层 sessionId 旧形态可提取", () => {
357
- expect(extractCreatedSessionId({ session: { id: "sess_alt" } })).toBe(
358
- "sess_alt"
359
- );
360
- expect(extractCreatedSessionId({ sessionId: "sess_top" })).toBe("sess_top");
361
- expect(
362
- extractCreatedSessionId({ projection: { sessionId: "unknown" } })
363
- ).toBeUndefined();
364
- });
365
- });
366
-
367
- // ============================================================
368
- // 终态判定(D4 + 不变量 1)
369
- // ============================================================
370
-
371
- describe("终态判定", () => {
372
- it("turn.terminal 权威:success → resolve,terminal={status:'success', source:'turn.terminal'}", async () => {
373
- const { ch, workspacePath } = makeChannel();
374
- const r = await ch.runTurn({ workspacePath, mode: "yolo" }, "做点什么");
375
- expect(r.terminal).toEqual({ status: "success", source: "turn.terminal" });
376
- }, 10_000);
377
-
378
- it("turn.terminal status:'error' 亦终态(不归类会挂到超时);read 兜底照常", async () => {
379
- const errorTerminal = JSON.stringify({
380
- method: "v4/telemetry/event",
381
- params: { kind: "turn.terminal", status: "error" },
382
- });
383
- const finalFrame = ZCODE_APPSERVER_GOLDEN.terminal[1];
384
- const { ch, workspacePath } = makeChannel({
385
- replaceSendPushes: [
386
- ...ZCODE_APPSERVER_GOLDEN.pushStream,
387
- errorTerminal,
388
- finalFrame,
389
- ],
390
- });
391
- const r = await ch.runTurn({ workspacePath, mode: "yolo" }, "做点什么");
392
- expect(r.terminal).toEqual({ status: "error", source: "turn.terminal" });
393
- expect(r.response).toBe(GOLDEN_FULL_TEXT);
394
- }, 10_000);
395
-
396
- it("宽松匹配防洪堤:turn.terminal 缺失时收尾帧(payload.response 存在)判定终态", async () => {
397
- const { ch, workspacePath } = makeChannel({ dropTurnTerminal: true });
398
- const r = await ch.runTurn({ workspacePath, mode: "yolo" }, "做点什么");
399
- expect(r.terminal).toEqual({ status: "success", source: "final-frame" });
400
- expect(r.response).toBe(GOLDEN_FULL_TEXT);
401
- }, 10_000);
402
-
403
- it("迟到帧丢弃:收尾帧判定终态后,迟到的 turn.terminal 不改判、迟到的 delta 不触发回调", async () => {
404
- const lateDelta = JSON.stringify({
405
- method: "session/event",
406
- params: { sessionId: GOLDEN_SESSION_ID, payload: { delta: "迟到文本" } },
407
- });
408
- const lateTerminal = JSON.stringify({
409
- method: "v4/telemetry/event",
410
- params: { kind: "turn.terminal", status: "success" },
411
- });
412
- const deltas: string[] = [];
413
- const { ch, workspacePath } = makeChannel({
414
- dropTurnTerminal: true,
415
- extraSendPushes: [lateDelta, lateTerminal],
416
- });
417
- const r = await ch.runTurn({ workspacePath, mode: "yolo" }, "做点什么", {
418
- onTextDelta: (d) => deltas.push(d),
419
- });
420
- expect(deltas).toEqual(["你好", ",", "任务完成"]); // 迟到 delta 未触发(不变量 2)
421
- expect(r.terminal).toEqual({ status: "success", source: "final-frame" }); // 迟到 turn.terminal 未改判
422
- }, 10_000);
423
-
424
- it("他人 sessionId 的推送帧不归因(防串线)", async () => {
425
- const crossTalk = JSON.stringify({
426
- method: "session/event",
427
- params: {
428
- sessionId: "sess_someone_else",
429
- payload: { delta: "串线文本" },
430
- },
431
- });
432
- const deltas: string[] = [];
433
- const { ch, workspacePath } = makeChannel({ extraSendPushes: [crossTalk] });
434
- const r = await ch.runTurn({ workspacePath, mode: "yolo" }, "做点什么", {
435
- onTextDelta: (d) => deltas.push(d),
436
- });
437
- expect(deltas).toEqual(["你好", ",", "任务完成"]);
438
- expect(r.response).not.toContain("串线文本");
439
- }, 10_000);
440
- });
441
-
442
- // ============================================================
443
- // read 兜底与不变量 1(text_delta 拼接 == read 全文,双来源)
444
- // ============================================================
445
-
446
- describe("read 兜底与不变量 1", () => {
447
- it("golden 全链:delta 拼接 === resolve.response === read 全文 === 收尾帧 response(四方一致)", async () => {
448
- const deltas: string[] = [];
449
- const { ch, workspacePath } = makeChannel();
450
- const r = await ch.runTurn({ workspacePath, mode: "yolo" }, "做点什么", {
451
- onTextDelta: (d) => deltas.push(d),
452
- });
453
- expect(deltas).toEqual(["你好", ",", "任务完成"]);
454
- expect(deltas.join("")).toBe(GOLDEN_FULL_TEXT);
455
- // read 是权威来源:golden readResponse 的 assistant text parts 拼接与 delta 流一致
456
- expect(
457
- extractAssistantText(JSON.parse(ZCODE_APPSERVER_GOLDEN.readResponse))
458
- ).toBe(GOLDEN_FULL_TEXT);
459
- expect(r.response).toBe(deltas.join(""));
460
- // 收尾帧 response(第二来源)亦一致
461
- const finalFrame = JSON.parse(ZCODE_APPSERVER_GOLDEN.terminal[1]) as {
462
- params: { payload: { response: string } };
463
- };
464
- expect(finalFrame.params.payload.response).toBe(GOLDEN_FULL_TEXT);
465
- expect(r.response).toBe(finalFrame.params.payload.response);
466
- }, 10_000);
467
-
468
- it("read 失败(-32004)→ 收尾帧 response 兜底,不抛", async () => {
469
- const { ch, workspacePath } = makeChannel({
470
- readError: { code: -32004, message: "Session is not active" },
471
- });
472
- const r = await ch.runTurn({ workspacePath, mode: "yolo" }, "做点什么");
473
- expect(r.response).toBe(GOLDEN_FULL_TEXT); // 收尾帧
474
- expect(r.usage).toEqual(GOLDEN_FINAL_USAGE); // usage 仍从收尾帧提取
475
- }, 10_000);
476
-
477
- it("read 失败 + 无收尾帧 → delta 聚合兜底(三层降级链末位),usage 缺席", async () => {
478
- const deltas: string[] = [];
479
- const { ch, workspacePath } = makeChannel({
480
- dropFinalFrame: true,
481
- readError: { code: -32003, message: "boom" },
482
- });
483
- const r = await ch.runTurn({ workspacePath, mode: "yolo" }, "做点什么", {
484
- onTextDelta: (d) => deltas.push(d),
485
- });
486
- expect(r.response).toBe(deltas.join(""));
487
- expect(r.response).toBe(GOLDEN_FULL_TEXT);
488
- expect(r.usage).toBeUndefined();
489
- }, 10_000);
490
-
491
- it("read 成功但提取不到 assistant 文本(messages 空)→ 收尾帧兜底", async () => {
492
- const { ch, workspacePath } = makeChannel({ readResult: { messages: [] } });
493
- const r = await ch.runTurn({ workspacePath, mode: "yolo" }, "做点什么");
494
- expect(r.response).toBe(GOLDEN_FULL_TEXT);
495
- }, 10_000);
496
- });
497
-
498
- // ============================================================
499
- // usage(收尾帧权威 → read step-finish tokens 宽容兜底)
500
- // ============================================================
501
-
502
- describe("usage", () => {
503
- it("usage 从收尾帧提取并随 resolve 返回(A.2 权威形态,精确对象——read tokens 不遮蔽)", async () => {
504
- const { ch, workspacePath } = makeChannel();
505
- const r = await ch.runTurn({ workspacePath, mode: "yolo" }, "做点什么");
506
- expect(r.usage).toEqual(GOLDEN_FINAL_USAGE);
507
- }, 10_000);
508
-
509
- it("收尾帧缺失时 read step-finish tokens 宽容兜底(A.5 未确认项——字段缺席不抛)", async () => {
510
- const { ch, workspacePath } = makeChannel({ dropFinalFrame: true });
511
- const r = await ch.runTurn({ workspacePath, mode: "yolo" }, "做点什么");
512
- expect(r.usage).toEqual(GOLDEN_READ_TOKENS);
513
- // 提取函数单点:golden readResponse 的 tokens 结构(宽容解析面)
514
- expect(
515
- extractReadUsage(JSON.parse(ZCODE_APPSERVER_GOLDEN.readResponse))
516
- ).toEqual(GOLDEN_READ_TOKENS);
517
- }, 10_000);
518
-
519
- it("收尾帧与 read tokens 皆无 → usage 缺席(不给残缺对象)", async () => {
520
- const { ch, workspacePath } = makeChannel({
521
- dropFinalFrame: true,
522
- readResult: { messages: [] },
523
- });
524
- const r = await ch.runTurn({ workspacePath, mode: "yolo" }, "做点什么");
525
- expect(r.usage).toBeUndefined();
526
- }, 10_000);
527
- });
528
-
529
- // ============================================================
530
- // close(D4 用后即毁:成功/失败路径都 close——try/finally)
531
- // ============================================================
532
-
533
- describe("close(D4 用后即毁)", () => {
534
- it("成功路径:请求帧序恰 create→subscribe→send→read→close(close 最后)", async () => {
535
- const { ch, stateFile, workspacePath } = makeChannel();
536
- await ch.runTurn({ workspacePath, mode: "yolo" }, "做点什么");
537
- expect(sentMethods(stateFile)).toEqual([
538
- "session/create",
539
- "session/subscribe",
540
- "session/send",
541
- "session/read",
542
- "session/close",
543
- ]);
544
- }, 10_000);
545
-
546
- it("send 失败(-32010 busy)→ reject 且 close 仍被调用(try/finally)", async () => {
547
- const { ch, stateFile, workspacePath } = makeChannel({
548
- sendError: {
549
- code: -32010,
550
- message: "A prompt is already running for this session",
551
- },
552
- });
553
- const err = await ch
554
- .runTurn({ workspacePath, mode: "yolo" }, "做点什么")
555
- .then(
556
- () => {
557
- throw new Error("should reject");
558
- },
559
- (e: unknown) => e
560
- );
561
- expect(isAppServerRpcError(err)).toBe(true);
562
- if (isAppServerRpcError(err)) expect(err.code).toBe(-32010);
563
- expect(sentFrames(stateFile, "session/close")).toHaveLength(1); // 失败路径也 close
564
- }, 10_000);
565
-
566
- it("send 返回 accepted:false → 显式 reject 且 close 已尽力", async () => {
567
- const { ch, stateFile, workspacePath } = makeChannel({
568
- sendResult: { accepted: false },
569
- });
570
- await expect(
571
- ch.runTurn({ workspacePath, mode: "yolo" }, "做点什么")
572
- ).rejects.toThrow(/accepted:false/);
573
- expect(sentFrames(stateFile, "session/close")).toHaveLength(1);
574
- }, 10_000);
575
-
576
- it("终态超时(turn.terminal 与收尾帧均未达)→ TurnTimeoutError(ceiling 形态,P0-1 两 timer 语义)且 close 仍被调用", async () => {
577
- const onlyRunning = [ZCODE_APPSERVER_GOLDEN.pushStream[0]]; // 仅 state.updated,无终态
578
- const { ch, stateFile, workspacePath } = makeChannel({
579
- replaceSendPushes: onlyRunning,
580
- });
581
- const err = await ch
582
- .runTurn({ workspacePath, mode: "yolo" }, "做点什么", {
583
- turnTimeoutMs: 250, // P0-1 语义收窄:显式总上界(不再是固定墙钟预算)
584
- })
585
- .then(
586
- () => {
587
- throw new Error("should reject");
588
- },
589
- (e: unknown) => e
590
- );
591
- expect(err).toBeInstanceOf(TurnTimeoutError);
592
- expect((err as TurnTimeoutError).kind).toBe("ceiling");
593
- expect(sentFrames(stateFile, "session/close")).toHaveLength(1);
594
- }, 10_000);
595
- });
596
-
597
- // ============================================================
598
- // 错误透传(R5 降级链地基:-32601/-32602 结构化透传)
599
- // ============================================================
600
-
601
- describe("错误透传(-32601/-32602 漂移类)", () => {
602
- it("create -32602:reject 形态 isAppServerRpcError,code/message/data 原样(zod 诊断随 data 带出);无会话故无 close 帧", async () => {
603
- const zodIssues = [{ path: ["model"], message: "not an object" }];
604
- const { ch, stateFile, workspacePath } = makeChannel({
605
- createError: { code: -32602, message: "Invalid params", data: zodIssues },
606
- });
607
- const err = await ch
608
- .runTurn({ workspacePath, mode: "yolo" }, "做点什么")
609
- .then(
610
- () => {
611
- throw new Error("should reject");
612
- },
613
- (e: unknown) => e
614
- );
615
- expect(isAppServerRpcError(err)).toBe(true);
616
- if (isAppServerRpcError(err)) {
617
- expect(err.code).toBe(-32602);
618
- expect(err.data).toEqual(zodIssues);
619
- expect(err.message).toContain("Invalid params");
620
- expect(err.message).toContain("[-32602]");
621
- }
622
- // sessionId 未建立:无从 close(D4 会话尚未存在)
623
- expect(sentFrames(stateFile, "session/close")).toHaveLength(0);
624
- }, 10_000);
625
-
626
- it("send -32602:透传且会话已建立 → close 已尽力(try/finally 不因透传跳过)", async () => {
627
- const { ch, stateFile, workspacePath } = makeChannel({
628
- sendError: {
629
- code: -32602,
630
- message: "Invalid params",
631
- data: [{ path: ["content"] }],
632
- },
633
- });
634
- const err = await ch
635
- .runTurn({ workspacePath, mode: "yolo" }, "做点什么")
636
- .then(
637
- () => {
638
- throw new Error("should reject");
639
- },
640
- (e: unknown) => e
641
- );
642
- expect(isAppServerRpcError(err)).toBe(true);
643
- if (isAppServerRpcError(err)) expect(err.code).toBe(-32602);
644
- expect(sentFrames(stateFile, "session/close")).toHaveLength(1);
645
- }, 10_000);
646
-
647
- it("-32601(方法不存在)同样结构化透传", async () => {
648
- const { ch, workspacePath } = makeChannel({
649
- createError: { code: -32601, message: "Method not found" },
650
- });
651
- const err = await ch.createSession({ workspacePath, mode: "yolo" }).then(
652
- () => {
653
- throw new Error("should reject");
654
- },
655
- (e: unknown) => e
656
- );
657
- expect(isAppServerRpcError(err)).toBe(true);
658
- if (isAppServerRpcError(err)) {
659
- expect(err.code).toBe(-32601);
660
- expect(err.message).toContain("Method not found");
661
- }
662
- }, 10_000);
663
- });
664
-
665
- // ============================================================
666
- // resolve 时序(不变量 2:resolve 严格晚于全部事件回调)
667
- // ============================================================
668
-
669
- describe("resolve 时序(不变量 2)", () => {
670
- it("事件回调序在前、resolve 严格最后(终态判定 → read 兜底 → 才 resolve)", async () => {
671
- const order: string[] = [];
672
- const { ch, workspacePath } = makeChannel();
673
- const r = await ch.runTurn({ workspacePath, mode: "yolo" }, "做点什么", {
674
- onTextDelta: (d) => order.push(`delta:${d}`),
675
- });
676
- order.push("resolve");
677
- expect(order).toEqual([
678
- "delta:你好",
679
- "delta:,",
680
- "delta:任务完成",
681
- "resolve",
682
- ]);
683
- expect(r.response).toBe(GOLDEN_FULL_TEXT);
684
- }, 10_000);
685
- });
686
-
687
- // ============================================================
688
- // [R4] 连接崩溃收割(onClose 面 → failAllTurns——不再挂到 turnTimeoutMs)
689
- // ============================================================
690
-
691
- describe("连接崩溃收割(R4 onClose 面)", () => {
692
- it("进程崩溃(test/suicide)→ 在途 turn 立即 reject(崩溃 reason 含 stderr 尾)——不等 turnTimeoutMs", async () => {
693
- // 挂起场景(无终态)+ 长预算:崩溃收割前 turnTimeoutMs 兜底永远不会到
694
- const onlyRunning = [ZCODE_APPSERVER_GOLDEN.pushStream[0]];
695
- const { ch, conn, stateFile, workspacePath } = makeChannel({ replaceSendPushes: onlyRunning });
696
- const turn = ch.runTurn({ workspacePath, mode: "yolo" }, "做点什么", {
697
- turnTimeoutMs: 60_000,
698
- });
699
- // 等 send 已达(fake 流水可观测)后在同一连接上触发自杀(崩溃收割的触发面)
700
- await vi.waitFor(() => {
701
- expect(sentFrames(stateFile, "session/send")).toHaveLength(1);
702
- }, { timeout: 5_000 });
703
- // suicide 不回应答——崩溃时该请求随 pending 一起 reject(预期,吞掉)
704
- await conn.request("test/suicide").then(
705
- () => undefined,
706
- () => undefined,
707
- );
708
- const err = await turn.then(
709
- () => {
710
- throw new Error("should reject");
711
- },
712
- (e: unknown) => e,
713
- );
714
- expect(err).toBeInstanceOf(Error);
715
- expect(String((err as Error).message)).toContain("app-server");
716
- expect(String((err as Error).message)).toMatch(/进程退出|spawn 失败|CRASH-MARK-TAIL/);
717
- }, 10_000);
718
-
719
- it("post:fire-and-forget 帧不等待应答(活连接 true;返回布尔不抛)", async () => {
720
- const { conn } = makeChannel();
721
- expect(typeof conn.post).toBe("function");
722
- expect(conn.post("session/close", { sessionId: "sess_x" })).toBe(true);
723
- await conn.shutdown({ graceMs: 1_000 });
724
- // 死连接:ensureStarted 重建一代(D1 重建语义)→ post 复又可用;不抛即可
725
- expect(typeof conn.post("session/close", { sessionId: "sess_x" })).toBe("boolean");
726
- await conn.shutdown({ graceMs: 1_000 });
727
- }, 10_000);
728
- });
729
-
730
- // ============================================================
731
- // golden 双副本与帧序列语料(R3 验收 ②)
732
- // ============================================================
733
-
734
- describe("golden 双副本与四类样本", () => {
735
- it("双副本 diff:golden-sample.ts 内嵌副本 === fixture frames(防漂移,更新需同步两处)", () => {
736
- expect(ZCODE_APPSERVER_GOLDEN).toEqual(GOLDEN_FIXTURE.frames);
737
- });
738
-
739
- it("四类样本形态自检:create 可提取 sessionId≠unknown;推送流含三 method 且 stream.chunk 无文本;终态双帧;read 可提取全文与 tokens", () => {
740
- // ① create 应答
741
- expect(
742
- extractCreatedSessionId(JSON.parse(ZCODE_APPSERVER_GOLDEN.createResponse))
743
- ).toBe(GOLDEN_SESSION_ID);
744
- // ④ 推送流:state.updated + stream.chunk(无文本)+ session/event(文本在此)
745
- const pushMethods = ZCODE_APPSERVER_GOLDEN.pushStream.map(
746
- (l) => (JSON.parse(l) as { method: string }).method
747
- );
748
- expect(new Set(pushMethods)).toEqual(
749
- new Set(["state.updated", "v4/telemetry/event", "session/event"])
750
- );
751
- const streamChunk = JSON.parse(ZCODE_APPSERVER_GOLDEN.pushStream[1]) as {
752
- params: Record<string, unknown>;
753
- };
754
- expect(streamChunk.params.kind).toBe("stream.chunk");
755
- for (const textKey of ["chunk", "text", "content", "delta"]) {
756
- expect(streamChunk.params[textKey]).toBeUndefined(); // A.2:stream.chunk 无文本
757
- }
758
- const deltaFrame = JSON.parse(ZCODE_APPSERVER_GOLDEN.pushStream[2]) as {
759
- params: { payload: { delta: string } };
760
- };
761
- expect(deltaFrame.params.payload.delta).toBe("你好");
762
- // ⑤ 终态双帧
763
- const terminalFrame = JSON.parse(ZCODE_APPSERVER_GOLDEN.terminal[0]) as {
764
- params: { kind: string; status: string };
765
- };
766
- expect(terminalFrame.params).toEqual({
767
- kind: "turn.terminal",
768
- status: "success",
769
- });
770
- const finalFrame = JSON.parse(ZCODE_APPSERVER_GOLDEN.terminal[1]) as {
771
- params: { payload: { response: string; usage: unknown } };
772
- };
773
- expect(finalFrame.params.payload.response).toBe(GOLDEN_FULL_TEXT);
774
- expect(finalFrame.params.payload.usage).toEqual(GOLDEN_FINAL_USAGE);
775
- // ⑥ read 应答
776
- expect(
777
- extractAssistantText(JSON.parse(ZCODE_APPSERVER_GOLDEN.readResponse))
778
- ).toBe(GOLDEN_FULL_TEXT);
779
- expect(
780
- extractReadUsage(JSON.parse(ZCODE_APPSERVER_GOLDEN.readResponse))
781
- ).toEqual(GOLDEN_READ_TOKENS);
782
- });
783
-
784
- it("golden 帧序列全链回放(fake 逐字回放四类样本):不变量 1 + usage + sessionId 一次收口", async () => {
785
- const { ch, workspacePath } = makeChannel(); // scenario 即 golden 四类样本
786
- const r = await ch.runTurn({ workspacePath, mode: "yolo" }, "做点什么");
787
- expect(r.sessionId).toBe(GOLDEN_SESSION_ID);
788
- expect(r.response).toBe(GOLDEN_FULL_TEXT); // delta 拼接 == read 全文 == 收尾帧
789
- expect(r.usage).toEqual(GOLDEN_FINAL_USAGE);
790
- expect(r.terminal).toEqual({ status: "success", source: "turn.terminal" });
791
- }, 10_000);
792
- });