@zhushanwen/subagent-core 0.5.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/chunk-KXVBIVOU.js +16 -0
- package/dist/chunk-T2SYBW3I.js +24 -0
- package/dist/chunk-VURUAGMM.js +2305 -0
- package/dist/{types-Dv4QhSJ_.d.cts → engine-discovery-scan-ocpM8FmI.d.cts} +835 -668
- package/dist/{types-Dv4QhSJ_.d.ts → engine-discovery-scan-ocpM8FmI.d.ts} +835 -668
- package/dist/execution/engine/engine-discovery-scan.cjs +2210 -0
- package/dist/execution/engine/engine-discovery-scan.d.cts +4 -0
- package/dist/execution/engine/engine-discovery-scan.d.ts +4 -0
- package/dist/execution/engine/engine-discovery-scan.js +20 -0
- package/dist/execution/engine/paths.cjs +6 -23
- package/dist/execution/engine/paths.d.cts +1 -8
- package/dist/execution/engine/paths.d.ts +1 -8
- package/dist/execution/engine/paths.js +1 -1
- package/dist/execution/relay-env.cjs +12 -28
- package/dist/execution/relay-env.d.cts +1 -33
- package/dist/execution/relay-env.d.ts +1 -33
- package/dist/execution/relay-env.js +5 -3
- package/dist/index.cjs +14123 -15467
- package/dist/index.d.cts +1179 -1540
- package/dist/index.d.ts +1179 -1540
- package/dist/index.js +7694 -10986
- package/dist.bundle/index.cjs +33485 -0
- package/package.json +20 -26
- package/src/__tests__/record-store-last-line.test.ts +16 -7
- package/src/core/error-message.ts +7 -6
- package/src/core/host-services.ts +8 -2
- package/src/core/notify-ports.ts +17 -2
- package/src/execution/__tests__/agent-registry.test.ts +1 -1
- package/src/execution/__tests__/alive-store.test.ts +1 -1
- package/src/execution/__tests__/chat-engine-routing.test.ts +62 -28
- package/src/execution/__tests__/chat-round-first-round-watchdog.test.ts +255 -0
- package/src/execution/__tests__/collect-budget.test.ts +291 -0
- package/src/execution/__tests__/collect-coordinator-service.test.ts +316 -0
- package/src/execution/__tests__/collect-coordinator.test.ts +371 -0
- package/src/execution/__tests__/collect-mixed-dispatch.test.ts +229 -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 +179 -237
- package/src/execution/__tests__/execute-and-await-worktree.test.ts +0 -2
- package/src/execution/__tests__/execute-nesting.test.ts +20 -13
- package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +21 -34
- package/src/execution/__tests__/finalized-marker.test.ts +1 -1
- package/src/execution/__tests__/gc-timer.test.ts +8 -23
- package/src/execution/__tests__/get-record-for-action-restart.test.ts +20 -10
- package/src/execution/__tests__/helpers/fake-engine-port.ts +187 -0
- package/src/execution/__tests__/helpers/spawn-mock.ts +36 -9
- package/src/execution/__tests__/helpers/subagent-service-mocks.ts +1 -1
- package/src/execution/__tests__/idle-gc.test.ts +200 -0
- package/src/execution/__tests__/lifecycle-predicates.test.ts +26 -21
- package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +1 -1
- package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +5 -2
- package/src/execution/__tests__/notify-batch.test.ts +500 -0
- package/src/execution/__tests__/notify-ledger.test.ts +27 -0
- package/src/execution/__tests__/reconcile-sweep.test.ts +200 -0
- package/src/execution/__tests__/record-entry-collect.test.ts +102 -0
- package/src/execution/__tests__/record-store.test.ts +60 -6
- package/src/execution/__tests__/recursive-visibility-baseline.test.ts +20 -13
- package/src/execution/__tests__/relay-env.test.ts +12 -1
- package/src/execution/__tests__/round-supervisor.test.ts +293 -0
- package/src/execution/__tests__/session-file-gc.test.ts +1 -1
- package/src/execution/__tests__/session-pending.test.ts +119 -17
- package/src/execution/__tests__/start-collect-guard.test.ts +255 -0
- package/src/execution/__tests__/stream-sink-retirement.test.ts +10 -10
- package/src/execution/__tests__/subagent-actions-core.test.ts +6 -0
- package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +14 -11
- package/src/execution/__tests__/subagent-service-notify-gate.test.ts +33 -30
- package/src/execution/__tests__/subagent-service-parent-guard.test.ts +2 -9
- package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +59 -129
- package/src/execution/__tests__/subagent-service.test.ts +2 -2
- package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +109 -7
- package/src/execution/__tests__/subprocess-agent-runner.test.ts +108 -36
- package/src/execution/__tests__/sync-collect-recovery.test.ts +1102 -0
- package/src/execution/__tests__/tombstone-store.test.ts +1 -1
- package/src/execution/__tests__/workflow-state-root.test.ts +232 -0
- package/src/execution/__tests__/worktree-git-ops.test.ts +1 -1
- 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 +6 -2
- package/src/execution/collect-coordinator.ts +200 -0
- package/src/execution/config.ts +77 -5
- package/src/execution/dialog-queue.ts +6 -73
- package/src/execution/engine/__tests__/common/capability-gate.test.ts +115 -15
- package/src/execution/engine/__tests__/common/errors.test.ts +11 -18
- package/src/execution/engine/__tests__/common/event-journal.test.ts +1 -1
- package/src/execution/engine/__tests__/common/kill-chain.test.ts +7 -0
- package/src/execution/engine/__tests__/common/nesting-guard.test.ts +9 -3
- package/src/execution/engine/__tests__/common/pool-manager.test.ts +2 -2
- package/src/execution/engine/__tests__/common/session-view-service.test.ts +90 -3
- package/src/execution/engine/__tests__/conformance/H9-test-disposition.md +116 -0
- package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/fake-engine-protocol.mjs +377 -0
- package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/recorded.fixture.json +331 -0
- package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/smoke-run.fixture.json +213 -0
- package/src/execution/engine/__tests__/conformance/chat-round-protocol.test.ts +230 -0
- package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +69 -42
- package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +4 -81
- package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +67 -44
- package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +82 -55
- package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +133 -70
- package/src/execution/engine/__tests__/conformance/engine-crash.test.ts +143 -0
- package/src/execution/engine/__tests__/conformance/fake-engine-capabilities.ts +19 -0
- package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +8 -3
- package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +6 -5
- package/src/execution/engine/__tests__/conformance/protocol-blackbox.test.ts +398 -0
- package/src/execution/engine/__tests__/conformance/registry-fork-filter.test.ts +221 -0
- package/src/execution/engine/__tests__/conformance/round-liveness-supervisor.test.ts +302 -0
- package/src/execution/engine/__tests__/conformance/static-spawn-guard.test.ts +58 -0
- package/src/execution/engine/__tests__/engine-discovery-scan.test.ts +558 -0
- package/src/execution/engine/__tests__/host-task-spec.test.ts +45 -0
- package/src/execution/engine/__tests__/model-ref-split.test.ts +74 -0
- package/src/execution/engine/__tests__/registry.test.ts +206 -1
- package/src/execution/engine/__tests__/routing.test.ts +97 -0
- package/src/execution/engine/__tests__/run-failure-worktree-cleanup.test.ts +97 -0
- package/src/execution/engine/client/__tests__/__fixtures__/fake-engine.mjs +294 -0
- package/src/execution/engine/client/__tests__/engine-client.test.ts +516 -0
- package/src/execution/engine/client/__tests__/mirror.test.ts +67 -0
- package/src/execution/engine/client/__tests__/pid-file.test.ts +257 -0
- package/src/execution/engine/client/__tests__/protocol-closure.test.ts +95 -0
- package/src/execution/engine/client/__tests__/remote-engine.test.ts +457 -0
- package/src/execution/engine/client/client-options.ts +103 -0
- package/src/execution/engine/client/engine-client.ts +687 -0
- package/src/execution/engine/client/mirror.ts +167 -0
- package/src/execution/engine/client/pid-file.ts +392 -0
- package/src/execution/engine/client/reaper.ts +95 -0
- package/src/execution/engine/client/remote-engine.ts +441 -0
- package/src/execution/engine/client/reverse-router.ts +255 -0
- package/src/execution/engine/common/capability-gate.ts +109 -22
- package/src/execution/engine/common/errors.ts +9 -25
- package/src/execution/engine/common/event-journal.ts +4 -50
- package/src/execution/engine/common/journal-replay.ts +3 -1
- package/src/execution/engine/common/kill-chain.ts +18 -251
- package/src/execution/engine/common/nesting-guard.ts +18 -114
- package/src/execution/engine/common/pool-manager.ts +4 -0
- package/src/execution/engine/common/session-view-service.ts +56 -84
- package/src/execution/engine/config.ts +151 -0
- package/src/execution/engine/d8-compat.ts +346 -0
- package/src/execution/engine/engine-discovery-roots.ts +107 -0
- package/src/execution/engine/engine-discovery-scan.ts +269 -0
- package/src/execution/engine/engine-discovery.ts +30 -7
- package/src/execution/engine/engine-inspect-package.ts +282 -0
- package/src/execution/engine/engine-manifest.ts +244 -0
- package/src/execution/engine/host/__tests__/host-askuser-endpoint.test.ts +123 -0
- package/src/execution/engine/host/__tests__/host-bridge.test.ts +135 -0
- package/src/execution/engine/host/__tests__/spawned-children.test.ts +73 -0
- package/src/execution/engine/host/host-bridge.ts +150 -0
- package/src/execution/engine/host/host-ui-endpoint.ts +47 -0
- package/src/execution/engine/host/pi-host-binding.ts +91 -0
- package/src/execution/engine/host/spawned-children.ts +139 -0
- package/src/execution/engine/host-task-spec.ts +29 -2
- package/src/execution/engine/model-validation.ts +55 -2
- package/src/execution/engine/paths.ts +16 -41
- package/src/execution/engine/port.ts +48 -2
- package/src/execution/engine/registry.ts +153 -34
- package/src/execution/engine/routing.ts +225 -52
- package/src/execution/engine/types.ts +25 -200
- package/src/execution/execution-record.ts +7 -0
- package/src/execution/finalize-record.ts +40 -11
- package/src/execution/idle-gc.ts +89 -14
- package/src/execution/lifecycle-manager.ts +10 -7
- package/src/execution/lifecycle-predicates.ts +10 -8
- package/src/execution/notifier.ts +234 -25
- package/src/execution/notify-host.ts +21 -1
- package/src/execution/record-entry.ts +16 -0
- package/src/execution/record-store.ts +189 -31
- package/src/execution/relay-env.ts +19 -36
- package/src/execution/round-supervisor/domain.ts +89 -0
- package/src/execution/round-supervisor/index.ts +33 -0
- package/src/execution/round-supervisor/notify-accounting.ts +87 -0
- package/src/execution/round-supervisor/reconcile-sweep.ts +235 -0
- package/src/execution/round-supervisor/service-binding.test.ts +661 -0
- package/src/execution/round-supervisor/service-binding.ts +273 -0
- package/src/execution/round-supervisor/supervisor.ts +426 -0
- package/src/execution/session-pending.ts +68 -74
- package/src/execution/settled-watchdog.ts +39 -0
- package/src/execution/subagent-actions-core.ts +62 -6
- package/src/execution/subagent-service.ts +1161 -406
- package/src/execution/subprocess-agent-runner.ts +16 -8
- package/src/execution/sync-rebuild.ts +87 -0
- package/src/execution/types.ts +104 -117
- package/src/execution/ui-channels.ts +13 -214
- package/src/execution/ui-request-handler-factory.ts +7 -4
- package/src/execution/ui-request-observability.ts +2 -2
- package/src/execution/workflow-state-root.ts +67 -0
- package/src/execution/worktree-git-ops.ts +5 -0
- package/src/execution/worktree-manager.ts +11 -1
- package/src/index.ts +55 -26
- package/src/orchestration/__tests__/config-loader.test.ts +1 -1
- package/src/orchestration/__tests__/execute-agent-call.test.ts +1 -1
- 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__/review-fix-loop-scriptpath-failfast.test.ts +1 -1
- package/src/orchestration/__tests__/script-generate.test.ts +5 -5
- package/src/orchestration/__tests__/skill-discovery.test.ts +2 -2
- package/src/orchestration/__tests__/workflow-files.test.ts +10 -10
- package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +1 -1
- package/src/orchestration/execute-agent-call.ts +1 -1
- package/src/orchestration/file-run-store.ts +144 -59
- package/src/orchestration/models/types.ts +43 -42
- package/src/shared/__tests__/atomic-write.test.ts +1 -1
- package/src/shared/__tests__/resource-discovery-host-roots.test.ts +5 -5
- package/src/shared/zcode-model-ref.ts +31 -0
- package/dist/chunk-43ONBFZX.js +0 -240
- package/dist/chunk-A4IVZWVX.js +0 -31
- package/dist/chunk-APZY4IME.js +0 -27
- package/dist/chunk-V3VHZ2VX.js +0 -59
- package/dist/execution/engine/engines/zcode/constants.cjs +0 -107
- package/dist/execution/engine/engines/zcode/constants.d.cts +0 -135
- package/dist/execution/engine/engines/zcode/constants.d.ts +0 -135
- package/dist/execution/engine/engines/zcode/constants.js +0 -54
- package/dist/execution/engine/engines/zcode/reader.cjs +0 -276
- package/dist/execution/engine/engines/zcode/reader.d.cts +0 -21
- package/dist/execution/engine/engines/zcode/reader.d.ts +0 -21
- package/dist/execution/engine/engines/zcode/reader.js +0 -9
- package/src/__tests__/append-system-prompt-assembly.test.ts +0 -243
- package/src/__tests__/fr4-get-state-handshake.test.ts +0 -125
- package/src/__tests__/session-runner.test.ts +0 -69
- package/src/execution/__tests__/ask-user-transit-e2e.test.ts +0 -490
- package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +0 -191
- package/src/execution/__tests__/descendant-sweep-guards.test.ts +0 -220
- package/src/execution/__tests__/descendant-sweep.test.ts +0 -269
- package/src/execution/__tests__/engine-model-validation.test.ts +0 -741
- package/src/execution/__tests__/epipe-fallback.test.ts +0 -248
- package/src/execution/__tests__/execution-runtime-face.test.ts +0 -272
- package/src/execution/__tests__/get-state-handshake.test.ts +0 -127
- package/src/execution/__tests__/helpers/session-runner-mocks.ts +0 -113
- package/src/execution/__tests__/keep-alive-no-progress.test.ts +0 -280
- package/src/execution/__tests__/kill-all-escalation.test.ts +0 -196
- package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +0 -113
- package/src/execution/__tests__/output-collector.test.ts +0 -469
- package/src/execution/__tests__/pi-invocation.test.ts +0 -134
- package/src/execution/__tests__/recursive-visibility-env.test.ts +0 -149
- package/src/execution/__tests__/rpc-mode.test.ts +0 -89
- package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +0 -387
- package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +0 -277
- package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +0 -254
- package/src/execution/__tests__/run-spawn-edges.test.ts +0 -1079
- package/src/execution/__tests__/run-spawn-integration.test.ts +0 -933
- package/src/execution/__tests__/run-spawn-resume.test.ts +0 -322
- package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +0 -196
- package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +0 -207
- package/src/execution/__tests__/service-kill-escalation.test.ts +0 -91
- package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +0 -110
- package/src/execution/__tests__/session-runner-close-prune.test.ts +0 -181
- package/src/execution/__tests__/session-runner-dispatch.test.ts +0 -489
- package/src/execution/__tests__/session-runner-epipe.test.ts +0 -179
- package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +0 -227
- package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +0 -290
- package/src/execution/__tests__/session-runner-schema-env.test.ts +0 -352
- package/src/execution/__tests__/settled-watchdog.test.ts +0 -524
- package/src/execution/__tests__/spawn-args.test.ts +0 -446
- package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +0 -192
- package/src/execution/__tests__/spawn-event-adapter.test.ts +0 -167
- package/src/execution/__tests__/spawn-worktree-guidance.test.ts +0 -208
- package/src/execution/__tests__/spawned-children.test.ts +0 -236
- package/src/execution/__tests__/start-sync-model-guard.test.ts +0 -150
- package/src/execution/__tests__/stdin-writer.test.ts +0 -464
- package/src/execution/__tests__/subagent-service-message-close.test.ts +0 -693
- package/src/execution/__tests__/temp-prompt.test.ts +0 -53
- package/src/execution/__tests__/timeout-integration.test.ts +0 -616
- package/src/execution/__tests__/turn-limiter-semantics.test.ts +0 -194
- package/src/execution/__tests__/turn-limiter.test.ts +0 -65
- package/src/execution/__tests__/ui-request-handler.test.ts +0 -205
- package/src/execution/__tests__/ui-request-queue.test.ts +0 -298
- package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +0 -230
- package/src/execution/engine/__tests__/common/schema-emulation.test.ts +0 -128
- package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +0 -149
- package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +0 -28
- package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +0 -128
- package/src/execution/engine/__tests__/paths.test.ts +0 -39
- package/src/execution/engine/common/schema-emulation.ts +0 -189
- package/src/execution/engine/common/session-view-projection.ts +0 -51
- package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +0 -615
- package/src/execution/engine/engines/pi/__tests__/reader.test.ts +0 -155
- package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +0 -257
- package/src/execution/engine/engines/pi/argv-mirror.ts +0 -135
- package/src/execution/engine/engines/pi/get-state-handshake.ts +0 -179
- package/src/execution/engine/engines/pi/output-collector.ts +0 -317
- package/src/execution/engine/engines/pi/pi-engine.ts +0 -811
- package/src/execution/engine/engines/pi/pi-invocation.ts +0 -120
- package/src/execution/engine/engines/pi/reader.ts +0 -59
- package/src/execution/engine/engines/pi/registration.ts +0 -37
- package/src/execution/engine/engines/pi/session-runner.ts +0 -2840
- package/src/execution/engine/engines/pi/spawn-event-adapter.ts +0 -404
- package/src/execution/engine/engines/pi/stdin-writer.ts +0 -199
- package/src/execution/engine/engines/pi/temp-prompt.ts +0 -62
- package/src/execution/engine/engines/pi/turn-limiter.ts +0 -102
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +0 -301
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +0 -36
- package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +0 -321
- package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +0 -473
- package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +0 -43
- package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +0 -124
- package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +0 -210
- package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +0 -71
- package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +0 -247
- package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +0 -451
- package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +0 -792
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +0 -782
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +0 -168
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +0 -210
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +0 -353
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +0 -282
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +0 -372
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +0 -145
- package/src/execution/engine/engines/zcode/appserver-launcher.ts +0 -167
- package/src/execution/engine/engines/zcode/connection.ts +0 -598
- package/src/execution/engine/engines/zcode/constants.ts +0 -176
- package/src/execution/engine/engines/zcode/golden-sample.ts +0 -47
- package/src/execution/engine/engines/zcode/parser.ts +0 -102
- package/src/execution/engine/engines/zcode/preparer.ts +0 -235
- package/src/execution/engine/engines/zcode/reader.ts +0 -381
- package/src/execution/engine/engines/zcode/registration.ts +0 -37
- package/src/execution/engine/engines/zcode/session-channel.ts +0 -968
- package/src/execution/engine/engines/zcode/zcode-engine.ts +0 -1431
- package/src/execution/round-settlement.ts +0 -93
- package/src/execution/ui-request-queue.ts +0 -215
- package/src/shared/schema-env.ts +0 -44
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
// D-017 时序:collectPatch → completeRecord → archive → cleanup(finalized+worktree+
|
|
6
6
|
// aliveMarker+pending注销) → manifest(最后 best-effort)。
|
|
7
7
|
//
|
|
8
|
-
// [Critical #1 / PR #85] cleanup 全部在 manifest 写之前执行——manifest
|
|
9
|
-
|
|
10
|
-
//
|
|
11
|
-
// pending 记账错乱。现 manifest 写移到 Step 4(最后),best-effort
|
|
12
|
-
|
|
8
|
+
// [Critical #1 / PR #85] cleanup 全部在 manifest 写之前执行——manifest 是 sa- id 反查
|
|
9
|
+
// 索引(最新已知快照),非正确性依赖,写失败仅记录不阻断。旧实现 Step 2.5 throw 会跳过
|
|
10
|
+
// Step 3 cleanup,导致磁盘满/权限错时 worktree 泄漏 + finalized marker 不写 + alive
|
|
11
|
+
// marker 残留 + pending 记账错乱。现 manifest 写移到 Step 4(最后),best-effort
|
|
12
|
+
//(console.error + appendEntry,不 throw)。
|
|
13
13
|
//
|
|
14
14
|
// B9 兜底:completeRecord/archive 抛错→后续 cleanup/manifest 仍执行。
|
|
15
15
|
|
|
@@ -43,6 +43,16 @@ export interface FinalizeDeps {
|
|
|
43
43
|
pi: { appendEntry?: (type: string, data: unknown) => void } | null;
|
|
44
44
|
/** pending-notifications 终态注销(绑定 pi.events.emit,由调用方闭包提供)。 */
|
|
45
45
|
emitUnregister(id: string, status: string): void;
|
|
46
|
+
/**
|
|
47
|
+
* [F-5 修复] record 终态化的宿主侧收口钩子(recordId;best-effort,抛错由调用方
|
|
48
|
+
* 在闭包内自行兜底)。单一汇聚点:doFinalizeRecord 是全部 closed 终态的必经路径
|
|
49
|
+
* (finalizeRecord / closeChatIdle / closeAfterRoundSettled / finalizeFailed /
|
|
50
|
+
* finalizeAborted / consumeCloseAfterRound),chat 轮路由注销(chatRoundRoutes)
|
|
51
|
+
* 由 SubagentService 经此钩子统一执行——此前仅 cancelBackground 注销,chat record
|
|
52
|
+
* 经 finalize 链终态化后路由闭包(持 record/stream/守护引用)泄漏。finalizeRoundToIdle
|
|
53
|
+
* 不经本钩子(回 idle 非终态,续聊仍需路由)。
|
|
54
|
+
*/
|
|
55
|
+
onFinalized?: (recordId: string) => void;
|
|
46
56
|
/**
|
|
47
57
|
* [T1/PS-9] subagent sessionDir(getSubagentSessionDir(agentDir, rootCwd),调用方注入)。
|
|
48
58
|
*
|
|
@@ -177,11 +187,13 @@ function removeAliveMarkerIfPresent(record: ExecutionRecord): void {
|
|
|
177
187
|
/**
|
|
178
188
|
* Step 4 (last): manifest 持久化(best-effort,不阻断、不 throw)。
|
|
179
189
|
*
|
|
180
|
-
* [Critical #1] manifest
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
190
|
+
* [Critical #1] manifest 是 sa- id 反查索引(最新已知快照),不是正确性依赖。本步骤
|
|
191
|
+
* 写终态快照;sync 批成功成员不经此处——落标出口(subagent-service
|
|
192
|
+
* appendBatchFinalizedEntry)已先行补写,message upgrade 到终态时被本步骤原子
|
|
193
|
+
* 覆盖。写失败时仅记录(console.error + appendEntry),绝不让 manifest 写失败
|
|
194
|
+
* 跳过 Step 3 cleanup 或抛出打断 finalize 链。旧实现 Step 2.5 throw 会
|
|
195
|
+
* 跳过 Step 3 cleanup。task/slug/model 从 ExecutionRecord 抓取(配合
|
|
196
|
+
* ManifestRecord 补字段),manifestToSubagent 投影时用真实值而非硬编码空串。
|
|
185
197
|
*/
|
|
186
198
|
async function writeManifestBestEffort(deps: FinalizeDeps, record: ExecutionRecord): Promise<void> {
|
|
187
199
|
try {
|
|
@@ -252,8 +264,23 @@ export async function doFinalizeRecord(
|
|
|
252
264
|
removeAliveMarkerIfPresent(record);
|
|
253
265
|
|
|
254
266
|
// pending-notifications:终态注销(只记 registry 状态,通知由 BgNotifier 发)
|
|
267
|
+
// [W4 发射点枚举归属①] 注销合法发射点枚举(设计 D2)第 ① 处:subagent record
|
|
268
|
+
// 终态化(finalizeRecord 路径,含监督器放弃)。其余合法发射点:② chatMode 轮末
|
|
269
|
+
// idle(doFinalizeRoundToIdle,本文件下方);③ workflow run 终态迁移
|
|
270
|
+
// (transition("done") 路径);④ 监督器显式放弃(走本路径,终态化+注销同批);
|
|
271
|
+
// ⑤ 注册对账 sweep 补发(round-supervisor/reconcile-sweep.ts)。进程退出本身
|
|
272
|
+
// 永远不是注销理由(subagent-service disposeAllRecords 的 emit 属①——其同批
|
|
273
|
+
// completeRecord+archive 终态化)。
|
|
255
274
|
deps.emitUnregister(record.id, status);
|
|
256
275
|
|
|
276
|
+
// [F-5 修复] 宿主侧终态收口钩子(chat 轮路由注销的单一汇聚点,见 FinalizeDeps
|
|
277
|
+
// .onFinalized 注释)。fire-and-forget:钩子失败不阻断 manifest 收尾(闭包内自查)。
|
|
278
|
+
try {
|
|
279
|
+
deps.onFinalized?.(record.id);
|
|
280
|
+
} catch (err) {
|
|
281
|
+
bestEffort(err, "onFinalized hook (finalizeRecord)");
|
|
282
|
+
}
|
|
283
|
+
|
|
257
284
|
// ── Step 4 (last): manifest 持久化(best-effort,不阻断、不 throw)──
|
|
258
285
|
await writeManifestBestEffort(deps, record);
|
|
259
286
|
}
|
|
@@ -265,7 +292,9 @@ export async function doFinalizeRecord(
|
|
|
265
292
|
* - 不调 completeRecord(record 不冻结,保留 turns[] 等运行时状态供续聊累积)
|
|
266
293
|
* - 不调 store.archive(record 留内存,getMutable 可查、list 可见)
|
|
267
294
|
* - 不 cleanup worktree(保留对话模式工作目录)
|
|
268
|
-
* - 不写 manifest
|
|
295
|
+
* - 不写 manifest:轮终回 running-resumable 非终态化,本方法无终态快照可写——
|
|
296
|
+
* manifest 是 sa- id 反查索引(最新已知快照),sync 批成员的先行快照由落标出口
|
|
297
|
+
* 补写(subagent-service appendBatchFinalizedEntry),此处不重复
|
|
269
298
|
* - 删 .alive marker(进程已 SIGTERM 回收,不再是活进程)
|
|
270
299
|
* - emitUnregister(进程已死,从 pending 活跃后代差集移除;record 留内存不 archive)
|
|
271
300
|
*
|
package/src/execution/idle-gc.ts
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
* SP-4 idle record GC:30 天 TTL 定时归档,防 idle record 永久驻留内存。
|
|
3
3
|
* 从 subagent-service.ts 抽出(文件超 max-lines;逻辑自包含:interval + TTL 扫描)。
|
|
4
4
|
* 启动幂等由调用方(service.startGcTimer)守卫;返回 stop 函数供 dispose 清理。
|
|
5
|
+
*
|
|
6
|
+
* [W4 idle-gc 扩展 · 设计 chat-domain-v1x-liveness-governance D4 连带面 2③]
|
|
7
|
+
* 注册翻 process 档后的兜底通道扩展(与翻档同批生效,先行期 = 无兜底挂账窗口):
|
|
8
|
+
* 1. 锚扩展:无 idleSince 的 resumable record 以 startedAt(创建时确定,
|
|
9
|
+
* types.ts ExecutionRecord.startedAt)为锚——兜底定位 30 天量级终态归档,
|
|
10
|
+
* 创建时锚的精度损失在该量级可接受(同设计对 record startedAt 的 rationale)。
|
|
11
|
+
* 2. **只归档不补注销**:被归档 record 的注册注销统一交 core 注册对账 sweep
|
|
12
|
+
* (判据含「已归档 = 视同终态」)——归档跨 session record 时 appendEntry 只达
|
|
13
|
+
* 当前 session entries(写达域无效),且 archive 不走 finalizeRecord、无注销
|
|
14
|
+
* 发射枚举身份(发射点枚举 D2 的 5 处不含 GC)。
|
|
15
|
+
* 3. WorkflowRun store 同批纳入:running 且 meta.startedAt 超 IDLE_TTL_MS 的 run
|
|
16
|
+
* 终态化归档(transition("done","time_limited") + save 持久化终态)。时间锚选择
|
|
17
|
+
* = WorkflowRunMeta.startedAt(run 创建时刻,ISO string)——run 状态无 idleSince
|
|
18
|
+
* 等价物,meta.completedAt 仅终态存在,startedAt 是唯一创建时确定的锚(与
|
|
19
|
+
* record startedAt 同 rationale)。**只终态化不补注销**同上:run 的注销发射
|
|
20
|
+
* 身份在 transition("done") 路径(发射点③)与其宿主收口链,GC 不越权补发。
|
|
5
21
|
*/
|
|
6
22
|
import { getLogger } from "../core/logger.ts";
|
|
7
23
|
import { getEngineDataDir } from "./engine/common/data-dir.ts";
|
|
@@ -16,17 +32,32 @@ const logger = getLogger("subagents");
|
|
|
16
32
|
/** GC 扫描间隔:1 小时。 */
|
|
17
33
|
// eslint-disable-next-line no-magic-numbers -- 60*60*1000 = 1h 的毫秒换算常数
|
|
18
34
|
const GC_INTERVAL_MS = 60 * 60 * 1000;
|
|
19
|
-
/** idle record TTL:30 天(超龄归档)。 */
|
|
35
|
+
/** idle record TTL:30 天(超龄归档)。record 锚窗与 workflow run 锚窗共用。 */
|
|
20
36
|
// eslint-disable-next-line no-magic-numbers -- 30*24*60*60*1000 = 30d 的毫秒换算常数
|
|
21
37
|
const IDLE_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
22
38
|
/** 毫秒/天(GC 日志的 d 换算)。 */
|
|
23
39
|
// eslint-disable-next-line no-magic-numbers -- 24*60*60*1000 = 1d 的毫秒换算常数
|
|
24
40
|
const MS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
25
41
|
|
|
42
|
+
/**
|
|
43
|
+
* WorkflowRun GC 窄口(idle-gc 对 WorkflowRun store 的最小依赖面,结构类型——
|
|
44
|
+
* 调用方传 FileRunStore 实例即可,不 import orchestration 具体类,保持本模块
|
|
45
|
+
* 可独立编译 + 单测)。loadAll 失败(宿主未 configureCore / IO 错)由实现侧
|
|
46
|
+
* 或本模块 catch 吞掉,单轮跳过下轮重试。
|
|
47
|
+
*/
|
|
48
|
+
export interface WorkflowRunGcStore {
|
|
49
|
+
loadAll(): Promise<Array<{ runId: string; state: { status: string }; meta: { startedAt: string }; transition(target: "done", reason?: string): void }>>;
|
|
50
|
+
save(run: unknown): Promise<void>;
|
|
51
|
+
}
|
|
52
|
+
|
|
26
53
|
/**
|
|
27
54
|
* 启动 idle record GC 定时器,返回 stop 函数(清理 interval;幂等)。
|
|
28
|
-
*
|
|
29
|
-
*
|
|
55
|
+
* 每个扫描周期:
|
|
56
|
+
* - record 面:对 store 内全部 active record 中 resumable 的,锚点(idleSince
|
|
57
|
+
* 优先,缺失回退 startedAt——[W4 锚扩展])超过 IDLE_TTL_MS 的归档。archive
|
|
58
|
+
* 单条失败不阻断其余(bestEffort 留痕)。**只归档不补注销**(见文件头注)。
|
|
59
|
+
* - workflow 面(注入 workflowRuns 时):running 且 meta.startedAt 超
|
|
60
|
+
* IDLE_TTL_MS 的 run 终态化归档(transition + save),单 run 失败不阻断。
|
|
30
61
|
*
|
|
31
62
|
* [D8 池引用计数接线] 归档时同步释放该 record 的引擎池引用(releasePoolRef:
|
|
32
63
|
* journal 跟随 record 删除 + refs 移除 + 归零删池内原生状态)。锚点选在 30 天 TTL
|
|
@@ -37,28 +68,72 @@ const MS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
|
37
68
|
* 不能删——那是 record 主数据死亡(主 session 文件被 pi 侧删除,core 无触发点)才有
|
|
38
69
|
* 的处置,由 cleanupExpiredPoolRefs 的 TTL 兜底覆盖。
|
|
39
70
|
*/
|
|
40
|
-
export function startIdleGc(store: RecordStore): () => void {
|
|
71
|
+
export function startIdleGc(store: RecordStore, workflowRuns?: WorkflowRunGcStore): () => void {
|
|
41
72
|
const timer = setInterval(() => {
|
|
42
73
|
const now = Date.now();
|
|
43
74
|
for (const record of store.listAllActive()) {
|
|
44
|
-
if (isResumable(record)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
75
|
+
if (!isResumable(record)) continue;
|
|
76
|
+
// [W4 锚扩展] idleSince(轮终写点)优先;缺失(resumable 但无轮终信号的
|
|
77
|
+
// 存量/异常形态)回退 startedAt(创建时确定)——两锚同为「最晚活性证据」,
|
|
78
|
+
// 30 天量级下 created 锚的精度损失可接受。
|
|
79
|
+
const anchorMs = record.idleSince ?? record.startedAt;
|
|
80
|
+
const age = now - anchorMs;
|
|
81
|
+
if (age > IDLE_TTL_MS) {
|
|
82
|
+
logger.warn(
|
|
83
|
+
`[subagents] GC: archiving idle record ${record.id} (idle for ${Math.round(age / MS_PER_DAY)}d)`,
|
|
84
|
+
);
|
|
85
|
+
try {
|
|
86
|
+
store.archive(record);
|
|
87
|
+
} catch (err) {
|
|
88
|
+
bestEffort(err, `GC archive record ${record.id}`);
|
|
54
89
|
}
|
|
90
|
+
releaseEnginePoolRef(record);
|
|
55
91
|
}
|
|
56
92
|
}
|
|
93
|
+
if (workflowRuns !== undefined) {
|
|
94
|
+
void gcWorkflowRuns(workflowRuns, now);
|
|
95
|
+
}
|
|
57
96
|
}, GC_INTERVAL_MS);
|
|
58
97
|
timer.unref?.();
|
|
59
98
|
return () => clearInterval(timer);
|
|
60
99
|
}
|
|
61
100
|
|
|
101
|
+
/**
|
|
102
|
+
* [W4 WorkflowRun store 纳入] running 且 startedAt 超锚窗的 run 终态化归档。
|
|
103
|
+
* reason = "time_limited"(超龄归档语义的 DoneReason,对主 agent 的语义是
|
|
104
|
+
* 「run 因超时被收口」而非 completed)。失败吞错留痕(清理是旁路维护,不能
|
|
105
|
+
* 拖垮 GC interval;下轮重试)。
|
|
106
|
+
*/
|
|
107
|
+
async function gcWorkflowRuns(workflowRuns: WorkflowRunGcStore, now: number): Promise<void> {
|
|
108
|
+
let runs: Awaited<ReturnType<WorkflowRunGcStore["loadAll"]>>;
|
|
109
|
+
try {
|
|
110
|
+
runs = await workflowRuns.loadAll();
|
|
111
|
+
} catch (err) {
|
|
112
|
+
// 宿主未 configureCore(core_host_not_configured)/ IO 失败:本轮跳过,
|
|
113
|
+
// 下轮重试。debug 留痕——这是「workflow 域未启用」的正常形态,warn 会噪声。
|
|
114
|
+
logger.debug(`[subagents] GC: workflow run store loadAll failed (skipped this cycle): ${
|
|
115
|
+
err instanceof Error ? err.message : String(err)
|
|
116
|
+
}`);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
for (const run of runs) {
|
|
120
|
+
if (run.state.status !== "running") continue;
|
|
121
|
+
const startedMs = Date.parse(run.meta.startedAt);
|
|
122
|
+
if (!Number.isFinite(startedMs)) continue; // 畸形锚不过判(宁挂账不失明)
|
|
123
|
+
const age = now - startedMs;
|
|
124
|
+
if (age <= IDLE_TTL_MS) continue;
|
|
125
|
+
logger.warn(
|
|
126
|
+
`[subagents] GC: terminating stale running workflow run ${run.runId} (started ${Math.round(age / MS_PER_DAY)}d ago)`,
|
|
127
|
+
);
|
|
128
|
+
try {
|
|
129
|
+
run.transition("done", "time_limited");
|
|
130
|
+
await workflowRuns.save(run);
|
|
131
|
+
} catch (err) {
|
|
132
|
+
bestEffort(err, `GC terminate workflow run ${run.runId}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
62
137
|
/**
|
|
63
138
|
* 释放 record 的引擎池引用(best-effort)。无 engineHandle(存量 record / 非引擎
|
|
64
139
|
* 路径)时 no-op;engine 缺省投影 'pi'(与读侧存量 entry 零迁移口径一致)。
|
|
@@ -13,7 +13,10 @@
|
|
|
13
13
|
// 【已接线:session-runner.ts agent_settled arm / subagent-service 新 turn disarm】
|
|
14
14
|
// 2. 全局 ceiling —— 活进程上限,超限时按 LRU 挤出最久空闲(决策 4)【未接线,deferred】
|
|
15
15
|
// 3. shutdown 收割 —— 父进程 shutdown 时显式 SIGTERM 全部 activation(决策 7 防线 i)
|
|
16
|
-
// 【未接线:
|
|
16
|
+
// 【未接线:[F-7 纠偏] 旧注「reapSpawnedChildrenOnShutdown 已等价覆盖」失实——
|
|
17
|
+
// 该 hook 调 killAllSpawnedChildren,后者协议化后仅镜像置死 no-op(见
|
|
18
|
+
// engine/host/spawned-children.ts 与 src/index.ts 导出注释);引擎进程实际回收 =
|
|
19
|
+
// stdin-EOF 自灭链,本职责仍无显式收割接线】
|
|
17
20
|
// 4. 孤儿扫描 —— 父进程启动时按持久化 PID 扫收上次崩溃遗留的孤儿(决策 7 防线 ii)【deferred】
|
|
18
21
|
// 5. activate 互斥 —— 同 recordId 的并发 activate 串行化(决策 7 防线 iii,防双写者)
|
|
19
22
|
// 【已接线:subagent-service.ts 冷路径 resume 前 acquireActivateLock】
|
|
@@ -295,12 +298,12 @@ export function getActiveProcessCount(): number {
|
|
|
295
298
|
// ============================================================
|
|
296
299
|
// 职责 3:shutdown 收割(防线 i)
|
|
297
300
|
//
|
|
298
|
-
// [
|
|
299
|
-
//
|
|
300
|
-
//
|
|
301
|
-
//
|
|
302
|
-
//
|
|
303
|
-
//
|
|
301
|
+
// [状态:未接线,无等价实现([F-7 纠偏])] reapAllAliveProcesses 无生产调用方。旧注
|
|
302
|
+
// 「shutdown 收割已由 reapSpawnedChildrenOnShutdown 等价覆盖」失实:该 hook 调
|
|
303
|
+
// killAllSpawnedChildren,而后者协议化后(engines/pi inproc 删除、子进程活在引擎
|
|
304
|
+
// 进程内)仅做 core 侧镜像置死,不发任何 SIGTERM(_signal 参数被忽略)——引擎进程
|
|
305
|
+
// 实际回收走 stdin-EOF 自灭链,本职责的显式收割仍无接线。本函数保留作未来接入面;
|
|
306
|
+
// 若未来接入须先保证 register/unregister 记账完整,否则收割列表不全。
|
|
304
307
|
// ============================================================
|
|
305
308
|
|
|
306
309
|
/**
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
//
|
|
5
5
|
// 旧三态(running/idle/cancelled)折为两态(running/closed)后,「对话模式等待续聊」
|
|
6
6
|
//(旧 idle)和「正在执行」都是 status="running",需派生谓词区分。谓词复用已有
|
|
7
|
-
// lifecycle-manager.hasIdleTimer(idle timer 是否 armed)与
|
|
8
|
-
|
|
7
|
+
// lifecycle-manager.hasIdleTimer(idle timer 是否 armed)与 core 侧 spawnedChildren
|
|
8
|
+
// 状态镜像读点(engine/host/spawned-children.ts,活进程句柄是否存在),不新增状态记账。
|
|
9
9
|
//
|
|
10
10
|
// 两种 running 子态(v4 B-1):
|
|
11
11
|
// - 等待续聊(旧 idle):isIdle=true(timer armed)。又分两路:
|
|
@@ -20,19 +20,21 @@
|
|
|
20
20
|
// 故 isIdle 在这两个检查点恒为 true——notify 守卫与 early-return 判据可靠。
|
|
21
21
|
|
|
22
22
|
import { hasIdleTimer } from "./lifecycle-manager.ts";
|
|
23
|
-
|
|
23
|
+
// [W6 拆依赖] 活进程句柄读点改经 core 侧 spawnedChildren 状态镜像公共面
|
|
24
|
+
// (engine/host/spawned-children.ts),不再深路径 import inproc pi 引擎目录 内部——
|
|
25
|
+
// 行为不变(镜像 ∪ inproc 权威 map 并读,见该文件头注释的过渡桥语义)。
|
|
26
|
+
import { hasLiveProcessHandleCore } from "./engine/host/spawned-children.ts";
|
|
24
27
|
import type { ExecutionRecord } from "./types.ts";
|
|
25
28
|
|
|
26
29
|
/**
|
|
27
30
|
* 活进程句柄是否存在(isResumable 子判据)。
|
|
28
31
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
+
* [W6] 改读 core 侧状态镜像(engine/host/spawned-children.ts 的 hasLiveProcessHandleCore:
|
|
33
|
+
* 镜像项存在且未 killed = 有活进程句柄;迁移期内建 pi 的 inproc 权威 map 并读兜底)。
|
|
34
|
+
* 进程 close / 被杀后镜像项置死或移除 = 无活进程(Path B / 跨重启)。
|
|
32
35
|
*/
|
|
33
36
|
export function hasLiveProcessHandle(recordId: string): boolean {
|
|
34
|
-
|
|
35
|
-
return child !== undefined && !child.killed;
|
|
37
|
+
return hasLiveProcessHandleCore(recordId);
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/execution/notifier.ts
|
|
2
2
|
//
|
|
3
|
-
// Background 完成回注主对话。sync
|
|
3
|
+
// Background 完成回注主对话。collect:"sync" 的批通知不经单条 notify() 生命周期,
|
|
4
|
+
// 由 collect-coordinator 合批后调本文件 notifyBatch 投递(成员结果不直接返回调用方)。
|
|
4
5
|
//
|
|
5
6
|
// 职责(U2 后):
|
|
6
7
|
// - buildLlmContent:格式化通知文案(本文件唯一逻辑职责)
|
|
@@ -9,6 +10,7 @@
|
|
|
9
10
|
// notify-ledger.ts);未装配时退回投递内核路径(createDelivery 经通知域窄端口
|
|
10
11
|
// NotifyDomainPorts 注入——core 依赖闭包不含 session-delivery,见 core/notify-ports.ts;
|
|
11
12
|
// 合并窗口 / 去重 / 退避 / flush 委托内核,旧装配 / 无 ledger 测试兼容)
|
|
13
|
+
// - notifyBatch / buildBatchLlmContent / computeBatchBudget:sync 批通知三件套(U3/U4)
|
|
12
14
|
//
|
|
13
15
|
// 投递通道(D5 单通道化):ledger 路径经 courier 在 settled 边沿直达
|
|
14
16
|
// pi.sendMessage({triggerTurn:true});内核路径的 port.send 同样只传
|
|
@@ -17,6 +19,8 @@
|
|
|
17
19
|
// 设计 D5 实测证伪);busy 场景由 ledger(settled 边沿 + isIdle 二次复查)或内核
|
|
18
20
|
// settled 订阅驱动在空闲边沿投递。
|
|
19
21
|
|
|
22
|
+
import { createHash } from "node:crypto";
|
|
23
|
+
|
|
20
24
|
import { getLogger } from "../core/logger.ts";
|
|
21
25
|
import { getNotifyDomainPorts, type DeliveryHandle, type DeliveryPort } from "../core/notify-ports.ts";
|
|
22
26
|
|
|
@@ -104,6 +108,169 @@ export interface NotifierHost {
|
|
|
104
108
|
onAgentSettled?(handler: () => void): void;
|
|
105
109
|
}
|
|
106
110
|
|
|
111
|
+
/** sync 批通知幂等键前缀(subagent-sync-collect 设计 §3.1.3 批身份)。 */
|
|
112
|
+
const BATCH_NOTIFY_ID_PREFIX = "sync-batch:";
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* sync 批通知 notifyId:`sync-batch:<sha1(sorted member ids)>`。
|
|
116
|
+
*
|
|
117
|
+
* 成员集身份(D2 隐式批):字典序排序后 sha1——同成员集不同登记顺序同 hash(重启
|
|
118
|
+
* 重建批 / E1 补发凭此与账本原条目对齐,幂等去重);不同成员集必异 hash(跨批不互吞)。
|
|
119
|
+
* 成员 id 为 sa-xxx 格式无分隔歧义,join(",") 仅作边界防御。
|
|
120
|
+
*/
|
|
121
|
+
export function buildBatchNotifyId(memberIds: readonly string[]): string {
|
|
122
|
+
const digest = createHash("sha1")
|
|
123
|
+
.update([...memberIds].sort().join(","))
|
|
124
|
+
.digest("hex");
|
|
125
|
+
return `${BATCH_NOTIFY_ID_PREFIX}${digest}`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ─── U4 预算截断 + 指针(设计 §3.1.2 两段式预算,确定性统一收紧)───
|
|
129
|
+
|
|
130
|
+
/** sync 批预算参数(config collectSync 节语义)。U4 以设计默认值锁规格;
|
|
131
|
+
* config 热读(flush 时读值)需 service 传参接线,不在本函数内自行读 config。 */
|
|
132
|
+
export interface BatchBudgetParams {
|
|
133
|
+
/** 单条目正文上限(第一段截断)。 */
|
|
134
|
+
perItemChars: number;
|
|
135
|
+
/** 全批正文中量预算(第二段收紧触发阈值)。 */
|
|
136
|
+
totalChars: number;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** 设计默认预算(§3.1.3 config 示例值)。 */
|
|
140
|
+
const DEFAULT_BATCH_BUDGET: BatchBudgetParams = { perItemChars: 4000, totalChars: 24000 };
|
|
141
|
+
|
|
142
|
+
/** 预算计划(computeBatchBudget 输出,纯数据)。 */
|
|
143
|
+
export interface BatchBudgetPlan {
|
|
144
|
+
/** ② 总量再压缩是否触发(Σ per-item 截断后正文 > totalChars)。 */
|
|
145
|
+
tightened: boolean;
|
|
146
|
+
/** 每条目正文有效上限:未收紧 = perItemChars(兼作第一段截断上限);纯清单退化 = 0。 */
|
|
147
|
+
effectivePerItem: number;
|
|
148
|
+
/** 纯清单退化(floor(totalChars/n) < 200 → 条目只剩头行 + 指针行,正文 0 字符)。 */
|
|
149
|
+
listOnly: boolean;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** 绝对下限(设计 §3.1.2):预算折算低于它时放弃正文,条目退化为「头行 + 指针行」。 */
|
|
153
|
+
const LIST_ONLY_FLOOR = 200;
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 两段式批预算计划(纯函数,规格测试 collect-budget.test.ts 锁定)。
|
|
157
|
+
*
|
|
158
|
+
* ① per-item:每条目正文 ≤ perItemChars;② 总量:Σ(截断后) > totalChars 时
|
|
159
|
+
* effectivePerItem = clamp(floor(totalChars / n), 200, perItemChars) 统一收紧
|
|
160
|
+
* (n = 成员数)。刻意放弃按剩余预算的瀑布分配(非确定、依赖条目顺序),
|
|
161
|
+
* 统一收紧换确定性;收紧后 Σ ≤ n × floor(totalChars/n) ≤ totalChars 恒回到预算内。
|
|
162
|
+
* 未触发收紧时 effectivePerItem = perItemChars(同时承担第一段截断上限)。
|
|
163
|
+
*/
|
|
164
|
+
export function computeBatchBudget(
|
|
165
|
+
bodyLengths: readonly number[],
|
|
166
|
+
perItemChars: number,
|
|
167
|
+
totalChars: number,
|
|
168
|
+
): BatchBudgetPlan {
|
|
169
|
+
const n = bodyLengths.length;
|
|
170
|
+
const sumAfterPerItem = bodyLengths.reduce((sum, len) => sum + Math.min(len, perItemChars), 0);
|
|
171
|
+
if (n === 0 || sumAfterPerItem <= totalChars) {
|
|
172
|
+
return { tightened: false, effectivePerItem: perItemChars, listOnly: false };
|
|
173
|
+
}
|
|
174
|
+
const tight = Math.floor(totalChars / n);
|
|
175
|
+
if (tight < LIST_ONLY_FLOOR) {
|
|
176
|
+
return { tightened: true, effectivePerItem: 0, listOnly: true };
|
|
177
|
+
}
|
|
178
|
+
return { tightened: true, effectivePerItem: Math.min(tight, perItemChars), listOnly: false };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** 千位分隔(对齐设计样例 11,234;手写正则零 ICU 依赖)。 */
|
|
182
|
+
function formatChars(n: number): string {
|
|
183
|
+
return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* session_read result action 的默认单条字符上限(adversarial-review-fixes §3.4 C3)。
|
|
188
|
+
* 对齐 extensions/universal/session-reader/src/result-action.ts 的 RESULT_DEFAULT_LIMIT
|
|
189
|
+
* (8000)——extension 与 runtime 包无共享常量处、反向 import 会建立 subagent-core 对
|
|
190
|
+
* extension 的依赖,故本地常量 + 注释锚定;两侧改动须同步。
|
|
191
|
+
*/
|
|
192
|
+
const SESSION_READ_DEFAULT_LIMIT = 8000;
|
|
193
|
+
|
|
194
|
+
/** 截断指针行(设计 §3.1.2 样例):主 agent 据此按需 session_read 取回全文。
|
|
195
|
+
* [S8 code-simplify 口径注] 本函数的 X(total - kept)= **丢弃**字符数;而
|
|
196
|
+
* session-reader result-action.ts formatResultTruncation 同模板的 X = **保留**
|
|
197
|
+
* 字符数(limit)——同模板异语义,统一字符串 = 行为变更,勿顺手改口径。
|
|
198
|
+
* [C3 limit 随附] perItemLimit = 该成员实际截断预算(effectivePerItem):预算超过
|
|
199
|
+
* session_read 默认 limit(8000) 时附 "limit":N——模型照抄指针行 JSON 调用才能取到
|
|
200
|
+
* ≥ 默认值的有效正文(否则默认 8000 反而少于通知里已见的 kept,触发第二层截断);
|
|
201
|
+
* ≤8000 不附(默认已覆盖,避免噪音)。 */
|
|
202
|
+
function buildTruncationPointer(id: string, kept: number, total: number, perItemLimit: number): string {
|
|
203
|
+
const limitPart = perItemLimit > SESSION_READ_DEFAULT_LIMIT ? `,"limit":${perItemLimit}` : "";
|
|
204
|
+
return `[truncated ${formatChars(total - kept)} of ${formatChars(total)} chars — full result: session_read {"action":"result","session":"${id}"${limitPart}}]`;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** closed 成员 outcome 兑底物化(与下方 notify() 投影边界同款);running 原样透传。 */
|
|
208
|
+
function withMaterializedOutcome(record: BgNotifyRecord): BgNotifyRecord {
|
|
209
|
+
return record.status === "closed"
|
|
210
|
+
? { ...record, outcome: record.outcome ?? deriveOutcome(record.closedReason, record.error) }
|
|
211
|
+
: record;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* sync 批通知文案(U3 批形态 + U4 两段式预算截断)。
|
|
216
|
+
*
|
|
217
|
+
* 形态(设计 §3.1.1 交互样例):批头行 `Subagent batch completed: N finished,
|
|
218
|
+
* M failed, K cancelled.` + 各成员条目(buildLlmContent 同语义)以 "\n\n---\n\"
|
|
219
|
+
* \n" join(分隔符对齐 ledger mergeItems / 内核合批——TUI/LLM 两消费方同构)。
|
|
220
|
+
* 批头计数口径 = 成员 outcome 三态(completed→finished);closed 成员 outcome 缺省
|
|
221
|
+
* 时按单一权威 deriveOutcome 兑底(与 notify() 投影边界同款,幂等无害)。
|
|
222
|
+
*
|
|
223
|
+
* [U4 预算截断] 仅对展示结果正文的条目(closed+completed / running)生效;
|
|
224
|
+
* failed/cancelled 条目无 result 可取回(指针无意义)不计正文预算、原样输出。
|
|
225
|
+
* totalChars 口径(§3.1.2):仅计条目正文(截断后)之和,批头/头行/指针行/
|
|
226
|
+
* 分隔符等包装开销为有界常量不入预算。未触预算时输出与 U3 基线逐字节一致。
|
|
227
|
+
* budget 缺省 = 设计默认值;config 热读接线由 flush 调用方传入。
|
|
228
|
+
*/
|
|
229
|
+
export function buildBatchLlmContent(
|
|
230
|
+
records: readonly BgNotifyRecord[],
|
|
231
|
+
budget: BatchBudgetParams = DEFAULT_BATCH_BUDGET,
|
|
232
|
+
): string {
|
|
233
|
+
// 入口一次物化(S1,code-simplify):closed 成员 outcome 兑底仅此一处,bodies/计数/
|
|
234
|
+
// 条目全部读物化结果——「单一权威 deriveOutcome」从注释承诺变构造事实。幂等纯函数,
|
|
235
|
+
// 与原四处各自推导逐字节等价;running 原样透传零拷贝。
|
|
236
|
+
const mat = records.map(withMaterializedOutcome);
|
|
237
|
+
let finished = 0;
|
|
238
|
+
let failed = 0;
|
|
239
|
+
let cancelled = 0;
|
|
240
|
+
// 各条目参与预算的正文(§3.1.2 口径);null = 该条目不展示 result 正文。
|
|
241
|
+
const bodies = mat.map((record): string | null => {
|
|
242
|
+
if (record.status === "closed" && record.outcome !== "completed") return null;
|
|
243
|
+
return record.result ?? "(empty)";
|
|
244
|
+
});
|
|
245
|
+
const plan = computeBatchBudget(
|
|
246
|
+
bodies.map((body) => body?.length ?? 0),
|
|
247
|
+
budget.perItemChars,
|
|
248
|
+
budget.totalChars,
|
|
249
|
+
);
|
|
250
|
+
const items = mat.map((record, i) => {
|
|
251
|
+
if (record.status === "closed") {
|
|
252
|
+
const outcome = record.outcome;
|
|
253
|
+
if (outcome === "completed") finished += 1;
|
|
254
|
+
else if (outcome === "failed") failed += 1;
|
|
255
|
+
else if (outcome === "cancelled") cancelled += 1;
|
|
256
|
+
}
|
|
257
|
+
const body = bodies[i];
|
|
258
|
+
if (body === null || body.length <= plan.effectivePerItem) {
|
|
259
|
+
// 未触预算(含 failed/cancelled 条目):U3 基线字节不变。
|
|
260
|
+
return buildLlmContent(record);
|
|
261
|
+
}
|
|
262
|
+
// 截断:正文保留前 limit 字符 + 省略号,尾接指针行(kept=0 纯清单时省略号一并省略)。
|
|
263
|
+
const kept = body.slice(0, plan.effectivePerItem);
|
|
264
|
+
const truncated = buildLlmContent({ ...record, result: plan.listOnly ? "" : `${kept}…` });
|
|
265
|
+
// 纯清单时 result 置空会在 buildLlmContent 内残留头行尾换行("Result:\n" + ""),
|
|
266
|
+
// 收掉该空行让条目严格为「头行 + 指针行」两行形态(设计 §3.1.2 纯清单退化)。
|
|
267
|
+
const stripped = plan.listOnly ? truncated.replace(/\n$/, "") : truncated;
|
|
268
|
+
return `${stripped}\n${buildTruncationPointer(record.id, kept.length, body.length, plan.effectivePerItem)}`;
|
|
269
|
+
});
|
|
270
|
+
const header = `Subagent batch completed: ${finished} finished, ${failed} failed, ${cancelled} cancelled.`;
|
|
271
|
+
return [header, ...items].join("\n\n---\n\n");
|
|
272
|
+
}
|
|
273
|
+
|
|
107
274
|
/**
|
|
108
275
|
* 将 BgNotifyRecord 格式化为 LLM 可读的 notification content。
|
|
109
276
|
*
|
|
@@ -164,6 +331,13 @@ function buildLlmContent(record: BgNotifyRecord): string {
|
|
|
164
331
|
export interface BgNotifier {
|
|
165
332
|
/** 入队一条完成通知(去重 + 合批窗口合并)。dispose 后短路。 */
|
|
166
333
|
notify(record: BgNotifyRecord): void;
|
|
334
|
+
/**
|
|
335
|
+
* sync 批通知(subagent-sync-collect U3):成员集 → 单条批投递。幂等键
|
|
336
|
+
* `sync-batch:<sha1(sorted ids)>`,走与 notify 同一写账→settled 边沿投递链(单
|
|
337
|
+
* entry)。返回 false = 幂等拒绝(同成员集批已在账/已销账)或空批 / dispose 后——
|
|
338
|
+
* 调用方(service flushBatch 接线)据此决定 batchFinalized 落标。
|
|
339
|
+
*/
|
|
340
|
+
notifyBatch(records: readonly BgNotifyRecord[], budget?: BatchBudgetParams): boolean;
|
|
167
341
|
/** 立即 flush(session_shutdown 调用,防丢失)。 */
|
|
168
342
|
flushPendingNotifications(): void;
|
|
169
343
|
/** session 结束:清队列,dispose 内核 handle。 */
|
|
@@ -292,6 +466,37 @@ export function createNotifier(host: NotifierHost): BgNotifier {
|
|
|
292
466
|
};
|
|
293
467
|
let handle: DeliveryHandle = createHandle();
|
|
294
468
|
|
|
469
|
+
/** 四步投递链尾部共用段(S2,code-simplify):notify 单条与 notifyBatch 逐行同构
|
|
470
|
+
* 的收尾——原两份手写拷贝使注释宣称的「同一四步链」无结构强制,现单点实现。
|
|
471
|
+
* ledger 在 → ①写账(拒绝 = false,幂等去重:同 notifyId 已在账/已销账)→
|
|
472
|
+
* ②attemptDeliver(settled 边沿 + isIdle 二次复查,③④销账/重放在 ledger 内);
|
|
473
|
+
* ledger 缺(旧装配 / 部分测试,含 jiti 单例分裂的失效形态)→ 内核路径降级 +
|
|
474
|
+
* 降级留痕 warn(C-ext-06 配套,不改变向后兼容行为)。返回 false 仅 notifyBatch
|
|
475
|
+
* 消费(调用方跳过落标);notify 忽略(fire-and-forget)。闭包读 let handle:
|
|
476
|
+
* revive 重建后自然指向新 handle。 */
|
|
477
|
+
function deliverViaLedgerOrKernel(notifyId: string, content: string, details: object): boolean {
|
|
478
|
+
const ledger = getBoundNotifyLedger();
|
|
479
|
+
if (ledger) {
|
|
480
|
+
if (!ledger.record(notifyId, content, details)) return false;
|
|
481
|
+
ledger.attemptDeliver();
|
|
482
|
+
return true;
|
|
483
|
+
}
|
|
484
|
+
notifyLogger.warn("notify ledger not bound, falling back to delivery kernel path (at-most-once)", {
|
|
485
|
+
notifyId,
|
|
486
|
+
});
|
|
487
|
+
handle.send({
|
|
488
|
+
payload: {
|
|
489
|
+
kind: "custom",
|
|
490
|
+
customType: NOTIFY_CUSTOM_TYPE,
|
|
491
|
+
content,
|
|
492
|
+
display: true,
|
|
493
|
+
details,
|
|
494
|
+
},
|
|
495
|
+
dedupeKey: notifyId,
|
|
496
|
+
});
|
|
497
|
+
return true;
|
|
498
|
+
}
|
|
499
|
+
|
|
295
500
|
return {
|
|
296
501
|
notify(record: BgNotifyRecord): void {
|
|
297
502
|
if (disposed) return;
|
|
@@ -314,31 +519,35 @@ export function createNotifier(host: NotifierHost): BgNotifier {
|
|
|
314
519
|
// 投递尝试)→ ②courier 边沿投递(settled 边沿 + 120s 看门狗 + isIdle 二次
|
|
315
520
|
// 复查)→ ③回执销账 / ④重放幂等均在 ledger 内。record 返回 false = 同
|
|
316
521
|
// notifyId 已在账或已销账(幂等去重,含重启恢复后的已送达账号零重发)。
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
522
|
+
// 无 ledger 装配时内核路径降级 + 降级留痕(C-ext-06 配套,见 helper 注释)。
|
|
523
|
+
// 单条通知为 fire-and-forget,返回值忽略。
|
|
524
|
+
deliverViaLedgerOrKernel(notifyId, content, payload);
|
|
525
|
+
},
|
|
323
526
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
//
|
|
328
|
-
//
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
527
|
+
notifyBatch(records: readonly BgNotifyRecord[], budget?: BatchBudgetParams): boolean {
|
|
528
|
+
if (disposed || records.length === 0) return false;
|
|
529
|
+
|
|
530
|
+
// 投影边界物化(与 notify 同款):closed 成员补 outcome——批头计数与成员条目
|
|
531
|
+
// 单一来源。消源自入参浅拷贝,不改写协调器缓冲持有的快照对象。
|
|
532
|
+
const payloads = records.map((record) =>
|
|
533
|
+
record.status === "closed"
|
|
534
|
+
? { ...record, outcome: record.outcome ?? deriveOutcome(record.closedReason, record.error) }
|
|
535
|
+
: { ...record },
|
|
536
|
+
);
|
|
537
|
+
const batchNotifyId = buildBatchNotifyId(payloads.map((p) => p.id));
|
|
538
|
+
// budget(可选)= service flushBatch/E1 补发接线传入的 config 热读值(U4 deviation #8
|
|
539
|
+
// 由 U5 接线);undefined → buildBatchLlmContent 参数缺省 = 设计默认值(4000/24000)。
|
|
540
|
+
// 账本重放走写入时已定格的 content,预算在写账时刻生效(“flush 时热读”语义)。
|
|
541
|
+
const content = buildBatchLlmContent(payloads, budget);
|
|
542
|
+
// details 形态 = ledger mergeItems 批量分支({batch:true, items}——bg-notify-render
|
|
543
|
+
// extractBatch 已支持,⛔1 核对)+ 顶层 notifyId:collectDeliveredNotifyIds 的回执
|
|
544
|
+
// 匹配键认 details.notifyId / details.items[].notifyId,批身份键必须顶层可达,
|
|
545
|
+
// 否则批 entry 永不销账、重投至放弃。顶层 notifyId 键对 extractBatch 透明(多键无感)。
|
|
546
|
+
const details = { batch: true as const, notifyId: batchNotifyId, items: payloads };
|
|
547
|
+
|
|
548
|
+
// 与 notify 同一四步链(deliverViaLedgerOrKernel,S2 提取):返回 false = 同成
|
|
549
|
+
// 员集批已在账(E1 重建重发 / 重复 flush)→ 调用方跳过落标。
|
|
550
|
+
return deliverViaLedgerOrKernel(batchNotifyId, content, details);
|
|
342
551
|
},
|
|
343
552
|
|
|
344
553
|
flushPendingNotifications(): void {
|