@zhushanwen/subagent-core 0.2.0 → 0.5.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 (347) hide show
  1. package/README.md +45 -2
  2. package/agents/analyst.md +60 -0
  3. package/agents/coder.md +69 -0
  4. package/agents/debugger.md +66 -0
  5. package/agents/doc-reviewer.md +49 -0
  6. package/agents/explorer.md +63 -0
  7. package/agents/general-purpose.md +32 -0
  8. package/agents/orchestrator.md +61 -0
  9. package/agents/planner.md +53 -0
  10. package/agents/researcher.md +67 -0
  11. package/agents/reviewer.md +73 -0
  12. package/dist/chunk-43ONBFZX.js +240 -0
  13. package/dist/chunk-APZY4IME.js +27 -0
  14. package/dist/chunk-V3VHZ2VX.js +59 -0
  15. package/dist/execution/engine/engines/zcode/constants.cjs +62 -9
  16. package/dist/execution/engine/engines/zcode/constants.d.cts +116 -12
  17. package/dist/execution/engine/engines/zcode/constants.d.ts +116 -12
  18. package/dist/execution/engine/engines/zcode/constants.js +39 -7
  19. package/dist/execution/engine/engines/zcode/reader.d.cts +2 -1
  20. package/dist/execution/engine/engines/zcode/reader.d.ts +2 -1
  21. package/dist/execution/engine/engines/zcode/reader.js +4 -234
  22. package/dist/execution/engine/paths.js +7 -19
  23. package/dist/index.cjs +18234 -1277
  24. package/dist/index.d.cts +5646 -673
  25. package/dist/index.d.ts +5646 -673
  26. package/dist/index.js +17828 -1277
  27. package/dist/{types-BxyAidGf.d.cts → types-Dv4QhSJ_.d.cts} +678 -112
  28. package/dist/{types-BxyAidGf.d.ts → types-Dv4QhSJ_.d.ts} +678 -112
  29. package/package.json +7 -4
  30. package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +0 -1
  31. package/src/__tests__/append-system-prompt-assembly.test.ts +8 -7
  32. package/src/__tests__/fr4-get-state-handshake.test.ts +2 -2
  33. package/src/__tests__/m2-append-content-probe.test.ts +0 -1
  34. package/src/__tests__/manifest-store.test.ts +10 -9
  35. package/src/__tests__/record-store-cache.test.ts +0 -2
  36. package/src/__tests__/record-store-index.test.ts +1 -2
  37. package/src/__tests__/review-fix-loop-script.test.ts +93 -3
  38. package/src/__tests__/review-fix-loop-utils.test.ts +31 -10
  39. package/src/__tests__/robustness-low-batch1.test.ts +6 -5
  40. package/src/__tests__/robustness-medium-batch1.test.ts +2 -2
  41. package/src/__tests__/robustness-medium-batch2.test.ts +17 -6
  42. package/src/__tests__/robustness-medium-batch4.test.ts +2 -2
  43. package/src/__tests__/session-runner.test.ts +1 -1
  44. package/src/__tests__/smoke.test.ts +9 -2
  45. package/src/core/__tests__/host-services.test.ts +2 -2
  46. package/src/core/__tests__/logger.test.ts +1 -1
  47. package/src/core/error-message.ts +9 -0
  48. package/src/core/host-services.ts +21 -5
  49. package/src/core/notify-ports.ts +18 -5
  50. package/src/execution/__tests__/agent-profile.test.ts +232 -0
  51. package/src/execution/__tests__/agent-registry.test.ts +370 -0
  52. package/src/execution/__tests__/agent-result-mapper.test.ts +18 -2
  53. package/src/execution/__tests__/agents-assembly.test.ts +219 -0
  54. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +6 -6
  55. package/src/execution/__tests__/chat-engine-routing.test.ts +136 -30
  56. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +5 -4
  57. package/src/execution/__tests__/cold-resurrect.test.ts +283 -0
  58. package/src/execution/__tests__/create-concurrency-pool.test.ts +194 -0
  59. package/src/execution/__tests__/delivery-methods.test.ts +77 -62
  60. package/src/execution/__tests__/descendant-sweep-guards.test.ts +220 -0
  61. package/src/execution/__tests__/descendant-sweep.test.ts +269 -0
  62. package/src/execution/__tests__/dialog-queue.test.ts +199 -1
  63. package/src/execution/__tests__/engine-model-validation.test.ts +741 -0
  64. package/src/execution/__tests__/epipe-fallback.test.ts +31 -24
  65. package/src/execution/__tests__/execute-and-await-worktree.test.ts +21 -91
  66. package/src/execution/__tests__/execute-nesting.test.ts +34 -188
  67. package/src/execution/__tests__/execution-record.test.ts +217 -60
  68. package/src/execution/__tests__/execution-runtime-face.test.ts +272 -0
  69. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +3 -3
  70. package/src/execution/__tests__/finalize-record.test.ts +278 -16
  71. package/src/execution/__tests__/gc-timer.test.ts +57 -2
  72. package/src/execution/__tests__/get-record-for-action-restart.test.ts +14 -12
  73. package/src/execution/__tests__/get-state-handshake.test.ts +127 -0
  74. package/src/execution/__tests__/helpers/mock-extension-api.ts +1 -1
  75. package/src/execution/__tests__/helpers/session-runner-mocks.ts +113 -0
  76. package/src/execution/__tests__/helpers/spawn-mock.ts +150 -3
  77. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +137 -0
  78. package/src/execution/__tests__/keep-alive-no-progress.test.ts +280 -0
  79. package/src/execution/__tests__/kill-all-escalation.test.ts +196 -0
  80. package/src/execution/__tests__/lifecycle-manager-idle-timer-identity.test.ts +117 -0
  81. package/src/execution/__tests__/lifecycle-manager.test.ts +39 -0
  82. package/src/execution/__tests__/lifecycle-predicates.test.ts +1 -2
  83. package/src/execution/__tests__/manifest-parentid.test.ts +4 -4
  84. package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +99 -0
  85. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +113 -0
  86. package/src/execution/__tests__/model-resolver.test.ts +2 -2
  87. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +23 -121
  88. package/src/execution/__tests__/nested-visibility.test.ts +2 -0
  89. package/src/execution/__tests__/notify-ledger.test.ts +1249 -0
  90. package/src/execution/__tests__/output-collector.test.ts +117 -6
  91. package/src/execution/__tests__/pi-invocation.test.ts +1 -1
  92. package/src/execution/__tests__/record-store-orphan-revive.test.ts +159 -0
  93. package/src/execution/__tests__/record-store.test.ts +89 -5
  94. package/src/execution/__tests__/records-cwd-isolation.test.ts +2 -2
  95. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +29 -132
  96. package/src/execution/__tests__/recursive-visibility-env.test.ts +55 -179
  97. package/src/execution/__tests__/rpc-mode.test.ts +2 -2
  98. package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +387 -0
  99. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +19 -9
  100. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +5 -4
  101. package/src/execution/__tests__/run-spawn-edges.test.ts +418 -26
  102. package/src/execution/__tests__/run-spawn-integration.test.ts +3 -3
  103. package/src/execution/__tests__/run-spawn-resume.test.ts +2 -2
  104. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +2 -2
  105. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +16 -8
  106. package/src/execution/__tests__/service-kill-escalation.test.ts +91 -0
  107. package/src/execution/__tests__/session-context-resolver.test.ts +0 -2
  108. package/src/execution/__tests__/session-file-gc.test.ts +35 -0
  109. package/src/execution/__tests__/session-pending.test.ts +471 -0
  110. package/src/execution/__tests__/session-reconstructor.test.ts +366 -15
  111. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +110 -0
  112. package/src/execution/__tests__/session-runner-close-prune.test.ts +181 -0
  113. package/src/execution/__tests__/session-runner-dispatch.test.ts +489 -0
  114. package/src/execution/__tests__/session-runner-epipe.test.ts +4 -3
  115. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +227 -0
  116. package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +290 -0
  117. package/src/execution/__tests__/session-runner-schema-env.test.ts +7 -5
  118. package/src/execution/__tests__/settled-watchdog.test.ts +524 -0
  119. package/src/execution/__tests__/spawn-args.test.ts +11 -10
  120. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +26 -23
  121. package/src/execution/__tests__/spawn-event-adapter.test.ts +1 -1
  122. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +5 -4
  123. package/src/execution/__tests__/spawned-children.test.ts +155 -11
  124. package/src/execution/__tests__/start-sync-model-guard.test.ts +4 -4
  125. package/src/execution/__tests__/stdin-writer.test.ts +6 -4
  126. package/src/execution/__tests__/stream-sink-retirement.test.ts +6 -6
  127. package/src/execution/__tests__/subagent-actions-core.test.ts +1017 -0
  128. package/src/execution/__tests__/subagent-service-message-close.test.ts +99 -35
  129. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +174 -0
  130. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +271 -0
  131. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +33 -29
  132. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +341 -0
  133. package/src/execution/__tests__/subagent-service.test.ts +37 -30
  134. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +88 -12
  135. package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +75 -0
  136. package/src/execution/__tests__/subprocess-agent-runner.test.ts +22 -20
  137. package/src/execution/__tests__/temp-prompt.test.ts +1 -1
  138. package/src/execution/__tests__/timeout-integration.test.ts +13 -12
  139. package/src/execution/__tests__/turn-limiter-semantics.test.ts +1 -1
  140. package/src/execution/__tests__/turn-limiter.test.ts +1 -1
  141. package/src/execution/__tests__/ui-request-handler-factory.test.ts +107 -7
  142. package/src/execution/__tests__/ui-request-handler.test.ts +4 -3
  143. package/src/execution/__tests__/ui-request-queue.test.ts +167 -2
  144. package/src/execution/__tests__/worktree-git-ops.test.ts +571 -0
  145. package/src/execution/__tests__/worktree-manager.test.ts +14 -5
  146. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +4 -3
  147. package/src/execution/__tests__/worktree-reconcile-aging.test.ts +204 -0
  148. package/src/execution/agent-registry.ts +298 -30
  149. package/src/execution/agent-result-mapper.ts +3 -0
  150. package/src/execution/agents-assembly.ts +88 -0
  151. package/src/execution/cold-resurrect.ts +199 -0
  152. package/src/execution/concurrency-pool.ts +71 -9
  153. package/src/execution/dialog-queue.ts +101 -3
  154. package/src/execution/engine/__tests__/common/capability-gate.test.ts +91 -0
  155. package/src/execution/engine/__tests__/common/data-dir.test.ts +1 -1
  156. package/src/execution/engine/__tests__/common/journal-wiring.test.ts +84 -0
  157. package/src/execution/engine/__tests__/common/kill-chain.test.ts +34 -7
  158. package/src/execution/engine/__tests__/common/persona-router.test.ts +4 -5
  159. package/src/execution/engine/__tests__/common/pool-manager.test.ts +211 -51
  160. package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +149 -0
  161. package/src/execution/engine/__tests__/common/session-view-service.test.ts +499 -0
  162. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +56 -99
  163. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +88 -15
  164. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +33 -7
  165. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +1 -1
  166. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +75 -54
  167. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +128 -0
  168. package/src/execution/engine/__tests__/engine-discovery.test.ts +15 -3
  169. package/src/execution/engine/__tests__/model-prompt.test.ts +15 -3
  170. package/src/execution/engine/__tests__/registry.test.ts +94 -3
  171. package/src/execution/engine/__tests__/routing.test.ts +115 -6
  172. package/src/execution/engine/common/capability-gate.ts +92 -0
  173. package/src/execution/engine/common/journal-wiring.ts +97 -0
  174. package/src/execution/engine/common/kill-chain.ts +42 -10
  175. package/src/execution/engine/common/nesting-guard.ts +66 -0
  176. package/src/execution/engine/common/persona-router.ts +18 -8
  177. package/src/execution/engine/common/pool-manager.ts +332 -78
  178. package/src/execution/engine/common/session-view-service.ts +506 -0
  179. package/src/execution/engine/common/session-view-types.ts +125 -0
  180. package/src/execution/engine/engine-discovery.ts +12 -16
  181. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +174 -28
  182. package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +257 -0
  183. package/src/execution/{argv-mirror.ts → engine/engines/pi/argv-mirror.ts} +43 -21
  184. package/src/execution/{get-state-handshake.ts → engine/engines/pi/get-state-handshake.ts} +86 -11
  185. package/src/execution/{output-collector.ts → engine/engines/pi/output-collector.ts} +108 -19
  186. package/src/execution/engine/engines/pi/pi-engine.ts +434 -38
  187. package/src/execution/{pi-invocation.ts → engine/engines/pi/pi-invocation.ts} +2 -2
  188. package/src/execution/engine/engines/pi/reader.ts +11 -0
  189. package/src/execution/engine/engines/pi/registration.ts +4 -2
  190. package/src/execution/engine/engines/pi/session-runner.ts +2840 -0
  191. package/src/execution/{spawn-event-adapter.ts → engine/engines/pi/spawn-event-adapter.ts} +75 -34
  192. package/src/execution/{stdin-writer.ts → engine/engines/pi/stdin-writer.ts} +10 -9
  193. package/src/execution/{temp-prompt.ts → engine/engines/pi/temp-prompt.ts} +2 -2
  194. package/src/execution/{turn-limiter.ts → engine/engines/pi/turn-limiter.ts} +1 -1
  195. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +301 -0
  196. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +36 -0
  197. package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +321 -0
  198. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +473 -0
  199. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +7 -210
  200. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +6 -110
  201. package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +247 -0
  202. package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +451 -0
  203. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +792 -0
  204. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +782 -0
  205. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +168 -0
  206. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +210 -0
  207. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +353 -0
  208. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +282 -0
  209. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +372 -0
  210. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +65 -47
  211. package/src/execution/engine/engines/zcode/appserver-launcher.ts +167 -0
  212. package/src/execution/engine/engines/zcode/connection.ts +598 -0
  213. package/src/execution/engine/engines/zcode/constants.ts +147 -14
  214. package/src/execution/engine/engines/zcode/golden-sample.ts +42 -34
  215. package/src/execution/engine/engines/zcode/parser.ts +12 -346
  216. package/src/execution/engine/engines/zcode/preparer.ts +22 -150
  217. package/src/execution/engine/engines/zcode/session-channel.ts +968 -0
  218. package/src/execution/engine/engines/zcode/zcode-engine.ts +1023 -250
  219. package/src/execution/engine/host-task-spec.ts +32 -36
  220. package/src/execution/engine/model-validation.ts +177 -0
  221. package/src/execution/engine/port.ts +65 -7
  222. package/src/execution/engine/registry.ts +53 -0
  223. package/src/execution/engine/routing.ts +75 -1
  224. package/src/execution/engine/types.ts +30 -103
  225. package/src/execution/execution-record.ts +222 -144
  226. package/src/execution/finalize-record.ts +181 -85
  227. package/src/execution/idle-gc.ts +28 -0
  228. package/src/execution/lifecycle-manager.ts +27 -3
  229. package/src/execution/lifecycle-predicates.ts +1 -1
  230. package/src/execution/manifest-store.ts +53 -62
  231. package/src/execution/notifier.ts +10 -10
  232. package/src/execution/notify-host.ts +212 -0
  233. package/src/execution/notify-ledger.ts +117 -16
  234. package/src/execution/record-entry.ts +8 -2
  235. package/src/execution/record-store.ts +148 -88
  236. package/src/execution/round-settlement.ts +93 -0
  237. package/src/execution/session-file-gc.ts +96 -62
  238. package/src/execution/session-pending.ts +213 -62
  239. package/src/execution/session-reconstructor.ts +256 -143
  240. package/src/execution/sessions-index.ts +127 -102
  241. package/src/execution/settled-watchdog.ts +311 -0
  242. package/src/execution/subagent-actions-core.ts +686 -0
  243. package/src/execution/subagent-service.ts +1106 -907
  244. package/src/execution/subprocess-agent-runner.ts +144 -97
  245. package/src/execution/types.ts +9 -0
  246. package/src/execution/ui-request-handler-factory.ts +126 -75
  247. package/src/execution/ui-request-queue.ts +50 -19
  248. package/src/execution/worktree-git-ops.ts +397 -0
  249. package/src/execution/worktree-manager.ts +72 -10
  250. package/src/execution/worktree-registry.ts +6 -12
  251. package/src/index.ts +542 -9
  252. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +42 -3
  253. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +3 -5
  254. package/src/orchestration/__tests__/agent-call-stream.test.ts +2 -2
  255. package/src/orchestration/__tests__/args-meta.test.ts +358 -0
  256. package/src/orchestration/__tests__/args-validator.test.ts +14 -0
  257. package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +312 -0
  258. package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +384 -0
  259. package/src/orchestration/__tests__/execute-agent-call.test.ts +151 -11
  260. package/src/orchestration/__tests__/file-run-store-prune.test.ts +168 -0
  261. package/src/orchestration/__tests__/file-run-store-throttle.test.ts +168 -0
  262. package/src/orchestration/__tests__/file-run-store.test.ts +390 -0
  263. package/src/orchestration/__tests__/helpers/flush-microtasks.ts +13 -0
  264. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +34 -0
  265. package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +357 -0
  266. package/src/orchestration/__tests__/lifecycle-recover-crashed.test.ts +289 -0
  267. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +3 -3
  268. package/src/orchestration/__tests__/lifecycle.test.ts +84 -5
  269. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +1 -1
  270. package/src/orchestration/__tests__/run-snapshot.test.ts +356 -0
  271. package/src/orchestration/__tests__/script-generate.test.ts +314 -0
  272. package/src/orchestration/__tests__/script-lint.test.ts +17 -0
  273. package/src/orchestration/__tests__/test-mocks.ts +2 -0
  274. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +7 -6
  275. package/src/orchestration/__tests__/worker-message-pump-finalize-run.test.ts +229 -0
  276. package/src/orchestration/__tests__/{error-recovery-handlers.test.ts → worker-message-pump-handlers.test.ts} +28 -27
  277. package/src/orchestration/__tests__/{error-recovery-postmessage-defense.test.ts → worker-message-pump-postmessage-defense.test.ts} +8 -8
  278. package/src/orchestration/__tests__/{error-recovery-serialize-failed-result.test.ts → worker-message-pump-serialize-failed-result.test.ts} +2 -2
  279. package/src/orchestration/__tests__/{error-recovery-workflow-call.test.ts → worker-message-pump-workflow-call.test.ts} +1 -1
  280. package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +275 -0
  281. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +78 -1
  282. package/src/orchestration/__tests__/worker-script-builder.test.ts +1 -1
  283. package/src/orchestration/__tests__/workflow-files.test.ts +186 -0
  284. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +1 -1
  285. package/src/orchestration/__tests__/workflow-run-summary.test.ts +119 -0
  286. package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +124 -0
  287. package/src/orchestration/agent-opts-resolver.ts +8 -8
  288. package/src/orchestration/args-meta.ts +198 -0
  289. package/src/orchestration/args-validator.ts +53 -31
  290. package/src/orchestration/execute-agent-call.ts +25 -79
  291. package/src/orchestration/file-run-store.ts +327 -0
  292. package/src/orchestration/launcher.ts +160 -104
  293. package/src/orchestration/lifecycle.ts +371 -97
  294. package/src/orchestration/models/agent-call.ts +7 -7
  295. package/src/orchestration/models/budget.ts +5 -5
  296. package/src/orchestration/models/ports.ts +4 -4
  297. package/src/orchestration/models/run-runtime.ts +15 -15
  298. package/src/orchestration/models/trace.ts +9 -9
  299. package/src/orchestration/models/types.ts +111 -22
  300. package/src/orchestration/models/workflow-run.ts +28 -28
  301. package/src/orchestration/models/workflow-script.ts +4 -4
  302. package/src/orchestration/run-snapshot.ts +266 -0
  303. package/src/orchestration/script-generate.ts +154 -0
  304. package/src/orchestration/script-lint.ts +123 -90
  305. package/src/orchestration/worker-handle.ts +10 -10
  306. package/src/orchestration/worker-host.ts +10 -10
  307. package/src/orchestration/{error-recovery.ts → worker-message-pump.ts} +498 -221
  308. package/src/orchestration/worker-script-builder.ts +401 -346
  309. package/src/orchestration/workflow-files.ts +37 -11
  310. package/src/orchestration/workflow-run-summary.ts +69 -0
  311. package/src/orchestration/workflow-script-registry-impl.ts +31 -9
  312. package/src/shared/__tests__/agent-ref.test.ts +209 -2
  313. package/src/shared/__tests__/atomic-write.test.ts +267 -0
  314. package/src/shared/__tests__/bounded-serialize.test.ts +236 -0
  315. package/src/shared/__tests__/injection-render.test.ts +518 -0
  316. package/src/shared/__tests__/meta-parser.test.ts +151 -0
  317. package/src/shared/__tests__/resource-discovery-host-roots.test.ts +474 -0
  318. package/src/shared/__tests__/resource-discovery.test.ts +261 -217
  319. package/src/shared/agent-ref.ts +143 -2
  320. package/src/shared/atomic-write.ts +323 -0
  321. package/src/shared/bounded-serialize.ts +154 -0
  322. package/src/shared/injection-render.ts +279 -0
  323. package/src/shared/meta-parser.ts +182 -68
  324. package/src/shared/model-ref.ts +15 -2
  325. package/src/shared/resource-discovery.ts +97 -204
  326. package/src/shared/resource-meta.ts +14 -0
  327. package/src/shared/schema-jsonify.ts +1 -1
  328. package/src/shared/xml-injection.ts +9 -9
  329. package/workflows/README.md +9 -9
  330. package/workflows/chain.js +4 -2
  331. package/workflows/map-reduce.js +5 -3
  332. package/workflows/parallel.js +5 -3
  333. package/workflows/review-fix-loop-utils.cjs +165 -92
  334. package/workflows/review-fix-loop-utils.d.cts +287 -0
  335. package/workflows/review-fix-loop.js +18 -7
  336. package/workflows/scatter-gather.js +4 -2
  337. package/dist/chunk-3VOERJPJ.js +0 -22
  338. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
  339. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  340. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  341. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  342. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  343. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -580
  344. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  345. package/src/execution/execute-options-mapper.ts +0 -115
  346. package/src/execution/session-runner.ts +0 -1789
  347. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -288
@@ -1,51 +1,14 @@
1
- // parser.test.ts —— golden 样本回归(验收 2)+ 损坏 stdout 错误路径 + 缓冲有界性。
2
- // golden 原文来自 2026-08-25 真机实录(zcode 0.16.5),采集元数据见 fixture _meta。
3
-
4
- import { readFileSync } from "node:fs";
5
- import { fileURLToPath } from "node:url";
1
+ // parser.test.ts —— usage 字段映射(事件层 execution 版)+ coarse 事件合成不变量回归。
2
+ // stdout 有界收集/终 JSON 解析/运行失败文案一族(含 golden spawn 样本回归)已随
3
+ // CLI spawn 链删除(2026-09 单一 app-server 形态——终态来自 session-channel 收尾帧,
4
+ // 不经 stdout 解析);mapZcodeOutcomeUsage(终态层映射)经 zcode-engine-appserver.test.ts
5
+ // outcome.usage 断言覆盖。
6
6
 
7
7
  import { describe, expect, it } from "vitest";
8
8
 
9
- import {
10
- buildRunFailedMessage,
11
- createBoundedLineBuffer,
12
- mapZcodeUsage,
13
- parseZcodeStdoutJson,
14
- parseZcodeTerminal,
15
- synthesizeCoarseEvents,
16
- } from "../parser.ts";
17
-
18
- interface GoldenFixture {
19
- stdoutRaw: string;
20
- _meta: { engineVersion: string; exitCode: number; usageMapping: string };
21
- }
22
-
23
- const golden: GoldenFixture = JSON.parse(
24
- readFileSync(fileURLToPath(new URL("./__fixtures__/zcode-golden-spawn.json", import.meta.url)), "utf8"),
25
- );
26
-
27
- describe("parseZcodeTerminal:golden 回归", () => {
28
- it("实录样本解析出完整终态(sessionId/response/usage/turnCount)", () => {
29
- const r = parseZcodeTerminal(golden.stdoutRaw);
30
- expect(r.ok).toBe(true);
31
- if (!r.ok) return;
32
- expect(r.payload.sessionId).toBe("sess_35852a0f-1302-4e20-9e48-87f47527abe3");
33
- expect(r.payload.response).toBe("ok");
34
- expect(r.payload.usage).toEqual({ input: 12599, output: 17, cacheRead: 512, cacheWrite: 0 });
35
- expect(r.payload.turnCount).toBe(1);
36
- // 终态层 usage(orchestration 版):cost 显式 0(zcode 不回传)、contextTokens=
37
- // projection.contextUsed、turns=projection.turnCount
38
- expect(r.payload.outcomeUsage).toEqual({
39
- input: 12599,
40
- output: 17,
41
- cacheRead: 512,
42
- cacheWrite: 0,
43
- cost: 0,
44
- contextTokens: 12616,
45
- turns: 1,
46
- });
47
- });
9
+ import { mapZcodeUsage, synthesizeCoarseEvents } from "../parser.ts";
48
10
 
11
+ describe("mapZcodeUsage:字段名映射", () => {
49
12
  it("usage 字段名映射:inputTokens→input 等四项(cost 无来源不出现)", () => {
50
13
  const u = mapZcodeUsage({
51
14
  inputTokens: 10,
@@ -64,42 +27,6 @@ describe("parseZcodeTerminal:golden 回归", () => {
64
27
  });
65
28
  });
66
29
 
67
- describe("parseZcodeTerminal:损坏 stdout → ok:false(engine_run_failed 输入)", () => {
68
- it("非 JSON 文本", () => {
69
- const r = parseZcodeTerminal("Error: something broke\n");
70
- expect(r.ok).toBe(false);
71
- if (r.ok) return;
72
- expect(r.reason).toContain("不是 JSON");
73
- });
74
-
75
- it("JSON 但缺 string 型 response(格式漂移形态)", () => {
76
- const r = parseZcodeTerminal('{"sessionId": "sess_x", "usage": {}}');
77
- expect(r.ok).toBe(false);
78
- if (r.ok) return;
79
- expect(r.reason).toContain("response");
80
- });
81
-
82
- it("数组/标量 JSON 拒收", () => {
83
- expect(parseZcodeTerminal("[1,2,3]").ok).toBe(false);
84
- expect(parseZcodeTerminal("42").ok).toBe(false);
85
- });
86
-
87
- it("混入日志行时首尾大括号容错提取", () => {
88
- const mixed = '[info] boot\n{"sessionId":"sess_a","response":"done"}\n[info] exit';
89
- const r = parseZcodeTerminal(mixed);
90
- expect(r.ok).toBe(true);
91
- if (!r.ok) return;
92
- expect(r.payload.sessionId).toBe("sess_a");
93
- expect(r.payload.response).toBe("done");
94
- });
95
- });
96
-
97
- describe("parseZcodeStdoutJson:空对象形态防御", () => {
98
- it("无大括号文本返回 null", () => {
99
- expect(parseZcodeStdoutJson("plain text")).toBeNull();
100
- });
101
- });
102
-
103
30
  describe("synthesizeCoarseEvents:coarse 不变量", () => {
104
31
  it("message_end(含 usage)在前、turn_end 最后", () => {
105
32
  const usage = { input: 1, output: 2, cacheRead: 0, cacheWrite: 0 };
@@ -114,133 +41,3 @@ describe("synthesizeCoarseEvents:coarse 不变量", () => {
114
41
  expect(evs[evs.length - 1]).toEqual({ type: "turn_end" });
115
42
  });
116
43
  });
117
-
118
- describe("buildRunFailedMessage:错误规格(stdout 尾部 + exit code + 恢复指引)", () => {
119
- it("含 exit code、尾部内容与三段恢复动作", () => {
120
- const msg = buildRunFailedMessage({
121
- exitCode: 3,
122
- stdoutTail: "x".repeat(5000),
123
- stderrTail: "boom",
124
- parseReason: "stdout 不是 JSON 对象",
125
- });
126
- expect(msg).toContain("engine_run_failed");
127
- expect(msg).toContain("exit code: 3");
128
- expect(msg).toContain("解析失败");
129
- expect(msg).toContain("stderr 尾部: boom");
130
- // stdout 尾部截断到 2000 字
131
- expect(msg).toContain("x".repeat(2000));
132
- expect(msg).not.toContain("x".repeat(2001));
133
- expect(msg).toContain("--version");
134
- expect(msg).toContain("golden");
135
- expect(msg).toContain("engine: pi");
136
- });
137
-
138
- it("null exit code 标注被信号杀死", () => {
139
- const msg = buildRunFailedMessage({ exitCode: null, stdoutTail: "" });
140
- expect(msg).toContain("null(被信号杀死)");
141
- });
142
-
143
- it("空 stdout 不产生空 part(防「stdout 尾部: <指引段>」视觉嵌套误读)", () => {
144
- const msg = buildRunFailedMessage({ exitCode: 1, stdoutTail: " " });
145
- expect(msg).not.toContain("stdout 尾部");
146
- });
147
-
148
- it("LLM API 失败(AI_APICallError)归因到端点/凭据指引,不再给 probe 指引", () => {
149
- const msg = buildRunFailedMessage({
150
- cliPath: "/fake/zcode.cjs",
151
- exitCode: 1,
152
- stdoutTail: "",
153
- stderrTail: "AI_APICallError: 500 Internal Server Error\nError: Turn execution failed",
154
- modelRef: "router/mimo-v2.5-pro",
155
- configPath: "/pool/.zcode/cli/config.json",
156
- });
157
- expect(msg).toContain("模型 API 调用失败");
158
- expect(msg).toContain("router/mimo-v2.5-pro");
159
- expect(msg).toContain("/pool/.zcode/cli/config.json");
160
- expect(msg).toContain("baseURL");
161
- expect(msg).toContain("engine: pi");
162
- // probe/golden 指引不出现(误导排查方向——2026-08-25 真机教训)
163
- expect(msg).not.toContain("--version");
164
- expect(msg).not.toContain("golden");
165
- expect(msg).not.toContain("stdout 尾部");
166
- });
167
-
168
- it("401 有响应形态(无 AI_APICallError 类名)也归因,且带 HTTP 状态与凭据定向", () => {
169
- const stderr401 = [
170
- " 'x-mife-upstream-status': '401'",
171
- " },",
172
- " responseStatus: 401,",
173
- " statusCode: 401",
174
- "}",
175
- "Error: Turn execution failed (traceId: a487a9b3)",
176
- ].join("\n");
177
- const msg = buildRunFailedMessage({
178
- cliPath: "/fake/zcode.cjs",
179
- exitCode: 1,
180
- stdoutTail: "",
181
- stderrTail: stderr401,
182
- modelRef: "router/mimo-v2.5-pro",
183
- configPath: "/pool/.zcode/cli/config.json",
184
- });
185
- expect(msg).toContain("模型 API 调用失败");
186
- expect(msg).toContain("HTTP 401");
187
- expect(msg).toContain("凭据无效或过期");
188
- expect(msg).toContain("apiKey 大概率无效或过期");
189
- expect(msg).not.toContain("--version");
190
- expect(msg).not.toContain("golden");
191
- });
192
-
193
- it("LLM 归因在缺省 modelRef/configPath 时兜底可读", () => {
194
- const msg = buildRunFailedMessage({
195
- cliPath: "/fake/zcode.cjs",
196
- exitCode: 1,
197
- stdoutTail: "ok",
198
- stderrTail: "Symbol(vercel.ai.error.AI_APICallError): true",
199
- });
200
- expect(msg).toContain("模型 API 调用失败");
201
- expect(msg).toContain("provider 的 baseURL 可达性");
202
- expect(msg).not.toContain("undefined");
203
- });
204
-
205
- it("stderr 的 [Object] 噪音行折叠为计数", () => {
206
- const noisy = [
207
- "AIError: fail",
208
- ...Array.from({ length: 12 }, () => " [Object], [Object],"),
209
- " statusCode: undefined,",
210
- ].join("\n");
211
- const msg = buildRunFailedMessage({ cliPath: "/c", exitCode: 1, stdoutTail: "", stderrTail: noisy });
212
- expect(msg).toContain("[Object]×12");
213
- expect(msg).not.toContain("[Object], [Object]");
214
- });
215
- });
216
-
217
- describe("createBoundedLineBuffer:有界双缓冲", () => {
218
- it("头 4K + 尾 64K 保留,中间丢弃并标记", () => {
219
- const buf = createBoundedLineBuffer();
220
- const line = "a".repeat(100) + "\n"; // 每行 101 字节
221
- for (let i = 0; i < 2000; i++) buf.push(line); // ~202KB,远超 4K+64K
222
- const text = buf.text();
223
- expect(text.startsWith("a".repeat(100))).toBe(true); // 头部保留
224
- expect(text).toContain("已丢弃"); // 中间丢弃标记
225
- expect(text.endsWith("\n")).toBe(true);
226
- // 尾部窗口 ≈ 64K:总长有界
227
- expect(text.length).toBeLessThan(4 * 1024 + 64 * 1024 + 200);
228
- });
229
-
230
- it("单行超整个尾部窗口时保留行尾(错误信息在末尾)", () => {
231
- const buf = createBoundedLineBuffer({ headLimit: 0, tailLimit: 1000 });
232
- buf.push("B".repeat(5000) + "\n");
233
- const text = buf.text();
234
- // 有效载荷 ≤ 1000(text 里另有「已丢弃 N 字节」标记行)
235
- expect(text.match(/B/g)?.length ?? 0).toBeLessThanOrEqual(1000);
236
- expect(text.endsWith("\n")).toBe(true);
237
- expect(text).toContain("已丢弃");
238
- });
239
-
240
- it("未超限时原文完整(golden 646 字节场景)", () => {
241
- const buf = createBoundedLineBuffer();
242
- buf.push(golden.stdoutRaw);
243
- expect(buf.text()).toBe(golden.stdoutRaw);
244
- expect(buf.tail(10)).toBe(golden.stdoutRaw.slice(-10));
245
- });
246
- });
@@ -1,6 +1,8 @@
1
- // preparer.test.ts —— 池目录 SSOT / 原子写 / mtime 免重写 / 无 plugins 块 / 凭据与模型
2
- // 前置错误(验收 3)。凭据源 = v2 config 单源(2026-08-25 拍板:不读
3
- // ~/.zcode/cli/config.json——GUI 不管理该文件,可能残留历史验证配置)。
1
+ // preparer.test.ts —— v2 单源模型解析 / 模型可发现性 / 凭据与模型前置错误。
2
+ // 凭据源 = v2 config 单源(2026-08-25 拍板:不读 ~/.zcode/cli/config.json——GUI 不
3
+ // 管理该文件,可能残留历史验证配置)。原 HOME 池化面(池目录 SSOT / 原子写 /
4
+ // mtime 免重写 / 池 config 无 plugins 块)已随「共享宿主 HOME」重构删除(2026-09:
5
+ // app-server 侧凭据经 appserver-launcher fs 拦截注入,不再建池写 config)。
4
6
 
5
7
  import * as fs from "node:fs";
6
8
  import * as os from "node:os";
@@ -8,17 +10,9 @@ import * as path from "node:path";
8
10
 
9
11
  import { beforeEach, describe, expect, it } from "vitest";
10
12
 
11
- import { resolvePoolDir } from "../../../paths.ts";
12
- import {
13
- ZcodePrepareError,
14
- computeZcodePoolKey,
15
- listZcodeModels,
16
- prepareZcodeHome,
17
- resolveZcodeModelRef,
18
- } from "../preparer.ts";
13
+ import { ZcodePrepareError, listZcodeModels, resolveZcodeModelRef } from "../preparer.ts";
19
14
 
20
15
  let tmpRoot: string;
21
- let dataDir: string;
22
16
  let v2Path: string;
23
17
 
24
18
  function writeJson(p: string, v: unknown): void {
@@ -41,7 +35,6 @@ function seedSources(): void {
41
35
 
42
36
  beforeEach(() => {
43
37
  tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "zcode-preparer-"));
44
- dataDir = path.join(tmpRoot, "data");
45
38
  v2Path = path.join(tmpRoot, "v2-config.json");
46
39
  seedSources();
47
40
  });
@@ -129,100 +122,3 @@ describe("listZcodeModels(U7 可发现性)", () => {
129
122
  expect(listZcodeModels({ v2ConfigPath: path.join(tmpRoot, "absent.json") })).toEqual([]);
130
123
  });
131
124
  });
132
-
133
- describe("computeZcodePoolKey", () => {
134
- it("provider 特殊字符安全化 + model 短名保留点号(zsub homePoolDir 同构)", () => {
135
- expect(computeZcodePoolKey("builtin:bigmodel-coding-plan/GLM-5.3")).toBe(
136
- "home-builtin-bigmodel-coding-plan-GLM-5.3",
137
- );
138
- expect(computeZcodePoolKey("router/mimo-v2.5-pro")).toBe("home-router-mimo-v2.5-pro");
139
- });
140
- });
141
-
142
- describe("prepareZcodeHome(验收 3)", () => {
143
- it("池目录 = resolvePoolDir SSOT,config.json 原子写且无 plugins 块", () => {
144
- const prepared = prepareZcodeHome({
145
- engineDataDir: dataDir,
146
- modelRef: `${PROVIDER_A}/GLM-5.3`,
147
- sources: { v2ConfigPath: v2Path },
148
- });
149
- expect(prepared.wroteConfig).toBe(true);
150
- // 池目录必须与 paths.ts SSOT 同源(禁自拼)
151
- expect(prepared.homeDir).toBe(resolvePoolDir(dataDir, "zcode", prepared.poolKey));
152
- expect(prepared.poolKey).toBe("home-builtin-bigmodel-coding-plan-GLM-5.3");
153
-
154
- const written = JSON.parse(fs.readFileSync(prepared.configPath, "utf8")) as Record<string, unknown>;
155
- expect(written["model"]).toEqual({ main: `${PROVIDER_A}/GLM-5.3` });
156
- expect(Object.keys(written["provider"] as Record<string, unknown>)).toEqual([PROVIDER_A]);
157
- // 第二重门禁:刻意不写 plugins 块(防递归 + 不加载宿主插件)
158
- expect("plugins" in written).toBe(false);
159
- // 原子写:无 tmp 残留
160
- const leftovers = fs.readdirSync(path.dirname(prepared.configPath)).filter((f) => f.includes(".tmp-"));
161
- expect(leftovers).toEqual([]);
162
- // 只写目标 provider(每池单 provider 单模型,凭据落盘面最小)
163
- const providerEntry = (written["provider"] as Record<string, { options?: { apiKey?: string } }>)[PROVIDER_A]!;
164
- expect(providerEntry.options?.apiKey).toBe("key-a");
165
- });
166
-
167
- it("mtime 比对免重写:源未变时第二次 prepare 零写入", () => {
168
- const opts = {
169
- engineDataDir: dataDir,
170
- modelRef: `${PROVIDER_A}/GLM-5.3`,
171
- sources: { v2ConfigPath: v2Path },
172
- };
173
- const first = prepareZcodeHome(opts);
174
- expect(first.wroteConfig).toBe(true);
175
- const statAfterFirst = fs.statSync(first.configPath);
176
- const second = prepareZcodeHome(opts);
177
- expect(second.wroteConfig).toBe(false);
178
- expect(fs.statSync(second.configPath).mtimeMs).toBe(statAfterFirst.mtimeMs);
179
- });
180
-
181
- it("源 config 变新(mtime 推进)触发重写(凭据刷新传播)", async () => {
182
- const opts = {
183
- engineDataDir: dataDir,
184
- modelRef: `${PROVIDER_A}/GLM-5.3`,
185
- sources: { v2ConfigPath: v2Path },
186
- };
187
- prepareZcodeHome(opts);
188
- // 源 mtime 推到未来(模拟桌面端刷新 apiKey)
189
- const future = new Date(Date.now() + 10_000);
190
- fs.utimesSync(v2Path, future, future);
191
- const again = prepareZcodeHome(opts);
192
- expect(again.wroteConfig).toBe(true);
193
- });
194
-
195
- it("池 config 损坏(torn write 形态)时重建", () => {
196
- const opts = {
197
- engineDataDir: dataDir,
198
- modelRef: `${PROVIDER_A}/GLM-5.3`,
199
- sources: { v2ConfigPath: v2Path },
200
- };
201
- const first = prepareZcodeHome(opts);
202
- fs.writeFileSync(first.configPath, "{ torn", "utf8");
203
- const again = prepareZcodeHome(opts);
204
- expect(again.wroteConfig).toBe(true);
205
- expect(() => JSON.parse(fs.readFileSync(again.configPath, "utf8"))).not.toThrow();
206
- });
207
-
208
- it("v2 注册表内非默认 provider(如自定义 UUID provider)也能建池", () => {
209
- const prepared = prepareZcodeHome({
210
- engineDataDir: dataDir,
211
- modelRef: `${PROVIDER_B}/mimo-v2.5-pro`,
212
- sources: { v2ConfigPath: v2Path },
213
- });
214
- const written = JSON.parse(fs.readFileSync(prepared.configPath, "utf8")) as {
215
- provider: Record<string, { options?: { apiKey?: string } }>;
216
- };
217
- expect(written.provider[PROVIDER_B]!.options?.apiKey).toBe("key-b");
218
- });
219
-
220
- it("模型引用在 v2 不存在 → model_not_available", () => {
221
- try {
222
- prepareZcodeHome({ engineDataDir: dataDir, modelRef: "ghost/m", sources: { v2ConfigPath: v2Path } });
223
- expect.unreachable("should throw");
224
- } catch (err) {
225
- expect((err as ZcodePrepareError).code).toBe("model_not_available");
226
- }
227
- });
228
- });
@@ -0,0 +1,247 @@
1
+ // session-channel-dispose-harvest.test.ts —— P0-1 U5 dispose 前收割单测(机制层)。
2
+ //
3
+ // 设计权威源:docs/design/timeout-zcode-turn-and-settled-watchdog.md §3.4(退化路径)、
4
+ // §6 D7(dispose 收割兜底)、§11 P-Z3(close 吞没 → dispose 触发 failAllTurns)。
5
+ // P-Z3 的「close 事件吞没」在真实子进程形态不可确定性注入(Node child 进程退出后
6
+ // stdio 关闭即触发 close 事件)——真实现场归 Gate B;本文件用 mock 连接承载机制层
7
+ // 可判定形态:不触发 onClose(= close 吞没的机制等价物)→ dispose → 在途 turn 收敛
8
+ // 为明确失败。与 zcode-engine-dispose.test.ts(fake-appserver 子进程集成面)互补。
9
+ //
10
+ // 覆盖:
11
+ // ① close 吞没形态:dispose 触发 failAllTurns——在途 turn 于 dispose 时收割
12
+ // (P-Z3 核心断言),收割后到达的终态不再改写(settled 守卫);
13
+ // ② grace 窗口内全部终态化:并发多会话 turn 全量收割,不留永久 pending;
14
+ // ③ 正常 close 主路径零回归:onClose 收割先行 → dispose no-op 不改写收割错误;
15
+ // ④ 终态已落定(成功)后 dispose:settled 守卫不改写已成功结果;
16
+ // ⑤ dispose 收割后 closeSession 短路:runTurn finally 不再发 session/close
17
+ // (防写死进程/惰性重建——D7 收割语义的必要闭环)+ 正常完成对照(发 close);
18
+ // ⑥ 二次 dispose 幂等。
19
+
20
+ import { afterEach, describe, expect, it } from "vitest";
21
+
22
+ import type { AppServerConnection } from "../connection.ts";
23
+ import {
24
+ SessionChannel,
25
+ type SessionTurnResult,
26
+ type SessionTurnOptions,
27
+ } from "../session-channel.ts";
28
+
29
+ // ============================================================
30
+ // mock 连接 + 驱动器(与 session-channel-turn-timers.test.ts 同形态,
31
+ // 增补 onClose handler 的手动触发面——close 吞没注入的机制等价物)
32
+ // ============================================================
33
+
34
+ interface MockChannel {
35
+ ch: SessionChannel;
36
+ /** 我方发出的请求帧流水(method 序可断言)。 */
37
+ requests: Array<{ method: string; params: unknown }>;
38
+ /** 手动触发连接崩溃广播(close 吞没形态 = 不触发;正常形态 = 测试显式触发)。 */
39
+ emitClose: (reason: string) => void;
40
+ emit: (method: string, params: unknown) => void;
41
+ emitTerminal: (sessionId: string | undefined, status: string) => void;
42
+ }
43
+
44
+ let sidSeq = 0;
45
+
46
+ function makeMockChannel(): MockChannel {
47
+ const notifications = new Map<string, (params: unknown) => void>();
48
+ const requests: Array<{ method: string; params: unknown }> = [];
49
+ const closeHandlers = new Set<(reason: string) => void>();
50
+ const conn = {
51
+ alive: true,
52
+ onNotification: (method: string, h: (params: unknown) => void) => {
53
+ notifications.set(method, h);
54
+ return () => notifications.delete(method);
55
+ },
56
+ onClose: (h: (reason: string) => void) => {
57
+ closeHandlers.add(h);
58
+ return () => closeHandlers.delete(h);
59
+ },
60
+ request: (method: string, params: unknown) => {
61
+ requests.push({ method, params });
62
+ if (method === "session/create") {
63
+ sidSeq += 1;
64
+ return Promise.resolve({ session: { sessionId: `sess_h${sidSeq}` } });
65
+ }
66
+ if (method === "session/send") return Promise.resolve({ accepted: true });
67
+ if (method === "session/read") return Promise.resolve({ messages: [] });
68
+ return Promise.resolve({});
69
+ },
70
+ } as unknown as AppServerConnection;
71
+ const ch = new SessionChannel(conn);
72
+ return {
73
+ ch,
74
+ requests,
75
+ emitClose: (reason) => {
76
+ for (const fn of [...closeHandlers]) fn(reason);
77
+ },
78
+ emit: (method, params) => notifications.get(method)?.(params),
79
+ emitTerminal: (sessionId, status) =>
80
+ notifications
81
+ .get("v4/telemetry/event")
82
+ ?.({ kind: "turn.terminal", status, sessionId }),
83
+ };
84
+ }
85
+
86
+ interface TurnTracker {
87
+ outcome: "pending" | "fulfilled" | "rejected";
88
+ result: SessionTurnResult | undefined;
89
+ error: unknown;
90
+ promise: Promise<SessionTurnResult>;
91
+ /** onSessionCreated 回填(getter——回调晚于 tracker 构造,值拷贝会恒空)。 */
92
+ readonly sessionId: string;
93
+ }
94
+
95
+ function startTurn(ch: SessionChannel, opts: SessionTurnOptions = {}): TurnTracker {
96
+ let sessionId = "";
97
+ const promise = ch.runTurn({ workspacePath: "/tmp/ws-harvest", mode: "yolo" }, "任务", {
98
+ onSessionCreated: (sid) => {
99
+ sessionId = sid;
100
+ },
101
+ ...opts,
102
+ });
103
+ const t: TurnTracker = {
104
+ outcome: "pending",
105
+ result: undefined,
106
+ error: undefined,
107
+ promise,
108
+ get sessionId(): string {
109
+ return sessionId;
110
+ },
111
+ };
112
+ void promise.then(
113
+ (r) => {
114
+ t.outcome = "fulfilled";
115
+ t.result = r;
116
+ },
117
+ (e) => {
118
+ t.outcome = "rejected";
119
+ t.error = e;
120
+ },
121
+ );
122
+ return t;
123
+ }
124
+
125
+ /** 排空微任务链(create→subscribe→send 全是已 resolve 的 promise——一拍 setTimeout 足够)。 */
126
+ function flush(): Promise<void> {
127
+ return new Promise((resolve) => setTimeout(resolve, 0));
128
+ }
129
+
130
+ async function settle(t: TurnTracker): Promise<void> {
131
+ await t.promise.then(
132
+ () => undefined,
133
+ () => undefined,
134
+ );
135
+ }
136
+
137
+ let enginesChannels: SessionChannel[] = [];
138
+ afterEach(() => {
139
+ for (const ch of enginesChannels.splice(0)) ch.dispose();
140
+ });
141
+
142
+ // ============================================================
143
+ // dispose 收割(P0-1 U5/D7:close 缺失/被吞没的退化路径闭合)
144
+ // ============================================================
145
+
146
+ describe("dispose 收割(P-Z3 机制层:close 吞没 → dispose 触发 failAllTurns)", () => {
147
+ it("close 吞没形态:dispose 时在途 turn 收割为明确失败,迟到终态不再改写", async () => {
148
+ const m = makeMockChannel();
149
+ enginesChannels.push(m.ch);
150
+ const t = startTurn(m.ch);
151
+ await flush();
152
+ expect(t.outcome).toBe("pending"); // send 已发、无终态——在途
153
+
154
+ m.ch.dispose(); // close 吞没(onClose 未触发)→ dispose 收割兜底
155
+ await settle(t);
156
+
157
+ expect(t.outcome).toBe("rejected");
158
+ const err = t.error as Error;
159
+ expect(err).toBeInstanceOf(Error);
160
+ expect(err.message).toContain("已 dispose");
161
+ expect(err.message).toContain("收割");
162
+ expect(err.message).toContain("恢复指引");
163
+ // 收割后到达的权威终态:settled 守卫——落定结果不改写
164
+ m.emitTerminal(t.sessionId, "success");
165
+ expect(t.outcome).toBe("rejected");
166
+ expect((t.error as Error).message).toBe(err.message);
167
+ });
168
+
169
+ it("grace 窗口内全部终态化:并发双会话 turn 全量收割,不留永久 pending", async () => {
170
+ const m = makeMockChannel();
171
+ enginesChannels.push(m.ch);
172
+ const t1 = startTurn(m.ch);
173
+ const t2 = startTurn(m.ch);
174
+ await flush();
175
+ expect(t1.sessionId).not.toBe(t2.sessionId); // 双会话确实并发
176
+ expect(t1.outcome).toBe("pending");
177
+ expect(t2.outcome).toBe("pending");
178
+
179
+ m.ch.dispose();
180
+ await Promise.all([settle(t1), settle(t2)]);
181
+
182
+ expect(t1.outcome).toBe("rejected");
183
+ expect(t2.outcome).toBe("rejected");
184
+ expect((t1.error as Error).message).toContain("已 dispose");
185
+ expect((t2.error as Error).message).toContain("已 dispose");
186
+ });
187
+
188
+ it("正常 close 主路径零回归:onClose 收割先行(错误=连接层 reason),dispose no-op 不改写", async () => {
189
+ const m = makeMockChannel();
190
+ enginesChannels.push(m.ch);
191
+ const t = startTurn(m.ch);
192
+ await flush();
193
+
194
+ m.emitClose("进程退出(code=0 signal=SIGTERM)"); // onClose 主路径收割(正常 close 到达)
195
+ await settle(t);
196
+ expect(t.outcome).toBe("rejected");
197
+ const onCloseErr = t.error as Error;
198
+ expect(onCloseErr.message).toContain("app-server 进程退出");
199
+
200
+ m.ch.dispose(); // activeTurns 已空——幂等 no-op,收割错误不被 dispose 文案改写
201
+ expect(t.outcome).toBe("rejected");
202
+ expect((t.error as Error).message).toBe(onCloseErr.message);
203
+ });
204
+
205
+ it("终态已落定(成功)后 dispose:settled 守卫不改写已成功结果", async () => {
206
+ const m = makeMockChannel();
207
+ enginesChannels.push(m.ch);
208
+ const t = startTurn(m.ch);
209
+ await flush();
210
+ m.emitTerminal(t.sessionId, "success");
211
+ await settle(t);
212
+ expect(t.outcome).toBe("fulfilled");
213
+
214
+ m.ch.dispose(); // 已成功收尾的 turn 不受 dispose 收割影响
215
+ expect(t.outcome).toBe("fulfilled");
216
+ expect(t.result?.terminal).toEqual({ status: "success", source: "turn.terminal" });
217
+ });
218
+
219
+ it("dispose 收割后 closeSession 短路:finally 不发 session/close(防写死进程/惰性重建);正常完成对照=发 close", async () => {
220
+ const m1 = makeMockChannel();
221
+ enginesChannels.push(m1.ch);
222
+ const t1 = startTurn(m1.ch);
223
+ await flush();
224
+ m1.ch.dispose();
225
+ await settle(t1);
226
+ await flush(); // runTurn finally 的 closeSession 已过
227
+ expect(t1.outcome).toBe("rejected");
228
+ expect(m1.requests.map((r) => r.method)).not.toContain("session/close");
229
+
230
+ // 对照:正常完成的 turn,finally 发 session/close(D4 用后即毁——零回归面)
231
+ const m2 = makeMockChannel();
232
+ enginesChannels.push(m2.ch);
233
+ const t2 = startTurn(m2.ch);
234
+ await flush();
235
+ m2.emitTerminal(t2.sessionId, "success");
236
+ await settle(t2);
237
+ expect(t2.outcome).toBe("fulfilled");
238
+ expect(m2.requests.map((r) => r.method)).toContain("session/close");
239
+ });
240
+
241
+ it("二次 dispose 幂等:无在途 turn 时 dispose 是 no-op 不抛", async () => {
242
+ const m = makeMockChannel();
243
+ enginesChannels.push(m.ch);
244
+ expect(() => m.ch.dispose()).not.toThrow();
245
+ expect(() => m.ch.dispose()).not.toThrow();
246
+ });
247
+ });