@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,385 +0,0 @@
1
- // src/__tests__/tool-action.test.ts
2
- //
3
- // tool action 路由 + adapter 出参结构测试(AC-2/AC-3/AC-9)。
4
- // 用 stub SubagentService(不依赖真实 SDK),测 handler + adapter 纯逻辑。
5
-
6
- import { describe, expect, it, vi } from "vitest";
7
-
8
- import { adapter, cancelHandler, listHandler, startHandler } from "../../interface/subagent-actions.ts";
9
- import type { SubagentService } from "../subagent-service.ts";
10
- import type {
11
- ExecutionHandle,
12
- RecordSnapshot,
13
- SubagentRecord,
14
- SubagentToolDetails,
15
- } from "../types.ts";
16
-
17
- // ── stub 工厂 ──
18
-
19
- function makeDetails(over: Partial<SubagentToolDetails> = {}): SubagentToolDetails {
20
- return {
21
- status: "closed",
22
- mode: "background",
23
- agent: "worker",
24
- model: "test/model",
25
- thinkingLevel: undefined,
26
- slug: "test-slug",
27
- turns: 1,
28
- totalTokens: 10,
29
- elapsedSeconds: 1,
30
- eventLog: [],
31
- result: "ok",
32
- ...over,
33
- };
34
- }
35
-
36
- function makeSnapshot(over: Partial<RecordSnapshot> = {}): RecordSnapshot {
37
- return {
38
- id: "run-1",
39
- agent: "worker",
40
- model: "test/model",
41
- thinkingLevel: undefined,
42
- mode: "background",
43
- task: "t",
44
- slug: "test-slug",
45
- status: "closed",
46
- eventLog: [],
47
- turns: 1,
48
- totalTokens: 10,
49
- startedAt: 1000,
50
- endedAt: 2000,
51
- result: "ok",
52
- error: undefined,
53
- sessionFile: undefined,
54
- ...over,
55
- };
56
- }
57
-
58
- function makeService(over: Partial<SubagentService> = {}): SubagentService {
59
- return {
60
- execute: vi.fn(),
61
- findRecord: vi.fn(() => undefined),
62
- cancel: vi.fn(() => false),
63
- collectRecords: vi.fn(() => [] as SubagentRecord[]),
64
- // [perf] listHandler 逐项 enrich 会调 getFullRecord;mock 回 undefined → 调用方回退 light record
65
- getFullRecord: vi.fn(() => undefined as SubagentRecord | undefined),
66
- ...over,
67
- } as SubagentService;
68
- }
69
-
70
- // ============================================================
71
- // startHandler
72
- // ============================================================
73
- describe("startHandler", () => {
74
- it("缺 input → throw + Correct 正例", async () => {
75
- const svc = makeService();
76
- await expect(startHandler(svc, undefined, undefined)).rejects.toThrow(/task and slug/);
77
- // Correct 正例存在(让弱模型撞错后能直接照抄平铺形态)
78
- await expect(startHandler(svc, undefined, undefined)).rejects.toThrow(/Correct: \{"action":"start"/);
79
- });
80
-
81
- it("task 空白 → throw + Correct 正例", async () => {
82
- const svc = makeService();
83
- await expect(startHandler(svc, { task: " ", slug: "x" }, undefined)).rejects.toThrow(/task is required/);
84
- await expect(startHandler(svc, { task: " ", slug: "x" }, undefined)).rejects.toThrow(/Correct: \{"action":"start"/);
85
- });
86
-
87
- it("slug 缺失 → throw + Correct 正例", async () => {
88
- const svc = makeService();
89
- await expect(startHandler(svc, { task: "ok" }, undefined)).rejects.toThrow(/slug is required/);
90
- await expect(startHandler(svc, { task: "ok" }, undefined)).rejects.toThrow(/Correct: \{"action":"start"/);
91
- });
92
-
93
- it("slug 空白 → throw + Correct 正例", async () => {
94
- const svc = makeService();
95
- await expect(startHandler(svc, { task: "ok", slug: " " }, undefined)).rejects.toThrow(/slug is required/);
96
- await expect(startHandler(svc, { task: "ok", slug: " " }, undefined)).rejects.toThrow(/Correct: \{"action":"start"/);
97
- });
98
-
99
- it("slug 超 35 字符 → throw", async () => {
100
- const svc = makeService();
101
- await expect(
102
- startHandler(svc, { task: "ok", slug: "a".repeat(36) }, undefined),
103
- ).rejects.toThrow(/≤35 chars/);
104
- });
105
-
106
- it("background 启动 → kind=bg + bgResponse.message 含 detached", async () => {
107
- const svc = makeService({
108
- execute: vi.fn(async (): Promise<ExecutionHandle> => ({
109
- mode: "background",
110
- subagentId: "bg-1-123",
111
- sessionFile: undefined,
112
- details: makeDetails({ status: "running", mode: "background" }),
113
- })),
114
- });
115
- const r = await startHandler(svc, { task: "long", slug: "long-running" }, undefined);
116
- expect(r.kind).toBe("bg");
117
- if (r.kind !== "bg") return;
118
- expect(r.subagentId).toBe("bg-1-123");
119
- expect(r.response.message).toMatch(/detached/);
120
- });
121
- });
122
-
123
- // ============================================================
124
- // listHandler
125
- // ============================================================
126
- describe("listHandler", () => {
127
- it("空 → running:0, items:[]", () => {
128
- const svc = makeService({ collectRecords: vi.fn(() => [] as SubagentRecord[]) });
129
- const r = listHandler(svc, undefined);
130
- expect(r.response).toEqual({ running: 0, items: [] });
131
- });
132
-
133
- it("limit 夹紧 [1,100]——collectRecords 收到夹紧后的值(C1 回归)", () => {
134
- const collect = vi.fn(() => [] as SubagentRecord[]);
135
- const svc = makeService({ collectRecords: collect });
136
- // includeFinished=true 时 filter="all",验证 limit 夹紧:
137
- // 0 → 1
138
- listHandler(svc, { includeFinished: true, limit: 0 });
139
- expect(collect).toHaveBeenLastCalledWith(1, "all");
140
- // 100000 → 100
141
- listHandler(svc, { includeFinished: true, limit: 100000 });
142
- expect(collect).toHaveBeenLastCalledWith(100, "all");
143
- // undefined → 20(默认)
144
- listHandler(svc, { includeFinished: true });
145
- expect(collect).toHaveBeenLastCalledWith(20, "all");
146
- // 负数 → 1
147
- listHandler(svc, { includeFinished: true, limit: -5 });
148
- expect(collect).toHaveBeenLastCalledWith(1, "all");
149
- });
150
-
151
- it("includeFinished=false → collectRecords 收到 filter='running'(C2 回归)", () => {
152
- const collect = vi.fn(() => [] as SubagentRecord[]);
153
- const svc = makeService({ collectRecords: collect });
154
- // includeFinished=false → filter="running"(防截断下沉到 store)
155
- listHandler(svc, { includeFinished: false, limit: 5 });
156
- expect(collect).toHaveBeenLastCalledWith(5, "running");
157
- // includeFinished=true → filter="all"
158
- listHandler(svc, { includeFinished: true, limit: 5 });
159
- expect(collect).toHaveBeenLastCalledWith(5, "all");
160
- });
161
-
162
- it("includeFinished=false → collectRecords 返回 running-only(过滤在 store 层)", () => {
163
- // store 层已过滤,listHandler 只做透传——mock 返回的即 running-only。
164
- const records: SubagentRecord[] = [
165
- { id: "r1", agent: "w", status: "running", mode: "background", startedAt: 1, endedAt: undefined, turns: 0, totalTokens: 0, model: "m", thinkingLevel: undefined, eventLog: [] },
166
- ];
167
- const svc = makeService({ collectRecords: vi.fn(() => records) });
168
- const r = listHandler(svc, { includeFinished: false });
169
- expect(r.response.items).toHaveLength(1);
170
- expect(r.response.items[0].subagentId).toBe("r1");
171
- expect(r.response.running).toBe(1);
172
- });
173
-
174
- it("item 8 字段齐全(含 duration 实时计算)", () => {
175
- const records: SubagentRecord[] = [
176
- { id: "r1", agent: "w", status: "closed", mode: "background", startedAt: 1000, endedAt: 2500, turns: 2, totalTokens: 50, model: "m", thinkingLevel: "high", eventLog: [], sessionFile: "x.jsonl" },
177
- ];
178
- const svc = makeService({ collectRecords: vi.fn(() => records) });
179
- const r = listHandler(svc, { includeFinished: true });
180
- const item = r.response.items[0];
181
- expect(item).toMatchObject({
182
- subagentId: "r1", agent: "w", status: "closed", mode: "background",
183
- duration: 1, model: "m", totalTokens: 50, sessionFile: "x.jsonl",
184
- });
185
- });
186
-
187
- it("items 超过 limit → 截断在 store 层(listHandler 透传 limit)", () => {
188
- // 截断责任在 collectRecords(store 层),listHandler 只透传 limit + map。
189
- // mock 返回 3 条(模拟 store 未截断),验证 listHandler 不自行截断——全量透传。
190
- const records: SubagentRecord[] = [
191
- { id: "r1", agent: "w", status: "running", mode: "background", startedAt: 1, endedAt: undefined, turns: 0, totalTokens: 0, model: "m", thinkingLevel: undefined, eventLog: [] },
192
- { id: "r2", agent: "w", status: "running", mode: "background", startedAt: 2, endedAt: undefined, turns: 0, totalTokens: 0, model: "m", thinkingLevel: undefined, eventLog: [] },
193
- { id: "r3", agent: "w", status: "running", mode: "background", startedAt: 3, endedAt: undefined, turns: 0, totalTokens: 0, model: "m", thinkingLevel: undefined, eventLog: [] },
194
- ];
195
- const collect = vi.fn(() => records);
196
- const svc = makeService({ collectRecords: collect });
197
- const r = listHandler(svc, { includeFinished: true, limit: 2 });
198
- // listHandler 透传 collectRecords 的结果(截断是 store 的责任)。
199
- expect(r.response.items).toHaveLength(3);
200
- // 验证 limit 确实透传给了 collectRecords。
201
- expect(collect).toHaveBeenCalledWith(2, "all");
202
- });
203
-
204
- // TC-2: running 态实时 duration(Date.now()-startedAt)随时间增长,
205
- // 区别于终态 endedAt-startedAt。喂给 live TUI,更易出 bug。
206
- it("running 态 duration 实时计算:两次 list 间隔 2s,duration 差 = 2(TC-2)", () => {
207
- vi.useFakeTimers({ now: 10_000 });
208
- try {
209
- const records: SubagentRecord[] = [
210
- { id: "r1", agent: "w", status: "running", mode: "background", startedAt: 7_000, endedAt: undefined, turns: 0, totalTokens: 0, model: "m", thinkingLevel: undefined, eventLog: [] },
211
- ];
212
- const svc = makeService({ collectRecords: vi.fn(() => records) });
213
- const r1 = listHandler(svc, { includeFinished: true });
214
- expect(r1.response.items[0].duration).toBe(3); // (10-7)s
215
- vi.advanceTimersByTime(2_000);
216
- const r2 = listHandler(svc, { includeFinished: true });
217
- expect(r2.response.items[0].duration).toBe(5); // (12-7)s
218
- } finally {
219
- vi.useRealTimers();
220
- }
221
- });
222
-
223
- // TC-3: listHandler 保持 collectRecords 返回的顺序(排序责任在 service 层,
224
- // listHandler 是顺序透传)。故意传入反序 list 验证 listHandler 不自行排序。
225
- it("保持 collectRecords 顺序,不自行重排(TC-3)", () => {
226
- // 故意按 startedAt 升序(与 desc 相反)
227
- const records: SubagentRecord[] = [
228
- { id: "old", agent: "w", status: "running", mode: "background", startedAt: 1, endedAt: undefined, turns: 0, totalTokens: 0, model: "m", thinkingLevel: undefined, eventLog: [] },
229
- { id: "new", agent: "w", status: "running", mode: "background", startedAt: 5, endedAt: undefined, turns: 0, totalTokens: 0, model: "m", thinkingLevel: undefined, eventLog: [] },
230
- ];
231
- const svc = makeService({ collectRecords: vi.fn(() => records) });
232
- const r = listHandler(svc, { includeFinished: true });
233
- expect(r.response.items.map((i) => i.subagentId)).toEqual(["old", "new"]);
234
- });
235
- });
236
-
237
- // ============================================================
238
- // cancelHandler
239
- // ============================================================
240
- describe("cancelHandler", () => {
241
- it("缺 subagentId → throw", async () => {
242
- const svc = makeService();
243
- await expect(cancelHandler(svc, undefined)).rejects.toThrow(/subagentId is required/);
244
- await expect(cancelHandler(svc, { subagentId: " " })).rejects.toThrow(/subagentId is required/);
245
- });
246
-
247
- it("id 不存在 → throw No subagent record", async () => {
248
- const svc = makeService({ findRecord: vi.fn(() => undefined) });
249
- await expect(cancelHandler(svc, { subagentId: "nope" })).rejects.toThrow(/No subagent record with id "nope"/);
250
- });
251
-
252
- it("[S-19] id 属树内其他进程的 running record(磁盘可见、本进程内存无)→ throw 跨进程专属消息", async () => {
253
- // MF-1 全树可见后:子进程 list 能看到父/兄弟进程的 running record,但 cancel 只作用于
254
- // 本进程内存。旧消息「may have finished」误导(该 record 未 finished、正被列出)。
255
- const foreign: SubagentRecord = {
256
- id: "bg-foreign", agent: "w", status: "running", mode: "background", startedAt: 1,
257
- endedAt: undefined, turns: 0, totalTokens: 0, model: "m", thinkingLevel: undefined, eventLog: [],
258
- };
259
- const svc = makeService({
260
- findRecord: vi.fn(() => undefined),
261
- collectRecords: vi.fn(() => [foreign] as SubagentRecord[]),
262
- });
263
- await expect(cancelHandler(svc, { subagentId: "bg-foreign" })).rejects.toThrow(/owned by another process in the tree/);
264
- });
265
-
266
- it("[S-19] id 在树内但已终态(磁盘可见 done)→ 仍用 may have finished 消息", async () => {
267
- const done: SubagentRecord = {
268
- id: "bg-done", agent: "w", status: "closed", mode: "background", startedAt: 1,
269
- endedAt: 2, turns: 0, totalTokens: 0, model: "m", thinkingLevel: undefined, eventLog: [],
270
- };
271
- const svc = makeService({
272
- findRecord: vi.fn(() => undefined),
273
- collectRecords: vi.fn(() => [done] as SubagentRecord[]),
274
- });
275
- await expect(cancelHandler(svc, { subagentId: "bg-done" })).rejects.toThrow(/may have finished/);
276
- });
277
-
278
- it("已终态(cancel 返回 false)→ throw could not be cancelled", async () => {
279
- const svc = makeService({
280
- findRecord: vi.fn(() => makeSnapshot({ id: "bg-1", mode: "background", status: "closed" })),
281
- cancel: vi.fn(() => false),
282
- });
283
- await expect(cancelHandler(svc, { subagentId: "bg-1" })).rejects.toThrow(/could not be cancelled.*status: closed/);
284
- });
285
-
286
- it("成功 → cancelled:true", async () => {
287
- const svc = makeService({
288
- findRecord: vi.fn(() => makeSnapshot({ id: "bg-1", mode: "background", status: "running" })),
289
- cancel: vi.fn(() => true),
290
- });
291
- const r = await cancelHandler(svc, { subagentId: "bg-1" });
292
- expect(r.subagentId).toBe("bg-1");
293
- expect(r.response.cancelled).toBe(true);
294
- });
295
-
296
- // TC-1: 并发 CAS 场景——同一 id 两次 cancel,第一次抢锁成功,第二次 CAS 失败后
297
- // re-query 到终态。覆盖 cancelHandler 的 re-query 分支(subagent-actions.ts:267-272)。
298
- it("并发 CAS:两次 cancel 同一 id,第二次 CAS 失败 re-query 到终态", async () => {
299
- const running = makeSnapshot({ id: "bg-cas", mode: "background", status: "running" });
300
- const done = makeSnapshot({ id: "bg-cas", mode: "background", status: "closed" });
301
-
302
- let cancelCalls = 0;
303
- const svc = makeService({
304
- // findRecord 调用序列:
305
- // call 1: 第一次 cancel 初始查询 → running
306
- // call 2: 第二次 cancel 初始查询 → running(快照尚未过期)
307
- // call 3: 第二次 cancel CAS 失败后 re-query → done(状态已变)
308
- findRecord: vi.fn()
309
- .mockReturnValueOnce(running)
310
- .mockReturnValueOnce(running)
311
- .mockReturnValueOnce(done),
312
- cancel: vi.fn(() => {
313
- cancelCalls++;
314
- return cancelCalls === 1; // 第一次 true,第二次 false(CAS 失败)
315
- }),
316
- });
317
-
318
- // 第一次 cancel:抢锁成功
319
- const r1 = await cancelHandler(svc, { subagentId: "bg-cas" });
320
- expect(r1.response.cancelled).toBe(true);
321
-
322
- // 第二次 cancel:CAS 失败,re-query 返回 closed(不是初始的 running)
323
- await expect(cancelHandler(svc, { subagentId: "bg-cas" }))
324
- .rejects.toThrow(/could not be cancelled.*status: closed/);
325
-
326
- // 验证 re-query 确实发生(3 次 findRecord:1 初始 + 1 初始 + 1 re-query)
327
- expect(svc.findRecord).toHaveBeenCalledTimes(3);
328
- expect(svc.cancel).toHaveBeenCalledTimes(2);
329
- });
330
-
331
- // BL-3 回归:CAS 失败后 re-query 时 record 已被 archive 移出内存,
332
- // 文案诚实报告 "evicted" 而非回落到可能过期的 stale status。
333
- it("CAS 失败 + record 被内存淘汰 → 文案诚实报告 evicted(BL-3)", async () => {
334
- const svc = makeService({
335
- // 第一次 findRecord:初始查询 → running
336
- // 第二次 findRecord:CAS 失败后 re-query → undefined(已被淘汰)
337
- findRecord: vi.fn()
338
- .mockReturnValueOnce(makeSnapshot({ id: "bg-evict", mode: "background", status: "running" }))
339
- .mockReturnValueOnce(undefined),
340
- cancel: vi.fn(() => false),
341
- });
342
- await expect(cancelHandler(svc, { subagentId: "bg-evict" }))
343
- .rejects.toThrow(/could not be cancelled.*status: unknown \(evicted from memory\)/);
344
- expect(svc.findRecord).toHaveBeenCalledTimes(2);
345
- });
346
- });
347
-
348
- // ============================================================
349
- // adapter
350
- // ============================================================
351
- describe("adapter", () => {
352
- it("start bg → SubagentToolResult.bgResponse + slug 透传 + content 合法 JSON(C3 回归)", () => {
353
- const r = adapter({
354
- action: "start",
355
- domain: {
356
- kind: "bg", subagentId: "bg-1", sessionFile: undefined, slug: "extract-urls",
357
- response: { status: "running", mode: "background", message: "detached, will notify on completion" },
358
- },
359
- });
360
- expect(r.details.action).toBe("start");
361
- expect(r.details.subagentId).toBe("bg-1");
362
- expect(r.details.bgResponse).toBeDefined();
363
- expect(r.details.sessionFile).toBeNull();
364
- // slug 透传到 result(renderResult 的 background 行展示用)
365
- expect(r.details.slug).toBe("extract-urls");
366
- // content JSON round-trip(bg 序列化回归)
367
- const parsed = JSON.parse(r.content[0]!.text);
368
- expect(parsed.bgResponse.message).toMatch(/detached/);
369
- expect(parsed.slug).toBe("extract-urls");
370
- });
371
-
372
- it("list → 最外层 subagentId/sessionFile 为 null", () => {
373
- const r = adapter({ action: "list", domain: { response: { running: 0, items: [] } } });
374
- expect(r.details.action).toBe("list");
375
- expect(r.details.subagentId).toBeNull();
376
- expect(r.details.sessionFile).toBeNull();
377
- expect(r.details.listResponse).toEqual({ running: 0, items: [] });
378
- });
379
-
380
- it("cancel → cancelResponse.cancelled:true 字面量", () => {
381
- const r = adapter({ action: "cancel", domain: { subagentId: "bg-1", response: { cancelled: true } } });
382
- expect(r.details.cancelResponse).toEqual({ cancelled: true });
383
- expect(r.details.subagentId).toBe("bg-1");
384
- });
385
- });
@@ -1,81 +0,0 @@
1
- /**
2
- * truncLine — indexOf 化(IF12/#18)byte-identical 快照 + 非 SGR ESC 行为锚定。
3
- *
4
- * fixture(__fixtures__/truncline.snapshot.json)由**改造前实现**生成落盘
5
- * (本文件同 commit),断言重构后输出逐字节一致——含 OSC / 裸 \x1b / \x1b[K
6
- * 非 SGR ESC 用例(等价定义点:非 SGR ESC 不构成文本段边界,按文本计宽进 result)。
7
- */
8
- import { readFileSync } from "node:fs";
9
- import { fileURLToPath } from "node:url";
10
- import { dirname, join } from "node:path";
11
-
12
- import { describe, expect, it } from "vitest";
13
-
14
- import { truncLine } from "../../interface/format.ts";
15
-
16
- interface SnapshotCase {
17
- label: string;
18
- maxWidth: number;
19
- text: string;
20
- expected: string;
21
- }
22
-
23
- function loadCases(): SnapshotCase[] {
24
- const here = dirname(fileURLToPath(import.meta.url));
25
- return JSON.parse(
26
- readFileSync(join(here, "__fixtures__", "truncline.snapshot.json"), "utf8"),
27
- ) as SnapshotCase[];
28
- }
29
-
30
- describe("truncLine — indexOf 化 byte-identical 快照(IF12)", () => {
31
- it("全部 fixture 用例(10k ANSI 混合行 / CJK / OSC / 裸 ESC / CSI-K / emoji)与改造前输出逐字节一致", () => {
32
- const cases = loadCases();
33
- expect(cases.length).toBeGreaterThanOrEqual(10);
34
- for (const c of cases) {
35
- expect(truncLine(c.text, c.maxWidth)).toBe(c.expected);
36
- }
37
- });
38
-
39
- it("10k 混合长行在多个截断宽度下均一致(fixture 内 maxWidth 之外再加宽度扫描)", () => {
40
- const mixed = loadCases().find((c) => c.label === "sgr-mixed-10k")!;
41
- for (const w of [8, 15, 30, 61, 100]) {
42
- const expected = mixed.expected; // fixture 锁 maxWidth=60;其他宽度靠 oracle 断言
43
- void expected;
44
- // 非 fixture 宽度用行为断言:输出含省略号且无裸换行
45
- const out = truncLine(mixed.text, w);
46
- expect(out).toContain("…");
47
- expect(out).not.toContain("\n");
48
- }
49
- });
50
- });
51
-
52
- describe("truncLine — 非 SGR ESC 行为(等价定义点,显式锚定)", () => {
53
- it("OSC 序列(\\x1b]0;title\\x07)按文本计宽进 result(不作为样式捕获/段边界)", () => {
54
- // OSC 计入可见宽度(\x1b]0;title\x07 各字符占宽),随后文本继续同段累加
55
- const out = truncLine("AB\x1b]0;title\x07CDEFGHIJKLMNOP", 10);
56
- expect(out.endsWith("…")).toBe(true);
57
- expect(out).not.toContain("\x1b]0;title\x07"); // 截断点在 OSC 之内或之前——
58
- // 逐字节形态由 fixture(osc-title / osc-inside-truncation 用例)锚定
59
- });
60
-
61
- it("裸 \\x1b 后跟非 [ 字符:不匹配 SGR、并入文本段(不丢字符不死循环)", () => {
62
- const out = truncLine("before \x1bZ after bare esc tail", 12);
63
- expect(out.endsWith("…")).toBe(true);
64
- // 输出是输入前缀(前 11 可见列 + …),未吞字符
65
- expect(out.startsWith("before ")).toBe(true);
66
- });
67
-
68
- it("非 SGR CSI(\\x1b[K)与 SGR 混排且触发截断:\x1b[K 不进 activeStyles(无错位 reset)", () => {
69
- const out = truncLine("\x1b[31mred\x1b[K\x1b[32mgreen tail past width", 10);
70
- // SGR(31m/32m)被捕获;\x1b[K 按文本进 result——逐字节形态由 fixture
71
- //(csi-k-mixed-sgr 用例)锚定;此处断言 SGR 样式重应用语义未回归
72
- expect(out).toMatch(/\x1b\[31m/);
73
- expect(out.endsWith("…\x1b[0m")).toBe(true);
74
- });
75
-
76
- it("ESC 恰在串尾:无后续字符可判 SGR,按文本并入(end=flat.length)", () => {
77
- const out = truncLine("text almost at width limit\x1b", 22);
78
- expect(out.endsWith("…")).toBe(true);
79
- expect(out).not.toContain("\x1b["); // 末尾裸 ESC 未触发 SGR 捕获
80
- });
81
- });
@@ -1,194 +0,0 @@
1
- // src/execution/__tests__/turn-limiter-semantics.test.ts
2
- //
3
- // SP-9: turn-limiter chatMode 语义——chatMode 下 maxTurns 每轮 reset(不跨轮累计)。
4
- // 设计 D9 决策:续聊本质是无限轮,累计上限违背 G1。
5
-
6
- import { describe, expect, it, vi } from "vitest";
7
-
8
- import { createTurnLimiter } from "../turn-limiter.ts";
9
-
10
- describe("SP-9: turn-limiter chatMode semantics (maxTurns reset per round)", () => {
11
- describe("TC-1: chatMode 多轮后 maxTurns 每轮 reset(不累计)", () => {
12
- it("reset 后 maxTurns 不累计——第二轮从 0 开始,不触发 steer/abort", () => {
13
- const steer = vi.fn();
14
- const abort = vi.fn();
15
- const limiter = createTurnLimiter({ maxTurns: 3, graceTurns: 2, steer, abort });
16
-
17
- // 第一轮:3 turns → steer(达到 maxTurns=3)
18
- limiter.onTurnEnd(1);
19
- limiter.onTurnEnd(2);
20
- limiter.onTurnEnd(3);
21
- expect(steer).toHaveBeenCalledTimes(1);
22
- expect(abort).not.toHaveBeenCalled();
23
-
24
- // agent_settled → reset(模拟 chatMode 每轮 reset)
25
- limiter.reset();
26
-
27
- // 第二轮从 turnCount=0 开始(record.turnCount 由 session-runner 归零):
28
- // 2 turns 不触发 steer(距离 maxTurns=3 还差 1)
29
- limiter.onTurnEnd(1);
30
- limiter.onTurnEnd(2);
31
- // steer 仍然是 1 次(第一轮那次),第二轮未触发
32
- expect(steer).toHaveBeenCalledTimes(1);
33
- expect(abort).not.toHaveBeenCalled();
34
- });
35
-
36
- it("reset 后第二轮同样达到 maxTurns 时仍触发 steer", () => {
37
- const steer = vi.fn();
38
- const abort = vi.fn();
39
- const limiter = createTurnLimiter({ maxTurns: 2, graceTurns: 1, steer, abort });
40
-
41
- // 第一轮:steer + grace + abort
42
- limiter.onTurnEnd(1);
43
- limiter.onTurnEnd(2); // steer
44
- limiter.onTurnEnd(3); // abort
45
- expect(steer).toHaveBeenCalledTimes(1);
46
- expect(abort).toHaveBeenCalledTimes(1);
47
-
48
- // reset(chatMode 新轮)
49
- limiter.reset();
50
- expect(limiter.didSteer).toBe(false);
51
- expect(limiter.didAbort).toBe(false);
52
-
53
- // 第二轮:同样走完 steer + grace 路径
54
- limiter.onTurnEnd(1);
55
- limiter.onTurnEnd(2); // steer again
56
- expect(steer).toHaveBeenCalledTimes(2);
57
- expect(abort).toHaveBeenCalledTimes(1); // 还没到 abort
58
-
59
- limiter.onTurnEnd(3); // abort again
60
- expect(abort).toHaveBeenCalledTimes(2);
61
- });
62
-
63
- it("多轮连续 reset 后 limiter 始终可用", () => {
64
- const steer = vi.fn();
65
- const abort = vi.fn();
66
- const limiter = createTurnLimiter({ maxTurns: 2, graceTurns: 0, steer, abort });
67
-
68
- for (let round = 1; round <= 5; round++) {
69
- limiter.onTurnEnd(1);
70
- limiter.onTurnEnd(2); // steer + abort (graceTurns=0)
71
- expect(steer).toHaveBeenCalledTimes(round);
72
- expect(abort).toHaveBeenCalledTimes(round);
73
- limiter.reset();
74
- }
75
- });
76
- });
77
-
78
- describe("TC-2: graceTurns 每轮 reset", () => {
79
- it("reset 后 graceTurns 从 0 重新计数", () => {
80
- const steer = vi.fn();
81
- const abort = vi.fn();
82
- const limiter = createTurnLimiter({ maxTurns: 2, graceTurns: 3, steer, abort });
83
-
84
- // 第一轮:steer at turn 2,用掉 1 个 grace turn
85
- limiter.onTurnEnd(1);
86
- limiter.onTurnEnd(2); // steer
87
- limiter.onTurnEnd(3); // grace turn 1
88
- expect(abort).not.toHaveBeenCalled();
89
-
90
- // reset(模拟 chatMode agent_settled)
91
- limiter.reset();
92
-
93
- // 第二轮:从 turn 1 开始,graceTurns 重新从 0 计数
94
- limiter.onTurnEnd(1);
95
- limiter.onTurnEnd(2); // steer again
96
- limiter.onTurnEnd(3); // grace turn 1(重计)
97
- limiter.onTurnEnd(4); // grace turn 2
98
- limiter.onTurnEnd(5); // grace turn 3 → abort
99
- expect(abort).toHaveBeenCalledTimes(1);
100
- });
101
-
102
- it("reset 前未 steer 时 reset 后仍正常工作", () => {
103
- const steer = vi.fn();
104
- const abort = vi.fn();
105
- const limiter = createTurnLimiter({ maxTurns: 5, graceTurns: 2, steer, abort });
106
-
107
- // 第一轮:只走了 2 turns(未达 maxTurns)
108
- limiter.onTurnEnd(1);
109
- limiter.onTurnEnd(2);
110
- expect(steer).not.toHaveBeenCalled();
111
-
112
- limiter.reset();
113
-
114
- // 第二轮:从 turn 1 开始,到 maxTurns=5 触发 steer
115
- limiter.onTurnEnd(1);
116
- limiter.onTurnEnd(2);
117
- limiter.onTurnEnd(3);
118
- limiter.onTurnEnd(4);
119
- limiter.onTurnEnd(5); // steer
120
- expect(steer).toHaveBeenCalledTimes(1);
121
- limiter.onTurnEnd(6);
122
- limiter.onTurnEnd(7); // abort (5+2)
123
- expect(abort).toHaveBeenCalledTimes(1);
124
- });
125
- });
126
-
127
- describe("TC-3: 非 chatMode 行为不变(全程累计)", () => {
128
- it("不调用 reset 时 turn-limiter 行为与原来完全一致", () => {
129
- const steer = vi.fn();
130
- const abort = vi.fn();
131
- const limiter = createTurnLimiter({ maxTurns: 3, graceTurns: 2, steer, abort });
132
-
133
- // 全程累计(非 chatMode,不 reset):3→steer,5→abort
134
- limiter.onTurnEnd(1);
135
- limiter.onTurnEnd(2);
136
- limiter.onTurnEnd(3); // steer
137
- expect(steer).toHaveBeenCalledTimes(1);
138
- limiter.onTurnEnd(4); // grace 1
139
- limiter.onTurnEnd(5); // grace 2 → abort
140
- expect(abort).toHaveBeenCalledTimes(1);
141
- expect(limiter.didSteer).toBe(true);
142
- expect(limiter.didAbort).toBe(true);
143
- });
144
-
145
- it("maxTurns=0(禁用)不因 reset 而误启用", () => {
146
- const steer = vi.fn();
147
- const abort = vi.fn();
148
- const limiter = createTurnLimiter({ maxTurns: 0, graceTurns: 2, steer, abort });
149
-
150
- limiter.onTurnEnd(100);
151
- expect(steer).not.toHaveBeenCalled();
152
-
153
- limiter.reset();
154
- limiter.onTurnEnd(100);
155
- // maxTurns=0(禁用),即使 reset 也不触发
156
- expect(steer).not.toHaveBeenCalled();
157
- expect(abort).not.toHaveBeenCalled();
158
- });
159
-
160
- it("全程累计(不 reset)达到 maxTurns+graceTurns 后不再触发", () => {
161
- const steer = vi.fn();
162
- const abort = vi.fn();
163
- const limiter = createTurnLimiter({ maxTurns: 2, graceTurns: 1, steer, abort });
164
-
165
- limiter.onTurnEnd(1);
166
- limiter.onTurnEnd(2); // steer
167
- limiter.onTurnEnd(3); // abort
168
- expect(steer).toHaveBeenCalledTimes(1);
169
- expect(abort).toHaveBeenCalledTimes(1);
170
-
171
- // 已 abort 后继续调用 onTurnEnd(不应再次触发)
172
- limiter.onTurnEnd(4);
173
- limiter.onTurnEnd(5);
174
- expect(steer).toHaveBeenCalledTimes(1);
175
- expect(abort).toHaveBeenCalledTimes(1);
176
- });
177
- });
178
-
179
- describe("TC-4: reset 诊断属性", () => {
180
- it("reset 清除 didSteer 和 didAbort 诊断标志", () => {
181
- const steer = vi.fn();
182
- const abort = vi.fn();
183
- const limiter = createTurnLimiter({ maxTurns: 1, graceTurns: 0, steer, abort });
184
-
185
- limiter.onTurnEnd(1); // steer + abort (graceTurns=0)
186
- expect(limiter.didSteer).toBe(true);
187
- expect(limiter.didAbort).toBe(true);
188
-
189
- limiter.reset();
190
- expect(limiter.didSteer).toBe(false);
191
- expect(limiter.didAbort).toBe(false);
192
- });
193
- });
194
- });