@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,254 +0,0 @@
1
- // src/execution/__tests__/get-record-for-action-restart.test.ts
2
- //
3
- // [M10] getRecordForAction 跨重启磁盘重建分支测试(subagent-service.ts L933-951)。
4
- //
5
- // 背景:内存 miss → collectRecords(1000,"all",undefined).find(status==="running") →
6
- // createRecord({chatMode: true}) → register → 回填 sessionFile/round。代码注释
7
- // (L946-949)自设强制约束「改动此处必须带 S3 回归场景(跨重启 message 续聊验证)」
8
- // ——该 S3 场景此前不存在,分支零测试。该分支含多个仅此处独有的决策:
9
- // - 无条件 chatMode: true(v4 A-3 跨重启恢复入口,V3 方案 A 方向)
10
- // - rootSessionFilter 传 undefined 后置校验(异树仍 throw not owned)
11
- // - sessionFile/round 从磁盘重建结果回填(round 无磁盘持久化 → undefined)
12
- //
13
- // fixture 构造参照 record-store.test.ts 的 writeSessionJsonl(真实 .jsonl 文件,
14
- // identity custom entry + assistant message;无 .alive sidecar → record-store
15
- // sidecar 矩阵分支 4 → status="running" 跨重启可续聊态)。
16
-
17
- import * as fs from "node:fs";
18
- import * as os from "node:os";
19
- import * as path from "node:path";
20
-
21
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
22
-
23
- const { loggerMock } = vi.hoisted(() => ({
24
- loggerMock: { debug: vi.fn(), warn: vi.fn(), error: vi.fn(), info: vi.fn() },
25
- }));
26
- vi.mock("@zhushanwen/pi-extension-logger", () => ({ getLogger: () => loggerMock }));
27
-
28
- // mock session-runner:import 链需要(getRecordForAction 本身不调 spawn,仅守卫读
29
- // hasLiveProcessHandle——默认无活进程,与跨重启场景一致)。
30
- vi.mock("../session-runner.ts", () => ({
31
- runSpawn: vi.fn(),
32
- killAllSpawnedChildren: vi.fn(),
33
- getChildByRecord: vi.fn(() => undefined),
34
- spawnedChildren: new Map(),
35
- }));
36
-
37
- import { writeFinalized } from "../finalized-marker.ts";
38
- import { ModelConfigService } from "../model-config-service.ts";
39
- import { getSubagentSessionDir } from "../path-encoding.ts";
40
- import { RecordStore } from "../record-store.ts";
41
- import { SubagentService } from "../subagent-service.ts";
42
-
43
- // 身份 env 名(与 subagent-service.ts 常量一致;beforeEach/afterEach 清理防泄漏——
44
- // 测试进程可能继承 subagent env,会污染 rootCwd 编码目录与 sessionRootId 基线)。
45
- const IDENTITY_ENV_KEYS = [
46
- "PI_SUBAGENT_ROOT_SESSION_ID",
47
- "PI_SUBAGENT_SELF_RECORD_ID",
48
- "PI_SUBAGENT_DEPTH",
49
- "PI_SUBAGENT_ROOT_CWD",
50
- "PI_SUBAGENT_FORK_DEPTH",
51
- ] as const;
52
-
53
- /** initSession 注入的最小 pi duck-type(同 subagent-service PiLike 形状,结构匹配即可)。 */
54
- interface PiStub {
55
- appendEntry(customType: string, data?: unknown): void;
56
- events: { emit(channel: string, data: unknown): void };
57
- sendMessage(
58
- message: { customType: string; content: string; display: boolean; details?: unknown },
59
- options?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" | "nextTurn" },
60
- ): void;
61
- }
62
-
63
- function makePi(): PiStub {
64
- return {
65
- appendEntry: vi.fn(),
66
- events: { emit: vi.fn() },
67
- sendMessage: vi.fn(),
68
- };
69
- }
70
-
71
- /** 写一个最小合法 session.jsonl(session header + identity entry + 1 条 assistant message)。
72
- * 不写任何 sidecar(.alive/.cancelled/.finalized)→ sidecar 矩阵分支 4 → running。 */
73
- function writeSessionJsonl(
74
- sessionsDir: string,
75
- identity: {
76
- id: string;
77
- rootSessionId: string;
78
- parentRecordId?: string;
79
- depth?: number;
80
- chatMode?: boolean;
81
- worktree?: boolean;
82
- },
83
- ): string {
84
- const file = path.join(sessionsDir, `${identity.id}.jsonl`);
85
- const startedAt = 1_700_000_000_000;
86
- const lines = [
87
- JSON.stringify({
88
- type: "session",
89
- version: 3,
90
- id: "sess-uuid",
91
- timestamp: new Date(startedAt).toISOString(),
92
- cwd: "/tmp",
93
- }),
94
- JSON.stringify({
95
- type: "custom",
96
- id: "id-1",
97
- parentId: null,
98
- timestamp: new Date(startedAt).toISOString(),
99
- customType: "subagent-identity",
100
- data: {
101
- id: identity.id,
102
- agent: "general-purpose",
103
- mode: "background",
104
- task: "restart recovery task",
105
- slug: "restart-test",
106
- startedAt,
107
- rootSessionId: identity.rootSessionId,
108
- ...(identity.parentRecordId !== undefined ? { parentRecordId: identity.parentRecordId } : {}),
109
- ...(identity.depth !== undefined ? { depth: identity.depth } : {}),
110
- ...(identity.chatMode !== undefined ? { chatMode: identity.chatMode } : {}),
111
- ...(identity.worktree !== undefined ? { worktree: identity.worktree } : {}),
112
- },
113
- }),
114
- JSON.stringify({
115
- type: "message",
116
- id: "msg-1",
117
- parentId: "id-1",
118
- timestamp: new Date(startedAt + 1000).toISOString(),
119
- message: {
120
- role: "assistant",
121
- content: [{ type: "text", text: "first round done" }],
122
- usage: { input: 10, output: 20, cacheRead: 0, cacheWrite: 0 },
123
- stopReason: "stop",
124
- timestamp: startedAt + 1000,
125
- },
126
- }),
127
- ];
128
- fs.writeFileSync(file, `${lines.join("\n")}\n`, "utf-8");
129
- return file;
130
- }
131
-
132
- /** 暴露私有 store 供断言 register 副作用。 */
133
- interface ServiceInternals {
134
- store: RecordStore;
135
- }
136
-
137
- describe("[M10] getRecordForAction 跨重启磁盘重建(S3 回归场景)", () => {
138
- let agentDir: string;
139
- let sessionsDir: string;
140
- let service: SubagentService;
141
- let store: RecordStore;
142
-
143
- beforeEach(() => {
144
- for (const k of IDENTITY_ENV_KEYS) delete process.env[k];
145
- agentDir = fs.mkdtempSync(path.join(os.tmpdir(), "restart-recon-"));
146
- sessionsDir = getSubagentSessionDir(agentDir, agentDir);
147
- fs.mkdirSync(sessionsDir, { recursive: true });
148
-
149
- const modelService = new ModelConfigService({ agentDir, cwd: agentDir });
150
- service = new SubagentService({ cwd: agentDir, modelService });
151
- // 根进程身份:无 env → sessionRootId = sessionId(自己是 root)、execCtxBaseline = null
152
- service.initSession({ pi: makePi(), sessionId: "root-session" });
153
- store = (service as unknown as ServiceInternals).store;
154
- });
155
-
156
- afterEach(() => {
157
- service.dispose();
158
- // maxRetries:collectRecords 触发的 fire-and-forget sessions-index 写可能与删除
159
- // 并发(ENOTEMPTY 竞态,全量并行跑时机器负载高会放大窗口)
160
- fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
161
- for (const k of IDENTITY_ENV_KEYS) delete process.env[k];
162
- });
163
-
164
- it("内存 miss + 磁盘 running(无 .alive)→ 重建 record:chatMode 无条件 true、sessionFile 回填、register 进内存", () => {
165
- // identity entry 故意不带 chatMode 字段——证明重建分支的无条件 chatMode:true
166
- //(不依赖磁盘是否记录过对话模式标志)
167
- const file = writeSessionJsonl(sessionsDir, { id: "sa-restart-1", rootSessionId: "root-session" });
168
- expect(store.getMutable("sa-restart-1")).toBeUndefined(); // 前置:内存确无
169
-
170
- const record = service.getRecordForAction("sa-restart-1");
171
-
172
- // [v4 A-3] 无条件 chatMode=true(跨重启恢复入口)
173
- expect(record.chatMode).toBe(true);
174
- // sessionFile 从磁盘重建结果回填
175
- expect(record.sessionFile).toBe(file);
176
- // round 从 found 回填:light 磁盘重建无 round(内存态字段,跨重启不恢复)→ undefined
177
- expect(record.round).toBeUndefined();
178
- // 身份字段从磁盘 identity entry 回填
179
- expect(record.agent).toBe("general-purpose");
180
- expect(record.task).toBe("restart recovery task");
181
- expect(record.slug).toBe("restart-test");
182
- expect(record.rootSessionId).toBe("root-session");
183
- expect(record.parentRecordId).toBeUndefined();
184
- expect(record.status).toBe("running");
185
- // register 生效:进内存,二次调用走内存命中(同一引用)
186
- expect(store.getMutable("sa-restart-1")).toBe(record);
187
- expect(service.getRecordForAction("sa-restart-1")).toBe(record);
188
- });
189
-
190
- it("rootSessionId 校验仍生效:异树 record(other-session)→ throw not found or not owned", () => {
191
- // rootSessionFilter 传 undefined(扫全量磁盘),异树 record 会被 find 命中并重建,
192
- // 但后置校验 record.rootSessionId !== this.sessionRootId 必须拦截
193
- writeSessionJsonl(sessionsDir, { id: "sa-foreign", rootSessionId: "other-session" });
194
-
195
- expect(() => service.getRecordForAction("sa-foreign")).toThrow(/not found or not owned/);
196
- });
197
-
198
- it("直接父校验仍生效:孙级 record(parentRecordId=sa-parent)→ 主进程 throw direct parent", () => {
199
- writeSessionJsonl(sessionsDir, {
200
- id: "sa-grand",
201
- rootSessionId: "root-session",
202
- parentRecordId: "sa-parent",
203
- depth: 2,
204
- });
205
-
206
- // 重建后 parentRecordId=sa-parent ≠ 主进程 baseline(undefined) → cross-layer 守卫拦截
207
- expect(() => service.getRecordForAction("sa-grand")).toThrow(/direct parent/);
208
- });
209
-
210
- it(".finalized sidecar(closed 终态)不重建 → throw not found or not owned", () => {
211
- const file = writeSessionJsonl(sessionsDir, { id: "sa-fin", rootSessionId: "root-session" });
212
- writeFinalized(file); // sidecar 矩阵分支 2 → status=closed → find(status==="running") miss
213
-
214
- expect(() => service.getRecordForAction("sa-fin")).toThrow(/not found or not owned/);
215
- expect(store.getMutable("sa-fin")).toBeUndefined(); // 未重建注册
216
- });
217
-
218
- // ============================================================
219
- // [review round2] 跨重启 worktree 绑定丢失防护
220
- // ============================================================
221
- // 场景:worktree:true + conversation:true 的 subagent 在父进程重启后续聊。WorktreeHandle
222
- // 不可序列化、重建后恒缺失,若无守卫 → 冷路径 resume 的 spawn cwd 静默回落主 repo
223
- //(隔离失效,正是 worktree 要防的并发写冲突场景)。
224
- it("[review round2] worktree record 跨重启重建 → hadWorktree 标记 + 续聊被拒(行动语言)", async () => {
225
- writeSessionJsonl(sessionsDir, {
226
- id: "sa-wt",
227
- rootSessionId: "root-session",
228
- worktree: true,
229
- });
230
-
231
- const record = service.getRecordForAction("sa-wt");
232
- // hadWorktree 从磁盘 identity entry 的 worktree 标志恢复
233
- expect(record.hadWorktree).toBe(true);
234
- // handle 无法恢复(不可序列化)
235
- expect(record.worktreeHandle).toBeUndefined();
236
-
237
- // 冷路径续聊(无活进程)→ resumeRound 守卫拒绝,不 spawn 回落主 repo
238
- await expect(service.deliverMessage(record, "resume after restart", false)).rejects.toThrow(
239
- /worktree isolation.*lost when the parent process restarted/,
240
- );
241
- });
242
-
243
- it("[review round2] 非 worktree record 跨重启重建 → 续聊不受 worktree 守卫拦截(向后兼容)", async () => {
244
- // identity entry 无 worktree 字段(旧文件)→ found.worktree undefined → hadWorktree false
245
- writeSessionJsonl(sessionsDir, { id: "sa-nowt", rootSessionId: "root-session" });
246
-
247
- const record = service.getRecordForAction("sa-nowt");
248
- expect(record.hadWorktree).toBe(false);
249
-
250
- // deliverMessage 冷路径不被 worktree 守卫拦截(resume 正常发起;后续 spawn 编排
251
- // 超出本用例关注点——runSpawn 在本文件是 no-op mock)
252
- await expect(service.deliverMessage(record, "resume normal", false)).resolves.toBeUndefined();
253
- });
254
- });
@@ -1,59 +0,0 @@
1
- // src/__tests__/gui-mode-dispatch.test.ts
2
- //
3
- // S6: ctx.mode RPC 分发契约测试。
4
- //
5
- // 审查发现:sdk-contract.test.ts 只测 ctx.model 透传,未覆盖 ctx.mode === "rpc"
6
- // 时 __gui__ 被填充、ctx.mode === "tui" 时不填充的 GUI 通道分发。
7
- //
8
- // adapter() 是纯函数,ctx.mode === "rpc" → details.__gui__ 被附加,
9
- // ctx.mode === "tui"/"json"/"print" → details.__gui__ 为 undefined。
10
-
11
- import type { GuiContext } from "@xyz-agent/extension-protocol";
12
- import { describe, expect, it } from "vitest";
13
-
14
- import type { AdapterInput } from "../../interface/subagent-actions.ts";
15
- import { adapter } from "../../interface/subagent-actions.ts";
16
-
17
- function makeStartInput(): AdapterInput {
18
- return {
19
- action: "start",
20
- domain: {
21
- subagentId: "test-id",
22
- sessionFile: "/test/session.jsonl",
23
- slug: "test-slug",
24
- response: { status: "started" },
25
- },
26
- } as unknown as AdapterInput;
27
- }
28
-
29
- describe("S6: ctx.mode dispatches __gui__ output correctly", () => {
30
- it("ctx.mode=rpc → details.__gui__ is populated", () => {
31
- const ctx = { mode: "rpc", hasUI: true } as GuiContext;
32
- const result = adapter(makeStartInput(), ctx);
33
- expect(result.details).toHaveProperty("__gui__");
34
- expect(result.details.__gui__).toBeDefined();
35
- });
36
-
37
- it("ctx.mode=tui → details.__gui__ is undefined (TUI renders differently)", () => {
38
- const ctx = { mode: "tui", hasUI: true } as GuiContext;
39
- const result = adapter(makeStartInput(), ctx);
40
- expect(result.details.__gui__).toBeUndefined();
41
- });
42
-
43
- it("ctx.mode=json → details.__gui__ is undefined (headless)", () => {
44
- const ctx = { mode: "json", hasUI: false } as GuiContext;
45
- const result = adapter(makeStartInput(), ctx);
46
- expect(result.details.__gui__).toBeUndefined();
47
- });
48
-
49
- it("ctx.mode=print → details.__gui__ is undefined (headless)", () => {
50
- const ctx = { mode: "print", hasUI: false } as GuiContext;
51
- const result = adapter(makeStartInput(), ctx);
52
- expect(result.details.__gui__).toBeUndefined();
53
- });
54
-
55
- it("ctx=undefined → details.__gui__ is undefined (backward compat)", () => {
56
- const result = adapter(makeStartInput(), undefined);
57
- expect(result.details.__gui__).toBeUndefined();
58
- });
59
- });
@@ -1,30 +0,0 @@
1
- /**
2
- * 创建一个结构兼容的 ExtensionAPI mock,供 sdk-contract / 注册契约测试用。
3
- *
4
- * ExtensionAPI 有 ~30 个必需方法(on/registerTool/registerCommand/...),逐个手写
5
- * 不现实。本 helper 用 Proxy 把所有未显式 override 的方法/属性短路为 no-op 函数,
6
- * 让对象**结构兼容** ExtensionAPI——避免 `as unknown as ExtensionAPI` 双重断言
7
- * (taste/no-unsafe-cast 规则禁止)。测试只需 override 关心的方法:
8
- *
9
- * const pi = mockExtensionApi({
10
- * registerCommand: (name) => { capturedName = name; },
11
- * });
12
- *
13
- * Proxy 的 trap 对所有属性访问返回 override 值或 no-op 函数,运行时安全
14
- * (注册 handler 只调用被 override 的方法,其余方法测试不触及)。
15
- */
16
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
17
-
18
- export function mockExtensionApi(
19
- overrides: Record<string, unknown> = {},
20
- ): ExtensionAPI {
21
- const noop = (): void => { /* test mock: method not invoked by this test */ };
22
- // Proxy<T> 泛型参数决定返回类型——直接声明为 ExtensionAPI,
23
- // TS 接受(Proxy handler 对 target 的类型不约束 T)。
24
- return new Proxy<ExtensionAPI>(overrides as ExtensionAPI, {
25
- get(target, prop: string | symbol): unknown {
26
- if (prop in target) return target[prop as keyof ExtensionAPI];
27
- return noop;
28
- },
29
- });
30
- }
@@ -1,238 +0,0 @@
1
- // src/execution/__tests__/helpers/spawn-mock.ts
2
- //
3
- // run-spawn-* 三文件(integration/edges/rpc-mode)共享的 FakeChild + 工具函数。
4
- //
5
- // 背景:vitest 的 vi.mock 会被提升到文件顶部,工厂函数体内**不能引用顶层 import 的变量**
6
- //(vitest 官方文档明确警告)。例外:async 工厂内可用 `await import()`(在模块需求时执行,
7
- // 此时所有模块已加载)。故本 helper 不导出「mock 工厂函数」(无法被 vi.mock 顶层引用),
8
- // 而是导出 **FakeChild class + 工具函数**,让各测试文件的 vi.mock 工厂用 `await import`
9
- // 动态取回 FakeChild。这样每个 vi.mock 工厂从 ~15 行(定义 class + vi.fn)缩到 ~4 行,
10
- // 且 FakeChild 定义只有一个权威来源。
11
- //
12
- // 共享内容(原 ~80 行 × 3 重复):
13
- // - FakeChild:EventEmitter + PassThrough stdout/stderr/stdin + kill 记录(class 定义)
14
- // - lastSpawnedChild(mockSpawn):从 mock.results 取回最近 spawn 返回的 FakeChild
15
- // - waitForSpawn(mockSpawn):轮询等 spawn 被调(比 vi.waitFor 在该 vitest 版本下可靠)
16
- // - emitStdoutLine / sessionHeader:构造 stdout 行的辅助
17
- // - makeRecord / makeOpts / makeCtx:构造最小合法的 record/opts/ctx(3 文件一致)
18
- //
19
- // 各测试文件 vi.mock 模式(每文件独立声明,vitest 的 vi.mock 是文件作用域):
20
- // ```ts
21
- // vi.mock("node:child_process", async () => {
22
- // const { EventEmitter } = await import("node:events");
23
- // const events = { EventEmitter }; // 兼容旧注释
24
- // const { FakeChild } = await import("./helpers/spawn-mock.ts");
25
- // return {
26
- // spawn: vi.fn(() => new FakeChild()),
27
- // // buildEnvBlock 用 execFile 异步取 git branch:默认 err-first 兜底(catch → branch=""),
28
- // // 形态同 worktree-manager.test.ts 的 setupExecFile
29
- // execFile: vi.fn(
30
- // (
31
- // _cmd: string,
32
- // _args: readonly string[],
33
- // _opts: unknown,
34
- // cb: (err: Error | null, stdout?: string, stderr?: string) => void,
35
- // ) => cb(new Error("execFile not configured in this test")),
36
- // ),
37
- // };
38
- // });
39
- // vi.mock("node:fs", async () => {
40
- // const actual = await import("node:fs");
41
- // return {
42
- // default: { ...actual, mkdirSync: vi.fn(), existsSync: vi.fn(() => false),
43
- // appendFileSync: vi.fn(), writeFileSync: vi.fn(), readdirSync: vi.fn(() => []) },
44
- // mkdirSync: vi.fn(), existsSync: vi.fn(() => false),
45
- // appendFileSync: vi.fn(), writeFileSync: vi.fn(), readdirSync: vi.fn(() => []),
46
- // promises: actual.promises,
47
- // };
48
- // });
49
- // vi.mock("../alive-store.ts", () => ({ writeAliveMarker: vi.fn() }));
50
- // vi.mock("../temp-prompt.ts", () => ({
51
- // writePromptToTempFile: vi.fn(async (agent: string) => {
52
- // const safeName = agent.replace(/[^\w.-]+/g, "_");
53
- // return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
54
- // }),
55
- // cleanupTempPrompt: vi.fn(async () => {}),
56
- // }));
57
- // ```
58
-
59
- import type { ChildProcess } from "node:child_process";
60
- import { EventEmitter } from "node:events";
61
- import { PassThrough } from "node:stream";
62
-
63
- import { createRecord } from "../../execution-record.ts";
64
- import type { RunOptions, SessionRunnerContext } from "../../session-runner.ts";
65
-
66
- /** FakeChild 的假 pid(满足 ChildProcess.pid 形状,无真实进程语义)。 */
67
- const FAKE_PID = 12345;
68
-
69
- /** waitForSpawn 默认超时(ms),超时说明 spawn 未被调(runSpawn 前置 await 卡死)。 */
70
- const WAIT_SPAWN_TIMEOUT_MS = 1000;
71
-
72
- /** waitForSpawn 轮询间隔(ms),微任务级等待的粒度。 */
73
- const SPAWN_POLL_INTERVAL_MS = 5;
74
-
75
- /**
76
- * FakeChild:模拟 ChildProcess(EventEmitter + PassThrough streams)。
77
- *
78
- * 测试通过 mockSpawn.mock.results.at(-1).value 取回实例,控制 emit data/close/error 时序。
79
- *
80
- * 导出 class(而非只在工厂内部定义)是为了:
81
- * 1. vi.mock 工厂内 `await import("./helpers/spawn-mock.ts")` 后 `new FakeChild()` 与
82
- * 测试侧 `instanceof FakeChild` / 类型断言用同一个 class。
83
- * 2. C13 e2e 测试可直接 `new FakeChild()` 手动构造 child 喂给 ui-request-queue(不经 spawn)。
84
- */
85
- export class FakeChild extends EventEmitter {
86
- pid = FAKE_PID;
87
- stdout = new PassThrough();
88
- stderr = new PassThrough();
89
- stdin = new PassThrough();
90
- killed = false;
91
- killSignal: string | undefined;
92
- kill(sig?: string): boolean {
93
- this.killed = true;
94
- this.killSignal = sig;
95
- return true;
96
- }
97
- }
98
-
99
- /**
100
- * spawn mock 返回的 fake child 类型(结构子集)。
101
- *
102
- * 测试文件优先 import { FakeChild } 用真实 class 类型;此 interface 仅为兼容现有
103
- * describe 块内 `child: FakeChild` 的类型注解风格(与原代码一致,降低 diff 噪声)。
104
- */
105
- export interface FakeChildLike {
106
- pid: number;
107
- stdout: PassThrough;
108
- stderr: PassThrough;
109
- stdin: PassThrough;
110
- killed: boolean;
111
- killSignal: string | undefined;
112
- kill(sig?: string): boolean;
113
- emit(event: string, ...args: unknown[]): boolean;
114
- }
115
-
116
- /**
117
- * 从最近一次 spawn 调用取回返回的 FakeChild(测试控制器)。
118
- *
119
- * @param mockSpawn 调用方用 `vi.mocked(spawn)` 取回的 mock 引用
120
- */
121
- export function lastSpawnedChild<
122
- T extends { mock: { results: Array<{ value: ChildProcess | unknown }> } },
123
- >(mockSpawn: T): FakeChild {
124
- const result = mockSpawn.mock.results.at(-1);
125
- if (!result) throw new Error("spawn was not called yet");
126
- return result.value as FakeChild;
127
- }
128
-
129
- /**
130
- * 等待 runSpawn 内部调到 spawn(拿到 child 控制器)。
131
- *
132
- * runSpawn 是 async,spawn 在 buildEnvBlock + writePromptToTempFile 两个 await 之后才调
133
- *(均为微任务级延迟)。用 setTimeout 轮询 mockSpawn.mock.results,比 vi.waitFor 在该
134
- * vitest 版本下更可靠(vi.waitFor 偶发过早 resolve 导致后续读取竞态)。
135
- *
136
- * [快照语义] 等待的是「调用时刻之后**新发生**的一次 spawn」,而非「任意历史 spawn」。
137
- * 旧实现只在 results 为空时等待(即只在文件/测试内首次 spawn 有效):同一文件第二次
138
- * runSpawn 时立即返回,而新 runSpawn 尚未跨过 await 到达 spawn → lastSpawnedChild 取回
139
- * **上一次**的 child,stdout/close 事件发给已死的旧 child,当前 runSpawn 永远收不到
140
- * close → 测试超时。buildEnvBlock 异步化(多一个 await 微任务)后该隐式时序假设失效,
141
- * 故改为快照 baseline:等待 results.length 超过调用时的快照值,天然对「每测试/每文件
142
- * N 次 spawn」都正确(调用前先记 baseline,本次 runSpawn 的 spawn 必然使 length 增长)。
143
- *
144
- * @param mockSpawn 调用方用 `vi.mocked(spawn)` 取回的 mock 引用
145
- */
146
- export async function waitForSpawn<
147
- T extends { mock: { results: unknown[] } },
148
- >(mockSpawn: T, timeoutMs: number = WAIT_SPAWN_TIMEOUT_MS): Promise<void> {
149
- const start = Date.now();
150
- const baseline = mockSpawn.mock.results.length;
151
- while (mockSpawn.mock.results.length <= baseline) {
152
- if (Date.now() - start > timeoutMs) {
153
- throw new Error(`spawn was not called within ${timeoutMs}ms`);
154
- }
155
- await new Promise((r) => setTimeout(r, SPAWN_POLL_INTERVAL_MS));
156
- }
157
- }
158
-
159
- /** 向 stdout 写一行(自动补换行,runSpawn 按 \n split 行)。 */
160
- export function emitStdoutLine(child: FakeChild, obj: Record<string, unknown>): void {
161
- child.stdout.write(`${JSON.stringify(obj)}\n`);
162
- }
163
-
164
- /** 构造 session header 行(stdout 首行)。 */
165
- export function sessionHeader(id = "sess-abc"): Record<string, unknown> {
166
- return {
167
- type: "session",
168
- id,
169
- timestamp: "2026-07-03T12-00-00-000Z",
170
- cwd: "/tmp/test",
171
- };
172
- }
173
-
174
- /**
175
- * 让 sessionFile 存在校验通过——runSpawn 在进程退出后用 existsSync(record.sessionFile)
176
- * 判断是否补写 identity。默认 mock existsSync 返回 false(兜底查找),此 helper 在指定
177
- * 路径返回 true。
178
- */
179
- export function mockSessionFileExists(
180
- mockExistsSync: { mockImplementation: (fn: (p: unknown) => boolean) => void },
181
- sessionFilePath: string,
182
- ): void {
183
- mockExistsSync.mockImplementation((p: unknown) => String(p) === sessionFilePath);
184
- }
185
-
186
- // ── record / opts / ctx 构造(3 文件一致的最小合法形状)──
187
-
188
- /** 构造最小合法的 ExecutionRecord(runSpawn 入参)。 */
189
- export function makeRecord(id = "run-1") {
190
- return createRecord(id, {
191
- agent: "general-purpose",
192
- model: "test-model",
193
- mode: "sync",
194
- task: "do something",
195
- startedAt: 1_000_000,
196
- rootSessionId: "root-session",
197
- parentRecordId: undefined,
198
- depth: 0,
199
- });
200
- }
201
-
202
- /** 构造最小合法的 RunOptions(runSpawn 入参,可 override 关键字段)。 */
203
- export function makeOpts(overrides: Partial<RunOptions> = {}): RunOptions {
204
- return {
205
- resolved: {
206
- model: {
207
- id: "test-model",
208
- name: "Test Model",
209
- provider: "test",
210
- reasoning: false,
211
- },
212
- thinkingLevel: undefined,
213
- },
214
- agentConfig: undefined,
215
- appendSystemPrompt: undefined,
216
- skillPath: undefined,
217
- schema: undefined,
218
- maxTurns: undefined,
219
- graceTurns: undefined,
220
- signal: undefined,
221
- onEvent: undefined,
222
- ...overrides,
223
- };
224
- }
225
-
226
- /** 构造最小合法的 SessionRunnerContext(runSpawn 入参,可 override 关键字段)。 */
227
- export function makeCtx(overrides: Partial<SessionRunnerContext> = {}): SessionRunnerContext {
228
- return {
229
- cwd: "/tmp/test",
230
- agentDir: "/tmp/test/agents",
231
- skillDirs: [],
232
- mainCwd: "/tmp/test",
233
- mainSessionFile: undefined,
234
- sessionRootId: "root-session-test",
235
- rootCwd: "/tmp/test",
236
- ...overrides,
237
- };
238
- }
@@ -1,87 +0,0 @@
1
- // src/__tests__/host-mode.test.ts
2
- //
3
- // W1 红灯测试:host-mode.ts 主进程运行模式分类工具。
4
- //
5
- // 测试对象:extensions/universal/subagent-workflow/src/execution/host-mode.ts(新建)
6
- // 契约来源:.fix-plans/00-master-summary.md §一 冲突 4 + §2.5(W4 守卫消费点)
7
- //
8
- // 三个导出:
9
- // - resolveHostMode(mode): ExtensionMode | undefined → "tui" | "gui" | "headless"
10
- // - willRespondToAskUser(mode): boolean(tui/gui → true,headless → false)
11
- // - hasInteractiveUI(mode): boolean(非 headless → true)
12
- //
13
- // 红灯原因:host-mode.ts 尚未创建,import 失败。
14
-
15
- import { describe, expect, it } from "vitest";
16
-
17
- import {
18
- hasInteractiveUI,
19
- resolveHostMode,
20
- willRespondToAskUser,
21
- } from "../host-mode.ts";
22
-
23
- describe("resolveHostMode — ExtensionMode 聚合为 HostMode", () => {
24
- it('"tui" → "tui"(纯 Pi TUI,ctx.ui.custom 可用)', () => {
25
- expect(resolveHostMode("tui")).toBe("tui");
26
- });
27
-
28
- it('"rpc" → "gui"(xyz-agent GUI,sidecar 通道)', () => {
29
- expect(resolveHostMode("rpc")).toBe("gui");
30
- });
31
-
32
- it('"json" → "headless"(无交互通道)', () => {
33
- expect(resolveHostMode("json")).toBe("headless");
34
- });
35
-
36
- it('"print" → "headless"(无交互通道)', () => {
37
- expect(resolveHostMode("print")).toBe("headless");
38
- });
39
-
40
- it('undefined → "headless"(向后兼容:mode 未穿透时按 headless)', () => {
41
- expect(resolveHostMode(undefined)).toBe("headless");
42
- });
43
- });
44
-
45
- describe("willRespondToAskUser — 主进程是否响应子进程 ask_user", () => {
46
- it("tui → true(冲突 3 裁决:TUI 必须注入 handler)", () => {
47
- expect(willRespondToAskUser("tui")).toBe(true);
48
- });
49
-
50
- it("rpc → true(GUI 透传所有 UI)", () => {
51
- expect(willRespondToAskUser("rpc")).toBe(true);
52
- });
53
-
54
- it("json → false(headless 无 UI 通道)", () => {
55
- expect(willRespondToAskUser("json")).toBe(false);
56
- });
57
-
58
- it("print → false(headless 无 UI 通道)", () => {
59
- expect(willRespondToAskUser("print")).toBe(false);
60
- });
61
-
62
- it("undefined → false(向后兼容)", () => {
63
- expect(willRespondToAskUser(undefined)).toBe(false);
64
- });
65
- });
66
-
67
- describe("hasInteractiveUI — 是否有交互 UI 通道", () => {
68
- it("tui → true", () => {
69
- expect(hasInteractiveUI("tui")).toBe(true);
70
- });
71
-
72
- it("rpc → true", () => {
73
- expect(hasInteractiveUI("rpc")).toBe(true);
74
- });
75
-
76
- it("json → false", () => {
77
- expect(hasInteractiveUI("json")).toBe(false);
78
- });
79
-
80
- it("print → false", () => {
81
- expect(hasInteractiveUI("print")).toBe(false);
82
- });
83
-
84
- it("undefined → false", () => {
85
- expect(hasInteractiveUI(undefined)).toBe(false);
86
- });
87
- });