@zhushanwen/pi-subagent-workflow 8.6.0 → 8.8.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 (364) hide show
  1. package/README.md +5 -11
  2. package/package.json +13 -20
  3. package/relay/relay.mjs +1 -1
  4. package/skills/workflow-script-format/SKILL.md +32 -13
  5. package/src/host/__tests__/pi-host.test.ts +235 -0
  6. package/src/host/pi-host.ts +163 -0
  7. package/src/index.ts +141 -138
  8. package/src/injectors/__tests__/engine-awareness.test.ts +292 -0
  9. package/src/injectors/__tests__/engine-section-stability.test.ts +343 -0
  10. package/src/injectors/__tests__/model-list-injector.test.ts +18 -21
  11. package/src/injectors/__tests__/subagent-list-injector.test.ts +59 -19
  12. package/src/injectors/__tests__/workflow-list-injector.test.ts +31 -17
  13. package/src/injectors/engine-awareness.ts +119 -0
  14. package/src/injectors/model-list-injector.ts +15 -58
  15. package/src/injectors/subagent-list-injector.ts +56 -114
  16. package/src/injectors/workflow-list-injector.ts +37 -70
  17. package/src/interface/__tests__/detectors.test.ts +48 -37
  18. package/src/interface/__tests__/subagent-tool-path-guard.test.ts +2 -2
  19. package/src/interface/__tests__/subagent-tool-prompt.test.ts +6 -3
  20. package/src/interface/__tests__/tool-workflow-run-builtin-name.test.ts +216 -0
  21. package/src/interface/__tests__/tool-workflow-script-generate.test.ts +17 -6
  22. package/src/interface/__tests__/tool-workflow-throw-paths.test.ts +38 -2
  23. package/src/interface/bg-notify-render.ts +7 -17
  24. package/src/interface/command-actions.ts +5 -15
  25. package/src/interface/commands.ts +4 -4
  26. package/src/interface/format.ts +17 -6
  27. package/src/interface/gui-mappers.ts +18 -22
  28. package/src/interface/helpers.ts +9 -130
  29. package/src/interface/list-component.ts +5 -5
  30. package/src/interface/list-shared.ts +3 -3
  31. package/src/interface/list-view.ts +3 -3
  32. package/src/interface/subagent-actions.ts +66 -439
  33. package/src/interface/subagent-tool-schema.ts +21 -17
  34. package/src/interface/subagent-tool.ts +12 -7
  35. package/src/interface/subagents.ts +4 -4
  36. package/src/interface/tool-render.ts +37 -15
  37. package/src/interface/tool-workflow-script.ts +34 -76
  38. package/src/interface/tool-workflow.ts +57 -94
  39. package/src/interface/views/WorkflowsView.ts +8 -16
  40. package/src/interface/views/__tests__/WorkflowsView-signature.test.ts +3 -3
  41. package/src/interface/views/__tests__/detail-content-session-file.test.ts +2 -2
  42. package/src/interface/views/detail-content.ts +4 -4
  43. package/src/interface/views/format.ts +23 -62
  44. package/src/{orchestration/jsonl-run-store.ts → jsonl-run-store.ts} +137 -225
  45. package/agents/analyst.md +0 -61
  46. package/agents/coder.md +0 -70
  47. package/agents/debugger.md +0 -67
  48. package/agents/doc-reviewer.md +0 -50
  49. package/agents/explorer.md +0 -64
  50. package/agents/general-purpose.md +0 -32
  51. package/agents/orchestrator.md +0 -63
  52. package/agents/planner.md +0 -54
  53. package/agents/researcher.md +0 -65
  54. package/agents/reviewer.md +0 -74
  55. package/src/execution/__tests__/__fixtures__/notifier-golden-snapshots.json +0 -53
  56. package/src/execution/__tests__/__fixtures__/truncline.snapshot.json +0 -1
  57. package/src/execution/__tests__/agent-registry.test.ts +0 -363
  58. package/src/execution/__tests__/agent-result-mapper.test.ts +0 -150
  59. package/src/execution/__tests__/alive-store.test.ts +0 -147
  60. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +0 -490
  61. package/src/execution/__tests__/before-agent-start-injection.test.ts +0 -132
  62. package/src/execution/__tests__/bg-notify-render.test.ts +0 -329
  63. package/src/execution/__tests__/channel-registry-handshake.test.ts +0 -243
  64. package/src/execution/__tests__/chat-engine-routing.test.ts +0 -601
  65. package/src/execution/__tests__/chatmode-first-round-closure-service.test.ts +0 -365
  66. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +0 -190
  67. package/src/execution/__tests__/chatmode-round-notify-real-chain.test.ts +0 -215
  68. package/src/execution/__tests__/concurrency-pool.test.ts +0 -250
  69. package/src/execution/__tests__/config.test.ts +0 -110
  70. package/src/execution/__tests__/conversation-wiring.test.ts +0 -198
  71. package/src/execution/__tests__/crash-recovery.test.ts +0 -320
  72. package/src/execution/__tests__/delivery-methods.test.ts +0 -422
  73. package/src/execution/__tests__/dialog-queue.test.ts +0 -299
  74. package/src/execution/__tests__/epipe-fallback.test.ts +0 -241
  75. package/src/execution/__tests__/execute-and-await-worktree.test.ts +0 -243
  76. package/src/execution/__tests__/execute-nesting.test.ts +0 -323
  77. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
  78. package/src/execution/__tests__/execution-record.test.ts +0 -1394
  79. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +0 -171
  80. package/src/execution/__tests__/finalize-record.test.ts +0 -368
  81. package/src/execution/__tests__/finalized-marker.test.ts +0 -82
  82. package/src/execution/__tests__/format-schema-instruction.test.ts +0 -169
  83. package/src/execution/__tests__/format.test.ts +0 -458
  84. package/src/execution/__tests__/gc-timer.test.ts +0 -184
  85. package/src/execution/__tests__/get-record-for-action-restart.test.ts +0 -254
  86. package/src/execution/__tests__/gui-mode-dispatch.test.ts +0 -59
  87. package/src/execution/__tests__/helpers/mock-extension-api.ts +0 -30
  88. package/src/execution/__tests__/helpers/spawn-mock.ts +0 -242
  89. package/src/execution/__tests__/host-mode.test.ts +0 -87
  90. package/src/execution/__tests__/index-session-start-identity.test.ts +0 -371
  91. package/src/execution/__tests__/index-session-start.test.ts +0 -672
  92. package/src/execution/__tests__/lifecycle-manager-lock.test.ts +0 -211
  93. package/src/execution/__tests__/lifecycle-manager.test.ts +0 -383
  94. package/src/execution/__tests__/lifecycle-predicates.test.ts +0 -116
  95. package/src/execution/__tests__/list-component.test.ts +0 -401
  96. package/src/execution/__tests__/list-fields.test.ts +0 -140
  97. package/src/execution/__tests__/manifest-parentid.test.ts +0 -117
  98. package/src/execution/__tests__/model-resolver.test.ts +0 -465
  99. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +0 -287
  100. package/src/execution/__tests__/nested-visibility.test.ts +0 -325
  101. package/src/execution/__tests__/notifier-flush.test.ts +0 -612
  102. package/src/execution/__tests__/notifier-golden-snapshot.test.ts +0 -261
  103. package/src/execution/__tests__/notify-ledger.test.ts +0 -826
  104. package/src/execution/__tests__/one-shot-upgrade.test.ts +0 -205
  105. package/src/execution/__tests__/output-collector.test.ts +0 -358
  106. package/src/execution/__tests__/parent-child-matrix.test.ts +0 -336
  107. package/src/execution/__tests__/path-encoding.test.ts +0 -104
  108. package/src/execution/__tests__/pi-invocation.test.ts +0 -134
  109. package/src/execution/__tests__/record-store.test.ts +0 -1057
  110. package/src/execution/__tests__/records-cwd-isolation.test.ts +0 -91
  111. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +0 -339
  112. package/src/execution/__tests__/recursive-visibility-env.test.ts +0 -273
  113. package/src/execution/__tests__/relay-agent.test.ts +0 -448
  114. package/src/execution/__tests__/relay-env.test.ts +0 -42
  115. package/src/execution/__tests__/resource-policy.test.ts +0 -109
  116. package/src/execution/__tests__/rpc-mode.test.ts +0 -89
  117. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +0 -267
  118. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +0 -253
  119. package/src/execution/__tests__/run-spawn-edges.test.ts +0 -687
  120. package/src/execution/__tests__/run-spawn-integration.test.ts +0 -933
  121. package/src/execution/__tests__/run-spawn-resume.test.ts +0 -322
  122. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +0 -196
  123. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +0 -199
  124. package/src/execution/__tests__/sdk-contract.test.ts +0 -273
  125. package/src/execution/__tests__/session-context-resolver.test.ts +0 -167
  126. package/src/execution/__tests__/session-file-gc.test.ts +0 -293
  127. package/src/execution/__tests__/session-pending.test.ts +0 -197
  128. package/src/execution/__tests__/session-reconstructor.test.ts +0 -379
  129. package/src/execution/__tests__/session-runner-epipe.test.ts +0 -178
  130. package/src/execution/__tests__/session-runner-schema-env.test.ts +0 -350
  131. package/src/execution/__tests__/session-start-reaper.test.ts +0 -233
  132. package/src/execution/__tests__/spawn-args.test.ts +0 -445
  133. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +0 -189
  134. package/src/execution/__tests__/spawn-event-adapter.test.ts +0 -167
  135. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +0 -207
  136. package/src/execution/__tests__/spawned-children.test.ts +0 -92
  137. package/src/execution/__tests__/start-sync-model-guard.test.ts +0 -150
  138. package/src/execution/__tests__/startup-config-declaration.test.ts +0 -35
  139. package/src/execution/__tests__/status-refactor.test.ts +0 -345
  140. package/src/execution/__tests__/stdin-writer.test.ts +0 -463
  141. package/src/execution/__tests__/stream-sink-retirement.test.ts +0 -261
  142. package/src/execution/__tests__/subagent-service-abort.test.ts +0 -60
  143. package/src/execution/__tests__/subagent-service-message-close.test.ts +0 -629
  144. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +0 -180
  145. package/src/execution/__tests__/subagent-service.test.ts +0 -788
  146. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +0 -310
  147. package/src/execution/__tests__/subprocess-agent-runner.test.ts +0 -599
  148. package/src/execution/__tests__/temp-prompt.test.ts +0 -53
  149. package/src/execution/__tests__/timeout-integration.test.ts +0 -615
  150. package/src/execution/__tests__/tombstone-store.test.ts +0 -73
  151. package/src/execution/__tests__/tool-action.test.ts +0 -476
  152. package/src/execution/__tests__/truncline-snapshot.test.ts +0 -81
  153. package/src/execution/__tests__/turn-limiter-semantics.test.ts +0 -194
  154. package/src/execution/__tests__/turn-limiter.test.ts +0 -65
  155. package/src/execution/__tests__/ui-channels.test.ts +0 -187
  156. package/src/execution/__tests__/ui-interaction-model.test.ts +0 -67
  157. package/src/execution/__tests__/ui-request-handler-factory.test.ts +0 -370
  158. package/src/execution/__tests__/ui-request-handler.test.ts +0 -204
  159. package/src/execution/__tests__/ui-request-observability.test.ts +0 -114
  160. package/src/execution/__tests__/ui-request-queue.test.ts +0 -133
  161. package/src/execution/__tests__/worktree-manager.test.ts +0 -633
  162. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +0 -229
  163. package/src/execution/__tests__/worktree-reconcile.integration.test.ts +0 -181
  164. package/src/execution/__tests__/worktree-registry.test.ts +0 -199
  165. package/src/execution/agent-registry.ts +0 -199
  166. package/src/execution/agent-result-mapper.ts +0 -90
  167. package/src/execution/alive-store.ts +0 -92
  168. package/src/execution/argv-mirror.ts +0 -113
  169. package/src/execution/best-effort.ts +0 -38
  170. package/src/execution/channel-registry-access.ts +0 -144
  171. package/src/execution/concurrency-pool.ts +0 -116
  172. package/src/execution/config.ts +0 -96
  173. package/src/execution/dialog-queue.ts +0 -330
  174. package/src/execution/engine/__tests__/common/data-dir.test.ts +0 -53
  175. package/src/execution/engine/__tests__/common/errors.test.ts +0 -132
  176. package/src/execution/engine/__tests__/common/event-journal.test.ts +0 -177
  177. package/src/execution/engine/__tests__/common/kill-chain.test.ts +0 -192
  178. package/src/execution/engine/__tests__/common/nesting-guard.test.ts +0 -81
  179. package/src/execution/engine/__tests__/common/persona-router.test.ts +0 -123
  180. package/src/execution/engine/__tests__/common/pool-manager.test.ts +0 -154
  181. package/src/execution/engine/__tests__/common/schema-emulation.test.ts +0 -128
  182. package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +0 -28
  183. package/src/execution/engine/__tests__/conformance/agent-event-invariants.ts +0 -141
  184. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +0 -109
  185. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +0 -101
  186. package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +0 -77
  187. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +0 -104
  188. package/src/execution/engine/__tests__/conformance/contract.relay.test.ts +0 -342
  189. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +0 -201
  190. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +0 -76
  191. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +0 -79
  192. package/src/execution/engine/__tests__/engine-discovery.test.ts +0 -87
  193. package/src/execution/engine/__tests__/engines-declaration.test.ts +0 -36
  194. package/src/execution/engine/__tests__/model-prompt.test.ts +0 -85
  195. package/src/execution/engine/__tests__/paths.test.ts +0 -39
  196. package/src/execution/engine/__tests__/registry.test.ts +0 -120
  197. package/src/execution/engine/__tests__/routing.test.ts +0 -231
  198. package/src/execution/engine/common/data-dir.ts +0 -62
  199. package/src/execution/engine/common/errors.ts +0 -183
  200. package/src/execution/engine/common/event-journal.ts +0 -254
  201. package/src/execution/engine/common/journal-replay.ts +0 -62
  202. package/src/execution/engine/common/kill-chain.ts +0 -221
  203. package/src/execution/engine/common/nesting-guard.ts +0 -50
  204. package/src/execution/engine/common/persona-router.ts +0 -108
  205. package/src/execution/engine/common/pool-manager.ts +0 -226
  206. package/src/execution/engine/common/schema-emulation.ts +0 -189
  207. package/src/execution/engine/common/session-view-projection.ts +0 -51
  208. package/src/execution/engine/engine-discovery.ts +0 -65
  209. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +0 -469
  210. package/src/execution/engine/engines/pi/__tests__/reader.test.ts +0 -155
  211. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  212. package/src/execution/engine/engines/pi/pi-engine.ts +0 -415
  213. package/src/execution/engine/engines/pi/reader.ts +0 -48
  214. package/src/execution/engine/engines/pi/registration.ts +0 -35
  215. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  216. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  217. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  218. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +0 -246
  219. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +0 -228
  220. package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +0 -210
  221. package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +0 -64
  222. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +0 -127
  223. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -580
  224. package/src/execution/engine/engines/zcode/constants.ts +0 -43
  225. package/src/execution/engine/engines/zcode/golden-sample.ts +0 -39
  226. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  227. package/src/execution/engine/engines/zcode/parser.ts +0 -436
  228. package/src/execution/engine/engines/zcode/preparer.ts +0 -363
  229. package/src/execution/engine/engines/zcode/reader.ts +0 -381
  230. package/src/execution/engine/engines/zcode/registration.ts +0 -37
  231. package/src/execution/engine/engines/zcode/zcode-engine.ts +0 -658
  232. package/src/execution/engine/host-task-spec.ts +0 -47
  233. package/src/execution/engine/model-prompt.ts +0 -59
  234. package/src/execution/engine/paths.ts +0 -42
  235. package/src/execution/engine/port.ts +0 -153
  236. package/src/execution/engine/registry.ts +0 -123
  237. package/src/execution/engine/routing.ts +0 -218
  238. package/src/execution/engine/types.ts +0 -309
  239. package/src/execution/execute-options-mapper.ts +0 -112
  240. package/src/execution/execution-record.ts +0 -965
  241. package/src/execution/finalize-record.ts +0 -251
  242. package/src/execution/finalized-marker.ts +0 -51
  243. package/src/execution/get-state-handshake.ts +0 -104
  244. package/src/execution/host-mode.ts +0 -56
  245. package/src/execution/idle-gc.ts +0 -47
  246. package/src/execution/lifecycle-manager.ts +0 -513
  247. package/src/execution/lifecycle-predicates.ts +0 -65
  248. package/src/execution/manifest-store.ts +0 -256
  249. package/src/execution/model-config-service.ts +0 -219
  250. package/src/execution/model-resolver.ts +0 -248
  251. package/src/execution/notifier.ts +0 -330
  252. package/src/execution/notify-ledger.ts +0 -580
  253. package/src/execution/output-collector.ts +0 -228
  254. package/src/execution/path-encoding.ts +0 -60
  255. package/src/execution/pi-invocation.ts +0 -120
  256. package/src/execution/record-entry.ts +0 -132
  257. package/src/execution/record-store.ts +0 -1234
  258. package/src/execution/relay-env.ts +0 -37
  259. package/src/execution/session-context-resolver.ts +0 -64
  260. package/src/execution/session-file-gc.ts +0 -120
  261. package/src/execution/session-pending.ts +0 -164
  262. package/src/execution/session-reconstructor.ts +0 -678
  263. package/src/execution/session-runner.ts +0 -1781
  264. package/src/execution/sessions-index.ts +0 -304
  265. package/src/execution/spawn-event-adapter.ts +0 -363
  266. package/src/execution/stdin-writer.ts +0 -198
  267. package/src/execution/stream-sink.ts +0 -126
  268. package/src/execution/subagent-service.ts +0 -2141
  269. package/src/execution/subprocess-agent-runner.ts +0 -317
  270. package/src/execution/temp-prompt.ts +0 -62
  271. package/src/execution/tombstone-store.ts +0 -72
  272. package/src/execution/turn-limiter.ts +0 -102
  273. package/src/execution/types.ts +0 -973
  274. package/src/execution/ui-channels.ts +0 -216
  275. package/src/execution/ui-interaction-model.ts +0 -48
  276. package/src/execution/ui-request-handler-factory.ts +0 -212
  277. package/src/execution/ui-request-observability.ts +0 -81
  278. package/src/execution/ui-request-queue.ts +0 -184
  279. package/src/execution/worktree-manager.ts +0 -688
  280. package/src/execution/worktree-registry.ts +0 -222
  281. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +0 -349
  282. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +0 -202
  283. package/src/orchestration/__tests__/agent-call-stream.test.ts +0 -152
  284. package/src/orchestration/__tests__/args-validator.test.ts +0 -143
  285. package/src/orchestration/__tests__/config-loader.test.ts +0 -503
  286. package/src/orchestration/__tests__/error-recovery-handlers.test.ts +0 -809
  287. package/src/orchestration/__tests__/error-recovery-postmessage-defense.test.ts +0 -405
  288. package/src/orchestration/__tests__/error-recovery-serialize-failed-result.test.ts +0 -56
  289. package/src/orchestration/__tests__/error-recovery-workflow-call.test.ts +0 -166
  290. package/src/orchestration/__tests__/execute-agent-call.test.ts +0 -451
  291. package/src/orchestration/__tests__/jsonl-run-store-corrupt-entry.test.ts +0 -150
  292. package/src/orchestration/__tests__/jsonl-run-store-loadall-sources.test.ts +0 -171
  293. package/src/orchestration/__tests__/jsonl-run-store-retention.test.ts +0 -202
  294. package/src/orchestration/__tests__/jsonl-run-store-session-file.test.ts +0 -1030
  295. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +0 -600
  296. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +0 -96
  297. package/src/orchestration/__tests__/lifecycle.test.ts +0 -659
  298. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +0 -95
  299. package/src/orchestration/__tests__/review-fix-loop-e2e.test.ts +0 -2025
  300. package/src/orchestration/__tests__/script-lint.test.ts +0 -831
  301. package/src/orchestration/__tests__/skill-discovery.test.ts +0 -201
  302. package/src/orchestration/__tests__/test-mocks.ts +0 -197
  303. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +0 -368
  304. package/src/orchestration/__tests__/worker-host.test.ts +0 -120
  305. package/src/orchestration/__tests__/worker-returnmeta-passthrough.test.ts +0 -164
  306. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +0 -563
  307. package/src/orchestration/__tests__/worker-script-builder.test.ts +0 -309
  308. package/src/orchestration/__tests__/worker-script-template-snapshot.test.ts +0 -129
  309. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +0 -317
  310. package/src/orchestration/__tests__/workflow-script-lint-memo.test.ts +0 -110
  311. package/src/orchestration/__tests__/workflows-e2e.test.ts +0 -537
  312. package/src/orchestration/agent-opts-resolver.ts +0 -174
  313. package/src/orchestration/args-validator.ts +0 -127
  314. package/src/orchestration/config-loader.ts +0 -315
  315. package/src/orchestration/error-recovery.ts +0 -1018
  316. package/src/orchestration/execute-agent-call.ts +0 -256
  317. package/src/orchestration/launcher.ts +0 -455
  318. package/src/orchestration/lifecycle.ts +0 -399
  319. package/src/orchestration/models/__tests__/budget.test.ts +0 -307
  320. package/src/orchestration/models/__tests__/trace.test.ts +0 -408
  321. package/src/orchestration/models/agent-call.ts +0 -83
  322. package/src/orchestration/models/budget.ts +0 -118
  323. package/src/orchestration/models/ports.ts +0 -164
  324. package/src/orchestration/models/run-runtime.ts +0 -104
  325. package/src/orchestration/models/run-spec.ts +0 -83
  326. package/src/orchestration/models/run-state.ts +0 -44
  327. package/src/orchestration/models/trace.ts +0 -183
  328. package/src/orchestration/models/types.ts +0 -301
  329. package/src/orchestration/models/workflow-run.ts +0 -254
  330. package/src/orchestration/models/workflow-script-registry.ts +0 -35
  331. package/src/orchestration/models/workflow-script.ts +0 -117
  332. package/src/orchestration/script-lint.ts +0 -766
  333. package/src/orchestration/skill-discovery.ts +0 -115
  334. package/src/orchestration/worker-handle.ts +0 -115
  335. package/src/orchestration/worker-host.ts +0 -98
  336. package/src/orchestration/worker-script-builder.ts +0 -421
  337. package/src/orchestration/workflow-files.ts +0 -85
  338. package/src/orchestration/workflow-script-registry-impl.ts +0 -133
  339. package/src/shared/__tests__/agent-ref.test.ts +0 -34
  340. package/src/shared/__tests__/meta-parser.test.ts +0 -304
  341. package/src/shared/__tests__/model-ref.test.ts +0 -306
  342. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -280
  343. package/src/shared/__tests__/resource-discovery.test.ts +0 -736
  344. package/src/shared/__tests__/resource-meta.test.ts +0 -51
  345. package/src/shared/__tests__/schema-jsonify.test.ts +0 -81
  346. package/src/shared/__tests__/timer-delay.test.ts +0 -61
  347. package/src/shared/agent-event.ts +0 -13
  348. package/src/shared/agent-ref.ts +0 -57
  349. package/src/shared/meta-parser.ts +0 -261
  350. package/src/shared/model-ref.ts +0 -286
  351. package/src/shared/resource-discovery.ts +0 -811
  352. package/src/shared/resource-meta.ts +0 -65
  353. package/src/shared/schema-env.ts +0 -44
  354. package/src/shared/schema-jsonify.ts +0 -58
  355. package/src/shared/timer-delay.ts +0 -54
  356. package/src/shared/xml-injection.ts +0 -35
  357. package/workflows/README.md +0 -81
  358. package/workflows/_shared/agent-refs.cjs +0 -40
  359. package/workflows/chain.js +0 -131
  360. package/workflows/map-reduce.js +0 -174
  361. package/workflows/parallel.js +0 -148
  362. package/workflows/review-fix-loop-utils.cjs +0 -1309
  363. package/workflows/review-fix-loop.js +0 -1404
  364. package/workflows/scatter-gather.js +0 -178
@@ -1,809 +0,0 @@
1
- /**
2
- * error-recovery handlers — handleWorkerExit/Error/ScriptError + postBudgetUpdate 测试。
3
- *
4
- * 参考 error-recovery-workflow-call.test.ts 的 mock 构建。通过 vi.useFakeTimers() 跳过
5
- * scheduleRebuild 的指数退避(1s/2s/4s)。
6
- *
7
- * 覆盖:
8
- * - handleWorkerExit:code=0 正常退出(no-op) / code!=0 委托 handleWorkerError / stale handle 过滤
9
- * - handleWorkerError:超限(count > MAX=3)→ transition done,failed + emit pending:unregister
10
- * / 未超限 → rebuildRuntime(workerHost.start 重建)
11
- * - handleScriptError:超限 → transition done,failed / workerLogs 捕获
12
- * - postBudgetUpdate:postMessage budget-update(usedTokens/usedCost)
13
- * - stale handle 过滤(handle.isCurrent=false)+ terminal stale 守卫(isTerminal 语义)
14
- * - rebuildRuntime:worker 崩溃后 workerHost.start + scheduleTimeBudget 重排 + replaceRuntime
15
- * + 在飞 call 清理(discardInFlightCalls 生效:在飞清除、done 保留)
16
- */
17
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
18
-
19
- import {
20
- handleScriptError,
21
- handleWorkerError,
22
- handleWorkerExit,
23
- handleWorkerMessage,
24
- postBudgetUpdate,
25
- rebuildRuntime,
26
- } from "../error-recovery.ts";
27
- import { Budget } from "../models/budget.ts";
28
- import { RunRuntime } from "../models/run-runtime.ts";
29
- import { Trace } from "../models/trace.ts";
30
- import type { AgentResult } from "../models/types.ts";
31
- import { WorkflowRun } from "../models/workflow-run.ts";
32
- import type { LifecycleDeps, WorkerHandlers } from "../models/ports.ts";
33
- import type { WorkerHandle } from "../worker-handle.ts";
34
-
35
- // ── helpers ──────────────────────────────────────────────────
36
-
37
- /** 构造一个 status="running" 的 mock WorkflowRun,meta 可配置。 */
38
- function makeRunningRun(opts: {
39
- workerErrorCount?: number;
40
- scriptErrorCount?: number;
41
- budgetTimeMs?: number;
42
- postMessage?: ReturnType<typeof vi.fn>;
43
- /** [F1] 预置本 runtime 代际已收到终态消息(return/error)。 */
44
- receivedTerminalMessage?: boolean;
45
- } = {}): WorkflowRun {
46
- return {
47
- state: {
48
- status: "running",
49
- budget: { usedTokens: 50, usedCost: 0.1 },
50
- // L9: errorLogs 现在用 push 追加——必须是真实数组,不能省略
51
- errorLogs: [],
52
- // rebuildRuntime 内 discardInFlightCalls 遍历 calls + 移除 trace 节点——
53
- // 必须是真实 Map(空 = 无在飞 call,discard 为 no-op)
54
- calls: new Map(),
55
- trace: { removeByStepIndex: vi.fn() },
56
- },
57
- meta: {
58
- startedAt: new Date().toISOString(),
59
- workerErrorCount: opts.workerErrorCount,
60
- scriptErrorCount: opts.scriptErrorCount,
61
- },
62
- spec: {
63
- scriptName: "test-wf",
64
- scriptSource: "execute() {}",
65
- args: {},
66
- budgetTimeMs: opts.budgetTimeMs,
67
- },
68
- runtime: {
69
- worker: { postMessage: opts.postMessage ?? vi.fn() },
70
- receivedTerminalMessage: opts.receivedTerminalMessage,
71
- },
72
- // transition 副作用——run.state.status 由调用方通过 mock 控制后再次断言
73
- transition(target: string, reason?: string): void {
74
- this.state.status = target;
75
- if (target === "done") this.state.reason = reason;
76
- },
77
- replaceRuntime(rt: unknown): void {
78
- this.runtime = rt;
79
- },
80
- } as unknown as WorkflowRun;
81
- }
82
-
83
- /** LifecycleDeps mock:store/workerHost/runner/eventBus/scheduleTimeBudget 可观察。 */
84
- function makeDeps(opts: {
85
- 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
- } {
93
- return {
94
- store: { save: vi.fn(async () => {}) },
95
- workerHost: { start: vi.fn(() => ({ postMessage: vi.fn() })) },
96
- runner: { run: vi.fn(async () => ({})) },
97
- runs: new Map(),
98
- eventBus: { emit: vi.fn() },
99
- onRunDone: vi.fn(),
100
- log: vi.fn(),
101
- scheduleTimeBudget: opts.scheduleTimeBudget,
102
- } as unknown as ReturnType<typeof makeDeps>;
103
- }
104
-
105
- /** WorkerHandlers 占位(handler 路径递归调本对象上的回调,但测试场景不触发)。 */
106
- function makeHandlers(): WorkerHandlers {
107
- return {
108
- onMessage: vi.fn(async () => {}),
109
- onError: vi.fn(async () => {}),
110
- onExit: vi.fn(async () => {}),
111
- } as unknown as WorkerHandlers;
112
- }
113
-
114
- /** 构造 mock WorkerHandle(isCurrent 可配)。 */
115
- function makeHandle(isCurrent = true): WorkerHandle {
116
- return { isCurrent } as unknown as WorkerHandle;
117
- }
118
-
119
- beforeEach(() => {
120
- vi.useFakeTimers();
121
- });
122
-
123
- afterEach(() => {
124
- vi.useRealTimers();
125
- });
126
-
127
- // ── handleWorkerExit ─────────────────────────────────────────
128
-
129
- describe("handleWorkerExit", () => {
130
- it("code=0 且已收到终态消息:no-op(不 transition、不 save)", async () => {
131
- // [F1] 语义更新:exit(0) no-op 的前提是本代际已交付 return/error(正常收尾退出,
132
- // 或 script-error 重试退避窗口)。无终态消息的 exit(0) 现转 done,failed,
133
- // 见下方用例与 worker-exit-without-result.test.ts。
134
- const run = makeRunningRun({ receivedTerminalMessage: true });
135
- const deps = makeDeps();
136
- const handle = makeHandle(true);
137
-
138
- await handleWorkerExit(run, 0, handle, deps, makeHandlers());
139
-
140
- expect(run.state.status).toBe("running"); // 未改
141
- expect(deps.store.save).not.toHaveBeenCalled();
142
- expect(deps.eventBus.emit).not.toHaveBeenCalled();
143
- });
144
-
145
- it("code=0 且无终态消息:[F1] 转 done,failed(不可克隆 return 被吞的悬挂防线)", async () => {
146
- const run = makeRunningRun();
147
- const deps = makeDeps();
148
- const handle = makeHandle(true);
149
-
150
- await handleWorkerExit(run, 0, handle, deps, makeHandlers());
151
-
152
- expect(run.state.status).toBe("done");
153
- expect(run.state.reason).toBe("failed");
154
- expect(run.state.error).toContain("structured-cloneable");
155
- expect(deps.store.save).toHaveBeenCalledTimes(1);
156
- expect(deps.onRunDone).toHaveBeenCalledTimes(1);
157
- });
158
-
159
- it("code!=0 异常退出:委托 handleWorkerError → 超 MAX 重试 → transition done,failed", async () => {
160
- // workerErrorCount 已达 MAX=3 → handleWorkerError 内 count=4 > 3 → failed
161
- const run = makeRunningRun({ workerErrorCount: 3 });
162
- const deps = makeDeps();
163
- const handle = makeHandle(true);
164
-
165
- await handleWorkerExit(run, 1, handle, deps, makeHandlers());
166
-
167
- expect(run.state.status).toBe("done");
168
- expect(run.state.reason).toBe("failed");
169
- expect(run.state.error).toContain("Worker exited with code 1");
170
- // 持久化 + 完成通知
171
- expect(deps.store.save).toHaveBeenCalledTimes(1);
172
- expect(deps.onRunDone).toHaveBeenCalledTimes(1);
173
- });
174
-
175
- it("stale handle(isCurrent=false):丢弃 exit 事件,不处理", async () => {
176
- const run = makeRunningRun();
177
- const deps = makeDeps();
178
- const staleHandle = makeHandle(false);
179
-
180
- await handleWorkerExit(run, 1, staleHandle, deps, makeHandlers());
181
-
182
- // 状态未变,store 未 save
183
- expect(run.state.status).toBe("running");
184
- expect(deps.store.save).not.toHaveBeenCalled();
185
- });
186
-
187
- it("run 已终态(done):stale 守卫前置丢弃", async () => {
188
- const run = makeRunningRun();
189
- run.state.status = "done";
190
- (run.state as { reason?: string }).reason = "completed";
191
- const deps = makeDeps();
192
- const handle = makeHandle(true);
193
-
194
- await handleWorkerExit(run, 1, handle, deps, makeHandlers());
195
-
196
- expect(deps.store.save).not.toHaveBeenCalled();
197
- });
198
- });
199
-
200
- // ── handleWorkerError ────────────────────────────────────────
201
-
202
- describe("handleWorkerError", () => {
203
- it("count > MAX(3):transition done,failed + save + emit pending:unregister", async () => {
204
- // workerErrorCount=3 → count=4 > MAX
205
- const run = makeRunningRun({ workerErrorCount: 3 });
206
- const deps = makeDeps();
207
-
208
- await handleWorkerError(run, new Error("worker boom"), deps, makeHandlers());
209
-
210
- expect(run.meta.workerErrorCount).toBe(4);
211
- expect(run.state.status).toBe("done");
212
- expect(run.state.reason).toBe("failed");
213
- expect(run.state.error).toBe("worker boom");
214
- expect(deps.store.save).toHaveBeenCalledTimes(1);
215
- expect(deps.eventBus.emit).toHaveBeenCalledWith("pending:unregister", {
216
- id: undefined, // mock run 无 runId
217
- reason: "failed",
218
- });
219
- expect(deps.onRunDone).toHaveBeenCalledTimes(1);
220
- });
221
-
222
- it("count <= MAX:退避 + rebuildRuntime(workerHost.start 重建新 runtime)", async () => {
223
- const run = makeRunningRun({ workerErrorCount: 0 }); // count=1 <= MAX
224
- const deps = makeDeps();
225
-
226
- const promise = handleWorkerError(run, new Error("transient"), deps, makeHandlers());
227
-
228
- // 推进指数退避(第 1 次重试:1s)
229
- await vi.advanceTimersByTimeAsync(1000);
230
- await promise;
231
-
232
- expect(run.meta.workerErrorCount).toBe(1);
233
- // 状态仍 running(重试不改 status)
234
- expect(run.state.status).toBe("running");
235
- // workerHost.start 被调(rebuildRuntime 内重建 worker)
236
- expect(deps.workerHost.start).toHaveBeenCalledTimes(1);
237
- });
238
-
239
- it("终态(done):stale 守卫前置丢弃(不递增 workerErrorCount)", async () => {
240
- const run = makeRunningRun();
241
- run.state.status = "done";
242
- (run.state as { reason?: string }).reason = "completed";
243
- const deps = makeDeps();
244
-
245
- await handleWorkerError(run, new Error("stale"), deps, makeHandlers());
246
-
247
- expect(run.meta.workerErrorCount).toBeUndefined(); // 未递增
248
- expect(deps.store.save).not.toHaveBeenCalled();
249
- });
250
- });
251
-
252
- // ── handleScriptError ────────────────────────────────────────
253
-
254
- describe("handleScriptError", () => {
255
- it("count > MAX(3):transition done,failed + 捕获 workerLogs", async () => {
256
- const run = makeRunningRun({ scriptErrorCount: 3 }); // count=4 > MAX
257
- const deps = makeDeps();
258
- const workerLogs = [
259
- { level: "error" as const, message: "line 5 boom" },
260
- ];
261
-
262
- await handleScriptError(run, "TypeError: x is undefined", workerLogs, deps, makeHandlers());
263
-
264
- expect(run.meta.scriptErrorCount).toBe(4);
265
- expect(run.state.status).toBe("done");
266
- expect(run.state.reason).toBe("failed");
267
- expect(run.state.error).toContain("Workflow failed after 3 retries");
268
- expect(run.state.error).toContain("TypeError: x is undefined");
269
- // workerLogs 捕获到 errorLogs
270
- expect(run.state.errorLogs).toEqual(workerLogs);
271
- expect(deps.store.save).toHaveBeenCalledTimes(1);
272
- expect(deps.onRunDone).toHaveBeenCalledTimes(1);
273
- });
274
-
275
- it("count <= MAX:退避 + rebuildRuntime", async () => {
276
- const run = makeRunningRun({ scriptErrorCount: 1 }); // count=2 <= MAX
277
- const deps = makeDeps();
278
-
279
- const promise = handleScriptError(run, "ReferenceError", [], deps, makeHandlers());
280
-
281
- // 第 2 次重试退避:2s
282
- await vi.advanceTimersByTimeAsync(2000);
283
- await promise;
284
-
285
- expect(run.meta.scriptErrorCount).toBe(2);
286
- expect(run.state.status).toBe("running");
287
- expect(deps.workerHost.start).toHaveBeenCalledTimes(1);
288
- });
289
-
290
- it("terminal 状态:stale 守卫前置丢弃", async () => {
291
- const run = makeRunningRun();
292
- run.state.status = "done";
293
- (run.state as { reason?: string }).reason = "completed";
294
- const deps = makeDeps();
295
-
296
- await handleScriptError(run, "late error", [], deps, makeHandlers());
297
-
298
- expect(run.meta.scriptErrorCount).toBeUndefined();
299
- expect(deps.store.save).not.toHaveBeenCalled();
300
- });
301
- });
302
-
303
- // ── postBudgetUpdate ─────────────────────────────────────────
304
-
305
- describe("postBudgetUpdate", () => {
306
- it("向 worker postMessage budget-update(usedTokens/usedCost)", () => {
307
- const postMessage = vi.fn();
308
- const run = makeRunningRun({ postMessage });
309
-
310
- postBudgetUpdate(run);
311
-
312
- expect(postMessage).toHaveBeenCalledWith({
313
- type: "budget-update",
314
- budget: { usedTokens: 50, usedCost: 0.1 },
315
- });
316
- });
317
-
318
- it("runtime 不存在时 no-op(不抛错)", () => {
319
- const run = makeRunningRun();
320
- // runtime.worker.postMessage 为 undefined 时应安全
321
- run.runtime = undefined;
322
-
323
- expect(() => postBudgetUpdate(run)).not.toThrow();
324
- });
325
- });
326
-
327
- // ── rebuildRuntime ───────────────────────────────────────────
328
-
329
- describe("rebuildRuntime", () => {
330
- it("worker 崩溃后重建:workerHost.start + replaceRuntime(保持 running)", () => {
331
- const run = makeRunningRun({ budgetTimeMs: 0 }); // 无时间预算
332
- const deps = makeDeps();
333
-
334
- rebuildRuntime(run, deps, makeHandlers());
335
-
336
- // workerHost.start 被调(构造新 worker)
337
- expect(deps.workerHost.start).toHaveBeenCalledTimes(1);
338
- // replaceRuntime 被调(新 runtime 绑定,mock 内仅替换 runtime 字段)
339
- expect(run.runtime).toBeDefined();
340
- // status 仍 running(replaceRuntime 不改 status)
341
- expect(run.state.status).toBe("running");
342
- });
343
-
344
- it("带 budgetTimeMs 时重排 scheduleTimeBudget 计时器", () => {
345
- const run = makeRunningRun({ budgetTimeMs: 5000 });
346
- const scheduleTimeBudget = vi.fn(() => undefined);
347
- const deps = makeDeps({ scheduleTimeBudget });
348
-
349
- rebuildRuntime(run, deps, makeHandlers());
350
-
351
- // D-12 regression fix (round-2 #2):replaceRuntime 后重排时间预算
352
- expect(scheduleTimeBudget).toHaveBeenCalledTimes(1);
353
- // 第 1 参 = runId(mock run 无 runId),第 2 参 = budgetTimeMs
354
- const args = scheduleTimeBudget.mock.calls[0]!;
355
- expect(args[1]).toBe(5000);
356
- });
357
-
358
- it("无 scheduleTimeBudget 注入时不重排(向后兼容,不抛错)", () => {
359
- const run = makeRunningRun({ budgetTimeMs: 5000 });
360
- const deps = makeDeps({ scheduleTimeBudget: undefined });
361
-
362
- expect(() => rebuildRuntime(run, deps, makeHandlers())).not.toThrow();
363
- expect(deps.workerHost.start).toHaveBeenCalledTimes(1);
364
- });
365
-
366
- it("rebuildRuntime 后同步清理在飞 call(status !== done 清除、done 保留)", () => {
367
- const run = makeRunningRun({ budgetTimeMs: 0 });
368
- // 注入在飞 call(running)与已完成 call(done)+ trace 节点移除 spy
369
- const removeByStepIndex = vi.fn();
370
- const calls = new Map<number, { id: number; status: string }>([
371
- [7, { id: 7, status: "running" }],
372
- [8, { id: 8, status: "done" }],
373
- ]);
374
- (run.state as { calls: unknown }).calls = calls;
375
- (run.state as { trace: unknown }).trace = { removeByStepIndex };
376
- const deps = makeDeps();
377
-
378
- rebuildRuntime(run, deps, makeHandlers());
379
-
380
- // 在飞 call(running)被移除(含 trace 节点);已完成 call(done)保留供重跑 replay
381
- expect(calls.has(7)).toBe(false);
382
- expect(calls.has(8)).toBe(true);
383
- expect(removeByStepIndex).toHaveBeenCalledTimes(1);
384
- expect(removeByStepIndex).toHaveBeenCalledWith(7);
385
- // 重建本身不受影响
386
- expect(deps.workerHost.start).toHaveBeenCalledTimes(1);
387
- });
388
- });
389
-
390
- // ── orphan call guard(S7-second 竞态回归) ─────────────────
391
- //
392
- // 复刻 gate-report §3 的竞态形态:dispatch agent-call → 立即 rebuildRuntime(同步
393
- // discard 在飞 call)→ 旧 dispatch 的 promise 以失败/成功 resolve → 迟到的旧代际
394
- // 结果不得经 postAgentResult 投给**新 worker** 的同 callId pending(否则重跑中的
395
- // agent() 被旧结果劫持 resolve 为空串 → 脚本假成功,PHASE_B 子进程被连带收割)。
396
- //
397
- // 用真实 WorkflowRun/RunRuntime/Trace/Budget(而非 makeRunningRun 的简化 mock):
398
- // rebuildRuntime → replaceRuntime → release 的 abort 旧 controller / terminate 旧
399
- // worker / discardInFlightCalls 全链路需要真实聚合根行为才成立。
400
-
401
- // ── race-F3:rebuild 时间预算折算(已耗墙钟不重置) ─────────────
402
- //
403
- // 旧实现 rebuildRuntime 重排计时器用满额 budgetTimeMs——每吃一次 worker/script
404
- // 错误重试就重置一次预算,最坏 6 次重试放大 ~6× 墙钟。修复后:
405
- // - 重排值 = max(0, 原预算 - 已耗墙钟)(从 run.meta.startedAt 推算)
406
- // - 重试前已耗尽 → 不 rebuild,直接 done,time_limited 终态
407
- //
408
- // 确定性说明:本文件 beforeEach 已 useFakeTimers()(默认 fake Date)——Date.now
409
- // 冻结,startedAt 倒拨值即精确已耗墙钟,断言可精确等值。
410
- describe("race-F3: rebuild 时间预算折算", () => {
411
- it("剩余 30% → 重排预算 = 30% 而非满额", () => {
412
- const run = makeRunningRun({ budgetTimeMs: 5000 });
413
- // 已耗 70%(3500ms)→ 剩余 1500ms;fake Date 冻结,elapsed 精确
414
- run.meta.startedAt = new Date(Date.now() - 3500).toISOString();
415
- const scheduleTimeBudget = vi.fn(() => undefined);
416
- const deps = makeDeps({ scheduleTimeBudget });
417
-
418
- rebuildRuntime(run, deps, makeHandlers());
419
-
420
- expect(scheduleTimeBudget).toHaveBeenCalledTimes(1);
421
- const args = scheduleTimeBudget.mock.calls[0]!;
422
- expect(args[1]).toBe(1500);
423
- // run 保持 running(正常 rebuild 路径不受影响)
424
- expect(run.state.status).toBe("running");
425
- });
426
-
427
- it("重试前预算已耗尽(已耗 > 预算)→ 不 rebuild,直接 done,time_limited", async () => {
428
- const run = makeRunningRun({ budgetTimeMs: 5000 });
429
- // 已耗 6000ms > 预算 5000ms(退避 advance 1000ms 后已耗 7000ms,仍耗尽)
430
- run.meta.startedAt = new Date(Date.now() - 6000).toISOString();
431
- const scheduleTimeBudget = vi.fn(() => undefined);
432
- const deps = makeDeps({ scheduleTimeBudget });
433
-
434
- const p = handleScriptError(run, "boom", [], deps, makeHandlers());
435
- await vi.advanceTimersByTimeAsync(1000); // 推进退避(1s)
436
- await p;
437
-
438
- // 不 rebuild:不启新 worker、不重排计时器
439
- expect(deps.workerHost.start).not.toHaveBeenCalled();
440
- expect(scheduleTimeBudget).not.toHaveBeenCalled();
441
- // 直接 time_limited 终态 + 持久化 + 注销通知 + onRunDone
442
- expect(run.state.status).toBe("done");
443
- expect(run.state.reason).toBe("time_limited");
444
- expect(deps.store.save).toHaveBeenCalled();
445
- expect(deps.eventBus.emit).toHaveBeenCalledWith(
446
- "pending:unregister",
447
- expect.objectContaining({ reason: "time_limited" }),
448
- );
449
- expect(deps.onRunDone).toHaveBeenCalled();
450
- });
451
-
452
- it("预算未耗尽(fresh run)→ 照常 rebuild,不误转 time_limited(防误伤回归)", async () => {
453
- const run = makeRunningRun({ budgetTimeMs: 5000 }); // startedAt ≈ now,remaining 满
454
- const scheduleTimeBudget = vi.fn(() => undefined);
455
- const deps = makeDeps({ scheduleTimeBudget });
456
-
457
- const p = handleScriptError(run, "boom", [], deps, makeHandlers());
458
- await vi.advanceTimersByTimeAsync(1000);
459
- await p;
460
-
461
- expect(deps.workerHost.start).toHaveBeenCalledTimes(1);
462
- expect(scheduleTimeBudget).toHaveBeenCalledTimes(1);
463
- expect(run.state.status).toBe("running");
464
- });
465
- });
466
-
467
- /** 手动控制的 deferred——精确编排「dispatch 挂起 → rebuild → 旧 promise settle」交错。 */
468
- interface Deferred<T> {
469
- promise: Promise<T>;
470
- resolve: (value: T) => void;
471
- reject: (err: unknown) => void;
472
- }
473
-
474
- function createDeferred<T>(): Deferred<T> {
475
- let resolve!: (value: T) => void;
476
- let reject!: (err: unknown) => void;
477
- const promise = new Promise<T>((res, rej) => {
478
- resolve = res;
479
- reject = rej;
480
- });
481
- return { promise, resolve, reject };
482
- }
483
-
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
488
- await Promise.resolve();
489
- }
490
- }
491
-
492
- /** 构造真实 WorkflowRun(真实状态机/replaceRuntime/Trace/Budget)+ 初始 RunRuntime。 */
493
- function makeRealRun(runId: string, opts: { budgetTimeMs?: number } = {}): WorkflowRun {
494
- const run = new WorkflowRun(
495
- runId,
496
- {
497
- scriptName: "test-wf",
498
- scriptSource: "agent('hi')",
499
- args: {},
500
- scriptPath: "/tmp/test-wf.js",
501
- budgetTimeMs: opts.budgetTimeMs,
502
- },
503
- {
504
- status: "running",
505
- budget: new Budget(),
506
- calls: new Map(),
507
- trace: new Trace(),
508
- errorLogs: [],
509
- },
510
- { startedAt: new Date().toISOString() },
511
- );
512
- // 初始 worker mock 必须带 terminate——真实 replaceRuntime 会 release 旧 runtime
513
- const initialWorker = {
514
- postMessage: vi.fn(),
515
- terminate: vi.fn(async () => {}),
516
- } as unknown as WorkerHandle;
517
- run.assignRuntime(new RunRuntime(initialWorker, new AbortController()));
518
- return run;
519
- }
520
-
521
- /** agent-call 消息(opts 无 skill/schema——resolveAgentOpts 直通,无 IO)。 */
522
- function makeAgentCallMsg(callId: number): unknown {
523
- return {
524
- type: "agent-call",
525
- callId,
526
- opts: { prompt: "test task", agent: "worker", description: "test-slug" },
527
- };
528
- }
529
-
530
- /** 从 postMessage spy 的调用记录中找 type:"agent-result" 且 callId 匹配的报文。 */
531
- function findAgentResultPost(
532
- postMessage: ReturnType<typeof vi.fn>,
533
- callId: number,
534
- ): { type: string; callId: number; result: AgentResult; cached: boolean } | undefined {
535
- for (const call of postMessage.mock.calls) {
536
- const msg = call[0] as { type?: string; callId?: number };
537
- if (msg?.type === "agent-result" && msg.callId === callId) {
538
- return msg as never;
539
- }
540
- }
541
- return undefined;
542
- }
543
-
544
- describe("orphan call guard(rebuild 后迟到 completion 不投递新 worker)", () => {
545
- it("失败路径:discard + 重跑替换后,旧 dispatch 的失败 completion 不投新 worker、不复活 Map 条目", async () => {
546
- const run = makeRealRun("wf-orphan-1");
547
- const deps = makeDeps();
548
- // rebuild 的新 worker——postMessage 记录用于断言「孤儿结果未投递」
549
- const newWorkerPost = vi.fn();
550
- deps.workerHost.start.mockImplementation(
551
- () =>
552
- ({ postMessage: newWorkerPost, terminate: vi.fn(async () => {}) }) as unknown as WorkerHandle,
553
- );
554
- const handlers = makeHandlers();
555
-
556
- // 旧 runtime 代际:dispatch callId=1,runner 挂起(模拟在飞子进程)
557
- const deferredA = createDeferred<AgentResult>();
558
- deps.runner.run.mockImplementation(() => deferredA.promise);
559
- await handleWorkerMessage(run, makeAgentCallMsg(1), deps, handlers);
560
- await flushMicrotasks();
561
- expect(run.state.calls.get(1)?.status).toBe("running");
562
-
563
- // worker 崩溃 → rebuildRuntime:replaceRuntime(abort 旧 controller)+ 同步 discard 在飞 call
564
- rebuildRuntime(run, deps, handlers);
565
- expect(run.state.calls.has(1)).toBe(false);
566
- expect(run.state.trace.find(1)).toBeUndefined();
567
-
568
- // 新 worker 重跑脚本:同 callId=1 再 dispatch(重跑实例 B 挂起在飞)
569
- const deferredB = createDeferred<AgentResult>();
570
- deps.runner.run.mockImplementation(() => deferredB.promise);
571
- await handleWorkerMessage(run, makeAgentCallMsg(1), deps, handlers);
572
- await flushMicrotasks();
573
- const rerunCall = run.state.calls.get(1);
574
- expect(rerunCall).toBeDefined();
575
-
576
- // 旧 dispatch 的 promise 以失败 resolve(abort 收割子进程后的迟到 finalize,
577
- // error 含 "aborted" 命中 stale-context 快速路径——真实形态)
578
- deferredA.resolve({ content: "", error: "Subprocess aborted by runtime shutdown" });
579
- await flushMicrotasks();
580
-
581
- // 孤儿失败结果不得投给新 worker(否则劫持重跑 pending 为空串假成功)
582
- expect(findAgentResultPost(newWorkerPost, 1)).toBeUndefined();
583
- // calls Map 无孤儿复活条目:callId=1 仍是重跑实例 B
584
- expect(run.state.calls.get(1)).toBe(rerunCall);
585
- });
586
-
587
- it("成功路径:discard 后旧 dispatch 的成功 completion 不投新 worker、不复活 Map/trace 条目", async () => {
588
- const run = makeRealRun("wf-orphan-2");
589
- const deps = makeDeps();
590
- const newWorkerPost = vi.fn();
591
- deps.workerHost.start.mockImplementation(
592
- () =>
593
- ({ postMessage: newWorkerPost, terminate: vi.fn(async () => {}) }) as unknown as WorkerHandle,
594
- );
595
-
596
- const deferred = createDeferred<AgentResult>();
597
- deps.runner.run.mockImplementation(() => deferred.promise);
598
- await handleWorkerMessage(run, makeAgentCallMsg(2), deps, makeHandlers());
599
- await flushMicrotasks();
600
-
601
- rebuildRuntime(run, deps, makeHandlers());
602
- expect(run.state.calls.has(2)).toBe(false);
603
-
604
- // 旧 promise 以成功 resolve(runner 已完成、结果晚于 rebuild 到达)
605
- deferred.resolve({ content: "late success", durationMs: 5, error: undefined, toolCalls: [] });
606
- await flushMicrotasks();
607
-
608
- expect(findAgentResultPost(newWorkerPost, 2)).toBeUndefined();
609
- expect(run.state.calls.has(2)).toBe(false);
610
- expect(run.state.trace.find(2)).toBeUndefined();
611
- });
612
-
613
- it("catch 路径:discard 后旧 dispatch 的异常 reject 不投新 worker、不复活 Map 条目", async () => {
614
- // catch 块的 logger.error 会打印——静默防噪音
615
- const spy = vi.spyOn(console, "error").mockImplementation(() => {});
616
- try {
617
- const run = makeRealRun("wf-orphan-3");
618
- const deps = makeDeps();
619
- const newWorkerPost = vi.fn();
620
- deps.workerHost.start.mockImplementation(
621
- () =>
622
- ({ postMessage: newWorkerPost, terminate: vi.fn(async () => {}) }) as unknown as WorkerHandle,
623
- );
624
-
625
- const deferred = createDeferred<AgentResult>();
626
- deps.runner.run.mockImplementation(() => deferred.promise);
627
- await handleWorkerMessage(run, makeAgentCallMsg(3), deps, makeHandlers());
628
- await flushMicrotasks();
629
-
630
- rebuildRuntime(run, deps, makeHandlers());
631
- expect(run.state.calls.has(3)).toBe(false);
632
-
633
- deferred.reject(new Error("runner exploded"));
634
- await flushMicrotasks();
635
-
636
- expect(findAgentResultPost(newWorkerPost, 3)).toBeUndefined();
637
- expect(run.state.calls.has(3)).toBe(false);
638
- } finally {
639
- spy.mockRestore();
640
- }
641
- });
642
-
643
- it("非孤儿正常路径:成功 completion 照常投递 agent-result(守卫不误伤)", async () => {
644
- const run = makeRealRun("wf-orphan-4");
645
- const deps = makeDeps();
646
- const postMessage = run.runtime!.worker.postMessage as ReturnType<typeof vi.fn>;
647
- deps.runner.run.mockImplementation(
648
- async () => ({ content: "real result", durationMs: 1, error: undefined, toolCalls: [] }) as AgentResult,
649
- );
650
-
651
- await handleWorkerMessage(run, makeAgentCallMsg(4), deps, makeHandlers());
652
- await flushMicrotasks(20);
653
-
654
- const posted = findAgentResultPost(postMessage, 4);
655
- expect(posted).toBeDefined();
656
- expect(posted?.result.content).toBe("real result");
657
- expect(run.state.calls.get(4)?.status).toBe("done");
658
- expect(run.state.trace.find(4)?.status).toBe("completed");
659
- });
660
-
661
- it("U6/S3 场景重放:discard + 重跑替换后,旧 finalize 不污染重跑新 trace 节点(旧 call 实例仍 markDone)", async () => {
662
- const run = makeRealRun("wf-orphan-s3");
663
- const deps = makeDeps();
664
- const newWorkerPost = vi.fn();
665
- deps.workerHost.start.mockImplementation(
666
- () => ({ postMessage: newWorkerPost, terminate: vi.fn(async () => {}) }) as unknown as WorkerHandle,
667
- );
668
- const handlers = makeHandlers();
669
-
670
- // 旧代际 dispatch callId=9:runner 挂起(模拟在飞子进程)
671
- const deferredA = createDeferred<AgentResult>();
672
- deps.runner.run.mockImplementation(() => deferredA.promise);
673
- await handleWorkerMessage(run, makeAgentCallMsg(9), deps, handlers);
674
- await flushMicrotasks();
675
- const oldCall = run.state.calls.get(9);
676
- expect(oldCall?.status).toBe("running");
677
-
678
- // rebuild:discard 移除旧 call 条目 + trace 节点(replaceRuntime 同步 abort 旧 signal)
679
- rebuildRuntime(run, deps, handlers);
680
- expect(run.state.calls.has(9)).toBe(false);
681
- expect(run.state.trace.find(9)).toBeUndefined();
682
-
683
- // 重跑 dispatch 同 callId=9:新实例 + 新 trace 节点 running,挂起在飞
684
- const deferredB = createDeferred<AgentResult>();
685
- deps.runner.run.mockImplementation(() => deferredB.promise);
686
- await handleWorkerMessage(run, makeAgentCallMsg(9), deps, handlers);
687
- await flushMicrotasks();
688
- const rerunCall = run.state.calls.get(9);
689
- expect(rerunCall).toBeDefined();
690
- expect(rerunCall).not.toBe(oldCall);
691
- expect(run.state.trace.find(9)?.status).toBe("running");
692
-
693
- // 旧 runner promise 以非 stale 失败 resolve——rebuild 已 abort 旧 signal,旧
694
- // executeAgentCall 醒来走 signal.aborted finalize 调用点(错误文案不含 stale
695
- // 模式词,确保不进 stale 分支)。红性锚点:无 OB2 守卫时此处 trace.update(9)
696
- // 命中重跑新节点 → 短暂污染为 failed。
697
- deferredA.resolve({ content: "", durationMs: 3, error: "old generation failure", toolCalls: [] });
698
- await flushMicrotasks();
699
-
700
- // 新 trace 节点未被旧 finalize 污染:仍 running、无 result、无 completedAt
701
- const newNode = run.state.trace.find(9);
702
- expect(newNode?.status).toBe("running");
703
- expect(newNode?.result).toBeUndefined();
704
- expect(newNode?.completedAt).toBeUndefined();
705
- // 旧实例 markDone 保留(dispatch 层 catch 路径依赖 call.status 语义)
706
- expect(oldCall?.status).toBe("done");
707
- expect(oldCall?.result?.error).toBe("old generation failure");
708
- // 重跑实例仍在飞,未被旧 completion 干扰;孤儿结果不投新 worker
709
- expect(rerunCall?.status).toBe("running");
710
- expect(findAgentResultPost(newWorkerPost, 9)).toBeUndefined();
711
-
712
- // 收尾:resolve 重跑 deferred,让挂起的 promise 链走完(非孤儿 → 正常完成路径)
713
- deferredB.resolve({ content: "rerun ok", durationMs: 1, error: undefined, toolCalls: [] });
714
- await flushMicrotasks();
715
- expect(run.state.trace.find(9)?.status).toBe("completed");
716
- });
717
- });
718
-
719
- // ── rebuildRuntime 可观察性(OB3 日志点 L1-L4) ─────────────
720
-
721
- /** 从 deps.log 的 mock 调用记录提取 (message, payload)——message 非字符串时置空串(断言自然失败)。 */
722
- function toLogEntries(calls: unknown[][]): Array<{ message: string; payload: unknown }> {
723
- return calls.map((c) => ({
724
- message: typeof c[2] === "string" ? c[2] : "",
725
- payload: c[3],
726
- }));
727
- }
728
-
729
- describe("rebuildRuntime 可观察性(OB3 日志点)", () => {
730
- it("U7: 无 budgetTimeMs 时按序打 L1 start → L4 complete(payload 含 runId),无 L2 重排日志", () => {
731
- const run = makeRealRun("wf-rebuild-log-1");
732
- const deps = makeDeps();
733
-
734
- rebuildRuntime(run, deps, makeHandlers());
735
-
736
- const entries = toLogEntries(deps.log.mock.calls);
737
- const messages = entries.map((e) => e.message);
738
- expect(messages).toContain("runtime rebuild start");
739
- expect(messages).toContain("runtime rebuild complete");
740
- // L4 在 L1 之后(顺序锚)
741
- expect(messages.indexOf("runtime rebuild complete")).toBeGreaterThan(
742
- messages.indexOf("runtime rebuild start"),
743
- );
744
- // 无 budgetTimeMs → 该分支本就跳过重排,L2 不打
745
- expect(messages).not.toContain("time budget rescheduled");
746
- // L1/L4 payload 含 runId
747
- const l1 = entries.find((e) => e.message === "runtime rebuild start");
748
- expect(l1?.payload).toMatchObject({ runId: "wf-rebuild-log-1" });
749
- const l4 = entries.find((e) => e.message === "runtime rebuild complete");
750
- expect(l4?.payload).toEqual({ runId: "wf-rebuild-log-1" });
751
- });
752
-
753
- it("U7: 带 budgetTimeMs + scheduleTimeBudget 注入时含 L2(在 L1 之后、L3 之前,payload 含 budgetTimeMs)", () => {
754
- const run = makeRealRun("wf-rebuild-log-2", { budgetTimeMs: 5000 });
755
- const scheduleTimeBudget = vi.fn(() => undefined);
756
- const deps = makeDeps({ scheduleTimeBudget });
757
-
758
- rebuildRuntime(run, deps, makeHandlers());
759
-
760
- const entries = toLogEntries(deps.log.mock.calls);
761
- const messages = entries.map((e) => e.message);
762
- expect(messages).toContain("time budget rescheduled");
763
- expect(messages.indexOf("time budget rescheduled")).toBeGreaterThan(
764
- messages.indexOf("runtime rebuild start"),
765
- );
766
- expect(messages.indexOf("time budget rescheduled")).toBeLessThan(
767
- messages.indexOf("in-flight calls discarded"),
768
- );
769
- const l2 = entries.find((e) => e.message === "time budget rescheduled");
770
- expect(l2?.payload).toEqual({ runId: "wf-rebuild-log-2", budgetTimeMs: 5000 });
771
- });
772
-
773
- it("U8: 含 2 个在飞 call 的 run 经 rebuild → L3 callIds 升序、count === 2,与实际被弃 callId 一致", async () => {
774
- const run = makeRealRun("wf-rebuild-log-3");
775
- const deps = makeDeps();
776
- const handlers = makeHandlers();
777
-
778
- // 两个在飞 call(dispatch 插入序 5 → 3,验证 L3 callIds 是升序而非插入序)
779
- const deferreds = [createDeferred<AgentResult>(), createDeferred<AgentResult>()];
780
- let next = 0;
781
- deps.runner.run.mockImplementation(() => {
782
- const d = deferreds[next];
783
- next += 1;
784
- return d!.promise;
785
- });
786
- await handleWorkerMessage(run, makeAgentCallMsg(5), deps, handlers);
787
- await handleWorkerMessage(run, makeAgentCallMsg(3), deps, handlers);
788
- await flushMicrotasks();
789
- expect(run.state.calls.get(5)?.status).toBe("running");
790
- expect(run.state.calls.get(3)?.status).toBe("running");
791
-
792
- rebuildRuntime(run, deps, makeHandlers());
793
-
794
- // L3 payload:callIds 升序 [3, 5]、count === 2(即 discardInFlightCalls 返回值)
795
- const entries = toLogEntries(deps.log.mock.calls);
796
- const l3 = entries.find((e) => e.message === "in-flight calls discarded");
797
- expect(l3?.payload).toEqual({ runId: "wf-rebuild-log-3", callIds: [3, 5], count: 2 });
798
- // 返回值(经 L3 暴露)与实际被弃 callId 一致:Map/trace 条目均已移除
799
- expect(run.state.calls.has(5)).toBe(false);
800
- expect(run.state.calls.has(3)).toBe(false);
801
- expect(run.state.trace.find(5)).toBeUndefined();
802
- expect(run.state.trace.find(3)).toBeUndefined();
803
-
804
- // 收尾:resolve 两个挂起的 deferred(孤儿守卫 drop,无投递无污染)
805
- deferreds[0]!.resolve({ content: "", durationMs: 1, error: undefined, toolCalls: [] });
806
- deferreds[1]!.resolve({ content: "", durationMs: 1, error: undefined, toolCalls: [] });
807
- await flushMicrotasks();
808
- });
809
- });