@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
@@ -0,0 +1,321 @@
1
+ // appserver-launcher.test.ts —— wrapper fs 拦截语义 + 落盘幂等/覆盖 + exit 面。
2
+ //
3
+ // 防线背景:wrapper 的失效模式是静默的(曾出过首调走未赋值 __origReadFileSync
4
+ // 导致拦截整体失效的 bug——56a49ad4c),本文件用「落盘产物 + 探针 fake CLI」的
5
+ // 真进程集成形态锚定核心语义:v2 注入优先合并、existsSync 拦截(GUI-only 宿主)、
6
+ // encoding 归一、no-patch 分支、透传不受影响。
7
+
8
+ import * as childProcess from "node:child_process";
9
+ import * as fs from "node:fs";
10
+ import * as os from "node:os";
11
+ import * as path from "node:path";
12
+
13
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
14
+
15
+ import {
16
+ APPSERVER_LAUNCHER_SOURCE,
17
+ ZCODE_APPSERVER_LAUNCHER_NAME,
18
+ ensureAppServerLauncher,
19
+ } from "../appserver-launcher.ts";
20
+
21
+ // 探针 fake CLI:以 wrapper 注入后的 fs 形态读取 CONFIG_PATH 的各种调用形态并
22
+ // 落盘 state(wrapper 对它 import() 启动——与真实 zcode.cjs 同入口形态)
23
+ const PROBE_SOURCE = `'use strict';
24
+ const fs = require('node:fs');
25
+ const path = require('node:path');
26
+ const os = require('node:os');
27
+ const CONFIG = path.join(os.homedir(), '.zcode', 'cli', 'config.json');
28
+ const OTHER = path.join(os.homedir(), '.probe-other.txt');
29
+ const STATE = process.env.ZCODE_PROBE_STATE;
30
+ function grab(v) {
31
+ if (Buffer.isBuffer(v)) return { kind: 'buffer', text: v.toString('utf8') };
32
+ return { kind: typeof v, text: String(v) };
33
+ }
34
+ async function main() {
35
+ const out = {};
36
+ out.existsConfig = fs.existsSync(CONFIG);
37
+ out.existsMissing = fs.existsSync(path.join(os.homedir(), '.no-such-file'));
38
+ out.existsOther = fs.existsSync(OTHER);
39
+ const tries = (label, fn) => {
40
+ try { out[label] = grab(fn()); } catch (err) { out[label] = { kind: 'error', code: err && err.code }; }
41
+ };
42
+ tries('syncUtf8', () => fs.readFileSync(CONFIG, 'utf8'));
43
+ tries('syncUtf8Dash', () => fs.readFileSync(CONFIG, 'utf-8'));
44
+ tries('syncUtf8Obj', () => fs.readFileSync(CONFIG, { encoding: 'utf-8' }));
45
+ tries('syncNoEnc', () => fs.readFileSync(CONFIG));
46
+ tries('syncOther', () => fs.readFileSync(OTHER, 'utf8'));
47
+ out.promiseUtf8Dash = await fs.promises.readFile(CONFIG, 'utf-8').then(
48
+ (v) => grab(v), (err) => ({ kind: 'error', code: err && err.code }));
49
+ fs.writeFileSync(STATE, JSON.stringify(out));
50
+ }
51
+ main().then(() => process.exit(0), (err) => {
52
+ process.stderr.write('probe failed: ' + (err && err.stack || err));
53
+ process.exit(9);
54
+ });
55
+ `;
56
+
57
+ interface ProbeState {
58
+ existsConfig: boolean;
59
+ existsMissing: boolean;
60
+ existsOther: boolean;
61
+ syncUtf8: { kind: string; text?: string; code?: string };
62
+ syncUtf8Dash: { kind: string; text?: string; code?: string };
63
+ syncUtf8Obj: { kind: string; text?: string; code?: string };
64
+ syncNoEnc: { kind: string; text?: string; code?: string };
65
+ syncOther: { kind: string; text?: string; code?: string };
66
+ promiseUtf8Dash: { kind: string; text?: string; code?: string };
67
+ [k: string]: unknown;
68
+ }
69
+
70
+ let tmpRoot: string;
71
+
72
+ interface HomeSpec {
73
+ /** v2 config 原文(与 v2 字段互斥,用于损坏/缺失场景) */
74
+ v2Raw?: string;
75
+ /** v2 config 对象形态 */
76
+ v2?: unknown;
77
+ /** cli config 原文(与 real 字段互斥) */
78
+ realRaw?: string;
79
+ /** cli config 对象形态;undefined = 不布置该文件(GUI-only 宿主) */
80
+ real?: unknown;
81
+ }
82
+
83
+ function setupHome(spec: HomeSpec): { run: () => childProcess.SpawnSyncReturns<string>; state: () => ProbeState } {
84
+ const home = fs.mkdtempSync(path.join(tmpRoot, "home-"));
85
+ const engineDataDir = fs.mkdtempSync(path.join(tmpRoot, "eng-"));
86
+ const cliDir = path.join(home, ".zcode", "cli");
87
+ fs.mkdirSync(cliDir, { recursive: true });
88
+ fs.writeFileSync(path.join(home, ".probe-other.txt"), "OTHER-CONTENT");
89
+ const v2Path = path.join(home, ".zcode", "v2", "config.json");
90
+ fs.mkdirSync(path.dirname(v2Path), { recursive: true });
91
+ if (spec.v2Raw !== undefined) fs.writeFileSync(v2Path, spec.v2Raw);
92
+ else if (spec.v2 !== undefined) fs.writeFileSync(v2Path, JSON.stringify(spec.v2, null, 2));
93
+ const cliConfig = path.join(cliDir, "config.json");
94
+ if (spec.realRaw !== undefined) fs.writeFileSync(cliConfig, spec.realRaw);
95
+ else if (spec.real !== undefined) fs.writeFileSync(cliConfig, JSON.stringify(spec.real, null, 2));
96
+
97
+ const probePath = path.join(tmpRoot, `probe-${path.basename(home)}.cjs`);
98
+ fs.writeFileSync(probePath, PROBE_SOURCE);
99
+ const launcher = ensureAppServerLauncher(engineDataDir);
100
+ const statePath = path.join(tmpRoot, `state-${path.basename(home)}.json`);
101
+
102
+ return {
103
+ run: () =>
104
+ childProcess.spawnSync(process.execPath, [launcher], {
105
+ env: {
106
+ ...process.env,
107
+ HOME: home,
108
+ ZCODE_ENG_CLI_PATH: probePath,
109
+ ZCODE_ENG_V2_CONFIG: v2Path,
110
+ ZCODE_PROBE_STATE: statePath,
111
+ } as NodeJS.ProcessEnv,
112
+ encoding: "utf8",
113
+ timeout: 15_000,
114
+ }),
115
+ state: () => JSON.parse(fs.readFileSync(statePath, "utf8")) as ProbeState,
116
+ };
117
+ }
118
+
119
+ const providerEntry = (apiKey: string) => ({ options: { apiKey, baseURL: "https://example.test/v1" } });
120
+
121
+ beforeEach(() => {
122
+ tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "zcode-launcher-test-"));
123
+ });
124
+
125
+ afterEach(() => {
126
+ fs.rmSync(tmpRoot, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
127
+ });
128
+
129
+ describe("wrapper 合并语义(v2 注入优先)", () => {
130
+ it("同 id 时 v2 条目整条优先于真实文件(等价复刻 GUI 直传:v2 权威),real 独有条目保留", () => {
131
+ const h = setupHome({
132
+ v2: { provider: { shared: providerEntry("v2-new-key") } },
133
+ real: {
134
+ provider: { shared: providerEntry("real-stale-key"), realOnly: providerEntry("real-key") },
135
+ model: { main: "shared/model-a" },
136
+ },
137
+ });
138
+ const res = h.run();
139
+ expect(res.status).toBe(0);
140
+ const cfg = JSON.parse(h.state().syncUtf8.text as string);
141
+ expect(cfg.provider.shared.options.apiKey).toBe("v2-new-key");
142
+ expect(cfg.provider.realOnly.options.apiKey).toBe("real-key");
143
+ expect(cfg.model.main).toBe("shared/model-a");
144
+ });
145
+
146
+ it("real 无 model.main 时以 v2.model.main / FALLBACK 兜底注入", () => {
147
+ const h = setupHome({
148
+ v2: { provider: { p1: providerEntry("k1") } },
149
+ real: { provider: { p2: providerEntry("k2") } },
150
+ });
151
+ const res = h.run();
152
+ expect(res.status).toBe(0);
153
+ const cfg = JSON.parse(h.state().syncUtf8.text as string);
154
+ expect(cfg.model.main).toBe("builtin:bigmodel-coding-plan/GLM-5.3");
155
+ });
156
+
157
+ it("v2.model.main 存在时优先于 FALLBACK", () => {
158
+ const h = setupHome({
159
+ v2: { provider: { p1: providerEntry("k1") }, model: { main: "p1/model-x" } },
160
+ real: {},
161
+ });
162
+ const res = h.run();
163
+ expect(res.status).toBe(0);
164
+ const cfg = JSON.parse(h.state().syncUtf8.text as string);
165
+ expect(cfg.model.main).toBe("p1/model-x");
166
+ });
167
+ });
168
+
169
+ describe("existsSync 拦截(GUI-only 宿主:cli config 不存在)", () => {
170
+ it("真实文件缺失时 existsSync 返回 true 且读取拿到注入配置(短路门不复 exist = 注入不可见)", () => {
171
+ const h = setupHome({
172
+ v2: { provider: { p1: providerEntry("k1") } },
173
+ // real 不布置 → cli/config.json 不存在
174
+ });
175
+ const res = h.run();
176
+ expect(res.status).toBe(0);
177
+ const st = h.state();
178
+ expect(st.existsConfig).toBe(true);
179
+ const cfg = JSON.parse(st.syncUtf8.text as string);
180
+ expect(cfg.provider.p1.options.apiKey).toBe("k1");
181
+ });
182
+ });
183
+
184
+ describe("no-patch 分支(无凭据可注入 → 原生行为透出)", () => {
185
+ it("v2 无带 apiKey 的 provider:不 patch,读原始内容、existsSync 原生", () => {
186
+ const h = setupHome({
187
+ v2: { provider: { p1: { options: { apiKey: "" } } } },
188
+ realRaw: '{"provider":{"r1":{"options":{"apiKey":"real"}}}}',
189
+ });
190
+ const res = h.run();
191
+ expect(res.status).toBe(0);
192
+ const st = h.state();
193
+ expect(st.syncUtf8.text).toBe('{"provider":{"r1":{"options":{"apiKey":"real"}}}}');
194
+ expect(st.existsConfig).toBe(true);
195
+ });
196
+
197
+ it("v2 文件缺失(ENOENT,未登录):静默 no-patch,stderr 无 v2 读取失败", () => {
198
+ const h = setupHome({ realRaw: '{"provider":{}}' });
199
+ const res = h.run();
200
+ expect(res.status).toBe(0);
201
+ expect(res.stderr).not.toContain("v2 config 读取失败");
202
+ const st = h.state();
203
+ expect(st.syncUtf8.text).toBe('{"provider":{}}');
204
+ });
205
+
206
+ it("v2 损坏(非 ENOENT):no-patch 且 stderr 出声(与「未登录」区分)", () => {
207
+ const h = setupHome({ v2Raw: "{broken-json", realRaw: '{"provider":{}}' });
208
+ const res = h.run();
209
+ expect(res.status).toBe(0);
210
+ expect(res.stderr).toContain("v2 config 读取失败");
211
+ expect(h.state().syncUtf8.text).toBe('{"provider":{}}');
212
+ });
213
+
214
+ it("no-patch 且真实文件不存在:existsSync 原生 false、读取 ENOENT(原生报错透出)", () => {
215
+ const h = setupHome({ v2Raw: "{broken" });
216
+ const res = h.run();
217
+ expect(res.status).toBe(0);
218
+ const st = h.state();
219
+ expect(st.existsConfig).toBe(false);
220
+ expect(st.syncUtf8.code).toBe("ENOENT");
221
+ });
222
+ });
223
+
224
+ describe("调用形态(encoding 归一与透传)", () => {
225
+ const base = { v2: { provider: { p1: providerEntry("k1") } }, real: {} };
226
+
227
+ it("'utf8' / 'utf-8' / {encoding:'utf-8'} 均返回 string;无 encoding 返回 Buffer(内容一致)", () => {
228
+ const h = setupHome(base);
229
+ const res = h.run();
230
+ expect(res.status).toBe(0);
231
+ const st = h.state();
232
+ for (const key of ["syncUtf8", "syncUtf8Dash", "syncUtf8Obj", "promiseUtf8Dash"] as const) {
233
+ expect(st[key].kind, key).toBe("string");
234
+ }
235
+ expect(st.syncNoEnc.kind).toBe("buffer");
236
+ const merged = JSON.parse(st.syncUtf8.text as string);
237
+ expect(JSON.parse(st.syncNoEnc.text as string)).toEqual(merged);
238
+ });
239
+
240
+ it("非 CONFIG_PATH 读取与 existsSync 穿透不受影响", () => {
241
+ const h = setupHome(base);
242
+ const res = h.run();
243
+ expect(res.status).toBe(0);
244
+ const st = h.state();
245
+ expect(st.syncOther.text).toBe("OTHER-CONTENT");
246
+ expect(st.existsOther).toBe(true);
247
+ expect(st.existsMissing).toBe(false);
248
+ });
249
+ });
250
+
251
+ describe("exit 面", () => {
252
+ it("ZCODE_ENG_CLI_PATH 缺失:exit 2 + stderr 含恢复指引", () => {
253
+ const home = fs.mkdtempSync(path.join(tmpRoot, "home-"));
254
+ const engineDataDir = fs.mkdtempSync(path.join(tmpRoot, "eng-"));
255
+ const launcher = ensureAppServerLauncher(engineDataDir);
256
+ // 密封性:本测试自身可能跑在 app-server 宿主链路下(env 带 ZCODE_ENG_CLI_PATH),
257
+ // 不剥掉则「缺失」前置不成立,launcher 会拿到真 CLI 走正常启动路径(exit 1)而非配置错误 exit 2
258
+ const { ZCODE_ENG_CLI_PATH: _ambient, ...hermeticEnv } = process.env;
259
+ const res = childProcess.spawnSync(process.execPath, [launcher], {
260
+ env: { ...hermeticEnv, HOME: home, ZCODE_ENG_V2_CONFIG: path.join(home, "v2.json") },
261
+ encoding: "utf8",
262
+ timeout: 15_000,
263
+ });
264
+ expect(res.status).toBe(2);
265
+ expect(res.stderr).toContain("ZCODE_ENG_CLI_PATH");
266
+ });
267
+
268
+ it("CLI_PATH 指向不存在文件:exit 3 + stderr 加载失败", () => {
269
+ const home = fs.mkdtempSync(path.join(tmpRoot, "home-"));
270
+ const engineDataDir = fs.mkdtempSync(path.join(tmpRoot, "eng-"));
271
+ const launcher = ensureAppServerLauncher(engineDataDir);
272
+ const res = childProcess.spawnSync(process.execPath, [launcher], {
273
+ env: {
274
+ ...process.env,
275
+ HOME: home,
276
+ ZCODE_ENG_CLI_PATH: path.join(tmpRoot, "no-such-cli.cjs"),
277
+ ZCODE_ENG_V2_CONFIG: path.join(home, "v2.json"),
278
+ },
279
+ encoding: "utf8",
280
+ timeout: 15_000,
281
+ });
282
+ expect(res.status).toBe(3);
283
+ expect(res.stderr).toContain("加载失败");
284
+ });
285
+ });
286
+
287
+ describe("落盘端(ensureAppServerLauncher)", () => {
288
+ it("首调落盘产物内容与 APPSERVER_LAUNCHER_SOURCE 一致,路径含 engines/zcode/", () => {
289
+ const engineDataDir = fs.mkdtempSync(path.join(tmpRoot, "eng-"));
290
+ const file = ensureAppServerLauncher(engineDataDir);
291
+ expect(path.basename(file)).toBe(ZCODE_APPSERVER_LAUNCHER_NAME);
292
+ expect(file).toContain(path.join("engines", "zcode"));
293
+ expect(fs.readFileSync(file, "utf8")).toBe(APPSERVER_LAUNCHER_SOURCE);
294
+ });
295
+
296
+ it("重复调用幂等:返回同一路径,内容不变", () => {
297
+ const engineDataDir = fs.mkdtempSync(path.join(tmpRoot, "eng-"));
298
+ const a = ensureAppServerLauncher(engineDataDir);
299
+ const b = ensureAppServerLauncher(engineDataDir);
300
+ expect(b).toBe(a);
301
+ expect(fs.readFileSync(b, "utf8")).toBe(APPSERVER_LAUNCHER_SOURCE);
302
+ });
303
+
304
+ it("陈旧内容(升级后旧 wrapper)被覆盖刷新", () => {
305
+ const engineDataDir = fs.mkdtempSync(path.join(tmpRoot, "eng-"));
306
+ const dir = path.join(engineDataDir, "engines", "zcode");
307
+ fs.mkdirSync(dir, { recursive: true });
308
+ fs.writeFileSync(path.join(dir, ZCODE_APPSERVER_LAUNCHER_NAME), "// stale old wrapper");
309
+ const file = ensureAppServerLauncher(engineDataDir);
310
+ expect(fs.readFileSync(file, "utf8")).toBe(APPSERVER_LAUNCHER_SOURCE);
311
+ });
312
+
313
+ it("落盘失败包装为含恢复动作的错误(目录只读)", () => {
314
+ const engineDataDir = fs.mkdtempSync(path.join(tmpRoot, "eng-"));
315
+ // 预置同名目录占位目标文件路径,使 renameSync 前的 writeFileSync 落在
316
+ // 目录形态上失败(EISDIR)——覆盖 recovery 文案分支
317
+ const dir = path.join(engineDataDir, "engines", "zcode");
318
+ fs.mkdirSync(path.join(dir, ZCODE_APPSERVER_LAUNCHER_NAME), { recursive: true });
319
+ expect(() => ensureAppServerLauncher(engineDataDir)).toThrow(/落盘失败[\s\S]*恢复/);
320
+ });
321
+ });
@@ -63,7 +63,6 @@ interface FakeHandle {
63
63
  conn: AppServerConnection;
64
64
  stateFile: string;
65
65
  stderrLog: string;
66
- homeDir: string;
67
66
  cwd: string;
68
67
  }
69
68
 
@@ -88,10 +87,10 @@ function makeConnection(opts: FakeOpts = {}): FakeHandle {
88
87
  connSeq += 1;
89
88
  const stateFile = join(TMP, `state-${connSeq}.jsonl`);
90
89
  const stderrLog = join(TMP, `stderr-${connSeq}.log`);
91
- const homeDir = join(TMP, `home-${connSeq}`);
92
90
  const cwd = join(TMP, `cwd-${connSeq}`);
93
91
  const base: NodeJS.ProcessEnv = {
94
92
  PATH: process.env.PATH ?? "",
93
+ HOME: "/fake-host-home",
95
94
  FAKE_STATE_FILE: stateFile,
96
95
  ...(opts.stderrLines ? { FAKE_STDERR: "1" } : {}),
97
96
  ...(opts.extraKeys ? { FAKE_EXTRA_KEYS: "1" } : {}),
@@ -101,12 +100,12 @@ function makeConnection(opts: FakeOpts = {}): FakeHandle {
101
100
  const conn = new AppServerConnection({
102
101
  cliPath: FAKE_CLI,
103
102
  cwd,
104
- env: buildAppServerEnv(homeDir, base),
103
+ env: buildAppServerEnv(base),
105
104
  stderrLogPath: stderrLog,
106
105
  ...(opts.reverseHandlers ? { reverseHandlers: opts.reverseHandlers } : {}),
107
106
  });
108
107
  CONNECTIONS.push(conn);
109
- return { conn, stateFile, stderrLog, homeDir, cwd };
108
+ return { conn, stateFile, stderrLog, cwd };
110
109
  }
111
110
 
112
111
  afterEach(async () => {
@@ -171,7 +170,7 @@ describe("四帧型分发(NDJSON,无 jsonrpc 字段)", () => {
171
170
  const { conn, stateFile } = makeConnection();
172
171
  await conn.request("test/echo", { a: 1 });
173
172
  const frames = readState(stateFile)
174
- .map((e) => e.frame)
173
+ .map((e) => (e as Record<string, unknown>).frame)
175
174
  .filter((f): f is Record<string, unknown> => isRecord(f) && f.method === "test/echo");
176
175
  expect(frames).toHaveLength(1);
177
176
  expect(Object.keys(frames[0]).sort()).toEqual(["id", "method", "params"]);
@@ -254,7 +253,7 @@ describe("请求-应答 id 关联", () => {
254
253
  expect(b).toEqual({ who: "b", delayMs: 30 });
255
254
  expect(c).toEqual({ who: "c" });
256
255
  const ids = readState(stateFile)
257
- .map((e) => e.frame)
256
+ .map((e) => (e as Record<string, unknown>).frame)
258
257
  .filter((f): f is { id: number } => isRecord(f) && typeof f.method === "string" && typeof f.id === "number")
259
258
  .map((f) => f.id);
260
259
  expect(ids).toHaveLength(3);
@@ -297,10 +296,10 @@ describe("反向请求应答(D9)", () => {
297
296
  ]);
298
297
  // 反向应答帧出站精确键集 {id, result}(无 jsonrpc、无未知键)
299
298
  const answerFrame = readState(stateFile)
300
- .map((e) => e.frame)
299
+ .map((e) => (e as Record<string, unknown>).frame)
301
300
  .find((f): f is Record<string, unknown> => isRecord(f) && typeof f.id === "string");
302
301
  expect(answerFrame).toBeDefined();
303
- expect(Object.keys(answerFrame).sort()).toEqual(["id", "result"]);
302
+ expect(Object.keys(answerFrame!).sort()).toEqual(["id", "result"]);
304
303
  });
305
304
 
306
305
  it("未知反向请求 → 回 {id, result:{}}(不答会 15s 超时断连,旧实测 -32022)", async () => {
@@ -385,9 +384,9 @@ describe("stderr tee 落盘", () => {
385
384
 
386
385
  // ------------------------------------------- env 惯例(构造注入面)
387
386
 
388
- describe("env 惯例(沿用 launcher 惯例的参数化注入)", () => {
389
- it("buildAppServerEnv:HOME 隔离 + 遥测关闭 + nesting guard 剥离/注入(fake 侧 env 快照)", async () => {
390
- const { conn, stateFile, homeDir } = makeConnection({ polluteNested: true });
387
+ describe("env 惯例(共享宿主 HOME 形态)", () => {
388
+ it("buildAppServerEnv:HOME 正向透传(共享宿主)+ 遥测关闭 + nesting guard 剥离/注入(fake 侧 env 快照)", async () => {
389
+ const { conn, stateFile } = makeConnection({ polluteNested: true });
391
390
  await conn.request("test/echo", {});
392
391
  const envEvents = readState(stateFile).filter((e) => e.ev === "env");
393
392
  expect(envEvents).toHaveLength(1);
@@ -397,7 +396,9 @@ describe("env 惯例(沿用 launcher 惯例的参数化注入)", () => {
397
396
  nested?: string;
398
397
  unifiedNested?: string;
399
398
  };
400
- expect(snap.home).toBe(homeDir);
399
+ // 共享宿主 HOME 契约(2026-09 起):base env 携带的宿主 HOME 值原样透传到子进程
400
+ // env(不覆写、不剥离——db/plugins/MCP 全继承宿主 HOME)
401
+ expect(snap.home).toBe("/fake-host-home");
401
402
  expect(snap.telemetry).toBe("false");
402
403
  expect(snap.nested).toBeUndefined();
403
404
  expect(snap.unifiedNested).toBe("1");
@@ -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
- });