@zhushanwen/pi-subagent-workflow 8.6.0 → 8.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (347) hide show
  1. package/package.json +9 -13
  2. package/relay/relay.mjs +1 -1
  3. package/src/host/__tests__/pi-host.test.ts +214 -0
  4. package/src/host/pi-host.ts +109 -0
  5. package/src/index.ts +89 -32
  6. package/src/injectors/__tests__/engine-awareness.test.ts +292 -0
  7. package/src/injectors/__tests__/engine-section-stability.test.ts +341 -0
  8. package/src/injectors/__tests__/subagent-list-injector.test.ts +5 -5
  9. package/src/injectors/__tests__/workflow-list-injector.test.ts +5 -5
  10. package/src/injectors/engine-awareness.ts +123 -0
  11. package/src/injectors/model-list-injector.ts +1 -1
  12. package/src/injectors/subagent-list-injector.ts +8 -8
  13. package/src/injectors/workflow-list-injector.ts +8 -8
  14. package/src/interface/__tests__/detectors.test.ts +3 -3
  15. package/src/interface/__tests__/subagent-tool-path-guard.test.ts +2 -2
  16. package/src/interface/__tests__/tool-workflow-script-generate.test.ts +2 -2
  17. package/src/interface/__tests__/tool-workflow-throw-paths.test.ts +1 -1
  18. package/src/interface/bg-notify-render.ts +4 -4
  19. package/src/interface/commands.ts +3 -3
  20. package/src/interface/format.ts +2 -2
  21. package/src/interface/helpers.ts +1 -1
  22. package/src/interface/list-component.ts +4 -4
  23. package/src/interface/list-shared.ts +2 -2
  24. package/src/interface/list-view.ts +2 -2
  25. package/src/interface/subagent-actions.ts +263 -11
  26. package/src/interface/subagent-tool-schema.ts +20 -13
  27. package/src/interface/subagent-tool.ts +11 -6
  28. package/src/interface/subagents.ts +3 -3
  29. package/src/interface/tool-render.ts +34 -2
  30. package/src/interface/tool-workflow-script.ts +4 -4
  31. package/src/interface/tool-workflow.ts +6 -6
  32. package/src/interface/views/WorkflowsView.ts +5 -5
  33. package/src/interface/views/__tests__/WorkflowsView-signature.test.ts +3 -3
  34. package/src/interface/views/__tests__/detail-content-session-file.test.ts +2 -2
  35. package/src/interface/views/detail-content.ts +4 -4
  36. package/src/interface/views/format.ts +4 -4
  37. package/src/{orchestration/jsonl-run-store.ts → jsonl-run-store.ts} +11 -11
  38. package/src/execution/__tests__/__fixtures__/notifier-golden-snapshots.json +0 -53
  39. package/src/execution/__tests__/__fixtures__/truncline.snapshot.json +0 -1
  40. package/src/execution/__tests__/agent-registry.test.ts +0 -363
  41. package/src/execution/__tests__/agent-result-mapper.test.ts +0 -150
  42. package/src/execution/__tests__/alive-store.test.ts +0 -147
  43. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +0 -490
  44. package/src/execution/__tests__/before-agent-start-injection.test.ts +0 -132
  45. package/src/execution/__tests__/bg-notify-render.test.ts +0 -329
  46. package/src/execution/__tests__/channel-registry-handshake.test.ts +0 -243
  47. package/src/execution/__tests__/chat-engine-routing.test.ts +0 -601
  48. package/src/execution/__tests__/chatmode-first-round-closure-service.test.ts +0 -365
  49. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +0 -190
  50. package/src/execution/__tests__/chatmode-round-notify-real-chain.test.ts +0 -215
  51. package/src/execution/__tests__/concurrency-pool.test.ts +0 -250
  52. package/src/execution/__tests__/config.test.ts +0 -110
  53. package/src/execution/__tests__/conversation-wiring.test.ts +0 -198
  54. package/src/execution/__tests__/crash-recovery.test.ts +0 -320
  55. package/src/execution/__tests__/delivery-methods.test.ts +0 -422
  56. package/src/execution/__tests__/dialog-queue.test.ts +0 -299
  57. package/src/execution/__tests__/epipe-fallback.test.ts +0 -241
  58. package/src/execution/__tests__/execute-and-await-worktree.test.ts +0 -243
  59. package/src/execution/__tests__/execute-nesting.test.ts +0 -323
  60. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
  61. package/src/execution/__tests__/execution-record.test.ts +0 -1394
  62. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +0 -171
  63. package/src/execution/__tests__/finalize-record.test.ts +0 -368
  64. package/src/execution/__tests__/finalized-marker.test.ts +0 -82
  65. package/src/execution/__tests__/format-schema-instruction.test.ts +0 -169
  66. package/src/execution/__tests__/format.test.ts +0 -458
  67. package/src/execution/__tests__/gc-timer.test.ts +0 -184
  68. package/src/execution/__tests__/get-record-for-action-restart.test.ts +0 -254
  69. package/src/execution/__tests__/gui-mode-dispatch.test.ts +0 -59
  70. package/src/execution/__tests__/helpers/mock-extension-api.ts +0 -30
  71. package/src/execution/__tests__/helpers/spawn-mock.ts +0 -242
  72. package/src/execution/__tests__/host-mode.test.ts +0 -87
  73. package/src/execution/__tests__/index-session-start-identity.test.ts +0 -371
  74. package/src/execution/__tests__/index-session-start.test.ts +0 -672
  75. package/src/execution/__tests__/lifecycle-manager-lock.test.ts +0 -211
  76. package/src/execution/__tests__/lifecycle-manager.test.ts +0 -383
  77. package/src/execution/__tests__/lifecycle-predicates.test.ts +0 -116
  78. package/src/execution/__tests__/list-component.test.ts +0 -401
  79. package/src/execution/__tests__/list-fields.test.ts +0 -140
  80. package/src/execution/__tests__/manifest-parentid.test.ts +0 -117
  81. package/src/execution/__tests__/model-resolver.test.ts +0 -465
  82. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +0 -287
  83. package/src/execution/__tests__/nested-visibility.test.ts +0 -325
  84. package/src/execution/__tests__/notifier-flush.test.ts +0 -612
  85. package/src/execution/__tests__/notifier-golden-snapshot.test.ts +0 -261
  86. package/src/execution/__tests__/notify-ledger.test.ts +0 -826
  87. package/src/execution/__tests__/one-shot-upgrade.test.ts +0 -205
  88. package/src/execution/__tests__/output-collector.test.ts +0 -358
  89. package/src/execution/__tests__/parent-child-matrix.test.ts +0 -336
  90. package/src/execution/__tests__/path-encoding.test.ts +0 -104
  91. package/src/execution/__tests__/pi-invocation.test.ts +0 -134
  92. package/src/execution/__tests__/record-store.test.ts +0 -1057
  93. package/src/execution/__tests__/records-cwd-isolation.test.ts +0 -91
  94. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +0 -339
  95. package/src/execution/__tests__/recursive-visibility-env.test.ts +0 -273
  96. package/src/execution/__tests__/relay-agent.test.ts +0 -448
  97. package/src/execution/__tests__/relay-env.test.ts +0 -42
  98. package/src/execution/__tests__/resource-policy.test.ts +0 -109
  99. package/src/execution/__tests__/rpc-mode.test.ts +0 -89
  100. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +0 -267
  101. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +0 -253
  102. package/src/execution/__tests__/run-spawn-edges.test.ts +0 -687
  103. package/src/execution/__tests__/run-spawn-integration.test.ts +0 -933
  104. package/src/execution/__tests__/run-spawn-resume.test.ts +0 -322
  105. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +0 -196
  106. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +0 -199
  107. package/src/execution/__tests__/sdk-contract.test.ts +0 -273
  108. package/src/execution/__tests__/session-context-resolver.test.ts +0 -167
  109. package/src/execution/__tests__/session-file-gc.test.ts +0 -293
  110. package/src/execution/__tests__/session-pending.test.ts +0 -197
  111. package/src/execution/__tests__/session-reconstructor.test.ts +0 -379
  112. package/src/execution/__tests__/session-runner-epipe.test.ts +0 -178
  113. package/src/execution/__tests__/session-runner-schema-env.test.ts +0 -350
  114. package/src/execution/__tests__/session-start-reaper.test.ts +0 -233
  115. package/src/execution/__tests__/spawn-args.test.ts +0 -445
  116. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +0 -189
  117. package/src/execution/__tests__/spawn-event-adapter.test.ts +0 -167
  118. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +0 -207
  119. package/src/execution/__tests__/spawned-children.test.ts +0 -92
  120. package/src/execution/__tests__/start-sync-model-guard.test.ts +0 -150
  121. package/src/execution/__tests__/startup-config-declaration.test.ts +0 -35
  122. package/src/execution/__tests__/status-refactor.test.ts +0 -345
  123. package/src/execution/__tests__/stdin-writer.test.ts +0 -463
  124. package/src/execution/__tests__/stream-sink-retirement.test.ts +0 -261
  125. package/src/execution/__tests__/subagent-service-abort.test.ts +0 -60
  126. package/src/execution/__tests__/subagent-service-message-close.test.ts +0 -629
  127. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +0 -180
  128. package/src/execution/__tests__/subagent-service.test.ts +0 -788
  129. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +0 -310
  130. package/src/execution/__tests__/subprocess-agent-runner.test.ts +0 -599
  131. package/src/execution/__tests__/temp-prompt.test.ts +0 -53
  132. package/src/execution/__tests__/timeout-integration.test.ts +0 -615
  133. package/src/execution/__tests__/tombstone-store.test.ts +0 -73
  134. package/src/execution/__tests__/tool-action.test.ts +0 -476
  135. package/src/execution/__tests__/truncline-snapshot.test.ts +0 -81
  136. package/src/execution/__tests__/turn-limiter-semantics.test.ts +0 -194
  137. package/src/execution/__tests__/turn-limiter.test.ts +0 -65
  138. package/src/execution/__tests__/ui-channels.test.ts +0 -187
  139. package/src/execution/__tests__/ui-interaction-model.test.ts +0 -67
  140. package/src/execution/__tests__/ui-request-handler-factory.test.ts +0 -370
  141. package/src/execution/__tests__/ui-request-handler.test.ts +0 -204
  142. package/src/execution/__tests__/ui-request-observability.test.ts +0 -114
  143. package/src/execution/__tests__/ui-request-queue.test.ts +0 -133
  144. package/src/execution/__tests__/worktree-manager.test.ts +0 -633
  145. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +0 -229
  146. package/src/execution/__tests__/worktree-reconcile.integration.test.ts +0 -181
  147. package/src/execution/__tests__/worktree-registry.test.ts +0 -199
  148. package/src/execution/agent-registry.ts +0 -199
  149. package/src/execution/agent-result-mapper.ts +0 -90
  150. package/src/execution/alive-store.ts +0 -92
  151. package/src/execution/argv-mirror.ts +0 -113
  152. package/src/execution/best-effort.ts +0 -38
  153. package/src/execution/channel-registry-access.ts +0 -144
  154. package/src/execution/concurrency-pool.ts +0 -116
  155. package/src/execution/config.ts +0 -96
  156. package/src/execution/dialog-queue.ts +0 -330
  157. package/src/execution/engine/__tests__/common/data-dir.test.ts +0 -53
  158. package/src/execution/engine/__tests__/common/errors.test.ts +0 -132
  159. package/src/execution/engine/__tests__/common/event-journal.test.ts +0 -177
  160. package/src/execution/engine/__tests__/common/kill-chain.test.ts +0 -192
  161. package/src/execution/engine/__tests__/common/nesting-guard.test.ts +0 -81
  162. package/src/execution/engine/__tests__/common/persona-router.test.ts +0 -123
  163. package/src/execution/engine/__tests__/common/pool-manager.test.ts +0 -154
  164. package/src/execution/engine/__tests__/common/schema-emulation.test.ts +0 -128
  165. package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +0 -28
  166. package/src/execution/engine/__tests__/conformance/agent-event-invariants.ts +0 -141
  167. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +0 -109
  168. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +0 -101
  169. package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +0 -77
  170. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +0 -104
  171. package/src/execution/engine/__tests__/conformance/contract.relay.test.ts +0 -342
  172. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +0 -201
  173. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +0 -76
  174. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +0 -79
  175. package/src/execution/engine/__tests__/engine-discovery.test.ts +0 -87
  176. package/src/execution/engine/__tests__/engines-declaration.test.ts +0 -36
  177. package/src/execution/engine/__tests__/model-prompt.test.ts +0 -85
  178. package/src/execution/engine/__tests__/paths.test.ts +0 -39
  179. package/src/execution/engine/__tests__/registry.test.ts +0 -120
  180. package/src/execution/engine/__tests__/routing.test.ts +0 -231
  181. package/src/execution/engine/common/data-dir.ts +0 -62
  182. package/src/execution/engine/common/errors.ts +0 -183
  183. package/src/execution/engine/common/event-journal.ts +0 -254
  184. package/src/execution/engine/common/journal-replay.ts +0 -62
  185. package/src/execution/engine/common/kill-chain.ts +0 -221
  186. package/src/execution/engine/common/nesting-guard.ts +0 -50
  187. package/src/execution/engine/common/persona-router.ts +0 -108
  188. package/src/execution/engine/common/pool-manager.ts +0 -226
  189. package/src/execution/engine/common/schema-emulation.ts +0 -189
  190. package/src/execution/engine/common/session-view-projection.ts +0 -51
  191. package/src/execution/engine/engine-discovery.ts +0 -65
  192. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +0 -469
  193. package/src/execution/engine/engines/pi/__tests__/reader.test.ts +0 -155
  194. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  195. package/src/execution/engine/engines/pi/pi-engine.ts +0 -415
  196. package/src/execution/engine/engines/pi/reader.ts +0 -48
  197. package/src/execution/engine/engines/pi/registration.ts +0 -35
  198. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  199. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  200. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  201. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +0 -246
  202. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +0 -228
  203. package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +0 -210
  204. package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +0 -64
  205. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +0 -127
  206. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -580
  207. package/src/execution/engine/engines/zcode/constants.ts +0 -43
  208. package/src/execution/engine/engines/zcode/golden-sample.ts +0 -39
  209. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  210. package/src/execution/engine/engines/zcode/parser.ts +0 -436
  211. package/src/execution/engine/engines/zcode/preparer.ts +0 -363
  212. package/src/execution/engine/engines/zcode/reader.ts +0 -381
  213. package/src/execution/engine/engines/zcode/registration.ts +0 -37
  214. package/src/execution/engine/engines/zcode/zcode-engine.ts +0 -658
  215. package/src/execution/engine/host-task-spec.ts +0 -47
  216. package/src/execution/engine/model-prompt.ts +0 -59
  217. package/src/execution/engine/paths.ts +0 -42
  218. package/src/execution/engine/port.ts +0 -153
  219. package/src/execution/engine/registry.ts +0 -123
  220. package/src/execution/engine/routing.ts +0 -218
  221. package/src/execution/engine/types.ts +0 -309
  222. package/src/execution/execute-options-mapper.ts +0 -112
  223. package/src/execution/execution-record.ts +0 -965
  224. package/src/execution/finalize-record.ts +0 -251
  225. package/src/execution/finalized-marker.ts +0 -51
  226. package/src/execution/get-state-handshake.ts +0 -104
  227. package/src/execution/host-mode.ts +0 -56
  228. package/src/execution/idle-gc.ts +0 -47
  229. package/src/execution/lifecycle-manager.ts +0 -513
  230. package/src/execution/lifecycle-predicates.ts +0 -65
  231. package/src/execution/manifest-store.ts +0 -256
  232. package/src/execution/model-config-service.ts +0 -219
  233. package/src/execution/model-resolver.ts +0 -248
  234. package/src/execution/notifier.ts +0 -330
  235. package/src/execution/notify-ledger.ts +0 -580
  236. package/src/execution/output-collector.ts +0 -228
  237. package/src/execution/path-encoding.ts +0 -60
  238. package/src/execution/pi-invocation.ts +0 -120
  239. package/src/execution/record-entry.ts +0 -132
  240. package/src/execution/record-store.ts +0 -1234
  241. package/src/execution/relay-env.ts +0 -37
  242. package/src/execution/session-context-resolver.ts +0 -64
  243. package/src/execution/session-file-gc.ts +0 -120
  244. package/src/execution/session-pending.ts +0 -164
  245. package/src/execution/session-reconstructor.ts +0 -678
  246. package/src/execution/session-runner.ts +0 -1781
  247. package/src/execution/sessions-index.ts +0 -304
  248. package/src/execution/spawn-event-adapter.ts +0 -363
  249. package/src/execution/stdin-writer.ts +0 -198
  250. package/src/execution/stream-sink.ts +0 -126
  251. package/src/execution/subagent-service.ts +0 -2141
  252. package/src/execution/subprocess-agent-runner.ts +0 -317
  253. package/src/execution/temp-prompt.ts +0 -62
  254. package/src/execution/tombstone-store.ts +0 -72
  255. package/src/execution/turn-limiter.ts +0 -102
  256. package/src/execution/types.ts +0 -973
  257. package/src/execution/ui-channels.ts +0 -216
  258. package/src/execution/ui-interaction-model.ts +0 -48
  259. package/src/execution/ui-request-handler-factory.ts +0 -212
  260. package/src/execution/ui-request-observability.ts +0 -81
  261. package/src/execution/ui-request-queue.ts +0 -184
  262. package/src/execution/worktree-manager.ts +0 -688
  263. package/src/execution/worktree-registry.ts +0 -222
  264. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +0 -349
  265. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +0 -202
  266. package/src/orchestration/__tests__/agent-call-stream.test.ts +0 -152
  267. package/src/orchestration/__tests__/args-validator.test.ts +0 -143
  268. package/src/orchestration/__tests__/config-loader.test.ts +0 -503
  269. package/src/orchestration/__tests__/error-recovery-handlers.test.ts +0 -809
  270. package/src/orchestration/__tests__/error-recovery-postmessage-defense.test.ts +0 -405
  271. package/src/orchestration/__tests__/error-recovery-serialize-failed-result.test.ts +0 -56
  272. package/src/orchestration/__tests__/error-recovery-workflow-call.test.ts +0 -166
  273. package/src/orchestration/__tests__/execute-agent-call.test.ts +0 -451
  274. package/src/orchestration/__tests__/jsonl-run-store-corrupt-entry.test.ts +0 -150
  275. package/src/orchestration/__tests__/jsonl-run-store-loadall-sources.test.ts +0 -171
  276. package/src/orchestration/__tests__/jsonl-run-store-retention.test.ts +0 -202
  277. package/src/orchestration/__tests__/jsonl-run-store-session-file.test.ts +0 -1030
  278. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +0 -600
  279. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +0 -96
  280. package/src/orchestration/__tests__/lifecycle.test.ts +0 -659
  281. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +0 -95
  282. package/src/orchestration/__tests__/review-fix-loop-e2e.test.ts +0 -2025
  283. package/src/orchestration/__tests__/script-lint.test.ts +0 -831
  284. package/src/orchestration/__tests__/skill-discovery.test.ts +0 -201
  285. package/src/orchestration/__tests__/test-mocks.ts +0 -197
  286. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +0 -368
  287. package/src/orchestration/__tests__/worker-host.test.ts +0 -120
  288. package/src/orchestration/__tests__/worker-returnmeta-passthrough.test.ts +0 -164
  289. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +0 -563
  290. package/src/orchestration/__tests__/worker-script-builder.test.ts +0 -309
  291. package/src/orchestration/__tests__/worker-script-template-snapshot.test.ts +0 -129
  292. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +0 -317
  293. package/src/orchestration/__tests__/workflow-script-lint-memo.test.ts +0 -110
  294. package/src/orchestration/__tests__/workflows-e2e.test.ts +0 -537
  295. package/src/orchestration/agent-opts-resolver.ts +0 -174
  296. package/src/orchestration/args-validator.ts +0 -127
  297. package/src/orchestration/config-loader.ts +0 -315
  298. package/src/orchestration/error-recovery.ts +0 -1018
  299. package/src/orchestration/execute-agent-call.ts +0 -256
  300. package/src/orchestration/launcher.ts +0 -455
  301. package/src/orchestration/lifecycle.ts +0 -399
  302. package/src/orchestration/models/__tests__/budget.test.ts +0 -307
  303. package/src/orchestration/models/__tests__/trace.test.ts +0 -408
  304. package/src/orchestration/models/agent-call.ts +0 -83
  305. package/src/orchestration/models/budget.ts +0 -118
  306. package/src/orchestration/models/ports.ts +0 -164
  307. package/src/orchestration/models/run-runtime.ts +0 -104
  308. package/src/orchestration/models/run-spec.ts +0 -83
  309. package/src/orchestration/models/run-state.ts +0 -44
  310. package/src/orchestration/models/trace.ts +0 -183
  311. package/src/orchestration/models/types.ts +0 -301
  312. package/src/orchestration/models/workflow-run.ts +0 -254
  313. package/src/orchestration/models/workflow-script-registry.ts +0 -35
  314. package/src/orchestration/models/workflow-script.ts +0 -117
  315. package/src/orchestration/script-lint.ts +0 -766
  316. package/src/orchestration/skill-discovery.ts +0 -115
  317. package/src/orchestration/worker-handle.ts +0 -115
  318. package/src/orchestration/worker-host.ts +0 -98
  319. package/src/orchestration/worker-script-builder.ts +0 -421
  320. package/src/orchestration/workflow-files.ts +0 -85
  321. package/src/orchestration/workflow-script-registry-impl.ts +0 -133
  322. package/src/shared/__tests__/agent-ref.test.ts +0 -34
  323. package/src/shared/__tests__/meta-parser.test.ts +0 -304
  324. package/src/shared/__tests__/model-ref.test.ts +0 -306
  325. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -280
  326. package/src/shared/__tests__/resource-discovery.test.ts +0 -736
  327. package/src/shared/__tests__/resource-meta.test.ts +0 -51
  328. package/src/shared/__tests__/schema-jsonify.test.ts +0 -81
  329. package/src/shared/__tests__/timer-delay.test.ts +0 -61
  330. package/src/shared/agent-event.ts +0 -13
  331. package/src/shared/agent-ref.ts +0 -57
  332. package/src/shared/meta-parser.ts +0 -261
  333. package/src/shared/model-ref.ts +0 -286
  334. package/src/shared/resource-discovery.ts +0 -811
  335. package/src/shared/resource-meta.ts +0 -65
  336. package/src/shared/schema-env.ts +0 -44
  337. package/src/shared/schema-jsonify.ts +0 -58
  338. package/src/shared/timer-delay.ts +0 -54
  339. package/src/shared/xml-injection.ts +0 -35
  340. package/workflows/README.md +0 -81
  341. package/workflows/_shared/agent-refs.cjs +0 -40
  342. package/workflows/chain.js +0 -131
  343. package/workflows/map-reduce.js +0 -174
  344. package/workflows/parallel.js +0 -148
  345. package/workflows/review-fix-loop-utils.cjs +0 -1309
  346. package/workflows/review-fix-loop.js +0 -1404
  347. package/workflows/scatter-gather.js +0 -178
@@ -1,615 +0,0 @@
1
- // src/__tests__/timeout-integration.test.ts
2
- //
3
- // timeoutMs / signal abort → child.kill 端到端路径测试。
4
- //
5
- // 背景:session-runner.ts 无独立 timeoutMs 字段——超时机制是 watchdog(基于
6
- // computeWatchdogMs(opts.maxTurns) 动态计算的下限 30min timer,兜底 SIGTERM);
7
- // 外部取消通过 opts.signal (AbortSignal) 传播:onAbort → child.kill("SIGTERM")。
8
- // 故「timeout 端到端路径」实际是 watchdog timer + signal abort → child.kill 两条链路。
9
- //
10
- // 本文件聚焦三条终止语义路径(与 run-spawn-integration.test.ts §12 watchdog 测试互补,
11
- // 该文件关注 timer 边界值,本文件关注端到端 kill 语义 + 外部 signal 场景):
12
- // 1. watchdog 到期 → child.kill(maxTurns 驱动的整体超时兜底)
13
- // 2. 正常完成先于 watchdog 到期 → clearTimeout 生效,不 kill
14
- // 3. 外部 signal abort(运行中 abort / spawn 前已 aborted)→ child.kill
15
- //
16
- // mock 策略(与 run-spawn-integration.test.ts / run-spawn-edges.test.ts 一致):
17
- // - node:child_process.spawn → 返回 FakeChild(EventEmitter + PassThrough)。
18
- // - node:child_process.execFile → err-first callback 默认兜底(buildEnvBlock git branch
19
- // 失败 → catch → branch="")。
20
- // - node:fs 同步方法 → mock(避免触碰真实文件系统),promises 保留真实实现。
21
- // - temp-prompt → mock(返回固定路径,消除 fake-timers flaky)。
22
- // - alive-store.writeAliveMarker → mock。
23
-
24
- import type { PassThrough } from "node:stream";
25
-
26
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
27
-
28
- // ── mock modules ──
29
- // vitest 把 vi.mock 提升到文件顶部,工厂内部引用模块用 await import。
30
-
31
- vi.mock("node:child_process", async () => {
32
- const { EventEmitter } = await import("node:events");
33
- const { PassThrough } = await import("node:stream");
34
-
35
- // FakeChild:模拟 ChildProcess(EventEmitter + PassThrough streams)。
36
- class FakeChild extends EventEmitter {
37
- pid = 12345;
38
- stdout = new PassThrough();
39
- stdin = new PassThrough(); // runSpawn 注册 stdin EPIPE handler(session-runner),FakeChild 必须提供
40
- stderr = new PassThrough();
41
- killed = false;
42
- killSignal: string | undefined;
43
- // [race-F4] SIGKILL 升级判定读 exitCode/signalCode(真 ChildProcess 未退出时均 null),
44
- // FakeChild 同形提供才能驱动升级路径测试(undefined === null 为 false 会误判已退出)。
45
- exitCode: number | null = null;
46
- signalCode: string | null = null;
47
- kill(sig?: string): boolean {
48
- this.killed = true;
49
- this.killSignal = sig;
50
- return true;
51
- }
52
- }
53
-
54
- return {
55
- spawn: vi.fn(() => new FakeChild()),
56
- // buildEnvBlock 用 execFile 异步取 git branch:默认 err-first 兜底(catch → branch="")
57
- execFile: vi.fn(
58
- (
59
- _cmd: string,
60
- _args: readonly string[],
61
- _opts: unknown,
62
- cb: (err: Error | null, stdout?: string, stderr?: string) => void,
63
- ) => cb(new Error("execFile not configured in this test")),
64
- ),
65
- };
66
- });
67
-
68
- vi.mock("node:fs", async () => {
69
- const actual = await import("node:fs");
70
- return {
71
- default: {
72
- ...actual,
73
- mkdirSync: vi.fn(),
74
- existsSync: vi.fn(() => false),
75
- appendFileSync: vi.fn(),
76
- writeFileSync: vi.fn(),
77
- readdirSync: vi.fn(() => []),
78
- },
79
- mkdirSync: vi.fn(),
80
- existsSync: vi.fn(() => false),
81
- appendFileSync: vi.fn(),
82
- writeFileSync: vi.fn(),
83
- readdirSync: vi.fn(() => []),
84
- promises: actual.promises,
85
- };
86
- });
87
-
88
- vi.mock("../alive-store.ts", () => ({
89
- writeAliveMarker: vi.fn(),
90
- }));
91
-
92
- vi.mock("../temp-prompt.ts", () => ({
93
- writePromptToTempFile: vi.fn(async (agent: string) => {
94
- const safeName = agent.replace(/[^\w.-]+/g, "_");
95
- return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
96
- }),
97
- cleanupTempPrompt: vi.fn(async () => {}),
98
- }));
99
-
100
- import { spawn } from "node:child_process";
101
- import * as fs from "node:fs";
102
-
103
- import { createRecord } from "../execution-record.ts";
104
- import {
105
- computeWatchdogMs,
106
- type RunOptions,
107
- resolveSpawnWatchdogMs,
108
- runSpawn,
109
- SPAWN_WATCHDOG_ENV,
110
- type SessionRunnerContext,
111
- } from "../session-runner.ts";
112
-
113
- const mockSpawn = vi.mocked(spawn);
114
- const mockExistsSync = vi.mocked(fs.existsSync);
115
-
116
- /**
117
- * spawn mock 返回的 fake child 类型(结构子集)。
118
- * FakeChild 定义在 vi.mock 工厂内部(作用域隔离),测试代码通过此类型访问成员。
119
- */
120
- interface FakeChild {
121
- pid: number;
122
- stdout: PassThrough;
123
- stderr: PassThrough;
124
- killed: boolean;
125
- killSignal: string | undefined;
126
- kill(sig?: string): boolean;
127
- emit(event: string, ...args: unknown[]): boolean;
128
- }
129
-
130
- /** 从最近一次 spawn 调用取回返回的 FakeChild。 */
131
- function lastSpawnedChild(): FakeChild {
132
- const result = mockSpawn.mock.results.at(-1);
133
- if (!result) throw new Error("spawn was not called yet");
134
- return result.value as FakeChild;
135
- }
136
-
137
- // ============================================================
138
- // 辅助:向 stdout 写一行(自动补换行)
139
- // ============================================================
140
-
141
- function emitStdoutLine(child: FakeChild, obj: Record<string, unknown>): void {
142
- child.stdout.write(`${JSON.stringify(obj)}\n`);
143
- }
144
-
145
- /** 构造 session header 行(stdout 首行)。 */
146
- function sessionHeader(id = "sess-abc"): Record<string, unknown> {
147
- return {
148
- type: "session",
149
- id,
150
- timestamp: "2026-07-03T12-00-00-000Z",
151
- cwd: "/tmp/test",
152
- };
153
- }
154
-
155
- // ============================================================
156
- // 辅助:构造最小合法的 record / opts / ctx
157
- // ============================================================
158
-
159
- function makeRecord() {
160
- return createRecord("run-1", {
161
- agent: "general-purpose",
162
- model: "test-model",
163
- mode: "sync",
164
- task: "do something",
165
- startedAt: 1_000_000,
166
- rootSessionId: "root-session",
167
- parentRecordId: undefined,
168
- depth: 0,
169
- });
170
- }
171
-
172
- function makeOpts(overrides: Partial<RunOptions> = {}): RunOptions {
173
- return {
174
- resolved: {
175
- model: {
176
- id: "test-model",
177
- name: "Test Model",
178
- provider: "test",
179
- reasoning: false,
180
- },
181
- thinkingLevel: undefined,
182
- },
183
- agentConfig: undefined,
184
- appendSystemPrompt: undefined,
185
- skillPath: undefined,
186
- schema: undefined,
187
- maxTurns: undefined,
188
- graceTurns: undefined,
189
- signal: undefined,
190
- onEvent: undefined,
191
- ...overrides,
192
- };
193
- }
194
-
195
- function makeCtx(overrides: Partial<SessionRunnerContext> = {}): SessionRunnerContext {
196
- return {
197
- cwd: "/tmp/test",
198
- agentDir: "/tmp/test/agents",
199
- skillDirs: [],
200
- mainCwd: "/tmp/test",
201
- mainSessionFile: undefined,
202
- sessionRootId: "root-session-test",
203
- rootCwd: "/tmp/test",
204
- ...overrides,
205
- };
206
- }
207
-
208
- /**
209
- * fake timers 下推进时间直到 spawn 被调用(返回 child 控制器,与共享 helper 的 void 返回
210
- * 不同,故就地维护)。
211
- *
212
- * [快照语义] 同 helpers/spawn-mock.ts 的 waitForSpawn:记调用时 baseline,等待其后的
213
- * **新** spawn——支持同一测试/文件内多次 runSpawn(旧 `length === 0` 只对首次有效)。
214
- *
215
- * runSpawn 在 mkdirSync + writePromptToTempFile(mock 的 async I/O)之后才调 spawn。
216
- * 每次推进 10ms 让轮询 setTimeout 触发,advanceTimersByTimeAsync 同时 flush 已 resolve
217
- * 的 I/O promise,使 runSpawn 继续走到 spawn。
218
- */
219
- async function waitForSpawnFake(timeoutSteps = 200): Promise<FakeChild> {
220
- const baseline = mockSpawn.mock.results.length;
221
- for (let i = 0; i < timeoutSteps; i++) {
222
- if (mockSpawn.mock.results.length > baseline) break;
223
- await vi.advanceTimersByTimeAsync(10);
224
- }
225
- if (mockSpawn.mock.results.length <= baseline) {
226
- throw new Error("spawn was not called (fake timers did not progress to spawn)");
227
- }
228
- return lastSpawnedChild();
229
- }
230
-
231
- /**
232
- * 真实 timers 下轮询直到 spawn 被调用(返回 child 控制器;用于 signal abort 测试——
233
- * 不需要推进 watchdog,用 queueMicrotask 触发 abort,真实 timers 下 mock I/O 正常 resolve)。
234
- *
235
- * [快照语义] 同 helpers/spawn-mock.ts 的 waitForSpawn:记 baseline 等**新** spawn,
236
- * 支持同文件多次 runSpawn。
237
- */
238
- async function waitForSpawnReal(timeoutMs = 1000): Promise<FakeChild> {
239
- const start = Date.now();
240
- const baseline = mockSpawn.mock.results.length;
241
- while (mockSpawn.mock.results.length <= baseline) {
242
- if (Date.now() - start > timeoutMs) {
243
- throw new Error(`spawn was not called within ${timeoutMs}ms`);
244
- }
245
- await new Promise((r) => setTimeout(r, 5));
246
- }
247
- return lastSpawnedChild();
248
- }
249
-
250
- // ============================================================
251
- // 测试
252
- // ============================================================
253
-
254
- describe("timeoutMs / signal abort → child.kill 端到端路径", () => {
255
- beforeEach(() => {
256
- vi.clearAllMocks();
257
- mockExistsSync.mockReturnValue(false);
258
- // [F-4 假红源修复] env 隔离:多数用例默认 maxTurns=undefined →
259
- // resolveSpawnWatchdogMs 走 env 兑底分支,宿主 export SPAWN_WATCHDOG 即假红
260
- //(极端值 3e9 还会在 assertSafeTimerDelay fail-fast 直接炸掉 runSpawn)。
261
- // 用例内显式 stubEnv 的值照常叠加生效(inner describe 的 afterEach
262
- // unstubAllEnvs 与本处兼容)。
263
- vi.stubEnv(SPAWN_WATCHDOG_ENV, "");
264
- });
265
-
266
- afterEach(() => {
267
- vi.restoreAllMocks();
268
- vi.unstubAllEnvs();
269
- });
270
-
271
- // ── 1. watchdog 到期 → child.kill ──
272
- //
273
- // [R1] watchdog = setTimeout(() => child.kill("SIGTERM"), computeWatchdogMs(maxTurns))。
274
- // computeWatchdogMs 下限 30min(SPAWN_WATCHDOG_FLOOR_MS),maxTurns=6 → max(30min, 30min)=30min。
275
- // 子进程卡死(turn_end 永不触发)时 limiter 失效,watchdog 兜底 kill 防资源泄漏。
276
- describe("watchdog 到期 → signal abort → child.kill", () => {
277
- beforeEach(() => {
278
- vi.useFakeTimers();
279
- });
280
-
281
- afterEach(() => {
282
- vi.useRealTimers();
283
- });
284
-
285
- it("watchdog 到期(超过 computeWatchdogMs 阈值)→ child.kill(SIGTERM) 被调用", async () => {
286
- const record = makeRecord();
287
- // maxTurns=6 → computeWatchdogMs = max(30min, 6*5min) = 30min
288
- // 不 await:runSpawn 内部 await 子进程 close,watchdog kill 后还需 emit close 才 resolve
289
- const promise = runSpawn(record, "Task: hang", makeOpts({ maxTurns: 6 }), makeCtx());
290
-
291
- const child = await waitForSpawnFake();
292
-
293
- // spawn 后尚未触发 kill
294
- expect(child.killed).toBe(false);
295
-
296
- // 推进时间越过 watchdog 阈值(30 * 60 * 1000 + 100ms 余量)
297
- await vi.advanceTimersByTimeAsync(30 * 60 * 1000 + 100);
298
-
299
- // watchdog 触发 child.kill("SIGTERM")
300
- expect(child.killed).toBe(true);
301
- expect(child.killSignal).toBe("SIGTERM");
302
-
303
- // 收尾:emit close 让 runSpawn resolve(避免悬挂)
304
- emitStdoutLine(child, sessionHeader());
305
- child.stdout.end();
306
- child.emit("close", 143); // SIGTERM = 128+15
307
-
308
- const result = await promise;
309
- // 信号终止(>=128)视为正常完成
310
- expect(result.success).toBe(true);
311
- });
312
-
313
- // [race-F4] SIGKILL 升级:watchdog SIGTERM 后子进程无视信号(exitCode/signalCode
314
- // 保持 null = 未退出)→ 30s 后升级 SIGKILL。挂住子进程永不回收的兑底防线。
315
- it("[race-F4] watchdog SIGTERM 后 30s 未 exit → 升级 SIGKILL", async () => {
316
- const record = makeRecord();
317
- const promise = runSpawn(record, "Task: hang ignores SIGTERM", makeOpts({ maxTurns: 6 }), makeCtx());
318
-
319
- const child = await waitForSpawnFake();
320
-
321
- // watchdog 到期 → SIGTERM(升级路径入口)
322
- await vi.advanceTimersByTimeAsync(30 * 60 * 1000 + 100);
323
- expect(child.killSignal).toBe("SIGTERM");
324
-
325
- // 子进程无视 SIGTERM:不 emit exit/close,exitCode/signalCode 保持 null。
326
- // 推进升级窗口:非贴边断言(waitForSpawnFake 每步 10ms 推进,升级 timer 挂载
327
- // 时刻有 ≤100ms 抖动)——先推 29s(余量 > 抖动)断言未升级,再推 2s 越过窗口。
328
- await vi.advanceTimersByTimeAsync(29 * 1000);
329
- expect(child.killSignal).toBe("SIGTERM"); // 尚未升级
330
- await vi.advanceTimersByTimeAsync(2 * 1000);
331
- expect(child.killSignal).toBe("SIGKILL"); // 已升级
332
-
333
- // 收尾:SIGKILL 后子进程退出 → runSpawn resolve(信号终止视为完成)
334
- emitStdoutLine(child, sessionHeader());
335
- child.stdout.end();
336
- child.emit("close", 137); // SIGKILL = 128+9
337
- const result = await promise;
338
- expect(result.success).toBe(true);
339
- });
340
-
341
- // [race-F4] 升级 timer 的 exit 自动 clear:SIGTERM 后子进程正常退出(exit 事件到
342
- // 达),升级 timer 被清,不误发 SIGKILL(killSignal 停留在 SIGTERM)。
343
- it("[race-F4] SIGTERM 后子进程 exit → 升级 timer 被 clear,不误发 SIGKILL", async () => {
344
- const record = makeRecord();
345
- const promise = runSpawn(record, "Task: dies on SIGTERM", makeOpts({ maxTurns: 6 }), makeCtx());
346
-
347
- const child = await waitForSpawnFake();
348
-
349
- // watchdog 到期 → SIGTERM
350
- await vi.advanceTimersByTimeAsync(30 * 60 * 1000 + 100);
351
- expect(child.killSignal).toBe("SIGTERM");
352
-
353
- // 子进程响应 SIGTERM 退出:exitCode 143(SIGTERM 终止形态)+ exit + close。
354
- // exit 事件触发升级 timer clear(killChildWithEscalation 内 once("exit") 挂钩)。
355
- child.exitCode = 143;
356
- child.emit("exit", 143);
357
- child.stdout.end();
358
- child.emit("close", 143);
359
- const result = await promise;
360
-
361
- // 越过升级窗口:无 SIGKILL(timer 已被 exit clear),killSignal 停留在 SIGTERM
362
- await vi.advanceTimersByTimeAsync(60 * 1000);
363
- expect(child.killSignal).toBe("SIGTERM");
364
- expect(result.success).toBe(true);
365
- });
366
- });
367
-
368
- // ── 2. watchdog 默认不挂载 + env 兑底(预算语义对齐 2026-08)──
369
- //
370
- // 语义:maxTurns 未传 → env 兑底;显式 0/负 → 压过 env 不挂(U5,SP-6 参数 > env,
371
- // 旧实现 maxTurns:0 落到 env 兑底,参数显式关不掉 watchdog,已废);
372
- // env XYZ_SUBAGENT_SPAWN_WATCHDOG_MS 设置时按绝对时限兑底挂载(hang 泄漏防线
373
- // opt-in)。resolveSpawnWatchdogMs 是挂载判定唯一入口。
374
- describe("watchdog 默认不挂载 + env 兑底(预算语义对齐)", () => {
375
- beforeEach(() => {
376
- vi.useFakeTimers();
377
- });
378
-
379
- afterEach(() => {
380
- vi.useRealTimers();
381
- vi.unstubAllEnvs();
382
- });
383
-
384
- it("resolveSpawnWatchdogMs:maxTurns 未传/0/负数且 env 未设 → undefined(不挂)", () => {
385
- vi.stubEnv(SPAWN_WATCHDOG_ENV, ""); // 空串 = 未设(raw falsy)
386
- expect(resolveSpawnWatchdogMs(undefined)).toBeUndefined();
387
- expect(resolveSpawnWatchdogMs(null)).toBeUndefined();
388
- expect(resolveSpawnWatchdogMs(0)).toBeUndefined();
389
- expect(resolveSpawnWatchdogMs(-5)).toBeUndefined();
390
- });
391
-
392
- it("resolveSpawnWatchdogMs:env 设置 → 绝对时限;非法值 → undefined;maxTurns 有效优先", () => {
393
- vi.stubEnv(SPAWN_WATCHDOG_ENV, "60000");
394
- expect(resolveSpawnWatchdogMs(undefined)).toBe(60_000);
395
- // [U5] SP-6 参数 > env:显式 0 压过 env 兑底 → 不挂(旧实现落成 60000)
396
- expect(resolveSpawnWatchdogMs(0)).toBeUndefined();
397
- expect(resolveSpawnWatchdogMs(-5)).toBeUndefined();
398
- // maxTurns 有效(>0)→ turns 估算优先于 env 兑底
399
- expect(resolveSpawnWatchdogMs(6)).toBe(computeWatchdogMs(6));
400
- vi.stubEnv(SPAWN_WATCHDOG_ENV, "abc");
401
- expect(resolveSpawnWatchdogMs(undefined)).toBeUndefined();
402
- vi.stubEnv(SPAWN_WATCHDOG_ENV, "-1");
403
- expect(resolveSpawnWatchdogMs(undefined)).toBeUndefined();
404
- });
405
-
406
- // [U1] setTimeout 2^31-1 溢出 fail-fast:溢出 delay 被 Node 置 1ms 立即触发
407
- //(watchdog 变「启动即杀」),两路径(env 兑底 / turns 估算)都必须在入口拦截。
408
- it("resolveSpawnWatchdogMs:env 值溢出(>2^31-1)→ fail-fast throw(不静默 clamp/不挂)", () => {
409
- vi.stubEnv(SPAWN_WATCHDOG_ENV, "3000000000");
410
- expect(() => resolveSpawnWatchdogMs(undefined)).toThrowError(/2147483647/);
411
- expect(() => resolveSpawnWatchdogMs(undefined)).toThrowError(/Recovery/);
412
- });
413
-
414
- it("resolveSpawnWatchdogMs:maxTurns 估算值溢出 → fail-fast throw", () => {
415
- vi.stubEnv(SPAWN_WATCHDOG_ENV, "");
416
- // maxTurns=1e6 → 估算 5e9 ms > 2^31-1,入口拦截
417
- expect(() => resolveSpawnWatchdogMs(1_000_000)).toThrowError(/2147483647/);
418
- });
419
-
420
- // [F-R3] maxTurns 垃圾值网格:NaN/""/null/0/负数(含 ±Infinity)。
421
- // 旧实现 NaN/"" 绕过 Number.isFinite 且压过 env 兑底成「显式不限」。
422
- it("resolveSpawnWatchdogMs:maxTurns NaN → fail-fast throw(压过 env);\"\"→Number(\"\")=0 显式不限;null/0/负数网格(F-R3)", () => {
423
- vi.stubEnv(SPAWN_WATCHDOG_ENV, "60000");
424
- // NaN:非有限数 fail-fast(与 timer-delay U1 同语义),不再压过 env 兑底成「显式不限」
425
- expect(() => resolveSpawnWatchdogMs(NaN)).toThrowError(/not a finite number/);
426
- expect(() => resolveSpawnWatchdogMs(NaN)).toThrowError(/Recovery/);
427
- // ±Infinity:同非有限数 fail-fast
428
- expect(() => resolveSpawnWatchdogMs(Infinity)).toThrowError(/not a finite number/);
429
- // "":Number("") === 0 → 与显式 0 同路径(显式不限,压过 env,U5 语义不回归)
430
- expect(resolveSpawnWatchdogMs("" as unknown as number)).toBeUndefined();
431
- // null → 未传语义,env 兑底生效
432
- expect(resolveSpawnWatchdogMs(null)).toBe(60_000);
433
- // 0 / 负数 → 显式不限,压过 env(既有 U5 网格不回归)
434
- expect(resolveSpawnWatchdogMs(0)).toBeUndefined();
435
- expect(resolveSpawnWatchdogMs(-5)).toBeUndefined();
436
- });
437
-
438
- it("maxTurns 未传 → 推进 56min 不 kill(旧实现 50min 估算会误杀)", async () => {
439
- vi.stubEnv(SPAWN_WATCHDOG_ENV, "");
440
- const record = makeRecord();
441
- const promise = runSpawn(record, "Task: no-turns", makeOpts({ maxTurns: undefined }), makeCtx());
442
-
443
- const child = await waitForSpawnFake();
444
- expect(child.killed).toBe(false);
445
-
446
- // 旧实现的估算默认(10 turns → 50min)已过,仍不 kill = watchdog 未挂载
447
- await vi.advanceTimersByTimeAsync(56 * 60 * 1000);
448
- expect(child.killed).toBe(false);
449
-
450
- emitStdoutLine(child, sessionHeader());
451
- child.stdout.end();
452
- child.emit("close", 0);
453
- const result = await promise;
454
- expect(result.success).toBe(true);
455
- });
456
-
457
- it("maxTurns: 0(显式关不掉的旧默认已废)→ 同样不挂 watchdog", async () => {
458
- vi.stubEnv(SPAWN_WATCHDOG_ENV, "");
459
- const record = makeRecord();
460
- const promise = runSpawn(record, "Task: zero-turns", makeOpts({ maxTurns: 0 }), makeCtx());
461
-
462
- const child = await waitForSpawnFake();
463
- await vi.advanceTimersByTimeAsync(56 * 60 * 1000);
464
- expect(child.killed).toBe(false);
465
-
466
- emitStdoutLine(child, sessionHeader());
467
- child.stdout.end();
468
- child.emit("close", 0);
469
- const result = await promise;
470
- expect(result.success).toBe(true);
471
- });
472
-
473
- // [U5] SP-6 参数 > env:显式 maxTurns:0 压过 SPAWN_WATCHDOG_ENV 兑底——
474
- // 旧实现落成 env 时限 kill,参数显式关不掉 watchdog。
475
- it("maxTurns: 0 + env=60000 → 显式 0 压过 env,61s 不 kill(U5)", async () => {
476
- vi.stubEnv(SPAWN_WATCHDOG_ENV, "60000");
477
- const record = makeRecord();
478
- const promise = runSpawn(record, "Task: zero-turns-override", makeOpts({ maxTurns: 0 }), makeCtx());
479
-
480
- const child = await waitForSpawnFake();
481
- // 推进越过 env 兑底时限(60s):若 maxTurns:0 未压过 env,此处已被 kill
482
- await vi.advanceTimersByTimeAsync(61 * 1000);
483
- expect(child.killed).toBe(false);
484
-
485
- emitStdoutLine(child, sessionHeader());
486
- child.stdout.end();
487
- child.emit("close", 0);
488
- const result = await promise;
489
- expect(result.success).toBe(true);
490
- });
491
-
492
- it("env SPAWN_WATCHDOG_MS=60000 + maxTurns 未传 → 1min 兑底 kill", async () => {
493
- vi.stubEnv(SPAWN_WATCHDOG_ENV, "60000");
494
- const record = makeRecord();
495
- const promise = runSpawn(record, "Task: env-watchdog", makeOpts(), makeCtx());
496
-
497
- const child = await waitForSpawnFake();
498
- // 非贴边断言:waitForSpawnFake 每步 10ms 推进,watchdog 挂载时刻有 ≤10ms 抖动,
499
- // 用 55s / 61s 两个窗口避开 60s 贴边比较
500
- await vi.advanceTimersByTimeAsync(55_000);
501
- expect(child.killed).toBe(false);
502
- await vi.advanceTimersByTimeAsync(6_000);
503
- expect(child.killed).toBe(true);
504
- expect(child.killSignal).toBe("SIGTERM");
505
-
506
- emitStdoutLine(child, sessionHeader());
507
- child.stdout.end();
508
- child.emit("close", 143);
509
- const result = await promise;
510
- expect(result.success).toBe(true);
511
- });
512
- });
513
-
514
- // ── 3. watchdog 到期前正常完成 → clearTimeout 生效,不 kill ──
515
- describe("watchdog 到期前正常完成 → 不 kill", () => {
516
- beforeEach(() => {
517
- vi.useFakeTimers();
518
- });
519
-
520
- afterEach(() => {
521
- vi.useRealTimers();
522
- });
523
-
524
- it("正常 close(0) 先于 watchdog 到期 → clearTimeout 生效,推进时间后 child 未被 kill", async () => {
525
- const record = makeRecord();
526
- // maxTurns=6 → watchdog=30min
527
- const promise = runSpawn(record, "Task: quick", makeOpts({ maxTurns: 6 }), makeCtx());
528
-
529
- const child = await waitForSpawnFake();
530
-
531
- // 正常完成:emit header + close(0)(远早于 30min watchdog)
532
- emitStdoutLine(child, sessionHeader());
533
- child.stdout.end();
534
- child.emit("close", 0);
535
-
536
- const result = await promise;
537
- expect(result.success).toBe(true);
538
-
539
- // close 后 runSpawn 已 clearTimeout(watchdog);推进 30+ 分钟验证 watchdog 未触发 kill
540
- await vi.advanceTimersByTimeAsync(30 * 60 * 1000 + 100);
541
-
542
- expect(child.killed).toBe(false);
543
- expect(child.killSignal).toBeUndefined();
544
- });
545
- });
546
-
547
- // ── 3. 外部 signal abort → child.kill ──
548
- //
549
- // [d] onAbort = () => child.kill("SIGTERM"),opts.signal.addEventListener("abort", onAbort, {once:true})。
550
- // 前置检查:if (opts.signal?.aborted) onAbort()——spawn 前已 aborted 时 addEventListener
551
- // 不会触发,立即 kill 兑现取消语义。
552
- describe("外部 signal abort → child.kill", () => {
553
- it("运行中 abort signal → child.kill(SIGTERM) 被调用,success=false(取消语义)", async () => {
554
- const controller = new AbortController();
555
- const record = makeRecord();
556
- const promise = runSpawn(
557
- record,
558
- "Task: cancelled",
559
- makeOpts({ signal: controller.signal }),
560
- makeCtx(),
561
- );
562
-
563
- const child = await waitForSpawnReal();
564
-
565
- // abort 必须在 spawn 之后(addEventListener 已注册)。
566
- // queueMicrotask 延迟到当前微任务清空后触发,确保 listener 已挂载。
567
- queueMicrotask(() => controller.abort());
568
-
569
- // emit header + close(被 kill 后子进程退出,signal 终止 exitCode>=128)
570
- emitStdoutLine(child, sessionHeader());
571
- child.stdout.end();
572
- child.emit("close", 143); // SIGTERM = 128+15
573
-
574
- const result = await promise;
575
-
576
- expect(child.killed).toBe(true);
577
- expect(child.killSignal).toBe("SIGTERM");
578
- // signal.aborted 路径:success=false,但 error 为 undefined(取消不算 error)
579
- expect(result.success).toBe(false);
580
- expect(result.error).toBeUndefined();
581
- });
582
-
583
- it("spawn 前已 aborted 的 signal → 前置检查立即 kill,兑现取消语义", async () => {
584
- // 覆盖 session-runner.ts L570: if (opts.signal?.aborted) onAbort()
585
- // 已 aborted 的 signal addEventListener("abort") 不会再触发回调,
586
- // 故 runSpawn 在注册 listener 后立即前置检查,直接 kill 兑现取消。
587
- const controller = new AbortController();
588
- controller.abort(); // spawn 前已 abort
589
-
590
- const record = makeRecord();
591
- const promise = runSpawn(
592
- record,
593
- "Task: pre-aborted",
594
- makeOpts({ signal: controller.signal }),
595
- makeCtx(),
596
- );
597
-
598
- const child = await waitForSpawnReal();
599
-
600
- // 前置检查在 spawn 后同步执行 → child 立即被 kill
601
- expect(child.killed).toBe(true);
602
- expect(child.killSignal).toBe("SIGTERM");
603
-
604
- // 收尾:emit close 让 runSpawn resolve
605
- emitStdoutLine(child, sessionHeader());
606
- child.stdout.end();
607
- child.emit("close", 143);
608
-
609
- const result = await promise;
610
- // signal.aborted → success=false,error undefined
611
- expect(result.success).toBe(false);
612
- expect(result.error).toBeUndefined();
613
- });
614
- });
615
- });
@@ -1,73 +0,0 @@
1
- // src/__tests__/tombstone-store.test.ts
2
- //
3
- // tombstone-store 专属测试。
4
- // 覆盖:write→read 往返 / 缺 sidecar → undefined / 损坏 sidecar → undefined / 结构校验。
5
-
6
- import * as fs from "node:fs";
7
- import * as os from "node:os";
8
- import * as path from "node:path";
9
-
10
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
11
-
12
- import { readCancelledTombstone, writeCancelledTombstone } from "../tombstone-store.ts";
13
-
14
- describe("tombstone-store", () => {
15
- let tmpDir: string;
16
- let sessionFile: string;
17
-
18
- beforeEach(() => {
19
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "ts-test-"));
20
- sessionFile = path.join(tmpDir, "2026-01-01_uuid.jsonl");
21
- });
22
- afterEach(() => {
23
- fs.rmSync(tmpDir, { recursive: true, force: true });
24
- });
25
-
26
- describe("write → read 往返", () => {
27
- it("写入后读回完整数据", () => {
28
- writeCancelledTombstone(sessionFile, {
29
- id: "bg-1", status: "cancelled", agent: "worker", startedAt: 1000, endedAt: 2000,
30
- });
31
- const tomb = readCancelledTombstone(sessionFile);
32
- expect(tomb).toEqual({
33
- id: "bg-1", status: "cancelled", agent: "worker", startedAt: 1000, endedAt: 2000,
34
- });
35
- });
36
-
37
- it("sidecar 路径 = sessionFile + '.cancelled'", () => {
38
- writeCancelledTombstone(sessionFile, {
39
- id: "bg-1", status: "cancelled", agent: "w", startedAt: 1, endedAt: 2,
40
- });
41
- expect(fs.existsSync(`${sessionFile}.cancelled`)).toBe(true);
42
- });
43
- });
44
-
45
- describe("读降级", () => {
46
- it("无 sidecar → undefined(正常——非 cancelled record)", () => {
47
- expect(readCancelledTombstone(sessionFile)).toBeUndefined();
48
- });
49
-
50
- it("损坏 JSON → undefined", () => {
51
- fs.writeFileSync(`${sessionFile}.cancelled`, "NOT JSON\n", "utf-8");
52
- expect(readCancelledTombstone(sessionFile)).toBeUndefined();
53
- });
54
-
55
- it("status 非 'cancelled' → undefined", () => {
56
- fs.writeFileSync(
57
- `${sessionFile}.cancelled`,
58
- `${JSON.stringify({ id: "x", status: "done", agent: "w", startedAt: 1, endedAt: 2 })}\n`,
59
- "utf-8",
60
- );
61
- expect(readCancelledTombstone(sessionFile)).toBeUndefined();
62
- });
63
-
64
- it("缺必填字段 → undefined", () => {
65
- fs.writeFileSync(
66
- `${sessionFile}.cancelled`,
67
- `${JSON.stringify({ id: "x", status: "cancelled" })}\n`, // 缺 agent/startedAt/endedAt
68
- "utf-8",
69
- );
70
- expect(readCancelledTombstone(sessionFile)).toBeUndefined();
71
- });
72
- });
73
- });