@zhushanwen/subagent-core 0.4.0 → 0.6.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 (309) hide show
  1. package/dist/{chunk-4KN4TTG7.js → chunk-43ONBFZX.js} +1 -1
  2. package/dist/{chunk-X7SZ5HLQ.js → chunk-V3VHZ2VX.js} +20 -27
  3. package/dist/execution/engine/engines/zcode/constants.cjs +29 -41
  4. package/dist/execution/engine/engines/zcode/constants.d.cts +65 -53
  5. package/dist/execution/engine/engines/zcode/constants.d.ts +65 -53
  6. package/dist/execution/engine/engines/zcode/constants.js +17 -27
  7. package/dist/execution/engine/engines/zcode/reader.d.cts +2 -1
  8. package/dist/execution/engine/engines/zcode/reader.d.ts +2 -1
  9. package/dist/execution/engine/engines/zcode/reader.js +2 -2
  10. package/dist/index.cjs +17100 -14082
  11. package/dist/index.d.cts +5336 -4033
  12. package/dist/index.d.ts +5336 -4033
  13. package/dist/index.js +17020 -14055
  14. package/dist/{types-DpUO16pj.d.cts → types-C3fE3R9x.d.cts} +280 -112
  15. package/dist/{types-DpUO16pj.d.ts → types-C3fE3R9x.d.ts} +280 -112
  16. package/dist.bundle/index.cjs +34933 -0
  17. package/package.json +3 -3
  18. package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +0 -1
  19. package/src/__tests__/append-system-prompt-assembly.test.ts +8 -7
  20. package/src/__tests__/fr4-get-state-handshake.test.ts +2 -2
  21. package/src/__tests__/m2-append-content-probe.test.ts +0 -1
  22. package/src/__tests__/manifest-store.test.ts +10 -9
  23. package/src/__tests__/record-store-cache.test.ts +0 -2
  24. package/src/__tests__/record-store-index.test.ts +1 -2
  25. package/src/__tests__/review-fix-loop-script.test.ts +4 -2
  26. package/src/__tests__/review-fix-loop-utils.test.ts +10 -10
  27. package/src/__tests__/robustness-low-batch1.test.ts +6 -5
  28. package/src/__tests__/robustness-medium-batch1.test.ts +2 -2
  29. package/src/__tests__/robustness-medium-batch2.test.ts +17 -6
  30. package/src/__tests__/robustness-medium-batch4.test.ts +2 -2
  31. package/src/__tests__/session-runner.test.ts +1 -1
  32. package/src/core/__tests__/host-services.test.ts +2 -2
  33. package/src/core/__tests__/logger.test.ts +1 -1
  34. package/src/core/error-message.ts +9 -0
  35. package/src/core/host-services.ts +21 -5
  36. package/src/core/notify-ports.ts +18 -5
  37. package/src/execution/__tests__/agent-profile.test.ts +4 -4
  38. package/src/execution/__tests__/agent-registry.test.ts +370 -0
  39. package/src/execution/__tests__/agent-result-mapper.test.ts +18 -2
  40. package/src/execution/__tests__/agents-assembly.test.ts +2 -2
  41. package/src/execution/__tests__/alive-store.test.ts +1 -1
  42. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +6 -6
  43. package/src/execution/__tests__/chat-engine-routing.test.ts +68 -33
  44. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +4 -4
  45. package/src/execution/__tests__/cold-resurrect.test.ts +283 -0
  46. package/src/execution/__tests__/collect-budget.test.ts +291 -0
  47. package/src/execution/__tests__/collect-coordinator-service.test.ts +327 -0
  48. package/src/execution/__tests__/collect-coordinator.test.ts +371 -0
  49. package/src/execution/__tests__/collect-mixed-dispatch.test.ts +262 -0
  50. package/src/execution/__tests__/config-collect-sync.test.ts +120 -0
  51. package/src/execution/__tests__/config.test.ts +1 -1
  52. package/src/execution/__tests__/delivery-methods.test.ts +75 -62
  53. package/src/execution/__tests__/descendant-sweep-guards.test.ts +220 -0
  54. package/src/execution/__tests__/descendant-sweep.test.ts +6 -6
  55. package/src/execution/__tests__/dialog-queue.test.ts +2 -2
  56. package/src/execution/__tests__/engine-model-validation.test.ts +741 -0
  57. package/src/execution/__tests__/epipe-fallback.test.ts +29 -24
  58. package/src/execution/__tests__/execute-and-await-worktree.test.ts +21 -91
  59. package/src/execution/__tests__/execute-nesting.test.ts +34 -188
  60. package/src/execution/__tests__/execution-record.test.ts +217 -60
  61. package/src/execution/__tests__/execution-runtime-face.test.ts +5 -5
  62. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +4 -4
  63. package/src/execution/__tests__/finalize-record.test.ts +173 -17
  64. package/src/execution/__tests__/finalized-marker.test.ts +1 -1
  65. package/src/execution/__tests__/gc-timer.test.ts +56 -3
  66. package/src/execution/__tests__/get-record-for-action-restart.test.ts +12 -12
  67. package/src/execution/__tests__/get-state-handshake.test.ts +1 -1
  68. package/src/execution/__tests__/helpers/mock-extension-api.ts +1 -1
  69. package/src/execution/__tests__/helpers/session-runner-mocks.ts +113 -0
  70. package/src/execution/__tests__/helpers/spawn-mock.ts +150 -3
  71. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +137 -0
  72. package/src/execution/__tests__/keep-alive-no-progress.test.ts +50 -148
  73. package/src/execution/__tests__/kill-all-escalation.test.ts +2 -2
  74. package/src/execution/__tests__/lifecycle-predicates.test.ts +1 -2
  75. package/src/execution/__tests__/manifest-parentid.test.ts +4 -4
  76. package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +1 -1
  77. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +1 -1
  78. package/src/execution/__tests__/model-resolver.test.ts +2 -2
  79. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +23 -121
  80. package/src/execution/__tests__/nested-visibility.test.ts +2 -0
  81. package/src/execution/__tests__/notify-batch.test.ts +500 -0
  82. package/src/execution/__tests__/notify-ledger.test.ts +1000 -18
  83. package/src/execution/__tests__/output-collector.test.ts +117 -6
  84. package/src/execution/__tests__/pi-invocation.test.ts +36 -4
  85. package/src/execution/__tests__/record-entry-collect.test.ts +102 -0
  86. package/src/execution/__tests__/record-store.test.ts +10 -9
  87. package/src/execution/__tests__/records-cwd-isolation.test.ts +2 -2
  88. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +29 -132
  89. package/src/execution/__tests__/recursive-visibility-env.test.ts +55 -179
  90. package/src/execution/__tests__/relay-env.test.ts +12 -1
  91. package/src/execution/__tests__/rpc-mode.test.ts +2 -2
  92. package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +387 -0
  93. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +15 -9
  94. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +4 -4
  95. package/src/execution/__tests__/run-spawn-edges.test.ts +17 -15
  96. package/src/execution/__tests__/run-spawn-integration.test.ts +2 -2
  97. package/src/execution/__tests__/run-spawn-resume.test.ts +2 -2
  98. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +2 -2
  99. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +4 -4
  100. package/src/execution/__tests__/service-kill-escalation.test.ts +7 -5
  101. package/src/execution/__tests__/session-context-resolver.test.ts +0 -2
  102. package/src/execution/__tests__/session-file-gc.test.ts +36 -1
  103. package/src/execution/__tests__/session-pending.test.ts +257 -14
  104. package/src/execution/__tests__/session-reconstructor.test.ts +366 -15
  105. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +1 -1
  106. package/src/execution/__tests__/session-runner-close-prune.test.ts +2 -2
  107. package/src/execution/__tests__/session-runner-dispatch.test.ts +489 -0
  108. package/src/execution/__tests__/session-runner-epipe.test.ts +3 -3
  109. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +5 -4
  110. package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +290 -0
  111. package/src/execution/__tests__/session-runner-schema-env.test.ts +7 -5
  112. package/src/execution/__tests__/settled-watchdog.test.ts +282 -47
  113. package/src/execution/__tests__/spawn-args.test.ts +11 -10
  114. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +26 -23
  115. package/src/execution/__tests__/spawn-event-adapter.test.ts +3 -3
  116. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +5 -4
  117. package/src/execution/__tests__/spawned-children.test.ts +2 -1
  118. package/src/execution/__tests__/start-collect-guard.test.ts +255 -0
  119. package/src/execution/__tests__/start-sync-model-guard.test.ts +5 -5
  120. package/src/execution/__tests__/stdin-writer.test.ts +6 -4
  121. package/src/execution/__tests__/stream-sink-retirement.test.ts +6 -6
  122. package/src/execution/__tests__/subagent-actions-core.test.ts +48 -22
  123. package/src/execution/__tests__/subagent-service-message-close.test.ts +29 -26
  124. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +21 -8
  125. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +27 -22
  126. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +30 -29
  127. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +45 -20
  128. package/src/execution/__tests__/subagent-service.test.ts +37 -30
  129. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +88 -12
  130. package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +75 -0
  131. package/src/execution/__tests__/subprocess-agent-runner.test.ts +22 -20
  132. package/src/execution/__tests__/sync-collect-recovery.test.ts +1117 -0
  133. package/src/execution/__tests__/temp-prompt.test.ts +1 -1
  134. package/src/execution/__tests__/timeout-integration.test.ts +6 -5
  135. package/src/execution/__tests__/tombstone-store.test.ts +1 -1
  136. package/src/execution/__tests__/turn-limiter-semantics.test.ts +1 -1
  137. package/src/execution/__tests__/turn-limiter.test.ts +1 -1
  138. package/src/execution/__tests__/ui-request-handler-factory.test.ts +12 -6
  139. package/src/execution/__tests__/ui-request-handler.test.ts +4 -3
  140. package/src/execution/__tests__/ui-request-queue.test.ts +142 -2
  141. package/src/execution/__tests__/worktree-git-ops.test.ts +1 -1
  142. package/src/execution/__tests__/worktree-manager.test.ts +14 -5
  143. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +4 -3
  144. package/src/execution/__tests__/worktree-reconcile-aging.test.ts +2 -2
  145. package/src/execution/__tests__/worktree-reconcile.integration.test.ts +1 -1
  146. package/src/execution/__tests__/worktree-registry.test.ts +1 -1
  147. package/src/execution/agent-registry.ts +74 -30
  148. package/src/execution/agent-result-mapper.ts +3 -0
  149. package/src/execution/cold-resurrect.ts +199 -0
  150. package/src/execution/collect-coordinator.ts +200 -0
  151. package/src/execution/config.ts +77 -5
  152. package/src/execution/engine/__tests__/common/capability-gate.test.ts +91 -0
  153. package/src/execution/engine/__tests__/common/data-dir.test.ts +1 -1
  154. package/src/execution/engine/__tests__/common/event-journal.test.ts +1 -1
  155. package/src/execution/engine/__tests__/common/journal-wiring.test.ts +84 -0
  156. package/src/execution/engine/__tests__/common/kill-chain.test.ts +34 -7
  157. package/src/execution/engine/__tests__/common/persona-router.test.ts +4 -5
  158. package/src/execution/engine/__tests__/common/pool-manager.test.ts +213 -53
  159. package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +149 -0
  160. package/src/execution/engine/__tests__/common/session-view-service.test.ts +499 -0
  161. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +22 -112
  162. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +15 -28
  163. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +1 -1
  164. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +12 -12
  165. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +2 -2
  166. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +21 -82
  167. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +4 -7
  168. package/src/execution/engine/__tests__/engine-discovery.test.ts +15 -3
  169. package/src/execution/engine/__tests__/model-prompt.test.ts +15 -3
  170. package/src/execution/engine/__tests__/registry.test.ts +4 -2
  171. package/src/execution/engine/__tests__/routing.test.ts +115 -6
  172. package/src/execution/engine/common/capability-gate.ts +92 -0
  173. package/src/execution/engine/common/journal-wiring.ts +97 -0
  174. package/src/execution/engine/common/kill-chain.ts +42 -10
  175. package/src/execution/engine/common/nesting-guard.ts +66 -0
  176. package/src/execution/engine/common/persona-router.ts +18 -8
  177. package/src/execution/engine/common/pool-manager.ts +332 -78
  178. package/src/execution/engine/common/session-view-service.ts +506 -0
  179. package/src/execution/engine/common/session-view-types.ts +125 -0
  180. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +174 -28
  181. package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +257 -0
  182. package/src/execution/{argv-mirror.ts → engine/engines/pi/argv-mirror.ts} +43 -21
  183. package/src/execution/{get-state-handshake.ts → engine/engines/pi/get-state-handshake.ts} +1 -1
  184. package/src/execution/{output-collector.ts → engine/engines/pi/output-collector.ts} +108 -19
  185. package/src/execution/engine/engines/pi/pi-engine.ts +434 -38
  186. package/src/execution/{pi-invocation.ts → engine/engines/pi/pi-invocation.ts} +2 -2
  187. package/src/execution/engine/engines/pi/reader.ts +11 -0
  188. package/src/execution/engine/engines/pi/registration.ts +4 -2
  189. package/src/execution/{session-runner.ts → engine/engines/pi/session-runner.ts} +617 -412
  190. package/src/execution/{spawn-event-adapter.ts → engine/engines/pi/spawn-event-adapter.ts} +75 -34
  191. package/src/execution/{stdin-writer.ts → engine/engines/pi/stdin-writer.ts} +10 -9
  192. package/src/execution/{temp-prompt.ts → engine/engines/pi/temp-prompt.ts} +2 -2
  193. package/src/execution/{turn-limiter.ts → engine/engines/pi/turn-limiter.ts} +1 -1
  194. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +54 -27
  195. package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +321 -0
  196. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +13 -12
  197. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +7 -210
  198. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +6 -110
  199. package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +247 -0
  200. package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +451 -0
  201. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +20 -8
  202. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +77 -110
  203. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +26 -320
  204. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +210 -0
  205. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +353 -0
  206. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +282 -0
  207. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +372 -0
  208. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +48 -148
  209. package/src/execution/engine/engines/zcode/appserver-launcher.ts +167 -0
  210. package/src/execution/engine/engines/zcode/connection.ts +24 -11
  211. package/src/execution/engine/engines/zcode/constants.ts +75 -71
  212. package/src/execution/engine/engines/zcode/golden-sample.ts +8 -46
  213. package/src/execution/engine/engines/zcode/parser.ts +12 -346
  214. package/src/execution/engine/engines/zcode/preparer.ts +12 -143
  215. package/src/execution/engine/engines/zcode/session-channel.ts +351 -39
  216. package/src/execution/engine/engines/zcode/zcode-engine.ts +665 -760
  217. package/src/execution/engine/host-task-spec.ts +32 -36
  218. package/src/execution/engine/model-validation.ts +177 -0
  219. package/src/execution/engine/port.ts +24 -6
  220. package/src/execution/engine/routing.ts +75 -1
  221. package/src/execution/engine/types.ts +30 -103
  222. package/src/execution/execution-record.ts +229 -144
  223. package/src/execution/finalize-record.ts +141 -99
  224. package/src/execution/idle-gc.ts +28 -0
  225. package/src/execution/lifecycle-predicates.ts +1 -1
  226. package/src/execution/notifier.ts +234 -25
  227. package/src/execution/notify-host.ts +232 -0
  228. package/src/execution/record-entry.ts +16 -0
  229. package/src/execution/record-store.ts +280 -103
  230. package/src/execution/round-settlement.ts +93 -0
  231. package/src/execution/session-file-gc.ts +96 -62
  232. package/src/execution/session-reconstructor.ts +256 -143
  233. package/src/execution/sessions-index.ts +117 -47
  234. package/src/execution/settled-watchdog.ts +264 -52
  235. package/src/execution/subagent-actions-core.ts +84 -28
  236. package/src/execution/subagent-service.ts +1225 -964
  237. package/src/execution/subprocess-agent-runner.ts +144 -97
  238. package/src/execution/sync-rebuild.ts +87 -0
  239. package/src/execution/types.ts +72 -0
  240. package/src/execution/ui-request-handler-factory.ts +102 -74
  241. package/src/execution/ui-request-queue.ts +50 -19
  242. package/src/index.ts +442 -316
  243. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +1 -1
  244. package/src/orchestration/__tests__/agent-call-stream.test.ts +2 -2
  245. package/src/orchestration/__tests__/args-validator.test.ts +14 -0
  246. package/src/orchestration/__tests__/config-loader.test.ts +1 -1
  247. package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +1 -1
  248. package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +11 -9
  249. package/src/orchestration/__tests__/execute-agent-call.test.ts +151 -11
  250. package/src/orchestration/__tests__/file-run-store-prune.test.ts +1 -1
  251. package/src/orchestration/__tests__/file-run-store-throttle.test.ts +1 -1
  252. package/src/orchestration/__tests__/file-run-store.test.ts +7 -6
  253. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +2 -2
  254. package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +1 -1
  255. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +3 -3
  256. package/src/orchestration/__tests__/lifecycle.test.ts +7 -5
  257. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +1 -1
  258. package/src/orchestration/__tests__/review-fix-loop-scriptpath-failfast.test.ts +1 -1
  259. package/src/orchestration/__tests__/run-snapshot.test.ts +8 -5
  260. package/src/orchestration/__tests__/script-generate.test.ts +5 -5
  261. package/src/orchestration/__tests__/script-lint.test.ts +17 -0
  262. package/src/orchestration/__tests__/skill-discovery.test.ts +2 -2
  263. package/src/orchestration/__tests__/test-mocks.ts +2 -0
  264. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +7 -6
  265. package/src/orchestration/__tests__/worker-message-pump-finalize-run.test.ts +229 -0
  266. package/src/orchestration/__tests__/{error-recovery-handlers.test.ts → worker-message-pump-handlers.test.ts} +27 -19
  267. package/src/orchestration/__tests__/{error-recovery-postmessage-defense.test.ts → worker-message-pump-postmessage-defense.test.ts} +8 -8
  268. package/src/orchestration/__tests__/{error-recovery-serialize-failed-result.test.ts → worker-message-pump-serialize-failed-result.test.ts} +2 -2
  269. package/src/orchestration/__tests__/{error-recovery-workflow-call.test.ts → worker-message-pump-workflow-call.test.ts} +1 -1
  270. package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +1 -1
  271. package/src/orchestration/__tests__/worker-script-builder.test.ts +1 -1
  272. package/src/orchestration/__tests__/workflow-files.test.ts +10 -10
  273. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +1 -1
  274. package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +1 -1
  275. package/src/orchestration/agent-opts-resolver.ts +1 -1
  276. package/src/orchestration/args-validator.ts +53 -31
  277. package/src/orchestration/execute-agent-call.ts +19 -73
  278. package/src/orchestration/launcher.ts +128 -81
  279. package/src/orchestration/lifecycle.ts +43 -45
  280. package/src/orchestration/models/ports.ts +4 -4
  281. package/src/orchestration/models/run-runtime.ts +2 -2
  282. package/src/orchestration/models/trace.ts +1 -1
  283. package/src/orchestration/models/types.ts +111 -22
  284. package/src/orchestration/models/workflow-run.ts +1 -1
  285. package/src/orchestration/script-lint.ts +91 -58
  286. package/src/orchestration/{error-recovery.ts → worker-message-pump.ts} +157 -150
  287. package/src/shared/__tests__/atomic-write.test.ts +1 -1
  288. package/src/shared/__tests__/meta-parser.test.ts +151 -0
  289. package/src/shared/__tests__/resource-discovery-host-roots.test.ts +5 -5
  290. package/src/shared/__tests__/resource-discovery.test.ts +258 -215
  291. package/src/shared/atomic-write.ts +12 -9
  292. package/src/shared/meta-parser.ts +158 -84
  293. package/src/shared/model-ref.ts +15 -2
  294. package/src/shared/resource-discovery.ts +9 -183
  295. package/src/shared/schema-jsonify.ts +1 -1
  296. package/workflows/review-fix-loop-utils.cjs +151 -89
  297. package/workflows/review-fix-loop-utils.d.cts +287 -0
  298. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
  299. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  300. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  301. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  302. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  303. package/src/execution/engine/engines/zcode/__tests__/preparer-appserver.test.ts +0 -387
  304. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -710
  305. package/src/execution/engine/engines/zcode/appserver-home.ts +0 -442
  306. package/src/execution/engine/engines/zcode/appserver-probe.ts +0 -141
  307. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  308. package/src/execution/execute-options-mapper.ts +0 -115
  309. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -288
@@ -72,7 +72,7 @@ vi.mock("../session-pending.ts", () => ({
72
72
  prunePendingCursor: vi.fn(),
73
73
  }));
74
74
 
75
- vi.mock("../temp-prompt.ts", () => ({
75
+ vi.mock("../engine/engines/pi/temp-prompt.ts", () => ({
76
76
  writePromptToTempFile: vi.fn(async (agent: string) => {
77
77
  const safeName = agent.replace(/[^\w.-]+/g, "_");
78
78
  return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt-${safeName}.md` };
@@ -80,8 +80,8 @@ vi.mock("../temp-prompt.ts", () => ({
80
80
  cleanupTempPrompt: vi.fn(async () => {}),
81
81
  }));
82
82
 
83
- import { runSpawn, SPAWN_WATCHDOG_ENV } from "../session-runner.ts";
84
- import type { SessionRunnerContext } from "../session-runner.ts";
83
+ import { runSpawn, SPAWN_WATCHDOG_ENV } from "../engine/engines/pi/session-runner.ts";
84
+ import type { SessionRunnerContext } from "../engine/engines/pi/session-runner.ts";
85
85
  import { hasIdleTimer, _resetLifecycleState } from "../lifecycle-manager.ts";
86
86
  import { createRecord } from "../execution-record.ts";
87
87
  import type { ExecutionRecord } from "../types.ts";
@@ -106,7 +106,7 @@ function makeChatModeRecord(id = "sa-f2-idle"): ExecutionRecord {
106
106
  return createRecord(id, {
107
107
  agent: "general-purpose",
108
108
  model: "test-model",
109
- mode: "sync",
109
+ mode: "background",
110
110
  task: "chat task",
111
111
  slug: "chat",
112
112
  startedAt: 1_000_000,
@@ -10,12 +10,14 @@
10
10
 
11
11
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
12
12
 
13
+ import type { ChildProcess } from "node:child_process";
14
+
13
15
  import { FakeChild } from "./helpers/spawn-mock.ts";
14
16
  import {
15
17
  _resetServiceKillStateForTest,
16
18
  killRecordChildWithEscalation,
17
19
  spawnedChildren,
18
- } from "../session-runner.ts";
20
+ } from "../engine/engines/pi/session-runner.ts";
19
21
 
20
22
  const SIGKILL_ESCALATION_MS = 30_000;
21
23
 
@@ -41,7 +43,7 @@ describe("T2④ killRecordChildWithEscalation", () => {
41
43
  const child = new FakeChild();
42
44
  child.killed = true;
43
45
  const killSpy = vi.spyOn(child, "kill");
44
- spawnedChildren.set("sa-killed", child);
46
+ spawnedChildren.set("sa-killed", child as unknown as ChildProcess);
45
47
  killRecordChildWithEscalation("sa-killed", "test");
46
48
  expect(killSpy).not.toHaveBeenCalled();
47
49
  });
@@ -49,7 +51,7 @@ describe("T2④ killRecordChildWithEscalation", () => {
49
51
  it("sends SIGTERM and escalates to SIGKILL after 30s without exit", async () => {
50
52
  const child = new FakeChild();
51
53
  const killSpy = vi.spyOn(child, "kill");
52
- spawnedChildren.set("sa-escalate", child);
54
+ spawnedChildren.set("sa-escalate", child as unknown as ChildProcess);
53
55
 
54
56
  killRecordChildWithEscalation("sa-escalate", "closeChatIdle");
55
57
  expect(killSpy).toHaveBeenNthCalledWith(1, "SIGTERM");
@@ -64,7 +66,7 @@ describe("T2④ killRecordChildWithEscalation", () => {
64
66
  it("clears the escalation timer when the child exits in time", async () => {
65
67
  const child = new FakeChild();
66
68
  const killSpy = vi.spyOn(child, "kill");
67
- spawnedChildren.set("sa-exit", child);
69
+ spawnedChildren.set("sa-exit", child as unknown as ChildProcess);
68
70
 
69
71
  killRecordChildWithEscalation("sa-exit", "cancelBackground");
70
72
  child.emit("exit", 0, null);
@@ -75,7 +77,7 @@ describe("T2④ killRecordChildWithEscalation", () => {
75
77
  it("repeated calls clear the previous escalation timer (no stacking)", async () => {
76
78
  const child = new FakeChild();
77
79
  const killSpy = vi.spyOn(child, "kill");
78
- spawnedChildren.set("sa-repeat", child);
80
+ spawnedChildren.set("sa-repeat", child as unknown as ChildProcess);
79
81
 
80
82
  killRecordChildWithEscalation("sa-repeat", "first");
81
83
  await vi.advanceTimersByTimeAsync(10_000);
@@ -23,7 +23,6 @@ describe("resolveSessionContext", () => {
23
23
  it("fork=false, worktree=false → shouldFork=false, effectiveCwd=mainCwd", () => {
24
24
  const result = resolveSessionContext({
25
25
  fork: false,
26
- worktree: false,
27
26
  mainCwd: MAIN_CWD,
28
27
  agentDir: AGENT_DIR,
29
28
  });
@@ -37,7 +36,6 @@ describe("resolveSessionContext", () => {
37
36
  it("fork=true, worktree=false → shouldFork=true, forkSource=mainSessionFile", () => {
38
37
  const result = resolveSessionContext({
39
38
  fork: true,
40
- worktree: false,
41
39
  mainCwd: MAIN_CWD,
42
40
  mainSessionFile: SESSION_FILE,
43
41
  agentDir: AGENT_DIR,
@@ -17,6 +17,12 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
17
17
  import * as aliveStore from "../alive-store.ts";
18
18
  import { maybeCleanupExpiredSessionFiles } from "../session-file-gc.ts";
19
19
 
20
+ // [D8] 引擎池 TTL 兜底走 getEngineDataDir() 解析 dataDir——测试必须钉到 tmpAgentDir,
21
+ // 防止命中真实 XYZ_AGENT_DATA_DIR(或宿主 fallback)误清开发机数据。
22
+ vi.mock("../engine/common/data-dir.ts", () => ({
23
+ getEngineDataDir: () => tmpAgentDir,
24
+ }));
25
+
20
26
  // ============================================================
21
27
  // helpers
22
28
  // ============================================================
@@ -28,7 +34,7 @@ beforeEach(() => {
28
34
  });
29
35
 
30
36
  afterEach(() => {
31
- fs.rmSync(tmpAgentDir, { recursive: true, force: true });
37
+ fs.rmSync(tmpAgentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
32
38
  vi.restoreAllMocks();
33
39
  });
34
40
 
@@ -291,3 +297,32 @@ describe("maybeCleanupExpiredSessionFiles", () => {
291
297
  expect(fs.existsSync(tmp)).toBe(true);
292
298
  });
293
299
  });
300
+
301
+ describe("[D8] 引擎池 TTL 兜底(maybeCleanupExpiredSessionFiles 挂载 cleanupExpiredPoolRefs)", () => {
302
+ /** 池内建一个 journal 并把 mtime 设为指定天数前。 */
303
+ function createPoolJournal(poolKey: string, taskId: string, daysAgo: number): string {
304
+ const poolDir = path.join(tmpAgentDir, "engines", "zcode", poolKey);
305
+ fs.mkdirSync(poolDir, { recursive: true });
306
+ const journal = path.join(poolDir, `journal-${taskId}.jsonl`);
307
+ fs.writeFileSync(journal, "{}\n", "utf-8");
308
+ const targetTime = Date.now() / 1000 - daysAgo * 86400;
309
+ fs.utimesSync(journal, targetTime, targetTime);
310
+ return journal;
311
+ }
312
+
313
+ it("超龄 journal 随概率门触发回收(engines/<engineId>/<poolKey>/ 下)", () => {
314
+ forceCleanupTrigger();
315
+ const oldJournal = createPoolJournal("home-p-m", "bg-1", 31);
316
+ const youngJournal = createPoolJournal("home-p-m", "bg-2", 5);
317
+ maybeCleanupExpiredSessionFiles(tmpAgentDir, "/cwd");
318
+ expect(fs.existsSync(oldJournal)).toBe(false);
319
+ expect(fs.existsSync(youngJournal)).toBe(true);
320
+ });
321
+
322
+ it("概率门跳过时引擎池不动", () => {
323
+ forceCleanupSkip();
324
+ const oldJournal = createPoolJournal("home-p-m", "bg-3", 31);
325
+ maybeCleanupExpiredSessionFiles(tmpAgentDir, "/cwd");
326
+ expect(fs.existsSync(oldJournal)).toBe(true);
327
+ });
328
+ });
@@ -1,24 +1,32 @@
1
1
  // src/execution/__tests__/session-pending.test.ts
2
2
  //
3
- // [LC-6/T6②] session-pending 增量游标有界化:register−unregister 差集计数 + 剪枝。
3
+ // readActivePendingFromSessionFile:读 session 文件算活跃后代(pending 差集)。
4
+ // 覆盖:差集、快速路径过滤、文件不存在/坏行/未回填 sessionFile。
5
+ // u-5c 迁自壳套件 src/__tests__/session-pending.test.ts(被测 module 是 core 件,
6
+ // 唯一测试覆盖原落壳——设计 §2.2 C6 / §1 目标 6)。
4
7
  //
5
- // 覆盖:
6
- // - 差集等值(语义不变):register/unregister 差集、同 id register 覆盖、
7
- // unregister 后同 id 再 register 恢复活跃、count 端口收到的 = 活跃差集、
8
- // latestUnregister 60s 唤醒窗口判据不受差集化影响。
9
- // - 剪枝:文件删除后 cursor 自动剪枝(重建同名文件从头全量读,不残留旧 offset);
10
- // prunePendingCursor 原语(进程 close 侧回收);prune 不存在条目 no-op。
8
+ // 迁移改写:原壳版注入真实 @zhushanwen/pi-pending-notifications 计数器锚定差集
9
+ // 语义;core 依赖闭包禁 pi 系包(u-5c 验收「零跨包 specifier」),改为下方
10
+ // countActiveFromEntries 等价转写(逐字对照 extensions/universal/
11
+ // pending-notifications/src/state.ts 同名实现的两遍差集算法:先收 unregister id
12
+ // 集,再计 register 差集按 id 去重)——pi 侧算法自身由该包自有测试守卫。
11
13
  //
12
- // 真实 fs(临时目录)——增量游标的 offset/差集语义被真实文件形态执行
13
- //(对齐 descendant-sweep.test.ts 模式;该文件的差集清单/交错用例继续回归)。
14
+ // 合并终态(feat 容器 + dev [LC-6/T6②] 增量游标有界化增量):
15
+ // - 差集已由 per-file 游标内联(activeRegisters Map,见 session-pending.ts
16
+ // PendingReadCursor)——countActiveFromEntries 端口收到的 = 活跃差集(非全量
17
+ // 行),等价转写在差集输入上幂等(unregister 行已被游标抵消,转写第一遍空
18
+ // 扫不改变结果),feat 族结果断言全部保持。
19
+ // - 追加 [LC-6] 差集计数语义等值 + 游标剪枝两族(差集清单 listActivePending
20
+ // FromSessionFile / prunePendingCursor 原语 / 文件删除惰性剪枝 / 端口收差集
21
+ // 口径断言)——真实 fs(临时目录)执行 offset/差集语义。
14
22
 
15
23
  import * as fs from "node:fs";
16
24
  import * as os from "node:os";
17
25
  import * as path from "node:path";
18
26
 
19
27
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
20
-
21
28
  import { configureNotifyDomain, resetNotifyDomainForTests } from "../../core/notify-ports.ts";
29
+
22
30
  import {
23
31
  clearPendingCursors,
24
32
  listActivePendingFromSessionFile,
@@ -26,19 +34,83 @@ import {
26
34
  readActivePendingFromSessionFile,
27
35
  } from "../session-pending.ts";
28
36
 
37
+ /** pi pending entries 的最小可识别形状(与 pi state.ts EntryLike 同构,运行时守卫)。 */
38
+ interface PendingEntryLike {
39
+ customType?: unknown;
40
+ data?: { id?: unknown } | null;
41
+ }
42
+
43
+ /**
44
+ * countActiveFromEntries 等价转写(u-5c:core 闭包禁 pi 包 import)。
45
+ * 算法逐字对照 @zhushanwen/pi-pending-notifications src/state.ts:
46
+ * 两遍扫描——第一遍收 pending:unregister 的 id 集,第二遍计 pending:register
47
+ * 中未被注销且未见过的 id(按 id 去重)。返回 { count }(端口契约拆 .count
48
+ * 的上游形态)。
49
+ */
50
+ function countActiveFromEntries(entries: unknown[]): { count: number } {
51
+ const unregistered = new Set<string>();
52
+ for (const raw of entries) {
53
+ // S-10:entries 元素可能是 null/undefined(坏数据),断言前先守卫
54
+ if (!raw || typeof raw !== "object") continue;
55
+ const entry = raw as PendingEntryLike;
56
+ if (entry.customType !== "pending:unregister") continue;
57
+ const data = (entry.data ?? {}) as { id?: unknown };
58
+ if (typeof data.id === "string") unregistered.add(data.id);
59
+ }
60
+
61
+ const seen = new Set<string>();
62
+ let count = 0;
63
+ for (const raw of entries) {
64
+ if (!raw || typeof raw !== "object") continue;
65
+ const entry = raw as PendingEntryLike;
66
+ if (entry.customType !== "pending:register") continue;
67
+ const data = (entry.data ?? {}) as { id?: unknown };
68
+ if (typeof data.id !== "string" || unregistered.has(data.id) || seen.has(data.id)) continue;
69
+ seen.add(data.id);
70
+ count += 1;
71
+ }
72
+ return { count };
73
+ }
74
+
75
+ // 计数器经通知域窄端口注入(session-pending 不再直接 import pi-pending-notifications)
76
+ // ——注入差集计数等价实现保住差集语义回归面(真函数返回 CountActiveResult,端口
77
+ // 契约是 number(拆 .count,与 pi 壳 createPiNotifyDomainPorts 适配同构);
78
+ // afterEach 重置防注入态泄漏。dev [LC-6] 族用 stubCountingPort 按用例覆盖
79
+ // (configureNotifyDomain 覆盖式写入,内层后执行生效)。
29
80
  let sessionDir = "";
30
81
 
31
82
  beforeEach(() => {
83
+ configureNotifyDomain({
84
+ countActiveFromEntries: (entries) => countActiveFromEntries(entries).count,
85
+ });
32
86
  clearPendingCursors();
33
87
  sessionDir = fs.mkdtempSync(path.join(os.tmpdir(), "session-pending-"));
34
88
  });
35
89
 
36
90
  afterEach(() => {
37
- clearPendingCursors();
38
91
  resetNotifyDomainForTests();
39
- fs.rmSync(sessionDir, { recursive: true, force: true });
92
+ clearPendingCursors();
93
+ fs.rmSync(sessionDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
40
94
  });
41
95
 
96
+ function makeTmpSessionFile(lines: string[]): string {
97
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "session-pending-test-"));
98
+ const file = path.join(dir, "session.jsonl");
99
+ fs.writeFileSync(file, lines.join("\n") + "\n");
100
+ return file;
101
+ }
102
+
103
+ const mkRegister = (id: string, type: "subagent" | "workflow" = "subagent") =>
104
+ JSON.stringify({ type: "custom", customType: "pending:register", data: { id, type, name: id } });
105
+
106
+ const mkUnregister = (id: string, reason = "completed", ts = "2026-08-06T08:00:00.000Z") =>
107
+ JSON.stringify({ type: "custom", customType: "pending:unregister", data: { id, reason }, timestamp: ts });
108
+
109
+ const mkMessage = (role: string, text: string) =>
110
+ JSON.stringify({ type: "message", id: `m-${Date.now()}-${Math.random()}`, message: { role, content: [{ type: "text", text }] } });
111
+
112
+ // ─── dev [LC-6] 族 helpers(真实 fs + entryLine 形态) ─────
113
+
42
114
  function entryLine(customType: string, data: Record<string, unknown>): string {
43
115
  return `${JSON.stringify({
44
116
  type: "custom",
@@ -54,12 +126,183 @@ function registerData(id: string, sessionId: string): Record<string, unknown> {
54
126
  }
55
127
 
56
128
  /** 注入直数端口:返回收到的 entry 数(观察 count 口径的消费面)。 */
57
- function stubCountingPort(): (entries: unknown[]) => number {
58
- const counter = vi.fn((entries: unknown[]) => entries.length);
129
+ function stubCountingPort(): ReturnType<typeof vi.fn<(entries: unknown[]) => number>> {
130
+ const counter = vi.fn((entries: unknown[]) => entries.length) as ReturnType<typeof vi.fn<(entries: unknown[]) => number>>;
59
131
  configureNotifyDomain({ countActiveFromEntries: counter });
60
132
  return counter;
61
133
  }
62
134
 
135
+ describe("readActivePendingFromSessionFile", () => {
136
+ const tmpFiles: string[] = [];
137
+
138
+ afterEach(() => {
139
+ for (const f of tmpFiles.splice(0)) {
140
+ try {
141
+ fs.rmSync(path.dirname(f), { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
142
+ } catch {
143
+ // 清理失败不影响断言
144
+ }
145
+ }
146
+ });
147
+
148
+ it("无 pending entries → count 0", () => {
149
+ const file = makeTmpSessionFile([mkMessage("user", "hi"), mkMessage("assistant", "hello")]);
150
+ tmpFiles.push(file);
151
+ expect(readActivePendingFromSessionFile(file)).toEqual({ count: 0, recentUnregister: false });
152
+ });
153
+
154
+ it("纯 register → count = 活跃后代数", () => {
155
+ const file = makeTmpSessionFile([mkRegister("bg-1"), mkRegister("bg-2", "workflow")]);
156
+ tmpFiles.push(file);
157
+ expect(readActivePendingFromSessionFile(file)).toEqual({ count: 2, recentUnregister: false });
158
+ });
159
+
160
+ it("register + unregister 同 id → 差集抵消", () => {
161
+ const file = makeTmpSessionFile([mkRegister("bg-1"), mkUnregister("bg-1")]);
162
+ tmpFiles.push(file);
163
+ expect(readActivePendingFromSessionFile(file)).toEqual({ count: 0, recentUnregister: false });
164
+ });
165
+
166
+ it("混合:部分注销 → 只统计仍活跃的(真实 e2e 场景:P 的 agent_end 时 explorer 未注销)", () => {
167
+ const file = makeTmpSessionFile([
168
+ mkRegister("bg-1"),
169
+ mkUnregister("bg-1", "completed"),
170
+ mkRegister("explorer-1"),
171
+ mkMessage("assistant", "waiting for explorer..."),
172
+ ]);
173
+ tmpFiles.push(file);
174
+ expect(readActivePendingFromSessionFile(file)).toEqual({ count: 1, recentUnregister: false });
175
+ });
176
+
177
+ it("fork 继承的主 session register 已被 expired unregister 抵消 → 不干扰", () => {
178
+ // 模拟 P fork 主 session:继承 register(sessionId 不匹配),session_start 重建补 unregister(expired)
179
+ const file = makeTmpSessionFile([
180
+ mkRegister("parent-bg"),
181
+ mkUnregister("parent-bg", "expired"),
182
+ mkRegister("my-bg"),
183
+ ]);
184
+ tmpFiles.push(file);
185
+ expect(readActivePendingFromSessionFile(file)).toEqual({ count: 1, recentUnregister: false });
186
+ });
187
+
188
+ it("坏行(截断 JSON)跳过,不影响其余判定", () => {
189
+ const file = makeTmpSessionFile([
190
+ '{"type":"custom","customType":"pending:register","data":{"id":"bg-1"',
191
+ mkRegister("bg-2"),
192
+ ]);
193
+ tmpFiles.push(file);
194
+ expect(readActivePendingFromSessionFile(file)).toEqual({ count: 1, recentUnregister: false });
195
+ });
196
+
197
+ it("sessionFile 未回填(undefined)→ error(调用方保守不 kill)", () => {
198
+ const res = readActivePendingFromSessionFile(undefined);
199
+ expect(res.count).toBe(0);
200
+ expect(res.error).toBeDefined();
201
+ });
202
+
203
+ it("文件不存在 → error(调用方保守不 kill)", () => {
204
+ const res = readActivePendingFromSessionFile("/nonexistent/path/session.jsonl");
205
+ expect(res.count).toBe(0);
206
+ expect(res.error).toBeDefined();
207
+ });
208
+
209
+ it("最近 60s 内有 unregister → recentUnregister=true(后代刚完成,唤醒在路上——竞态窗口不 kill)", () => {
210
+ const file = makeTmpSessionFile([
211
+ mkRegister("bg-live"),
212
+ mkUnregister("bg-live", "completed", new Date().toISOString()),
213
+ ]);
214
+ tmpFiles.push(file);
215
+ expect(readActivePendingFromSessionFile(file)).toEqual({ count: 0, recentUnregister: true });
216
+ });
217
+
218
+ it("unregister 在 60s 窗口外 → recentUnregister=false(正常 kill 路径)", () => {
219
+ const file = makeTmpSessionFile([
220
+ mkRegister("bg-live"),
221
+ mkUnregister("bg-live", "completed", "2020-01-01T00:00:00.000Z"),
222
+ ]);
223
+ tmpFiles.push(file);
224
+ expect(readActivePendingFromSessionFile(file)).toEqual({ count: 0, recentUnregister: false });
225
+ });
226
+
227
+ // [S-4] fast-path 按值匹配而非序列化格式。旧行 `line.includes('"customType":"pending:')`
228
+ // 耦合 pi 的 JSON 序列化空格习惯(冒号后无空格)。若 pi 改序列化(单行内冒号后加空格),
229
+ // 旧实现全过滤 → count=0 → keep-alive 静默失效 → recursive tree 被杀、steer 丢失。
230
+ // 本用例构造冒号后带空格的单行 JSON,验证值匹配仍命中 + 解析正确。
231
+ it("S-4: 序列化冒号后带空格 → 仍正确解析(防 fast-path 格式耦合导致 keep-alive 静默失效)", () => {
232
+ const file = makeTmpSessionFile([
233
+ `{ "type": "message", "message": { "role": "user", "content": [] } }`,
234
+ `{ "type": "custom", "customType": "pending:register", "data": { "id": "bg-1", "type": "subagent", "name": "bg-1" } }`,
235
+ `{ "type": "custom", "customType": "pending:unregister", "data": { "id": "bg-1", "reason": "completed" }, "timestamp": "2020-01-01T00:00:00.000Z" }`,
236
+ `{ "type": "custom", "customType": "pending:register", "data": { "id": "bg-2", "type": "subagent", "name": "bg-2" } }`,
237
+ ]);
238
+ tmpFiles.push(file);
239
+ // bg-1 register+unregister 抵消,bg-2 仍活跃 → count=1。
240
+ // 旧 fast-path(`"customType":"pending:` 冒号无空格)会跳过所有行 → count=0(静默失效)。
241
+ expect(readActivePendingFromSessionFile(file)).toEqual({ count: 1, recentUnregister: false });
242
+ });
243
+ });
244
+
245
+ // [perf] 增量游标行为:同文件重复判定(层主被多个后代唤醒 N 次 → N 次 agent_end)
246
+ // 只读上次 offset 之后的新增行,累计差集语义与全量读一致。
247
+ describe("readActivePendingFromSessionFile — 增量游标 [perf]", () => {
248
+ let dir: string;
249
+ let file: string;
250
+
251
+ beforeEach(() => {
252
+ dir = fs.mkdtempSync(path.join(os.tmpdir(), "session-pending-incr-"));
253
+ file = path.join(dir, "session.jsonl");
254
+ clearPendingCursors();
255
+ });
256
+
257
+ afterEach(() => {
258
+ fs.rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
259
+ });
260
+
261
+ it("首次全量 + append 后增量:unregister 只抵消对应 id(与全量读一致)", () => {
262
+ fs.writeFileSync(file, mkRegister("d1") + "\n" + mkRegister("d2") + "\n");
263
+ expect(readActivePendingFromSessionFile(file).count).toBe(2);
264
+
265
+ fs.appendFileSync(file, mkUnregister("d1", "completed") + "\n");
266
+ const r = readActivePendingFromSessionFile(file);
267
+ expect(r.error).toBeUndefined();
268
+ expect(r.count).toBe(1);
269
+ });
270
+
271
+ it("EOF 半行(append 竞态)不入账,补全换行后入账", () => {
272
+ fs.writeFileSync(file, mkRegister("d1") + "\n");
273
+ expect(readActivePendingFromSessionFile(file).count).toBe(1);
274
+
275
+ // 半行:无尾换行 → offset 不推进该行
276
+ const full = mkUnregister("d1", "completed", new Date().toISOString());
277
+ fs.appendFileSync(file, full.slice(0, full.length - 4));
278
+ expect(readActivePendingFromSessionFile(file).count).toBe(1);
279
+
280
+ // 补全(剩余字符 + 换行)→ 消费
281
+ fs.appendFileSync(file, full.slice(-4) + "\n");
282
+ const r = readActivePendingFromSessionFile(file);
283
+ expect(r.count).toBe(0);
284
+ expect(r.recentUnregister).toBe(true);
285
+ });
286
+
287
+ it("文件 truncate/重建(size 回退)→ 重置游标从头全读", () => {
288
+ fs.writeFileSync(file, mkRegister("d1") + "\n" + mkRegister("d2") + "\n");
289
+ expect(readActivePendingFromSessionFile(file).count).toBe(2);
290
+
291
+ fs.writeFileSync(file, mkRegister("d3") + "\n");
292
+ expect(readActivePendingFromSessionFile(file).count).toBe(1);
293
+ });
294
+
295
+ it("clearPendingCursors 后重新全量(累计 entries 不残留旧账)", () => {
296
+ fs.writeFileSync(file, mkRegister("d1") + "\n");
297
+ expect(readActivePendingFromSessionFile(file).count).toBe(1);
298
+ clearPendingCursors();
299
+ fs.appendFileSync(file, mkRegister("d2") + "\n");
300
+ expect(readActivePendingFromSessionFile(file).count).toBe(2);
301
+ });
302
+ });
303
+
304
+ // ─── [LC-6/T6②] 差集计数语义等值 + 游标剪枝(dev 增量) ────
305
+
63
306
  describe("[LC-6] 差集计数语义等值", () => {
64
307
  it("register−unregister 差集:unregister 后 count/list 归零", () => {
65
308
  const counter = stubCountingPort();