@zhushanwen/subagent-core 0.2.0 → 0.5.1

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 (347) hide show
  1. package/README.md +45 -2
  2. package/agents/analyst.md +60 -0
  3. package/agents/coder.md +69 -0
  4. package/agents/debugger.md +66 -0
  5. package/agents/doc-reviewer.md +49 -0
  6. package/agents/explorer.md +63 -0
  7. package/agents/general-purpose.md +32 -0
  8. package/agents/orchestrator.md +61 -0
  9. package/agents/planner.md +53 -0
  10. package/agents/researcher.md +67 -0
  11. package/agents/reviewer.md +73 -0
  12. package/dist/chunk-43ONBFZX.js +240 -0
  13. package/dist/chunk-APZY4IME.js +27 -0
  14. package/dist/chunk-V3VHZ2VX.js +59 -0
  15. package/dist/execution/engine/engines/zcode/constants.cjs +62 -9
  16. package/dist/execution/engine/engines/zcode/constants.d.cts +116 -12
  17. package/dist/execution/engine/engines/zcode/constants.d.ts +116 -12
  18. package/dist/execution/engine/engines/zcode/constants.js +39 -7
  19. package/dist/execution/engine/engines/zcode/reader.d.cts +2 -1
  20. package/dist/execution/engine/engines/zcode/reader.d.ts +2 -1
  21. package/dist/execution/engine/engines/zcode/reader.js +4 -234
  22. package/dist/execution/engine/paths.js +7 -19
  23. package/dist/index.cjs +18234 -1277
  24. package/dist/index.d.cts +5646 -673
  25. package/dist/index.d.ts +5646 -673
  26. package/dist/index.js +17828 -1277
  27. package/dist/{types-BxyAidGf.d.cts → types-Dv4QhSJ_.d.cts} +678 -112
  28. package/dist/{types-BxyAidGf.d.ts → types-Dv4QhSJ_.d.ts} +678 -112
  29. package/package.json +7 -4
  30. package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +0 -1
  31. package/src/__tests__/append-system-prompt-assembly.test.ts +8 -7
  32. package/src/__tests__/fr4-get-state-handshake.test.ts +2 -2
  33. package/src/__tests__/m2-append-content-probe.test.ts +0 -1
  34. package/src/__tests__/manifest-store.test.ts +10 -9
  35. package/src/__tests__/record-store-cache.test.ts +0 -2
  36. package/src/__tests__/record-store-index.test.ts +1 -2
  37. package/src/__tests__/review-fix-loop-script.test.ts +93 -3
  38. package/src/__tests__/review-fix-loop-utils.test.ts +31 -10
  39. package/src/__tests__/robustness-low-batch1.test.ts +6 -5
  40. package/src/__tests__/robustness-medium-batch1.test.ts +2 -2
  41. package/src/__tests__/robustness-medium-batch2.test.ts +17 -6
  42. package/src/__tests__/robustness-medium-batch4.test.ts +2 -2
  43. package/src/__tests__/session-runner.test.ts +1 -1
  44. package/src/__tests__/smoke.test.ts +9 -2
  45. package/src/core/__tests__/host-services.test.ts +2 -2
  46. package/src/core/__tests__/logger.test.ts +1 -1
  47. package/src/core/error-message.ts +9 -0
  48. package/src/core/host-services.ts +21 -5
  49. package/src/core/notify-ports.ts +18 -5
  50. package/src/execution/__tests__/agent-profile.test.ts +232 -0
  51. package/src/execution/__tests__/agent-registry.test.ts +370 -0
  52. package/src/execution/__tests__/agent-result-mapper.test.ts +18 -2
  53. package/src/execution/__tests__/agents-assembly.test.ts +219 -0
  54. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +6 -6
  55. package/src/execution/__tests__/chat-engine-routing.test.ts +136 -30
  56. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +5 -4
  57. package/src/execution/__tests__/cold-resurrect.test.ts +283 -0
  58. package/src/execution/__tests__/create-concurrency-pool.test.ts +194 -0
  59. package/src/execution/__tests__/delivery-methods.test.ts +77 -62
  60. package/src/execution/__tests__/descendant-sweep-guards.test.ts +220 -0
  61. package/src/execution/__tests__/descendant-sweep.test.ts +269 -0
  62. package/src/execution/__tests__/dialog-queue.test.ts +199 -1
  63. package/src/execution/__tests__/engine-model-validation.test.ts +741 -0
  64. package/src/execution/__tests__/epipe-fallback.test.ts +31 -24
  65. package/src/execution/__tests__/execute-and-await-worktree.test.ts +21 -91
  66. package/src/execution/__tests__/execute-nesting.test.ts +34 -188
  67. package/src/execution/__tests__/execution-record.test.ts +217 -60
  68. package/src/execution/__tests__/execution-runtime-face.test.ts +272 -0
  69. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +3 -3
  70. package/src/execution/__tests__/finalize-record.test.ts +278 -16
  71. package/src/execution/__tests__/gc-timer.test.ts +57 -2
  72. package/src/execution/__tests__/get-record-for-action-restart.test.ts +14 -12
  73. package/src/execution/__tests__/get-state-handshake.test.ts +127 -0
  74. package/src/execution/__tests__/helpers/mock-extension-api.ts +1 -1
  75. package/src/execution/__tests__/helpers/session-runner-mocks.ts +113 -0
  76. package/src/execution/__tests__/helpers/spawn-mock.ts +150 -3
  77. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +137 -0
  78. package/src/execution/__tests__/keep-alive-no-progress.test.ts +280 -0
  79. package/src/execution/__tests__/kill-all-escalation.test.ts +196 -0
  80. package/src/execution/__tests__/lifecycle-manager-idle-timer-identity.test.ts +117 -0
  81. package/src/execution/__tests__/lifecycle-manager.test.ts +39 -0
  82. package/src/execution/__tests__/lifecycle-predicates.test.ts +1 -2
  83. package/src/execution/__tests__/manifest-parentid.test.ts +4 -4
  84. package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +99 -0
  85. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +113 -0
  86. package/src/execution/__tests__/model-resolver.test.ts +2 -2
  87. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +23 -121
  88. package/src/execution/__tests__/nested-visibility.test.ts +2 -0
  89. package/src/execution/__tests__/notify-ledger.test.ts +1249 -0
  90. package/src/execution/__tests__/output-collector.test.ts +117 -6
  91. package/src/execution/__tests__/pi-invocation.test.ts +1 -1
  92. package/src/execution/__tests__/record-store-orphan-revive.test.ts +159 -0
  93. package/src/execution/__tests__/record-store.test.ts +89 -5
  94. package/src/execution/__tests__/records-cwd-isolation.test.ts +2 -2
  95. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +29 -132
  96. package/src/execution/__tests__/recursive-visibility-env.test.ts +55 -179
  97. package/src/execution/__tests__/rpc-mode.test.ts +2 -2
  98. package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +387 -0
  99. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +19 -9
  100. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +5 -4
  101. package/src/execution/__tests__/run-spawn-edges.test.ts +418 -26
  102. package/src/execution/__tests__/run-spawn-integration.test.ts +3 -3
  103. package/src/execution/__tests__/run-spawn-resume.test.ts +2 -2
  104. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +2 -2
  105. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +16 -8
  106. package/src/execution/__tests__/service-kill-escalation.test.ts +91 -0
  107. package/src/execution/__tests__/session-context-resolver.test.ts +0 -2
  108. package/src/execution/__tests__/session-file-gc.test.ts +35 -0
  109. package/src/execution/__tests__/session-pending.test.ts +471 -0
  110. package/src/execution/__tests__/session-reconstructor.test.ts +366 -15
  111. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +110 -0
  112. package/src/execution/__tests__/session-runner-close-prune.test.ts +181 -0
  113. package/src/execution/__tests__/session-runner-dispatch.test.ts +489 -0
  114. package/src/execution/__tests__/session-runner-epipe.test.ts +4 -3
  115. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +227 -0
  116. package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +290 -0
  117. package/src/execution/__tests__/session-runner-schema-env.test.ts +7 -5
  118. package/src/execution/__tests__/settled-watchdog.test.ts +524 -0
  119. package/src/execution/__tests__/spawn-args.test.ts +11 -10
  120. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +26 -23
  121. package/src/execution/__tests__/spawn-event-adapter.test.ts +1 -1
  122. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +5 -4
  123. package/src/execution/__tests__/spawned-children.test.ts +155 -11
  124. package/src/execution/__tests__/start-sync-model-guard.test.ts +4 -4
  125. package/src/execution/__tests__/stdin-writer.test.ts +6 -4
  126. package/src/execution/__tests__/stream-sink-retirement.test.ts +6 -6
  127. package/src/execution/__tests__/subagent-actions-core.test.ts +1017 -0
  128. package/src/execution/__tests__/subagent-service-message-close.test.ts +99 -35
  129. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +174 -0
  130. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +271 -0
  131. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +33 -29
  132. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +341 -0
  133. package/src/execution/__tests__/subagent-service.test.ts +37 -30
  134. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +88 -12
  135. package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +75 -0
  136. package/src/execution/__tests__/subprocess-agent-runner.test.ts +22 -20
  137. package/src/execution/__tests__/temp-prompt.test.ts +1 -1
  138. package/src/execution/__tests__/timeout-integration.test.ts +13 -12
  139. package/src/execution/__tests__/turn-limiter-semantics.test.ts +1 -1
  140. package/src/execution/__tests__/turn-limiter.test.ts +1 -1
  141. package/src/execution/__tests__/ui-request-handler-factory.test.ts +107 -7
  142. package/src/execution/__tests__/ui-request-handler.test.ts +4 -3
  143. package/src/execution/__tests__/ui-request-queue.test.ts +167 -2
  144. package/src/execution/__tests__/worktree-git-ops.test.ts +571 -0
  145. package/src/execution/__tests__/worktree-manager.test.ts +14 -5
  146. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +4 -3
  147. package/src/execution/__tests__/worktree-reconcile-aging.test.ts +204 -0
  148. package/src/execution/agent-registry.ts +298 -30
  149. package/src/execution/agent-result-mapper.ts +3 -0
  150. package/src/execution/agents-assembly.ts +88 -0
  151. package/src/execution/cold-resurrect.ts +199 -0
  152. package/src/execution/concurrency-pool.ts +71 -9
  153. package/src/execution/dialog-queue.ts +101 -3
  154. package/src/execution/engine/__tests__/common/capability-gate.test.ts +91 -0
  155. package/src/execution/engine/__tests__/common/data-dir.test.ts +1 -1
  156. package/src/execution/engine/__tests__/common/journal-wiring.test.ts +84 -0
  157. package/src/execution/engine/__tests__/common/kill-chain.test.ts +34 -7
  158. package/src/execution/engine/__tests__/common/persona-router.test.ts +4 -5
  159. package/src/execution/engine/__tests__/common/pool-manager.test.ts +211 -51
  160. package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +149 -0
  161. package/src/execution/engine/__tests__/common/session-view-service.test.ts +499 -0
  162. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +56 -99
  163. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +88 -15
  164. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +33 -7
  165. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +1 -1
  166. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +75 -54
  167. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +128 -0
  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 +94 -3
  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/engine-discovery.ts +12 -16
  181. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +174 -28
  182. package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +257 -0
  183. package/src/execution/{argv-mirror.ts → engine/engines/pi/argv-mirror.ts} +43 -21
  184. package/src/execution/{get-state-handshake.ts → engine/engines/pi/get-state-handshake.ts} +86 -11
  185. package/src/execution/{output-collector.ts → engine/engines/pi/output-collector.ts} +108 -19
  186. package/src/execution/engine/engines/pi/pi-engine.ts +434 -38
  187. package/src/execution/{pi-invocation.ts → engine/engines/pi/pi-invocation.ts} +2 -2
  188. package/src/execution/engine/engines/pi/reader.ts +11 -0
  189. package/src/execution/engine/engines/pi/registration.ts +4 -2
  190. package/src/execution/engine/engines/pi/session-runner.ts +2840 -0
  191. package/src/execution/{spawn-event-adapter.ts → engine/engines/pi/spawn-event-adapter.ts} +75 -34
  192. package/src/execution/{stdin-writer.ts → engine/engines/pi/stdin-writer.ts} +10 -9
  193. package/src/execution/{temp-prompt.ts → engine/engines/pi/temp-prompt.ts} +2 -2
  194. package/src/execution/{turn-limiter.ts → engine/engines/pi/turn-limiter.ts} +1 -1
  195. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +301 -0
  196. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +36 -0
  197. package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +321 -0
  198. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +473 -0
  199. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +7 -210
  200. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +6 -110
  201. package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +247 -0
  202. package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +451 -0
  203. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +792 -0
  204. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +782 -0
  205. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +168 -0
  206. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +210 -0
  207. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +353 -0
  208. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +282 -0
  209. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +372 -0
  210. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +65 -47
  211. package/src/execution/engine/engines/zcode/appserver-launcher.ts +167 -0
  212. package/src/execution/engine/engines/zcode/connection.ts +598 -0
  213. package/src/execution/engine/engines/zcode/constants.ts +147 -14
  214. package/src/execution/engine/engines/zcode/golden-sample.ts +42 -34
  215. package/src/execution/engine/engines/zcode/parser.ts +12 -346
  216. package/src/execution/engine/engines/zcode/preparer.ts +22 -150
  217. package/src/execution/engine/engines/zcode/session-channel.ts +968 -0
  218. package/src/execution/engine/engines/zcode/zcode-engine.ts +1023 -250
  219. package/src/execution/engine/host-task-spec.ts +32 -36
  220. package/src/execution/engine/model-validation.ts +177 -0
  221. package/src/execution/engine/port.ts +65 -7
  222. package/src/execution/engine/registry.ts +53 -0
  223. package/src/execution/engine/routing.ts +75 -1
  224. package/src/execution/engine/types.ts +30 -103
  225. package/src/execution/execution-record.ts +222 -144
  226. package/src/execution/finalize-record.ts +181 -85
  227. package/src/execution/idle-gc.ts +28 -0
  228. package/src/execution/lifecycle-manager.ts +27 -3
  229. package/src/execution/lifecycle-predicates.ts +1 -1
  230. package/src/execution/manifest-store.ts +53 -62
  231. package/src/execution/notifier.ts +10 -10
  232. package/src/execution/notify-host.ts +212 -0
  233. package/src/execution/notify-ledger.ts +117 -16
  234. package/src/execution/record-entry.ts +8 -2
  235. package/src/execution/record-store.ts +148 -88
  236. package/src/execution/round-settlement.ts +93 -0
  237. package/src/execution/session-file-gc.ts +96 -62
  238. package/src/execution/session-pending.ts +213 -62
  239. package/src/execution/session-reconstructor.ts +256 -143
  240. package/src/execution/sessions-index.ts +127 -102
  241. package/src/execution/settled-watchdog.ts +311 -0
  242. package/src/execution/subagent-actions-core.ts +686 -0
  243. package/src/execution/subagent-service.ts +1106 -907
  244. package/src/execution/subprocess-agent-runner.ts +144 -97
  245. package/src/execution/types.ts +9 -0
  246. package/src/execution/ui-request-handler-factory.ts +126 -75
  247. package/src/execution/ui-request-queue.ts +50 -19
  248. package/src/execution/worktree-git-ops.ts +397 -0
  249. package/src/execution/worktree-manager.ts +72 -10
  250. package/src/execution/worktree-registry.ts +6 -12
  251. package/src/index.ts +542 -9
  252. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +42 -3
  253. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +3 -5
  254. package/src/orchestration/__tests__/agent-call-stream.test.ts +2 -2
  255. package/src/orchestration/__tests__/args-meta.test.ts +358 -0
  256. package/src/orchestration/__tests__/args-validator.test.ts +14 -0
  257. package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +312 -0
  258. package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +384 -0
  259. package/src/orchestration/__tests__/execute-agent-call.test.ts +151 -11
  260. package/src/orchestration/__tests__/file-run-store-prune.test.ts +168 -0
  261. package/src/orchestration/__tests__/file-run-store-throttle.test.ts +168 -0
  262. package/src/orchestration/__tests__/file-run-store.test.ts +390 -0
  263. package/src/orchestration/__tests__/helpers/flush-microtasks.ts +13 -0
  264. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +34 -0
  265. package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +357 -0
  266. package/src/orchestration/__tests__/lifecycle-recover-crashed.test.ts +289 -0
  267. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +3 -3
  268. package/src/orchestration/__tests__/lifecycle.test.ts +84 -5
  269. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +1 -1
  270. package/src/orchestration/__tests__/run-snapshot.test.ts +356 -0
  271. package/src/orchestration/__tests__/script-generate.test.ts +314 -0
  272. package/src/orchestration/__tests__/script-lint.test.ts +17 -0
  273. package/src/orchestration/__tests__/test-mocks.ts +2 -0
  274. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +7 -6
  275. package/src/orchestration/__tests__/worker-message-pump-finalize-run.test.ts +229 -0
  276. package/src/orchestration/__tests__/{error-recovery-handlers.test.ts → worker-message-pump-handlers.test.ts} +28 -27
  277. package/src/orchestration/__tests__/{error-recovery-postmessage-defense.test.ts → worker-message-pump-postmessage-defense.test.ts} +8 -8
  278. package/src/orchestration/__tests__/{error-recovery-serialize-failed-result.test.ts → worker-message-pump-serialize-failed-result.test.ts} +2 -2
  279. package/src/orchestration/__tests__/{error-recovery-workflow-call.test.ts → worker-message-pump-workflow-call.test.ts} +1 -1
  280. package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +275 -0
  281. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +78 -1
  282. package/src/orchestration/__tests__/worker-script-builder.test.ts +1 -1
  283. package/src/orchestration/__tests__/workflow-files.test.ts +186 -0
  284. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +1 -1
  285. package/src/orchestration/__tests__/workflow-run-summary.test.ts +119 -0
  286. package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +124 -0
  287. package/src/orchestration/agent-opts-resolver.ts +8 -8
  288. package/src/orchestration/args-meta.ts +198 -0
  289. package/src/orchestration/args-validator.ts +53 -31
  290. package/src/orchestration/execute-agent-call.ts +25 -79
  291. package/src/orchestration/file-run-store.ts +327 -0
  292. package/src/orchestration/launcher.ts +160 -104
  293. package/src/orchestration/lifecycle.ts +371 -97
  294. package/src/orchestration/models/agent-call.ts +7 -7
  295. package/src/orchestration/models/budget.ts +5 -5
  296. package/src/orchestration/models/ports.ts +4 -4
  297. package/src/orchestration/models/run-runtime.ts +15 -15
  298. package/src/orchestration/models/trace.ts +9 -9
  299. package/src/orchestration/models/types.ts +111 -22
  300. package/src/orchestration/models/workflow-run.ts +28 -28
  301. package/src/orchestration/models/workflow-script.ts +4 -4
  302. package/src/orchestration/run-snapshot.ts +266 -0
  303. package/src/orchestration/script-generate.ts +154 -0
  304. package/src/orchestration/script-lint.ts +123 -90
  305. package/src/orchestration/worker-handle.ts +10 -10
  306. package/src/orchestration/worker-host.ts +10 -10
  307. package/src/orchestration/{error-recovery.ts → worker-message-pump.ts} +498 -221
  308. package/src/orchestration/worker-script-builder.ts +401 -346
  309. package/src/orchestration/workflow-files.ts +37 -11
  310. package/src/orchestration/workflow-run-summary.ts +69 -0
  311. package/src/orchestration/workflow-script-registry-impl.ts +31 -9
  312. package/src/shared/__tests__/agent-ref.test.ts +209 -2
  313. package/src/shared/__tests__/atomic-write.test.ts +267 -0
  314. package/src/shared/__tests__/bounded-serialize.test.ts +236 -0
  315. package/src/shared/__tests__/injection-render.test.ts +518 -0
  316. package/src/shared/__tests__/meta-parser.test.ts +151 -0
  317. package/src/shared/__tests__/resource-discovery-host-roots.test.ts +474 -0
  318. package/src/shared/__tests__/resource-discovery.test.ts +261 -217
  319. package/src/shared/agent-ref.ts +143 -2
  320. package/src/shared/atomic-write.ts +323 -0
  321. package/src/shared/bounded-serialize.ts +154 -0
  322. package/src/shared/injection-render.ts +279 -0
  323. package/src/shared/meta-parser.ts +182 -68
  324. package/src/shared/model-ref.ts +15 -2
  325. package/src/shared/resource-discovery.ts +97 -204
  326. package/src/shared/resource-meta.ts +14 -0
  327. package/src/shared/schema-jsonify.ts +1 -1
  328. package/src/shared/xml-injection.ts +9 -9
  329. package/workflows/README.md +9 -9
  330. package/workflows/chain.js +4 -2
  331. package/workflows/map-reduce.js +5 -3
  332. package/workflows/parallel.js +5 -3
  333. package/workflows/review-fix-loop-utils.cjs +165 -92
  334. package/workflows/review-fix-loop-utils.d.cts +287 -0
  335. package/workflows/review-fix-loop.js +18 -7
  336. package/workflows/scatter-gather.js +4 -2
  337. package/dist/chunk-3VOERJPJ.js +0 -22
  338. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
  339. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  340. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  341. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  342. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  343. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -580
  344. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  345. package/src/execution/execute-options-mapper.ts +0 -115
  346. package/src/execution/session-runner.ts +0 -1789
  347. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -288
@@ -1,7 +1,7 @@
1
1
  /**
2
- * error-recovery handlers — handleWorkerExit/Error/ScriptError + postBudgetUpdate 测试。
2
+ * worker-message-pump handlers — handleWorkerExit/Error/ScriptError + postBudgetUpdate 测试。
3
3
  *
4
- * 参考 error-recovery-workflow-call.test.ts 的 mock 构建。通过 vi.useFakeTimers() 跳过
4
+ * 参考 worker-message-pump-workflow-call.test.ts 的 mock 构建。通过 vi.useFakeTimers() 跳过
5
5
  * scheduleRebuild 的指数退避(1s/2s/4s)。
6
6
  *
7
7
  * 覆盖:
@@ -23,14 +23,15 @@ import {
23
23
  handleWorkerMessage,
24
24
  postBudgetUpdate,
25
25
  rebuildRuntime,
26
- } from "../error-recovery.ts";
26
+ } from "../worker-message-pump.ts";
27
27
  import { Budget } from "../models/budget.ts";
28
28
  import { RunRuntime } from "../models/run-runtime.ts";
29
29
  import { Trace } from "../models/trace.ts";
30
- import type { AgentResult } from "../models/types.ts";
30
+ import type { AgentResult, DoneReason, RunStatus } from "../models/types.ts";
31
31
  import { WorkflowRun } from "../models/workflow-run.ts";
32
32
  import type { LifecycleDeps, WorkerHandlers } from "../models/ports.ts";
33
33
  import type { WorkerHandle } from "../worker-handle.ts";
34
+ import { flushMicrotasks } from "./helpers/flush-microtasks.ts";
34
35
 
35
36
  // ── helpers ──────────────────────────────────────────────────
36
37
 
@@ -70,26 +71,34 @@ function makeRunningRun(opts: {
70
71
  receivedTerminalMessage: opts.receivedTerminalMessage,
71
72
  },
72
73
  // transition 副作用——run.state.status 由调用方通过 mock 控制后再次断言
73
- transition(target: string, reason?: string): void {
74
+ transition(this: WorkflowRun, target: RunStatus, reason?: DoneReason): void {
74
75
  this.state.status = target;
75
76
  if (target === "done") this.state.reason = reason;
76
77
  },
77
- replaceRuntime(rt: unknown): void {
78
+ replaceRuntime(this: WorkflowRun, rt: NonNullable<WorkflowRun["runtime"]>): void {
78
79
  this.runtime = rt;
79
80
  },
80
81
  } as unknown as WorkflowRun;
81
82
  }
82
83
 
83
- /** LifecycleDeps mock:store/workerHost/runner/eventBus/scheduleTimeBudget 可观察。 */
84
+ /** LifecycleDeps mock:store/workerHost/runner/eventBus/scheduleTimeBudget 可观察。
85
+ * mock 成员 = 真实签名 & vi.fn 能力(交叉纯 Mock 会丢真实签名,传回被测函数即报错)。 */
86
+ type MockLifecycleDeps = Omit<
87
+ LifecycleDeps,
88
+ "store" | "workerHost" | "runner" | "eventBus" | "onRunDone" | "log"
89
+ > & {
90
+ // 真实类型整体保留(RunStore/WorkerHost 等接口成员完整),仅 mock 方法交叉 vi.fn 能力
91
+ store: LifecycleDeps["store"] & { save: LifecycleDeps["store"]["save"] & ReturnType<typeof vi.fn> };
92
+ workerHost: LifecycleDeps["workerHost"] & { start: LifecycleDeps["workerHost"]["start"] & ReturnType<typeof vi.fn> };
93
+ runner: LifecycleDeps["runner"] & { run: LifecycleDeps["runner"]["run"] & ReturnType<typeof vi.fn> };
94
+ eventBus: NonNullable<LifecycleDeps["eventBus"]> & { emit: NonNullable<LifecycleDeps["eventBus"]>["emit"] & ReturnType<typeof vi.fn> };
95
+ onRunDone: LifecycleDeps["onRunDone"] & ReturnType<typeof vi.fn>;
96
+ log: LifecycleDeps["log"] & ReturnType<typeof vi.fn>;
97
+ };
98
+
84
99
  function makeDeps(opts: {
85
100
  scheduleTimeBudget?: LifecycleDeps["scheduleTimeBudget"];
86
- } = {}): LifecycleDeps & {
87
- store: { save: ReturnType<typeof vi.fn> };
88
- workerHost: { start: ReturnType<typeof vi.fn> };
89
- eventBus: { emit: ReturnType<typeof vi.fn> };
90
- onRunDone: ReturnType<typeof vi.fn>;
91
- log: ReturnType<typeof vi.fn>;
92
- } {
101
+ } = {}): MockLifecycleDeps {
93
102
  return {
94
103
  store: { save: vi.fn(async () => {}) },
95
104
  workerHost: { start: vi.fn(() => ({ postMessage: vi.fn() })) },
@@ -343,7 +352,7 @@ describe("rebuildRuntime", () => {
343
352
 
344
353
  it("带 budgetTimeMs 时重排 scheduleTimeBudget 计时器", () => {
345
354
  const run = makeRunningRun({ budgetTimeMs: 5000 });
346
- const scheduleTimeBudget = vi.fn(() => undefined);
355
+ const scheduleTimeBudget = vi.fn((_runId: string, _budgetTimeMs: number) => undefined);
347
356
  const deps = makeDeps({ scheduleTimeBudget });
348
357
 
349
358
  rebuildRuntime(run, deps, makeHandlers());
@@ -412,7 +421,7 @@ describe("race-F3: rebuild 时间预算折算", () => {
412
421
  const run = makeRunningRun({ budgetTimeMs: 5000 });
413
422
  // 已耗 70%(3500ms)→ 剩余 1500ms;fake Date 冻结,elapsed 精确
414
423
  run.meta.startedAt = new Date(Date.now() - 3500).toISOString();
415
- const scheduleTimeBudget = vi.fn(() => undefined);
424
+ const scheduleTimeBudget = vi.fn((_runId: string, _budgetTimeMs: number) => undefined);
416
425
  const deps = makeDeps({ scheduleTimeBudget });
417
426
 
418
427
  rebuildRuntime(run, deps, makeHandlers());
@@ -428,7 +437,7 @@ describe("race-F3: rebuild 时间预算折算", () => {
428
437
  const run = makeRunningRun({ budgetTimeMs: 5000 });
429
438
  // 已耗 6000ms > 预算 5000ms(退避 advance 1000ms 后已耗 7000ms,仍耗尽)
430
439
  run.meta.startedAt = new Date(Date.now() - 6000).toISOString();
431
- const scheduleTimeBudget = vi.fn(() => undefined);
440
+ const scheduleTimeBudget = vi.fn((_runId: string, _budgetTimeMs: number) => undefined);
432
441
  const deps = makeDeps({ scheduleTimeBudget });
433
442
 
434
443
  const p = handleScriptError(run, "boom", [], deps, makeHandlers());
@@ -451,7 +460,7 @@ describe("race-F3: rebuild 时间预算折算", () => {
451
460
 
452
461
  it("预算未耗尽(fresh run)→ 照常 rebuild,不误转 time_limited(防误伤回归)", async () => {
453
462
  const run = makeRunningRun({ budgetTimeMs: 5000 }); // startedAt ≈ now,remaining 满
454
- const scheduleTimeBudget = vi.fn(() => undefined);
463
+ const scheduleTimeBudget = vi.fn((_runId: string, _budgetTimeMs: number) => undefined);
455
464
  const deps = makeDeps({ scheduleTimeBudget });
456
465
 
457
466
  const p = handleScriptError(run, "boom", [], deps, makeHandlers());
@@ -481,14 +490,6 @@ function createDeferred<T>(): Deferred<T> {
481
490
  return { promise, resolve, reject };
482
491
  }
483
492
 
484
- /** flush 微任务队列(dispatch 的 fire-and-forget promise 链推进到稳定态)。 */
485
- async function flushMicrotasks(ticks = 10): Promise<void> {
486
- for (let i = 0; i < ticks; i++) {
487
- // eslint-disable-next-line no-await-in-loop -- 排空微任务队列的固定 tick 循环(fire-and-forget promise 链推进到稳定态),非逐项等待
488
- await Promise.resolve();
489
- }
490
- }
491
-
492
493
  /** 构造真实 WorkflowRun(真实状态机/replaceRuntime/Trace/Budget)+ 初始 RunRuntime。 */
493
494
  function makeRealRun(runId: string, opts: { budgetTimeMs?: number } = {}): WorkflowRun {
494
495
  const run = new WorkflowRun(
@@ -752,7 +753,7 @@ describe("rebuildRuntime 可观察性(OB3 日志点)", () => {
752
753
 
753
754
  it("U7: 带 budgetTimeMs + scheduleTimeBudget 注入时含 L2(在 L1 之后、L3 之前,payload 含 budgetTimeMs)", () => {
754
755
  const run = makeRealRun("wf-rebuild-log-2", { budgetTimeMs: 5000 });
755
- const scheduleTimeBudget = vi.fn(() => undefined);
756
+ const scheduleTimeBudget = vi.fn((_runId: string, _budgetTimeMs: number) => undefined);
756
757
  const deps = makeDeps({ scheduleTimeBudget });
757
758
 
758
759
  rebuildRuntime(run, deps, makeHandlers());
@@ -1,4 +1,4 @@
1
- // src/orchestration/__tests__/error-recovery-postmessage-defense.test.ts
1
+ // src/orchestration/__tests__/worker-message-pump-postmessage-defense.test.ts
2
2
  //
3
3
  // W2: 主线程层 postMessage 防御测试。
4
4
  //
@@ -35,13 +35,13 @@ vi.mock("../../core/logger.ts", () => ({
35
35
  getLogger: () => loggerMock,
36
36
  }));
37
37
 
38
- import { handleWorkerMessage, postBudgetUpdate } from "../error-recovery.ts";
38
+ import { handleWorkerMessage, postBudgetUpdate } from "../worker-message-pump.ts";
39
39
  import type { LifecycleDeps, WorkerHandlers } from "../models/ports.ts";
40
40
  import type { WorkflowRun } from "../models/workflow-run.ts";
41
41
 
42
42
  const __dirname = dirname(fileURLToPath(import.meta.url));
43
- const ERROR_RECOVERY_SRC = readFileSync(
44
- join(__dirname, "../error-recovery.ts"),
43
+ const WORKER_MESSAGE_PUMP_SRC = readFileSync(
44
+ join(__dirname, "../worker-message-pump.ts"),
45
45
  "utf-8",
46
46
  );
47
47
 
@@ -273,7 +273,7 @@ describe("W2b: dispatchWorkflowCall postResult 防御 DataCloneError", () => {
273
273
  // 子进程)。利用这一点构造行为测试——往 cached.result 里塞不可克隆值(function),
274
274
  // mock postMessage 在首次发送 agent-result 时抛 DataCloneError,验证 fallback 路径。
275
275
  //
276
- // 另外补充:验证 error-recovery.ts 源码中三处 postMessage 调用点都有 try/catch 包裹,
276
+ // 另外补充:验证 worker-message-pump.ts 源码中三处 postMessage 调用点都有 try/catch 包裹,
277
277
  // 防止未来重构误删防御(类似 worker-script-builder.test.ts 的源码字符串断言模式)。
278
278
 
279
279
  /** 构造 status="running" 且 calls 已含一个 done 结果(含不可克隆成员)的 mock run。
@@ -376,7 +376,7 @@ describe("W2c: postAgentResult 防御(源码结构断言)", () => {
376
376
  }
377
377
 
378
378
  it("postAgentResult 包含 try/catch + fallback result", () => {
379
- const match = ERROR_RECOVERY_SRC.match(/function postAgentResult\([\s\S]*?\n\}/);
379
+ const match = WORKER_MESSAGE_PUMP_SRC.match(/function postAgentResult\([\s\S]*?\n\}/);
380
380
  expect(match, "postAgentResult 函数定义应存在").toBeTruthy();
381
381
  expect(hasTryCatchAroundPostMessage(match![0])).toBe(true);
382
382
  // fallback 通过共享工厂 makeSerializeFailedResult 构造纯字符串 result(必可克隆)
@@ -387,14 +387,14 @@ describe("W2c: postAgentResult 防御(源码结构断言)", () => {
387
387
  });
388
388
 
389
389
  it("postBudgetUpdate 包含 try/catch", () => {
390
- const match = ERROR_RECOVERY_SRC.match(/export function postBudgetUpdate\([\s\S]*?\n\}/);
390
+ const match = WORKER_MESSAGE_PUMP_SRC.match(/export function postBudgetUpdate\([\s\S]*?\n\}/);
391
391
  expect(match, "postBudgetUpdate 函数定义应存在").toBeTruthy();
392
392
  expect(hasTryCatchAroundPostMessage(match![0])).toBe(true);
393
393
  });
394
394
 
395
395
  it("dispatchWorkflowCall postResult 闭包包含 try/catch + fallback", () => {
396
396
  // postResult 是 const 闭包,匹配到下一个 }; 结尾
397
- const match = ERROR_RECOVERY_SRC.match(/const postResult = \(result[\s\S]*?\n \};/);
397
+ const match = WORKER_MESSAGE_PUMP_SRC.match(/const postResult = \(result[\s\S]*?\n \};/);
398
398
  expect(match, "postResult 闭包定义应存在").toBeTruthy();
399
399
  expect(hasTryCatchAroundPostMessage(match![0])).toBe(true);
400
400
  expect(match![0]).toContain("Workflow result serialization failed");
@@ -1,4 +1,4 @@
1
- // src/orchestration/__tests__/error-recovery-serialize-failed-result.test.ts
1
+ // src/orchestration/__tests__/worker-message-pump-serialize-failed-result.test.ts
2
2
  //
3
3
  // makeSerializeFailedResult 独立单测(W2 防御关键纯函数)。
4
4
  //
@@ -15,7 +15,7 @@
15
15
 
16
16
  import { describe, expect, it } from "vitest";
17
17
 
18
- import { makeSerializeFailedResult } from "../error-recovery.ts";
18
+ import { makeSerializeFailedResult } from "../worker-message-pump.ts";
19
19
 
20
20
  describe("makeSerializeFailedResult", () => {
21
21
  it("返回 {content:'', error:'<prefix>: <errMsg>'} 形状(必可克隆 fallback)", () => {
@@ -10,7 +10,7 @@
10
10
  */
11
11
  import { describe, expect, it, vi } from "vitest";
12
12
 
13
- import { handleWorkerMessage } from "../error-recovery.ts";
13
+ import { handleWorkerMessage } from "../worker-message-pump.ts";
14
14
  import type { LifecycleDeps, WorkerHandlers } from "../models/ports.ts";
15
15
  import type { WorkflowRun } from "../models/workflow-run.ts";
16
16
 
@@ -0,0 +1,275 @@
1
+ /**
2
+ * [OR-3] worker 侧 pending per-call timeoutMs + abort 广播 测试(真实 Worker 线程,P-T3)。
3
+ *
4
+ * 覆盖(unbounded-wait-audit §4.1 OR-3 / §7.2 T3③ / §7.3 P-T3):
5
+ * - agent() 传 timeoutMs:主线程不回话 → pending 以错误 resolve(不 reject、不放大成
6
+ * 脚本 error)+ 超时 warn 记入 _workerLogs——消息层自己的超时,旧实现零超时永挂
7
+ * - returnMeta 模式超时 resolve {value:"", error}
8
+ * - 主线程不传 timeoutMs(缺省不限):pending 不挂 timer(行为不变)
9
+ * - abort 广播:await 中的 agent() 以 WorkflowAbortedError reject(脚本 catch 可感知
10
+ * 取消)→ 脚本自然收尾 → worker 自然退出(exit 0)
11
+ * - P-T3:abort 广播与 worker 自然退出交错不产 unhandledRejection——广播后立即
12
+ * terminate 的竞态、fire-and-forget 后 return 的自然退出,均不产生主线程
13
+ * unhandledRejection 与 worker 'error' 事件
14
+ */
15
+ import { Worker } from "node:worker_threads";
16
+
17
+ import { afterEach, describe, expect, it } from "vitest";
18
+
19
+ import { buildWorkerScript } from "../worker-script-builder.ts";
20
+
21
+ // ── 判别联合(对齐 worker-script-builder-runtime.test.ts 的守卫形态) ──
22
+
23
+ interface ReturnMsg {
24
+ type: "return";
25
+ result: unknown;
26
+ workerLogs?: Array<{ level: string; message: string }>;
27
+ }
28
+ interface ErrorMsg {
29
+ type: "error";
30
+ error: string;
31
+ workerLogs?: Array<{ level: string; message: string }>;
32
+ }
33
+
34
+ function isReturn(m: unknown): m is ReturnMsg {
35
+ return typeof m === "object" && m !== null && (m as { type?: unknown }).type === "return";
36
+ }
37
+ function isError(m: unknown): m is ErrorMsg {
38
+ return typeof m === "object" && m !== null && (m as { type?: unknown }).type === "error";
39
+ }
40
+ function isAgentCall(m: unknown): m is { type: "agent-call"; callId: number } {
41
+ return typeof m === "object" && m !== null && (m as { type?: unknown }).type === "agent-call";
42
+ }
43
+
44
+ // ── harness:主线程「不回话」的可控 Worker 运行器 ──────────────
45
+
46
+ interface RunResult {
47
+ returnValue?: unknown;
48
+ errorMessage?: string;
49
+ /** return/error 消息带回的 workerLogs(超时 warn 通路)。 */
50
+ workerLogs: Array<{ level: string; message: string }>;
51
+ exitCode?: number;
52
+ workerError?: string;
53
+ /** 收到的 agent-call 消息数(确认脚本确实发起了调用)。 */
54
+ agentCallCount: number;
55
+ }
56
+
57
+ interface RunOptions {
58
+ /** 收到 agent-call 后的主线程动作(默认:不回话——pending 留在 worker 侧)。 */
59
+ onAgentCall?: (msg: { callId: number; opts: Record<string, unknown> }, worker: Worker) => void;
60
+ /** 显式 finish(供 abort 竞态场景手工收尾)。 */
61
+ timeoutMs?: number;
62
+ }
63
+
64
+ const createdWorkers: Worker[] = [];
65
+
66
+ afterEach(() => {
67
+ for (const w of createdWorkers.splice(0)) {
68
+ w.terminate().catch(() => {});
69
+ }
70
+ });
71
+
72
+ function runWorker(userScript: string, opts: RunOptions = {}): Promise<RunResult> {
73
+ const timeoutMs = opts.timeoutMs ?? (process.env.CI ? 5000 : 2000);
74
+ return new Promise((resolve) => {
75
+ const worker = new Worker(buildWorkerScript(userScript), {
76
+ eval: true,
77
+ workerData: {
78
+ scriptPath: "test.js",
79
+ args: {},
80
+ workspace: process.cwd(),
81
+ budget: { maxTokens: 0, usedTokens: 0, usedCost: 0 },
82
+ },
83
+ });
84
+ createdWorkers.push(worker);
85
+
86
+ const result: RunResult = { workerLogs: [], agentCallCount: 0 };
87
+ let settled = false;
88
+ const finish = (): void => {
89
+ if (settled) return;
90
+ settled = true;
91
+ clearTimeout(timer);
92
+ clearTimeout(graceTimer);
93
+ resolve(result);
94
+ };
95
+ // 兜底:永不 settle 时 terminate + 以当前观察收尾
96
+ const timer = setTimeout(() => {
97
+ worker.terminate().catch(() => {});
98
+ finish();
99
+ }, timeoutMs);
100
+ // 收到终态消息后的宽限窗:给潜在 worker 'error' 事件一个到达机会,
101
+ // 然后收尾(parentPort 挂着 message listener 时 worker 线程不自然退出,
102
+ // 与生产一致由 terminate 回收——故不依赖 exit 事件断言)。
103
+ let graceTimer: ReturnType<typeof setTimeout>;
104
+
105
+ worker.on("message", (raw: unknown) => {
106
+ if (isAgentCall(raw)) {
107
+ result.agentCallCount += 1;
108
+ opts.onAgentCall?.(raw as unknown as { callId: number; opts: Record<string, unknown> }, worker);
109
+ return;
110
+ }
111
+ if (isReturn(raw)) {
112
+ result.returnValue = raw.result;
113
+ result.workerLogs = raw.workerLogs ?? [];
114
+ graceTimer = setTimeout(finish, 150);
115
+ return;
116
+ }
117
+ if (isError(raw)) {
118
+ result.errorMessage = raw.error;
119
+ result.workerLogs = raw.workerLogs ?? [];
120
+ graceTimer = setTimeout(finish, 150);
121
+ }
122
+ });
123
+ worker.on("error", (err: Error) => {
124
+ result.workerError = err.message;
125
+ });
126
+ worker.on("exit", (code: number) => {
127
+ result.exitCode = code;
128
+ finish();
129
+ });
130
+ });
131
+ }
132
+
133
+ /** 收集本测试进程的 unhandledRejection(P-T3 断言)。 */
134
+ function trackUnhandledRejections(): { reasons: unknown[]; dispose: () => void } {
135
+ const reasons: unknown[] = [];
136
+ const handler = (reason: unknown): void => {
137
+ reasons.push(reason);
138
+ };
139
+ process.on("unhandledRejection", handler);
140
+ return { reasons, dispose: () => process.off("unhandledRejection", handler) };
141
+ }
142
+
143
+ // ── [OR-3] per-call timeoutMs(消息层自己的超时) ─────────────
144
+
145
+ describe("[OR-3] agent() pending per-call timeoutMs", () => {
146
+ it("主线程不回话 + timeoutMs=50 → pending 以错误 resolve([B-5] 单值错误消息字符串)", async () => {
147
+ const script = `
148
+ const r = await agent("never answered", { timeoutMs: 50 });
149
+ return { got: r };
150
+ `;
151
+ const res = await runWorker(script);
152
+
153
+ // 脚本正常 return(超时未放大成脚本 error → 不触发 rebuild 矩阵)
154
+ expect(res.errorMessage).toBeUndefined();
155
+ expect(res.workerError).toBeUndefined();
156
+ expect(res.agentCallCount).toBe(1);
157
+ // [B-5] 非 returnMeta 超时 resolve 单值字符串(对齐 agent-result 失败路径
158
+ // resolve _value = parsedOutput ?? content 的形态)——字符串消费型脚本
159
+ // (r.trim() 类)不再 TypeError
160
+ const got = (res.returnValue as { got: string }).got;
161
+ expect(typeof got).toBe("string");
162
+ expect(got).toContain("timed out after 50ms");
163
+ expect(got).toContain("no agent-result received");
164
+ // 超时 warn 记入 _workerLogs(诊断可见)
165
+ expect(res.workerLogs.some((l) => l.level === "warn" && l.message.includes("timed out after 50ms"))).toBe(true);
166
+ });
167
+
168
+ it("returnMeta 模式超时(对象分支透传 returnMeta)→ resolve {value:\"\", error}", async () => {
169
+ const script = `
170
+ const r = await agent({ prompt: "never answered", timeoutMs: 50, returnMeta: true });
171
+ return r;
172
+ `;
173
+ const res = await runWorker(script);
174
+
175
+ expect(res.errorMessage).toBeUndefined();
176
+ expect(res.returnValue).toEqual({ value: "", error: expect.stringContaining("timed out after 50ms") });
177
+ });
178
+
179
+ it("[B-5] 字符串消费型脚本(r.trim())在超时路径不 TypeError(resolve 单值字符串)", async () => {
180
+ // 旧实现超时 resolve {content:"", error} 对象 → r.trim() 抛 TypeError →
181
+ // 脚本 error → 放大成 rebuild;修复后 resolve 字符串,脚本按普通失败文本消费
182
+ const script = `
183
+ const r = await agent("never answered", { timeoutMs: 40 });
184
+ return { trimmed: r.trim().length > 0 };
185
+ `;
186
+ const res = await runWorker(script);
187
+
188
+ expect(res.errorMessage).toBeUndefined();
189
+ expect(res.workerError).toBeUndefined();
190
+ expect(res.returnValue).toEqual({ trimmed: true });
191
+ });
192
+
193
+ it("string+secondArg 分支:timeoutMs 透传到 agent-call 消息 opts(此前该分支丢弃该字段)", async () => {
194
+ let seenOpts: Record<string, unknown> | undefined;
195
+ const script = `await agent("with timeout", { timeoutMs: 1234, label: "probe" }); return "unreachable";`;
196
+ await runWorker(script, {
197
+ onAgentCall: (msg, worker) => {
198
+ seenOpts = msg.opts;
199
+ // 回发结果让脚本收尾(透传断言与挂起行为解耦)
200
+ worker.postMessage({ type: "agent-result", callId: msg.callId, result: { content: "ok" }, cached: false });
201
+ },
202
+ });
203
+
204
+ expect(seenOpts?.timeoutMs).toBe(1234);
205
+ expect(seenOpts?.prompt).toBe("with timeout");
206
+ });
207
+ });
208
+
209
+ // ── [OR-3] abort 广播(worker 侧接收) ───────────────────────
210
+
211
+ describe("[OR-3] abort 广播接收(P-T3:交错不产 unhandledRejection)", () => {
212
+ it("await 中的 agent() 收到 abort → WorkflowAbortedError → 脚本 catch 后自然 return → exit 0", async () => {
213
+ const script = `
214
+ try {
215
+ await agent("will be aborted");
216
+ return "should not reach";
217
+ } catch (e) {
218
+ return { caught: e.name, reason: e.reason };
219
+ }
220
+ `;
221
+ const res = await runWorker(script, {
222
+ onAgentCall: (msg, worker) => {
223
+ worker.postMessage({ type: "abort", reason: "run aborted by user" });
224
+ },
225
+ });
226
+
227
+ expect(res.workerError).toBeUndefined();
228
+ expect(res.errorMessage).toBeUndefined();
229
+ expect(res.returnValue).toEqual({ caught: "WorkflowAbortedError", reason: "run aborted by user" });
230
+ });
231
+
232
+ it("广播后立即 terminate(竞态):无 worker error、无 unhandledRejection", async () => {
233
+ const tracked = trackUnhandledRejections();
234
+ try {
235
+ const script = `return await agent("race with terminate");`;
236
+ const res = await runWorker(script, {
237
+ onAgentCall: (msg, worker) => {
238
+ // 广播与 terminate 同 tick 发出——模拟 abortRun 的广播→transition(terminate) 竞态
239
+ worker.postMessage({ type: "abort", reason: "race" });
240
+ void worker.terminate();
241
+ },
242
+ });
243
+ // 无论脚本是否来得及收尾:主线程侧无 error 事件、进程无 unhandledRejection
244
+ expect(res.workerError).toBeUndefined();
245
+ // 给潜在异步 unhandledRejection 一个窗口
246
+ await new Promise((r) => { setTimeout(r, 20); });
247
+ expect(tracked.reasons).toEqual([]);
248
+ } finally {
249
+ tracked.dispose();
250
+ }
251
+ });
252
+
253
+ it("fire-and-forget agent() 后脚本 return:无 worker error(超时/abort 通路外的孤儿 pending 不产 rejection)", async () => {
254
+ const script = `
255
+ agent("fire and forget");
256
+ return "returned early";
257
+ `;
258
+ const res = await runWorker(script, { timeoutMs: 400 });
259
+
260
+ expect(res.returnValue).toBe("returned early");
261
+ expect(res.workerError).toBeUndefined();
262
+ });
263
+
264
+ it("fire-and-forget + timeoutMs:超时 resolve(非 reject)→ 无 worker error 事件", async () => {
265
+ const script = `
266
+ agent("orphan with timeout", { timeoutMs: 40 });
267
+ return "returned before timeout";
268
+ `;
269
+ const res = await runWorker(script, { timeoutMs: 600 });
270
+
271
+ // 超时以 resolve 收口(容错策略)——即使无人消费该 promise 也不产 rejection
272
+ expect(res.returnValue).toBe("returned before timeout");
273
+ expect(res.workerError).toBeUndefined();
274
+ });
275
+ });
@@ -53,6 +53,12 @@ interface ErrorMsg {
53
53
  error: string;
54
54
  workerLogs?: unknown[];
55
55
  }
56
+ /** log 消息:脚本 log() 全局发出的独立诊断消息(协议见 worker-script-builder 头注释)。 */
57
+ interface LogMsg {
58
+ type: "log";
59
+ phase?: string;
60
+ message?: string;
61
+ }
56
62
 
57
63
  // ── 类型守卫:从 unknown 收窄到判别联合 ──
58
64
  // 共享 hasType 辅助:避免每个守卫重复 `(m as {type?:string})` 断言(taste/no-unsafe-catch)。
@@ -74,16 +80,23 @@ function isReturn(m: unknown): m is ReturnMsg {
74
80
  function isError(m: unknown): m is ErrorMsg {
75
81
  return hasType(m, "error");
76
82
  }
83
+ function isLog(m: unknown): m is LogMsg {
84
+ return hasType(m, "log");
85
+ }
77
86
 
78
87
  // ── 测试辅助:起一个真实 Worker 跑 buildWorkerScript 产物 ──────────────
79
88
 
80
89
  interface RunResult {
81
90
  /** 收到的 return 消息的 result 字段(脚本正常结束时)。 */
82
91
  returnValue?: unknown;
92
+ /** return 消息带回的 workerLogs([B-3] 起只含 console.* 捕获条目,log() 不再走此通路)。 */
93
+ returnWorkerLogs?: unknown[];
83
94
  /** 收到的 error 消息的 error 字段(脚本 throw 时)。 */
84
95
  errorMessage?: string;
85
96
  /** error 消息带回的 workerLogs(验证诊断不丢)。 */
86
97
  errorWorkerLogs?: unknown[];
98
+ /** 收到的 {type:"log"} 独立消息列表([B-3] 起 log() 的唯一通路)。 */
99
+ logMessages: LogMsg[];
87
100
  /** Worker exit code(0=正常,1=崩溃)。 */
88
101
  exitCode?: number;
89
102
  /** Worker 'error' 事件的错误消息(uncaught exception,正常应为 undefined)。 */
@@ -94,6 +107,15 @@ interface RunResult {
94
107
  workflowCalls: WorkflowCallMsg[];
95
108
  }
96
109
 
110
+ /** workerLogs 条目守卫收窄 + message 包含判定(元素类型 unknown,断言前先收窄)。 */
111
+ function someLogMessageContains(logs: unknown[] | undefined, text: string): boolean {
112
+ return (logs ?? []).some((l) => {
113
+ if (typeof l !== "object" || l === null) return false;
114
+ const message = (l as { message?: unknown }).message;
115
+ return typeof message === "string" && message.includes(text);
116
+ });
117
+ }
118
+
97
119
  interface RunOptions {
98
120
  /** $ARGS。 */
99
121
  args?: Record<string, unknown>;
@@ -144,7 +166,7 @@ function runWorker(userScript: string, opts: RunOptions = {}): Promise<RunResult
144
166
  // S8:创建后立即登记,afterEach 兜底清理(防止 promise 泄漏导致 Worker 未终止)
145
167
  createdWorkers.push(worker);
146
168
 
147
- const result: RunResult = { agentCalls: [], workflowCalls: [] };
169
+ const result: RunResult = { agentCalls: [], workflowCalls: [], logMessages: [] };
148
170
  let agentCallIdx = 0;
149
171
  let resolved = false;
150
172
  const timer = setTimeout(() => {
@@ -181,8 +203,13 @@ function runWorker(userScript: string, opts: RunOptions = {}): Promise<RunResult
181
203
  result.workflowCalls.push(raw);
182
204
  const wfResult = opts.handleWorkflowCall ? opts.handleWorkflowCall(raw) : { ok: true };
183
205
  worker.postMessage({ type: "workflow-result", callId: raw.callId, result: wfResult });
206
+ } else if (isLog(raw)) {
207
+ // [OR-6/T7④] 独立 log 消息收集(主线程真实 runtime 当前无消费 case,测试
208
+ // harness 收集以断言「协议消息仍发出」通路不被回退)。
209
+ result.logMessages.push(raw);
184
210
  } else if (isReturn(raw)) {
185
211
  result.returnValue = raw.result;
212
+ result.returnWorkerLogs = raw.workerLogs;
186
213
  finish(result);
187
214
  } else if (isError(raw)) {
188
215
  result.errorMessage = raw.error;
@@ -382,6 +409,56 @@ describe("buildWorkerScript runtime — 之前缺失的路径覆盖", () => {
382
409
  });
383
410
  });
384
411
 
412
+ // ── [B-3] log() 单通路可观测性 ─────────────────────────────────────
413
+ // 旧双通路(log() 同时 post 独立消息 + 记入 _workerLogs 随 return/error 带回)让
414
+ // 同一日志在主线程 errorLogs 占两格、TUI 双份。修复后 log() 只发独立 {type:"log"}
415
+ // 消息,主线程 handleWorkerLog 即时消费(error-recovery.ts);崩溃场景丢 log 条目
416
+ // 可接受(log 是 T7 补充可观测)。
417
+
418
+ describe("buildWorkerScript runtime — [B-3] log() 单通路", () => {
419
+ it("log() 发出独立 {type:\"log\"} 消息且携带 phase(主线程即时消费的唯一通路)", async () => {
420
+ const script = `
421
+ phase("build");
422
+ log("hello from script");
423
+ return { ok: true };
424
+ `;
425
+ const res = await runWorker(script);
426
+ expect(res.workerError).toBeUndefined();
427
+ expect(res.logMessages).toEqual([{ type: "log", phase: "build", message: "hello from script" }]);
428
+ expect(res.exitCode).not.toBe(1);
429
+ });
430
+
431
+ it("log() 内容不再随 return 消息的 workerLogs 带回(双份入账已退役)", async () => {
432
+ const script = `
433
+ phase("build");
434
+ log("hello from script");
435
+ return { ok: true };
436
+ `;
437
+ const res = await runWorker(script);
438
+ expect(res.workerError).toBeUndefined();
439
+ // workerLogs 中不得出现 log() 的条目(否则主线程 log case + workerLogs 双份入账)
440
+ expect(someLogMessageContains(res.returnWorkerLogs, "hello from script")).toBe(false);
441
+ });
442
+
443
+ it("log() 内容不再随 error 消息的 workerLogs 带回(console.* 捕获通路不受影响)", async () => {
444
+ const script = `
445
+ log("before-crash");
446
+ console.error("crash-context");
447
+ throw new Error("boom");
448
+ `;
449
+ const res = await runWorker(script);
450
+ expect(res.errorMessage).toBe("boom");
451
+ // log() 的条目不在 error workerLogs(单通路)
452
+ expect(someLogMessageContains(res.errorWorkerLogs, "before-crash")).toBe(false);
453
+ // console.* 捕获通路保持(workerLogs 仍带回 console.error 条目)
454
+ expect(res.errorWorkerLogs).toEqual(
455
+ expect.arrayContaining([
456
+ expect.objectContaining({ level: "error", message: "crash-context" }),
457
+ ]),
458
+ );
459
+ });
460
+ });
461
+
385
462
  // ── W2: agent() returnMeta 模式运行时验证 ───────────────────────────
386
463
  // 现有 worker-script-builder.test.ts 的 16 条 returnMeta 断言全是字符串 toContain,
387
464
  // 无法验证「真实 Worker 线程执行时 agent({returnMeta:true}) resolve 出对象、
@@ -225,7 +225,7 @@ describe("buildWorkerScript — W2 agent() returnMeta mode", () => {
225
225
  // ── thinkingLevel 参数透传(agent() 三分支) ──
226
226
  // 验证 thinkingLevel 在 string / task-agent / object.prompt 三个分支均被透传至
227
227
  // agent-call 的 opts,且加入 _knownFields 白名单(object.prompt 分支整体透传,
228
- // 不被 unknown-fields warn 误报)。底层 AgentCallOpts→mapToExecuteOptions→
228
+ // 不被 unknown-fields warn 误报)。底层 AgentCallOpts(D6 合流后 EnginePort 直传,
229
229
  // buildSpawnArgs 拼 pi CLI --model provider/modelId:thinkingLevel 已打通,
230
230
  // 此处只验入口层 wiring。
231
231