@zhushanwen/subagent-core 0.4.0 → 0.5.1
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 +16418 -14070
- package/dist/index.d.cts +5136 -4016
- package/dist/index.d.ts +5136 -4016
- package/dist/index.js +16343 -14048
- package/dist/{types-DpUO16pj.d.cts → types-Dv4QhSJ_.d.cts} +215 -112
- package/dist/{types-DpUO16pj.d.ts → types-Dv4QhSJ_.d.ts} +215 -112
- 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__/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__/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 +3 -3
- package/src/execution/__tests__/finalize-record.test.ts +173 -17
- package/src/execution/__tests__/gc-timer.test.ts +55 -2
- 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__/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-ledger.test.ts +973 -18
- package/src/execution/__tests__/output-collector.test.ts +117 -6
- package/src/execution/__tests__/pi-invocation.test.ts +1 -1
- 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__/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 +35 -0
- 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 +1 -1
- 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-sync-model-guard.test.ts +4 -4
- 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 +42 -22
- package/src/execution/__tests__/subagent-service-message-close.test.ts +29 -26
- package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +8 -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__/temp-prompt.test.ts +1 -1
- package/src/execution/__tests__/timeout-integration.test.ts +6 -5
- 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-manager.test.ts +14 -5
- package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +4 -3
- 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/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/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 +211 -51
- 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/engine-conformance.live.test.ts +12 -12
- package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +1 -1
- 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 +25 -319
- 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 +222 -144
- package/src/execution/finalize-record.ts +131 -93
- package/src/execution/idle-gc.ts +28 -0
- package/src/execution/lifecycle-predicates.ts +1 -1
- package/src/execution/notify-host.ts +212 -0
- package/src/execution/record-store.ts +119 -73
- 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 +22 -22
- package/src/execution/subagent-service.ts +815 -963
- package/src/execution/subprocess-agent-runner.ts +144 -97
- package/src/execution/types.ts +9 -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__/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.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__/run-snapshot.test.ts +8 -5
- package/src/orchestration/__tests__/script-lint.test.ts +17 -0
- 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-nesting-e2e.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__/meta-parser.test.ts +151 -0
- 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
|
@@ -29,8 +29,25 @@
|
|
|
29
29
|
//
|
|
30
30
|
// 连接崩溃的 turn 收割(R4 已补齐):SessionChannel 在构造时订阅 AppServerConnection
|
|
31
31
|
// 的 onClose 面——进程死亡(崩溃/我方杀链)时立即 fail 全部在途 turn(错误即连接层
|
|
32
|
-
// 的崩溃 reason,含 stderr 尾部),不再依赖
|
|
32
|
+
// 的崩溃 reason,含 stderr 尾部),不再依赖 turn 等待预算挂满才收割。
|
|
33
33
|
// (onClose 由连接层保证在全部在途 request reject 之后触发。)
|
|
34
|
+
//
|
|
35
|
+
// turn 等待两 timer 状态机(P0-1 根修,设计权威源
|
|
36
|
+
// docs/design/timeout-zcode-turn-and-settled-watchdog.md §6 D1/D2):旧 300s 固定
|
|
37
|
+
// 墙钟(timer 从 send 起跳、事件不刷新,T001 实测 21% 活跃任务被误杀)替换为——
|
|
38
|
+
// 1. idle 主判定:本 turn 任何事件(session/event、telemetry stream.chunk/
|
|
39
|
+
// turn.terminal)刷新计时;连续静默达阈值判「执行已不可推进」(活跃事件流
|
|
40
|
+
// 零误杀,ADR-0047 逆否面);缺省 30min(ZCODE_TURN_IDLE_TIMEOUT_MS,
|
|
41
|
+
// ⛔P-Z1 标定前先验值);
|
|
42
|
+
// 2. 总上界回收兜底:从挂载起固定不刷新,兜 idle 覆盖不了的 chatty-wedge
|
|
43
|
+
// (事件持续但终态永不到达);缺省 60min(ZCODE_TURN_MAX_TIMEOUT_MS,
|
|
44
|
+
// ⛔P-Z0 标定前先验值;对超上界合法极长任务是显式接受的残余误杀面)。
|
|
45
|
+
// 任一 fire → reject TurnTimeoutError{kind:"idle"|"ceiling", lastEventAt,
|
|
46
|
+
// elapsed}(类型化,R4 引擎据此分流与合成 engine_timeout 文案)。两阈值 env
|
|
47
|
+
// 可调(XYZ_ZCODE_TURN_IDLE_TIMEOUT_MS / XYZ_ZCODE_TURN_MAX_TIMEOUT_MS)、
|
|
48
|
+
// ≤0 显式关闭(关闭时 warn 明示后果——规则 19 opt-out,A10① 断言依据)。
|
|
49
|
+
// create 应答先于挂 timer 到达(runTurn 先 createSession 后 openTurn),不参与
|
|
50
|
+
// 刷新。
|
|
34
51
|
|
|
35
52
|
import { createHash } from "node:crypto";
|
|
36
53
|
|
|
@@ -39,9 +56,14 @@ import { getLogger } from "../../../../core/logger.ts";
|
|
|
39
56
|
import type { AppServerConnection } from "./connection.ts";
|
|
40
57
|
import {
|
|
41
58
|
ZCODE_APPSERVER_TURN_CLOSE_TIMEOUT_MS,
|
|
42
|
-
ZCODE_APPSERVER_TURN_DEFAULT_TIMEOUT_MS,
|
|
43
59
|
ZCODE_APPSERVER_TURN_READ_TIMEOUT_MS,
|
|
60
|
+
ZCODE_TURN_IDLE_TIMEOUT_ENV,
|
|
61
|
+
ZCODE_TURN_IDLE_TIMEOUT_MS,
|
|
62
|
+
ZCODE_TURN_MAX_TIMEOUT_ENV,
|
|
63
|
+
ZCODE_TURN_MAX_TIMEOUT_MS,
|
|
64
|
+
parseZcodeTurnTimeoutEnv,
|
|
44
65
|
} from "./constants.ts";
|
|
66
|
+
import { toErrorMessage } from "../../../../core/error-message.ts";
|
|
45
67
|
|
|
46
68
|
const logger = getLogger("subagents");
|
|
47
69
|
|
|
@@ -62,7 +84,7 @@ function isRecord(v: unknown): v is Record<string, unknown> {
|
|
|
62
84
|
}
|
|
63
85
|
|
|
64
86
|
function errMessage(err: unknown): string {
|
|
65
|
-
return
|
|
87
|
+
return toErrorMessage(err);
|
|
66
88
|
}
|
|
67
89
|
|
|
68
90
|
// ============================================================
|
|
@@ -264,9 +286,69 @@ export interface SessionTurnCallbacks {
|
|
|
264
286
|
onSessionCreated?: (sessionId: string) => void;
|
|
265
287
|
}
|
|
266
288
|
|
|
289
|
+
/** turn 超时的判定形态(P0-1 D1:idle 主判定 / 总上界兜底——引擎分流与文案的判据)。 */
|
|
290
|
+
export type TurnTimeoutKind = "idle" | "ceiling";
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* turn 等待两 timer 的类型化超时错误(P0-1 D1/D4)。引擎(R4)按 `kind` 分流:
|
|
294
|
+
* 超时入口接 abort 链(stop 应答三态裁决)+ `engine_timeout` 前缀合成——不经
|
|
295
|
+
* 字符串匹配。字段供 outcome 文案使用:`elapsed` 距 openTurn 挂载的总时长;
|
|
296
|
+
* `lastEventAt` 最后一次事件到达时刻(整轮无任何事件时 undefined——进程假死/
|
|
297
|
+
* 协议静默形态的证据)。
|
|
298
|
+
*/
|
|
299
|
+
export class TurnTimeoutError extends Error {
|
|
300
|
+
readonly kind: TurnTimeoutKind;
|
|
301
|
+
readonly elapsed: number;
|
|
302
|
+
readonly lastEventAt: number | undefined;
|
|
303
|
+
readonly thresholdMs: number;
|
|
304
|
+
|
|
305
|
+
constructor(
|
|
306
|
+
kind: TurnTimeoutKind,
|
|
307
|
+
parts: {
|
|
308
|
+
thresholdMs: number;
|
|
309
|
+
elapsed: number;
|
|
310
|
+
lastEventAt: number | undefined;
|
|
311
|
+
}
|
|
312
|
+
) {
|
|
313
|
+
super(
|
|
314
|
+
kind === "idle"
|
|
315
|
+
? `zcode turn idle 判死:连续 ${parts.thresholdMs}ms 未观察到本 turn 任何事件` +
|
|
316
|
+
"(session/event 与 v4/telemetry/event 均静默),终态未到达。" +
|
|
317
|
+
(parts.lastEventAt === undefined
|
|
318
|
+
? "本轮自 send 起未观察到任何事件(进程假死/协议静默形态)。"
|
|
319
|
+
: `最后事件时刻 ${new Date(parts.lastEventAt).toISOString()}。`) +
|
|
320
|
+
"恢复指引:直接重跑本任务;若持续出现,检查 ZCode 桌面端模型连通性或改用 engine: pi。"
|
|
321
|
+
: `zcode turn 总上界判死:${parts.thresholdMs}ms 内未观察到终态` +
|
|
322
|
+
"(turn.terminal 与收尾帧均未到达;idle 判定未触发——事件流仍活跃,chatty-wedge 形态)。" +
|
|
323
|
+
(parts.lastEventAt === undefined
|
|
324
|
+
? ""
|
|
325
|
+
: `最后事件时刻 ${new Date(parts.lastEventAt).toISOString()}。`) +
|
|
326
|
+
`恢复指引:直接重跑本任务;若本任务属合法超长任务(预期超过 ${parts.thresholdMs}ms 总上界),` +
|
|
327
|
+
`重跑前设 ${ZCODE_TURN_MAX_TIMEOUT_ENV} 为更大毫秒值,或设 0 关闭总上界` +
|
|
328
|
+
"(关闭后 chatty 形态不再自动回收,静默 wedged 仍由 idle 层兜底——自行权衡)。"
|
|
329
|
+
);
|
|
330
|
+
this.name = "TurnTimeoutError";
|
|
331
|
+
this.kind = kind;
|
|
332
|
+
this.elapsed = parts.elapsed;
|
|
333
|
+
this.lastEventAt = parts.lastEventAt;
|
|
334
|
+
this.thresholdMs = parts.thresholdMs;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
267
338
|
export interface SessionTurnOptions extends SessionTurnCallbacks {
|
|
268
|
-
/**
|
|
339
|
+
/**
|
|
340
|
+
* 显式总上界(ms,P0-1 D1/D2 语义收窄:不再是从 send 起跳的固定墙钟缺省预算,
|
|
341
|
+
* 而是「显式总上界」传参面——缺省走 env `XYZ_ZCODE_TURN_MAX_TIMEOUT_MS` →
|
|
342
|
+
* `ZCODE_TURN_MAX_TIMEOUT_MS`(60min 先验值);≤0 显式关闭该 timer。工具面
|
|
343
|
+
* 不暴露(D2),引擎内部传参点(D6 重试预算继承传剩余值)。
|
|
344
|
+
*/
|
|
269
345
|
turnTimeoutMs?: number;
|
|
346
|
+
/**
|
|
347
|
+
* idle 主判定静默阈值(ms,P0-1 D2 内部传参点):缺省走 env
|
|
348
|
+
* `XYZ_ZCODE_TURN_IDLE_TIMEOUT_MS` → `ZCODE_TURN_IDLE_TIMEOUT_MS`(30min
|
|
349
|
+
* 先验值);≤0 显式关闭该 timer。
|
|
350
|
+
*/
|
|
351
|
+
idleTimeoutMs?: number;
|
|
270
352
|
}
|
|
271
353
|
|
|
272
354
|
/** 终态信号来源(D4:turn.terminal 权威;final-frame = 宽松判定防洪堤)。 */
|
|
@@ -279,6 +361,18 @@ export interface SessionTurnResult {
|
|
|
279
361
|
/** usage 优先级:收尾帧(A.2 权威)→ read step-finish tokens(宽容)→ 缺席。 */
|
|
280
362
|
usage?: SessionTurnUsage;
|
|
281
363
|
terminal: { status: string; source: TerminalSource };
|
|
364
|
+
/**
|
|
365
|
+
* 权威终态 turn.terminal 的 status 记录(P0-1 D5①/S5 修复):权威终态晚于
|
|
366
|
+
* final-frame 宽松终态到达时只记录不改写已落定 `terminal`——消费层据此识破
|
|
367
|
+
* 假成功(final-frame 恒 success)。无 turn.terminal 到达时缺席。
|
|
368
|
+
*/
|
|
369
|
+
lastTerminalStatus?: string;
|
|
370
|
+
/**
|
|
371
|
+
* 权威终态 turn.terminal 的错误详情(⛔P-Z2 实证:真实 failed 终态的 errorCode/
|
|
372
|
+
* errorMessage 只在 terminal 帧携带——read/delta 拿不到,丢即诊断信息永久丢失)。
|
|
373
|
+
* 先到/迟到都记录(与 lastTerminalStatus 同型);至少其一存在才带此字段。
|
|
374
|
+
*/
|
|
375
|
+
lastTerminalError?: { code?: string; message?: string };
|
|
282
376
|
}
|
|
283
377
|
|
|
284
378
|
// ============================================================
|
|
@@ -304,8 +398,85 @@ interface ActiveTurn {
|
|
|
304
398
|
finalText: string | undefined;
|
|
305
399
|
finalUsage: SessionTurnUsage | undefined;
|
|
306
400
|
terminal: TerminalInfo | undefined;
|
|
401
|
+
/** 权威终态 turn.terminal 的 status 记录(先到/迟到都记——D5①)。 */
|
|
402
|
+
lastTerminalStatus: string | undefined;
|
|
403
|
+
/** 权威终态 turn.terminal 的错误详情(先到/迟到都记——⛔P-Z2,terminal 帧独有)。 */
|
|
404
|
+
lastTerminalError: { code?: string; message?: string } | undefined;
|
|
405
|
+
/** 本轮生效阈值(openTurn 解析后的值,fire 时进 TurnTimeoutError 文案)。 */
|
|
406
|
+
idleMs: number;
|
|
407
|
+
ceilingMs: number;
|
|
408
|
+
/** 挂载时刻(elapsed 起算点)。 */
|
|
409
|
+
startedAt: number;
|
|
410
|
+
/** 最后一次事件到达时刻(epoch ms;整轮无事件 undefined——idle 判定的证据面)。 */
|
|
411
|
+
lastEventAt: number | undefined;
|
|
412
|
+
/** 两 timer 句柄(刷新=clearTimeout+重挂;settle/fail/fire 统一清理)。 */
|
|
413
|
+
idleTimer: NodeJS.Timeout | undefined;
|
|
414
|
+
ceilingTimer: NodeJS.Timeout | undefined;
|
|
307
415
|
settle: (t: TerminalInfo) => void;
|
|
308
416
|
fail: (err: Error) => void;
|
|
417
|
+
/** 超时落定(openTurn 装配:互斥守卫 + 清 timer + 类型化 reject)。 */
|
|
418
|
+
fireTimeout: (kind: TurnTimeoutKind) => void;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/** 两 timer 统一清理(settle/fail/超时 fire/runTurn finally 四路共匯)。 */
|
|
422
|
+
function clearTurnTimers(turn: ActiveTurn): void {
|
|
423
|
+
if (turn.idleTimer !== undefined) {
|
|
424
|
+
clearTimeout(turn.idleTimer);
|
|
425
|
+
turn.idleTimer = undefined;
|
|
426
|
+
}
|
|
427
|
+
if (turn.ceilingTimer !== undefined) {
|
|
428
|
+
clearTimeout(turn.ceilingTimer);
|
|
429
|
+
turn.ceilingTimer = undefined;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/** setTimeout + unref(守卫进程退出不被 turn 预算拖住——既有 300s timer 同形态)。 */
|
|
434
|
+
function armTurnTimer(onFire: () => void, ms: number): NodeJS.Timeout {
|
|
435
|
+
const timer = setTimeout(onFire, ms);
|
|
436
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
437
|
+
return timer;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* 单个 turn timer 阈值解析(P0-1 D2):显式传参 > env > 缺省默认。env ≤0 与显式
|
|
442
|
+
* ≤0 同为「显式关闭」语义(与 `XYZ_SUBAGENT_IDLE_TIMEOUT_MS` 先例的刻意分歧,
|
|
443
|
+
* 设计 D2/r3 SG-5 登记);env 非法 warn+回落默认。**关闭必须 warn 明示后果**——
|
|
444
|
+
* 静默失去回收层 = 「以为有兜底、实际裸奔」,生效行为必须可见(A10① 断言依据)。
|
|
445
|
+
*/
|
|
446
|
+
function resolveTurnTimerMs(parts: {
|
|
447
|
+
explicit: number | undefined;
|
|
448
|
+
envName: string;
|
|
449
|
+
fallbackMs: number;
|
|
450
|
+
label: string;
|
|
451
|
+
offConsequence: string;
|
|
452
|
+
}): number {
|
|
453
|
+
let value: number;
|
|
454
|
+
let source: string;
|
|
455
|
+
if (parts.explicit !== undefined) {
|
|
456
|
+
value = parts.explicit;
|
|
457
|
+
source = "显式传参";
|
|
458
|
+
} else {
|
|
459
|
+
const raw = process.env[parts.envName];
|
|
460
|
+
const parsed = parseZcodeTurnTimeoutEnv(raw);
|
|
461
|
+
if (parsed.state === "valid") {
|
|
462
|
+
value = parsed.ms;
|
|
463
|
+
source = `env ${parts.envName}=${raw}`;
|
|
464
|
+
} else {
|
|
465
|
+
if (parsed.state === "invalid") {
|
|
466
|
+
logger.warn(
|
|
467
|
+
`[session-channel] ${parts.envName}="${raw}" 非法(应为毫秒数字)——回落默认 ${parts.fallbackMs}ms(${parts.label})。设置正毫秒值覆盖,或 0 显式关闭`
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
value = parts.fallbackMs;
|
|
471
|
+
source = "默认值";
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
if (value <= 0) {
|
|
475
|
+
logger.warn(
|
|
476
|
+
`[session-channel] zcode turn ${parts.label}已关闭(${source})——${parts.offConsequence}。设正毫秒值(env ${parts.envName} 或显式传参)恢复回收层`
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
return value;
|
|
309
480
|
}
|
|
310
481
|
|
|
311
482
|
/**
|
|
@@ -319,6 +490,13 @@ export class SessionChannel {
|
|
|
319
490
|
/** sessionId → 在途 turn(终态落定后仍保留到 runTurn 收尾——收尾帧数据吸收窗口)。 */
|
|
320
491
|
private readonly activeTurns = new Map<string, ActiveTurn>();
|
|
321
492
|
private readonly offHandlers: Array<() => void>;
|
|
493
|
+
/**
|
|
494
|
+
* [P0-1 U5/D7] dispose 标志:dispose 收割后 closeSession 短路——收割形态下连接
|
|
495
|
+
* 可能仍 alive(close 事件迟到,finalize 未跑、child 非 null),此时发 close
|
|
496
|
+
* request 会写死进程(请求挂到控制面超时)或触发惰性重建(凭空 spawn 无人使用的
|
|
497
|
+
* 进程,违背 dispose 防泄漏语义);进程随即被引擎杀链回收,会话驻留随之消亡。
|
|
498
|
+
*/
|
|
499
|
+
private disposed = false;
|
|
322
500
|
|
|
323
501
|
constructor(conn: AppServerConnection) {
|
|
324
502
|
this.conn = conn;
|
|
@@ -331,13 +509,26 @@ export class SessionChannel {
|
|
|
331
509
|
),
|
|
332
510
|
// 连接崩溃收割:进程死亡(崩溃/我方杀链)立即 fail 全部在途 turn——
|
|
333
511
|
// onClose 契约保证触发时连接层在途 request 已全部 reject,此处补齐
|
|
334
|
-
// 「无在途 request 的 turn」的收割(否则挂到
|
|
512
|
+
// 「无在途 request 的 turn」的收割(否则挂到 turn idle/总上界预算耗尽)
|
|
335
513
|
conn.onClose((reason) => this.failAllTurns(`app-server ${reason}`)),
|
|
336
514
|
];
|
|
337
515
|
}
|
|
338
516
|
|
|
339
|
-
/**
|
|
517
|
+
/**
|
|
518
|
+
* [P0-1 U5/D7] dispose 前收割 + 退订:「退订 = 不会再有事件」在结构上蕴含「在途
|
|
519
|
+
* turn 不应再等」——close 缺失/被吞没形态(onClose 收割主路径未触发)下,在途
|
|
520
|
+
* turn 于退订前经既有 failAllTurns 收敛为明确失败,不再挂满自身 idle/总上界预算
|
|
521
|
+
* (设计 §3.4 退化路径闭合;race 窗口在引擎 shutdownRuntimeAndDisposeChannel,
|
|
522
|
+
* 量级与 awaitConnFinalized 同源 ZCODE_APPSERVER_HARVEST_GRACE_MS)。幂等:与
|
|
523
|
+
* onClose 收割先到者赢(turn.fail 的 settled 守卫——已落定 turn 不改写),
|
|
524
|
+
* activeTurns 已空则 no-op。不影响连接自身生命周期(R4)。
|
|
525
|
+
*/
|
|
340
526
|
dispose(): void {
|
|
527
|
+
this.disposed = true;
|
|
528
|
+
this.failAllTurns(
|
|
529
|
+
"zcode session channel 已 dispose:连接 close 事件未在收割窗口内到达," +
|
|
530
|
+
"在途 turn 于退订前收割为明确失败(不再挂到 turn 自身预算耗尽)。恢复指引:直接重跑本任务。",
|
|
531
|
+
);
|
|
341
532
|
for (const off of this.offHandlers.splice(0)) off();
|
|
342
533
|
}
|
|
343
534
|
|
|
@@ -413,6 +604,9 @@ export class SessionChannel {
|
|
|
413
604
|
* 对死连接发请求会触发惰性重建(凭空 spawn 一代无人使用的进程)。
|
|
414
605
|
*/
|
|
415
606
|
async closeSession(sessionId: string): Promise<void> {
|
|
607
|
+
// [P0-1 U5/D7] dispose 收割后短路(先于 alive 守卫——收割形态下 alive 可仍为
|
|
608
|
+
// true,见 disposed 字段注释)
|
|
609
|
+
if (this.disposed) return;
|
|
416
610
|
if (!this.conn.alive) return;
|
|
417
611
|
try {
|
|
418
612
|
await this.conn.request(
|
|
@@ -470,9 +664,15 @@ export class SessionChannel {
|
|
|
470
664
|
response,
|
|
471
665
|
...(usage !== undefined ? { usage } : {}),
|
|
472
666
|
terminal,
|
|
667
|
+
...(opened.turn.lastTerminalStatus !== undefined
|
|
668
|
+
? { lastTerminalStatus: opened.turn.lastTerminalStatus }
|
|
669
|
+
: {}),
|
|
670
|
+
...(opened.turn.lastTerminalError !== undefined
|
|
671
|
+
? { lastTerminalError: opened.turn.lastTerminalError }
|
|
672
|
+
: {}),
|
|
473
673
|
};
|
|
474
674
|
} finally {
|
|
475
|
-
|
|
675
|
+
opened.stopTimers();
|
|
476
676
|
this.activeTurns.delete(sessionId);
|
|
477
677
|
await this.closeSession(sessionId);
|
|
478
678
|
}
|
|
@@ -485,7 +685,7 @@ export class SessionChannel {
|
|
|
485
685
|
private openTurn(
|
|
486
686
|
sessionId: string,
|
|
487
687
|
opts: SessionTurnOptions
|
|
488
|
-
): { turn: ActiveTurn; done: Promise<TerminalInfo>;
|
|
688
|
+
): { turn: ActiveTurn; done: Promise<TerminalInfo>; stopTimers: () => void } {
|
|
489
689
|
let resolveDone!: (t: TerminalInfo) => void;
|
|
490
690
|
let rejectDone!: (err: Error) => void;
|
|
491
691
|
const done = new Promise<TerminalInfo>((res, rej) => {
|
|
@@ -500,38 +700,87 @@ export class SessionChannel {
|
|
|
500
700
|
finalText: undefined,
|
|
501
701
|
finalUsage: undefined,
|
|
502
702
|
terminal: undefined,
|
|
503
|
-
|
|
703
|
+
lastTerminalStatus: undefined,
|
|
704
|
+
lastTerminalError: undefined,
|
|
705
|
+
idleMs: 0,
|
|
706
|
+
ceilingMs: 0,
|
|
707
|
+
startedAt: Date.now(),
|
|
708
|
+
lastEventAt: undefined,
|
|
709
|
+
idleTimer: undefined,
|
|
710
|
+
ceilingTimer: undefined,
|
|
711
|
+
// 装配占位:下方 timer 创建后重绑(settle/fail 需要清理两 timer)
|
|
504
712
|
settle: () => {},
|
|
505
713
|
fail: () => {},
|
|
714
|
+
fireTimeout: () => {},
|
|
506
715
|
};
|
|
507
|
-
|
|
508
|
-
opts.
|
|
509
|
-
|
|
716
|
+
turn.idleMs = resolveTurnTimerMs({
|
|
717
|
+
explicit: opts.idleTimeoutMs,
|
|
718
|
+
envName: ZCODE_TURN_IDLE_TIMEOUT_ENV,
|
|
719
|
+
fallbackMs: ZCODE_TURN_IDLE_TIMEOUT_MS,
|
|
720
|
+
label: "idle 主判定",
|
|
721
|
+
offConsequence:
|
|
722
|
+
"静默 wedged(无事件)形态将无自动回收,任务可能挂到宿主进程退出",
|
|
723
|
+
});
|
|
724
|
+
turn.ceilingMs = resolveTurnTimerMs({
|
|
725
|
+
explicit: opts.turnTimeoutMs,
|
|
726
|
+
envName: ZCODE_TURN_MAX_TIMEOUT_ENV,
|
|
727
|
+
fallbackMs: ZCODE_TURN_MAX_TIMEOUT_MS,
|
|
728
|
+
label: "总上界",
|
|
729
|
+
offConsequence:
|
|
730
|
+
"chatty-wedge(有事件无终态)形态将无自动回收,仅剩 idle 静默判定兜底",
|
|
731
|
+
});
|
|
732
|
+
// 两 timer 状态机(P0-1 D1):idle 事件刷新重挂、总上界固定倒数;
|
|
733
|
+
// 任一 fire → 类型化 TurnTimeoutError reject(kind 供 R4 分流)。
|
|
734
|
+
const fireTimeout = (kind: TurnTimeoutKind): void => {
|
|
735
|
+
if (turn.settled) return;
|
|
510
736
|
turn.settled = true;
|
|
737
|
+
clearTurnTimers(turn);
|
|
511
738
|
this.activeTurns.delete(sessionId);
|
|
512
739
|
rejectDone(
|
|
513
|
-
new
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
740
|
+
new TurnTimeoutError(kind, {
|
|
741
|
+
thresholdMs: kind === "idle" ? turn.idleMs : turn.ceilingMs,
|
|
742
|
+
elapsed: Date.now() - turn.startedAt,
|
|
743
|
+
lastEventAt: turn.lastEventAt,
|
|
744
|
+
})
|
|
745
|
+
);
|
|
746
|
+
};
|
|
747
|
+
turn.fireTimeout = fireTimeout;
|
|
748
|
+
if (turn.idleMs > 0) {
|
|
749
|
+
turn.idleTimer = armTurnTimer(() => turn.fireTimeout("idle"), turn.idleMs);
|
|
750
|
+
}
|
|
751
|
+
if (turn.ceilingMs > 0) {
|
|
752
|
+
turn.ceilingTimer = armTurnTimer(
|
|
753
|
+
() => turn.fireTimeout("ceiling"),
|
|
754
|
+
turn.ceilingMs
|
|
517
755
|
);
|
|
518
|
-
}
|
|
519
|
-
if (typeof timer.unref === "function") timer.unref();
|
|
756
|
+
}
|
|
520
757
|
turn.settle = (t: TerminalInfo): void => {
|
|
521
758
|
if (turn.settled) return;
|
|
522
759
|
turn.settled = true;
|
|
523
760
|
turn.terminal = t;
|
|
524
|
-
|
|
761
|
+
clearTurnTimers(turn);
|
|
525
762
|
resolveDone(t);
|
|
526
763
|
};
|
|
527
764
|
turn.fail = (err: Error): void => {
|
|
528
765
|
if (turn.settled) return;
|
|
529
766
|
turn.settled = true;
|
|
530
|
-
|
|
767
|
+
clearTurnTimers(turn);
|
|
531
768
|
rejectDone(err);
|
|
532
769
|
};
|
|
533
770
|
this.activeTurns.set(sessionId, turn);
|
|
534
|
-
return { turn, done,
|
|
771
|
+
return { turn, done, stopTimers: () => clearTurnTimers(turn) };
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/**
|
|
775
|
+
* idle 主判定的事件刷新(P0-1 D1):本 turn 任何事件到达即重置 idle 倒数——
|
|
776
|
+
* 活跃事件流零误杀的结构保证。总上界不受影响(固定倒数)。已落定 turn 无
|
|
777
|
+
* timer 可刷新(只剩 lastEventAt 记账)。
|
|
778
|
+
*/
|
|
779
|
+
private refreshIdle(turn: ActiveTurn): void {
|
|
780
|
+
turn.lastEventAt = Date.now();
|
|
781
|
+
if (turn.settled || turn.idleTimer === undefined) return;
|
|
782
|
+
clearTimeout(turn.idleTimer);
|
|
783
|
+
turn.idleTimer = armTurnTimer(() => turn.fireTimeout("idle"), turn.idleMs);
|
|
535
784
|
}
|
|
536
785
|
|
|
537
786
|
private lookupTurn(sessionId: string | undefined): ActiveTurn | undefined {
|
|
@@ -543,12 +792,34 @@ export class SessionChannel {
|
|
|
543
792
|
return pending.length === 1 ? pending[0] : undefined;
|
|
544
793
|
}
|
|
545
794
|
|
|
795
|
+
/**
|
|
796
|
+
* turn.terminal 专用归因(P0-1 U1/S5 归因放宽):在 `lookupTurn` 基础上放宽
|
|
797
|
+
* 「已落定 turn 不再受理」——权威终态晚于 final-frame 宽松终态到达(runTurn
|
|
798
|
+
* 收尾窗口内 turn 仍在册)时仍归因,**只记录 status 不改写落定结果**(D5①)。
|
|
799
|
+
* 带 sid 精确归因(落定与否均可);无 sid 仍守宁丢勿错:唯一未落定优先,
|
|
800
|
+
* 全部落定且在册仅剩一个(单任务收尾窗口的典型形态)才归因,多在途丢弃。
|
|
801
|
+
*/
|
|
802
|
+
private lookupTurnForTerminal(
|
|
803
|
+
sessionId: string | undefined
|
|
804
|
+
): ActiveTurn | undefined {
|
|
805
|
+
if (sessionId !== undefined) return this.activeTurns.get(sessionId);
|
|
806
|
+
const pending: ActiveTurn[] = [];
|
|
807
|
+
for (const t of this.activeTurns.values()) if (!t.settled) pending.push(t);
|
|
808
|
+
if (pending.length === 1) return pending[0];
|
|
809
|
+
if (pending.length === 0 && this.activeTurns.size === 1) {
|
|
810
|
+
return [...this.activeTurns.values()][0];
|
|
811
|
+
}
|
|
812
|
+
return undefined;
|
|
813
|
+
}
|
|
814
|
+
|
|
546
815
|
private handleSessionEvent(params: unknown): void {
|
|
547
816
|
const turn = this.lookupTurn(extractPushSessionId(params));
|
|
548
817
|
if (turn === undefined) return;
|
|
549
818
|
const payload =
|
|
550
819
|
isRecord(params) && isRecord(params.payload) ? params.payload : undefined;
|
|
551
820
|
if (payload === undefined) return;
|
|
821
|
+
// 事件到达即刷新 idle 主判定(P0-1 D1——本 turn 的任何 session/event 都算进展)
|
|
822
|
+
this.refreshIdle(turn);
|
|
552
823
|
if (this.applyFinalFrame(turn, payload)) return;
|
|
553
824
|
this.applyStreamDelta(turn, payload);
|
|
554
825
|
}
|
|
@@ -607,27 +878,68 @@ export class SessionChannel {
|
|
|
607
878
|
private handleTelemetry(params: unknown): void {
|
|
608
879
|
if (!isRecord(params)) return;
|
|
609
880
|
if (params.kind === "turn.terminal") {
|
|
610
|
-
|
|
611
|
-
const turn = this.lookupTurn(extractPushSessionId(params));
|
|
612
|
-
if (turn === undefined) return;
|
|
613
|
-
turn.settle({
|
|
614
|
-
status: typeof params.status === "string" ? params.status : "unknown",
|
|
615
|
-
source: "turn.terminal",
|
|
616
|
-
});
|
|
881
|
+
this.applyTerminalTelemetry(params);
|
|
617
882
|
return;
|
|
618
883
|
}
|
|
619
884
|
if (params.kind === "stream.chunk") {
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
885
|
+
this.applyStreamChunkTelemetry(params);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
/** turn.terminal 遥测的吸收(含已落定 turn 的只记录路径,见 D5①)。 */
|
|
890
|
+
private applyTerminalTelemetry(params: Record<string, unknown>): void {
|
|
891
|
+
// 终态权威(A.2 ⑤):status success/error 均算终态(旧实证:不归类挂到超时)。
|
|
892
|
+
// 归因放宽(P0-1 S5):已落定 turn(final-frame 宽松终态先到)仍归因,
|
|
893
|
+
// 权威 status 只记录不改写落定结果(D5①——假成功的识破依据)。
|
|
894
|
+
const turn = this.lookupTurnForTerminal(extractPushSessionId(params));
|
|
895
|
+
if (turn === undefined) return;
|
|
896
|
+
const status =
|
|
897
|
+
typeof params.status === "string" ? params.status : "unknown";
|
|
898
|
+
turn.lastTerminalStatus = status;
|
|
899
|
+
this.recordTerminalError(turn, params);
|
|
900
|
+
if (turn.settled) {
|
|
901
|
+
logger.warn(
|
|
902
|
+
`权威终态晚于落定结果到达(会话 ${turn.sessionId},已落定 source=${
|
|
903
|
+
turn.terminal?.source
|
|
904
|
+
}):turn.terminal status="${status}" 仅记录不改写(P0-1 D5①)`
|
|
905
|
+
);
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
turn.settle({ status, source: "turn.terminal" });
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
/** terminal 帧专属错误信息入账(⛔P-Z2 实证:真实 failed 终态的 errorCode/errorMessage
|
|
912
|
+
* 只在 terminal 帧携带,read/delta 拿不到——先到/迟到都记录,消费层合成失败
|
|
913
|
+
* 文案时优先采信)。二者均缺失时不挂 lastTerminalError 字段。 */
|
|
914
|
+
private recordTerminalError(
|
|
915
|
+
turn: ActiveTurn,
|
|
916
|
+
params: Record<string, unknown>
|
|
917
|
+
): void {
|
|
918
|
+
const errorCode =
|
|
919
|
+
typeof params.errorCode === "string" ? params.errorCode : undefined;
|
|
920
|
+
const errorMessage =
|
|
921
|
+
typeof params.errorMessage === "string" ? params.errorMessage : undefined;
|
|
922
|
+
if (errorCode === undefined && errorMessage === undefined) return;
|
|
923
|
+
turn.lastTerminalError = {
|
|
924
|
+
...(errorCode !== undefined ? { code: errorCode } : {}),
|
|
925
|
+
...(errorMessage !== undefined ? { message: errorMessage } : {}),
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
/** stream.chunk 遥测的吸收(无文本遥测 + 文本字段漂移兜底)。 */
|
|
930
|
+
private applyStreamChunkTelemetry(params: Record<string, unknown>): void {
|
|
931
|
+
// A.2 ④:stream.chunk 无文本(chunkLength 遥测)。A.5 未确认项:是否偶发
|
|
932
|
+
// 携带文本字段——保留旧实现的形态漂移兜底:带文本则当 delta 收(不变量 1)
|
|
933
|
+
const turn = this.lookupTurn(extractPushSessionId(params));
|
|
934
|
+
if (turn === undefined || turn.settled) return;
|
|
935
|
+
// 遥测到达即刷新 idle 主判定(P0-1 D1:telemetry 事件同算进展)
|
|
936
|
+
this.refreshIdle(turn);
|
|
937
|
+
for (const key of ["chunk", "text", "content"] as const) {
|
|
938
|
+
const v = params[key];
|
|
939
|
+
if (typeof v === "string" && v !== "") {
|
|
940
|
+
turn.deltas.push(v);
|
|
941
|
+
turn.callbacks.onTextDelta?.(v);
|
|
942
|
+
return;
|
|
631
943
|
}
|
|
632
944
|
}
|
|
633
945
|
}
|