@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
package/src/index.ts CHANGED
@@ -24,7 +24,7 @@
24
24
  // 0.4.0 = 首个公开发布的收敛收口面(0.3.0 为 2026-08-30 裁决的跳号占位,永不单独
25
25
  // 发布;+ minor changeset 收口面落本号);与 package.json version 的一致性由
26
26
  // src/__tests__/smoke.test.ts 动态守护,改版本须两处同步。
27
- export const CORE_PACKAGE_VERSION = "0.8.0";
27
+ export const CORE_PACKAGE_VERSION = "0.9.0";
28
28
 
29
29
  // ── 宿主端口接线面(core/)────────────────────────────────────
30
30
  // HostServices:dataRoot / log / discoveryRoots 端口 + configureCore 注入;
@@ -66,8 +66,6 @@ export type {
66
66
  EngineCapabilities,
67
67
  EngineHandle,
68
68
  EngineHandleData,
69
- InteractAction,
70
- InteractResult,
71
69
  ProbeReport,
72
70
  ReplayedTurn,
73
71
  SessionView,
@@ -212,13 +210,16 @@ export {
212
210
  // globalThis[Symbol.for] slot 防 jiti 多实例分裂,/resume /fork 复用既有实例
213
211
  // (SR-3/SR-4 语义)。createSubagentService / SubagentServiceInit 为构造依赖参数
214
212
  // 注入形态(modelService 等,无全局查找,@experimental U10 / D6)。
213
+ // [H3/R6] 单例访问器 + Init 接口外移支撑文件 service/service-bootstrap.ts,本
214
+ // barrel 直接改指向该文件(导出符号面不变;壳对 bootstrap 零 re-export——防壳↔
215
+ // bootstrap 值环,设计 v4 import 纪律)。对外消费方零感知。
216
+ export { SubagentService } from "./execution/subagent-service.ts";
215
217
  export {
216
- SubagentService,
217
218
  getSubagentService,
218
219
  setSubagentService,
219
220
  createSubagentService,
220
221
  type SubagentServiceInit,
221
- } from "./execution/subagent-service.ts";
222
+ } from "./execution/service/service-bootstrap.ts";
222
223
  // notifyGateAllowsDelivery:closedReason → 投递许可判定(通知门)——
223
224
  // 投递内核与壳侧 notify 链的共同语义锚点(execution 生产域消费,A2a)。
224
225
  export { notifyGateAllowsDelivery } from "./execution/subagent-service.ts";
@@ -135,13 +135,12 @@ describe("U7a: catch 分支兜底回发 agent-result(非 Abort 异常)", ()
135
135
  expect(posted.result.error).toBe("runner exploded");
136
136
  expect(posted.result.content).toBe("");
137
137
 
138
- // state 一致性三件套(review should_fix):
138
+ // state 一致性三件套(review should_fix;[H2 W3] trace.live 字段已删除,
139
+ // 「live 清除」由类型层面收敛——节点无运行期附属对象):
139
140
  // trace node 标 failed(不遗留 running 幽灵节点)
140
141
  const traceNode = run.state.trace.find(2);
141
142
  expect(traceNode?.status).toBe("failed");
142
143
  expect(traceNode?.result?.error).toBe("runner exploded");
143
- // node.live 清除(防内存泄漏)
144
- expect(traceNode?.live).toBeUndefined();
145
144
  // 持久化(catch 是最需留证的场景)
146
145
  expect(deps.store.save).toHaveBeenCalled();
147
146
  } finally {
@@ -1,10 +1,18 @@
1
1
  // src/orchestration/__tests__/agent-call-stream.test.ts
2
2
  //
3
- // U4: dispatchAgentCall 创建 SubagentStream 并在 agent call 结束后 dispose
4
- // U5: dispatchAgentCall widgetKey 格式 = subagent-stream-<runId>-<stepIndex>
5
- // U6: streamSink undefined 时 dispatchAgentCall 不创建 stream 不报错
3
+ // [H2 W3] 原 U4/U5(dispatchAgentCall 创建 SubagentStream + widgetKey 格式)与
4
+ // U6(streamSink undefined 降级)锁定的是 pump 旁路 streaming 通道——该族随设计
5
+ // subagent-workflow-record-unification.md D2「stream 通道承接」退役:pump 不再
6
+ // 构造 SubagentStream(streaming 由 service 派发路径既有通道承载)。
6
7
  //
7
- // 通过 handleWorkerMessage 触发 dispatchAgentCall(内部函数不 export)。
8
+ // [R2-5] streamSink 注入面已彻底删除(LifecycleDeps 死字段清理):U4' 的运行时
9
+ // 负向断言(「streamSink 已注入 → setWidget 零调用」)前提消失,改为**编译期结构
10
+ // 断言**(下方 HasStreamSink)——字段若被重新引入,keyof LifecycleDeps 重新出现
11
+ // "streamSink",类型断言立即编译红;比运行时负向用例更强的永久护栏。
12
+ //
13
+ // 本文件现锁定:
14
+ // - U4'(编译期):LifecycleDeps 不再有 streamSink 注入面
15
+ // - U6(运行时):最简 deps(无 stream 通道)下 runner.run 仍被调用,无异常
8
16
 
9
17
  import { describe, expect, it, vi } from "vitest";
10
18
 
@@ -52,9 +60,8 @@ function makeRunningRun(runId: string): WorkflowRun {
52
60
  } as unknown as WorkflowRun;
53
61
  }
54
62
 
55
- /** LifecycleDeps mockrunner.run 可控制返回值,streamSink 可配置 */
63
+ /** LifecycleDeps mockrunner.run 可控制返回值;streamSink 注入面已随 [R2-5] 删除) */
56
64
  function makeDeps(opts: {
57
- streamSink?: { setWidget: ReturnType<typeof vi.fn> };
58
65
  runnerResult?: AgentResult;
59
66
  } = {}): LifecycleDeps {
60
67
  return {
@@ -65,7 +72,6 @@ function makeDeps(opts: {
65
72
  eventBus: { emit: vi.fn() },
66
73
  onRunDone: vi.fn(),
67
74
  log: vi.fn(),
68
- streamSink: opts.streamSink,
69
75
  } as unknown as LifecycleDeps;
70
76
  }
71
77
 
@@ -87,50 +93,20 @@ function makeAgentCallMsg(callId: number): unknown {
87
93
  };
88
94
  }
89
95
 
90
- // ── U4: dispatchAgentCall 创建 SubagentStream 并 dispose ──
91
-
92
- describe("U4: dispatchAgentCall stream dispose", () => {
93
- it("agent call 结束后 stream.dispose 被调用(setWidget 末次 lines=undefined)", async () => {
94
- const setWidget = vi.fn();
95
- const deps = makeDeps({ streamSink: { setWidget } });
96
- const run = makeRunningRun("wf-test-123");
97
- const handlers = makeHandlers();
98
-
99
- await handleWorkerMessage(run, makeAgentCallMsg(0), deps, handlers);
100
- // dispatchAgentCall 内部 void withSlot(...)(fire-and-forget),需等 microtask 完成
101
- await vi.waitFor(() => {
102
- expect(setWidget.mock.calls.length).toBeGreaterThanOrEqual(1);
103
- });
104
-
105
- const calls = setWidget.mock.calls;
106
- const lastCall = calls[calls.length - 1];
107
- expect(lastCall[1]).toBeUndefined();
108
- });
109
- });
110
-
111
- // ── U5: widgetKey 格式 ──
112
-
113
- describe("U5: widgetKey 格式", () => {
114
- it("widgetKey = subagent-stream-<runId>-<stepIndex>", async () => {
115
- const setWidget = vi.fn();
116
- const deps = makeDeps({ streamSink: { setWidget } });
117
- const run = makeRunningRun("wf-test-123");
118
- const handlers = makeHandlers();
96
+ // ── U4': stream 通道退役(编译期结构断言,[R2-5]) ──
119
97
 
120
- await handleWorkerMessage(run, makeAgentCallMsg(2), deps, handlers);
121
- await vi.waitFor(() => {
122
- expect(setWidget.mock.calls.length).toBeGreaterThanOrEqual(1);
123
- });
124
-
125
- const widgetKey = setWidget.mock.calls[0]![0] as string;
126
- expect(widgetKey).toBe("subagent-stream-wf-test-123-2");
127
- });
128
- });
98
+ /** "streamSink" keyof LifecycleDeps 时为 true——与下方显式 false 标注冲突即编译红。 */
99
+ type HasStreamSink = "streamSink" extends keyof LifecycleDeps ? true : false;
100
+ // 结构断言本体:字段已删 = false。若有人重新引入 streamSink 字段,本行类型错误,
101
+ // tsc / vue-tsc 拦截(vitest 转译不做类型检查;类型门由 pre-commit/CI 的 tsc 面
102
+ // 覆盖——比运行时负向用例更强的注入面消失护栏)。
103
+ const assertNoStreamSinkField: HasStreamSink = false;
104
+ void assertNoStreamSinkField;
129
105
 
130
- // ── U6: streamSink undefined 时不报错 ──
106
+ // ── U6: 最简 deps 下派发不报错 ──
131
107
 
132
- describe("U6: streamSink undefined 降级", () => {
133
- it("streamSink=undefined → runner.run 仍被调用,无异常", async () => {
108
+ describe("U6: 最简 deps(无 stream 通道)→ runner.run 仍被调用,无异常", () => {
109
+ it("dispatchAgentCall 不依赖 stream 注入面 → runner.run 正常派发", async () => {
134
110
  const runnerRun = vi.fn(async () => makeMockResult());
135
111
  const deps = {
136
112
  store: { save: vi.fn(async () => {}) },
@@ -140,7 +116,6 @@ describe("U6: streamSink undefined 降级", () => {
140
116
  eventBus: { emit: vi.fn() },
141
117
  onRunDone: vi.fn(),
142
118
  log: vi.fn(),
143
- // streamSink 不设 = undefined
144
119
  } as unknown as LifecycleDeps;
145
120
  const run = makeRunningRun("wf-test-456");
146
121
  const handlers = makeHandlers();
@@ -10,8 +10,9 @@
10
10
  * - OR-6 主线程半边:{type:"log"} 计入 errorLogs + deps.log debug 留痕(含上限裁剪);
11
11
  * 未知 type warn 留痕(协议漂移防线)
12
12
  * - OR-8:脚本 return 时残留 fire-and-forget in-flight call 收口为取消终态
13
- * (call done + trace failed + Cancelled 文案 + completedAt + live 清除),
14
- * 先收口再落盘——done 快照不含 running 节点
13
+ * (call done + trace failed + Cancelled 文案 + completedAt;[H2 W3] trace.live
14
+ * 字段已删除——节点无运行期附属对象可滞留),先收口再落盘——done 快照不含
15
+ * running 节点
15
16
  */
16
17
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
17
18
 
@@ -320,12 +321,11 @@ describe("[OR-8] run done 时残留 in-flight call 收口 cancelled", () => {
320
321
  // call 收口为 done + 取消文案(不删除条目——保留调用痕迹)
321
322
  expect(call?.status).toBe("done");
322
323
  expect(call?.result?.error).toContain("Cancelled");
323
- // trace 节点 failed + completedAt + live 清除(快照/GUI 不再显示 running 步骤)
324
+ // trace 节点 failed + completedAt(快照/GUI 不再显示 running 步骤)
324
325
  const node = run.state.trace.find(1);
325
326
  expect(node?.status).toBe("failed");
326
327
  expect(node?.error).toContain("Cancelled");
327
328
  expect(node?.completedAt).toBeDefined();
328
- expect(node?.live).toBeUndefined();
329
329
  // 持久化快照(收口先于 save)不含 running 形态
330
330
  const snap = toRunSnapshot(run);
331
331
  expect(snap.state.calls.map((c) => c.status)).toEqual(["done"]);
@@ -298,13 +298,15 @@ describe("FileRunStore — 版本衔接与 live-strip(U8 / ⛔5,D4 裁决)
298
298
  expect(JSON.parse(raw).v).toBe("wf-run-v2");
299
299
 
300
300
  // 存量行宽容读 → 再 save:落盘行升级为带 v 当前版本(不做自动迁移的
301
- // 渐进收敛——D4 裁决②「写入时补 v」)
301
+ // 渐进收敛——D4 裁决②「写入时补 v」)。[H2 W3] live 字段退役后 codec 不再
302
+ // 承担键清洗(strip 分支随字段删除退役)——存量行的多余键原样透传(类型
303
+ // 层面已无写点,新写行不可能再产生 live 键;真实存量行经旧 codec strip 也不带)。
302
304
  writeStateFile("wf-legacy-2.jsonl", LEGACY_LINE + "\n");
303
305
  const legacy = (await store.loadAll()).find((r) => r.runId === "wf-legacy-1")!;
304
306
  await store.save(legacy);
305
307
  const migrated = readFileSync(store.stateFilePath("wf-legacy-1"), "utf8").trim();
306
308
  expect(JSON.parse(migrated).v).toBe("wf-run-v2");
307
- expect(migrated.includes('"live"')).toBe(false);
309
+ expect(JSON.parse(migrated).state.status).toBe("running");
308
310
  });
309
311
 
310
312
  it("⛔5 未知更高版本行跳过 + warn(消息含实际版本值,可定位)", async () => {
@@ -326,7 +328,7 @@ describe("FileRunStore — 版本衔接与 live-strip(U8 / ⛔5,D4 裁决)
326
328
  expect(warnMessages().some((m) => m.includes("unsupported version") && m.includes("wf-run-v3"))).toBe(true);
327
329
  });
328
330
 
329
- it("⛔5 live 字段 strip 后落盘(落盘行无 live 键,内存 run 不受影响)", async () => {
331
+ it("⛔5 [H2 W3] live 字段退役:落盘行无 live 键(类型层面收敛的端到端回归)", async () => {
330
332
  const run = makeRun("wf-livestrip-1");
331
333
  const node: import("../models/types.ts").ExecutionTraceNode = {
332
334
  stepIndex: 0,
@@ -334,20 +336,16 @@ describe("FileRunStore — 版本衔接与 live-strip(U8 / ⛔5,D4 裁决)
334
336
  task: "do work",
335
337
  model: "test-model",
336
338
  status: "running",
337
- live: undefined,
338
339
  };
339
340
  run.state.trace.append(node);
340
341
  const AgentCallMod = await import("../models/agent-call.ts");
341
342
  const call = new AgentCallMod.AgentCall(0, { prompt: "do work" }, node);
342
343
  run.state.calls.set(0, call);
343
- node.live = { id: "run-0", turns: [] } as unknown as import("../../execution/types.ts").ExecutionRecord;
344
344
 
345
345
  await store.save(run);
346
346
 
347
347
  const raw = readFileSync(store.stateFilePath("wf-livestrip-1"), "utf8");
348
348
  expect(raw.includes('"live"')).toBe(false);
349
- // strip 产出快照副本:内存中 run 的 live 保留(save 后 run 可继续跑)
350
- expect(node.live).toBeDefined();
351
349
  });
352
350
 
353
351
  it("pi 现网形态行(带 v)经 store 端到端恢复", async () => {
@@ -160,14 +160,13 @@ describe("[OR-3] abort 广播(abortRun / terminateRunningRuns)", () => {
160
160
  const deps = makeDeps();
161
161
  const runId = await runWorkflow(makeSpec(), deps);
162
162
  const run = deps.runs.get(runId)!;
163
- // 注入 in-flight call(真实 AgentCall 实例:running + live record
163
+ // 注入 in-flight call(真实 AgentCall 实例:running)
164
164
  const node: ExecutionTraceNode = {
165
165
  stepIndex: 1,
166
166
  agent: "a",
167
167
  task: "t",
168
168
  model: "m",
169
169
  status: "running",
170
- live: { turns: [] } as never,
171
170
  };
172
171
  run.state.trace.append(node);
173
172
  const call = new AgentCall(1, { prompt: "t" }, node);
@@ -176,7 +175,7 @@ describe("[OR-3] abort 广播(abortRun / terminateRunningRuns)", () => {
176
175
 
177
176
  await abortRun(runId, deps, "mid-flight abort");
178
177
 
179
- // 收口:call done + trace failed(Cancelled 文案)+ live 清除
178
+ // 收口:call done + trace failed(Cancelled 文案;[H2 W3] trace.live 已删除)
180
179
  expect(call.status).toBe("done");
181
180
  expect(call.result?.error).toContain("Cancelled");
182
181
  expect(run.state.trace.find(1)?.status).toBe("failed");
@@ -3,7 +3,8 @@
3
3
  // 四视角:
4
4
  // ①使用者——toRunSnapshot/fromRunSnapshot 往返等值(含 pi 壳 jsonl-run-store
5
5
  // serializeRun 现网形态样本:键序/v 字段/无 live 的快照行,⛔5 往返逐字节一致);
6
- // ②隔离者——live-strip 产出新对象,不 mutate 内存中的 run(save 后 run 可继续跑);
6
+ // ②隔离者——[H2 W3] ExecutionTraceNode.live 字段退役后节点无运行期附属对象,
7
+ // 序列化直出无 live 键(strip 分支随字段删除退役,防御回归锁定);
7
8
  // ③幸存者——版本 guard(D4 裁决③:v 不匹配即拒,字符串无大小序)+ 形状校验
8
9
  // 全分支不抛(返回 undefined);
9
10
  // ④接线者——「缺 v 宽容」不内聚进 codec(D4 裁决②归属:store 层预处理职责,
@@ -47,33 +48,10 @@ function makeRun(runId: string, opts: { status?: "running" | "done" } = {}): Wor
47
48
  );
48
49
  }
49
50
 
50
- /** 最小 live 执行进度对象(strip 逻辑只解构键名,值形态不可达——对齐
51
- * lifecycle-predicates.test.ts 的 partial + as ExecutionRecord 先例)。 */
52
- function makeLiveRecord(id: string): ExecutionRecord {
53
- return {
54
- id,
55
- agent: "coder",
56
- model: "test-model",
57
- thinkingLevel: undefined,
58
- mode: "background",
59
- task: "do work",
60
- slug: "do-work",
61
- startedAt: 0,
62
- rootSessionId: undefined,
63
- parentRecordId: undefined,
64
- depth: 0,
65
- status: "running",
66
- turns: [],
67
- turnCount: 0,
68
- totalTokens: 0,
69
- lastError: undefined,
70
- round: 0,
71
- endedAt: undefined,
72
- result: undefined,
73
- error: undefined,
74
- agentResult: undefined,
75
- controller: undefined,
76
- } as ExecutionRecord;
51
+ /** 最小多余键载体([H2 W3] live 字段退役后的防御样本——模拟旧版本运行期对象
52
+ * 残留的多余键,重水合链路的丢弃行为回归用)。 */
53
+ function makeStaleExtraKey(): Record<string, unknown> {
54
+ return { stale: "legacy-key" };
77
55
  }
78
56
 
79
57
  /**
@@ -204,49 +182,55 @@ describe("run-snapshot — toRunSnapshot/fromRunSnapshot 往返等值", () => {
204
182
  });
205
183
  });
206
184
 
207
- describe("run-snapshot — live-strip 防御内聚", () => {
208
- it("trace 节点与 calls[].traceNode 带 live 序列化输出无 live ", () => {
209
- const run = makeRun("wf-live-1");
185
+ describe("run-snapshot — [H2 W3] live 字段退役后的序列化行为", () => {
186
+ it("running 节点(类型已无 live 字段)全量直出:序列化无 live 键且往返保真", () => {
187
+ const run = makeRun("wf-nolive-1");
210
188
  const node: ExecutionTraceNode = {
211
189
  stepIndex: 0,
212
190
  agent: "coder",
213
191
  task: "do work",
214
192
  model: "test-model",
215
193
  status: "running",
216
- live: undefined,
217
194
  };
218
195
  run.state.trace.append(node);
219
196
  const call = new AgentCall(0, { prompt: "do work" }, node);
220
197
  run.state.calls.set(0, call);
221
- // D-10:call.traceNode 与 trace 节点共享引用,live 挂节点上(running 态常态)
222
- node.live = makeLiveRecord("run-0");
223
198
 
224
199
  const snap = toRunSnapshot(run);
225
200
  const serialized = JSON.stringify(snap);
226
201
 
202
+ // strip 分支随 ExecutionTraceNode.live 字段删除退役——节点不再携带运行期对象,
203
+ // 序列化直出即无 live 键(类型层面收敛的行为回归锁定)
227
204
  expect(serialized.includes('"live"')).toBe(false);
228
205
  expect(snap.state.trace[0]).not.toHaveProperty("live");
229
206
  expect(snap.state.calls[0].traceNode).not.toHaveProperty("live");
207
+
208
+ const back = fromRunSnapshot(JSON.parse(serialized))!;
209
+ expect(back.state.trace.toArray()[0]?.status).toBe("running");
210
+ expect(back.state.trace.toArray()[0]?.task).toBe("do work");
230
211
  });
231
212
 
232
- it("strip 产出新对象,不 mutate 内存 run(save 后 run 可继续跑)", () => {
233
- const run = makeRun("wf-live-2");
213
+ it("运行期多余键残留不进重水合快照的往返链(防御回归)", () => {
214
+ const run = makeRun("wf-stale-1");
234
215
  const node: ExecutionTraceNode = {
235
216
  stepIndex: 0,
236
217
  agent: "coder",
237
218
  task: "do work",
238
219
  model: "test-model",
239
220
  status: "running",
240
- live: undefined,
241
221
  };
242
- run.state.trace.append(node);
243
- node.live = makeLiveRecord("run-0");
222
+ // 模拟旧版本运行期对象残留的多余键(类型外 attach)
223
+ const withStale = Object.assign(node, makeStaleExtraKey()) as ExecutionTraceNode;
224
+ run.state.trace.append(withStale);
244
225
 
245
- toRunSnapshot(run);
226
+ const snap = toRunSnapshot(run);
227
+ // trace 直出拷贝会带多余键(codec 不再承担键清洗——多余键是调用方脏数据,
228
+ // 正常路径类型层面已无写点);但 JSON 往返后重水合按 Trace.fromArray 拷贝,
229
+ // 快照行保持可解析、状态保真
230
+ expect(JSON.parse(JSON.stringify(snap)).state.trace[0].status).toBe("running");
246
231
 
247
- // 原节点 live 保留(运行期消费面——TUI 事件流投影——不受落盘影响)
248
- expect(node.live).toBeDefined();
249
- expect(node.live!.id).toBe("run-0");
232
+ const back = fromRunSnapshot(JSON.parse(JSON.stringify(snap)))!;
233
+ expect(back.state.trace.toArray()[0]?.status).toBe("running");
250
234
  });
251
235
  });
252
236