@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
@@ -2,31 +2,62 @@
2
2
  //
3
3
  // 隔离目录池管理(P2 公共降级层)。设计权威源:
4
4
  // docs/architecture/subagent-engine-abstraction.md D5(隔离目录池化保留,随 record
5
- // 生命周期回收,清理只做到池粒度)+ §3.3.9(目录布局 / refs 方案)。
5
+ // 生命周期回收,清理只做到池粒度)+ §3.3.9(目录布局 / refs.json 方案)。
6
6
  //
7
- // P2 口径(与 §3.3.9 完整设计的差异,签名所限):引用计数是**进程内注册表**——
8
- // acquire/release 的任务书签名无 taskId,refs.json 文件形态(登记 taskId + ts)留给
9
- // 后续 wave preparer/host 接线时落地(宿主是唯一写者,升级为文件形态不改变本模块
10
- // acquire/release 语义边界)。
7
+ // refs.json 文件形态(D8 接线,dual-track-convergence):引用计数的唯一权威源是
8
+ // 池目录内 refs.json——进程重启后计数可恢复(文件在即计数在)。.acquire 登记
9
+ // taskId(幂等刷新 ts),release 移除 taskId 并删对应 journal;计数归零删池内
10
+ // 引擎原生状态。读写全同步 fs 且无 await 让出点——单线程事件循环天然不交错,
11
+ // §3.3.9「进程内互斥」由此满足(宿主是唯一写者,无跨进程竞争)。
11
12
  //
12
13
  // 删除边界(D5 三条硬规则):
13
14
  // 1. 只删引擎原生状态(隔离 HOME / config / db.sqlite,均可由 preparer 重建);
14
- // 2. journal-*.jsonl 不随池删——生命周期跟随 record,避免「池删导致仍存 record
15
- // 历史从②级静默跌③级」;
15
+ // 2. journal-*.jsonl 不随池删——生命周期跟随 record,release 只删「该 record 自己的」
16
+ // journal(record GC 时联动,避免「池删导致仍存 record 的历史从②级静默跌③级」);
16
17
  // 3. 删除失败置 .pool-cleanup-failed 标记文件(可观测不静默),启动期扫描该标记告警。
18
+ //
19
+ // TTL 兜底(cleanupExpiredPoolRefs):record 主数据的死亡(主 session 文件被 pi 侧
20
+ // 管理)对 core 无触发点,done record 的 journal 因此没有精确回收锚——按 30 天 mtime
21
+ // 兜底回收(journal/refs 条目),与 session-file-gc 的 session TTL 同时间尺度,是
22
+ // D8 分域口径「journal 依赖 30 天 TTL 自然回收」的落地(workflow 域 taskId 占位无
23
+ // record 生命周期锚,同样依赖此兜底)。
17
24
 
18
- import { mkdir, readdir, rm, rmdir, writeFile } from "node:fs/promises";
25
+ import * as fsSync from "node:fs";
19
26
  import { join } from "node:path";
20
27
 
21
28
  import { getLogger } from "../../../core/logger.ts";
22
29
 
23
- import { resolvePoolDir } from "../paths.ts";
30
+ import { resolveEnginesRoot, resolveJournalPath, resolvePoolDir, sanitizeSeg } from "../paths.ts";
31
+ import { toErrorMessage } from "../../../core/error-message.ts";
24
32
 
25
33
  const logger = getLogger("subagents");
26
34
 
27
35
  /** 清理失败标记文件名(置于池目录内;内容为 JSON 失败清单)。 */
28
36
  export const POOL_CLEANUP_FAILED_MARKER = ".pool-cleanup-failed";
29
37
 
38
+ /** 池引用登记文件名(§3.3.9 目录布局)。 */
39
+ export const REFS_JSON_FILENAME = "refs.json";
40
+
41
+ /** refs.json 的 v1 schema 版本号。 */
42
+ const REFS_VERSION = 1 as const;
43
+
44
+ /** journal 文件名前缀(与 paths.ts 的 journal-<taskId>.jsonl 命名约定一致)。 */
45
+ const JOURNAL_PREFIX = "journal-";
46
+ /** journal 文件名后缀。 */
47
+ const JOURNAL_SUFFIX = ".jsonl";
48
+
49
+ /** refs.json 单条引用登记(§3.3.9 v1 形态)。 */
50
+ interface PoolRefEntry {
51
+ taskId: string;
52
+ ts: number;
53
+ }
54
+
55
+ /** refs.json v1 文件形态。 */
56
+ interface PoolRefsFile {
57
+ v: 1;
58
+ refs: Record<string, PoolRefEntry>;
59
+ }
60
+
30
61
  /** readdir withFileTypes 的条目结构子集。 */
31
62
  interface DirEntryLike {
32
63
  name: string;
@@ -35,37 +66,78 @@ interface DirEntryLike {
35
66
 
36
67
  /** 池管理的文件系统依赖面(结构接口:测试注入 fake,免 vi.mock 整个 fs 模块)。 */
37
68
  export interface PoolFsDeps {
38
- mkdir(path: string, opts: { recursive: boolean }): Promise<unknown>;
39
- readdir(path: string): Promise<DirEntryLike[]>;
40
- rm(path: string, opts: { recursive: boolean; force: boolean }): Promise<void>;
41
- rmdir(path: string): Promise<void>;
42
- writeFile(path: string, data: string): Promise<void>;
69
+ mkdirSync(path: string, opts: { recursive: boolean }): void;
70
+ readdirSync(path: string): DirEntryLike[];
71
+ statSync(path: string): { mtimeMs: number };
72
+ existsSync(path: string): boolean;
73
+ readFileSync(path: string): string;
74
+ writeFileSync(path: string, data: string): void;
75
+ renameSync(from: string, to: string): void;
76
+ rmSync(path: string, opts: { recursive: boolean; force: boolean }): void;
77
+ rmdirSync(path: string): void;
43
78
  }
44
79
 
45
80
  // ============================================================
46
- // 进程内池注册表(引用计数)
81
+ // refs.json 读写(原子写 + 防御读)
47
82
  // ============================================================
48
83
 
84
+ function emptyRefs(): PoolRefsFile {
85
+ return { v: REFS_VERSION, refs: {} };
86
+ }
87
+
49
88
  /**
50
- * key = 池目录绝对路径活跃引用计数。进程级单例状态,用 globalThis[Symbol.for]
51
- * 持有防 jiti 双路径加载分裂(development-guide §7.5),不用模块级 const。
89
+ * 读池引用登记。ENOENT / 损坏 JSON / 非 v1 形态 空登记起步(warn 留痕)——
90
+ * refs 丢失的后果是「池留置」(保守方向),不是误删。
52
91
  */
53
- const POOL_REFS_SLOT_KEY = Symbol.for("@zhushanwen/pi-subagent-workflow.poolRefs");
54
-
55
- function getPoolRefs(): Map<string, number> {
56
- // globalThis 无 symbol 索引签名,但运行时支持 symbol 键——用 Reflect 安全读写,
57
- // 避免双重断言(同 model-config-service.ts 先例)。
58
- let refs = Reflect.get(globalThis, POOL_REFS_SLOT_KEY) as Map<string, number> | undefined;
59
- if (!refs) {
60
- refs = new Map();
61
- Reflect.set(globalThis, POOL_REFS_SLOT_KEY, refs);
92
+ function readPoolRefs(poolDir: string, fs: Pick<PoolFsDeps, "existsSync" | "readFileSync">): PoolRefsFile {
93
+ const refsPath = join(poolDir, REFS_JSON_FILENAME);
94
+ if (!fs.existsSync(refsPath)) return emptyRefs();
95
+ let parsed: unknown;
96
+ try {
97
+ parsed = JSON.parse(fs.readFileSync(refsPath));
98
+ } catch (err) {
99
+ logger.warn(
100
+ `[pool-manager] refs.json unparsable for ${poolDir}, starting from empty refs: ` +
101
+ `${toErrorMessage(err)}`,
102
+ );
103
+ return emptyRefs();
104
+ }
105
+ if (typeof parsed !== "object" || parsed === null) return emptyRefs();
106
+ const obj = parsed as Record<string, unknown>;
107
+ if (obj.v !== REFS_VERSION || typeof obj.refs !== "object" || obj.refs === null) {
108
+ logger.warn(`[pool-manager] refs.json unexpected shape for ${poolDir}, starting from empty refs`);
109
+ return emptyRefs();
110
+ }
111
+ const refs: Record<string, PoolRefEntry> = {};
112
+ for (const [taskId, entry] of Object.entries(obj.refs as Record<string, unknown>)) {
113
+ if (typeof entry !== "object" || entry === null) continue;
114
+ const e = entry as Record<string, unknown>;
115
+ if (typeof e.taskId !== "string" || typeof e.ts !== "number") continue;
116
+ refs[taskId] = { taskId: e.taskId, ts: e.ts };
62
117
  }
63
- return refs;
118
+ return { v: REFS_VERSION, refs };
64
119
  }
65
120
 
66
- /** 测试隔离专用:清空进程内注册表(生产禁用——会丢失活跃引用计数)。 */
67
- export function resetPoolRegistryForTests(): void {
68
- getPoolRefs().clear();
121
+ /** 原子写回 refs.json(tmp + rename)。失败置清理失败标记(§3.3.9),不 throw。 */
122
+ function writePoolRefs(
123
+ poolDir: string,
124
+ file: PoolRefsFile,
125
+ fs: Pick<PoolFsDeps, "writeFileSync" | "renameSync">,
126
+ ): boolean {
127
+ const refsPath = join(poolDir, REFS_JSON_FILENAME);
128
+ const tmpPath = `${refsPath}.tmp.${process.pid}`;
129
+ try {
130
+ fs.writeFileSync(tmpPath, `${JSON.stringify(file)}\n`);
131
+ fs.renameSync(tmpPath, refsPath);
132
+ return true;
133
+ } catch (err) {
134
+ markCleanupFailed(
135
+ poolDir,
136
+ [`(${REFS_JSON_FILENAME} write): ${toErrorMessage(err)}`],
137
+ fs,
138
+ );
139
+ return false;
140
+ }
69
141
  }
70
142
 
71
143
  // ============================================================
@@ -73,63 +145,237 @@ export function resetPoolRegistryForTests(): void {
73
145
  // ============================================================
74
146
 
75
147
  /**
76
- * 获取池目录:mkdir -p(幂等)+ 进程内引用计数 +1,返回池目录绝对路径。
77
- * 路径一律经 paths.ts resolvePoolDir 派生(双端同源,禁自拼)。
148
+ * 获取池目录:mkdir -p(幂等)+ refs.json 登记 taskId(幂等:已存在刷新 ts),
149
+ * 返回池目录绝对路径。路径一律经 paths.ts resolvePoolDir 派生(双端同源,禁自拼)。
150
+ * taskId = 引用计数 key(chat 域 = record.id;与 journal 文件名同源)。
78
151
  */
79
- export async function acquirePool(
152
+ export function acquirePool(
80
153
  dataDir: string,
81
154
  engineId: string,
82
155
  poolKey: string,
83
- fs: Pick<PoolFsDeps, "mkdir"> = nodeFs,
84
- ): Promise<string> {
156
+ taskId: string,
157
+ fs: Pick<PoolFsDeps, "mkdirSync" | "existsSync" | "readFileSync" | "writeFileSync" | "renameSync"> = nodeFs,
158
+ ): string {
85
159
  const poolDir = resolvePoolDir(dataDir, engineId, poolKey);
86
- await fs.mkdir(poolDir, { recursive: true });
87
- const poolRefs = getPoolRefs();
88
- poolRefs.set(poolDir, (poolRefs.get(poolDir) ?? 0) + 1);
160
+ fs.mkdirSync(poolDir, { recursive: true });
161
+ const file = readPoolRefs(poolDir, fs);
162
+ file.refs[taskId] = { taskId, ts: Date.now() };
163
+ // 写失败置标记但池目录已建——run 照常用池(标记可观测, refs 登记下次 acquire 重试)
164
+ writePoolRefs(poolDir, file, fs);
89
165
  return poolDir;
90
166
  }
91
167
 
92
168
  /**
93
- * 释放一次池引用(record GC / 删除时调用):计数递减;归零删整池——只删引擎原生
94
- * 状态,journal-*.jsonl 保留(D5);删除失败置标记文件并 warn,不静默不 throw
95
- * (清理失败不是 record GC 的失败)。
169
+ * 释放一次池引用(record GC / 删除时调用):
170
+ * 1. 删该 taskId 的 journal 文件(journal 生命周期跟随 record,§3.3.9 release 语义
171
+ * ——record 已死则②级数据源无保留意义;无条目也删,record 死亡是充分条件);
172
+ * 2. refs.json 移除该 taskId;移除后归零 → 删池内引擎原生状态(其他 journal 保留);
173
+ * 3. taskId 不在 refs(进程重启前 refs 丢失 / 未 acquire 的释放)→ 保守不删池:
174
+ * 删池决策必须有 refs 归零证据,凭空删除可能误删其他引用方正在使用的池。
175
+ * 删除失败置标记文件并 warn,不静默不 throw(清理失败不是 record GC 的失败)。
96
176
  */
97
- export async function releasePoolRef(
177
+ export function releasePoolRef(
98
178
  dataDir: string,
99
179
  engineId: string,
100
180
  poolKey: string,
181
+ taskId: string,
101
182
  fs: PoolFsDeps = nodeFs,
102
- ): Promise<void> {
183
+ ): void {
103
184
  const poolDir = resolvePoolDir(dataDir, engineId, poolKey);
104
- const poolRefs = getPoolRefs();
105
- const current = poolRefs.get(poolDir);
106
- // 无计数(进程重启后释放 / acquire 的释放)→ 保守不删池:删池决策必须有本进程
107
- // 内的归零证据,凭空删除可能误删其他进程正在使用的池
108
- if (current === undefined || current <= 0) {
109
- poolRefs.delete(poolDir);
110
- logger.debug(`[pool-manager] release without live ref, skip pool deletion: ${poolDir}`);
185
+ removeJournalFile(dataDir, engineId, poolKey, taskId, fs);
186
+
187
+ const file = readPoolRefs(poolDir, fs);
188
+ if (file.refs[taskId] === undefined) {
189
+ logger.debug(`[pool-manager] release without live ref, skip pool deletion: ${poolDir} (${taskId})`);
111
190
  return;
112
191
  }
113
- if (current > 1) {
114
- poolRefs.set(poolDir, current - 1);
192
+ delete file.refs[taskId];
193
+ writePoolRefs(poolDir, file, fs);
194
+ if (Object.keys(file.refs).length === 0) {
195
+ deletePoolNativeState(poolDir, fs);
196
+ }
197
+ }
198
+
199
+ /** 删单个 journal 文件(force 豁免 ENOENT;失败置标记可观测)。 */
200
+ function removeJournalFile(
201
+ dataDir: string,
202
+ engineId: string,
203
+ poolKey: string,
204
+ taskId: string,
205
+ fs: Pick<PoolFsDeps, "rmSync" | "writeFileSync" | "renameSync">,
206
+ ): void {
207
+ const journalPath = resolveJournalPath(dataDir, engineId, poolKey, taskId);
208
+ try {
209
+ fs.rmSync(journalPath, { force: true, recursive: true });
210
+ } catch (err) {
211
+ markCleanupFailed(
212
+ resolvePoolDir(dataDir, engineId, poolKey),
213
+ [`(journal ${taskId}): ${toErrorMessage(err)}`],
214
+ fs,
215
+ );
216
+ }
217
+ }
218
+
219
+ // ============================================================
220
+ // TTL 兜底清理(journal / refs 条目按 30 天 mtime 回收)
221
+ // ============================================================
222
+
223
+ /**
224
+ * 按 TTL 回收全部引擎池的超龄引用:journal mtime 超龄 → 删 journal + 移除 refs 条目;
225
+ * refs 条目 ts 超龄且 journal 不存在 → 移除(孤儿条目);无 refs 对应的超龄 journal
226
+ * (refs 丢失场景)→ 删;归零删池内引擎原生状态(未超龄 journal 保留——目录随之保留)。
227
+ * 语义 = D8 分域口径「journal 依赖 30 天 TTL 自然回收」的落地(record 主数据由 pi 侧
228
+ * 主 session 文件管理,其删除对 core 无触发点,done record 只能靠 mtime 兜底)。
229
+ */
230
+ export function cleanupExpiredPoolRefs(
231
+ dataDir: string,
232
+ ttlMs: number,
233
+ fs: PoolFsDeps = nodeFs,
234
+ now: number = Date.now(),
235
+ ): void {
236
+ const enginesRoot = resolveEnginesRoot(dataDir);
237
+ let engines: DirEntryLike[];
238
+ try {
239
+ engines = fs.readdirSync(enginesRoot);
240
+ } catch {
241
+ return; // engines 根不存在(从未建池)= 无可清理
242
+ }
243
+ for (const engineEntry of engines) {
244
+ if (!engineEntry.isDirectory()) continue;
245
+ const engineDir = join(enginesRoot, engineEntry.name);
246
+ let pools: DirEntryLike[];
247
+ try {
248
+ pools = fs.readdirSync(engineDir);
249
+ } catch {
250
+ continue;
251
+ }
252
+ for (const poolEntry of pools) {
253
+ if (!poolEntry.isDirectory()) continue;
254
+ cleanupPoolByTtl(join(engineDir, poolEntry.name), ttlMs, fs, now);
255
+ }
256
+ }
257
+ }
258
+
259
+ /** 单池 TTL 清理(见 cleanupExpiredPoolRefs 语义分解)。两阶段见下方辅助函数。 */
260
+ function cleanupPoolByTtl(poolDir: string, ttlMs: number, fs: PoolFsDeps, now: number): void {
261
+ const file = readPoolRefs(poolDir, fs);
262
+ const hadRefs = Object.keys(file.refs).length > 0;
263
+
264
+ // 1. refs 条目决策:journal 超龄(或 journal 不在 + 条目 ts 超龄)→ 移除
265
+ let changed = removeExpiredRefEntries(poolDir, file, ttlMs, fs, now);
266
+
267
+ // 2. 无主超龄 journal(无任何 refs 条目对应——refs 丢失/损坏重建场景):按 mtime 删。
268
+ // readdir 失败 → 中止本池清理(阶段 1 的 refs 变更不落盘,下次扫描按原 refs 重算)。
269
+ let entries: DirEntryLike[];
270
+ try {
271
+ entries = fs.readdirSync(poolDir);
272
+ } catch {
115
273
  return;
116
274
  }
117
- // 归零:删引擎原生状态(journal 保留)
118
- poolRefs.delete(poolDir);
119
- await deletePoolNativeState(poolDir, fs);
275
+ if (removeOrphanJournals(poolDir, file, entries, ttlMs, fs, now)) changed = true;
276
+
277
+ if (!changed) return;
278
+ writePoolRefs(poolDir, file, fs);
279
+ if (hadRefs && Object.keys(file.refs).length === 0) {
280
+ deletePoolNativeState(poolDir, fs);
281
+ }
120
282
  }
121
283
 
284
+ /**
285
+ * TTL 阶段 1——refs 条目决策(直接变更 file.refs,按需删对应 journal):
286
+ * journal 超龄 → 删 journal + 移除条目;journal 不在 + 条目 ts 超龄 → 移除(孤儿条目)。
287
+ * 返回 refs 是否有变更。
288
+ */
289
+ function removeExpiredRefEntries(
290
+ poolDir: string,
291
+ file: PoolRefsFile,
292
+ ttlMs: number,
293
+ fs: PoolFsDeps,
294
+ now: number,
295
+ ): boolean {
296
+ let changed = false;
297
+ for (const [taskId, entry] of Object.entries(file.refs)) {
298
+ const journalPath = join(poolDir, `${JOURNAL_PREFIX}${sanitizeSeg(taskId)}${JOURNAL_SUFFIX}`);
299
+ if (fs.existsSync(journalPath)) {
300
+ const age = now - fs.statSync(journalPath).mtimeMs;
301
+ if (age > ttlMs) {
302
+ unlinkBestEffort(journalPath, fs);
303
+ delete file.refs[taskId];
304
+ changed = true;
305
+ }
306
+ } else if (now - entry.ts > ttlMs) {
307
+ delete file.refs[taskId];
308
+ changed = true;
309
+ }
310
+ }
311
+ return changed;
312
+ }
313
+
314
+ /**
315
+ * TTL 阶段 2——无主超龄 journal 回收。ownedSegments 基于阶段 1 变更后的 refs
316
+ * (仍有 refs 对应的 journal 不动)。返回是否有删除。
317
+ */
318
+ function removeOrphanJournals(
319
+ poolDir: string,
320
+ file: PoolRefsFile,
321
+ entries: DirEntryLike[],
322
+ ttlMs: number,
323
+ fs: PoolFsDeps,
324
+ now: number,
325
+ ): boolean {
326
+ let changed = false;
327
+ const ownedSegments = new Set(Object.keys(file.refs).map((taskId) => sanitizeSeg(taskId)));
328
+ for (const entry of entries) {
329
+ if (!isJournalFile(entry.name)) continue;
330
+ const segment = journalTaskSegment(entry.name);
331
+ if (segment !== undefined && ownedSegments.has(segment)) continue;
332
+ const journalPath = join(poolDir, entry.name);
333
+ try {
334
+ if (now - fs.statSync(journalPath).mtimeMs > ttlMs) {
335
+ unlinkBestEffort(journalPath, fs);
336
+ changed = true;
337
+ }
338
+ } catch (err) {
339
+ // stat 失败(并发删除等)跳过该条——TTL 扫描周期性重跑,最终一致
340
+ logger.debug(
341
+ `[pool-manager] ttl cleanup stat failed for ${journalPath}: ` +
342
+ `${toErrorMessage(err)}`,
343
+ );
344
+ }
345
+ }
346
+ return changed;
347
+ }
348
+
349
+ /** journal 文件名 → taskId 段(journal-<seg>.jsonl → seg;形态不符返回 undefined)。 */
350
+ function journalTaskSegment(name: string): string | undefined {
351
+ if (!name.startsWith(JOURNAL_PREFIX) || !name.endsWith(JOURNAL_SUFFIX)) return undefined;
352
+ return name.slice(JOURNAL_PREFIX.length, name.length - JOURNAL_SUFFIX.length);
353
+ }
354
+
355
+ /** unlink 单文件(force 豁免 ENOENT;失败 best-effort 留痕)。 */
356
+ function unlinkBestEffort(path: string, fs: Pick<PoolFsDeps, "rmSync">): void {
357
+ try {
358
+ fs.rmSync(path, { force: true, recursive: true });
359
+ } catch (err) {
360
+ logger.debug(`[pool-manager] ttl cleanup failed for ${path}: ${toErrorMessage(err)}`);
361
+ }
362
+ }
363
+
364
+ // ============================================================
365
+ // 归零删池(引擎原生状态)
366
+ // ============================================================
367
+
122
368
  /** journal 文件名判定(与 paths.ts 的 journal-<taskId>.jsonl 命名约定一致)。 */
123
369
  function isJournalFile(name: string): boolean {
124
- return name.startsWith("journal-") && name.endsWith(".jsonl");
370
+ return name.startsWith(JOURNAL_PREFIX) && name.endsWith(JOURNAL_SUFFIX);
125
371
  }
126
372
 
127
373
  /** 删池内引擎原生状态:逐条目删除(跳过 journal),目录清空后移除目录本身。 */
128
- async function deletePoolNativeState(poolDir: string, fs: PoolFsDeps): Promise<void> {
374
+ function deletePoolNativeState(poolDir: string, fs: PoolFsDeps): void {
129
375
  const failures: string[] = [];
130
376
  let entries: DirEntryLike[];
131
377
  try {
132
- entries = await fs.readdir(poolDir);
378
+ entries = fs.readdirSync(poolDir);
133
379
  } catch {
134
380
  // 池目录不存在(从未创建 / 已删)= 无原生状态可清,视为成功
135
381
  return;
@@ -137,31 +383,35 @@ async function deletePoolNativeState(poolDir: string, fs: PoolFsDeps): Promise<v
137
383
  for (const entry of entries) {
138
384
  if (isJournalFile(entry.name)) continue;
139
385
  try {
140
- await fs.rm(join(poolDir, entry.name), { recursive: true, force: true });
386
+ fs.rmSync(join(poolDir, entry.name), { recursive: true, force: true });
141
387
  } catch (err) {
142
- failures.push(`${entry.name}: ${err instanceof Error ? err.message : String(err)}`);
388
+ failures.push(`${entry.name}: ${toErrorMessage(err)}`);
143
389
  }
144
390
  }
145
391
  if (failures.length > 0) {
146
- await markCleanupFailed(poolDir, failures, fs);
392
+ markCleanupFailed(poolDir, failures, fs);
147
393
  return;
148
394
  }
149
395
  try {
150
396
  // 目录内只剩 journal(或已空):只剩 journal 时保留目录(journal 生命周期跟 record)
151
- const remaining = await fs.readdir(poolDir);
397
+ const remaining = fs.readdirSync(poolDir);
152
398
  if (remaining.every((e) => isJournalFile(e.name))) return;
153
- await fs.rmdir(poolDir);
399
+ fs.rmdirSync(poolDir);
154
400
  } catch (err) {
155
- await markCleanupFailed(
401
+ markCleanupFailed(
156
402
  poolDir,
157
- [`(rmdir): ${err instanceof Error ? err.message : String(err)}`],
403
+ [`(rmdir): ${toErrorMessage(err)}`],
158
404
  fs,
159
405
  );
160
406
  }
161
407
  }
162
408
 
163
409
  /** 置 .pool-cleanup-failed 标记(失败清单 JSON)+ warn——可观测不静默(D5)。 */
164
- async function markCleanupFailed(poolDir: string, failures: string[], fs: PoolFsDeps): Promise<void> {
410
+ function markCleanupFailed(
411
+ poolDir: string,
412
+ failures: string[],
413
+ fs: Pick<PoolFsDeps, "writeFileSync">,
414
+ ): void {
165
415
  const marker = join(poolDir, POOL_CLEANUP_FAILED_MARKER);
166
416
  const payload = JSON.stringify({ ts: Date.now(), failures });
167
417
  logger.warn(
@@ -169,12 +419,12 @@ async function markCleanupFailed(poolDir: string, failures: string[], fs: PoolFs
169
419
  `marker written to ${marker} — re-run cleanup after fixing the underlying error`,
170
420
  );
171
421
  try {
172
- await fs.writeFile(marker, `${payload}\n`);
422
+ fs.writeFileSync(marker, `${payload}\n`);
173
423
  } catch (err) {
174
424
  // 标记本身也写不进(目录只读/磁盘满)——error 级留痕是最后防线,不再上抛
175
425
  logger.warn(
176
426
  `[pool-manager] failed to write cleanup-failed marker ${marker}: ` +
177
- `${err instanceof Error ? err.message : String(err)}`,
427
+ `${toErrorMessage(err)}`,
178
428
  );
179
429
  }
180
430
  }
@@ -197,19 +447,19 @@ export interface SpawnedFilesCleanupOpts {
197
447
  * 任务结束即清理(D5);单条失败收集 warn 不 throw(部分清理优于整体失败),ENOENT
198
448
  * 幂等忽略。
199
449
  */
200
- export async function cleanupSpawnedFiles(
450
+ export function cleanupSpawnedFiles(
201
451
  paths: string[],
202
452
  opts: SpawnedFilesCleanupOpts,
203
- fs: Pick<PoolFsDeps, "rm"> = nodeFs,
204
- ): Promise<void> {
453
+ fs: Pick<PoolFsDeps, "rmSync"> = nodeFs,
454
+ ): void {
205
455
  if (opts.keepForResume) return;
206
456
  for (const p of paths) {
207
457
  try {
208
- await fs.rm(p, { recursive: true, force: true });
458
+ fs.rmSync(p, { recursive: true, force: true });
209
459
  } catch (err) {
210
460
  logger.warn(
211
461
  `[pool-manager] spawned file cleanup failed for ${p}: ` +
212
- `${err instanceof Error ? err.message : String(err)}`,
462
+ `${toErrorMessage(err)}`,
213
463
  );
214
464
  }
215
465
  }
@@ -218,9 +468,13 @@ export async function cleanupSpawnedFiles(
218
468
  // ── 默认 fs 实现 ────────────────────────────────────────────────
219
469
 
220
470
  const nodeFs: PoolFsDeps = {
221
- mkdir: (p, o) => mkdir(p, o),
222
- readdir: (p) => readdir(p, { withFileTypes: true }) as Promise<DirEntryLike[]>,
223
- rm: (p, o) => rm(p, o),
224
- rmdir: (p) => rmdir(p),
225
- writeFile: (p, d) => writeFile(p, d, "utf8"),
471
+ mkdirSync: (p, o) => fsSync.mkdirSync(p, o),
472
+ readdirSync: (p) => fsSync.readdirSync(p, { withFileTypes: true }),
473
+ statSync: (p) => fsSync.statSync(p),
474
+ existsSync: (p) => fsSync.existsSync(p),
475
+ readFileSync: (p) => fsSync.readFileSync(p, "utf8"),
476
+ writeFileSync: (p, d) => fsSync.writeFileSync(p, d, "utf8"),
477
+ renameSync: (from, to) => fsSync.renameSync(from, to),
478
+ rmSync: (p, o) => fsSync.rmSync(p, o),
479
+ rmdirSync: (p) => fsSync.rmdirSync(p),
226
480
  };