@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,349 @@
1
+ "use strict";
2
+ // Module-scope helpers: accessible to BOTH the IIFE body AND the outer
3
+ // .then()/.catch() handlers (which run outside the IIFE). _workerLogs/
4
+ // _pushWorkerLog/_safePost + the parentPort/workerData handles must all live
5
+ // here — a previous version declared _safePost inside the IIFE, so the
6
+ // .then()/.catch() return/error handlers threw ReferenceError: _safePost is
7
+ // not defined, crashing the Worker on EVERY script return (exit code 1) and
8
+ // losing all diagnostics. require() is cached, so destructuring once at module
9
+ // load and reusing everywhere avoids the redundant require calls that used to
10
+ // appear in the IIFE and the outer .then/.catch.
11
+ const { parentPort: _parentPort, workerData: _workerData } = require("node:worker_threads");
12
+ const _workerLogs = [];
13
+ // IF6(#12): known agent() fields — hoisted to module scope, built once per worker
14
+ // (was rebuilt inside agent() on every call; field set is call-invariant).
15
+ const _KNOWN_FIELDS = new Set(["prompt", "description", "schema", "model", "scene", "label", "task", "agent", "phase", "skill", "timeoutMs", "maxTurns", "cwd", "fork", "worktree", "returnMeta", "thinkingLevel", "engine"]);
16
+ function _pushWorkerLog(level, args) {
17
+ try { _workerLogs.push({ level, message: args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ") }); } catch (e) { /* swallow */ }
18
+ }
19
+ // ── safePostMessage wrapper: 统一 postMessage 防御(DataCloneError 等)──
20
+ // Module-scope so the outer .then/.catch return/error handlers can use it.
21
+ // context 取值约定(诊断标识):固定为消息类型字面量——
22
+ // "agent-call" / "workflow-call" / "return" / "error",调用方据此在日志里
23
+ // 一眼定位是哪类 postMessage 失败。新增调用点必须传对应 context。
24
+ function _safePost(msg, context) {
25
+ try { _parentPort.postMessage(msg); return true; }
26
+ catch (e) {
27
+ const errMsg = e && e.message ? e.message : String(e);
28
+ const stack = e && e.stack ? e.stack : "";
29
+ _pushWorkerLog("error", ["[postMessage failed:" + context + "]", errMsg, stack]);
30
+ return false;
31
+ }
32
+ }
33
+ (async () => {
34
+ const parentPort = _parentPort;
35
+ const workerData = _workerData;
36
+
37
+ if (!parentPort) {
38
+ throw new Error("Workflow worker: parentPort is null — not running in a Worker thread");
39
+ }
40
+
41
+ // ── Intercept console.* to avoid leaking worker diagnostics into the input area ──
42
+ console.log = function (...args) { _pushWorkerLog("log", args); };
43
+ console.warn = function (...args) { _pushWorkerLog("warn", args); };
44
+ console.error = function (...args) { _pushWorkerLog("error", args); };
45
+ console.info = function (...args) { _pushWorkerLog("info", args); };
46
+
47
+ // ── Internal state ──
48
+ let _callIdCounter = 0;
49
+ let _agentCallCount = 0;
50
+ const _pendingCalls = new Map();
51
+ const _callCache = workerData.callCache instanceof Map
52
+ ? workerData.callCache
53
+ : new Map(Object.entries(workerData.callCache || {}).map(([k, v]) => [Number(k), v]));
54
+
55
+ // ── Injected globals ──
56
+ const $ARGS = (workerData.args && typeof workerData.args === "object") ? workerData.args : {};
57
+ const args = $ARGS;
58
+ const $WORKSPACE = typeof workerData.workspace === "string" ? workerData.workspace : "";
59
+ const _budgetData = {
60
+ total: (workerData.budget && workerData.budget.maxTokens) || 0,
61
+ _spentTokens: (workerData.budget && workerData.budget.usedTokens) ?? 0,
62
+ _spentCost: (workerData.budget && workerData.budget.usedCost) ?? 0,
63
+ };
64
+ const $BUDGET = {
65
+ get total() { return _budgetData.total; },
66
+ spent() { return _budgetData._spentTokens; },
67
+ remaining() { return Math.max(0, _budgetData.total - _budgetData._spentTokens); },
68
+ };
69
+
70
+ // ── Run-level model/thinkingLevel override (Option B symmetric single-path) ──
71
+ // Injected from workerData (set by workerHost from RunSpec.model/thinkingLevel).
72
+ // agent() reads these as run-level defaults; per-call explicit opts still win.
73
+ const $MODEL = (workerData.model && typeof workerData.model === "string") ? workerData.model : undefined;
74
+ const $THINKING_LEVEL = (workerData.thinkingLevel && typeof workerData.thinkingLevel === "string") ? workerData.thinkingLevel : undefined;
75
+
76
+ // ── WorkflowAbortedError ──
77
+ class WorkflowAbortedError extends Error {
78
+ constructor(reason) {
79
+ super("Workflow aborted: " + (reason || "No reason"));
80
+ this.name = "WorkflowAbortedError";
81
+ this.reason = reason || "";
82
+ }
83
+ }
84
+
85
+ // ── Message handler (main thread → worker) ──
86
+ parentPort.on("message", (msg) => {
87
+ if (msg.type === "agent-result") {
88
+ const pending = _pendingCalls.get(msg.callId);
89
+ if (pending) {
90
+ _pendingCalls.delete(msg.callId);
91
+ if (typeof msg.result !== "undefined") {
92
+ _callCache.set(msg.callId, msg.result);
93
+ }
94
+ // 失败不传播到 agent 外部:resolve 而非 reject。
95
+ // 旧实现在 result.error 时 reject,单 agent 失败会冒到 worker 顶层 .catch()
96
+ // → 发 type:"error" → handleScriptError → rebuildRuntime → SIGKILL 同伴进程,
97
+ // 把单点失败放大成整批崩溃。改为始终 resolve(错误时回退到 content 文本),
98
+ // 让 parallel() 下的脚本容错循环(parseResult → null → skip)自然接管。
99
+ // 错误原因已由主线程 executeAgentCall → trace.update(result.error) 保留在 trace/TUI,
100
+ // 不丢失。失败 resolve 为空字符串是既定容错策略。
101
+ // [MF-4] schema 模式下失败时 agent() 仍 resolve(content 回退、不 throw)——
102
+ // 需要检查错误时请用 returnMeta:true(resolve 值含 error 字段)。
103
+ // parsedOutput: validated data object from structured-output execute().
104
+ // Fallback to content (raw text) when no schema was requested or on error.
105
+ // W2 改动 9(b):returnMeta===true 时 resolve {value,sessionFile,worktreePath,error,usage,durationMs,sessionId}
106
+ // (对称缓存重放 9c);否则单值。usage/durationMs/sessionId 为 rfl 仪表透传(tier-1 §7.1,
107
+ // AgentResult 已含、此处原样转发,缺省 undefined 兼容旧主线程)。
108
+ const _value = msg.result.parsedOutput ?? msg.result.content;
109
+ if (pending.returnMeta) {
110
+ pending.resolve({
111
+ value: _value,
112
+ sessionFile: msg.result.sessionFile,
113
+ worktreePath: msg.result.worktreePath,
114
+ error: msg.result.error,
115
+ usage: msg.result.usage,
116
+ durationMs: msg.result.durationMs,
117
+ sessionId: msg.result.sessionId,
118
+ });
119
+ } else {
120
+ pending.resolve(_value);
121
+ }
122
+ }
123
+ } else if (msg.type === "workflow-result") {
124
+ const pending = _pendingCalls.get(msg.callId);
125
+ if (pending) {
126
+ _pendingCalls.delete(msg.callId);
127
+ pending.resolve(msg.result);
128
+ }
129
+ } else if (msg.type === "abort") {
130
+ const err = new WorkflowAbortedError(msg.reason);
131
+ _pendingCalls.forEach((p) => { p.reject(err); });
132
+ _pendingCalls.clear();
133
+ } else if (msg.type === "budget-update" && msg.budget) {
134
+ _budgetData._spentTokens = msg.budget.usedTokens ?? _budgetData._spentTokens;
135
+ _budgetData._spentCost = msg.budget.usedCost ?? _budgetData._spentCost;
136
+ }
137
+ // "budget-warning" is informational; no required handling
138
+ });
139
+
140
+ let _currentPhase = "";
141
+ function phase(name) { _currentPhase = String(name); }
142
+
143
+ function log(msg) {
144
+ try { parentPort.postMessage({ type: "log", phase: _currentPhase, message: String(msg) }); } catch(e) { /* swallow */ }
145
+ }
146
+
147
+ async function agent(firstArg, secondArg) {
148
+ let opts;
149
+ if (typeof firstArg === "string") {
150
+ opts = {
151
+ prompt: firstArg,
152
+ description: (secondArg && typeof secondArg === "object" && secondArg.label) || undefined,
153
+ schema: (secondArg && typeof secondArg === "object" && secondArg.schema) || undefined,
154
+ model: (secondArg && typeof secondArg === "object" && secondArg.model) || $MODEL,
155
+ scene: (secondArg && typeof secondArg === "object" && secondArg.scene) || undefined,
156
+ phase: (secondArg && typeof secondArg === "object" && secondArg.phase) || undefined,
157
+ thinkingLevel: (secondArg && typeof secondArg === "object" && secondArg.thinkingLevel) || $THINKING_LEVEL,
158
+ // step 级 turn 上限(turn limiter;显式 0/负 = 显式不限,压过 spawn watchdog env 兑底,SP-6)
159
+ // ?? 语义保真:仅 null/undefined 归 undefined(走 env 兑底),显式 0 保留(U5 参数 > env)
160
+ maxTurns: (secondArg && typeof secondArg === "object" ? secondArg.maxTurns : undefined) ?? undefined,
161
+ // P4 D9③:step 级 engine 显式指定(仅限必须某引擎独有能力的场景)
162
+ engine: (secondArg && typeof secondArg === "object" && secondArg.engine) || undefined,
163
+ };
164
+ } else if (typeof firstArg === "object" && firstArg !== null) {
165
+ if (firstArg.prompt) {
166
+ opts = firstArg;
167
+ } else if (firstArg.task || firstArg.agent) {
168
+ // fork/worktree/returnMeta forwarded here (parity with agent({prompt,...}) and direct-pass
169
+ // branches). Previously this shortcut dropped them (w1 only noted, not wired).
170
+ opts = {
171
+ prompt: firstArg.task || firstArg.prompt || "",
172
+ description: firstArg.label || firstArg.description,
173
+ agent: firstArg.agent,
174
+ schema: firstArg.schema,
175
+ model: firstArg.model || $MODEL,
176
+ scene: firstArg.scene,
177
+ skill: firstArg.skill,
178
+ timeoutMs: firstArg.timeoutMs,
179
+ maxTurns: firstArg.maxTurns,
180
+ cwd: firstArg.cwd,
181
+ fork: firstArg.fork,
182
+ worktree: firstArg.worktree,
183
+ returnMeta: firstArg.returnMeta,
184
+ thinkingLevel: firstArg.thinkingLevel || $THINKING_LEVEL,
185
+ engine: firstArg.engine,
186
+ };
187
+ } else {
188
+ opts = firstArg;
189
+ }
190
+ } else {
191
+ throw new Error("agent() requires a prompt string or options object as first argument");
192
+ }
193
+
194
+ // Run-level fallback for object branches (opts = firstArg direct assign).
195
+ // Priority: agent() explicit > $MODEL global (set from workerData.model).
196
+ if (!opts.model && $MODEL) opts.model = $MODEL;
197
+ if (!opts.thinkingLevel && $THINKING_LEVEL) opts.thinkingLevel = $THINKING_LEVEL;
198
+
199
+ // Validate known agent() fields to catch API misuse early (_KNOWN_FIELDS at module scope)
200
+ const _unknownFields = Object.keys(opts).filter((k) => !_KNOWN_FIELDS.has(k));
201
+ if (_unknownFields.length > 0) {
202
+ _pushWorkerLog("warn", ["[workflow] agent() received unknown fields: " + _unknownFields.join(", ") + ". Known fields: prompt, description, schema, model, scene, label, task, agent, phase, skill, timeoutMs, maxTurns, cwd, fork, worktree, returnMeta, thinkingLevel, engine"]);
203
+ }
204
+
205
+ const callId = _callIdCounter;
206
+ _callIdCounter++;
207
+ _agentCallCount++;
208
+ if (_callCache.has(callId)) {
209
+ const cached = _callCache.get(callId);
210
+ // 与 live handler 对齐:失败也 resolve(回退 content),不 throw。
211
+ // 见 agent-result 消息处理的注释:拒绝传播失败到 agent 外部。
212
+ // W2 改动 9(c):returnMeta 分支(对称 handler 9b),opts 仍在闭包作用域。
213
+ if (!cached) return undefined;
214
+ const _cachedValue = cached.parsedOutput ?? cached.content;
215
+ if (opts.returnMeta === true) {
216
+ return {
217
+ value: _cachedValue,
218
+ sessionFile: cached.sessionFile,
219
+ worktreePath: cached.worktreePath,
220
+ error: cached.error,
221
+ usage: cached.usage,
222
+ durationMs: cached.durationMs,
223
+ sessionId: cached.sessionId,
224
+ };
225
+ }
226
+ return _cachedValue;
227
+ }
228
+
229
+ const _effectivePhase = opts.phase || _currentPhase;
230
+ delete opts.phase;
231
+
232
+ if (!_safePost({ type: "agent-call", callId, opts, phase: _effectivePhase }, "agent-call")) {
233
+ return Promise.reject(new Error("postMessage failed for agent-call (callId=" + callId + "): see workerLogs"));
234
+ }
235
+ return new Promise((resolve, reject) => {
236
+ _pendingCalls.set(callId, { resolve, reject, returnMeta: opts.returnMeta === true });
237
+ });
238
+ }
239
+
240
+ async function parallel(calls) {
241
+ if (typeof calls === "function") { return calls(); }
242
+ const settled = await Promise.allSettled(calls.map((c) => {
243
+ if (c && typeof c.then === "function") { return c; }
244
+ if (typeof c === "function") { return c(); }
245
+ if (typeof c === "object" && c !== null && (c.task || c.agent)) { return agent(c); }
246
+ return agent(c);
247
+ }));
248
+ return settled.map((r) => {
249
+ if (r.status === "fulfilled") {
250
+ const v = r.value;
251
+ if (v !== null && typeof v === "object" && !Array.isArray(v)) {
252
+ // 主线程 fallback(postAgentResult/postResult serialization failed)回发的对象含 error 字段
253
+ // → 归一化为 failed 形状,与脚本侧 r.status === "failed" 检查统一
254
+ if (typeof v.error === "string" && v.error.length > 0) return { status: "failed", error: v.error };
255
+ return v;
256
+ }
257
+ return { status: "failed", error: "agent returned non-object result (type=" + typeof v + ")" };
258
+ }
259
+ const reason = r.reason;
260
+ const errMsg = reason instanceof Error ? reason.message : String(reason);
261
+ return { status: "failed", error: errMsg };
262
+ });
263
+ }
264
+
265
+ async function pipeline(firstArg, ...restStages) {
266
+ // Single-arg mode: pipeline([stage1, stage2, ...])
267
+ if (Array.isArray(firstArg) && restStages.length === 0) {
268
+ let result;
269
+ for (let i = 0; i < firstArg.length; i++) {
270
+ try { result = await firstArg[i](result); }
271
+ catch (e) {
272
+ const msg = e && e.message ? e.message : String(e);
273
+ _pushWorkerLog("error", ["[pipeline stage " + i + " failed]", msg]);
274
+ throw e;
275
+ }
276
+ }
277
+ return result;
278
+ }
279
+ // Cartesian product mode: pipeline([items], stage1, stage2, ...)
280
+ if (Array.isArray(firstArg) && restStages.length > 0 && typeof restStages[0] === "function") {
281
+ const results = [];
282
+ for (let idx = 0; idx < firstArg.length; idx++) {
283
+ const item = firstArg[idx];
284
+ let val = item;
285
+ let failed = false;
286
+ for (const stage of restStages) {
287
+ if (failed) break;
288
+ try { val = await stage(val); }
289
+ catch (e) {
290
+ const msg = e && e.message ? e.message : String(e);
291
+ _pushWorkerLog("error", ["[pipeline cartesian stage failed for item " + (idx + 1) + "]", msg]);
292
+ val = null; failed = true;
293
+ }
294
+ }
295
+ results.push(val);
296
+ }
297
+ return results;
298
+ }
299
+ throw new Error("pipeline() expects pipeline([stage1, ...]) or pipeline([items], stage1, ...)");
300
+ }
301
+
302
+ // ── workflow global — nested workflow invocation ──
303
+ async function workflow(name, args) {
304
+ if (typeof name !== "string" || name.length === 0) {
305
+ throw new Error("workflow() requires a workflow name string as first argument");
306
+ }
307
+ const workflowArgs = (typeof args === "object" && args !== null) ? args : {};
308
+ const callId = _callIdCounter;
309
+ _callIdCounter++;
310
+ if (!_safePost({ type: "workflow-call", callId, name, args: workflowArgs }, "workflow-call")) {
311
+ return Promise.reject(new Error("postMessage failed for workflow-call (name=" + name + "): see workerLogs"));
312
+ }
313
+ return new Promise((resolve, reject) => {
314
+ _pendingCalls.set(callId, { resolve, reject });
315
+ });
316
+ }
317
+
318
+ // ── User workflow script ──
319
+ // sample exercising template features
320
+ const name = $ARGS.name ?? "default";
321
+ phase("build");
322
+ const spec = await agent("build it", { label: "builder", schema: { type: "object" } });
323
+ const results = await parallel([
324
+ agent({ task: "unit tests", agent: "./tester.md" }),
325
+ agent({ task: "lint", agent: "./linter.md", skill: "lint-skill" }),
326
+ ]);
327
+ await pipeline([(x) => x, (x) => x]);
328
+ await workflow("deploy", { env: "prod" });
329
+ log("done " + name + " " + $WORKSPACE + " " + $BUDGET.remaining());
330
+ module.exports = { execute: async (ctx) => ctx.agent("finalize") };
331
+
332
+ // ── Auto-invoke execute() for module.exports pattern ──
333
+ if (typeof module !== "undefined" && module.exports && typeof module.exports.execute === "function") {
334
+ return await module.exports.execute({ agent, parallel, pipeline, phase, log, workflow, $ARGS, $WORKSPACE, $BUDGET });
335
+ }
336
+ })().then((result) => {
337
+ const runId = (_workerData.args && typeof _workerData.args === "object" && _workerData.args._runId) || "";
338
+ if (!_safePost({ type: "return", runId, result, workerLogs: _workerLogs }, "return")) {
339
+ // [F1] return 值不可克隆(含 function/Symbol/循环引用 → DataCloneError)时 _safePost
340
+ // 只能记日志返回 false——若不补救,worker 将静默 exit(0),主线程收不到任何终态消息,
341
+ // run 永久 running、runAndWait 悬挂。回发可克隆的 error 消息(DataCloneError 详情
342
+ // 已由 _safePost 记入 _workerLogs 随消息带回),让主线程 handleScriptError 接管,
343
+ // run 经既有重试矩阵收敛到终态 failed。
344
+ _safePost({ type: "error", runId, error: "Workflow return value could not be delivered (structured-clone failed) — see workerLogs for the postMessage error", workerLogs: _workerLogs }, "error");
345
+ }
346
+ }).catch((err) => {
347
+ const runId = (_workerData.args && typeof _workerData.args === "object" && _workerData.args._runId) || "";
348
+ _safePost({ type: "error", runId, error: err.message || String(err), workerLogs: _workerLogs }, "error");
349
+ });
@@ -0,0 +1,202 @@
1
+ // src/orchestration/__tests__/agent-call-catch-fallback.test.ts
2
+ //
3
+ // U7: dispatchAgentCall catch 分支回发 agent-result 给 worker(吞错兜底)
4
+ //
5
+ // 场景:executeAgentCall(或 gate.withSlot)抛非 Abort 异常时,原 catch 块仅
6
+ // console.error,worker 内对应 callId 的 pending Promise 永不 resolve → agent()
7
+ // 永久 await → worker 脚本挂死。修复后 catch 块构造 failed AgentResult 并
8
+ // postAgentResult 回 worker,使 pending Promise 能 resolve(结果为 error)。
9
+ //
10
+ // 通过 handleWorkerMessage 触发 dispatchAgentCall(内部函数不 export)。
11
+ // Mock 构造模式参考 agent-call-stream.test.ts。
12
+
13
+ import { describe, expect, it, vi } from "vitest";
14
+
15
+ import { handleWorkerMessage } from "../error-recovery.ts";
16
+ import type { LifecycleDeps, WorkerHandlers } from "../models/ports.ts";
17
+ import { Trace } from "../models/trace.ts";
18
+ import type { WorkflowRun } from "../models/workflow-run.ts";
19
+ import type { AgentResult } from "../models/types.ts";
20
+
21
+ // ── helpers ──────────────────────────────────────────────────
22
+
23
+ /** 构造 status="running" 的 mock WorkflowRun,含 trace/budget/calls/runtime。
24
+ * worker.postMessage 为 vi.fn spy,便于断言回发内容。gate.withSlot 直接 await fn()
25
+ * 让异常透传到外层 .catch(模拟 executeAgentCall 抛错的真实链路)。 */
26
+ function makeRunningRun(runId: string): WorkflowRun {
27
+ const trace = new Trace();
28
+ const controller = new AbortController();
29
+ return {
30
+ runId,
31
+ spec: { scriptName: "test-wf", scriptSource: "agent('hi')", args: {}, runId, slug: undefined },
32
+ state: {
33
+ status: "running" as const,
34
+ reason: undefined,
35
+ trace,
36
+ budget: {
37
+ usedTokens: 0,
38
+ usedCost: 0,
39
+ totalCallCount: 0,
40
+ consume: vi.fn(),
41
+ isExceeded: vi.fn(() => false),
42
+ incrementCallCount: vi.fn(),
43
+ },
44
+ calls: new Map(),
45
+ scriptResult: undefined,
46
+ },
47
+ meta: { startedAt: new Date().toISOString(), workerErrorCount: 0, scriptErrorCount: 0 },
48
+ runtime: {
49
+ controller,
50
+ worker: { postMessage: vi.fn() },
51
+ },
52
+ transition: vi.fn(),
53
+ replaceRuntime: vi.fn(),
54
+ } as unknown as WorkflowRun;
55
+ }
56
+
57
+ /** LifecycleDeps mock,runner.run 可配置为 reject 指定 error。 */
58
+ function makeDeps(opts: { runnerError?: Error } = {}): LifecycleDeps {
59
+ const runnerRun = vi.fn(async () => {
60
+ if (opts.runnerError) throw opts.runnerError;
61
+ return { content: "OK", durationMs: 10, error: undefined, toolCalls: [] } as AgentResult;
62
+ });
63
+ return {
64
+ store: { save: vi.fn(async () => {}) },
65
+ workerHost: { start: vi.fn() },
66
+ runner: { run: runnerRun },
67
+ runs: new Map(),
68
+ eventBus: { emit: vi.fn() },
69
+ onRunDone: vi.fn(),
70
+ log: vi.fn(),
71
+ // streamSink 不设 = undefined(本测试不关心 stream)
72
+ } as unknown as LifecycleDeps;
73
+ }
74
+
75
+ function makeHandlers(): WorkerHandlers {
76
+ return {
77
+ onMessage: vi.fn(async () => {}),
78
+ onError: vi.fn(async () => {}),
79
+ onExit: vi.fn(async () => {}),
80
+ } as unknown as WorkerHandlers;
81
+ }
82
+
83
+ /** agent-call 消息 */
84
+ function makeAgentCallMsg(callId: number): unknown {
85
+ return {
86
+ type: "agent-call",
87
+ callId,
88
+ phase: "test-phase",
89
+ opts: { prompt: "test task", agent: "worker", description: "test-slug" },
90
+ };
91
+ }
92
+
93
+ /** 从 postMessage spy 的所有调用中找 type:"agent-result" 且 callId 匹配的参数。 */
94
+ function findAgentResultPost(
95
+ postMessage: ReturnType<typeof vi.fn>,
96
+ callId: number,
97
+ ): { type: string; callId: number; result: AgentResult; cached: boolean } | undefined {
98
+ for (const call of postMessage.mock.calls) {
99
+ const msg = call[0] as { type?: string; callId?: number; result?: AgentResult; cached?: boolean };
100
+ if (msg?.type === "agent-result" && msg.callId === callId) return msg as never;
101
+ }
102
+ return undefined;
103
+ }
104
+
105
+ /** 静默 console.error(catch 块会打印失败日志,避免污染测试输出)。 */
106
+ function silenceConsoleError(): () => void {
107
+ const spy = vi.spyOn(console, "error").mockImplementation(() => {});
108
+ return () => spy.mockRestore();
109
+ }
110
+
111
+ // ── U7a: 非 Abort 异常 → postAgentResult 回发 failed result ──
112
+
113
+ describe("U7a: catch 分支兜底回发 agent-result(非 Abort 异常)", () => {
114
+ it("runner.run reject 'runner exploded' → worker 收到 {success:false, error:'runner exploded'}", async () => {
115
+ const restore = silenceConsoleError();
116
+ try {
117
+ const deps = makeDeps({ runnerError: new Error("runner exploded") });
118
+ const run = makeRunningRun("wf-catch-001");
119
+ const handlers = makeHandlers();
120
+ const postMessage = run.runtime!.worker.postMessage as ReturnType<typeof vi.fn>;
121
+
122
+ await handleWorkerMessage(run, makeAgentCallMsg(2), deps, handlers);
123
+
124
+ // dispatchAgentCall 内 void withSlot(...)(fire-and-forget),需等微任务完成
125
+ await vi.waitFor(() => {
126
+ expect(findAgentResultPost(postMessage, 2)).toBeDefined();
127
+ });
128
+
129
+ const posted = findAgentResultPost(postMessage, 2)!;
130
+ expect(posted.type).toBe("agent-result");
131
+ expect(posted.callId).toBe(2);
132
+ expect(posted.cached).toBe(false);
133
+ // AgentResult.error 字段承载错误信息(与 resolveAgentOpts 失败路径一致)
134
+ expect(posted.result.error).toBe("runner exploded");
135
+ expect(posted.result.content).toBe("");
136
+
137
+ // state 一致性三件套(review should_fix):
138
+ // trace node 标 failed(不遗留 running 幽灵节点)
139
+ const traceNode = run.state.trace.find(2);
140
+ expect(traceNode?.status).toBe("failed");
141
+ expect(traceNode?.result?.error).toBe("runner exploded");
142
+ // node.live 清除(防内存泄漏)
143
+ expect(traceNode?.live).toBeUndefined();
144
+ // 持久化(catch 是最需留证的场景)
145
+ expect(deps.store.save).toHaveBeenCalled();
146
+ } finally {
147
+ restore();
148
+ }
149
+ });
150
+
151
+ it("非 Error 值 reject(字符串)→ error 字段为该字符串", async () => {
152
+ const restore = silenceConsoleError();
153
+ try {
154
+ const deps = makeDeps({ runnerError: "string failure" as unknown as Error });
155
+ const run = makeRunningRun("wf-catch-002");
156
+ const handlers = makeHandlers();
157
+ const postMessage = run.runtime!.worker.postMessage as ReturnType<typeof vi.fn>;
158
+
159
+ await handleWorkerMessage(run, makeAgentCallMsg(7), deps, handlers);
160
+
161
+ await vi.waitFor(() => {
162
+ expect(findAgentResultPost(postMessage, 7)).toBeDefined();
163
+ });
164
+
165
+ const posted = findAgentResultPost(postMessage, 7)!;
166
+ expect(posted.result.error).toBe("string failure");
167
+ } finally {
168
+ restore();
169
+ }
170
+ });
171
+ });
172
+
173
+ // ── U7b: AbortError → 不回发(预期路径,吞掉) ──
174
+
175
+ describe("U7b: AbortError 不回发 agent-result", () => {
176
+ it("runner.run reject AbortError → postAgentResult 不被调用", async () => {
177
+ const restore = silenceConsoleError();
178
+ try {
179
+ const abortErr = new Error("aborted");
180
+ abortErr.name = "AbortError";
181
+ const deps = makeDeps({ runnerError: abortErr });
182
+ const run = makeRunningRun("wf-catch-003");
183
+ const handlers = makeHandlers();
184
+ const postMessage = run.runtime!.worker.postMessage as ReturnType<typeof vi.fn>;
185
+
186
+ await handleWorkerMessage(run, makeAgentCallMsg(3), deps, handlers);
187
+
188
+ // 等待 fire-and-forget 链路稳定:无 agent-result 回发。
189
+ // 先 flush 微任务队列(多次确保 withSlot promise 已 settle),
190
+ // 再断言断言期内无 agent-result。vi.waitFor 用反向断言会一满足就 return,
191
+ // 故采用:跑若干 microtask tick 后静态断言。
192
+ for (let i = 0; i < 5; i++) {
193
+ // eslint-disable-next-line no-await-in-loop -- 排空微任务队列的固定 tick 循环(vi.waitFor 反向断言会一满足即返回,不适用),非逐项等待
194
+ await Promise.resolve();
195
+ }
196
+ const posted = findAgentResultPost(postMessage, 3);
197
+ expect(posted).toBeUndefined();
198
+ } finally {
199
+ restore();
200
+ }
201
+ });
202
+ });