@zhushanwen/pi-subagent-workflow 8.5.0 → 8.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (336) hide show
  1. package/package.json +13 -16
  2. package/relay/relay.mjs +1 -1
  3. package/src/host/__tests__/pi-host.test.ts +214 -0
  4. package/src/host/pi-host.ts +109 -0
  5. package/src/index.ts +142 -32
  6. package/src/injectors/__tests__/engine-awareness.test.ts +292 -0
  7. package/src/injectors/__tests__/engine-section-stability.test.ts +341 -0
  8. package/src/injectors/__tests__/subagent-list-injector.test.ts +5 -5
  9. package/src/injectors/__tests__/workflow-list-injector.test.ts +5 -5
  10. package/src/injectors/engine-awareness.ts +123 -0
  11. package/src/injectors/model-list-injector.ts +1 -1
  12. package/src/injectors/subagent-list-injector.ts +8 -8
  13. package/src/injectors/workflow-list-injector.ts +8 -8
  14. package/src/interface/__tests__/detectors.test.ts +3 -3
  15. package/src/interface/__tests__/subagent-tool-path-guard.test.ts +157 -0
  16. package/src/interface/__tests__/subagent-tool-prompt.test.ts +12 -0
  17. package/src/interface/__tests__/tool-workflow-script-generate.test.ts +2 -2
  18. package/src/interface/__tests__/tool-workflow-throw-paths.test.ts +1 -1
  19. package/src/interface/bg-notify-render.ts +34 -13
  20. package/src/interface/commands.ts +3 -3
  21. package/src/interface/format.ts +2 -2
  22. package/src/interface/helpers.ts +3 -3
  23. package/src/interface/list-component.ts +4 -4
  24. package/src/interface/list-shared.ts +2 -2
  25. package/src/interface/list-view.ts +2 -2
  26. package/src/interface/subagent-actions.ts +288 -19
  27. package/src/interface/subagent-tool-schema.ts +163 -0
  28. package/src/interface/subagent-tool.ts +65 -129
  29. package/src/interface/subagents.ts +5 -5
  30. package/src/interface/tool-render.ts +34 -2
  31. package/src/interface/tool-workflow-script.ts +4 -4
  32. package/src/interface/tool-workflow.ts +6 -6
  33. package/src/interface/views/WorkflowsView.ts +5 -5
  34. package/src/interface/views/__tests__/WorkflowsView-signature.test.ts +3 -3
  35. package/src/interface/views/__tests__/detail-content-session-file.test.ts +2 -2
  36. package/src/interface/views/detail-content.ts +4 -4
  37. package/src/interface/views/format.ts +4 -4
  38. package/src/{orchestration/jsonl-run-store.ts → jsonl-run-store.ts} +132 -18
  39. package/src/execution/__tests__/__fixtures__/notifier-golden-snapshots.json +0 -53
  40. package/src/execution/__tests__/__fixtures__/truncline.snapshot.json +0 -1
  41. package/src/execution/__tests__/agent-registry.test.ts +0 -363
  42. package/src/execution/__tests__/agent-result-mapper.test.ts +0 -150
  43. package/src/execution/__tests__/alive-store.test.ts +0 -147
  44. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +0 -490
  45. package/src/execution/__tests__/before-agent-start-injection.test.ts +0 -132
  46. package/src/execution/__tests__/bg-notify-render.test.ts +0 -256
  47. package/src/execution/__tests__/channel-registry-handshake.test.ts +0 -243
  48. package/src/execution/__tests__/chat-engine-routing.test.ts +0 -597
  49. package/src/execution/__tests__/chatmode-first-round-closure-service.test.ts +0 -365
  50. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +0 -190
  51. package/src/execution/__tests__/chatmode-round-notify-real-chain.test.ts +0 -215
  52. package/src/execution/__tests__/concurrency-pool.test.ts +0 -250
  53. package/src/execution/__tests__/config.test.ts +0 -110
  54. package/src/execution/__tests__/conversation-wiring.test.ts +0 -198
  55. package/src/execution/__tests__/crash-recovery.test.ts +0 -320
  56. package/src/execution/__tests__/delivery-methods.test.ts +0 -385
  57. package/src/execution/__tests__/dialog-queue.test.ts +0 -299
  58. package/src/execution/__tests__/epipe-fallback.test.ts +0 -241
  59. package/src/execution/__tests__/execute-and-await-worktree.test.ts +0 -243
  60. package/src/execution/__tests__/execute-nesting.test.ts +0 -323
  61. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -189
  62. package/src/execution/__tests__/execution-record.test.ts +0 -1284
  63. package/src/execution/__tests__/finalize-record.test.ts +0 -368
  64. package/src/execution/__tests__/finalized-marker.test.ts +0 -82
  65. package/src/execution/__tests__/format-schema-instruction.test.ts +0 -138
  66. package/src/execution/__tests__/format.test.ts +0 -458
  67. package/src/execution/__tests__/gc-timer.test.ts +0 -184
  68. package/src/execution/__tests__/get-record-for-action-restart.test.ts +0 -254
  69. package/src/execution/__tests__/gui-mode-dispatch.test.ts +0 -59
  70. package/src/execution/__tests__/helpers/mock-extension-api.ts +0 -30
  71. package/src/execution/__tests__/helpers/spawn-mock.ts +0 -238
  72. package/src/execution/__tests__/host-mode.test.ts +0 -87
  73. package/src/execution/__tests__/index-session-start-identity.test.ts +0 -371
  74. package/src/execution/__tests__/index-session-start.test.ts +0 -593
  75. package/src/execution/__tests__/lifecycle-manager-lock.test.ts +0 -211
  76. package/src/execution/__tests__/lifecycle-manager.test.ts +0 -337
  77. package/src/execution/__tests__/lifecycle-predicates.test.ts +0 -116
  78. package/src/execution/__tests__/list-component.test.ts +0 -401
  79. package/src/execution/__tests__/list-fields.test.ts +0 -109
  80. package/src/execution/__tests__/manifest-parentid.test.ts +0 -117
  81. package/src/execution/__tests__/model-resolver.test.ts +0 -413
  82. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +0 -287
  83. package/src/execution/__tests__/nested-visibility.test.ts +0 -325
  84. package/src/execution/__tests__/notifier-flush.test.ts +0 -574
  85. package/src/execution/__tests__/notifier-golden-snapshot.test.ts +0 -261
  86. package/src/execution/__tests__/one-shot-upgrade.test.ts +0 -205
  87. package/src/execution/__tests__/output-collector.test.ts +0 -61
  88. package/src/execution/__tests__/parent-child-matrix.test.ts +0 -336
  89. package/src/execution/__tests__/path-encoding.test.ts +0 -104
  90. package/src/execution/__tests__/pi-invocation.test.ts +0 -134
  91. package/src/execution/__tests__/record-store.test.ts +0 -1057
  92. package/src/execution/__tests__/records-cwd-isolation.test.ts +0 -91
  93. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +0 -339
  94. package/src/execution/__tests__/recursive-visibility-env.test.ts +0 -273
  95. package/src/execution/__tests__/relay-agent.test.ts +0 -448
  96. package/src/execution/__tests__/relay-env.test.ts +0 -42
  97. package/src/execution/__tests__/resource-policy.test.ts +0 -109
  98. package/src/execution/__tests__/rpc-mode.test.ts +0 -89
  99. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +0 -267
  100. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +0 -253
  101. package/src/execution/__tests__/run-spawn-edges.test.ts +0 -644
  102. package/src/execution/__tests__/run-spawn-integration.test.ts +0 -933
  103. package/src/execution/__tests__/run-spawn-resume.test.ts +0 -322
  104. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +0 -196
  105. package/src/execution/__tests__/sdk-contract.test.ts +0 -273
  106. package/src/execution/__tests__/session-context-resolver.test.ts +0 -167
  107. package/src/execution/__tests__/session-file-gc.test.ts +0 -293
  108. package/src/execution/__tests__/session-pending.test.ts +0 -197
  109. package/src/execution/__tests__/session-reconstructor.test.ts +0 -379
  110. package/src/execution/__tests__/session-runner-epipe.test.ts +0 -178
  111. package/src/execution/__tests__/session-runner-schema-env.test.ts +0 -311
  112. package/src/execution/__tests__/session-start-reaper.test.ts +0 -233
  113. package/src/execution/__tests__/spawn-args.test.ts +0 -434
  114. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +0 -189
  115. package/src/execution/__tests__/spawn-event-adapter.test.ts +0 -167
  116. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +0 -207
  117. package/src/execution/__tests__/spawned-children.test.ts +0 -92
  118. package/src/execution/__tests__/startup-config-declaration.test.ts +0 -35
  119. package/src/execution/__tests__/status-refactor.test.ts +0 -345
  120. package/src/execution/__tests__/stdin-writer.test.ts +0 -463
  121. package/src/execution/__tests__/stream-sink-retirement.test.ts +0 -261
  122. package/src/execution/__tests__/subagent-service-abort.test.ts +0 -60
  123. package/src/execution/__tests__/subagent-service-message-close.test.ts +0 -629
  124. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +0 -180
  125. package/src/execution/__tests__/subagent-service.test.ts +0 -788
  126. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +0 -310
  127. package/src/execution/__tests__/subprocess-agent-runner.test.ts +0 -506
  128. package/src/execution/__tests__/temp-prompt.test.ts +0 -53
  129. package/src/execution/__tests__/timeout-integration.test.ts +0 -397
  130. package/src/execution/__tests__/tombstone-store.test.ts +0 -73
  131. package/src/execution/__tests__/tool-action.test.ts +0 -385
  132. package/src/execution/__tests__/truncline-snapshot.test.ts +0 -81
  133. package/src/execution/__tests__/turn-limiter-semantics.test.ts +0 -194
  134. package/src/execution/__tests__/turn-limiter.test.ts +0 -65
  135. package/src/execution/__tests__/ui-channels.test.ts +0 -187
  136. package/src/execution/__tests__/ui-interaction-model.test.ts +0 -67
  137. package/src/execution/__tests__/ui-request-handler-factory.test.ts +0 -370
  138. package/src/execution/__tests__/ui-request-handler.test.ts +0 -204
  139. package/src/execution/__tests__/ui-request-observability.test.ts +0 -114
  140. package/src/execution/__tests__/ui-request-queue.test.ts +0 -133
  141. package/src/execution/__tests__/worktree-manager.test.ts +0 -633
  142. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +0 -229
  143. package/src/execution/__tests__/worktree-reconcile.integration.test.ts +0 -181
  144. package/src/execution/__tests__/worktree-registry.test.ts +0 -199
  145. package/src/execution/agent-registry.ts +0 -193
  146. package/src/execution/agent-result-mapper.ts +0 -90
  147. package/src/execution/alive-store.ts +0 -92
  148. package/src/execution/argv-mirror.ts +0 -109
  149. package/src/execution/best-effort.ts +0 -38
  150. package/src/execution/channel-registry-access.ts +0 -144
  151. package/src/execution/concurrency-pool.ts +0 -116
  152. package/src/execution/config.ts +0 -96
  153. package/src/execution/dialog-queue.ts +0 -330
  154. package/src/execution/engine/__tests__/common/data-dir.test.ts +0 -53
  155. package/src/execution/engine/__tests__/common/errors.test.ts +0 -132
  156. package/src/execution/engine/__tests__/common/event-journal.test.ts +0 -177
  157. package/src/execution/engine/__tests__/common/kill-chain.test.ts +0 -192
  158. package/src/execution/engine/__tests__/common/nesting-guard.test.ts +0 -81
  159. package/src/execution/engine/__tests__/common/persona-router.test.ts +0 -123
  160. package/src/execution/engine/__tests__/common/pool-manager.test.ts +0 -154
  161. package/src/execution/engine/__tests__/common/schema-emulation.test.ts +0 -128
  162. package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +0 -28
  163. package/src/execution/engine/__tests__/conformance/agent-event-invariants.ts +0 -141
  164. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +0 -109
  165. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +0 -101
  166. package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +0 -77
  167. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +0 -104
  168. package/src/execution/engine/__tests__/conformance/contract.relay.test.ts +0 -342
  169. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +0 -201
  170. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +0 -76
  171. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +0 -79
  172. package/src/execution/engine/__tests__/engine-discovery.test.ts +0 -87
  173. package/src/execution/engine/__tests__/engines-declaration.test.ts +0 -36
  174. package/src/execution/engine/__tests__/model-prompt.test.ts +0 -85
  175. package/src/execution/engine/__tests__/paths.test.ts +0 -39
  176. package/src/execution/engine/__tests__/registry.test.ts +0 -120
  177. package/src/execution/engine/__tests__/routing.test.ts +0 -231
  178. package/src/execution/engine/common/data-dir.ts +0 -62
  179. package/src/execution/engine/common/errors.ts +0 -183
  180. package/src/execution/engine/common/event-journal.ts +0 -254
  181. package/src/execution/engine/common/journal-replay.ts +0 -62
  182. package/src/execution/engine/common/kill-chain.ts +0 -221
  183. package/src/execution/engine/common/nesting-guard.ts +0 -50
  184. package/src/execution/engine/common/persona-router.ts +0 -108
  185. package/src/execution/engine/common/pool-manager.ts +0 -226
  186. package/src/execution/engine/common/schema-emulation.ts +0 -189
  187. package/src/execution/engine/common/session-view-projection.ts +0 -51
  188. package/src/execution/engine/engine-discovery.ts +0 -65
  189. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +0 -469
  190. package/src/execution/engine/engines/pi/__tests__/reader.test.ts +0 -155
  191. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  192. package/src/execution/engine/engines/pi/pi-engine.ts +0 -415
  193. package/src/execution/engine/engines/pi/reader.ts +0 -48
  194. package/src/execution/engine/engines/pi/registration.ts +0 -35
  195. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  196. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  197. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  198. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +0 -246
  199. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +0 -228
  200. package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +0 -210
  201. package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +0 -64
  202. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +0 -127
  203. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -567
  204. package/src/execution/engine/engines/zcode/constants.ts +0 -43
  205. package/src/execution/engine/engines/zcode/golden-sample.ts +0 -39
  206. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  207. package/src/execution/engine/engines/zcode/parser.ts +0 -436
  208. package/src/execution/engine/engines/zcode/preparer.ts +0 -363
  209. package/src/execution/engine/engines/zcode/reader.ts +0 -381
  210. package/src/execution/engine/engines/zcode/registration.ts +0 -37
  211. package/src/execution/engine/engines/zcode/zcode-engine.ts +0 -648
  212. package/src/execution/engine/host-task-spec.ts +0 -47
  213. package/src/execution/engine/model-prompt.ts +0 -59
  214. package/src/execution/engine/paths.ts +0 -42
  215. package/src/execution/engine/port.ts +0 -153
  216. package/src/execution/engine/registry.ts +0 -123
  217. package/src/execution/engine/routing.ts +0 -218
  218. package/src/execution/engine/types.ts +0 -304
  219. package/src/execution/execute-options-mapper.ts +0 -111
  220. package/src/execution/execution-record.ts +0 -906
  221. package/src/execution/finalize-record.ts +0 -251
  222. package/src/execution/finalized-marker.ts +0 -51
  223. package/src/execution/get-state-handshake.ts +0 -104
  224. package/src/execution/host-mode.ts +0 -56
  225. package/src/execution/idle-gc.ts +0 -47
  226. package/src/execution/lifecycle-manager.ts +0 -491
  227. package/src/execution/lifecycle-predicates.ts +0 -65
  228. package/src/execution/manifest-store.ts +0 -256
  229. package/src/execution/model-config-service.ts +0 -204
  230. package/src/execution/model-resolver.ts +0 -276
  231. package/src/execution/notifier.ts +0 -260
  232. package/src/execution/output-collector.ts +0 -88
  233. package/src/execution/path-encoding.ts +0 -60
  234. package/src/execution/pi-invocation.ts +0 -120
  235. package/src/execution/record-entry.ts +0 -132
  236. package/src/execution/record-store.ts +0 -1234
  237. package/src/execution/relay-env.ts +0 -37
  238. package/src/execution/session-context-resolver.ts +0 -64
  239. package/src/execution/session-file-gc.ts +0 -120
  240. package/src/execution/session-pending.ts +0 -164
  241. package/src/execution/session-reconstructor.ts +0 -678
  242. package/src/execution/session-runner.ts +0 -1548
  243. package/src/execution/sessions-index.ts +0 -304
  244. package/src/execution/spawn-event-adapter.ts +0 -363
  245. package/src/execution/stdin-writer.ts +0 -198
  246. package/src/execution/stream-sink.ts +0 -126
  247. package/src/execution/subagent-service.ts +0 -2119
  248. package/src/execution/subprocess-agent-runner.ts +0 -303
  249. package/src/execution/temp-prompt.ts +0 -62
  250. package/src/execution/tombstone-store.ts +0 -72
  251. package/src/execution/turn-limiter.ts +0 -102
  252. package/src/execution/types.ts +0 -910
  253. package/src/execution/ui-channels.ts +0 -216
  254. package/src/execution/ui-interaction-model.ts +0 -48
  255. package/src/execution/ui-request-handler-factory.ts +0 -212
  256. package/src/execution/ui-request-observability.ts +0 -81
  257. package/src/execution/ui-request-queue.ts +0 -174
  258. package/src/execution/worktree-manager.ts +0 -688
  259. package/src/execution/worktree-registry.ts +0 -222
  260. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +0 -336
  261. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +0 -208
  262. package/src/orchestration/__tests__/agent-call-stream.test.ts +0 -157
  263. package/src/orchestration/__tests__/args-validator.test.ts +0 -143
  264. package/src/orchestration/__tests__/concurrency-gate.test.ts +0 -125
  265. package/src/orchestration/__tests__/config-loader.test.ts +0 -503
  266. package/src/orchestration/__tests__/error-recovery-handlers.test.ts +0 -724
  267. package/src/orchestration/__tests__/error-recovery-postmessage-defense.test.ts +0 -405
  268. package/src/orchestration/__tests__/error-recovery-serialize-failed-result.test.ts +0 -56
  269. package/src/orchestration/__tests__/error-recovery-workflow-call.test.ts +0 -166
  270. package/src/orchestration/__tests__/execute-agent-call.test.ts +0 -314
  271. package/src/orchestration/__tests__/jsonl-run-store-loadall-sources.test.ts +0 -171
  272. package/src/orchestration/__tests__/jsonl-run-store-session-file.test.ts +0 -1030
  273. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +0 -277
  274. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +0 -96
  275. package/src/orchestration/__tests__/lifecycle.test.ts +0 -625
  276. package/src/orchestration/__tests__/review-fix-loop-e2e.test.ts +0 -1971
  277. package/src/orchestration/__tests__/script-lint.test.ts +0 -831
  278. package/src/orchestration/__tests__/skill-discovery.test.ts +0 -157
  279. package/src/orchestration/__tests__/test-mocks.ts +0 -197
  280. package/src/orchestration/__tests__/worker-host.test.ts +0 -120
  281. package/src/orchestration/__tests__/worker-returnmeta-passthrough.test.ts +0 -164
  282. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +0 -520
  283. package/src/orchestration/__tests__/worker-script-builder.test.ts +0 -309
  284. package/src/orchestration/__tests__/worker-script-template-snapshot.test.ts +0 -110
  285. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +0 -317
  286. package/src/orchestration/__tests__/workflow-script-lint-memo.test.ts +0 -110
  287. package/src/orchestration/__tests__/workflows-e2e.test.ts +0 -537
  288. package/src/orchestration/agent-opts-resolver.ts +0 -93
  289. package/src/orchestration/args-validator.ts +0 -127
  290. package/src/orchestration/concurrency-gate.ts +0 -69
  291. package/src/orchestration/config-loader.ts +0 -315
  292. package/src/orchestration/error-recovery.ts +0 -862
  293. package/src/orchestration/execute-agent-call.ts +0 -217
  294. package/src/orchestration/launcher.ts +0 -410
  295. package/src/orchestration/lifecycle.ts +0 -396
  296. package/src/orchestration/models/__tests__/budget.test.ts +0 -367
  297. package/src/orchestration/models/__tests__/trace.test.ts +0 -408
  298. package/src/orchestration/models/agent-call.ts +0 -83
  299. package/src/orchestration/models/budget.ts +0 -148
  300. package/src/orchestration/models/ports.ts +0 -164
  301. package/src/orchestration/models/run-runtime.ts +0 -89
  302. package/src/orchestration/models/run-spec.ts +0 -83
  303. package/src/orchestration/models/run-state.ts +0 -44
  304. package/src/orchestration/models/trace.ts +0 -183
  305. package/src/orchestration/models/types.ts +0 -292
  306. package/src/orchestration/models/workflow-run.ts +0 -254
  307. package/src/orchestration/models/workflow-script-registry.ts +0 -35
  308. package/src/orchestration/models/workflow-script.ts +0 -117
  309. package/src/orchestration/script-lint.ts +0 -766
  310. package/src/orchestration/skill-discovery.ts +0 -92
  311. package/src/orchestration/worker-handle.ts +0 -115
  312. package/src/orchestration/worker-host.ts +0 -98
  313. package/src/orchestration/worker-script-builder.ts +0 -408
  314. package/src/orchestration/workflow-files.ts +0 -85
  315. package/src/orchestration/workflow-script-registry-impl.ts +0 -133
  316. package/src/shared/__tests__/agent-ref.test.ts +0 -34
  317. package/src/shared/__tests__/meta-parser.test.ts +0 -304
  318. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -280
  319. package/src/shared/__tests__/resource-discovery.test.ts +0 -736
  320. package/src/shared/__tests__/resource-meta.test.ts +0 -51
  321. package/src/shared/__tests__/schema-jsonify.test.ts +0 -81
  322. package/src/shared/agent-event.ts +0 -13
  323. package/src/shared/agent-ref.ts +0 -57
  324. package/src/shared/meta-parser.ts +0 -261
  325. package/src/shared/resource-discovery.ts +0 -811
  326. package/src/shared/resource-meta.ts +0 -65
  327. package/src/shared/schema-jsonify.ts +0 -56
  328. package/src/shared/xml-injection.ts +0 -35
  329. package/workflows/README.md +0 -81
  330. package/workflows/_shared/agent-refs.cjs +0 -40
  331. package/workflows/chain.js +0 -131
  332. package/workflows/map-reduce.js +0 -174
  333. package/workflows/parallel.js +0 -148
  334. package/workflows/review-fix-loop-utils.cjs +0 -1307
  335. package/workflows/review-fix-loop.js +0 -1396
  336. package/workflows/scatter-gather.js +0 -178
@@ -1,574 +0,0 @@
1
- /**
2
- * BgNotifier flushPendingNotifications — deliverAs 契约(FR-3/AC-3)。
3
- *
4
- * 修复:deliverAs 从 'followUp' 改为 'steer',确保 subagent 完成通知在主 agent
5
- * 处于 processing 状态(如轮询 loop)时也能立即抢占下一个 turn。
6
- *
7
- * 修复背景:commit d214d0d83 已验证 steer 能避免 'Agent is already processing';
8
- * workflow helpers.ts:151 已在同语义下用 steer。
9
- *
10
- * 测试方法:mock NotifierHost,捕获 sendMessage 调用参数,断言 deliverAs === 'steer'。
11
- */
12
-
13
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
14
-
15
- import { createNotifier, type BgNotifier, type NotifierHost } from "../notifier.ts";
16
-
17
- /** mock host:捕获所有 sendMessage 调用 + 控制 hasRunningBackground + isIdle。 */
18
- function makeMockHost(): NotifierHost & {
19
- sendMessageCalls: { message: unknown; options: unknown }[];
20
- hasRunningBackground: ReturnType<typeof vi.fn>;
21
- isIdle: ReturnType<typeof vi.fn>;
22
- } {
23
- const sendMessageCalls: { message: unknown; options: unknown }[] = [];
24
- const hasRunningBackground = vi.fn(() => false);
25
- const isIdle = vi.fn(() => true);
26
- return {
27
- sendMessageCalls,
28
- hasRunningBackground,
29
- isIdle,
30
- sendMessage(message, options) {
31
- sendMessageCalls.push({ message, options });
32
- },
33
- };
34
- }
35
-
36
- describe("BgNotifier.flushPendingNotifications — deliverAs 契约", () => {
37
- let host: ReturnType<typeof makeMockHost>;
38
- let notifier: BgNotifier;
39
-
40
- beforeEach(() => {
41
- host = makeMockHost();
42
- notifier = createNotifier(host);
43
- });
44
-
45
- afterEach(() => {
46
- notifier.dispose();
47
- });
48
-
49
- it("flush 时调 sendMessage 的 options.deliverAs === 'steer'(FR-3/AC-3)", () => {
50
- notifier.notify({
51
- id: "bg-test-1",
52
- status: "closed",
53
- agent: "explorer",
54
- result: "done",
55
- startedAt: Date.now() - 1000,
56
- endedAt: Date.now(),
57
- });
58
-
59
- // hasRunningBackground=false → notify 立即 flush
60
- expect(host.sendMessageCalls).toHaveLength(1);
61
- const call = host.sendMessageCalls[0];
62
- expect(call.options).toMatchObject({ deliverAs: "steer" });
63
- });
64
-
65
- it("flush 时 triggerTurn 也必须为 true(让父 agent 立即唤醒)", () => {
66
- notifier.notify({
67
- id: "bg-test-2",
68
- status: "closed",
69
- agent: "worker",
70
- error: "boom",
71
- startedAt: Date.now(),
72
- endedAt: Date.now(),
73
- });
74
-
75
- expect(host.sendMessageCalls).toHaveLength(1);
76
- expect(host.sendMessageCalls[0].options).toMatchObject({
77
- triggerTurn: true,
78
- deliverAs: "steer",
79
- });
80
- });
81
- });
82
-
83
- describe("BgNotifier — isIdle gate 竞态修复", () => {
84
- let host: ReturnType<typeof makeMockHost>;
85
- let notifier: BgNotifier;
86
-
87
- beforeEach(() => {
88
- vi.useFakeTimers();
89
- host = makeMockHost();
90
- notifier = createNotifier(host);
91
- });
92
-
93
- afterEach(() => {
94
- notifier.dispose();
95
- vi.useRealTimers();
96
- });
97
-
98
- it("主 agent busy 时 flush 退避,idle 后才 sendMessage(规避 agent_end→finishRun 竞态窗口)", () => {
99
- // 模拟竞态:notify 时主 agent 仍 streaming(isIdle=false)
100
- host.isIdle.mockReturnValue(false);
101
- notifier.notify({
102
- id: "bg-race-1",
103
- status: "closed",
104
- agent: "worker",
105
- result: "ok",
106
- startedAt: Date.now(),
107
- endedAt: Date.now(),
108
- });
109
-
110
- // busy 退避:未发送
111
- expect(host.sendMessageCalls).toHaveLength(0);
112
- expect(host.isIdle).toHaveBeenCalled();
113
-
114
- // 推进 1 个退避间隔(100ms)——仍 busy,继续退避
115
- vi.advanceTimersByTime(100);
116
- expect(host.sendMessageCalls).toHaveLength(0);
117
-
118
- // 主 agent 变 idle
119
- host.isIdle.mockReturnValue(true);
120
- vi.advanceTimersByTime(100);
121
-
122
- // idle 后发送,deliverAs=steer + triggerTurn=true
123
- expect(host.sendMessageCalls).toHaveLength(1);
124
- expect(host.sendMessageCalls[0].options).toMatchObject({
125
- triggerTurn: true,
126
- deliverAs: "steer",
127
- });
128
- });
129
-
130
- it("主 agent 持续 busy 达退避上限后强制发送(防通知饿死)", () => {
131
- host.isIdle.mockReturnValue(false);
132
- notifier.notify({
133
- id: "bg-starve-1",
134
- status: "closed",
135
- agent: "worker",
136
- result: "ok",
137
- startedAt: Date.now(),
138
- endedAt: Date.now(),
139
- });
140
-
141
- expect(host.sendMessageCalls).toHaveLength(0);
142
- // 推进超过退避上限(50 × 100ms = 5s)
143
- vi.advanceTimersByTime(10_000);
144
-
145
- // 达上限后 fallthrough 强制发送(至少不丢消息)
146
- expect(host.sendMessageCalls).toHaveLength(1);
147
- });
148
-
149
- it("未注入 isIdle 时不 gate,保持原立即发送行为(向后兼容)", () => {
150
- // 重建无 isIdle 的 host(模拟旧调用方/测试 host)
151
- const legacyHost: NotifierHost = {
152
- sendMessage: (message, options) => host.sendMessage(message, options),
153
- hasRunningBackground: () => false,
154
- };
155
- const legacyNotifier = createNotifier(legacyHost);
156
- legacyNotifier.notify({
157
- id: "bg-legacy-1",
158
- status: "closed",
159
- agent: "worker",
160
- result: "ok",
161
- startedAt: Date.now(),
162
- endedAt: Date.now(),
163
- });
164
-
165
- // 无 isIdle gate → 立即发送
166
- expect(host.sendMessageCalls).toHaveLength(1);
167
- legacyNotifier.dispose();
168
- });
169
-
170
- it("dispose 后退避 timer 不再触发发送", () => {
171
- host.isIdle.mockReturnValue(false);
172
- notifier.notify({
173
- id: "bg-dispose-1",
174
- status: "closed",
175
- agent: "worker",
176
- result: "ok",
177
- startedAt: Date.now(),
178
- endedAt: Date.now(),
179
- });
180
-
181
- notifier.dispose();
182
- // 推进足够久,退避 timer 若未清会触发
183
- vi.advanceTimersByTime(10_000);
184
- expect(host.sendMessageCalls).toHaveLength(0);
185
- });
186
- });
187
-
188
- describe("BgNotifier dedup 按轮次(G1 决策 9:对话模式豁免 60s dedup)", () => {
189
- let host: ReturnType<typeof makeMockHost>;
190
- let notifier: BgNotifier;
191
-
192
- beforeEach(() => {
193
- host = makeMockHost();
194
- // hasRunningBackground=false → notify 立即 flush(不排队);dedup 仍在 push 前生效
195
- notifier = createNotifier(host);
196
- });
197
-
198
- afterEach(() => {
199
- notifier.dispose();
200
- });
201
-
202
- it("对话模式:同 id 不同 round 的两次 notify 不互相吞(round 参与 dedup key)", () => {
203
- notifier.notify({
204
- id: "sa-chat", status: "idle", agent: "w", round: 1, result: "round1",
205
- startedAt: 1, endedAt: 2,
206
- });
207
- notifier.notify({
208
- id: "sa-chat", status: "idle", agent: "w", round: 2, result: "round2",
209
- startedAt: 3, endedAt: 4,
210
- });
211
-
212
- // MF-1 修复:dedup key=id:round,不同 round 不互相吞
213
- expect(host.sendMessageCalls).toHaveLength(2);
214
- });
215
-
216
- it("同 id 同 round 60s 内第二次被 dedup 吞(防重复通知)", () => {
217
- notifier.notify({
218
- id: "sa-dup", status: "idle", agent: "w", round: 1, result: "r",
219
- startedAt: 1, endedAt: 2,
220
- });
221
- notifier.notify({
222
- id: "sa-dup", status: "idle", agent: "w", round: 1, result: "r",
223
- startedAt: 3, endedAt: 4,
224
- });
225
-
226
- // 第二条被吞(dedup key=sa-dup:1 命中)
227
- expect(host.sendMessageCalls).toHaveLength(1);
228
- });
229
-
230
- it("非 chatMode(round undefined → key=id:0)行为同旧(向后兼容,60s 内同 id 吞)", () => {
231
- // round undefined → dedup key="sa-once:0",与旧 record.id 单 key 行为一致
232
- notifier.notify({
233
- id: "sa-once", status: "closed", agent: "w", result: "done",
234
- startedAt: 1, endedAt: 2,
235
- });
236
- notifier.notify({
237
- id: "sa-once", status: "closed", agent: "w", result: "done",
238
- startedAt: 3, endedAt: 4,
239
- });
240
-
241
- // 第二条被吞(旧 dedup 行为不变,一次性模式回归)
242
- expect(host.sendMessageCalls).toHaveLength(1);
243
- });
244
- });
245
-
246
- describe("BgNotifier buildLlmContent 指针行(wave2:chatMode sessionFile 透传)", () => {
247
- let host: ReturnType<typeof makeMockHost>;
248
- let notifier: BgNotifier;
249
-
250
- beforeEach(() => {
251
- host = makeMockHost();
252
- // hasRunningBackground=false → notify 立即 flush,sendMessageCalls 恰 1 条
253
- notifier = createNotifier(host);
254
- });
255
-
256
- afterEach(() => {
257
- notifier.dispose();
258
- });
259
-
260
- /** 取唯一一条已发消息的 content(前置断言恰 1 条)。 */
261
- function sentContent(): string {
262
- expect(host.sendMessageCalls).toHaveLength(1);
263
- return (host.sendMessageCalls[0]!.message as { content: string }).content;
264
- }
265
-
266
- it("running(轮次通知)+ sessionFile → 末尾追加 \\n\\n 空行分隔的指针行", () => {
267
- notifier.notify({
268
- id: "sa-ptr-1", status: "running", agent: "w", round: 1, result: "round1 text",
269
- sessionFile: "/tmp/sessions/child-1.jsonl",
270
- startedAt: 1, endedAt: 2,
271
- });
272
-
273
- expect(sentContent()).toBe(
274
- 'Subagent "w" (sa-ptr-1) finished a round. Reply:\nround1 text' +
275
- "\n\nFull transcript: /tmp/sessions/child-1.jsonl",
276
- );
277
- });
278
-
279
- it("closed 成功文案 + sessionFile → 指针行追加在最终串末尾", () => {
280
- notifier.notify({
281
- id: "sa-ptr-2", status: "closed", agent: "w", result: "final result",
282
- sessionFile: "/tmp/sessions/child-2.jsonl",
283
- startedAt: 1, endedAt: 2,
284
- });
285
-
286
- expect(sentContent()).toBe(
287
- 'Subagent "w" (sa-ptr-2) completed. Result:\nfinal result' +
288
- "\n\nFull transcript: /tmp/sessions/child-2.jsonl",
289
- );
290
- });
291
-
292
- it("closed + patchFile + sessionFile → 指针行追加在 patch 提示串末尾(最终串)", () => {
293
- notifier.notify({
294
- id: "sa-ptr-3", status: "closed", agent: "w", result: "did work",
295
- patchFile: "/tmp/patches/sa-ptr-3.patch",
296
- sessionFile: "/tmp/sessions/child-3.jsonl",
297
- startedAt: 1, endedAt: 2,
298
- });
299
-
300
- const content = sentContent();
301
- expect(content).toContain("git apply /tmp/patches/sa-ptr-3.patch");
302
- expect(content.endsWith("\n\nFull transcript: /tmp/sessions/child-3.jsonl")).toBe(true);
303
- });
304
-
305
- it("sessionFile 缺失 → 省略整行(running 通知正文与无指针形态逐字节一致)", () => {
306
- notifier.notify({
307
- id: "sa-ptr-4", status: "running", agent: "w", round: 2, result: "round2 text",
308
- startedAt: 1, endedAt: 2,
309
- });
310
-
311
- expect(sentContent()).toBe('Subagent "w" (sa-ptr-4) finished a round. Reply:\nround2 text');
312
- });
313
-
314
- it("cancelled → 不追加指针行(即使 sessionFile 有值)", () => {
315
- notifier.notify({
316
- id: "sa-ptr-5", status: "closed", closedReason: "cancelled", agent: "w",
317
- sessionFile: "/tmp/sessions/child-5.jsonl",
318
- startedAt: 1, endedAt: 2,
319
- });
320
-
321
- expect(sentContent()).toBe('Subagent "w" (sa-ptr-5) cancelled.');
322
- });
323
-
324
- it("gc-failed(closed + closedReason=gc + error 有值)→ 不追加指针行", () => {
325
- notifier.notify({
326
- id: "sa-ptr-6", status: "closed", closedReason: "gc", agent: "w",
327
- error: "spawn EPIPE",
328
- sessionFile: "/tmp/sessions/child-6.jsonl",
329
- startedAt: 1, endedAt: 2,
330
- });
331
-
332
- expect(sentContent()).toBe('Subagent "w" (sa-ptr-6) failed: spawn EPIPE');
333
- });
334
-
335
- it("[review 修复] gc-failed + patchFile 并存 → failed 文案优先(失败轮也会写 patchFile,patch 提示不可达)", () => {
336
- // 回归锚定:doFinalizeRecord Step 0 对 worktreeHandle 无条件 collectPatch,gc 失败 +
337
- // worktree 并存时 patchFile 有值。判定顺序必须与 deriveClosedDisplay / renderRecordLines
338
- // 同构(cancelled → gc+error → patch/result),否则 LLM 被告知 completed 掩盖失败。
339
- notifier.notify({
340
- id: "sa-ptr-8", status: "closed", closedReason: "gc", agent: "w",
341
- error: "spawn EPIPE",
342
- patchFile: "/tmp/patches/sa-ptr-8.patch",
343
- sessionFile: "/tmp/sessions/child-8.jsonl",
344
- startedAt: 1, endedAt: 2,
345
- });
346
-
347
- expect(sentContent()).toBe('Subagent "w" (sa-ptr-8) failed: spawn EPIPE');
348
- });
349
-
350
- it("one-shot(sessionFile 未透传 → undefined)→ closed 通知与改造前逐字节一致(基线常量锚定)", () => {
351
- notifier.notify({
352
- id: "sa-ptr-7", status: "closed", agent: "worker", result: "done",
353
- startedAt: 1, endedAt: 2,
354
- });
355
-
356
- // 改造前形态:sessionFile undefined 时指针为空串,追加不改变输出——逐字节锁定
357
- expect(sentContent()).toBe('Subagent "worker" (sa-ptr-7) completed. Result:\ndone');
358
- });
359
-
360
- it("[C-2] closed + totalRounds(chatMode close 终态通知)→ 文案附轮次统计 after N rounds", () => {
361
- notifier.notify({
362
- id: "sa-rounds-1", status: "closed", agent: "w", totalRounds: 3, result: "",
363
- sessionFile: "/tmp/sessions/child-rounds.jsonl",
364
- startedAt: 1, endedAt: 2,
365
- });
366
-
367
- // 设计 D2 路径①:closed 分支文案附轮次统计 + sessionFile 提示;
368
- // 路径②正文空串(idle close)形态也走本分支(result 空串非 nullish,不触发 (empty))
369
- expect(sentContent()).toBe(
370
- 'Subagent "w" (sa-rounds-1) completed after 3 rounds. Result:\n' +
371
- "\n\nFull transcript: /tmp/sessions/child-rounds.jsonl",
372
- );
373
- });
374
-
375
- it("[C-2] 对照:totalRounds 缺失(one-shot 完成通知)→ 无轮次统计,文案逐字节保持 completed. Result:", () => {
376
- notifier.notify({
377
- id: "sa-rounds-2", status: "closed", agent: "w", result: "done",
378
- sessionFile: "/tmp/sessions/child-oneshot.jsonl",
379
- startedAt: 1, endedAt: 2,
380
- });
381
-
382
- // one-shot record 无轮次语义(totalRounds 不设置)——G4:文案不含统计
383
- expect(sentContent()).toBe(
384
- 'Subagent "w" (sa-rounds-2) completed. Result:\ndone' +
385
- "\n\nFull transcript: /tmp/sessions/child-oneshot.jsonl",
386
- );
387
- });
388
- });
389
-
390
- describe("BgNotifier — subscribeSettled 装配(must-fix #4 / D8 settled 边沿驱动)", () => {
391
- let host: ReturnType<typeof makeMockHost>;
392
- let notifier: BgNotifier;
393
- /** 内核经 port.subscribeSettled 注册的 settled 边沿回调(测试手动触发模拟 pi 事件)。 */
394
- let settledEdges: Array<() => void>;
395
-
396
- beforeEach(() => {
397
- vi.useFakeTimers();
398
- host = makeMockHost();
399
- settledEdges = [];
400
- // host 注入原生订阅能力;notifier port 内部用 disposed 标志包装(D8 适配)
401
- host.onAgentSettled = (handler) => {
402
- settledEdges.push(handler);
403
- };
404
- notifier = createNotifier(host);
405
- });
406
-
407
- afterEach(() => {
408
- notifier.dispose();
409
- vi.useRealTimers();
410
- });
411
-
412
- it("busy 入队 → settled 事件触发 → flush 送达(不依赖退避轮询)", () => {
413
- host.isIdle.mockReturnValue(false);
414
- notifier.notify({
415
- id: "bg-settled-1",
416
- status: "closed",
417
- agent: "worker",
418
- result: "ok",
419
- startedAt: Date.now(),
420
- endedAt: Date.now(),
421
- });
422
-
423
- // busy 入队:内核装配了 subscribeSettled → settled 边沿驱动,注册过订阅
424
- expect(host.sendMessageCalls).toHaveLength(0);
425
- expect(settledEdges.length).toBeGreaterThanOrEqual(1);
426
-
427
- // 有订阅装配下内核不走退避强发——推进远超退避上限(50 × 100ms)仍不发送
428
- vi.advanceTimersByTime(10_000);
429
- expect(host.sendMessageCalls).toHaveLength(0);
430
-
431
- // settled 边沿(isIdle 已先于事件复位,agent-session.js:327-336)→ flush 送达
432
- host.isIdle.mockReturnValue(true);
433
- settledEdges[0]!();
434
-
435
- expect(host.sendMessageCalls).toHaveLength(1);
436
- expect(host.sendMessageCalls[0]!.options).toMatchObject({
437
- triggerTurn: true,
438
- deliverAs: "steer",
439
- });
440
- });
441
-
442
- it("dispose 后 settled 边沿不再触发发送(内核 disposed 拦截)", () => {
443
- host.isIdle.mockReturnValue(false);
444
- notifier.notify({
445
- id: "bg-settled-2",
446
- status: "closed",
447
- agent: "worker",
448
- result: "ok",
449
- startedAt: Date.now(),
450
- endedAt: Date.now(),
451
- });
452
- notifier.dispose();
453
-
454
- host.isIdle.mockReturnValue(true);
455
- settledEdges[0]!();
456
-
457
- expect(host.sendMessageCalls).toHaveLength(0);
458
- });
459
- });
460
-
461
- describe("BgNotifier — revive 重建内核 handle(must-fix #5)", () => {
462
- let host: ReturnType<typeof makeMockHost>;
463
- let notifier: BgNotifier;
464
-
465
- beforeEach(() => {
466
- host = makeMockHost();
467
- notifier = createNotifier(host);
468
- });
469
-
470
- afterEach(() => {
471
- notifier.dispose();
472
- });
473
-
474
- it("dispose → notify 静默丢弃 → revive → notify 恢复送达", () => {
475
- notifier.notify({
476
- id: "bg-revive-1",
477
- status: "closed",
478
- agent: "worker",
479
- result: "ok",
480
- startedAt: 1,
481
- endedAt: 2,
482
- });
483
- expect(host.sendMessageCalls).toHaveLength(1);
484
-
485
- // session_shutdown:dispose(内核 handle 销毁,disposed 不可逆)
486
- notifier.dispose();
487
-
488
- // dispose 窗口内的 notify:外层 disposed 短路,静默丢弃(旧有语义)
489
- notifier.notify({
490
- id: "bg-revive-2",
491
- status: "closed",
492
- agent: "worker",
493
- result: "dropped",
494
- startedAt: 3,
495
- endedAt: 4,
496
- });
497
- expect(host.sendMessageCalls).toHaveLength(1);
498
-
499
- // /resume /fork /new 后的 revive:必须重建内核 handle——旧实现仅复位外层标志,
500
- // 内核已 dispose,此后所有 notify 被内核静默吞(通知永久丢失)
501
- notifier.revive();
502
- notifier.notify({
503
- id: "bg-revive-3",
504
- status: "closed",
505
- agent: "worker",
506
- result: "alive again",
507
- startedAt: 5,
508
- endedAt: 6,
509
- });
510
-
511
- expect(host.sendMessageCalls).toHaveLength(2);
512
- const msg = host.sendMessageCalls[1]!.message as { content: string };
513
- expect(msg.content).toContain("bg-revive-3");
514
- });
515
-
516
- it("revive 后同 id 重复通知不被旧生命周期的 dedup LRU 吞(新 handle 状态复位)", () => {
517
- notifier.notify({ id: "bg-revive-dup", status: "closed", agent: "w", result: "r1", startedAt: 1, endedAt: 2 });
518
- notifier.dispose();
519
- notifier.revive();
520
- // 同 id 第二次:旧 handle 的 dedup LRU 已随 dispose 清空 + 重建,不被吞
521
- notifier.notify({ id: "bg-revive-dup", status: "closed", agent: "w", result: "r2", startedAt: 3, endedAt: 4 });
522
- expect(host.sendMessageCalls).toHaveLength(2);
523
- });
524
- });
525
-
526
- describe("U3_UNIT: createNotifier unit verification", () => {
527
- it("createNotifier returns object with notify/flush/dispose/revive", () => {
528
- const host = makeMockHost();
529
- const notifier = createNotifier(host);
530
- expect(typeof notifier.notify).toBe("function");
531
- expect(typeof notifier.flushPendingNotifications).toBe("function");
532
- expect(typeof notifier.dispose).toBe("function");
533
- expect(typeof notifier.revive).toBe("function");
534
- notifier.dispose();
535
- });
536
-
537
- it("notify calls host.sendMessage with customType=subagent-bg-notify", () => {
538
- const host = makeMockHost();
539
- const notifier = createNotifier(host);
540
- notifier.notify({
541
- id: "u3-test-1", status: "closed", agent: "worker", result: "ok",
542
- startedAt: 1, endedAt: 2,
543
- });
544
- expect(host.sendMessageCalls).toHaveLength(1);
545
- const msg = host.sendMessageCalls[0]!.message as { customType: string };
546
- expect(msg.customType).toBe("subagent-bg-notify");
547
- notifier.dispose();
548
- });
549
-
550
- it("notify with mergeHoldActive=true defers flush until explicit call", () => {
551
- const sendMessageCalls: unknown[] = [];
552
- const host: NotifierHost = {
553
- sendMessage: (msg) => { sendMessageCalls.push(msg); },
554
- hasRunningBackground: () => true,
555
- isIdle: () => true,
556
- };
557
- const notifier = createNotifier(host);
558
- notifier.notify({
559
- id: "u3-merge-1", status: "closed", agent: "w", result: "r1",
560
- startedAt: 1, endedAt: 2,
561
- });
562
- notifier.notify({
563
- id: "u3-merge-2", status: "closed", agent: "w", result: "r2",
564
- startedAt: 3, endedAt: 4,
565
- });
566
- // mergeHoldActive=true → messages queued, not sent yet
567
- expect(sendMessageCalls).toHaveLength(0);
568
- notifier.flushPendingNotifications();
569
- expect(sendMessageCalls).toHaveLength(1);
570
- const content = (sendMessageCalls[0] as { content: string }).content;
571
- expect(content).toContain("\n\n---\n\n");
572
- notifier.dispose();
573
- });
574
- });