@zhushanwen/pi-subagent-workflow 8.6.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 (347) hide show
  1. package/package.json +9 -13
  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 +89 -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 +2 -2
  16. package/src/interface/__tests__/tool-workflow-script-generate.test.ts +2 -2
  17. package/src/interface/__tests__/tool-workflow-throw-paths.test.ts +1 -1
  18. package/src/interface/bg-notify-render.ts +4 -4
  19. package/src/interface/commands.ts +3 -3
  20. package/src/interface/format.ts +2 -2
  21. package/src/interface/helpers.ts +1 -1
  22. package/src/interface/list-component.ts +4 -4
  23. package/src/interface/list-shared.ts +2 -2
  24. package/src/interface/list-view.ts +2 -2
  25. package/src/interface/subagent-actions.ts +263 -11
  26. package/src/interface/subagent-tool-schema.ts +20 -13
  27. package/src/interface/subagent-tool.ts +11 -6
  28. package/src/interface/subagents.ts +3 -3
  29. package/src/interface/tool-render.ts +34 -2
  30. package/src/interface/tool-workflow-script.ts +4 -4
  31. package/src/interface/tool-workflow.ts +6 -6
  32. package/src/interface/views/WorkflowsView.ts +5 -5
  33. package/src/interface/views/__tests__/WorkflowsView-signature.test.ts +3 -3
  34. package/src/interface/views/__tests__/detail-content-session-file.test.ts +2 -2
  35. package/src/interface/views/detail-content.ts +4 -4
  36. package/src/interface/views/format.ts +4 -4
  37. package/src/{orchestration/jsonl-run-store.ts → jsonl-run-store.ts} +11 -11
  38. package/src/execution/__tests__/__fixtures__/notifier-golden-snapshots.json +0 -53
  39. package/src/execution/__tests__/__fixtures__/truncline.snapshot.json +0 -1
  40. package/src/execution/__tests__/agent-registry.test.ts +0 -363
  41. package/src/execution/__tests__/agent-result-mapper.test.ts +0 -150
  42. package/src/execution/__tests__/alive-store.test.ts +0 -147
  43. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +0 -490
  44. package/src/execution/__tests__/before-agent-start-injection.test.ts +0 -132
  45. package/src/execution/__tests__/bg-notify-render.test.ts +0 -329
  46. package/src/execution/__tests__/channel-registry-handshake.test.ts +0 -243
  47. package/src/execution/__tests__/chat-engine-routing.test.ts +0 -601
  48. package/src/execution/__tests__/chatmode-first-round-closure-service.test.ts +0 -365
  49. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +0 -190
  50. package/src/execution/__tests__/chatmode-round-notify-real-chain.test.ts +0 -215
  51. package/src/execution/__tests__/concurrency-pool.test.ts +0 -250
  52. package/src/execution/__tests__/config.test.ts +0 -110
  53. package/src/execution/__tests__/conversation-wiring.test.ts +0 -198
  54. package/src/execution/__tests__/crash-recovery.test.ts +0 -320
  55. package/src/execution/__tests__/delivery-methods.test.ts +0 -422
  56. package/src/execution/__tests__/dialog-queue.test.ts +0 -299
  57. package/src/execution/__tests__/epipe-fallback.test.ts +0 -241
  58. package/src/execution/__tests__/execute-and-await-worktree.test.ts +0 -243
  59. package/src/execution/__tests__/execute-nesting.test.ts +0 -323
  60. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
  61. package/src/execution/__tests__/execution-record.test.ts +0 -1394
  62. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +0 -171
  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 -169
  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 -242
  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 -672
  75. package/src/execution/__tests__/lifecycle-manager-lock.test.ts +0 -211
  76. package/src/execution/__tests__/lifecycle-manager.test.ts +0 -383
  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 -140
  80. package/src/execution/__tests__/manifest-parentid.test.ts +0 -117
  81. package/src/execution/__tests__/model-resolver.test.ts +0 -465
  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 -612
  85. package/src/execution/__tests__/notifier-golden-snapshot.test.ts +0 -261
  86. package/src/execution/__tests__/notify-ledger.test.ts +0 -826
  87. package/src/execution/__tests__/one-shot-upgrade.test.ts +0 -205
  88. package/src/execution/__tests__/output-collector.test.ts +0 -358
  89. package/src/execution/__tests__/parent-child-matrix.test.ts +0 -336
  90. package/src/execution/__tests__/path-encoding.test.ts +0 -104
  91. package/src/execution/__tests__/pi-invocation.test.ts +0 -134
  92. package/src/execution/__tests__/record-store.test.ts +0 -1057
  93. package/src/execution/__tests__/records-cwd-isolation.test.ts +0 -91
  94. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +0 -339
  95. package/src/execution/__tests__/recursive-visibility-env.test.ts +0 -273
  96. package/src/execution/__tests__/relay-agent.test.ts +0 -448
  97. package/src/execution/__tests__/relay-env.test.ts +0 -42
  98. package/src/execution/__tests__/resource-policy.test.ts +0 -109
  99. package/src/execution/__tests__/rpc-mode.test.ts +0 -89
  100. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +0 -267
  101. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +0 -253
  102. package/src/execution/__tests__/run-spawn-edges.test.ts +0 -687
  103. package/src/execution/__tests__/run-spawn-integration.test.ts +0 -933
  104. package/src/execution/__tests__/run-spawn-resume.test.ts +0 -322
  105. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +0 -196
  106. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +0 -199
  107. package/src/execution/__tests__/sdk-contract.test.ts +0 -273
  108. package/src/execution/__tests__/session-context-resolver.test.ts +0 -167
  109. package/src/execution/__tests__/session-file-gc.test.ts +0 -293
  110. package/src/execution/__tests__/session-pending.test.ts +0 -197
  111. package/src/execution/__tests__/session-reconstructor.test.ts +0 -379
  112. package/src/execution/__tests__/session-runner-epipe.test.ts +0 -178
  113. package/src/execution/__tests__/session-runner-schema-env.test.ts +0 -350
  114. package/src/execution/__tests__/session-start-reaper.test.ts +0 -233
  115. package/src/execution/__tests__/spawn-args.test.ts +0 -445
  116. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +0 -189
  117. package/src/execution/__tests__/spawn-event-adapter.test.ts +0 -167
  118. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +0 -207
  119. package/src/execution/__tests__/spawned-children.test.ts +0 -92
  120. package/src/execution/__tests__/start-sync-model-guard.test.ts +0 -150
  121. package/src/execution/__tests__/startup-config-declaration.test.ts +0 -35
  122. package/src/execution/__tests__/status-refactor.test.ts +0 -345
  123. package/src/execution/__tests__/stdin-writer.test.ts +0 -463
  124. package/src/execution/__tests__/stream-sink-retirement.test.ts +0 -261
  125. package/src/execution/__tests__/subagent-service-abort.test.ts +0 -60
  126. package/src/execution/__tests__/subagent-service-message-close.test.ts +0 -629
  127. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +0 -180
  128. package/src/execution/__tests__/subagent-service.test.ts +0 -788
  129. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +0 -310
  130. package/src/execution/__tests__/subprocess-agent-runner.test.ts +0 -599
  131. package/src/execution/__tests__/temp-prompt.test.ts +0 -53
  132. package/src/execution/__tests__/timeout-integration.test.ts +0 -615
  133. package/src/execution/__tests__/tombstone-store.test.ts +0 -73
  134. package/src/execution/__tests__/tool-action.test.ts +0 -476
  135. package/src/execution/__tests__/truncline-snapshot.test.ts +0 -81
  136. package/src/execution/__tests__/turn-limiter-semantics.test.ts +0 -194
  137. package/src/execution/__tests__/turn-limiter.test.ts +0 -65
  138. package/src/execution/__tests__/ui-channels.test.ts +0 -187
  139. package/src/execution/__tests__/ui-interaction-model.test.ts +0 -67
  140. package/src/execution/__tests__/ui-request-handler-factory.test.ts +0 -370
  141. package/src/execution/__tests__/ui-request-handler.test.ts +0 -204
  142. package/src/execution/__tests__/ui-request-observability.test.ts +0 -114
  143. package/src/execution/__tests__/ui-request-queue.test.ts +0 -133
  144. package/src/execution/__tests__/worktree-manager.test.ts +0 -633
  145. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +0 -229
  146. package/src/execution/__tests__/worktree-reconcile.integration.test.ts +0 -181
  147. package/src/execution/__tests__/worktree-registry.test.ts +0 -199
  148. package/src/execution/agent-registry.ts +0 -199
  149. package/src/execution/agent-result-mapper.ts +0 -90
  150. package/src/execution/alive-store.ts +0 -92
  151. package/src/execution/argv-mirror.ts +0 -113
  152. package/src/execution/best-effort.ts +0 -38
  153. package/src/execution/channel-registry-access.ts +0 -144
  154. package/src/execution/concurrency-pool.ts +0 -116
  155. package/src/execution/config.ts +0 -96
  156. package/src/execution/dialog-queue.ts +0 -330
  157. package/src/execution/engine/__tests__/common/data-dir.test.ts +0 -53
  158. package/src/execution/engine/__tests__/common/errors.test.ts +0 -132
  159. package/src/execution/engine/__tests__/common/event-journal.test.ts +0 -177
  160. package/src/execution/engine/__tests__/common/kill-chain.test.ts +0 -192
  161. package/src/execution/engine/__tests__/common/nesting-guard.test.ts +0 -81
  162. package/src/execution/engine/__tests__/common/persona-router.test.ts +0 -123
  163. package/src/execution/engine/__tests__/common/pool-manager.test.ts +0 -154
  164. package/src/execution/engine/__tests__/common/schema-emulation.test.ts +0 -128
  165. package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +0 -28
  166. package/src/execution/engine/__tests__/conformance/agent-event-invariants.ts +0 -141
  167. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +0 -109
  168. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +0 -101
  169. package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +0 -77
  170. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +0 -104
  171. package/src/execution/engine/__tests__/conformance/contract.relay.test.ts +0 -342
  172. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +0 -201
  173. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +0 -76
  174. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +0 -79
  175. package/src/execution/engine/__tests__/engine-discovery.test.ts +0 -87
  176. package/src/execution/engine/__tests__/engines-declaration.test.ts +0 -36
  177. package/src/execution/engine/__tests__/model-prompt.test.ts +0 -85
  178. package/src/execution/engine/__tests__/paths.test.ts +0 -39
  179. package/src/execution/engine/__tests__/registry.test.ts +0 -120
  180. package/src/execution/engine/__tests__/routing.test.ts +0 -231
  181. package/src/execution/engine/common/data-dir.ts +0 -62
  182. package/src/execution/engine/common/errors.ts +0 -183
  183. package/src/execution/engine/common/event-journal.ts +0 -254
  184. package/src/execution/engine/common/journal-replay.ts +0 -62
  185. package/src/execution/engine/common/kill-chain.ts +0 -221
  186. package/src/execution/engine/common/nesting-guard.ts +0 -50
  187. package/src/execution/engine/common/persona-router.ts +0 -108
  188. package/src/execution/engine/common/pool-manager.ts +0 -226
  189. package/src/execution/engine/common/schema-emulation.ts +0 -189
  190. package/src/execution/engine/common/session-view-projection.ts +0 -51
  191. package/src/execution/engine/engine-discovery.ts +0 -65
  192. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +0 -469
  193. package/src/execution/engine/engines/pi/__tests__/reader.test.ts +0 -155
  194. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  195. package/src/execution/engine/engines/pi/pi-engine.ts +0 -415
  196. package/src/execution/engine/engines/pi/reader.ts +0 -48
  197. package/src/execution/engine/engines/pi/registration.ts +0 -35
  198. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  199. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  200. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  201. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +0 -246
  202. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +0 -228
  203. package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +0 -210
  204. package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +0 -64
  205. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +0 -127
  206. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -580
  207. package/src/execution/engine/engines/zcode/constants.ts +0 -43
  208. package/src/execution/engine/engines/zcode/golden-sample.ts +0 -39
  209. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  210. package/src/execution/engine/engines/zcode/parser.ts +0 -436
  211. package/src/execution/engine/engines/zcode/preparer.ts +0 -363
  212. package/src/execution/engine/engines/zcode/reader.ts +0 -381
  213. package/src/execution/engine/engines/zcode/registration.ts +0 -37
  214. package/src/execution/engine/engines/zcode/zcode-engine.ts +0 -658
  215. package/src/execution/engine/host-task-spec.ts +0 -47
  216. package/src/execution/engine/model-prompt.ts +0 -59
  217. package/src/execution/engine/paths.ts +0 -42
  218. package/src/execution/engine/port.ts +0 -153
  219. package/src/execution/engine/registry.ts +0 -123
  220. package/src/execution/engine/routing.ts +0 -218
  221. package/src/execution/engine/types.ts +0 -309
  222. package/src/execution/execute-options-mapper.ts +0 -112
  223. package/src/execution/execution-record.ts +0 -965
  224. package/src/execution/finalize-record.ts +0 -251
  225. package/src/execution/finalized-marker.ts +0 -51
  226. package/src/execution/get-state-handshake.ts +0 -104
  227. package/src/execution/host-mode.ts +0 -56
  228. package/src/execution/idle-gc.ts +0 -47
  229. package/src/execution/lifecycle-manager.ts +0 -513
  230. package/src/execution/lifecycle-predicates.ts +0 -65
  231. package/src/execution/manifest-store.ts +0 -256
  232. package/src/execution/model-config-service.ts +0 -219
  233. package/src/execution/model-resolver.ts +0 -248
  234. package/src/execution/notifier.ts +0 -330
  235. package/src/execution/notify-ledger.ts +0 -580
  236. package/src/execution/output-collector.ts +0 -228
  237. package/src/execution/path-encoding.ts +0 -60
  238. package/src/execution/pi-invocation.ts +0 -120
  239. package/src/execution/record-entry.ts +0 -132
  240. package/src/execution/record-store.ts +0 -1234
  241. package/src/execution/relay-env.ts +0 -37
  242. package/src/execution/session-context-resolver.ts +0 -64
  243. package/src/execution/session-file-gc.ts +0 -120
  244. package/src/execution/session-pending.ts +0 -164
  245. package/src/execution/session-reconstructor.ts +0 -678
  246. package/src/execution/session-runner.ts +0 -1781
  247. package/src/execution/sessions-index.ts +0 -304
  248. package/src/execution/spawn-event-adapter.ts +0 -363
  249. package/src/execution/stdin-writer.ts +0 -198
  250. package/src/execution/stream-sink.ts +0 -126
  251. package/src/execution/subagent-service.ts +0 -2141
  252. package/src/execution/subprocess-agent-runner.ts +0 -317
  253. package/src/execution/temp-prompt.ts +0 -62
  254. package/src/execution/tombstone-store.ts +0 -72
  255. package/src/execution/turn-limiter.ts +0 -102
  256. package/src/execution/types.ts +0 -973
  257. package/src/execution/ui-channels.ts +0 -216
  258. package/src/execution/ui-interaction-model.ts +0 -48
  259. package/src/execution/ui-request-handler-factory.ts +0 -212
  260. package/src/execution/ui-request-observability.ts +0 -81
  261. package/src/execution/ui-request-queue.ts +0 -184
  262. package/src/execution/worktree-manager.ts +0 -688
  263. package/src/execution/worktree-registry.ts +0 -222
  264. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +0 -349
  265. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +0 -202
  266. package/src/orchestration/__tests__/agent-call-stream.test.ts +0 -152
  267. package/src/orchestration/__tests__/args-validator.test.ts +0 -143
  268. package/src/orchestration/__tests__/config-loader.test.ts +0 -503
  269. package/src/orchestration/__tests__/error-recovery-handlers.test.ts +0 -809
  270. package/src/orchestration/__tests__/error-recovery-postmessage-defense.test.ts +0 -405
  271. package/src/orchestration/__tests__/error-recovery-serialize-failed-result.test.ts +0 -56
  272. package/src/orchestration/__tests__/error-recovery-workflow-call.test.ts +0 -166
  273. package/src/orchestration/__tests__/execute-agent-call.test.ts +0 -451
  274. package/src/orchestration/__tests__/jsonl-run-store-corrupt-entry.test.ts +0 -150
  275. package/src/orchestration/__tests__/jsonl-run-store-loadall-sources.test.ts +0 -171
  276. package/src/orchestration/__tests__/jsonl-run-store-retention.test.ts +0 -202
  277. package/src/orchestration/__tests__/jsonl-run-store-session-file.test.ts +0 -1030
  278. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +0 -600
  279. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +0 -96
  280. package/src/orchestration/__tests__/lifecycle.test.ts +0 -659
  281. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +0 -95
  282. package/src/orchestration/__tests__/review-fix-loop-e2e.test.ts +0 -2025
  283. package/src/orchestration/__tests__/script-lint.test.ts +0 -831
  284. package/src/orchestration/__tests__/skill-discovery.test.ts +0 -201
  285. package/src/orchestration/__tests__/test-mocks.ts +0 -197
  286. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +0 -368
  287. package/src/orchestration/__tests__/worker-host.test.ts +0 -120
  288. package/src/orchestration/__tests__/worker-returnmeta-passthrough.test.ts +0 -164
  289. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +0 -563
  290. package/src/orchestration/__tests__/worker-script-builder.test.ts +0 -309
  291. package/src/orchestration/__tests__/worker-script-template-snapshot.test.ts +0 -129
  292. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +0 -317
  293. package/src/orchestration/__tests__/workflow-script-lint-memo.test.ts +0 -110
  294. package/src/orchestration/__tests__/workflows-e2e.test.ts +0 -537
  295. package/src/orchestration/agent-opts-resolver.ts +0 -174
  296. package/src/orchestration/args-validator.ts +0 -127
  297. package/src/orchestration/config-loader.ts +0 -315
  298. package/src/orchestration/error-recovery.ts +0 -1018
  299. package/src/orchestration/execute-agent-call.ts +0 -256
  300. package/src/orchestration/launcher.ts +0 -455
  301. package/src/orchestration/lifecycle.ts +0 -399
  302. package/src/orchestration/models/__tests__/budget.test.ts +0 -307
  303. package/src/orchestration/models/__tests__/trace.test.ts +0 -408
  304. package/src/orchestration/models/agent-call.ts +0 -83
  305. package/src/orchestration/models/budget.ts +0 -118
  306. package/src/orchestration/models/ports.ts +0 -164
  307. package/src/orchestration/models/run-runtime.ts +0 -104
  308. package/src/orchestration/models/run-spec.ts +0 -83
  309. package/src/orchestration/models/run-state.ts +0 -44
  310. package/src/orchestration/models/trace.ts +0 -183
  311. package/src/orchestration/models/types.ts +0 -301
  312. package/src/orchestration/models/workflow-run.ts +0 -254
  313. package/src/orchestration/models/workflow-script-registry.ts +0 -35
  314. package/src/orchestration/models/workflow-script.ts +0 -117
  315. package/src/orchestration/script-lint.ts +0 -766
  316. package/src/orchestration/skill-discovery.ts +0 -115
  317. package/src/orchestration/worker-handle.ts +0 -115
  318. package/src/orchestration/worker-host.ts +0 -98
  319. package/src/orchestration/worker-script-builder.ts +0 -421
  320. package/src/orchestration/workflow-files.ts +0 -85
  321. package/src/orchestration/workflow-script-registry-impl.ts +0 -133
  322. package/src/shared/__tests__/agent-ref.test.ts +0 -34
  323. package/src/shared/__tests__/meta-parser.test.ts +0 -304
  324. package/src/shared/__tests__/model-ref.test.ts +0 -306
  325. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -280
  326. package/src/shared/__tests__/resource-discovery.test.ts +0 -736
  327. package/src/shared/__tests__/resource-meta.test.ts +0 -51
  328. package/src/shared/__tests__/schema-jsonify.test.ts +0 -81
  329. package/src/shared/__tests__/timer-delay.test.ts +0 -61
  330. package/src/shared/agent-event.ts +0 -13
  331. package/src/shared/agent-ref.ts +0 -57
  332. package/src/shared/meta-parser.ts +0 -261
  333. package/src/shared/model-ref.ts +0 -286
  334. package/src/shared/resource-discovery.ts +0 -811
  335. package/src/shared/resource-meta.ts +0 -65
  336. package/src/shared/schema-env.ts +0 -44
  337. package/src/shared/schema-jsonify.ts +0 -58
  338. package/src/shared/timer-delay.ts +0 -54
  339. package/src/shared/xml-injection.ts +0 -35
  340. package/workflows/README.md +0 -81
  341. package/workflows/_shared/agent-refs.cjs +0 -40
  342. package/workflows/chain.js +0 -131
  343. package/workflows/map-reduce.js +0 -174
  344. package/workflows/parallel.js +0 -148
  345. package/workflows/review-fix-loop-utils.cjs +0 -1309
  346. package/workflows/review-fix-loop.js +0 -1404
  347. 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
- });