@zhushanwen/subagent-core 0.4.0 → 0.6.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 (309) hide show
  1. package/dist/{chunk-4KN4TTG7.js → chunk-43ONBFZX.js} +1 -1
  2. package/dist/{chunk-X7SZ5HLQ.js → chunk-V3VHZ2VX.js} +20 -27
  3. package/dist/execution/engine/engines/zcode/constants.cjs +29 -41
  4. package/dist/execution/engine/engines/zcode/constants.d.cts +65 -53
  5. package/dist/execution/engine/engines/zcode/constants.d.ts +65 -53
  6. package/dist/execution/engine/engines/zcode/constants.js +17 -27
  7. package/dist/execution/engine/engines/zcode/reader.d.cts +2 -1
  8. package/dist/execution/engine/engines/zcode/reader.d.ts +2 -1
  9. package/dist/execution/engine/engines/zcode/reader.js +2 -2
  10. package/dist/index.cjs +17100 -14082
  11. package/dist/index.d.cts +5336 -4033
  12. package/dist/index.d.ts +5336 -4033
  13. package/dist/index.js +17020 -14055
  14. package/dist/{types-DpUO16pj.d.cts → types-C3fE3R9x.d.cts} +280 -112
  15. package/dist/{types-DpUO16pj.d.ts → types-C3fE3R9x.d.ts} +280 -112
  16. package/dist.bundle/index.cjs +34933 -0
  17. package/package.json +3 -3
  18. package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +0 -1
  19. package/src/__tests__/append-system-prompt-assembly.test.ts +8 -7
  20. package/src/__tests__/fr4-get-state-handshake.test.ts +2 -2
  21. package/src/__tests__/m2-append-content-probe.test.ts +0 -1
  22. package/src/__tests__/manifest-store.test.ts +10 -9
  23. package/src/__tests__/record-store-cache.test.ts +0 -2
  24. package/src/__tests__/record-store-index.test.ts +1 -2
  25. package/src/__tests__/review-fix-loop-script.test.ts +4 -2
  26. package/src/__tests__/review-fix-loop-utils.test.ts +10 -10
  27. package/src/__tests__/robustness-low-batch1.test.ts +6 -5
  28. package/src/__tests__/robustness-medium-batch1.test.ts +2 -2
  29. package/src/__tests__/robustness-medium-batch2.test.ts +17 -6
  30. package/src/__tests__/robustness-medium-batch4.test.ts +2 -2
  31. package/src/__tests__/session-runner.test.ts +1 -1
  32. package/src/core/__tests__/host-services.test.ts +2 -2
  33. package/src/core/__tests__/logger.test.ts +1 -1
  34. package/src/core/error-message.ts +9 -0
  35. package/src/core/host-services.ts +21 -5
  36. package/src/core/notify-ports.ts +18 -5
  37. package/src/execution/__tests__/agent-profile.test.ts +4 -4
  38. package/src/execution/__tests__/agent-registry.test.ts +370 -0
  39. package/src/execution/__tests__/agent-result-mapper.test.ts +18 -2
  40. package/src/execution/__tests__/agents-assembly.test.ts +2 -2
  41. package/src/execution/__tests__/alive-store.test.ts +1 -1
  42. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +6 -6
  43. package/src/execution/__tests__/chat-engine-routing.test.ts +68 -33
  44. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +4 -4
  45. package/src/execution/__tests__/cold-resurrect.test.ts +283 -0
  46. package/src/execution/__tests__/collect-budget.test.ts +291 -0
  47. package/src/execution/__tests__/collect-coordinator-service.test.ts +327 -0
  48. package/src/execution/__tests__/collect-coordinator.test.ts +371 -0
  49. package/src/execution/__tests__/collect-mixed-dispatch.test.ts +262 -0
  50. package/src/execution/__tests__/config-collect-sync.test.ts +120 -0
  51. package/src/execution/__tests__/config.test.ts +1 -1
  52. package/src/execution/__tests__/delivery-methods.test.ts +75 -62
  53. package/src/execution/__tests__/descendant-sweep-guards.test.ts +220 -0
  54. package/src/execution/__tests__/descendant-sweep.test.ts +6 -6
  55. package/src/execution/__tests__/dialog-queue.test.ts +2 -2
  56. package/src/execution/__tests__/engine-model-validation.test.ts +741 -0
  57. package/src/execution/__tests__/epipe-fallback.test.ts +29 -24
  58. package/src/execution/__tests__/execute-and-await-worktree.test.ts +21 -91
  59. package/src/execution/__tests__/execute-nesting.test.ts +34 -188
  60. package/src/execution/__tests__/execution-record.test.ts +217 -60
  61. package/src/execution/__tests__/execution-runtime-face.test.ts +5 -5
  62. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +4 -4
  63. package/src/execution/__tests__/finalize-record.test.ts +173 -17
  64. package/src/execution/__tests__/finalized-marker.test.ts +1 -1
  65. package/src/execution/__tests__/gc-timer.test.ts +56 -3
  66. package/src/execution/__tests__/get-record-for-action-restart.test.ts +12 -12
  67. package/src/execution/__tests__/get-state-handshake.test.ts +1 -1
  68. package/src/execution/__tests__/helpers/mock-extension-api.ts +1 -1
  69. package/src/execution/__tests__/helpers/session-runner-mocks.ts +113 -0
  70. package/src/execution/__tests__/helpers/spawn-mock.ts +150 -3
  71. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +137 -0
  72. package/src/execution/__tests__/keep-alive-no-progress.test.ts +50 -148
  73. package/src/execution/__tests__/kill-all-escalation.test.ts +2 -2
  74. package/src/execution/__tests__/lifecycle-predicates.test.ts +1 -2
  75. package/src/execution/__tests__/manifest-parentid.test.ts +4 -4
  76. package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +1 -1
  77. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +1 -1
  78. package/src/execution/__tests__/model-resolver.test.ts +2 -2
  79. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +23 -121
  80. package/src/execution/__tests__/nested-visibility.test.ts +2 -0
  81. package/src/execution/__tests__/notify-batch.test.ts +500 -0
  82. package/src/execution/__tests__/notify-ledger.test.ts +1000 -18
  83. package/src/execution/__tests__/output-collector.test.ts +117 -6
  84. package/src/execution/__tests__/pi-invocation.test.ts +36 -4
  85. package/src/execution/__tests__/record-entry-collect.test.ts +102 -0
  86. package/src/execution/__tests__/record-store.test.ts +10 -9
  87. package/src/execution/__tests__/records-cwd-isolation.test.ts +2 -2
  88. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +29 -132
  89. package/src/execution/__tests__/recursive-visibility-env.test.ts +55 -179
  90. package/src/execution/__tests__/relay-env.test.ts +12 -1
  91. package/src/execution/__tests__/rpc-mode.test.ts +2 -2
  92. package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +387 -0
  93. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +15 -9
  94. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +4 -4
  95. package/src/execution/__tests__/run-spawn-edges.test.ts +17 -15
  96. package/src/execution/__tests__/run-spawn-integration.test.ts +2 -2
  97. package/src/execution/__tests__/run-spawn-resume.test.ts +2 -2
  98. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +2 -2
  99. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +4 -4
  100. package/src/execution/__tests__/service-kill-escalation.test.ts +7 -5
  101. package/src/execution/__tests__/session-context-resolver.test.ts +0 -2
  102. package/src/execution/__tests__/session-file-gc.test.ts +36 -1
  103. package/src/execution/__tests__/session-pending.test.ts +257 -14
  104. package/src/execution/__tests__/session-reconstructor.test.ts +366 -15
  105. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +1 -1
  106. package/src/execution/__tests__/session-runner-close-prune.test.ts +2 -2
  107. package/src/execution/__tests__/session-runner-dispatch.test.ts +489 -0
  108. package/src/execution/__tests__/session-runner-epipe.test.ts +3 -3
  109. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +5 -4
  110. package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +290 -0
  111. package/src/execution/__tests__/session-runner-schema-env.test.ts +7 -5
  112. package/src/execution/__tests__/settled-watchdog.test.ts +282 -47
  113. package/src/execution/__tests__/spawn-args.test.ts +11 -10
  114. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +26 -23
  115. package/src/execution/__tests__/spawn-event-adapter.test.ts +3 -3
  116. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +5 -4
  117. package/src/execution/__tests__/spawned-children.test.ts +2 -1
  118. package/src/execution/__tests__/start-collect-guard.test.ts +255 -0
  119. package/src/execution/__tests__/start-sync-model-guard.test.ts +5 -5
  120. package/src/execution/__tests__/stdin-writer.test.ts +6 -4
  121. package/src/execution/__tests__/stream-sink-retirement.test.ts +6 -6
  122. package/src/execution/__tests__/subagent-actions-core.test.ts +48 -22
  123. package/src/execution/__tests__/subagent-service-message-close.test.ts +29 -26
  124. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +21 -8
  125. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +27 -22
  126. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +30 -29
  127. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +45 -20
  128. package/src/execution/__tests__/subagent-service.test.ts +37 -30
  129. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +88 -12
  130. package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +75 -0
  131. package/src/execution/__tests__/subprocess-agent-runner.test.ts +22 -20
  132. package/src/execution/__tests__/sync-collect-recovery.test.ts +1117 -0
  133. package/src/execution/__tests__/temp-prompt.test.ts +1 -1
  134. package/src/execution/__tests__/timeout-integration.test.ts +6 -5
  135. package/src/execution/__tests__/tombstone-store.test.ts +1 -1
  136. package/src/execution/__tests__/turn-limiter-semantics.test.ts +1 -1
  137. package/src/execution/__tests__/turn-limiter.test.ts +1 -1
  138. package/src/execution/__tests__/ui-request-handler-factory.test.ts +12 -6
  139. package/src/execution/__tests__/ui-request-handler.test.ts +4 -3
  140. package/src/execution/__tests__/ui-request-queue.test.ts +142 -2
  141. package/src/execution/__tests__/worktree-git-ops.test.ts +1 -1
  142. package/src/execution/__tests__/worktree-manager.test.ts +14 -5
  143. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +4 -3
  144. package/src/execution/__tests__/worktree-reconcile-aging.test.ts +2 -2
  145. package/src/execution/__tests__/worktree-reconcile.integration.test.ts +1 -1
  146. package/src/execution/__tests__/worktree-registry.test.ts +1 -1
  147. package/src/execution/agent-registry.ts +74 -30
  148. package/src/execution/agent-result-mapper.ts +3 -0
  149. package/src/execution/cold-resurrect.ts +199 -0
  150. package/src/execution/collect-coordinator.ts +200 -0
  151. package/src/execution/config.ts +77 -5
  152. package/src/execution/engine/__tests__/common/capability-gate.test.ts +91 -0
  153. package/src/execution/engine/__tests__/common/data-dir.test.ts +1 -1
  154. package/src/execution/engine/__tests__/common/event-journal.test.ts +1 -1
  155. package/src/execution/engine/__tests__/common/journal-wiring.test.ts +84 -0
  156. package/src/execution/engine/__tests__/common/kill-chain.test.ts +34 -7
  157. package/src/execution/engine/__tests__/common/persona-router.test.ts +4 -5
  158. package/src/execution/engine/__tests__/common/pool-manager.test.ts +213 -53
  159. package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +149 -0
  160. package/src/execution/engine/__tests__/common/session-view-service.test.ts +499 -0
  161. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +22 -112
  162. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +15 -28
  163. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +1 -1
  164. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +12 -12
  165. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +2 -2
  166. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +21 -82
  167. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +4 -7
  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 +4 -2
  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/engines/pi/__tests__/pi-engine.test.ts +174 -28
  181. package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +257 -0
  182. package/src/execution/{argv-mirror.ts → engine/engines/pi/argv-mirror.ts} +43 -21
  183. package/src/execution/{get-state-handshake.ts → engine/engines/pi/get-state-handshake.ts} +1 -1
  184. package/src/execution/{output-collector.ts → engine/engines/pi/output-collector.ts} +108 -19
  185. package/src/execution/engine/engines/pi/pi-engine.ts +434 -38
  186. package/src/execution/{pi-invocation.ts → engine/engines/pi/pi-invocation.ts} +2 -2
  187. package/src/execution/engine/engines/pi/reader.ts +11 -0
  188. package/src/execution/engine/engines/pi/registration.ts +4 -2
  189. package/src/execution/{session-runner.ts → engine/engines/pi/session-runner.ts} +617 -412
  190. package/src/execution/{spawn-event-adapter.ts → engine/engines/pi/spawn-event-adapter.ts} +75 -34
  191. package/src/execution/{stdin-writer.ts → engine/engines/pi/stdin-writer.ts} +10 -9
  192. package/src/execution/{temp-prompt.ts → engine/engines/pi/temp-prompt.ts} +2 -2
  193. package/src/execution/{turn-limiter.ts → engine/engines/pi/turn-limiter.ts} +1 -1
  194. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +54 -27
  195. package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +321 -0
  196. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +13 -12
  197. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +7 -210
  198. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +6 -110
  199. package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +247 -0
  200. package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +451 -0
  201. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +20 -8
  202. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +77 -110
  203. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +26 -320
  204. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +210 -0
  205. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +353 -0
  206. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +282 -0
  207. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +372 -0
  208. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +48 -148
  209. package/src/execution/engine/engines/zcode/appserver-launcher.ts +167 -0
  210. package/src/execution/engine/engines/zcode/connection.ts +24 -11
  211. package/src/execution/engine/engines/zcode/constants.ts +75 -71
  212. package/src/execution/engine/engines/zcode/golden-sample.ts +8 -46
  213. package/src/execution/engine/engines/zcode/parser.ts +12 -346
  214. package/src/execution/engine/engines/zcode/preparer.ts +12 -143
  215. package/src/execution/engine/engines/zcode/session-channel.ts +351 -39
  216. package/src/execution/engine/engines/zcode/zcode-engine.ts +665 -760
  217. package/src/execution/engine/host-task-spec.ts +32 -36
  218. package/src/execution/engine/model-validation.ts +177 -0
  219. package/src/execution/engine/port.ts +24 -6
  220. package/src/execution/engine/routing.ts +75 -1
  221. package/src/execution/engine/types.ts +30 -103
  222. package/src/execution/execution-record.ts +229 -144
  223. package/src/execution/finalize-record.ts +141 -99
  224. package/src/execution/idle-gc.ts +28 -0
  225. package/src/execution/lifecycle-predicates.ts +1 -1
  226. package/src/execution/notifier.ts +234 -25
  227. package/src/execution/notify-host.ts +232 -0
  228. package/src/execution/record-entry.ts +16 -0
  229. package/src/execution/record-store.ts +280 -103
  230. package/src/execution/round-settlement.ts +93 -0
  231. package/src/execution/session-file-gc.ts +96 -62
  232. package/src/execution/session-reconstructor.ts +256 -143
  233. package/src/execution/sessions-index.ts +117 -47
  234. package/src/execution/settled-watchdog.ts +264 -52
  235. package/src/execution/subagent-actions-core.ts +84 -28
  236. package/src/execution/subagent-service.ts +1225 -964
  237. package/src/execution/subprocess-agent-runner.ts +144 -97
  238. package/src/execution/sync-rebuild.ts +87 -0
  239. package/src/execution/types.ts +72 -0
  240. package/src/execution/ui-request-handler-factory.ts +102 -74
  241. package/src/execution/ui-request-queue.ts +50 -19
  242. package/src/index.ts +442 -316
  243. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +1 -1
  244. package/src/orchestration/__tests__/agent-call-stream.test.ts +2 -2
  245. package/src/orchestration/__tests__/args-validator.test.ts +14 -0
  246. package/src/orchestration/__tests__/config-loader.test.ts +1 -1
  247. package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +1 -1
  248. package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +11 -9
  249. package/src/orchestration/__tests__/execute-agent-call.test.ts +151 -11
  250. package/src/orchestration/__tests__/file-run-store-prune.test.ts +1 -1
  251. package/src/orchestration/__tests__/file-run-store-throttle.test.ts +1 -1
  252. package/src/orchestration/__tests__/file-run-store.test.ts +7 -6
  253. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +2 -2
  254. package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +1 -1
  255. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +3 -3
  256. package/src/orchestration/__tests__/lifecycle.test.ts +7 -5
  257. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +1 -1
  258. package/src/orchestration/__tests__/review-fix-loop-scriptpath-failfast.test.ts +1 -1
  259. package/src/orchestration/__tests__/run-snapshot.test.ts +8 -5
  260. package/src/orchestration/__tests__/script-generate.test.ts +5 -5
  261. package/src/orchestration/__tests__/script-lint.test.ts +17 -0
  262. package/src/orchestration/__tests__/skill-discovery.test.ts +2 -2
  263. package/src/orchestration/__tests__/test-mocks.ts +2 -0
  264. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +7 -6
  265. package/src/orchestration/__tests__/worker-message-pump-finalize-run.test.ts +229 -0
  266. package/src/orchestration/__tests__/{error-recovery-handlers.test.ts → worker-message-pump-handlers.test.ts} +27 -19
  267. package/src/orchestration/__tests__/{error-recovery-postmessage-defense.test.ts → worker-message-pump-postmessage-defense.test.ts} +8 -8
  268. package/src/orchestration/__tests__/{error-recovery-serialize-failed-result.test.ts → worker-message-pump-serialize-failed-result.test.ts} +2 -2
  269. package/src/orchestration/__tests__/{error-recovery-workflow-call.test.ts → worker-message-pump-workflow-call.test.ts} +1 -1
  270. package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +1 -1
  271. package/src/orchestration/__tests__/worker-script-builder.test.ts +1 -1
  272. package/src/orchestration/__tests__/workflow-files.test.ts +10 -10
  273. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +1 -1
  274. package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +1 -1
  275. package/src/orchestration/agent-opts-resolver.ts +1 -1
  276. package/src/orchestration/args-validator.ts +53 -31
  277. package/src/orchestration/execute-agent-call.ts +19 -73
  278. package/src/orchestration/launcher.ts +128 -81
  279. package/src/orchestration/lifecycle.ts +43 -45
  280. package/src/orchestration/models/ports.ts +4 -4
  281. package/src/orchestration/models/run-runtime.ts +2 -2
  282. package/src/orchestration/models/trace.ts +1 -1
  283. package/src/orchestration/models/types.ts +111 -22
  284. package/src/orchestration/models/workflow-run.ts +1 -1
  285. package/src/orchestration/script-lint.ts +91 -58
  286. package/src/orchestration/{error-recovery.ts → worker-message-pump.ts} +157 -150
  287. package/src/shared/__tests__/atomic-write.test.ts +1 -1
  288. package/src/shared/__tests__/meta-parser.test.ts +151 -0
  289. package/src/shared/__tests__/resource-discovery-host-roots.test.ts +5 -5
  290. package/src/shared/__tests__/resource-discovery.test.ts +258 -215
  291. package/src/shared/atomic-write.ts +12 -9
  292. package/src/shared/meta-parser.ts +158 -84
  293. package/src/shared/model-ref.ts +15 -2
  294. package/src/shared/resource-discovery.ts +9 -183
  295. package/src/shared/schema-jsonify.ts +1 -1
  296. package/workflows/review-fix-loop-utils.cjs +151 -89
  297. package/workflows/review-fix-loop-utils.d.cts +287 -0
  298. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
  299. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  300. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  301. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  302. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  303. package/src/execution/engine/engines/zcode/__tests__/preparer-appserver.test.ts +0 -387
  304. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -710
  305. package/src/execution/engine/engines/zcode/appserver-home.ts +0 -442
  306. package/src/execution/engine/engines/zcode/appserver-probe.ts +0 -141
  307. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  308. package/src/execution/execute-options-mapper.ts +0 -115
  309. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -288
@@ -1,44 +1,28 @@
1
- // zcode-engine-degrade.test.ts —— [R5] 降级链四步(D2)+ probe 冒烟(D8)单测。
2
- // 全部跑 __fixtures__/fake-appserver.mjs 子进程(scenario 注入),绝不 spawn
3
- // zcode.cjs。覆盖(impl-plan R5 验收条款 ①-④):
4
- // RA5-① 回归门:fixture 注入 -32602(create)/ -32601(send)→ 首任务降级
5
- // spawn 重跑成功 + outcome.engineFallback 标注 degraded: spawn + 第二任务直走
6
- // spawn(断言不再尝试 app-server 连接:boot/create 帧计数冻结);
7
- // probe 冒烟:探针连接帧序(create→close、无 send 帧——不发模型请求)、
8
- // mtime 未变不重探、mtime 变化(fake 触碰)后重探、预算超时(hangCreate)失败降级、
9
- // 探针 create error 失败降级;
10
- // ③ env 三态:缺省(探+降)/ appserver 定向(不探不降,失败直接报)/ spawn 定向
11
- // (不探不降直 spawn);
12
- // ④ 错误分类表:-32603 → engine_credential_missing 不降级;-32004/-32010 →
13
- // engine_run_failed 上报不降级(非漂移类)。
14
- // 探针/主连接区分判据:探针 env 带 ZCODE_APPSERVER_PROBE_CONN=1(fake env 快照记
15
- // probe 标志;探针 scenario 只读 FAKE_PROBE_SCENARIO——故障注入互不串扰)。
1
+ // zcode-engine-degrade.test.ts —— appserver 错误分类回归门(降级链已删,仅存本主题)。
2
+ // 2026-09 单一 app-server 形态重构删除了整条降级机制:CLI spawn 降级重跑、probe 冒烟
3
+ // 门控、模式钉扎 env 三态均不复存在——协议漂移类错误直接上报可操作错误,不再换通道
4
+ // 保底。原「首败漂移降级」「probe 冒烟」「env 三态」describe 随机制删除,本文件仅存
5
+ // 错误规格表回归门:
6
+ // - -32603 "Model config is missing" → engine_credential_missing(共享宿主 HOME——
7
+ // 凭据在 ZCode 桌面端管理),不换路径(第二任务仍走 app-server,create 帧计数增长);
8
+ // - -32004 / -32010 → engine_run_failed 按任务失败上报(非漂移类,无 fallback 标注)。
9
+ // 全部跑 __fixtures__/fake-appserver.mjs 子进程(scenario 注入),绝不 spawn 真 zcode.cjs。
16
10
 
17
- import { spawn, type ChildProcess } from "node:child_process";
18
11
  import * as fs from "node:fs";
19
12
  import * as os from "node:os";
20
13
  import * as path from "node:path";
21
- import { PassThrough } from "node:stream";
22
14
  import { fileURLToPath } from "node:url";
23
15
 
24
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
16
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
25
17
 
26
18
  import type { RunContext } from "../../../port.ts";
27
- import type { AgentTaskSpec } from "../../../types.ts";
28
- import { getLogger } from "../../../../../core/logger.ts";
29
- import { resolvePoolDir } from "../../../paths.ts";
30
- import { ZCODE_APPSERVER_GOLDEN, ZCODE_GOLDEN_STDOUT } from "../golden-sample.ts";
31
- import type { ZcodeLaunchedProcess } from "../launcher.ts";
32
- import { ZcodeEngine, pinnedZcodeMode, type ZcodeEngineDeps } from "../zcode-engine.ts";
19
+ import type { AgentCallOpts } from "../../../../../orchestration/models/types.ts";
20
+ import { ZCODE_APPSERVER_GOLDEN } from "../golden-sample.ts";
21
+ import { ZcodeEngine, type ZcodeEngineDeps } from "../zcode-engine.ts";
33
22
 
34
23
  const FAKE_CLI = fileURLToPath(new URL("./__fixtures__/fake-appserver.mjs", import.meta.url));
35
24
  const PROVIDER = "test-provider";
36
25
 
37
- /** 与 zcode-engine.ts 同一 facade 单例引用(spy 它的方法即拦截引擎的 warn 出声)。 */
38
- const subagentsLogger = getLogger("subagents");
39
-
40
- const GOLDEN_SESSION_ID = "sess_golden_r3_01";
41
-
42
26
  const engines: ZcodeEngine[] = [];
43
27
  let seq = 0;
44
28
  let tmpRoot: string;
@@ -61,50 +45,17 @@ beforeEach(() => {
61
45
 
62
46
  afterEach(async () => {
63
47
  for (const engine of engines.splice(0)) await engine.dispose().catch(() => undefined);
64
- fs.rmSync(tmpRoot, { recursive: true, force: true });
48
+ fs.rmSync(tmpRoot, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
65
49
  });
66
50
 
67
- // ── fake launcher(降级 spawn 重跑的成功通道——golden stdout 单轮) ──
68
-
69
- function makeFakeLaunch(stdout = ZCODE_GOLDEN_STDOUT) {
70
- const calls: Array<{ cliPath: string; args: string[]; env: NodeJS.ProcessEnv }> = [];
71
- const launch = (o: { cliPath: string; args: string[]; env: NodeJS.ProcessEnv }): ZcodeLaunchedProcess => {
72
- calls.push(o);
73
- const child: ChildProcess = spawn(process.execPath, ["-e", ""]);
74
- const out = new PassThrough();
75
- out.end(stdout);
76
- const stderr = new PassThrough();
77
- stderr.end("");
78
- let killed = false;
79
- return {
80
- child,
81
- pid: 4242,
82
- stdout: out,
83
- stderr,
84
- abort: async () => {
85
- killed = true;
86
- },
87
- exited: Promise.resolve({ code: 0, signal: undefined }),
88
- killedByUs: () => killed,
89
- };
90
- };
91
- return { launch, calls };
92
- }
93
-
94
- interface DegradeScenario {
95
- /** 主连接(常驻)scenario 覆盖(缺省 golden 成功形态)。 */
51
+ interface ErrorScenario {
52
+ /** 主连接(常驻)scenario 覆盖:create/send 阶段错误注入。 */
96
53
  createError?: { code: number; message: string; data?: unknown };
97
54
  sendError?: { code: number; message: string; data?: unknown };
98
- /** 探针 scenario(只作用于探针连接)。 */
99
- probeScenario?: Record<string, unknown>;
100
- /** 探针预算(缺省走 10s 常量——测试注入短值验证超时路径)。 */
101
- probeBudgetMs?: number;
102
- /** env 定向(XYZ_ZCODE_MODE)。 */
103
- mode?: "appserver" | "spawn";
104
55
  }
105
56
 
106
- /** 建一个缺省(探+降)或定向模式的引擎 + 配套 launch fake。 */
107
- function makeDegradeEngine(s: DegradeScenario = {}) {
57
+ /** 建一个连到 fake 的引擎(scenario 文件随 env 固化进子进程;每测试独立 state 文件)。 */
58
+ function makeEngine(s: ErrorScenario = {}) {
108
59
  seq += 1;
109
60
  const stateFile = path.join(tmpRoot, `state-${seq}.jsonl`);
110
61
  const scenarioFile = path.join(tmpRoot, `scenario-${seq}.json`);
@@ -119,32 +70,22 @@ function makeDegradeEngine(s: DegradeScenario = {}) {
119
70
  ...(s.createError !== undefined ? { createError: s.createError } : {}),
120
71
  ...(s.sendError !== undefined ? { sendError: s.sendError } : {}),
121
72
  });
122
- let probeScenarioFile: string | undefined;
123
- if (s.probeScenario !== undefined) {
124
- probeScenarioFile = path.join(tmpRoot, `probe-scenario-${seq}.json`);
125
- writeJson(probeScenarioFile, s.probeScenario);
126
- }
127
- const fakeLaunch = makeFakeLaunch();
128
73
  const deps: ZcodeEngineDeps = {
129
74
  engineDataDir: () => dataDir,
130
75
  cliPath: FAKE_CLI,
131
76
  sources: { v2ConfigPath: v2Path },
132
77
  processEnv: {
133
78
  PATH: process.env.PATH ?? "",
134
- ...(s.mode !== undefined ? { XYZ_ZCODE_MODE: s.mode } : {}),
135
79
  FAKE_STATE_FILE: stateFile,
136
80
  FAKE_SESSION_SCENARIO: scenarioFile,
137
- ...(probeScenarioFile !== undefined ? { FAKE_PROBE_SCENARIO: probeScenarioFile } : {}),
138
81
  },
139
- ...(s.probeBudgetMs !== undefined ? { probeBudgetMs: s.probeBudgetMs } : {}),
140
- launch: fakeLaunch.launch as unknown as ZcodeEngineDeps["launch"],
141
82
  };
142
83
  const engine = new ZcodeEngine(deps);
143
84
  engines.push(engine);
144
- return { engine, stateFile, workspace, fakeLaunch };
85
+ return { engine, stateFile, workspace };
145
86
  }
146
87
 
147
- // ── 流水读取 helpers(与 zcode-engine-appserver.test.ts 同款 + 探针判据) ──
88
+ // ── 流水读取 helpers(与 zcode-engine-appserver.test.ts 同款) ──
148
89
 
149
90
  interface StateEvent {
150
91
  seq: number;
@@ -179,26 +120,8 @@ function recvFrames(stateFile: string, method: string): Array<{ id: number; para
179
120
  );
180
121
  }
181
122
 
182
- function recvMethods(stateFile: string): string[] {
183
- return readState(stateFile)
184
- .filter((e) => e.ev === "recv")
185
- .map((e) => e.frame)
186
- .filter((f): f is Record<string, unknown> => isRecord(f) && typeof f.method === "string")
187
- .map((f) => f.method as string);
188
- }
189
-
190
- /** 探针进程 boot 数(env 快照带 probe='1'——探针连接标记)。 */
191
- function probeBootCount(stateFile: string): number {
192
- return readState(stateFile).filter((e) => e.ev === "env" && e["probe"] === "1").length;
193
- }
194
-
195
- /** 全部 boot 数(探针 + 常驻主连接)。 */
196
- function bootCount(stateFile: string): number {
197
- return readState(stateFile).filter((e) => e.ev === "boot").length;
198
- }
199
-
200
- function makeTask(overrides?: Partial<AgentTaskSpec>): AgentTaskSpec {
201
- return { task: "做点什么", slug: "s", model: `${PROVIDER}/m1`, ...overrides };
123
+ function makeTask(overrides?: Partial<AgentCallOpts>): AgentCallOpts {
124
+ return { prompt: "做点什么", description: "s", model: `${PROVIDER}/m1`, ...overrides };
202
125
  }
203
126
 
204
127
  function makeCtx(overrides?: Partial<RunContext>): RunContext {
@@ -206,257 +129,40 @@ function makeCtx(overrides?: Partial<RunContext>): RunContext {
206
129
  }
207
130
 
208
131
  // ============================================================
209
- // RA5-① 回归门:首败漂移降级(D2②)
210
- // ============================================================
211
-
212
- describe("首败漂移降级(RA5-① 回归门)", () => {
213
- it("-32602(create 阶段):首任务降级 spawn 重跑成功 + 标注 degraded: spawn + 第二任务直走 spawn", async () => {
214
- const { engine, stateFile, workspace, fakeLaunch } = makeDegradeEngine({
215
- createError: { code: -32602, message: "Invalid params", data: [{ path: ["model"] }] },
216
- });
217
- // 首任务:探针过(探针连接不受主 scenario 影响)→ 主连接 create -32602 → 漂移
218
- // → 同一任务 spawn 重跑(fake launch golden)成功
219
- const r1 = await engine.run(makeTask({ cwd: workspace }), makeCtx({ taskId: "d1" }));
220
- expect(r1.outcome.error).toBeUndefined();
221
- expect(r1.outcome.content).toBe("ok"); // spawn golden 单轮回复(ZCODE_GOLDEN_STDOUT)
222
- expect(r1.outcome.engineFallback).toBeDefined();
223
- expect(r1.outcome.engineFallback?.from).toBe("zcode:appserver");
224
- expect(r1.outcome.engineFallback?.reason).toContain("degraded: spawn");
225
- expect(r1.outcome.engineFallback?.reason).toContain("protocol-drift");
226
- expect(r1.outcome.engineFallback?.reason).toContain("-32602");
227
- expect(fakeLaunch.calls).toHaveLength(1); // 重跑走的是 spawn 通道
228
- const bootsAfter1 = bootCount(stateFile);
229
- const createsAfter1 = recvFrames(stateFile, "session/create").length;
230
-
231
- // 第二任务:直走 spawn——不再尝试 app-server 连接(boot/create 帧计数冻结)
232
- const r2 = await engine.run(makeTask({ cwd: workspace }), makeCtx({ taskId: "d2" }));
233
- expect(r2.outcome.error).toBeUndefined();
234
- expect(r2.outcome.engineFallback?.reason).toContain("degraded: spawn");
235
- expect(fakeLaunch.calls).toHaveLength(2);
236
- expect(bootCount(stateFile)).toBe(bootsAfter1);
237
- expect(recvFrames(stateFile, "session/create").length).toBe(createsAfter1);
238
- }, 20_000);
239
-
240
- it("-32601(send 阶段)同归类:首任务漂移降级 + 后续直走 spawn", async () => {
241
- const { engine, stateFile, workspace, fakeLaunch } = makeDegradeEngine({
242
- sendError: { code: -32601, message: "method not found: session/send" },
243
- });
244
- const r1 = await engine.run(makeTask({ cwd: workspace }), makeCtx());
245
- expect(r1.outcome.error).toBeUndefined();
246
- expect(r1.outcome.content).toBe("ok"); // spawn golden 单轮回复(ZCODE_GOLDEN_STDOUT)
247
- expect(r1.outcome.engineFallback?.reason).toContain("-32601");
248
- expect(fakeLaunch.calls).toHaveLength(1);
249
- const createsAfter1 = recvFrames(stateFile, "session/create").length;
250
- const r2 = await engine.run(makeTask({ cwd: workspace }), makeCtx());
251
- expect(r2.outcome.error).toBeUndefined();
252
- expect(recvFrames(stateFile, "session/create").length).toBe(createsAfter1);
253
- expect(fakeLaunch.calls).toHaveLength(2);
254
- }, 20_000);
255
-
256
- it("RX2-F3:ctxModel 忽略 warn 在降级链仅一次(appserver 首跑出声,spawn 重跑不重复)", async () => {
257
- // task.model 缺省走 ZCODE_FALLBACK_DEFAULT_MODEL 解析链——v2 config 需含其 provider
258
- writeJson(v2Path, {
259
- provider: {
260
- [PROVIDER]: { options: { apiKey: "k", baseURL: "https://t.example" }, models: { m1: {} } },
261
- "builtin:bigmodel-coding-plan": { options: { apiKey: "k" }, models: { "GLM-5.3": {} } },
262
- },
263
- });
264
- const { engine, fakeLaunch } = makeDegradeEngine({
265
- createError: { code: -32602, message: "Invalid params", data: [{ path: ["model"] }] },
266
- });
267
- const warns: string[] = [];
268
- const warnSpy = vi.spyOn(subagentsLogger, "warn").mockImplementation(((msg: string) => {
269
- warns.push(msg);
270
- }) as typeof subagentsLogger.warn);
271
- let r1: Awaited<ReturnType<typeof engine.run>>;
272
- try {
273
- r1 = await engine.run(
274
- makeTask({ model: undefined }),
275
- makeCtx({ taskId: "d-ctxmodel", ctxModel: { id: "main-model", name: "Main", provider: "p", reasoning: false } }),
276
- );
277
- } finally {
278
- warnSpy.mockRestore();
279
- }
280
- // 降级重跑成功(spawn golden 单轮)
281
- expect(r1.outcome.error).toBeUndefined();
282
- expect(fakeLaunch.calls).toHaveLength(1);
283
- // 旧实现:appserver 首跑 + spawn 重跑各 warn 一次 = 同 taskId 双份相同 warn(本断言会红)
284
- const ctxModelWarns = warns.filter((m) => m.includes("ctxModel"));
285
- expect(ctxModelWarns).toHaveLength(1);
286
- expect(ctxModelWarns[0]).toContain("main-model");
287
- // 降级宣告 warn 不受抑制(漂移降级本身仍出声)
288
- expect(warns.some((m) => m.includes("漂移类错误"))).toBe(true);
289
- }, 20_000);
290
- });
291
-
292
- // ============================================================
293
- // ② probe 冒烟(D8:帧序 / 无模型请求 / mtime 缓存与重探 / 预算)
294
- // ============================================================
295
-
296
- describe("probe 冒烟(D8)", () => {
297
- it("探针通过 → 走 app-server;帧序 create→close、无 send 帧(不发模型请求);结论记 CLI mtime", async () => {
298
- const { engine, stateFile, workspace, fakeLaunch } = makeDegradeEngine();
299
- const r1 = await engine.run(makeTask({ cwd: workspace }), makeCtx());
300
- expect(r1.outcome.error).toBeUndefined();
301
- expect(r1.outcome.sessionId).toBe(GOLDEN_SESSION_ID); // 走的是 app-server 常驻路径
302
- expect(fakeLaunch.calls).toHaveLength(0);
303
- // 探针连接恰好 1 个 boot,且先于常驻主连接(首帧 env 快照带 probe 标志)
304
- expect(probeBootCount(stateFile)).toBe(1);
305
- const envs = readState(stateFile).filter((e) => e.ev === "env");
306
- expect(envs[0]?.["probe"]).toBe("1");
307
- expect(envs[1]?.["probe"]).toBeUndefined();
308
- // 探针帧序:探针 create(workspace=常驻 HOME)→ 探针 close,均在主连接 create 之前
309
- const creates = recvFrames(stateFile, "session/create");
310
- const homeDir = resolvePoolDir(dataDir, "zcode", "home-appserver");
311
- expect(creates[0]?.params["workspace"]).toMatchObject({ workspacePath: homeDir });
312
- const methods = recvMethods(stateFile);
313
- const firstCreate = methods.indexOf("session/create");
314
- const firstClose = methods.indexOf("session/close");
315
- const secondCreate = methods.indexOf("session/create", firstCreate + 1);
316
- expect(firstClose).toBeGreaterThan(firstCreate);
317
- expect(secondCreate).toBeGreaterThan(firstClose);
318
- // 不发模型请求:探针会话(首条 close 帧的 sessionId)从未出现 send 帧
319
- const probeSessionId = recvFrames(stateFile, "session/close")[0]?.params["sessionId"];
320
- expect(typeof probeSessionId).toBe("string");
321
- expect(probeSessionId).not.toBe(GOLDEN_SESSION_ID);
322
- const sendTargets = recvFrames(stateFile, "session/send").map((f) => f.params["sessionId"]);
323
- expect(sendTargets).not.toContain(probeSessionId);
324
- }, 20_000);
325
-
326
- it("mtime 未变不重探:第二任务探针 boot 计数不增;mtime 变化(触碰 CLI)后重探", async () => {
327
- const { engine, stateFile, workspace } = makeDegradeEngine();
328
- await engine.run(makeTask({ cwd: workspace }), makeCtx());
329
- await engine.run(makeTask({ cwd: workspace }), makeCtx());
330
- expect(probeBootCount(stateFile)).toBe(1); // 缓存命中
331
- // 触碰 CLI 伪造 zcode 升级(mtime 前移 2s——避开 fs 时间精度)
332
- const now = new Date();
333
- fs.utimesSync(FAKE_CLI, new Date(now.getTime() + 2000), new Date(now.getTime() + 2000));
334
- const r3 = await engine.run(makeTask({ cwd: workspace }), makeCtx());
335
- expect(r3.outcome.error).toBeUndefined();
336
- expect(probeBootCount(stateFile)).toBe(2); // mtime 变化 → 首个任务前重探
337
- }, 20_000);
338
-
339
- it("探针预算超时(hangCreate + 500ms 预算)→ 探针失败 → 直接 spawn + 标注 probe-failed", async () => {
340
- const { engine, stateFile, workspace, fakeLaunch } = makeDegradeEngine({
341
- probeScenario: { hangCreate: true },
342
- probeBudgetMs: 500,
343
- });
344
- const started = Date.now();
345
- const r1 = await engine.run(makeTask({ cwd: workspace }), makeCtx());
346
- // 500ms 预算内收割(不真等 10s 常量),随后 spawn 重跑成功
347
- expect(Date.now() - started).toBeLessThan(8_000);
348
- expect(r1.outcome.error).toBeUndefined();
349
- expect(r1.outcome.engineFallback?.reason).toContain("probe-failed");
350
- expect(r1.outcome.engineFallback?.reason).toContain("超时");
351
- expect(fakeLaunch.calls).toHaveLength(1);
352
- // 探针连接未发出 close(create 挂死)——但也没有任何 send 帧
353
- expect(recvMethods(stateFile)).not.toContain("session/send");
354
- }, 20_000);
355
-
356
- it("探针 create error(-32602)→ 探针失败 → 直接 spawn(主连接 scenario 不受影响也未被触碰)", async () => {
357
- const { engine, stateFile, workspace, fakeLaunch } = makeDegradeEngine({
358
- probeScenario: { createError: { code: -32602, message: "Invalid params" } },
359
- });
360
- const r1 = await engine.run(makeTask({ cwd: workspace }), makeCtx());
361
- expect(r1.outcome.error).toBeUndefined();
362
- expect(r1.outcome.engineFallback?.reason).toContain("probe-failed");
363
- expect(fakeLaunch.calls).toHaveLength(1);
364
- // 探针失败短路:常驻主连接从未建立(boot 全部来自探针)
365
- expect(bootCount(stateFile)).toBe(probeBootCount(stateFile));
366
- // 失败结论同样绑 mtime 缓存(smokeConclusion 缓存命中不分成败):同一 CLI mtime 下
367
- // 第二任务不重探(探针 boot 冻结)且仍直走 spawn,不再触碰主连接
368
- const probeBootsAfter1 = probeBootCount(stateFile);
369
- const bootsAfter1 = bootCount(stateFile);
370
- const r2 = await engine.run(makeTask({ cwd: workspace }), makeCtx());
371
- expect(r2.outcome.error).toBeUndefined();
372
- expect(r2.outcome.engineFallback?.reason).toContain("probe-failed");
373
- expect(fakeLaunch.calls).toHaveLength(2);
374
- expect(probeBootCount(stateFile)).toBe(probeBootsAfter1);
375
- expect(bootCount(stateFile)).toBe(bootsAfter1);
376
- }, 20_000);
377
- });
378
-
379
- // ============================================================
380
- // ③ env 三态(D2④ 定向不探不降)
381
- // ============================================================
382
-
383
- describe("env 三态(XYZ_ZCODE_MODE)", () => {
384
- it("pinnedZcodeMode 三态判定:定向值原样 / 缺省与未知值 undefined", () => {
385
- expect(pinnedZcodeMode({ XYZ_ZCODE_MODE: "spawn" })).toBe("spawn");
386
- expect(pinnedZcodeMode({ XYZ_ZCODE_MODE: "appserver" })).toBe("appserver");
387
- expect(pinnedZcodeMode({})).toBeUndefined();
388
- expect(pinnedZcodeMode({ XYZ_ZCODE_MODE: "garbage" })).toBeUndefined();
389
- });
390
-
391
- it("appserver 定向:不探(探针 boot 0)不降(-32602 直接上报,spawn 通道不启用)", async () => {
392
- const { engine, stateFile, workspace, fakeLaunch } = makeDegradeEngine({
393
- mode: "appserver",
394
- createError: { code: -32602, message: "Invalid params" },
395
- });
396
- const r1 = await engine.run(makeTask({ cwd: workspace }), makeCtx());
397
- expect(r1.outcome.error).toContain("engine_run_failed");
398
- expect(r1.outcome.error).toContain("-32602");
399
- expect(r1.outcome.engineFallback).toBeUndefined(); // 不降级 → 无标注
400
- expect(fakeLaunch.calls).toHaveLength(0);
401
- expect(probeBootCount(stateFile)).toBe(0);
402
- // 定向持续失败直接报:第二任务仍走 app-server(create 计数增长,非降级冻结)
403
- const createsBefore = recvFrames(stateFile, "session/create").length;
404
- const r2 = await engine.run(makeTask({ cwd: workspace }), makeCtx());
405
- expect(r2.outcome.error).toContain("-32602");
406
- expect(recvFrames(stateFile, "session/create").length).toBeGreaterThan(createsBefore);
407
- expect(fakeLaunch.calls).toHaveLength(0);
408
- }, 20_000);
409
-
410
- it("spawn 定向:不探不降直 spawn(零 app-server boot、零协议帧)", async () => {
411
- const { engine, stateFile, workspace, fakeLaunch } = makeDegradeEngine({ mode: "spawn" });
412
- const r1 = await engine.run(makeTask({ cwd: workspace }), makeCtx());
413
- expect(r1.outcome.error).toBeUndefined();
414
- expect(r1.outcome.engineFallback).toBeUndefined();
415
- expect(fakeLaunch.calls).toHaveLength(1);
416
- expect(bootCount(stateFile)).toBe(0);
417
- expect(readState(stateFile).filter((e) => e.ev === "recv")).toHaveLength(0);
418
- }, 15_000);
419
- });
420
-
421
- // ============================================================
422
- // ④ 错误分类表(非漂移类:-32603 / -32004 / -32010 不降级)
132
+ // 错误分类表(非漂移类:-32603 / -32004 / -32010 不降级)
423
133
  // ============================================================
424
134
 
425
135
  describe("错误分类(错误规格表)", () => {
426
136
  it("-32603 'Model config is missing' → engine_credential_missing,不降级(第二任务仍走 app-server)", async () => {
427
- const { engine, stateFile, workspace, fakeLaunch } = makeDegradeEngine({
137
+ const { engine, stateFile, workspace } = makeEngine({
428
138
  createError: { code: -32603, message: "Model config is missing" },
429
139
  });
430
140
  const r1 = await engine.run(makeTask({ cwd: workspace }), makeCtx());
431
141
  expect(r1.outcome.error).toContain("engine_credential_missing");
432
142
  expect(r1.outcome.engineFallback).toBeUndefined();
433
- expect(fakeLaunch.calls).toHaveLength(0);
434
143
  const createsBefore = recvFrames(stateFile, "session/create").length;
435
144
  const r2 = await engine.run(makeTask({ cwd: workspace }), makeCtx());
436
145
  expect(r2.outcome.error).toContain("engine_credential_missing");
437
146
  expect(recvFrames(stateFile, "session/create").length).toBeGreaterThan(createsBefore);
438
- expect(fakeLaunch.calls).toHaveLength(0);
439
147
  }, 20_000);
440
148
 
441
149
  it("-32004 'Session is not active' → 任务失败上报,不降级", async () => {
442
- const { engine, workspace, fakeLaunch } = makeDegradeEngine({
150
+ const { engine, workspace } = makeEngine({
443
151
  createError: { code: -32004, message: "Session is not active" },
444
152
  });
445
153
  const r1 = await engine.run(makeTask({ cwd: workspace }), makeCtx());
446
154
  expect(r1.outcome.error).toContain("engine_run_failed");
447
155
  expect(r1.outcome.error).toContain("-32004");
448
156
  expect(r1.outcome.engineFallback).toBeUndefined();
449
- expect(fakeLaunch.calls).toHaveLength(0);
450
157
  }, 20_000);
451
158
 
452
159
  it("-32010(send busy)→ 任务失败上报,不降级", async () => {
453
- const { engine, workspace, fakeLaunch } = makeDegradeEngine({
160
+ const { engine, workspace } = makeEngine({
454
161
  sendError: { code: -32010, message: "A turn is already running" },
455
162
  });
456
163
  const r1 = await engine.run(makeTask({ cwd: workspace }), makeCtx());
457
164
  expect(r1.outcome.error).toContain("engine_run_failed");
458
165
  expect(r1.outcome.error).toContain("-32010");
459
166
  expect(r1.outcome.engineFallback).toBeUndefined();
460
- expect(fakeLaunch.calls).toHaveLength(0);
461
167
  }, 20_000);
462
168
  });
@@ -0,0 +1,210 @@
1
+ // zcode-engine-dispose.test.ts —— P0-1 U5 dispose 收割(引擎集成面,fake-appserver
2
+ // 子进程,绝不 spawn 真 zcode.cjs)。
3
+ //
4
+ // 设计权威源:docs/design/timeout-zcode-turn-and-settled-watchdog.md §3.4(退化
5
+ // 路径)、§6 D7(dispose 收割兜底)、§11 P-Z3。真实现场(真实环境的 close 吞没)
6
+ // 归 Gate B;本文件验证引擎层可注入的集成行为:
7
+ // ① dispose 时在途 turn 收敛为明确失败(不挂满 turn 预算)且不触发重试轮
8
+ // (u-z4 disposed 标志衔接:收割 ≠ 瞬时崩溃——boot 1 / create×1 机械证据);
9
+ // ② dispose 全链窗口 = HARVEST_GRACE 量级(与 awaitConnFinalized 同源常量——
10
+ // 行为耗时断言 + 源码常量同源静态断言)。
11
+ // channel 层 close 吞没形态的机制断言在 session-channel-dispose-harvest.test.ts
12
+ // (mock 连接——真实子进程形态下 Node close 事件不可吞没,P-Z3 真实现场归 Gate B)。
13
+
14
+ import * as fs from "node:fs";
15
+ import * as os from "node:os";
16
+ import * as path from "node:path";
17
+ import { fileURLToPath } from "node:url";
18
+
19
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
20
+
21
+ import type { EngineRunResult, RunContext } from "../../../port.ts";
22
+ import type { AgentCallOpts } from "../../../../../orchestration/models/types.ts";
23
+ import {
24
+ ZCODE_APPSERVER_HARVEST_GRACE_MS,
25
+ ZCODE_KILL_GRACE_MS,
26
+ } from "../constants.ts";
27
+ import { ZCODE_APPSERVER_GOLDEN } from "../golden-sample.ts";
28
+ import { ZcodeEngine, type ZcodeEngineDeps } from "../zcode-engine.ts";
29
+
30
+ const FAKE_CLI = fileURLToPath(new URL("./__fixtures__/fake-appserver.mjs", import.meta.url));
31
+ const PROVIDER = "test-provider";
32
+
33
+ const GOLDEN_SESSION_ID = "sess_golden_r3_01";
34
+ /** 挂起场景:send 后零推送——turn 无事件无终态(dispose 收割的形态前提)。 */
35
+ const HANG_PUSHES: string[] = [];
36
+
37
+ const engines: ZcodeEngine[] = [];
38
+ let seq = 0;
39
+ let tmpRoot: string;
40
+ let dataDir: string;
41
+ let v2Path: string;
42
+
43
+ function writeJson(p: string, v: unknown): void {
44
+ fs.mkdirSync(path.dirname(p), { recursive: true });
45
+ fs.writeFileSync(p, JSON.stringify(v, null, 2));
46
+ }
47
+
48
+ beforeEach(() => {
49
+ tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "zcode-eng-dispose-"));
50
+ dataDir = path.join(tmpRoot, "data");
51
+ v2Path = path.join(tmpRoot, "v2.json");
52
+ writeJson(v2Path, {
53
+ provider: { [PROVIDER]: { options: { apiKey: "k", baseURL: "https://t.example" }, models: { m1: {} } } },
54
+ });
55
+ });
56
+
57
+ afterEach(async () => {
58
+ vi.unstubAllEnvs();
59
+ for (const engine of engines.splice(0)) await engine.dispose().catch(() => undefined);
60
+ fs.rmSync(tmpRoot, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
61
+ });
62
+
63
+ interface EngineFixture {
64
+ engine: ZcodeEngine;
65
+ stateFile: string;
66
+ scenarioFile: string;
67
+ workspace: string;
68
+ }
69
+
70
+ function makeEngine(): EngineFixture {
71
+ seq += 1;
72
+ const stateFile = path.join(tmpRoot, `state-${seq}.jsonl`);
73
+ const scenarioFile = path.join(tmpRoot, `scenario-${seq}.json`);
74
+ const workspace = path.join(tmpRoot, `ws-${seq}`);
75
+ writeJson(scenarioFile, {
76
+ createResult: JSON.parse(ZCODE_APPSERVER_GOLDEN.createResponse),
77
+ readResult: JSON.parse(ZCODE_APPSERVER_GOLDEN.readResponse),
78
+ sendPushes: HANG_PUSHES.map((l) => JSON.parse(l) as Record<string, unknown>),
79
+ });
80
+ const deps: ZcodeEngineDeps = {
81
+ engineDataDir: () => dataDir,
82
+ cliPath: FAKE_CLI,
83
+ sources: { v2ConfigPath: v2Path },
84
+ processEnv: {
85
+ PATH: process.env.PATH ?? "",
86
+ XYZ_ZCODE_MODE: "appserver",
87
+ FAKE_STATE_FILE: stateFile,
88
+ FAKE_SESSION_SCENARIO: scenarioFile,
89
+ },
90
+ };
91
+ const engine = new ZcodeEngine(deps);
92
+ engines.push(engine);
93
+ return { engine, stateFile, scenarioFile, workspace };
94
+ }
95
+
96
+ // ── 流水读取 helpers(与 zcode-engine-timeout.test.ts 同款) ──
97
+
98
+ interface StateEvent {
99
+ seq: number;
100
+ ev: string;
101
+ [key: string]: unknown;
102
+ }
103
+
104
+ function readState(file: string): StateEvent[] {
105
+ try {
106
+ return fs
107
+ .readFileSync(file, "utf8")
108
+ .trim()
109
+ .split("\n")
110
+ .filter(Boolean)
111
+ .map((l) => JSON.parse(l) as StateEvent);
112
+ } catch {
113
+ return [];
114
+ }
115
+ }
116
+
117
+ function isRecord(v: unknown): v is Record<string, unknown> {
118
+ return typeof v === "object" && v !== null && !Array.isArray(v);
119
+ }
120
+
121
+ function sentMethods(stateFile: string): string[] {
122
+ return readState(stateFile)
123
+ .map((e) => e.frame)
124
+ .filter((f): f is Record<string, unknown> => isRecord(f) && typeof f.method === "string")
125
+ .map((f) => f.method as string);
126
+ }
127
+
128
+ function bootCount(stateFile: string): number {
129
+ return readState(stateFile).filter((e) => e.ev === "boot").length;
130
+ }
131
+
132
+ /** 轮询流水直到指定 method 出现(在途 turn 已挂的同步点;超时抛错防静默空转)。 */
133
+ async function waitForMethod(stateFile: string, method: string, timeoutMs = 5000): Promise<void> {
134
+ const deadline = Date.now() + timeoutMs;
135
+ while (Date.now() < deadline) {
136
+ if (sentMethods(stateFile).includes(method)) return;
137
+ await new Promise((r) => setTimeout(r, 20));
138
+ }
139
+ throw new Error(`流水 ${timeoutMs}ms 内未出现 ${method}——在途 turn 未建立`);
140
+ }
141
+
142
+ function makeTask(overrides?: Partial<AgentCallOpts>): AgentCallOpts {
143
+ return { prompt: "做点什么", description: "s", model: `${PROVIDER}/m1`, ...overrides };
144
+ }
145
+
146
+ function makeCtx(overrides?: Partial<RunContext>): RunContext {
147
+ return { taskId: "sa-dispose", poolKey: "", ...overrides };
148
+ }
149
+
150
+ // ============================================================
151
+ // dispose 收割(P0-1 U5/D7:在途 turn 收敛 + 不重试 + 窗口量级)
152
+ // ============================================================
153
+
154
+ describe("dispose 收割(P0-1 U5:引擎集成面)", () => {
155
+ it("dispose 时在途 turn 收敛为明确失败且不重试:engine_run_failed、boot 1、create×1(收割≠瞬时崩溃,u-z4 disposed 短路)", async () => {
156
+ // 两 timer 显式关闭(规则 19 opt-out 形态)——排除 timeout 判死路径,
157
+ // 本用例的失败终态只能来自 dispose 收割链(onClose failAllTurns → dispose
158
+ // no-op 幂等;close 吞没变体由 channel 层 mock 测试承载)
159
+ vi.stubEnv("XYZ_ZCODE_TURN_IDLE_TIMEOUT_MS", "0");
160
+ vi.stubEnv("XYZ_ZCODE_TURN_MAX_TIMEOUT_MS", "0");
161
+ const f = makeEngine();
162
+ const runPromise: Promise<EngineRunResult> = f.engine.run(makeTask({ cwd: f.workspace }), makeCtx());
163
+ await waitForMethod(f.stateFile, "session/send"); // 在途 turn 已建立
164
+
165
+ const disposeStartedAt = Date.now();
166
+ await f.engine.dispose();
167
+ const disposeMs = Date.now() - disposeStartedAt;
168
+
169
+ // run promise 在 dispose 窗口内落定(收割生效的结构性证据——5s 保护超时)
170
+ const r = await Promise.race([
171
+ runPromise,
172
+ new Promise<never>((_, rej) =>
173
+ setTimeout(() => rej(new Error("run promise 未在 dispose 后 5s 内落定——收割未生效")), 5000),
174
+ ),
175
+ ]);
176
+ expect(r.outcome.error).toMatch(/^engine_run_failed:/);
177
+ // 收割错误带连接层崩溃 reason(close 正常到达形态:onClose 主路径先收割)
178
+ expect(r.outcome.error).toContain("进程退出");
179
+
180
+ // 收割 ≠ 瞬时崩溃:无重试轮、无惰性重建(boot 1 / create×1 机械证据)
181
+ const methods = sentMethods(f.stateFile);
182
+ expect(methods.filter((m) => m === "session/create").length).toBe(1);
183
+ expect(bootCount(f.stateFile)).toBe(1);
184
+
185
+ // 窗口量级:dispose 全链不超 kill 链 + HARVEST_GRACE + 进程调度余量——
186
+ // 收割在 grace 量级窗口闭合,而非挂满 turn 预算(30min/60min 量级差异)
187
+ expect(disposeMs).toBeLessThan(ZCODE_KILL_GRACE_MS + ZCODE_APPSERVER_HARVEST_GRACE_MS + 2000);
188
+ }, 30_000);
189
+
190
+ it("grace 窗口量级一致性:dispose 收割 race 窗口与 awaitConnFinalized 同源 ZCODE_APPSERVER_HARVEST_GRACE_MS(验收⑤静态断言)", async () => {
191
+ const engineSrc = fs.readFileSync(
192
+ fileURLToPath(new URL("../zcode-engine.ts", import.meta.url)),
193
+ "utf8",
194
+ );
195
+ // awaitConnFinalized:close 永不到达时同量级兜底(u-z2 先例)
196
+ expect(engineSrc).toContain("setTimeout(() => finish(), ZCODE_APPSERVER_HARVEST_GRACE_MS)");
197
+ // shutdownRuntimeAndDisposeChannel:dispose 收割 race 窗口(本单元挂接点)
198
+ expect(engineSrc).toContain("delayResolved(ZCODE_APPSERVER_HARVEST_GRACE_MS");
199
+ // channel dispose 收割本体是同步操作(无自有 timer)——窗口归引擎 race 单点
200
+ const channelSrc = fs.readFileSync(
201
+ fileURLToPath(new URL("../session-channel.ts", import.meta.url)),
202
+ "utf8",
203
+ );
204
+ const disposeBody = channelSrc.slice(
205
+ channelSrc.indexOf(" dispose(): void {"),
206
+ channelSrc.indexOf(" /** 连接死亡收割"),
207
+ );
208
+ expect(disposeBody).not.toContain("setTimeout");
209
+ });
210
+ });