@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.
- package/dist/{chunk-4KN4TTG7.js → chunk-43ONBFZX.js} +1 -1
- package/dist/{chunk-X7SZ5HLQ.js → chunk-V3VHZ2VX.js} +20 -27
- package/dist/execution/engine/engines/zcode/constants.cjs +29 -41
- package/dist/execution/engine/engines/zcode/constants.d.cts +65 -53
- package/dist/execution/engine/engines/zcode/constants.d.ts +65 -53
- package/dist/execution/engine/engines/zcode/constants.js +17 -27
- package/dist/execution/engine/engines/zcode/reader.d.cts +2 -1
- package/dist/execution/engine/engines/zcode/reader.d.ts +2 -1
- package/dist/execution/engine/engines/zcode/reader.js +2 -2
- package/dist/index.cjs +17100 -14082
- package/dist/index.d.cts +5336 -4033
- package/dist/index.d.ts +5336 -4033
- package/dist/index.js +17020 -14055
- package/dist/{types-DpUO16pj.d.cts → types-C3fE3R9x.d.cts} +280 -112
- package/dist/{types-DpUO16pj.d.ts → types-C3fE3R9x.d.ts} +280 -112
- package/dist.bundle/index.cjs +34933 -0
- package/package.json +3 -3
- package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +0 -1
- package/src/__tests__/append-system-prompt-assembly.test.ts +8 -7
- package/src/__tests__/fr4-get-state-handshake.test.ts +2 -2
- package/src/__tests__/m2-append-content-probe.test.ts +0 -1
- package/src/__tests__/manifest-store.test.ts +10 -9
- package/src/__tests__/record-store-cache.test.ts +0 -2
- package/src/__tests__/record-store-index.test.ts +1 -2
- package/src/__tests__/review-fix-loop-script.test.ts +4 -2
- package/src/__tests__/review-fix-loop-utils.test.ts +10 -10
- package/src/__tests__/robustness-low-batch1.test.ts +6 -5
- package/src/__tests__/robustness-medium-batch1.test.ts +2 -2
- package/src/__tests__/robustness-medium-batch2.test.ts +17 -6
- package/src/__tests__/robustness-medium-batch4.test.ts +2 -2
- package/src/__tests__/session-runner.test.ts +1 -1
- package/src/core/__tests__/host-services.test.ts +2 -2
- package/src/core/__tests__/logger.test.ts +1 -1
- package/src/core/error-message.ts +9 -0
- package/src/core/host-services.ts +21 -5
- package/src/core/notify-ports.ts +18 -5
- package/src/execution/__tests__/agent-profile.test.ts +4 -4
- package/src/execution/__tests__/agent-registry.test.ts +370 -0
- package/src/execution/__tests__/agent-result-mapper.test.ts +18 -2
- package/src/execution/__tests__/agents-assembly.test.ts +2 -2
- package/src/execution/__tests__/alive-store.test.ts +1 -1
- package/src/execution/__tests__/ask-user-transit-e2e.test.ts +6 -6
- package/src/execution/__tests__/chat-engine-routing.test.ts +68 -33
- package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +4 -4
- package/src/execution/__tests__/cold-resurrect.test.ts +283 -0
- package/src/execution/__tests__/collect-budget.test.ts +291 -0
- package/src/execution/__tests__/collect-coordinator-service.test.ts +327 -0
- package/src/execution/__tests__/collect-coordinator.test.ts +371 -0
- package/src/execution/__tests__/collect-mixed-dispatch.test.ts +262 -0
- package/src/execution/__tests__/config-collect-sync.test.ts +120 -0
- package/src/execution/__tests__/config.test.ts +1 -1
- package/src/execution/__tests__/delivery-methods.test.ts +75 -62
- package/src/execution/__tests__/descendant-sweep-guards.test.ts +220 -0
- package/src/execution/__tests__/descendant-sweep.test.ts +6 -6
- package/src/execution/__tests__/dialog-queue.test.ts +2 -2
- package/src/execution/__tests__/engine-model-validation.test.ts +741 -0
- package/src/execution/__tests__/epipe-fallback.test.ts +29 -24
- package/src/execution/__tests__/execute-and-await-worktree.test.ts +21 -91
- package/src/execution/__tests__/execute-nesting.test.ts +34 -188
- package/src/execution/__tests__/execution-record.test.ts +217 -60
- package/src/execution/__tests__/execution-runtime-face.test.ts +5 -5
- package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +4 -4
- package/src/execution/__tests__/finalize-record.test.ts +173 -17
- package/src/execution/__tests__/finalized-marker.test.ts +1 -1
- package/src/execution/__tests__/gc-timer.test.ts +56 -3
- package/src/execution/__tests__/get-record-for-action-restart.test.ts +12 -12
- package/src/execution/__tests__/get-state-handshake.test.ts +1 -1
- package/src/execution/__tests__/helpers/mock-extension-api.ts +1 -1
- package/src/execution/__tests__/helpers/session-runner-mocks.ts +113 -0
- package/src/execution/__tests__/helpers/spawn-mock.ts +150 -3
- package/src/execution/__tests__/helpers/subagent-service-mocks.ts +137 -0
- package/src/execution/__tests__/keep-alive-no-progress.test.ts +50 -148
- package/src/execution/__tests__/kill-all-escalation.test.ts +2 -2
- package/src/execution/__tests__/lifecycle-predicates.test.ts +1 -2
- package/src/execution/__tests__/manifest-parentid.test.ts +4 -4
- package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +1 -1
- package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +1 -1
- package/src/execution/__tests__/model-resolver.test.ts +2 -2
- package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +23 -121
- package/src/execution/__tests__/nested-visibility.test.ts +2 -0
- package/src/execution/__tests__/notify-batch.test.ts +500 -0
- package/src/execution/__tests__/notify-ledger.test.ts +1000 -18
- package/src/execution/__tests__/output-collector.test.ts +117 -6
- package/src/execution/__tests__/pi-invocation.test.ts +36 -4
- package/src/execution/__tests__/record-entry-collect.test.ts +102 -0
- package/src/execution/__tests__/record-store.test.ts +10 -9
- package/src/execution/__tests__/records-cwd-isolation.test.ts +2 -2
- package/src/execution/__tests__/recursive-visibility-baseline.test.ts +29 -132
- package/src/execution/__tests__/recursive-visibility-env.test.ts +55 -179
- package/src/execution/__tests__/relay-env.test.ts +12 -1
- package/src/execution/__tests__/rpc-mode.test.ts +2 -2
- package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +387 -0
- package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +15 -9
- package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +4 -4
- package/src/execution/__tests__/run-spawn-edges.test.ts +17 -15
- package/src/execution/__tests__/run-spawn-integration.test.ts +2 -2
- package/src/execution/__tests__/run-spawn-resume.test.ts +2 -2
- package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +2 -2
- package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +4 -4
- package/src/execution/__tests__/service-kill-escalation.test.ts +7 -5
- package/src/execution/__tests__/session-context-resolver.test.ts +0 -2
- package/src/execution/__tests__/session-file-gc.test.ts +36 -1
- package/src/execution/__tests__/session-pending.test.ts +257 -14
- package/src/execution/__tests__/session-reconstructor.test.ts +366 -15
- package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +1 -1
- package/src/execution/__tests__/session-runner-close-prune.test.ts +2 -2
- package/src/execution/__tests__/session-runner-dispatch.test.ts +489 -0
- package/src/execution/__tests__/session-runner-epipe.test.ts +3 -3
- package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +5 -4
- package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +290 -0
- package/src/execution/__tests__/session-runner-schema-env.test.ts +7 -5
- package/src/execution/__tests__/settled-watchdog.test.ts +282 -47
- package/src/execution/__tests__/spawn-args.test.ts +11 -10
- package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +26 -23
- package/src/execution/__tests__/spawn-event-adapter.test.ts +3 -3
- package/src/execution/__tests__/spawn-worktree-guidance.test.ts +5 -4
- package/src/execution/__tests__/spawned-children.test.ts +2 -1
- package/src/execution/__tests__/start-collect-guard.test.ts +255 -0
- package/src/execution/__tests__/start-sync-model-guard.test.ts +5 -5
- package/src/execution/__tests__/stdin-writer.test.ts +6 -4
- package/src/execution/__tests__/stream-sink-retirement.test.ts +6 -6
- package/src/execution/__tests__/subagent-actions-core.test.ts +48 -22
- package/src/execution/__tests__/subagent-service-message-close.test.ts +29 -26
- package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +21 -8
- package/src/execution/__tests__/subagent-service-notify-gate.test.ts +27 -22
- package/src/execution/__tests__/subagent-service-parent-guard.test.ts +30 -29
- package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +45 -20
- package/src/execution/__tests__/subagent-service.test.ts +37 -30
- package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +88 -12
- package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +75 -0
- package/src/execution/__tests__/subprocess-agent-runner.test.ts +22 -20
- package/src/execution/__tests__/sync-collect-recovery.test.ts +1117 -0
- package/src/execution/__tests__/temp-prompt.test.ts +1 -1
- package/src/execution/__tests__/timeout-integration.test.ts +6 -5
- package/src/execution/__tests__/tombstone-store.test.ts +1 -1
- package/src/execution/__tests__/turn-limiter-semantics.test.ts +1 -1
- package/src/execution/__tests__/turn-limiter.test.ts +1 -1
- package/src/execution/__tests__/ui-request-handler-factory.test.ts +12 -6
- package/src/execution/__tests__/ui-request-handler.test.ts +4 -3
- package/src/execution/__tests__/ui-request-queue.test.ts +142 -2
- package/src/execution/__tests__/worktree-git-ops.test.ts +1 -1
- package/src/execution/__tests__/worktree-manager.test.ts +14 -5
- package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +4 -3
- package/src/execution/__tests__/worktree-reconcile-aging.test.ts +2 -2
- package/src/execution/__tests__/worktree-reconcile.integration.test.ts +1 -1
- package/src/execution/__tests__/worktree-registry.test.ts +1 -1
- package/src/execution/agent-registry.ts +74 -30
- package/src/execution/agent-result-mapper.ts +3 -0
- package/src/execution/cold-resurrect.ts +199 -0
- package/src/execution/collect-coordinator.ts +200 -0
- package/src/execution/config.ts +77 -5
- package/src/execution/engine/__tests__/common/capability-gate.test.ts +91 -0
- package/src/execution/engine/__tests__/common/data-dir.test.ts +1 -1
- package/src/execution/engine/__tests__/common/event-journal.test.ts +1 -1
- package/src/execution/engine/__tests__/common/journal-wiring.test.ts +84 -0
- package/src/execution/engine/__tests__/common/kill-chain.test.ts +34 -7
- package/src/execution/engine/__tests__/common/persona-router.test.ts +4 -5
- package/src/execution/engine/__tests__/common/pool-manager.test.ts +213 -53
- package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +149 -0
- package/src/execution/engine/__tests__/common/session-view-service.test.ts +499 -0
- package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +22 -112
- package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +15 -28
- package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +1 -1
- package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +12 -12
- package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +2 -2
- package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +21 -82
- package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +4 -7
- package/src/execution/engine/__tests__/engine-discovery.test.ts +15 -3
- package/src/execution/engine/__tests__/model-prompt.test.ts +15 -3
- package/src/execution/engine/__tests__/registry.test.ts +4 -2
- package/src/execution/engine/__tests__/routing.test.ts +115 -6
- package/src/execution/engine/common/capability-gate.ts +92 -0
- package/src/execution/engine/common/journal-wiring.ts +97 -0
- package/src/execution/engine/common/kill-chain.ts +42 -10
- package/src/execution/engine/common/nesting-guard.ts +66 -0
- package/src/execution/engine/common/persona-router.ts +18 -8
- package/src/execution/engine/common/pool-manager.ts +332 -78
- package/src/execution/engine/common/session-view-service.ts +506 -0
- package/src/execution/engine/common/session-view-types.ts +125 -0
- package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +174 -28
- package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +257 -0
- package/src/execution/{argv-mirror.ts → engine/engines/pi/argv-mirror.ts} +43 -21
- package/src/execution/{get-state-handshake.ts → engine/engines/pi/get-state-handshake.ts} +1 -1
- package/src/execution/{output-collector.ts → engine/engines/pi/output-collector.ts} +108 -19
- package/src/execution/engine/engines/pi/pi-engine.ts +434 -38
- package/src/execution/{pi-invocation.ts → engine/engines/pi/pi-invocation.ts} +2 -2
- package/src/execution/engine/engines/pi/reader.ts +11 -0
- package/src/execution/engine/engines/pi/registration.ts +4 -2
- package/src/execution/{session-runner.ts → engine/engines/pi/session-runner.ts} +617 -412
- package/src/execution/{spawn-event-adapter.ts → engine/engines/pi/spawn-event-adapter.ts} +75 -34
- package/src/execution/{stdin-writer.ts → engine/engines/pi/stdin-writer.ts} +10 -9
- package/src/execution/{temp-prompt.ts → engine/engines/pi/temp-prompt.ts} +2 -2
- package/src/execution/{turn-limiter.ts → engine/engines/pi/turn-limiter.ts} +1 -1
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +54 -27
- package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +321 -0
- package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +13 -12
- package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +7 -210
- package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +6 -110
- package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +247 -0
- package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +451 -0
- package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +20 -8
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +77 -110
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +26 -320
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +210 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +353 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +282 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +372 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +48 -148
- package/src/execution/engine/engines/zcode/appserver-launcher.ts +167 -0
- package/src/execution/engine/engines/zcode/connection.ts +24 -11
- package/src/execution/engine/engines/zcode/constants.ts +75 -71
- package/src/execution/engine/engines/zcode/golden-sample.ts +8 -46
- package/src/execution/engine/engines/zcode/parser.ts +12 -346
- package/src/execution/engine/engines/zcode/preparer.ts +12 -143
- package/src/execution/engine/engines/zcode/session-channel.ts +351 -39
- package/src/execution/engine/engines/zcode/zcode-engine.ts +665 -760
- package/src/execution/engine/host-task-spec.ts +32 -36
- package/src/execution/engine/model-validation.ts +177 -0
- package/src/execution/engine/port.ts +24 -6
- package/src/execution/engine/routing.ts +75 -1
- package/src/execution/engine/types.ts +30 -103
- package/src/execution/execution-record.ts +229 -144
- package/src/execution/finalize-record.ts +141 -99
- package/src/execution/idle-gc.ts +28 -0
- package/src/execution/lifecycle-predicates.ts +1 -1
- package/src/execution/notifier.ts +234 -25
- package/src/execution/notify-host.ts +232 -0
- package/src/execution/record-entry.ts +16 -0
- package/src/execution/record-store.ts +280 -103
- package/src/execution/round-settlement.ts +93 -0
- package/src/execution/session-file-gc.ts +96 -62
- package/src/execution/session-reconstructor.ts +256 -143
- package/src/execution/sessions-index.ts +117 -47
- package/src/execution/settled-watchdog.ts +264 -52
- package/src/execution/subagent-actions-core.ts +84 -28
- package/src/execution/subagent-service.ts +1225 -964
- package/src/execution/subprocess-agent-runner.ts +144 -97
- package/src/execution/sync-rebuild.ts +87 -0
- package/src/execution/types.ts +72 -0
- package/src/execution/ui-request-handler-factory.ts +102 -74
- package/src/execution/ui-request-queue.ts +50 -19
- package/src/index.ts +442 -316
- package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +1 -1
- package/src/orchestration/__tests__/agent-call-stream.test.ts +2 -2
- package/src/orchestration/__tests__/args-validator.test.ts +14 -0
- package/src/orchestration/__tests__/config-loader.test.ts +1 -1
- package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +1 -1
- package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +11 -9
- package/src/orchestration/__tests__/execute-agent-call.test.ts +151 -11
- package/src/orchestration/__tests__/file-run-store-prune.test.ts +1 -1
- package/src/orchestration/__tests__/file-run-store-throttle.test.ts +1 -1
- package/src/orchestration/__tests__/file-run-store.test.ts +7 -6
- package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +2 -2
- package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +1 -1
- package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +3 -3
- package/src/orchestration/__tests__/lifecycle.test.ts +7 -5
- package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +1 -1
- package/src/orchestration/__tests__/review-fix-loop-scriptpath-failfast.test.ts +1 -1
- package/src/orchestration/__tests__/run-snapshot.test.ts +8 -5
- package/src/orchestration/__tests__/script-generate.test.ts +5 -5
- package/src/orchestration/__tests__/script-lint.test.ts +17 -0
- package/src/orchestration/__tests__/skill-discovery.test.ts +2 -2
- package/src/orchestration/__tests__/test-mocks.ts +2 -0
- package/src/orchestration/__tests__/worker-exit-without-result.test.ts +7 -6
- package/src/orchestration/__tests__/worker-message-pump-finalize-run.test.ts +229 -0
- package/src/orchestration/__tests__/{error-recovery-handlers.test.ts → worker-message-pump-handlers.test.ts} +27 -19
- package/src/orchestration/__tests__/{error-recovery-postmessage-defense.test.ts → worker-message-pump-postmessage-defense.test.ts} +8 -8
- package/src/orchestration/__tests__/{error-recovery-serialize-failed-result.test.ts → worker-message-pump-serialize-failed-result.test.ts} +2 -2
- package/src/orchestration/__tests__/{error-recovery-workflow-call.test.ts → worker-message-pump-workflow-call.test.ts} +1 -1
- package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +1 -1
- package/src/orchestration/__tests__/worker-script-builder.test.ts +1 -1
- package/src/orchestration/__tests__/workflow-files.test.ts +10 -10
- package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +1 -1
- package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +1 -1
- package/src/orchestration/agent-opts-resolver.ts +1 -1
- package/src/orchestration/args-validator.ts +53 -31
- package/src/orchestration/execute-agent-call.ts +19 -73
- package/src/orchestration/launcher.ts +128 -81
- package/src/orchestration/lifecycle.ts +43 -45
- package/src/orchestration/models/ports.ts +4 -4
- package/src/orchestration/models/run-runtime.ts +2 -2
- package/src/orchestration/models/trace.ts +1 -1
- package/src/orchestration/models/types.ts +111 -22
- package/src/orchestration/models/workflow-run.ts +1 -1
- package/src/orchestration/script-lint.ts +91 -58
- package/src/orchestration/{error-recovery.ts → worker-message-pump.ts} +157 -150
- package/src/shared/__tests__/atomic-write.test.ts +1 -1
- package/src/shared/__tests__/meta-parser.test.ts +151 -0
- package/src/shared/__tests__/resource-discovery-host-roots.test.ts +5 -5
- package/src/shared/__tests__/resource-discovery.test.ts +258 -215
- package/src/shared/atomic-write.ts +12 -9
- package/src/shared/meta-parser.ts +158 -84
- package/src/shared/model-ref.ts +15 -2
- package/src/shared/resource-discovery.ts +9 -183
- package/src/shared/schema-jsonify.ts +1 -1
- package/workflows/review-fix-loop-utils.cjs +151 -89
- package/workflows/review-fix-loop-utils.d.cts +287 -0
- package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
- package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
- package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
- package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
- package/src/execution/engine/engines/zcode/__tests__/preparer-appserver.test.ts +0 -387
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -710
- package/src/execution/engine/engines/zcode/appserver-home.ts +0 -442
- package/src/execution/engine/engines/zcode/appserver-probe.ts +0 -141
- package/src/execution/engine/engines/zcode/launcher.ts +0 -161
- package/src/execution/execute-options-mapper.ts +0 -115
- 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
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
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
|
|
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
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
*
|
|
51
|
-
*
|
|
89
|
+
* 读池引用登记。ENOENT / 损坏 JSON / 非 v1 形态 → 空登记起步(warn 留痕)——
|
|
90
|
+
* refs 丢失的后果是「池留置」(保守方向),不是误删。
|
|
52
91
|
*/
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
68
|
-
|
|
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(幂等)+
|
|
77
|
-
*
|
|
148
|
+
* 获取池目录:mkdir -p(幂等)+ refs.json 登记 taskId(幂等:已存在刷新 ts),
|
|
149
|
+
* 返回池目录绝对路径。路径一律经 paths.ts resolvePoolDir 派生(双端同源,禁自拼)。
|
|
150
|
+
* taskId = 引用计数 key(chat 域 = record.id;与 journal 文件名同源)。
|
|
78
151
|
*/
|
|
79
|
-
export
|
|
152
|
+
export function acquirePool(
|
|
80
153
|
dataDir: string,
|
|
81
154
|
engineId: string,
|
|
82
155
|
poolKey: string,
|
|
83
|
-
|
|
84
|
-
|
|
156
|
+
taskId: string,
|
|
157
|
+
fs: Pick<PoolFsDeps, "mkdirSync" | "existsSync" | "readFileSync" | "writeFileSync" | "renameSync"> = nodeFs,
|
|
158
|
+
): string {
|
|
85
159
|
const poolDir = resolvePoolDir(dataDir, engineId, poolKey);
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
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
|
-
*
|
|
95
|
-
*
|
|
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
|
|
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
|
-
):
|
|
183
|
+
): void {
|
|
103
184
|
const poolDir = resolvePoolDir(dataDir, engineId, poolKey);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
114
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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(
|
|
370
|
+
return name.startsWith(JOURNAL_PREFIX) && name.endsWith(JOURNAL_SUFFIX);
|
|
125
371
|
}
|
|
126
372
|
|
|
127
373
|
/** 删池内引擎原生状态:逐条目删除(跳过 journal),目录清空后移除目录本身。 */
|
|
128
|
-
|
|
374
|
+
function deletePoolNativeState(poolDir: string, fs: PoolFsDeps): void {
|
|
129
375
|
const failures: string[] = [];
|
|
130
376
|
let entries: DirEntryLike[];
|
|
131
377
|
try {
|
|
132
|
-
entries =
|
|
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
|
-
|
|
386
|
+
fs.rmSync(join(poolDir, entry.name), { recursive: true, force: true });
|
|
141
387
|
} catch (err) {
|
|
142
|
-
failures.push(`${entry.name}: ${
|
|
388
|
+
failures.push(`${entry.name}: ${toErrorMessage(err)}`);
|
|
143
389
|
}
|
|
144
390
|
}
|
|
145
391
|
if (failures.length > 0) {
|
|
146
|
-
|
|
392
|
+
markCleanupFailed(poolDir, failures, fs);
|
|
147
393
|
return;
|
|
148
394
|
}
|
|
149
395
|
try {
|
|
150
396
|
// 目录内只剩 journal(或已空):只剩 journal 时保留目录(journal 生命周期跟 record)
|
|
151
|
-
const remaining =
|
|
397
|
+
const remaining = fs.readdirSync(poolDir);
|
|
152
398
|
if (remaining.every((e) => isJournalFile(e.name))) return;
|
|
153
|
-
|
|
399
|
+
fs.rmdirSync(poolDir);
|
|
154
400
|
} catch (err) {
|
|
155
|
-
|
|
401
|
+
markCleanupFailed(
|
|
156
402
|
poolDir,
|
|
157
|
-
[`(rmdir): ${
|
|
403
|
+
[`(rmdir): ${toErrorMessage(err)}`],
|
|
158
404
|
fs,
|
|
159
405
|
);
|
|
160
406
|
}
|
|
161
407
|
}
|
|
162
408
|
|
|
163
409
|
/** 置 .pool-cleanup-failed 标记(失败清单 JSON)+ warn——可观测不静默(D5)。 */
|
|
164
|
-
|
|
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
|
-
|
|
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
|
-
`${
|
|
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
|
|
450
|
+
export function cleanupSpawnedFiles(
|
|
201
451
|
paths: string[],
|
|
202
452
|
opts: SpawnedFilesCleanupOpts,
|
|
203
|
-
fs: Pick<PoolFsDeps, "
|
|
204
|
-
):
|
|
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
|
-
|
|
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
|
-
`${
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
};
|