@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,672 +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 { afterEach, 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
- * spies(可选):U2 通知账本用例注入 appendEntry / sendMessage 观察桩。 */
196
- function createMockPi(spies?: { appendEntry?: (customType: string, data: unknown) => void; sendMessage?: (message: unknown, options?: unknown) => void }): {
197
- pi: ExtensionAPI;
198
- getSessionStartHandler: () => ((event: unknown, ctx: unknown) => Promise<void>) | undefined;
199
- getSessionShutdownHandler: () => ((event: unknown, ctx: unknown) => Promise<void>) | undefined;
200
- } {
201
- let sessionStartHandler: ((event: unknown, ctx: unknown) => Promise<void>) | undefined;
202
- let sessionShutdownHandler: ((event: unknown, ctx: unknown) => Promise<void>) | undefined;
203
- const events = { emit: vi.fn() };
204
- const noop = (): void => {
205
- /* mock */
206
- };
207
- const pi = new Proxy<ExtensionAPI>({} as ExtensionAPI, {
208
- get(_target, prop: string | symbol): unknown {
209
- if (prop === "on") {
210
- return (event: string, handler: (...args: unknown[]) => unknown) => {
211
- if (event === "session_start") {
212
- sessionStartHandler = handler as (event: unknown, ctx: unknown) => Promise<void>;
213
- }
214
- if (event === "session_shutdown") {
215
- sessionShutdownHandler = handler as (event: unknown, ctx: unknown) => Promise<void>;
216
- }
217
- };
218
- }
219
- if (prop === "events") return events;
220
- if (prop === "appendEntry") return spies?.appendEntry ?? noop;
221
- if (prop === "sendMessage") return spies?.sendMessage ?? noop;
222
- if (prop === "registerMessageRenderer") return noop;
223
- return noop;
224
- },
225
- });
226
- return {
227
- pi,
228
- getSessionStartHandler: () => sessionStartHandler,
229
- getSessionShutdownHandler: () => sessionShutdownHandler,
230
- };
231
- }
232
-
233
- /** 最小 ExtensionContext mock。mode 由参数控制(决定 handler 注入行为)。
234
- * entries(可选):U2 通知账本用例注入 sessionManager.getEntries 返回值
235
- * (模拟重启前落盘的 ledger/ack entry)。 */
236
- function createMockCtx(mode: "tui" | "rpc" | "json" | "print", entries?: unknown[]): Record<string, unknown> {
237
- const sessionManager = {
238
- getSessionId: () => "session-inject-1",
239
- getSessionFile: () => "/home/user/.pi/agent/sessions/session-inject-1.jsonl",
240
- getSessionDir: () => "/home/user/.pi/agent/sessions",
241
- getCwd: () => "/home/user/project",
242
- getEntries: () => entries ?? [],
243
- getBranch: () => [],
244
- getLeafId: () => null,
245
- getLeafEntry: () => undefined,
246
- getEntry: () => undefined,
247
- getHeader: () => null,
248
- getTree: () => [],
249
- getSessionName: () => undefined,
250
- };
251
- const ui = mode === "rpc" ? { setWidget: vi.fn() } : undefined;
252
- return {
253
- cwd: "/home/user/project",
254
- mode,
255
- modelRegistry: {
256
- getAvailable: () => [],
257
- find: () => undefined,
258
- hasConfiguredAuth: () => false,
259
- },
260
- model: undefined,
261
- sessionManager,
262
- ui,
263
- isIdle: () => true,
264
- };
265
- }
266
-
267
- beforeEach(() => {
268
- vi.clearAllMocks();
269
- mockLoadAll.mockResolvedValue([]);
270
- existingServiceRef.current = null;
271
- });
272
-
273
- // ── tests ──
274
-
275
- describe("session_start UI handler 注入链路(SR-3)", () => {
276
- it("new 路径(existingService=null):setUiRequestHandler 被调,参数是函数(tui mode)", async () => {
277
- const { pi, getSessionStartHandler } = createMockPi();
278
- subagentsExtension(pi);
279
-
280
- const handler = getSessionStartHandler();
281
- expect(handler).toBeDefined();
282
- await handler!({ type: "session_start" }, createMockCtx("tui"));
283
-
284
- // SR-3 核心:setUiRequestHandler 必须被调
285
- expect(mockSetUiRequestHandler).toHaveBeenCalledTimes(1);
286
- // 非 headless mode → 注入的是函数(UiRequestHandler),非 undefined
287
- const injected = mockSetUiRequestHandler.mock.calls[0]?.[0];
288
- expect(typeof injected).toBe("function");
289
- });
290
-
291
- it("existing 路径(existingService=mockService):setUiRequestHandler 仍被调(SR-3 关键)", async () => {
292
- // 模拟 /resume /fork:getSubagentService() 返回已存在的 service
293
- //(同一 mock 类的实例——spy 仍是 mockSetUiRequestHandler,断言可观察到调用)
294
- existingServiceRef.current = {
295
- initSession: mockInitSession,
296
- setUiRequestHandler: mockSetUiRequestHandler,
297
- // ADR-035:与 SubagentService mock class 形状一致——session_start
298
- // 会调 service.recoverManifestTmpFiles(),缺方法会抛 TypeError 被吞为 console.warn
299
- recoverManifestTmpFiles: mockRecoverManifestTmpFiles,
300
- startGcTimer: vi.fn(),
301
- getStreamSink: () => null,
302
- dispose: vi.fn(),
303
- };
304
-
305
- const { pi, getSessionStartHandler } = createMockPi();
306
- subagentsExtension(pi);
307
-
308
- const handler = getSessionStartHandler();
309
- expect(handler).toBeDefined();
310
- await handler!({ type: "session_start" }, createMockCtx("tui"));
311
-
312
- // SR-3 关键断言:existing 路径下 setUiRequestHandler 仍被调
313
- //(旧 handler 可能已失效,session_start 必须重新注入)
314
- expect(mockSetUiRequestHandler).toHaveBeenCalledTimes(1);
315
- const injected = mockSetUiRequestHandler.mock.calls[0]?.[0];
316
- expect(typeof injected).toBe("function");
317
-
318
- // ADR-035 existing 路径守护:/resume /fork 复用 service 时 session_start
319
- // 也必须调 recoverManifestTmpFiles。守护 case 走 new 路径抓不到 existing 误删,
320
- // 此处对称断言(与 setUiRequestHandler 在本 case 的独立断言同模式)。
321
- expect(mockRecoverManifestTmpFiles).toHaveBeenCalledTimes(1);
322
- });
323
-
324
- it("headless mode(json):createUiRequestHandlerForMode 返回 undefined → setUiRequestHandler(undefined)", async () => {
325
- const { pi, getSessionStartHandler } = createMockPi();
326
- subagentsExtension(pi);
327
-
328
- const handler = getSessionStartHandler();
329
- await handler!({ type: "session_start" }, createMockCtx("json"));
330
-
331
- // SR-3 形式不破:仍调 setUiRequestHandler(参数是 undefined,表示不注入)
332
- expect(mockSetUiRequestHandler).toHaveBeenCalledTimes(1);
333
- expect(mockSetUiRequestHandler.mock.calls[0]?.[0]).toBeUndefined();
334
- });
335
-
336
- it("rpc mode:setUiRequestHandler 被调,参数是函数", async () => {
337
- const { pi, getSessionStartHandler } = createMockPi();
338
- subagentsExtension(pi);
339
-
340
- const handler = getSessionStartHandler();
341
- await handler!({ type: "session_start" }, createMockCtx("rpc"));
342
-
343
- expect(mockSetUiRequestHandler).toHaveBeenCalledTimes(1);
344
- const injected = mockSetUiRequestHandler.mock.calls[0]?.[0];
345
- expect(typeof injected).toBe("function");
346
- });
347
-
348
- it("initSession 不再收 uiRequestHandler(#24 单一注入入口:setUiRequestHandler 唯一通道)", async () => {
349
- const { pi, getSessionStartHandler } = createMockPi();
350
- subagentsExtension(pi);
351
-
352
- const handler = getSessionStartHandler();
353
- await handler!({ type: "session_start" }, createMockCtx("tui"));
354
-
355
- // [#24] uiRequestHandler 单一注入入口:index.ts 只调 service.setUiRequestHandler(h),
356
- // 不再重复传 initSession.uiRequestHandler——避免同一 handler 双路径注入造成
357
- // "哪一个是 source of truth" 歧义。此处断言该契约不被回退。
358
- expect(mockInitSession).toHaveBeenCalledTimes(1);
359
- const initArg = mockInitSession.mock.calls[0]?.[0] as {
360
- uiRequestHandler?: unknown;
361
- mode?: unknown;
362
- dialogQueue?: unknown;
363
- } | undefined;
364
- expect(initArg).toBeDefined();
365
- // uiRequestHandler 不再走 initSession(已被 #24 移除)
366
- expect(initArg?.uiRequestHandler).toBeUndefined();
367
- // mode 仍需 session 级注入(uiObservability.setMode 依赖它)
368
- expect(initArg?.mode).toBe("tui");
369
- // dialogQueue 仍注入(SR-4 清理路径接通)
370
- expect(initArg).toHaveProperty("dialogQueue");
371
- // handler 经 setUiRequestHandler 注入(单一入口)——前面 case 已断言,此处复断不再赘述。
372
- expect(mockSetUiRequestHandler).toHaveBeenCalledTimes(1);
373
- });
374
-
375
- it("session_start 调用 recoverManifestTmpFiles(接线守护,防 ADR-035 启动恢复再次断线)", async () => {
376
- const { pi, getSessionStartHandler } = createMockPi();
377
- subagentsExtension(pi);
378
-
379
- const handler = getSessionStartHandler();
380
- expect(handler).toBeDefined();
381
- await handler!({ type: "session_start" }, createMockCtx("tui"));
382
-
383
- // ADR-035 接线断言:session_start 必须调 service.recoverManifestTmpFiles(防死代码回退)
384
- expect(mockRecoverManifestTmpFiles).toHaveBeenCalledTimes(1);
385
- });
386
- });
387
-
388
- describe("session_shutdown: store.dispose 接线(W2TC16)", () => {
389
- it("W2TC16: session_shutdown 触发后 store 实例 dispose 被调用(terminate 完成之后、sessionState 清理之前)", async () => {
390
- // 预热:session_start 建立一个 sessionState 条目(含 mock store 实例)。
391
- // loadAll 注入 running run 使其进入 sessionState.runs——duck-typed 对象 +
392
- // no-op transition:session_start 的 kill-9 恢复会把 running run 转 done,failed
393
- // (真实 WorkflowRun.reconstruct 无法保持 running),no-op transition 吞掉该转换
394
- // 让 run 以 running 进入 sessionState.runs。运行时形状由消费路径保证:handler
395
- // 只读 state.status(string)与 transition(可调用),duck typing 满足。
396
- mockStoreDispose.mockClear();
397
- const runningRun = {
398
- runId: "wf-w2tc16-1",
399
- state: { status: "running", error: undefined as string | undefined },
400
- transition: vi.fn(),
401
- } as unknown as WorkflowRun;
402
- mockLoadAll.mockResolvedValue([runningRun]);
403
- const { pi, getSessionStartHandler, getSessionShutdownHandler } = createMockPi();
404
- subagentsExtension(pi);
405
-
406
- const startHandler = getSessionStartHandler();
407
- expect(startHandler).toBeDefined();
408
- await startHandler!({ type: "session_start" }, createMockCtx("tui"));
409
-
410
- // terminate gate:deferred 模拟 terminateRunningRuns 内部 `await store.save(run)`
411
- // 落盘边界——resolve 前 dispose 不得发生(terminate 未完成 = failed 状态未落盘,
412
- // 此刻 dispose 刷 pending 批会把未定稿的 running 尾巴刷出去,重启后 kill-9
413
- // 恢复误判)。
414
- let resolveTerminate = (): void => {};
415
- mockTerminateRunningRuns.mockImplementationOnce(async () => {
416
- await new Promise<void>((r) => {
417
- resolveTerminate = r;
418
- });
419
- });
420
-
421
- // 触发 session_shutdown(event 触发模式对齐本文件 session_start 既有写法)
422
- const shutdownHandler = getSessionShutdownHandler();
423
- expect(shutdownHandler).toBeDefined();
424
- const shutdownDone = shutdownHandler!({ type: "session_shutdown" }, createMockCtx("tui"));
425
-
426
- // terminate 未完成(save 未落盘)→ dispose 未被调(await 边界真实生效)
427
- await Promise.resolve();
428
- expect(mockStoreDispose).not.toHaveBeenCalled();
429
-
430
- resolveTerminate();
431
- await shutdownDone;
432
-
433
- // 每 sessionState 条目一次:session_start 建了 1 个 session → terminate 调用 1 次
434
- expect(mockTerminateRunningRuns).toHaveBeenCalledTimes(1);
435
- // 原因串字面值(session_shutdown 路径,接口契约锁定)
436
- expect(mockTerminateRunningRuns.mock.calls[0]?.[1]).toBe("Session shutdown: run terminated");
437
- // dispose 在 terminate 完成后被调(每 session 1 次)
438
- expect(mockStoreDispose).toHaveBeenCalledTimes(1);
439
-
440
- // 顺序断言「sessionState 清理之前」(标题后半):sessionState.delete 是 Map
441
- // 同步操作、无外部可观察 spy——用第二次 shutdown 的幂等性间接证明:第一次
442
- // handler 内条目已删(delete 在 dispose await 之后执行),重复 shutdown 遍历
443
- // 空 Map 不再二次 terminate/dispose。若清理被跳过,此处会涨到 2 次。
444
- await shutdownHandler!({ type: "session_shutdown" }, createMockCtx("tui"));
445
- expect(mockTerminateRunningRuns).toHaveBeenCalledTimes(1);
446
- expect(mockStoreDispose).toHaveBeenCalledTimes(1);
447
- });
448
- });
449
-
450
- // ── W-N: done run 淘汰接线(loadAll 裁剪 + onRunDone 裁剪) ──
451
-
452
- /** fixture 基准时刻(过去时刻,保证 now 恒晚于全部 fixture completedAt)。 */
453
- const W3_T0 = Date.parse("2020-01-01T00:00:00.000Z");
454
- function w3IsoAt(min: number): string {
455
- return new Date(W3_T0 + min * 60_000).toISOString();
456
- }
457
-
458
- /** loadAll 重水合输入 fixture:done run(status done reason completed + completedAt)。 */
459
- function makeLoadedDoneRun(runId: string, completedAt: string): WorkflowRun {
460
- return WorkflowRun.reconstruct(
461
- runId,
462
- {
463
- scriptSource: "execute() {}",
464
- args: {},
465
- scriptName: "test",
466
- scriptPath: "/fake/test.js",
467
- },
468
- {
469
- status: "done",
470
- reason: "completed",
471
- budget: new Budget({ maxTokens: 1000 }),
472
- calls: new Map(),
473
- trace: new Trace(),
474
- errorLogs: [],
475
- },
476
- { startedAt: completedAt, completedAt },
477
- );
478
- }
479
-
480
- /** loadAll 重水合输入 fixture:running run(kill-9 恢复输入,reconstruct 跳 I1 合法)。 */
481
- function makeLoadedRunningRun(runId: string): WorkflowRun {
482
- return WorkflowRun.reconstruct(
483
- runId,
484
- {
485
- scriptSource: "execute() {}",
486
- args: {},
487
- scriptName: "test",
488
- scriptPath: "/fake/test.js",
489
- },
490
- {
491
- status: "running",
492
- budget: new Budget({ maxTokens: 1000 }),
493
- calls: new Map(),
494
- trace: new Trace(),
495
- errorLogs: [],
496
- },
497
- { startedAt: w3IsoAt(0) },
498
- );
499
- }
500
-
501
- describe("W-3: done run 淘汰接线(loadAll 裁剪 + onRunDone 裁剪)", () => {
502
- it("W3TC7: session_start loadAll 灌入后裁剪到 K(21 done + 1 running kill-9 恢复)", async () => {
503
- // 21 个 done(completedAt 递增)+ 1 个 running(kill-9 恢复输入)
504
- const doneRuns = Array.from({ length: 21 }, (_, i) =>
505
- makeLoadedDoneRun(`wf-loaded-${i}`, w3IsoAt(i)));
506
- const runningRun = makeLoadedRunningRun("wf-recovered-1");
507
- mockLoadAll.mockResolvedValue([...doneRuns, runningRun]);
508
-
509
- const { pi, getSessionStartHandler } = createMockPi();
510
- subagentsExtension(pi);
511
- const handler = getSessionStartHandler();
512
- expect(handler).toBeDefined();
513
- await handler!({ type: "session_start" }, createMockCtx("tui"));
514
-
515
- // runs Map 观察点 = registerWorkflowsCommand 第二参数的 runs getter
516
- //(工厂入口同步调用,mock.calls[0] 恒存在)
517
- const getRuns = mockRegisterWorkflowsCommand.mock.calls[0]?.[1] as
518
- | (() => Map<string, WorkflowRun>)
519
- | undefined;
520
- expect(typeof getRuns).toBe("function");
521
- const runs = getRuns!();
522
-
523
- // kill-9 恢复链把 running 转 done,failed(既有语义)后共 22 done → 裁到 20
524
- expect(runs.size).toBe(20);
525
- // 恢复 run 的 completedAt 为 transition 时刻(now,晚于全部 fixture 过去时间戳)
526
- // → 必在保留端
527
- expect(runs.has("wf-recovered-1")).toBe(true);
528
- expect(runs.get("wf-recovered-1")?.state.status).toBe("done");
529
- expect(runs.get("wf-recovered-1")?.state.reason).toBe("failed");
530
- // 被淘汰 2 个 = fixture 最旧两个
531
- expect(runs.has("wf-loaded-0")).toBe(false);
532
- expect(runs.has("wf-loaded-1")).toBe(false);
533
- for (let i = 2; i < 21; i++) {
534
- expect(runs.has(`wf-loaded-${i}`)).toBe(true);
535
- }
536
- });
537
-
538
- it("W3TC8: onRunDone 回调接线——notify → track → evict,本轮 run 不被自身触发的裁剪淘汰", async () => {
539
- // session_start 预热:恰好 20 个 done(completedAt t0..t19)
540
- const doneRuns = Array.from({ length: 20 }, (_, i) =>
541
- makeLoadedDoneRun(`wf-pre-${i}`, w3IsoAt(i)));
542
- mockLoadAll.mockResolvedValue(doneRuns);
543
-
544
- const { pi, getSessionStartHandler } = createMockPi();
545
- subagentsExtension(pi);
546
- const handler = getSessionStartHandler();
547
- expect(handler).toBeDefined();
548
- await handler!({ type: "session_start" }, createMockCtx("tui"));
549
-
550
- // lazyDeps = registerWorkflowTool 第二参数(getter 转发 makeDeps 的 onRunDone)
551
- const lazyDeps = mockRegisterWorkflowTool.mock.calls[0]?.[1] as
552
- | { onRunDone: (run: WorkflowRun) => void }
553
- | undefined;
554
- expect(lazyDeps).toBeDefined();
555
-
556
- // 本轮 newDoneRun:completedAt = now(晚于全部 fixture 过去时间戳)
557
- const newDoneRun = WorkflowRun.reconstruct(
558
- "wf-current-1",
559
- {
560
- scriptSource: "execute() {}",
561
- args: {},
562
- scriptName: "test",
563
- scriptPath: "/fake/test.js",
564
- },
565
- {
566
- status: "done",
567
- reason: "completed",
568
- budget: new Budget({ maxTokens: 1000 }),
569
- calls: new Map(),
570
- trace: new Trace(),
571
- errorLogs: [],
572
- },
573
- { startedAt: w3IsoAt(999), completedAt: new Date().toISOString() },
574
- );
575
-
576
- // 模拟真实流程的注册步骤:lifecycle.ts runWorkflow 在创建时 deps.runs.set(runId, run)
577
- //(onRunDone 回调只裁剪不注册——本轮 run 必须先在 Map 中,「保留端」断言才有对象)
578
- const getRuns = mockRegisterWorkflowsCommand.mock.calls[0]?.[1] as
579
- | (() => Map<string, WorkflowRun>)
580
- | undefined;
581
- expect(typeof getRuns).toBe("function");
582
- const runs = getRuns!();
583
- runs.set("wf-current-1", newDoneRun);
584
-
585
- // 回调不抛错(notifyDone 对 mock pi Proxy 安全:sendMessage 返回 noop;
586
- // toGuiCtx(tui ctx) isGuiCapable false 走无 __gui__ 分支)
587
- expect(() => lazyDeps!.onRunDone(newDoneRun)).not.toThrow();
588
-
589
- // 21 done 裁 1:size 回到 20
590
- expect(runs.size).toBe(20);
591
- // 被淘汰的是 t0 最旧 run 而非本轮 run(completedAt 最新恒在保留端)
592
- expect(runs.has("wf-current-1")).toBe(true);
593
- expect(runs.has("wf-pre-0")).toBe(false);
594
- for (let i = 1; i < 20; i++) {
595
- expect(runs.has(`wf-pre-${i}`)).toBe(true);
596
- }
597
- });
598
- });
599
-
600
- // ============================================================
601
- // [U2] session_start 通知账本装配 + 重启恢复钩子(设计 D4)
602
- // ============================================================
603
- //
604
- // mock session 文件(ctx.sessionManager.getEntries)含 ledger/ack entry →
605
- // session_start handler 装配账本并重放差集:未销账号经 pi.sendMessage({triggerTurn:true})
606
- // 单通道重投;已销账零重发。bind 顺序在 service.initSession 之前(notifier.notify
607
- // 经模块级绑定消费账本)。
608
-
609
- import {
610
- _resetNotifyLedgerForTest,
611
- NOTIFY_ACK_CUSTOM_TYPE,
612
- NOTIFY_CUSTOM_TYPE,
613
- NOTIFY_LEDGER_CUSTOM_TYPE,
614
- } from "../notify-ledger.ts";
615
-
616
- describe("session_start 通知账本恢复钩子(U2 B-ledger)", () => {
617
- afterEach(() => {
618
- _resetNotifyLedgerForTest();
619
- });
620
-
621
- it("mock session 含 ledger/ack entry → 未销账号重放(单通道 triggerTurn),已销账零重发", async () => {
622
- const appendEntryCalls: Array<{ customType: string; data: unknown }> = [];
623
- const sendMessageCalls: Array<{ message: unknown; options: unknown }> = [];
624
- const { pi, getSessionStartHandler } = createMockPi({
625
- appendEntry: (customType, data) => {
626
- appendEntryCalls.push({ customType, data });
627
- },
628
- sendMessage: (message, options) => {
629
- sendMessageCalls.push({ message, options });
630
- },
631
- });
632
- subagentsExtension(pi);
633
-
634
- // 模拟重启前落盘:2 条 ledger entry(s-a 已销账 + s-b 未销账)+ 1 条 ack
635
- const entries: unknown[] = [
636
- { type: "custom", customType: NOTIFY_LEDGER_CUSTOM_TYPE, data: { v: 1, notifyId: "s-a", content: "ca", record: { notifyId: "s-a" } } },
637
- { type: "custom", customType: NOTIFY_LEDGER_CUSTOM_TYPE, data: { v: 1, notifyId: "s-b", content: "cb", record: { notifyId: "s-b" } } },
638
- { type: "custom", customType: NOTIFY_ACK_CUSTOM_TYPE, data: { v: 1, notifyId: "s-a" } },
639
- ];
640
- const handler = getSessionStartHandler();
641
- expect(handler).toBeDefined();
642
- await handler!({ type: "session_start" }, createMockCtx("rpc", entries));
643
-
644
- // 未销账 s-b 单条重放:sendMessage 恰 1 次,options 恰 {triggerTurn:true}
645
- expect(sendMessageCalls).toHaveLength(1);
646
- expect(sendMessageCalls[0]!.options).toEqual({ triggerTurn: true });
647
- const sent = sendMessageCalls[0]!.message as { customType: string; content: string; details?: { notifyId?: string } };
648
- expect(sent.customType).toBe(NOTIFY_CUSTOM_TYPE);
649
- expect(sent.content).toBe("cb");
650
- expect(sent.details?.notifyId).toBe("s-b");
651
- // 已销账 s-a 零重发(不追加新 ledger entry,不重投)
652
- expect(appendEntryCalls.filter((c) => c.customType === NOTIFY_LEDGER_CUSTOM_TYPE)).toHaveLength(0);
653
- });
654
-
655
- it("全部已销账(差集为空)→ 零重放零发送", async () => {
656
- const sendMessageCalls: unknown[] = [];
657
- const { pi, getSessionStartHandler } = createMockPi({
658
- sendMessage: () => {
659
- sendMessageCalls.push({});
660
- },
661
- });
662
- subagentsExtension(pi);
663
-
664
- const entries: unknown[] = [
665
- { type: "custom", customType: NOTIFY_LEDGER_CUSTOM_TYPE, data: { v: 1, notifyId: "s-x", content: "cx", record: { notifyId: "s-x" } } },
666
- { type: "custom", customType: NOTIFY_ACK_CUSTOM_TYPE, data: { v: 1, notifyId: "s-x" } },
667
- ];
668
- await getSessionStartHandler()!({ type: "session_start" }, createMockCtx("rpc", entries));
669
-
670
- expect(sendMessageCalls).toHaveLength(0);
671
- });
672
- });