@zhushanwen/subagent-core 0.7.1 → 0.9.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-VURUAGMM.js → chunk-LZJOEDGQ.js} +603 -154
- package/dist/engine-discovery-scan-Da4Dkzsi.d.cts +818 -0
- package/dist/engine-discovery-scan-Da4Dkzsi.d.ts +818 -0
- package/dist/execution/engine/engine-discovery-scan.cjs +325 -154
- package/dist/execution/engine/engine-discovery-scan.d.cts +1 -2
- package/dist/execution/engine/engine-discovery-scan.d.ts +1 -2
- package/dist/execution/engine/engine-discovery-scan.js +1 -1
- package/dist/execution/relay-env.js +5 -4
- package/dist/index.cjs +9799 -7954
- package/dist/index.d.cts +1896 -1360
- package/dist/index.d.ts +1896 -1360
- package/dist/index.js +8952 -7513
- package/dist.bundle/index.cjs +7897 -6054
- package/package.json +5 -5
- package/src/__tests__/manifest-store.test.ts +16 -15
- package/src/__tests__/record-store-last-line.test.ts +12 -0
- package/src/__tests__/robustness-medium-batch1.test.ts +10 -14
- package/src/__tests__/robustness-medium-batch2.test.ts +7 -2
- package/src/core/logger.ts +1 -1
- package/src/execution/__tests__/alive-store.test.ts +117 -1
- package/src/execution/__tests__/batch-finalized.test.ts +206 -0
- package/src/execution/__tests__/chat-engine-routing.test.ts +117 -1
- package/src/execution/__tests__/{cold-resurrect.test.ts → cold-lookup.test.ts} +133 -33
- package/src/execution/__tests__/collect-coordinator-service.test.ts +7 -5
- package/src/execution/__tests__/conversation-continuation.test.ts +1452 -0
- package/src/execution/__tests__/delivery-methods.test.ts +90 -176
- package/src/execution/__tests__/dialog-queue.test.ts +70 -3
- package/src/execution/__tests__/dispose-manifest-recovery.test.ts +475 -0
- package/src/execution/__tests__/execute-and-await-worktree.test.ts +3 -3
- package/src/execution/__tests__/execute-nesting.test.ts +8 -5
- package/src/execution/__tests__/execution-record.test.ts +8 -148
- package/src/execution/__tests__/finalize-record.test.ts +295 -129
- package/src/execution/__tests__/get-record-for-action-restart.test.ts +21 -29
- package/src/execution/__tests__/helpers/fake-engine-port.ts +13 -68
- package/src/execution/__tests__/helpers/legacy-sidecar.ts +33 -0
- package/src/execution/__tests__/helpers/subagent-service-mocks.ts +9 -5
- package/src/execution/__tests__/idle-gc.test.ts +60 -0
- package/src/execution/__tests__/inflight-production-wiring.test.ts +223 -0
- package/src/execution/__tests__/lifecycle-manager.test.ts +3 -254
- package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +43 -22
- package/src/execution/__tests__/model-config-service.test.ts +111 -0
- package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +10 -6
- package/src/execution/__tests__/nested-visibility.test.ts +13 -1
- package/src/execution/__tests__/notify-ledger.test.ts +42 -1
- package/src/execution/__tests__/rebuild-indexes.test.ts +329 -0
- package/src/execution/__tests__/record-binding.test.ts +613 -0
- package/src/execution/__tests__/record-origin.test.ts +318 -0
- package/src/execution/__tests__/record-store-intent-api.test.ts +600 -0
- package/src/execution/__tests__/record-store-orphan-revive.test.ts +71 -3
- package/src/execution/__tests__/record-store.test.ts +72 -54
- package/src/execution/__tests__/recursive-visibility-baseline.test.ts +8 -5
- package/src/execution/__tests__/round-supervisor-workflow-origin.test.ts +205 -0
- package/src/execution/__tests__/run-orchestration-write-lease.test.ts +293 -0
- package/src/execution/__tests__/run-orchestration.test.ts +199 -0
- package/src/execution/__tests__/session-file-gc.test.ts +35 -4
- package/src/execution/__tests__/session-reconstructor.test.ts +78 -0
- package/src/execution/__tests__/state-marker.test.ts +288 -0
- package/src/execution/__tests__/stream-sink-retirement.test.ts +8 -5
- package/src/execution/__tests__/subagent-actions-core.test.ts +67 -16
- package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +0 -1
- package/src/execution/__tests__/subagent-service-notify-gate.test.ts +27 -11
- package/src/execution/__tests__/subagent-service-parent-guard.test.ts +17 -11
- package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +130 -53
- package/src/execution/__tests__/subagent-service.test.ts +17 -215
- package/src/execution/__tests__/subprocess-agent-runner-no-progress-killall.test.ts +138 -0
- package/src/execution/__tests__/subprocess-agent-runner.test.ts +105 -660
- package/src/execution/__tests__/sync-collect-recovery.test.ts +23 -16
- package/src/execution/__tests__/terminal-write-latency.test.ts +79 -0
- package/src/execution/__tests__/workflow-agent-dispatch.test.ts +782 -0
- package/src/execution/alive-store.ts +37 -28
- package/src/execution/{cold-resurrect.ts → cold-lookup.ts} +48 -45
- package/src/execution/config.ts +1 -1
- package/src/execution/conversation-continuation.ts +541 -0
- package/src/execution/dialog-queue.ts +60 -12
- package/src/execution/engine/__tests__/common/capability-gate.test.ts +1 -1
- package/src/execution/engine/__tests__/common/journal-wiring.test.ts +16 -1
- package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/fake-engine-protocol.mjs +17 -51
- package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/smoke-run.fixture.json +0 -5
- package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +1 -1
- package/src/execution/engine/__tests__/conformance/protocol-blackbox.test.ts +5 -9
- package/src/execution/engine/__tests__/conformance/registry-fork-filter.test.ts +55 -53
- package/src/execution/engine/__tests__/engine-discovery-scan.test.ts +0 -3
- package/src/execution/engine/__tests__/engine-discovery.test.ts +0 -3
- package/src/execution/engine/__tests__/inflight-snapshot.test.ts +180 -0
- package/src/execution/engine/__tests__/model-prompt.test.ts +0 -3
- package/src/execution/engine/__tests__/registry.test.ts +0 -1
- package/src/execution/engine/__tests__/routing.test.ts +0 -1
- package/src/execution/engine/__tests__/run-failure-worktree-cleanup.test.ts +0 -3
- package/src/execution/engine/client/__tests__/__fixtures__/fake-engine.mjs +0 -3
- package/src/execution/engine/client/__tests__/engine-client-reap.test.ts +212 -0
- package/src/execution/engine/client/__tests__/mirror.test.ts +73 -1
- package/src/execution/engine/client/__tests__/protocol-closure.test.ts +2 -9
- package/src/execution/engine/client/__tests__/remote-engine.test.ts +46 -12
- package/src/execution/engine/client/engine-client.ts +111 -26
- package/src/execution/engine/client/mirror.ts +9 -0
- package/src/execution/engine/client/remote-engine.ts +52 -34
- package/src/execution/engine/client/reverse-router.ts +6 -27
- package/src/execution/engine/common/__tests__/run-signals.test.ts +79 -0
- package/src/execution/engine/common/capability-gate.ts +27 -0
- package/src/execution/engine/common/data-dir.ts +3 -2
- package/src/execution/engine/common/errors.ts +1 -1
- package/src/execution/engine/common/journal-wiring.ts +18 -9
- package/src/execution/engine/common/run-signals.ts +83 -0
- package/src/execution/engine/d8-compat.ts +1 -1
- package/src/execution/engine/host/__tests__/host-bridge.test.ts +3 -18
- package/src/execution/engine/host/host-bridge.ts +26 -41
- package/src/execution/engine/host/pi-host-binding.ts +5 -5
- package/src/execution/engine/host/spawned-children.ts +1 -1
- package/src/execution/engine/inflight-snapshot.ts +92 -0
- package/src/execution/engine/port.ts +52 -54
- package/src/execution/engine/types.ts +11 -14
- package/src/execution/execution-record.ts +15 -47
- package/src/execution/finalize-record.ts +94 -197
- package/src/execution/idle-gc.ts +9 -3
- package/src/execution/lifecycle-manager.ts +25 -361
- package/src/execution/lifecycle-predicates.ts +22 -19
- package/src/execution/manifest-store.ts +40 -42
- package/src/execution/model-config-service.ts +1 -1
- package/src/execution/notifier.ts +39 -1
- package/src/execution/notify-host.ts +13 -4
- package/src/execution/path-encoding.ts +6 -0
- package/src/execution/record-entry.ts +20 -2
- package/src/execution/record-store.ts +925 -160
- package/src/execution/relay-env.ts +8 -3
- package/src/execution/round-supervisor/domain.ts +17 -5
- package/src/execution/round-supervisor/service-binding.test.ts +89 -53
- package/src/execution/round-supervisor/service-binding.ts +71 -21
- package/src/execution/round-supervisor/supervisor.ts +23 -9
- package/src/execution/service/record-access.ts +547 -0
- package/src/execution/service/record-lifecycle.ts +542 -0
- package/src/execution/service/run-orchestration.ts +1647 -0
- package/src/execution/service/service-bootstrap.ts +111 -0
- package/src/execution/service/service-constants.ts +27 -0
- package/src/execution/service/session-baselines.ts +401 -0
- package/src/execution/service/sync-collect-domain.ts +442 -0
- package/src/execution/service/workflow-dispatch.ts +567 -0
- package/src/execution/session-file-gc.ts +11 -8
- package/src/execution/session-reconstructor.ts +44 -1
- package/src/execution/sessions-index.ts +20 -3
- package/src/execution/settled-watchdog.ts +86 -43
- package/src/execution/state-marker.ts +516 -0
- package/src/execution/stream-sink.ts +1 -1
- package/src/execution/subagent-actions-core.ts +53 -31
- package/src/execution/subagent-service.ts +643 -2955
- package/src/execution/subprocess-agent-runner.ts +46 -323
- package/src/execution/types.ts +60 -43
- package/src/execution/ui-request-handler-factory.ts +5 -0
- package/src/execution/worktree-manager.ts +1 -1
- package/src/execution/worktree-registry.ts +12 -8
- package/src/index.ts +16 -5
- package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +2 -3
- package/src/orchestration/__tests__/agent-call-stream.test.ts +24 -49
- package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +4 -4
- package/src/orchestration/__tests__/file-run-store.test.ts +5 -7
- package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +2 -3
- package/src/orchestration/__tests__/run-snapshot.test.ts +27 -43
- package/src/orchestration/__tests__/worker-message-pump-workflow-dispatch.test.ts +274 -0
- package/src/orchestration/execute-agent-call.ts +1 -1
- package/src/orchestration/file-run-store.ts +1 -1
- package/src/orchestration/models/ports.ts +20 -13
- package/src/orchestration/models/types.ts +11 -13
- package/src/orchestration/run-snapshot.ts +18 -22
- package/src/orchestration/worker-message-pump.ts +33 -57
- package/src/shared/atomic-write.ts +10 -8
- package/dist/chunk-T2SYBW3I.js +0 -24
- package/dist/engine-discovery-scan-ocpM8FmI.d.cts +0 -1608
- package/dist/engine-discovery-scan-ocpM8FmI.d.ts +0 -1608
- package/src/execution/__tests__/chat-round-first-round-watchdog.test.ts +0 -255
- package/src/execution/__tests__/finalized-marker.test.ts +0 -104
- package/src/execution/__tests__/lifecycle-manager-lock.test.ts +0 -211
- package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +0 -488
- package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +0 -75
- package/src/execution/__tests__/tombstone-store.test.ts +0 -73
- package/src/execution/engine/__tests__/conformance/chat-round-protocol.test.ts +0 -230
- package/src/execution/finalized-marker.ts +0 -70
- package/src/execution/tombstone-store.ts +0 -72
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
// §3.3.5(EnginePort 与中立类型完整契约)+ §3.3.6(EngineHandle/journal/SessionView 格式)。
|
|
5
5
|
//
|
|
6
6
|
// [S4 簇 3 收编] 契约类型单源化(type-only):AgentOutcome / EngineHandleData /
|
|
7
|
-
// ReplayedTurn / SessionView / EngineCapabilities / ProbeReport
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
7
|
+
// ReplayedTurn / SessionView / EngineCapabilities / ProbeReport 的本地定义已删除,
|
|
8
|
+
// 自 @zhushanwen/subagent-engine-sdk re-export(SDK protocol/contract-types.ts 是
|
|
9
|
+
// 类型闭包 SSOT,core 反向 re-export 保上层消费面——import 方路径零改动;结构等价
|
|
10
|
+
// 由 protocol-closure.test.ts 断言族守卫)。[H1 U5/U6] InteractAction / InteractResult
|
|
11
|
+
// 已随 chat 域 interact 面退役删除。
|
|
11
12
|
// EngineHandle 留守本地:SDK 无此类型(协议面用裸 data 载荷),宿主侧句柄包装接口
|
|
12
13
|
// 是 core 域概念。
|
|
13
14
|
//
|
|
@@ -20,7 +21,7 @@
|
|
|
20
21
|
|
|
21
22
|
import type { EngineHandleData } from "@zhushanwen/subagent-engine-sdk";
|
|
22
23
|
|
|
23
|
-
// AgentEvent
|
|
24
|
+
// AgentEvent 9 种事件:权威定义在 SDK contract-types,经 execution/types.ts re-export
|
|
24
25
|
// → shared/agent-event.ts 转发层 → 本文件(链条保持,维持「shared/ 是类型共享层」的
|
|
25
26
|
// 架构约定)。新增粗粒度约束(coarse 引擎至少合成一次 message_end + 一次 turn_end)
|
|
26
27
|
// 由 conformance 套件断言(P4),不在类型层编码。
|
|
@@ -34,26 +35,22 @@ export type {
|
|
|
34
35
|
AgentOutcome,
|
|
35
36
|
EngineCapabilities,
|
|
36
37
|
EngineHandleData,
|
|
37
|
-
InteractAction,
|
|
38
|
-
InteractResult,
|
|
39
38
|
ProbeReport,
|
|
40
39
|
ReplayedTurn,
|
|
41
40
|
SessionView,
|
|
42
41
|
} from "@zhushanwen/subagent-engine-sdk";
|
|
43
42
|
|
|
44
43
|
// ============================================================
|
|
45
|
-
// EngineHandle(run/
|
|
44
|
+
// EngineHandle(run/read 两面的连接件,D1/§3.3.6)——core 域类型留宿主侧
|
|
46
45
|
// ============================================================
|
|
47
46
|
|
|
48
47
|
/**
|
|
49
|
-
* 引擎会话句柄(run 返回、
|
|
48
|
+
* 引擎会话句柄(run 返回、read 入参;[H1 U5/U6] interact 面退役后不再有 interact
|
|
49
|
+
* 消费方)。
|
|
50
50
|
*
|
|
51
51
|
* 契约三条(D1):不透明(上层不解构——唯一例外是 record 持久化层序列化 data 字段与
|
|
52
|
-
* read 降级链)、可持久化(data 是纯 JSON,主会话 reload 后 read
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* 对进程已死的 handle 调 interact 必须返回 engine_session_not_resumable(指向 cold
|
|
56
|
-
* resume 路径),而非笼统失败——由各引擎 interact 实现保证。
|
|
52
|
+
* read 降级链)、可持久化(data 是纯 JSON,主会话 reload 后 read 仍可用)、自描述
|
|
53
|
+
* (data 含 engineId + 引擎 session 定位符 + pool key + adapter 版本)。
|
|
57
54
|
*/
|
|
58
55
|
export interface EngineHandle {
|
|
59
56
|
/** 持久化数据。上层不得解构其内部字段(见契约三条)。 */
|
|
@@ -470,6 +470,10 @@ export function updateFromEvent(record: ExecutionRecord, event: AgentEvent): voi
|
|
|
470
470
|
case "compaction":
|
|
471
471
|
return;
|
|
472
472
|
|
|
473
|
+
// ── activity:纯活性信号,reducer no-op(协议语义见 SDK contract-types)──
|
|
474
|
+
case "activity":
|
|
475
|
+
return;
|
|
476
|
+
|
|
473
477
|
default: {
|
|
474
478
|
// 穷尽性检查:新增 AgentEvent variant 时编译期报错
|
|
475
479
|
const _exhaustive: never = event;
|
|
@@ -612,50 +616,10 @@ export function getFullText(record: ExecutionRecord): string {
|
|
|
612
616
|
.join("\n\n");
|
|
613
617
|
}
|
|
614
618
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
* filter(text => text.length > 0)(判据与 getFullText 逐字对齐——空白串 " " 按非空
|
|
620
|
-
* 处理,不发散)后 join("\n\n")。
|
|
621
|
-
*
|
|
622
|
-
* 不变式(单测锁死):
|
|
623
|
-
* - fromTurnIndex = 0 与 getFullText(record) 逐字节等价(one-shot/首轮回退零成本)
|
|
624
|
-
* - fromTurnIndex >= turns.length 返回 ""(不抛错、不回绕)
|
|
625
|
-
* - fromTurnIndex < 0 规范化为 0(防御,Math.max 不产生 slice 负偏移语义)
|
|
626
|
-
*
|
|
627
|
-
* 纯函数只读;getFullText 本体与既有调用方(collectResult 等)零改动。
|
|
628
|
-
* 唯一调用点 onRoundSettled 的增量派生(record.roundBaseTurnIndex ?? 0)。
|
|
629
|
-
*/
|
|
630
|
-
export function getFullTextFrom(record: ExecutionRecord, fromTurnIndex: number): string {
|
|
631
|
-
return record.turns
|
|
632
|
-
.slice(Math.max(0, fromTurnIndex))
|
|
633
|
-
.map((t) => t.text)
|
|
634
|
-
.filter((text) => text.length > 0)
|
|
635
|
-
.join("\n\n");
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
/**
|
|
639
|
-
* [增量通知] 轮次边界推进公式(D1):返回下一轮增量的 turns[] 起始下标。
|
|
640
|
-
*
|
|
641
|
-
* record.turns.length - (末 turn 存在且 !closed 且 text.length === 0 ? 1 : 0)
|
|
642
|
-
*
|
|
643
|
-
* - 末 turn 已闭合(pi 现序常态:带 usage 的 message_end 恒先于 turn_end,settle 时
|
|
644
|
-
* 全闭合)→ 返回 length,下一轮从新 turn 起。
|
|
645
|
-
* - 末 turn 是滞后 message_end 开出的空 turn(防御分支,pi 现序不可达)→ 不计入边界
|
|
646
|
-
* (-1),留在下一轮增量内:新轮首个 text_delta 经 currentTurn 复用该空 turn,复用
|
|
647
|
-
* 累积被 slice(from) 覆盖;若直用 turns.length 会把这段文本挤出 slice 范围静默丢失(D1)。
|
|
648
|
-
* - 末 turn 未闭合且 text 非空(防御形态,pi 现序不可达)→ 计入本轮返回 length(该形态
|
|
649
|
-
* 即 onRoundSettled 推进前观测哨 logger.warn 的触发条件)。
|
|
650
|
-
*
|
|
651
|
-
* 纯函数只读不写 record;唯一调用点 onRoundSettled 第 5 步(notify 之后推进)。
|
|
652
|
-
*/
|
|
653
|
-
export function nextRoundBaseTurnIndex(record: ExecutionRecord): number {
|
|
654
|
-
const last = record.turns[record.turns.length - 1];
|
|
655
|
-
const trailingOpenEmpty =
|
|
656
|
-
last !== undefined && !last.closed && last.text.length === 0 ? 1 : 0;
|
|
657
|
-
return record.turns.length - trailingOpenEmpty;
|
|
658
|
-
}
|
|
619
|
+
// [H1 U6 / D7 ③] getFullTextFrom / nextRoundBaseTurnIndex(增量通知 base 死记账族)
|
|
620
|
+
// 已退役删除:唯一调用点 onRoundSettled(inproc 时代)消亡后生产零调用,写点
|
|
621
|
+
// settleChatRoundFromResponse 随载体删除;「notify 失败 base 不推进」防丢文本语义由
|
|
622
|
+
// record.result 恒写承接(D7 ③ 退役不迁移)。getFullText 本体与既有调用方零改动。
|
|
659
623
|
|
|
660
624
|
/**
|
|
661
625
|
* 聚合所有 turn 的 toolCalls(扁平化),并 strip InternalToolCall 的内部字段。
|
|
@@ -990,8 +954,8 @@ function translateMessageUpdate(raw: JsonlEvent): AgentEvent[] {
|
|
|
990
954
|
/**
|
|
991
955
|
* 把一条 JSONL 事件翻译成 AgentEvent。
|
|
992
956
|
*
|
|
993
|
-
* 返回 undefined 表示该事件不映射到任何 AgentEvent(如 session header、message_start
|
|
994
|
-
*
|
|
957
|
+
* 返回 undefined 表示该事件不映射到任何 AgentEvent(如 session header、message_start),
|
|
958
|
+
* 调用方应跳过。
|
|
995
959
|
*
|
|
996
960
|
* 一个 JSONL 事件可能产出**多条** AgentEvent(message_end 的 usage + error 各一条),
|
|
997
961
|
* 故返回数组。绝大多数情况长度为 0 或 1;message_end 最多 2 条。
|
|
@@ -1003,9 +967,13 @@ export function jsonlToAgentEvent(raw: JsonlEvent): AgentEvent[] {
|
|
|
1003
967
|
case "session":
|
|
1004
968
|
case "message_start":
|
|
1005
969
|
case "turn_start":
|
|
1006
|
-
case "tool_execution_update":
|
|
1007
970
|
return [];
|
|
1008
971
|
|
|
972
|
+
// 工具执行期活性信号(与 pi 侧 spawn-event-translator 的 TOOL_ACTIVITY_EVENT 同
|
|
973
|
+
// 语义):不产数据,只驱动宿主无进展守护刷新。
|
|
974
|
+
case "tool_execution_update":
|
|
975
|
+
return [{ type: "activity" }];
|
|
976
|
+
|
|
1009
977
|
case "tool_execution_start":
|
|
1010
978
|
return translateToolExecutionStart(raw);
|
|
1011
979
|
|
|
@@ -2,32 +2,30 @@
|
|
|
2
2
|
//
|
|
3
3
|
// 时序收尾逻辑(从 subagent-service.ts 提取,降低主文件行数 < 1000 上限)。
|
|
4
4
|
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
5
|
+
// [U2a / §3.1 意图 API 迁移] 终态持久化四件套(.state 写 + entry/archive + manifest +
|
|
6
|
+
// .alive 删)归口 store.markFinalized / markCancelled——本文件降级为**副作用编排层**
|
|
7
|
+
// (collectPatch / completeRecord / worktree cleanup / pending 注销① / onFinalized 钩子,
|
|
8
|
+
// §3.1 副作用归属边界)。文件布局知识(写哪个文件、什么顺序)不再散落此处:D8 v7 写序
|
|
9
|
+
//(.state writeSync 先 → manifest writeSync 后 → .alive 删)由 store 内部单点保证。
|
|
7
10
|
//
|
|
8
|
-
// [Critical #1 / PR #85]
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//(console.error + appendEntry,不 throw)。
|
|
11
|
+
// [Critical #1 / PR #85 精神保持] manifest 写失败响亮上报不阻断(logger.error +
|
|
12
|
+
// subagent:manifest-write-failed entry,§3.4 manifest 面)与终态原语返回 false 均不
|
|
13
|
+
// 得跳过 worktree cleanup——磁盘满/权限错时 worktree 泄漏比索引缺失严重;终态写失
|
|
14
|
+
// 败时 record 留 running 形态(磁盘无终态位),下次 boot 孤儿恢复终态化承接(§3.4)。
|
|
13
15
|
//
|
|
14
|
-
// B9 兜底:completeRecord
|
|
16
|
+
// B9 兜底:completeRecord/终态原语抛错→后续 cleanup 仍执行。
|
|
15
17
|
|
|
16
18
|
import * as fs from "node:fs";
|
|
17
19
|
import * as path from "node:path";
|
|
18
20
|
|
|
19
21
|
import { getLogger } from "../core/logger.ts";
|
|
20
22
|
|
|
21
|
-
import { removeAliveMarker } from "./alive-store.ts";
|
|
22
23
|
import { bestEffort } from "./best-effort.ts";
|
|
23
24
|
import { completeRecord } from "./execution-record.ts";
|
|
24
|
-
import { writeFinalized } from "./finalized-marker.ts";
|
|
25
|
-
import type { ManifestStore } from "./manifest-store.ts";
|
|
26
25
|
import type { ModelConfigService } from "./model-config-service.ts";
|
|
27
26
|
import { getSubagentSessionDir } from "./path-encoding.ts";
|
|
28
27
|
import type { RecordStore } from "./record-store.ts";
|
|
29
28
|
import { readIdentityHeader, readIdentityTail } from "./session-reconstructor.ts";
|
|
30
|
-
import { writeCancelledTombstone } from "./tombstone-store.ts";
|
|
31
29
|
import type { AgentResult, ClosedReason, ExecutionRecord } from "./types.ts";
|
|
32
30
|
import type { WorktreeManager } from "./worktree-manager.ts";
|
|
33
31
|
|
|
@@ -35,31 +33,30 @@ const logger = getLogger("subagents");
|
|
|
35
33
|
|
|
36
34
|
/** doFinalizeRecord 的依赖(从 SubagentService 注入,避免 this 绑定 + 解耦可测试)。 */
|
|
37
35
|
export interface FinalizeDeps {
|
|
38
|
-
manifestStore: ManifestStore;
|
|
39
36
|
worktreeManager: WorktreeManager;
|
|
40
37
|
store: RecordStore;
|
|
41
38
|
modelService: ModelConfigService;
|
|
42
|
-
/** Pi ExtensionAPI
|
|
39
|
+
/** Pi ExtensionAPI(终态写失败的响亮 entry 上报)。null 在 dispose 后。 */
|
|
43
40
|
pi: { appendEntry?: (type: string, data: unknown) => void } | null;
|
|
44
41
|
/** pending-notifications 终态注销(绑定 pi.events.emit,由调用方闭包提供)。 */
|
|
45
42
|
emitUnregister(id: string, status: string): void;
|
|
46
43
|
/**
|
|
47
44
|
* [F-5 修复] record 终态化的宿主侧收口钩子(recordId;best-effort,抛错由调用方
|
|
48
45
|
* 在闭包内自行兜底)。单一汇聚点:doFinalizeRecord 是全部 closed 终态的必经路径
|
|
49
|
-
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* 不经本钩子(回 idle 非终态,续聊仍需路由)。
|
|
46
|
+
*(finalizeRecord / closeChatIdle / finalizeFailed / finalizeAborted /
|
|
47
|
+
* consumeCloseAfterRound)。收口面现 = Continuation 实例清理([H1 U6] 旧 chat 轮
|
|
48
|
+
* 路由注销面已随 interact 面退役)。finalizeRoundToIdle 不经本钩子(回 idle 非终态,
|
|
49
|
+
* 续聊仍需容器)。
|
|
54
50
|
*/
|
|
55
51
|
onFinalized?: (recordId: string) => void;
|
|
56
52
|
/**
|
|
57
53
|
* [T1/PS-9] subagent sessionDir(getSubagentSessionDir(agentDir, rootCwd),调用方注入)。
|
|
58
54
|
*
|
|
59
55
|
* record.sessionFile 缺失(RC-1 握手失败 + LC-4 反查也未命中的残余形态)时用于磁盘
|
|
60
|
-
* 反查:按 identity(record.id)扫目录找真实 session
|
|
61
|
-
*
|
|
62
|
-
* alive marker 残留」。undefined =
|
|
56
|
+
* 反查:按 identity(record.id)扫目录找真实 session 文件,作为终态原语
|
|
57
|
+
* (store.markFinalized/markCancelled 的 .state/manifest/.alive 面)的锚点依据——
|
|
58
|
+
* 消除「sessionFile 缺失 → 终态原因丢失 + alive marker 残留」。undefined = 调用方
|
|
59
|
+
* 无法提供(反查跳过,行为退回修复前)。
|
|
63
60
|
*/
|
|
64
61
|
sessionDir?: string;
|
|
65
62
|
// [review 修复] 已删除 redeliverPending 回调(MF-1 消费确认制补投):pendingMessages
|
|
@@ -98,11 +95,10 @@ function findSessionFileByRecordIdentity(
|
|
|
98
95
|
/**
|
|
99
96
|
* [T1/PS-9] Step 序言:sessionFile 缺失时按 record 身份在 sessionDir 反查回填。
|
|
100
97
|
*
|
|
101
|
-
*
|
|
102
|
-
* RC-1(握手失败)+ LC-4(收尾反查未命中)的残余形态下,磁盘上 session
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* 保持旧行为(best-effort 跳过)。
|
|
98
|
+
* 放在一切步骤前,让终态原语(archive 投影 / .state / manifest / .alive release)
|
|
99
|
+
* 统一受益。RC-1(握手失败)+ LC-4(收尾反查未命中)的残余形态下,磁盘上 session
|
|
100
|
+
* 文件仍可能真实存在(identity 携带 record.id)——反查命中则终态原语有了锚点,
|
|
101
|
+
* 终态原因不再丢失、alive marker 不再残留;未命中则保持旧行为(best-effort 跳过)。
|
|
106
102
|
*/
|
|
107
103
|
function resolveMissingSessionFile(deps: FinalizeDeps, record: ExecutionRecord): void {
|
|
108
104
|
if (record.sessionFile || !deps.sessionDir) return;
|
|
@@ -136,34 +132,9 @@ async function collectPatchIfWorktree(deps: FinalizeDeps, record: ExecutionRecor
|
|
|
136
132
|
}
|
|
137
133
|
}
|
|
138
134
|
|
|
139
|
-
/**
|
|
140
|
-
* Step 3a: finalized/tombstone sidecar(best-effort 幂等,仅 sessionFile 存在时执行)。
|
|
141
|
-
* MF-1 fix / v4 B-1: cancelled(closedReason='cancelled')写 tombstone 而非 finalized,
|
|
142
|
-
* 防重建丢失 cancelled;其余 reason 写 finalized sidecar(真实 reason 进 sidecar 内容,
|
|
143
|
-
* 磁盘重建用它还原 closedReason,不再一律硬编码 gc)。
|
|
144
|
-
*/
|
|
145
|
-
function writeFinalizedOrTombstone(record: ExecutionRecord, closedReason: ClosedReason | undefined): void {
|
|
146
|
-
if (!record.sessionFile) return;
|
|
147
|
-
try {
|
|
148
|
-
if (closedReason === "cancelled") {
|
|
149
|
-
writeCancelledTombstone(record.sessionFile, {
|
|
150
|
-
id: record.id,
|
|
151
|
-
status: "cancelled",
|
|
152
|
-
agent: record.agent,
|
|
153
|
-
startedAt: record.startedAt,
|
|
154
|
-
endedAt: record.endedAt ?? Date.now(),
|
|
155
|
-
});
|
|
156
|
-
} else {
|
|
157
|
-
writeFinalized(record.sessionFile, closedReason);
|
|
158
|
-
}
|
|
159
|
-
} catch (err) {
|
|
160
|
-
bestEffort(err, "writeFinalized/tombstone (finalizeRecord Step3)");
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
135
|
/**
|
|
165
136
|
* Step 3b: worktree cleanup(best-effort 幂等,仅 worktree 绑定时执行)。
|
|
166
|
-
* [Critical]
|
|
137
|
+
* [Critical] 绝不能因终态原语失败(含 manifest 写失败)而跳过(否则 worktree 泄漏)。
|
|
167
138
|
*/
|
|
168
139
|
async function cleanupWorktreeIfBound(deps: FinalizeDeps, record: ExecutionRecord): Promise<void> {
|
|
169
140
|
if (!record.worktreeHandle) return;
|
|
@@ -174,59 +145,17 @@ async function cleanupWorktreeIfBound(deps: FinalizeDeps, record: ExecutionRecor
|
|
|
174
145
|
}
|
|
175
146
|
}
|
|
176
147
|
|
|
177
|
-
/** Step 3c: 删 .alive marker(best-effort 幂等,仅 sessionFile 存在时执行)。 */
|
|
178
|
-
function removeAliveMarkerIfPresent(record: ExecutionRecord): void {
|
|
179
|
-
if (!record.sessionFile) return;
|
|
180
|
-
try {
|
|
181
|
-
removeAliveMarker(record.sessionFile);
|
|
182
|
-
} catch (err) {
|
|
183
|
-
bestEffort(err, "removeAliveMarker (finalizeRecord Step3)");
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
148
|
/**
|
|
188
|
-
*
|
|
149
|
+
* 时序收尾(D-017,[U2a] 终态持久化面归口 store 意图原语后的编排形态)。
|
|
189
150
|
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
* 跳过 Step 3 cleanup 或抛出打断 finalize 链。旧实现 Step 2.5 throw 会
|
|
195
|
-
* 跳过 Step 3 cleanup。task/slug/model 从 ExecutionRecord 抓取(配合
|
|
196
|
-
* ManifestRecord 补字段),manifestToSubagent 投影时用真实值而非硬编码空串。
|
|
197
|
-
*/
|
|
198
|
-
async function writeManifestBestEffort(deps: FinalizeDeps, record: ExecutionRecord): Promise<void> {
|
|
199
|
-
try {
|
|
200
|
-
await deps.manifestStore.writeManifest({
|
|
201
|
-
id: record.id,
|
|
202
|
-
rootSessionId: record.rootSessionId ?? "",
|
|
203
|
-
parentRecordId: record.parentRecordId,
|
|
204
|
-
agentName: record.agent,
|
|
205
|
-
// v4 B-1: manifest status 统一为 closed(cancelled 折入 closed,区分靠 tombstone sidecar)
|
|
206
|
-
status: "closed",
|
|
207
|
-
createdAt: record.startedAt,
|
|
208
|
-
completedAt: record.endedAt ?? Date.now(),
|
|
209
|
-
sessionFile: record.sessionFile,
|
|
210
|
-
task: record.task,
|
|
211
|
-
slug: record.slug,
|
|
212
|
-
model: record.model,
|
|
213
|
-
});
|
|
214
|
-
} catch (err) {
|
|
215
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
216
|
-
logger.error(`[subagent] manifest 写入失败 (record=${record.id}): ${msg}`);
|
|
217
|
-
deps.pi?.appendEntry?.("subagent:manifest-write-failed", {
|
|
218
|
-
id: record.id,
|
|
219
|
-
error: msg,
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* 时序收尾(D-017)。步骤 0→4 全部 best-effort 互不阻断(除 manifest 外都幂等)。
|
|
151
|
+
* 步骤:序言 sessionFile 反查 → Step 0 collectPatch → Step 1 completeRecord →
|
|
152
|
+
* Step 2 终态原语(store.markFinalized / markCancelled:.state writeSync + entry/
|
|
153
|
+
* archive + manifest + .alive 删,D8 v7 写序)→ Step 3b worktree cleanup →
|
|
154
|
+
* pending 注销① → onFinalized 钩子。
|
|
226
155
|
*
|
|
227
|
-
* [Critical #1]
|
|
228
|
-
*
|
|
229
|
-
*
|
|
156
|
+
* [Critical #1] 终态原语失败(返回 false = .state 重试耗尽;或意外抛错)不得跳过
|
|
157
|
+
* worktree cleanup——写失败时 record 留 running(磁盘无终态位),副作用清理照常
|
|
158
|
+
* (幂等);§3.4 响亮 entry 上报由本编排层接线(store 层已 logger.error)。
|
|
230
159
|
*/
|
|
231
160
|
export async function doFinalizeRecord(
|
|
232
161
|
deps: FinalizeDeps,
|
|
@@ -248,132 +177,100 @@ export async function doFinalizeRecord(
|
|
|
248
177
|
bestEffort(err, "completeRecord (finalizeRecord B9)", "error");
|
|
249
178
|
}
|
|
250
179
|
|
|
251
|
-
// ── Step 2:
|
|
180
|
+
// ── Step 2: 终态持久化四件套归口(B1 迁移点)──
|
|
181
|
+
// cancelled 走 markCancelled(.state 载荷 status:"cancelled" + 精确 endedAt),其余
|
|
182
|
+
// markFinalized。两原语内部写序 = .state writeSync 先 → binding/archive/manifest →
|
|
183
|
+
// .alive 删(release 出口①,D8 v7)。生产路径恒 manifestDir 接线(subagent-service
|
|
184
|
+
// 构造点);缺省异步分支仅纯内存测试形态。
|
|
185
|
+
let persisted = false;
|
|
252
186
|
try {
|
|
253
|
-
|
|
187
|
+
persisted =
|
|
188
|
+
closedReason === "cancelled"
|
|
189
|
+
? deps.store.markCancelled(record)
|
|
190
|
+
: deps.store.markFinalized(record, closedReason);
|
|
254
191
|
} catch (err) {
|
|
255
|
-
bestEffort(err, "store
|
|
192
|
+
bestEffort(err, "store terminal primitive (finalizeRecord B9)", "error");
|
|
193
|
+
}
|
|
194
|
+
if (!persisted) {
|
|
195
|
+
// [§3.4 / U1 偏差 6 接线] 终态写失败(重试耗尽)响亮 entry 上报——GUI 通知面腿;
|
|
196
|
+
// 日志 error 级腿已由 state-marker 内部完成。record 留 running 形态(磁盘无终态
|
|
197
|
+
// 位、未 archive),下次 boot 孤儿恢复终态化承接。
|
|
198
|
+
const reasonDesc = closedReason ?? record.closedReason ?? "gc";
|
|
199
|
+
logger.error(
|
|
200
|
+
`[subagent] terminal state write failed after retries (record=${record.id}, reason=${reasonDesc}); ` +
|
|
201
|
+
`record stays running on disk — boot orphan recovery will finalize it`,
|
|
202
|
+
);
|
|
203
|
+
deps.pi?.appendEntry?.("subagent:state-write-failed", {
|
|
204
|
+
id: record.id,
|
|
205
|
+
status,
|
|
206
|
+
closedReason: reasonDesc,
|
|
207
|
+
});
|
|
256
208
|
}
|
|
257
209
|
|
|
258
|
-
// ── Step
|
|
259
|
-
// [Critical] 清理必须在 manifest 写入之前:worktree cleanup / finalized marker / aliveMarker
|
|
260
|
-
// 都是幂等且不可跳过的副作用。绝不能因 manifest 写失败而跳过 worktree cleanup
|
|
261
|
-
// (否则 worktree 泄漏)。各件独立 try/catch,互不阻断。
|
|
262
|
-
writeFinalizedOrTombstone(record, closedReason);
|
|
210
|
+
// ── Step 3b: worktree cleanup(best-effort,不可被终态写失败跳过)──
|
|
263
211
|
await cleanupWorktreeIfBound(deps, record);
|
|
264
|
-
removeAliveMarkerIfPresent(record);
|
|
265
212
|
|
|
266
213
|
// pending-notifications:终态注销(只记 registry 状态,通知由 BgNotifier 发)
|
|
267
214
|
// [W4 发射点枚举归属①] 注销合法发射点枚举(设计 D2)第 ① 处:subagent record
|
|
268
|
-
// 终态化(finalizeRecord 路径,含监督器放弃)。其余合法发射点:②
|
|
269
|
-
//
|
|
270
|
-
//
|
|
215
|
+
// 终态化(finalizeRecord 路径,含监督器放弃)。其余合法发射点:② = store
|
|
216
|
+
// .markRoundIdle 簿记⑧(U5 收口后编排层无直发;setPendingUnregister →
|
|
217
|
+
// emitPendingUnregister 唯一发射);③ workflow run 终态迁移
|
|
218
|
+
//(transition("done") 路径);④ 监督器显式放弃(走本路径,终态化+注销同批);
|
|
271
219
|
// ⑤ 注册对账 sweep 补发(round-supervisor/reconcile-sweep.ts)。进程退出本身
|
|
272
220
|
// 永远不是注销理由(subagent-service disposeAllRecords 的 emit 属①——其同批
|
|
273
221
|
// completeRecord+archive 终态化)。
|
|
274
222
|
deps.emitUnregister(record.id, status);
|
|
275
223
|
|
|
276
224
|
// [F-5 修复] 宿主侧终态收口钩子(chat 轮路由注销的单一汇聚点,见 FinalizeDeps
|
|
277
|
-
// .onFinalized 注释)。fire-and-forget
|
|
225
|
+
// .onFinalized 注释)。fire-and-forget:钩子失败不阻断收尾(闭包内自查)。
|
|
278
226
|
try {
|
|
279
227
|
deps.onFinalized?.(record.id);
|
|
280
228
|
} catch (err) {
|
|
281
229
|
bestEffort(err, "onFinalized hook (finalizeRecord)");
|
|
282
230
|
}
|
|
283
|
-
|
|
284
|
-
// ── Step 4 (last): manifest 持久化(best-effort,不阻断、不 throw)──
|
|
285
|
-
await writeManifestBestEffort(deps, record);
|
|
286
231
|
}
|
|
287
232
|
|
|
233
|
+
/**
|
|
234
|
+
* [H1 U2 / D7] 轮终处置的 outcome 入参(判别联合)。
|
|
235
|
+
*
|
|
236
|
+
* 命名与协议 AgentOutcome 同名不同物(设计 D7 v6:kind 判别联合消歧)——
|
|
237
|
+
* kind 判别轮终分流:成功轮正文来源 = Continuation 成功分支的 content(协议
|
|
238
|
+
* AgentOutcome.content 映射产物);失败轮 reason = 失败原因原文。
|
|
239
|
+
*/
|
|
240
|
+
export type RoundSettlementOutcome =
|
|
241
|
+
| { kind: "success"; content: string }
|
|
242
|
+
| { kind: "failed"; reason: string };
|
|
243
|
+
|
|
288
244
|
/**
|
|
289
245
|
* 对话模式轮次完成收尾:record 进 idle 态(非终态化,等待续聊)。
|
|
290
246
|
*
|
|
291
|
-
*
|
|
247
|
+
* [U2a/B5] 轮终簿记全集(①-⑨)归口 store.markRoundIdle——本方法瘦身为编排薄壳。
|
|
248
|
+
* 簿记语义(细节与 result 写入规则见 record-store.markRoundIdle 方法头):
|
|
292
249
|
* - 不调 completeRecord(record 不冻结,保留 turns[] 等运行时状态供续聊累积)
|
|
293
250
|
* - 不调 store.archive(record 留内存,getMutable 可查、list 可见)
|
|
294
251
|
* - 不 cleanup worktree(保留对话模式工作目录)
|
|
295
|
-
* - 不写 manifest
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
* -
|
|
300
|
-
*
|
|
301
|
-
* MF-2:设 record.result = result.text(否则 notifier idle 回复正文恒为 "(empty)",
|
|
302
|
-
* G1/G2 多轮回复送达不成立)。失败轮次(result.success=false,MF-6 回退 idle 路径)
|
|
303
|
-
* error 优先于 text:collectResult 恒带 getFullText 全量正文,text 优先会把失败的
|
|
304
|
-
* 恢复指引覆盖成轮内旧正文(Gate B S-B-1 实测:settled watchdog kill 后宿主收到
|
|
305
|
-
* 成功形态通知,无从得知挂死与恢复方式)。
|
|
306
|
-
*
|
|
307
|
-
* 状态:record.status = "idle"(覆盖 tryTransition 设的 done/failed),record.round += 1。
|
|
308
|
-
* 各步骤 best-effort 互不阻断(参照 doFinalizeRecord 的 bestEffort 用法)。
|
|
252
|
+
* - 不写 manifest(轮终回 running-resumable 非终态化,无终态快照可写)
|
|
253
|
+
* - **[B5/D3a] `.alive` 不再删除**——写权声明跨轮延续(release = 终态原语或
|
|
254
|
+
* idle-GC 归档两出口;轮终 record 仍 resumable、随时续聊 spawn 写同一
|
|
255
|
+
* sessionFile,删则轮后跨进程防御空窗)
|
|
256
|
+
* - [A3] 终态簿记已冻结(endedAt 已设)的调用由 store 内硬断言 fail-fast
|
|
257
|
+
* (复活终态的调用即 bug——S7 防御),throw 先于 store 簿记⑧的注销发射
|
|
309
258
|
*
|
|
310
|
-
* @
|
|
311
|
-
*
|
|
259
|
+
* @throws Error record 终态簿记已冻结(completeRecord 已跑)仍被调用(store.markRoundIdle
|
|
260
|
+
* 内硬断言——冻结权威判据 = endedAt 已设,写点枚举与两构造性调用面论证见其方法头)。
|
|
312
261
|
*/
|
|
313
262
|
export async function doFinalizeRoundToIdle(
|
|
314
263
|
deps: FinalizeDeps,
|
|
315
264
|
record: ExecutionRecord,
|
|
316
|
-
|
|
265
|
+
outcome: RoundSettlementOutcome,
|
|
317
266
|
): Promise<void> {
|
|
318
|
-
//
|
|
319
|
-
//
|
|
320
|
-
|
|
321
|
-
//
|
|
322
|
-
|
|
323
|
-
// success=true、真实可达,本写点被 subagent-service runAndFinalize 的成功分支共用)首轮
|
|
324
|
-
// result 前值 undefined,兜底补 "(empty)" 占位——措辞与 notifier buildLlmContent 的
|
|
325
|
-
// `record.result ?? "(empty)"` 兜底同款,通知文案逐字节不变([增量 G2] G4 取舍保持)。
|
|
326
|
-
// 轮终信号优先:record.result 非 undefined 是 renderer hasRunning 排除「轮终
|
|
327
|
-
// running-resumable」的判据(shared SubagentRecord.result 契约),保持 undefined 会让
|
|
328
|
-
// 完成注入后末位 turn 永久「工作中」。续轮(前值存在)沿用前值:one-shot 无增量语义,
|
|
329
|
-
// record.result = 该 subagent 最终输出。chatMode 空增量轮 → 固定占位
|
|
330
|
-
// "(no output this round)"(D5:增量语义下沿用旧 record.result = 上一轮增量,本轮通知
|
|
331
|
-
// 正文 = 上一轮内容,父 agent 误读为原样重复回复)。
|
|
332
|
-
let nextResult: string | undefined;
|
|
333
|
-
if (result.error && (!result.success || !result.text)) {
|
|
334
|
-
nextResult = `round did not complete: ${result.error}`;
|
|
335
|
-
} else if (result.text) {
|
|
336
|
-
nextResult = result.text;
|
|
337
|
-
} else if (record.chatMode) {
|
|
338
|
-
nextResult = "(no output this round)";
|
|
339
|
-
} else {
|
|
340
|
-
nextResult = record.result ?? "(empty)";
|
|
341
|
-
}
|
|
342
|
-
record.result = nextResult;
|
|
343
|
-
|
|
344
|
-
// 删 .alive marker(进程已 SIGTERM 回收)。
|
|
345
|
-
// sessionFile 窗口期可能 undefined(极少——对话模式轮次完成意味着 session 已跑过),
|
|
346
|
-
// 缺失时跳过但仍设内存 idle(重启后磁盘重建会落到 crashed,边界可接受)。
|
|
347
|
-
if (record.sessionFile) {
|
|
348
|
-
try {
|
|
349
|
-
removeAliveMarker(record.sessionFile);
|
|
350
|
-
} catch (err) {
|
|
351
|
-
bestEffort(err, "removeAliveMarker (doFinalizeRoundToIdle)");
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// pending-notifications:进程已死,从活跃后代差集移除(record 留内存不 archive,
|
|
356
|
-
// v4 B-1:record 现为 running-resumable,但 pending 注册的是进程活跃性,进程死了需注销)。
|
|
357
|
-
deps.emitUnregister(record.id, "running");
|
|
358
|
-
|
|
359
|
-
// 状态机(v4 B-1):record 保持 running(旧 idle 折入 running,覆盖 tryTransition 设的 closed,
|
|
360
|
-
// 可冷路径 resume),轮次计数 +1。idleSince 时间戳独立保留供 GC 判据。
|
|
361
|
-
// [S10] closedReason 同步清除:调用方(runAndFinalize catch / MF-6 分支)先 tryTransition
|
|
362
|
-
// 设了 closed+closedReason 再回退 running——不清则 "gc"/"cancelled" 残留在 running record 上,
|
|
363
|
-
// 泄漏进 list 投影与后续 notify 载荷(toNotifyRecord 透传 record.closedReason),让一个
|
|
364
|
-
// 活跃 record 看起来像已被某原因关闭过。
|
|
365
|
-
record.status = "running";
|
|
366
|
-
record.closedReason = undefined;
|
|
367
|
-
record.round = (record.round ?? 0) + 1;
|
|
368
|
-
record.idleSince = Date.now();
|
|
369
|
-
// 执行态信号(residual-fixes):轮终回 running-resumable = 无活进程驱动(idle timer
|
|
370
|
-
// 回收/保活等待续聊),GUI 侧据此判 waiting(非 streaming)。冷路径续轮(进程启动)清除。
|
|
371
|
-
record.resumable = true;
|
|
372
|
-
|
|
373
|
-
// W16 [D4]:轮终回 running-resumable 是类外状态写点(record 留内存不走 archive),
|
|
374
|
-
// 显式上报迁移——entry 携带新 round 与本轮 result,pi 文件的重建源不滞后。
|
|
375
|
-
deps.store.reportRecordTransition(record);
|
|
267
|
+
// 簿记①-⑨归口(含 A3 硬断言与⑨ reportRecordTransition entry 上报)。
|
|
268
|
+
// 返回 false = record 不在 store 内存(debug 留痕,无副作用)——两构造性调用面
|
|
269
|
+
//(Continuation 轮末分流 / settleOneShotOutcome SP-5)的 record 均在内存,false 即
|
|
270
|
+
// 调用方 bug,留痕足够。
|
|
271
|
+
deps.store.markRoundIdle(record.id, outcome);
|
|
376
272
|
|
|
377
|
-
// [
|
|
378
|
-
//
|
|
273
|
+
// [W4 发射点②] pending 注销(进程已死,从活跃后代差集移除)已随 U5 收口进
|
|
274
|
+
// markRoundIdle 簿记⑧——SubagentService 构造点经 setPendingUnregister 注入
|
|
275
|
+
// notifyHost.emitPendingUnregister,本编排层不再双轨重复发射。
|
|
379
276
|
}
|
package/src/execution/idle-gc.ts
CHANGED
|
@@ -54,8 +54,10 @@ export interface WorkflowRunGcStore {
|
|
|
54
54
|
* 启动 idle record GC 定时器,返回 stop 函数(清理 interval;幂等)。
|
|
55
55
|
* 每个扫描周期:
|
|
56
56
|
* - record 面:对 store 内全部 active record 中 resumable 的,锚点(idleSince
|
|
57
|
-
* 优先,缺失回退 startedAt——[W4 锚扩展])超过 IDLE_TTL_MS
|
|
58
|
-
*
|
|
57
|
+
* 优先,缺失回退 startedAt——[W4 锚扩展])超过 IDLE_TTL_MS 的归档
|
|
58
|
+
* ([U2b] markIdleArchived:archive 先 + `.alive` release 后——归档 = 放弃持有
|
|
59
|
+
* 即放弃写权声明,D3a release 出口②)。单条失败不阻断其余(bestEffort 留痕)。
|
|
60
|
+
* **只归档不补注销**(见文件头注)。
|
|
59
61
|
* - workflow 面(注入 workflowRuns 时):running 且 meta.startedAt 超
|
|
60
62
|
* IDLE_TTL_MS 的 run 终态化归档(transition + save),单 run 失败不阻断。
|
|
61
63
|
*
|
|
@@ -83,7 +85,11 @@ export function startIdleGc(store: RecordStore, workflowRuns?: WorkflowRunGcStor
|
|
|
83
85
|
`[subagents] GC: archiving idle record ${record.id} (idle for ${Math.round(age / MS_PER_DAY)}d)`,
|
|
84
86
|
);
|
|
85
87
|
try {
|
|
86
|
-
store.archive
|
|
88
|
+
// [U2b / D3a release 出口②] 归口 markIdleArchived:store.archive 先、`.alive`
|
|
89
|
+
// release 后(写序在 store 内部——归档 = 放弃持有 = 放弃写权声明,残留声明
|
|
90
|
+
// 会把 idle 归档后的预期通道 fork-from/新 start 拦死至宿主退出,纯成本零防御
|
|
91
|
+
// 收益;归档 record 后续被接管时统一 acquireWriteLease 重新声明)。
|
|
92
|
+
store.markIdleArchived(record);
|
|
87
93
|
} catch (err) {
|
|
88
94
|
bestEffort(err, `GC archive record ${record.id}`);
|
|
89
95
|
}
|