@zhushanwen/subagent-core 0.2.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 (330) hide show
  1. package/README.md +114 -0
  2. package/dist/chunk-3VOERJPJ.js +22 -0
  3. package/dist/chunk-A4IVZWVX.js +31 -0
  4. package/dist/execution/engine/engines/zcode/constants.cjs +54 -0
  5. package/dist/execution/engine/engines/zcode/constants.d.cts +31 -0
  6. package/dist/execution/engine/engines/zcode/constants.d.ts +31 -0
  7. package/dist/execution/engine/engines/zcode/constants.js +22 -0
  8. package/dist/execution/engine/engines/zcode/reader.cjs +276 -0
  9. package/dist/execution/engine/engines/zcode/reader.d.cts +20 -0
  10. package/dist/execution/engine/engines/zcode/reader.d.ts +20 -0
  11. package/dist/execution/engine/engines/zcode/reader.js +239 -0
  12. package/dist/execution/engine/paths.cjs +55 -0
  13. package/dist/execution/engine/paths.d.cts +8 -0
  14. package/dist/execution/engine/paths.d.ts +8 -0
  15. package/dist/execution/engine/paths.js +26 -0
  16. package/dist/execution/relay-env.cjs +62 -0
  17. package/dist/execution/relay-env.d.cts +33 -0
  18. package/dist/execution/relay-env.d.ts +33 -0
  19. package/dist/execution/relay-env.js +20 -0
  20. package/dist/index.cjs +1852 -0
  21. package/dist/index.d.cts +1184 -0
  22. package/dist/index.d.ts +1184 -0
  23. package/dist/index.js +1810 -0
  24. package/dist/types-BxyAidGf.d.cts +875 -0
  25. package/dist/types-BxyAidGf.d.ts +875 -0
  26. package/package.json +99 -0
  27. package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +142 -0
  28. package/src/__tests__/append-system-prompt-assembly.test.ts +242 -0
  29. package/src/__tests__/builtin-workflows-structure.test.ts +180 -0
  30. package/src/__tests__/derive-closed-display-parity.test.ts +93 -0
  31. package/src/__tests__/fr4-get-state-handshake.test.ts +125 -0
  32. package/src/__tests__/fr8-orphan-recovery.test.ts +259 -0
  33. package/src/__tests__/m2-append-content-probe.test.ts +73 -0
  34. package/src/__tests__/manifest-store.test.ts +368 -0
  35. package/src/__tests__/record-store-cache.test.ts +335 -0
  36. package/src/__tests__/record-store-index.test.ts +560 -0
  37. package/src/__tests__/record-store-last-line.test.ts +129 -0
  38. package/src/__tests__/review-fix-loop-script.test.ts +288 -0
  39. package/src/__tests__/review-fix-loop-utils.test.ts +2377 -0
  40. package/src/__tests__/robustness-low-batch1.test.ts +286 -0
  41. package/src/__tests__/robustness-medium-batch1.test.ts +69 -0
  42. package/src/__tests__/robustness-medium-batch2.test.ts +65 -0
  43. package/src/__tests__/robustness-medium-batch3.test.ts +30 -0
  44. package/src/__tests__/robustness-medium-batch4.test.ts +358 -0
  45. package/src/__tests__/session-runner.test.ts +69 -0
  46. package/src/__tests__/smoke.test.ts +9 -0
  47. package/src/__tests__/stream-sink.test.ts +203 -0
  48. package/src/core/__tests__/host-services.test.ts +144 -0
  49. package/src/core/__tests__/logger.test.ts +78 -0
  50. package/src/core/__tests__/notify-ports.test.ts +104 -0
  51. package/src/core/host-services.ts +97 -0
  52. package/src/core/logger.ts +47 -0
  53. package/src/core/notify-ports.ts +126 -0
  54. package/src/execution/__tests__/__fixtures__/notifier-golden-snapshots.json +53 -0
  55. package/src/execution/__tests__/__fixtures__/truncline.snapshot.json +1 -0
  56. package/src/execution/__tests__/agent-result-mapper.test.ts +150 -0
  57. package/src/execution/__tests__/alive-store.test.ts +147 -0
  58. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +490 -0
  59. package/src/execution/__tests__/channel-registry-handshake.test.ts +242 -0
  60. package/src/execution/__tests__/chat-engine-routing.test.ts +610 -0
  61. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +190 -0
  62. package/src/execution/__tests__/concurrency-pool.test.ts +250 -0
  63. package/src/execution/__tests__/config.test.ts +302 -0
  64. package/src/execution/__tests__/delivery-methods.test.ts +424 -0
  65. package/src/execution/__tests__/dialog-queue.test.ts +299 -0
  66. package/src/execution/__tests__/epipe-fallback.test.ts +241 -0
  67. package/src/execution/__tests__/execute-and-await-worktree.test.ts +243 -0
  68. package/src/execution/__tests__/execute-nesting.test.ts +323 -0
  69. package/src/execution/__tests__/execute-options-mapper.test.ts +200 -0
  70. package/src/execution/__tests__/execution-record.test.ts +1394 -0
  71. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +171 -0
  72. package/src/execution/__tests__/finalize-record.test.ts +367 -0
  73. package/src/execution/__tests__/finalized-marker.test.ts +104 -0
  74. package/src/execution/__tests__/format-schema-instruction.test.ts +169 -0
  75. package/src/execution/__tests__/gc-timer.test.ts +184 -0
  76. package/src/execution/__tests__/get-record-for-action-restart.test.ts +254 -0
  77. package/src/execution/__tests__/helpers/mock-extension-api.ts +30 -0
  78. package/src/execution/__tests__/helpers/spawn-mock.ts +242 -0
  79. package/src/execution/__tests__/host-mode.test.ts +87 -0
  80. package/src/execution/__tests__/lifecycle-manager-lock.test.ts +211 -0
  81. package/src/execution/__tests__/lifecycle-manager.test.ts +383 -0
  82. package/src/execution/__tests__/lifecycle-predicates.test.ts +116 -0
  83. package/src/execution/__tests__/manifest-parentid.test.ts +117 -0
  84. package/src/execution/__tests__/model-resolver.test.ts +465 -0
  85. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +287 -0
  86. package/src/execution/__tests__/nested-visibility.test.ts +325 -0
  87. package/src/execution/__tests__/output-collector.test.ts +358 -0
  88. package/src/execution/__tests__/path-encoding.test.ts +104 -0
  89. package/src/execution/__tests__/pi-invocation.test.ts +134 -0
  90. package/src/execution/__tests__/record-store.test.ts +1057 -0
  91. package/src/execution/__tests__/records-cwd-isolation.test.ts +91 -0
  92. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +339 -0
  93. package/src/execution/__tests__/recursive-visibility-env.test.ts +273 -0
  94. package/src/execution/__tests__/relay-env.test.ts +42 -0
  95. package/src/execution/__tests__/resource-policy.test.ts +109 -0
  96. package/src/execution/__tests__/rpc-mode.test.ts +89 -0
  97. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +267 -0
  98. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +253 -0
  99. package/src/execution/__tests__/run-spawn-edges.test.ts +687 -0
  100. package/src/execution/__tests__/run-spawn-integration.test.ts +933 -0
  101. package/src/execution/__tests__/run-spawn-resume.test.ts +322 -0
  102. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +196 -0
  103. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +199 -0
  104. package/src/execution/__tests__/session-context-resolver.test.ts +167 -0
  105. package/src/execution/__tests__/session-file-gc.test.ts +293 -0
  106. package/src/execution/__tests__/session-reconstructor.test.ts +379 -0
  107. package/src/execution/__tests__/session-runner-epipe.test.ts +178 -0
  108. package/src/execution/__tests__/session-runner-schema-env.test.ts +350 -0
  109. package/src/execution/__tests__/spawn-args.test.ts +445 -0
  110. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +189 -0
  111. package/src/execution/__tests__/spawn-event-adapter.test.ts +167 -0
  112. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +207 -0
  113. package/src/execution/__tests__/spawned-children.test.ts +92 -0
  114. package/src/execution/__tests__/start-sync-model-guard.test.ts +150 -0
  115. package/src/execution/__tests__/stdin-writer.test.ts +462 -0
  116. package/src/execution/__tests__/stream-sink-retirement.test.ts +261 -0
  117. package/src/execution/__tests__/subagent-service-abort.test.ts +60 -0
  118. package/src/execution/__tests__/subagent-service-message-close.test.ts +629 -0
  119. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +180 -0
  120. package/src/execution/__tests__/subagent-service.test.ts +788 -0
  121. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +310 -0
  122. package/src/execution/__tests__/subprocess-agent-runner.test.ts +612 -0
  123. package/src/execution/__tests__/temp-prompt.test.ts +53 -0
  124. package/src/execution/__tests__/timeout-integration.test.ts +615 -0
  125. package/src/execution/__tests__/tombstone-store.test.ts +73 -0
  126. package/src/execution/__tests__/turn-limiter-semantics.test.ts +194 -0
  127. package/src/execution/__tests__/turn-limiter.test.ts +65 -0
  128. package/src/execution/__tests__/ui-channels.test.ts +187 -0
  129. package/src/execution/__tests__/ui-interaction-model.test.ts +67 -0
  130. package/src/execution/__tests__/ui-request-handler-factory.test.ts +369 -0
  131. package/src/execution/__tests__/ui-request-handler.test.ts +204 -0
  132. package/src/execution/__tests__/ui-request-observability.test.ts +113 -0
  133. package/src/execution/__tests__/ui-request-queue.test.ts +133 -0
  134. package/src/execution/__tests__/worktree-manager.test.ts +633 -0
  135. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +229 -0
  136. package/src/execution/__tests__/worktree-reconcile.integration.test.ts +181 -0
  137. package/src/execution/__tests__/worktree-registry.test.ts +199 -0
  138. package/src/execution/agent-registry.ts +199 -0
  139. package/src/execution/agent-result-mapper.ts +90 -0
  140. package/src/execution/alive-store.ts +119 -0
  141. package/src/execution/argv-mirror.ts +113 -0
  142. package/src/execution/best-effort.ts +38 -0
  143. package/src/execution/channel-registry-access.ts +144 -0
  144. package/src/execution/concurrency-pool.ts +116 -0
  145. package/src/execution/config.ts +165 -0
  146. package/src/execution/dialog-queue.ts +330 -0
  147. package/src/execution/engine/__tests__/common/data-dir.test.ts +78 -0
  148. package/src/execution/engine/__tests__/common/errors.test.ts +132 -0
  149. package/src/execution/engine/__tests__/common/event-journal.test.ts +177 -0
  150. package/src/execution/engine/__tests__/common/kill-chain.test.ts +192 -0
  151. package/src/execution/engine/__tests__/common/nesting-guard.test.ts +81 -0
  152. package/src/execution/engine/__tests__/common/persona-router.test.ts +123 -0
  153. package/src/execution/engine/__tests__/common/pool-manager.test.ts +154 -0
  154. package/src/execution/engine/__tests__/common/schema-emulation.test.ts +128 -0
  155. package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +28 -0
  156. package/src/execution/engine/__tests__/conformance/agent-event-invariants.ts +141 -0
  157. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +109 -0
  158. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +101 -0
  159. package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +77 -0
  160. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +104 -0
  161. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +201 -0
  162. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +76 -0
  163. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +79 -0
  164. package/src/execution/engine/__tests__/engine-discovery.test.ts +87 -0
  165. package/src/execution/engine/__tests__/model-prompt.test.ts +197 -0
  166. package/src/execution/engine/__tests__/paths.test.ts +39 -0
  167. package/src/execution/engine/__tests__/registry.test.ts +120 -0
  168. package/src/execution/engine/__tests__/routing.test.ts +231 -0
  169. package/src/execution/engine/common/data-dir.ts +66 -0
  170. package/src/execution/engine/common/errors.ts +183 -0
  171. package/src/execution/engine/common/event-journal.ts +254 -0
  172. package/src/execution/engine/common/journal-replay.ts +62 -0
  173. package/src/execution/engine/common/kill-chain.ts +221 -0
  174. package/src/execution/engine/common/nesting-guard.ts +50 -0
  175. package/src/execution/engine/common/persona-router.ts +108 -0
  176. package/src/execution/engine/common/pool-manager.ts +226 -0
  177. package/src/execution/engine/common/schema-emulation.ts +189 -0
  178. package/src/execution/engine/common/session-view-projection.ts +51 -0
  179. package/src/execution/engine/engine-discovery.ts +65 -0
  180. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +469 -0
  181. package/src/execution/engine/engines/pi/__tests__/reader.test.ts +155 -0
  182. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +164 -0
  183. package/src/execution/engine/engines/pi/pi-engine.ts +415 -0
  184. package/src/execution/engine/engines/pi/reader.ts +48 -0
  185. package/src/execution/engine/engines/pi/registration.ts +35 -0
  186. package/src/execution/engine/engines/pi/task-spec-mapper.ts +100 -0
  187. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +39 -0
  188. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +150 -0
  189. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +246 -0
  190. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +228 -0
  191. package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +210 -0
  192. package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +71 -0
  193. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +127 -0
  194. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +580 -0
  195. package/src/execution/engine/engines/zcode/constants.ts +43 -0
  196. package/src/execution/engine/engines/zcode/golden-sample.ts +39 -0
  197. package/src/execution/engine/engines/zcode/launcher.ts +161 -0
  198. package/src/execution/engine/engines/zcode/parser.ts +436 -0
  199. package/src/execution/engine/engines/zcode/preparer.ts +363 -0
  200. package/src/execution/engine/engines/zcode/reader.ts +381 -0
  201. package/src/execution/engine/engines/zcode/registration.ts +37 -0
  202. package/src/execution/engine/engines/zcode/zcode-engine.ts +658 -0
  203. package/src/execution/engine/host-task-spec.ts +47 -0
  204. package/src/execution/engine/model-prompt.ts +158 -0
  205. package/src/execution/engine/paths.ts +42 -0
  206. package/src/execution/engine/port.ts +153 -0
  207. package/src/execution/engine/registry.ts +133 -0
  208. package/src/execution/engine/routing.ts +218 -0
  209. package/src/execution/engine/types.ts +309 -0
  210. package/src/execution/execute-options-mapper.ts +115 -0
  211. package/src/execution/execution-record.ts +984 -0
  212. package/src/execution/finalize-record.ts +254 -0
  213. package/src/execution/finalized-marker.ts +70 -0
  214. package/src/execution/get-state-handshake.ts +104 -0
  215. package/src/execution/host-mode.ts +56 -0
  216. package/src/execution/idle-gc.ts +47 -0
  217. package/src/execution/lifecycle-manager.ts +513 -0
  218. package/src/execution/lifecycle-predicates.ts +65 -0
  219. package/src/execution/manifest-store.ts +256 -0
  220. package/src/execution/model-config-service.ts +250 -0
  221. package/src/execution/model-resolver.ts +248 -0
  222. package/src/execution/notifier.ts +372 -0
  223. package/src/execution/notify-ledger.ts +580 -0
  224. package/src/execution/output-collector.ts +228 -0
  225. package/src/execution/path-encoding.ts +60 -0
  226. package/src/execution/pi-invocation.ts +120 -0
  227. package/src/execution/record-entry.ts +132 -0
  228. package/src/execution/record-store.ts +1265 -0
  229. package/src/execution/relay-env.ts +37 -0
  230. package/src/execution/session-context-resolver.ts +64 -0
  231. package/src/execution/session-file-gc.ts +120 -0
  232. package/src/execution/session-pending.ts +170 -0
  233. package/src/execution/session-reconstructor.ts +678 -0
  234. package/src/execution/session-runner.ts +1789 -0
  235. package/src/execution/sessions-index.ts +304 -0
  236. package/src/execution/spawn-event-adapter.ts +363 -0
  237. package/src/execution/stdin-writer.ts +198 -0
  238. package/src/execution/stream-sink.ts +126 -0
  239. package/src/execution/subagent-service.ts +2268 -0
  240. package/src/execution/subprocess-agent-runner.ts +317 -0
  241. package/src/execution/temp-prompt.ts +62 -0
  242. package/src/execution/tombstone-store.ts +72 -0
  243. package/src/execution/turn-limiter.ts +102 -0
  244. package/src/execution/types.ts +1023 -0
  245. package/src/execution/ui-channels.ts +216 -0
  246. package/src/execution/ui-interaction-model.ts +48 -0
  247. package/src/execution/ui-request-handler-factory.ts +243 -0
  248. package/src/execution/ui-request-observability.ts +81 -0
  249. package/src/execution/ui-request-queue.ts +184 -0
  250. package/src/execution/worktree-manager.ts +688 -0
  251. package/src/execution/worktree-registry.ts +279 -0
  252. package/src/index.ts +87 -0
  253. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +349 -0
  254. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +202 -0
  255. package/src/orchestration/__tests__/agent-call-stream.test.ts +152 -0
  256. package/src/orchestration/__tests__/args-validator.test.ts +143 -0
  257. package/src/orchestration/__tests__/config-loader.test.ts +503 -0
  258. package/src/orchestration/__tests__/error-recovery-handlers.test.ts +809 -0
  259. package/src/orchestration/__tests__/error-recovery-postmessage-defense.test.ts +404 -0
  260. package/src/orchestration/__tests__/error-recovery-serialize-failed-result.test.ts +56 -0
  261. package/src/orchestration/__tests__/error-recovery-workflow-call.test.ts +166 -0
  262. package/src/orchestration/__tests__/execute-agent-call.test.ts +451 -0
  263. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +600 -0
  264. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +96 -0
  265. package/src/orchestration/__tests__/lifecycle.test.ts +659 -0
  266. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +95 -0
  267. package/src/orchestration/__tests__/review-fix-loop-scriptpath-failfast.test.ts +183 -0
  268. package/src/orchestration/__tests__/script-lint.test.ts +831 -0
  269. package/src/orchestration/__tests__/skill-discovery.test.ts +210 -0
  270. package/src/orchestration/__tests__/test-mocks.ts +197 -0
  271. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +368 -0
  272. package/src/orchestration/__tests__/worker-host.test.ts +120 -0
  273. package/src/orchestration/__tests__/worker-returnmeta-passthrough.test.ts +164 -0
  274. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +563 -0
  275. package/src/orchestration/__tests__/worker-script-builder.test.ts +309 -0
  276. package/src/orchestration/__tests__/worker-script-template-snapshot.test.ts +129 -0
  277. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +317 -0
  278. package/src/orchestration/__tests__/workflow-script-lint-memo.test.ts +110 -0
  279. package/src/orchestration/agent-opts-resolver.ts +174 -0
  280. package/src/orchestration/args-validator.ts +127 -0
  281. package/src/orchestration/config-loader.ts +316 -0
  282. package/src/orchestration/error-recovery.ts +1018 -0
  283. package/src/orchestration/execute-agent-call.ts +256 -0
  284. package/src/orchestration/launcher.ts +455 -0
  285. package/src/orchestration/lifecycle.ts +399 -0
  286. package/src/orchestration/models/__tests__/budget.test.ts +307 -0
  287. package/src/orchestration/models/agent-call.ts +83 -0
  288. package/src/orchestration/models/budget.ts +118 -0
  289. package/src/orchestration/models/ports.ts +164 -0
  290. package/src/orchestration/models/run-runtime.ts +104 -0
  291. package/src/orchestration/models/run-spec.ts +83 -0
  292. package/src/orchestration/models/run-state.ts +44 -0
  293. package/src/orchestration/models/trace.ts +183 -0
  294. package/src/orchestration/models/types.ts +301 -0
  295. package/src/orchestration/models/workflow-run.ts +254 -0
  296. package/src/orchestration/models/workflow-script-registry.ts +35 -0
  297. package/src/orchestration/models/workflow-script.ts +117 -0
  298. package/src/orchestration/script-lint.ts +766 -0
  299. package/src/orchestration/skill-discovery.ts +135 -0
  300. package/src/orchestration/worker-handle.ts +115 -0
  301. package/src/orchestration/worker-host.ts +98 -0
  302. package/src/orchestration/worker-script-builder.ts +421 -0
  303. package/src/orchestration/workflow-files.ts +85 -0
  304. package/src/orchestration/workflow-script-registry-impl.ts +133 -0
  305. package/src/shared/__tests__/agent-ref.test.ts +34 -0
  306. package/src/shared/__tests__/meta-parser.test.ts +304 -0
  307. package/src/shared/__tests__/model-ref.test.ts +306 -0
  308. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +288 -0
  309. package/src/shared/__tests__/resource-discovery.test.ts +749 -0
  310. package/src/shared/__tests__/resource-meta.test.ts +51 -0
  311. package/src/shared/__tests__/schema-jsonify.test.ts +81 -0
  312. package/src/shared/__tests__/timer-delay.test.ts +61 -0
  313. package/src/shared/agent-event.ts +13 -0
  314. package/src/shared/agent-ref.ts +57 -0
  315. package/src/shared/meta-parser.ts +261 -0
  316. package/src/shared/model-ref.ts +286 -0
  317. package/src/shared/resource-discovery.ts +835 -0
  318. package/src/shared/resource-meta.ts +65 -0
  319. package/src/shared/schema-env.ts +44 -0
  320. package/src/shared/schema-jsonify.ts +58 -0
  321. package/src/shared/timer-delay.ts +54 -0
  322. package/src/shared/xml-injection.ts +35 -0
  323. package/workflows/README.md +81 -0
  324. package/workflows/_shared/agent-refs.cjs +40 -0
  325. package/workflows/chain.js +137 -0
  326. package/workflows/map-reduce.js +180 -0
  327. package/workflows/parallel.js +154 -0
  328. package/workflows/review-fix-loop-utils.cjs +1542 -0
  329. package/workflows/review-fix-loop.js +1605 -0
  330. package/workflows/scatter-gather.js +184 -0
@@ -0,0 +1,161 @@
1
+ // src/execution/engine/engines/zcode/launcher.ts
2
+ //
3
+ // ZcodeEngine launcher(P3):spawn 命令组装 + 进程启动——四件套里唯一持有 spawn 权
4
+ // 的模块(设计 §3.3.7)。TS 重写自 zsub driver.js 的 runHeadless 命令组装段,机制
5
+ // 保留(真机验证过):
6
+ // - argv 形态:`node <zcode.cjs> --json --cwd <dir> --mode yolo [--disallowed-tools a,b]
7
+ // [--resume <sid>] --prompt <text>`;flag 拒收清单(--model/--skill/--max-turns 等
8
+ // help 漂移项)一个不用;
9
+ // - stdin: 'ignore'(CLI 不读 stdin,置管道会挂起——zsub 实测);
10
+ // - env:HOME=隔离池目录(provider 配置与 db 全部落池内)+ 嵌套防护标记;
11
+ // - 杀链:SIGTERM → grace → SIGKILL,exited 用 close 事件维护的标志判断
12
+ // (child.killed 在 kill() 调用后恒 true,zsub 修过的原版死代码 bug)。
13
+ //
14
+ // 与 zsub 的差异:超时不在本模块(宿主公共杀链职责,设计 D4/附录 A「CLI 超时 6/6
15
+ // 全缺全补」);abort 由 AbortSignal 驱动(EnginePort 契约),zsub 的 cancel() 变为
16
+ // abort(graceMs)。
17
+
18
+ import { spawn, type ChildProcess } from "node:child_process";
19
+ import { Readable } from "node:stream";
20
+
21
+ import { killChain } from "../../common/kill-chain.ts";
22
+ import { buildNestedSpawnEnv } from "../../common/nesting-guard.ts";
23
+ import { assertArgvBudget } from "../../common/persona-router.ts";
24
+ import { ZCODE_KILL_GRACE_MS } from "./constants.ts";
25
+
26
+ // ============================================================
27
+ // argv 组装与字节预算(对齐点②收口:估算/断言单一权威 = common/persona-router,
28
+ // 本模块只组装;引擎侧在 launch 前调 assertArgvBudget)
29
+ // ============================================================
30
+
31
+ export interface ZcodeSpawnSpec {
32
+ /** 任务运行目录(--cwd flag 的值)。 */
33
+ cwd: string;
34
+ /** 完整 prompt(persona 拼接后的产物)。 */
35
+ prompt: string;
36
+ /** 工具 denylist(逗号连接进 --disallowed-tools;空数组不加 flag)。 */
37
+ denyTools?: string[];
38
+ /** 冷续聊目标 session(--resume;spawn 单轮模式内不做 resume 仿真,留给宿主)。 */
39
+ resumeSessionId?: string;
40
+ }
41
+
42
+ /** 组装 zcode.cjs 的 argv(不含 `node` 与 cli 路径两个前导元素)。 */
43
+ export function buildZcodeArgv(spec: ZcodeSpawnSpec): string[] {
44
+ const args = ["--json", "--cwd", spec.cwd, "--mode", "yolo"];
45
+ // denylist 硬约束:非空数组才落 flag(CLI 对空值行为未定义,不赌——zsub 同判)
46
+ const disallowed = (spec.denyTools ?? []).filter((t) => typeof t === "string" && t.trim() !== "");
47
+ if (disallowed.length > 0) args.push("--disallowed-tools", disallowed.join(","));
48
+ if (spec.resumeSessionId) args.push("--resume", String(spec.resumeSessionId));
49
+ args.push("--prompt", String(spec.prompt));
50
+ return args;
51
+ }
52
+
53
+ /**
54
+ * argv 预算断言(launch 前调用;对齐点②:公共 persona-router 的单一权威实现,
55
+ * DEFAULT_ARGV_BUDGET_BYTES 128KB 阈值同源——超限抛 EngineError(prompt_too_large),
56
+ * 进程创建前拦截,禁止 spawn 后撞 E2BIG)。
57
+ */
58
+ export function assertZcodeArgvBudget(nodeBin: string, cliPath: string, args: string[]): void {
59
+ assertArgvBudget([nodeBin, cliPath, ...args]);
60
+ }
61
+
62
+ // ============================================================
63
+ // env 构造
64
+ // ============================================================
65
+
66
+ /**
67
+ * 构造子进程 env:嵌套防护经公共 nesting-guard(D8:注入统一 XYZ_AGENT_SUBAGENT=1 +
68
+ * 剥离引擎原生嵌套标记 ZSW_NESTED/PI_SUBAGENT_*——旧标记继承会让孙代理误判嵌套层),
69
+ * HOME 最后落(隔离 HOME 是 provider 配置与 db 的定位锚,基 env 同名键不许覆盖)。
70
+ */
71
+ export function buildZcodeEnv(homeDir: string, baseEnv: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEnv {
72
+ return { ...buildNestedSpawnEnv(baseEnv), HOME: homeDir };
73
+ }
74
+
75
+ // ============================================================
76
+ // 进程启动(EngineProcess 形态,设计 §3.3.7)
77
+ // ============================================================
78
+
79
+ /** launcher 产出的进程句柄(parser 消费 stdout/stderr/exited;abort 是杀链执行体)。 */
80
+ export interface ZcodeLaunchedProcess {
81
+ /** spawn 出的原始子进程句柄([U0 D10] 终止链记账——引擎经 RunContext.onChildSpawned 注册进宿主 spawnedChildren)。 */
82
+ readonly child: ChildProcess;
83
+ readonly pid: number;
84
+ readonly stdout: Readable;
85
+ readonly stderr: Readable;
86
+ /** 杀链:SIGTERM → graceMs 后未退出则 SIGKILL;resolve 于进程退出。幂等。 */
87
+ readonly abort: (graceMs?: number) => Promise<void>;
88
+ /** 进程退出(code=null 表示被信号杀死)。 */
89
+ readonly exited: Promise<{ code: number | null; signal: string | undefined }>;
90
+ /** 本方杀链是否介入过(合成终态的判据:介入后 code 语义不再是引擎自身失败)。 */
91
+ readonly killedByUs: () => boolean;
92
+ }
93
+
94
+ export interface ZcodeLaunchOptions {
95
+ /** zcode CLI 路径(node 脚本)。 */
96
+ cliPath: string;
97
+ /** cliPath 之后的 argv(buildZcodeArgv 产物)。 */
98
+ args: string[];
99
+ /** 完整 env(buildZcodeEnv 产物)。 */
100
+ env: NodeJS.ProcessEnv;
101
+ /** node 二进制(缺省 'node',走 PATH)。 */
102
+ nodeBin?: string;
103
+ }
104
+
105
+ /**
106
+ * 启动 zcode 子进程。spawn 同步失败(如 node 不存在)直接 throw——调用方(引擎 run)
107
+ * 按进程创建前错误处理;启动成功后的一切失败归 parser 终态路径(不 throw)。
108
+ *
109
+ * 杀链(对齐点②收口):SIGTERM → grace → SIGKILL 的实现单一权威 =
110
+ * common/kill-chain.killChain(ChildProcess 结构满足 KillableChild)——本模块只保留
111
+ * killedByUs 标志(合成终态判据:「我方介入过」与「引擎自身失败」的区分)与幂等守卫。
112
+ */
113
+ export function launchZcodeProcess(opts: ZcodeLaunchOptions): ZcodeLaunchedProcess {
114
+ const nodeBin = opts.nodeBin ?? "node";
115
+ const child = spawn(nodeBin, [opts.cliPath, ...opts.args], {
116
+ env: opts.env,
117
+ stdio: ["ignore", "pipe", "pipe"],
118
+ });
119
+
120
+ let exited = false;
121
+ let killTriggered = false;
122
+
123
+ // ENOENT(node 缺失)等 spawn 失败经 'error' 事件异步到达且 stdout/stderr 可能为
124
+ // null——空流兜底,保证消费方(parser)始终拿到可读流
125
+ const emptyStream = (): Readable => Readable.from([]);
126
+ const stdoutStream = child.stdout ?? emptyStream();
127
+ const stderrStream = child.stderr ?? emptyStream();
128
+
129
+ const exitedPromise = new Promise<{ code: number | null; signal: string | undefined }>((resolve) => {
130
+ child.once("close", (code, signal) => {
131
+ exited = true;
132
+ resolve({ code, signal: signal ?? undefined });
133
+ });
134
+ child.once("error", () => {
135
+ // spawn 失败(ENOENT 等)也会走 error;close 不一定触发,这里兜底 resolve 防
136
+ // exited 永挂——code 取 null(无引擎语义可判)
137
+ exited = true;
138
+ resolve({ code: null, signal: undefined });
139
+ });
140
+ });
141
+
142
+ const abort = (graceMs: number = ZCODE_KILL_GRACE_MS): Promise<void> => {
143
+ if (!killTriggered && !exited) {
144
+ killTriggered = true;
145
+ // 公共杀链(不 await:abort 以 exitedPromise 为准;killChain 自身含 grace/
146
+ // SIGKILL/收尸全时序,fire-and-forget 不改变 abort 的 resolve 语义)
147
+ void killChain(child, { graceMs });
148
+ }
149
+ return exitedPromise.then(() => undefined);
150
+ };
151
+
152
+ return {
153
+ child,
154
+ pid: child.pid ?? -1,
155
+ stdout: stdoutStream,
156
+ stderr: stderrStream,
157
+ abort,
158
+ exited: exitedPromise,
159
+ killedByUs: () => killTriggered,
160
+ };
161
+ }
@@ -0,0 +1,436 @@
1
+ // src/execution/engine/engines/zcode/parser.ts
2
+ //
3
+ // ZcodeEngine parser(P3):stdout 有界收集 → 终 JSON 解析 → 合成 coarse AgentEvent
4
+ // 流(设计 §3.3.7「批量引擎进程退出后一次性 emit 合成事件」形态)。TS 重写自 zsub
5
+ // driver.js 的 createBoundedLineBuffer / parseStdoutJson,机制保留:
6
+ // - 有界双缓冲(头 4K + 尾 64K):zcode response 可达数十 KB,无上限累加在长任务
7
+ // 下爆内存;超限以「行」为单位丢弃,头尾各自保持可读。代价:单行 JSON 超过尾部
8
+ // 窗口时无法完整解析——按错误路径处理(错误信息带尾部),内存安全优先。
9
+ // - 容错解析:正常是单个 JSON 文档;混入日志行则截取首尾大括号间内容。
10
+ //
11
+ // 事件产出不变量(设计 §3.3.7,coarse 口径):turn_end 前至少一个 message_end;
12
+ // message_end.usage 出现时为完整 AgentUsage 形状(缺数据给显式 0,不给残缺对象)。
13
+
14
+ import type { Readable } from "node:stream";
15
+
16
+ import type { AgentUsage as ExecutionAgentUsage } from "../../../types.ts";
17
+ import type { AgentUsage as OutcomeAgentUsage } from "../../../../orchestration/models/types.ts";
18
+ import type { AgentEvent } from "../../types.ts";
19
+ import { ZCODE_ERROR_TAIL_CHARS } from "./constants.ts";
20
+ import type { ZcodeLaunchedProcess } from "./launcher.ts";
21
+
22
+ // ============================================================
23
+ // 有界行缓冲(zsub createBoundedLineBuffer 移植)
24
+ // ============================================================
25
+
26
+ export interface BoundedLineBuffer {
27
+ push(chunk: string): void;
28
+ flush(): void;
29
+ text(): string;
30
+ tail(n: number): string;
31
+ }
32
+
33
+ const DEFAULT_HEAD_BYTES = 4096;
34
+ // eslint-disable-next-line no-magic-numbers -- 64KB = 64 * 1024 bytes
35
+ const DEFAULT_TAIL_BYTES = 64 * 1024;
36
+
37
+ export function createBoundedLineBuffer(opts: { headLimit?: number; tailLimit?: number } = {}): BoundedLineBuffer {
38
+ const headLimit = opts.headLimit ?? DEFAULT_HEAD_BYTES;
39
+ const tailLimit = opts.tailLimit ?? DEFAULT_TAIL_BYTES;
40
+ let pending = "";
41
+ let head = "";
42
+ let headFull = false;
43
+ const tailLines: string[] = [];
44
+ let tailBytes = 0;
45
+ let droppedBytes = 0;
46
+
47
+ function addLine(line: string): void {
48
+ if (!headFull) {
49
+ if (head.length + line.length <= headLimit) {
50
+ head += line;
51
+ return;
52
+ }
53
+ headFull = true;
54
+ }
55
+ tailLines.push(line);
56
+ tailBytes += line.length;
57
+ // 超出尾部窗口:从最旧的行开始丢
58
+ while (tailBytes > tailLimit && tailLines.length > 1) {
59
+ const dropped = tailLines.shift()!;
60
+ tailBytes -= dropped.length;
61
+ droppedBytes += dropped.length;
62
+ }
63
+ // 单行超过整个尾部窗口:只保留该行结尾(JSON 的错误信息通常在末尾)
64
+ if (tailBytes > tailLimit && tailLines.length === 1) {
65
+ const over = tailBytes - tailLimit;
66
+ tailLines[0] = tailLines[0]!.slice(over);
67
+ tailBytes -= over;
68
+ droppedBytes += over;
69
+ }
70
+ }
71
+
72
+ return {
73
+ push(chunk: string): void {
74
+ pending += chunk;
75
+ let nl: number;
76
+ while ((nl = pending.indexOf("\n")) >= 0) {
77
+ addLine(pending.slice(0, nl + 1));
78
+ pending = pending.slice(nl + 1);
79
+ }
80
+ },
81
+ flush(): void {
82
+ if (pending !== "") {
83
+ addLine(pending);
84
+ pending = "";
85
+ }
86
+ },
87
+ text(): string {
88
+ this.flush();
89
+ const mid =
90
+ droppedBytes > 0 ? `\n[zcode-engine] 输出过长,头尾之间已丢弃 ${droppedBytes} 字节\n` : "";
91
+ return head + mid + tailLines.join("");
92
+ },
93
+ tail(n: number): string {
94
+ const t = this.text();
95
+ return t.length > n ? t.slice(t.length - n) : t;
96
+ },
97
+ };
98
+ }
99
+
100
+ // ============================================================
101
+ // 终 JSON 解析(含运行时 guard——禁 any)
102
+ // ============================================================
103
+
104
+ /** stdout 里的原生 usage 形状(2026-08-25 实测 0.16.5,字段名带 Tokens 后缀)。 */
105
+ export interface ZcodeRawUsage {
106
+ source?: unknown;
107
+ modelRequestCount?: unknown;
108
+ inputTokens?: unknown;
109
+ outputTokens?: unknown;
110
+ totalTokens?: unknown;
111
+ cacheReadTokens?: unknown;
112
+ cacheWriteTokens?: unknown;
113
+ reasoningTokens?: unknown;
114
+ }
115
+
116
+ /** 容错解析 stdout:直接 JSON.parse;失败则截取首尾大括号间内容再试。 */
117
+ export function parseZcodeStdoutJson(stdout: string): unknown {
118
+ const trimmed = stdout.trim();
119
+ try {
120
+ return JSON.parse(trimmed);
121
+ } catch (err) {
122
+ // 混入日志行等形态——吞掉异常继续首尾大括号容错提取(malformed 是降级输入不是错误态)
123
+ void err;
124
+ }
125
+ const first = trimmed.indexOf("{");
126
+ const last = trimmed.lastIndexOf("}");
127
+ if (first >= 0 && last > first) {
128
+ try {
129
+ return JSON.parse(trimmed.slice(first, last + 1));
130
+ } catch {
131
+ return null;
132
+ }
133
+ }
134
+ return null;
135
+ }
136
+
137
+ function finiteOr(v: unknown, fallback: number): number {
138
+ const n = typeof v === "number" ? v : Number(v);
139
+ return Number.isFinite(n) ? n : fallback;
140
+ }
141
+
142
+ /** 原生 usage → 事件层 AgentUsage(execution 版:四项 token;cost 无来源缺省)。 */
143
+ export function mapZcodeUsage(raw: unknown): ExecutionAgentUsage | undefined {
144
+ if (typeof raw !== "object" || raw === null) return undefined;
145
+ const r = raw as ZcodeRawUsage;
146
+ if (r.inputTokens === undefined && r.outputTokens === undefined) return undefined;
147
+ return {
148
+ input: finiteOr(r.inputTokens, 0),
149
+ output: finiteOr(r.outputTokens, 0),
150
+ cacheRead: finiteOr(r.cacheReadTokens, 0),
151
+ cacheWrite: finiteOr(r.cacheWriteTokens, 0),
152
+ };
153
+ }
154
+
155
+ /**
156
+ * 原生 usage + projection → 终态层 AgentUsage(orchestration 版:cost/contextTokens/
157
+ * turns 为必填)。zcode 不回传 cost(调研附录 A「cost 回传 ❌」)——显式 0(消费方按
158
+ * 「显示降级」处理,不给残缺);contextTokens 取 projection.contextUsed(当前上下文
159
+ * 占用),turns 取 projection.turnCount。
160
+ */
161
+ export function mapZcodeOutcomeUsage(rawUsage: unknown, projection: unknown): OutcomeAgentUsage | undefined {
162
+ const base = mapZcodeUsage(rawUsage);
163
+ if (base === undefined) return undefined;
164
+ const p =
165
+ typeof projection === "object" && projection !== null ? (projection as Record<string, unknown>) : {};
166
+ const r = typeof rawUsage === "object" && rawUsage !== null ? (rawUsage as ZcodeRawUsage) : {};
167
+ const contextTokens = firstFinite(p["contextUsed"], r.totalTokens, 0);
168
+ const turns = firstFinite(p["turnCount"], 1);
169
+ return { ...base, cost: 0, contextTokens, turns };
170
+ }
171
+
172
+ function firstFinite(...vals: unknown[]): number {
173
+ for (const v of vals) {
174
+ const n = typeof v === "number" ? v : Number(v);
175
+ if (Number.isFinite(n)) return n;
176
+ }
177
+ return 0;
178
+ }
179
+
180
+ /** 终 JSON 的解析产物(引擎消费面——response 必须是 string 才算合法终态)。 */
181
+ export interface ZcodeTerminalPayload {
182
+ sessionId?: string;
183
+ response: string;
184
+ /** 事件层 usage(execution 版 AgentUsage——message_end 合成用)。 */
185
+ usage?: ExecutionAgentUsage;
186
+ /** 终态层 usage(orchestration 版 AgentUsage——AgentOutcome.usage 用)。 */
187
+ outcomeUsage?: OutcomeAgentUsage;
188
+ /** projection.turnCount(gui/record 的轮数参考;解析不出则缺省)。 */
189
+ turnCount?: number;
190
+ }
191
+
192
+ export type ZcodeTerminalParse =
193
+ | { ok: true; payload: ZcodeTerminalPayload }
194
+ | { ok: false; reason: string };
195
+
196
+ /** 解析终 JSON 并做形状校验(sessionId/usage/turnCount 逐字段 guard)。 */
197
+ export function parseZcodeTerminal(stdout: string): ZcodeTerminalParse {
198
+ const parsed = parseZcodeStdoutJson(stdout);
199
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
200
+ return { ok: false, reason: "stdout 不是 JSON 对象" };
201
+ }
202
+ const obj = parsed as Record<string, unknown>;
203
+ if (typeof obj.response !== "string") {
204
+ return { ok: false, reason: "终 JSON 缺 string 型 response 字段(zcode 格式漂移嫌疑)" };
205
+ }
206
+ const usage = mapZcodeUsage(obj.usage);
207
+ const projection =
208
+ typeof obj.projection === "object" && obj.projection !== null
209
+ ? (obj.projection as Record<string, unknown>)
210
+ : undefined;
211
+ const outcomeUsage = mapZcodeOutcomeUsage(obj.usage, projection);
212
+ const turnCountRaw = projection?.turnCount;
213
+ return {
214
+ ok: true,
215
+ payload: {
216
+ response: obj.response,
217
+ ...(typeof obj.sessionId === "string" ? { sessionId: obj.sessionId } : {}),
218
+ ...(usage !== undefined ? { usage } : {}),
219
+ ...(outcomeUsage !== undefined ? { outcomeUsage } : {}),
220
+ ...(typeof turnCountRaw === "number" && Number.isFinite(turnCountRaw) ? { turnCount: turnCountRaw } : {}),
221
+ },
222
+ };
223
+ }
224
+
225
+ // ============================================================
226
+ // 输出收集(进程退出 + 有界缓冲终态)
227
+ // ============================================================
228
+
229
+ export interface ZcodeCollectedOutput {
230
+ exitCode: number | null;
231
+ signal?: string;
232
+ /** 有界 stdout 全文(头 4K + 尾 64K,中间丢弃段有标记行)。 */
233
+ stdoutText: string;
234
+ /** stderr 尾部(有界 4K)。 */
235
+ stderrTail: string;
236
+ }
237
+
238
+ /**
239
+ * 等待可读流终止(end/close/error 任一,1s 超时兜底)。
240
+ * 为什么不能只 await 进程退出:宿主在微任务上下文里续跑时(vitest / async 编排链),
241
+ * 进程 exited 的 promise 续跑可能先于流 data/end 事件的 nextTick 冲刷——直接读缓冲
242
+ * 会拿到空串。等流终止事件是确定性的「数据已收完」判据;超时兜底防流异常挂起。
243
+ */
244
+ /** 流终止等待超时(兜底挂起流;比杀链 grace 短一个量级即可)。 */
245
+ const DRAIN_TIMEOUT_MS = 1_000;
246
+
247
+ function drainReadable(stream: Readable): Promise<void> {
248
+ return new Promise((resolve) => {
249
+ if (stream.readableEnded || stream.destroyed) {
250
+ resolve();
251
+ return;
252
+ }
253
+ let settled = false;
254
+ const finish = (): void => {
255
+ if (settled) return;
256
+ settled = true;
257
+ stream.removeListener("end", finish);
258
+ stream.removeListener("close", finish);
259
+ stream.removeListener("error", finish);
260
+ clearTimeout(timer);
261
+ resolve();
262
+ };
263
+ stream.once("end", finish);
264
+ stream.once("close", finish);
265
+ stream.once("error", finish);
266
+ const timer = setTimeout(finish, DRAIN_TIMEOUT_MS);
267
+ if (typeof timer.unref === "function") timer.unref();
268
+ });
269
+ }
270
+
271
+ /** 消费进程输出直到退出(事件 emit 前的收集段——终态解析素材)。 */
272
+ export async function collectZcodeOutput(
273
+ proc: Pick<ZcodeLaunchedProcess, "stdout" | "stderr" | "exited">,
274
+ ): Promise<ZcodeCollectedOutput> {
275
+ const outBuf = createBoundedLineBuffer();
276
+ // stderr 只做诊断:不进解析路径,尾部 4K 足够
277
+ const errBuf = createBoundedLineBuffer({ headLimit: 0, tailLimit: DEFAULT_TAIL_BYTES });
278
+ proc.stdout.on("data", (d: Buffer | string) => {
279
+ outBuf.push(typeof d === "string" ? d : d.toString("utf8"));
280
+ });
281
+ proc.stderr.on("data", (d: Buffer | string) => {
282
+ errBuf.push(typeof d === "string" ? d : d.toString("utf8"));
283
+ });
284
+ const { code, signal } = await proc.exited;
285
+ await drainReadable(proc.stdout);
286
+ await drainReadable(proc.stderr);
287
+ outBuf.flush();
288
+ errBuf.flush();
289
+ return {
290
+ exitCode: code,
291
+ ...(signal !== undefined ? { signal } : {}),
292
+ stdoutText: outBuf.text(),
293
+ stderrTail: errBuf.text(),
294
+ };
295
+ }
296
+
297
+ // ============================================================
298
+ // coarse 事件合成(不变量:turn_end 最后、其前至少一个 message_end)
299
+ // ============================================================
300
+
301
+ /** 终态成功时合成的最小事件序列(coarse 引擎只有终态级信息——设计 D3 eventGranularity)。 */
302
+ export function synthesizeCoarseEvents(response: string, usage?: ExecutionAgentUsage): AgentEvent[] {
303
+ return [
304
+ // usage 给不出完整形状时显式缺省整个字段,不给残缺对象(不变量 2)
305
+ { type: "message_end", ...(usage !== undefined ? { usage } : {}) },
306
+ { type: "turn_end" },
307
+ ] as AgentEvent[];
308
+ }
309
+
310
+ // ============================================================
311
+ // engine_run_failed 错误文案(设计 §3.3.3 错误规格行)
312
+ // ============================================================
313
+
314
+ /**
315
+ * 运行中失败的结构化文案:stdout 尾部 2000 字 + exit code + 恢复指引。
316
+ *
317
+ * 指引按 stderr 特征归因(2026-08-25 真机教训:LLM 端点不可达导致的
318
+ * AI_APICallError 曾套用「查版本/重跑探针/补 golden」指引,误导排查方向):
319
+ * - LLM API 调用失败(stderr 含 AI_APICallError):CLI 本体与输出解析正常,
320
+ * 指向池内 provider 的 baseURL/apiKey 排查;
321
+ * - 其余(spawn 成功但格式漂移/解析失败):保留 probe/golden 指引。
322
+ */
323
+ /** 错误文案里 stderr 尾部回显长度(stdout 有专用 2000 常量,stderr 更短防刷屏)。 */
324
+ const STDERR_TAIL_IN_MSG_CHARS = 500;
325
+
326
+ /** vercel.ai SDK 的 API 调用错误类名——zcode CLI 内部 LLM 调用失败的稳定特征。 */
327
+ const LLM_API_FAILURE_SIGNATURE = "APICallError";
328
+
329
+ /**
330
+ * LLM/turn 级失败判定。两种实测形态(2026-08-25 真机):
331
+ * - 无响应形态(端点不可达):stderr 含 AI_APICallError 类名 + Symbol 行;
332
+ * - 有响应形态(如 HTTP 401):stderr 打印 response headers + responseStatus/
333
+ * statusCode,类名行被截断不在尾部——靠 `Turn execution failed`(zcode CLI
334
+ * turn 失败统一尾巴)兜住。
335
+ */
336
+ function isLlmTurnFailure(stderrTail: string | undefined): boolean {
337
+ if (stderrTail === undefined) return false;
338
+ return (
339
+ stderrTail.includes(LLM_API_FAILURE_SIGNATURE) ||
340
+ stderrTail.includes("Turn execution failed")
341
+ );
342
+ }
343
+
344
+ /** stderr 中提取 HTTP 状态码(有响应形态),用于凭据/限流/上游错误的定向指引。 */
345
+ function extractHttpStatus(stderrTail: string | undefined): string {
346
+ if (stderrTail === undefined) return "";
347
+ const m = stderrTail.match(/statusCode:\s*(\d{3})/);
348
+ if (m === null) return "";
349
+ const code = m[1]!;
350
+ const meaning: Record<string, string> = {
351
+ "401": "——凭据无效或过期(apiKey 被上游拒绝)",
352
+ "403": "——凭据无权限(apiKey 有效但无该模型/资源权限)",
353
+ "404": "——模型或路径不存在(核对模型名与 baseURL)",
354
+ "429": "——限流/配额耗尽",
355
+ };
356
+ return `,HTTP ${code}${meaning[code] ?? ""}`;
357
+ }
358
+
359
+ /**
360
+ * 折叠 stderr 里 console.log 浅序列化产生的 "[Object]" 噪音行
361
+ * (真实堆栈里常出现连续十余行 `[Object], [Object], …`,折叠后保留计数)。
362
+ */
363
+ function compactStderrObjectNoise(tail: string): string {
364
+ const OBJECT_LINE = /^(\[Object\][,\s]*)+$/;
365
+ const lines = tail.split("\n");
366
+ const out: string[] = [];
367
+ let run = 0;
368
+ const flush = (): void => {
369
+ if (run > 0) {
370
+ out.push(`[Object]×${run}`);
371
+ run = 0;
372
+ }
373
+ };
374
+ for (const line of lines) {
375
+ if (OBJECT_LINE.test(line.trim())) {
376
+ run++;
377
+ continue;
378
+ }
379
+ flush();
380
+ out.push(line);
381
+ }
382
+ flush();
383
+ return out.join("\n");
384
+ }
385
+
386
+ export function buildRunFailedMessage(opts: {
387
+ /** 实际使用的 CLI 路径(用户可经 XYZ_ZCODE_CLI 覆盖——文案必须引用真路径,不硬编码缺省值)。 */
388
+ cliPath: string;
389
+ exitCode: number | null;
390
+ stdoutTail: string;
391
+ stderrTail?: string;
392
+ parseReason?: string;
393
+ /** 本次任务解析出的模型全名(provider/model)——LLM 失败归因时点名排查对象。 */
394
+ modelRef?: string;
395
+ /** 池内 config.json 绝对路径——LLM 失败归因时给出 baseURL/apiKey 核对位置。 */
396
+ configPath?: string;
397
+ }): string {
398
+ const parts: string[] = [];
399
+ if (opts.parseReason !== undefined) {
400
+ parts.push(`解析失败:${opts.parseReason}。`);
401
+ }
402
+ parts.push(`exit code: ${opts.exitCode ?? "null(被信号杀死)"}。`);
403
+ if (opts.stderrTail !== undefined && opts.stderrTail.trim() !== "") {
404
+ const compacted = compactStderrObjectNoise(opts.stderrTail);
405
+ parts.push(`stderr 尾部: ${compacted.slice(-STDERR_TAIL_IN_MSG_CHARS)}`);
406
+ }
407
+ // stdout 空段跳过(与 stderr 同判)——turn 0 即失败时 stdout 恒空,保留空 part
408
+ // 只会制造「stdout 尾部: <指引段>」的视觉嵌套误读(2026-08-25 真机复盘)
409
+ if (opts.stdoutTail.trim() !== "") {
410
+ parts.push(`stdout 尾部: ${opts.stdoutTail.slice(-ZCODE_ERROR_TAIL_CHARS)}`);
411
+ }
412
+
413
+ if (isLlmTurnFailure(opts.stderrTail)) {
414
+ const statusHint = extractHttpStatus(opts.stderrTail);
415
+ const modelHint =
416
+ opts.modelRef !== undefined ? `(本任务模型 '${opts.modelRef}')` : "";
417
+ const configHint =
418
+ opts.configPath !== undefined
419
+ ? `① 核对池内 provider 的 baseURL 可达性与 apiKey 有效性(\`${opts.configPath}\`)${statusHint.includes("401") || statusHint.includes("403") ? "——apiKey 大概率无效或过期,需在凭据来源(ZCode 桌面或自建网关)更新" : ""};`
420
+ : `① 核对 zcode 池内 provider 的 baseURL 可达性与 apiKey 有效性${statusHint};`;
421
+ parts.push(
422
+ `恢复指引:模型 API 调用失败${statusHint}——CLI 本体与输出解析正常,问题在模型端点或凭据${modelHint}。` +
423
+ configHint +
424
+ "② 修复后直接重跑本任务(probe 缓存不受影响——运行期失败不缓存);" +
425
+ "③ 或任务显式指定其他可用模型(provider/model 全名);" +
426
+ "④ 或改用 engine: pi 重跑本任务。",
427
+ );
428
+ } else {
429
+ parts.push(
430
+ `恢复指引:跑 \`node ${opts.cliPath} --version\` 确认版本后重跑探针(probe)——` +
431
+ "若为格式漂移,把新 stdout 样本补录进 golden 库(\`__tests__/__fixtures__/zcode-golden-spawn.json\`)并更新 parser;" +
432
+ "或改用 engine: pi 重跑本任务。详见 docs/research/agent-engine-zcode.md。",
433
+ );
434
+ }
435
+ return `engine_run_failed: zcode CLI 运行失败。${parts.join(" ")}`;
436
+ }