@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,644 +0,0 @@
1
- // src/__tests__/run-spawn-edges.test.ts
2
- //
3
- // runSpawn 的 stdout 边界与 orphan 进程兜底集成测试(从 run-spawn-integration.test.ts 拆出)。
4
- //
5
- // 本文件覆盖:
6
- // - [C1] orphan 进程兜底:killAllSpawnedChildren 对未退出 child 发 SIGTERM。
7
- // - [M8] stdout 边界:损坏行(非法 JSON / 缺 type)静默忽略 + 残留尾行(close 前无 \n)
8
- // 由 close handler 再 parse。
9
- // - [agent_end] rpc 长驻进程自然完成:agent_end(willRetry=false)→ kill SIGTERM 触发 close。
10
- //
11
- // mock 工厂 + FakeChild + 工具函数共享自 helpers/spawn-mock.ts(详见该文件头注释)。
12
- // vi.mock 必须各文件独立声明(文件作用域),工厂内用 `await import` 取回 FakeChild。
13
-
14
- import { spawn } from "node:child_process";
15
- import * as fs from "node:fs";
16
-
17
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
18
-
19
- vi.mock("node:child_process", async () => {
20
- const { FakeChild } = await import("./helpers/spawn-mock.ts");
21
- return {
22
- spawn: vi.fn(() => new FakeChild()),
23
- // buildEnvBlock 的 git branch 调用(execFile 异步):默认 err-first 兜底 → catch → branch=""
24
- execFile: vi.fn(
25
- (
26
- _cmd: string,
27
- _args: readonly string[],
28
- _opts: unknown,
29
- cb: (err: Error | null, stdout?: string, stderr?: string) => void,
30
- ) => cb(new Error("execFile not configured in this test")),
31
- ),
32
- };
33
- });
34
-
35
- vi.mock("node:fs", async () => {
36
- const actual = await import("node:fs");
37
- return {
38
- default: {
39
- ...actual,
40
- mkdirSync: vi.fn(),
41
- existsSync: vi.fn(() => false),
42
- appendFileSync: vi.fn(),
43
- writeFileSync: vi.fn(),
44
- readdirSync: vi.fn(() => []),
45
- },
46
- mkdirSync: vi.fn(),
47
- existsSync: vi.fn(() => false),
48
- appendFileSync: vi.fn(),
49
- writeFileSync: vi.fn(),
50
- readdirSync: vi.fn(() => []),
51
- promises: actual.promises,
52
- };
53
- });
54
-
55
- vi.mock("../alive-store.ts", () => ({
56
- writeAliveMarker: vi.fn(),
57
- }));
58
-
59
- // [recursive-orchestration] agent_end 后代判定独立 mock(判定函数本身在 session-pending.test.ts 单独测)。
60
- vi.mock("../session-pending.ts", () => ({
61
- readActivePendingFromSessionFile: vi.fn(() => ({ count: 0 })),
62
- }));
63
-
64
- vi.mock("../temp-prompt.ts", () => ({
65
- writePromptToTempFile: vi.fn(async (agent: string) => {
66
- const safeName = agent.replace(/[^\w.-]+/g, "_");
67
- return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
68
- }),
69
- cleanupTempPrompt: vi.fn(async () => {}),
70
- }));
71
-
72
- import { killAllSpawnedChildren, runSpawn, spawnedChildren, WAKEUP_GRACE_MS, computeWatchdogMs } from "../session-runner.ts";
73
- import { readActivePendingFromSessionFile } from "../session-pending.ts";
74
- import {
75
- emitStdoutLine,
76
- type FakeChild,
77
- lastSpawnedChild as lastSpawnedChildOf,
78
- makeCtx,
79
- makeOpts,
80
- makeRecord,
81
- sessionHeader,
82
- waitForSpawn as waitForSpawnOf,
83
- } from "./helpers/spawn-mock.ts";
84
-
85
- const mockSpawn = vi.mocked(spawn);
86
- const mockExistsSync = vi.mocked(fs.existsSync);
87
-
88
- // 绑定到本文件 mockSpawn 的 lastSpawnedChild/waitForSpawn(需读 mockSpawn.mock.results)
89
- const lastSpawnedChild = (): FakeChild => lastSpawnedChildOf(mockSpawn);
90
- const waitForSpawn = (timeoutMs = 1000): Promise<void> => waitForSpawnOf(mockSpawn, timeoutMs);
91
-
92
- // ============================================================
93
- // 测试
94
- // ============================================================
95
-
96
- describe("runSpawn", () => {
97
- beforeEach(() => {
98
- vi.clearAllMocks();
99
- // existsSync 默认 false(sessionFile 不存在兜底路径)
100
- mockExistsSync.mockReturnValue(false);
101
- });
102
-
103
- afterEach(() => {
104
- vi.restoreAllMocks();
105
- });
106
-
107
- // ── 1. orphan 进程兜底(C1)──
108
- //
109
- // [C1] runSpawn 把每个 spawned child(sync + background)注册到模块级 spawnedChildren Set。
110
- // SubagentService.dispose 调 killAllSpawnedChildren 遍历该 Set 对仍存活的子进程发 SIGTERM,
111
- // 覆盖 sync 子进程(controller=undefined,abortRunningControllers 跳过它)。
112
- //
113
- // 关键验证:
114
- // 1. child 退出(close/error)后从 Set 移除 → killAllSpawnedChildren 不重复 kill。
115
- // 2. child 未退出时 killAllSpawnedChildren → child.kill("SIGTERM") 被调。
116
- // 3. 已 kill 的 child 二次调用无害(killAllSpawnedChildren 跳过 killed=true 的)。
117
- describe("orphan 进程兜底 (C1)", () => {
118
- it("未退出的 child → killAllSpawnedChildren 对它发 SIGTERM", async () => {
119
- const record = makeRecord();
120
- // 不 await——runSpawn 内部 await 子进程 close,killAllSpawnedChildren 测试在 close 前
121
- const promise = runSpawn(record, "Task: orphan", makeOpts(), makeCtx());
122
-
123
- await waitForSpawn();
124
- const child = lastSpawnedChild();
125
-
126
- // spawn 后 child.killed 应为 false(尚未被 kill)
127
- expect(child.killed).toBe(false);
128
-
129
- // dispose 兜底:killAllSpawnedChildren 应 kill 未退出的 child
130
- const n = killAllSpawnedChildren();
131
- expect(n).toBeGreaterThanOrEqual(1);
132
- expect(child.killed).toBe(true);
133
- expect(child.killSignal).toBe("SIGTERM");
134
-
135
- // 收尾:emit close 让 runSpawn resolve(避免悬挂)
136
- emitStdoutLine(child, sessionHeader());
137
- child.stdout.end();
138
- child.emit("close", 143);
139
-
140
- const result = await promise;
141
- expect(result.success).toBe(true); // 信号终止视为正常完成
142
- });
143
-
144
- it("已 close 的 child → 从 Set 移除,killAllSpawnedChildren 不重复 kill", async () => {
145
- const record = makeRecord();
146
- const promise = runSpawn(record, "Task: closed", makeOpts(), makeCtx());
147
-
148
- await waitForSpawn();
149
- const child = lastSpawnedChild();
150
-
151
- // 子进程正常退出
152
- emitStdoutLine(child, sessionHeader());
153
- child.stdout.end();
154
- child.emit("close", 0);
155
-
156
- await promise;
157
- expect(child.killed).toBe(false); // 正常退出未触发 kill
158
-
159
- // close 后 child 已从 Set 移除;再调 killAllSpawnedChildren 不会 kill 它
160
- const n = killAllSpawnedChildren();
161
- expect(n).toBe(0);
162
- expect(child.killed).toBe(false);
163
- });
164
-
165
- it("多个未退出 child(sync + bg)→ killAllSpawnedChildren 全部 kill", async () => {
166
- // spawn 两个 child(模拟 sync + background 并发),都未退出。
167
- const rec1 = makeRecord("spawn-c1");
168
- const p1 = runSpawn(rec1, "Task: c1", makeOpts(), makeCtx());
169
- await waitForSpawn();
170
- const c1 = lastSpawnedChild();
171
-
172
- const rec2 = makeRecord("spawn-c2");
173
- const p2 = runSpawn(rec2, "Task: c2", makeOpts(), makeCtx());
174
- // waitForSpawn 是快照语义(记 baseline 等新 spawn),天然支持二次等待
175
- await waitForSpawn();
176
- const c2 = lastSpawnedChild();
177
-
178
- // c1 和 c2 是不同实例
179
- expect(c1).not.toBe(c2);
180
- expect(c1.killed).toBe(false);
181
- expect(c2.killed).toBe(false);
182
-
183
- // dispose 兜底 kill 两个
184
- const n = killAllSpawnedChildren();
185
- expect(n).toBeGreaterThanOrEqual(2);
186
- expect(c1.killed).toBe(true);
187
- expect(c2.killed).toBe(true);
188
-
189
- // 收尾
190
- for (const { child, promise } of [
191
- { child: c1, promise: p1 },
192
- { child: c2, promise: p2 },
193
- ]) {
194
- emitStdoutLine(child, sessionHeader());
195
- child.stdout.end();
196
- child.emit("close", 143);
197
- const r = await promise;
198
- expect(r.success).toBe(true);
199
- }
200
- });
201
-
202
- // [dispose-cleanup Minor 优化2] killAllSpawnedChildren 末尾 clear spawnedChildren Set。
203
- // 防主进程崩溃/close 事件漏触发时 Set 无限增长。正常路径 close 事件会 delete(保留 per-child
204
- // 精细清理语义);killAllSpawnedChildren 是 dispose 全量兼底。
205
- it("killAllSpawnedChildren 后 spawnedChildren Set 被 clear(size===0)", async () => {
206
- // 前置清理:即他测试可能残留的 child(close 未触发场景)
207
- killAllSpawnedChildren();
208
- expect(spawnedChildren.size).toBe(0);
209
-
210
- // spawn 两个未 close 的 child(模拟 close 事件漏触发的极端累积场景)
211
- const rec1 = makeRecord("clear-c1");
212
- const p1 = runSpawn(rec1, "Task: clear-1", makeOpts(), makeCtx());
213
- await waitForSpawn();
214
- const c1 = lastSpawnedChild();
215
-
216
- const rec2 = makeRecord("clear-c2");
217
- const p2 = runSpawn(rec2, "Task: clear-2", makeOpts(), makeCtx());
218
- // 快照语义二次等待:等 rec2 的 runSpawn 调到 spawn
219
- await waitForSpawn();
220
- const c2 = lastSpawnedChild();
221
-
222
- // 两个 child 都在 Set 中
223
- expect(spawnedChildren.size).toBe(2);
224
-
225
- // dispose 兼底:kill + clear
226
- const n = killAllSpawnedChildren();
227
- expect(n).toBe(2);
228
- expect(c1.killed).toBe(true);
229
- expect(c2.killed).toBe(true);
230
- // Set 被 clear(兑底防泄漏)
231
- expect(spawnedChildren.size).toBe(0);
232
-
233
- // 再次调用:Set 已空,返回 0(不会重复 kill 已 kill 的 child)
234
- const n2 = killAllSpawnedChildren();
235
- expect(n2).toBe(0);
236
-
237
- // 收尾
238
- for (const { child, promise } of [
239
- { child: c1, promise: p1 },
240
- { child: c2, promise: p2 },
241
- ]) {
242
- emitStdoutLine(child, sessionHeader());
243
- child.stdout.end();
244
- child.emit("close", 143);
245
- const r = await promise;
246
- expect(r.success).toBe(true);
247
- }
248
- });
249
- });
250
-
251
- // ── 2. stdout 边界:损坏行 + 残留尾行 (M8) ──
252
- //
253
- // [M8] runSpawn 的 stdout 解析容错:
254
- // - parseSpawnLine 对「非法 JSON」「合法 JSON 但缺 type 字段」归为 kind:"invalid"。
255
- // - runSpawn 的 data 处理器只认 header/event 两类,invalid 行静默忽略(L559 注释
256
- // "invalid 行忽略")——单行损坏不应中断整个事件流。
257
- // - close 前 stdoutBuffer 若残留未以 \n 结尾的合法 event 行,close handler 会再 parse
258
- // 一次(L574-579)——覆盖子进程末行漏 \n 的场景。
259
- describe("stdout 边界:损坏行 + 残留尾行 (M8)", () => {
260
- it("stdout 夹杂非法 JSON 行 → 该行被忽略,合法 turn_end 正常计数(不抛错)", async () => {
261
- const record = makeRecord();
262
- const promise = runSpawn(record, "Task: garbage", makeOpts(), makeCtx());
263
-
264
- // 等待 spawn 被调用拿到 child
265
- await waitForSpawn();
266
- const child = lastSpawnedChild();
267
-
268
- emitStdoutLine(child, sessionHeader());
269
- // 非法 JSON 行(如 pi 的调试输出 / 进度条残片)—— parseSpawnLine 归为 invalid
270
- child.stdout.write("this is not json\n");
271
- // 合法 turn_end 事件
272
- emitStdoutLine(child, { type: "turn_end" });
273
- child.stdout.end();
274
- child.emit("close", 0);
275
-
276
- const result = await promise;
277
-
278
- expect(result.success).toBe(true);
279
- expect(record.turnCount).toBe(1); // 非法行被忽略,仅 turn_end 计数
280
- });
281
-
282
- it("stdout 夹杂合法 JSON 但缺 type 字段 → 该行被忽略,不抛错", async () => {
283
- const record = makeRecord();
284
- const promise = runSpawn(record, "Task: notype", makeOpts(), makeCtx());
285
-
286
- // 等待 spawn 被调用拿到 child
287
- await waitForSpawn();
288
- const child = lastSpawnedChild();
289
-
290
- emitStdoutLine(child, sessionHeader());
291
- // 合法 JSON 但无 type 字段 —— parseSpawnLine 归为 invalid("missing string 'type'")
292
- child.stdout.write('{"foo":"bar"}\n');
293
- emitStdoutLine(child, { type: "turn_end" });
294
- child.stdout.end();
295
- child.emit("close", 0);
296
-
297
- const result = await promise;
298
-
299
- expect(result.success).toBe(true);
300
- expect(record.turnCount).toBe(1); // 无 type 行被忽略
301
- });
302
-
303
- it("残留尾行(close 前未以 \\n 结尾的合法 event)→ close handler 再 parse 处理", async () => {
304
- // 覆盖 session-runner.ts L574-579:close 前 stdoutBuffer 残留的合法 event 行。
305
- //
306
- // 关键:不能用 emitStdoutLine(它会补 \n,残留行在 data 处理器就被 split 消费了,
307
- // 走不到 close handler 的残留 parse 分支)。需同步 emit data(无 \n)确保该行
308
- // 残留在 stdoutBuffer 直到 close handler 处理。
309
- //
310
- // 同步 emit 的必要性:PassThrough 的 .write() 会把 data flush 排到后续微任务,
311
- // 若先 .write() 再 emit("close"),close listener 同步执行时 stdoutBuffer 仍为空
312
- // → 残留逻辑被跳过 → turnCount=0(测出真实 bug 风险)。直接 emit("data", ...) 同步
313
- // 触发 data 处理器,使行残留在 buffer(split("\n") 无换行 → pop 回 buffer),close
314
- // handler 才能捕到它。
315
- const record = makeRecord();
316
- const promise = runSpawn(record, "Task: tail", makeOpts(), makeCtx());
317
-
318
- // 等待 spawn 被调用拿到 child
319
- await waitForSpawn();
320
- const child = lastSpawnedChild();
321
-
322
- emitStdoutLine(child, sessionHeader());
323
- // 合法 turn_end 但不带末尾 \n:同步 emit(绕过 write 的异步 flush)。
324
- // data 处理器把它整体留在 stdoutBuffer(无 \n → split 后 pop 回 buffer),
325
- // 由 close handler 的残留 parse 逻辑(L574-579)处理。
326
- child.stdout.emit("data", JSON.stringify({ type: "turn_end" }));
327
- child.emit("close", 0);
328
-
329
- const result = await promise;
330
-
331
- expect(result.success).toBe(true);
332
- expect(record.turnCount).toBe(1); // 残留尾行被 close handler 正确解析
333
- });
334
-
335
- it("同一 JSON 行跨 3 次 data 事件分片 → stdoutBuffer 字符串拼接后正确解析", async () => {
336
- // 覆盖 stdoutBuffer += data 的字符串拼接(setEncoding("utf8") 后 data 收到 string,
337
- // 非 Buffer)。拆成 3 片写入(跨 type 字段名边界 + 跨 turn_end 值边界),验证拼接无误。
338
- // .write() 的异步 flush 在 await promise(resolve 排在 data 微任务之后)前完成。
339
- const record = makeRecord();
340
- const promise = runSpawn(record, "Task: split3", makeOpts(), makeCtx());
341
-
342
- // 等待 spawn 被调用拿到 child
343
- await waitForSpawn();
344
- const child = lastSpawnedChild();
345
-
346
- emitStdoutLine(child, sessionHeader());
347
- child.stdout.write('{"typ');
348
- child.stdout.write('e":"turn_en');
349
- child.stdout.write('d"}\n');
350
- child.stdout.end();
351
- child.emit("close", 0);
352
-
353
- const result = await promise;
354
-
355
- expect(result.success).toBe(true);
356
- expect(record.turnCount).toBe(1); // 3 片拼接后解析为 1 次 turn_end
357
- });
358
- });
359
-
360
- // ── 3. agent_end 自然完成(rpc 长驻进程不自动退出,需主动 kill)──
361
- //
362
- // [rpc agent_end] pi --mode rpc 是长驻进程(runRpcMode 末尾 return new Promise(() => {})),
363
- // 处理完 prompt 后不退出。runSpawn 只靠 child.on("close") 判完成——如果不处理 agent_end,
364
- // 子进程会卡到 watchdog 30 分钟兜底 kill。修复:收到 agent_end(willRetry=false)后
365
- // 主动 child.kill("SIGTERM") 让子进程退出,触发 close → runSpawn resolve。
366
- // willRetry=true 时 agent 会重试,不能 kill。
367
- describe("agent_end 自然完成", () => {
368
- // [recursive-orchestration] 条件 kill:agent_end 时读子进程 session 文件算活跃后代
369
- // (pending:register − unregister 差集)。有活跃后代 → 保持进程 idle 等 steer 唤醒。
370
- const mockPending = vi.mocked(readActivePendingFromSessionFile);
371
-
372
- // [MF-3] recentUnregister 竞态分支的秒级宽限:count=0 + recentUnregister=true → keep alive
373
- // 挂 WAKEUP_GRACE_MS(15s)定时器,到期无新 agent_end(未被唤醒)即 kill。
374
- // 背景:旧实现挂固定 2h(WAIT_DESCENDANT_TIMEOUT_MS)——层主 closeout 的最终 agent_end
375
- // 距最后一次 unregister <60s 必命中此分支,空等 2h 才 kill + 冒牌完成通知级联。
376
- it("MF-3: agent_end(count=0 + recentUnregister)→ 15s 宽限到期后 kill", async () => {
377
- mockPending.mockReturnValue({ count: 0, recentUnregister: true });
378
- const record = makeRecord();
379
- const promise = runSpawn(record, "Task: wake-grace", makeOpts(), makeCtx());
380
-
381
- await waitForSpawn();
382
- const child = lastSpawnedChild();
383
-
384
- // 必须在 emit agent_end 之前启用 fake timers——keep-alive 定时器在 agent_end 处理器里
385
- // 新建,新建时若已是 fake 定时器才可被 advance。不 fake setImmediate:stream .write() 的
386
- // data flush 靠真实事件循环(微任务/nextTick)交付,用真实 setImmediate 让出事件循环。
387
- vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout", "Date"] });
388
- try {
389
- emitStdoutLine(child, sessionHeader());
390
- emitStdoutLine(child, { type: "agent_end", messages: [], willRetry: false });
391
- // 让事件 pump 处理 agent_end(stream flush 是异步的)
392
- await new Promise((r) => setImmediate(r));
393
- // keep alive:宽限挂起,不 kill
394
- expect(child.killed).toBe(false);
395
-
396
- // 宽限未到(14999ms):仍不 kill
397
- await vi.advanceTimersByTimeAsync(WAKEUP_GRACE_MS - 1);
398
- expect(child.killed).toBe(false);
399
-
400
- // 第 15000ms 到期:无新 agent_end(未被唤醒)→ kill
401
- await vi.advanceTimersByTimeAsync(1);
402
- expect(child.killed).toBe(true);
403
- expect(child.killSignal).toBe("SIGTERM");
404
-
405
- // 收尾:close 让 runSpawn resolve
406
- child.stdout.end();
407
- child.stderr.end();
408
- child.emit("close", 143);
409
-
410
- const result = await promise;
411
- expect(result.success).toBe(true);
412
- } finally {
413
- vi.useRealTimers();
414
- }
415
- });
416
-
417
- // [MF-3] 唤醒重评估:宽限挂起期间父被 steer 唤醒(续跑产出新一轮)→ 下一次 agent_end
418
- // 重新判定;此时后代已完成(count=0,recentUnregister=false)→ 正常 kill,不等宽限到期。
419
- it("MF-3: 宽限期内被唤醒 → 下一次 agent_end 重新评估(后代已完成 → kill)", async () => {
420
- mockPending
421
- .mockReturnValueOnce({ count: 0, recentUnregister: true })
422
- .mockReturnValueOnce({ count: 0, recentUnregister: false });
423
- const record = makeRecord();
424
- const promise = runSpawn(record, "Task: wake-then-done", makeOpts(), makeCtx());
425
-
426
- await waitForSpawn();
427
- const child = lastSpawnedChild();
428
-
429
- vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout", "Date"] });
430
- try {
431
- emitStdoutLine(child, sessionHeader());
432
- // 第一次 agent_end:recentUnregister 竞态 → keep alive(宽限挂起)
433
- emitStdoutLine(child, { type: "agent_end", messages: [], willRetry: false });
434
- await new Promise((r) => setImmediate(r));
435
- expect(child.killed).toBe(false);
436
-
437
- // 被唤醒后父续跑产出新一轮 agent_end:后代已 unregister → 立即 kill(不等宽限)
438
- emitStdoutLine(child, { type: "agent_end", messages: [], willRetry: false });
439
- await new Promise((r) => setImmediate(r));
440
- expect(child.killed).toBe(true);
441
- expect(child.killSignal).toBe("SIGTERM");
442
-
443
- child.stdout.end();
444
- child.stderr.end();
445
- child.emit("close", 143);
446
-
447
- const result = await promise;
448
- expect(result.success).toBe(true);
449
- } finally {
450
- vi.useRealTimers();
451
- }
452
- });
453
-
454
- // [MF-4] count>0 分支的动态超时:等待超时 = computeWatchdogMs(maxTurns),非固定 2h。
455
- // maxTurns=20 → 20×5min = 100min 到期 kill。若回归到固定 2h 常量,100min 处不会 kill,
456
- // 本用例 advance(computeWatchdogMs − 1) 后仍不 kill、再 +1 才 kill 的断言会失败。
457
- it("MF-4: agent_end(count>0)→ 等待超时 = computeWatchdogMs(maxTurns)(动态,非固定 2h)", async () => {
458
- const maxTurns = 20;
459
- const expected = computeWatchdogMs(maxTurns);
460
- mockPending.mockReturnValue({ count: 2 });
461
- const record = makeRecord();
462
- const promise = runSpawn(record, "Task: slow-desc", makeOpts({ maxTurns }), makeCtx());
463
-
464
- await waitForSpawn();
465
- const child = lastSpawnedChild();
466
-
467
- vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout", "Date"] });
468
- try {
469
- emitStdoutLine(child, sessionHeader());
470
- emitStdoutLine(child, { type: "agent_end", messages: [], willRetry: false });
471
- await new Promise((r) => setImmediate(r));
472
- // keep alive:动态超时挂起,不 kill
473
- expect(child.killed).toBe(false);
474
-
475
- // 未到动态超时(100min−1ms):不 kill
476
- await vi.advanceTimersByTimeAsync(expected - 1);
477
- expect(child.killed).toBe(false);
478
-
479
- // 动态超时到期:kill。若误用固定 2h,此处不会 kill → 用例失败
480
- await vi.advanceTimersByTimeAsync(1);
481
- expect(child.killed).toBe(true);
482
- expect(child.killSignal).toBe("SIGTERM");
483
-
484
- child.stdout.end();
485
- child.stderr.end();
486
- child.emit("close", 143);
487
-
488
- const result = await promise;
489
- expect(result.success).toBe(true);
490
- } finally {
491
- vi.useRealTimers();
492
- }
493
- });
494
-
495
- // [S-9] pending.error 分支(sessionFile 不可读 → 保守 keep-alive + re-arm dynamic watchdog)
496
- // 集成行为 guard:session-pending 单测覆盖 error 返回值,但 session-runner 的 no-kill +
497
- // re-arm 到 computeWatchdogMs(maxTurns) 行为无集成 guard。若 re-arm 误删/误用固定超时,
498
- // 保守 keep-alive 会退化成永久挂起或被固定超时误杀。
499
- it("S-9: agent_end(count=0 + error)→ 保守不 kill + watchdog re-arm 到动态超时", async () => {
500
- const maxTurns = 20;
501
- const expected = computeWatchdogMs(maxTurns);
502
- mockPending.mockReturnValue({ count: 0, error: "session file unreadable: EACCES" });
503
- const record = makeRecord();
504
- const promise = runSpawn(record, "Task: unreadable", makeOpts({ maxTurns }), makeCtx());
505
-
506
- await waitForSpawn();
507
- const child = lastSpawnedChild();
508
-
509
- vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout", "Date"] });
510
- try {
511
- emitStdoutLine(child, sessionHeader());
512
- emitStdoutLine(child, { type: "agent_end", messages: [], willRetry: false });
513
- await new Promise((r) => setImmediate(r));
514
- // 保守策略:sessionFile 不可读时不 kill(宁可空等也不误杀有后代的进程)
515
- expect(child.killed).toBe(false);
516
-
517
- // watchdog re-arm 到动态超时(computeWatchdogMs(maxTurns)),未到期不 kill
518
- await vi.advanceTimersByTimeAsync(expected - 1);
519
- expect(child.killed).toBe(false);
520
-
521
- // 动态超时到期:kill。若 error 分支漏了 re-arm(或误用固定超时),此断言失败
522
- await vi.advanceTimersByTimeAsync(1);
523
- expect(child.killed).toBe(true);
524
- expect(child.killSignal).toBe("SIGTERM");
525
-
526
- child.stdout.end();
527
- child.stderr.end();
528
- child.emit("close", 143);
529
-
530
- const result = await promise;
531
- expect(result.success).toBe(true);
532
- } finally {
533
- vi.useRealTimers();
534
- }
535
- });
536
-
537
- it("agent_end(willRetry=false,无活跃后代)→ child.kill(SIGTERM) 被调用,close 后 success=true", async () => {
538
- mockPending.mockReturnValue({ count: 0 });
539
- const record = makeRecord();
540
- const promise = runSpawn(record, "Task: done", makeOpts(), makeCtx());
541
-
542
- await waitForSpawn();
543
- const child = lastSpawnedChild();
544
-
545
- emitStdoutLine(child, sessionHeader());
546
- // agent 自然完成(willRetry=false)
547
- emitStdoutLine(child, { type: "agent_end", messages: [], willRetry: false });
548
- child.stdout.end();
549
- child.stderr.end();
550
- // agent_end 触发 kill(SIGTERM) → 子进程退出(exitCode 143 = 128+15)
551
- child.emit("close", 143);
552
-
553
- const result = await promise;
554
-
555
- expect(child.killed).toBe(true);
556
- expect(child.killSignal).toBe("SIGTERM");
557
- // 信号终止(>=128)视为正常完成
558
- expect(result.success).toBe(true);
559
- });
560
-
561
- it("agent_end(willRetry=false,有活跃后代)→ 不 kill,进程保持 idle 等 steer 唤醒;后代完成后 kill", async () => {
562
- // 第一次 agent_end:有活跃后代(count=1)→ 不 kill
563
- mockPending.mockReturnValueOnce({ count: 1 });
564
- // 第二次 agent_end:后代已 unregister(count=0)→ kill
565
- mockPending.mockReturnValueOnce({ count: 0 });
566
- const record = makeRecord();
567
- const promise = runSpawn(record, "Task: orchestrate", makeOpts(), makeCtx());
568
-
569
- await waitForSpawn();
570
- const child = lastSpawnedChild();
571
-
572
- emitStdoutLine(child, sessionHeader());
573
- emitStdoutLine(child, { type: "agent_end", messages: [], willRetry: false });
574
- // 给事件 pump 一点时间处理 agent_end(判定读 session 文件)
575
- await new Promise((r) => setTimeout(r, 20));
576
- expect(child.killed).toBe(false);
577
-
578
- // 后代完成(unregister 已写入)后再次 agent_end → 无活跃后代 → kill
579
- emitStdoutLine(child, { type: "agent_end", messages: [], willRetry: false });
580
- child.stdout.end();
581
- child.stderr.end();
582
- child.emit("close", 143);
583
-
584
- const result = await promise;
585
-
586
- expect(child.killed).toBe(true);
587
- expect(child.killSignal).toBe("SIGTERM");
588
- expect(result.success).toBe(true);
589
- });
590
-
591
- it("agent_end(willRetry=true)→ child.kill 不被调用(agent 会重试,等下一个 agent_end)", async () => {
592
- mockPending.mockReturnValue({ count: 0 });
593
- const record = makeRecord();
594
- const promise = runSpawn(record, "Task: retry", makeOpts(), makeCtx());
595
-
596
- await waitForSpawn();
597
- const child = lastSpawnedChild();
598
-
599
- emitStdoutLine(child, sessionHeader());
600
- // willRetry=true:agent 会重试,不应 kill
601
- emitStdoutLine(child, { type: "agent_end", messages: [], willRetry: true });
602
- // 此时 child.killed 应仍为 false。给一点时间让 event pump 处理完。
603
- await new Promise((r) => setTimeout(r, 10));
604
- expect(child.killed).toBe(false);
605
-
606
- // 收尾:模拟重试后的最终完成(willRetry=false)
607
- emitStdoutLine(child, { type: "agent_end", messages: [], willRetry: false });
608
- child.stdout.end();
609
- child.stderr.end();
610
- child.emit("close", 143);
611
-
612
- const result = await promise;
613
-
614
- // 最终的 agent_end(willRetry=false)触发 kill
615
- expect(child.killed).toBe(true);
616
- expect(child.killSignal).toBe("SIGTERM");
617
- expect(result.success).toBe(true);
618
- });
619
-
620
- it("agent_end 后的后续 event 仍被 handleSdkEvent 处理(kill 不阻塞 event pump)", async () => {
621
- mockPending.mockReturnValue({ count: 0 });
622
- const record = makeRecord();
623
- const promise = runSpawn(record, "Task: flush", makeOpts(), makeCtx());
624
-
625
- await waitForSpawn();
626
- const child = lastSpawnedChild();
627
-
628
- emitStdoutLine(child, sessionHeader());
629
- // agent_end kill 是 fire-and-forget(SIGTERM 异步),后续 stdout 行仍被 event pump 处理
630
- emitStdoutLine(child, { type: "agent_end", messages: [], willRetry: false });
631
- emitStdoutLine(child, { type: "turn_end" }); // kill 后的 turn_end 仍应被处理
632
- child.stdout.end();
633
- child.stderr.end();
634
- child.emit("close", 143);
635
-
636
- const result = await promise;
637
-
638
- expect(child.killed).toBe(true);
639
- expect(result.success).toBe(true);
640
- // turn_end 被处理 → turnCount=1
641
- expect(record.turnCount).toBe(1);
642
- });
643
- });
644
- });