@zhushanwen/subagent-core 0.5.1 → 0.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 (318) hide show
  1. package/README.md +1 -1
  2. package/dist/chunk-KXVBIVOU.js +16 -0
  3. package/dist/chunk-T2SYBW3I.js +24 -0
  4. package/dist/chunk-VURUAGMM.js +2305 -0
  5. package/dist/{types-Dv4QhSJ_.d.cts → engine-discovery-scan-ocpM8FmI.d.cts} +835 -668
  6. package/dist/{types-Dv4QhSJ_.d.ts → engine-discovery-scan-ocpM8FmI.d.ts} +835 -668
  7. package/dist/execution/engine/engine-discovery-scan.cjs +2210 -0
  8. package/dist/execution/engine/engine-discovery-scan.d.cts +4 -0
  9. package/dist/execution/engine/engine-discovery-scan.d.ts +4 -0
  10. package/dist/execution/engine/engine-discovery-scan.js +20 -0
  11. package/dist/execution/engine/paths.cjs +6 -23
  12. package/dist/execution/engine/paths.d.cts +1 -8
  13. package/dist/execution/engine/paths.d.ts +1 -8
  14. package/dist/execution/engine/paths.js +1 -1
  15. package/dist/execution/relay-env.cjs +12 -28
  16. package/dist/execution/relay-env.d.cts +1 -33
  17. package/dist/execution/relay-env.d.ts +1 -33
  18. package/dist/execution/relay-env.js +5 -3
  19. package/dist/index.cjs +14123 -15467
  20. package/dist/index.d.cts +1179 -1540
  21. package/dist/index.d.ts +1179 -1540
  22. package/dist/index.js +7694 -10986
  23. package/dist.bundle/index.cjs +33485 -0
  24. package/package.json +20 -26
  25. package/src/__tests__/record-store-last-line.test.ts +16 -7
  26. package/src/core/error-message.ts +7 -6
  27. package/src/core/host-services.ts +8 -2
  28. package/src/core/notify-ports.ts +17 -2
  29. package/src/execution/__tests__/agent-registry.test.ts +1 -1
  30. package/src/execution/__tests__/alive-store.test.ts +1 -1
  31. package/src/execution/__tests__/chat-engine-routing.test.ts +62 -28
  32. package/src/execution/__tests__/chat-round-first-round-watchdog.test.ts +255 -0
  33. package/src/execution/__tests__/collect-budget.test.ts +291 -0
  34. package/src/execution/__tests__/collect-coordinator-service.test.ts +316 -0
  35. package/src/execution/__tests__/collect-coordinator.test.ts +371 -0
  36. package/src/execution/__tests__/collect-mixed-dispatch.test.ts +229 -0
  37. package/src/execution/__tests__/config-collect-sync.test.ts +120 -0
  38. package/src/execution/__tests__/config.test.ts +1 -1
  39. package/src/execution/__tests__/delivery-methods.test.ts +179 -237
  40. package/src/execution/__tests__/execute-and-await-worktree.test.ts +0 -2
  41. package/src/execution/__tests__/execute-nesting.test.ts +20 -13
  42. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +21 -34
  43. package/src/execution/__tests__/finalized-marker.test.ts +1 -1
  44. package/src/execution/__tests__/gc-timer.test.ts +8 -23
  45. package/src/execution/__tests__/get-record-for-action-restart.test.ts +20 -10
  46. package/src/execution/__tests__/helpers/fake-engine-port.ts +187 -0
  47. package/src/execution/__tests__/helpers/spawn-mock.ts +36 -9
  48. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +1 -1
  49. package/src/execution/__tests__/idle-gc.test.ts +200 -0
  50. package/src/execution/__tests__/lifecycle-predicates.test.ts +26 -21
  51. package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +1 -1
  52. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +5 -2
  53. package/src/execution/__tests__/notify-batch.test.ts +500 -0
  54. package/src/execution/__tests__/notify-ledger.test.ts +27 -0
  55. package/src/execution/__tests__/reconcile-sweep.test.ts +200 -0
  56. package/src/execution/__tests__/record-entry-collect.test.ts +102 -0
  57. package/src/execution/__tests__/record-store.test.ts +60 -6
  58. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +20 -13
  59. package/src/execution/__tests__/relay-env.test.ts +12 -1
  60. package/src/execution/__tests__/round-supervisor.test.ts +293 -0
  61. package/src/execution/__tests__/session-file-gc.test.ts +1 -1
  62. package/src/execution/__tests__/session-pending.test.ts +119 -17
  63. package/src/execution/__tests__/start-collect-guard.test.ts +255 -0
  64. package/src/execution/__tests__/stream-sink-retirement.test.ts +10 -10
  65. package/src/execution/__tests__/subagent-actions-core.test.ts +6 -0
  66. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +14 -11
  67. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +33 -30
  68. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +2 -9
  69. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +59 -129
  70. package/src/execution/__tests__/subagent-service.test.ts +2 -2
  71. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +109 -7
  72. package/src/execution/__tests__/subprocess-agent-runner.test.ts +108 -36
  73. package/src/execution/__tests__/sync-collect-recovery.test.ts +1102 -0
  74. package/src/execution/__tests__/tombstone-store.test.ts +1 -1
  75. package/src/execution/__tests__/workflow-state-root.test.ts +232 -0
  76. package/src/execution/__tests__/worktree-git-ops.test.ts +1 -1
  77. package/src/execution/__tests__/worktree-reconcile-aging.test.ts +2 -2
  78. package/src/execution/__tests__/worktree-reconcile.integration.test.ts +1 -1
  79. package/src/execution/__tests__/worktree-registry.test.ts +1 -1
  80. package/src/execution/agent-registry.ts +6 -2
  81. package/src/execution/collect-coordinator.ts +200 -0
  82. package/src/execution/config.ts +77 -5
  83. package/src/execution/dialog-queue.ts +6 -73
  84. package/src/execution/engine/__tests__/common/capability-gate.test.ts +115 -15
  85. package/src/execution/engine/__tests__/common/errors.test.ts +11 -18
  86. package/src/execution/engine/__tests__/common/event-journal.test.ts +1 -1
  87. package/src/execution/engine/__tests__/common/kill-chain.test.ts +7 -0
  88. package/src/execution/engine/__tests__/common/nesting-guard.test.ts +9 -3
  89. package/src/execution/engine/__tests__/common/pool-manager.test.ts +2 -2
  90. package/src/execution/engine/__tests__/common/session-view-service.test.ts +90 -3
  91. package/src/execution/engine/__tests__/conformance/H9-test-disposition.md +116 -0
  92. package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/fake-engine-protocol.mjs +377 -0
  93. package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/recorded.fixture.json +331 -0
  94. package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/smoke-run.fixture.json +213 -0
  95. package/src/execution/engine/__tests__/conformance/chat-round-protocol.test.ts +230 -0
  96. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +69 -42
  97. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +4 -81
  98. package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +67 -44
  99. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +82 -55
  100. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +133 -70
  101. package/src/execution/engine/__tests__/conformance/engine-crash.test.ts +143 -0
  102. package/src/execution/engine/__tests__/conformance/fake-engine-capabilities.ts +19 -0
  103. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +8 -3
  104. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +6 -5
  105. package/src/execution/engine/__tests__/conformance/protocol-blackbox.test.ts +398 -0
  106. package/src/execution/engine/__tests__/conformance/registry-fork-filter.test.ts +221 -0
  107. package/src/execution/engine/__tests__/conformance/round-liveness-supervisor.test.ts +302 -0
  108. package/src/execution/engine/__tests__/conformance/static-spawn-guard.test.ts +58 -0
  109. package/src/execution/engine/__tests__/engine-discovery-scan.test.ts +558 -0
  110. package/src/execution/engine/__tests__/host-task-spec.test.ts +45 -0
  111. package/src/execution/engine/__tests__/model-ref-split.test.ts +74 -0
  112. package/src/execution/engine/__tests__/registry.test.ts +206 -1
  113. package/src/execution/engine/__tests__/routing.test.ts +97 -0
  114. package/src/execution/engine/__tests__/run-failure-worktree-cleanup.test.ts +97 -0
  115. package/src/execution/engine/client/__tests__/__fixtures__/fake-engine.mjs +294 -0
  116. package/src/execution/engine/client/__tests__/engine-client.test.ts +516 -0
  117. package/src/execution/engine/client/__tests__/mirror.test.ts +67 -0
  118. package/src/execution/engine/client/__tests__/pid-file.test.ts +257 -0
  119. package/src/execution/engine/client/__tests__/protocol-closure.test.ts +95 -0
  120. package/src/execution/engine/client/__tests__/remote-engine.test.ts +457 -0
  121. package/src/execution/engine/client/client-options.ts +103 -0
  122. package/src/execution/engine/client/engine-client.ts +687 -0
  123. package/src/execution/engine/client/mirror.ts +167 -0
  124. package/src/execution/engine/client/pid-file.ts +392 -0
  125. package/src/execution/engine/client/reaper.ts +95 -0
  126. package/src/execution/engine/client/remote-engine.ts +441 -0
  127. package/src/execution/engine/client/reverse-router.ts +255 -0
  128. package/src/execution/engine/common/capability-gate.ts +109 -22
  129. package/src/execution/engine/common/errors.ts +9 -25
  130. package/src/execution/engine/common/event-journal.ts +4 -50
  131. package/src/execution/engine/common/journal-replay.ts +3 -1
  132. package/src/execution/engine/common/kill-chain.ts +18 -251
  133. package/src/execution/engine/common/nesting-guard.ts +18 -114
  134. package/src/execution/engine/common/pool-manager.ts +4 -0
  135. package/src/execution/engine/common/session-view-service.ts +56 -84
  136. package/src/execution/engine/config.ts +151 -0
  137. package/src/execution/engine/d8-compat.ts +346 -0
  138. package/src/execution/engine/engine-discovery-roots.ts +107 -0
  139. package/src/execution/engine/engine-discovery-scan.ts +269 -0
  140. package/src/execution/engine/engine-discovery.ts +30 -7
  141. package/src/execution/engine/engine-inspect-package.ts +282 -0
  142. package/src/execution/engine/engine-manifest.ts +244 -0
  143. package/src/execution/engine/host/__tests__/host-askuser-endpoint.test.ts +123 -0
  144. package/src/execution/engine/host/__tests__/host-bridge.test.ts +135 -0
  145. package/src/execution/engine/host/__tests__/spawned-children.test.ts +73 -0
  146. package/src/execution/engine/host/host-bridge.ts +150 -0
  147. package/src/execution/engine/host/host-ui-endpoint.ts +47 -0
  148. package/src/execution/engine/host/pi-host-binding.ts +91 -0
  149. package/src/execution/engine/host/spawned-children.ts +139 -0
  150. package/src/execution/engine/host-task-spec.ts +29 -2
  151. package/src/execution/engine/model-validation.ts +55 -2
  152. package/src/execution/engine/paths.ts +16 -41
  153. package/src/execution/engine/port.ts +48 -2
  154. package/src/execution/engine/registry.ts +153 -34
  155. package/src/execution/engine/routing.ts +225 -52
  156. package/src/execution/engine/types.ts +25 -200
  157. package/src/execution/execution-record.ts +7 -0
  158. package/src/execution/finalize-record.ts +40 -11
  159. package/src/execution/idle-gc.ts +89 -14
  160. package/src/execution/lifecycle-manager.ts +10 -7
  161. package/src/execution/lifecycle-predicates.ts +10 -8
  162. package/src/execution/notifier.ts +234 -25
  163. package/src/execution/notify-host.ts +21 -1
  164. package/src/execution/record-entry.ts +16 -0
  165. package/src/execution/record-store.ts +189 -31
  166. package/src/execution/relay-env.ts +19 -36
  167. package/src/execution/round-supervisor/domain.ts +89 -0
  168. package/src/execution/round-supervisor/index.ts +33 -0
  169. package/src/execution/round-supervisor/notify-accounting.ts +87 -0
  170. package/src/execution/round-supervisor/reconcile-sweep.ts +235 -0
  171. package/src/execution/round-supervisor/service-binding.test.ts +661 -0
  172. package/src/execution/round-supervisor/service-binding.ts +273 -0
  173. package/src/execution/round-supervisor/supervisor.ts +426 -0
  174. package/src/execution/session-pending.ts +68 -74
  175. package/src/execution/settled-watchdog.ts +39 -0
  176. package/src/execution/subagent-actions-core.ts +62 -6
  177. package/src/execution/subagent-service.ts +1161 -406
  178. package/src/execution/subprocess-agent-runner.ts +16 -8
  179. package/src/execution/sync-rebuild.ts +87 -0
  180. package/src/execution/types.ts +104 -117
  181. package/src/execution/ui-channels.ts +13 -214
  182. package/src/execution/ui-request-handler-factory.ts +7 -4
  183. package/src/execution/ui-request-observability.ts +2 -2
  184. package/src/execution/workflow-state-root.ts +67 -0
  185. package/src/execution/worktree-git-ops.ts +5 -0
  186. package/src/execution/worktree-manager.ts +11 -1
  187. package/src/index.ts +55 -26
  188. package/src/orchestration/__tests__/config-loader.test.ts +1 -1
  189. package/src/orchestration/__tests__/execute-agent-call.test.ts +1 -1
  190. package/src/orchestration/__tests__/file-run-store-prune.test.ts +1 -1
  191. package/src/orchestration/__tests__/file-run-store-throttle.test.ts +1 -1
  192. package/src/orchestration/__tests__/review-fix-loop-scriptpath-failfast.test.ts +1 -1
  193. package/src/orchestration/__tests__/script-generate.test.ts +5 -5
  194. package/src/orchestration/__tests__/skill-discovery.test.ts +2 -2
  195. package/src/orchestration/__tests__/workflow-files.test.ts +10 -10
  196. package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +1 -1
  197. package/src/orchestration/execute-agent-call.ts +1 -1
  198. package/src/orchestration/file-run-store.ts +144 -59
  199. package/src/orchestration/models/types.ts +43 -42
  200. package/src/shared/__tests__/atomic-write.test.ts +1 -1
  201. package/src/shared/__tests__/resource-discovery-host-roots.test.ts +5 -5
  202. package/src/shared/zcode-model-ref.ts +31 -0
  203. package/dist/chunk-43ONBFZX.js +0 -240
  204. package/dist/chunk-A4IVZWVX.js +0 -31
  205. package/dist/chunk-APZY4IME.js +0 -27
  206. package/dist/chunk-V3VHZ2VX.js +0 -59
  207. package/dist/execution/engine/engines/zcode/constants.cjs +0 -107
  208. package/dist/execution/engine/engines/zcode/constants.d.cts +0 -135
  209. package/dist/execution/engine/engines/zcode/constants.d.ts +0 -135
  210. package/dist/execution/engine/engines/zcode/constants.js +0 -54
  211. package/dist/execution/engine/engines/zcode/reader.cjs +0 -276
  212. package/dist/execution/engine/engines/zcode/reader.d.cts +0 -21
  213. package/dist/execution/engine/engines/zcode/reader.d.ts +0 -21
  214. package/dist/execution/engine/engines/zcode/reader.js +0 -9
  215. package/src/__tests__/append-system-prompt-assembly.test.ts +0 -243
  216. package/src/__tests__/fr4-get-state-handshake.test.ts +0 -125
  217. package/src/__tests__/session-runner.test.ts +0 -69
  218. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +0 -490
  219. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +0 -191
  220. package/src/execution/__tests__/descendant-sweep-guards.test.ts +0 -220
  221. package/src/execution/__tests__/descendant-sweep.test.ts +0 -269
  222. package/src/execution/__tests__/engine-model-validation.test.ts +0 -741
  223. package/src/execution/__tests__/epipe-fallback.test.ts +0 -248
  224. package/src/execution/__tests__/execution-runtime-face.test.ts +0 -272
  225. package/src/execution/__tests__/get-state-handshake.test.ts +0 -127
  226. package/src/execution/__tests__/helpers/session-runner-mocks.ts +0 -113
  227. package/src/execution/__tests__/keep-alive-no-progress.test.ts +0 -280
  228. package/src/execution/__tests__/kill-all-escalation.test.ts +0 -196
  229. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +0 -113
  230. package/src/execution/__tests__/output-collector.test.ts +0 -469
  231. package/src/execution/__tests__/pi-invocation.test.ts +0 -134
  232. package/src/execution/__tests__/recursive-visibility-env.test.ts +0 -149
  233. package/src/execution/__tests__/rpc-mode.test.ts +0 -89
  234. package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +0 -387
  235. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +0 -277
  236. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +0 -254
  237. package/src/execution/__tests__/run-spawn-edges.test.ts +0 -1079
  238. package/src/execution/__tests__/run-spawn-integration.test.ts +0 -933
  239. package/src/execution/__tests__/run-spawn-resume.test.ts +0 -322
  240. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +0 -196
  241. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +0 -207
  242. package/src/execution/__tests__/service-kill-escalation.test.ts +0 -91
  243. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +0 -110
  244. package/src/execution/__tests__/session-runner-close-prune.test.ts +0 -181
  245. package/src/execution/__tests__/session-runner-dispatch.test.ts +0 -489
  246. package/src/execution/__tests__/session-runner-epipe.test.ts +0 -179
  247. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +0 -227
  248. package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +0 -290
  249. package/src/execution/__tests__/session-runner-schema-env.test.ts +0 -352
  250. package/src/execution/__tests__/settled-watchdog.test.ts +0 -524
  251. package/src/execution/__tests__/spawn-args.test.ts +0 -446
  252. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +0 -192
  253. package/src/execution/__tests__/spawn-event-adapter.test.ts +0 -167
  254. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +0 -208
  255. package/src/execution/__tests__/spawned-children.test.ts +0 -236
  256. package/src/execution/__tests__/start-sync-model-guard.test.ts +0 -150
  257. package/src/execution/__tests__/stdin-writer.test.ts +0 -464
  258. package/src/execution/__tests__/subagent-service-message-close.test.ts +0 -693
  259. package/src/execution/__tests__/temp-prompt.test.ts +0 -53
  260. package/src/execution/__tests__/timeout-integration.test.ts +0 -616
  261. package/src/execution/__tests__/turn-limiter-semantics.test.ts +0 -194
  262. package/src/execution/__tests__/turn-limiter.test.ts +0 -65
  263. package/src/execution/__tests__/ui-request-handler.test.ts +0 -205
  264. package/src/execution/__tests__/ui-request-queue.test.ts +0 -298
  265. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +0 -230
  266. package/src/execution/engine/__tests__/common/schema-emulation.test.ts +0 -128
  267. package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +0 -149
  268. package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +0 -28
  269. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +0 -128
  270. package/src/execution/engine/__tests__/paths.test.ts +0 -39
  271. package/src/execution/engine/common/schema-emulation.ts +0 -189
  272. package/src/execution/engine/common/session-view-projection.ts +0 -51
  273. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +0 -615
  274. package/src/execution/engine/engines/pi/__tests__/reader.test.ts +0 -155
  275. package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +0 -257
  276. package/src/execution/engine/engines/pi/argv-mirror.ts +0 -135
  277. package/src/execution/engine/engines/pi/get-state-handshake.ts +0 -179
  278. package/src/execution/engine/engines/pi/output-collector.ts +0 -317
  279. package/src/execution/engine/engines/pi/pi-engine.ts +0 -811
  280. package/src/execution/engine/engines/pi/pi-invocation.ts +0 -120
  281. package/src/execution/engine/engines/pi/reader.ts +0 -59
  282. package/src/execution/engine/engines/pi/registration.ts +0 -37
  283. package/src/execution/engine/engines/pi/session-runner.ts +0 -2840
  284. package/src/execution/engine/engines/pi/spawn-event-adapter.ts +0 -404
  285. package/src/execution/engine/engines/pi/stdin-writer.ts +0 -199
  286. package/src/execution/engine/engines/pi/temp-prompt.ts +0 -62
  287. package/src/execution/engine/engines/pi/turn-limiter.ts +0 -102
  288. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +0 -301
  289. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +0 -36
  290. package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +0 -321
  291. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +0 -473
  292. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +0 -43
  293. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +0 -124
  294. package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +0 -210
  295. package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +0 -71
  296. package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +0 -247
  297. package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +0 -451
  298. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +0 -792
  299. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +0 -782
  300. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +0 -168
  301. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +0 -210
  302. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +0 -353
  303. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +0 -282
  304. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +0 -372
  305. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +0 -145
  306. package/src/execution/engine/engines/zcode/appserver-launcher.ts +0 -167
  307. package/src/execution/engine/engines/zcode/connection.ts +0 -598
  308. package/src/execution/engine/engines/zcode/constants.ts +0 -176
  309. package/src/execution/engine/engines/zcode/golden-sample.ts +0 -47
  310. package/src/execution/engine/engines/zcode/parser.ts +0 -102
  311. package/src/execution/engine/engines/zcode/preparer.ts +0 -235
  312. package/src/execution/engine/engines/zcode/reader.ts +0 -381
  313. package/src/execution/engine/engines/zcode/registration.ts +0 -37
  314. package/src/execution/engine/engines/zcode/session-channel.ts +0 -968
  315. package/src/execution/engine/engines/zcode/zcode-engine.ts +0 -1431
  316. package/src/execution/round-settlement.ts +0 -93
  317. package/src/execution/ui-request-queue.ts +0 -215
  318. package/src/shared/schema-env.ts +0 -44
@@ -0,0 +1,293 @@
1
+ // src/execution/__tests__/round-supervisor.test.ts
2
+ //
3
+ // [W4] 轮次活性监督器单测:三态判定(该等/该唤醒/该放弃)、纳管模型(死亡事件
4
+ // 纳管、重建不解管——镜像置死只作触发信号不作持续判据)、boot 分区(重认领 vs
5
+ // conversation 豁免 vs in-flight 跳过)、通知对账两级启发式(高置信撤销改送终止
6
+ // 通知 / 低置信豁免声明)。timer 用 fake timers;deps 全部替身注入(不触真实
7
+ // 数据目录——监督器对 fs 零依赖)。
8
+
9
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
10
+
11
+ import type { ExecutionRecord } from "../types.ts";
12
+ import { RoundSupervisor, ROUND_SUPERVISOR_WATCHDOG_DEFAULT_MS, type RoundSupervisorDeps, type SupervisorCandidateRecord, type SupervisorRecordView } from "../round-supervisor/index.ts";
13
+
14
+ function makeDeps(overrides: Partial<RoundSupervisorDeps> = {}): RoundSupervisorDeps & {
15
+ notices: string[];
16
+ guidances: Array<{ record: SupervisorRecordView; exemptionDisclaimer: boolean }>;
17
+ replaced: Array<{ record: SupervisorRecordView; replacementId: string }>;
18
+ givenUp: Array<{ recordId: string; kind: string; detail: { replacementId?: string } }>;
19
+ views: Map<string, SupervisorRecordView>;
20
+ candidates: SupervisorCandidateRecord[];
21
+ live: Set<string>;
22
+ } {
23
+ const views = new Map<string, SupervisorRecordView>();
24
+ const candidates: SupervisorCandidateRecord[] = [];
25
+ const live = new Set<string>();
26
+ const notices: string[] = [];
27
+ const guidances: Array<{ record: SupervisorRecordView; exemptionDisclaimer: boolean }> = [];
28
+ const replaced: Array<{ record: SupervisorRecordView; replacementId: string }> = [];
29
+ const givenUp: Array<{ recordId: string; kind: string; detail: { replacementId?: string } }> = [];
30
+ const deps: RoundSupervisorDeps = {
31
+ now: () => Date.now(),
32
+ getRecordView: (id) => views.get(id),
33
+ listCandidateRecords: () => candidates,
34
+ hasLiveProcess: (recordId) => live.has(recordId),
35
+ sendMergedFailureNotice: (record) => notices.push(`merged:${record.id}`),
36
+ sendDecisionGuidance: (record, opts) => guidances.push({ record, exemptionDisclaimer: opts.exemptionDisclaimer }),
37
+ sendReplacedNotice: (record, replacementId) => replaced.push({ record, replacementId }),
38
+ giveUp: (recordId, kind, detail) => givenUp.push({ recordId, kind, detail }),
39
+ };
40
+ return Object.assign(deps, { notices, guidances, replaced, givenUp, views, candidates, live });
41
+ }
42
+
43
+ /** 内存 record 替身(adoptOnProcessDeath 入参形态——字段访问面最小化)。 */
44
+ function makeRecord(overrides: Partial<ExecutionRecord> = {}): ExecutionRecord {
45
+ return {
46
+ id: "bg-1",
47
+ agent: "worker",
48
+ model: "m",
49
+ mode: "background",
50
+ task: "t",
51
+ slug: "fix-bug",
52
+ startedAt: Date.now() - 1000,
53
+ status: "running",
54
+ resumable: true,
55
+ rootSessionId: "sess-root",
56
+ turnCount: 0,
57
+ ...overrides,
58
+ } as ExecutionRecord;
59
+ }
60
+
61
+ describe("RoundSupervisor 三态判定", () => {
62
+ beforeEach(() => {
63
+ vi.useFakeTimers();
64
+ });
65
+ afterEach(() => {
66
+ vi.useRealTimers();
67
+ });
68
+
69
+ it("该等:有活进程驱动 → 不干预(无指引、无放弃)", () => {
70
+ const deps = makeDeps();
71
+ const supervisor = new RoundSupervisor(deps);
72
+ deps.views.set("bg-1", { id: "bg-1", status: "running", resumable: true, hasResult: false, chatMode: false, rootSessionId: "r", agent: "worker", slug: "s", startedAt: 1, closedReason: undefined });
73
+ deps.live.add("bg-1");
74
+ supervisor.adoptOnProcessDeath(makeRecord(), "boom");
75
+ expect(deps.guidances).toHaveLength(0);
76
+ expect(deps.givenUp).toHaveLength(0);
77
+ });
78
+
79
+ it("该等:在途 run 记账(noteRunStarted)→ 不干预;noteRunEnded 后重评估", () => {
80
+ const deps = makeDeps();
81
+ const supervisor = new RoundSupervisor(deps);
82
+ deps.views.set("bg-1", { id: "bg-1", status: "running", resumable: true, hasResult: false, chatMode: false, rootSessionId: "r", agent: "worker", slug: "s", startedAt: 1, closedReason: undefined });
83
+ supervisor.noteRunStarted("bg-1");
84
+ supervisor.adoptOnProcessDeath(makeRecord(), "boom");
85
+ expect(deps.guidances).toHaveLength(0);
86
+ supervisor.noteRunEnded("bg-1");
87
+ expect(deps.guidances).toHaveLength(1);
88
+ });
89
+
90
+ it("该唤醒:resumable 未终态 且 无在途 run/无进程驱动 → 决策指引(一窗一次)+ 看门狗 armed", () => {
91
+ const deps = makeDeps();
92
+ const supervisor = new RoundSupervisor(deps);
93
+ deps.views.set("bg-1", { id: "bg-1", status: "running", resumable: true, hasResult: false, chatMode: false, rootSessionId: "r", agent: "worker", slug: "s", startedAt: 1, closedReason: undefined });
94
+ supervisor.adoptOnProcessDeath(makeRecord(), "engine crashed");
95
+ expect(deps.notices).toEqual(["merged:bg-1"]); // 合并单条通知(failed 如实 + 接管契约)
96
+ expect(deps.guidances).toHaveLength(1);
97
+ expect(deps.guidances[0].exemptionDisclaimer).toBe(false);
98
+ // 重复死亡事件不重复通知、不重复指引(幂等)
99
+ supervisor.adoptOnProcessDeath(makeRecord(), "again");
100
+ expect(deps.notices).toHaveLength(1);
101
+ expect(deps.guidances).toHaveLength(1);
102
+ expect(supervisor.supervisedIds()).toEqual(["bg-1"]);
103
+ });
104
+
105
+ it("[重建不解管] 镜像重填再置死(引擎被动重建)不翻转判定:指引仍只送一次、纳管持续", () => {
106
+ const deps = makeDeps();
107
+ const supervisor = new RoundSupervisor(deps);
108
+ deps.views.set("bg-1", { id: "bg-1", status: "running", resumable: true, hasResult: false, chatMode: false, rootSessionId: "r", agent: "worker", slug: "s", startedAt: 1, closedReason: undefined });
109
+ // 死亡事件纳管时镜像已置死(hasLiveProcess=false)→ 该唤醒。
110
+ supervisor.adoptOnProcessDeath(makeRecord(), "engine crashed");
111
+ expect(deps.guidances).toHaveLength(1);
112
+ // 模拟引擎被动重建:任一会话的下个请求触发 ensureConnected → 镜像重填(live=true)
113
+ deps.live.add("bg-1");
114
+ supervisor.noteRunEnded("bg-1"); // 重建后的重评估——镜像判「该等」仅解除看门狗
115
+ expect(deps.guidances).toHaveLength(1); // 指引不重发(已在窗内)
116
+ expect(supervisor.supervisedIds()).toEqual(["bg-1"]); // 纳管未解除(重建不解管)
117
+ // 镜像再置死(重建的进程又死):record 级状态未变 → 监督器继续管辖(不重复指引)
118
+ deps.live.delete("bg-1");
119
+ supervisor.noteRunEnded("bg-1");
120
+ expect(deps.guidances).toHaveLength(1);
121
+ expect(supervisor.supervisedIds()).toEqual(["bg-1"]);
122
+ });
123
+
124
+ it("已有完成产出(SP-5 upgrade 等待态)→ 不唤醒", () => {
125
+ const deps = makeDeps();
126
+ const supervisor = new RoundSupervisor(deps);
127
+ deps.views.set("bg-1", { id: "bg-1", status: "running", resumable: true, hasResult: true, chatMode: false, rootSessionId: "r", agent: "worker", slug: "s", startedAt: 1, closedReason: undefined });
128
+ supervisor.adoptOnProcessDeath(makeRecord({ result: "done text" }), "x");
129
+ expect(deps.guidances).toHaveLength(0);
130
+ });
131
+
132
+ it("conversation 形态(chatMode)豁免:不纳管不通知", () => {
133
+ const deps = makeDeps();
134
+ const supervisor = new RoundSupervisor(deps);
135
+ supervisor.adoptOnProcessDeath(makeRecord({ chatMode: true, resumable: true }), "x");
136
+ expect(deps.notices).toHaveLength(0);
137
+ expect(supervisor.supervisedIds()).toEqual([]);
138
+ });
139
+
140
+ it("该放弃:决策看门狗到期 → giveUp(watchdog-expired) 并解除纳管;record 已终态则跳过", async () => {
141
+ const deps = makeDeps();
142
+ const supervisor = new RoundSupervisor(deps);
143
+ deps.views.set("bg-1", { id: "bg-1", status: "running", resumable: true, hasResult: false, chatMode: false, rootSessionId: "r", agent: "worker", slug: "s", startedAt: 1, closedReason: undefined });
144
+ supervisor.adoptOnProcessDeath(makeRecord(), "x");
145
+ expect(deps.givenUp).toHaveLength(0);
146
+ await vi.advanceTimersByTimeAsync(ROUND_SUPERVISOR_WATCHDOG_DEFAULT_MS + 1);
147
+ expect(deps.givenUp).toEqual([{ recordId: "bg-1", kind: "watchdog-expired", detail: {} }]);
148
+ expect(supervisor.supervisedIds()).toEqual([]);
149
+ });
150
+
151
+ it("看门狗等待期内 run 恢复(决策收敛)→ 解除看门狗,不放弃", async () => {
152
+ const deps = makeDeps();
153
+ const supervisor = new RoundSupervisor(deps);
154
+ deps.views.set("bg-1", { id: "bg-1", status: "running", resumable: true, hasResult: false, chatMode: false, rootSessionId: "r", agent: "worker", slug: "s", startedAt: 1, closedReason: undefined });
155
+ supervisor.adoptOnProcessDeath(makeRecord(), "x");
156
+ supervisor.noteRunStarted("bg-1"); // 主 agent resume = 决策收敛
157
+ await vi.advanceTimersByTimeAsync(ROUND_SUPERVISOR_WATCHDOG_DEFAULT_MS + 1);
158
+ expect(deps.givenUp).toHaveLength(0);
159
+ });
160
+
161
+ it("[F5] 挂账转完成(SP-5 result 回填)→ 解除看门狗:到期不误 giveUp 已完成挂账 record", async () => {
162
+ const deps = makeDeps();
163
+ const supervisor = new RoundSupervisor(deps);
164
+ deps.views.set("bg-1", { id: "bg-1", status: "running", resumable: true, hasResult: false, chatMode: false, rootSessionId: "r", agent: "worker", slug: "s", startedAt: 1, closedReason: undefined });
165
+ supervisor.adoptOnProcessDeath(makeRecord(), "x"); // 该唤醒 → 指引 + 看门狗 armed
166
+ expect(deps.guidances).toHaveLength(1);
167
+ // upgrade 完成 → result 回填(挂账态),重评估触发(noteRunEnded 携带视图更新)
168
+ deps.views.set("bg-1", { id: "bg-1", status: "running", resumable: true, hasResult: true, chatMode: false, rootSessionId: "r", agent: "worker", slug: "s", startedAt: 1, closedReason: undefined });
169
+ supervisor.noteRunEnded("bg-1");
170
+ // armed 的看门狗必须已解除——2h 到期不得把已完成挂账 record 判死
171
+ await vi.advanceTimersByTimeAsync(ROUND_SUPERVISOR_WATCHDOG_DEFAULT_MS + 1);
172
+ expect(deps.givenUp).toHaveLength(0);
173
+ expect(supervisor.supervisedIds()).toEqual(["bg-1"]); // 挂账态保持纳管(归 idle-gc 收口)
174
+ });
175
+
176
+ it("env ≤0 关闭该放弃路径:只指引永不放弃(回收层 opt-out)", async () => {
177
+ vi.stubEnv("XYZ_SUBAGENT_ROUND_SUPERVISOR_WATCHDOG_MS", "0");
178
+ RoundSupervisor._resetEnvCacheForTest();
179
+ try {
180
+ const deps = makeDeps();
181
+ const supervisor = new RoundSupervisor(deps);
182
+ deps.views.set("bg-1", { id: "bg-1", status: "running", resumable: true, hasResult: false, chatMode: false, rootSessionId: "r", agent: "worker", slug: "s", startedAt: 1, closedReason: undefined });
183
+ supervisor.adoptOnProcessDeath(makeRecord(), "x");
184
+ expect(deps.guidances).toHaveLength(1);
185
+ await vi.advanceTimersByTimeAsync(ROUND_SUPERVISOR_WATCHDOG_DEFAULT_MS * 3);
186
+ expect(deps.givenUp).toHaveLength(0);
187
+ } finally {
188
+ vi.unstubAllEnvs();
189
+ RoundSupervisor._resetEnvCacheForTest();
190
+ }
191
+ });
192
+ });
193
+
194
+ describe("RoundSupervisor boot 分区", () => {
195
+ beforeEach(() => {
196
+ vi.useFakeTimers();
197
+ });
198
+ afterEach(() => {
199
+ vi.useRealTimers();
200
+ });
201
+
202
+ it("already-resumable-idle(非 conversation)→ 重认领接管 + 送达指引", () => {
203
+ const deps = makeDeps();
204
+ const supervisor = new RoundSupervisor(deps);
205
+ deps.candidates.push({ id: "bg-1", rootSessionId: "r", agent: "worker", slug: "s", startedAt: 1 });
206
+ deps.views.set("bg-1", { id: "bg-1", status: "running", resumable: true, hasResult: false, chatMode: false, rootSessionId: "r", agent: "worker", slug: "s", startedAt: 1, closedReason: undefined });
207
+ const { readopted } = supervisor.bootPartition();
208
+ expect(readopted).toEqual(["bg-1"]);
209
+ expect(deps.guidances).toHaveLength(1);
210
+ expect(supervisor.supervisedIds()).toEqual(["bg-1"]);
211
+ });
212
+
213
+ it("in-flight 形态(无 resumable 信号)→ 跳过(直断归孤儿恢复,防双收尾)", () => {
214
+ const deps = makeDeps();
215
+ const supervisor = new RoundSupervisor(deps);
216
+ deps.candidates.push({ id: "bg-2", rootSessionId: "r", agent: "worker", slug: "s", startedAt: 1 });
217
+ deps.views.set("bg-2", { id: "bg-2", status: "running", resumable: false, hasResult: false, chatMode: false, rootSessionId: "r", agent: "worker", slug: "s", startedAt: 1, closedReason: undefined });
218
+ const { readopted } = supervisor.bootPartition();
219
+ expect(readopted).toEqual([]);
220
+ expect(deps.guidances).toHaveLength(0);
221
+ expect(deps.givenUp).toHaveLength(0);
222
+ });
223
+
224
+ it("conversation 形态 → 豁免(现状机制管辖)", () => {
225
+ const deps = makeDeps();
226
+ const supervisor = new RoundSupervisor(deps);
227
+ deps.candidates.push({ id: "bg-3", rootSessionId: "r", agent: "chat", slug: "s", startedAt: 1 });
228
+ deps.views.set("bg-3", { id: "bg-3", status: "running", resumable: true, hasResult: false, chatMode: true, rootSessionId: "r", agent: "chat", slug: "s", startedAt: 1, closedReason: undefined });
229
+ const { readopted } = supervisor.bootPartition();
230
+ expect(readopted).toEqual([]);
231
+ expect(supervisor.supervisedIds()).toEqual([]);
232
+ });
233
+ });
234
+
235
+ describe("RoundSupervisor 通知对账(送指引前查替代)", () => {
236
+ beforeEach(() => {
237
+ vi.useFakeTimers();
238
+ });
239
+ afterEach(() => {
240
+ vi.useRealTimers();
241
+ });
242
+
243
+ function setupDeadRecord(): { deps: ReturnType<typeof makeDeps>; supervisor: RoundSupervisor } {
244
+ const deps = makeDeps();
245
+ const supervisor = new RoundSupervisor(deps);
246
+ deps.views.set("bg-1", { id: "bg-1", status: "running", resumable: true, hasResult: false, chatMode: false, rootSessionId: "r", agent: "worker", slug: "fix-bug", startedAt: 1, closedReason: undefined });
247
+ return { deps, supervisor };
248
+ }
249
+
250
+ it("高置信(同 root + 同 agent + 同 slug + 看门狗窗内新建)→ 撤销指引改送替代终止通知 + 该放弃", () => {
251
+ const { deps, supervisor } = setupDeadRecord();
252
+ deps.candidates.push({ id: "bg-2", rootSessionId: "r", agent: "worker", slug: "fix-bug", startedAt: Date.now() - 1000 });
253
+ supervisor.adoptOnProcessDeath(makeRecord(), "x");
254
+ expect(deps.guidances).toHaveLength(0); // 指引撤销
255
+ expect(deps.replaced).toHaveLength(1);
256
+ expect(deps.replaced[0].replacementId).toBe("bg-2");
257
+ expect(deps.givenUp).toEqual([{ recordId: "bg-1", kind: "superseded", detail: { replacementId: "bg-2" } }]);
258
+ expect(supervisor.supervisedIds()).toEqual([]);
259
+ });
260
+
261
+ it("低置信(仅 agent 同名)→ 不撤销指引,指引自带豁免声明", () => {
262
+ const { deps, supervisor } = setupDeadRecord();
263
+ deps.candidates.push({ id: "bg-2", rootSessionId: "r", agent: "worker", slug: "other-task", startedAt: Date.now() - 1000 });
264
+ supervisor.adoptOnProcessDeath(makeRecord(), "x");
265
+ expect(deps.guidances).toHaveLength(1);
266
+ expect(deps.guidances[0].exemptionDisclaimer).toBe(true);
267
+ expect(deps.givenUp).toHaveLength(0);
268
+ });
269
+
270
+ it("同名但超出看门狗窗(无时序关联)→ 无替代命中,指引不带豁免声明", () => {
271
+ const { deps, supervisor } = setupDeadRecord();
272
+ deps.candidates.push({ id: "bg-2", rootSessionId: "r", agent: "worker", slug: "fix-bug", startedAt: Date.now() - ROUND_SUPERVISOR_WATCHDOG_DEFAULT_MS - 10_000 });
273
+ supervisor.adoptOnProcessDeath(makeRecord(), "x");
274
+ expect(deps.guidances).toHaveLength(1);
275
+ expect(deps.guidances[0].exemptionDisclaimer).toBe(false);
276
+ expect(deps.givenUp).toHaveLength(0);
277
+ });
278
+
279
+ it("无候选 → 指引不带豁免声明", () => {
280
+ const { deps, supervisor } = setupDeadRecord();
281
+ supervisor.adoptOnProcessDeath(makeRecord(), "x");
282
+ expect(deps.guidances).toHaveLength(1);
283
+ expect(deps.guidances[0].exemptionDisclaimer).toBe(false);
284
+ });
285
+
286
+ it("高置信对账按 slug 改写漏判 → 落入低置信(残余双执行风险面,豁免声明兜住)", () => {
287
+ const { deps, supervisor } = setupDeadRecord();
288
+ deps.candidates.push({ id: "bg-2", rootSessionId: "r", agent: "worker", slug: "rewritten-slug", startedAt: Date.now() - 1000 });
289
+ supervisor.adoptOnProcessDeath(makeRecord(), "x");
290
+ expect(deps.guidances).toHaveLength(1);
291
+ expect(deps.guidances[0].exemptionDisclaimer).toBe(true);
292
+ });
293
+ });
@@ -34,7 +34,7 @@ beforeEach(() => {
34
34
  });
35
35
 
36
36
  afterEach(() => {
37
- fs.rmSync(tmpAgentDir, { recursive: true, force: true });
37
+ fs.rmSync(tmpAgentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
38
38
  vi.restoreAllMocks();
39
39
  });
40
40
 
@@ -27,9 +27,11 @@ import * as path from "node:path";
27
27
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
28
28
  import { configureNotifyDomain, resetNotifyDomainForTests } from "../../core/notify-ports.ts";
29
29
 
30
+ // [D5 消亡] listActivePendingFromSessionFile(后代补杀清单口径)随 inproc pi 引擎目录
31
+ // 删除消亡(消费方全在待删 session-runner;止损语义由 W4 监督器该放弃承接)——
32
+ // 本文件覆盖收敛为 count 口径 + 游标剪枝原语。
30
33
  import {
31
34
  clearPendingCursors,
32
- listActivePendingFromSessionFile,
33
35
  prunePendingCursor,
34
36
  readActivePendingFromSessionFile,
35
37
  } from "../session-pending.ts";
@@ -175,7 +177,10 @@ describe("readActivePendingFromSessionFile", () => {
175
177
  });
176
178
 
177
179
  it("fork 继承的主 session register 已被 expired unregister 抵消 → 不干扰", () => {
178
- // 模拟 P fork 主 session:继承 register(sessionId 不匹配),session_start 重建补 unregister(expired)
180
+ // 模拟 P fork 主 session:继承 register 且文件里已有对应 unregister 抵消行
181
+ // (历史 session 档 U4 产物)。翻 process 档后 U4 不再补写抵消行,fork 残留的
182
+ // 现行收口 = 读侧过滤②(见文末 [F1] describe)——本用例锁「文件内抵消行仍生效」
183
+ // 的差集语义本身,不锁清理机制。
179
184
  const file = makeTmpSessionFile([
180
185
  mkRegister("parent-bg"),
181
186
  mkUnregister("parent-bg", "expired"),
@@ -319,9 +324,6 @@ describe("[LC-6] 差集计数语义等值", () => {
319
324
  expect(r.count).toBe(1); // 2 register − 1 unregister
320
325
  expect(counter.mock.calls[0]?.[0]).toHaveLength(1); // 端口收到差集(非全量 3 行)
321
326
  expect((counter.mock.calls[0]?.[0] as unknown[])[0]).toMatchObject({ customType: "pending:register" });
322
-
323
- const list = listActivePendingFromSessionFile(f);
324
- expect(list.items.map((i) => i.id)).toEqual(["bg-2"]);
325
327
  });
326
328
 
327
329
  it("同 id 重 register(未 unregister)覆盖 → 仍 1 个活跃", () => {
@@ -330,9 +332,7 @@ describe("[LC-6] 差集计数语义等值", () => {
330
332
  fs.writeFileSync(f, entryLine("pending:register", registerData("bg-1", "sess-1")));
331
333
  fs.appendFileSync(f, entryLine("pending:register", registerData("bg-1", "sess-1-moved")));
332
334
 
333
- const list = listActivePendingFromSessionFile(f);
334
- expect(list.items).toHaveLength(1);
335
- expect(list.items[0]?.sessionId).toBe("sess-1-moved");
335
+ expect(readActivePendingFromSessionFile(f).count).toBe(1);
336
336
  });
337
337
 
338
338
  it("unregister 后同 id 再 register → 恢复活跃", () => {
@@ -345,9 +345,6 @@ describe("[LC-6] 差集计数语义等值", () => {
345
345
  entryLine("pending:register", registerData("bg-1", "sess-2")),
346
346
  );
347
347
 
348
- const list = listActivePendingFromSessionFile(f);
349
- expect(list.items.map((i) => i.id)).toEqual(["bg-1"]);
350
- expect(list.items[0]?.sessionId).toBe("sess-2");
351
348
  expect(readActivePendingFromSessionFile(f).count).toBe(1);
352
349
  });
353
350
 
@@ -382,8 +379,7 @@ describe("[LC-6] 差集计数语义等值", () => {
382
379
  entryLine("pending:register", registerData("bg-2", "sess-2")),
383
380
  );
384
381
 
385
- const list = listActivePendingFromSessionFile(f);
386
- expect(list.items.map((i) => i.id)).toEqual(["bg-2"]);
382
+ expect(readActivePendingFromSessionFile(f).count).toBe(1); // 畸形行丢弃,好行入差集
387
383
  });
388
384
  });
389
385
 
@@ -410,8 +406,7 @@ describe("[LC-6] 游标剪枝", () => {
410
406
  expect(Buffer.byteLength(gen2, "utf-8")).toBeGreaterThan(150);
411
407
  fs.writeFileSync(f, gen2);
412
408
 
413
- const list = listActivePendingFromSessionFile(f);
414
- expect(list.items.map((i) => i.id)).toEqual(["bg-x"]); // 剪枝生效:从头全量读
409
+ expect(readActivePendingFromSessionFile(f).count).toBe(1); // 剪枝生效:从头全量读
415
410
  });
416
411
 
417
412
  it("prunePendingCursor:回收后重新判定等值(全量重读差集不变)", () => {
@@ -429,7 +424,6 @@ describe("[LC-6] 游标剪枝", () => {
429
424
  fs.appendFileSync(f, entryLine("pending:unregister", { id: "bg-1" }));
430
425
  const r = readActivePendingFromSessionFile(f);
431
426
  expect(r.count).toBe(1); // 重建后差集正确(无重复无丢失)
432
- expect(listActivePendingFromSessionFile(f).items.map((i) => i.id)).toEqual(["bg-2"]);
433
427
  });
434
428
 
435
429
  it("prunePendingCursor:prune 后覆写文件(size ≥ 旧 offset)→ 新内容完整可见", () => {
@@ -446,7 +440,7 @@ describe("[LC-6] 游标剪枝", () => {
446
440
  fs.writeFileSync(f, gen2); // 覆写(模拟文件替换),size > 旧 offset
447
441
  prunePendingCursor(f);
448
442
 
449
- expect(listActivePendingFromSessionFile(f).items.map((i) => i.id)).toEqual(["bg-y"]);
443
+ expect(readActivePendingFromSessionFile(f).count).toBe(1);
450
444
  });
451
445
 
452
446
  it("prunePendingCursor 不存在的条目为 no-op(不抛错)", () => {
@@ -469,3 +463,111 @@ describe("[LC-6] 游标剪枝", () => {
469
463
  expect(readActivePendingFromSessionFile(f).count).toBe(1);
470
464
  });
471
465
  });
466
+
467
+ // ─── [F1 读侧过滤②] 跨 session 残留基准 = 被读文件所属 session ───
468
+ //
469
+ // fork 继承的父级 register 残留(翻 process 档后 U4 补注销不再中性化,永久留存)
470
+ // 由读侧过滤收口:本文件读侧以「被读文件所属 session id」(pi session 文件首行
471
+ // SessionHeader.id)为基准经端口第二参传入,端口实现按 entry sessionId ≠ 基准
472
+ // 跳过——后代判定差集不含跨 session 残留,层主不被残留误判「尚有活跃后代」。
473
+
474
+ describe("[F1 读侧过滤②] 后代判定差集的跨 session 残留基准", () => {
475
+ const tmpFiles: string[] = [];
476
+
477
+ afterEach(() => {
478
+ for (const f of tmpFiles.splice(0)) {
479
+ try {
480
+ fs.rmSync(path.dirname(f), { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
481
+ } catch {
482
+ // 清理失败不影响断言
483
+ }
484
+ }
485
+ });
486
+
487
+ /** pi session 文件首行 SessionHeader(session-manager.js newSession 形态)。 */
488
+ const mkSessionHeader = (sessionId: string) =>
489
+ JSON.stringify({ type: "session", version: 3, id: sessionId, timestamp: "2026-09-01T00:00:00.000Z", cwd: "/tmp" });
490
+
491
+ /** 带 sessionId 的 register(pending-notifications listener 落盘形态)。 */
492
+ const mkRegisterIn = (id: string, sessionId: string) =>
493
+ JSON.stringify({ type: "custom", customType: "pending:register", data: { id, type: "subagent", name: id, registeredAt: 1, sessionId } });
494
+
495
+ /** 端口等价实现(消费基准):按 currentSessionId 过滤后直数——pending-notifications
496
+ * filterActiveRegisters 的最小转写(缺 sessionId 的旧形态条目视为本 session,不过滤)。 */
497
+ function filteredCountingPort(): ReturnType<typeof vi.fn<(entries: unknown[], opts?: { currentSessionId?: string }) => number>> {
498
+ const counter = vi.fn((entries: unknown[], opts?: { currentSessionId?: string }) => {
499
+ const seen = new Set<string>();
500
+ let count = 0;
501
+ for (const raw of entries) {
502
+ if (!raw || typeof raw !== "object") continue;
503
+ const data = (raw as { data?: { id?: unknown; sessionId?: unknown } }).data ?? {};
504
+ if (typeof data.id !== "string" || seen.has(data.id)) continue;
505
+ seen.add(data.id);
506
+ if (
507
+ opts?.currentSessionId !== undefined &&
508
+ typeof data.sessionId === "string" &&
509
+ data.sessionId !== opts.currentSessionId
510
+ ) {
511
+ continue;
512
+ }
513
+ count += 1;
514
+ }
515
+ return count;
516
+ }) as ReturnType<typeof vi.fn<(entries: unknown[], opts?: { currentSessionId?: string }) => number>>;
517
+ configureNotifyDomain({ countActiveFromEntries: counter });
518
+ return counter;
519
+ }
520
+
521
+ it("带 session header 的文件 → 端口收到的基准 = header.id(被读文件所属 session)", () => {
522
+ const counter = filteredCountingPort();
523
+ const file = makeTmpSessionFile([
524
+ mkSessionHeader("sess-child"),
525
+ mkRegisterIn("bg-own", "sess-child"),
526
+ ]);
527
+ tmpFiles.push(file);
528
+ expect(readActivePendingFromSessionFile(file).count).toBe(1);
529
+ const call = counter.mock.calls[0] as unknown[];
530
+ expect(call[1]).toEqual({ currentSessionId: "sess-child" });
531
+ });
532
+
533
+ it("端到端:fork 继承的父级 register 残留(sessionId = 父 session)不进后代判定差集", () => {
534
+ filteredCountingPort();
535
+ // 子 session 文件 = header(sess-child) + fork 继承的父级注册残留 + 子进程自己的注册
536
+ const file = makeTmpSessionFile([
537
+ mkSessionHeader("sess-child"),
538
+ mkRegisterIn("bg-parent", "sess-parent"),
539
+ mkRegisterIn("wf-parent", "sess-parent"),
540
+ mkRegisterIn("bg-own", "sess-child"),
541
+ ]);
542
+ tmpFiles.push(file);
543
+ // 无读侧过滤时差集为 3(层主误判「尚有活跃后代」空等);过滤后只数本 session = 1
544
+ expect(readActivePendingFromSessionFile(file)).toEqual({ count: 1, recentUnregister: false });
545
+ });
546
+
547
+ it("无 session header(旧 fixture 形态)→ 基准 undefined = 不过滤(向后兼容)", () => {
548
+ const counter = filteredCountingPort();
549
+ const file = makeTmpSessionFile([
550
+ mkRegisterIn("bg-parent", "sess-parent"),
551
+ mkRegisterIn("bg-own", "sess-child"),
552
+ ]);
553
+ tmpFiles.push(file);
554
+ expect(readActivePendingFromSessionFile(file).count).toBe(2);
555
+ const call = counter.mock.calls[0] as unknown[];
556
+ expect(call[1]).toBeUndefined();
557
+ });
558
+
559
+ it("增量读(cursor 已热)时基准仍从 cursor 缓存透传(不依赖重读首行)", () => {
560
+ const counter = filteredCountingPort();
561
+ const file = makeTmpSessionFile([
562
+ mkSessionHeader("sess-child"),
563
+ mkRegisterIn("bg-own", "sess-child"),
564
+ ]);
565
+ tmpFiles.push(file);
566
+ expect(readActivePendingFromSessionFile(file).count).toBe(1); // 首读建 cursor
567
+ // 追加新 register 后增量读——offset > 0 不重读首行,基准取 cursor 缓存
568
+ fs.appendFileSync(file, mkRegisterIn("bg-own-2", "sess-child") + "\n", "utf-8");
569
+ expect(readActivePendingFromSessionFile(file).count).toBe(2);
570
+ const lastCall = counter.mock.calls[counter.mock.calls.length - 1] as unknown[];
571
+ expect(lastCall[1]).toEqual({ currentSessionId: "sess-child" });
572
+ });
573
+ });