@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,370 +0,0 @@
1
- // src/__tests__/ui-request-handler-factory.test.ts
2
- //
3
- // C1 测试:ui-request-handler-factory.ts — createUiRequestHandlerForMode 透传矩阵。
4
- //
5
- // 透传矩阵(createUiRequestHandlerForMode 返回的 handler 行为):
6
- // - headless(json/print/undefined):返回 undefined(不注入 handler)
7
- // - TUI:fire-and-forget 回 ack 不透传;dialog 进 dialogQueue 串行
8
- // - GUI(rpc):fire-and-forget 直接调 realHandler;dialog 进 dialogQueue 串行
9
- // realHandler 路由:channel 命中 → channelHandler(经 coerceUiResponse 形变);未命中 → defaultDialogForward(dialog 转发结果,fire-and-forget 转发 ctx.ui.* 后回 ack,未知 method warn + ack)。
10
- // 测接口契约,不测实现细节。
11
-
12
- import type { ExtensionContext, ExtensionMode } from "@earendil-works/pi-coding-agent";
13
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
14
-
15
- const { loggerMock } = vi.hoisted(() => ({
16
- loggerMock: {
17
- debug: vi.fn(),
18
- warn: vi.fn(),
19
- error: vi.fn(),
20
- info: vi.fn(),
21
- },
22
- }));
23
- vi.mock("@zhushanwen/pi-extension-logger", () => ({
24
- getLogger: () => loggerMock,
25
- }));
26
-
27
- import { DialogGlobalQueue, type UiRequest } from "../dialog-queue.ts";
28
- import { type ChannelHandler,createUiChannelRegistry } from "../ui-channels.ts";
29
- import { createUiRequestHandlerForMode } from "../ui-request-handler-factory.ts";
30
-
31
- // mock ExtensionContext 已补 mode 字段(host-mode.ts 读它分流)。最小形状构造。
32
- function makeCtx(mode: ExtensionMode): ExtensionContext {
33
- return {
34
- cwd: "/tmp/test",
35
- mode,
36
- sessionManager: {
37
- getSessionId: () => "s1",
38
- getSessionFile: () => undefined,
39
- getSessionDir: () => "/tmp/test/sessions",
40
- },
41
- modelRegistry: undefined,
42
- model: undefined,
43
- } as ExtensionContext;
44
- }
45
-
46
- /** 带 mock ctx.ui 的 ExtensionContext(GUI fire-and-forget 转发测试用)。
47
- * dialog method(select/confirm/input/editor)返回 undefined/true/"" 兜底,
48
- * fire-and-forget method(notify/setStatus/setWidget/setTitle/setEditorText)是 void spy。 */
49
- function makeCtxWithUi(mode: ExtensionMode = "rpc"): ExtensionContext & { ui: Record<string, ReturnType<typeof vi.fn>> } {
50
- const ui: Record<string, ReturnType<typeof vi.fn>> = {
51
- select: vi.fn(async () => undefined),
52
- confirm: vi.fn(async () => true),
53
- input: vi.fn(async () => ""),
54
- editor: vi.fn(async () => ""),
55
- notify: vi.fn(),
56
- setStatus: vi.fn(),
57
- setWidget: vi.fn(),
58
- setTitle: vi.fn(),
59
- setEditorText: vi.fn(),
60
- };
61
- return {
62
- cwd: "/tmp/test",
63
- mode,
64
- sessionManager: {
65
- getSessionId: () => "s1",
66
- getSessionFile: () => undefined,
67
- getSessionDir: () => "/tmp/test/sessions",
68
- },
69
- modelRegistry: undefined,
70
- model: undefined,
71
- ui,
72
- } as unknown as ExtensionContext & { ui: Record<string, ReturnType<typeof vi.fn>> };
73
- }
74
-
75
- function dialogReq(id: string, channel?: string): UiRequest {
76
- return { method: "select", id, title: `q-${id}`, ...(channel ? { channel } : {}) };
77
- }
78
-
79
- function fireAndForgetReq(id: string): UiRequest {
80
- return { method: "notify", id, message: `n-${id}` };
81
- }
82
-
83
- // dialog 路径用 fake timers 推进 processNext;静默 console.warn/error(stub 故意 warn)。
84
- beforeEach(() => {
85
- vi.useFakeTimers();
86
- vi.spyOn(console, "warn").mockImplementation(() => {});
87
- vi.spyOn(console, "error").mockImplementation(() => {});
88
- loggerMock.warn.mockClear();
89
- });
90
-
91
- afterEach(() => {
92
- vi.useRealTimers();
93
- vi.restoreAllMocks();
94
- });
95
-
96
- describe("createUiRequestHandlerForMode — headless 返回 undefined", () => {
97
- it("mode='json' → undefined(不注入 handler)", () => {
98
- const queue = new DialogGlobalQueue();
99
- expect(createUiRequestHandlerForMode(makeCtx("json"), createUiChannelRegistry(), queue))
100
- .toBeUndefined();
101
- });
102
-
103
- it("mode='print' → undefined", () => {
104
- const queue = new DialogGlobalQueue();
105
- expect(createUiRequestHandlerForMode(makeCtx("print"), createUiChannelRegistry(), queue))
106
- .toBeUndefined();
107
- });
108
- });
109
-
110
- describe("createUiRequestHandlerForMode — TUI 模式透传", () => {
111
- it("fire-and-forget(notify)→ {ack:true},不调 realHandler / 不入队", async () => {
112
- const registry = createUiChannelRegistry();
113
- const queue = new DialogGlobalQueue();
114
- const enqueueSpy = vi.spyOn(queue, "enqueue");
115
-
116
- const handler = createUiRequestHandlerForMode(makeCtx("tui"), registry, queue)!;
117
- const resp = await handler(fireAndForgetReq("f1"));
118
-
119
- expect(resp).toEqual({ ack: true });
120
- expect(console.warn).not.toHaveBeenCalled(); // realHandler(defaultDialogForward)未走
121
- expect(enqueueSpy).not.toHaveBeenCalled();
122
- });
123
-
124
- it("dialog(select 无 channel)→ 进 dialogQueue(enqueue 被调,defaultDialogForward stub cancelled)", async () => {
125
- const registry = createUiChannelRegistry();
126
- const queue = new DialogGlobalQueue();
127
- const enqueueSpy = vi.spyOn(queue, "enqueue");
128
-
129
- const handler = createUiRequestHandlerForMode(makeCtx("tui"), registry, queue)!;
130
- const pending = handler(dialogReq("d1"));
131
-
132
- expect(enqueueSpy).toHaveBeenCalledTimes(1);
133
- await vi.advanceTimersByTimeAsync(0);
134
- await expect(pending).resolves.toEqual({ cancelled: true });
135
- });
136
- });
137
-
138
- describe("createUiRequestHandlerForMode — GUI(rpc)模式透传", () => {
139
- it("fire-and-forget(notify)→ 直接调 realHandler,不入队", async () => {
140
- const registry = createUiChannelRegistry();
141
- const queue = new DialogGlobalQueue();
142
- const enqueueSpy = vi.spyOn(queue, "enqueue");
143
-
144
- const ctx = makeCtxWithUi("rpc");
145
- const handler = createUiRequestHandlerForMode(ctx, registry, queue)!;
146
- // notify 无 channel → realHandler → defaultDialogForward → case "notify" → {ack:true}
147
- const resp = await handler(fireAndForgetReq("f1"));
148
-
149
- expect(enqueueSpy).not.toHaveBeenCalled();
150
- expect(resp).toEqual({ ack: true });
151
- expect(ctx.ui.notify).toHaveBeenCalledWith("n-f1", "info");
152
- });
153
-
154
- it("dialog(select 无 channel)→ 进 dialogQueue", async () => {
155
- const registry = createUiChannelRegistry();
156
- const queue = new DialogGlobalQueue();
157
- const enqueueSpy = vi.spyOn(queue, "enqueue");
158
-
159
- const handler = createUiRequestHandlerForMode(makeCtx("rpc"), registry, queue)!;
160
- const pending = handler(dialogReq("d1"));
161
-
162
- expect(enqueueSpy).toHaveBeenCalledTimes(1);
163
- await vi.advanceTimersByTimeAsync(0);
164
- await expect(pending).resolves.toEqual({ cancelled: true });
165
- });
166
- });
167
-
168
- describe("createUiRequestHandlerForMode — channel 业务路由", () => {
169
- it("channel 命中 registry → 调注册的 channelHandler(不走 defaultDialogForward)", async () => {
170
- const registry = createUiChannelRegistry();
171
- const channelHandler: ChannelHandler = vi.fn(async () => ({ value: "from-channel" }));
172
- registry.register("ask_user", channelHandler);
173
-
174
- // GUI fire-and-forget 直接调 realHandler,绕过队列;channel 命中立即生效
175
- const handler = createUiRequestHandlerForMode(makeCtx("rpc"), registry, new DialogGlobalQueue())!;
176
- const resp = await handler({ method: "notify", id: "f1", message: "m", channel: "ask_user" });
177
-
178
- expect(channelHandler).toHaveBeenCalledTimes(1);
179
- expect(resp).toEqual({ value: "from-channel" });
180
- });
181
-
182
- it("channel 未命中 → defaultDialogForward(fire-and-forget 走 ack)", async () => {
183
- const ctx = makeCtxWithUi("rpc");
184
- const handler = createUiRequestHandlerForMode(
185
- ctx, createUiChannelRegistry(), new DialogGlobalQueue())!;
186
- const resp = await handler({ method: "notify", id: "f1", message: "m", channel: "unknown" });
187
- // notify 是 fire-and-forget,channel miss 后走 defaultDialogForward 的 notify case → ack
188
- expect(resp).toEqual({ ack: true });
189
- expect(ctx.ui.notify).toHaveBeenCalledWith("m", "info");
190
- });
191
- });
192
-
193
- // coerceUiResponse 形变(通过 channelHandler 返回不同 shape 间接测)
194
- describe("createUiRequestHandlerForMode — coerceUiResponse 形变", () => {
195
- async function callWithChannel(raw: unknown) {
196
- const registry = createUiChannelRegistry();
197
- registry.register("ask_user", (async () => raw) as ChannelHandler);
198
- const handler = createUiRequestHandlerForMode(makeCtx("rpc"), registry, new DialogGlobalQueue())!;
199
- return handler({ method: "notify", id: "f1", message: "m", channel: "ask_user" });
200
- }
201
-
202
- it("channelHandler 返回 {value:'x'} → {value:'x'}", async () => {
203
- expect(await callWithChannel({ value: "x" })).toEqual({ value: "x" });
204
- });
205
-
206
- it("channelHandler 返回 {confirmed:true} → {confirmed:true}", async () => {
207
- expect(await callWithChannel({ confirmed: true })).toEqual({ confirmed: true });
208
- });
209
-
210
- it("channelHandler 返回 null(非法)→ 降级 {cancelled:true}", async () => {
211
- expect(await callWithChannel(null)).toEqual({ cancelled: true });
212
- });
213
- });
214
-
215
- // ── P1:GUI fire-and-forget 分类转发(§3.2 映射表 + §3.7 D1/D2) ──
216
- describe("defaultDialogForward — fire-and-forget 分类转发", () => {
217
- function makeHandler(ctx: ExtensionContext) {
218
- return createUiRequestHandlerForMode(
219
- ctx, createUiChannelRegistry(), new DialogGlobalQueue())!;
220
- }
221
-
222
- // (a) 五个 case 转发调用与 ack 返回
223
- it("notify → ctx.ui.notify + {ack:true}", async () => {
224
- const ctx = makeCtxWithUi();
225
- const handler = makeHandler(ctx);
226
- const resp = await handler({ method: "notify", id: "n1", message: "hello" });
227
- expect(resp).toEqual({ ack: true });
228
- expect(ctx.ui.notify).toHaveBeenCalledTimes(1);
229
- expect(ctx.ui.notify).toHaveBeenCalledWith("hello", "info");
230
- });
231
-
232
- it("setStatus → ctx.ui.setStatus + {ack:true}", async () => {
233
- const ctx = makeCtxWithUi();
234
- const handler = makeHandler(ctx);
235
- const resp = await handler({ method: "setStatus", id: "s1", statusKey: "progress", statusText: "50%" });
236
- expect(resp).toEqual({ ack: true });
237
- expect(ctx.ui.setStatus).toHaveBeenCalledTimes(1);
238
- expect(ctx.ui.setStatus).toHaveBeenCalledWith("progress", "50%");
239
- });
240
-
241
- it("setWidget(channel=undefined)→ ctx.ui.setWidget 含 placement + {ack:true}", async () => {
242
- const ctx = makeCtxWithUi();
243
- const handler = makeHandler(ctx);
244
- const resp = await handler({
245
- method: "setWidget", id: "w1",
246
- widgetKey: "my-widget", widgetLines: ["line1", "line2"],
247
- widgetPlacement: "belowEditor",
248
- });
249
- expect(resp).toEqual({ ack: true });
250
- expect(ctx.ui.setWidget).toHaveBeenCalledTimes(1);
251
- expect(ctx.ui.setWidget).toHaveBeenCalledWith("my-widget", ["line1", "line2"], { placement: "belowEditor" });
252
- });
253
-
254
- it("setTitle → ctx.ui.setTitle + {ack:true}", async () => {
255
- const ctx = makeCtxWithUi();
256
- const handler = makeHandler(ctx);
257
- const resp = await handler({ method: "setTitle", id: "t1", title: "My Title" });
258
- expect(resp).toEqual({ ack: true });
259
- expect(ctx.ui.setTitle).toHaveBeenCalledTimes(1);
260
- expect(ctx.ui.setTitle).toHaveBeenCalledWith("My Title");
261
- });
262
-
263
- it("set_editor_text → ctx.ui.setEditorText + {ack:true}", async () => {
264
- const ctx = makeCtxWithUi();
265
- const handler = makeHandler(ctx);
266
- const resp = await handler({ method: "set_editor_text", id: "e1", text: "some code" });
267
- expect(resp).toEqual({ ack: true });
268
- expect(ctx.ui.setEditorText).toHaveBeenCalledTimes(1);
269
- expect(ctx.ui.setEditorText).toHaveBeenCalledWith("some code");
270
- });
271
-
272
- // (b) notifyType 收窄三档 + 非法值 fallback info
273
- it.each([
274
- ["info", "info"],
275
- ["warning", "warning"],
276
- ["error", "error"],
277
- ] as const)("notifyType='%s' → 透传 '%s'", async (input, expected) => {
278
- const ctx = makeCtxWithUi();
279
- const handler = makeHandler(ctx);
280
- await handler({ method: "notify", id: "n1", message: "m", notifyType: input });
281
- expect(ctx.ui.notify).toHaveBeenCalledWith("m", expected);
282
- });
283
-
284
- it("notifyType 非法值 → fallback 'info'", async () => {
285
- const ctx = makeCtxWithUi();
286
- const handler = makeHandler(ctx);
287
- await handler({ method: "notify", id: "n1", message: "m", notifyType: "debug" });
288
- expect(ctx.ui.notify).toHaveBeenCalledWith("m", "info");
289
- });
290
-
291
- it("notifyType undefined → fallback 'info'", async () => {
292
- const ctx = makeCtxWithUi();
293
- const handler = makeHandler(ctx);
294
- await handler({ method: "notify", id: "n1", message: "m" });
295
- expect(ctx.ui.notify).toHaveBeenCalledWith("m", "info");
296
- });
297
-
298
- // (c) setWidget 两分支
299
- it("setWidget channel='gui_widget' → 不调 ctx.ui.setWidget,回 {ack:true}", async () => {
300
- const ctx = makeCtxWithUi();
301
- const handler = makeHandler(ctx);
302
- const resp = await handler({
303
- method: "setWidget", id: "w1",
304
- widgetKey: "gui-w", widgetLines: ["\0XYZ_GUI_WIDGET:{...}"],
305
- channel: "gui_widget",
306
- });
307
- expect(resp).toEqual({ ack: true });
308
- expect(ctx.ui.setWidget).not.toHaveBeenCalled();
309
- });
310
-
311
- it("setWidget channel=undefined → 转发含 placement", async () => {
312
- const ctx = makeCtxWithUi();
313
- const handler = makeHandler(ctx);
314
- await handler({
315
- method: "setWidget", id: "w1",
316
- widgetKey: "k", widgetLines: ["a"],
317
- widgetPlacement: "aboveEditor",
318
- });
319
- expect(ctx.ui.setWidget).toHaveBeenCalledWith("k", ["a"], { placement: "aboveEditor" });
320
- });
321
-
322
- // (d) 未知 method warn + ack
323
- it("未知 method → logger.warn + {ack:true}(非 cancelled)", async () => {
324
- const ctx = makeCtxWithUi();
325
- const handler = makeHandler(ctx);
326
- const resp = await handler({ method: "futureMethod", id: "x1" });
327
- expect(resp).toEqual({ ack: true });
328
- expect(loggerMock.warn).toHaveBeenCalledWith(
329
- expect.stringContaining("unknown method"),
330
- expect.objectContaining({ detail: { method: "futureMethod", id: "x1" } }),
331
- );
332
- });
333
-
334
- // (e) dialog 既有 case 不回归(select/confirm 代表)
335
- it("select 无 channel → 调 ctx.ui.select + 透传 value", async () => {
336
- const ctx = makeCtxWithUi();
337
- ctx.ui.select.mockResolvedValueOnce("picked");
338
- const handler = makeHandler(ctx);
339
- const resp = await handler({ method: "select", id: "d1", title: "Choose", options: ["a", "b"] });
340
- expect(resp).toEqual({ value: "picked" });
341
- expect(ctx.ui.select).toHaveBeenCalledWith("Choose", ["a", "b"]);
342
- });
343
-
344
- it("confirm 无 channel → 调 ctx.ui.confirm + 透传 confirmed", async () => {
345
- const ctx = makeCtxWithUi();
346
- ctx.ui.confirm.mockResolvedValueOnce(false);
347
- const handler = makeHandler(ctx);
348
- const resp = await handler({ method: "confirm", id: "d2", title: "Sure?", message: "Go?" });
349
- expect(resp).toEqual({ confirmed: false });
350
- expect(ctx.ui.confirm).toHaveBeenCalledWith("Sure?", "Go?");
351
- });
352
- });
353
-
354
- // ── 确认 createUiRequestHandlerForMode 未改动(TUI 行为零变化) ──
355
- describe("createUiRequestHandlerForMode — TUI 零回归", () => {
356
- it("TUI 下所有 fire-and-forget method 均回 ack 且不调 ctx.ui", async () => {
357
- const methods = ["notify", "setStatus", "setWidget", "setTitle", "set_editor_text"];
358
- for (const method of methods) {
359
- const ctx = makeCtxWithUi("tui");
360
- const handler = createUiRequestHandlerForMode(
361
- ctx, createUiChannelRegistry(), new DialogGlobalQueue())!;
362
- const resp = await handler({ method, id: "t1" });
363
- expect(resp).toEqual({ ack: true });
364
- // TUI 不透传,ctx.ui 方法不应被调用
365
- for (const fn of Object.values(ctx.ui)) {
366
- expect(fn).not.toHaveBeenCalled();
367
- }
368
- }
369
- });
370
- });
@@ -1,204 +0,0 @@
1
- // src/__tests__/ui-request-handler.test.ts
2
- //
3
- // W2 测试(TC-W2 / SR-2):UI 请求处理 —— Pi 原生协议格式。
4
- //
5
- // 测试对象:session-runner.ts 的 handleUiRequest + spawn-event-adapter.ts 的 parseSpawnLine
6
- // 契约来源:.fix-plans/00-master-summary.md §二 2.1(ExtensionUiRequest)+ 2.2(UiRequest/channel)
7
- // + 2.3(stdin 回写 extension_ui_response 格式)+ §一冲突 2(channel 提取在 session-runner 层)
8
- //
9
- // 修复原因(FR-12/SR-2):
10
- // 旧测试用错误的 JSON-RPC 2.0 格式 mock(jsonrpc:"2.0" + params.marker:"ASK_USER"
11
- // + params.questions),测试绿但生产红(Pi 实际发 {type, method, title, options} 平铺)。
12
- // 改为 Pi 真实格式 + channel/channelPayload 断言 + extension_ui_response 回写格式。
13
- //
14
- // 红灯原因:handleUiRequest 签名仍是旧的 (child, id, params, ctx, signal)
15
- // + parseChannel 未接入 session-runner,编译失败。W2 改完签名后转绿。
16
-
17
- import { PassThrough } from "node:stream";
18
-
19
- import { describe, expect, it, vi } from "vitest";
20
-
21
- import { ASK_USER_RPC_PROMPT } from "../session-runner.ts";
22
- import { parseSpawnLine } from "../spawn-event-adapter.ts";
23
- import { parseChannel } from "../ui-channels.ts";
24
- import { createUiRequestQueue, type UiRequest, type UiRequestHandler } from "../ui-request-queue.ts";
25
-
26
- // ── Pi 原生协议样本构造 ────────────────────────────────────
27
- // 真实格式:{type:"extension_ui_request", id, method:"select",
28
- // title:"\0XYZ_ASK_USER", options:[JSON.stringify({questions, allowCancel})]}
29
- // 无 jsonrpc 字段、无 params 包裹、method 在顶层(非 method:"extension_ui_request")。
30
-
31
- const ASK_USER_MARKER = "\0XYZ_ASK_USER";
32
- const askUserPayload = {
33
- questions: [
34
- {
35
- question: "What is your preference?",
36
- options: [{ label: "Option A" }, { label: "Option B" }],
37
- },
38
- ],
39
- allowCancel: true,
40
- };
41
-
42
- function askUserLine(id: string): string {
43
- return JSON.stringify({
44
- type: "extension_ui_request",
45
- id,
46
- method: "select",
47
- title: ASK_USER_MARKER,
48
- options: [JSON.stringify(askUserPayload)],
49
- });
50
- }
51
-
52
- // ── 解析:Pi 原生格式 → ExtensionUiRequest(method 平铺,无 params) ─────
53
-
54
- describe("parseSpawnLine — ask_user 请求解析(Pi 原生格式)", () => {
55
- it("ask_user 行被识别为 extension_ui_request kind", () => {
56
- const result = parseSpawnLine(askUserLine("ui-req-001"));
57
- expect(result?.kind).toBe("extension_ui_request");
58
- });
59
-
60
- it("request.method === 'select'(ask_user 借道 select dialog 通道)", () => {
61
- const result = parseSpawnLine(askUserLine("ui-req-001"));
62
- if (result?.kind !== "extension_ui_request") {
63
- expect.fail("expected extension_ui_request kind");
64
- return;
65
- }
66
- expect(result.request.method).toBe("select");
67
- });
68
-
69
- it("request.title === ASK_USER_MARKER(NUL 前缀 marker 原样保留,由 parseChannel 提取)", () => {
70
- const result = parseSpawnLine(askUserLine("ui-req-001"));
71
- if (result?.kind !== "extension_ui_request") return;
72
- expect(result.request.title).toBe(ASK_USER_MARKER);
73
- });
74
-
75
- it("id 被正确提取(用于 response 关联)", () => {
76
- const result = parseSpawnLine(askUserLine("ui-req-001"));
77
- if (result?.kind !== "extension_ui_request") return;
78
- expect(result.id).toBe("ui-req-001");
79
- });
80
- });
81
-
82
- // ── channel 提取(session-runner 层消费 parseChannel) ──────────────
83
- // adapter 层只做协议解析(method + 字段平铺),channel 提取在 session-runner 层。
84
- // 对 select.title 的 NUL 前缀解析出 channel='ask_user' + payload={questions, allowCancel}。
85
-
86
- describe("parseChannel — ask_user channel 提取(session-runner 层)", () => {
87
- it("select.title 含 ASK_USER_MARKER → channel='ask_user'", () => {
88
- const result = parseSpawnLine(askUserLine("ui-req-001"));
89
- if (result?.kind !== "extension_ui_request") return;
90
- const channel = parseChannel(result.request);
91
- expect(channel.channel).toBe("ask_user");
92
- });
93
-
94
- it("channelPayload 已 parse 为 {questions, allowCancel}", () => {
95
- const result = parseSpawnLine(askUserLine("ui-req-001"));
96
- if (result?.kind !== "extension_ui_request") return;
97
- const channel = parseChannel(result.request);
98
- expect(channel.channelPayload).toEqual(askUserPayload);
99
- });
100
- });
101
-
102
- // ── stdin 回写格式:extension_ui_response(非 JSON-RPC 2.0 result) ─────
103
-
104
- describe("handleUiRequest — stdin 回写 extension_ui_response(Pi 原生格式)", () => {
105
- it("handler 返回 {value} → stdin 写入 {type:extension_ui_response, id, value}", async () => {
106
- const stdin = new PassThrough();
107
- const written: string[] = [];
108
- stdin.on("data", (chunk: Buffer) => written.push(chunk.toString()));
109
-
110
- const child = { stdin, on: vi.fn(), removeListener: vi.fn() } as unknown as Parameters<
111
- typeof createUiRequestQueue
112
- >[0];
113
-
114
- const handler: UiRequestHandler = vi.fn(
115
- async (req: UiRequest) => ({ value: `answer-for-${req.id}` }),
116
- );
117
- const ctx = { uiRequestHandler: handler } as unknown as Parameters<
118
- typeof createUiRequestQueue
119
- >[1];
120
-
121
- const enqueue = createUiRequestQueue(child, ctx);
122
- // W2 新签名:enqueue(id, request) —— request 是 ExtensionUiRequest(method 平铺)
123
- enqueue("ui-req-002", {
124
- method: "select",
125
- title: ASK_USER_MARKER,
126
- options: [JSON.stringify(askUserPayload)],
127
- });
128
-
129
- // 等微任务 + stdin flush
130
- await new Promise((r) => setImmediate(r));
131
-
132
- const raw = written.join("");
133
- expect(raw).toContain('"type":"extension_ui_response"');
134
- expect(raw).toContain('"id":"ui-req-002"');
135
- expect(raw).toContain('"value":"answer-for-ui-req-002"');
136
- // 旧 JSON-RPC 2.0 格式不应出现
137
- expect(raw).not.toContain('"jsonrpc"');
138
- expect(raw).not.toContain('"result"');
139
- });
140
- });
141
-
142
- // ── handler 抛错兜底(M5):catch → 回 cancelled → 写 stdin ─────
143
- // session-runner.ts:489-494 的 catch 分支:handler reject 时兜底写 cancelled。
144
-
145
- describe("handleUiRequest — handler 抛错兜底回 cancelled", () => {
146
- it("handler reject(new Error('boom')) → stdin 写入 cancelled:true", async () => {
147
- const stdin = new PassThrough();
148
- const written: string[] = [];
149
- stdin.on("data", (chunk: Buffer) => written.push(chunk.toString()));
150
-
151
- const child = { stdin, on: vi.fn(), removeListener: vi.fn() } as unknown as Parameters<
152
- typeof createUiRequestQueue
153
- >[0];
154
-
155
- // handler 抛错(reject)
156
- const handler: UiRequestHandler = vi.fn(
157
- async () => Promise.reject(new Error("boom")),
158
- );
159
- const ctx = { uiRequestHandler: handler } as unknown as Parameters<
160
- typeof createUiRequestQueue
161
- >[1];
162
-
163
- const enqueue = createUiRequestQueue(child, ctx);
164
- enqueue("ui-req-err", {
165
- method: "select",
166
- title: ASK_USER_MARKER,
167
- options: [JSON.stringify(askUserPayload)],
168
- });
169
-
170
- // 等微任务 + stdin flush
171
- await new Promise((r) => setImmediate(r));
172
-
173
- const raw = written.join("");
174
- expect(raw).toContain('"type":"extension_ui_response"');
175
- expect(raw).toContain('"id":"ui-req-err"');
176
- expect(raw).toContain('"cancelled":true');
177
- });
178
- });
179
-
180
- // ── W4 提示词(保留,不受格式修复影响) ──────────────────────────
181
-
182
- describe("W4: ask_user RPC 系统提示词注入", () => {
183
- it("ASK_USER_RPC_PROMPT 常量已导出且非空", () => {
184
- expect(ASK_USER_RPC_PROMPT).toBeDefined();
185
- expect(typeof ASK_USER_RPC_PROMPT).toBe("string");
186
- expect(ASK_USER_RPC_PROMPT.length).toBeGreaterThan(0);
187
- });
188
-
189
- it("提示词包含 ask_user 工具说明", () => {
190
- expect(ASK_USER_RPC_PROMPT).toContain("ask_user");
191
- expect(ASK_USER_RPC_PROMPT).toContain("Tool Availability");
192
- });
193
-
194
- it("提示词告知 LLM ask_user 走 RPC 转发", () => {
195
- expect(ASK_USER_RPC_PROMPT).toContain("RPC");
196
- expect(ASK_USER_RPC_PROMPT).toContain("main agent");
197
- expect(ASK_USER_RPC_PROMPT).toContain("forwarded");
198
- });
199
-
200
- it("提示词说明用户在主 agent 界面回答", () => {
201
- expect(ASK_USER_RPC_PROMPT).toContain("user");
202
- expect(ASK_USER_RPC_PROMPT).toContain("answers");
203
- });
204
- });
@@ -1,114 +0,0 @@
1
- // src/__tests__/ui-request-observability.test.ts
2
- //
3
- // M4 测试:ui-request-observability.ts — UiRequestObservability 纯逻辑类。
4
- //
5
- // 测试对象:extensions/universal/subagent-workflow/src/execution/ui-request-observability.ts
6
- // 契约来源:类注释(per-session 去重 + resetMissingHandlerWarnings 清洗 + setMode/getMode 往返)
7
- //
8
- // UiRequestObservability 职责:
9
- // - setMode/getMode:sessionMode 往返存储
10
- // - notifyMissingHandler(sessionId):per-session 去重,每 session 只 console.warn 一次
11
- // - resetMissingHandlerWarnings():清去重集合,清洗后可重新 warn
12
- //
13
- // 纯逻辑无异步,测试最简单。console.warn spy 验证调用次数。
14
-
15
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
16
-
17
- // Mock 共享 logger,让 logger.warn 可被 spy(源码已从 console.warn 改为 logger.warn)
18
- const { loggerMock } = vi.hoisted(() => ({
19
- loggerMock: {
20
- debug: vi.fn(),
21
- warn: vi.fn(),
22
- error: vi.fn(),
23
- info: vi.fn(),
24
- },
25
- }));
26
- vi.mock("@zhushanwen/pi-extension-logger", () => ({
27
- getLogger: () => loggerMock,
28
- }));
29
-
30
- import { UiRequestObservability } from "../ui-request-observability.ts";
31
-
32
- // ── 公共 fixture ──────────────────────────────────────────────
33
-
34
- beforeEach(() => {
35
- loggerMock.warn.mockClear();
36
- });
37
-
38
- afterEach(() => {
39
- vi.restoreAllMocks();
40
- });
41
-
42
- describe("UiRequestObservability — setMode/getMode 往返", () => {
43
- it("setMode('tui') → getMode() === 'tui'", () => {
44
- const obs = new UiRequestObservability();
45
- obs.setMode("tui");
46
- expect(obs.getMode()).toBe("tui");
47
- });
48
-
49
- it("setMode('rpc') → getMode() === 'rpc'", () => {
50
- const obs = new UiRequestObservability();
51
- obs.setMode("rpc");
52
- expect(obs.getMode()).toBe("rpc");
53
- });
54
-
55
- it("初始 getMode() === undefined(未 set)", () => {
56
- const obs = new UiRequestObservability();
57
- expect(obs.getMode()).toBeUndefined();
58
- });
59
-
60
- it("setMode(undefined) → getMode() === undefined(可重置)", () => {
61
- const obs = new UiRequestObservability();
62
- obs.setMode("json");
63
- obs.setMode(undefined);
64
- expect(obs.getMode()).toBeUndefined();
65
- });
66
- });
67
-
68
- describe("UiRequestObservability — notifyMissingHandler per-session 去重", () => {
69
- it("同 sessionId 调两次 → 只 warn 一次", () => {
70
- const obs = new UiRequestObservability();
71
- obs.notifyMissingHandler("s1");
72
- obs.notifyMissingHandler("s1");
73
- expect(loggerMock.warn).toHaveBeenCalledTimes(1);
74
- });
75
-
76
- it("warn 内容含 sessionId 和 mode(可观测性)", () => {
77
- const obs = new UiRequestObservability();
78
- obs.setMode("tui");
79
- obs.notifyMissingHandler("s1");
80
- expect(loggerMock.warn).toHaveBeenCalledWith(
81
- expect.stringContaining("session=s1"),
82
- );
83
- expect(loggerMock.warn).toHaveBeenCalledWith(
84
- expect.stringContaining("mode=tui"),
85
- );
86
- });
87
- });
88
-
89
- describe("UiRequestObservability — resetMissingHandlerWarnings 后可重新 warn", () => {
90
- it("notify(s1) → reset → notify(s1) → warn 被调 2 次", () => {
91
- const obs = new UiRequestObservability();
92
- obs.notifyMissingHandler("s1");
93
- expect(loggerMock.warn).toHaveBeenCalledTimes(1);
94
-
95
- obs.resetMissingHandlerWarnings();
96
-
97
- obs.notifyMissingHandler("s1");
98
- expect(loggerMock.warn).toHaveBeenCalledTimes(2);
99
- });
100
- });
101
-
102
- describe("UiRequestObservability — 不同 session 各自首次 warn", () => {
103
- it("notify(s1) + notify(s2) → warn 被调 2 次(互不干扰)", () => {
104
- const obs = new UiRequestObservability();
105
- obs.notifyMissingHandler("s1");
106
- obs.notifyMissingHandler("s2");
107
- expect(loggerMock.warn).toHaveBeenCalledTimes(2);
108
-
109
- // 再次各自调用,已被去重,不再 warn
110
- obs.notifyMissingHandler("s1");
111
- obs.notifyMissingHandler("s2");
112
- expect(loggerMock.warn).toHaveBeenCalledTimes(2);
113
- });
114
- });