@zhushanwen/subagent-core 0.8.0 → 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.
Files changed (167) hide show
  1. package/dist/{chunk-OL5BK4VN.js → chunk-LZJOEDGQ.js} +455 -203
  2. package/dist/engine-discovery-scan-Da4Dkzsi.d.cts +818 -0
  3. package/dist/engine-discovery-scan-Da4Dkzsi.d.ts +818 -0
  4. package/dist/execution/engine/engine-discovery-scan.cjs +216 -155
  5. package/dist/execution/engine/engine-discovery-scan.d.cts +1 -2
  6. package/dist/execution/engine/engine-discovery-scan.d.ts +1 -2
  7. package/dist/execution/engine/engine-discovery-scan.js +1 -1
  8. package/dist/execution/relay-env.js +5 -4
  9. package/dist/index.cjs +7686 -5894
  10. package/dist/index.d.cts +1876 -1360
  11. package/dist/index.d.ts +1876 -1360
  12. package/dist/index.js +7048 -5493
  13. package/dist.bundle/index.cjs +7747 -5957
  14. package/package.json +4 -4
  15. package/src/__tests__/manifest-store.test.ts +16 -15
  16. package/src/__tests__/robustness-medium-batch1.test.ts +10 -14
  17. package/src/__tests__/robustness-medium-batch2.test.ts +7 -2
  18. package/src/execution/__tests__/alive-store.test.ts +117 -1
  19. package/src/execution/__tests__/batch-finalized.test.ts +206 -0
  20. package/src/execution/__tests__/chat-engine-routing.test.ts +117 -1
  21. package/src/execution/__tests__/{cold-resurrect.test.ts → cold-lookup.test.ts} +133 -33
  22. package/src/execution/__tests__/collect-coordinator-service.test.ts +7 -5
  23. package/src/execution/__tests__/conversation-continuation.test.ts +1452 -0
  24. package/src/execution/__tests__/delivery-methods.test.ts +90 -176
  25. package/src/execution/__tests__/dialog-queue.test.ts +70 -3
  26. package/src/execution/__tests__/dispose-manifest-recovery.test.ts +475 -0
  27. package/src/execution/__tests__/execute-and-await-worktree.test.ts +3 -3
  28. package/src/execution/__tests__/execute-nesting.test.ts +8 -5
  29. package/src/execution/__tests__/execution-record.test.ts +8 -148
  30. package/src/execution/__tests__/finalize-record.test.ts +295 -129
  31. package/src/execution/__tests__/get-record-for-action-restart.test.ts +21 -29
  32. package/src/execution/__tests__/helpers/fake-engine-port.ts +13 -68
  33. package/src/execution/__tests__/helpers/legacy-sidecar.ts +33 -0
  34. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +9 -5
  35. package/src/execution/__tests__/idle-gc.test.ts +60 -0
  36. package/src/execution/__tests__/lifecycle-manager.test.ts +3 -254
  37. package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +43 -22
  38. package/src/execution/__tests__/model-config-service.test.ts +111 -0
  39. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +10 -6
  40. package/src/execution/__tests__/notify-ledger.test.ts +42 -1
  41. package/src/execution/__tests__/rebuild-indexes.test.ts +329 -0
  42. package/src/execution/__tests__/record-binding.test.ts +613 -0
  43. package/src/execution/__tests__/record-origin.test.ts +318 -0
  44. package/src/execution/__tests__/record-store-intent-api.test.ts +600 -0
  45. package/src/execution/__tests__/record-store-orphan-revive.test.ts +71 -3
  46. package/src/execution/__tests__/record-store.test.ts +72 -54
  47. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +8 -5
  48. package/src/execution/__tests__/round-supervisor-workflow-origin.test.ts +205 -0
  49. package/src/execution/__tests__/run-orchestration-write-lease.test.ts +293 -0
  50. package/src/execution/__tests__/run-orchestration.test.ts +199 -0
  51. package/src/execution/__tests__/session-file-gc.test.ts +35 -4
  52. package/src/execution/__tests__/session-reconstructor.test.ts +78 -0
  53. package/src/execution/__tests__/state-marker.test.ts +288 -0
  54. package/src/execution/__tests__/stream-sink-retirement.test.ts +8 -5
  55. package/src/execution/__tests__/subagent-actions-core.test.ts +67 -16
  56. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +0 -1
  57. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +27 -11
  58. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +17 -11
  59. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +130 -53
  60. package/src/execution/__tests__/subagent-service.test.ts +17 -215
  61. package/src/execution/__tests__/subprocess-agent-runner-no-progress-killall.test.ts +138 -0
  62. package/src/execution/__tests__/subprocess-agent-runner.test.ts +105 -660
  63. package/src/execution/__tests__/sync-collect-recovery.test.ts +23 -16
  64. package/src/execution/__tests__/terminal-write-latency.test.ts +79 -0
  65. package/src/execution/__tests__/workflow-agent-dispatch.test.ts +782 -0
  66. package/src/execution/alive-store.ts +37 -28
  67. package/src/execution/{cold-resurrect.ts → cold-lookup.ts} +48 -45
  68. package/src/execution/config.ts +1 -1
  69. package/src/execution/conversation-continuation.ts +541 -0
  70. package/src/execution/dialog-queue.ts +60 -12
  71. package/src/execution/engine/__tests__/common/capability-gate.test.ts +1 -1
  72. package/src/execution/engine/__tests__/common/journal-wiring.test.ts +16 -1
  73. package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/fake-engine-protocol.mjs +17 -51
  74. package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/smoke-run.fixture.json +0 -5
  75. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +1 -1
  76. package/src/execution/engine/__tests__/conformance/protocol-blackbox.test.ts +5 -9
  77. package/src/execution/engine/__tests__/engine-discovery-scan.test.ts +0 -3
  78. package/src/execution/engine/__tests__/engine-discovery.test.ts +0 -3
  79. package/src/execution/engine/__tests__/model-prompt.test.ts +0 -3
  80. package/src/execution/engine/__tests__/registry.test.ts +0 -1
  81. package/src/execution/engine/__tests__/routing.test.ts +0 -1
  82. package/src/execution/engine/__tests__/run-failure-worktree-cleanup.test.ts +0 -3
  83. package/src/execution/engine/client/__tests__/__fixtures__/fake-engine.mjs +0 -3
  84. package/src/execution/engine/client/__tests__/engine-client-reap.test.ts +212 -0
  85. package/src/execution/engine/client/__tests__/mirror.test.ts +73 -1
  86. package/src/execution/engine/client/__tests__/protocol-closure.test.ts +2 -9
  87. package/src/execution/engine/client/__tests__/remote-engine.test.ts +46 -12
  88. package/src/execution/engine/client/engine-client.ts +81 -25
  89. package/src/execution/engine/client/mirror.ts +9 -0
  90. package/src/execution/engine/client/remote-engine.ts +52 -34
  91. package/src/execution/engine/client/reverse-router.ts +6 -27
  92. package/src/execution/engine/common/__tests__/run-signals.test.ts +79 -0
  93. package/src/execution/engine/common/capability-gate.ts +27 -0
  94. package/src/execution/engine/common/data-dir.ts +3 -2
  95. package/src/execution/engine/common/errors.ts +1 -1
  96. package/src/execution/engine/common/journal-wiring.ts +18 -9
  97. package/src/execution/engine/common/run-signals.ts +83 -0
  98. package/src/execution/engine/d8-compat.ts +1 -1
  99. package/src/execution/engine/host/__tests__/host-bridge.test.ts +3 -18
  100. package/src/execution/engine/host/host-bridge.ts +19 -41
  101. package/src/execution/engine/host/pi-host-binding.ts +5 -5
  102. package/src/execution/engine/port.ts +34 -54
  103. package/src/execution/engine/types.ts +11 -14
  104. package/src/execution/execution-record.ts +15 -47
  105. package/src/execution/finalize-record.ts +94 -197
  106. package/src/execution/idle-gc.ts +9 -3
  107. package/src/execution/lifecycle-manager.ts +25 -361
  108. package/src/execution/lifecycle-predicates.ts +22 -19
  109. package/src/execution/manifest-store.ts +40 -42
  110. package/src/execution/model-config-service.ts +1 -1
  111. package/src/execution/notifier.ts +39 -1
  112. package/src/execution/notify-host.ts +13 -4
  113. package/src/execution/record-entry.ts +20 -2
  114. package/src/execution/record-store.ts +925 -160
  115. package/src/execution/relay-env.ts +8 -3
  116. package/src/execution/round-supervisor/domain.ts +17 -5
  117. package/src/execution/round-supervisor/service-binding.test.ts +89 -53
  118. package/src/execution/round-supervisor/service-binding.ts +71 -21
  119. package/src/execution/round-supervisor/supervisor.ts +23 -9
  120. package/src/execution/service/record-access.ts +547 -0
  121. package/src/execution/service/record-lifecycle.ts +542 -0
  122. package/src/execution/service/run-orchestration.ts +1647 -0
  123. package/src/execution/service/service-bootstrap.ts +111 -0
  124. package/src/execution/service/service-constants.ts +27 -0
  125. package/src/execution/service/session-baselines.ts +401 -0
  126. package/src/execution/service/sync-collect-domain.ts +442 -0
  127. package/src/execution/service/workflow-dispatch.ts +567 -0
  128. package/src/execution/session-file-gc.ts +11 -8
  129. package/src/execution/session-reconstructor.ts +44 -1
  130. package/src/execution/sessions-index.ts +20 -3
  131. package/src/execution/settled-watchdog.ts +86 -43
  132. package/src/execution/state-marker.ts +516 -0
  133. package/src/execution/stream-sink.ts +1 -1
  134. package/src/execution/subagent-actions-core.ts +53 -31
  135. package/src/execution/subagent-service.ts +643 -2980
  136. package/src/execution/subprocess-agent-runner.ts +46 -323
  137. package/src/execution/types.ts +60 -43
  138. package/src/execution/ui-request-handler-factory.ts +5 -0
  139. package/src/execution/worktree-manager.ts +1 -1
  140. package/src/execution/worktree-registry.ts +1 -1
  141. package/src/index.ts +6 -5
  142. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +2 -3
  143. package/src/orchestration/__tests__/agent-call-stream.test.ts +24 -49
  144. package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +4 -4
  145. package/src/orchestration/__tests__/file-run-store.test.ts +5 -7
  146. package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +2 -3
  147. package/src/orchestration/__tests__/run-snapshot.test.ts +27 -43
  148. package/src/orchestration/__tests__/worker-message-pump-workflow-dispatch.test.ts +274 -0
  149. package/src/orchestration/execute-agent-call.ts +1 -1
  150. package/src/orchestration/file-run-store.ts +1 -1
  151. package/src/orchestration/models/ports.ts +20 -13
  152. package/src/orchestration/models/types.ts +11 -13
  153. package/src/orchestration/run-snapshot.ts +18 -22
  154. package/src/orchestration/worker-message-pump.ts +33 -57
  155. package/src/shared/atomic-write.ts +10 -8
  156. package/dist/chunk-T2SYBW3I.js +0 -24
  157. package/dist/engine-discovery-scan-BXLA8y-z.d.cts +0 -1624
  158. package/dist/engine-discovery-scan-BXLA8y-z.d.ts +0 -1624
  159. package/src/execution/__tests__/chat-round-first-round-watchdog.test.ts +0 -255
  160. package/src/execution/__tests__/finalized-marker.test.ts +0 -104
  161. package/src/execution/__tests__/lifecycle-manager-lock.test.ts +0 -211
  162. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +0 -488
  163. package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +0 -75
  164. package/src/execution/__tests__/tombstone-store.test.ts +0 -73
  165. package/src/execution/engine/__tests__/conformance/chat-round-protocol.test.ts +0 -230
  166. package/src/execution/finalized-marker.ts +0 -70
  167. package/src/execution/tombstone-store.ts +0 -72
@@ -1,230 +0,0 @@
1
- // chat-round-protocol.test.ts —— [W6] chat 轮次协议 v1.x 增量的协议黑盒契约。
2
- //
3
- // 设计权威源:chat-domain-v1x-liveness-governance.md §3.2 D1-A + §5 W1/W6 行 +
4
- // 验收 A6。W1 SDK 已交付第 9 反向通道 host/roundLifecycle(settled/idle/failed
5
- // 三相位,关联键 runId|recordId 互斥分路)与 conversation gate 负向构造器;本套件
6
- // 在 conformance 层(协议客户端 × fake 引擎 CLI)钉住三条语义,供 W3 chat 改线与
7
- // A7 live 门回归:
8
- //
9
- // ① roundLifecycle 三相位(settled/idle/failed)× 双关联键:数据面回执链闭合
10
- // (宿主必须确认收到——fake 引擎收到帧②应答后回发 host/log "ack …" 回声,
11
- // NDJSON 单连接有序保证回声先于 run 应答帧到达);core 不误判引擎故障
12
- // (run 正常 resolve + 客户端保持 ready,10s 数据面守卫不触发)。
13
- // ② recordId 键 streamDelta 分路:续聊轮 delta 经 recordId 关联(D1-A 裁定,
14
- // InteractParams/Result 无 runId)。[W3 接线落地] reverse-router 的 recordId
15
- // 分支接 EngineClient.recordRoutes 真实消费——本用例钉「宿主确认收到 +
16
- // delta 投递到 recordId 注册的 chat 轮消费点 + 不误投 runId 路由」。
17
- // ③ conversation gate 负向(A6):manifest 无 conversation 位的引擎(capabilities
18
- // 覆写 unsupported)→ assertChatConversationSupported 同步拒——错误码
19
- // engine_capability_unsupported + 恢复指引含「升级引擎包」文案契约;同一
20
- // 客户端的 run 域完全正常(gate 只拦 chat 面不伤 run 域)。
21
- //
22
- // 帧载荷类型权威 = W1 SDK `HostRoundLifecycleParams`(isHostRoundLifecycleParams
23
- // 的相位居留形状由 SDK 包内测试覆盖);fake 引擎按该形状发帧,本套件断言消费侧。
24
-
25
- import { mkdtempSync, rmSync } from "node:fs";
26
- import { tmpdir } from "node:os";
27
- import { dirname, join } from "node:path";
28
- import { fileURLToPath } from "node:url";
29
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
30
-
31
- import {
32
- assertChatConversationSupported,
33
- isHostRoundLifecycleParams,
34
- type InitializeResult,
35
- } from "@zhushanwen/subagent-engine-sdk";
36
-
37
- import { EngineClient } from "../../client/engine-client.ts";
38
-
39
- const FIXTURE_DIR = join(dirname(fileURLToPath(import.meta.url)), "__fixtures__", "engine-protocol");
40
- const FAKE_ENGINE = join(FIXTURE_DIR, "fake-engine-protocol.mjs");
41
- const FIXTURE = join(FIXTURE_DIR, "smoke-run.fixture.json");
42
-
43
- let dataDir: string;
44
-
45
- beforeEach(() => {
46
- dataDir = mkdtempSync(join(tmpdir(), "w6-chat-round-protocol-"));
47
- });
48
-
49
- afterEach(() => {
50
- rmSync(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
51
- });
52
-
53
- interface Harness {
54
- client: EngineClient;
55
- logs: Array<{ level: string; message: string }>;
56
- streamDeltas: string[];
57
- /** [W3] recordId 键 chat 轮路由捕获(续聊轮 delta 消费点)。 */
58
- recordDeltas: string[];
59
- recordPhases: Array<Record<string, unknown>>;
60
- }
61
-
62
- function makeHarness(extraEnv: Record<string, string> = {}): Harness {
63
- const h: Harness = {
64
- client: undefined as unknown as EngineClient,
65
- logs: [],
66
- streamDeltas: [],
67
- recordDeltas: [],
68
- recordPhases: [],
69
- };
70
- h.client = new EngineClient({
71
- engineId: "fake",
72
- command: process.execPath,
73
- args: [FAKE_ENGINE],
74
- hostKind: "test",
75
- hostVersion: "w6-chat-round-protocol",
76
- dataDir,
77
- envPrefixes: [],
78
- log: (p) => h.logs.push({ level: p.level, message: p.message }),
79
- baseEnv: {
80
- ...process.env,
81
- FAKE_PROTOCOL_FIXTURE: FIXTURE,
82
- ...extraEnv,
83
- },
84
- });
85
- const unregister = h.client.registerRunRoute("run-smoke-1", {
86
- onStreamDelta: (delta) => {
87
- h.streamDeltas.push(delta);
88
- },
89
- });
90
- void unregister;
91
- // [W3] recordId 键 chat 轮路由(registerRecordRoute——RemoteEngine.registerChatRoundRoute
92
- // 的底层实现;conformance 层直接驱动 EngineClient 面)。
93
- const unregisterRecord = h.client.registerRecordRoute("rec-chat-1", {
94
- onStreamDelta: (delta) => {
95
- h.recordDeltas.push(delta);
96
- },
97
- onRoundLifecycle: (phase) => {
98
- h.recordPhases.push(phase as Record<string, unknown>);
99
- },
100
- });
101
- void unregisterRecord;
102
- return h;
103
- }
104
-
105
- describe("[W6] host/roundLifecycle 三相位 × 双关联键(协议黑盒回执链)", () => {
106
- it("settled/idle/failed 相位 × runId|recordId 键全部被宿主确认(ack 回声)且不判引擎故障", async () => {
107
- const h = makeHarness({ FAKE_RUN_LIFECYCLE: "1" });
108
- try {
109
- await h.client.ensureConnected();
110
- const result = (await h.client.request("run", {
111
- runId: "run-smoke-1",
112
- task: "w6 round lifecycle probe",
113
- ctx: { poolKey: "shared", cwd: dataDir, model: "fake/fake-1" },
114
- })) as { outcome: Record<string, unknown> };
115
- void result;
116
-
117
- // 回执链闭合:6 帧(三相位 × runId 键 "run-smoke-1" / recordId 键 "rec-chat-1")
118
- // 的帧②应答全部到达 fake 引擎并回发回声——「宿主必须确认收到」的数据面契约。
119
- // 回声帧经引擎侧往返(core 帧②应答 → fake 收到 → 回发 log),晚于 run 应答
120
- // 帧到达——断言轮询等待(NDJSON 单连接有序,run resolve 后毫秒级必达)。
121
- await expect
122
- .poll(
123
- () => h.logs.map((l) => l.message).filter((m) => m.startsWith("ack roundLifecycle ")),
124
- { timeout: 5_000, interval: 20 },
125
- )
126
- .toEqual([
127
- "ack roundLifecycle run-smoke-1 settled",
128
- "ack roundLifecycle run-smoke-1 idle",
129
- "ack roundLifecycle run-smoke-1 failed",
130
- "ack roundLifecycle rec-chat-1 settled",
131
- "ack roundLifecycle rec-chat-1 idle",
132
- "ack roundLifecycle rec-chat-1 failed",
133
- ]);
134
-
135
- // [W3 接线契约] recordId 键相位帧投递到 registerRecordRoute 注册的 chat 轮
136
- // 消费点(settled/idle/failed 三相位按序;宿主编排层据此做交棒/挂 idle timer/
137
- // 失败分诊——分发层只做键路由)。
138
- await expect
139
- .poll(() => h.recordPhases.map((p) => `${p.recordId}:${String(p.phase)}`), { timeout: 5_000, interval: 20 })
140
- .toEqual([
141
- "rec-chat-1:settled",
142
- "rec-chat-1:idle",
143
- "rec-chat-1:failed",
144
- ]);
145
-
146
- // 不误判引擎故障:run 正常 resolve + 客户端保持 ready(10s 数据面应答守卫
147
- // 未触发杀链——roundLifecycle 是数据面回执语义,不是故障信号)。
148
- expect(h.client.currentState).toBe("ready");
149
-
150
- // failed 相位的 error{code,message} 居留形状(isHostRoundLifecycleParams
151
- // 判据的 failed 分支)——fake 引擎按 W1 载荷类型构造,宿主侧对同形状自证。
152
- const failedShape = {
153
- runId: "run-smoke-1",
154
- phase: "failed",
155
- error: { code: "engine_run_failed", message: "round failed (scripted)", recovery: "retry" },
156
- };
157
- expect(isHostRoundLifecycleParams(failedShape)).toBe(true);
158
- const malformedShape = { runId: "r", recordId: "c", phase: "settled" };
159
- expect(isHostRoundLifecycleParams(malformedShape)).toBe(false);
160
- } finally {
161
- await h.client.dispose();
162
- }
163
- });
164
- });
165
-
166
- describe("[W6] recordId 键 streamDelta / roundLifecycle 分路(续聊轮关联键,D1-A)", () => {
167
- it("recordId 键 delta:宿主确认收到(ack 回声)+ 投递到 recordId 注册的 chat 轮消费点,不误投 runId 路由", async () => {
168
- const h = makeHarness({ FAKE_RUN_RECORD_DELTA: "1" });
169
- try {
170
- await h.client.ensureConnected();
171
- await h.client.request("run", {
172
- runId: "run-smoke-1",
173
- task: "w6 record-key delta probe",
174
- ctx: { poolKey: "shared", cwd: dataDir, model: "fake/fake-1" },
175
- });
176
-
177
- // 宿主确认收到(数据面回执闭合;回声帧晚于 run 应答到达,轮询等待)。
178
- await expect
179
- .poll(() => h.logs.map((l) => l.message), { timeout: 5_000, interval: 20 })
180
- .toContain("ack streamDelta recordId=rec-chat-1");
181
- // [W3 接线契约] recordId 键投递到 recordRoutes 注册的 chat 轮消费点
182
- //(W1 偏差 #1 的 no-op 分支已替换为真实分发——reverse-router recordId 分支)。
183
- expect(h.recordDeltas).toEqual(["chat-delta-not-routed-to-runId"]);
184
- // 关联键分路互斥:recordId 键不误投 runId 路由(runId 键 fixture delta 不受影响)。
185
- expect(h.streamDeltas).toEqual(["Hello", " world"]);
186
- expect(h.client.currentState).toBe("ready");
187
- } finally {
188
- await h.client.dispose();
189
- }
190
- });
191
-
192
- });
193
-
194
- describe("[W6] conversation gate 负向(A6:无 gate 位引擎 chat 请求同步拒,run 域不受影响)", () => {
195
- it("capabilities.conversation=unsupported → assertChatConversationSupported 同步拒 + 文案契约;同客户端 run 正常", async () => {
196
- const h = makeHarness({ FAKE_CAPABILITIES_CONVERSATION: "unsupported" });
197
- try {
198
- await h.client.ensureConnected();
199
- const diagnostics = h.client.getInitializeDiagnostics() as InitializeResult;
200
- expect(diagnostics.capabilities.conversation).toBe("unsupported");
201
-
202
- // W1 具名构造器(W3 chat 路由接线的判据单源):同步拒、错误码与恢复指引
203
- // 文案契约——「升级引擎包」恢复指引对齐 core capability-gate conversation
204
- // 分支(错误 → 权威源 → 重试闭环)。
205
- let thrown: unknown;
206
- try {
207
- assertChatConversationSupported("fake", diagnostics.capabilities);
208
- } catch (err) {
209
- thrown = err;
210
- }
211
- expect(thrown).toMatchObject({
212
- name: "EngineSdkError",
213
- code: "engine_capability_unsupported",
214
- });
215
- expect((thrown as Error).message).toContain("conversation");
216
- expect((thrown as { recovery: string }).recovery).toContain("升级引擎包");
217
-
218
- // A6 后半:gate 只拦 chat 面——同一客户端的 run 域完全正常。
219
- const result = (await h.client.request("run", {
220
- runId: "run-smoke-1",
221
- task: "w6 gate-negative run-domain probe",
222
- ctx: { poolKey: "shared", cwd: dataDir, model: "fake/fake-1" },
223
- })) as { outcome: Record<string, unknown> };
224
- expect(result.outcome).toBeDefined();
225
- expect(h.client.currentState).toBe("ready");
226
- } finally {
227
- await h.client.dispose();
228
- }
229
- });
230
- });
@@ -1,70 +0,0 @@
1
- // src/runtime/execution/finalized-marker.ts
2
- //
3
- // Finalized 状态的 sidecar 持久化。
4
- //
5
- // 背景:subagent 执行完成(done/failed)后,session.jsonl 的最终写入可能因进程
6
- // 异常终止而丢失。`.finalized` sidecar 标记该 session 已正常结束,collectRecords
7
- // 重建时可用它区分「正常结束但文件截断」与「执行中断」。
8
- //
9
- // 设计:对称 cancelled sidecar(tombstone-store.ts)。`.finalized` 内容为可选的
10
- // 关闭原因字符串(v8.5 起 writeFinalized 第二参数写入),旧格式为空文件——空内容
11
- // 表示死因不可考,磁盘重建兜底 closedReason=disconnected(不再误导为 gc)。
12
- // 与 `.cancelled` 互斥——一个 session 要么 finalized 要么 cancelled,不可能两者兼有。
13
- //
14
- // best-effort:写 IO 错静默,不阻断主流程。
15
-
16
- import * as fs from "node:fs";
17
-
18
- // ============================================================
19
- // 公开函数
20
- // ============================================================
21
-
22
- /**
23
- * 在 session.jsonl 旁写 `.finalized` sidecar。
24
- * best-effort:任何 I/O 错误静默(finalize 标记是次要信号,status 已在内存 record 上设好)。
25
- *
26
- * 与 `.cancelled` 互斥(BC-4):写前删除 `.cancelled`(如存在),确保不会两者共存。
27
- *
28
- * @param sessionFile session.jsonl 绝对路径
29
- * @param reason 可选的 L2 关闭原因写入 sidecar 内容(v8.5 起):磁盘重建时用它区分
30
- * 真实死因(user-close / parent-* 等),替代旧的「一律 gc」误导标签。传
31
- * undefined = 空文件(旧格式兼容:未知死因,重建兜底 disconnected)。
32
- */
33
- export function writeFinalized(sessionFile: string, reason?: string): void {
34
- try {
35
- // BC-4 互斥:清理可能存在的 .cancelled。force:true 静默 ENOENT——
36
- // 未 cancel 过的 session(done/failed/aborted)本就无 .cancelled,属正常路径。
37
- // 此前用 unlinkSync+bestEffort 记 console.debug,取消嵌套链条时每层 ENOENT 刷屏。
38
- fs.rmSync(`${sessionFile}.cancelled`, { force: true });
39
- fs.writeFileSync(`${sessionFile}.finalized`, reason ?? "", "utf-8");
40
- } catch (_e) {
41
- void _e; // 静默:写失败不阻断 finalize 主流程。
42
- }
43
- }
44
-
45
- /**
46
- * 读 session.jsonl 旁的 `.finalized` sidecar 内容携带的关闭原因。
47
- * 返回 trim 后的字符串:sidecar 存在时可能为空串(旧格式空文件 / 未携 reason)。
48
- * 返回 undefined:sidecar 不存在 / 读取失败(与空串区分「没正常结束过」)。
49
- */
50
- export function readFinalizedReason(sessionFile: string): string | undefined {
51
- try {
52
- return fs.readFileSync(`${sessionFile}.finalized`, "utf-8").trim();
53
- } catch {
54
- return undefined;
55
- }
56
- }
57
-
58
- /**
59
- * 读 session.jsonl 旁的 `.finalized` sidecar 是否存在。
60
- * 返回 true:sidecar 存在(内容不校验,存在性即信号)。
61
- * 返回 false:sidecar 不存在 / 读取失败。
62
- */
63
- export function readFinalized(sessionFile: string): boolean {
64
- try {
65
- fs.accessSync(`${sessionFile}.finalized`);
66
- return true;
67
- } catch {
68
- return false;
69
- }
70
- }
@@ -1,72 +0,0 @@
1
- // src/runtime/execution/tombstone-store.ts
2
- //
3
- // Cancelled 状态的 sidecar 持久化。
4
- //
5
- // 背景:用户 cancel background subagent 时 session.abort() → prompt() reject →
6
- // session.jsonl 被中途截断(无最终 assistant message,无 stopReason 标记)。所以
7
- // cancelled 状态无法从 session.jsonl 可靠检测。本模块在 session.jsonl 旁写一个
8
- // `.cancelled` sidecar 文件(单行 JSON),collectRecords 重建时读它 override status。
9
- //
10
- // 设计:单条 cancelled = 单 sidecar,无全局 index 可损坏,无并发问题,被现有
11
- // session-file-gc 的目录扫描自然清理(GC 已扩展为也清理 .cancelled)。YAGNI 更重方案。
12
-
13
- import * as fs from "node:fs";
14
-
15
- // ============================================================
16
- // 类型
17
- // ============================================================
18
-
19
- /** Tombstone 内容(单行 JSON)。 */
20
- export interface CancelledTombstone {
21
- id: string;
22
- status: "cancelled";
23
- agent: string;
24
- startedAt: number;
25
- endedAt: number;
26
- }
27
-
28
- // ============================================================
29
- // 公开函数
30
- // ============================================================
31
-
32
- /**
33
- * 在 session.jsonl 旁写 cancelled tombstone。
34
- * best-effort:任何 I/O 错误静默(cancel 已成功,持久化是次要的——内存 record 即将被
35
- * archive 淘汰,下次读靠 tombstone 标记 cancelled)。
36
- */
37
- export function writeCancelledTombstone(sessionFile: string, meta: CancelledTombstone): void {
38
- try {
39
- const tombstonePath = `${sessionFile}.cancelled`;
40
- fs.writeFileSync(tombstonePath, `${JSON.stringify(meta)}\n`, "utf-8");
41
- } catch (_e) {
42
- void _e; // 静默:写失败不阻断 cancel 主流程(status 已在内存 record 上设好)。
43
- }
44
- }
45
-
46
- /**
47
- * 读 session.jsonl 旁的 cancelled tombstone。
48
- * 返回 undefined:sidecar 不存在 / 损坏 / 解析失败。
49
- */
50
- export function readCancelledTombstone(sessionFile: string): CancelledTombstone | undefined {
51
- let raw: string;
52
- try {
53
- raw = fs.readFileSync(`${sessionFile}.cancelled`, "utf-8");
54
- } catch {
55
- return undefined; // sidecar 不存在(正常——非 cancelled record 无 sidecar)。
56
- }
57
- try {
58
- const parsed = JSON.parse(raw) as Partial<CancelledTombstone>;
59
- if (
60
- typeof parsed.id === "string" &&
61
- parsed.status === "cancelled" &&
62
- typeof parsed.agent === "string" &&
63
- typeof parsed.startedAt === "number" &&
64
- typeof parsed.endedAt === "number"
65
- ) {
66
- return parsed as CancelledTombstone;
67
- }
68
- return undefined; // 结构不合法 → 降级。
69
- } catch {
70
- return undefined; // JSON 损坏 → 降级。
71
- }
72
- }