@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
|
@@ -159,6 +159,21 @@ interface NegativeFileEntry {
|
|
|
159
159
|
/** fileCache 值类型:正常条目或负缓存条目。 */
|
|
160
160
|
type FileCacheValue = FileCacheEntry | NegativeFileEntry;
|
|
161
161
|
|
|
162
|
+
/** scanFile 单文件本轮 stat 戳集合(jsonl + 3 sidecar)。 */
|
|
163
|
+
interface FileStamps {
|
|
164
|
+
jsonl: Stamp;
|
|
165
|
+
cancelled: Stamp | null;
|
|
166
|
+
finalized: Stamp | null;
|
|
167
|
+
alive: Stamp | null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** sidecar payload 读取结果(索引命中与探测重建两分支共享的读点)。 */
|
|
171
|
+
interface SidecarPayloads {
|
|
172
|
+
tomb: CancelledTombstone | undefined;
|
|
173
|
+
aliveData: AliveMarker | undefined;
|
|
174
|
+
finalReason: string | undefined;
|
|
175
|
+
}
|
|
176
|
+
|
|
162
177
|
/** 孤儿判定的末行读取初始窗口(常规 entry 远小于此,避免全文件读)。 */
|
|
163
178
|
// eslint-disable-next-line no-magic-numbers -- 64KB = 64 * 1024 bytes 字节换算常数
|
|
164
179
|
const LAST_LINE_WINDOW_BYTES = 64 * 1024;
|
|
@@ -244,40 +259,103 @@ function collectLastRecordEntries(content: string): Map<string, Record<string, u
|
|
|
244
259
|
return lastById;
|
|
245
260
|
}
|
|
246
261
|
|
|
262
|
+
/** entry data 字段的安全 string 读取(非 string → undefined)。 */
|
|
263
|
+
function entryStr(d: Record<string, unknown>, k: string): string | undefined {
|
|
264
|
+
return typeof d[k] === "string" ? (d[k] as string) : undefined;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** entry data 字段的安全 number 读取(非 number → undefined)。 */
|
|
268
|
+
function entryNum(d: Record<string, unknown>, k: string): number | undefined {
|
|
269
|
+
return typeof d[k] === "number" ? (d[k] as number) : undefined;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** 终态域投影:status 只认 "closed" 字面量(其余含缺省 → "running",旧调用方行为不变);
|
|
273
|
+
* closedReason 经枚举守卫(非法/缺省 → undefined)。 */
|
|
274
|
+
function readEntryTerminalFields(
|
|
275
|
+
d: Record<string, unknown>,
|
|
276
|
+
): Pick<SubagentRecord, "status" | "closedReason"> {
|
|
277
|
+
const closedReason = entryStr(d, "closedReason");
|
|
278
|
+
return {
|
|
279
|
+
status: d.status === "closed" ? "closed" : "running",
|
|
280
|
+
closedReason: isValidClosedReason(closedReason) ? closedReason : undefined,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/** 批收集域投影(U5 E1):仅显式字面量收敛,缺省 undefined(JSON 序列化自然缺省)。 */
|
|
285
|
+
function readEntryBatchFields(
|
|
286
|
+
d: Record<string, unknown>,
|
|
287
|
+
): Pick<SubagentRecord, "collectMode" | "batchFinalized" | "resumable"> {
|
|
288
|
+
return {
|
|
289
|
+
collectMode: d.collectMode === "sync" ? "sync" : undefined,
|
|
290
|
+
batchFinalized: d.batchFinalized === true ? true : undefined,
|
|
291
|
+
resumable: d.resumable === true ? true : undefined,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/** engine 域投影:engineFallback/engineHandle 经运行时 guard(未知 JSON 不裸收)。 */
|
|
296
|
+
function readEntryEngineFields(
|
|
297
|
+
d: Record<string, unknown>,
|
|
298
|
+
): Pick<SubagentRecord, "engine" | "engineFallback" | "engineHandle"> {
|
|
299
|
+
return {
|
|
300
|
+
engine: entryStr(d, "engine"),
|
|
301
|
+
engineFallback: isEngineFallbackShape(d.engineFallback) ? d.engineFallback : undefined,
|
|
302
|
+
engineHandle: isEngineHandleShape(d.engineHandle) ? d.engineHandle : undefined,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
247
306
|
/** entry data 即 SubagentRecord v1 快照——带运行时 guard 重建(taste/no-unsafe-cast)。
|
|
248
|
-
* 损坏 entry(agent/task/startedAt 任一缺失)返回 null,由调用方跳过。
|
|
307
|
+
* 损坏 entry(agent/task/startedAt 任一缺失)返回 null,由调用方跳过。
|
|
308
|
+
* [U5 E1] 投影白名单扩展(设计 §3.1.3「标记读取通路」):collectMode/batchFinalized
|
|
309
|
+
* + 终态五字段 status/endedAt/closedReason/result/error——原实现硬编码
|
|
310
|
+
* status:"running" 且不投影终态,E1 重建成员恒被视为 running,「全员终态→补发」
|
|
311
|
+
* 判定永假、补发内容缺失,整条补发路径成死代码。status 守卫只认 "closed" 字面量
|
|
312
|
+
* (其余含缺省 → "running",旧调用方 recoverEntryOnlyOrphans 行为不变——其候选
|
|
313
|
+
* 守卫已滤非 running 末条);closedReason 经 isValidClosedReason 枚举守卫。
|
|
314
|
+
* [v2 D3] 再补 resumable/sessionFile 两投影:成功成员崩溃时末条恒为轮终
|
|
315
|
+
* running+resumable entry(SP-5 有意语义),不投影 resumable 则 E1 无法与协调器
|
|
316
|
+
* hasRunningSync 同构豁免(§2.3 断链 3);sessionFile 原硬编码 undefined,导致
|
|
317
|
+
* E1 落标路径重建快照丢失反查索引锚(断链 1 前置依赖)。recoverEntryOnlyOrphans
|
|
318
|
+
* 的候选判定(isEntryOrphanCandidate)只认 status==="running",两新字段不参与
|
|
319
|
+
* 判定(P-rebuild 探针守卫面)。
|
|
320
|
+
* [E1 恢复批语义修复] 再补 patchFile 投影:entry data 携带该字段
|
|
321
|
+
* (toSubagentRecordEntry 落盘含 patchFile)但原投影丢弃 → E1 补发记录丢失
|
|
322
|
+
* worktree patch 的 git-apply 回收指针(正常 flush 路径经 toNotifyRecord 特意
|
|
323
|
+
* 携带,notify-host.ts patchFile 透传)。undefined 经 JSON.stringify 自然缺省,
|
|
324
|
+
* 无 patchFile 的存量 entry 序列化字节不变(落标出口经 toSubagentRecordEntry
|
|
325
|
+
* 按名重投影,重建对象字段顺序不影响序列化字节形态)。
|
|
326
|
+
* 字段顺序 = 对象字面量原序(终态/批收集/engine 域以 spread 在原位置展开),
|
|
327
|
+
* entry 序列化字节形态不变。 */
|
|
249
328
|
function rebuildEntryRecord(id: string, d: Record<string, unknown>): SubagentRecord | null {
|
|
250
|
-
const
|
|
251
|
-
const
|
|
252
|
-
const
|
|
253
|
-
const task = str("task");
|
|
254
|
-
const startedAt = num("startedAt");
|
|
329
|
+
const agent = entryStr(d, "agent");
|
|
330
|
+
const task = entryStr(d, "task");
|
|
331
|
+
const startedAt = entryNum(d, "startedAt");
|
|
255
332
|
if (agent === undefined || task === undefined || startedAt === undefined) return null; // 损坏 entry:跳过
|
|
256
333
|
return {
|
|
257
334
|
id,
|
|
258
335
|
agent,
|
|
259
336
|
task,
|
|
260
|
-
slug:
|
|
261
|
-
|
|
337
|
+
slug: entryStr(d, "slug") ?? "",
|
|
338
|
+
...readEntryTerminalFields(d),
|
|
262
339
|
mode: "background",
|
|
263
340
|
startedAt,
|
|
264
|
-
rootSessionId:
|
|
265
|
-
parentRecordId:
|
|
266
|
-
depth:
|
|
267
|
-
endedAt:
|
|
268
|
-
turns:
|
|
269
|
-
totalTokens:
|
|
270
|
-
model:
|
|
271
|
-
thinkingLevel:
|
|
341
|
+
rootSessionId: entryStr(d, "rootSessionId"),
|
|
342
|
+
parentRecordId: entryStr(d, "parentRecordId"),
|
|
343
|
+
depth: entryNum(d, "depth") ?? 0,
|
|
344
|
+
endedAt: entryNum(d, "endedAt"),
|
|
345
|
+
turns: entryNum(d, "turns") ?? 0,
|
|
346
|
+
totalTokens: entryNum(d, "totalTokens") ?? 0,
|
|
347
|
+
model: entryStr(d, "model") ?? "",
|
|
348
|
+
thinkingLevel: entryStr(d, "thinkingLevel"),
|
|
272
349
|
eventLog: [],
|
|
273
350
|
displayItems: [],
|
|
274
|
-
|
|
351
|
+
result: entryStr(d, "result"),
|
|
352
|
+
error: entryStr(d, "error"),
|
|
353
|
+
sessionFile: entryStr(d, "sessionFile"),
|
|
354
|
+
patchFile: entryStr(d, "patchFile"),
|
|
275
355
|
chatMode: d.chatMode === true,
|
|
276
|
-
round:
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
isEngineFallbackShape(d.engineFallback) ? d.engineFallback : undefined,
|
|
280
|
-
engineHandle: isEngineHandleShape(d.engineHandle) ? d.engineHandle : undefined,
|
|
356
|
+
round: entryNum(d, "round"),
|
|
357
|
+
...readEntryEngineFields(d),
|
|
358
|
+
...readEntryBatchFields(d),
|
|
281
359
|
};
|
|
282
360
|
}
|
|
283
361
|
|
|
@@ -335,6 +413,41 @@ function sameNullableStamp(a: Stamp | null, b: Stamp | null): boolean {
|
|
|
335
413
|
return sameStamp(a, b);
|
|
336
414
|
}
|
|
337
415
|
|
|
416
|
+
/** 缓存条目与本轮 stat 戳全同(jsonl + 3 sidecar,null 语义对齐)→ 零读取复用。 */
|
|
417
|
+
function isFreshCache(cached: FileCacheValue, stamps: FileStamps): boolean {
|
|
418
|
+
return (
|
|
419
|
+
sameStamp(cached.jsonl, stamps.jsonl) &&
|
|
420
|
+
sameNullableStamp(cached.cancelled, stamps.cancelled) &&
|
|
421
|
+
sameNullableStamp(cached.finalized, stamps.finalized) &&
|
|
422
|
+
sameNullableStamp(cached.alive, stamps.alive)
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* identity 三级定位——头部 64KB(首轮会话,~34%)→ 尾部 64KB(续聊场景最后一轮
|
|
428
|
+
* session_start 追加,~65%)→ 全文 fallback(~0.2%)。均不解析 message entries。
|
|
429
|
+
* size ≤ 头部读取上限时 head 读到的即全文,tail/anywhere 只会重复读同一份内容——
|
|
430
|
+
* 直接判负(head miss = 全文无 identity),省去同内容两连读。
|
|
431
|
+
*/
|
|
432
|
+
function detectIdentity(file: string, size: number): IdentityHeaderRecon | undefined {
|
|
433
|
+
return size <= IDENTITY_HEAD_BYTES
|
|
434
|
+
? readIdentityHeader(file)
|
|
435
|
+
: readIdentityHeader(file) ?? readIdentityTail(file) ?? readIdentityAnywhere(file);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* sidecar payload 读取(读顺序:tombstone → alive marker → finalized reason)。
|
|
440
|
+
* tombstone/alive 是活态数据,调用方沿用每轮重读语义;finalized reason 静态数据
|
|
441
|
+
* 仅在 sidecar 存在时读一次(文件小,成本可忽略)。
|
|
442
|
+
*/
|
|
443
|
+
function readSidecarPayloads(file: string, stamps: FileStamps): SidecarPayloads {
|
|
444
|
+
return {
|
|
445
|
+
tomb: stamps.cancelled !== null ? readCancelledTombstone(file) : undefined,
|
|
446
|
+
aliveData: stamps.alive !== null ? readAliveMarker(file) : undefined,
|
|
447
|
+
finalReason: stamps.finalized !== null ? readFinalizedReason(file) : undefined,
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
|
|
338
451
|
/** Pi ExtensionAPI 的最小子集(仅 collectRecords 跳过损坏 manifest 时上报用)。
|
|
339
452
|
* 解构为局部类型,避免与 subagent-service 的 PiLike 循环依赖。 */
|
|
340
453
|
export type RecordStorePi = {
|
|
@@ -595,35 +708,53 @@ export class RecordStore {
|
|
|
595
708
|
* - 文件不可读(IO 错误,可能暂时)→ 不判终态,落 resumable entry(防御性路径,
|
|
596
709
|
* IO 恢复后重开可重判)。
|
|
597
710
|
*
|
|
711
|
+
* [v2 D3] mainSessionFile = 覆写 merge 数据源(主 session 每 id 末条 entry,与 E1
|
|
712
|
+
* 的 scanLastRecordEntries 同款通路):崩溃前的批域标记(collectMode/batchFinalized)
|
|
713
|
+
* 与轮终 result/model 只活在主文件 entry——重建矩阵(buildRecord)的数据源
|
|
714
|
+
* (sidecar/子文件 identity)不含它们,覆写前不 merge 就会被抹掉(v2 §2.2 断链 2:
|
|
715
|
+
* E1 候选集恒空的真根因)。缺省(undefined)时 merge 无源,行为与旧版一致。
|
|
716
|
+
* 参数为追加式第二参(rootSessionFilter 保持首参):既有调用面只传过滤参。
|
|
717
|
+
*
|
|
598
718
|
* 防重:orphanJudged 实例级缓存(resumable 形态无 sidecar 锚,同进程重复调用跳过;
|
|
599
719
|
* 终态形态双重防护 = sidecar + 缓存)。调用方:index.ts session_start 恢复段(一次)。
|
|
600
720
|
*/
|
|
601
|
-
recoverOrphanRecords(rootSessionFilter?: string): void {
|
|
721
|
+
recoverOrphanRecords(rootSessionFilter?: string, mainSessionFile?: string): void {
|
|
722
|
+
const lastById = new Map(this.scanLastRecordEntries(mainSessionFile).map((r) => [r.id, r]));
|
|
602
723
|
for (const rec of this.reconstructAll(rootSessionFilter)) {
|
|
603
724
|
// 分支 4 命中集 = running 且无活进程实例(分支 3 带 externalInstance,分支 1/2 已 closed)。
|
|
604
725
|
if (rec.status !== "running" || rec.externalInstance !== undefined) continue;
|
|
605
726
|
if (this.orphanJudged.has(rec.id)) continue;
|
|
606
727
|
this.orphanJudged.add(rec.id);
|
|
607
|
-
this.finalizeOrphanRecord(rec);
|
|
728
|
+
this.finalizeOrphanRecord(rec, lastById.get(rec.id));
|
|
608
729
|
}
|
|
609
730
|
}
|
|
610
731
|
|
|
611
732
|
/**
|
|
612
733
|
* 单孤儿 record 的终态判定与落 entry(residual-fixes §5.2 三判据 + chat 分流)。
|
|
613
734
|
* 防重锚(orphanJudged 标记)已由调用方完成。
|
|
735
|
+
*
|
|
736
|
+
* [v2 D3] 覆写前 merge(lastEntry = 主 session 同 id 末条 entry 重建,调用方构建):
|
|
737
|
+
* 覆写是状态迁移不是信息重建,迁移不应丢末条既有信息。三个落 entry 分支(chatMode
|
|
738
|
+
* 分流 / IO 保守 / 终态覆写)统一基于 merge 后的 rec,同款口径。
|
|
614
739
|
*/
|
|
615
|
-
private finalizeOrphanRecord(rec: SubagentRecord): void {
|
|
616
|
-
|
|
740
|
+
private finalizeOrphanRecord(rec: SubagentRecord, lastEntry?: SubagentRecord): void {
|
|
741
|
+
// 仅补 undefined/空值字段、不覆盖已有值——覆写自带的 status/closedReason/endedAt/
|
|
742
|
+
// error 不在 merge 字段集,天然不受影响。批域字段(collectMode/batchFinalized)对
|
|
743
|
+
// 非 sync 成员恒 no-op(末条 entry collectMode undefined → 无值可补);result/model
|
|
744
|
+
// 的修补对 async/chat 成员同样生效——拉齐 light 重建丢 result/model 与 full 重建的
|
|
745
|
+
// 既有形态不一致(v2 D3 影响面诚实口径),不触碰任何通知文案(golden 不锁 entry 字节)。
|
|
746
|
+
const rec0 = lastEntry === undefined ? rec : RecordStore.mergeOrphanLastEntry(rec, lastEntry);
|
|
747
|
+
if (rec0.chatMode === true) {
|
|
617
748
|
// chat 会话跨重启等续聊:保留 running(可续聊),仅落执行态信号。
|
|
618
|
-
this.reportSubagentRecord({ ...
|
|
749
|
+
this.reportSubagentRecord({ ...rec0, resumable: true });
|
|
619
750
|
return;
|
|
620
751
|
}
|
|
621
|
-
const sessionFile =
|
|
752
|
+
const sessionFile = rec0.sessionFile;
|
|
622
753
|
if (sessionFile === undefined) return; // 无子文件锚(目录扫描源不可达,防御)
|
|
623
754
|
const lastLine = readLastJsonlLine(sessionFile);
|
|
624
755
|
if (!lastLine.ok) {
|
|
625
756
|
// IO 错误可能暂时——判终态不可逆,保守落 resumable 等重开重判。
|
|
626
|
-
this.reportSubagentRecord({ ...
|
|
757
|
+
this.reportSubagentRecord({ ...rec0, resumable: true });
|
|
627
758
|
return;
|
|
628
759
|
}
|
|
629
760
|
let parseOk = false;
|
|
@@ -639,7 +770,7 @@ export class RecordStore {
|
|
|
639
770
|
// 把正常完成的记录误标成断联。
|
|
640
771
|
writeFinalized(sessionFile, "gc");
|
|
641
772
|
this.reportSubagentRecord({
|
|
642
|
-
...
|
|
773
|
+
...rec0,
|
|
643
774
|
status: "closed",
|
|
644
775
|
closedReason: "gc",
|
|
645
776
|
endedAt: Date.now(),
|
|
@@ -647,6 +778,25 @@ export class RecordStore {
|
|
|
647
778
|
});
|
|
648
779
|
}
|
|
649
780
|
|
|
781
|
+
/** [v2 D3] 孤儿覆写 merge 字段集:末条 entry 的批域标记 + 轮终正文/模型,仅补 rec 侧
|
|
782
|
+
* undefined/空值(model 的空值形态是 ""——light 重建无 model_change entry 时起步
|
|
783
|
+
* 空串),不覆盖已有值。merge 后写 entry 经 reportSubagentRecord →
|
|
784
|
+
* toSubagentRecordEntry 序列化,undefined 字段自然缺省(不引入显式 null)。 */
|
|
785
|
+
private static mergeOrphanLastEntry(rec: SubagentRecord, last: SubagentRecord): SubagentRecord {
|
|
786
|
+
const pickStr = (cur: string | undefined, src: string | undefined): string | undefined =>
|
|
787
|
+
cur !== undefined && cur !== "" ? cur : src;
|
|
788
|
+
return {
|
|
789
|
+
...rec,
|
|
790
|
+
collectMode: rec.collectMode ?? last.collectMode,
|
|
791
|
+
batchFinalized: rec.batchFinalized ?? last.batchFinalized,
|
|
792
|
+
result: pickStr(rec.result, last.result),
|
|
793
|
+
// 类型收尾:两侧实参恒 string(rec.model 类型非可选;last.model 重建投影自带
|
|
794
|
+
// `?? ""`),pickStr 签名宽返回 string|undefined —— `?? ""` 运行时不可达,
|
|
795
|
+
// 仅满足 model 非可选类型,空串回退语义不变(cur 空 → src,src 也空 → "")。
|
|
796
|
+
model: pickStr(rec.model, last.model) ?? "",
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
|
|
650
800
|
/**
|
|
651
801
|
* [E2E 实测缺口] entry-born 孤儿恢复:register entry 已落主 session、但子 session 文件
|
|
652
802
|
* 从未创建(父进程死在 spawn 窗口期——register 写点与子进程首笔写入之间的窗口;外部
|
|
@@ -712,6 +862,32 @@ export class RecordStore {
|
|
|
712
862
|
});
|
|
713
863
|
}
|
|
714
864
|
|
|
865
|
+
/**
|
|
866
|
+
* [E1/U5] sync 批崩溃恢复扫描:主 session 文件「每 id 末条 subagent-record entry」
|
|
867
|
+
* (collectLastRecordEntries + rebuildEntryRecord 组合通路,设计 §3.1.3「标记读取
|
|
868
|
+
* 通路」——batchFinalized 落标 entry 写主 session 文件,本扫描同文件域才可见;禁走
|
|
869
|
+
* collectRecords light 路径,它只读子文件 identity 头+sidecar,主 session 落标
|
|
870
|
+
* entry 不可见)。返回每 id 末条重建的完整 record(含 collectMode/batchFinalized /
|
|
871
|
+
* 终态五字段,损坏 entry 跳过);调用方(service.recoverSyncCollectBatch 的 E1 过滤、
|
|
872
|
+
* recoverOrphanRecords 的覆写 merge)自行取舍。主文件不可读(含新 session 未 flush
|
|
873
|
+
* 的 ENOENT)→ 空数组静默。
|
|
874
|
+
*/
|
|
875
|
+
scanLastRecordEntries(mainSessionFile: string | undefined): SubagentRecord[] {
|
|
876
|
+
if (mainSessionFile === undefined) return [];
|
|
877
|
+
let content: string;
|
|
878
|
+
try {
|
|
879
|
+
content = fs.readFileSync(mainSessionFile, "utf-8");
|
|
880
|
+
} catch {
|
|
881
|
+
return []; // 与 recoverEntryOnlyOrphans 同判:best-effort 恢复,不可读静默跳过
|
|
882
|
+
}
|
|
883
|
+
const out: SubagentRecord[] = [];
|
|
884
|
+
for (const [id, d] of collectLastRecordEntries(content)) {
|
|
885
|
+
const rec = rebuildEntryRecord(id, d);
|
|
886
|
+
if (rec !== null) out.push(rec);
|
|
887
|
+
}
|
|
888
|
+
return out;
|
|
889
|
+
}
|
|
890
|
+
|
|
715
891
|
/** 订阅变更。返回取消订阅函数。 */
|
|
716
892
|
onChange(listener: ChangeListener): () => void {
|
|
717
893
|
this.listeners.add(listener);
|
|
@@ -865,18 +1041,15 @@ export class RecordStore {
|
|
|
865
1041
|
this.fileCache.delete(file);
|
|
866
1042
|
return null;
|
|
867
1043
|
}
|
|
868
|
-
const
|
|
869
|
-
|
|
870
|
-
|
|
1044
|
+
const stamps: FileStamps = {
|
|
1045
|
+
jsonl,
|
|
1046
|
+
cancelled: statStamp(`${file}.cancelled`),
|
|
1047
|
+
finalized: statStamp(`${file}.finalized`),
|
|
1048
|
+
alive: statStamp(`${file}.alive`),
|
|
1049
|
+
};
|
|
871
1050
|
|
|
872
1051
|
const cached = this.fileCache.get(file);
|
|
873
|
-
if (
|
|
874
|
-
cached !== undefined &&
|
|
875
|
-
sameStamp(cached.jsonl, jsonl) &&
|
|
876
|
-
sameNullableStamp(cached.cancelled, cancelled) &&
|
|
877
|
-
sameNullableStamp(cached.finalized, finalized) &&
|
|
878
|
-
sameNullableStamp(cached.alive, alive)
|
|
879
|
-
) {
|
|
1052
|
+
if (cached !== undefined && isFreshCache(cached, stamps)) {
|
|
880
1053
|
if (cached.negative) return null; // 负缓存命中:确认无 identity,零读取跳过
|
|
881
1054
|
// pid 探活结果不落盘(进程死亡无 IO)——分支 3 的 running 项每扫重查,
|
|
882
1055
|
// 保留原语义(旧实现每次 collectRecords 都重新 isProcessAlive)。
|
|
@@ -885,83 +1058,54 @@ export class RecordStore {
|
|
|
885
1058
|
}
|
|
886
1059
|
|
|
887
1060
|
// [perf L-1] 磁盘索引查询(首扫惰性装载,miss/空索引时 get 恒 undefined = 无索引)。
|
|
888
|
-
// 条目戳匹配 jsonl 当前 stat → 零内容读取构造缓存条目。
|
|
889
|
-
//
|
|
890
|
-
|
|
891
|
-
if (
|
|
892
|
-
const hit = this.indexEntries.get(path.basename(file));
|
|
893
|
-
if (hit !== undefined && hit.mtimeMs === jsonl.mtimeMs && hit.size === jsonl.size) {
|
|
894
|
-
if (hit.negative === true) {
|
|
895
|
-
// 负条目命中:「确认无 identity」跨实例持久,零探测跳过(与下方负缓存同款形态)。
|
|
896
|
-
this.fileCache.set(file, { negative: true, jsonl, cancelled, finalized, alive });
|
|
897
|
-
return null;
|
|
898
|
-
}
|
|
899
|
-
const tomb = cancelled !== null ? readCancelledTombstone(file) : undefined;
|
|
900
|
-
const aliveData = alive !== null ? readAliveMarker(file) : undefined;
|
|
901
|
-
const finalReason = finalized !== null ? readFinalizedReason(file) : undefined;
|
|
902
|
-
const entry: FileCacheEntry = {
|
|
903
|
-
light: RecordStore.buildRecord(
|
|
904
|
-
{ ...hit, forkDepth: undefined, sessionFile: file },
|
|
905
|
-
{ tomb, finalized: finalized !== null, finalizedReason: finalReason, alive: aliveData, jsonlMtimeMs: jsonl.mtimeMs, now },
|
|
906
|
-
),
|
|
907
|
-
full: undefined,
|
|
908
|
-
jsonl,
|
|
909
|
-
cancelled,
|
|
910
|
-
finalized,
|
|
911
|
-
alive,
|
|
912
|
-
tomb,
|
|
913
|
-
aliveData,
|
|
914
|
-
finalReason,
|
|
915
|
-
};
|
|
916
|
-
this.fileCache.set(file, entry);
|
|
917
|
-
this.idToFile.set(hit.id, file);
|
|
918
|
-
return entry;
|
|
919
|
-
}
|
|
920
|
-
}
|
|
1061
|
+
// 条目戳匹配 jsonl 当前 stat → 零内容读取构造缓存条目。undefined = 未命中
|
|
1062
|
+
// (落到下方探测),null = 负条目命中(零探测跳过)。
|
|
1063
|
+
const fromIndex = this.buildEntryFromIndex(file, stamps, now);
|
|
1064
|
+
if (fromIndex !== undefined) return fromIndex;
|
|
921
1065
|
|
|
922
1066
|
// [perf L-1] 索引 miss/戳不匹配落到原三级探测:本轮探测结果必须进索引(含负探测)。
|
|
923
1067
|
// 覆盖两种形态:首扫(映像已装载但 miss/不匹配)与后续轮次(映像已释放,凡进重建分支必是戳变化)。
|
|
924
1068
|
this.indexDirty = true;
|
|
925
1069
|
|
|
926
|
-
|
|
927
|
-
// 最后一轮 session_start 追加,~65%)→ 全文 fallback(~0.2%)。均不解析 message entries。
|
|
928
|
-
// size ≤ 头部读取上限时 head 读到的即全文,tail/anywhere 只会重复读同一份内容——
|
|
929
|
-
// 直接判负(head miss = 全文无 identity),省去同内容两连读。
|
|
930
|
-
const header =
|
|
931
|
-
jsonl.size <= IDENTITY_HEAD_BYTES
|
|
932
|
-
? readIdentityHeader(file)
|
|
933
|
-
: readIdentityHeader(file) ?? readIdentityTail(file) ?? readIdentityAnywhere(file);
|
|
1070
|
+
const header = detectIdentity(file, jsonl.size);
|
|
934
1071
|
if (!header) {
|
|
935
1072
|
// 负缓存:确认无 identity。后续扫描 stat 命中直接跳过;戳变化(文件补写)自动重试。
|
|
936
|
-
this.fileCache.set(file, { negative: true,
|
|
1073
|
+
this.fileCache.set(file, { negative: true, ...stamps });
|
|
937
1074
|
return null;
|
|
938
1075
|
}
|
|
939
|
-
const
|
|
940
|
-
const
|
|
941
|
-
const finalReason = finalized !== null ? readFinalizedReason(file) : undefined;
|
|
942
|
-
const entry: FileCacheEntry = {
|
|
943
|
-
light: RecordStore.buildRecord(header, {
|
|
944
|
-
tomb,
|
|
945
|
-
finalized: finalized !== null,
|
|
946
|
-
finalizedReason: finalReason,
|
|
947
|
-
alive: aliveData,
|
|
948
|
-
jsonlMtimeMs: jsonl.mtimeMs,
|
|
949
|
-
now,
|
|
950
|
-
}),
|
|
951
|
-
full: undefined,
|
|
952
|
-
jsonl,
|
|
953
|
-
cancelled,
|
|
954
|
-
finalized,
|
|
955
|
-
alive,
|
|
956
|
-
tomb,
|
|
957
|
-
aliveData,
|
|
958
|
-
finalReason,
|
|
959
|
-
};
|
|
1076
|
+
const payloads = readSidecarPayloads(file, stamps);
|
|
1077
|
+
const entry = RecordStore.buildFileCacheEntry(header, file, stamps, payloads, now);
|
|
960
1078
|
this.fileCache.set(file, entry);
|
|
961
1079
|
this.idToFile.set(header.id, file);
|
|
962
1080
|
return entry;
|
|
963
1081
|
}
|
|
964
1082
|
|
|
1083
|
+
/**
|
|
1084
|
+
* [perf L-1] 磁盘索引查询(首扫惰性装载,miss/空索引时 get 恒 undefined = 无索引)。
|
|
1085
|
+
* 条目戳匹配 jsonl 当前 stat → 零内容读取构造缓存条目。sidecar payload(tombstone/
|
|
1086
|
+
* alive)是活态数据,沿用探测分支的每轮重读语义;finalized reason 静态数据仅在
|
|
1087
|
+
* sidecar 存在时读一次(文件小,成本可忽略)。
|
|
1088
|
+
*
|
|
1089
|
+
* 返回 undefined = 索引未命中/戳不匹配(调用方落到原三级探测);null = 负条目命中
|
|
1090
|
+
* (「确认无 identity」跨实例持久,零探测跳过,与内存负缓存同款形态)。
|
|
1091
|
+
*/
|
|
1092
|
+
private buildEntryFromIndex(file: string, stamps: FileStamps, now: number): FileCacheEntry | null | undefined {
|
|
1093
|
+
if (this.indexEntries === null) return undefined;
|
|
1094
|
+
const hit = this.indexEntries.get(path.basename(file));
|
|
1095
|
+
if (hit === undefined || hit.mtimeMs !== stamps.jsonl.mtimeMs || hit.size !== stamps.jsonl.size) {
|
|
1096
|
+
return undefined;
|
|
1097
|
+
}
|
|
1098
|
+
if (hit.negative === true) {
|
|
1099
|
+
this.fileCache.set(file, { negative: true, ...stamps });
|
|
1100
|
+
return null;
|
|
1101
|
+
}
|
|
1102
|
+
const payloads = readSidecarPayloads(file, stamps);
|
|
1103
|
+
const entry = RecordStore.buildFileCacheEntry({ ...hit, forkDepth: undefined, sessionFile: file }, file, stamps, payloads, now);
|
|
1104
|
+
this.fileCache.set(file, entry);
|
|
1105
|
+
this.idToFile.set(hit.id, file);
|
|
1106
|
+
return entry;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
965
1109
|
/**
|
|
966
1110
|
* [perf L-1] 扫描尾索引落盘(节流):释放映像 → dirty/高版本/60s 节流窗三重门 →
|
|
967
1111
|
* fire-and-forget saveIndex(fileCache 全量投影)。写决策与发起在同步栈(collectRecords
|
|
@@ -1089,6 +1233,34 @@ export class RecordStore {
|
|
|
1089
1233
|
entry.light.externalInstance = live ? marker : undefined;
|
|
1090
1234
|
}
|
|
1091
1235
|
|
|
1236
|
+
/** identity 基底 + sidecar 状态矩阵 → 缓存条目(索引命中与探测重建两分支的公共装配点)。 */
|
|
1237
|
+
private static buildFileCacheEntry(
|
|
1238
|
+
base: IdentityHeaderRecon,
|
|
1239
|
+
file: string,
|
|
1240
|
+
stamps: FileStamps,
|
|
1241
|
+
payloads: SidecarPayloads,
|
|
1242
|
+
now: number,
|
|
1243
|
+
): FileCacheEntry {
|
|
1244
|
+
return {
|
|
1245
|
+
light: RecordStore.buildRecord(base, {
|
|
1246
|
+
tomb: payloads.tomb,
|
|
1247
|
+
finalized: stamps.finalized !== null,
|
|
1248
|
+
finalizedReason: payloads.finalReason,
|
|
1249
|
+
alive: payloads.aliveData,
|
|
1250
|
+
jsonlMtimeMs: stamps.jsonl.mtimeMs,
|
|
1251
|
+
now,
|
|
1252
|
+
}),
|
|
1253
|
+
full: undefined,
|
|
1254
|
+
jsonl: stamps.jsonl,
|
|
1255
|
+
cancelled: stamps.cancelled,
|
|
1256
|
+
finalized: stamps.finalized,
|
|
1257
|
+
alive: stamps.alive,
|
|
1258
|
+
tomb: payloads.tomb,
|
|
1259
|
+
aliveData: payloads.aliveData,
|
|
1260
|
+
finalReason: payloads.finalReason,
|
|
1261
|
+
};
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1092
1264
|
/** identity 基底(头部 light 或全量 recon)+ 四分支 sidecar 状态矩阵 → SubagentRecord。 */
|
|
1093
1265
|
private static buildRecord(
|
|
1094
1266
|
base: IdentityHeaderRecon | ReconstructedRecord,
|
|
@@ -1274,6 +1446,11 @@ export class RecordStore {
|
|
|
1274
1446
|
engineFallback: r.engineFallback,
|
|
1275
1447
|
// U2:engineHandle 经 entry 持久化(register/archive 双写点均经本投影),无则 undefined 自然省略
|
|
1276
1448
|
engineHandle: r.engineHandle,
|
|
1449
|
+
// [U5 修复 U2 披露的投影缺口] 同步收集两字段随本投影持久化(register entry /
|
|
1450
|
+
// archive entry 双写点)——原缺失时闭合判定 flushBatch 重建、E1 重建扫描等消费方
|
|
1451
|
+
// 读不到原始值。undefined 经 JSON.stringify 自然缺省,旧 entry 零迁移。
|
|
1452
|
+
collectMode: r.collectMode,
|
|
1453
|
+
batchFinalized: r.batchFinalized,
|
|
1277
1454
|
};
|
|
1278
1455
|
}
|
|
1279
1456
|
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// [D4-② 轮次结算职责轴] chatMode 轮次完成的业务闭包(原 SubagentService
|
|
2
|
+
// buildSessionRunnerContext 内的 onRoundSettled 回调 ~95 行区域)整体搬移至此
|
|
3
|
+
// (行为逐字节等价:搬移 + 依赖注入,不重写逻辑)。变化轴:改轮次增量派生 /
|
|
4
|
+
// notify 时序 / base 推进 / closeAfterRound 消费语义,只改本文件;Service 经
|
|
5
|
+
// buildSessionRunnerContext 注入三个依赖回调(notify / 持久化上报 / close 兑现)。
|
|
6
|
+
|
|
7
|
+
import { getLogger } from "../core/logger.ts";
|
|
8
|
+
|
|
9
|
+
import { getFullTextFrom, nextRoundBaseTurnIndex } from "./execution-record.ts";
|
|
10
|
+
import type { ExecutionRecord } from "./types.ts";
|
|
11
|
+
|
|
12
|
+
const logger = getLogger("subagents");
|
|
13
|
+
|
|
14
|
+
/** 轮次结算的依赖注入(Service 侧供给)。 */
|
|
15
|
+
export interface RoundSettlementDeps {
|
|
16
|
+
/** 本轮增量的通知投递(Service.notifyComplete——经 notifyHost)。 */
|
|
17
|
+
notifyComplete: (record: ExecutionRecord) => void;
|
|
18
|
+
/** 轮终迁移持久化上报(store.reportRecordTransition——live ≡ reload 等价性)。 */
|
|
19
|
+
reportRecordTransition: (record: ExecutionRecord) => void;
|
|
20
|
+
/** closeAfterRound 兑现(Service.closeAfterRoundSettled——chatMode 优雅关闭的终态化)。 */
|
|
21
|
+
closeAfterRoundSettled: (record: ExecutionRecord) => Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** 构造 onRoundSettled 回调(session-runner agent_settled 时调用)。
|
|
25
|
+
*
|
|
26
|
+
* [V2 决策 2] chatMode 首轮闭环:session-runner 已 arm idle timer,
|
|
27
|
+
* isIdle=true 让 notify 守卫放行(时序:armIdleTimer → onRoundSettled)。
|
|
28
|
+
* 回调体原为 subagent-service.ts buildSessionRunnerContext 的内联闭包,D4-② 拆出。 */
|
|
29
|
+
export function createRoundSettler(deps: RoundSettlementDeps): (record: ExecutionRecord) => void {
|
|
30
|
+
return (record) => {
|
|
31
|
+
// v4 B-1:status 保持 running(旧 idle 折入 running);session-runner 已 arm idle timer,
|
|
32
|
+
// isIdle=true 让 notify 守卫放行(时序:armIdleTimer → onRoundSettled,见 session-runner.ts:670)。
|
|
33
|
+
// round 可能初始 undefined(与 notifier.ts `record.round ?? 0` 兜底一致),
|
|
34
|
+
// 首轮 0+1=1。round 是 notifier dedup key 的组成部分,递增后同 id 下一轮不被 60s dedup 吞。
|
|
35
|
+
record.round = (record.round ?? 0) + 1;
|
|
36
|
+
// [N2][增量] 轮次回复写点(增量语义):roundText 自 roundBaseTurnIndex 起派生本轮增量
|
|
37
|
+
//(undefined 视为 0——首轮增量 = 全量,与改造前首轮逐字节一致)。成功轮次的 MF-2 原写点
|
|
38
|
+
//(doFinalizeRoundToIdle)不可达——agent_settled 恒 arm idle timer → runAndFinalize 恒
|
|
39
|
+
// early return。写入 record.result 后再 notify;本轮无非空增量且无 lastError(纯工具轮 /
|
|
40
|
+
// interrupt 抢占轮 / 模型空回复)时固定占位 "(no output this round)"(D5:增量语义下沿用
|
|
41
|
+
// 旧 record.result = 上一轮增量 → 本轮通知正文 = 上一轮内容,父 agent 误读为原样重复回复;
|
|
42
|
+
// lastError 兜底保留让失败轮通知可读)。后续 closeAfterRoundSettled 的合成 result 读
|
|
43
|
+
// record.result,同样携带本轮增量。
|
|
44
|
+
const roundText = getFullTextFrom(record, record.roundBaseTurnIndex ?? 0);
|
|
45
|
+
record.result = roundText ||
|
|
46
|
+
(record.lastError ? `round did not complete: ${record.lastError}` : "(no output this round)");
|
|
47
|
+
// 先送达本轮增量(notify),再推进 base / 消费 closeAfterRound——终态通知由
|
|
48
|
+
// closeAfterRoundSettled / closeChatIdle 的 notifyClosed 显式发出(dedup 身份为裸 id,
|
|
49
|
+
// 与本次 round notify 的 id:round key 区分),保证「本轮增量 + 终态通知」都送达;
|
|
50
|
+
// 轮次收尾 .then 的冷路径 notifyComplete 仍与本次 round notify 同 key 被 60s
|
|
51
|
+
// dedup 吞(不构成第三条)。
|
|
52
|
+
//
|
|
53
|
+
// 幂等性(覆盖面如实限定):同步路径 at-least-once——notifyComplete(同步 void)抛错时
|
|
54
|
+
// 推进/消费被跳过 → base 不推进 → 增量未消费,下轮 roundText 必含本轮文本(重发载体为
|
|
55
|
+
// 后续轮次增量拼接)。轮次收尾 .then 的冷路径 notifyComplete 不构成重发通道
|
|
56
|
+
// (notifier dedup.set 与 pending.splice 均先于 sendMessage,同 key `${id}:${round}`——
|
|
57
|
+
// round 已递增——60s 窗内重入被吞)。异步 flush 窗口不保证:合并 timer armed(其他 busy
|
|
58
|
+
// background 在场)或 isIdle 退避期间 notify 的『成功』只是入队,实际 sendMessage 发生在
|
|
59
|
+
// base 推进之后的异步时机;该窗口进程崩溃或 sendMessage 失败 → 丢失不可重发(现状全量
|
|
60
|
+
// 重发的次轮自愈在增量语义下消失),wave1 期恢复通道仅父 agent 经 /subagents 详情读取
|
|
61
|
+
// record.result,wave2 指针行落地后补全。反序(先推进后 notify)在同步路径 notify 失败时
|
|
62
|
+
// 静默丢增量且无任何重算机会,故 notify 后推进是定案。重复发送由 notifier dedup key
|
|
63
|
+
// 60s 窗界定。
|
|
64
|
+
deps.notifyComplete(record);
|
|
65
|
+
// R1 观测哨(不变式违反形态):推进前检查末 turn 未闭合且 text 非空——pi 现序下不可达
|
|
66
|
+
//(带 usage 的 message_end 恒先于 turn_end,settle 时 turn 全闭合,见 types.ts
|
|
67
|
+
// roundBaseTurnIndex 注释的行号锚定),ES1 单测自造事件序列锁不住 pi 层变化;pi 升级若
|
|
68
|
+
// 改变 turn_end/agent_end 时序,此哨兵留痕(该形态下公式仍把文本计入本轮,不丢数据)。
|
|
69
|
+
const lastTurn = record.turns[record.turns.length - 1];
|
|
70
|
+
if (lastTurn !== undefined && !lastTurn.closed && lastTurn.text.length > 0) {
|
|
71
|
+
logger.warn(
|
|
72
|
+
`[subagents] round settle with unclosed non-empty turn (record=${record.id}, turnIndex=${record.turns.length - 1}) — pi turn_end/agent_end ordering may have changed`,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
// [增量] base 推进(notify 之后):下一轮增量从本轮边界起。滞后空 turn 不计入边界
|
|
76
|
+
//(防御分支,留在下一轮增量内防丢文本——nextRoundBaseTurnIndex 注释)。
|
|
77
|
+
record.roundBaseTurnIndex = nextRoundBaseTurnIndex(record);
|
|
78
|
+
// 轮终迁移持久化(residual-fixes U3 补全):热路径轮终不经 doFinalizeRoundToIdle
|
|
79
|
+
//(agent_settled 恒 arm idle timer → runAndFinalize 恒 early return,MF-2 原写点
|
|
80
|
+
// 不可达)——不 appendEntry 则 runtime/W18 派生缓存不失效,renderer 停留在
|
|
81
|
+
// register 快照(无 result),chat 等续聊的 waiting 形态显示不出来、spinner
|
|
82
|
+
// 卡死。显式上报:entry 携带本轮 result/round/chatMode(§5.4:result 有值 +
|
|
83
|
+
// chatMode=true → waiting)。closeAfterRound 的终态 entry 在此后追加,序不变。
|
|
84
|
+
deps.reportRecordTransition(record);
|
|
85
|
+
// [M5] closeAfterRound 消费点:chatMode 每轮完成的统一汇聚点(热路径轮不经
|
|
86
|
+
// runAndFinalize CAS 分支——agent_settled 恒 arm idle timer → runAndFinalize 恒
|
|
87
|
+
// early return,旧消费点对 chatMode 不可达,标志置了无人消费、tool 谎报 closed:true)。
|
|
88
|
+
if (record.closeAfterRound) {
|
|
89
|
+
record.closeAfterRound = undefined;
|
|
90
|
+
void deps.closeAfterRoundSettled(record);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|