@zhushanwen/subagent-core 0.2.0 → 0.4.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 (195) hide show
  1. package/README.md +45 -2
  2. package/agents/analyst.md +60 -0
  3. package/agents/coder.md +69 -0
  4. package/agents/debugger.md +66 -0
  5. package/agents/doc-reviewer.md +49 -0
  6. package/agents/explorer.md +63 -0
  7. package/agents/general-purpose.md +32 -0
  8. package/agents/orchestrator.md +61 -0
  9. package/agents/planner.md +53 -0
  10. package/agents/researcher.md +67 -0
  11. package/agents/reviewer.md +73 -0
  12. package/dist/chunk-4KN4TTG7.js +240 -0
  13. package/dist/chunk-APZY4IME.js +27 -0
  14. package/dist/chunk-X7SZ5HLQ.js +66 -0
  15. package/dist/execution/engine/engines/zcode/constants.cjs +65 -0
  16. package/dist/execution/engine/engines/zcode/constants.d.cts +93 -1
  17. package/dist/execution/engine/engines/zcode/constants.d.ts +93 -1
  18. package/dist/execution/engine/engines/zcode/constants.js +43 -1
  19. package/dist/execution/engine/engines/zcode/reader.d.cts +1 -1
  20. package/dist/execution/engine/engines/zcode/reader.d.ts +1 -1
  21. package/dist/execution/engine/engines/zcode/reader.js +4 -234
  22. package/dist/execution/engine/paths.js +7 -19
  23. package/dist/index.cjs +14709 -100
  24. package/dist/index.d.cts +4053 -200
  25. package/dist/index.d.ts +4053 -200
  26. package/dist/index.js +14358 -102
  27. package/dist/{types-BxyAidGf.d.cts → types-DpUO16pj.d.cts} +464 -1
  28. package/dist/{types-BxyAidGf.d.ts → types-DpUO16pj.d.ts} +464 -1
  29. package/package.json +6 -3
  30. package/src/__tests__/review-fix-loop-script.test.ts +90 -2
  31. package/src/__tests__/review-fix-loop-utils.test.ts +21 -0
  32. package/src/__tests__/smoke.test.ts +9 -2
  33. package/src/execution/__tests__/agent-profile.test.ts +232 -0
  34. package/src/execution/__tests__/agents-assembly.test.ts +219 -0
  35. package/src/execution/__tests__/chat-engine-routing.test.ts +71 -0
  36. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +1 -0
  37. package/src/execution/__tests__/create-concurrency-pool.test.ts +194 -0
  38. package/src/execution/__tests__/delivery-methods.test.ts +2 -0
  39. package/src/execution/__tests__/descendant-sweep.test.ts +269 -0
  40. package/src/execution/__tests__/dialog-queue.test.ts +199 -1
  41. package/src/execution/__tests__/epipe-fallback.test.ts +2 -0
  42. package/src/execution/__tests__/execution-runtime-face.test.ts +272 -0
  43. package/src/execution/__tests__/finalize-record.test.ts +106 -0
  44. package/src/execution/__tests__/gc-timer.test.ts +2 -0
  45. package/src/execution/__tests__/get-record-for-action-restart.test.ts +2 -0
  46. package/src/execution/__tests__/get-state-handshake.test.ts +127 -0
  47. package/src/execution/__tests__/keep-alive-no-progress.test.ts +378 -0
  48. package/src/execution/__tests__/kill-all-escalation.test.ts +196 -0
  49. package/src/execution/__tests__/lifecycle-manager-idle-timer-identity.test.ts +117 -0
  50. package/src/execution/__tests__/lifecycle-manager.test.ts +39 -0
  51. package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +99 -0
  52. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +113 -0
  53. package/src/execution/__tests__/notify-ledger.test.ts +294 -0
  54. package/src/execution/__tests__/record-store-orphan-revive.test.ts +159 -0
  55. package/src/execution/__tests__/record-store.test.ts +83 -0
  56. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +4 -0
  57. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +1 -0
  58. package/src/execution/__tests__/run-spawn-edges.test.ts +407 -17
  59. package/src/execution/__tests__/run-spawn-integration.test.ts +1 -1
  60. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +12 -4
  61. package/src/execution/__tests__/service-kill-escalation.test.ts +89 -0
  62. package/src/execution/__tests__/session-pending.test.ts +228 -0
  63. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +110 -0
  64. package/src/execution/__tests__/session-runner-close-prune.test.ts +181 -0
  65. package/src/execution/__tests__/session-runner-epipe.test.ts +1 -0
  66. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +226 -0
  67. package/src/execution/__tests__/settled-watchdog.test.ts +289 -0
  68. package/src/execution/__tests__/spawned-children.test.ts +153 -10
  69. package/src/execution/__tests__/subagent-actions-core.test.ts +997 -0
  70. package/src/execution/__tests__/subagent-service-message-close.test.ts +71 -10
  71. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +174 -0
  72. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +266 -0
  73. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +3 -0
  74. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +316 -0
  75. package/src/execution/__tests__/timeout-integration.test.ts +7 -7
  76. package/src/execution/__tests__/ui-request-handler-factory.test.ts +95 -1
  77. package/src/execution/__tests__/ui-request-queue.test.ts +25 -0
  78. package/src/execution/__tests__/worktree-git-ops.test.ts +571 -0
  79. package/src/execution/__tests__/worktree-reconcile-aging.test.ts +204 -0
  80. package/src/execution/agent-registry.ts +225 -1
  81. package/src/execution/agents-assembly.ts +88 -0
  82. package/src/execution/concurrency-pool.ts +71 -9
  83. package/src/execution/dialog-queue.ts +101 -3
  84. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +49 -2
  85. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +87 -1
  86. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +26 -0
  87. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +83 -1
  88. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +131 -0
  89. package/src/execution/engine/__tests__/registry.test.ts +90 -1
  90. package/src/execution/engine/engine-discovery.ts +12 -16
  91. package/src/execution/engine/engines/pi/task-spec-mapper.ts +3 -3
  92. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +274 -0
  93. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +36 -0
  94. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +472 -0
  95. package/src/execution/engine/engines/zcode/__tests__/preparer-appserver.test.ts +387 -0
  96. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +780 -0
  97. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +815 -0
  98. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +462 -0
  99. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +119 -1
  100. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +136 -6
  101. package/src/execution/engine/engines/zcode/appserver-home.ts +442 -0
  102. package/src/execution/engine/engines/zcode/appserver-probe.ts +141 -0
  103. package/src/execution/engine/engines/zcode/connection.ts +585 -0
  104. package/src/execution/engine/engines/zcode/constants.ts +129 -0
  105. package/src/execution/engine/engines/zcode/golden-sample.ts +54 -8
  106. package/src/execution/engine/engines/zcode/preparer.ts +25 -22
  107. package/src/execution/engine/engines/zcode/session-channel.ts +656 -0
  108. package/src/execution/engine/engines/zcode/zcode-engine.ts +909 -41
  109. package/src/execution/engine/host-task-spec.ts +3 -3
  110. package/src/execution/engine/port.ts +41 -1
  111. package/src/execution/engine/registry.ts +53 -0
  112. package/src/execution/finalize-record.ts +63 -5
  113. package/src/execution/get-state-handshake.ts +85 -10
  114. package/src/execution/lifecycle-manager.ts +27 -3
  115. package/src/execution/manifest-store.ts +53 -62
  116. package/src/execution/notifier.ts +10 -10
  117. package/src/execution/notify-ledger.ts +117 -16
  118. package/src/execution/record-entry.ts +8 -2
  119. package/src/execution/record-store.ts +29 -15
  120. package/src/execution/session-pending.ts +213 -62
  121. package/src/execution/session-runner.ts +974 -128
  122. package/src/execution/sessions-index.ts +10 -55
  123. package/src/execution/settled-watchdog.ts +99 -0
  124. package/src/execution/subagent-actions-core.ts +686 -0
  125. package/src/execution/subagent-service.ts +396 -49
  126. package/src/execution/ui-request-handler-factory.ts +27 -4
  127. package/src/execution/worktree-git-ops.ts +397 -0
  128. package/src/execution/worktree-manager.ts +72 -10
  129. package/src/execution/worktree-registry.ts +6 -12
  130. package/src/index.ts +413 -6
  131. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +42 -3
  132. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +2 -4
  133. package/src/orchestration/__tests__/args-meta.test.ts +358 -0
  134. package/src/orchestration/__tests__/error-recovery-handlers.test.ts +1 -8
  135. package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +312 -0
  136. package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +382 -0
  137. package/src/orchestration/__tests__/file-run-store-prune.test.ts +168 -0
  138. package/src/orchestration/__tests__/file-run-store-throttle.test.ts +168 -0
  139. package/src/orchestration/__tests__/file-run-store.test.ts +389 -0
  140. package/src/orchestration/__tests__/helpers/flush-microtasks.ts +13 -0
  141. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +34 -0
  142. package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +357 -0
  143. package/src/orchestration/__tests__/lifecycle-recover-crashed.test.ts +289 -0
  144. package/src/orchestration/__tests__/lifecycle.test.ts +77 -0
  145. package/src/orchestration/__tests__/run-snapshot.test.ts +353 -0
  146. package/src/orchestration/__tests__/script-generate.test.ts +314 -0
  147. package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +275 -0
  148. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +78 -1
  149. package/src/orchestration/__tests__/workflow-files.test.ts +186 -0
  150. package/src/orchestration/__tests__/workflow-run-summary.test.ts +119 -0
  151. package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +124 -0
  152. package/src/orchestration/agent-opts-resolver.ts +7 -7
  153. package/src/orchestration/args-meta.ts +198 -0
  154. package/src/orchestration/error-recovery.ts +416 -146
  155. package/src/orchestration/execute-agent-call.ts +9 -9
  156. package/src/orchestration/file-run-store.ts +327 -0
  157. package/src/orchestration/launcher.ts +35 -26
  158. package/src/orchestration/lifecycle.ts +355 -79
  159. package/src/orchestration/models/agent-call.ts +7 -7
  160. package/src/orchestration/models/budget.ts +5 -5
  161. package/src/orchestration/models/run-runtime.ts +13 -13
  162. package/src/orchestration/models/trace.ts +8 -8
  163. package/src/orchestration/models/workflow-run.ts +27 -27
  164. package/src/orchestration/models/workflow-script.ts +4 -4
  165. package/src/orchestration/run-snapshot.ts +266 -0
  166. package/src/orchestration/script-generate.ts +154 -0
  167. package/src/orchestration/script-lint.ts +33 -33
  168. package/src/orchestration/worker-handle.ts +10 -10
  169. package/src/orchestration/worker-host.ts +10 -10
  170. package/src/orchestration/worker-script-builder.ts +401 -346
  171. package/src/orchestration/workflow-files.ts +37 -11
  172. package/src/orchestration/workflow-run-summary.ts +69 -0
  173. package/src/orchestration/workflow-script-registry-impl.ts +31 -9
  174. package/src/shared/__tests__/agent-ref.test.ts +209 -2
  175. package/src/shared/__tests__/atomic-write.test.ts +267 -0
  176. package/src/shared/__tests__/bounded-serialize.test.ts +236 -0
  177. package/src/shared/__tests__/injection-render.test.ts +518 -0
  178. package/src/shared/__tests__/resource-discovery-host-roots.test.ts +474 -0
  179. package/src/shared/__tests__/resource-discovery.test.ts +3 -2
  180. package/src/shared/agent-ref.ts +143 -2
  181. package/src/shared/atomic-write.ts +320 -0
  182. package/src/shared/bounded-serialize.ts +154 -0
  183. package/src/shared/injection-render.ts +279 -0
  184. package/src/shared/meta-parser.ts +41 -1
  185. package/src/shared/resource-discovery.ts +104 -37
  186. package/src/shared/resource-meta.ts +14 -0
  187. package/src/shared/xml-injection.ts +9 -9
  188. package/workflows/README.md +9 -9
  189. package/workflows/chain.js +4 -2
  190. package/workflows/map-reduce.js +5 -3
  191. package/workflows/parallel.js +5 -3
  192. package/workflows/review-fix-loop-utils.cjs +14 -3
  193. package/workflows/review-fix-loop.js +18 -7
  194. package/workflows/scatter-gather.js +4 -2
  195. package/dist/chunk-3VOERJPJ.js +0 -22
@@ -30,15 +30,15 @@ export type ReleaseMode = "terminal";
30
30
  // ── RunRuntime ───────────────────────────────────────────────
31
31
 
32
32
  export class RunRuntime {
33
- /** Worker 线程句柄。 */
33
+ /** Worker 线程句柄。 */
34
34
  readonly worker: WorkerHandle;
35
- /** per-running-segment AbortController(一次性,无法复用——G3-001)。 */
35
+ /** per-running-segment AbortController(一次性,无法复用——G3-001)。 */
36
36
  readonly controller: AbortController;
37
- /** Run 级墙钟时间预算计时器(spec.budgetTimeMs > 0 时由 lifecycle 调度,
37
+ /** Run 级墙钟时间预算计时器(spec.budgetTimeMs > 0 时由 lifecycle 调度,
38
38
  * 到期 abortRun time_limited)。release 时清理,避免 abort/replaceRuntime
39
39
  * 后孤儿计时器仍触发(rebuildRuntime 会重排一个全新的计时器,旧的不应残留)。 */
40
40
  readonly timeBudgetTimer?: ReturnType<typeof setTimeout>;
41
- /**
41
+ /**
42
42
  * 本 runtime 代际是否已收到 worker 的终态消息(return / error)。
43
43
  *
44
44
  * [F1] worker exit(0) 且本标记为 false = worker 静默退出、未交付任何终态——最常见根因
@@ -57,7 +57,7 @@ export class RunRuntime {
57
57
  * rebuildRuntime 构造新 RunRuntime 自然重置。
58
58
  */
59
59
  receivedTerminalMessage = false;
60
- /** 防止 release 重复执行(幂等)。 */
60
+ /** 防止 release 重复执行(幂等)。 */
61
61
  private released = false;
62
62
 
63
63
  constructor(
@@ -70,7 +70,7 @@ export class RunRuntime {
70
70
  this.timeBudgetTimer = timeBudgetTimer;
71
71
  }
72
72
 
73
- /**
73
+ /**
74
74
  * 释放所有资源:terminate worker + abort controller。
75
75
  *
76
76
  * 幂等——重复调用安全(第二次起 no-op,released flag 守卫)。
@@ -86,18 +86,18 @@ export class RunRuntime {
86
86
  release(_mode: ReleaseMode): void {
87
87
  if (this.released) return;
88
88
  this.released = true;
89
- // 清理 run 级时间预算计时器——abort/terminate/replaceRuntime 后它不应再触发
90
- // (rebuildRuntime 会重排全新计时器;孤儿触发会把已终态的 run 误转 done)。
89
+ // 清理 run 级时间预算计时器——abort/terminate/replaceRuntime 后它不应再触发
90
+ // (rebuildRuntime 会重排全新计时器;孤儿触发会把已终态的 run 误转 done)。
91
91
  if (this.timeBudgetTimer) clearTimeout(this.timeBudgetTimer);
92
- // worker.terminate 异步但幂等——不 await(release 是同步签名,调用方
93
- // 不应被底层线程关闭阻塞;worker 收到 terminate 后自行清理)。
92
+ // worker.terminate 异步但幂等——不 await(release 是同步签名,调用方
93
+ // 不应被底层线程关闭阻塞;worker 收到 terminate 后自行清理)。
94
94
  void this.worker.terminate();
95
- // controller.abort 触发 listener(kill agent subprocess、中止在飞调用)。
96
- // 一次性语义——已 aborted 的 controller 重复 abort 无副作用。
95
+ // controller.abort 触发 listener(kill agent subprocess、中止在飞调用)。
96
+ // 一次性语义——已 aborted 的 controller 重复 abort 无副作用。
97
97
  this.controller.abort();
98
98
  }
99
99
 
100
- /** 是否已 release(测试 + 诊断用)。 */
100
+ /** 是否已 release(测试 + 诊断用)。 */
101
101
  get isReleased(): boolean {
102
102
  return this.released;
103
103
  }
@@ -75,7 +75,7 @@ export class Trace {
75
75
  /** stepIndex 倒排索引(查询加速 O(1);值与 nodes 元素引用共享)。 */
76
76
  private readonly byIndex = new Map<number, ExecutionTraceNode>();
77
77
 
78
- /**
78
+ /**
79
79
  * 从已有节点数组重建 Trace(用于 RunStore 反序列化重水合)。
80
80
  *
81
81
  * 防御性拷贝——传入数组不被持有,外部 mutation 不影响 Trace。
@@ -96,7 +96,7 @@ export class Trace {
96
96
  return trace;
97
97
  }
98
98
 
99
- /**
99
+ /**
100
100
  * Append a trace node(append-only,不改已有节点)。
101
101
  *
102
102
  * 入口裁剪:超长 result.content 先 mutate 入参节点的 result 字段,
@@ -109,7 +109,7 @@ export class Trace {
109
109
  this.byIndex.set(node.stepIndex, node);
110
110
  }
111
111
 
112
- /**
112
+ /**
113
113
  * Update a trace node by stepIndex (callId) with a partial patch.
114
114
  *
115
115
  * 只改 patch 中提供的字段(status/result/error/completedAt/sessionId)。
@@ -129,7 +129,7 @@ export class Trace {
129
129
  if (patch.sessionFile !== undefined) node.sessionFile = patch.sessionFile;
130
130
  }
131
131
 
132
- /**
132
+ /**
133
133
  * 查找指定 stepIndex 的节点(byIndex O(1),trace 中 stepIndex 应唯一)。
134
134
  *
135
135
  * 语义差异声明(旧线性扫 first-match → Map last-wins):仅在破坏
@@ -145,12 +145,12 @@ export class Trace {
145
145
  return this.byIndex.get(stepIndex);
146
146
  }
147
147
 
148
- /** 按节点引用删除(仅用于测试或 run 重建场景;正常运行不调用)。 */
148
+ /** 按节点引用删除(仅用于测试或 run 重建场景;正常运行不调用)。 */
149
149
  find(stepIndex: number): ExecutionTraceNode | undefined {
150
150
  return this.findByStepIndex(stepIndex);
151
151
  }
152
152
 
153
- /**
153
+ /**
154
154
  * 按 stepIndex 移除节点(崩溃重建清理在飞 call 用)。
155
155
  *
156
156
  * 正常运行不调用(append-only 不变式)。仅 error-recovery 的 discardInFlightCalls
@@ -167,7 +167,7 @@ export class Trace {
167
167
  this.byIndex.delete(stepIndex);
168
168
  }
169
169
 
170
- /**
170
+ /**
171
171
  * readonly 视图——返回内部 nodes 数组引用(仅类型级 readonly,运行时无
172
172
  * 防御)。消费方禁止结构化 mutate(push/splice/重排/覆盖元素):byIndex
173
173
  * 引入后外部结构化 mutate 会使 nodes 与倒排索引 desync。字段级变更走 update()。
@@ -176,7 +176,7 @@ export class Trace {
176
176
  return this.nodes;
177
177
  }
178
178
 
179
- /** 当前节点数。 */
179
+ /** 当前节点数。 */
180
180
  get length(): number {
181
181
  return this.nodes.length;
182
182
  }
@@ -62,7 +62,7 @@ export class WorkflowRun {
62
62
  runtime?: RunRuntime;
63
63
  meta: WorkflowRunMeta;
64
64
 
65
- /**
65
+ /**
66
66
  * 创建聚合根。初始状态 "running"(一次性生命周期:run 从创建起即在执行,
67
67
  * runtime 由紧随其后的 assignRuntime 注入)。也可传入 done 状态用于重水合
68
68
  * 已完成的 run(loadAll 后的只读聚合)。
@@ -82,15 +82,15 @@ export class WorkflowRun {
82
82
  this.spec = spec;
83
83
  this.state = state;
84
84
  this.meta = meta;
85
- // runtime 在构造时始终为 undefined——run 创建时无活 worker,loadAll 重水合
86
- // 时也不恢复 runtime(worker 必须由 lifecycle 重新 start)。
85
+ // runtime 在构造时始终为 undefined——run 创建时无活 worker,loadAll 重水合
86
+ // 时也不恢复 runtime(worker 必须由 lifecycle 重新 start)。
87
87
  this.runtime = undefined;
88
- // 构造期仅校验 I2(I1 跳过,见方法 doc);I1 由 assignRuntime 末尾
89
- // validateInvariants 恢复。
88
+ // 构造期仅校验 I2(I1 跳过,见方法 doc);I1 由 assignRuntime 末尾
89
+ // validateInvariants 恢复。
90
90
  this.validateInvariantI2();
91
91
  }
92
92
 
93
- /**
93
+ /**
94
94
  * 从持久化快照重水合聚合根。与构造函数同语义(构造期跳过 I1——持久化的
95
95
  * running 状态没有 worker,进程被杀后 worker 不可能还活着)。保留独立工厂
96
96
  * 标注重水合意图;调用方(D-4 kill-9 恢复)负责在 session_start 时把残留
@@ -102,15 +102,15 @@ export class WorkflowRun {
102
102
  return new WorkflowRun(runId, spec, state, meta);
103
103
  }
104
104
 
105
- // ── 不变式校验 ─────────────────────────────────────────────
105
+ // ── 不变式校验 ─────────────────────────────────────────────
106
106
 
107
- /**
107
+ /**
108
108
  * 校验不变式 I1 + I2。违反抛错(聚合根自我保护,fail-fast)。
109
109
  * 在每个 mutation 方法末尾调用(防御式编程 + 测试可断言)。
110
110
  */
111
111
  private validateInvariants(): void {
112
112
  this.validateInvariantI2();
113
- // I1: status==="running" ⟺ runtime!==undefined
113
+ // I1: status==="running" ⟺ runtime!==undefined
114
114
  if (this.state.status === "running" && this.runtime === undefined) {
115
115
  throw new Error(
116
116
  `WorkflowRun invariant I1 violated: status==="running" but runtime is undefined (runId=${this.runId})`,
@@ -123,7 +123,7 @@ export class WorkflowRun {
123
123
  }
124
124
  }
125
125
 
126
- /**
126
+ /**
127
127
  * 仅校验不变式 I2(done ⟹ reason)。构造期用——「创建即 running」与重水合的
128
128
  * running 快照都无 runtime(I1 构造期跳过),但 I2 必须保证(done 缺 reason 是真 bug)。
129
129
  */
@@ -135,9 +135,9 @@ export class WorkflowRun {
135
135
  }
136
136
  }
137
137
 
138
- // ── 状态机转换 ─────────────────────────────────────────────
138
+ // ── 状态机转换 ─────────────────────────────────────────────
139
139
 
140
- /**
140
+ /**
141
141
  * 状态机转换。合法转换:running→done。
142
142
  *
143
143
  * running 的进入不走 transition——构造即 running,replaceRuntime 保持 running。
@@ -151,7 +151,7 @@ export class WorkflowRun {
151
151
  * @throws 非法转换 / done 缺 reason / target==="running"
152
152
  */
153
153
  transition(target: RunStatus, reason?: DoneReason): void {
154
- // "running" 必须经 assignRuntime(需 runtime 参数,transition 无法提供)
154
+ // "running" 必须经 assignRuntime(需 runtime 参数,transition 无法提供)
155
155
  if (target === "running") {
156
156
  throw new Error(
157
157
  `WorkflowRun.transition: cannot transition to "running" directly — use assignRuntime() (runId=${this.runId})`,
@@ -164,15 +164,15 @@ export class WorkflowRun {
164
164
  );
165
165
  }
166
166
 
167
- // →done 需 reason(不变式 I2)
167
+ // →done 需 reason(不变式 I2)
168
168
  if (target === "done" && reason === undefined) {
169
169
  throw new Error(
170
170
  `WorkflowRun.transition: transition to "done" requires a reason (runId=${this.runId})`,
171
171
  );
172
172
  }
173
173
 
174
- // 副作用:先清理 runtime(releaseRuntime 守不变式 I1),再改 status
175
- // (canRunTransition 已排除 target==="running",此处 target 恒为 "done")
174
+ // 副作用:先清理 runtime(releaseRuntime 守不变式 I1),再改 status
175
+ // (canRunTransition 已排除 target==="running",此处 target 恒为 "done")
176
176
  this.releaseRuntime();
177
177
  this.state.status = target;
178
178
  this.state.reason = reason;
@@ -181,9 +181,9 @@ export class WorkflowRun {
181
181
  this.validateInvariants();
182
182
  }
183
183
 
184
- // ── Runtime 生命周期 ───────────────────────────────────────
184
+ // ── Runtime 生命周期 ───────────────────────────────────────
185
185
 
186
- /**
186
+ /**
187
187
  * 绑定 runtime(run 创建后注入执行资源)。
188
188
  *
189
189
  * 前置:status==="running" && runtime===undefined(runWorkflow 创建路径——
@@ -204,14 +204,14 @@ export class WorkflowRun {
204
204
  `WorkflowRun.assignRuntime: requires status==="running" (current: ${this.state.status}, runId=${this.runId})`,
205
205
  );
206
206
  }
207
- // 原子绑定:构造期 I1 处于跳过窗口(running 而 runtime undefined),设 runtime
208
- // 后末尾 validateInvariants 恢复 I1。调用方在 assignRuntime 后才对外注册
209
- // (lifecycle.runWorkflow 的 runs.set 后移),窗口外部不可见。
207
+ // 原子绑定:构造期 I1 处于跳过窗口(running 而 runtime undefined),设 runtime
208
+ // 后末尾 validateInvariants 恢复 I1。调用方在 assignRuntime 后才对外注册
209
+ // (lifecycle.runWorkflow 的 runs.set 后移),窗口外部不可见。
210
210
  this.runtime = rt;
211
211
  this.validateInvariants();
212
212
  }
213
213
 
214
- /**
214
+ /**
215
215
  * 解绑 runtime(done 时由 transition 调用,也可独立调用)。
216
216
  *
217
217
  * 前置:无(runtime===undefined 时 no-op,幂等)。
@@ -221,11 +221,11 @@ export class WorkflowRun {
221
221
  if (this.runtime === undefined) return;
222
222
  this.runtime.release("terminal");
223
223
  this.runtime = undefined;
224
- // 不改 status——调用方(transition)负责。独立调用时调用方需自行确保
225
- // status 一致(如 worker-error-retry 用 replaceRuntime 而非 release+assign)。
224
+ // 不改 status——调用方(transition)负责。独立调用时调用方需自行确保
225
+ // status 一致(如 worker-error-retry 用 replaceRuntime 而非 release+assign)。
226
226
  }
227
227
 
228
- /**
228
+ /**
229
229
  * 原地替换 runtime(G5-001:worker-error-retry)。
230
230
  *
231
231
  * 前置:status==="running"(G6-001:终态 run 拒绝重建)。
@@ -243,8 +243,8 @@ export class WorkflowRun {
243
243
  `WorkflowRun.replaceRuntime: requires status==="running" (current: ${this.state.status}, runId=${this.runId})`,
244
244
  );
245
245
  }
246
- // 原子替换:旧 runtime 释放(terminate+abort),新 runtime 绑定。
247
- // status 保持 "running",runtime 全程 !== undefined,I1 不违反。
246
+ // 原子替换:旧 runtime 释放(terminate+abort),新 runtime 绑定。
247
+ // status 保持 "running",runtime 全程 !== undefined,I1 不违反。
248
248
  if (this.runtime !== undefined) {
249
249
  this.runtime.release("terminal");
250
250
  }
@@ -60,10 +60,10 @@ export class WorkflowScript {
60
60
  readonly name: string;
61
61
  readonly source: WorkflowSource;
62
62
  readonly path: string;
63
- /** 原始文件内容(可编辑)。toExecutable 返回 strip 后的副本,不改本字段。 */
63
+ /** 原始文件内容(可编辑)。toExecutable 返回 strip 后的副本,不改本字段。 */
64
64
  sourceCode: string;
65
65
  readonly meta: WorkflowMeta;
66
- /** false 当 meta 提取失败(loader 不抛错,标记不可用但仍列出)。 */
66
+ /** false 当 meta 提取失败(loader 不抛错,标记不可用但仍列出)。 */
67
67
  available: boolean;
68
68
 
69
69
  constructor(opts: {
@@ -82,7 +82,7 @@ export class WorkflowScript {
82
82
  this.available = opts.available;
83
83
  }
84
84
 
85
- /**
85
+ /**
86
86
  * 静态检查脚本合法性。
87
87
  *
88
88
  * 委托 engine/script-lint.ts 的 lintScript——检查项含:
@@ -104,7 +104,7 @@ export class WorkflowScript {
104
104
  return result;
105
105
  }
106
106
 
107
- /**
107
+ /**
108
108
  * 返回可执行源。
109
109
  *
110
110
  * m2:不再 strip `export const meta`——meta 现为 @pi-meta 块注释(合法 JS,
@@ -0,0 +1,266 @@
1
+ // src/orchestration/run-snapshot.ts
2
+ //
3
+ // WorkflowRun 快照 codec(下沉收口 D4——设计件 subagent-core-sink-design.md U8)。
4
+ //
5
+ // 为什么需要它:WorkflowRun 的 JSONL 快照投影此前两宿主各写一份(core
6
+ // file-run-store.ts 的 toSnapshot/fromSnapshot 与 pi 壳 jsonl-run-store.ts 的
7
+ // serializeRun/deserializeRun)——字段集一致但语义细节分叉(live-strip、版本
8
+ // guard、budgetRef 剔除),修一处漏一处。本模块收敛为单源 codec:字段演进
9
+ // 单点(G2),两宿主(FileRunStore / pi JsonlRunStore)各自只保留 IO 策略
10
+ // (rewrite/append/去抖),投影与版本衔接语义全部经此模块。
11
+ //
12
+ // 版本衔接三裁决(D4,含审查 MF-2):
13
+ // ① 版本值沿用 pi 现有字符串 "wf-run-v2"——pi 存量逐字节可读;
14
+ // ② FileRunStore 存量行(无 v 字段)按「缺版本 = 当前版本」宽容读取,写入时
15
+ // 补 v,不做自动迁移——「缺 v 宽容」实现于 FileRunStore 层预处理(读出的行
16
+ // 先补缺省 v 再进 fromRunSnapshot),**不内聚进本 codec**:codec 层面缺 v
17
+ // 即拒绝,保 pi 侧「v1 存量静默跳过」既有语义不被宽容化误读;
18
+ // ③ guard 语义 = v 不匹配当前版本即拒(字符串版本无大小序,不引入比较逻辑);
19
+ // 「跳过 + warn」的可见性由宿主 store 层补(本 codec 只返回 undefined)。
20
+ //
21
+ // 序列化形态(键序即 JSON.stringify 输出序)逐键对齐 pi serializeRun 现网形态,
22
+ // 使 pi 切换本 codec 后存量往返逐字节一致(⛔5)。
23
+
24
+ import { AgentCall } from "./models/agent-call.ts";
25
+ import { Budget } from "./models/budget.ts";
26
+ import type { RunSpec } from "./models/run-spec.ts";
27
+ import type {
28
+ RunStatus,
29
+ DoneReason,
30
+ WorkerLogEntry,
31
+ ExecutionTraceNode,
32
+ AgentCallOpts,
33
+ AgentResult,
34
+ } from "./models/types.ts";
35
+ import { Trace } from "./models/trace.ts";
36
+ import { WorkflowRun } from "./models/workflow-run.ts";
37
+ import type { WorkflowRunMeta } from "./models/workflow-run.ts";
38
+
39
+ /**
40
+ * 快照格式版本(D4 裁决①:字符串相等比较,无大小序)。
41
+ *
42
+ * 版本历史(沿用 pi jsonl-run-store 口径):
43
+ * - wf-run-v1:status 三态(含 paused)、meta 含 pausedAt(pi 旧格式,读路径拒绝)。
44
+ * - wf-run-v2(当前):status 两态(running/done)、meta 无 pausedAt。
45
+ *
46
+ * 升级格式时 bump 此常量——旧版本快照经 fromRunSnapshot 返回 undefined,由
47
+ * 宿主 store 层决定跳过可见性(FileRunStore warn / pi 静默)。
48
+ */
49
+ export const SNAPSHOT_VERSION = "wf-run-v2" as const;
50
+
51
+ /** Budget 实例的可序列化投影(构造 opts 同形,重水合直接 new Budget(...))。 */
52
+ interface BudgetSnapshot {
53
+ maxTokens?: number;
54
+ maxCost?: number;
55
+ maxTimeMs?: number;
56
+ usedTokens: number;
57
+ usedCost: number;
58
+ totalCallCount: number;
59
+ }
60
+
61
+ /**
62
+ * AgentCall 实例的可序列化投影。traceNode 整体落盘(节点引用不可序列化,
63
+ * 落盘值拷贝;重水合后 D-10「引用共享」由 fromRunSnapshot 的 trace 回链尽力
64
+ * 恢复——Trace.fromArray 注释先例)。
65
+ */
66
+ interface CallSnapshot {
67
+ id: number;
68
+ opts: AgentCallOpts;
69
+ status: "pending" | "running" | "done";
70
+ attempts: number;
71
+ result?: AgentResult;
72
+ sessionId?: string;
73
+ sessionFile?: string;
74
+ traceNode: ExecutionTraceNode;
75
+ }
76
+
77
+ /**
78
+ * WorkflowRun 的持久化快照形态(JSONL 单行,全量而非增量)。
79
+ *
80
+ * 形态与 pi 壳 jsonl-run-store.ts 的 RunSnapshot 同构(v 字段含内)——两宿主
81
+ * 存量互读的前提,任何字段增删必须同步两处并评估存量行。
82
+ */
83
+ export interface RunSnapshot {
84
+ v: typeof SNAPSHOT_VERSION;
85
+ runId: string;
86
+ spec: RunSpec;
87
+ state: {
88
+ status: RunStatus;
89
+ reason?: DoneReason;
90
+ budget: BudgetSnapshot;
91
+ calls: CallSnapshot[];
92
+ trace: ExecutionTraceNode[];
93
+ errorLogs: WorkerLogEntry[];
94
+ error?: string;
95
+ scriptResult?: unknown;
96
+ };
97
+ meta: WorkflowRunMeta;
98
+ }
99
+
100
+ // ── 序列化 ──────────────────────────────────────────────────
101
+
102
+ /**
103
+ * WorkflowRun → 单行快照。
104
+ *
105
+ * - 补 v 字段(D4 裁决②:写入恒带当前版本)。
106
+ * - strip live(防御内聚):calls[].traceNode 与 trace 数组节点的 `live` 运行期
107
+ * 对象剥除——ExecutionRecord 含可变 turns[]/controller,不可序列化且跨进程
108
+ * 必死(重跑时由 dispatchAgentCall 重建);strip 产出新对象,不 mutate 内存
109
+ * 中的 run(save 后 run 可继续跑)。
110
+ * - spec.budgetRef 剔除:父 Budget 共享引用是进程内优化(嵌套 workflow 预算
111
+ * 共享),非持久化数据;Budget 实例若混入 spec 落盘将退化为普通对象投影
112
+ * (重水合后类型不符的脏字段)——重水合后 budget 从 state.budget 独立重建,
113
+ * 嵌套 run 的预算共享不跨进程存活。
114
+ * - runtime 不落盘(worker/controller/timer 不可序列化且跨进程必死——重水合
115
+ * 语义见 WorkflowRun.reconstruct 注释)。
116
+ */
117
+ export function toRunSnapshot(run: WorkflowRun): RunSnapshot {
118
+ const { budgetRef: _budgetRef, ...spec } = run.spec;
119
+ return {
120
+ v: SNAPSHOT_VERSION,
121
+ runId: run.runId,
122
+ spec,
123
+ state: {
124
+ status: run.state.status,
125
+ reason: run.state.reason,
126
+ budget: {
127
+ maxTokens: run.state.budget.maxTokens,
128
+ maxCost: run.state.budget.maxCost,
129
+ maxTimeMs: run.state.budget.maxTimeMs,
130
+ usedTokens: run.state.budget.usedTokens,
131
+ usedCost: run.state.budget.usedCost,
132
+ totalCallCount: run.state.budget.totalCallCount,
133
+ },
134
+ calls: Array.from(run.state.calls.values(), (c) => {
135
+ // strip live(同 trace 序列化,不持久化运行期对象)
136
+ const { live: _live, ...traceNodeRest } = c.traceNode;
137
+ return {
138
+ id: c.id,
139
+ opts: c.opts,
140
+ status: c.status,
141
+ attempts: c.attempts,
142
+ result: c.result,
143
+ sessionId: c.sessionId,
144
+ sessionFile: c.sessionFile,
145
+ traceNode: traceNodeRest,
146
+ };
147
+ }),
148
+ // trace 节点浅拷贝时 strip live 字段
149
+ trace: run.state.trace.toArray().map(({ live: _live, ...rest }) => rest),
150
+ errorLogs: run.state.errorLogs,
151
+ error: run.state.error,
152
+ scriptResult: run.state.scriptResult,
153
+ },
154
+ meta: run.meta,
155
+ };
156
+ }
157
+
158
+ // ── 重水合 ──────────────────────────────────────────────────
159
+
160
+ /** 「truthy 且 typeof object」——与原内联守卫 `!x || typeof x !== "object"` 拒绝集一致。 */
161
+ function isPresentObject(v: unknown): v is object {
162
+ return !!v && typeof v === "object";
163
+ }
164
+
165
+ /** 通过形状校验的快照视图:顶层必填字段(v/runId/spec/state/meta)均已验存在。 */
166
+ interface ValidatedSnapshot {
167
+ v: typeof SNAPSHOT_VERSION;
168
+ runId: string;
169
+ spec: RunSpec;
170
+ state: NonNullable<RunSnapshot["state"]>;
171
+ meta: WorkflowRunMeta;
172
+ }
173
+
174
+ /**
175
+ * 顶层形状校验(检查顺序与原内联守卫逐条一致)。
176
+ *
177
+ * 含 D4 裁决③ version guard(字符串相等,无大小序):v 不等于当前版本即拒
178
+ * (含缺版本——「缺 v 宽容」是 FileRunStore 层预处理职责,不内聚进本 codec;
179
+ * pi 侧对 v1 存量行的静默跳过语义依赖此拒绝行为)。
180
+ */
181
+ function isValidSnapshotShape(s: Partial<RunSnapshot>): s is ValidatedSnapshot {
182
+ if (s.v !== SNAPSHOT_VERSION) return false;
183
+ if (typeof s.runId !== "string" || !s.runId) return false;
184
+ if (!isPresentObject(s.spec)) return false;
185
+ const st = s.state;
186
+ if (!isPresentObject(st)) return false;
187
+ if (st.status !== "running" && st.status !== "done") return false;
188
+ if (!isPresentObject(st.budget)) return false;
189
+ if (!Array.isArray(st.calls) || !Array.isArray(st.trace)) return false;
190
+ if (!isPresentObject(s.meta)) return false;
191
+ return true;
192
+ }
193
+
194
+ /** call.traceNode → Trace 节点回链(D-10 尽力恢复,匹配不到退化为独立浅拷贝)。 */
195
+ function linkTraceNode(c: CallSnapshot, trace: Trace): ExecutionTraceNode | undefined {
196
+ return (
197
+ trace.toArray().find((n) => n.stepIndex === c.traceNode?.stepIndex) ??
198
+ (c.traceNode ? { ...c.traceNode } : undefined)
199
+ );
200
+ }
201
+
202
+ /**
203
+ * 单条 CallSnapshot → AgentCall。残缺条目(非对象 / id 非数 / traceNode 缺失)
204
+ * 返回 undefined,由调用方跳过——不炸整个 run。
205
+ */
206
+ function rehydrateCall(c: CallSnapshot, trace: Trace): AgentCall | undefined {
207
+ if (c === null || typeof c !== "object" || typeof c.id !== "number") return undefined;
208
+ const linked = linkTraceNode(c, trace);
209
+ if (!linked) return undefined;
210
+ const call = new AgentCall(c.id, c.opts, linked);
211
+ call.status = c.status;
212
+ call.attempts = c.attempts;
213
+ // Restore result directly — bypasses markRunning/markDone state-machine guards
214
+ // because we're reconstructing a known-good persisted state, not transitioning.
215
+ if (c.result !== undefined) call.result = c.result;
216
+ if (c.sessionId !== undefined) call.sessionId = c.sessionId;
217
+ if (c.sessionFile !== undefined) call.sessionFile = c.sessionFile;
218
+ return call;
219
+ }
220
+
221
+ function rehydrateCalls(snapshots: CallSnapshot[], trace: Trace): Map<number, AgentCall> {
222
+ const calls = new Map<number, AgentCall>();
223
+ for (const c of snapshots) {
224
+ const call = rehydrateCall(c, trace);
225
+ if (call) calls.set(c.id, call);
226
+ }
227
+ return calls;
228
+ }
229
+
230
+ /**
231
+ * 快照 → WorkflowRun 重水合。
232
+ *
233
+ * 版本 guard(D4 裁决③):v 不等于当前版本即返回 undefined(含缺版本——
234
+ * 「缺 v 宽容」是 FileRunStore 层预处理职责,不内聚进本函数;pi 侧对 v1 存量
235
+ * 行的静默跳过语义依赖此拒绝行为)。
236
+ *
237
+ * 形状校验失败同样返回 undefined(调用方按损坏行处理,本函数不抛——唯一例外:
238
+ * done 快照缺 reason 触发 WorkflowRun I2 不变式抛错,属真 bug 不可吞)。
239
+ */
240
+ export function fromRunSnapshot(snap: unknown): WorkflowRun | undefined {
241
+ if (snap === null || typeof snap !== "object") return undefined;
242
+ const s = snap as Partial<RunSnapshot>;
243
+ if (!isValidSnapshotShape(s)) return undefined;
244
+
245
+ // Trace 先重建:calls 的 traceNode 回链到 Trace 副本(D-10 尽力恢复——
246
+ // fromArray 拷贝节点,按 stepIndex 匹配使 call.traceNode 与 trace.nodes
247
+ // 共享同一副本引用;匹配不到(快照数据漂移)退化为独立浅拷贝,仅保构造不炸。
248
+ const trace = Trace.fromArray(s.state.trace);
249
+ const calls = rehydrateCalls(s.state.calls, trace);
250
+
251
+ return WorkflowRun.reconstruct(
252
+ s.runId,
253
+ s.spec,
254
+ {
255
+ status: s.state.status,
256
+ reason: s.state.reason,
257
+ budget: new Budget(s.state.budget),
258
+ calls,
259
+ trace,
260
+ errorLogs: Array.isArray(s.state.errorLogs) ? s.state.errorLogs : [],
261
+ error: s.state.error,
262
+ scriptResult: s.state.scriptResult,
263
+ },
264
+ s.meta,
265
+ );
266
+ }