@zhushanwen/subagent-core 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. package/dist/{chunk-4KN4TTG7.js → chunk-43ONBFZX.js} +1 -1
  2. package/dist/{chunk-X7SZ5HLQ.js → chunk-V3VHZ2VX.js} +20 -27
  3. package/dist/execution/engine/engines/zcode/constants.cjs +29 -41
  4. package/dist/execution/engine/engines/zcode/constants.d.cts +65 -53
  5. package/dist/execution/engine/engines/zcode/constants.d.ts +65 -53
  6. package/dist/execution/engine/engines/zcode/constants.js +17 -27
  7. package/dist/execution/engine/engines/zcode/reader.d.cts +2 -1
  8. package/dist/execution/engine/engines/zcode/reader.d.ts +2 -1
  9. package/dist/execution/engine/engines/zcode/reader.js +2 -2
  10. package/dist/index.cjs +17100 -14082
  11. package/dist/index.d.cts +5336 -4033
  12. package/dist/index.d.ts +5336 -4033
  13. package/dist/index.js +17020 -14055
  14. package/dist/{types-DpUO16pj.d.cts → types-C3fE3R9x.d.cts} +280 -112
  15. package/dist/{types-DpUO16pj.d.ts → types-C3fE3R9x.d.ts} +280 -112
  16. package/dist.bundle/index.cjs +34933 -0
  17. package/package.json +3 -3
  18. package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +0 -1
  19. package/src/__tests__/append-system-prompt-assembly.test.ts +8 -7
  20. package/src/__tests__/fr4-get-state-handshake.test.ts +2 -2
  21. package/src/__tests__/m2-append-content-probe.test.ts +0 -1
  22. package/src/__tests__/manifest-store.test.ts +10 -9
  23. package/src/__tests__/record-store-cache.test.ts +0 -2
  24. package/src/__tests__/record-store-index.test.ts +1 -2
  25. package/src/__tests__/review-fix-loop-script.test.ts +4 -2
  26. package/src/__tests__/review-fix-loop-utils.test.ts +10 -10
  27. package/src/__tests__/robustness-low-batch1.test.ts +6 -5
  28. package/src/__tests__/robustness-medium-batch1.test.ts +2 -2
  29. package/src/__tests__/robustness-medium-batch2.test.ts +17 -6
  30. package/src/__tests__/robustness-medium-batch4.test.ts +2 -2
  31. package/src/__tests__/session-runner.test.ts +1 -1
  32. package/src/core/__tests__/host-services.test.ts +2 -2
  33. package/src/core/__tests__/logger.test.ts +1 -1
  34. package/src/core/error-message.ts +9 -0
  35. package/src/core/host-services.ts +21 -5
  36. package/src/core/notify-ports.ts +18 -5
  37. package/src/execution/__tests__/agent-profile.test.ts +4 -4
  38. package/src/execution/__tests__/agent-registry.test.ts +370 -0
  39. package/src/execution/__tests__/agent-result-mapper.test.ts +18 -2
  40. package/src/execution/__tests__/agents-assembly.test.ts +2 -2
  41. package/src/execution/__tests__/alive-store.test.ts +1 -1
  42. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +6 -6
  43. package/src/execution/__tests__/chat-engine-routing.test.ts +68 -33
  44. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +4 -4
  45. package/src/execution/__tests__/cold-resurrect.test.ts +283 -0
  46. package/src/execution/__tests__/collect-budget.test.ts +291 -0
  47. package/src/execution/__tests__/collect-coordinator-service.test.ts +327 -0
  48. package/src/execution/__tests__/collect-coordinator.test.ts +371 -0
  49. package/src/execution/__tests__/collect-mixed-dispatch.test.ts +262 -0
  50. package/src/execution/__tests__/config-collect-sync.test.ts +120 -0
  51. package/src/execution/__tests__/config.test.ts +1 -1
  52. package/src/execution/__tests__/delivery-methods.test.ts +75 -62
  53. package/src/execution/__tests__/descendant-sweep-guards.test.ts +220 -0
  54. package/src/execution/__tests__/descendant-sweep.test.ts +6 -6
  55. package/src/execution/__tests__/dialog-queue.test.ts +2 -2
  56. package/src/execution/__tests__/engine-model-validation.test.ts +741 -0
  57. package/src/execution/__tests__/epipe-fallback.test.ts +29 -24
  58. package/src/execution/__tests__/execute-and-await-worktree.test.ts +21 -91
  59. package/src/execution/__tests__/execute-nesting.test.ts +34 -188
  60. package/src/execution/__tests__/execution-record.test.ts +217 -60
  61. package/src/execution/__tests__/execution-runtime-face.test.ts +5 -5
  62. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +4 -4
  63. package/src/execution/__tests__/finalize-record.test.ts +173 -17
  64. package/src/execution/__tests__/finalized-marker.test.ts +1 -1
  65. package/src/execution/__tests__/gc-timer.test.ts +56 -3
  66. package/src/execution/__tests__/get-record-for-action-restart.test.ts +12 -12
  67. package/src/execution/__tests__/get-state-handshake.test.ts +1 -1
  68. package/src/execution/__tests__/helpers/mock-extension-api.ts +1 -1
  69. package/src/execution/__tests__/helpers/session-runner-mocks.ts +113 -0
  70. package/src/execution/__tests__/helpers/spawn-mock.ts +150 -3
  71. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +137 -0
  72. package/src/execution/__tests__/keep-alive-no-progress.test.ts +50 -148
  73. package/src/execution/__tests__/kill-all-escalation.test.ts +2 -2
  74. package/src/execution/__tests__/lifecycle-predicates.test.ts +1 -2
  75. package/src/execution/__tests__/manifest-parentid.test.ts +4 -4
  76. package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +1 -1
  77. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +1 -1
  78. package/src/execution/__tests__/model-resolver.test.ts +2 -2
  79. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +23 -121
  80. package/src/execution/__tests__/nested-visibility.test.ts +2 -0
  81. package/src/execution/__tests__/notify-batch.test.ts +500 -0
  82. package/src/execution/__tests__/notify-ledger.test.ts +1000 -18
  83. package/src/execution/__tests__/output-collector.test.ts +117 -6
  84. package/src/execution/__tests__/pi-invocation.test.ts +36 -4
  85. package/src/execution/__tests__/record-entry-collect.test.ts +102 -0
  86. package/src/execution/__tests__/record-store.test.ts +10 -9
  87. package/src/execution/__tests__/records-cwd-isolation.test.ts +2 -2
  88. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +29 -132
  89. package/src/execution/__tests__/recursive-visibility-env.test.ts +55 -179
  90. package/src/execution/__tests__/relay-env.test.ts +12 -1
  91. package/src/execution/__tests__/rpc-mode.test.ts +2 -2
  92. package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +387 -0
  93. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +15 -9
  94. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +4 -4
  95. package/src/execution/__tests__/run-spawn-edges.test.ts +17 -15
  96. package/src/execution/__tests__/run-spawn-integration.test.ts +2 -2
  97. package/src/execution/__tests__/run-spawn-resume.test.ts +2 -2
  98. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +2 -2
  99. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +4 -4
  100. package/src/execution/__tests__/service-kill-escalation.test.ts +7 -5
  101. package/src/execution/__tests__/session-context-resolver.test.ts +0 -2
  102. package/src/execution/__tests__/session-file-gc.test.ts +36 -1
  103. package/src/execution/__tests__/session-pending.test.ts +257 -14
  104. package/src/execution/__tests__/session-reconstructor.test.ts +366 -15
  105. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +1 -1
  106. package/src/execution/__tests__/session-runner-close-prune.test.ts +2 -2
  107. package/src/execution/__tests__/session-runner-dispatch.test.ts +489 -0
  108. package/src/execution/__tests__/session-runner-epipe.test.ts +3 -3
  109. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +5 -4
  110. package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +290 -0
  111. package/src/execution/__tests__/session-runner-schema-env.test.ts +7 -5
  112. package/src/execution/__tests__/settled-watchdog.test.ts +282 -47
  113. package/src/execution/__tests__/spawn-args.test.ts +11 -10
  114. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +26 -23
  115. package/src/execution/__tests__/spawn-event-adapter.test.ts +3 -3
  116. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +5 -4
  117. package/src/execution/__tests__/spawned-children.test.ts +2 -1
  118. package/src/execution/__tests__/start-collect-guard.test.ts +255 -0
  119. package/src/execution/__tests__/start-sync-model-guard.test.ts +5 -5
  120. package/src/execution/__tests__/stdin-writer.test.ts +6 -4
  121. package/src/execution/__tests__/stream-sink-retirement.test.ts +6 -6
  122. package/src/execution/__tests__/subagent-actions-core.test.ts +48 -22
  123. package/src/execution/__tests__/subagent-service-message-close.test.ts +29 -26
  124. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +21 -8
  125. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +27 -22
  126. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +30 -29
  127. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +45 -20
  128. package/src/execution/__tests__/subagent-service.test.ts +37 -30
  129. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +88 -12
  130. package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +75 -0
  131. package/src/execution/__tests__/subprocess-agent-runner.test.ts +22 -20
  132. package/src/execution/__tests__/sync-collect-recovery.test.ts +1117 -0
  133. package/src/execution/__tests__/temp-prompt.test.ts +1 -1
  134. package/src/execution/__tests__/timeout-integration.test.ts +6 -5
  135. package/src/execution/__tests__/tombstone-store.test.ts +1 -1
  136. package/src/execution/__tests__/turn-limiter-semantics.test.ts +1 -1
  137. package/src/execution/__tests__/turn-limiter.test.ts +1 -1
  138. package/src/execution/__tests__/ui-request-handler-factory.test.ts +12 -6
  139. package/src/execution/__tests__/ui-request-handler.test.ts +4 -3
  140. package/src/execution/__tests__/ui-request-queue.test.ts +142 -2
  141. package/src/execution/__tests__/worktree-git-ops.test.ts +1 -1
  142. package/src/execution/__tests__/worktree-manager.test.ts +14 -5
  143. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +4 -3
  144. package/src/execution/__tests__/worktree-reconcile-aging.test.ts +2 -2
  145. package/src/execution/__tests__/worktree-reconcile.integration.test.ts +1 -1
  146. package/src/execution/__tests__/worktree-registry.test.ts +1 -1
  147. package/src/execution/agent-registry.ts +74 -30
  148. package/src/execution/agent-result-mapper.ts +3 -0
  149. package/src/execution/cold-resurrect.ts +199 -0
  150. package/src/execution/collect-coordinator.ts +200 -0
  151. package/src/execution/config.ts +77 -5
  152. package/src/execution/engine/__tests__/common/capability-gate.test.ts +91 -0
  153. package/src/execution/engine/__tests__/common/data-dir.test.ts +1 -1
  154. package/src/execution/engine/__tests__/common/event-journal.test.ts +1 -1
  155. package/src/execution/engine/__tests__/common/journal-wiring.test.ts +84 -0
  156. package/src/execution/engine/__tests__/common/kill-chain.test.ts +34 -7
  157. package/src/execution/engine/__tests__/common/persona-router.test.ts +4 -5
  158. package/src/execution/engine/__tests__/common/pool-manager.test.ts +213 -53
  159. package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +149 -0
  160. package/src/execution/engine/__tests__/common/session-view-service.test.ts +499 -0
  161. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +22 -112
  162. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +15 -28
  163. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +1 -1
  164. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +12 -12
  165. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +2 -2
  166. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +21 -82
  167. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +4 -7
  168. package/src/execution/engine/__tests__/engine-discovery.test.ts +15 -3
  169. package/src/execution/engine/__tests__/model-prompt.test.ts +15 -3
  170. package/src/execution/engine/__tests__/registry.test.ts +4 -2
  171. package/src/execution/engine/__tests__/routing.test.ts +115 -6
  172. package/src/execution/engine/common/capability-gate.ts +92 -0
  173. package/src/execution/engine/common/journal-wiring.ts +97 -0
  174. package/src/execution/engine/common/kill-chain.ts +42 -10
  175. package/src/execution/engine/common/nesting-guard.ts +66 -0
  176. package/src/execution/engine/common/persona-router.ts +18 -8
  177. package/src/execution/engine/common/pool-manager.ts +332 -78
  178. package/src/execution/engine/common/session-view-service.ts +506 -0
  179. package/src/execution/engine/common/session-view-types.ts +125 -0
  180. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +174 -28
  181. package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +257 -0
  182. package/src/execution/{argv-mirror.ts → engine/engines/pi/argv-mirror.ts} +43 -21
  183. package/src/execution/{get-state-handshake.ts → engine/engines/pi/get-state-handshake.ts} +1 -1
  184. package/src/execution/{output-collector.ts → engine/engines/pi/output-collector.ts} +108 -19
  185. package/src/execution/engine/engines/pi/pi-engine.ts +434 -38
  186. package/src/execution/{pi-invocation.ts → engine/engines/pi/pi-invocation.ts} +2 -2
  187. package/src/execution/engine/engines/pi/reader.ts +11 -0
  188. package/src/execution/engine/engines/pi/registration.ts +4 -2
  189. package/src/execution/{session-runner.ts → engine/engines/pi/session-runner.ts} +617 -412
  190. package/src/execution/{spawn-event-adapter.ts → engine/engines/pi/spawn-event-adapter.ts} +75 -34
  191. package/src/execution/{stdin-writer.ts → engine/engines/pi/stdin-writer.ts} +10 -9
  192. package/src/execution/{temp-prompt.ts → engine/engines/pi/temp-prompt.ts} +2 -2
  193. package/src/execution/{turn-limiter.ts → engine/engines/pi/turn-limiter.ts} +1 -1
  194. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +54 -27
  195. package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +321 -0
  196. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +13 -12
  197. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +7 -210
  198. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +6 -110
  199. package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +247 -0
  200. package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +451 -0
  201. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +20 -8
  202. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +77 -110
  203. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +26 -320
  204. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +210 -0
  205. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +353 -0
  206. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +282 -0
  207. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +372 -0
  208. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +48 -148
  209. package/src/execution/engine/engines/zcode/appserver-launcher.ts +167 -0
  210. package/src/execution/engine/engines/zcode/connection.ts +24 -11
  211. package/src/execution/engine/engines/zcode/constants.ts +75 -71
  212. package/src/execution/engine/engines/zcode/golden-sample.ts +8 -46
  213. package/src/execution/engine/engines/zcode/parser.ts +12 -346
  214. package/src/execution/engine/engines/zcode/preparer.ts +12 -143
  215. package/src/execution/engine/engines/zcode/session-channel.ts +351 -39
  216. package/src/execution/engine/engines/zcode/zcode-engine.ts +665 -760
  217. package/src/execution/engine/host-task-spec.ts +32 -36
  218. package/src/execution/engine/model-validation.ts +177 -0
  219. package/src/execution/engine/port.ts +24 -6
  220. package/src/execution/engine/routing.ts +75 -1
  221. package/src/execution/engine/types.ts +30 -103
  222. package/src/execution/execution-record.ts +229 -144
  223. package/src/execution/finalize-record.ts +141 -99
  224. package/src/execution/idle-gc.ts +28 -0
  225. package/src/execution/lifecycle-predicates.ts +1 -1
  226. package/src/execution/notifier.ts +234 -25
  227. package/src/execution/notify-host.ts +232 -0
  228. package/src/execution/record-entry.ts +16 -0
  229. package/src/execution/record-store.ts +280 -103
  230. package/src/execution/round-settlement.ts +93 -0
  231. package/src/execution/session-file-gc.ts +96 -62
  232. package/src/execution/session-reconstructor.ts +256 -143
  233. package/src/execution/sessions-index.ts +117 -47
  234. package/src/execution/settled-watchdog.ts +264 -52
  235. package/src/execution/subagent-actions-core.ts +84 -28
  236. package/src/execution/subagent-service.ts +1225 -964
  237. package/src/execution/subprocess-agent-runner.ts +144 -97
  238. package/src/execution/sync-rebuild.ts +87 -0
  239. package/src/execution/types.ts +72 -0
  240. package/src/execution/ui-request-handler-factory.ts +102 -74
  241. package/src/execution/ui-request-queue.ts +50 -19
  242. package/src/index.ts +442 -316
  243. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +1 -1
  244. package/src/orchestration/__tests__/agent-call-stream.test.ts +2 -2
  245. package/src/orchestration/__tests__/args-validator.test.ts +14 -0
  246. package/src/orchestration/__tests__/config-loader.test.ts +1 -1
  247. package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +1 -1
  248. package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +11 -9
  249. package/src/orchestration/__tests__/execute-agent-call.test.ts +151 -11
  250. package/src/orchestration/__tests__/file-run-store-prune.test.ts +1 -1
  251. package/src/orchestration/__tests__/file-run-store-throttle.test.ts +1 -1
  252. package/src/orchestration/__tests__/file-run-store.test.ts +7 -6
  253. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +2 -2
  254. package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +1 -1
  255. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +3 -3
  256. package/src/orchestration/__tests__/lifecycle.test.ts +7 -5
  257. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +1 -1
  258. package/src/orchestration/__tests__/review-fix-loop-scriptpath-failfast.test.ts +1 -1
  259. package/src/orchestration/__tests__/run-snapshot.test.ts +8 -5
  260. package/src/orchestration/__tests__/script-generate.test.ts +5 -5
  261. package/src/orchestration/__tests__/script-lint.test.ts +17 -0
  262. package/src/orchestration/__tests__/skill-discovery.test.ts +2 -2
  263. package/src/orchestration/__tests__/test-mocks.ts +2 -0
  264. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +7 -6
  265. package/src/orchestration/__tests__/worker-message-pump-finalize-run.test.ts +229 -0
  266. package/src/orchestration/__tests__/{error-recovery-handlers.test.ts → worker-message-pump-handlers.test.ts} +27 -19
  267. package/src/orchestration/__tests__/{error-recovery-postmessage-defense.test.ts → worker-message-pump-postmessage-defense.test.ts} +8 -8
  268. package/src/orchestration/__tests__/{error-recovery-serialize-failed-result.test.ts → worker-message-pump-serialize-failed-result.test.ts} +2 -2
  269. package/src/orchestration/__tests__/{error-recovery-workflow-call.test.ts → worker-message-pump-workflow-call.test.ts} +1 -1
  270. package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +1 -1
  271. package/src/orchestration/__tests__/worker-script-builder.test.ts +1 -1
  272. package/src/orchestration/__tests__/workflow-files.test.ts +10 -10
  273. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +1 -1
  274. package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +1 -1
  275. package/src/orchestration/agent-opts-resolver.ts +1 -1
  276. package/src/orchestration/args-validator.ts +53 -31
  277. package/src/orchestration/execute-agent-call.ts +19 -73
  278. package/src/orchestration/launcher.ts +128 -81
  279. package/src/orchestration/lifecycle.ts +43 -45
  280. package/src/orchestration/models/ports.ts +4 -4
  281. package/src/orchestration/models/run-runtime.ts +2 -2
  282. package/src/orchestration/models/trace.ts +1 -1
  283. package/src/orchestration/models/types.ts +111 -22
  284. package/src/orchestration/models/workflow-run.ts +1 -1
  285. package/src/orchestration/script-lint.ts +91 -58
  286. package/src/orchestration/{error-recovery.ts → worker-message-pump.ts} +157 -150
  287. package/src/shared/__tests__/atomic-write.test.ts +1 -1
  288. package/src/shared/__tests__/meta-parser.test.ts +151 -0
  289. package/src/shared/__tests__/resource-discovery-host-roots.test.ts +5 -5
  290. package/src/shared/__tests__/resource-discovery.test.ts +258 -215
  291. package/src/shared/atomic-write.ts +12 -9
  292. package/src/shared/meta-parser.ts +158 -84
  293. package/src/shared/model-ref.ts +15 -2
  294. package/src/shared/resource-discovery.ts +9 -183
  295. package/src/shared/schema-jsonify.ts +1 -1
  296. package/workflows/review-fix-loop-utils.cjs +151 -89
  297. package/workflows/review-fix-loop-utils.d.cts +287 -0
  298. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
  299. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  300. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  301. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  302. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  303. package/src/execution/engine/engines/zcode/__tests__/preparer-appserver.test.ts +0 -387
  304. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -710
  305. package/src/execution/engine/engines/zcode/appserver-home.ts +0 -442
  306. package/src/execution/engine/engines/zcode/appserver-probe.ts +0 -141
  307. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  308. package/src/execution/execute-options-mapper.ts +0 -115
  309. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -288
@@ -1,7 +1,13 @@
1
1
  /**
2
- * Workflow Extension — error-recovery
2
+ * Workflow Extension — worker-message-pump(原 error-recovery,D5-① 更名)
3
3
  *
4
- * Worker 失败处理 free functions(D-12)。
4
+ * Worker 消息泵 + 失败恢复 free functions(D-12)。承载四类职责:
5
+ * 1. 消息路由:handleWorkerMessage 分发 agent-call / workflow-call / return / error
6
+ * 2. IPC 序列化防御:postMessage 的 DataCloneError 拦截 + fallback 回发(W2)
7
+ * 3. retry/重建:worker/script 错误的指数退避重试 + rebuildRuntime(G3-001)
8
+ * 4. 终态化:finalizeRun ——「transition → closeOut in-flight → save →
9
+ * pending:unregister → onRunDone」终态序列的唯一定义点(D5-② 单点化,收敛原
10
+ * 8 处逐字复制;OR-8 收口步骤与 OR-4/B-4 双围栏内化于本函数)
5
11
  *
6
12
  * 4 个导出函数(domain-models.md §失败处理矩阵):
7
13
  * - handleWorkerMessage(run, raw, deps, handlers) — 路由 agent_call/return/error/log
@@ -33,7 +39,7 @@
33
39
 
34
40
  import { getLogger } from "../core/logger.ts";
35
41
 
36
- import { SLUG_MAX_LENGTH } from "../execution/execute-options-mapper.ts";
42
+ import { SLUG_MAX_LENGTH } from "./models/types.ts";
37
43
  import { createRecord, updateFromEvent } from "../execution/execution-record.ts";
38
44
  import { SubagentStream } from "../execution/stream-sink.ts";
39
45
  import type { AgentEvent } from "../shared/agent-event.ts";
@@ -43,9 +49,10 @@ import { AgentCall } from "./models/agent-call.ts";
43
49
  import type { LifecycleDeps, WorkerHandlers } from "./models/ports.ts";
44
50
  import { RunRuntime } from "./models/run-runtime.ts";
45
51
  import type { WorkerLogEntry } from "./models/types.ts";
46
- import type { AgentCallOpts, AgentResult, ExecutionTraceNode } from "./models/types.ts";
52
+ import type { AgentCallOpts, AgentResult, DoneReason, ExecutionTraceNode } from "./models/types.ts";
47
53
  import type { WorkflowRun } from "./models/workflow-run.ts";
48
54
  import type { WorkerHandle } from "./worker-handle.ts";
55
+ import { toErrorMessage } from "../core/error-message.ts";
49
56
 
50
57
  const logger = getLogger("subagents");
51
58
 
@@ -191,7 +198,7 @@ async function saveRunBestEffort(
191
198
  try {
192
199
  await deps.store.save(run);
193
200
  } catch (err) {
194
- const m = err instanceof Error ? err.message : String(err);
201
+ const m = toErrorMessage(err);
195
202
  logger.error(
196
203
  `[workflow] store.save failed (${context}, runId=${run.runId}): ${m}. ` +
197
204
  "Continuing state-machine finalization (in-memory state already terminal).",
@@ -199,55 +206,106 @@ async function saveRunBestEffort(
199
206
  }
200
207
  }
201
208
 
202
- function delay(ms: number): Promise<void> {
203
- return new Promise((resolve) => {
204
- const timer = setTimeout(resolve, ms);
205
- timer.unref();
206
- });
209
+ // ── finalizeRun(D5-② 终态 coda 单写点) ──────────────────────
210
+
211
+ /** finalizeRun 的可调项。 */
212
+ export interface FinalizeRunOptions {
213
+ /** store.save 失败日志的上下文标记(OB3 排障定位,如 "handleReturn (done,completed)")。 */
214
+ context: string;
215
+ /**
216
+ * 是否调 deps.onRunDone(Interface 层完成通知)。缺省 true。
217
+ * terminateRunningRuns 传 false——session 切换/关闭语境下主 agent 已离开本
218
+ * session,注入完成通知只会把消息发给已离开的 session(对齐 session_start
219
+ * 恢复先例:只发 unregister、不发 onRunDone)。
220
+ */
221
+ notifyDone?: boolean;
207
222
  }
208
223
 
209
224
  /**
210
- * [OR-4][B-4] pending:unregister emit 围栏(M12 同款 try 围栏)。
225
+ * Run 终态五步 coda 的唯一定义点(D5-② + OR-8 收口内化):
226
+ * transition(done) → closeOutInFlightCalls → save(best-effort)→
227
+ * pending:unregister → onRunDone。
211
228
  *
212
- * eventBus 是通知总线(pending-notifications 注销信号灯)——listener 同步抛错不得
213
- * 经 worker-host 绑定处的 `void handlers.onXxx(...)` 变 unhandledRejection 崩宿主。
214
- * catch error 留痕,调用方继续。lifecycle.terminateRunningRuns 的 emit(无
215
- * onRunDone 的终态路径)也消费本函数——围栏语义两处单源。
216
- */
217
- export function emitPendingUnregister(run: WorkflowRun, deps: LifecycleDeps, context: string): void {
218
- try {
219
- deps.log?.("debug", "workflow:error-recovery", "emit pending:unregister", { runId: run.runId, reason: run.state.reason });
220
- deps.eventBus?.emit("pending:unregister", { id: run.runId, reason: run.state.reason ?? "completed" });
221
- deps.log?.("debug", "workflow:error-recovery", "emit pending:unregister done", { runId: run.runId });
222
- } catch (err) {
223
- const m = err instanceof Error ? err.message : String(err);
224
- logger.error(`[workflow] pending:unregister emit failed (${context}): ${m}`);
225
- }
226
- }
227
-
228
- /**
229
- * [OR-4] 终态收尾副作用围栏:emit pending:unregister + onRunDone,各自独立 M12 同款
230
- * try 围栏。
229
+ * 收敛前 8 处逐字复制(本文件 6 处 + lifecycle 2 处)已全部改走本函数。原各副本
230
+ * 的三处微差统一为规范形态(收敛裁决,非行为回归):
231
+ * - transition 失败(M12):一律吞掉并中止后续步骤——并发 abort/terminate 抢先
232
+ * 终态化时 illegal-transition 是预期事件,本路径的 unregister/onRunDone 语义已
233
+ * 由抢先方兑现,重复执行只会造成重复注销/重复通知。
234
+ * - save 失败(SW-DATA-3):一律 best-effort——save 抛错若向上抛,handle*
235
+ * 调用方(worker-host 绑定处 `void handlers.onXxx(...)`)无人接 →
236
+ * unhandledRejection + pending:unregister / onRunDone 不执行 pending 通知
237
+ * 幽灵注销(列表残留永不清理的 running 条目)。
238
+ * - unregister reason:`run.state.reason ?? doneReason`——transition 成功后
239
+ * reason 恒有值(不变式 I2),兜底仅防御异常形态,取本路径的 doneReason 语义
240
+ * 最贴近(原各副本 `?? "completed"` / 固定 "failed" / `?? "time_limited"` 三种
241
+ * 死兜底等价收敛)。
231
242
  *
232
- * 这两个是真实副作用(通知注销 + Interface 层完成回调,后者内部含 evictDoneRunsBeyondCap
233
- * 内存淘汰)——任一同步抛错不得经 worker-host 绑定处的 `void handlers.onXxx(...)`
234
- * unhandledRejection 崩宿主,各自 catch 后 error 留痕,调用方继续。
243
+ * [OR-8] transition 成功后、save 之前 closeOutInFlightCalls——终态收口残留
244
+ * in-flight call(fire-and-forget agent() await / worker 死亡时已 dispatch 的
245
+ * call),先收口再落盘,内存态与持久化快照同一时点收敛(快照不再含 running 节点)。
235
246
  *
236
- * [B-4] 拆分为两个独立围栏(旧实现同一 try:emit 抛错会跳过 onRunDone)——通知总线
237
- * 故障不得吞掉 Interface 层完成回调(runAndWait 轮询依赖 onRunDone 语义收口,被跳过
238
- * 即悬挂)。本函数自身不重试不上抛。
247
+ * [OR-4][B-4] unregister 与 onRunDone 各自独立 try 围栏(不共用一个 try——emit
248
+ * 抛错会跳过 onRunDone):这两个是真实副作用,任一同步抛错不得经 worker-host
249
+ * `void handlers.onXxx(...)` 变 unhandledRejection 崩宿主;通知总线故障也不得吞掉
250
+ * Interface 层完成回调(runAndWait 轮询依赖 onRunDone 语义收口,被跳过即悬挂)。
239
251
  *
240
- * 旧实现四处裸调(handleReturn / handleWorkerError / handleScriptError /
241
- * handleWorkerExit 的终态路径)与 budget 分支的内联围栏不对称——统一收敛到本函数。
252
+ * @returns 是否成功 transition(false = 转移前已被并发终态化,后续步骤未执行)
242
253
  */
243
- export function emitTerminalSideEffects(run: WorkflowRun, deps: LifecycleDeps, context: string): void {
244
- emitPendingUnregister(run, deps, context);
254
+ export async function finalizeRun(
255
+ run: WorkflowRun,
256
+ deps: LifecycleDeps,
257
+ doneReason: DoneReason,
258
+ options: FinalizeRunOptions,
259
+ ): Promise<boolean> {
260
+ try {
261
+ run.transition("done", doneReason);
262
+ } catch (te: unknown) {
263
+ // M12:并发 abort/terminate 导致 illegal-transition 是预期的,可忽略——
264
+ // 抢先方已兑现 unregister/onRunDone,本路径让位(debug 日志留痕)。
265
+ void te;
266
+ deps.log?.("debug", "workflow:worker-message-pump", "finalize skipped: run already terminal", {
267
+ runId: run.runId,
268
+ doneReason,
269
+ context: options.context,
270
+ });
271
+ return false;
272
+ }
273
+ // [OR-8] 终态收口残留 in-flight call(先收口再落盘——快照不再含 running 节点)
274
+ closeOutInFlightCalls(run);
275
+ await saveRunBestEffort(run, deps, options.context);
276
+ deps.log?.("debug", "workflow:worker-message-pump", "run finalized", {
277
+ runId: run.runId,
278
+ reason: run.state.reason,
279
+ context: options.context,
280
+ });
281
+ // [OR-4] emit pending:unregister 独立围栏(M12 同款)——listener 同步抛错 error
282
+ // 留痕后继续,不崩宿主、不跳过 onRunDone
245
283
  try {
246
- deps.onRunDone?.(run);
284
+ deps.eventBus?.emit("pending:unregister", {
285
+ id: run.runId,
286
+ reason: run.state.reason ?? doneReason,
287
+ });
247
288
  } catch (err) {
248
- const m = err instanceof Error ? err.message : String(err);
249
- logger.error(`[workflow] onRunDone failed (${context}): ${m}`);
289
+ const m = toErrorMessage(err);
290
+ logger.error(`[workflow] pending:unregister emit failed (${options.context}): ${m}`);
250
291
  }
292
+ // [OR-4][B-4] onRunDone 独立围栏(与 emit 拆分——emit 抛错不得吞掉完成回调)
293
+ if (options.notifyDone !== false) {
294
+ try {
295
+ deps.onRunDone?.(run);
296
+ } catch (err) {
297
+ const m = toErrorMessage(err);
298
+ logger.error(`[workflow] onRunDone failed (${options.context}): ${m}`);
299
+ }
300
+ }
301
+ return true;
302
+ }
303
+
304
+ function delay(ms: number): Promise<void> {
305
+ return new Promise((resolve) => {
306
+ const timer = setTimeout(resolve, ms);
307
+ timer.unref();
308
+ });
251
309
  }
252
310
 
253
311
  // ── rebuildRuntime(G3-001 整重建) ─────────────────────────
@@ -395,29 +453,15 @@ function remainingTimeBudgetMs(run: WorkflowRun): number | undefined {
395
453
  * 重试前发现时间预算已耗尽的收尾:不 rebuild,直接 done,time_limited 终态。
396
454
  *
397
455
  * 副作用与 handleWorkerError 超限路径对齐:transition + 持久化 + 注销
398
- * pending-notification + onRunDone。transition 单独 try(M12)——并发 abort 导致
399
- * illegal-transition 是预期的,可忽略。
456
+ * pending-notification + onRunDone(D5-② 收敛为 finalizeRun 单写点)。
400
457
  */
401
458
  async function finalizeTimeBudgetExhausted(run: WorkflowRun, deps: LifecycleDeps): Promise<void> {
402
- deps.log?.("debug", "workflow:error-recovery", "time budget exhausted on rebuild, transition done", {
459
+ deps.log?.("debug", "workflow:worker-message-pump", "time budget exhausted on rebuild, transition done", {
403
460
  runId: run.runId,
404
461
  budgetTimeMs: run.spec.budgetTimeMs,
405
462
  });
406
463
  run.state.error = run.state.error ?? `Time budget exhausted (${run.spec.budgetTimeMs} ms wall clock) before retry rebuild`;
407
- let transitioned = false;
408
- try {
409
- run.transition("done", "time_limited");
410
- transitioned = true;
411
- } catch (te: unknown) {
412
- // run 可能在检查后、transition 前被并发 abort——预期,不记错
413
- void te;
414
- }
415
- if (!transitioned) return;
416
- // [OR-8] 终态收口残留 in-flight call(先收口再落盘)
417
- closeOutInFlightCalls(run);
418
- await saveRunBestEffort(run, deps, "finalizeTimeBudgetExhausted (done,time_limited)");
419
- // [OR-4] M12 同款围栏(终态副作用不经 void 变 unhandledRejection)
420
- emitTerminalSideEffects(run, deps, "finalizeTimeBudgetExhausted (done,time_limited)");
464
+ await finalizeRun(run, deps, "time_limited", { context: "time budget exhausted on rebuild" });
421
465
  }
422
466
 
423
467
  /**
@@ -455,7 +499,7 @@ export function rebuildRuntime(
455
499
  }
456
500
  // OB3(可观察性):rebuild 关键节点 debug 日志——此前函数体 0 处 deps.log,
457
501
  // 崩溃自愈只能靠行为证据诊断(L1 入口 / L2 重排 / L3 discard / L4 完成)。
458
- deps.log?.("debug", "workflow:error-recovery", "runtime rebuild start", {
502
+ deps.log?.("debug", "workflow:worker-message-pump", "runtime rebuild start", {
459
503
  runId: run.runId,
460
504
  budgetTimeMs: run.spec.budgetTimeMs,
461
505
  });
@@ -477,7 +521,7 @@ export function rebuildRuntime(
477
521
  const remainingBudgetMs = remainingTimeBudgetMs(run);
478
522
  if (remainingBudgetMs !== undefined && remainingBudgetMs > 0 && deps.scheduleTimeBudget) {
479
523
  timeBudgetTimer = deps.scheduleTimeBudget(run.runId, remainingBudgetMs);
480
- deps.log?.("debug", "workflow:error-recovery", "time budget rescheduled", {
524
+ deps.log?.("debug", "workflow:worker-message-pump", "time budget rescheduled", {
481
525
  runId: run.runId,
482
526
  budgetTimeMs: remainingBudgetMs,
483
527
  });
@@ -499,12 +543,12 @@ export function rebuildRuntime(
499
543
  // 孤儿守卫(isOrphanedCall)拦截,trace.update 的瞬时污染由 executeAgentCall
500
544
  // 的 isOrphaned 谓词(OB2)拦截,此处不重复设防。
501
545
  const discardedCallIds = discardInFlightCalls(run);
502
- deps.log?.("debug", "workflow:error-recovery", "in-flight calls discarded", {
546
+ deps.log?.("debug", "workflow:worker-message-pump", "in-flight calls discarded", {
503
547
  runId: run.runId,
504
548
  callIds: discardedCallIds,
505
549
  count: discardedCallIds.length,
506
550
  });
507
- deps.log?.("debug", "workflow:error-recovery", "runtime rebuild complete", {
551
+ deps.log?.("debug", "workflow:worker-message-pump", "runtime rebuild complete", {
508
552
  runId: run.runId,
509
553
  });
510
554
  }
@@ -574,7 +618,7 @@ export async function handleWorkerMessage(
574
618
  `[workflow] unknown worker message type dropped (runId=${run.runId}): ` +
575
619
  `${JSON.stringify((msg as { type?: unknown }).type)}`,
576
620
  );
577
- deps.log?.("warn", "workflow:error-recovery", "unknown worker message type", {
621
+ deps.log?.("warn", "workflow:worker-message-pump", "unknown worker message type", {
578
622
  runId: run.runId,
579
623
  type: (msg as { type?: unknown }).type,
580
624
  });
@@ -596,7 +640,7 @@ function handleWorkerLog(run: WorkflowRun, msg: LogMsg, deps: LifecycleDeps): vo
596
640
  if (run.state.errorLogs.length > MAX_ERROR_LOGS) {
597
641
  run.state.errorLogs = run.state.errorLogs.slice(-MAX_ERROR_LOGS);
598
642
  }
599
- deps.log?.("debug", "workflow:error-recovery", "worker log", {
643
+ deps.log?.("debug", "workflow:worker-message-pump", "worker log", {
600
644
  runId: run.runId,
601
645
  phase: msg.phase,
602
646
  message,
@@ -623,6 +667,16 @@ function handleWorkerLog(run: WorkflowRun, msg: LogMsg, deps: LifecycleDeps): vo
623
667
  * completion 不投递——rebuild 不改 status,第 1 层拦不住跨 runtime 代际的迟到
624
668
  * 结果(S7-second 竞态:旧失败结果投给新 worker 劫持重跑 pending → 假成功)。
625
669
  */
670
+ /**
671
+ * M4: agent-call 消息 IPC 字段校验谓词——畸形(opts 非对象/缺失、callId 非数字、
672
+ * prompt 缺失)= true。提取为谓词保持 dispatchAgentCall 主流程可读(圈复杂度门禁)。
673
+ */
674
+ function isMalformedAgentCallMsg(msg: AgentCallMsg): boolean {
675
+ return typeof msg.callId !== "number" || !Number.isFinite(msg.callId) ||
676
+ typeof msg.opts !== "object" || msg.opts === null ||
677
+ typeof msg.opts.prompt !== "string";
678
+ }
679
+
626
680
  function dispatchAgentCall(
627
681
  run: WorkflowRun,
628
682
  msg: AgentCallMsg,
@@ -631,9 +685,7 @@ function dispatchAgentCall(
631
685
  // M4: IPC 字段校验——畸形 agent-call 消息(opts 非对象/缺失、callId 非数字、prompt 缺失)
632
686
  // 不写 trace / 不 postAgentResult——这类消息通常意味着 worker 模块版本不匹配或内存损坏,
633
687
  // 回发结果给 worker 也没意义(worker 可能已崩)。仅记日志,让 worker timeout/exit 路径接管。
634
- if (typeof msg.callId !== "number" || !Number.isFinite(msg.callId) ||
635
- typeof msg.opts !== "object" || msg.opts === null ||
636
- typeof msg.opts.prompt !== "string") {
688
+ if (isMalformedAgentCallMsg(msg)) {
637
689
  logger.error(`[workflow] malformed agent-call message: callId=${JSON.stringify(msg.callId)}, opts=${JSON.stringify(msg.opts)?.slice(0, MALFORMED_MSG_LOG_PREVIEW_CHARS)}`);
638
690
  return;
639
691
  }
@@ -650,12 +702,14 @@ function dispatchAgentCall(
650
702
  // slug 复用 agentName(超长截断),live record 的 slug 仅用于 TUI 展示。
651
703
  const liveSlug = agentName.length > SLUG_MAX_LENGTH ? agentName.slice(0, SLUG_MAX_LENGTH) : agentName;
652
704
  const now = new Date().toISOString();
705
+ // 未显式指定 model 的展示口径(live record 与 trace node 共用,两处一致)。
706
+ const model = msg.opts.model ?? "default";
653
707
  // live record:收口 agent 执行过程中的 text/thinking/toolCalls/usage,
654
708
  // 供 TUI 在 agent 运行期间显示进度(getEventLog/getCurrentActivity)。
655
709
  // 完成时由下方 .then 清除(终态由 node.result 承载)。
656
710
  const liveRecord = createRecord(String(msg.callId), {
657
711
  agent: agentName,
658
- model: msg.opts.model ?? "default",
712
+ model,
659
713
  mode: "background",
660
714
  task: msg.opts.prompt,
661
715
  slug: liveSlug,
@@ -665,7 +719,7 @@ function dispatchAgentCall(
665
719
  stepIndex: msg.callId,
666
720
  agent: agentName,
667
721
  task: msg.opts.prompt,
668
- model: msg.opts.model ?? "default",
722
+ model,
669
723
  status: "running" as const,
670
724
  phase: msg.phase,
671
725
  startedAt: now,
@@ -705,7 +759,7 @@ function dispatchAgentCall(
705
759
  });
706
760
  postAgentResult(run, msg.callId, errorResult, false);
707
761
  deps.store.save(run).catch((e: unknown) => {
708
- logger.error(`[workflow] store.save failed (resolveAgentOpts): ${e instanceof Error ? e.message : String(e)}`);
762
+ logger.error(`[workflow] store.save failed (resolveAgentOpts): ${toErrorMessage(e)}`);
709
763
  });
710
764
  return;
711
765
  }
@@ -768,7 +822,7 @@ function dispatchAgentCall(
768
822
  // budget 同步 / 持久化,仅留日志。executeAgentCall 内 finalizeCall 的
769
823
  // trace.update 若已命中重跑新节点(瞬时污染),由重跑完成时的 update 覆盖。
770
824
  if (isOrphanedCall(run, msg.callId, call)) {
771
- deps.log?.("debug", "workflow:error-recovery", "orphan agent call completion dropped", { runId: run.runId, callId: msg.callId });
825
+ deps.log?.("debug", "workflow:worker-message-pump", "orphan agent call completion dropped", { runId: run.runId, callId: msg.callId });
772
826
  return;
773
827
  }
774
828
  if (call.result) postAgentResult(run, msg.callId, call.result, false);
@@ -776,44 +830,24 @@ function dispatchAgentCall(
776
830
  // 后同步 worker $BUDGET(否则 $BUDGET.spent()/remaining() 恒为 0)
777
831
  postBudgetUpdate(run);
778
832
  deps.store.save(run).catch((e: unknown) => {
779
- const m = e instanceof Error ? e.message : String(e);
833
+ const m = toErrorMessage(e);
780
834
  logger.error(`[workflow] store.save failed (agent call ${msg.callId}): ${m}`);
781
835
  });
782
836
 
783
837
  // C-2:budget 超限 → 终止整个 run(避免继续 spawn 烧预算)
784
838
  // 内联 terminate(不调 lifecycle.abortRun 避免 engine 内循环依赖):
785
839
  // 若 run 仍非终态,transition done,budget_limited + 持久化。
786
- // 上方 status !== "running" 已保证此处非 done(且 transition 内含 done no-op 守卫)。
840
+ // 上方 status !== "running" 已保证此处非 done(且 finalizeRun 内含 done 让位守卫)。
787
841
  if (run.state.budget.isExceeded()) {
788
842
  run.state.error = run.state.error ?? "Budget exceeded";
789
- deps.log?.("debug", "workflow:error-recovery", "budget exceeded, transition done", { runId: run.runId });
790
- // M12: transition 单独 try——并发 abort 导致 illegal-transition 是预期的,可忽略
791
- let transitioned = false;
792
- try {
793
- run.transition("done", "budget_limited");
794
- transitioned = true;
795
- } catch (te: unknown) {
796
- // run 可能在 budget 检查后、transition 前被并发 abort——预期,不记错
797
- void te;
798
- }
799
- if (transitioned) {
800
- // [OR-8] 终态收口残留 in-flight call(先收口再落盘)
801
- closeOutInFlightCalls(run);
802
- deps.store.save(run).catch((e: unknown) => {
803
- const m = e instanceof Error ? e.message : String(e);
804
- logger.error(`[workflow] store.save failed (budget done): ${m}`);
805
- });
806
- deps.log?.("debug", "workflow:error-recovery", "run saved after budget done", { runId: run.runId, reason: run.state.reason });
807
- // M12: onRunDone/emit 单独 try——这些是真实副作用,错误不应被静默吞掉
808
- //([OR-4] 围栏收敛到共享 helper,语义与原内联围栏一致)
809
- emitTerminalSideEffects(run, deps, "budget done");
810
- }
843
+ deps.log?.("debug", "workflow:worker-message-pump", "budget exceeded, transition done", { runId: run.runId });
844
+ void finalizeRun(run, deps, "budget_limited", { context: "agent call budget done" });
811
845
  }
812
846
  })
813
847
  .catch((err: unknown) => {
814
848
  // pre-abort 检查(原 gate.withSlot 语义)在 dispatchCall 入口 reject AbortError——预期,不记错。
815
849
  if (err instanceof Error && err.name === "AbortError") return;
816
- const message = err instanceof Error ? err.message : String(err);
850
+ const message = toErrorMessage(err);
817
851
  logger.error(`[workflow] agent call ${msg.callId} failed: ${message}`);
818
852
  // 兜底回发:executeAgentCall 抛非 Abort 异常时(如 runner undefined 的 TypeError、
819
853
  // dispatchCall 内部 bug)原 catch 仅 console.error,worker 内对 callId 的 pending
@@ -826,7 +860,7 @@ function dispatchAgentCall(
826
860
  // 留日志,全部跳过。node.live 无条件先清(旧节点已脱离 trace,防御性统一)。
827
861
  node.live = undefined;
828
862
  if (isOrphanedCall(run, msg.callId, call)) {
829
- deps.log?.("debug", "workflow:error-recovery", "orphan agent call failure dropped", { runId: run.runId, callId: msg.callId });
863
+ deps.log?.("debug", "workflow:worker-message-pump", "orphan agent call failure dropped", { runId: run.runId, callId: msg.callId });
830
864
  return;
831
865
  }
832
866
  const errorResult: AgentResult = { content: "", error: message };
@@ -850,7 +884,7 @@ function dispatchAgentCall(
850
884
  // S2: 与 .then 对称——catch 路径也同步 worker $BUDGET(幂等)
851
885
  postBudgetUpdate(run);
852
886
  deps.store.save(run).catch((e: unknown) => {
853
- logger.error(`[workflow] store.save failed (catch fallback): ${e instanceof Error ? e.message : String(e)}`);
887
+ logger.error(`[workflow] store.save failed (catch fallback): ${toErrorMessage(e)}`);
854
888
  });
855
889
  });
856
890
  }
@@ -861,7 +895,7 @@ function dispatchAgentCall(
861
895
  * postResult(workflow-call)与 postAgentResult(agent-call)各自前缀不同,故 prefix 参数化,
862
896
  * 共享返回类型与构造逻辑,避免字面量重复导致形状漂移。
863
897
  *
864
- * W2 防御关键纯函数——export 供独立单测(error-recovery-serialize-failed-result.test.ts)验证
898
+ * W2 防御关键纯函数——export 供独立单测(worker-message-pump-serialize-failed-result.test.ts)验证
865
899
  * 返回 shape `{content:"", error:"<prefix>: <errMsg>"}`,确保两条 fallback 路径(workflow-call /
866
900
  * agent-call)共享同一构造逻辑不漂移。
867
901
  */
@@ -905,7 +939,7 @@ function dispatchWorkflowCall(
905
939
  result,
906
940
  });
907
941
  } catch (err) {
908
- const errMsg = err instanceof Error ? err.message : String(err);
942
+ const errMsg = toErrorMessage(err);
909
943
  logger.error(`[workflow] postResult (workflow-call callId=${msg.callId}) failed: ${errMsg}. Sending error fallback.`);
910
944
  // 回发纯字符串 fallback result(必可克隆),让 worker pending resolve
911
945
  try {
@@ -935,7 +969,7 @@ function dispatchWorkflowCall(
935
969
  .catch((err: unknown) => {
936
970
  postResult({
937
971
  content: "",
938
- error: err instanceof Error ? err.message : String(err),
972
+ error: toErrorMessage(err),
939
973
  });
940
974
  });
941
975
  }
@@ -958,7 +992,7 @@ function postAgentResult(
958
992
  try {
959
993
  run.runtime?.worker.postMessage({ type: "agent-result", callId, result, cached });
960
994
  } catch (err) {
961
- const msg = err instanceof Error ? err.message : String(err);
995
+ const msg = toErrorMessage(err);
962
996
  logger.error(`[workflow] postAgentResult failed (callId=${callId}): ${msg}. Result likely contains non-cloneable value.`);
963
997
  // 回发纯字符串 fallback result(必可克隆),让 worker pending resolve(避免永久挂起)
964
998
  try {
@@ -981,7 +1015,7 @@ function postAgentResult(
981
1015
  * 更新 spent()/remaining())。每次 agent 调用消费 usage 后发送,保持 worker 内 $BUDGET
982
1016
  * 与主线程 Budget 值对象同步。
983
1017
  *
984
- * D-12 regression fix (round-2 #1):重建 budget-update 发送方。被 error-recovery 主路径调用
1018
+ * D-12 regression fix (round-2 #1):重建 budget-update 发送方。被 worker-message-pump 主路径调用
985
1019
  * (dispatch 后同步 worker $BUDGET)——单一实现,避免消息形状漂移。
986
1020
  */
987
1021
  export function postBudgetUpdate(run: WorkflowRun): void {
@@ -994,7 +1028,7 @@ export function postBudgetUpdate(run: WorkflowRun): void {
994
1028
  },
995
1029
  });
996
1030
  } catch (err) {
997
- const msg = err instanceof Error ? err.message : String(err);
1031
+ const msg = toErrorMessage(err);
998
1032
  // budget 是纯 number 不太可能失败,但防御性兜底——budget 同步非关键(worker 仍可
999
1033
  // 基于 $BUDGET.spent() 自行累计),失败仅记日志,不中断调用方流程。
1000
1034
  logger.error(`[workflow] postBudgetUpdate failed: ${msg}. Budget sync to worker skipped (non-critical).`);
@@ -1009,7 +1043,7 @@ async function handleReturn(
1009
1043
  msg: ReturnMsg,
1010
1044
  deps: LifecycleDeps,
1011
1045
  ): Promise<void> {
1012
- deps.log?.("debug", "workflow:error-recovery", "handleReturn", { runId: run.runId, status: run.state.status });
1046
+ deps.log?.("debug", "workflow:worker-message-pump", "handleReturn", { runId: run.runId, status: run.state.status });
1013
1047
  // 捕获 worker 诊断日志(P2-2)
1014
1048
  // L9: 追加而非覆盖——保留重试历史的诊断日志(各 worker 实例的 console 输出)
1015
1049
  if (msg.workerLogs && msg.workerLogs.length > 0) {
@@ -1019,16 +1053,9 @@ async function handleReturn(
1019
1053
  }
1020
1054
  }
1021
1055
  run.state.scriptResult = msg.result;
1022
- run.transition("done", "completed");
1023
- // [OR-8] 脚本 return 时可能仍有 fire-and-forget 的 in-flight call(不 await)——
1024
- // 终态收口为 cancelled,先收口再落盘(快照不再含 running 节点)
1025
- closeOutInFlightCalls(run);
1026
- // [SW-DATA-3] save 失败不阻断终态推进(原 await 裸抛 → unhandledRejection + 幽灵注销)
1027
- await saveRunBestEffort(run, deps, "handleReturn (done,completed)");
1028
- deps.log?.("debug", "workflow:error-recovery", "run saved after return", { runId: run.runId, reason: run.state.reason });
1029
1056
  // C-4: run 到达 done 终态 → 注销 pending-notification + 通知 Interface 层
1030
- // [OR-4] M12 同款围栏(此前裸调——listener 同步抛错经 void unhandledRejection)
1031
- emitTerminalSideEffects(run, deps, "handleReturn (done,completed)");
1057
+ // (D5-② 四步 coda 收敛为 finalizeRun 单写点,含 SW-DATA-3 save 兜底)
1058
+ await finalizeRun(run, deps, "completed", { context: "handleReturn (done,completed)" });
1032
1059
  }
1033
1060
 
1034
1061
  // ── handleWorkerError ────────────────────────────────────────
@@ -1076,16 +1103,10 @@ export async function handleWorkerError(
1076
1103
 
1077
1104
  // 超限 → failed
1078
1105
  run.state.error = err.message;
1079
- deps.log?.("debug", "workflow:error-recovery", "handleWorkerError retries exceeded, transition done", { runId: run.runId, count });
1080
- run.transition("done", "failed");
1081
- // [OR-8] 终态收口残留 in-flight call(先收口再落盘)
1082
- closeOutInFlightCalls(run);
1083
- // [SW-DATA-3] save 失败不阻断终态推进
1084
- await saveRunBestEffort(run, deps, "handleWorkerError (done,failed)");
1085
- deps.log?.("debug", "workflow:error-recovery", "run saved after worker error", { runId: run.runId, reason: run.state.reason });
1106
+ deps.log?.("debug", "workflow:worker-message-pump", "handleWorkerError retries exceeded, transition done", { runId: run.runId, count });
1086
1107
  // C-4: run 到达 done 终态 → 注销 pending-notification + 通知 Interface 层
1087
- // [OR-4] M12 同款围栏
1088
- emitTerminalSideEffects(run, deps, "handleWorkerError (done,failed)");
1108
+ // (D5-② 四步 coda 收敛为 finalizeRun 单写点)
1109
+ await finalizeRun(run, deps, "failed", { context: "handleWorkerError (done,failed)" });
1089
1110
  }
1090
1111
 
1091
1112
  // ── handleWorkerExit ─────────────────────────────────────────
@@ -1123,16 +1144,11 @@ export async function handleWorkerExit(
1123
1144
  // [F1] 无终态消息的 exit(0) = worker 静默退出(不可克隆 return 被吞 / 脚本直调
1124
1145
  // process.exit(0) 等)。置 failed 保证 runAndWait 必有终态。不重试:rebuild 重跑
1125
1146
  // 脚本对确定性根因(不可克隆 return)无意义,且 belt 路径优先给用户明确归因。
1126
- deps.log?.("debug", "workflow:error-recovery", "worker exited without terminal message, transition done", { runId: run.runId });
1147
+ deps.log?.("debug", "workflow:worker-message-pump", "worker exited without terminal message, transition done", { runId: run.runId });
1127
1148
  run.state.error = WORKER_EXITED_WITHOUT_RESULT_MSG;
1128
- run.transition("done", "failed");
1129
- // [OR-8] 终态收口残留 in-flight call(先收口再落盘)
1130
- closeOutInFlightCalls(run);
1131
- await saveRunBestEffort(run, deps, "handleWorkerExit (done,failed, no terminal message)");
1132
- deps.log?.("debug", "workflow:error-recovery", "run saved after exit without result", { runId: run.runId, reason: run.state.reason });
1133
1149
  // C-4: run 到达 done 终态 → 注销 pending-notification + 通知 Interface 层
1134
- // [OR-4] M12 同款围栏
1135
- emitTerminalSideEffects(run, deps, "handleWorkerExit (done,failed, no terminal message)");
1150
+ // (D5-② 四步 coda 收敛为 finalizeRun 单写点)
1151
+ await finalizeRun(run, deps, "failed", { context: "handleWorkerExit (done,failed, no terminal message)" });
1136
1152
  return;
1137
1153
  }
1138
1154
 
@@ -1185,16 +1201,10 @@ export async function handleScriptError(
1185
1201
 
1186
1202
  // 超限 → failed
1187
1203
  run.state.error = `Workflow failed after ${MAX_WORKER_RETRIES} retries: ${errorMsg}`;
1188
- deps.log?.("debug", "workflow:error-recovery", "handleScriptError retries exceeded, transition done", { runId: run.runId, count });
1189
- run.transition("done", "failed");
1190
- // [OR-8] 终态收口残留 in-flight call(先收口再落盘)
1191
- closeOutInFlightCalls(run);
1192
- // [SW-DATA-3] save 失败不阻断终态推进
1193
- await saveRunBestEffort(run, deps, "handleScriptError (done,failed)");
1194
- deps.log?.("debug", "workflow:error-recovery", "run saved after script error", { runId: run.runId, reason: run.state.reason });
1204
+ deps.log?.("debug", "workflow:worker-message-pump", "handleScriptError retries exceeded, transition done", { runId: run.runId, count });
1195
1205
  // C-4: run 到达 done 终态 → 注销 pending-notification + 通知 Interface 层
1196
- // [OR-4] M12 同款围栏
1197
- emitTerminalSideEffects(run, deps, "handleScriptError (done,failed)");
1206
+ // (D5-② 四步 coda 收敛为 finalizeRun 单写点)
1207
+ await finalizeRun(run, deps, "failed", { context: "handleScriptError (done,failed)" });
1198
1208
  }
1199
1209
 
1200
1210
  // ── scheduleRebuild(退避 + 重建) ──────────────────────────
@@ -1264,7 +1274,7 @@ async function handleRebuildStartFailure(
1264
1274
  handlers: WorkerHandlers,
1265
1275
  ): Promise<void> {
1266
1276
  if (isTerminal(run)) return;
1267
- const message = err instanceof Error ? err.message : String(err);
1277
+ const message = toErrorMessage(err);
1268
1278
  const count = (run.meta.workerErrorCount ?? 0) + 1;
1269
1279
  run.meta.workerErrorCount = count;
1270
1280
  logger.error(
@@ -1278,11 +1288,8 @@ async function handleRebuildStartFailure(
1278
1288
 
1279
1289
  // 耗尽 → 收敛 done,failed(不卡 running)
1280
1290
  run.state.error = `Runtime rebuild failed after ${MAX_WORKER_RETRIES} retries: ${message}`;
1281
- deps.log?.("debug", "workflow:error-recovery", "rebuild retries exhausted, transition done", { runId: run.runId, count });
1282
- run.transition("done", "failed");
1283
- // [OR-8] 终态收口残留 in-flight call(先收口再落盘)
1284
- closeOutInFlightCalls(run);
1285
- await saveRunBestEffort(run, deps, "handleRebuildStartFailure (done,failed)");
1286
- // [OR-4] M12 同款围栏
1287
- emitTerminalSideEffects(run, deps, "handleRebuildStartFailure (done,failed)");
1291
+ deps.log?.("debug", "workflow:worker-message-pump", "rebuild retries exhausted, transition done", { runId: run.runId, count });
1292
+ // 终态序列与其余 handle* 对齐:transition → closeOut → save → 围栏 emit/onRunDone
1293
+ // (D5-② 收敛为 finalizeRun 单写点)
1294
+ await finalizeRun(run, deps, "failed", { context: "handleRebuildStartFailure (done,failed)" });
1288
1295
  }
@@ -40,7 +40,7 @@ beforeEach(() => {
40
40
  });
41
41
 
42
42
  afterEach(() => {
43
- rmSync(dir, { recursive: true, force: true });
43
+ rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
44
44
  });
45
45
 
46
46
  /** 目录内约定形态 tmp 残留计数(断言「无残留」用)。 */