@zhushanwen/pi-subagent-workflow 8.5.0 → 8.7.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 (336) hide show
  1. package/package.json +13 -16
  2. package/relay/relay.mjs +1 -1
  3. package/src/host/__tests__/pi-host.test.ts +214 -0
  4. package/src/host/pi-host.ts +109 -0
  5. package/src/index.ts +142 -32
  6. package/src/injectors/__tests__/engine-awareness.test.ts +292 -0
  7. package/src/injectors/__tests__/engine-section-stability.test.ts +341 -0
  8. package/src/injectors/__tests__/subagent-list-injector.test.ts +5 -5
  9. package/src/injectors/__tests__/workflow-list-injector.test.ts +5 -5
  10. package/src/injectors/engine-awareness.ts +123 -0
  11. package/src/injectors/model-list-injector.ts +1 -1
  12. package/src/injectors/subagent-list-injector.ts +8 -8
  13. package/src/injectors/workflow-list-injector.ts +8 -8
  14. package/src/interface/__tests__/detectors.test.ts +3 -3
  15. package/src/interface/__tests__/subagent-tool-path-guard.test.ts +157 -0
  16. package/src/interface/__tests__/subagent-tool-prompt.test.ts +12 -0
  17. package/src/interface/__tests__/tool-workflow-script-generate.test.ts +2 -2
  18. package/src/interface/__tests__/tool-workflow-throw-paths.test.ts +1 -1
  19. package/src/interface/bg-notify-render.ts +34 -13
  20. package/src/interface/commands.ts +3 -3
  21. package/src/interface/format.ts +2 -2
  22. package/src/interface/helpers.ts +3 -3
  23. package/src/interface/list-component.ts +4 -4
  24. package/src/interface/list-shared.ts +2 -2
  25. package/src/interface/list-view.ts +2 -2
  26. package/src/interface/subagent-actions.ts +288 -19
  27. package/src/interface/subagent-tool-schema.ts +163 -0
  28. package/src/interface/subagent-tool.ts +65 -129
  29. package/src/interface/subagents.ts +5 -5
  30. package/src/interface/tool-render.ts +34 -2
  31. package/src/interface/tool-workflow-script.ts +4 -4
  32. package/src/interface/tool-workflow.ts +6 -6
  33. package/src/interface/views/WorkflowsView.ts +5 -5
  34. package/src/interface/views/__tests__/WorkflowsView-signature.test.ts +3 -3
  35. package/src/interface/views/__tests__/detail-content-session-file.test.ts +2 -2
  36. package/src/interface/views/detail-content.ts +4 -4
  37. package/src/interface/views/format.ts +4 -4
  38. package/src/{orchestration/jsonl-run-store.ts → jsonl-run-store.ts} +132 -18
  39. package/src/execution/__tests__/__fixtures__/notifier-golden-snapshots.json +0 -53
  40. package/src/execution/__tests__/__fixtures__/truncline.snapshot.json +0 -1
  41. package/src/execution/__tests__/agent-registry.test.ts +0 -363
  42. package/src/execution/__tests__/agent-result-mapper.test.ts +0 -150
  43. package/src/execution/__tests__/alive-store.test.ts +0 -147
  44. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +0 -490
  45. package/src/execution/__tests__/before-agent-start-injection.test.ts +0 -132
  46. package/src/execution/__tests__/bg-notify-render.test.ts +0 -256
  47. package/src/execution/__tests__/channel-registry-handshake.test.ts +0 -243
  48. package/src/execution/__tests__/chat-engine-routing.test.ts +0 -597
  49. package/src/execution/__tests__/chatmode-first-round-closure-service.test.ts +0 -365
  50. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +0 -190
  51. package/src/execution/__tests__/chatmode-round-notify-real-chain.test.ts +0 -215
  52. package/src/execution/__tests__/concurrency-pool.test.ts +0 -250
  53. package/src/execution/__tests__/config.test.ts +0 -110
  54. package/src/execution/__tests__/conversation-wiring.test.ts +0 -198
  55. package/src/execution/__tests__/crash-recovery.test.ts +0 -320
  56. package/src/execution/__tests__/delivery-methods.test.ts +0 -385
  57. package/src/execution/__tests__/dialog-queue.test.ts +0 -299
  58. package/src/execution/__tests__/epipe-fallback.test.ts +0 -241
  59. package/src/execution/__tests__/execute-and-await-worktree.test.ts +0 -243
  60. package/src/execution/__tests__/execute-nesting.test.ts +0 -323
  61. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -189
  62. package/src/execution/__tests__/execution-record.test.ts +0 -1284
  63. package/src/execution/__tests__/finalize-record.test.ts +0 -368
  64. package/src/execution/__tests__/finalized-marker.test.ts +0 -82
  65. package/src/execution/__tests__/format-schema-instruction.test.ts +0 -138
  66. package/src/execution/__tests__/format.test.ts +0 -458
  67. package/src/execution/__tests__/gc-timer.test.ts +0 -184
  68. package/src/execution/__tests__/get-record-for-action-restart.test.ts +0 -254
  69. package/src/execution/__tests__/gui-mode-dispatch.test.ts +0 -59
  70. package/src/execution/__tests__/helpers/mock-extension-api.ts +0 -30
  71. package/src/execution/__tests__/helpers/spawn-mock.ts +0 -238
  72. package/src/execution/__tests__/host-mode.test.ts +0 -87
  73. package/src/execution/__tests__/index-session-start-identity.test.ts +0 -371
  74. package/src/execution/__tests__/index-session-start.test.ts +0 -593
  75. package/src/execution/__tests__/lifecycle-manager-lock.test.ts +0 -211
  76. package/src/execution/__tests__/lifecycle-manager.test.ts +0 -337
  77. package/src/execution/__tests__/lifecycle-predicates.test.ts +0 -116
  78. package/src/execution/__tests__/list-component.test.ts +0 -401
  79. package/src/execution/__tests__/list-fields.test.ts +0 -109
  80. package/src/execution/__tests__/manifest-parentid.test.ts +0 -117
  81. package/src/execution/__tests__/model-resolver.test.ts +0 -413
  82. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +0 -287
  83. package/src/execution/__tests__/nested-visibility.test.ts +0 -325
  84. package/src/execution/__tests__/notifier-flush.test.ts +0 -574
  85. package/src/execution/__tests__/notifier-golden-snapshot.test.ts +0 -261
  86. package/src/execution/__tests__/one-shot-upgrade.test.ts +0 -205
  87. package/src/execution/__tests__/output-collector.test.ts +0 -61
  88. package/src/execution/__tests__/parent-child-matrix.test.ts +0 -336
  89. package/src/execution/__tests__/path-encoding.test.ts +0 -104
  90. package/src/execution/__tests__/pi-invocation.test.ts +0 -134
  91. package/src/execution/__tests__/record-store.test.ts +0 -1057
  92. package/src/execution/__tests__/records-cwd-isolation.test.ts +0 -91
  93. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +0 -339
  94. package/src/execution/__tests__/recursive-visibility-env.test.ts +0 -273
  95. package/src/execution/__tests__/relay-agent.test.ts +0 -448
  96. package/src/execution/__tests__/relay-env.test.ts +0 -42
  97. package/src/execution/__tests__/resource-policy.test.ts +0 -109
  98. package/src/execution/__tests__/rpc-mode.test.ts +0 -89
  99. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +0 -267
  100. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +0 -253
  101. package/src/execution/__tests__/run-spawn-edges.test.ts +0 -644
  102. package/src/execution/__tests__/run-spawn-integration.test.ts +0 -933
  103. package/src/execution/__tests__/run-spawn-resume.test.ts +0 -322
  104. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +0 -196
  105. package/src/execution/__tests__/sdk-contract.test.ts +0 -273
  106. package/src/execution/__tests__/session-context-resolver.test.ts +0 -167
  107. package/src/execution/__tests__/session-file-gc.test.ts +0 -293
  108. package/src/execution/__tests__/session-pending.test.ts +0 -197
  109. package/src/execution/__tests__/session-reconstructor.test.ts +0 -379
  110. package/src/execution/__tests__/session-runner-epipe.test.ts +0 -178
  111. package/src/execution/__tests__/session-runner-schema-env.test.ts +0 -311
  112. package/src/execution/__tests__/session-start-reaper.test.ts +0 -233
  113. package/src/execution/__tests__/spawn-args.test.ts +0 -434
  114. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +0 -189
  115. package/src/execution/__tests__/spawn-event-adapter.test.ts +0 -167
  116. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +0 -207
  117. package/src/execution/__tests__/spawned-children.test.ts +0 -92
  118. package/src/execution/__tests__/startup-config-declaration.test.ts +0 -35
  119. package/src/execution/__tests__/status-refactor.test.ts +0 -345
  120. package/src/execution/__tests__/stdin-writer.test.ts +0 -463
  121. package/src/execution/__tests__/stream-sink-retirement.test.ts +0 -261
  122. package/src/execution/__tests__/subagent-service-abort.test.ts +0 -60
  123. package/src/execution/__tests__/subagent-service-message-close.test.ts +0 -629
  124. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +0 -180
  125. package/src/execution/__tests__/subagent-service.test.ts +0 -788
  126. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +0 -310
  127. package/src/execution/__tests__/subprocess-agent-runner.test.ts +0 -506
  128. package/src/execution/__tests__/temp-prompt.test.ts +0 -53
  129. package/src/execution/__tests__/timeout-integration.test.ts +0 -397
  130. package/src/execution/__tests__/tombstone-store.test.ts +0 -73
  131. package/src/execution/__tests__/tool-action.test.ts +0 -385
  132. package/src/execution/__tests__/truncline-snapshot.test.ts +0 -81
  133. package/src/execution/__tests__/turn-limiter-semantics.test.ts +0 -194
  134. package/src/execution/__tests__/turn-limiter.test.ts +0 -65
  135. package/src/execution/__tests__/ui-channels.test.ts +0 -187
  136. package/src/execution/__tests__/ui-interaction-model.test.ts +0 -67
  137. package/src/execution/__tests__/ui-request-handler-factory.test.ts +0 -370
  138. package/src/execution/__tests__/ui-request-handler.test.ts +0 -204
  139. package/src/execution/__tests__/ui-request-observability.test.ts +0 -114
  140. package/src/execution/__tests__/ui-request-queue.test.ts +0 -133
  141. package/src/execution/__tests__/worktree-manager.test.ts +0 -633
  142. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +0 -229
  143. package/src/execution/__tests__/worktree-reconcile.integration.test.ts +0 -181
  144. package/src/execution/__tests__/worktree-registry.test.ts +0 -199
  145. package/src/execution/agent-registry.ts +0 -193
  146. package/src/execution/agent-result-mapper.ts +0 -90
  147. package/src/execution/alive-store.ts +0 -92
  148. package/src/execution/argv-mirror.ts +0 -109
  149. package/src/execution/best-effort.ts +0 -38
  150. package/src/execution/channel-registry-access.ts +0 -144
  151. package/src/execution/concurrency-pool.ts +0 -116
  152. package/src/execution/config.ts +0 -96
  153. package/src/execution/dialog-queue.ts +0 -330
  154. package/src/execution/engine/__tests__/common/data-dir.test.ts +0 -53
  155. package/src/execution/engine/__tests__/common/errors.test.ts +0 -132
  156. package/src/execution/engine/__tests__/common/event-journal.test.ts +0 -177
  157. package/src/execution/engine/__tests__/common/kill-chain.test.ts +0 -192
  158. package/src/execution/engine/__tests__/common/nesting-guard.test.ts +0 -81
  159. package/src/execution/engine/__tests__/common/persona-router.test.ts +0 -123
  160. package/src/execution/engine/__tests__/common/pool-manager.test.ts +0 -154
  161. package/src/execution/engine/__tests__/common/schema-emulation.test.ts +0 -128
  162. package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +0 -28
  163. package/src/execution/engine/__tests__/conformance/agent-event-invariants.ts +0 -141
  164. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +0 -109
  165. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +0 -101
  166. package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +0 -77
  167. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +0 -104
  168. package/src/execution/engine/__tests__/conformance/contract.relay.test.ts +0 -342
  169. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +0 -201
  170. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +0 -76
  171. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +0 -79
  172. package/src/execution/engine/__tests__/engine-discovery.test.ts +0 -87
  173. package/src/execution/engine/__tests__/engines-declaration.test.ts +0 -36
  174. package/src/execution/engine/__tests__/model-prompt.test.ts +0 -85
  175. package/src/execution/engine/__tests__/paths.test.ts +0 -39
  176. package/src/execution/engine/__tests__/registry.test.ts +0 -120
  177. package/src/execution/engine/__tests__/routing.test.ts +0 -231
  178. package/src/execution/engine/common/data-dir.ts +0 -62
  179. package/src/execution/engine/common/errors.ts +0 -183
  180. package/src/execution/engine/common/event-journal.ts +0 -254
  181. package/src/execution/engine/common/journal-replay.ts +0 -62
  182. package/src/execution/engine/common/kill-chain.ts +0 -221
  183. package/src/execution/engine/common/nesting-guard.ts +0 -50
  184. package/src/execution/engine/common/persona-router.ts +0 -108
  185. package/src/execution/engine/common/pool-manager.ts +0 -226
  186. package/src/execution/engine/common/schema-emulation.ts +0 -189
  187. package/src/execution/engine/common/session-view-projection.ts +0 -51
  188. package/src/execution/engine/engine-discovery.ts +0 -65
  189. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +0 -469
  190. package/src/execution/engine/engines/pi/__tests__/reader.test.ts +0 -155
  191. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  192. package/src/execution/engine/engines/pi/pi-engine.ts +0 -415
  193. package/src/execution/engine/engines/pi/reader.ts +0 -48
  194. package/src/execution/engine/engines/pi/registration.ts +0 -35
  195. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  196. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  197. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  198. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +0 -246
  199. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +0 -228
  200. package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +0 -210
  201. package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +0 -64
  202. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +0 -127
  203. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -567
  204. package/src/execution/engine/engines/zcode/constants.ts +0 -43
  205. package/src/execution/engine/engines/zcode/golden-sample.ts +0 -39
  206. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  207. package/src/execution/engine/engines/zcode/parser.ts +0 -436
  208. package/src/execution/engine/engines/zcode/preparer.ts +0 -363
  209. package/src/execution/engine/engines/zcode/reader.ts +0 -381
  210. package/src/execution/engine/engines/zcode/registration.ts +0 -37
  211. package/src/execution/engine/engines/zcode/zcode-engine.ts +0 -648
  212. package/src/execution/engine/host-task-spec.ts +0 -47
  213. package/src/execution/engine/model-prompt.ts +0 -59
  214. package/src/execution/engine/paths.ts +0 -42
  215. package/src/execution/engine/port.ts +0 -153
  216. package/src/execution/engine/registry.ts +0 -123
  217. package/src/execution/engine/routing.ts +0 -218
  218. package/src/execution/engine/types.ts +0 -304
  219. package/src/execution/execute-options-mapper.ts +0 -111
  220. package/src/execution/execution-record.ts +0 -906
  221. package/src/execution/finalize-record.ts +0 -251
  222. package/src/execution/finalized-marker.ts +0 -51
  223. package/src/execution/get-state-handshake.ts +0 -104
  224. package/src/execution/host-mode.ts +0 -56
  225. package/src/execution/idle-gc.ts +0 -47
  226. package/src/execution/lifecycle-manager.ts +0 -491
  227. package/src/execution/lifecycle-predicates.ts +0 -65
  228. package/src/execution/manifest-store.ts +0 -256
  229. package/src/execution/model-config-service.ts +0 -204
  230. package/src/execution/model-resolver.ts +0 -276
  231. package/src/execution/notifier.ts +0 -260
  232. package/src/execution/output-collector.ts +0 -88
  233. package/src/execution/path-encoding.ts +0 -60
  234. package/src/execution/pi-invocation.ts +0 -120
  235. package/src/execution/record-entry.ts +0 -132
  236. package/src/execution/record-store.ts +0 -1234
  237. package/src/execution/relay-env.ts +0 -37
  238. package/src/execution/session-context-resolver.ts +0 -64
  239. package/src/execution/session-file-gc.ts +0 -120
  240. package/src/execution/session-pending.ts +0 -164
  241. package/src/execution/session-reconstructor.ts +0 -678
  242. package/src/execution/session-runner.ts +0 -1548
  243. package/src/execution/sessions-index.ts +0 -304
  244. package/src/execution/spawn-event-adapter.ts +0 -363
  245. package/src/execution/stdin-writer.ts +0 -198
  246. package/src/execution/stream-sink.ts +0 -126
  247. package/src/execution/subagent-service.ts +0 -2119
  248. package/src/execution/subprocess-agent-runner.ts +0 -303
  249. package/src/execution/temp-prompt.ts +0 -62
  250. package/src/execution/tombstone-store.ts +0 -72
  251. package/src/execution/turn-limiter.ts +0 -102
  252. package/src/execution/types.ts +0 -910
  253. package/src/execution/ui-channels.ts +0 -216
  254. package/src/execution/ui-interaction-model.ts +0 -48
  255. package/src/execution/ui-request-handler-factory.ts +0 -212
  256. package/src/execution/ui-request-observability.ts +0 -81
  257. package/src/execution/ui-request-queue.ts +0 -174
  258. package/src/execution/worktree-manager.ts +0 -688
  259. package/src/execution/worktree-registry.ts +0 -222
  260. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +0 -336
  261. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +0 -208
  262. package/src/orchestration/__tests__/agent-call-stream.test.ts +0 -157
  263. package/src/orchestration/__tests__/args-validator.test.ts +0 -143
  264. package/src/orchestration/__tests__/concurrency-gate.test.ts +0 -125
  265. package/src/orchestration/__tests__/config-loader.test.ts +0 -503
  266. package/src/orchestration/__tests__/error-recovery-handlers.test.ts +0 -724
  267. package/src/orchestration/__tests__/error-recovery-postmessage-defense.test.ts +0 -405
  268. package/src/orchestration/__tests__/error-recovery-serialize-failed-result.test.ts +0 -56
  269. package/src/orchestration/__tests__/error-recovery-workflow-call.test.ts +0 -166
  270. package/src/orchestration/__tests__/execute-agent-call.test.ts +0 -314
  271. package/src/orchestration/__tests__/jsonl-run-store-loadall-sources.test.ts +0 -171
  272. package/src/orchestration/__tests__/jsonl-run-store-session-file.test.ts +0 -1030
  273. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +0 -277
  274. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +0 -96
  275. package/src/orchestration/__tests__/lifecycle.test.ts +0 -625
  276. package/src/orchestration/__tests__/review-fix-loop-e2e.test.ts +0 -1971
  277. package/src/orchestration/__tests__/script-lint.test.ts +0 -831
  278. package/src/orchestration/__tests__/skill-discovery.test.ts +0 -157
  279. package/src/orchestration/__tests__/test-mocks.ts +0 -197
  280. package/src/orchestration/__tests__/worker-host.test.ts +0 -120
  281. package/src/orchestration/__tests__/worker-returnmeta-passthrough.test.ts +0 -164
  282. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +0 -520
  283. package/src/orchestration/__tests__/worker-script-builder.test.ts +0 -309
  284. package/src/orchestration/__tests__/worker-script-template-snapshot.test.ts +0 -110
  285. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +0 -317
  286. package/src/orchestration/__tests__/workflow-script-lint-memo.test.ts +0 -110
  287. package/src/orchestration/__tests__/workflows-e2e.test.ts +0 -537
  288. package/src/orchestration/agent-opts-resolver.ts +0 -93
  289. package/src/orchestration/args-validator.ts +0 -127
  290. package/src/orchestration/concurrency-gate.ts +0 -69
  291. package/src/orchestration/config-loader.ts +0 -315
  292. package/src/orchestration/error-recovery.ts +0 -862
  293. package/src/orchestration/execute-agent-call.ts +0 -217
  294. package/src/orchestration/launcher.ts +0 -410
  295. package/src/orchestration/lifecycle.ts +0 -396
  296. package/src/orchestration/models/__tests__/budget.test.ts +0 -367
  297. package/src/orchestration/models/__tests__/trace.test.ts +0 -408
  298. package/src/orchestration/models/agent-call.ts +0 -83
  299. package/src/orchestration/models/budget.ts +0 -148
  300. package/src/orchestration/models/ports.ts +0 -164
  301. package/src/orchestration/models/run-runtime.ts +0 -89
  302. package/src/orchestration/models/run-spec.ts +0 -83
  303. package/src/orchestration/models/run-state.ts +0 -44
  304. package/src/orchestration/models/trace.ts +0 -183
  305. package/src/orchestration/models/types.ts +0 -292
  306. package/src/orchestration/models/workflow-run.ts +0 -254
  307. package/src/orchestration/models/workflow-script-registry.ts +0 -35
  308. package/src/orchestration/models/workflow-script.ts +0 -117
  309. package/src/orchestration/script-lint.ts +0 -766
  310. package/src/orchestration/skill-discovery.ts +0 -92
  311. package/src/orchestration/worker-handle.ts +0 -115
  312. package/src/orchestration/worker-host.ts +0 -98
  313. package/src/orchestration/worker-script-builder.ts +0 -408
  314. package/src/orchestration/workflow-files.ts +0 -85
  315. package/src/orchestration/workflow-script-registry-impl.ts +0 -133
  316. package/src/shared/__tests__/agent-ref.test.ts +0 -34
  317. package/src/shared/__tests__/meta-parser.test.ts +0 -304
  318. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -280
  319. package/src/shared/__tests__/resource-discovery.test.ts +0 -736
  320. package/src/shared/__tests__/resource-meta.test.ts +0 -51
  321. package/src/shared/__tests__/schema-jsonify.test.ts +0 -81
  322. package/src/shared/agent-event.ts +0 -13
  323. package/src/shared/agent-ref.ts +0 -57
  324. package/src/shared/meta-parser.ts +0 -261
  325. package/src/shared/resource-discovery.ts +0 -811
  326. package/src/shared/resource-meta.ts +0 -65
  327. package/src/shared/schema-jsonify.ts +0 -56
  328. package/src/shared/xml-injection.ts +0 -35
  329. package/workflows/README.md +0 -81
  330. package/workflows/_shared/agent-refs.cjs +0 -40
  331. package/workflows/chain.js +0 -131
  332. package/workflows/map-reduce.js +0 -174
  333. package/workflows/parallel.js +0 -148
  334. package/workflows/review-fix-loop-utils.cjs +0 -1307
  335. package/workflows/review-fix-loop.js +0 -1396
  336. package/workflows/scatter-gather.js +0 -178
@@ -1,593 +0,0 @@
1
- // src/execution/__tests__/index-session-start.test.ts
2
- //
3
- // C2 critical: index.ts session_start 的 UI handler 注入链路测试。
4
- //
5
- // 测试目标:验证 `subagentsWorkflowExtension(pi)` 的 session_start handler
6
- // 是否正确注入 uiRequestHandler,特别是 **SR-3**:
7
- // 无论 new 还是 existing SubagentService,session_start 都必须调
8
- // setUiRequestHandler——/resume /fork 复用 existingService 时旧 handler 可能失效。
9
- //
10
- // 4 个 case:
11
- // 1. new 路径(existingService=null):setUiRequestHandler 被调,参数是函数(tui mode)
12
- // 2. existing 路径(existingService=mockService):setUiRequestHandler 仍被调(SR-3 关键)
13
- // 3. headless mode(json):createUiRequestHandlerForMode 返回 undefined,
14
- // setUiRequestHandler(undefined) 被调(不注入但仍有调用,SR-3 形式不破)
15
- // 4. rpc mode:setUiRequestHandler 被调,参数是函数
16
- //
17
- // 既有 crash-recovery.test.ts / session-start-reaper.test.ts mock 了 SubagentService
18
- // 但**没有断言 setUiRequestHandler 被调用**——本测试补这个断言,且覆盖 existing 路径
19
- // (既有测试 fixed getSubagentService() => null,只能走 new 分支)。
20
-
21
- import { beforeEach, describe, expect, it, vi } from "vitest";
22
-
23
- // ── mock modules(在 import 前声明;路径相对 src/execution/__tests/) ──
24
- //
25
- // [D15] 为何此处内联 vi.mock 与 vitest.config.ts 的 alias(包根 mocks/pi-coding-agent.ts)并存:
26
- // 两套 mock 分工不同,不强制统一——
27
- // 1. **config alias(包根 mocks/pi-coding-agent.ts)**:完整 stub,导出 ExtensionAPI/
28
- // ExtensionContext/ExtensionMode 等 *类型*(编译期擦除)+ getAgentDir 运行时值。服务于
29
- // 全仓库绝大多数测试的类型解析与轻量 mock(不 import 真实重模块的测试直接吃 alias)。
30
- // 2. **本文件内联 vi.mock(下方)**:vi.mock 在运行时覆盖 config alias,确保本文件 import
31
- // 真实 index.ts(它 `import { getAgentDir } from "@earendil-works/pi-coding-agent"` +
32
- // 一组 type-only import)时,运行时只暴露 getAgentDir,彻底隔离真实 SDK 的模块顶层
33
- // 副作用(避免 jiti 加载真实 pi 包触发未 mock 的依赖链)。
34
- // 3. 形状一致性:内联 mock 的运行时值形状(`{ getAgentDir }`)与 alias stub 的运行时值
35
- // 形状完全一致(alias stub 运行时也只导出 getAgentDir 函数,类型导出在运行期擦除)。
36
- // 差异仅在「覆盖时机」——vi.mock 比 alias 更早介入模块图解析,保证真实 index.ts 加载时
37
- // 拿到的是纯函数桩而非 alias 的完整对象。
38
- // 4. crash-recovery.test.ts / session-start-reaper.test.ts 同此模式(import 真实模块的
39
- // 测试统一用内联 vi.mock 覆盖),三者保持一致。
40
- // 结论:不抽共享 mocks 文件。alias 已承担类型解析,内联 vi.mock 承担运行时隔离,职责正交。
41
-
42
- vi.mock("@earendil-works/pi-coding-agent", () => ({
43
- getAgentDir: () => "/home/user/.pi/agent",
44
- }));
45
- vi.mock("@earendil-works/pi-coding-agent", () => ({
46
- getAgentDir: () => "/home/user/.pi/agent",
47
- }));
48
- vi.mock("@earendil-works/pi-ai", () => ({
49
- StringEnum: (values: string[]) => ({ type: "string", enum: values }),
50
- }));
51
- vi.mock("@earendil-works/pi-ai", () => ({
52
- StringEnum: (values: string[]) => ({ type: "string", enum: values }),
53
- }));
54
- vi.mock("typebox", () => ({
55
- Type: {
56
- Object: (props: Record<string, unknown>) => ({ type: "object", properties: props }),
57
- Optional: (schema: unknown) => ({ ...(schema as object), optional: true }),
58
- String: () => ({ type: "string" }),
59
- Boolean: () => ({ type: "boolean" }),
60
- Number: () => ({ type: "number" }),
61
- Array: (items: unknown) => ({ type: "array", items }),
62
- Record: (key: unknown, value: unknown) => ({ type: "object", additionalProperties: value, key }),
63
- Unknown: () => ({ type: "unknown" }),
64
- Union: (members: unknown[]) => ({ type: "union", members }),
65
- Literal: (value: unknown) => ({ type: "literal", value }),
66
- },
67
- }));
68
-
69
- // ── hoisted mock 实例:捕获 setUiRequestHandler 调用 + 可控行为 ──
70
-
71
- const {
72
- mockSetUiRequestHandler,
73
- mockInitSession,
74
- mockLoadAll,
75
- mockRecoverManifestTmpFiles,
76
- /** existing service 引用——测试可改写以模拟 /resume /fork 复用。 */
77
- existingServiceRef,
78
- } = vi.hoisted(() => ({
79
- mockSetUiRequestHandler: vi.fn(),
80
- mockInitSession: vi.fn(),
81
- mockLoadAll: vi.fn(async () => []),
82
- // ADR-035 启动恢复接线守护:session_start 必须调 service.recoverManifestTmpFiles
83
- mockRecoverManifestTmpFiles: vi.fn(async () => ({ deleted: 0, recovered: 0 })),
84
- existingServiceRef: { current: null as unknown },
85
- }));
86
-
87
- // SubagentService mock:每次构造都返回同一组 spy,setUiRequestHandler 可观察。
88
- // getSubagentService 返回 existingServiceRef.current(null=走 new;非 null=走 existing)。
89
- vi.mock("../subagent-service.ts", () => ({
90
- SubagentService: class {
91
- initSession = mockInitSession;
92
- setUiRequestHandler = mockSetUiRequestHandler;
93
- recoverManifestTmpFiles = mockRecoverManifestTmpFiles;
94
- startGcTimer = vi.fn();
95
- getStreamSink = () => null;
96
- dispose = vi.fn();
97
- },
98
- getSubagentService: () => existingServiceRef.current,
99
- setSubagentService: vi.fn(),
100
- }));
101
-
102
- vi.mock("../model-config-service.ts", () => ({
103
- ModelConfigService: class {
104
- initModel = vi.fn();
105
- setCtxModel = vi.fn();
106
- },
107
- getModelConfigService: () => null,
108
- setModelConfigService: vi.fn(),
109
- }));
110
-
111
- vi.mock("../worktree-manager.ts", () => ({
112
- WorktreeManager: class {
113
- constructor(_agentDir: string) {
114
- /* mock */
115
- }
116
- scan = vi.fn();
117
- cleanup = vi.fn();
118
- create = vi.fn();
119
- collectPatch = vi.fn();
120
- registerPid = vi.fn();
121
- },
122
- }));
123
-
124
- vi.mock("../session-file-gc.ts", () => ({
125
- maybeCleanupExpiredSessionFiles: vi.fn(),
126
- }));
127
-
128
- // JsonlRunStore mock:loadAll 默认空数组(session_start 后段不抛即可)。
129
- // dispose/flushPendingSaves:session_shutdown handler 会调 state.store.dispose()(W2C5);
130
- // dispose 用 hoisted spy 以便 W2TC16 断言(instance.dispose 即 spy)。
131
- const { mockStoreDispose, mockStoreFlushPendingSaves } = vi.hoisted(() => ({
132
- mockStoreDispose: vi.fn(async () => {}),
133
- mockStoreFlushPendingSaves: vi.fn(async () => {}),
134
- }));
135
- vi.mock("../../orchestration/jsonl-run-store.ts", () => ({
136
- JsonlRunStore: class {
137
- loadAll = mockLoadAll;
138
- save = vi.fn(async () => {});
139
- dispose = mockStoreDispose;
140
- flushPendingSaves = mockStoreFlushPendingSaves;
141
- },
142
- }));
143
-
144
- // lifecycle mock:仅替换 terminateRunningRuns 为 hoisted spy(W2TC16 断言
145
- // terminate→dispose 顺序用),其余导出(scheduleTimeBudget/runWorkflow/abortRun/
146
- // evictDoneRunsBeyondCap)经 importOriginal 保留真实实现——本文件不执行真实
147
- // workflow,替换只影响 shutdown handler 的 terminate 调用点。
148
- const { mockTerminateRunningRuns } = vi.hoisted(() => ({
149
- mockTerminateRunningRuns: vi.fn(async () => {}),
150
- }));
151
- vi.mock("../../orchestration/lifecycle.ts", async (importOriginal) => {
152
- const actual = await importOriginal<typeof import("../../orchestration/lifecycle.ts")>();
153
- return { ...actual, terminateRunningRuns: mockTerminateRunningRuns };
154
- });
155
-
156
- // interface 层 mock:避免触发真实 pi.registerTool(pi 是 Proxy,真实模块访问 pi
157
- // 属性时可能抛错)。路径相对 src/execution/__tests/ → ../../interface/...
158
- // registerWorkflowTool / registerWorkflowsCommand 用 hoisted spy——W3TC7/8 需在
159
- // it 内读 mock.calls 捕获 lazyDeps 与 runs getter(对齐 mockStoreDispose 先例)。
160
- vi.mock("../../interface/subagent-tool.ts", () => ({
161
- registerSubagentTool: vi.fn(),
162
- }));
163
- vi.mock("../../interface/subagents.ts", () => ({
164
- registerSubagentsCommand: vi.fn(),
165
- }));
166
- vi.mock("../../interface/bg-notify-render.ts", () => ({
167
- renderBgNotifyMessage: vi.fn(),
168
- }));
169
- const { mockRegisterWorkflowTool, mockRegisterWorkflowsCommand } = vi.hoisted(() => ({
170
- mockRegisterWorkflowTool: vi.fn(),
171
- mockRegisterWorkflowsCommand: vi.fn(),
172
- }));
173
- vi.mock("../../interface/tool-workflow.ts", () => ({
174
- registerWorkflowTool: mockRegisterWorkflowTool,
175
- }));
176
- vi.mock("../../interface/tool-workflow-script.ts", () => ({
177
- registerWorkflowScriptTool: vi.fn(),
178
- }));
179
- vi.mock("../../interface/commands.ts", () => ({
180
- registerWorkflowsCommand: mockRegisterWorkflowsCommand,
181
- }));
182
-
183
- // ── import 被测工厂 ──
184
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
185
-
186
- import subagentsExtension from "../../index.ts";
187
- import { Budget } from "../../orchestration/models/budget.ts";
188
- import { Trace } from "../../orchestration/models/trace.ts";
189
- import { WorkflowRun } from "../../orchestration/models/workflow-run.ts";
190
-
191
- // ── helpers ──
192
-
193
- /** 创建可观察的 mock ExtensionAPI,捕获 session_start / session_shutdown handler。
194
- * Proxy 兜底:未显式处理的 pi.xxx 返回 noop,避免抛错。 */
195
- function createMockPi(): {
196
- pi: ExtensionAPI;
197
- getSessionStartHandler: () => ((event: unknown, ctx: unknown) => Promise<void>) | undefined;
198
- getSessionShutdownHandler: () => ((event: unknown, ctx: unknown) => Promise<void>) | undefined;
199
- } {
200
- let sessionStartHandler: ((event: unknown, ctx: unknown) => Promise<void>) | undefined;
201
- let sessionShutdownHandler: ((event: unknown, ctx: unknown) => Promise<void>) | undefined;
202
- const events = { emit: vi.fn() };
203
- const noop = (): void => {
204
- /* mock */
205
- };
206
- const pi = new Proxy<ExtensionAPI>({} as ExtensionAPI, {
207
- get(_target, prop: string | symbol): unknown {
208
- if (prop === "on") {
209
- return (event: string, handler: (...args: unknown[]) => unknown) => {
210
- if (event === "session_start") {
211
- sessionStartHandler = handler as (event: unknown, ctx: unknown) => Promise<void>;
212
- }
213
- if (event === "session_shutdown") {
214
- sessionShutdownHandler = handler as (event: unknown, ctx: unknown) => Promise<void>;
215
- }
216
- };
217
- }
218
- if (prop === "events") return events;
219
- if (prop === "appendEntry") return noop;
220
- if (prop === "registerMessageRenderer") return noop;
221
- return noop;
222
- },
223
- });
224
- return {
225
- pi,
226
- getSessionStartHandler: () => sessionStartHandler,
227
- getSessionShutdownHandler: () => sessionShutdownHandler,
228
- };
229
- }
230
-
231
- /** 最小 ExtensionContext mock。mode 由参数控制(决定 handler 注入行为)。 */
232
- function createMockCtx(mode: "tui" | "rpc" | "json" | "print"): Record<string, unknown> {
233
- const sessionManager = {
234
- getSessionId: () => "session-inject-1",
235
- getSessionFile: () => "/home/user/.pi/agent/sessions/session-inject-1.jsonl",
236
- getSessionDir: () => "/home/user/.pi/agent/sessions",
237
- getCwd: () => "/home/user/project",
238
- getEntries: () => [],
239
- getBranch: () => [],
240
- getLeafId: () => null,
241
- getLeafEntry: () => undefined,
242
- getEntry: () => undefined,
243
- getHeader: () => null,
244
- getTree: () => [],
245
- getSessionName: () => undefined,
246
- };
247
- const ui = mode === "rpc" ? { setWidget: vi.fn() } : undefined;
248
- return {
249
- cwd: "/home/user/project",
250
- mode,
251
- modelRegistry: {
252
- getAvailable: () => [],
253
- find: () => undefined,
254
- hasConfiguredAuth: () => false,
255
- },
256
- model: undefined,
257
- sessionManager,
258
- ui,
259
- };
260
- }
261
-
262
- beforeEach(() => {
263
- vi.clearAllMocks();
264
- mockLoadAll.mockResolvedValue([]);
265
- existingServiceRef.current = null;
266
- });
267
-
268
- // ── tests ──
269
-
270
- describe("session_start UI handler 注入链路(SR-3)", () => {
271
- it("new 路径(existingService=null):setUiRequestHandler 被调,参数是函数(tui mode)", async () => {
272
- const { pi, getSessionStartHandler } = createMockPi();
273
- subagentsExtension(pi);
274
-
275
- const handler = getSessionStartHandler();
276
- expect(handler).toBeDefined();
277
- await handler!({ type: "session_start" }, createMockCtx("tui"));
278
-
279
- // SR-3 核心:setUiRequestHandler 必须被调
280
- expect(mockSetUiRequestHandler).toHaveBeenCalledTimes(1);
281
- // 非 headless mode → 注入的是函数(UiRequestHandler),非 undefined
282
- const injected = mockSetUiRequestHandler.mock.calls[0]?.[0];
283
- expect(typeof injected).toBe("function");
284
- });
285
-
286
- it("existing 路径(existingService=mockService):setUiRequestHandler 仍被调(SR-3 关键)", async () => {
287
- // 模拟 /resume /fork:getSubagentService() 返回已存在的 service
288
- //(同一 mock 类的实例——spy 仍是 mockSetUiRequestHandler,断言可观察到调用)
289
- existingServiceRef.current = {
290
- initSession: mockInitSession,
291
- setUiRequestHandler: mockSetUiRequestHandler,
292
- // ADR-035:与 SubagentService mock class 形状一致——session_start
293
- // 会调 service.recoverManifestTmpFiles(),缺方法会抛 TypeError 被吞为 console.warn
294
- recoverManifestTmpFiles: mockRecoverManifestTmpFiles,
295
- startGcTimer: vi.fn(),
296
- getStreamSink: () => null,
297
- dispose: vi.fn(),
298
- };
299
-
300
- const { pi, getSessionStartHandler } = createMockPi();
301
- subagentsExtension(pi);
302
-
303
- const handler = getSessionStartHandler();
304
- expect(handler).toBeDefined();
305
- await handler!({ type: "session_start" }, createMockCtx("tui"));
306
-
307
- // SR-3 关键断言:existing 路径下 setUiRequestHandler 仍被调
308
- //(旧 handler 可能已失效,session_start 必须重新注入)
309
- expect(mockSetUiRequestHandler).toHaveBeenCalledTimes(1);
310
- const injected = mockSetUiRequestHandler.mock.calls[0]?.[0];
311
- expect(typeof injected).toBe("function");
312
-
313
- // ADR-035 existing 路径守护:/resume /fork 复用 service 时 session_start
314
- // 也必须调 recoverManifestTmpFiles。守护 case 走 new 路径抓不到 existing 误删,
315
- // 此处对称断言(与 setUiRequestHandler 在本 case 的独立断言同模式)。
316
- expect(mockRecoverManifestTmpFiles).toHaveBeenCalledTimes(1);
317
- });
318
-
319
- it("headless mode(json):createUiRequestHandlerForMode 返回 undefined → setUiRequestHandler(undefined)", async () => {
320
- const { pi, getSessionStartHandler } = createMockPi();
321
- subagentsExtension(pi);
322
-
323
- const handler = getSessionStartHandler();
324
- await handler!({ type: "session_start" }, createMockCtx("json"));
325
-
326
- // SR-3 形式不破:仍调 setUiRequestHandler(参数是 undefined,表示不注入)
327
- expect(mockSetUiRequestHandler).toHaveBeenCalledTimes(1);
328
- expect(mockSetUiRequestHandler.mock.calls[0]?.[0]).toBeUndefined();
329
- });
330
-
331
- it("rpc mode:setUiRequestHandler 被调,参数是函数", async () => {
332
- const { pi, getSessionStartHandler } = createMockPi();
333
- subagentsExtension(pi);
334
-
335
- const handler = getSessionStartHandler();
336
- await handler!({ type: "session_start" }, createMockCtx("rpc"));
337
-
338
- expect(mockSetUiRequestHandler).toHaveBeenCalledTimes(1);
339
- const injected = mockSetUiRequestHandler.mock.calls[0]?.[0];
340
- expect(typeof injected).toBe("function");
341
- });
342
-
343
- it("initSession 不再收 uiRequestHandler(#24 单一注入入口:setUiRequestHandler 唯一通道)", async () => {
344
- const { pi, getSessionStartHandler } = createMockPi();
345
- subagentsExtension(pi);
346
-
347
- const handler = getSessionStartHandler();
348
- await handler!({ type: "session_start" }, createMockCtx("tui"));
349
-
350
- // [#24] uiRequestHandler 单一注入入口:index.ts 只调 service.setUiRequestHandler(h),
351
- // 不再重复传 initSession.uiRequestHandler——避免同一 handler 双路径注入造成
352
- // "哪一个是 source of truth" 歧义。此处断言该契约不被回退。
353
- expect(mockInitSession).toHaveBeenCalledTimes(1);
354
- const initArg = mockInitSession.mock.calls[0]?.[0] as {
355
- uiRequestHandler?: unknown;
356
- mode?: unknown;
357
- dialogQueue?: unknown;
358
- } | undefined;
359
- expect(initArg).toBeDefined();
360
- // uiRequestHandler 不再走 initSession(已被 #24 移除)
361
- expect(initArg?.uiRequestHandler).toBeUndefined();
362
- // mode 仍需 session 级注入(uiObservability.setMode 依赖它)
363
- expect(initArg?.mode).toBe("tui");
364
- // dialogQueue 仍注入(SR-4 清理路径接通)
365
- expect(initArg).toHaveProperty("dialogQueue");
366
- // handler 经 setUiRequestHandler 注入(单一入口)——前面 case 已断言,此处复断不再赘述。
367
- expect(mockSetUiRequestHandler).toHaveBeenCalledTimes(1);
368
- });
369
-
370
- it("session_start 调用 recoverManifestTmpFiles(接线守护,防 ADR-035 启动恢复再次断线)", async () => {
371
- const { pi, getSessionStartHandler } = createMockPi();
372
- subagentsExtension(pi);
373
-
374
- const handler = getSessionStartHandler();
375
- expect(handler).toBeDefined();
376
- await handler!({ type: "session_start" }, createMockCtx("tui"));
377
-
378
- // ADR-035 接线断言:session_start 必须调 service.recoverManifestTmpFiles(防死代码回退)
379
- expect(mockRecoverManifestTmpFiles).toHaveBeenCalledTimes(1);
380
- });
381
- });
382
-
383
- describe("session_shutdown: store.dispose 接线(W2TC16)", () => {
384
- it("W2TC16: session_shutdown 触发后 store 实例 dispose 被调用(terminate 完成之后、sessionState 清理之前)", async () => {
385
- // 预热:session_start 建立一个 sessionState 条目(含 mock store 实例)。
386
- // loadAll 注入 running run 使其进入 sessionState.runs——duck-typed 对象 +
387
- // no-op transition:session_start 的 kill-9 恢复会把 running run 转 done,failed
388
- // (真实 WorkflowRun.reconstruct 无法保持 running),no-op transition 吞掉该转换
389
- // 让 run 以 running 进入 sessionState.runs。运行时形状由消费路径保证:handler
390
- // 只读 state.status(string)与 transition(可调用),duck typing 满足。
391
- mockStoreDispose.mockClear();
392
- const runningRun = {
393
- runId: "wf-w2tc16-1",
394
- state: { status: "running", error: undefined as string | undefined },
395
- transition: vi.fn(),
396
- } as unknown as WorkflowRun;
397
- mockLoadAll.mockResolvedValue([runningRun]);
398
- const { pi, getSessionStartHandler, getSessionShutdownHandler } = createMockPi();
399
- subagentsExtension(pi);
400
-
401
- const startHandler = getSessionStartHandler();
402
- expect(startHandler).toBeDefined();
403
- await startHandler!({ type: "session_start" }, createMockCtx("tui"));
404
-
405
- // terminate gate:deferred 模拟 terminateRunningRuns 内部 `await store.save(run)`
406
- // 落盘边界——resolve 前 dispose 不得发生(terminate 未完成 = failed 状态未落盘,
407
- // 此刻 dispose 刷 pending 批会把未定稿的 running 尾巴刷出去,重启后 kill-9
408
- // 恢复误判)。
409
- let resolveTerminate = (): void => {};
410
- mockTerminateRunningRuns.mockImplementationOnce(async () => {
411
- await new Promise<void>((r) => {
412
- resolveTerminate = r;
413
- });
414
- });
415
-
416
- // 触发 session_shutdown(event 触发模式对齐本文件 session_start 既有写法)
417
- const shutdownHandler = getSessionShutdownHandler();
418
- expect(shutdownHandler).toBeDefined();
419
- const shutdownDone = shutdownHandler!({ type: "session_shutdown" }, createMockCtx("tui"));
420
-
421
- // terminate 未完成(save 未落盘)→ dispose 未被调(await 边界真实生效)
422
- await Promise.resolve();
423
- expect(mockStoreDispose).not.toHaveBeenCalled();
424
-
425
- resolveTerminate();
426
- await shutdownDone;
427
-
428
- // 每 sessionState 条目一次:session_start 建了 1 个 session → terminate 调用 1 次
429
- expect(mockTerminateRunningRuns).toHaveBeenCalledTimes(1);
430
- // 原因串字面值(session_shutdown 路径,接口契约锁定)
431
- expect(mockTerminateRunningRuns.mock.calls[0]?.[1]).toBe("Session shutdown: run terminated");
432
- // dispose 在 terminate 完成后被调(每 session 1 次)
433
- expect(mockStoreDispose).toHaveBeenCalledTimes(1);
434
-
435
- // 顺序断言「sessionState 清理之前」(标题后半):sessionState.delete 是 Map
436
- // 同步操作、无外部可观察 spy——用第二次 shutdown 的幂等性间接证明:第一次
437
- // handler 内条目已删(delete 在 dispose await 之后执行),重复 shutdown 遍历
438
- // 空 Map 不再二次 terminate/dispose。若清理被跳过,此处会涨到 2 次。
439
- await shutdownHandler!({ type: "session_shutdown" }, createMockCtx("tui"));
440
- expect(mockTerminateRunningRuns).toHaveBeenCalledTimes(1);
441
- expect(mockStoreDispose).toHaveBeenCalledTimes(1);
442
- });
443
- });
444
-
445
- // ── W-N: done run 淘汰接线(loadAll 裁剪 + onRunDone 裁剪) ──
446
-
447
- /** fixture 基准时刻(过去时刻,保证 now 恒晚于全部 fixture completedAt)。 */
448
- const W3_T0 = Date.parse("2020-01-01T00:00:00.000Z");
449
- function w3IsoAt(min: number): string {
450
- return new Date(W3_T0 + min * 60_000).toISOString();
451
- }
452
-
453
- /** loadAll 重水合输入 fixture:done run(status done reason completed + completedAt)。 */
454
- function makeLoadedDoneRun(runId: string, completedAt: string): WorkflowRun {
455
- return WorkflowRun.reconstruct(
456
- runId,
457
- {
458
- scriptSource: "execute() {}",
459
- args: {},
460
- scriptName: "test",
461
- scriptPath: "/fake/test.js",
462
- },
463
- {
464
- status: "done",
465
- reason: "completed",
466
- budget: new Budget({ maxTokens: 1000 }),
467
- calls: new Map(),
468
- trace: new Trace(),
469
- errorLogs: [],
470
- },
471
- { startedAt: completedAt, completedAt },
472
- );
473
- }
474
-
475
- /** loadAll 重水合输入 fixture:running run(kill-9 恢复输入,reconstruct 跳 I1 合法)。 */
476
- function makeLoadedRunningRun(runId: string): WorkflowRun {
477
- return WorkflowRun.reconstruct(
478
- runId,
479
- {
480
- scriptSource: "execute() {}",
481
- args: {},
482
- scriptName: "test",
483
- scriptPath: "/fake/test.js",
484
- },
485
- {
486
- status: "running",
487
- budget: new Budget({ maxTokens: 1000 }),
488
- calls: new Map(),
489
- trace: new Trace(),
490
- errorLogs: [],
491
- },
492
- { startedAt: w3IsoAt(0) },
493
- );
494
- }
495
-
496
- describe("W-3: done run 淘汰接线(loadAll 裁剪 + onRunDone 裁剪)", () => {
497
- it("W3TC7: session_start loadAll 灌入后裁剪到 K(21 done + 1 running kill-9 恢复)", async () => {
498
- // 21 个 done(completedAt 递增)+ 1 个 running(kill-9 恢复输入)
499
- const doneRuns = Array.from({ length: 21 }, (_, i) =>
500
- makeLoadedDoneRun(`wf-loaded-${i}`, w3IsoAt(i)));
501
- const runningRun = makeLoadedRunningRun("wf-recovered-1");
502
- mockLoadAll.mockResolvedValue([...doneRuns, runningRun]);
503
-
504
- const { pi, getSessionStartHandler } = createMockPi();
505
- subagentsExtension(pi);
506
- const handler = getSessionStartHandler();
507
- expect(handler).toBeDefined();
508
- await handler!({ type: "session_start" }, createMockCtx("tui"));
509
-
510
- // runs Map 观察点 = registerWorkflowsCommand 第二参数的 runs getter
511
- //(工厂入口同步调用,mock.calls[0] 恒存在)
512
- const getRuns = mockRegisterWorkflowsCommand.mock.calls[0]?.[1] as
513
- | (() => Map<string, WorkflowRun>)
514
- | undefined;
515
- expect(typeof getRuns).toBe("function");
516
- const runs = getRuns!();
517
-
518
- // kill-9 恢复链把 running 转 done,failed(既有语义)后共 22 done → 裁到 20
519
- expect(runs.size).toBe(20);
520
- // 恢复 run 的 completedAt 为 transition 时刻(now,晚于全部 fixture 过去时间戳)
521
- // → 必在保留端
522
- expect(runs.has("wf-recovered-1")).toBe(true);
523
- expect(runs.get("wf-recovered-1")?.state.status).toBe("done");
524
- expect(runs.get("wf-recovered-1")?.state.reason).toBe("failed");
525
- // 被淘汰 2 个 = fixture 最旧两个
526
- expect(runs.has("wf-loaded-0")).toBe(false);
527
- expect(runs.has("wf-loaded-1")).toBe(false);
528
- for (let i = 2; i < 21; i++) {
529
- expect(runs.has(`wf-loaded-${i}`)).toBe(true);
530
- }
531
- });
532
-
533
- it("W3TC8: onRunDone 回调接线——notify → track → evict,本轮 run 不被自身触发的裁剪淘汰", async () => {
534
- // session_start 预热:恰好 20 个 done(completedAt t0..t19)
535
- const doneRuns = Array.from({ length: 20 }, (_, i) =>
536
- makeLoadedDoneRun(`wf-pre-${i}`, w3IsoAt(i)));
537
- mockLoadAll.mockResolvedValue(doneRuns);
538
-
539
- const { pi, getSessionStartHandler } = createMockPi();
540
- subagentsExtension(pi);
541
- const handler = getSessionStartHandler();
542
- expect(handler).toBeDefined();
543
- await handler!({ type: "session_start" }, createMockCtx("tui"));
544
-
545
- // lazyDeps = registerWorkflowTool 第二参数(getter 转发 makeDeps 的 onRunDone)
546
- const lazyDeps = mockRegisterWorkflowTool.mock.calls[0]?.[1] as
547
- | { onRunDone: (run: WorkflowRun) => void }
548
- | undefined;
549
- expect(lazyDeps).toBeDefined();
550
-
551
- // 本轮 newDoneRun:completedAt = now(晚于全部 fixture 过去时间戳)
552
- const newDoneRun = WorkflowRun.reconstruct(
553
- "wf-current-1",
554
- {
555
- scriptSource: "execute() {}",
556
- args: {},
557
- scriptName: "test",
558
- scriptPath: "/fake/test.js",
559
- },
560
- {
561
- status: "done",
562
- reason: "completed",
563
- budget: new Budget({ maxTokens: 1000 }),
564
- calls: new Map(),
565
- trace: new Trace(),
566
- errorLogs: [],
567
- },
568
- { startedAt: w3IsoAt(999), completedAt: new Date().toISOString() },
569
- );
570
-
571
- // 模拟真实流程的注册步骤:lifecycle.ts runWorkflow 在创建时 deps.runs.set(runId, run)
572
- //(onRunDone 回调只裁剪不注册——本轮 run 必须先在 Map 中,「保留端」断言才有对象)
573
- const getRuns = mockRegisterWorkflowsCommand.mock.calls[0]?.[1] as
574
- | (() => Map<string, WorkflowRun>)
575
- | undefined;
576
- expect(typeof getRuns).toBe("function");
577
- const runs = getRuns!();
578
- runs.set("wf-current-1", newDoneRun);
579
-
580
- // 回调不抛错(notifyDone 对 mock pi Proxy 安全:sendMessage 返回 noop;
581
- // toGuiCtx(tui ctx) isGuiCapable false 走无 __gui__ 分支)
582
- expect(() => lazyDeps!.onRunDone(newDoneRun)).not.toThrow();
583
-
584
- // 21 done 裁 1:size 回到 20
585
- expect(runs.size).toBe(20);
586
- // 被淘汰的是 t0 最旧 run 而非本轮 run(completedAt 最新恒在保留端)
587
- expect(runs.has("wf-current-1")).toBe(true);
588
- expect(runs.has("wf-pre-0")).toBe(false);
589
- for (let i = 1; i < 20; i++) {
590
- expect(runs.has(`wf-pre-${i}`)).toBe(true);
591
- }
592
- });
593
- });