@zhushanwen/pi-subagent-workflow 8.6.0 → 8.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (364) hide show
  1. package/README.md +5 -11
  2. package/package.json +13 -20
  3. package/relay/relay.mjs +1 -1
  4. package/skills/workflow-script-format/SKILL.md +32 -13
  5. package/src/host/__tests__/pi-host.test.ts +235 -0
  6. package/src/host/pi-host.ts +163 -0
  7. package/src/index.ts +141 -138
  8. package/src/injectors/__tests__/engine-awareness.test.ts +292 -0
  9. package/src/injectors/__tests__/engine-section-stability.test.ts +343 -0
  10. package/src/injectors/__tests__/model-list-injector.test.ts +18 -21
  11. package/src/injectors/__tests__/subagent-list-injector.test.ts +59 -19
  12. package/src/injectors/__tests__/workflow-list-injector.test.ts +31 -17
  13. package/src/injectors/engine-awareness.ts +119 -0
  14. package/src/injectors/model-list-injector.ts +15 -58
  15. package/src/injectors/subagent-list-injector.ts +56 -114
  16. package/src/injectors/workflow-list-injector.ts +37 -70
  17. package/src/interface/__tests__/detectors.test.ts +48 -37
  18. package/src/interface/__tests__/subagent-tool-path-guard.test.ts +2 -2
  19. package/src/interface/__tests__/subagent-tool-prompt.test.ts +6 -3
  20. package/src/interface/__tests__/tool-workflow-run-builtin-name.test.ts +216 -0
  21. package/src/interface/__tests__/tool-workflow-script-generate.test.ts +17 -6
  22. package/src/interface/__tests__/tool-workflow-throw-paths.test.ts +38 -2
  23. package/src/interface/bg-notify-render.ts +7 -17
  24. package/src/interface/command-actions.ts +5 -15
  25. package/src/interface/commands.ts +4 -4
  26. package/src/interface/format.ts +17 -6
  27. package/src/interface/gui-mappers.ts +18 -22
  28. package/src/interface/helpers.ts +9 -130
  29. package/src/interface/list-component.ts +5 -5
  30. package/src/interface/list-shared.ts +3 -3
  31. package/src/interface/list-view.ts +3 -3
  32. package/src/interface/subagent-actions.ts +66 -439
  33. package/src/interface/subagent-tool-schema.ts +21 -17
  34. package/src/interface/subagent-tool.ts +12 -7
  35. package/src/interface/subagents.ts +4 -4
  36. package/src/interface/tool-render.ts +37 -15
  37. package/src/interface/tool-workflow-script.ts +34 -76
  38. package/src/interface/tool-workflow.ts +57 -94
  39. package/src/interface/views/WorkflowsView.ts +8 -16
  40. package/src/interface/views/__tests__/WorkflowsView-signature.test.ts +3 -3
  41. package/src/interface/views/__tests__/detail-content-session-file.test.ts +2 -2
  42. package/src/interface/views/detail-content.ts +4 -4
  43. package/src/interface/views/format.ts +23 -62
  44. package/src/{orchestration/jsonl-run-store.ts → jsonl-run-store.ts} +137 -225
  45. package/agents/analyst.md +0 -61
  46. package/agents/coder.md +0 -70
  47. package/agents/debugger.md +0 -67
  48. package/agents/doc-reviewer.md +0 -50
  49. package/agents/explorer.md +0 -64
  50. package/agents/general-purpose.md +0 -32
  51. package/agents/orchestrator.md +0 -63
  52. package/agents/planner.md +0 -54
  53. package/agents/researcher.md +0 -65
  54. package/agents/reviewer.md +0 -74
  55. package/src/execution/__tests__/__fixtures__/notifier-golden-snapshots.json +0 -53
  56. package/src/execution/__tests__/__fixtures__/truncline.snapshot.json +0 -1
  57. package/src/execution/__tests__/agent-registry.test.ts +0 -363
  58. package/src/execution/__tests__/agent-result-mapper.test.ts +0 -150
  59. package/src/execution/__tests__/alive-store.test.ts +0 -147
  60. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +0 -490
  61. package/src/execution/__tests__/before-agent-start-injection.test.ts +0 -132
  62. package/src/execution/__tests__/bg-notify-render.test.ts +0 -329
  63. package/src/execution/__tests__/channel-registry-handshake.test.ts +0 -243
  64. package/src/execution/__tests__/chat-engine-routing.test.ts +0 -601
  65. package/src/execution/__tests__/chatmode-first-round-closure-service.test.ts +0 -365
  66. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +0 -190
  67. package/src/execution/__tests__/chatmode-round-notify-real-chain.test.ts +0 -215
  68. package/src/execution/__tests__/concurrency-pool.test.ts +0 -250
  69. package/src/execution/__tests__/config.test.ts +0 -110
  70. package/src/execution/__tests__/conversation-wiring.test.ts +0 -198
  71. package/src/execution/__tests__/crash-recovery.test.ts +0 -320
  72. package/src/execution/__tests__/delivery-methods.test.ts +0 -422
  73. package/src/execution/__tests__/dialog-queue.test.ts +0 -299
  74. package/src/execution/__tests__/epipe-fallback.test.ts +0 -241
  75. package/src/execution/__tests__/execute-and-await-worktree.test.ts +0 -243
  76. package/src/execution/__tests__/execute-nesting.test.ts +0 -323
  77. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
  78. package/src/execution/__tests__/execution-record.test.ts +0 -1394
  79. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +0 -171
  80. package/src/execution/__tests__/finalize-record.test.ts +0 -368
  81. package/src/execution/__tests__/finalized-marker.test.ts +0 -82
  82. package/src/execution/__tests__/format-schema-instruction.test.ts +0 -169
  83. package/src/execution/__tests__/format.test.ts +0 -458
  84. package/src/execution/__tests__/gc-timer.test.ts +0 -184
  85. package/src/execution/__tests__/get-record-for-action-restart.test.ts +0 -254
  86. package/src/execution/__tests__/gui-mode-dispatch.test.ts +0 -59
  87. package/src/execution/__tests__/helpers/mock-extension-api.ts +0 -30
  88. package/src/execution/__tests__/helpers/spawn-mock.ts +0 -242
  89. package/src/execution/__tests__/host-mode.test.ts +0 -87
  90. package/src/execution/__tests__/index-session-start-identity.test.ts +0 -371
  91. package/src/execution/__tests__/index-session-start.test.ts +0 -672
  92. package/src/execution/__tests__/lifecycle-manager-lock.test.ts +0 -211
  93. package/src/execution/__tests__/lifecycle-manager.test.ts +0 -383
  94. package/src/execution/__tests__/lifecycle-predicates.test.ts +0 -116
  95. package/src/execution/__tests__/list-component.test.ts +0 -401
  96. package/src/execution/__tests__/list-fields.test.ts +0 -140
  97. package/src/execution/__tests__/manifest-parentid.test.ts +0 -117
  98. package/src/execution/__tests__/model-resolver.test.ts +0 -465
  99. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +0 -287
  100. package/src/execution/__tests__/nested-visibility.test.ts +0 -325
  101. package/src/execution/__tests__/notifier-flush.test.ts +0 -612
  102. package/src/execution/__tests__/notifier-golden-snapshot.test.ts +0 -261
  103. package/src/execution/__tests__/notify-ledger.test.ts +0 -826
  104. package/src/execution/__tests__/one-shot-upgrade.test.ts +0 -205
  105. package/src/execution/__tests__/output-collector.test.ts +0 -358
  106. package/src/execution/__tests__/parent-child-matrix.test.ts +0 -336
  107. package/src/execution/__tests__/path-encoding.test.ts +0 -104
  108. package/src/execution/__tests__/pi-invocation.test.ts +0 -134
  109. package/src/execution/__tests__/record-store.test.ts +0 -1057
  110. package/src/execution/__tests__/records-cwd-isolation.test.ts +0 -91
  111. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +0 -339
  112. package/src/execution/__tests__/recursive-visibility-env.test.ts +0 -273
  113. package/src/execution/__tests__/relay-agent.test.ts +0 -448
  114. package/src/execution/__tests__/relay-env.test.ts +0 -42
  115. package/src/execution/__tests__/resource-policy.test.ts +0 -109
  116. package/src/execution/__tests__/rpc-mode.test.ts +0 -89
  117. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +0 -267
  118. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +0 -253
  119. package/src/execution/__tests__/run-spawn-edges.test.ts +0 -687
  120. package/src/execution/__tests__/run-spawn-integration.test.ts +0 -933
  121. package/src/execution/__tests__/run-spawn-resume.test.ts +0 -322
  122. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +0 -196
  123. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +0 -199
  124. package/src/execution/__tests__/sdk-contract.test.ts +0 -273
  125. package/src/execution/__tests__/session-context-resolver.test.ts +0 -167
  126. package/src/execution/__tests__/session-file-gc.test.ts +0 -293
  127. package/src/execution/__tests__/session-pending.test.ts +0 -197
  128. package/src/execution/__tests__/session-reconstructor.test.ts +0 -379
  129. package/src/execution/__tests__/session-runner-epipe.test.ts +0 -178
  130. package/src/execution/__tests__/session-runner-schema-env.test.ts +0 -350
  131. package/src/execution/__tests__/session-start-reaper.test.ts +0 -233
  132. package/src/execution/__tests__/spawn-args.test.ts +0 -445
  133. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +0 -189
  134. package/src/execution/__tests__/spawn-event-adapter.test.ts +0 -167
  135. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +0 -207
  136. package/src/execution/__tests__/spawned-children.test.ts +0 -92
  137. package/src/execution/__tests__/start-sync-model-guard.test.ts +0 -150
  138. package/src/execution/__tests__/startup-config-declaration.test.ts +0 -35
  139. package/src/execution/__tests__/status-refactor.test.ts +0 -345
  140. package/src/execution/__tests__/stdin-writer.test.ts +0 -463
  141. package/src/execution/__tests__/stream-sink-retirement.test.ts +0 -261
  142. package/src/execution/__tests__/subagent-service-abort.test.ts +0 -60
  143. package/src/execution/__tests__/subagent-service-message-close.test.ts +0 -629
  144. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +0 -180
  145. package/src/execution/__tests__/subagent-service.test.ts +0 -788
  146. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +0 -310
  147. package/src/execution/__tests__/subprocess-agent-runner.test.ts +0 -599
  148. package/src/execution/__tests__/temp-prompt.test.ts +0 -53
  149. package/src/execution/__tests__/timeout-integration.test.ts +0 -615
  150. package/src/execution/__tests__/tombstone-store.test.ts +0 -73
  151. package/src/execution/__tests__/tool-action.test.ts +0 -476
  152. package/src/execution/__tests__/truncline-snapshot.test.ts +0 -81
  153. package/src/execution/__tests__/turn-limiter-semantics.test.ts +0 -194
  154. package/src/execution/__tests__/turn-limiter.test.ts +0 -65
  155. package/src/execution/__tests__/ui-channels.test.ts +0 -187
  156. package/src/execution/__tests__/ui-interaction-model.test.ts +0 -67
  157. package/src/execution/__tests__/ui-request-handler-factory.test.ts +0 -370
  158. package/src/execution/__tests__/ui-request-handler.test.ts +0 -204
  159. package/src/execution/__tests__/ui-request-observability.test.ts +0 -114
  160. package/src/execution/__tests__/ui-request-queue.test.ts +0 -133
  161. package/src/execution/__tests__/worktree-manager.test.ts +0 -633
  162. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +0 -229
  163. package/src/execution/__tests__/worktree-reconcile.integration.test.ts +0 -181
  164. package/src/execution/__tests__/worktree-registry.test.ts +0 -199
  165. package/src/execution/agent-registry.ts +0 -199
  166. package/src/execution/agent-result-mapper.ts +0 -90
  167. package/src/execution/alive-store.ts +0 -92
  168. package/src/execution/argv-mirror.ts +0 -113
  169. package/src/execution/best-effort.ts +0 -38
  170. package/src/execution/channel-registry-access.ts +0 -144
  171. package/src/execution/concurrency-pool.ts +0 -116
  172. package/src/execution/config.ts +0 -96
  173. package/src/execution/dialog-queue.ts +0 -330
  174. package/src/execution/engine/__tests__/common/data-dir.test.ts +0 -53
  175. package/src/execution/engine/__tests__/common/errors.test.ts +0 -132
  176. package/src/execution/engine/__tests__/common/event-journal.test.ts +0 -177
  177. package/src/execution/engine/__tests__/common/kill-chain.test.ts +0 -192
  178. package/src/execution/engine/__tests__/common/nesting-guard.test.ts +0 -81
  179. package/src/execution/engine/__tests__/common/persona-router.test.ts +0 -123
  180. package/src/execution/engine/__tests__/common/pool-manager.test.ts +0 -154
  181. package/src/execution/engine/__tests__/common/schema-emulation.test.ts +0 -128
  182. package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +0 -28
  183. package/src/execution/engine/__tests__/conformance/agent-event-invariants.ts +0 -141
  184. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +0 -109
  185. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +0 -101
  186. package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +0 -77
  187. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +0 -104
  188. package/src/execution/engine/__tests__/conformance/contract.relay.test.ts +0 -342
  189. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +0 -201
  190. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +0 -76
  191. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +0 -79
  192. package/src/execution/engine/__tests__/engine-discovery.test.ts +0 -87
  193. package/src/execution/engine/__tests__/engines-declaration.test.ts +0 -36
  194. package/src/execution/engine/__tests__/model-prompt.test.ts +0 -85
  195. package/src/execution/engine/__tests__/paths.test.ts +0 -39
  196. package/src/execution/engine/__tests__/registry.test.ts +0 -120
  197. package/src/execution/engine/__tests__/routing.test.ts +0 -231
  198. package/src/execution/engine/common/data-dir.ts +0 -62
  199. package/src/execution/engine/common/errors.ts +0 -183
  200. package/src/execution/engine/common/event-journal.ts +0 -254
  201. package/src/execution/engine/common/journal-replay.ts +0 -62
  202. package/src/execution/engine/common/kill-chain.ts +0 -221
  203. package/src/execution/engine/common/nesting-guard.ts +0 -50
  204. package/src/execution/engine/common/persona-router.ts +0 -108
  205. package/src/execution/engine/common/pool-manager.ts +0 -226
  206. package/src/execution/engine/common/schema-emulation.ts +0 -189
  207. package/src/execution/engine/common/session-view-projection.ts +0 -51
  208. package/src/execution/engine/engine-discovery.ts +0 -65
  209. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +0 -469
  210. package/src/execution/engine/engines/pi/__tests__/reader.test.ts +0 -155
  211. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  212. package/src/execution/engine/engines/pi/pi-engine.ts +0 -415
  213. package/src/execution/engine/engines/pi/reader.ts +0 -48
  214. package/src/execution/engine/engines/pi/registration.ts +0 -35
  215. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  216. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  217. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  218. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +0 -246
  219. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +0 -228
  220. package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +0 -210
  221. package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +0 -64
  222. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +0 -127
  223. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -580
  224. package/src/execution/engine/engines/zcode/constants.ts +0 -43
  225. package/src/execution/engine/engines/zcode/golden-sample.ts +0 -39
  226. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  227. package/src/execution/engine/engines/zcode/parser.ts +0 -436
  228. package/src/execution/engine/engines/zcode/preparer.ts +0 -363
  229. package/src/execution/engine/engines/zcode/reader.ts +0 -381
  230. package/src/execution/engine/engines/zcode/registration.ts +0 -37
  231. package/src/execution/engine/engines/zcode/zcode-engine.ts +0 -658
  232. package/src/execution/engine/host-task-spec.ts +0 -47
  233. package/src/execution/engine/model-prompt.ts +0 -59
  234. package/src/execution/engine/paths.ts +0 -42
  235. package/src/execution/engine/port.ts +0 -153
  236. package/src/execution/engine/registry.ts +0 -123
  237. package/src/execution/engine/routing.ts +0 -218
  238. package/src/execution/engine/types.ts +0 -309
  239. package/src/execution/execute-options-mapper.ts +0 -112
  240. package/src/execution/execution-record.ts +0 -965
  241. package/src/execution/finalize-record.ts +0 -251
  242. package/src/execution/finalized-marker.ts +0 -51
  243. package/src/execution/get-state-handshake.ts +0 -104
  244. package/src/execution/host-mode.ts +0 -56
  245. package/src/execution/idle-gc.ts +0 -47
  246. package/src/execution/lifecycle-manager.ts +0 -513
  247. package/src/execution/lifecycle-predicates.ts +0 -65
  248. package/src/execution/manifest-store.ts +0 -256
  249. package/src/execution/model-config-service.ts +0 -219
  250. package/src/execution/model-resolver.ts +0 -248
  251. package/src/execution/notifier.ts +0 -330
  252. package/src/execution/notify-ledger.ts +0 -580
  253. package/src/execution/output-collector.ts +0 -228
  254. package/src/execution/path-encoding.ts +0 -60
  255. package/src/execution/pi-invocation.ts +0 -120
  256. package/src/execution/record-entry.ts +0 -132
  257. package/src/execution/record-store.ts +0 -1234
  258. package/src/execution/relay-env.ts +0 -37
  259. package/src/execution/session-context-resolver.ts +0 -64
  260. package/src/execution/session-file-gc.ts +0 -120
  261. package/src/execution/session-pending.ts +0 -164
  262. package/src/execution/session-reconstructor.ts +0 -678
  263. package/src/execution/session-runner.ts +0 -1781
  264. package/src/execution/sessions-index.ts +0 -304
  265. package/src/execution/spawn-event-adapter.ts +0 -363
  266. package/src/execution/stdin-writer.ts +0 -198
  267. package/src/execution/stream-sink.ts +0 -126
  268. package/src/execution/subagent-service.ts +0 -2141
  269. package/src/execution/subprocess-agent-runner.ts +0 -317
  270. package/src/execution/temp-prompt.ts +0 -62
  271. package/src/execution/tombstone-store.ts +0 -72
  272. package/src/execution/turn-limiter.ts +0 -102
  273. package/src/execution/types.ts +0 -973
  274. package/src/execution/ui-channels.ts +0 -216
  275. package/src/execution/ui-interaction-model.ts +0 -48
  276. package/src/execution/ui-request-handler-factory.ts +0 -212
  277. package/src/execution/ui-request-observability.ts +0 -81
  278. package/src/execution/ui-request-queue.ts +0 -184
  279. package/src/execution/worktree-manager.ts +0 -688
  280. package/src/execution/worktree-registry.ts +0 -222
  281. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +0 -349
  282. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +0 -202
  283. package/src/orchestration/__tests__/agent-call-stream.test.ts +0 -152
  284. package/src/orchestration/__tests__/args-validator.test.ts +0 -143
  285. package/src/orchestration/__tests__/config-loader.test.ts +0 -503
  286. package/src/orchestration/__tests__/error-recovery-handlers.test.ts +0 -809
  287. package/src/orchestration/__tests__/error-recovery-postmessage-defense.test.ts +0 -405
  288. package/src/orchestration/__tests__/error-recovery-serialize-failed-result.test.ts +0 -56
  289. package/src/orchestration/__tests__/error-recovery-workflow-call.test.ts +0 -166
  290. package/src/orchestration/__tests__/execute-agent-call.test.ts +0 -451
  291. package/src/orchestration/__tests__/jsonl-run-store-corrupt-entry.test.ts +0 -150
  292. package/src/orchestration/__tests__/jsonl-run-store-loadall-sources.test.ts +0 -171
  293. package/src/orchestration/__tests__/jsonl-run-store-retention.test.ts +0 -202
  294. package/src/orchestration/__tests__/jsonl-run-store-session-file.test.ts +0 -1030
  295. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +0 -600
  296. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +0 -96
  297. package/src/orchestration/__tests__/lifecycle.test.ts +0 -659
  298. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +0 -95
  299. package/src/orchestration/__tests__/review-fix-loop-e2e.test.ts +0 -2025
  300. package/src/orchestration/__tests__/script-lint.test.ts +0 -831
  301. package/src/orchestration/__tests__/skill-discovery.test.ts +0 -201
  302. package/src/orchestration/__tests__/test-mocks.ts +0 -197
  303. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +0 -368
  304. package/src/orchestration/__tests__/worker-host.test.ts +0 -120
  305. package/src/orchestration/__tests__/worker-returnmeta-passthrough.test.ts +0 -164
  306. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +0 -563
  307. package/src/orchestration/__tests__/worker-script-builder.test.ts +0 -309
  308. package/src/orchestration/__tests__/worker-script-template-snapshot.test.ts +0 -129
  309. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +0 -317
  310. package/src/orchestration/__tests__/workflow-script-lint-memo.test.ts +0 -110
  311. package/src/orchestration/__tests__/workflows-e2e.test.ts +0 -537
  312. package/src/orchestration/agent-opts-resolver.ts +0 -174
  313. package/src/orchestration/args-validator.ts +0 -127
  314. package/src/orchestration/config-loader.ts +0 -315
  315. package/src/orchestration/error-recovery.ts +0 -1018
  316. package/src/orchestration/execute-agent-call.ts +0 -256
  317. package/src/orchestration/launcher.ts +0 -455
  318. package/src/orchestration/lifecycle.ts +0 -399
  319. package/src/orchestration/models/__tests__/budget.test.ts +0 -307
  320. package/src/orchestration/models/__tests__/trace.test.ts +0 -408
  321. package/src/orchestration/models/agent-call.ts +0 -83
  322. package/src/orchestration/models/budget.ts +0 -118
  323. package/src/orchestration/models/ports.ts +0 -164
  324. package/src/orchestration/models/run-runtime.ts +0 -104
  325. package/src/orchestration/models/run-spec.ts +0 -83
  326. package/src/orchestration/models/run-state.ts +0 -44
  327. package/src/orchestration/models/trace.ts +0 -183
  328. package/src/orchestration/models/types.ts +0 -301
  329. package/src/orchestration/models/workflow-run.ts +0 -254
  330. package/src/orchestration/models/workflow-script-registry.ts +0 -35
  331. package/src/orchestration/models/workflow-script.ts +0 -117
  332. package/src/orchestration/script-lint.ts +0 -766
  333. package/src/orchestration/skill-discovery.ts +0 -115
  334. package/src/orchestration/worker-handle.ts +0 -115
  335. package/src/orchestration/worker-host.ts +0 -98
  336. package/src/orchestration/worker-script-builder.ts +0 -421
  337. package/src/orchestration/workflow-files.ts +0 -85
  338. package/src/orchestration/workflow-script-registry-impl.ts +0 -133
  339. package/src/shared/__tests__/agent-ref.test.ts +0 -34
  340. package/src/shared/__tests__/meta-parser.test.ts +0 -304
  341. package/src/shared/__tests__/model-ref.test.ts +0 -306
  342. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -280
  343. package/src/shared/__tests__/resource-discovery.test.ts +0 -736
  344. package/src/shared/__tests__/resource-meta.test.ts +0 -51
  345. package/src/shared/__tests__/schema-jsonify.test.ts +0 -81
  346. package/src/shared/__tests__/timer-delay.test.ts +0 -61
  347. package/src/shared/agent-event.ts +0 -13
  348. package/src/shared/agent-ref.ts +0 -57
  349. package/src/shared/meta-parser.ts +0 -261
  350. package/src/shared/model-ref.ts +0 -286
  351. package/src/shared/resource-discovery.ts +0 -811
  352. package/src/shared/resource-meta.ts +0 -65
  353. package/src/shared/schema-env.ts +0 -44
  354. package/src/shared/schema-jsonify.ts +0 -58
  355. package/src/shared/timer-delay.ts +0 -54
  356. package/src/shared/xml-injection.ts +0 -35
  357. package/workflows/README.md +0 -81
  358. package/workflows/_shared/agent-refs.cjs +0 -40
  359. package/workflows/chain.js +0 -131
  360. package/workflows/map-reduce.js +0 -174
  361. package/workflows/parallel.js +0 -148
  362. package/workflows/review-fix-loop-utils.cjs +0 -1309
  363. package/workflows/review-fix-loop.js +0 -1404
  364. package/workflows/scatter-gather.js +0 -178
@@ -1,365 +0,0 @@
1
- // src/execution/__tests__/chatmode-first-round-closure-service.test.ts
2
- //
3
- // [V2 决策 2/3] chatMode 首轮闭环——onRoundSettled 注入 + runAndFinalize early return
4
- // + double-notify 防护(Step 4a 改动 2/3)。
5
- //
6
- // 改动 2:buildSessionRunnerContext 注入 onRoundSettled 回调——设 record.status=idle +
7
- // round+=1 + notifyComplete(轻量 idle 化,不调 doFinalizeRoundToIdle)。
8
- // 改动 3:runAndFinalize 检测 chatMode + status===idle(onRoundSettled 已设)→ early return,
9
- // 不进现有 chatMode 分流(那是 close 后 done/failed/cancelled 终态化的)。
10
- // double-notify 防护:onRoundSettled notify + kickOffBackground.then notify 同 id:round,
11
- // notifier dedup(id:round key,60s TTL)吞第二次——notifier.ts L122 是天然防线。
12
- //
13
- // mock 结构与 run-and-finalize-chatmode.test.ts 一致(mock session-runner + logger,
14
- // 走真实 SubagentService 的 buildSessionRunnerContext / runAndFinalize 分流逻辑)。
15
-
16
- import * as fs from "node:fs";
17
- import * as os from "node:os";
18
- import * as path from "node:path";
19
-
20
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
21
-
22
- // mock logger(doFinalizeRecord 的 manifest 写入降级路径用 logger.error)
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:runSpawn 受控返回 result,killAllSpawnedChildren 空实现。
29
- // [M3] getChildByRecord 返回活进程假句柄:让 piAdapter.hasRunningBackground 的判定只能靠
30
- // 「!hasIdleTimer 排除等待续聊 record」通过(还原生产 Path A:轮次完成、进程保活)。
31
- vi.mock("../session-runner.ts", () => ({
32
- runSpawn: vi.fn(),
33
- killAllSpawnedChildren: vi.fn(),
34
- getChildByRecord: vi.fn(() => ({ killed: false, kill: () => true })),
35
- }));
36
-
37
- import { runSpawn, getChildByRecord } from "../session-runner.ts";
38
- import type { SessionRunnerContext } from "../session-runner.ts";
39
- import { armIdleTimer, disarmIdleTimer } from "../lifecycle-manager.ts";
40
- import { createRecord, updateFromEvent } from "../execution-record.ts";
41
- import { ModelConfigService } from "../model-config-service.ts";
42
- import type { ModelInfo, ModelRegistryLike } from "../model-resolver.ts";
43
- import { RecordStore } from "../record-store.ts";
44
- import { SubagentService } from "../subagent-service.ts";
45
- import type { PiLike } from "../subagent-service.ts";
46
- import type {
47
- AgentResult,
48
- ExecutionRecord,
49
- ExecuteOptions,
50
- } from "../types.ts";
51
-
52
- const mockRunSpawn = vi.mocked(runSpawn);
53
- const mockGetChildByRecord = vi.mocked(getChildByRecord);
54
-
55
- const STUB_MODEL: ModelInfo = {
56
- id: "test-model",
57
- name: "Test",
58
- provider: "test",
59
- reasoning: false,
60
- };
61
-
62
- /** 最小合法 registry(initModel fail-fast 需要;resolveModel 第三层直接透传 ctxModel)。 */
63
- function makeEmptyRegistry(): ModelRegistryLike {
64
- return { getAvailable: () => [], find: () => undefined, hasConfiguredAuth: () => true };
65
- }
66
-
67
- function makeTmpAgentDir(): string {
68
- return fs.mkdtempSync(path.join(os.tmpdir(), "chatmode-closure-"));
69
- }
70
-
71
- function makePi(): PiLike & {
72
- appendEntry: ReturnType<typeof vi.fn>;
73
- events: { emit: ReturnType<typeof vi.fn> };
74
- sendMessage: ReturnType<typeof vi.fn>;
75
- } {
76
- return { appendEntry: vi.fn(), events: { emit: vi.fn() }, sendMessage: vi.fn() };
77
- }
78
-
79
- function makeResult(success: boolean): AgentResult {
80
- return {
81
- text: success ? "done" : "err",
82
- turns: 1,
83
- durationMs: 100,
84
- success,
85
- error: success ? undefined : "boom",
86
- sessionId: "sess-1",
87
- toolCalls: [],
88
- };
89
- }
90
-
91
- function makeRecord(chatMode: boolean, id = "sa-test"): ExecutionRecord {
92
- return createRecord(id, {
93
- agent: "general-purpose",
94
- model: "test-model",
95
- mode: "background",
96
- task: "do something",
97
- slug: "test",
98
- startedAt: 1000,
99
- rootSessionId: "root-session",
100
- chatMode,
101
- });
102
- }
103
-
104
- /** 暴露 runAndFinalize / store / buildSessionRunnerContext / notifyComplete 私有访问。 */
105
- interface ServiceInternals {
106
- store: RecordStore;
107
- buildSessionRunnerContext(): SessionRunnerContext;
108
- notifyComplete(record: ExecutionRecord): void;
109
- runAndFinalize: (
110
- record: ExecutionRecord,
111
- opts: ExecuteOptions,
112
- ctx: SessionRunnerContext,
113
- identity: {
114
- agent: string;
115
- agentConfig: unknown;
116
- resolved: { model: ModelInfo; thinkingLevel: string | undefined };
117
- },
118
- signal: undefined,
119
- priority: number,
120
- ) => Promise<AgentResult>;
121
- }
122
-
123
- describe("[V2 决策 2/3] chatMode 首轮闭环:onRoundSettled 注入 + early return(改动 2/3)", () => {
124
- let agentDir: string;
125
- let modelService: ModelConfigService;
126
- let service: SubagentService;
127
- let internals: ServiceInternals;
128
-
129
- beforeEach(() => {
130
- agentDir = makeTmpAgentDir();
131
- modelService = new ModelConfigService({ agentDir });
132
- service = new SubagentService({ cwd: agentDir, modelService });
133
- service.initSession({ pi: makePi(), sessionId: "root-session" });
134
- internals = service as unknown as ServiceInternals;
135
- mockRunSpawn.mockReset();
136
- });
137
-
138
- afterEach(() => {
139
- service.dispose();
140
- fs.rmSync(agentDir, { recursive: true, force: true });
141
- });
142
-
143
- // ── 改动 2:onRoundSettled 回调注入 ──────────────────────────────────
144
-
145
- it("buildSessionRunnerContext 注入 onRoundSettled:设 running(idle 折入) + round+=1 + notifyComplete", () => {
146
- const ctx = internals.buildSessionRunnerContext();
147
- expect(typeof ctx.onRoundSettled).toBe("function");
148
-
149
- const record = makeRecord(true); // chatMode, status=running, round=undefined
150
- const spy = vi.spyOn(internals, "notifyComplete");
151
-
152
- ctx.onRoundSettled!(record);
153
-
154
- // [改动 2] 轻量 idle 化(v4 B-1:idle 折入 running):status=running(notify 守卫放行)+ round 0→1(dedup key 递增)
155
- expect(record.status).toBe("running");
156
- expect(record.round).toBe(1);
157
- // notifyComplete 被调 1 次,入参是当前 record(此时 running+isIdle,toNotifyRecord 守卫放行)
158
- expect(spy).toHaveBeenCalledTimes(1);
159
- expect(spy).toHaveBeenCalledWith(record);
160
- });
161
-
162
- it("onRoundSettled 连续两轮:round 累加(1→2,dedup key 区分每轮)", () => {
163
- const ctx = internals.buildSessionRunnerContext();
164
- const record = makeRecord(true);
165
- record.round = 1; // 第一轮已完成
166
- vi.spyOn(internals, "notifyComplete");
167
-
168
- ctx.onRoundSettled!(record);
169
-
170
- expect(record.status).toBe("running");
171
- expect(record.round).toBe(2); // 第二轮 round 累加
172
- });
173
-
174
- // ── 改动 3:runAndFinalize chatMode 首轮 early return ────────────────
175
-
176
- /** 直接调私有 runAndFinalize(mock runSpawn 后走完整分流逻辑)。 */
177
- async function callRunAndFinalize(record: ExecutionRecord, success: boolean): Promise<void> {
178
- mockRunSpawn.mockResolvedValueOnce(makeResult(success));
179
- const opts: ExecuteOptions = { task: "do something", slug: "test" };
180
- const ctx: SessionRunnerContext = {
181
- cwd: agentDir,
182
- agentDir,
183
- skillDirs: [],
184
- mainCwd: agentDir,
185
- };
186
- const identity = {
187
- agent: "general-purpose",
188
- agentConfig: undefined,
189
- resolved: { model: STUB_MODEL, thinkingLevel: undefined },
190
- };
191
- await internals.runAndFinalize(record, opts, ctx, identity, undefined, 0);
192
- }
193
-
194
- it("runAndFinalize chatMode 首轮(isIdle:timer armed)early return:不进 finalize 分流,record 保持 running-resumable", async () => {
195
- // [N3] 真实 isIdle 路径:守卫判据是 `record.chatMode && isIdle(record)`(isIdle = hasIdleTimer,
196
- // 非 status 值)。旧用例手工赋 status="idle" 且未 arm timer——守卫求值 false 不 early return,
197
- // 通过路径是「非法状态值被 tryTransition 拒绝」,与守卫无关(假覆盖)。此处构造生产可达状态:
198
- // agent_settled 已执行(armIdleTimer + onRoundSettled round+1),status 保持 running。
199
- const record = makeRecord(true);
200
- record.status = "running";
201
- record.round = 1; // onRoundSettled 已 +1
202
- internals.store.register(record);
203
- armIdleTimer(record.id, () => {}); // 真实 arm(isIdle=true),模拟 agent_settled
204
- try {
205
- await callRunAndFinalize(record, true);
206
-
207
- // [改动 3] early return 守卫正分支生效:不进 finalize 分流——round 不被
208
- // finalizeRoundToIdle 二次 +1(仍 1)、record.result 不被覆写(MF-2 写点会写
209
- // makeResult.text="done",early return 后保持 undefined)、未 archive、store 仍持有。
210
- expect(record.status).toBe("running");
211
- expect(record.round).toBe(1);
212
- expect(record.result).toBeUndefined();
213
- expect(internals.store.getMutable(record.id)).toBe(record);
214
- } finally {
215
- disarmIdleTimer(record.id);
216
- }
217
- });
218
-
219
- it("runAndFinalize chatMode 首轮 early return 不依赖 tryTransition:close 后 done 分流仍对 running record 生效", async () => {
220
- // 对照组:chatMode + status=running(未经 onRoundSettled,如旧路径或测试直调)→
221
- // tryTransition 成功 → 进现有 chatMode done 分流(finalizeRoundToIdle)。证明 early return
222
- // 守卫只挡 onRoundSettled 设的 idle,不误伤 running record 的正常分流。
223
- const record = makeRecord(true);
224
- record.status = "running";
225
- internals.store.register(record);
226
- await callRunAndFinalize(record, true);
227
-
228
- expect(record.status).toBe("running"); // finalizeRoundToIdle 设的(v4 B-1 idle 折入 running)
229
- expect(record.round).toBe(1); // finalizeRoundToIdle +1
230
- expect(internals.store.getMutable(record.id)).toBe(record);
231
- });
232
-
233
- // ── double-notify 防护 + [M3] 轮次完成通知立即送达 ───────────────────
234
-
235
- it("[M3] chatMode 轮次完成通知立即送达(不挂 60s 合并窗口)+ 同 id:round dedup 吞第二次", () => {
236
- // 生产场景:chatMode 轮次完成(agent_settled arm idle timer → onRoundSettled →
237
- // notifyComplete),record 留 store、status=running、进程保活(Path A)。
238
- // 旧 piAdapter.hasRunningBackground 按 mode==="background" 计数 → 对该 record 恒 true
239
- // → notify 恒挂 60s 合并窗口,主 agent 的续聊回复固定延迟 60s(G1 失效)。
240
- // 修复后排除 isIdle(timer armed)record → 立即 flush。
241
- //
242
- // 走真实 SubagentService.notifyComplete → BgNotifier → piAdapter(真实 host adapter),
243
- // 不再手搓 NotifierHost mock 绕过生产行为(旧用例 `hasRunningBackground: () => false`
244
- // 是对生产行为的 mock 绕过)。
245
- //
246
- // [N2] 禁止手工预置 record.result(这正是掩盖「通知正文恒 (empty)」断链的方式)——
247
- // 轮次文本经真实 updateFromEvent 累积进 turns,真实 onRoundSettled 回调派生写入
248
- // record.result(对齐 collectResult 的 getFullText)后 notify。
249
- const pi = makePi();
250
- service.initSession({ pi, sessionId: "root-session" }); // 换上带 spy 的 pi
251
- const record = makeRecord(true);
252
- record.status = "running";
253
- updateFromEvent(record, { type: "text_delta", delta: "first-round-done" });
254
- updateFromEvent(record, { type: "turn_end" });
255
- internals.store.register(record);
256
- armIdleTimer(record.id, () => {}); // 模拟 agent_settled:轮次完成、等待续聊(Path A)
257
- try {
258
- // 真实回调链:round 0→1 + record.result 从 turns 派生 + notifyComplete
259
- internals.buildSessionRunnerContext().onRoundSettled!(record);
260
- expect(record.round).toBe(1);
261
- expect(record.result).toBe("first-round-done"); // 真实派生(非手工预置)
262
-
263
- // [M3] 立即 flush——同步断言 sendMessage 已发出(旧实现此处挂 60s timer,0 次调用)
264
- expect(pi.sendMessage).toHaveBeenCalledTimes(1);
265
- const sentMsg = pi.sendMessage.mock.calls[0]![0] as { customType: string; content: string };
266
- expect(sentMsg.customType).toBe("subagent-bg-notify");
267
- expect(sentMsg.content).toContain("finished a round");
268
- expect(sentMsg.content).toContain("first-round-done");
269
-
270
- // double-notify 防护(原用例语义保留):onRoundSettled notify + kickOffBackground.then
271
- // notifyComplete 同 id:round → notifier dedup key=`${id}:${round}` 60s 内吞第二次。
272
- internals.notifyComplete(record);
273
- expect(pi.sendMessage).toHaveBeenCalledTimes(1);
274
- } finally {
275
- disarmIdleTimer(record.id);
276
- }
277
-
278
- // 对照:真在跑的 background 工作(活进程 + 无 timer)仍计入合并窗口——closed 通知
279
- // 挂 60s 不立即发送(合并窗口语义对真正的并发完成保留)。
280
- const busy = makeRecord(false, "sa-busy");
281
- busy.status = "running";
282
- internals.store.register(busy); // 活进程(mock 恒返回)+ 无 timer = busy,计入
283
- const done = makeRecord(false, "sa-done");
284
- done.status = "closed"; // 终态 notify(toNotifyRecord 放行 closed)
285
- try {
286
- internals.notifyComplete(done);
287
- expect(pi.sendMessage).toHaveBeenCalledTimes(1); // 未新增——busy 挂起合并窗口
288
- } finally {
289
- // busy/done 无 timer,无需 disarm;record 由 afterEach dispose 清理
290
- }
291
- });
292
- });
293
-
294
- // ── [N1] one-shot 成功完成通知(SP-5 回退 resumable 后仍送达)──────────────
295
-
296
- describe("[N1] one-shot 成功完成通知:SP-5 回退 resumable 后仍送达", () => {
297
- let agentDir: string;
298
- let modelService: ModelConfigService;
299
- let service: SubagentService;
300
- let internals: ServiceInternals;
301
-
302
- beforeEach(() => {
303
- agentDir = makeTmpAgentDir();
304
- modelService = new ModelConfigService({ agentDir });
305
- service = new SubagentService({ cwd: agentDir, modelService });
306
- service.initSession({ pi: makePi(), sessionId: "root-session" });
307
- internals = service as unknown as ServiceInternals;
308
- mockRunSpawn.mockReset();
309
- });
310
-
311
- afterEach(() => {
312
- service.dispose();
313
- fs.rmSync(agentDir, { recursive: true, force: true });
314
- // 还原默认活进程句柄实现(其他用例的 busy 判定依赖)
315
- mockGetChildByRecord.mockImplementation(() => ({ killed: false, kill: () => true }));
316
- });
317
-
318
- it("真实 execute + mock runSpawn(success) → 恰 1 条 subagent-bg-notify(status=closed、正文含真实结果),record 保持可升级", async () => {
319
- // 真实链路:execute → kickOffBackground → runAndFinalize(SP-5 分支 → finalizeRoundToIdle
320
- // 回退 running-resumable)→ .then notifyComplete → BgNotifier → pi.sendMessage。
321
- // round2 审查实证:旧守卫(closed/isIdle only)对 SP-5 完成态恒拒绝 → 发送数 0。
322
- modelService.initModel({
323
- modelRegistry: makeEmptyRegistry(),
324
- sessionId: "root-session",
325
- ctxModel: STUB_MODEL,
326
- });
327
- const pi = makePi();
328
- service.initSession({ pi, sessionId: "root-session" }); // 换上带 spy 的 pi
329
-
330
- // one-shot 成功完成态:进程已死(close 后 spawnedChildren 已清理)——getChildByRecord
331
- // 返回 undefined,SP-5 回退的 record 因此 isResumable(running + 无活进程)。
332
- mockRunSpawn.mockResolvedValueOnce(makeResult(true));
333
- mockGetChildByRecord.mockImplementation(() => undefined);
334
- try {
335
- const handle = await service.execute({ task: "one shot task", slug: "oneshot-n1" });
336
-
337
- await vi.waitFor(() => {
338
- expect(pi.sendMessage).toHaveBeenCalledTimes(1);
339
- });
340
- const sentMsg = pi.sendMessage.mock.calls[0]![0] as {
341
- customType: string;
342
- content: string;
343
- details?: { status?: string };
344
- };
345
- expect(sentMsg.customType).toBe("subagent-bg-notify");
346
- // 完成语义:status=closed(对齐 tool 契约 "runs once, notifies on completion";
347
- // running 分支文案不含 worktree patchFile 提示,one-shot 需要 closed 分支)
348
- expect(sentMsg.details?.status).toBe("closed");
349
- expect(sentMsg.content).toContain("completed");
350
- expect(sentMsg.content).toContain("done"); // makeResult(true).text,经 MF-2 写入 record.result
351
-
352
- // 恰好 1 条:无第二个通知点(onRoundSettled 仅 chatMode;.then 后无再触发)
353
- await new Promise((r) => setTimeout(r, 20));
354
- expect(pi.sendMessage).toHaveBeenCalledTimes(1);
355
-
356
- // SP-5 语义不破坏:record 回退 running-resumable(可 message 升级续聊),未终态化
357
- const record = internals.store.getMutable(handle.subagentId);
358
- expect(record).toBeDefined();
359
- expect(record!.status).toBe("running");
360
- expect(record!.result).toBe("done");
361
- } finally {
362
- mockGetChildByRecord.mockImplementation(() => ({ killed: false, kill: () => true }));
363
- }
364
- });
365
- });
@@ -1,190 +0,0 @@
1
- // src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts
2
- //
3
- // [V2 决策 2] chatMode 首轮闭环——runSpawn resolve 时机(Step 4a 改动 1)。
4
- //
5
- // 改动 1:chatMode agent_settled 时提前 resolve runSpawn 的 exitCode promise(exit code 0),
6
- // 让 runAndFinalize 拿到首轮 result,进程仍保活(idle timer armed)。非 chatMode 不变
7
- //(agent_end kill → close resolve)。
8
- //
9
- // 本文件验证 resolve 时机(run-spawn-chatmode-settled.test.ts 验证 idle timer + 回调挂载点,
10
- // 收尾靠 emit close 让 resolve——隐含 agent_settled 不 resolve;本文件显式断言 agent_settled
11
- // 即 resolve,且进程未 close)。
12
- //
13
- // mock 结构与 run-spawn-chatmode-settled.test.ts 一致(FakeChild + session-pending count=0)。
14
-
15
- import { spawn } from "node:child_process";
16
- import * as fs from "node:fs";
17
-
18
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
19
-
20
- vi.mock("node:child_process", async () => {
21
- const { FakeChild } = await import("./helpers/spawn-mock.ts");
22
- return {
23
- spawn: vi.fn(() => new FakeChild()),
24
- // buildEnvBlock 的 git branch 调用(execFile 异步):默认 err-first 兜底 → catch → branch=""
25
- execFile: vi.fn(
26
- (
27
- _cmd: string,
28
- _args: readonly string[],
29
- _opts: unknown,
30
- cb: (err: Error | null, stdout?: string, stderr?: string) => void,
31
- ) => cb(new Error("execFile not configured in this test")),
32
- ),
33
- };
34
- });
35
-
36
- vi.mock("node:fs", async () => {
37
- const actual = await import("node:fs");
38
- return {
39
- default: {
40
- ...actual,
41
- mkdirSync: vi.fn(),
42
- existsSync: vi.fn(() => false),
43
- appendFileSync: vi.fn(),
44
- writeFileSync: vi.fn(),
45
- readdirSync: vi.fn(() => []),
46
- },
47
- mkdirSync: vi.fn(),
48
- existsSync: vi.fn(() => false),
49
- appendFileSync: vi.fn(),
50
- writeFileSync: vi.fn(),
51
- readdirSync: vi.fn(() => []),
52
- promises: actual.promises,
53
- };
54
- });
55
-
56
- vi.mock("../alive-store.ts", () => ({ writeAliveMarker: vi.fn() }));
57
-
58
- // chatMode agent_end 早返回(continue),不读 session-pending;统一 count=0 防干扰。
59
- vi.mock("../session-pending.ts", () => ({
60
- readActivePendingFromSessionFile: vi.fn(() => ({ count: 0 })),
61
- }));
62
-
63
- vi.mock("../temp-prompt.ts", () => ({
64
- writePromptToTempFile: vi.fn(async (agent: string) => {
65
- const safeName = agent.replace(/[^\w.-]+/g, "_");
66
- return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
67
- }),
68
- cleanupTempPrompt: vi.fn(async () => {}),
69
- }));
70
-
71
- import { runSpawn } from "../session-runner.ts";
72
- import type { SessionRunnerContext } from "../session-runner.ts";
73
- import { _resetLifecycleState } from "../lifecycle-manager.ts";
74
- import { createRecord } from "../execution-record.ts";
75
- import type { ExecutionRecord } from "../types.ts";
76
- import {
77
- emitStdoutLine,
78
- type FakeChild,
79
- lastSpawnedChild as lastSpawnedChildOf,
80
- makeCtx,
81
- makeOpts,
82
- makeRecord,
83
- sessionHeader,
84
- waitForSpawn as waitForSpawnOf,
85
- } from "./helpers/spawn-mock.ts";
86
-
87
- const mockSpawn = vi.mocked(spawn);
88
-
89
- const lastSpawnedChild = (): FakeChild => lastSpawnedChildOf(mockSpawn);
90
- const waitForSpawn = (timeoutMs = 1000): Promise<void> => waitForSpawnOf(mockSpawn, timeoutMs);
91
-
92
- /** 构造 chatMode record(spawn-mock.makeRecord 默认非 chatMode,此处显式开启)。 */
93
- function makeChatModeRecord(id = "sa-chat"): ExecutionRecord {
94
- return createRecord(id, {
95
- agent: "general-purpose",
96
- model: "test-model",
97
- mode: "sync",
98
- task: "chat task",
99
- slug: "chat",
100
- startedAt: 1_000_000,
101
- rootSessionId: "root-session",
102
- parentRecordId: undefined,
103
- depth: 0,
104
- chatMode: true,
105
- });
106
- }
107
-
108
- /** 用 race 探测 promise 是否在指定时间内 settle(resolve 或 reject)。 */
109
- async function settlesWithin(p: Promise<unknown>, ms: number): Promise<boolean> {
110
- return Promise.race([
111
- p.then(
112
- () => true,
113
- () => true,
114
- ),
115
- new Promise<boolean>((r) => setTimeout(() => r(false), ms)),
116
- ]);
117
- }
118
-
119
- describe("[V2 决策 2] chatMode 首轮闭环:runSpawn resolve 时机(改动 1)", () => {
120
- beforeEach(() => {
121
- vi.clearAllMocks();
122
- _resetLifecycleState();
123
- });
124
-
125
- afterEach(() => {
126
- vi.restoreAllMocks();
127
- vi.useRealTimers();
128
- _resetLifecycleState();
129
- });
130
-
131
- it("chatMode agent_settled → runSpawn 提前 resolve(不等 close),进程仍保活", async () => {
132
- const record = makeChatModeRecord("sa-first-resolve");
133
- const promise = runSpawn(record, "Task: first-round", makeOpts(), makeCtx());
134
-
135
- await waitForSpawn();
136
- const child = lastSpawnedChild();
137
-
138
- emitStdoutLine(child, sessionHeader());
139
- emitStdoutLine(child, { type: "agent_end", willRetry: false });
140
- // 让 stdout flush 处理 agent_end(chatMode 分支 continue,不 kill)
141
- await new Promise((r) => setTimeout(r, 10));
142
- emitStdoutLine(child, { type: "agent_settled" });
143
-
144
- // [改动 1 核心] agent_settled 后 runSpawn 立即 resolve(resolveRun(0)),不等 close。
145
- // 若改动 1 未生效,promise 会 hang 等 close,settlesWithin 返回 false。
146
- expect(await settlesWithin(promise, 500)).toBe(true);
147
- const result = await promise;
148
- expect(result.success).toBe(true);
149
- expect(result.sessionId).toBe("sess-abc");
150
-
151
- // 进程仍保活:agent_settled 只 resolve promise,未 kill、未 emit close。
152
- // idle timer(5min 真实 setTimeout)armed 但未到期。
153
- expect(child.killed).toBe(false);
154
-
155
- // 收尾:手动 close(模拟后续 idle timer 超时 kill / 外部终止)。
156
- // close handler 的 cleanup 仍执行(resolve(code) 是 no-op,Promise 已 resolve)。
157
- child.stdout.end();
158
- child.stderr.end();
159
- child.emit("close", 0);
160
- });
161
-
162
- it("非 chatMode runSpawn 仍在 close resolve(agent_settled 不触发 resolve)", async () => {
163
- // makeRecord 默认非 chatMode
164
- const record = makeRecord("sa-nonchat-resolve");
165
- const promise = runSpawn(record, "Task: nonchat", makeOpts(), makeCtx());
166
-
167
- await waitForSpawn();
168
- const child = lastSpawnedChild();
169
-
170
- emitStdoutLine(child, sessionHeader());
171
- emitStdoutLine(child, { type: "agent_end", willRetry: false });
172
- // 非 chatMode agent_end → count=0 → SIGTERM kill
173
- await new Promise((r) => setTimeout(r, 10));
174
- expect(child.killed).toBe(true);
175
-
176
- // agent_settled 在非 chatMode 下被忽略(handler 直接 return,不调 resolveRun)
177
- emitStdoutLine(child, { type: "agent_settled" });
178
- await new Promise((r) => setTimeout(r, 20));
179
-
180
- // [回归防护] agent_settled 不 resolve 非 chatMode runSpawn——promise 仍 pending(等 close)。
181
- expect(await settlesWithin(promise, 200)).toBe(false);
182
-
183
- // emit close 才 resolve(现有行为,改动 1 不影响非 chatMode)
184
- child.stdout.end();
185
- child.stderr.end();
186
- child.emit("close", 143);
187
- const result = await promise;
188
- expect(result.success).toBe(true); // 信号终止(143 ≥ threshold)视为正常完成
189
- });
190
- });