@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
package/dist/index.d.ts CHANGED
@@ -1,20 +1,9 @@
1
1
  import { ChildProcess } from 'node:child_process';
2
- import { E as EngineCapabilities, P as ProbeReport, A as AgentTaskSpec, a as AgentEvent, b as EngineHandle, c as AgentOutcome, I as InteractAction, d as InteractResult, S as SessionView, e as AgentUsage, f as AgentCallOpts, g as AgentResult, h as ExecutionTraceNode, T as TracePatch, R as RunStatus, D as DoneReason, W as WorkerLogEntry } from './types-BxyAidGf.js';
3
- export { i as EngineHandleData, j as PersonaSpec, k as ReplayedTurn } from './types-BxyAidGf.js';
2
+ import { E as EngineCapabilities, P as ProbeReport, A as AgentTaskSpec, a as AgentEvent, M as ModelInfo, b as EngineHandleData, c as EngineHandle, d as AgentOutcome, I as InteractAction, e as InteractResult, S as SessionView, f as AgentUsage, g as AgentCallOpts, h as AgentResult, i as ExecutionTraceNode, T as TracePatch, R as RunStatus, D as DoneReason, W as WorkerLogEntry, j as SubagentsGlobalConfig, k as ModelRegistryLike, l as AgentConfig, m as ResolvedModel, n as ExecutionRecord, o as RecordSnapshot, p as SubagentRecord, C as ClosedReason, q as ExecuteOptions, r as ExecutionHandle, s as ExecutionStatus, t as ExecutionMode, u as AgentEventLogEntry, v as DisplayItem, w as CancelResponse, x as CloseResponse, F as ForkFromResponse, L as ListResponse, y as MessageResponse, B as BgResponse, z as ExternalState, G as SubagentListItem } from './types-DpUO16pj.js';
3
+ export { H as DirtyWorktreeError, J as ForkDepthExceededError, K as PersonaSpec, N as ReplayedTurn, O as ResurrectDeniedError } from './types-DpUO16pj.js';
4
4
  import { Worker } from 'node:worker_threads';
5
-
6
- /**
7
- * 模型信息(registry 返回元素 / ctx.model 鸭子类型兼容)。
8
- * ctx.model(SDK Model<Api>)是此类型的超集,运行时直接当 ModelInfo 用。
9
- */
10
- interface ModelInfo {
11
- id: string;
12
- name: string;
13
- provider: string;
14
- reasoning: boolean;
15
- thinkingLevelMap?: Record<string, unknown>;
16
- contextWindow?: number;
17
- }
5
+ import { Readable } from 'node:stream';
6
+ export { ZCODE_FALLBACK_DEFAULT_MODEL } from './execution/engine/engines/zcode/constants.js';
18
7
 
19
8
  /** 日志级别。对齐 @zhushanwen/pi-extension-logger 的 LogLevel(三值,无 info)。 */
20
9
  type LogLevel = "debug" | "warn" | "error";
@@ -136,6 +125,8 @@ interface NotifyDomainPorts {
136
125
  }
137
126
  declare function configureNotifyDomain(ports: NotifyDomainPorts): void;
138
127
 
128
+ type ExtensionMode = "tui" | "rpc" | "json" | "print";
129
+
139
130
  /**
140
131
  * subagent text_delta streaming sink。
141
132
  *
@@ -203,7 +194,13 @@ interface RunContext {
203
194
  signal?: AbortSignal;
204
195
  /** 事件流出口(host 消费后统一落 journal,D6 第②级)。 */
205
196
  onEvent?: (event: AgentEvent) => void;
206
- /** model 解析第三层兼底(现有 D-008 语义不变)。 */
197
+ /**
198
+ * model 解析第三层兜底(现有 D-008 语义不变)——**pi 链路专属兜底**:经
199
+ * taskSpecToExecuteOptions → resolveModel 第三层消费(PiEngine 直通)。自带
200
+ * provider 体系与缺省模型的引擎(如 zcode:requested > 引擎缺省常量链)按自身
201
+ * 默认链解析,不消费本字段(zcode 侧在「ctx 有模型但被忽略」时出声留痕,
202
+ * zcode-engine.warnIgnoredCtxModel)。
203
+ */
207
204
  ctxModel?: ModelInfo;
208
205
  /**
209
206
  * text_delta streaming 通道(宿主侧 UI widget)。与 onEvent 平行的 text_delta 出口:
@@ -238,12 +235,27 @@ interface RunContext {
238
235
  * 未来流式引擎需在事件出口前调用)。
239
236
  */
240
237
  onPoolResolved?: (poolKey: string) => void;
238
+ /**
239
+ * [R4 §3.4 不变量 3] 运行中句柄回填通道:引擎在「session/create 应答到达后」
240
+ * 立即回调(早于 run resolve——stream 引擎的 run 生命周期远长于会话建立)。
241
+ * 与 onPoolResolved 分立两个时点:poolKey 在 prepare 期(onPoolResolved,连接
242
+ * 建立前即可知),sessionRef 在 create 应答后(本回调)。编排层收到后立即回填
243
+ * record.engineHandle 并落 entry——运行中的 GUI 经 entry 重建 record 即拿到
244
+ * ①②级读取钥匙,不再等 run resolve 后的终态回填。可选回调:不支持运行中回填
245
+ * 的引擎(spawn 单轮、终态即回填)不调用,宿主语义不受影响。
246
+ */
247
+ onHandleReady?: (partial: Pick<EngineHandleData, "sessionRef" | "poolKey">) => void;
241
248
  /**
242
249
  * [U0 D10] 引擎 spawn 的子进程句柄注册钩子(宿主终止链记账)。引擎在 spawn 成功后
243
250
  * 同步回调(与 pi runSpawn 的 spawnedChildren.set 同构时机);宿主据此把 child 注册进
244
251
  * session-runner 的 spawnedChildren Map(cancel SIGTERM / dispose 收割兜底 / killAll
245
252
  * 全量清理对非 pi 引擎 record 生效)。close/error 后由宿主按句守卫移除。可选:引擎
246
253
  * 内部不 spawn 进程(如未来常驻 driver host 实现)时不调用,宿主记账自然为空。
254
+ *
255
+ * 边界声明(R1 D6):本钩子只用于 per-record 一次性 spawn(一任务一进程模态)。
256
+ * 引擎持有的常驻进程(跨任务共享,如 app-server 常驻连接)不经本钩子注册、不进
257
+ * spawnedChildren Map——其生命周期完全归引擎 dispose 管理(防 per-record 重复
258
+ * SIGTERM / 单任务 abort 误杀共享进程)。
247
259
  */
248
260
  onChildSpawned?: (child: ChildProcess) => void;
249
261
  }
@@ -296,6 +308,16 @@ interface EnginePort {
296
308
  id: string;
297
309
  name?: string;
298
310
  }> | null;
311
+ /**
312
+ * [R1 D6] 可选停机面:释放引擎持有的常驻资源(如 app-server 常驻进程 / 长连接)。
313
+ * 幂等契约(§3.4 不变量 4):重复调用无副作用;dispose 后首个 run 自动重建(与
314
+ * 「进程死后重建」同一代码路径)。可选成员保持向后兼容——无常驻资源的引擎(pi
315
+ * 现状 spawn 单轮)不必实现。等待策略(D6①「触发不等待」):宿主收割入口
316
+ * (registry disposeEngines → killAllSpawnedChildren)只同步调用拿 Promise 不
317
+ * await,引擎实现须自行保证同步面(立即 fire close 帧 + 同步 SIGTERM)在返回
318
+ * Promise 前完成;grace→SIGKILL 升级序列属异步面(promise 段)。
319
+ */
320
+ dispose?(): Promise<void>;
299
321
  }
300
322
 
301
323
  /** 三层路由的输入(各层值由调用方装配;undefined = 该层不指定)。 */
@@ -358,6 +380,292 @@ interface EngineRouteResult {
358
380
  */
359
381
  declare function routeEngine(opts: EngineRouteOptions): Promise<EngineRouteResult>;
360
382
 
383
+ /** 资源种类:agent 或 workflow */
384
+ type ResourceKind$1 = "agents" | "workflows";
385
+ /** 发现到的单个资源文件(原始数据,由调用方解析 frontmatter/meta) */
386
+ interface DiscoveredResource {
387
+ /** 绝对路径 */
388
+ path: string;
389
+ /** 来源层级 */
390
+ source: ResourceSource;
391
+ /** 是否可用(manifest 校验失败的包整体标 false) */
392
+ available: boolean;
393
+ }
394
+ /** 资源来源层级 */
395
+ type ResourceSource = "user-pi" | "user-agents" | "npm" | "npm-dev" | "user-extension-paths" | "project-pi" | "project-pi-tmp" | "project-host" | "project-agents";
396
+ /** 扫描配置 */
397
+ interface ScanConfig {
398
+ /** 资源种类 */
399
+ kind: ResourceKind$1;
400
+ /** 项目根目录(findWorkspaceRoot 推导结果) */
401
+ workspaceRoot: string;
402
+ /** 宿主注入的发现根(DiscoveryRoot.dir 已含 kind 末级目录与安装布局,
403
+ * source 为宿主语义标签——pi 壳 = user-pi/npm/npm-dev 三根,zsw 壳可另注入
404
+ * project-host 等)。buildScanTargets 按标签填充对应槽位,宿主未提供某标签
405
+ * 根时该槽位条目整体缺席。
406
+ * 同标签多根语义(W2④):同标签多条目依注入序全部保留、同序位依次扫描——
407
+ * 宿主(zsw)把「目录 symlink 展开目标 + 本体根」按注入序注入同标签,core
408
+ * 合并 last-writer-wins 下靠后者胜,本体根必须注入在展开目标之后(本体胜,
409
+ * 红线 2)。user-agents/project-agents 硬编码槽与同标签注入合并时硬编码根
410
+ * 自动后置(同为「本体在后」语义)。 */
411
+ hostRoots: DiscoveryRoot[];
412
+ /** 是否包含 tmp 源(仅 workflow 用 .pi/workflows/.tmp/) */
413
+ includeTmp?: boolean;
414
+ }
415
+ /** 便捷封装:只取 content(不存在 → null)。 */
416
+ declare function getCachedFileContent(filePath: string): string | null;
417
+ /**
418
+ * mtime 级解析结果缓存:mtime 未变返回缓存 parsed,变则经 getCachedFile 取 content
419
+ * 重新 parse 并缓存。文件不存在/不可读 → null(并驱逐条目)。缓存按 parse 函数隔离
420
+ * ——同一 path 的不同 parse 互不污染。
421
+ */
422
+ declare function getCachedParsed<T>(filePath: string, parse: (content: string) => T): T | null;
423
+ declare function findWorkspaceRoot(cwd?: string): string;
424
+ /**
425
+ * 发现所有资源文件(agent .md 或 workflow .js/.mjs)。
426
+ *
427
+ * 按优先级低→高扫描所有源,同名资源靠后覆盖靠前(last-writer-wins)。
428
+ * npm/dev 包内:有 manifest 只走 manifest(路径不存在则失败),无 manifest 扫约定目录。
429
+ *
430
+ * realpath 归一去重(W2①,仅 async 链):多个不同名 symlink 指向同一物理文件时
431
+ * (多链同文件),stem 去重防不住——清单按物理文件归一只留一条(首遇者,位置
432
+ * 固定语义与 stem 合并一致);同 stem 不同物理文件的遮蔽语义不受影响(仍按
433
+ * last-writer-wins 覆盖)。realpath 解析失败(扫描后竞态删除/ELOOP 深链)回退
434
+ * 原 path,属预期失败不抛。
435
+ *
436
+ * Throws on unrecoverable scan errors——未捕获异常向上抛(Promise.all 首个 reject
437
+ * 即整体拒绝,与串行版 discoverResourcesSync 的传播语义一致,见实现内 [perf] 注释)。
438
+ * 预期失败不抛:目录不存在/不可读返回空列表,manifest 声明路径缺失以 available=false 返回。
439
+ *
440
+ * @returns 去重后的资源列表(按优先级合并,高优先级覆盖低优先级同名)
441
+ */
442
+ declare function discoverResources(config: ScanConfig): Promise<DiscoveredResource[]>;
443
+
444
+ /**
445
+ * ResourceMeta — 资源元数据统一类型族(v5 §4.1 / DM1)
446
+ *
447
+ * workflow 与 agent 两类资源共用同一数据格式(YAML),仅 kind 判别 + 专属字段不同。
448
+ * skills 显式 out-of-scope(归 pi core)。
449
+ *
450
+ * 设计:
451
+ * - 整对象透传——config-loader.toCachedMeta / registry-impl.toScript 改为 meta: parsedMeta
452
+ * 不再 {name,description,phases} 解构重建(消灭 3 重映射丢字段 bug)。
453
+ * - workflow 的 parameters(JSON Schema)由 args-validator(m3)按 schema 校验 args;
454
+ * usage(markdown)覆盖 schema 表达不了的语义约束 + 真实合法示例命令。
455
+ * - agent 的 tools/model 供 AgentRegistry 执行侧 spawn 子进程用,不进 system prompt 注入段。
456
+ *
457
+ * 层归属:shared(L1 统一资源模型)。
458
+ */
459
+ /** 资源种类。skills 归 pi core,本 extension 仅 workflow + agent。 */
460
+ type ResourceKind = "workflow" | "agent";
461
+ /** 两类资源共有的路由字段。 */
462
+ interface ResourceMetaBase {
463
+ kind: ResourceKind;
464
+ /** 路由用一句话(受 SSOT lint W1/W2 约束:≤200 字符、不含参数引用语法)。 */
465
+ name: string;
466
+ description: string;
467
+ /** "Use when ..." 正向路由提示(PromptBudget 最后保)。 */
468
+ when?: string;
469
+ /** "Not for ..." 负向路由提示。 */
470
+ notFor?: string;
471
+ }
472
+ /** workflow 专属:phases + 参数契约 + 语义说明。 */
473
+ interface WorkflowMeta extends ResourceMetaBase {
474
+ kind: "workflow";
475
+ phases: (string | {
476
+ title: string;
477
+ detail?: string;
478
+ })[];
479
+ /** 参数契约(JSON Schema draft-07)。未声明则 $ARGS 透传不校验(向后兼容)。 */
480
+ parameters?: Record<string, unknown>;
481
+ /** markdown,覆盖 schema 表达不了的语义约束 + 真实合法示例命令。 */
482
+ usage?: string;
483
+ }
484
+ /** agent 路由样本(结构化,非嵌入 description 字符串)。 */
485
+ interface RoutingExample {
486
+ match: string;
487
+ action: string;
488
+ /** true=应触发,false=不应触发(正反各一)。 */
489
+ positive: boolean;
490
+ }
491
+ /** agent 专属:路由样本 + 执行配置。 */
492
+ interface AgentMeta extends ResourceMetaBase {
493
+ kind: "agent";
494
+ examples?: RoutingExample[];
495
+ /** 供 AgentRegistry 执行侧 spawn 时注入,不进 system prompt 注入段。 */
496
+ tools?: string[];
497
+ model?: string;
498
+ /**
499
+ * 执行引擎 id(D9 per-agent 主通道:调用参数 engine > 本字段 > 全局默认)。
500
+ * 与 model 字段同风格——路由字段(不进 system prompt),执行侧(P4 路由层)消费。
501
+ */
502
+ engine?: string;
503
+ /**
504
+ * 执行预算:turn 上限(sink 设计 D3 新增可选执行字段之一)。
505
+ * 消费优先级:显式参数 > 本字段 > 缺省——pi 工具参数 maxTurns 语义不变(本字段
506
+ * 对 pi 是清单元数据),zsw 决策链 timeoutMs > profile.maxTurns > 缺省。
507
+ */
508
+ maxTurns?: number;
509
+ /** tool denylist(D3 可选执行字段):spawn 侧剔除,与 tools allowlist 正交。 */
510
+ disallowedTools?: string[];
511
+ /**
512
+ * agent 声明依赖的 skill 名清单(D3 可选执行字段)。注意与本文件头注
513
+ * 「skills 归 pi core」的 skills(资源种类)不同物:此处是 agent frontmatter 的
514
+ * 执行配置字段,不是 ResourceKind。
515
+ */
516
+ skills?: string[];
517
+ }
518
+ /** 判别联合(kind 判别)。 */
519
+ type ResourceMeta = WorkflowMeta | AgentMeta;
520
+
521
+ /**
522
+ * Meta Parser — 资源元数据统一解析器(v5 §4.2 / IF1 + IF2)
523
+ *
524
+ * 两个变体,职责分离(R8-F1:discovery 的 fail-safe null 与 generate 的 linePos 需求互斥,
525
+ * 单一函数无法兼顾):
526
+ * - parseResourceMeta(IF1,discovery 用):fail-safe,任何失败返 null,不抛。
527
+ * 供 config-loader / registry / 两 injector / agent-registry 调用(4 parser 收敛为 1)。
528
+ * - parseResourceMetaDetailed(IF2,generate 闭环用):失败返 {ok:false, error, linePos},
529
+ * linePos 取自 eemeli/yaml YAMLParseError.linePos[0]([P-yaml] 探针实测:
530
+ * e.linePos 是 [start,end] 数组,取 [0] 作起止点),供 actionGenerate 报行列给 LLM 自纠正。
531
+ *
532
+ * 格式(v5 §7 / DM4):
533
+ * - workflow (.js):块注释 `/* @pi-meta <YAML> * /`(单星,非 JSDoc),WORKFLOW_META_RE 提取。
534
+ * - agent (.md):frontmatter `--- <YAML> ---`,FRONTMATTER_RE 提取。
535
+ * - 无 legacy fallback(D1):const meta 旧格式 → extractBlock 取不到块 → null。
536
+ *
537
+ * [P-yaml] 探针已验证:eemeli/yaml 2.9.0 的 YAMLParseError.linePos = [{line,col},{line,col}]。
538
+ *
539
+ * exec-review 修复(major-1 + minor-2..8):
540
+ * - 正则闭合符(星斜杠)必须独占行首,防止 YAML 正文里中途出现的星斜杠(如 usage 块标量
541
+ * 或 patternProperties 正则)截断块致 parameters 等字段静默丢失(§2.3 failure-A 同形态)。
542
+ * - typecheckMeta 严格化:kind 专属字段不可串类(workflow 不许 examples、agent 不许 phases),
543
+ * description 必填,phase detail 非字符串/parameters 非对象均 reject(消除「静默丢弃非法字段」)。
544
+ * - 区分「未找到块」(undefined) 与「块为空」(""),IF2 给可操作错误。
545
+ * - FRONTMATTER_RE 兼容 CRLF。
546
+ *
547
+ * 层归属:shared(L2 统一解析器)。
548
+ */
549
+
550
+ /**
551
+ * 统一 meta 解析入口(discovery 用)。仅认新格式,无 legacy fallback。
552
+ * 任何失败(缺块 / YAML 语法错 / 类型校验失败)→ return null(不抛)。
553
+ * discovery fail-safe:单文件解析失败仅让该资源 available=false,不阻塞其他资源。
554
+ */
555
+ declare function parseResourceMeta(content: string, kind: ResourceKind): ResourceMeta | null;
556
+
557
+ /** 从 agent .md frontmatter 提取的最小 agent 信息(随 D-3 从 pi-sw 下沉) */
558
+ interface AgentEntry {
559
+ name: string;
560
+ description: string;
561
+ when?: string;
562
+ examples?: Array<{
563
+ match: string;
564
+ action: string;
565
+ positive: boolean;
566
+ }>;
567
+ /** agentRef:agent .md 文件的绝对路径(注入段 <location>,模型直接引用) */
568
+ path: string;
569
+ }
570
+ /** 解析后的 workflow 条目(name + 摘要后的 description + 脚本路径) */
571
+ interface WorkflowEntry {
572
+ name: string;
573
+ description: string;
574
+ /** workflowRef:脚本 .js 文件的绝对路径(注入段 <location>,模型直接引用) */
575
+ path: string;
576
+ }
577
+ /** reasoning 对象形态(zsw 投影的档位结构);渲染面仅按 truthy 消费,字段值不进输出 */
578
+ interface ModelReasoningInfo {
579
+ variants?: unknown[];
580
+ defaultVariant?: unknown;
581
+ }
582
+ /**
583
+ * 注入段的最小模型投影——pi 与 zsw 两侧数据形态的并集(D-3 + 本仓 provider 补充):
584
+ * id/name 为条目最小必填(缺则无渲染意义),其余字段 optional(红线 5)。
585
+ * - pi 投影:provider/reasoning:boolean/input[]/contextWindow 全给;
586
+ * - zsw 投影:reasoning:{variants} 档位对象、input 缺席;
587
+ * - label 与 reasoning.variants 进类型并集但渲染面暂不消费(宿主按需再扩)。
588
+ */
589
+ interface ModelEntry {
590
+ id: string;
591
+ name: string;
592
+ provider?: string;
593
+ label?: string;
594
+ contextWindow?: number;
595
+ reasoning?: boolean | ModelReasoningInfo;
596
+ input?: string[];
597
+ }
598
+ /** 条目段(subagents/workflows)渲染选项:guide 宿主注入 + 可选条目预算 */
599
+ interface ListFormatOptions {
600
+ /** 注入段引导文案——必填,core 不内嵌平台文案(D-3 guide 参数化) */
601
+ guide: string;
602
+ /**
603
+ * 条目预算上限:超限按排序键码点序截尾(保留靠前条目)。缺省不限制
604
+ * (pi 现行为全量)。models 段无此参数(永不截,设计钉死)。
605
+ */
606
+ maxEntries?: number;
607
+ /** 截断发生时在段末追加的兜底指引行(宿主注入,如查看全量的命令);未提供则不追加 */
608
+ truncationNotice?: string;
609
+ }
610
+ /** models 段渲染选项:仅 guide——无条目预算(完整渲染永不截,D-3 钉死) */
611
+ interface ModelListFormatOptions {
612
+ guide: string;
613
+ }
614
+ /**
615
+ * 码点序排序(显式契约,禁 localeCompare——宿主 locale 差异会破坏跨环境
616
+ * 字节一致;注入段进每 turn system prompt,顺序必须与枚举序解耦)。
617
+ * 非变异:返回排序副本,入参数组不动(core 纯函数定位;截尾语义依赖此序)。
618
+ */
619
+ declare function sortByCodepoint<T>(items: readonly T[], key: (item: T) => string): T[];
620
+ /**
621
+ * 将 workflow description 截断为 prompt 友好的摘要。
622
+ * 优先在 limit 内的最后一个句末标点处断句;无合适断点则硬截断 + 省略号。
623
+ */
624
+ declare function summarizeDescription(desc: string, maxLen?: number): string;
625
+ /**
626
+ * 将 agent 列表格式化为 XML 注入段。
627
+ *
628
+ * 内部先按 name 码点序排序再渲染截断(超预算截尾语义依赖码点序;pi 调用链
629
+ * 数据已排时重排幂等,不破坏与 pi 现输出的逐字节等价)。空列表返回空串
630
+ * (不注入);预算截断发生时在段末追加宿主注入的兜底指引行(缺省不追加)。
631
+ */
632
+ declare function formatAgentList(agents: AgentEntry[], opts: ListFormatOptions): string;
633
+ /**
634
+ * 将 workflow 列表格式化为 XML 注入段。
635
+ *
636
+ * 与 formatAgentList 同约:先按 name 码点序排序再渲染截尾;空列表返回空串
637
+ * (不注入);截断时追加宿主注入的兜底指引行(缺省不追加)。
638
+ */
639
+ declare function formatWorkflowList(workflows: WorkflowEntry[], opts: ListFormatOptions): string;
640
+ /**
641
+ * 将模型列表格式化为 XML 注入段。
642
+ *
643
+ * 输入按归一 (provider, id) 码点序排序(见 compareModelEntries)——同一数据集
644
+ * 输出字节稳定(KV-cache 契约)。空列表返回空串(不注入)。
645
+ * models 段无条目预算(完整渲染永不截,D-3 钉死)。
646
+ * 红线 5 守卫:contextWindow 缺席不渲染该元素(不输出 "undefined" 垃圾);
647
+ * provider 缺席/空串时 id 裸渲染。
648
+ */
649
+ declare function formatModelList(models: ModelEntry[], opts: ModelListFormatOptions): string;
650
+
651
+ /**
652
+ * 转义 XML 特殊字符(注入段进每 turn system prompt,内容含 < > & 等会破坏
653
+ * XML 结构——全部字段过一遍转义防注入段破碎)。
654
+ */
655
+ declare function escapeXml(str: string): string;
656
+ /**
657
+ * XML 注入段渲染骨架:`"\n\n<tag>"` 前导(衔接宿主 prompt 末尾)+ 引导语 +
658
+ * 条目行 + 闭合标签,以 "\n" join。空条目返回空串(不注入)。
659
+ *
660
+ * 三个 injector 共用此骨架保证段落结构逐字节同构;KV-cache 契约(顺序稳定 =
661
+ * 注入段字节稳定)由调用方排序保证,本函数不重排。
662
+ */
663
+ declare function renderXmlSection(section: {
664
+ tag: string;
665
+ guide: string;
666
+ items: string[];
667
+ }): string;
668
+
361
669
  /**
362
670
  * Workflow Extension — Worker Handle
363
671
  *
@@ -383,12 +691,12 @@ type WorkerExitHandler = (code: number) => void;
383
691
  declare class WorkerHandle {
384
692
  private readonly worker;
385
693
  /**
386
- * 竞态守卫。true = 此 handle 仍是当前活动 handle;false = 已 terminate,
387
- * 后续事件(已终止 worker 延迟触发的 message/error/exit)必须忽略。
388
- *
389
- * 终止后置 false 并永不回升(幂等语义)。新 handle 由调用方(WorkerHost)
390
- * 重新创建,已终止 handle 留在内存里直到 GC,但其回调全部 no-op。
391
- */
694
+ * 竞态守卫。true = 此 handle 仍是当前活动 handle;false = 已 terminate,
695
+ * 后续事件(已终止 worker 延迟触发的 message/error/exit)必须忽略。
696
+ *
697
+ * 终止后置 false 并永不回升(幂等语义)。新 handle 由调用方(WorkerHost)
698
+ * 重新创建,已终止 handle 留在内存里直到 GC,但其回调全部 no-op。
699
+ */
392
700
  private current;
393
701
  constructor(worker: Worker);
394
702
  /** 此 handle 是否仍是当前活动 handle(terminate 后 false,G-025)。 */
@@ -396,29 +704,29 @@ declare class WorkerHandle {
396
704
  /** 底层 Worker(WorkerHost/RunRuntime 偶尔需要直接访问,如 ref/href)。 */
397
705
  get raw(): Worker;
398
706
  /**
399
- * 向 worker 发送消息。terminate 后 no-op(已终止 handle 的 postMessage 无意义)。
400
- */
707
+ * 向 worker 发送消息。terminate 后 no-op(已终止 handle 的 postMessage 无意义)。
708
+ */
401
709
  postMessage(msg: unknown): void;
402
710
  /**
403
- * 终止 worker 线程。幂等——重复调用安全,第二次起 no-op。
404
- * 置 isCurrent=false 后再 await worker.terminate,确保并发 exit 事件
405
- * 在 terminate resolve 之前到达时也被守卫拦下。
406
- */
711
+ * 终止 worker 线程。幂等——重复调用安全,第二次起 no-op。
712
+ * 置 isCurrent=false 后再 await worker.terminate,确保并发 exit 事件
713
+ * 在 terminate resolve 之前到达时也被守卫拦下。
714
+ */
407
715
  terminate(): Promise<void>;
408
716
  /**
409
- * 绑定 message 回调。仅当 isCurrent 时触发——已终止 handle 的事件被吞掉。
410
- * 返回 this 便于链式 onMessage(...).onError(...).onExit(...)。
411
- */
717
+ * 绑定 message 回调。仅当 isCurrent 时触发——已终止 handle 的事件被吞掉。
718
+ * 返回 this 便于链式 onMessage(...).onError(...).onExit(...)。
719
+ */
412
720
  onMessage(handler: WorkerMessageHandler): this;
413
721
  /**
414
- * 绑定 error 回调。仅当 isCurrent 时触发。
415
- */
722
+ * 绑定 error 回调。仅当 isCurrent 时触发。
723
+ */
416
724
  onError(handler: WorkerErrorHandler): this;
417
725
  /**
418
- * 绑定 exit 回调。仅当 isCurrent 时触发——这是 G-025 的关键守卫:
419
- * terminate(old) → startWorker(new) → old exit 触发时,old handle.current
420
- * 已为 false,回调 no-op,不会误删 new worker。
421
- */
726
+ * 绑定 exit 回调。仅当 isCurrent 时触发——这是 G-025 的关键守卫:
727
+ * terminate(old) → startWorker(new) → old exit 触发时,old handle.current
728
+ * 已为 false,回调 no-op,不会误删 new worker。
729
+ */
422
730
  onExit(handler: WorkerExitHandler): this;
423
731
  }
424
732
 
@@ -462,31 +770,31 @@ declare class Budget {
462
770
  totalCallCount?: number;
463
771
  });
464
772
  /**
465
- * 累加一次 agent 调用的 usage(加权口径)。
466
- *
467
- * 四项 token 按各自权重(INPUT/CACHE_READ/CACHE_WRITE/OUTPUT_WEIGHT)折算后求和,
468
- * 而非原始 token 数直接相加。retry 间的真实消耗如实记录,避免预算被低估。
469
- * 详见上方权重常量的口径说明。
470
- */
773
+ * 累加一次 agent 调用的 usage(加权口径)。
774
+ *
775
+ * 四项 token 按各自权重(INPUT/CACHE_READ/CACHE_WRITE/OUTPUT_WEIGHT)折算后求和,
776
+ * 而非原始 token 数直接相加。retry 间的真实消耗如实记录,避免预算被低估。
777
+ * 详见上方权重常量的口径说明。
778
+ */
471
779
  consume(usage: AgentUsage): void;
472
780
  /** 累加调用计数(每次 agent dispatch 后调用;持久化快照同步)。 */
473
781
  incrementCallCount(): void;
474
782
  /**
475
- * 是否超 token / cost 预算(FR-3)。
476
- *
477
- * maxTokens===0 或 undefined 视为不限制(守卫);
478
- * maxCost===0 或 undefined 视为不限制。
479
- * 时间预算(maxTimeMs)不由本方法判断——它是 wall-clock 约束,需参照 startedAt,
480
- * 由 lifecycle 层的 scheduleTimeBudget(runWorkflow 内 setTimeout)
481
- * 独立调度,到期 abortRun(doneReason="time_limited")。
482
- */
783
+ * 是否超 token / cost 预算(FR-3)。
784
+ *
785
+ * maxTokens===0 或 undefined 视为不限制(守卫);
786
+ * maxCost===0 或 undefined 视为不限制。
787
+ * 时间预算(maxTimeMs)不由本方法判断——它是 wall-clock 约束,需参照 startedAt,
788
+ * 由 lifecycle 层的 scheduleTimeBudget(runWorkflow 内 setTimeout)
789
+ * 独立调度,到期 abortRun(doneReason="time_limited")。
790
+ */
483
791
  isExceeded(): boolean;
484
792
  /**
485
- * 剩余 token 预算。maxTokens 未设或 ≤0 时返回 undefined(视为不限制)。
486
- *
487
- * 嵌套 workflow() 调用时由 executeNestedWorkflow 消费:子 run 的 budgetTokens
488
- * 继承父 run 的剩余预算,实现父子预算隔离下的总量约束。
489
- */
793
+ * 剩余 token 预算。maxTokens 未设或 ≤0 时返回 undefined(视为不限制)。
794
+ *
795
+ * 嵌套 workflow() 调用时由 executeNestedWorkflow 消费:子 run 的 budgetTokens
796
+ * 继承父 run 的剩余预算,实现父子预算隔离下的总量约束。
797
+ */
490
798
  remaining(): number | undefined;
491
799
  }
492
800
 
@@ -604,44 +912,44 @@ declare class RunRuntime {
604
912
  /** per-running-segment AbortController(一次性,无法复用——G3-001)。 */
605
913
  readonly controller: AbortController;
606
914
  /** Run 级墙钟时间预算计时器(spec.budgetTimeMs > 0 时由 lifecycle 调度,
607
- * 到期 abortRun time_limited)。release 时清理,避免 abort/replaceRuntime
608
- * 后孤儿计时器仍触发(rebuildRuntime 会重排一个全新的计时器,旧的不应残留)。 */
915
+ * 到期 abortRun time_limited)。release 时清理,避免 abort/replaceRuntime
916
+ * 后孤儿计时器仍触发(rebuildRuntime 会重排一个全新的计时器,旧的不应残留)。 */
609
917
  readonly timeBudgetTimer?: ReturnType<typeof setTimeout>;
610
918
  /**
611
- * 本 runtime 代际是否已收到 worker 的终态消息(return / error)。
612
- *
613
- * [F1] worker exit(0) 且本标记为 false = worker 静默退出、未交付任何终态——最常见根因
614
- * 是 execute() 返回值不可克隆,worker 侧 _safePost 吞掉 DataCloneError 后 return 消息
615
- * 根本没发出。旧实现 handleWorkerExit 对 code===0 no-op → run 永久 running、runAndWait
616
- * 悬挂。handleWorkerExit 据此判定转 done,failed。
617
- *
618
- * 按代际归零:字段挂在 RunRuntime(每代际 new 一个实例)而非 run.meta——script-error
619
- * 重试退避窗口内(error 消息已收到、run 仍 running、旧 worker exit(0))必须 no-op 等
620
- * rebuild;若挂 meta 则 rebuild 后新 worker 再静默退出时会被旧标记误放行,重新悬挂。
621
- *
622
- * 写点:① handleWorkerMessage 的 return/error 分支(WorkerHandle.isCurrent 守卫保证
623
- * 消息必来自当前代际);② handleWorkerError 进入处理前([R4-F1] 同代际幂等守卫——
624
- * worker 崩溃时 error + exit(1) 双事件各派发一次 handleWorkerError,第一个事件标记
625
- * 本代际已处理,第二个事件命中标志跳过,消除单次崩溃计数 +2 / 双 rebuild 交错)。
626
- * rebuildRuntime 构造新 RunRuntime 自然重置。
627
- */
919
+ * 本 runtime 代际是否已收到 worker 的终态消息(return / error)。
920
+ *
921
+ * [F1] worker exit(0) 且本标记为 false = worker 静默退出、未交付任何终态——最常见根因
922
+ * 是 execute() 返回值不可克隆,worker 侧 _safePost 吞掉 DataCloneError 后 return 消息
923
+ * 根本没发出。旧实现 handleWorkerExit 对 code===0 no-op → run 永久 running、runAndWait
924
+ * 悬挂。handleWorkerExit 据此判定转 done,failed。
925
+ *
926
+ * 按代际归零:字段挂在 RunRuntime(每代际 new 一个实例)而非 run.meta——script-error
927
+ * 重试退避窗口内(error 消息已收到、run 仍 running、旧 worker exit(0))必须 no-op 等
928
+ * rebuild;若挂 meta 则 rebuild 后新 worker 再静默退出时会被旧标记误放行,重新悬挂。
929
+ *
930
+ * 写点:① handleWorkerMessage 的 return/error 分支(WorkerHandle.isCurrent 守卫保证
931
+ * 消息必来自当前代际);② handleWorkerError 进入处理前([R4-F1] 同代际幂等守卫——
932
+ * worker 崩溃时 error + exit(1) 双事件各派发一次 handleWorkerError,第一个事件标记
933
+ * 本代际已处理,第二个事件命中标志跳过,消除单次崩溃计数 +2 / 双 rebuild 交错)。
934
+ * rebuildRuntime 构造新 RunRuntime 自然重置。
935
+ */
628
936
  receivedTerminalMessage: boolean;
629
937
  /** 防止 release 重复执行(幂等)。 */
630
938
  private released;
631
939
  constructor(worker: WorkerHandle, controller: AbortController, timeBudgetTimer?: ReturnType<typeof setTimeout>);
632
940
  /**
633
- * 释放所有资源:terminate worker + abort controller。
634
- *
635
- * 幂等——重复调用安全(第二次起 no-op,released flag 守卫)。
636
- * 调用后此 RunRuntime 应被调用方丢弃(WorkflowRun.runtime = undefined),
637
- * 崩溃重试时由 replaceRuntime 注入新实例(G3-001)。
638
- *
639
- * worker.terminate 本身幂等,controller.abort 本身幂等
640
- * (重复 abort 无副作用),但 released flag 让本方法语义更明确:
641
- * 「释放过一次的 runtime 不再释放第二次」。
642
- *
643
- * @param mode terminal —— 终局释放(唯一值,保留参数为调用方语义显式化)
644
- */
941
+ * 释放所有资源:terminate worker + abort controller。
942
+ *
943
+ * 幂等——重复调用安全(第二次起 no-op,released flag 守卫)。
944
+ * 调用后此 RunRuntime 应被调用方丢弃(WorkflowRun.runtime = undefined),
945
+ * 崩溃重试时由 replaceRuntime 注入新实例(G3-001)。
946
+ *
947
+ * worker.terminate 本身幂等,controller.abort 本身幂等
948
+ * (重复 abort 无副作用),但 released flag 让本方法语义更明确:
949
+ * 「释放过一次的 runtime 不再释放第二次」。
950
+ *
951
+ * @param mode terminal —— 终局释放(唯一值,保留参数为调用方语义显式化)
952
+ */
645
953
  release(_mode: ReleaseMode): void;
646
954
  /** 是否已 release(测试 + 诊断用)。 */
647
955
  get isReleased(): boolean;
@@ -689,14 +997,14 @@ declare class AgentCall {
689
997
  readonly traceNode: ExecutionTraceNode;
690
998
  constructor(id: number, opts: AgentCallOpts, traceNode: ExecutionTraceNode);
691
999
  /**
692
- * 标记进入 running 状态(dispatch 前)。attempts++(含首次)。
693
- * @throws 若已 done(不可重启)
694
- */
1000
+ * 标记进入 running 状态(dispatch 前)。attempts++(含首次)。
1001
+ * @throws 若已 done(不可重启)
1002
+ */
695
1003
  markRunning(): void;
696
1004
  /**
697
- * 标记完成(成功或失败均调用——result.error 区分)。
698
- * @throws 若当前非 running(pending 不能直接跳 done,必须先 markRunning)
699
- */
1005
+ * 标记完成(成功或失败均调用——result.error 区分)。
1006
+ * @throws 若当前非 running(pending 不能直接跳 done,必须先 markRunning)
1007
+ */
700
1008
  markDone(result: AgentResult): void;
701
1009
  /** 记录 pi subprocess session ID(dispatch 成功后)。 */
702
1010
  setSessionId(sessionId: string): void;
@@ -736,62 +1044,62 @@ declare class Trace {
736
1044
  /** stepIndex 倒排索引(查询加速 O(1);值与 nodes 元素引用共享)。 */
737
1045
  private readonly byIndex;
738
1046
  /**
739
- * 从已有节点数组重建 Trace(用于 RunStore 反序列化重水合)。
740
- *
741
- * 防御性拷贝——传入数组不被持有,外部 mutation 不影响 Trace。
742
- * 不验证节点顺序/唯一性(调用方保证快照来源可信)。
743
- * 不做裁剪——落盘快照已是 write 路径裁剪后形态,旧版本未裁剪长
744
- * content 重水合保持原样(read 路径无二次信息损失)。
745
- * 重水合后 call.traceNode(来自快照 calls[].traceNode,
746
- * jsonl-run-store.ts:156 直接传入)与 Trace.nodes 副本非同引用——
747
- * D-10 引用共享仅 live append 路径成立。
748
- */
1047
+ * 从已有节点数组重建 Trace(用于 RunStore 反序列化重水合)。
1048
+ *
1049
+ * 防御性拷贝——传入数组不被持有,外部 mutation 不影响 Trace。
1050
+ * 不验证节点顺序/唯一性(调用方保证快照来源可信)。
1051
+ * 不做裁剪——落盘快照已是 write 路径裁剪后形态,旧版本未裁剪长
1052
+ * content 重水合保持原样(read 路径无二次信息损失)。
1053
+ * 重水合后 call.traceNode(来自快照 calls[].traceNode,
1054
+ * jsonl-run-store.ts:156 直接传入)与 Trace.nodes 副本非同引用——
1055
+ * D-10 引用共享仅 live append 路径成立。
1056
+ */
749
1057
  static fromArray(nodes: readonly ExecutionTraceNode[]): Trace;
750
1058
  /**
751
- * Append a trace node(append-only,不改已有节点)。
752
- *
753
- * 入口裁剪:超长 result.content 先 mutate 入参节点的 result 字段,
754
- * 再 push 原节点引用(禁止 push 副本——保持 AgentCall.traceNode 与
755
- * Trace.nodes 共享同一引用的 D-10 不变式)。
756
- */
1059
+ * Append a trace node(append-only,不改已有节点)。
1060
+ *
1061
+ * 入口裁剪:超长 result.content 先 mutate 入参节点的 result 字段,
1062
+ * 再 push 原节点引用(禁止 push 副本——保持 AgentCall.traceNode 与
1063
+ * Trace.nodes 共享同一引用的 D-10 不变式)。
1064
+ */
757
1065
  append(node: ExecutionTraceNode): void;
758
1066
  /**
759
- * Update a trace node by stepIndex (callId) with a partial patch.
760
- *
761
- * 只改 patch 中提供的字段(status/result/error/completedAt/sessionId)。
762
- * stepIndex 不存在时 no-op(防御性——agent 完成/失败回调可能晚于 run 终止到达)。
763
- */
1067
+ * Update a trace node by stepIndex (callId) with a partial patch.
1068
+ *
1069
+ * 只改 patch 中提供的字段(status/result/error/completedAt/sessionId)。
1070
+ * stepIndex 不存在时 no-op(防御性——agent 完成/失败回调可能晚于 run 终止到达)。
1071
+ */
764
1072
  update(stepIndex: number, patch: TracePatch): void;
765
1073
  /**
766
- * 查找指定 stepIndex 的节点(byIndex O(1),trace 中 stepIndex 应唯一)。
767
- *
768
- * 语义差异声明(旧线性扫 first-match → Map last-wins):仅在破坏
769
- * stepIndex 唯一性的违规使用下可见,两组场景——
770
- * 1. 重复 append 同 stepIndex 且未 remove:返回最后一个节点(last-wins;
771
- * 旧线性扫 first-match 会返回第一个)。
772
- * 2. 重复 append 后 removeByStepIndex:remove 的 findIndex 命中首个旧节点
773
- * splice,而 byIndex.delete 把整个 stepIndex 键删掉——nodes 残留第二个
774
- * 节点成为孤儿(find/update 不可达,length/toArray 仍可见)。
775
- * 合法路径无差异:唯一性由 discard 先 remove 再 append 保证(W1TC12 锚定)。
776
- */
1074
+ * 查找指定 stepIndex 的节点(byIndex O(1),trace 中 stepIndex 应唯一)。
1075
+ *
1076
+ * 语义差异声明(旧线性扫 first-match → Map last-wins):仅在破坏
1077
+ * stepIndex 唯一性的违规使用下可见,两组场景——
1078
+ * 1. 重复 append 同 stepIndex 且未 remove:返回最后一个节点(last-wins;
1079
+ * 旧线性扫 first-match 会返回第一个)。
1080
+ * 2. 重复 append 后 removeByStepIndex:remove 的 findIndex 命中首个旧节点
1081
+ * splice,而 byIndex.delete 把整个 stepIndex 键删掉——nodes 残留第二个
1082
+ * 节点成为孤儿(find/update 不可达,length/toArray 仍可见)。
1083
+ * 合法路径无差异:唯一性由 discard 先 remove 再 append 保证(W1TC12 锚定)。
1084
+ */
777
1085
  private findByStepIndex;
778
1086
  /** 按节点引用删除(仅用于测试或 run 重建场景;正常运行不调用)。 */
779
1087
  find(stepIndex: number): ExecutionTraceNode | undefined;
780
1088
  /**
781
- * 按 stepIndex 移除节点(崩溃重建清理在飞 call 用)。
782
- *
783
- * 正常运行不调用(append-only 不变式)。仅 error-recovery 的 discardInFlightCalls
784
- * (rebuildRuntime 内,F2)清理被旧 runtime abort 的在飞 call 时用——移除其 trace
785
- * 节点,让重跑重发 agent-call 时 append 全新节点走全新执行路径(避免 stale
786
- * "running" 节点残留 + trace.update 命中旧节点导致新节点 orphan)。
787
- * stepIndex 不存在时 no-op(防御性)。
788
- */
1089
+ * 按 stepIndex 移除节点(崩溃重建清理在飞 call 用)。
1090
+ *
1091
+ * 正常运行不调用(append-only 不变式)。仅 error-recovery 的 discardInFlightCalls
1092
+ * (rebuildRuntime 内,F2)清理被旧 runtime abort 的在飞 call 时用——移除其 trace
1093
+ * 节点,让重跑重发 agent-call 时 append 全新节点走全新执行路径(避免 stale
1094
+ * "running" 节点残留 + trace.update 命中旧节点导致新节点 orphan)。
1095
+ * stepIndex 不存在时 no-op(防御性)。
1096
+ */
789
1097
  removeByStepIndex(stepIndex: number): void;
790
1098
  /**
791
- * readonly 视图——返回内部 nodes 数组引用(仅类型级 readonly,运行时无
792
- * 防御)。消费方禁止结构化 mutate(push/splice/重排/覆盖元素):byIndex
793
- * 引入后外部结构化 mutate 会使 nodes 与倒排索引 desync。字段级变更走 update()。
794
- */
1099
+ * readonly 视图——返回内部 nodes 数组引用(仅类型级 readonly,运行时无
1100
+ * 防御)。消费方禁止结构化 mutate(push/splice/重排/覆盖元素):byIndex
1101
+ * 引入后外部结构化 mutate 会使 nodes 与倒排索引 desync。字段级变更走 update()。
1102
+ */
795
1103
  toArray(): readonly ExecutionTraceNode[];
796
1104
  /** 当前节点数。 */
797
1105
  get length(): number;
@@ -890,79 +1198,79 @@ declare class WorkflowRun {
890
1198
  runtime?: RunRuntime;
891
1199
  meta: WorkflowRunMeta;
892
1200
  /**
893
- * 创建聚合根。初始状态 "running"(一次性生命周期:run 从创建起即在执行,
894
- * runtime 由紧随其后的 assignRuntime 注入)。也可传入 done 状态用于重水合
895
- * 已完成的 run(loadAll 后的只读聚合)。
896
- *
897
- * 不变式 I1 构造期跳过——「创建即 running」要求构造瞬间 runtime===undefined
898
- * 合法(runtime 必须由 assignRuntime 注入,构造函数无从持有);重水合的
899
- * running 快照同样无 worker。I1 的运行时校验在 assignRuntime/transition/
900
- * replaceRuntime 末尾的 validateInvariants 处生效。
901
- */
1201
+ * 创建聚合根。初始状态 "running"(一次性生命周期:run 从创建起即在执行,
1202
+ * runtime 由紧随其后的 assignRuntime 注入)。也可传入 done 状态用于重水合
1203
+ * 已完成的 run(loadAll 后的只读聚合)。
1204
+ *
1205
+ * 不变式 I1 构造期跳过——「创建即 running」要求构造瞬间 runtime===undefined
1206
+ * 合法(runtime 必须由 assignRuntime 注入,构造函数无从持有);重水合的
1207
+ * running 快照同样无 worker。I1 的运行时校验在 assignRuntime/transition/
1208
+ * replaceRuntime 末尾的 validateInvariants 处生效。
1209
+ */
902
1210
  constructor(runId: string, spec: RunSpec, state: RunState, meta: WorkflowRunMeta);
903
1211
  /**
904
- * 从持久化快照重水合聚合根。与构造函数同语义(构造期跳过 I1——持久化的
905
- * running 状态没有 worker,进程被杀后 worker 不可能还活着)。保留独立工厂
906
- * 标注重水合意图;调用方(D-4 kill-9 恢复)负责在 session_start 时把残留
907
- * running 转 done,failed,恢复 I1。
908
- *
909
- * @throws I2 违反(done 快照缺 reason 仍是 bug,不可跳过)
910
- */
1212
+ * 从持久化快照重水合聚合根。与构造函数同语义(构造期跳过 I1——持久化的
1213
+ * running 状态没有 worker,进程被杀后 worker 不可能还活着)。保留独立工厂
1214
+ * 标注重水合意图;调用方(D-4 kill-9 恢复)负责在 session_start 时把残留
1215
+ * running 转 done,failed,恢复 I1。
1216
+ *
1217
+ * @throws I2 违反(done 快照缺 reason 仍是 bug,不可跳过)
1218
+ */
911
1219
  static reconstruct(runId: string, spec: RunSpec, state: RunState, meta: WorkflowRunMeta): WorkflowRun;
912
1220
  /**
913
- * 校验不变式 I1 + I2。违反抛错(聚合根自我保护,fail-fast)。
914
- * 在每个 mutation 方法末尾调用(防御式编程 + 测试可断言)。
915
- */
1221
+ * 校验不变式 I1 + I2。违反抛错(聚合根自我保护,fail-fast)。
1222
+ * 在每个 mutation 方法末尾调用(防御式编程 + 测试可断言)。
1223
+ */
916
1224
  private validateInvariants;
917
1225
  /**
918
- * 仅校验不变式 I2(done ⟹ reason)。构造期用——「创建即 running」与重水合的
919
- * running 快照都无 runtime(I1 构造期跳过),但 I2 必须保证(done 缺 reason 是真 bug)。
920
- */
1226
+ * 仅校验不变式 I2(done ⟹ reason)。构造期用——「创建即 running」与重水合的
1227
+ * running 快照都无 runtime(I1 构造期跳过),但 I2 必须保证(done 缺 reason 是真 bug)。
1228
+ */
921
1229
  private validateInvariantI2;
922
1230
  /**
923
- * 状态机转换。合法转换:running→done。
924
- *
925
- * running 的进入不走 transition——构造即 running,replaceRuntime 保持 running。
926
- * 调用 transition("running") 抛错,防止绕过 runtime 注入直接改状态。
927
- *
928
- * 副作用:
929
- * - →done: releaseRuntime + 设 state.reason + meta.completedAt
930
- *
931
- * @param target 目标状态(不允许 "running"——runtime 注入只走 assignRuntime/replaceRuntime)
932
- * @param reason →done 时必填(done ⟹ reason,不变式 I2)
933
- * @throws 非法转换 / done 缺 reason / target==="running"
934
- */
1231
+ * 状态机转换。合法转换:running→done。
1232
+ *
1233
+ * running 的进入不走 transition——构造即 running,replaceRuntime 保持 running。
1234
+ * 调用 transition("running") 抛错,防止绕过 runtime 注入直接改状态。
1235
+ *
1236
+ * 副作用:
1237
+ * - →done: releaseRuntime + 设 state.reason + meta.completedAt
1238
+ *
1239
+ * @param target 目标状态(不允许 "running"——runtime 注入只走 assignRuntime/replaceRuntime)
1240
+ * @param reason →done 时必填(done ⟹ reason,不变式 I2)
1241
+ * @throws 非法转换 / done 缺 reason / target==="running"
1242
+ */
935
1243
  transition(target: RunStatus, reason?: DoneReason): void;
936
1244
  /**
937
- * 绑定 runtime(run 创建后注入执行资源)。
938
- *
939
- * 前置:status==="running" && runtime===undefined(runWorkflow 创建路径——
940
- * 构造即 running 但 runtime 延迟到此处注入)。
941
- * 原子地:设 runtime 后末尾 validateInvariants,恢复构造期跳过的 I1
942
- * (running ⟺ runtime!==undefined)。
943
- *
944
- * @throws runtime 已定义 / status 不是 "running"(done 僵尸不可复活)
945
- */
1245
+ * 绑定 runtime(run 创建后注入执行资源)。
1246
+ *
1247
+ * 前置:status==="running" && runtime===undefined(runWorkflow 创建路径——
1248
+ * 构造即 running 但 runtime 延迟到此处注入)。
1249
+ * 原子地:设 runtime 后末尾 validateInvariants,恢复构造期跳过的 I1
1250
+ * (running ⟺ runtime!==undefined)。
1251
+ *
1252
+ * @throws runtime 已定义 / status 不是 "running"(done 僵尸不可复活)
1253
+ */
946
1254
  assignRuntime(rt: RunRuntime): void;
947
1255
  /**
948
- * 解绑 runtime(done 时由 transition 调用,也可独立调用)。
949
- *
950
- * 前置:无(runtime===undefined 时 no-op,幂等)。
951
- * 副作用:调 runtime.release("terminal") 释放 worker/controller,置 runtime=undefined。
952
- */
1256
+ * 解绑 runtime(done 时由 transition 调用,也可独立调用)。
1257
+ *
1258
+ * 前置:无(runtime===undefined 时 no-op,幂等)。
1259
+ * 副作用:调 runtime.release("terminal") 释放 worker/controller,置 runtime=undefined。
1260
+ */
953
1261
  releaseRuntime(): void;
954
1262
  /**
955
- * 原地替换 runtime(G5-001:worker-error-retry)。
956
- *
957
- * 前置:status==="running"(G6-001:终态 run 拒绝重建)。
958
- * 原子地:释放旧 runtime(worker.terminate + abort)+ 绑定新 runtime,
959
- * 全程 status 保持 "running",不变式 I1 不违反(中间无 runtime===undefined 可见态)。
960
- *
961
- * 与 release+assign 的区别:replaceRuntime 不改 status,中间同步完成,
962
- * 外部观察不到违反不变式的瞬间。
963
- *
964
- * @throws status!=="running"
965
- */
1263
+ * 原地替换 runtime(G5-001:worker-error-retry)。
1264
+ *
1265
+ * 前置:status==="running"(G6-001:终态 run 拒绝重建)。
1266
+ * 原子地:释放旧 runtime(worker.terminate + abort)+ 绑定新 runtime,
1267
+ * 全程 status 保持 "running",不变式 I1 不违反(中间无 runtime===undefined 可见态)。
1268
+ *
1269
+ * 与 release+assign 的区别:replaceRuntime 不改 status,中间同步完成,
1270
+ * 外部观察不到违反不变式的瞬间。
1271
+ *
1272
+ * @throws status!=="running"
1273
+ */
966
1274
  replaceRuntime(rt: RunRuntime): void;
967
1275
  }
968
1276
 
@@ -1114,6 +1422,9 @@ interface LifecycleDeps {
1114
1422
  * - evictDoneRunsBeyondCap(runs, keepDone) → number(done run 内存淘汰)
1115
1423
  * - scheduleTimeBudget(runId, deps, budgetTimeMs) → timer(C.7 时间预算)
1116
1424
  *
1425
+ * 第 6 个导出:recoverCrashedRuns(store, runs, reason, hooks?) —— 崩溃恢复四步
1426
+ * 装配(loadAll→failed→save→evict,D8/B1),宿主专属事件经 hooks 外置。
1427
+ *
1117
1428
  * 私有 makeHandlers(run, deps) → WorkerHandlers:
1118
1429
  * - onMessage → handleWorkerMessage(run, raw, deps, handlers)
1119
1430
  * - onError → handleWorkerError(run, err, deps, handlers) + workerErrorCount++
@@ -1137,6 +1448,27 @@ interface LifecycleDeps {
1137
1448
  * 参考:domain-models.md §1(聚合根状态机)。
1138
1449
  */
1139
1450
 
1451
+ /**
1452
+ * done run 内存保留窗口(K=20)。
1453
+ *
1454
+ * 本淘汰是 done run 内存有界性的唯一来源:calls.result 不裁、单聚合大小不随 wave1
1455
+ * 裁剪缩小,故内存上限 = K × 实际聚合大小。同时定义 actionStatus 可查刚完成 run
1456
+ * 的窗口(超出窗口的 done run 不再出现在列表中——已接受的用户可见变化)。
1457
+ */
1458
+ declare const MAX_RETAINED_DONE_RUNS = 20;
1459
+ /**
1460
+ * 启动 run 级墙钟时间预算计时器:到期后 abortRun(doneReason="time_limited")。
1461
+ *
1462
+ * 恢复旧 orchestrator-budget.ts 的 scheduleTimeBudgetCheck 语义——runWorkflow 启动
1463
+ * 一个 setTimeout(maxTimeMs),到期若 run 仍未终态则转 done,time_limited。
1464
+ * 计时器存入 RunRuntime.timeBudgetTimer,release(abort/replaceRuntime)时
1465
+ * 自动清理,避免孤儿触发。worker/script 错误重试经 rebuildRuntime 重排新计时器。
1466
+ *
1467
+ * @returns 计时器句柄(未设预算时 undefined)
1468
+ * @throws budgetTimeMs 超出 Node setTimeout 上限(2^31-1)——溢出值会被 Node 置 1ms
1469
+ * 立即触发(「不限时预算」变「立即超时」),fail-fast 不静默 clamp(U1)。
1470
+ */
1471
+ declare function scheduleTimeBudget(runId: string, deps: LifecycleDeps, budgetTimeMs: number): ReturnType<typeof setTimeout>;
1140
1472
  /**
1141
1473
  * 启动一个 workflow run。
1142
1474
  *
@@ -1165,6 +1497,3525 @@ declare function runWorkflow(spec: RunSpec, deps: LifecycleDeps, signal?: AbortS
1165
1497
  * @throws runId 不存在
1166
1498
  */
1167
1499
  declare function abortRun(runId: string, deps: LifecycleDeps, reason?: string, doneReason?: DoneReason): Promise<void>;
1500
+ /**
1501
+ * 终止 deps.runs 中全部 running run(session 切换 / session 关闭时调用)。
1502
+ *
1503
+ * 一次性生命周期(D-2):session 离开当刻,running run 的 token 投入作废,转
1504
+ * done,failed 持久化落盘——重启后 kill-9 恢复不误判,也不再存在「挂起待恢复」
1505
+ * 的中间态。
1506
+ *
1507
+ * per-run 行为:`state.error = reason` → `transition("done","failed")`(内部先
1508
+ * releaseRuntime,A4)→ `await store.save(run)` → `eventBus.emit("pending:unregister",
1509
+ * {reason:"failed"})`。
1510
+ *
1511
+ * **不调 deps.onRunDone**:对齐 session_start 恢复先例(index.ts kill-9 恢复只发
1512
+ * unregister、不发 onRunDone)——session 切换/关闭语境下主 agent 已离开本 session,
1513
+ * 注入完成通知只会把消息发给已离开的 session。
1514
+ *
1515
+ * **不调 discardInFlightCalls**:run 已转终态不再 replay(无恢复路径),在飞 call
1516
+ * 缓存清不清都不影响结果;该清理仅 rebuildRuntime 需要(崩溃重试会重放脚本,
1517
+ * 假失败结果会污染重跑输出)。
1518
+ *
1519
+ * 单 run 失败(try/catch + log 带 runId/reason)不中断其余 run——终止是批量收尾,
1520
+ * 一个 run 落盘失败不应放走其余 run 的 failed 状态。
1521
+ *
1522
+ * @param deps LifecycleDeps(runs/store/eventBus/log)
1523
+ * @param reason 终止原因(写入 run.state.error,如 "Session switched: run terminated")
1524
+ */
1525
+ declare function terminateRunningRuns(deps: LifecycleDeps, reason: string): Promise<void>;
1526
+ /**
1527
+ * 淘汰 runs Map 中超出保留窗口的 done run,返回本次淘汰数量。
1528
+ *
1529
+ * 规则(契约 W3C1):
1530
+ * 1. **状态白名单**:仅 `state.status === "done"` 可淘汰(RunStatus 封闭两态,
1531
+ * 显式白名单而非「非 running」——未来新增状态不落淘汰端)。running
1532
+ * (活跃执行,isScriptRunning 遍历依赖)永不淘汰,即使 completedAt 缺失也
1533
+ * 绝不参与排序淘汰。
1534
+ * 2. **排序**:done 项按 `meta.completedAt` ISO 字符串字典序升序(toISOString 恒
1535
+ * UTC 毫秒格式,字典序=时间序)。completedAt 缺失(防御旧格式/异常快照)fallback
1536
+ * 排序键为空串——字典序最小=最旧,先被淘汰。
1537
+ * 3. **tie 稳定排序**:比较器三态返回(相等返回 0),Array#sort 稳定性(Node≥12)
1538
+ * 保持元素原序——原序 = Map 插入序 = 创建序,tie 组内先创建者视为更旧先被淘汰
1539
+ * (kill-9 批量恢复同 ms completedAt 场景的确定性保证)。
1540
+ * 4. **淘汰执行**:超限数 excess = doneCount - keepDone(<=0 时 no-op 返回 0),
1541
+ * 对升序前 excess 项逐个 `runs.delete(runId)`。
1542
+ * 5. **边界不变式**:禁止按 Map 插入序直接淘汰——嵌套 workflow 父 run 创建最早、
1543
+ * 完成最晚,插入序淘汰会在其自身 onRunDone 同步裁剪中淘汰它,runAndWait 轮询
1544
+ * 窗口内 get 不到 → 误返 "Run not found"。
1545
+ * 6. **副作用边界**:只清内存 runs Map,不动磁盘 state 文件、不删
1546
+ * workflow-state-link 指针条目、不发任何事件。
1547
+ *
1548
+ * @param runs per-session 的 run 注册表(原地裁剪)
1549
+ * @param keepDone done run 保留数(生产传 MAX_RETAINED_DONE_RUNS)
1550
+ * @returns 本次淘汰的 run 数量
1551
+ */
1552
+ declare function evictDoneRunsBeyondCap(runs: Map<string, WorkflowRun>, keepDone: number): number;
1553
+ /**
1554
+ * 崩溃恢复循环的宿主事件外置 hooks(core 平台中立,宿主注入专属行为)。
1555
+ *
1556
+ * pi 宿主在 onRunRecovered 中发射 `pending:unregister` 事件(pending-notifications
1557
+ * 扩展的注销信号灯);zsw/第三宿主可接自己的通知通道。不注入 = 无宿主事件,
1558
+ * 恢复语义(failed 转换 + 落盘 + 淘汰)不受影响。
1559
+ */
1560
+ interface RecoverCrashedRunsHooks {
1561
+ /**
1562
+ * 每个 running → done,failed 转换的 run 恰好调用一次。
1563
+ *
1564
+ * payload 形状对齐 pi `pending:unregister` 事件:`{ id: runId, reason: "failed" }`
1565
+ * ——宿主可直接把 payload 转发到自己的事件总线。
1566
+ *
1567
+ * 错误围栏:回调同步 throw 经 core logger facade warn 留痕后被吞掉,恢复循环
1568
+ * 继续其余 run(转换/落盘不受影响)——与 save 步骤「单 run 失败不中断其余」
1569
+ * 容错口径对称。
1570
+ */
1571
+ onRunRecovered?: (payload: {
1572
+ id: string;
1573
+ reason: string;
1574
+ }) => void;
1575
+ }
1576
+ /**
1577
+ * recoverCrashedRuns 的计数结果(宿主启动日志/健康面用)。
1578
+ *
1579
+ * `recovered` 只计 running 遗留被转换为 done,failed 的条数;`loaded` 是 loadAll
1580
+ * 重水合的全量数(含 done 历史快照)——两者分开口径,避免宿主把「重水合 N 条」
1581
+ * 误报为「恢复 N 条」。
1582
+ */
1583
+ interface RecoverCrashedRunsResult {
1584
+ /** loadAll 重水合的 run 总数(含 done 历史快照)。 */
1585
+ loaded: number;
1586
+ /** running 遗留被转换为 done,failed 的条数(0 = 无崩溃遗留)。 */
1587
+ recovered: number;
1588
+ }
1589
+ /**
1590
+ * 崩溃恢复四步装配(设计 D8/B1):loadAll → failed → save → evict。
1591
+ *
1592
+ * 平移自 pi 壳 session_start 恢复循环(subagent-workflow index.ts:578-627)与
1593
+ * zsw orchestration-host recoverOrphans(逐行同构,pending:unregister 为唯一
1594
+ * 宿主差异点——经 {@link RecoverCrashedRunsHooks} 外置)。
1595
+ *
1596
+ * 步骤语义:
1597
+ * 1. **loadAll**:从 store 全量重水合。失败向上抛——fail-fast 策略(pi 的
1598
+ * storeHealthy=false 停初始化)是宿主职责,调用方 catch 后自行决定;
1599
+ * 2. **failed**:残留 running run(进程被杀,worker 必死)逐个
1600
+ * `state.error = reason` → `transition("done","failed")`(A4:transition 内部
1601
+ * 先 releaseRuntime),并发宿主事件(hooks);done run 原样保留;
1602
+ * 3. **save**:转换后的 run 落盘(恢复终态必须持久化,不 save 则下次启动重水合
1603
+ * 仍见 running,侧栏永久卡 running)。单 run save 失败仅记日志不中断其余
1604
+ * run——恢复天然幂等,下次启动重开重试;
1605
+ * 4. **evict**:全量重水合后立即 `evictDoneRunsBeyondCap(runs,
1606
+ * MAX_RETAINED_DONE_RUNS)`(K=20)——done run 内存有界性;只 delete 内存
1607
+ * Map 条目,磁盘 state 文件不动(对齐 pi,历史审计保留)。
1608
+ *
1609
+ * 所有 loaded run(含 done)都注册进 runs Map(runId → WorkflowRun)——与 pi
1610
+ * 一致,done run 也入 Map 供列表/查询消费。
1611
+ *
1612
+ * @param store RunStore port(loadAll/save)
1613
+ * @param runs per-session 的 run 注册表(原地写入 + 淘汰)
1614
+ * @param reason 恢复原因(写入 running run 的 state.error,如
1615
+ * "Process killed (kill-9 or crash recovery)")
1616
+ * @param hooks 宿主事件外置(可选)
1617
+ * @returns 计数 `{ loaded, recovered }`——recovered 只计 running→failed 转换条数
1618
+ * @throws store.loadAll 失败时原样抛出(步骤 1)
1619
+ */
1620
+ declare function recoverCrashedRuns(store: RunStore, runs: Map<string, WorkflowRun>, reason: string, hooks?: RecoverCrashedRunsHooks): Promise<RecoverCrashedRunsResult>;
1621
+
1622
+ /**
1623
+ * Workflow Extension — 静态 lint
1624
+ *
1625
+ * 在执行前捕获常见的 workflow 脚本 API 误用。纯函数,零副作用,零 IO。
1626
+ *
1627
+ * 设计:
1628
+ * - lint 是编排层关注(非技术资源),归属 Engine 层。
1629
+ * - **entry-point 检查**:脚本必须含 agent/parallel/pipeline 之一,否则视为 error。
1630
+ * WorkflowScript.validate 直接委托 lintScript,故 entry-point 检查必须在此。
1631
+ * - LintFinding/LintResult 类型规范的 canonical 源在本文件。
1632
+ *
1633
+ * 检查项:
1634
+ * 1. 必须含 agent/parallel/pipeline 入口(error)
1635
+ * 2. agent 选项中 outputSchema 当 key 用 → 应为 schema(error)
1636
+ * 3. result.output / result.parsedOutput / result.content → agent 返回未包装值(error)
1637
+ * 4. readFileSync/writeFileSync 传状态 → 脆弱(warning)
1638
+ * 5. unlinkSync 清理状态 → 与 subprocess 文件读竞态(warning)
1639
+ * 6. 顶层未 await 的异步 IIFE + 内部调 agent/parallel/pipeline → 子进程被提前 kill(error)
1640
+ * 7. agent() 缺 description/label → TUI /workflows 显示 '(unnamed)'(warning)
1641
+ * 8. meta.phases 非字符串数组(对象数组等)→ 引擎忽略(warning)
1642
+ * 9. meta.phases 声明与 phase() 调用不一致 → 运行时分组与声明脱节(warning)
1643
+ *
1644
+ * 层归属:Engine。
1645
+ *
1646
+ * 参考:domain-models.md §7(validate 语义)。
1647
+ */
1648
+ /** Lint 检查发现项。 */
1649
+ interface LintFinding {
1650
+ /** error = 会导致运行时崩溃; warning = 可能的错误 */
1651
+ severity: "error" | "warning";
1652
+ line: number;
1653
+ message: string;
1654
+ suggestion: string;
1655
+ }
1656
+ /** Lint 检查结果。 */
1657
+ interface LintResult {
1658
+ valid: boolean;
1659
+ findings: LintFinding[];
1660
+ }
1661
+
1662
+ declare function lintScript(source: string): LintResult;
1663
+
1664
+ /**
1665
+ * Workflow Extension — WorkflowScript 实体
1666
+ *
1667
+ * 一个 workflow 脚本文件的数据 + 操作收敛(domain-models.md §7)。
1668
+ *
1669
+ * 设计:
1670
+ * - 将"脚本源 + meta + validate + toExecutable"收敛为实体。
1671
+ * - validate 委托 engine/script-lint.ts 的 lintScript。
1672
+ * - toExecutable 只做 strip `export const meta`(纯文本变换);worker 线程 wrap
1673
+ * (注入 agent/parallel/pipeline globals)由 infra/worker-script-builder.ts
1674
+ * 的 buildWorkerScript 承担——那是技术资源模板生成,不属于实体职责(D-12:
1675
+ * 模型只管数据+不变式)。
1676
+ *
1677
+ * 层归属:Engine。
1678
+ *
1679
+ * 参考:domain-models.md §7(字段/操作)、engine/script-lint.ts(lint 实现)。
1680
+ */
1681
+
1682
+ /** 脚本来源:saved(.pi/workflows/ 固定)或 tmp(.pi/workflows/.tmp/ 临时)。 */
1683
+ type WorkflowSource = "saved" | "tmp";
1684
+ /**
1685
+ * WorkflowScript 实体。
1686
+ *
1687
+ * 不变式:
1688
+ * - name 非空(meta 提取成功时来自 meta.name,失败时来自文件名 stem)
1689
+ * - available=false 时 meta 为空壳(name=stem, description="", phases=[])
1690
+ * - sourceCode 为原始文件内容(含 export);toExecutable 返回 strip 后的副本
1691
+ */
1692
+ declare class WorkflowScript {
1693
+ readonly name: string;
1694
+ readonly source: WorkflowSource;
1695
+ readonly path: string;
1696
+ /** 原始文件内容(可编辑)。toExecutable 返回 strip 后的副本,不改本字段。 */
1697
+ sourceCode: string;
1698
+ readonly meta: WorkflowMeta;
1699
+ /** false 当 meta 提取失败(loader 不抛错,标记不可用但仍列出)。 */
1700
+ available: boolean;
1701
+ constructor(opts: {
1702
+ name: string;
1703
+ source: WorkflowSource;
1704
+ path: string;
1705
+ sourceCode: string;
1706
+ meta: WorkflowMeta;
1707
+ available: boolean;
1708
+ });
1709
+ /**
1710
+ * 静态检查脚本合法性。
1711
+ *
1712
+ * 委托 engine/script-lint.ts 的 lintScript——检查项含:
1713
+ * - 必须含 agent/parallel/pipeline 入口之一
1714
+ * - agent 选项 outputSchema → schema
1715
+ * - result.output/parsedOutput/content 不存在
1716
+ * - 文件传状态警告
1717
+ *
1718
+ * IF9(#15):同 path 且 sourceCode 引用相等 → 返回缓存 lint 结果(launcher 嵌套
1719
+ * 场景下 registry 重建实例的重复全量 lint 消除);否则 lint + 覆写条目。
1720
+ */
1721
+ validate(): LintResult;
1722
+ /**
1723
+ * 返回可执行源。
1724
+ *
1725
+ * m2:不再 strip `export const meta`——meta 现为 @pi-meta 块注释(合法 JS,
1726
+ * worker 天然忽略),无 const meta 变量。toExecutable 返回原文(含块注释)。
1727
+ * Worker 线程 wrap(注入 globals)由 infra/worker-script-builder.ts buildWorkerScript 完成。
1728
+ */
1729
+ toExecutable(): string;
1730
+ }
1731
+
1732
+ /**
1733
+ * Workflow Extension — WorkflowScriptRegistry 仓库接口
1734
+ *
1735
+ * workflow 脚本的仓库(repository)接口——Engine 定义、Infra 实现。
1736
+ *
1737
+ * 与 Ports 节的 3 个注入 port(AgentRunner/RunStore/WorkerHost)的区别:
1738
+ * - 3 个 port 是"执行依赖"(子进程/文件系统/线程),注入到 LifecycleDeps
1739
+ * - WorkflowScriptRegistry 是"发现依赖"(扫描文件系统),是 repository(§8),
1740
+ * 不进 LifecycleDeps,由 Interface 层 tool 直接调用(list/get 脚本)
1741
+ *
1742
+ * 优先级:tmp > project > user(domain-models.md §8)。60s TTL,按 workspaceRoot 分桶。
1743
+ * 实现在 Infra 层 WorkflowScriptRegistryImpl(扫描 + 缓存 + 去重)。
1744
+ *
1745
+ * 层归属:Engine(interface),Infra(impl)。
1746
+ *
1747
+ * 参考:domain-models.md §8。
1748
+ */
1749
+
1750
+ /**
1751
+ * workflow 脚本仓库接口(repository,需 mock 文件扫描)。
1752
+ */
1753
+ interface WorkflowScriptRegistry {
1754
+ /** 扫描并返回所有 workflow 脚本(含 available=false 的解析失败项)。去重按 tmp>project>user。 */
1755
+ loadAll(): Promise<WorkflowScript[]>;
1756
+ /** 按名查单个脚本(含缓存)。返回 undefined 当 name 不存在。 */
1757
+ get(name: string): Promise<WorkflowScript | undefined>;
1758
+ /** 按绝对路径加载单个脚本(workflowRef 统一解析入口——S2 路径统一)。 */
1759
+ getPath(ref: string): Promise<WorkflowScript | undefined>;
1760
+ /** 失效缓存——下次 loadAll/get 重新扫描文件系统。 */
1761
+ invalidate(): void;
1762
+ }
1763
+
1764
+ /**
1765
+ * Workflow Extension — launcher
1766
+ *
1767
+ * runAndWait free function(D-12)。跨扩展编程入口(pi.__workflowRun)——
1768
+ * 阻塞至 run 到达 done 终态。
1769
+ *
1770
+ * **D-8 签名**:返回 WorkflowRunResult({status:"done", reason, ...})——
1771
+ * status 恒为 "done",具体原因由 reason 区分
1772
+ * (completed/failed/aborted/budget_limited/time_limited)。
1773
+ *
1774
+ * **C.7**:timeout → transition done,time_limited(仅返回 timeout 标记但不转终态
1775
+ * 会让 workflow 仍 running,资源泄漏)。
1776
+ *
1777
+ * 流程:
1778
+ * 1. registry.get(name) → WorkflowScript(未找到返回 failed)
1779
+ * 2. script.validate(lint 检查)→ 失败抛错(不进 runWorkflow)
1780
+ * 3. script.toExecutable → 可执行源
1781
+ * 4. 构建 RunSpec + runWorkflow(spec, deps, signal)
1782
+ * 5. 轮询至 done(间隔 STATUS_POLL_INTERVAL_MS)
1783
+ * 6. 显式 timeoutMs 到期 → abortRun + transition done,time_limited(未传不限时)
1784
+ * 7. signal.aborted → abortRun + reason=aborted
1785
+ *
1786
+ * 层归属:Engine。依赖 registry + runWorkflow/abortRun + LifecycleDeps。
1787
+ *
1788
+ * 参考:domain-models.md §D-8(WorkflowRunResult 签名)、clarification.md C.7。
1789
+ */
1790
+
1791
+ /**
1792
+ * runAndWait 的返回(D-8 签名)。
1793
+ *
1794
+ * status 恒为 "done"(runAndWait 阻塞至 done 才返回);具体原因由 reason 区分
1795
+ * (completed/failed/aborted/budget_limited/time_limited)。
1796
+ */
1797
+ interface WorkflowRunResult {
1798
+ /** 恒为 "done"(runAndWait 阻塞至 done)。 */
1799
+ status: "done";
1800
+ /** 终态原因(completed/failed/aborted/budget_limited/time_limited)。 */
1801
+ reason: DoneReason;
1802
+ /** 脚本返回值(reason==="completed" 时有)。 */
1803
+ scriptResult?: unknown;
1804
+ /** 错误信息(reason!=="completed" 时可有)。 */
1805
+ error?: string;
1806
+ /** run 标识。 */
1807
+ runId: string;
1808
+ }
1809
+ /**
1810
+ * Launcher 依赖:LifecycleDeps + registry(脚本发现)。
1811
+ *
1812
+ * registry 是「发现依赖」(文件系统扫描),与 LifecycleDeps 的 3 个 port
1813
+ * (执行依赖:子进程/线程/持久化)性质不同——故单独扩展,不进 LifecycleDeps。
1814
+ */
1815
+ interface LauncherDeps extends LifecycleDeps {
1816
+ /** workflow 脚本仓库。 */
1817
+ registry: WorkflowScriptRegistry;
1818
+ }
1819
+ /**
1820
+ * 同步运行 workflow 至终态(跨扩展编程入口)。
1821
+ *
1822
+ * 阻塞至 run 到达 done,返回 WorkflowRunResult。用于 pi.__workflowRun 等
1823
+ * 编程式调用——非交互场景(交互用 run + lifecycle tools)。
1824
+ *
1825
+ * **超时处理(C.7)**:timeout → abortRun + 返回 reason=time_limited。
1826
+ * 旧代码返回 status:"timeout" 但 workflow 可能仍 running(资源泄漏);
1827
+ * 本实现确保 timeout 转 done,time_limited 终态。
1828
+ *
1829
+ * **signal abort**:signal.aborted → abortRun + 返回 reason=aborted。
1830
+ *
1831
+ * **脚本未找到**:返回 reason=failed(不抛错——编程调用方据 reason 判断)。
1832
+ *
1833
+ * @param name workflow 脚本名(registry.get 查找)
1834
+ * @param args 调用参数(worker 内 $ARGS 访问)
1835
+ * @param deps LauncherDeps(LifecycleDeps + registry)
1836
+ * @param signal 外部 abort signal(可选)
1837
+ * @param timeoutMs 超时上限(可选)。[预算语义对齐 + U2] 未传或 <=0 = 不限(轮询至 done /
1838
+ * abort 为止,不限时由 XYZ_SUBAGENT_RUN_WATCHDOG_MS 兜底)——旧实现默认 10 分钟会误杀长任务,
1839
+ * 且 0/负值会落成立即超时;仅显式正数才限时。
1840
+ * @param model Run 级 model override(可选)。[host-surface] 经 spec.model → workerData →
1841
+ * $MODEL → agent() fallback(RunSpec Option B 同一路径)。缺省不覆盖——宿主未显式指定
1842
+ * model 时维持脚本内 agent() 显式参数 / agent .md / 引擎默认的既有解析序。此前该入口
1843
+ * 无 model 通道,消费方(zsw CLI --model)只能丢弃该参数——行为劈叉于走 buildSpec 的
1844
+ * 异步入口。
1845
+ * @returns WorkflowRunResult(status 恒 "done")
1846
+ */
1847
+ declare function runAndWait(name: string, args: Record<string, unknown>, deps: LauncherDeps, signal?: AbortSignal, timeoutMs?: number, model?: string): Promise<WorkflowRunResult>;
1848
+ /**
1849
+ * workflow() 嵌套调用的 Engine 实现。
1850
+ *
1851
+ * Worker 脚本内调 workflow(name, args) 时,error-recovery.dispatchWorkflowCall 路由
1852
+ * 到 deps.onWorkflowCall,后者(Interface 层 makeDeps 注入)委托本函数。
1853
+ *
1854
+ * 流程(6 步):
1855
+ * 1. 循环检测——name 已在 parentWorkflowChain 中则拒绝(防 A→B→A 死循环)
1856
+ * 2. signal 继承——子 run 响应父 run abort(parentController → childController)
1857
+ * 3. registry.get + lint——失败返回 error result(不抛错,让脚本 soft-fail)
1858
+ * 4. 构建 RunSpec(共享父 Budget 引用 + parentWorkflowChain 延长)+ runWorkflow
1859
+ * 5. pollRunToResult 轮询至 done(复用 runAndWait 的轮询逻辑)
1860
+ * 6. 结果转换(budget 已通过共享引用实时同步)
1861
+ *
1862
+ * 不走 runAndWait:runAndWait 内部构建 RunSpec 不支持 parentWorkflowChain 与 budget
1863
+ * 共享引用,故直接构建 spec + runWorkflow + pollRunToResult。
1864
+ *
1865
+ * @param name 子 workflow 脚本名(registry.get 查找)
1866
+ * @param args 调用参数(子 worker 内 $ARGS 访问)
1867
+ * @param parentRun 发起嵌套调用的父 WorkflowRun(budget 共享 + 循环链源)
1868
+ * @param deps LauncherDeps(与 runAndWait 同一组依赖 + registry)
1869
+ * @returns { content, parsedOutput?, error? }——dispatchWorkflowCall 原样 postMessage 回 worker
1870
+ */
1871
+ declare function executeNestedWorkflow(name: string, args: Record<string, unknown>, parentRun: WorkflowRun, deps: LauncherDeps): Promise<{
1872
+ content: string;
1873
+ parsedOutput?: unknown;
1874
+ error?: string;
1875
+ }>;
1876
+
1877
+ /**
1878
+ * Workflow Extension — Worker Host
1879
+ *
1880
+ * WorkerHost port 的 Infra 实现。
1881
+ *
1882
+ * 职责:启动一个 Worker thread 运行 workflow 脚本,返回 WorkerHandle,
1883
+ * 并把 worker 的 message/error/exit 事件绑定到调用方注入的 WorkerHandlers。
1884
+ *
1885
+ * 层归属:Infra(D-12)。implements Engine 层的 WorkerHost port。
1886
+ *
1887
+ * 设计:
1888
+ * - WorkerHostImpl implements WorkerHost(而非散落的 free function)。
1889
+ * - 返回 WorkerHandle(封装),而非裸 Worker——onExit 传 handle 给
1890
+ * handlers.onExit(code, handle),调用方用 handle.isCurrent 做竞态防护(C.3 + G-025)。
1891
+ * - eval:true + 内联 buildWorkerScript 源码字符串(C.2:不用不存在的 bootstrap 文件)。
1892
+ * - workerData: { scriptPath, args, workspace, meta }(不含 callCache/budget——
1893
+ * 这些是 RunState 字段,由 lifecycle 在调用 start 前注入到 args 或独立处理)。
1894
+ * - temp file 清理逻辑移到 Engine lifecycle,本处不管。
1895
+ */
1896
+
1897
+ declare class WorkerHostImpl implements WorkerHost {
1898
+ /**
1899
+ * 启动一个 Worker thread 运行 workflow 脚本。
1900
+ *
1901
+ * 1. 用 buildWorkerScript(spec.scriptSource) 包装用户脚本(注入 agent/parallel/
1902
+ * pipeline/$ARGS/$BUDGET 等全局,AC-4 格式契约由 buildWorkerScript 保证)
1903
+ * 2. new Worker(code, { eval: true, workerData })(C.2 修复:eval 内联源码,
1904
+ * 不 require bootstrap 文件)
1905
+ * 3. 包装为 WorkerHandle,绑定 onMessage/onError/onExit 回调到 handlers
1906
+ * 4. onExit 传 handle 给 handlers.onExit(code, handle)(C.3 修复——调用方用
1907
+ * handle.isCurrent 做竞态防护,G-025)
1908
+ *
1909
+ * 返回的 WorkerHandle 由调用方(lifecycle)保存到 RunRuntime.worker。
1910
+ * 终止/崩溃重建(rebuild)时由 RunRuntime.release 接管。
1911
+ */
1912
+ start(spec: RunSpec, args: Record<string, unknown>, handlers: WorkerHandlers): WorkerHandle;
1913
+ }
1914
+
1915
+ /**
1916
+ * Workflow Config Loader — 统一资源发现版(ADR-031)
1917
+ *
1918
+ * 扫描逻辑委托给 shared/resource-discovery(与 agent 发现共享同一套扫描源)。
1919
+ * 本文件只保留 workflow 专属的 meta 提取(经 shared/meta-parser.ts IF1 统一 parser)+ 60s TTL 缓存。
1920
+ *
1921
+ * m2 收敛:删 extractMetaViaRegex + safeEvalObject(new Function),改调 parseResourceMeta
1922
+ * (真实 YAML 解析 @pi-meta 块注释,发现期不执行作者代码,v5 原则 6 no-eval)。
1923
+ * toCachedMeta 整对象透传(...meta),不再 {name,description,phases} 解构——消灭第 1 处重映射。
1924
+ *
1925
+ * Failed imports are marked available=false — the loader never throws.
1926
+ */
1927
+
1928
+ interface CachedWorkflowMeta extends WorkflowMeta {
1929
+ /** Absolute path to the script file */
1930
+ path: string;
1931
+ /** false when the script failed to load or has no valid meta export */
1932
+ available: boolean;
1933
+ /** Whether this is a saved (fixed) or temporary (ad-hoc) workflow */
1934
+ source: WorkflowSource;
1935
+ }
1936
+ /**
1937
+ * workflow 发现的扫描配置。每个字段显式声明一个扫描源目录。
1938
+ *
1939
+ * 生产环境用 defaultScanConfig() 构造默认值(全局 ~/.pi/agent/* 目录)。
1940
+ * 测试/隔离环境构造完整 config 指向 tmp 目录,完全不碰全局文件系统。
1941
+ */
1942
+ interface WorkflowScanConfig {
1943
+ /** 项目级脚本目录(workspaceRoot/.pi/workflows) */
1944
+ projectDir: string;
1945
+ /** user 级脚本目录(~/.pi/agent/workflows) */
1946
+ userDir: string;
1947
+ /** 临时脚本目录(workspaceRoot/.pi/workflows/.tmp) */
1948
+ tmpDir: string;
1949
+ /** npm 包扫描目录(~/.pi/agent/npm/node_modules 等) */
1950
+ npmDirs: string[];
1951
+ }
1952
+ /**
1953
+ * 从指定 config 扫描所有 workflow 脚本,按 tmp>project>npm>user 优先级
1954
+ * 去重,60s TTL 缓存(按 workspaceRoot 分桶)。
1955
+ *
1956
+ * 扫描逻辑委托给 shared/resource-discovery(与 agent 发现共享同一套扫描源)。
1957
+ *
1958
+ * Never throws. 解析失败的脚本以 available=false 返回。
1959
+ *
1960
+ * @param configOrCwd 完整 WorkflowScanConfig(隔离用)、部分字段(覆盖默认)、
1961
+ * 或省略(纯生产默认)。可选 cwd 用于推导 workspaceRoot。
1962
+ */
1963
+ declare function discoverWorkflows(configOrCwd?: Partial<WorkflowScanConfig> & {
1964
+ cwd?: string;
1965
+ }): Promise<CachedWorkflowMeta[]>;
1966
+ /**
1967
+ * Load and cache all available workflow scripts from project-level
1968
+ * (.pi/workflows/) and user-level (~/.pi/agent/workflows/) directories.
1969
+ *
1970
+ * discoverWorkflows() 的生产 preset——用全局默认目录。
1971
+ *
1972
+ * Never throws. Failed imports are returned with available=false.
1973
+ */
1974
+ declare function loadWorkflows(): Promise<CachedWorkflowMeta[]>;
1975
+ /**
1976
+ * Get a specific workflow by name.
1977
+ * Returns cached result if still valid, otherwise triggers a fresh load.
1978
+ */
1979
+ declare function getWorkflow(name: string): Promise<CachedWorkflowMeta | undefined>;
1980
+ /**
1981
+ * 按绝对路径加载单个 workflow(workflowRef 统一解析入口——S2 路径统一)。
1982
+ *
1983
+ * - ~/ 前缀展开;相对路径/非 .js 引用返回 undefined(引用唯一形态 = 绝对路径)
1984
+ * - 任意路径(不限扫描源):内置包内脚本、用户任意位置脚本均可执行
1985
+ * - meta 提取失败/文件不可读 → available=false(fail-safe,不抛)
1986
+ * - [perf] 与 getWorkflow(name) 对称走 bucket 缓存(key=绝对路径,与 stem 名不冲突),
1987
+ * 消除 workflow tool 主路径每次 run 的全文 regex + YAML.parse(mtime 判变失效)
1988
+ */
1989
+ declare function getWorkflowByPath(ref: string): Promise<CachedWorkflowMeta | undefined>;
1990
+ /**
1991
+ * Invalidate the internal meta cache.
1992
+ * The next call to loadWorkflows or getWorkflow will re-scan directories.
1993
+ */
1994
+ declare function invalidateCache(): void;
1995
+
1996
+ /**
1997
+ * Workflow Extension — WorkflowScriptRegistryImpl
1998
+ *
1999
+ * WorkflowScriptRegistry port 的 Infra 实现。
2000
+ *
2001
+ * 职责:扫描 .pi/workflows/ + ~/.pi/agent/workflows/ 目录,按 regex 提取
2002
+ * meta(不执行用户代码),按 tmp>project>user 优先级去重,60s TTL 缓存。
2003
+ *
2004
+ * 层归属:Infra(D-12)。implements Engine 层的 WorkflowScriptRegistry port。
2005
+ *
2006
+ * 设计:
2007
+ * - WorkflowScriptRegistryImpl 是 port 的实现,但底层扫描/缓存/去重仍委托
2008
+ * config-loader.ts 的 loadWorkflows/getWorkflow/invalidateCache 自由函数
2009
+ * (config-loader 是稳定 Infra 工具,registry 在其上包装为 WorkflowScript 实体)。
2010
+ * - 返回 WorkflowScript 实体(而非裸 CachedWorkflowMeta)。
2011
+ * - get(name) 精确匹配;fuzzy 匹配由 Interface 层 tool 负责。
2012
+ */
2013
+
2014
+ /**
2015
+ * 按绝对路径加载单个 workflow 脚本的自由函数工厂(U1)。
2016
+ *
2017
+ * 与 `new WorkflowScriptRegistryImpl().getPath(path)` 等价的无状态形态:barrel
2018
+ * 消费面(zsw vendor / 第三宿主)不暴露 registry 实例时的一次性加载入口。底层走
2019
+ * getWorkflowByPath 的 60s TTL 缓存与 m5 mtime 缓存层,重复调用不重复读盘。
2020
+ *
2021
+ * 返回 undefined:引用非法(相对路径 / 非 .js / 含 `..` 段——normalizeRef 拒绝)。
2022
+ * 返回 available=false 的 stub:文件不可读或 meta 提取失败(loader never throws)。
2023
+ */
2024
+ declare function loadWorkflowScriptByPath(path: string): Promise<WorkflowScript | undefined>;
2025
+ /**
2026
+ * WorkflowScriptRegistry port 的 Infra 实现。
2027
+ *
2028
+ * @param config 可选扫描配置。传入时 registry 只扫 config 声明的目录
2029
+ * (测试隔离用);省略时走生产默认(全局 ~/.pi/agent/* 目录)。
2030
+ */
2031
+ declare class WorkflowScriptRegistryImpl implements WorkflowScriptRegistry {
2032
+ private readonly config?;
2033
+ constructor(config?: WorkflowScanConfig | undefined);
2034
+ /**
2035
+ * 扫描所有 workflow 脚本(project + user + tmp),按 tmp>project>user 优先级
2036
+ * 去重,返回 WorkflowScript 实体数组(含 available=false 的解析失败项)。
2037
+ *
2038
+ * 60s TTL 缓存——同 workspace 60s 内重复调用走缓存。
2039
+ */
2040
+ loadAll(): Promise<WorkflowScript[]>;
2041
+ /**
2042
+ * 按名查单个脚本。精确匹配。
2043
+ * 返回 undefined 当 name 不存在。
2044
+ *
2045
+ * 注:fuzzy 匹配由 Interface 层 tool-workflow负责——registry 只做精确查。
2046
+ *
2047
+ * 性能注记:无 config(生产路径)时走 getWorkflow 的 60s TTL 单条缓存。
2048
+ * 有 config(测试隔离)时退化为每次 discoverWorkflows 全扫——测试场景
2049
+ * 可接受,生产路径不受影响。
2050
+ */
2051
+ get(name: string): Promise<WorkflowScript | undefined>;
2052
+ /**
2053
+ * 按绝对路径加载单个脚本(S2 路径统一)。任意路径(不限扫描源)。
2054
+ * 供 workflow tool 的 run/info(name 参数 = workflowRef)。
2055
+ */
2056
+ getPath(ref: string): Promise<WorkflowScript | undefined>;
2057
+ /** 失效缓存——下次 loadAll/get 重新扫描文件系统。 */
2058
+ invalidate(): void;
2059
+ /**
2060
+ * 把 CachedWorkflowMeta 转换为 WorkflowScript 实体。
2061
+ *
2062
+ * m2:整对象透传——m 已是 WorkflowMeta(CachedWorkflowMeta extends WorkflowMeta),
2063
+ * 直接传 meta: m,不再 {name,description,phases} 重建。消灭第 3 处重映射,
2064
+ * parameters/usage/when/notFor 一路流到 script.meta。
2065
+ *
2066
+ * sourceCode 在此 readFile 填充(FR-2:registry 是唯一读文件处)。
2067
+ * available:meta 提取失败或文件不可读时为 false。
2068
+ */
2069
+ private toScript;
2070
+ }
2071
+
2072
+ /**
2073
+ * Workflow 脚本创作管线(generate)——pi-sw tool-workflow-script.ts actionGenerate
2074
+ * 的校验管线下沉(convergence D-6 / W4)。
2075
+ *
2076
+ * 五道闸 + round-trip + tmp 写盘,闸序与 pi 现版一致:
2077
+ * 1. ESM import 拒绝(Worker 跑 CJS);'export const meta' 例外
2078
+ * 2. 非 meta 的 export 拒绝
2079
+ * 3. meta 声明必需(@pi-meta YAML 块注释或 legacy const meta,过渡期 m0)
2080
+ * 4. agent() 调用必需
2081
+ * 5. 语法检查(包 async IIFE,与 runtime 包裹形态一致)
2082
+ * 6. round-trip:@pi-meta 存在时 parseResourceMetaDetailed 校验 YAML(报行列)
2083
+ * 7. 通过全部校验 → tmp 目录写盘
2084
+ *
2085
+ * 与 pi 版的差异(均为宿主职责,不属纯函数边界):
2086
+ * - 不含 signal aborted 检查(AbortSignal 是 pi tool 契约层关注,宿主改接时自留);
2087
+ * - 返回结构化结果而非 throw——pi 只对 execute throw 置 isError:true 的契约
2088
+ * 转换由宿主(C5 改接)负责,core 保持纯函数。
2089
+ *
2090
+ * 报错文案逐字平移自 pi 现版(含 round-trip 的 line/col 信息)——pi 侧行为
2091
+ * 不变是 CA2 验收前提,任何文案改动必须同步两处。
2092
+ *
2093
+ * tmp 目录经 options.tmpDir 宿主注入(缺省 pi 布局,与 workflow-files.ts
2094
+ * 同源常量——save/delete/generate 三入口共享同一目录参数化口径)。
2095
+ *
2096
+ * 层归属:orchestration(创作闭环,与 lintScript / workflow-files 同域)。
2097
+ */
2098
+ /** generate 目录注入参数:tmp 落盘目录宿主注入(缺省 DEFAULT_WORKFLOW_TMP_DIR)。 */
2099
+ interface GenerateWorkflowScriptOptions {
2100
+ tmpDir?: string;
2101
+ }
2102
+ /**
2103
+ * 管线结果:成功 = 落盘绝对路径;失败 = 报错文案(逐字对齐 pi 现版,
2104
+ * 含 round-trip 的行列信息——供宿主转 throw 后 LLM 自纠正)。
2105
+ */
2106
+ type GenerateWorkflowScriptResult = {
2107
+ ok: true;
2108
+ path: string;
2109
+ } | {
2110
+ ok: false;
2111
+ error: string;
2112
+ };
2113
+ /**
2114
+ * 校验并落盘一个 AI 生成的 workflow 临时脚本。
2115
+ *
2116
+ * @param name 脚本名(落盘 <tmpDir>/{name}.js)
2117
+ * @param script 完整脚本源码
2118
+ * @param options 目录注入(tmpDir 缺省 pi 布局,相对 cwd resolve)
2119
+ */
2120
+ declare function generateWorkflowScript(name: string, script: string, options?: GenerateWorkflowScriptOptions): GenerateWorkflowScriptResult;
2121
+
2122
+ /**
2123
+ * Workflow 文件持久化操作(save / delete)。
2124
+ *
2125
+ * 历史:saveWorkflow 曾有两套实现——commands.ts 用 renameSync 仅 project scope,
2126
+ * WorkflowsView.ts 用 copyFileSync 支持 user scope。本次统一为 rename + 仅 project
2127
+ * scope(决策 2):tmp 文件保存后自动消失,保存位置缺省 DEFAULT_WORKFLOW_SAVED_DIR
2128
+ * (pi 布局;W4/D-6 参数化后宿主经 WorkflowDirOptions 注入自有布局)。
2129
+ *
2130
+ * 代价:TUI 失去 user scope Tab 切换(功能倒退,已接受);
2131
+ * Windows/跨设备 rename 可能失败(已知风险,接受)。
2132
+ */
2133
+ /**
2134
+ * 缺省目录(pi 生态布局)。相对路径,调用时 resolve(相对当前 cwd)——与 pi
2135
+ * 宿主现行为一致;宿主注入绝对目录(如 zsw 的自有 workflows 布局)即脱离
2136
+ * pi 目录。全文件唯一的 .pi 路径来源即这两个缺省常量。
2137
+ */
2138
+ declare const DEFAULT_WORKFLOW_TMP_DIR = ".pi/workflows/.tmp";
2139
+ declare const DEFAULT_WORKFLOW_SAVED_DIR = ".pi/workflows";
2140
+ /** 落盘目录注入参数:宿主覆盖缺省 pi 布局(两目录独立可选注入)。 */
2141
+ interface WorkflowDirOptions {
2142
+ /** 临时脚本目录(generate 产物落盘处);缺省 DEFAULT_WORKFLOW_TMP_DIR */
2143
+ tmpDir?: string;
2144
+ /** 固化脚本目录(save 目标);缺省 DEFAULT_WORKFLOW_SAVED_DIR */
2145
+ savedDir?: string;
2146
+ }
2147
+ /**
2148
+ * 保存临时 workflow:{tmpDir}/{tmpName}.js → {savedDir}/{newName||tmpName}.js
2149
+ * 用 rename(tmp 文件保存后消失)。仅 project scope。
2150
+ *
2151
+ * 直接按路径查找 tmp 文件,不调 config-loader 全扫——save 只需知道 tmp 文件
2152
+ * 的路径,不需要 meta 提取或跨目录去重。
2153
+ *
2154
+ * @param options 目录注入(缺省 pi 布局;pi 现两参调用形态行为不变,W4 向后兼容)
2155
+ * @throws 若 tmp workflow 不存在、目标已存在、或 rename 失败
2156
+ */
2157
+ declare function saveWorkflow(tmpName: string, newName?: string, options?: WorkflowDirOptions): Promise<string>;
2158
+ /**
2159
+ * 删除 workflow 脚本文件(tmp 或 saved)。
2160
+ * @param isRunning 回调,判断某 name 是否正在运行(运行中拒绝删除)
2161
+ * @param options 目录注入(缺省 pi 布局;pi 现两参调用形态行为不变,W4 向后兼容)
2162
+ * @throws 若正在运行、或文件不存在
2163
+ */
2164
+ declare function deleteWorkflow(name: string, isRunning: (name: string) => boolean, options?: WorkflowDirOptions): string;
2165
+
2166
+ /** FileRunStore 构造参数(全部可选;缺省即生产形态)。 */
2167
+ interface FileRunStoreOptions {
2168
+ /**
2169
+ * save 节流最小间隔(ms);0 = 禁用节流(每次 save 都落盘)。缺省
2170
+ * {@link DEFAULT_SAVE_MIN_INTERVAL_MS}。测试经此注入小窗口(fake timers 推进)。
2171
+ */
2172
+ saveMinIntervalMs?: number;
2173
+ }
2174
+ /**
2175
+ * RunStore port 的宿主无关文件实现(port 见 models/ports.ts)。
2176
+ *
2177
+ * - save:append-only + 节流——快照行仍全量(崩溃时旧快照仍在,loadAll 取最后
2178
+ * 一条有效行恢复到最后一致状态),但同一 running run 两次落盘有最小间隔
2179
+ * (OR-5 ⑥a:节流前每次状态变更都 append 全量快照,快照体积 O(calls) ×
2180
+ * save 次数 O(calls) = 单 run 磁盘 O(n²);节流参数与语义见 save 注释)。
2181
+ * - loadAll:扫 <dataRoot>/workflow-state/*.jsonl,每文件从尾向头取第一条形状
2182
+ * 有效的快照行;损坏行(JSON.parse 失败 / 形状校验不过 / 版本不匹配)跳过并
2183
+ * warn——单行损坏不拖垮整个 run 的恢复(与 pi 壳 kill-9 恢复同容忍度)。
2184
+ * 版本衔接(快照 codec 归 run-snapshot.ts 单源,D4):存量无 v 行按当前版本
2185
+ * 宽容读、写入恒补 v、v 不匹配跳过 + warn(三裁决明细见 parseLine 注释)。
2186
+ * - stateFilePath:纯路径计算(<dataRoot>/workflow-state/<runId>.jsonl),不建目录。
2187
+ *
2188
+ * 未 configureCore 即 save/loadAll 会抛 core_host_not_configured(dataRoot 端口
2189
+ * 语义,host-services.ts §3.4)——宿主壳必须在初始化最早期注入。
2190
+ */
2191
+ declare class FileRunStore implements RunStore {
2192
+ /** run 状态目录绝对路径(dataRoot 每次现取——宿主覆盖配置即刻生效,对齐
2193
+ * data-dir.ts「不缓存路径防测试/宿主切换读到旧值」先例)。 */
2194
+ private stateDir;
2195
+ /** save 节流最小间隔(ms),0 = 禁用。 */
2196
+ private readonly saveMinIntervalMs;
2197
+ /**
2198
+ * per-runId 上次实际落盘时刻(节流判据)。终态落盘成功即删(终态后 runId 不再
2199
+ * save);残留条目只出现在「running 中 run 消失(崩溃/宿主弃用)」场景,单条
2200
+ * ~100B 可忽略(对齐 jsonl-run-store chains「每 runId 残留 settled Promise」
2201
+ * 的取舍先例)。时间源 Date.now()(fake timers 下可推进,测试友好)。
2202
+ */
2203
+ private readonly lastSavedAt;
2204
+ constructor(opts?: FileRunStoreOptions);
2205
+ stateFilePath(runId: string): string;
2206
+ /**
2207
+ * 快照落盘(OR-5 ⑥a 节流后):
2208
+ * - 首写(该 runId 尚无落盘记录)永不节流——保证新 run 至少一条快照,
2209
+ * loadAll 重水合可发现;
2210
+ * - 终态(status 非 running)永不节流——最终状态必落盘,末行即终态快照;
2211
+ * - running 中间态距上次落盘不足 {@link saveMinIntervalMs} → 跳过本次 append
2212
+ * (状态仍在调用方内存 runs Map,下次落盘带全量最新快照;本文件最后一条
2213
+ * 快照因此最多落后真实状态一个节流窗口——崩溃语义与 jsonl-run-store 去抖
2214
+ * 同源:未落盘的 running 尾部丢失,等价崩溃链由恢复路径收编)。
2215
+ *
2216
+ * 节流判据在落盘成功后才更新(IO 失败不吞下一次重试机会)。
2217
+ */
2218
+ save(run: WorkflowRun): Promise<void>;
2219
+ loadAll(): Promise<WorkflowRun[]>;
2220
+ /** 单文件从尾向头取第一条有效快照行;整文件无有效行返回 undefined(warn)。 */
2221
+ private loadLatestValidLine;
2222
+ /**
2223
+ * 单行解析 + 版本衔接预处理(D4 裁决②③,宿主侧职责)+ 形状校验;损坏
2224
+ * warn 并返回 undefined。
2225
+ *
2226
+ * - 缺 v 字段(core 存量行)→ 就地补当前版本再进 codec(「缺版本 = 当前
2227
+ * 版本」宽容读,不做自动迁移——写回时经 toRunSnapshot 自然补 v 完成渐进
2228
+ * 收敛);预处理留在 store 层而非 codec,保 pi 侧「v1 存量静默跳过」语义
2229
+ * 不被宽容化误读(D4 裁决②归属裁决)。
2230
+ * - v 存在但不匹配(未知更高版本/降级写入)→ 跳过 + warn(补可见性,对齐
2231
+ * pi 静默跳过语义;字符串版本无大小序,不引入比较逻辑——D4 裁决③)。
2232
+ * 此处版本判断仅为 warn 可见性,数据防线仍是 codec 内 guard(双保险,
2233
+ * pi 切换 codec 后共享同一防线)。
2234
+ */
2235
+ private parseLine;
2236
+ /**
2237
+ * 把 workflow-state 目录裁剪到上限个最新 state 文件(mtime 升序删最旧,C1)。
2238
+ *
2239
+ * 语义对齐 pi jsonl-run-store.pruneStateFilesBeyondCap(逐段同构):
2240
+ * - 只删本目录内命中 {@link STATE_FILE_GLOB} 的文件;任何失败都不抛(清理是
2241
+ * 旁路维护,不能拖垮持久化主链路):readdir 失败静默放弃本轮(ENOENT =
2242
+ * 从未持久化,正常态),单个 unlink 失败(非 ENOENT)warn 留证后继续删
2243
+ * 其余——ENOENT 视为并发删除竞态下的已达成目标,不告警;
2244
+ * - stat 全集取 mtime,allSettled 部分降级——单文件 stat 失败(并发删除
2245
+ * ENOENT 等)静默跳过该文件,不阻断本轮裁剪。
2246
+ *
2247
+ * 上限解析(envName 通道,OR-5 ⑥b 默认开;显式非法值 opt-out 对齐 pi 解析风格):
2248
+ * - `envName` 提供 → env 通道:`process.env[envName]` 未设/空 → 按默认上限
2249
+ * {@link DEFAULT_STATE_MAX_RUNS} 裁剪(**默认开**——OR-5 修复前的 opt-in
2250
+ * 「默认关」正是跨 run 无界累积缺陷本身);设了有限正数 → 上限 = env 值
2251
+ * (env 值即上限);设了非法值(非有限数/≤0)→ 不清理(显式 opt-out 通道:
2252
+ * 用户意图不明时不动磁盘——对齐本方法 readdir/stat 失败一律放弃的保守哲学,
2253
+ * 宿主如需自管保留可设足够大的正数值);
2254
+ * - `envName` 缺省 → 无 env 通道,直接按 `max` 参数裁剪(上限 = max,调用方
2255
+ * 自管启用时机)。
2256
+ *
2257
+ * 本方法只做磁盘裁剪,不动内存 runs Map(内存侧淘汰归
2258
+ * lifecycle.evictDoneRunsBeyondCap,两域独立)。
2259
+ *
2260
+ * @param max 上限(envName 缺省时生效;env 通道启用时被 env 值覆盖)
2261
+ * @param envName opt-in 开关 + 上限覆盖 env 变量名(可选;pi 先例
2262
+ * `XYZ_SUBAGENT_STATE_MAX_RUNS`)
2263
+ */
2264
+ pruneStateFilesBeyondCap(max: number, envName?: string): Promise<void>;
2265
+ }
2266
+
2267
+ /** launcher 产出的进程句柄(parser 消费 stdout/stderr/exited;abort 是杀链执行体)。 */
2268
+ interface ZcodeLaunchedProcess {
2269
+ /** spawn 出的原始子进程句柄([U0 D10] 终止链记账——引擎经 RunContext.onChildSpawned 注册进宿主 spawnedChildren)。 */
2270
+ readonly child: ChildProcess;
2271
+ readonly pid: number;
2272
+ readonly stdout: Readable;
2273
+ readonly stderr: Readable;
2274
+ /** 杀链:SIGTERM → graceMs 后未退出则 SIGKILL;resolve 于进程退出。幂等。 */
2275
+ readonly abort: (graceMs?: number) => Promise<void>;
2276
+ /** 进程退出(code=null 表示被信号杀死)。 */
2277
+ readonly exited: Promise<{
2278
+ code: number | null;
2279
+ signal: string | undefined;
2280
+ }>;
2281
+ /** 本方杀链是否介入过(合成终态的判据:介入后 code 语义不再是引擎自身失败)。 */
2282
+ readonly killedByUs: () => boolean;
2283
+ }
2284
+
2285
+ /** provider 注册表条目的最小消费面(凭据 + 模型清单校验)。 */
2286
+ interface ZcodeProviderEntry {
2287
+ options?: {
2288
+ apiKey?: unknown;
2289
+ };
2290
+ models?: Record<string, unknown>;
2291
+ [k: string]: unknown;
2292
+ }
2293
+ /** [R4] 规范化全名 provider/model → create 参数的 per-session model 拆分(A.2 ① strict 对象)。 */
2294
+ declare function splitZcodeModelRef(modelRef: string): {
2295
+ providerId: string;
2296
+ modelId: string;
2297
+ };
2298
+ declare function hasApiKey(entry: ZcodeProviderEntry): boolean;
2299
+ interface ZcodeSourcePaths {
2300
+ /** 桌面登录态 config(唯一凭据源)。缺省 ~/.zcode/v2/config.json。 */
2301
+ v2ConfigPath?: string;
2302
+ }
2303
+ /**
2304
+ * 短名(无 provider 前缀)解析的默认 provider(zsub DEFAULT_PROVIDER_ID 同构)。
2305
+ * 导出(sink 设计 U1 模型切分四件之一):barrel re-export 供第三宿主模型路由消费,
2306
+ * 实现体内聚本文件不挪。
2307
+ */
2308
+ declare const DEFAULT_PROVIDER_ID = "builtin:bigmodel-coding-plan";
2309
+
2310
+ /** ZcodeEngine 构造依赖(全部可注入——测试不依赖真机 CLI/真凭据)。 */
2311
+ interface ZcodeEngineDeps {
2312
+ /**
2313
+ * 引擎数据目录(池根:<dir>/engines/zcode/<poolKey>/)。来源通道(宿主 dataDir)
2314
+ * 由并行任务/W3 解决——本引擎只消费,见 registration.ts 缺省解析。
2315
+ */
2316
+ engineDataDir: () => string;
2317
+ /** zcode CLI 路径;缺省 ZCODE_CLI_DEFAULT_PATH。 */
2318
+ cliPath?: string;
2319
+ /** 源 config 路径覆盖(测试注入临时源;缺省读 ~/.zcode)。 */
2320
+ sources?: ZcodeSourcePaths;
2321
+ /** 版本探测执行器(probe check "version";测试注入 fake 防真实子进程)。 */
2322
+ probeVersion?: (cliPath: string) => Promise<string | undefined>;
2323
+ /** spawn 执行器(测试注入 fake 进程)。 */
2324
+ launch?: (opts: {
2325
+ cliPath: string;
2326
+ args: string[];
2327
+ env: NodeJS.ProcessEnv;
2328
+ }) => ZcodeLaunchedProcess;
2329
+ /** env 基底(测试注入;缺省 process.env——模式分派与 app-server env 组装都经它)。 */
2330
+ processEnv?: NodeJS.ProcessEnv;
2331
+ /**
2332
+ * [R5 D8] 协议冒烟探针总预算(缺省 ZCODE_APPSERVER_PROBE_BUDGET_MS = 10s;测试
2333
+ * 注入短预算验证超时降级路径,不真等 10s)。
2334
+ */
2335
+ probeBudgetMs?: number;
2336
+ }
2337
+ /** zcode 引擎适配器。 */
2338
+ declare class ZcodeEngine implements EnginePort {
2339
+ readonly id = "zcode";
2340
+ private readonly deps;
2341
+ private probeCache;
2342
+ private appserverRuntime;
2343
+ private homeState;
2344
+ /** 并发任务的首次锁获取在途 promise(重入守卫,见 ensureAppServerHome)。 */
2345
+ private homeAcquireInFlight;
2346
+ /**
2347
+ * [R5 D2③] 探针结论(与 CLI 文件 mtime 绑定的内存缓存):mtime 未变不重探;
2348
+ * zcode 升级(mtime 变化)后首个任务前重探。不落盘——进程重启后重探重建。
2349
+ */
2350
+ private smokeConclusion;
2351
+ /**
2352
+ * [R5 D2②] 漂移降级标志(内存化,不落盘):首任务运行中命中 -32601/-32602 后置
2353
+ * true,本进程后续任务直走 spawn;进程重启后经探针门控重建(重探通过则恢复
2354
+ * app-server)。
2355
+ */
2356
+ private driftDegraded;
2357
+ constructor(deps: ZcodeEngineDeps);
2358
+ /**
2359
+ * zcode 链路实际接通的能力(D3 链路口径;R4 D5 升级序:eventGranularity
2360
+ * coarse→stream——链路先行〔session/event payload.delta → text_delta 实时流出,
2361
+ * turn.terminal → turn_end,收尾帧 usage → message_end.usage〕,其余能力位维持
2362
+ * 现值。声明升级必须先改链路再改声明(C4 原则)。
2363
+ */
2364
+ capabilities(): EngineCapabilities;
2365
+ /** 探针(D7):二进制存在 + 版本解析 + golden 样本干跑回归(zsub 式逆向契约引擎必做)。 */
2366
+ probe(opts?: {
2367
+ force?: boolean;
2368
+ }): Promise<ProbeReport>;
2369
+ /** check 1:二进制存在性(isFile 才算——同名目录不是可执行入口)。 */
2370
+ private probeBinaryCheck;
2371
+ /** check 2:`--version` 解析(probeVersion 可注入——测试 fake 防真实子进程)。 */
2372
+ private probeVersionCheck;
2373
+ /** check 3:golden 样本干跑(parser 对实录样本解析——stdout 格式漂移的入口拦截)。 */
2374
+ private probeGoldenCheck;
2375
+ /** 探针失败的恢复指引(§3.3.3 终态四:版本确认命令 + 探针重跑 + 调研文档路径)。 */
2376
+ private probeFailureRecovery;
2377
+ /**
2378
+ * D1 主语义 + [R5] D2 降级链四步:
2379
+ * ① 定向(XYZ_ZCODE_MODE=appserver|spawn):不探不降——定向者要的就是这条通道,
2380
+ * 失败直接上报(spawn 兜底原路径 / appserver 直连);
2381
+ * ② 缺省 + 已漂移降级(内存标志):后续任务直走 spawn(record 标注降级事实);
2382
+ * ③ 缺省 + 探针门控(结论与 CLI mtime 绑定):探针失败 → 本任务起直接 spawn;
2383
+ * ④ 缺省 + 探针通过但首任务命中漂移类 RPC 错误(-32601/-32602)→ 本任务降级
2384
+ * spawn 重跑一次(同一任务,结果标注降级)+ 后续任务直走 spawn。
2385
+ */
2386
+ run(task: AgentTaskSpec, ctx: RunContext): Promise<EngineRunResult>;
2387
+ /**
2388
+ * [R5 D8] 探针门控:CLI 文件 mtime 与结论绑定——mtime 未变命中缓存不重探;变化
2389
+ * (zcode 升级)或首次 → 独立短命连接上跑协议冒烟(appserver-probe.ts;必须用已
2390
+ * 引导的常驻 HOME——D7 教训「先 bootstrap 再 probe 否则永远误降级」)。CLI 不存在
2391
+ * 按探针失败处理(spawn 路径自身还有 binary 检查兜底)。
2392
+ */
2393
+ private appServerProbeGate;
2394
+ /**
2395
+ * 常驻路径主编排:常驻 HOME(锁/派生/孤儿回收/凭据刷新——appserver-home D7 全量)→
2396
+ * 惰性连接 + runTurn(事件时序前移:text_delta 流式、终态后 message_end/turn_end)→
2397
+ * schema 仿真重试(与 spawn 同编排)→ outcome/handle。poolKey 静态常量,
2398
+ * onPoolResolved 在 prepare 期、onHandleReady 在 create 应答后(§3.4 不变量 3)。
2399
+ *
2400
+ * [R5] 返回附带 driftCode:末轮 attempt 以漂移类 RPC 错误(-32601/-32602)收场时
2401
+ * 给出 code(run 编排降级 spawn 重跑);其余终态(成功/中止/非漂移失败)为
2402
+ * undefined——-32004/-32010/-32603 按错误规格表各自上报,不降级。
2403
+ */
2404
+ private runViaAppServer;
2405
+ /** pre-aborted 短路收口(常驻路径专用):合成中止 outcome + 池锚定 handle。 */
2406
+ private abortedAppServerRun;
2407
+ /**
2408
+ * 首轮执行 + schema 仿真重试编排(常驻路径):schema 任务校验失败时重试一次(强化
2409
+ * JSON 输出指令——与 spawn/structured-output 的重试语义对齐)。重试轮是独立会话的
2410
+ * 独立 LLM 调用:token 计入 outcome.usage 总量;事件面 text_delta 按实际流出(含
2411
+ * 失败轮——journal 记录真实流水),message_end/turn_end 只在最终轮终态后合成
2412
+ * (不变量 2/5)。
2413
+ */
2414
+ private runAppServerAttemptsWithRetry;
2415
+ /** 常驻路径的 handle 合成(poolKey = 常驻 HOME 实际目录名——锚定不变量载体)。 */
2416
+ private appServerHandle;
2417
+ /**
2418
+ * 单轮常驻执行:runTurn 组合面 + D3 abort 链 + 事件前移(text_delta 实时流出;
2419
+ * 终态数据经 read 兜底收口后才 resolve——不变量 1/2)。产出三态与 spawn 同构。
2420
+ */
2421
+ private attemptAppServerTurn;
2422
+ /**
2423
+ * D3 abort 链执行体(fire-and-forget——与 turn promise 并行推进):
2424
+ * stop 帧(超时 ZCODE_APPSERVER_STOP_TIMEOUT_MS)→ grace 窗口内 turn 落定即止
2425
+ * (不杀共享进程)→ 超时 killChain(conn.shutdown 全序:SIGTERM→grace→SIGKILL)。
2426
+ * turn 的最终落定由 attempt 主路径 await 收口,本链不直接产出终态。abort 与
2427
+ * create 竞态(signal 先到、session 未建立):等会话建立(带上限)再发 stop——
2428
+ * 否则 stop 永远发不出,直接连坐杀共享进程。
2429
+ */
2430
+ private appServerAbortChain;
2431
+ /**
2432
+ * 每任务的常驻 HOME 保障:已持有(lockfile.pid=本进程)→ 只做凭据刷新比对
2433
+ * (config 内容 hash;不一致重写 + 重建连接——在途任务走崩溃路径,换取凭据变更
2434
+ * 下一任务生效);未持有(首任务/锁被夺)→ acquireAppServerHome 全量(锁判定/
2435
+ * 派生/接管 + pidfile 孤儿回收/引导)+ 启动锁心跳。
2436
+ */
2437
+ private ensureAppServerHome;
2438
+ /**
2439
+ * 惰性获取常驻运行时(D1:每引擎实例一条连接,全任务共享)。池 key 未变直接复用
2440
+ * (连接自身的崩溃重建在 connection 层内部完成——同一条代码路径,§3.4 不变量 4);
2441
+ * 池变更(派生目录名变化)→ 旧运行时整件丢弃(shutdown fire)+ 新建。常驻进程
2442
+ **不进**宿主 spawnedChildren、不调 onChildSpawned(D6——生命周期归 dispose)。
2443
+ */
2444
+ private ensureAppServerRuntime;
2445
+ /** 丢弃当前常驻运行时(凭据刷新/池变更):shutdown fire(killChain 全序),在途任务走崩溃路径。 */
2446
+ private teardownAppServerRuntime;
2447
+ /**
2448
+ * [R5 修复 R4 既有竞态] shutdown → 等崩溃收割实际发生 → channel 退订。killChain 在
2449
+ * `exit` 事件 resolve,而连接 finalize(onClose → channel 的 failAllTurns)挂
2450
+ * `close` 事件——两者之间有一个事件循环窗口:shutdown resolve 后立即退订,在途
2451
+ * turn 会错过收割、挂到 turnTimeoutMs(300s)。退订前等 onClose 触发(本方法先于
2452
+ * shutdown 订阅;channel 的订阅在构造期更早——其 failAllTurns 先于本 promise
2453
+ * resolve 执行);ZCODE_APPSERVER_HARVEST_GRACE_MS 兜底防 `close` 永不到达时挂死。
2454
+ */
2455
+ private shutdownRuntimeAndDisposeChannel;
2456
+ /**
2457
+ * [R1 D6/R4 主体] 引擎停机面:①fire 全部在途会话的 session/close 帧(不等待
2458
+ * 应答——D6① 顺序规定:close 帧必须先于 SIGTERM,否则对面来不及处理即被杀)→
2459
+ * ②同步 SIGTERM(conn.shutdown 调用内 killChain 前缀同步执行——同步面在返回
2460
+ * Promise 前完成)→ ③grace → SIGKILL(异步面,Promise resolve 于进程退出)。
2461
+ * 幂等:运行时字段取走即置空,二次调用零副作用;dispose 后首个 run 经
2462
+ * ensureAppServerRuntime 自动重建(与崩溃重建同一代码路径,不变量 4)。
2463
+ * 锁不释放(随宿主进程存活——活宿主持有语义;进程死锁自然无主可接管)。
2464
+ */
2465
+ dispose(): Promise<void>;
2466
+ /**
2467
+ * spawn 路径主编排(原 run 主体,行为零改动):preparer → launcher → parser → 仿真重试 → outcome/handle。
2468
+ * [R5] degrade 参数:降级链落点(探针失败 / 漂移首败重跑 / 降级后直走)——结果经
2469
+ * outcome.engineFallback 标注「degraded: spawn + 原因」(D9① 留痕面复用,record
2470
+ * 同步投影;capabilities 声明不降级——D2 降级是任务级兜底非能力级)。
2471
+ * [RX2-F3] degrade.skipCtxModelWarn(内部标志):漂移首败重跑场景置 true——该任务
2472
+ * 的 appserver 首跑已输出过 ctxModel 忽略留痕,spawn 重跑侧跳过防同 taskId 双份
2473
+ * 相同 warn;warnEffortUnsupportedBySpawn 不受此标志影响(降级重跑时最终结果出自
2474
+ * spawn,其出声合理,保持现状)。探针失败/降级直走两个落点不置位——任务此前未走
2475
+ * 过 appserver,spawn 侧的 warn 是首次出声。
2476
+ */
2477
+ private runViaSpawn;
2478
+ /**
2479
+ * 首轮执行 + schema 仿真重试编排(spawn 路径):schema 任务校验失败时重试一次(强化
2480
+ * JSON 输出指令——与 structured-output 的重试语义对齐,设计 §3.3.3
2481
+ * schema_emulation_failed 行)。重试轮产生的新 session 是独立 LLM 调用:token 计入
2482
+ * outcome.usage 总量,事件只在最终轮终态后一次性合成(不变量 5:事件 emit 完成先于
2483
+ * run resolve)。
2484
+ */
2485
+ private runSpawnAttemptsWithRetry;
2486
+ /** spawn 路径的 handle 合成(poolKey = 隔离池目录名;探针版本可留痕)。 */
2487
+ private buildSpawnEngineHandle;
2488
+ /** 终态合成(extension-conventions 函数 80 行上限,从 run 提取):aborted / run-failed / parsed 三分支。 */
2489
+ private finalizeOutcome;
2490
+ /** abort 合成终态:exitCode=null(record 正常收尾,不留僵尸)。 */
2491
+ private applyAbortedOutcome;
2492
+ /**
2493
+ * run-failed 合成终态:错误信息由 buildRunFailedMessage 产出(已含恢复指引)直接透传;
2494
+ * appserver 路径附带的会话 id 落 outcome.sessionId(错误规格表 -32004 行「含会话 id」
2495
+ * ——appServerHandle 据此写 handle.sessionRef,run-failed 不再恒缺)。
2496
+ */
2497
+ private applyRunFailedOutcome;
2498
+ /** parsed 合成终态:content/sessionId/usage 落位 + schema 校验分流 + coarse 事件。 */
2499
+ private applyParsedOutcome;
2500
+ /**
2501
+ * 单轮执行(launch → collect → parse → schema 校验)。产出三态之一给 run 编排:
2502
+ * aborted(我方杀链)/ run-failed(非零退出或解析失败)/ parsed(含 schema 校验结果)。
2503
+ */
2504
+ private attemptOnce;
2505
+ /**
2506
+ * D1 可选面:zcode 首期不支持 conversation(capabilities 声明)——同步拒绝、
2507
+ * 不创建进程,文案给可操作建议(A11)。
2508
+ */
2509
+ interact(_handle: EngineHandle, _action: InteractAction): Promise<InteractResult>;
2510
+ /**
2511
+ * D6 read 三级降级:①sqlite 原生读取 → ②宿主 event journal 重放(对齐点①接线:
2512
+ * replayJournalToSessionView 复用 live reducer,重放等价性见 §3.3.6)→ ③outcome-only。
2513
+ * sessionId 缺失(解析失败的 run 无法在共享池 db 内定位 session)跳过①级;②级
2514
+ * 依赖 handle.journalPath(宿主 run 后回填)。
2515
+ */
2516
+ /** [U7] 模型可发现性:v2 桌面登录态聚合(带凭据 provider × models),失败安全返回清单本身可能为空。 */
2517
+ listModels(): Array<{
2518
+ id: string;
2519
+ name?: string;
2520
+ }>;
2521
+ read(handle: EngineHandle): Promise<SessionView>;
2522
+ /**
2523
+ * prepare 期的能力拒绝(进程创建前):fork 是 pi 专属(AgentTaskSpec.fork 契约:
2524
+ * 其他引擎按 capabilities 拒绝);conversation 是 interact 控制面的 task 标志,
2525
+ * zcode 无此面(A11:同步拒绝 + 可操作建议,无进程创建);maxTurns 是 pi 引擎
2526
+ * 专属(turn limiter + spawn watchdog 估算依赖 pi 的 turn_end 事件流)——zcode
2527
+ * 无 turn_end 语义,静默丢弃会造成「传了上限却失控」的假象,显式拒绝(U4,
2528
+ * 同 fork 模式)。
2529
+ */
2530
+ private rejectUnsupportedTaskShapes;
2531
+ /**
2532
+ * [F15b] spawn 路径的 effort 丢弃信号:spawn CLI 无 thoughtLevel 类 flag(协议
2533
+ * 通道是 appserver 路径专属),effort 只能丢弃——但静默丢弃会让调用方误以为推理
2534
+ * 档位已生效,故出声留痕(引擎现成信号风格:logger.warn,同漂移降级先例)。
2535
+ * 诊断语义:effort 是可忽略档位(降档不改变任务正确性),warn 留痕而非硬拒绝
2536
+ * (与 maxTurns「传了上限却失控」的假象不同质性)。
2537
+ */
2538
+ private warnEffortUnsupportedBySpawn;
2539
+ /**
2540
+ * [RX2-F1] appserver 路径的非常见档位提示:effort → thoughtLevel 恒等透传(F15a),
2541
+ * 全 7 档放行不拦截——但部分档位(off/minimal/medium/xhigh 等)不在部分模型的合法
2542
+ * 值域内(如 GLM-5.3 仅接受 low/high/max),app-server 侧对不支持的档位 warn-skip
2543
+ * (会话照常但档位静默失效),调用方无从察觉。此处仅对 COMMON_THOUGHT_LEVELS 之外
2544
+ * 的档位出声一行提示(措辞是「若不支持将被忽略/回落」的或然警告,非无效断言);
2545
+ * 是否真不支持由目标模型决定,core 不做权威校验(引擎层不掌握各模型值域)。
2546
+ */
2547
+ private warnThoughtLevelUncommon;
2548
+ /**
2549
+ * [F16b] ctxModel 忽略留痕:ctxModel 是 pi 链路的第三层兜底(port.ts 契约——
2550
+ * 依赖 pi resolveModel 链的引擎才消费它),zcode 自带 provider 体系与缺省模型
2551
+ * (resolveZcodeModelRef:requested > ZCODE_FALLBACK_DEFAULT_MODEL),不消费
2552
+ * ctxModel。「调用方给了 ctxModel 但 task.model 未显式指定」时出声一行,说明
2553
+ * 实际落引擎缺省模型(含实际 model id)——防静默降档无据可查。只在「ctx 有模型
2554
+ * 但被忽略」场景输出:显式 task.model 走正常解析链、ctx 本就无模型属预期缺省,
2555
+ * 均不出声(避免噪音)。探针期(appServerProbeGate)不调用——同一任务的正式
2556
+ * run 链路必经此处,双份输出是噪音。[RX2-F3] 漂移首败的 spawn 重跑同理由调用方
2557
+ * 带 degrade.skipCtxModelWarn 跳过——appserver 首跑已出声过,同任务双份相同 warn
2558
+ * 是噪音(与探针场景同一自我要求)。
2559
+ */
2560
+ private warnIgnoredCtxModel;
2561
+ /**
2562
+ * persona 拼接后的完整 prompt(personaInjection: 'prompt'——zcode 无 flag 通道):
2563
+ * persona 段经 common/persona-router.applyPersona 按 capabilities 路由产出
2564
+ * (agentRef/skillPath 引用行 + appendSystemPrompt 正文统一拼装,S5 接线——替换
2565
+ * 原手拼 appendSystemPrompt 段,skillPath/agentRef 不再丢弃),task 正文居中,
2566
+ * schema 仿真段尾置(common/schema-emulation 公共层产出,D4 emulated 侧——zcode
2567
+ * 无 native schema 通道)。
2568
+ */
2569
+ private buildPrompt;
2570
+ }
2571
+
2572
+ /** 构造 ZcodeEngine(DI 工厂——测试/宿主注入 deps)。 */
2573
+ declare function createZcodeEngine(deps: ZcodeEngineDeps): ZcodeEngine;
2574
+ /**
2575
+ * 把 'zcode' 引擎登记进 registry(幂等——组合根可能多次执行,registerEngine 覆盖
2576
+ * 语义)。工厂惰性:登记不触发任何文件/进程探测,首次 getEngine 才建实例。
2577
+ */
2578
+ declare function registerZcodeEngine(engineDataDir?: () => string): void;
2579
+
2580
+ /** Pi extension_ui_request 的方法枚举(dialog + fire-and-forget 两类)。
2581
+ * dialog 类:select/confirm/input/editor(占输入焦点,等响应)。
2582
+ * fire-and-forget 类:notify/setStatus/setWidget/setTitle/set_editor_text(纯展示/写入)。
2583
+ * (string & {}) 兜底:Pi 未来新增 method 或未知 method 走字符串字面量类型。 */
2584
+ type UiMethod = "select" | "confirm" | "input" | "editor" | "notify" | "setStatus" | "setWidget" | "setTitle" | "set_editor_text" | (string & {});
2585
+ /** UI 请求(session-runner 构造后传给 handler)。
2586
+ *
2587
+ * method 是判别字段,决定排队策略(dialog 排队)和业务路由(channel 分发)。
2588
+ * method 特定字段按 method 可选出现(与 ExtensionUiRequest 1:1,由 session-runner 从
2589
+ * ExtensionUiRequest 平铺构造)。channel/channelPayload 由 parseChannel 填充。
2590
+ *
2591
+ * 契约来源:.fix-plans/00-master-summary.md §二 2.2。 */
2592
+ interface UiRequest {
2593
+ /** Pi rpc-types.ts 的 method(select/confirm/input/editor 为 dialog 类)。 */
2594
+ method: UiMethod;
2595
+ /** 请求 id(从 extension_ui_request envelope 顶层提取,用于 response 关联)。 */
2596
+ id: string;
2597
+ title?: string;
2598
+ options?: string[];
2599
+ message?: string;
2600
+ placeholder?: string;
2601
+ prefill?: string;
2602
+ notifyType?: string;
2603
+ statusKey?: string;
2604
+ statusText?: string | undefined;
2605
+ widgetKey?: string;
2606
+ widgetLines?: string[] | undefined;
2607
+ widgetPlacement?: "aboveEditor" | "belowEditor";
2608
+ text?: string;
2609
+ timeout?: number;
2610
+ /** channel 名(从 method 对应字段的 NUL 前缀解析)。
2611
+ * select → 从 title 解析;setWidget → 从 widgetLines[0] 解析;其他 → undefined。
2612
+ * 已知值:"ask_user"(select)、"gui_widget"(setWidget)。handler 按 channel 分发。 */
2613
+ channel?: string;
2614
+ /** channel 解析后的结构化 payload(已 JSON.parse)。
2615
+ * ask_user: {questions, allowCancel};gui_widget: {component};无 channel: undefined。 */
2616
+ channelPayload?: unknown;
2617
+ /** 内部元数据字段:发起该 UI 请求的子进程 pid(由 session-runner.handleUiRequest 从
2618
+ * child.pid 填入)。L2 队列据此关联 rejectChildDialogs(child close 时批量 reject)。
2619
+ * 下划线前缀表示内部字段,非 Pi 协议字段,不参与 stdin 回写。 */
2620
+ _childPid?: number;
2621
+ }
2622
+ /** UI 响应(handler 返回,session-runner 按 shape 回写 stdin)。
2623
+ * - {value}: select/input/editor 的答案
2624
+ * - {confirmed}: confirm 的答案
2625
+ * - {cancelled}: 取消(child close / handler 抛错 / 用户取消)
2626
+ * - {ack}: fire-and-forget(当前不透传到 TUI,留作协议完整) */
2627
+ type UiResponse = {
2628
+ value: string;
2629
+ } | {
2630
+ confirmed: boolean;
2631
+ } | {
2632
+ cancelled: true;
2633
+ } | {
2634
+ ack: true;
2635
+ };
2636
+ /** UI 请求 handler 签名(单函数,按 req.method 内部路由)。
2637
+ * 实现方负责:channel 业务路由(ask_user → AskUserComponent)+ 默认转发(ctx.ui.*)。
2638
+ * 抛错由调用方(DialogGlobalQueue / session-runner)兜底为 {cancelled:true}。 */
2639
+ type UiRequestHandler = (req: UiRequest) => Promise<UiResponse>;
2640
+ /** 入队项的 child 引用形状(只取 pid 用于 rejectChildDialogs 匹配)。 */
2641
+ interface DialogChildRef {
2642
+ pid: number;
2643
+ }
2644
+ /** enqueue 的可选项。child 用于 rejectChildDialogs 关联(child close 时批量 reject)。 */
2645
+ interface EnqueueOptions {
2646
+ child?: DialogChildRef;
2647
+ }
2648
+ /**
2649
+ * L2 跨子进程全局 dialog 串行队列(进程单例)。
2650
+ *
2651
+ * 用法(createUiRequestHandlerForMode 返回的总 handler 内):
2652
+ * ```ts
2653
+ * const dialogQueue = new DialogGlobalQueue();
2654
+ * return async (req: UiRequest) => {
2655
+ * // 调用方负责判断:dialog 入队,fire-and-forget 直接调 realHandler
2656
+ * if (isDialogMethod(req.method)) return dialogQueue.enqueue(req, realHandler);
2657
+ * return realHandler(req);
2658
+ * };
2659
+ * ```
2660
+ *
2661
+ * 语义保证:
2662
+ * - FIFO 串行:前一个 handler settle 后才处理下一个
2663
+ * - SR-4:rejectChildDialogs(child) 把该 child 的 pending 全部 resolve 为 {cancelled:true}
2664
+ * - handler 抛错兜底:catch → {cancelled:true} → 继续下一个(队列不卡死)
2665
+ * - 超时上界(LC-3/T2⑦):每个 dialog 项必有上界——req.timeout 显式传值优先,
2666
+ * 未传挂 DEFAULT_DIALOG_TIMEOUT_MS(30min,裁决值);到点 settle {cancelled:true}
2667
+ * 并 warn(恢复指引见 dialogTimeoutLogMessage),L2 processing 释放、队列继续推进。
2668
+ * 「等用户无限久」改为默认有界是有意的行为变更。
2669
+ * - 调用方约定只对 dialog 类调 enqueue;fire-and-forget 由调用方直接调 handler 不入队
2670
+ *(enqueue 内仍防御性兼容 fire-and-forget,但不保证行为)
2671
+ *
2672
+ * 线程模型:纯 Promise + 微任务驱动,无锁。Node 单线程 event loop 保证队列状态一致。
2673
+ *
2674
+ * 单 session 假设(M-2,与 index.ts lastSessionId 同源):本队列是进程级单例(实例挂在
2675
+ * globalThis[Symbol.for("@zhushanwen/pi-subagents.dialogQueue")],见 getOrCreateDialogQueue)。
2676
+ * rejectAll() 清空所有 pending dialog——无 per-session 隔离。Pi 当前架构保证单进程
2677
+ * 单 session 串行(同进程不会并发多个 session),故 session_shutdown 调 rejectAll() 只会清掉
2678
+ * 当前 session 的 pending。若未来 Pi 支持同进程多 session 并发,session A 退出会误清 session B
2679
+ * 的 pending dialog——届时需改为 per-session 隔离(入队项 QueueItem 带 sessionId,rejectAll
2680
+ * 改 rejectAllForSession(sessionId),session_shutdown 只清当前 session)。
2681
+ */
2682
+ declare class DialogGlobalQueue {
2683
+ /** 等待处理的队列(FIFO)。正在处理的项从 queue shift 出后由 current 持有。 */
2684
+ private queue;
2685
+ /** 正在处理的项(handler 已调、未 settle)。用于 rejectChildDialogs 取消占位中的 dialog。 */
2686
+ private current;
2687
+ private processing;
2688
+ /**
2689
+ * 入队一个 UI 请求,返回 Promise<UiResponse>。
2690
+ *
2691
+ * 调用方约定:只对 dialog 类(isDialogMethod===true)调 enqueue。fire-and-forget 由
2692
+ * 调用方(ui-request-handler-factory.ts)在 enqueue 前判 isDialogMethod 后直接调 handler,
2693
+ * 不经过本队列。enqueue 内仍防御性兼容 fire-and-forget(万一调用方未判):直接调 handler 返回,
2694
+ * 不入队串行,但调用方不应依赖此防御行为。
2695
+ *
2696
+ * dialog 项处理(TC-E4 case 1):进队列 FIFO 串行,等前一个 settle 后才调 handler
2697
+ *(争输入焦点,防并发弹窗)。
2698
+ *
2699
+ * handler 抛错兜底:catch → 回 {cancelled:true}(dialog 路径,队列不卡死)。
2700
+ * SR-4:opts.child 用于 rejectChildDialogs 关联(dialog 项会被批量 reject,含 current)。
2701
+ *
2702
+ * @param req UI 请求(约定只传 dialog 类;fire-and-forget 防御性兼容)
2703
+ * @param handler 真正执行请求的 handler(TUI/GUI 模式分流后的 realHandler)
2704
+ * @param opts 可选 child 引用(用于 rejectChildDialogs 关联)
2705
+ * @returns handler 的响应;dialog 抛错时回 {cancelled:true};child close 时回 {cancelled:true}
2706
+ */
2707
+ enqueue(req: UiRequest, handler: UiRequestHandler, opts?: EnqueueOptions): Promise<UiResponse>;
2708
+ /**
2709
+ * settle 一个 item(幂等)。handler 完成 / rejectChildDialogs / rejectAll / 超时 timer
2710
+ * 都通过本方法,settled 标志保证只 settle 一次(防竞争)。
2711
+ *
2712
+ * #19 单一推进点:本方法 settle Promise + 清状态后,**唯一**调 processNext 推进队列。
2713
+ * processNext 尾部不再调 processNext(旧代码双重推进,虽靠 processing 标志幂等,但语义混乱)。
2714
+ * 为什么推进必须在 settleItem 而非 processNext 尾部:rejectChildDialogs 取消一个永不 settle
2715
+ * 的 current(handler 等用户输入卡死)时,processNext 的 `await item.handler` 永不 resume,
2716
+ * 尾部不会执行;只有 settleItem 里的 processNext 才能打破死锁,推进下一个。
2717
+ *
2718
+ * [A2-2] 超时 timer 清理也统一收口在这里:任何路径抢先 settle(reject/rejectAll/超时
2719
+ * 自身)都必须撤下 armed timer——handler 永挂时 processNext 内联的 clearTimeout 不可达,
2720
+ * 不在这里清则 timer 到期触发虚假「dialog timed out」warn 且句柄滞留至超时点。
2721
+ */
2722
+ private settleItem;
2723
+ /**
2724
+ * SR-4:把指定 child 的所有 pending dialog resolve 为 {cancelled:true}。
2725
+ *
2726
+ * 触发场景:子进程 close(用户取消 / crash / 超时 kill)时,其 pending dialog 的 handler
2727
+ * 可能永不 settle(等用户输入),导致 Promise 永挂 + 内存泄漏。本方法批量清理。
2728
+ *
2729
+ * 处理范围(TC-E4 case 2):
2730
+ * - 正在处理中(current)的该 child 项:settle {cancelled:true},解阻塞队列推进下一个
2731
+ * (关键:handler 可能永不 settle,必须由这里打破死锁)
2732
+ * - 队列中等待处理的该 child 项:settle {cancelled:true} 并移除
2733
+ *
2734
+ * 不影响其他 child 的 pending dialog(TC-E4 case 2 子测试 2)。
2735
+ */
2736
+ rejectChildDialogs(child: DialogChildRef): void;
2737
+ /**
2738
+ * 处理队列下一项(FIFO)。
2739
+ *
2740
+ * processing 标志保证串行:handler 运行期间 processing=true,新的 processNext 调用直接返回;
2741
+ * handler settle 后由 settleItem 清 processing=false 并推进下一项(#19 单一推进点)。
2742
+ *
2743
+ * handler 抛错兜底(TC-E4 case 3):catch → settle {cancelled:true} → 继续。
2744
+ * 不能让一个失败卡死队列(processing 永远 true)。
2745
+ *
2746
+ * LC-3/T2⑦ 超时上界:`await item.handler` 原本无上界——host UI promise 挂死或用户
2747
+ * 永不回答时 processing 恒 true(全局 dialog 死锁)。现在每项挂队列级 timer:
2748
+ * req.timeout(请求方显式传值)优先,未传/非法挂 DEFAULT_DIALOG_TIMEOUT_MS。
2749
+ * 到点 settle {cancelled:true}(完整错误消息落父进程日志,含恢复指引与等待时长),
2750
+ * settleItem 的 settled 标志保证与 handler 完成 / rejectChildDialogs 三方竞态下
2751
+ * 恰 settle 一次。timer 回调闭包捕获 item(非读 this.current):迟到触发时
2752
+ * settled 标志已置位,直接 noop,不误伤后继项。
2753
+ */
2754
+ private processNext;
2755
+ /**
2756
+ * #10:把所有 pending dialog(queue + current)全部 settle 为 {cancelled:true},
2757
+ * 并清空 queue/current/processing 状态。session_shutdown 调用,保证不留永挂 Promise。
2758
+ *
2759
+ * 约定签名:rejectAll(): void(无参,返 void)。Group C 的 index.ts session_shutdown 依赖此契约。
2760
+ *
2761
+ * 幂等:依赖 settleItem 的 settled 标志——重复调用只会对已 settled 项 noop。
2762
+ * 顺序敏感(#19 推进点在 settleItem):必须先清空 queue 数组再 settle current,
2763
+ * 否则 settleItem(current) 同步触发的 processNext 会从旧 queue 抢占下一项作为新 current,
2764
+ * 避开本方法的 cancel 语义。清空后 processNext 看到空队列直接返回,新 current 不会被抢占。
2765
+ *
2766
+ * 单 session 假设(M-2):见类注释。本方法清空所有 pending 不分 session——依赖 Pi 单进程
2767
+ * 单 session 串行保证。session_shutdown handler(index.ts)调用本方法时,进程内只会有当前
2768
+ * session 的 pending dialog。多 session 并发场景的迁移策略(rejectAllForSession)见类注释。
2769
+ */
2770
+ rejectAll(): void;
2771
+ /** 清空队列状态(仅在 rejectAll 之后调用)。pending Promise 必须先由 rejectAll settle。
2772
+ * 不 settle Promise 的纯状态重置——单独调用会导致 Promise 永挂(footgun),故设为 private。
2773
+ * 外部调用方应使用 rejectAll()(它 settle 所有 pending + 重置状态,是原子操作)。 */
2774
+ private resetState;
2775
+ /** 当前队列长度(测试/诊断用)。含等待处理项(不含 current)。 */
2776
+ get size(): number;
2777
+ }
2778
+
2779
+ /**
2780
+ * [M-1] maxTurns → watchdog 毫秒换算(纯函数,可导出复用)。
2781
+ *
2782
+ * **换算语义(floor 文档化)**:`max(30min, maxTurns × 5min)`——按 maxTurns 线性估算,
2783
+ * 带 30 分钟下限(floor)。maxTurns 换算结果低于 30 分钟时(含 ≤6 的整数与小数,
2784
+ * 如 0.5)一律钳到 30 分钟:单 turn 约 5 分钟是经验值(复杂 tool + 长 LLM 响应约
2785
+ * 3-4 分钟,留 1-2 分钟余量),maxTurns 过小时不设 floor 会把 watchdog 紧到误杀。
2786
+ * - maxTurns=2 → 30min(floor 生效,非 2×5=10min——zsw 曾自实现无 floor 版本致该
2787
+ * 配置被 10min 误杀,见 sink 设计 §2.1 例 1;两宿主统一消费本函数即同语义)
2788
+ * - maxTurns=6 → 30min(恰为 floor 临界)
2789
+ * - maxTurns=20 → 100 分钟
2790
+ * - maxTurns=100 → 500 分钟(8 小时+,覆盖全量重构)
2791
+ *
2792
+ * 旧实现固定 30 分钟(SPAWN_WATCHDOG_MS),与 maxTurns 无关:maxTurns=100 的长任务
2793
+ * (全量重构/大规模迁移)正常需数小时,30 分钟到达即被误杀,limiter 机制形同虚设。
2794
+ *
2795
+ * [预算语义对齐 2026-08] maxTurns 未传/<=0 → 不挂 watchdog(不限)的挂载判定**不归本
2796
+ * 函数**——本函数只做换算,挂载判定单一入口是 resolveSpawnWatchdogMs(未传时走
2797
+ * SPAWN_WATCHDOG_ENV 兑底,显式 <=0 = 显式不限压过 env)。用户须知风险:watchdog 防
2798
+ * 的是 pi 子进程 hang 泄漏(卡死在单个 tool 内 turn_end 永不触发,limiter 失效),
2799
+ * 默认关闭意味着无 maxTurns 的 spawn 若 hang 将永不自动回收——须用 SPAWN_WATCHDOG_ENV
2800
+ * 显式兑底。
2801
+ *
2802
+ * [MF-4] 同时是 agent_end keep-alive 的「有活跃后代」等待超时(不 kill 分支),
2803
+ * 替代旧固定 2h(WAIT_DESCENDANT_TIMEOUT_MS,已删除)——wave 开发 >2h 不被误杀。
2804
+ * [export] 测试可观测(run-spawn-edges MF-4 用例断言 keep-alive 等待超时 = 动态值;
2805
+ * max-turns-to-watchdog-ms.test.ts 锚定 floor/边界换算)。
2806
+ *
2807
+ * @param maxTurns 调用方指定的 turn 上限;调用方保证 > 0(否则走 resolveSpawnWatchdogMs)
2808
+ */
2809
+ declare function maxTurnsToWatchdogMs(maxTurns: number): number;
2810
+ /**
2811
+ * kill 所有未退出的 spawned 子进程(dispose 兜底用)。
2812
+ *
2813
+ * [R1 D6③] 编排扩容:先触发 engine registry 各已实例化引擎的 dispose(常驻资源
2814
+ * 归引擎所有,见 EnginePort.dispose / registry.disposeEngines),再杀 per-record
2815
+ * children——顺序不可反(D6①:SIGTERM 先发会导致引擎侧 close 帧必丢)。dispose
2816
+ * 触发不等待:本函数保持同步契约(宿主调用点零改动,函数签名与导出名不变),
2817
+ * 引擎 dispose 的同步面(fire close 帧 + 同步 SIGTERM)由引擎实现保证,异步
2818
+ * promise 段(grace→SIGKILL)的 rejection 由 registry 侧 catch 吞掉,防
2819
+ * unhandledRejection 崩宿主。
2820
+ *
2821
+ * [R1 D6 注释契约] spawnedChildren Map 是 per-record 一次性 spawn 模态(一任务一
2822
+ * 进程,key=record.id);引擎持有的常驻进程(跨任务共享)**不进本 Map**——其生命
2823
+ * 周期完全归引擎 dispose 管理(边界声明见 RunContext.onChildSpawned)。常驻进程的
2824
+ * 注册/回收问题在引擎层解决(R4),此处只立 Map 模态契约。
2825
+ *
2826
+ * 遍历 spawnedChildren Map 的 values(),对每个「未确认死亡」的子进程发信号。
2827
+ * 已退出的子进程在 close/error 事件时已从 Map 移除(按句守卫 removeChildRegistration——
2828
+ * Map 当前值仍是该 child 才删,防误删 resume spawn 的新注册),故 Map 中只剩「活着的」
2829
+ * 或「已被 kill 但 close 事件尚未回调的」。
2830
+ *
2831
+ * [T2-⑤ / LC-2] 死亡判定按 exitCode/signalCode 而非 killed 标记——killed=true 只表示
2832
+ * 「发过 kill 请求」,不等于「已死」:SIGTERM 可能被无视(卡死在不可中断 native 调用),
2833
+ * 旧实现按 killed 跳过会让这类进程脱离最后一次回收窗口。现规则:
2834
+ * - 已确认死亡(exitCode/signalCode 任一非 null)→ 跳过(无论 killed 与否);
2835
+ * - killed 但未确认死亡(SIGTERM 已发、进程仍在)→ 直接升级 SIGKILL(dispose 是
2836
+ * 最后兜底,没有 30s 升级窗口可等——killAllSpawnedChildren 保持快速返回契约);
2837
+ * - 未 killed 且未确认死亡 → 发调用方指定 signal。
2838
+ *
2839
+ * 用于 SubagentService.dispose(进程退出路径):覆盖 sync 子进程(controller 为 undefined,
2840
+ * abortRunningControllers 跳过它们)。background 子进程此时已被 abortRunningControllers 经
2841
+ * controller.abort 路径 kill,本函数对它们的再处理是 SIGKILL 升级检查(T2-⑤ 语义),
2842
+ * 对已死句柄 child.kill 返回 false 无害。
2843
+ *
2844
+ * 不 await 子进程退出(dispose 要快速返回)。
2845
+ *
2846
+ * @returns 被 kill 的子进程数(诊断用)
2847
+ */
2848
+ declare function killAllSpawnedChildren(signal?: NodeJS.Signals): number;
2849
+
2850
+ /**
2851
+ * 归一化资源引用:~ 展开 + 绝对路径校验 + `..` 段拒绝(⛔2 安全收紧)。
2852
+ *
2853
+ * @param ref 原始引用(注入段 location / 工具参数值)
2854
+ * @param ext 期望扩展名(如 ".md" / ".js"),不匹配返回 null
2855
+ * @returns 归一化绝对路径;非法(空/相对路径/含 `..` 段/扩展名不符)返回 null
2856
+ */
2857
+ declare function normalizeRef(ref: string, ext?: string): string | null;
2858
+ /** invalidAgentRefMessage 的可选注入:宿主的清单段名(缺省 pi 的注入段)。 */
2859
+ interface InvalidAgentRefMessageOptions {
2860
+ /**
2861
+ * 清单指引:宿主注入段名。缺省 `<available_subagents>`(pi 注入段);第三宿主
2862
+ * 注入段名不同(zsw `<available_agents>` 等)时注入替代,文案口径不变。
2863
+ */
2864
+ howToList?: string;
2865
+ }
2866
+ /**
2867
+ * agent ref 非法的统一报错文案(消费方唯一文案口径——不各自拼 message)。
2868
+ *
2869
+ * 基准 = AgentRegistry.loadByPath(require) 的既有 throw 文案
2870
+ * ("Invalid agent ref: ... absolute paths to .md files ..."),工厂收敛后该消费点
2871
+ * 与后续宿主接线点共用此处。`..` 形态附纠正指引(⛔2 新失败路径——错误必须指向
2872
+ * 恢复动作:去掉 `..` 段、改用注入段 location)。
2873
+ */
2874
+ declare function invalidAgentRefMessage(ref: string, opts?: InvalidAgentRefMessageOptions): string;
2875
+ /** normalizeWorkflowRef 的可选注入:宿主已知的 workflow 名清单。 */
2876
+ interface NormalizeWorkflowRefOptions {
2877
+ /**
2878
+ * 宿主注入的已知 workflow 名清单(内置 + 用户级,宿主按自身优先级合并去重——
2879
+ * pi 为 config-loader 的 tmp>project>npm>user 去重产物,内置名优先已由该清单
2880
+ * 体现)。省略 = 无已知名,全部裸名按 unknown_name 拒绝。
2881
+ */
2882
+ knownNames?: Iterable<string>;
2883
+ }
2884
+ /** workflow ref 名分支的保留字:`.` / `..` 是文件系统相对路径段保留语义,任何域不可作名引用。 */
2885
+ declare const WORKFLOW_REF_RESERVED_NAMES: readonly string[];
2886
+ /** normalizeWorkflowRef 拒绝原因(结构化裁决——文案留宿主,与 knownNames 宿主注入哲学一致)。 */
2887
+ type WorkflowRefInvalidReason = "empty" | "reserved" | "unknown_name" | "not_absolute" | "parent_segment" | "bad_ext";
2888
+ /**
2889
+ * normalizeWorkflowRef 的三分裁决结果:
2890
+ * - name → 裸名命中 knownNames,按名引用(内置名优先已由宿主清单体现)
2891
+ * - path → 路径形态经 normalizeRef 全套校验(~/ 展开、绝对路径、.js、`..` 拒绝)
2892
+ * - invalid → 拒绝(reason 给精确裁决原因,消费方出恢复指引)
2893
+ */
2894
+ type NormalizedWorkflowRef = {
2895
+ kind: "name";
2896
+ name: string;
2897
+ } | {
2898
+ kind: "path";
2899
+ path: string;
2900
+ } | {
2901
+ kind: "invalid";
2902
+ ref: string;
2903
+ reason: WorkflowRefInvalidReason;
2904
+ };
2905
+ /**
2906
+ * workflow 引用统一原语(名/路径二分 + 保留字裁决 + 内置名优先,三层口径单源化——
2907
+ * pi「收裸名+路径」、zsw「入口拒裸名/内层宽松」的统一替代)。
2908
+ *
2909
+ * 二分判据:含 `/`、`\` 分隔符或 `~` 前缀 → 路径分支;否则 → 裸名分支。对齐 pi
2910
+ * 现行为「名字是简单标识符,路径串不会撞 workflow 名」(tool-workflow actionRun:
2911
+ * get(name) 先于 getPath——名命中即用,不猜路径,内置名优先由此成立)。
2912
+ *
2913
+ * 裁决顺序(裸名分支):保留字 > knownNames > unknown——保留字命中即拒,即便宿主
2914
+ * 清单异常含保留字也不放行(`..` 若被当名接受会绕过路径域的 ⛔2 收紧)。
2915
+ *
2916
+ * 路径分支复用 normalizeRef(WORKFLOW_REF_EXT):`..` 段拒绝、~/ 展开、扩展名校验
2917
+ * 与 agent 域同面(G1:同一校验语义)。
2918
+ */
2919
+ declare function normalizeWorkflowRef(ref: string, opts?: NormalizeWorkflowRefOptions): NormalizedWorkflowRef;
2920
+ /** agentRef 扩展名。 */
2921
+ declare const AGENT_REF_EXT = ".md";
2922
+ /** workflowRef 扩展名。 */
2923
+ declare const WORKFLOW_REF_EXT = ".js";
2924
+ /**
2925
+ * agent ref 的显示名:basename + 去 .md 扩展名(`/a/b/worker.md` → `worker`)。
2926
+ *
2927
+ * agentRef 是绝对路径,UI 显示层(TUI tool block 标题 / list / 完成通知、GUI list item /
2928
+ * pending 通知 name)统一经本函数取短名,避免长路径挤占显示宽度。数据层不动——
2929
+ * record.agent / env 注入(PI_SUBAGENT_AGENT)/ 持久化 / LLM 通知文本保持完整路径。
2930
+ *
2931
+ * 非路径值(DEFAULT_AGENT_NAME "general-purpose")与无 .md 后缀的值原样返回。
2932
+ * 手动 split(/[\\/]) 而非 path.basename:跨平台统一(macOS 的 path.basename
2933
+ * 不切 Windows `\` 分隔符,反之类推),且本模块避免引入平台分支。
2934
+ */
2935
+ declare function displayAgentName(ref: string): string;
2936
+
2937
+ /**
2938
+ * slug 最大长度。历史值 20 偏紧——描述性 slug 如 "audit-structured-output"(23)/
2939
+ * "fix-subagent-wf-tools"(21)会撞上限,放宽到 35 兼顾「短到能塞进 TUI 标题行」
2940
+ * 与「容纳合理描述性 kebab-case 名」。
2941
+ */
2942
+ declare const SLUG_MAX_LENGTH = 35;
2943
+
2944
+ /**
2945
+ * 检测 pid 是否存活。
2946
+ *
2947
+ * process.kill(pid, 0) 语义:不发信号,仅检查进程是否存在。
2948
+ * - 无异常 → 存活
2949
+ * - ESRCH(No such process)→ 死
2950
+ * - EPERM(Process exists but no permission)→ 存活(保守)
2951
+ * - 其他异常 → 保守判死 false,避免误删活进程
2952
+ */
2953
+ declare function isProcessAlive(pid: number): boolean;
2954
+
2955
+ /**
2956
+ * 排队策略(D7/U4):release 时从等待队列放行哪个条目。策略差异是宿主声明的
2957
+ * 有意决策(pi=priority / zsw=strict-fifo),保留为参数而非消灭(见 sink 设计
2958
+ * subagent-core-sink-design.md §3.3 D7)。
2959
+ *
2960
+ * - `"priority"`(缺省):priority 值最小(0=最高)者优先,同优先级按入队序(FIFO)。
2961
+ * 与 pi 既有行为等值。
2962
+ * - `"strict-fifo"`:忽略 priority,严格按入队顺序(seq)放行。纯 FIFO 语义,
2963
+ * 供 zsw 侧等需要「先到先得、不许插队」语义的宿主消费。
2964
+ */
2965
+ type QueuePolicy = "priority" | "strict-fifo";
2966
+ /** 并发池接口(可注入,便于测试 mock)。 */
2967
+ interface ConcurrencyPool {
2968
+ /**
2969
+ * 排队获取槽位(priority 0=最高;"strict-fifo" 策略下该值仅记录不参与出队选择,
2970
+ * 见 createConcurrencyPool)。可选 effectiveMaxConcurrent 覆盖实例级默认配额。
2971
+ * 可选 AbortSignal 在 abort 时 reject 排队条目。
2972
+ */
2973
+ acquire(priority: number, effectiveMaxConcurrent?: number, signal?: AbortSignal): Promise<void>;
2974
+ /** 归还槽位。必须无条件执行(finally)。 */
2975
+ release(): void;
2976
+ /** 当前已占用槽位数(诊断/widget 用)。 */
2977
+ readonly active: number;
2978
+ /** 实例级最大并发配额。调用方可据此计算分层配额(max(1, maxConcurrent - depth))。 */
2979
+ readonly maxConcurrent: number;
2980
+ }
2981
+ /** createConcurrencyPool 选项(对象参数构造——导出面禁止位置参数歧义,D7/U4)。 */
2982
+ interface CreateConcurrencyPoolOptions {
2983
+ /** 实例级最大并发配额。0/负数 clamp 到 1(防 acquire 永久排队死锁,C3 修复语义)。 */
2984
+ maxConcurrent: number;
2985
+ /**
2986
+ * 排队策略(见 QueuePolicy)。缺省 `"priority"`——与 pi 既有消费
2987
+ * (`new DefaultConcurrencyPool(n)`)行为逐点等值,缺省即零回归。
2988
+ */
2989
+ queuePolicy?: QueuePolicy;
2990
+ }
2991
+ /**
2992
+ * 并发池工厂(D7/U4 导出面):宿主经此创建并发池,无需感知实现类。
2993
+ *
2994
+ * 返回 ConcurrencyPool 接口而非 DefaultConcurrencyPool——barrel 导出面只认
2995
+ * 「对象参数 + 策略枚举」,实现类可内部替换,策略差异(pi=priority /
2996
+ * zsw=strict-fifo)显式化为参数而非两份复刻实现(深度分层公式与下限常量单源,
2997
+ * 公式见类注释与 slots 消费方)。
2998
+ */
2999
+ declare function createConcurrencyPool(options: CreateConcurrencyPoolOptions): ConcurrencyPool;
3000
+
3001
+ /**
3002
+ * gitRun 的包装错误:message 格式与提取源 gitRunAsync 逐字一致;exitCode/stderr/
3003
+ * timedOut 为诊断属性(P-errshape 实测 Node 24:execFile 的 err.stderr 为
3004
+ * undefined——stderr 在 callback 第三参;退出码在 err.code,数字时)。
3005
+ */
3006
+ declare class GitRunError extends Error {
3007
+ readonly exitCode?: number;
3008
+ readonly stderr?: string;
3009
+ readonly timedOut?: boolean;
3010
+ constructor(message: string, props: {
3011
+ exitCode?: number;
3012
+ stderr?: string;
3013
+ timedOut?: boolean;
3014
+ });
3015
+ }
3016
+ declare const SAFE_ID_RE: RegExp;
3017
+ /** recordId 是否匹配安全白名单 `^[\w-]+$`。 */
3018
+ declare function isSafeId(id: string): boolean;
3019
+ /**
3020
+ * 断言 recordId 匹配安全白名单,不合法抛 DirtyWorktreeError(与提取源 create()
3021
+ * 的拒绝语义一致,供 manager 收缩后无缝切换)。
3022
+ *
3023
+ * @throws DirtyWorktreeError(消息含白名单说明,可操作)
3024
+ */
3025
+ declare function assertSafeId(id: string, label?: string): void;
3026
+ /**
3027
+ * dirty 谓词:`git status --porcelain` 输出 trim 后非空即脏树。
3028
+ * 提取源 create() 的内联判定(「消费点自行 trim」的干净文本消费点之一)。
3029
+ */
3030
+ declare function isTreeDirty(statusPorcelain: string): boolean;
3031
+ /**
3032
+ * git 命令执行器。stdout 保真返回(不 trim):diff 输出原样落盘为 patch 文件,
3033
+ * 裁掉尾换行会产出 `git apply` 拒绝的 corrupt patch(worktree-manager.ts 头注释
3034
+ * 2026-08-16 门 4 实测)。需要干净文本的消费点自行 trim。
3035
+ *
3036
+ * maxBuffer(execFile stdout 上限):不传 = Node execFile 缺省 1MB(1024 * 1024),
3037
+ * 超限以 ERR_CHILD_PROCESS_STDIO_MAXBUFFER 失败(reject GitRunError)。大输出命令
3038
+ * (如批量重构的大 diff)由调用方显式提高,宿主建议 32 * 1024 * 1024(对齐旧 zsw
3039
+ * 宿主 GIT_MAX_BUFFER)。以条件展开实现而非 `maxBuffer: opts.maxBuffer` 直透:
3040
+ * Node 先铺缺省再展开 options,显式 undefined 会覆盖缺省为无界(实测探针),
3041
+ * 与「不传即 1MB 缺省」相悖。
3042
+ *
3043
+ * 失败 reject GitRunError。无 per-repo 写串行(编排职责留宿主,见文件头注释)。
3044
+ */
3045
+ declare function gitRun(args: string[], opts: {
3046
+ cwd: string;
3047
+ timeout?: number;
3048
+ maxBuffer?: number;
3049
+ }): Promise<string>;
3050
+ /** patch 基线锚点抽象(D5):内存 baseCommit 或宿主持久锚点文件,二选一注入。 */
3051
+ type PatchBaselineAnchor = {
3052
+ readonly kind: "commit";
3053
+ readonly baseCommit: string;
3054
+ } | {
3055
+ readonly kind: "anchor-file";
3056
+ readonly path: string;
3057
+ };
3058
+ interface CollectWorktreePatchOptions {
3059
+ /** worktree checkout 目录(add / diff 的 cwd)。 */
3060
+ readonly worktreePath: string;
3061
+ /** patch 输出绝对路径(须在 worktree 之外——cleanup 不会删除)。 */
3062
+ readonly patchFile: string;
3063
+ /**
3064
+ * 基线锚点。anchor-file 形态的 path 须在 worktree 之外(同 patchFile 约束):
3065
+ * 落在 worktree 内会被本机制的 `git add -A` 一并暂存、混入 diff 产物——patch
3066
+ * 被锚点文件自身污染(路径与内容进入 patch),且该形态无任何 warn 或
3067
+ * patchIncomplete 留痕(不在 ⛔3 降级规格内,属静默污染)。
3068
+ */
3069
+ readonly anchor: PatchBaselineAnchor;
3070
+ /** git 命令超时(ms),缺省 30_000。 */
3071
+ readonly timeout?: number;
3072
+ /**
3073
+ * execFile maxBuffer(字节)= diff 输出上限,缺省 1MB(1024 * 1024,Node execFile
3074
+ * 缺省)。超限即 GitRunError(先经 ⛔3① git 层触发降级 warn,降级裸 diff 同样
3075
+ * 超限后原样上抛——实测形态,见 worktree-git-ops.test.ts 大 diff 用例)。批量
3076
+ * 重构等大 diff 场景宿主建议 32 * 1024 * 1024(对齐旧 zsw 宿主 GIT_MAX_BUFFER)。
3077
+ * 仅透传至产生大输出的 diff 调用(`diff --cached` 与降级裸 diff);`add -A`
3078
+ * 等小输出命令不透传。
3079
+ */
3080
+ readonly maxBuffer?: number;
3081
+ }
3082
+ /**
3083
+ * patch 收集结果。`patchIncomplete` 即 ⛔3 留痕载体:true 表示 patch 相对完整
3084
+ * 机制有已知损失(丢已提交改动 / 丢新文件),宿主透传至 record/summary 的责任
3085
+ * 在姊妹文档 V6。缺省(undefined)= 完整机制产物。
3086
+ */
3087
+ interface WorktreePatchResult {
3088
+ readonly patchFile: string;
3089
+ /** true = diff 非空且写盘成功;false = 空 diff(不写文件,避免悬空路径)。 */
3090
+ readonly written: boolean;
3091
+ readonly patchIncomplete?: boolean;
3092
+ }
3093
+ /**
3094
+ * 收集 worktree 改动为 patch(统一 add + diff 基线机制)。
3095
+ *
3096
+ * 完整形态:`git add -A`(暂存全部改动,含未跟踪新文件)→ `git diff --cached
3097
+ * <baseline>`(暂存区 vs 基线锚点)。旧形态 `git diff HEAD <base>` 是树 vs 树对比:
3098
+ * worktree HEAD 初始即 baseCommit,子 agent 不提交时 diff 恒空 → 改动丢失(提取源
3099
+ * collectPatch [MF#2] 注释)。
3100
+ *
3101
+ * ⛔3 降级路径(均非致命、显著 warn、patchIncomplete 留痕):
3102
+ * ① 锚点缺失(anchor-file 不存在/不可读)、损坏(内容空白,或内容不被 git 认)
3103
+ * → 裸 diff HEAD(仅未提交改动,丢已提交改动)。
3104
+ * ② add 失败(如索引锁冲突瞬时态)→ 裸 diff HEAD(untracked 新文件不进 patch)。
3105
+ * 按 D5 裁决以 HEAD 为基线重定义降级形态(非 zsw `diff <base>` 等值平移),
3106
+ * 损失面差异由 patchIncomplete 留痕判断。
3107
+ *
3108
+ * 写盘失败(磁盘满/权限)不在 ⛔3 降级规格内:环境级故障不与「空 diff written=false」
3109
+ * 混淆(否则宿主把磁盘故障当无改动,patch 静默丢失),原样上抛由宿主处置。
3110
+ */
3111
+ declare function collectWorktreePatch(opts: CollectWorktreePatchOptions): Promise<WorktreePatchResult>;
3112
+ interface CleanupWorktreeOptions {
3113
+ /** 主仓库根目录(git -C 目标)。 */
3114
+ readonly repo: string;
3115
+ /** worktree checkout 绝对路径。 */
3116
+ readonly worktreePath: string;
3117
+ /** 分支名。 */
3118
+ readonly branch: string;
3119
+ /** 第三步宿主钩子(如 worktrees.json 注册表移除)。注册表归宿主(D5 目录布局
3120
+ * 与孤儿判定留宿主),内核经回调解耦;抛错仅 warn 不阻断(三步各自容错)。 */
3121
+ readonly onRemoved?: () => Promise<void> | void;
3122
+ /** git 命令超时(ms),缺省 30_000。 */
3123
+ readonly timeout?: number;
3124
+ }
3125
+ /**
3126
+ * 清理 worktree:worktree remove --force → branch -D → onRemoved 宿主钩子。
3127
+ * 三步各自独立容错——任一步失败不阻断其余(如 remove 失败仍尝试 branch -D +
3128
+ * 宿主钩子),避免单步失败导致后续资源泄漏(提取源 cleanup 的容错结构)。
3129
+ * 前两步失败 debug 留痕(best-effort 惯例);宿主钩子失败 warn(宿主态漂移值得
3130
+ * 显著,孤儿收敛兜底在宿主 reaper/对账)。本函数永不 reject。
3131
+ */
3132
+ declare function cleanupWorktree(opts: CleanupWorktreeOptions): Promise<void>;
3133
+ interface ListWorktreePorcelainOptions {
3134
+ /** 主仓库根目录(git -C 目标)。 */
3135
+ readonly repo: string;
3136
+ /** git 命令超时(ms),缺省 30_000。 */
3137
+ readonly timeout?: number;
3138
+ }
3139
+ /**
3140
+ * `git worktree list --porcelain` 原始 stdout 保真返回(不 trim / 不 split / 不
3141
+ * 逐行加工)。宿主 realpath 对账依赖原始行文(zsw 的 /var→/private/var 归账)。
3142
+ * 失败 reject GitRunError(读类命令失败 = 真故障,处置策略归宿主)。
3143
+ */
3144
+ declare function listWorktreePorcelain(opts: ListWorktreePorcelainOptions): Promise<string>;
3145
+
3146
+ /** 三态读取结果(判别联合,调用方按 status 分派)。 */
3147
+ type GlobalConfigReadResult =
3148
+ /** 读到明确值:JSON 可解析,字段已经 sanitize。 */
3149
+ {
3150
+ status: "ok";
3151
+ config: SubagentsGlobalConfig;
3152
+ }
3153
+ /** 明确缺省:文件不存在(ENOENT)。这是用户意图(删配置切回缺省 pi),不是故障。 */
3154
+ | {
3155
+ status: "absent";
3156
+ config: SubagentsGlobalConfig;
3157
+ }
3158
+ /** 读失败:坏 JSON / 权限等。携带原始错误消息供诊断;调用方保持 lastEngine 不动。 */
3159
+ | {
3160
+ status: "failed";
3161
+ reason: string;
3162
+ };
3163
+
3164
+ /** Service 构造参数(进程级,跨 session 不变)。 */
3165
+ interface ModelConfigServiceInit {
3166
+ agentDir: string;
3167
+ /** 项目根目录(ctx.cwd,用于推导 workspaceRoot 扫描 project 级资源)。 */
3168
+ cwd: string;
3169
+ }
3170
+ /** session_start 注入参数(session 级,每次重建)。 */
3171
+ interface ModelServiceSessionInit {
3172
+ /** 模型注册表(鉴权 + 发现)。null 立即抛错(fail-fast)。 */
3173
+ modelRegistry: ModelRegistryLike | null;
3174
+ /** 当前 session ID。 */
3175
+ sessionId: string;
3176
+ /**
3177
+ * 主 agent 当前 model(session_start 时注入,model_select 时刷新)。
3178
+ *
3179
+ * renderCall 阶段的 ToolRenderContext 不含 model 字段(SDK 限制),无法直接拿到
3180
+ * 主 agent model。缓存后 renderCall 的 resolveModel 能命中第三层(ctxModel),
3181
+ * 让标题行恢复显示 model——即使未显式传 model 也能展示默认 model。
3182
+ *
3183
+ * [HISTORICAL] 99f20da1e 引入三层 fallback 后,renderCall 因拿不到 ctxModel
3184
+ * 而 resolveModel 拗错→降级不显示 model。此缓存修复该降级。
3185
+ */
3186
+ ctxModel?: ModelInfo;
3187
+ }
3188
+ /**
3189
+ * 配置 + 模型解析 Service。进程级单例。
3190
+ *
3191
+ * ┌──────────────────────────────────────────────────────┐
3192
+ * │ globalConfig(~/.pi/.../config.json,仅 maxConcurrent)│
3193
+ * │ agentRegistry(agent .md 发现 + frontmatter) │
3194
+ * │ modelRegistry(SDK 注入的可用模型) │
3195
+ * │ │
3196
+ * │ resolveModel: override → agentConfig → 主 agent model │
3197
+ * └──────────────────────────────────────────────────────┘
3198
+ */
3199
+ declare class ModelConfigService {
3200
+ private globalConfig;
3201
+ private readonly agentRegistry;
3202
+ private readonly agentRegistryDir;
3203
+ private modelRegistry;
3204
+ private _sessionId;
3205
+ /** 主 agent 当前 model 缓存(session_start 注入,model_select 刷新)。 */
3206
+ private _ctxModel;
3207
+ constructor(init: ModelConfigServiceInit);
3208
+ /**
3209
+ * session_start 注入。封装 3 步固定时序:
3210
+ * 1. reloadGlobalConfig(复用时拿最新 config)
3211
+ * 2. injectModelRegistry(fail-fast:null 抛错)
3212
+ * 3. setSessionId
3213
+ */
3214
+ initModel(init: ModelServiceSessionInit): void;
3215
+ /**
3216
+ * 将一次三态读取结果提交到路由缓存(纯赋值幂等)。
3217
+ *
3218
+ * ok/absent 覆盖缓存、failed 保持缓存不动(坏 JSON 不能把好缓存打回缺省);
3219
+ * 返回入参便于链式消费。用途 = 构造性同源:session_start 初始化与 per-turn 引擎
3220
+ * 检测各只读一次文件,同一读取结果既刷新路由缓存又充当检测基准,消灭两次独立
3221
+ * 读取之间的分叉窗口(两次读值不一致时检测走 unchanged,状态段/路由永停旧值)。
3222
+ */
3223
+ applyGlobalConfig(read: GlobalConfigReadResult): GlobalConfigReadResult;
3224
+ /**
3225
+ * 三态重读全局配置并提交缓存(幂等可重入),返回本次读取结果供调用方感知。
3226
+ *
3227
+ * 从 initModel 提取(设计 D2):引擎感知检测器 per-turn poll 发现 config 变更时
3228
+ * 调用本方法,使「system prompt 现值、路由缓存、变更通知」同 turn 对齐——只改注入
3229
+ * 不刷新路由缓存,会出现 prompt 说引擎 B、实际派发跑引擎 A(权威信息源说谎)。
3230
+ * 幂等性:只做「读文件 → 按三态提交缓存」单向赋值,无时序状态,重复调用收敛到
3231
+ * 同一结果。三态语义(failed 保持缓存、静默回落 DEFAULT 是旧缺陷——读失败曾把
3232
+ * 好缓存打回缺省且调用方无法感知):ok/absent 覆盖、failed 保持并携带原因。
3233
+ */
3234
+ reloadGlobalConfig(): GlobalConfigReadResult;
3235
+ /**
3236
+ * 刷新主 agent model 缓存。model_select 事件时调用。
3237
+ * renderCall 的 resolveModel 读此缓存以显示标题行 model。
3238
+ */
3239
+ setCtxModel(model: ModelInfo | undefined): void;
3240
+ /**
3241
+ * 解析 agent 的模型(三层:override → agentConfig → 主 agent model)。
3242
+ *
3243
+ * @param agentRef agent 引用(.md 绝对路径;查 agentConfig 的 model override)
3244
+ * @param override 调用方显式 override(最高优先级)
3245
+ * @param ctxModel 主 agent 当前模型(兜底,直接透传)
3246
+ */
3247
+ resolveModel(agentRef: string, override?: {
3248
+ model?: string;
3249
+ thinkingLevel?: string;
3250
+ }, ctxModel?: ModelInfo,
3251
+ /** 已解析的 agent 配置(调用方已加载时复用,避免同一 agentRef 二次 loadByPath)。 */
3252
+ agentConfig?: AgentConfig): ResolvedModel;
3253
+ /** 查询 agent 配置(SubagentService 内部判定 defaultBackground 用)。
3254
+ * undefined = 合法缺省语义(未点名 / 默认 general-purpose 形态)。 */
3255
+ getAgentConfig(agentRef?: string): AgentConfig | undefined;
3256
+ /**
3257
+ * 查询 agent 配置——显式 ref 失败即 throw(SubagentService.resolveIdentity 用)。
3258
+ *
3259
+ * 与 getAgentConfig 的语义分界(「用户显式点名」vs「默认 general-purpose」):
3260
+ * 用户显式点名的 agentRef(工具 agent 参数 / workflow agent({agent}) opts)解析
3261
+ * 失败 = 配置错误,必须显式报错——错误文案含 <available_subagents> 恢复指引
3262
+ * (对齐 workflow name not found 反馈风格),不允许静默降级为无配置
3263
+ * general-purpose 形态(systemPrompt/工具白名单全丢且零反馈)。默认形态
3264
+ * (不传 agent)走 getAgentConfig:undefined = 合法缺省,走 override → ctxModel 兑底。
3265
+ */
3266
+ getRequiredAgentConfig(agentRef: string): AgentConfig;
3267
+ /** 全局配置深拷贝(调用方拿到副本,改不影响 Service 内部)。 */
3268
+ getGlobalConfig(): SubagentsGlobalConfig;
3269
+ /** 内部:session id 缓存(initModel 注入;当前无消费者,保留供未来 session 作用域需求)。 */
3270
+ get sessionId(): string | undefined;
3271
+ /** agent 配置目录(SubagentService 构造 store/SessionRunnerContext 时读)。 */
3272
+ getAgentDir(): string;
3273
+ /** modelRegistry(SubagentService 构造 factoryCtx 时读)。已注入保证非 null。 */
3274
+ getModelRegistry(): ModelRegistryLike;
3275
+ /** 校验 modelRegistry 已注入。 */
3276
+ private assertReady;
3277
+ }
3278
+ /** 获取进程单例。session_start 前为 null。 */
3279
+ declare function getModelConfigService(): ModelConfigService | null;
3280
+
3281
+ interface ManifestRecord {
3282
+ id: string;
3283
+ rootSessionId: string;
3284
+ /** 直接父 subagent record ID(层级树构建用)。顶层 record 缺失(undefined)。M3a 补字段。 */
3285
+ parentRecordId?: string;
3286
+ agentName: string;
3287
+ /**
3288
+ * 终态枚举:finalizeRecord 写 running/closed/cancelled 三态。
3289
+ * SP-1 重构:旧 completed/failed 合并为 closed(L1 统一终态)。
3290
+ * cancelled 保持独立(用户取消语义)。crashed 不进 manifest——
3291
+ * crashed 是重启重建时靠 sidecar 四分支推断的派生态(见 record-store.ts reconstructAll)。
3292
+ * 历史 "error"/"completed"/"failed" 值由读侧 mapManifestStatus 向后兼容映射。
3293
+ */
3294
+ status: "running" | "closed" | "cancelled";
3295
+ createdAt: number;
3296
+ completedAt?: number;
3297
+ sessionFile?: string;
3298
+ /** FR-7 补字段:manifest 写入时从 ExecutionRecord 抓取,供 manifestToSubagent 投影真实值。 */
3299
+ task?: string;
3300
+ slug?: string;
3301
+ model?: string;
3302
+ }
3303
+ declare class ManifestStore {
3304
+ private readonly dir;
3305
+ /** [perf] per-file 缓存:file → { stamp, record }。record=null 表示「已解析但非法」(缓存
3306
+ * 负结果避免反复 parse 损坏文件)。stat 戳变化(writeManifest tmp→rename 后 mtime/size 变)
3307
+ * 自动失效;删除的文件在下次扫描时修剪。 */
3308
+ private readonly cache;
3309
+ constructor(dir: string);
3310
+ /**
3311
+ * 原子写:tmp → fsync → rename → fsync dir(shared/atomic-write 统一原语,
3312
+ * U6b 迁移——原逐行实现与 writeAtomicFile 逐环等值)。真异步(fs.promises,
3313
+ * 不阻塞 event loop)。
3314
+ *
3315
+ * 失败时原语尽力清理残留 tmp(debug 记录,不掩盖原错误)并原样上抛——
3316
+ * 调用方(finalizeRecord)决定降级策略。
3317
+ */
3318
+ writeManifest(record: ManifestRecord): Promise<void>;
3319
+ /**
3320
+ * 按 id 读 manifest。文件不存在/JSON 损坏/schema 不合法均返回 null。
3321
+ * 调用方需处理 null。
3322
+ */
3323
+ readManifest(id: string): Promise<ManifestRecord | null>;
3324
+ /**
3325
+ * 同步读取所有 manifest 记录(best-effort,损坏/非法文件跳过)。
3326
+ * 供 RecordStore.collectRecords 投影 orphan 记录使用——替代对私有 dir 的反射访问。
3327
+ * 仅返回通过 isValidManifest 校验的记录。
3328
+ *
3329
+ * [perf] per-file 缓存 + stat 戳校验:collectRecords 每次渲染都调本方法,旧实现每次
3330
+ * 全量 readFileSync + JSON.parse 千级 manifest(实测 ~300ms/次)。命中缓存的文件零读取。
3331
+ */
3332
+ listAllSync(): readonly ManifestRecord[];
3333
+ /**
3334
+ * 启动时恢复 tmp 文件。
3335
+ * 3 分支逻辑:
3336
+ * 1. manifest 已存在 → 删 tmp(陈旧)
3337
+ * 2. tmp 合法 + manifest 缺失 → rename tmp 为 manifest
3338
+ * 3. tmp 非法 + manifest 缺失 → 删 tmp
3339
+ *
3340
+ * [T5④ / PS-13] per-file 容错:单个 tmp 文件操作失败(ENOENT——并发回收/外部清理
3341
+ * 抢先、EACCES 等)只 warn + 跳过该文件,不再中断整轮——旧实现单文件 ENOENT 即抛,
3342
+ * 剩余 tmp 本轮不再处理,自愈但不可见(残留顺延下次启动)。跳过数经 warn 汇总留痕,
3343
+ * 调用方返回值形态不变(跳过者不计数)。
3344
+ */
3345
+ recoverTmpFiles(): Promise<{
3346
+ deleted: number;
3347
+ recovered: number;
3348
+ }>;
3349
+ }
3350
+
3351
+ /** store 变更监听器(返回取消订阅函数)。 */
3352
+ type ChangeListener = () => void;
3353
+ /** status 过滤模式(collectRecords 的核心能力参数)。 */
3354
+ type StatusFilter = "running" | "all";
3355
+ /** Pi ExtensionAPI 的最小子集(仅 collectRecords 跳过损坏 manifest 时上报用)。
3356
+ * 解构为局部类型,避免与 subagent-service 的 PiLike 循环依赖。 */
3357
+ type RecordStorePi = {
3358
+ appendEntry?: (customType: string, data: unknown) => void;
3359
+ } | null | undefined;
3360
+ /**
3361
+ * Record 容器。进程单例(随 SubagentService 重建)。
3362
+ *
3363
+ * 内存只留 running record——终态 record 在 archive 时立即移除,collectRecords
3364
+ * 读时从 sessions/*.jsonl 重建([perf] light 头部扫描 + per-file 缓存)。
3365
+ *
3366
+ * 任何 mutate → notifyChange()(仅通知监听器;磁盘缓存靠 stat 戳自校验,不清空)。
3367
+ *
3368
+ * record 状态查询面(U10① D6):按状态枚举 listRunning/collectRecords(statusFilter)、
3369
+ * 按 id 查询 getMutable/findLightById/getFullRecord——方法签名即导出形态,本类零改动。
3370
+ *
3371
+ * @experimental execution 运行时面(设计 docs/design/subagent-core-sink-design.md §3.3 D6):
3372
+ * 一个 minor 周期内允许签名微调,稳定后转常规 semver 承诺。
3373
+ */
3374
+ declare class RecordStore {
3375
+ private readonly sessionsDir;
3376
+ private readonly manifestStore?;
3377
+ private readonly records;
3378
+ private readonly listeners;
3379
+ private _disposed;
3380
+ /** 孤儿终态恢复的已判定缓存(residual-fixes):resumable 形态无 sidecar 锚,同进程重复调用跳过。 */
3381
+ private orphanJudged;
3382
+ /** Pi handle(用于 appendEntry 上报损坏 manifest)。构造时可空,setPi() 后续注入。
3383
+ * 显式存为字段而非构造参数 readonly:setPi 需要写权限。 */
3384
+ private pi;
3385
+ /** [perf] per-file 缓存(key = sessionFile 绝对路径)。不再整体失效——stat 戳精准校验。
3386
+ * 值含负缓存(确认无 identity 的文件),防每轮全文 fallback 重读。 */
3387
+ private readonly fileCache;
3388
+ /** record id → sessionFile 索引(getFullRecord 按 id 定位文件)。随 fileCache 同步维护。 */
3389
+ private readonly idToFile;
3390
+ /** [perf] sessionsDir 最近一次全量扫描的 mtime(快路径判变,见 reconstructAll)。
3391
+ * null = 未扫过 / 已 dispose。 */
3392
+ private dirStamp;
3393
+ /** [perf L-1] 首扫惰性装载的磁盘索引只读映像(key = jsonl basename)。
3394
+ * 扫描尾(flushIndexAfterScan)与 readdir 失败路径释放——运行期索引不再被读(L1 接管)。 */
3395
+ private indexEntries;
3396
+ /** [perf L-1] 本轮起未落盘的探测标志:scanFile 走过探测分支即置位。发起写时消费
3397
+ * (置 false)、写失败恢复;未写路径不清位——未落盘的探测成果跨轮携带直至真正写入。 */
3398
+ private indexDirty;
3399
+ /** [perf L-1] 上次成功落盘墙钟(节流基准)。0 = 从未写过 → 首扫 dirty 必写;
3400
+ * 仅成功分支推进(写失败不推进节流窗,下轮过窗重试)。 */
3401
+ private lastIndexWriteAt;
3402
+ /** [perf L-1] loadIndex 高版本标志的进程级持久态:true 时本进程所有后续扫描均不
3403
+ * 落盘(防 v1/v2 last-writer-wins 覆盖振荡),直至下次 loadIndex 重新评估。 */
3404
+ private indexHigherVersion;
3405
+ constructor(sessionsDir: string, manifestStore?: ManifestStore | undefined,
3406
+ /** Pi 入口(注入 appendEntry 用于上报损坏 manifest)。
3407
+ * SubagentService 构造时 this.pi 尚未注入(session_start 之前),传 undefined 兜底;
3408
+ * 后续通过 setPi() 注入(见下)。允许 null = 兼容 PiLike 字段类型。 */
3409
+ pi?: RecordStorePi);
3410
+ /** session_start 后由 SubagentService.initSession 调,注入真实 Pi handle。
3411
+ * 设计为独立方法而非要求构造时必传——RecordStore 在 SubagentService 构造时即建
3412
+ * (与 sessionsDir/manifestStore 一同初始化),但 this.pi 此时尚未注入。
3413
+ * 后续构造期外的 appendEntry 上报才有意义。 */
3414
+ setPi(pi: RecordStorePi): void;
3415
+ /** 注册新 record。触发 onChange。
3416
+ * W16 [D4]:record 诞生(→ running)即 append 自描述快照 entry——pi 文件是
3417
+ * 扩展数据持久化权威,custom entry 不进 LLM context。 */
3418
+ register(record: ExecutionRecord): void;
3419
+ /**
3420
+ * 归档:record 已被 completeRecord 设置了终态 status。
3421
+ * 立即从内存移除(终态 record 下次读时从 session.jsonl 重建)。
3422
+ * cancelled record 由调用方先写 tombstone(cancel 路径),此处只负责移除。
3423
+ *
3424
+ * W16 [D4]:终态冻结字段(result/endedAt/closedReason)在 completeRecord 已就绪,
3425
+ * 此处 append 的快照即完整终态记录(所有终态路径的必经锚点)。
3426
+ */
3427
+ archive(record: ExecutionRecord): void;
3428
+ /**
3429
+ * W16 [D4]:类外状态写点上报(record-store 内的迁移点 register/archive 已内置)。
3430
+ *
3431
+ * 供 service 层直接改 record.status 的恢复写点调用(chatMode 续轮 idle→running
3432
+ * 冷路径 resumeRound、轮终 finalizeRoundToIdle 回 running-resumable)——这些
3433
+ * 写点绕过 register/archive,若不显式上报,pi 文件缺失该次迁移、重建源滞后。
3434
+ * pi 未注入(session_start 前)时可选链静默降级,不阻断主流程。
3435
+ */
3436
+ reportRecordTransition(record: ExecutionRecord): void;
3437
+ /** 按 id 查找。返回可变 record(仅 runtime 内部用)。 */
3438
+ getMutable(id: string): ExecutionRecord | undefined;
3439
+ /**
3440
+ * abort 所有 running record 的 controller(background 子进程 SIGTERM)。
3441
+ *
3442
+ * 仅在 SubagentService.dispose(进程退出路径)调用。不做 CAS/tombstone——dispose
3443
+ * 是终局,状态机收尾无意义;目的是让 background 子进程的 AbortSignal 触发 →
3444
+ * runSpawn 的 signal listener → child.kill("SIGTERM"),防止主进程退出后子进程成孤儿。
3445
+ *
3446
+ * sync record 无 controller(undefined),跳过——sync 是阻塞调用,主进程不会先于
3447
+ * sync subagent 退出(除非 SIGKILL/崩溃,此时任何清理都无效)。
3448
+ *
3449
+ * 返回被 abort 的 record 数(诊断用)。
3450
+ */
3451
+ abortRunningControllers(): number;
3452
+ /** 列出所有 running record 的只读快照(widget 计数、诊断用)。 */
3453
+ listRunning(): RecordSnapshot[];
3454
+ /** SP-4: 列出所有活跃 record(running + idle)的可变引用。
3455
+ * 供 SubagentService.disposeAllRecords 做级联关闭。 */
3456
+ listAllActive(): ExecutionRecord[];
3457
+ /**
3458
+ * 合并内存(running) + 磁盘(sessions/*.jsonl 重建) → SubagentRecord[]。
3459
+ *
3460
+ * ╔══════════════════════════════════════════════════════════════════╗
3461
+ * ║ 1. 磁盘源:扫 sessionsDir 的 .jsonl,逐个 scanFile([perf] 头部 ║
3462
+ * ║ identity 轻量重建 + stat 戳缓存命中零读取)。cancelled ║
3463
+ * ║ tombstone override status。详情字段(eventLog/result/turns) ║
3464
+ * ║ 缺省,由 getFullRecord(id) 懒加载 ║
3465
+ * ║ 2. 内存源覆盖(同 id 内存优先——running record 更新鲜) ║
3466
+ * ║ 3. session 过滤:只留 rootSessionId === rootSessionFilter 的 ║
3467
+ * ║ record。rootSessionId 缺失(旧文件)的 record 一律排除 ║
3468
+ * ║ (无法判定归属,隔离优先)。rootSessionFilter 为 undefined ║
3469
+ * ║ 时不过滤(向后兼容)。 ║
3470
+ * ║ 4. statusFilter:"running" → 只留 running(内存源); ║
3471
+ * ║ "all"(默认)→ 内存 + 磁盘 ║
3472
+ * ║ 5. 排序:STATUS_PRIORITY + startedAt desc ║
3473
+ * ║ 6. slice(limit) ║
3474
+ * ╚══════════════════════════════════════════════════════════════════╝
3475
+ *
3476
+ * statusFilter="running" 时仍先取够多再过滤(防 limit 截断把 running 滤没),
3477
+ * 与旧 listHandler 的防截断逻辑一致,下沉到此。
3478
+ *
3479
+ * session 隔离:同一 cwd 下多个 Pi session 共享 sessionsDir,靠 rootSessionId
3480
+ * 区分。内存与磁盘源都按 rootSessionFilter 过滤后再 merge/sort/slice。
3481
+ */
3482
+ collectRecords(limit: number, statusFilter?: StatusFilter, rootSessionFilter?: string): SubagentRecord[];
3483
+ /**
3484
+ * 重建 SubagentRecord 的自描述 entry 落盘入口(签名适配:reportRecordTransition 收
3485
+ * ExecutionRecord,重建孤儿的数据源是 SubagentRecord——直接经 toSubagentRecordEntry
3486
+ * 投影 appendEntry,绕过 recordToSubagent)。pi 未注入时可选链静默。
3487
+ */
3488
+ reportSubagentRecord(record: SubagentRecord): void;
3489
+ /**
3490
+ * 孤儿终态恢复:对重建矩阵分支 4 兜底(running 且无 externalInstance)的 record
3491
+ * 判定真实终态并落 entry,消除「父扩展死后再无人写终态 → 侧栏永久 running」。
3492
+ *
3493
+ * 判定(residual-fixes §5.2 三判据 + chat 分流):
3494
+ * - chatMode = true → 不终态化(跨重启可续聊是产品语义,v4 B-1),落 resumable
3495
+ * entry 供侧栏 waiting 细分;
3496
+ * - 子 JSONL 末行完整 JSON.parse → closed(closedReason=gc,与分支 2 重建映射一致;
3497
+ * done/failed 细分由 error 字段经 deriveClosedDisplay 派生)+ 写 .finalized sidecar
3498
+ * (防重锚——下次重建走分支 2 不再进判定);
3499
+ * - 末行截断 → closed + error(保守,错误方向安全)+ sidecar;
3500
+ * - 文件不可读(IO 错误,可能暂时)→ 不判终态,落 resumable entry(防御性路径,
3501
+ * IO 恢复后重开可重判)。
3502
+ *
3503
+ * 防重:orphanJudged 实例级缓存(resumable 形态无 sidecar 锚,同进程重复调用跳过;
3504
+ * 终态形态双重防护 = sidecar + 缓存)。调用方:index.ts session_start 恢复段(一次)。
3505
+ */
3506
+ recoverOrphanRecords(rootSessionFilter?: string): void;
3507
+ /**
3508
+ * 单孤儿 record 的终态判定与落 entry(residual-fixes §5.2 三判据 + chat 分流)。
3509
+ * 防重锚(orphanJudged 标记)已由调用方完成。
3510
+ */
3511
+ private finalizeOrphanRecord;
3512
+ /**
3513
+ * [E2E 实测缺口] entry-born 孤儿恢复:register entry 已落主 session、但子 session 文件
3514
+ * 从未创建(父进程死在 spawn 窗口期——register 写点与子进程首笔写入之间的窗口;外部
3515
+ * 删除子文件的已知边界同形)。目录扫描(reconstructAll)看不见这类 record(无文件即
3516
+ * 无扫描集),recoverOrphanRecords 判不到,侧栏(runtime entry 扫描源)永久 spinner。
3517
+ *
3518
+ * 判定:读主 session 的 subagent-record entry,取每 id 末条;末条 status=running 且
3519
+ * 无子文件锚(不在 reconstructAll 结果中)且不在内存活 record(防误杀刚 register 的
3520
+ * 在途 spawn)→ 按无文件判据收敛:chatMode=true → resumable(分流语义一致);否则
3521
+ * closed+gc+error(子文件由子进程创建,无文件 = 子进程从未开跑,error 方向安全)。
3522
+ * 调用点:initSession 的 recoverOrphanRecords 之后(session_start,内存恒空)。
3523
+ */
3524
+ recoverEntryOnlyOrphans(mainSessionFile: string | undefined, rootSessionFilter?: string): void;
3525
+ /**
3526
+ * entry-born 孤儿候选判定(recoverEntryOnlyOrphans 的守卫链拆出):末条 running、
3527
+ * root session 匹配、无子文件锚、不在内存活 record(防误杀在途 spawn)、未判过。
3528
+ */
3529
+ private isEntryOrphanCandidate;
3530
+ /** entry-born 孤儿按无文件判据收敛落 entry:chatMode → resumable(分流语义一致);
3531
+ * 否则 closed+gc+error(子文件由子进程创建,无文件 = 子进程从未开跑,error 方向安全)。 */
3532
+ private finalizeEntryOnlyOrphan;
3533
+ /** 订阅变更。返回取消订阅函数。 */
3534
+ onChange(listener: ChangeListener): () => void;
3535
+ /** 触发所有监听器(TUI widget/list requestRender)。dispose 后短路。
3536
+ * [perf] 不清空磁盘缓存:per-file stat 戳自校验(任何磁盘写入改变戳 → 单文件重建),
3537
+ * 内存事件(register/archive)不改变磁盘文件——旧实现整体失效是全量重扫的根因。 */
3538
+ notifyChange(): void;
3539
+ /** session 结束清理。 */
3540
+ dispose(): void;
3541
+ /**
3542
+ * /resume /fork /new 后复活(dispose 的逆操作)。
3543
+ *
3544
+ * [PS-10/T6④] 同步复位 orphanJudged 防重缓存:resumable 形态(IO-error 保守分支 /
3545
+ * chatMode 分流)没有 .finalized sidecar 锚,重判资格完全由本缓存承载——dispose 时
3546
+ * 有 clear(session 结束),但 revive 此前不复位,导致「同进程内曾经的 IO 失败记录
3547
+ * 永久停留 resumable」,与本文件 recoverOrphanRecords 注释承诺的「IO 恢复后重开可重判」
3548
+ * 不符。/new 复活正是「重开」语义:IO 已恢复的记录下次 recoverOrphanRecords 重新判定
3549
+ * 收敛终态;仍不可读的记录重判再落一次 resumable entry(幂等,末条语义不变)。
3550
+ */
3551
+ revive(): void;
3552
+ /**
3553
+ * 四分支 sidecar 矩阵重建([perf] light 版)。
3554
+ *
3555
+ * 优先级:
3556
+ * 1. .cancelled → closed(closedReason=cancelled)
3557
+ * 2. .finalized → closed(closedReason=sidecar 内容 reason;空/旧格式 → disconnected)
3558
+ * 3. .alive + pid 存活 + 未超软超时 → running, externalInstance=true
3559
+ * 4. 兜底(无 marker、pid 死、超时)→ running(v4 B-1 可续聊语义)
3560
+ *
3561
+ * [perf]:逐文件 scanFile(stat 戳校验 + 头部 identity 轻量重建)。命中缓存的
3562
+ * 文件零文件读取;变化的文件只重建自身,其余 N-1 个复用缓存。
3563
+ *
3564
+ * session 隔离:rootSessionFilter 非空时,只保留 rootSessionId 匹配的 record。
3565
+ * rootSessionId 缺失(旧文件,未带身份字段)一律排除(无法判定归属)。
3566
+ */
3567
+ private reconstructAll;
3568
+ /**
3569
+ * 扫描单文件:stat 戳(jsonl + 3 sidecar)校验,全同 → 复用缓存(零文件读取,
3570
+ * 含负缓存直接返回 null);否则重建 light。
3571
+ * identity 定位两级:头部 64KB(首轮会话)→ 全文 fallback(续聊场景 identity
3572
+ * append 在尾部);两级都找不到 → 写负缓存(防每轮全文重读)。
3573
+ * 返回 null:文件消失/读失败/无 identity → 跳过。
3574
+ */
3575
+ private scanFile;
3576
+ /**
3577
+ * [perf L-1] 扫描尾索引落盘(节流):释放映像 → dirty/高版本/60s 节流窗三重门 →
3578
+ * fire-and-forget saveIndex(fileCache 全量投影)。写决策与发起在同步栈(collectRecords
3579
+ * 返回后不会再有本轮写);仅写完成的回调(推进节流窗)是异步的。所有 return 路径均
3580
+ * 不清 dirty——未落盘的探测成果跨轮携带,直至真正写入。
3581
+ *
3582
+ * 并发安全:节流基准只在写成功后推进,W1 在途时新一轮过窗扫描可再 dispatch W2(不做
3583
+ * 进程内排队——fire-and-forget 语义保持)。安全性由 saveIndex 的 tmp 唯一性
3584
+ * (pid+单调序号)保证:交错 rename 的终态必为某一次的完整快照(last-writer-wins,
3585
+ * 陈旧快照胜出时下轮戳不匹配自愈),不依赖本方法串行化。
3586
+ */
3587
+ private flushIndexAfterScan;
3588
+ /**
3589
+ * [perf L-1] fileCache 全量投影 → 索引快照(basename → 正/负条目)。
3590
+ * 投影式单一 SSOT:不维护第二份可变索引映像(防双轨漂移);fileCache 已被
3591
+ * reconstructAll 修剪掉消失文件(修剪时置 indexDirty),下次过窗写时快照清除
3592
+ * 磁盘上的陈旧条目。
3593
+ */
3594
+ private projectIndexEntries;
3595
+ /**
3596
+ * [perf] byId 索引直查 light record(单文件 stat 校验,不触发 getFullRecord 的
3597
+ * 全量重建)。idToFile 未热(进程重启后尚未扫描过)时返回 undefined,调用方
3598
+ * 自行兜底全目录扫描——用于把「跨重启后每条 message 一次 collectRecords 全扫」
3599
+ * 降为 O(1) 索引命中。
3600
+ */
3601
+ findLightById(id: string): SubagentRecord | undefined;
3602
+ /**
3603
+ * [perf] 单 record 详情懒加载:内存 running record 投影全量;磁盘 record 全量重建
3604
+ * (reconstructFromFile)并套用同一 sidecar 状态矩阵。结果缓存在 FileCacheEntry.full,
3605
+ * stat 戳变化时随 light 一起失效。列表 collectRecords 返回 light(无 eventLog/
3606
+ * result/turns 等重数据),详情面板/工具 list 按需调本方法补齐。
3607
+ *
3608
+ * 返回 undefined:id 不存在(内存与磁盘均无)。reconstructFromFile 失败(无
3609
+ * assistant message 等)→ 返回 light(无详情可补,缓存哨兵防重复全文重读)。
3610
+ */
3611
+ getFullRecord(id: string): SubagentRecord | undefined;
3612
+ /** alive 探活刷新(scanFile 缓存命中与 reconstructAll 快路径共用):
3613
+ * 分支 3 的 running + alive 条目每扫重查 pid(结果不落盘,进程死亡无 IO),
3614
+ * 保留旧实现「每次 collectRecords 重新 isProcessAlive」的语义。 */
3615
+ private static refreshAlive;
3616
+ /** identity 基底(头部 light 或全量 recon)+ 四分支 sidecar 状态矩阵 → SubagentRecord。 */
3617
+ private static buildRecord;
3618
+ /** 排序比较器:status priority(running<failed<cancelled<done)+ startedAt desc。 */
3619
+ private static compareRecords;
3620
+ /** FR-8: 同步读取所有 manifest 记录(封装 ManifestStore.listAllSync,消除反射访问)。 */
3621
+ private readManifestsSync;
3622
+ /** FR-8: ManifestRecord → SubagentRecord(manifest 源投影)。
3623
+ * task/slug/model 从 manifest 真实值投影(配合 writeManifest 补字段),缺失兜底空串。
3624
+ * status 越界(mapManifestStatus 返回 null)时返回 null,由 collectRecords 跳过。 */
3625
+ private static manifestToSubagent;
3626
+ /** ExecutionRecord → SubagentRecord(内存源投影)。 */
3627
+ private static recordToSubagent;
3628
+ }
3629
+
3630
+ /** Pi ExtensionAPI 的最小接口(duck-typed)。
3631
+ * subagent-service 直接调 pi.sendMessage 发 background 完成通知(BgNotifier 滑动窗口合并),
3632
+ * 不委托 pending-notifications EventBus 中继——后者只管 registry 不参与通知发送。 */
3633
+ interface PiLike {
3634
+ appendEntry(customType: string, data?: unknown): void;
3635
+ events: {
3636
+ emit(channel: string, data: unknown): void;
3637
+ };
3638
+ sendMessage(message: {
3639
+ customType: string;
3640
+ content: string;
3641
+ display: boolean;
3642
+ details?: unknown;
3643
+ }, options?: {
3644
+ triggerTurn?: boolean;
3645
+ deliverAs?: "steer" | "followUp" | "nextTurn";
3646
+ }): void;
3647
+ /** 订阅 pi 事件(D8:notifier 的 settled 边沿订阅用 'agent_settled')。
3648
+ * pi 0.84.4 的 on 返回 void 且无 off——退订语义由调用侧 disposed 标志包装兑现。
3649
+ * 可选:旧测试 mock pi 可能未实现 on,缺省时 notifier 退化为内核退避路径。 */
3650
+ on?(event: "agent_settled", handler: () => void): void;
3651
+ }
3652
+
3653
+ /**
3654
+ * Service 构造参数(进程级)。
3655
+ *
3656
+ * @experimental execution 运行时面(设计 docs/design/subagent-core-sink-design.md §3.3 D6):
3657
+ * 一个 minor 周期内允许签名微调,稳定后转常规 semver 承诺。
3658
+ */
3659
+ interface SubagentServiceInit {
3660
+ cwd: string;
3661
+ /** 配置/模型域 Service(execute 内部调其 resolveModel)。 */
3662
+ modelService: ModelConfigService;
3663
+ /** 缓存的主 session file 获取函数(fork source 解析用)。 */
3664
+ getMainSessionFile?: () => string | undefined;
3665
+ /** W2: UI 请求处理回调(ask_user 扩展)。
3666
+ * 签名见 dialog-queue.ts UiRequestHandler:接收 UiRequest,返回 UiResponse。 */
3667
+ uiRequestHandler?: UiRequestHandler;
3668
+ }
3669
+ /** session_start 注入参数(session 级)。 */
3670
+ interface SubagentServiceSessionInit {
3671
+ pi: PiLike;
3672
+ sessionId: string;
3673
+ /** 主 session 文件路径(session_start 解析后直传)。
3674
+ * [E2E 实测] 不能经闭包缓存(getCachedMainSessionFile)读:jiti 多实例分裂下闭包
3675
+ * 变量不跨实例共享,恢复逻辑读到的是滞后一个事件的值(读到未 flush 的新 session
3676
+ * ENOENT 路径,entry-born 孤儿整段漏判)。 */
3677
+ mainSessionFile?: string;
3678
+ /** UI streaming sink(ctx.ui.setWidget),用于 background text_delta 转发。 */
3679
+ streamSink?: StreamSink;
3680
+ /** 主进程运行模式(W4 守卫:headless 不注入 ask_user RPC 提示词)。
3681
+ * initSession 读取后存入 this.sessionMode,buildSessionRunnerContext 透传给 session-runner。 */
3682
+ mode?: ExtensionMode;
3683
+ /** UI 请求 handler(session 级覆盖进程级)。
3684
+ * initSession 读取后覆盖 this.uiRequestHandler(setUiRequestHandler 的 session 级等价入口)。 */
3685
+ uiRequestHandler?: UiRequestHandler;
3686
+ /** L2 跨子进程全局 dialog 串行队列(进程单例)。透传给 session-runner,
3687
+ * child close 时调 rejectChildDialogs 清理 pending(SR-4 防全局死锁)。 */
3688
+ dialogQueue?: DialogGlobalQueue;
3689
+ /** [竞态修复] 主 agent 是否空闲查询(ctx.isIdle),透传给 notifier 的 flush isIdle gate。
3690
+ * 避免 background 完成通知在 agent_end→finishRun 窗口里走错 sendMessage 分支丢失。
3691
+ * 可选:未注入时 notifier flush 不 gate(原行为)。 */
3692
+ isIdle?: () => boolean;
3693
+ }
3694
+ /**
3695
+ * 执行编排 Service。进程级单例。
3696
+ *
3697
+ * session_start:
3698
+ * 1. modelService = getModelConfigService() ?? new ModelConfigService({cwd, agentDir})
3699
+ * 2. service = getSubagentService() ?? new SubagentService({cwd, modelService})
3700
+ * 3. modelService.initModel({modelRegistry, sessionId, entries})
3701
+ * 4. service.initSession({pi, sessionId})
3702
+ *
3703
+ * session_shutdown:
3704
+ * service.dispose()
3705
+ *
3706
+ * 第三宿主不经 session_start 流程时改用 createSubagentService(init) 参数注入构造。
3707
+ *
3708
+ * @experimental execution 运行时面(设计 docs/design/subagent-core-sink-design.md §3.3 D6):
3709
+ * 一个 minor 周期内允许签名微调,稳定后转常规 semver 承诺。
3710
+ */
3711
+ declare class SubagentService {
3712
+ private readonly pool;
3713
+ private readonly store;
3714
+ private readonly modelService;
3715
+ private readonly cwd;
3716
+ private readonly worktreeManager;
3717
+ private readonly getMainSessionFile;
3718
+ /** UI 请求 handler(进程级,可被 setUiRequestHandler / initSession 覆盖)。 */
3719
+ private uiRequestHandler;
3720
+ /** L2 dialog 串行队列(进程级)。SR-4:child close 时 session-runner 调 rejectChildDialogs 清理。 */
3721
+ private dialogQueue;
3722
+ /** UI 请求可观测性(sessionMode + handler 缺失告警去重,提取自本类降低行数)。 */
3723
+ private readonly uiObservability;
3724
+ private pi;
3725
+ /** 当前 Pi session ID(本进程 pi session,事件路由等用;record 过滤不用它)。initSession 时注入。 */
3726
+ private sessionId;
3727
+ /** 主 session 文件(initSession 按值直传——jiti 多实例下闭包缓存不可靠,见 SessionInit 注释)。 */
3728
+ private mainSessionFile;
3729
+ /** 所属根 session ID(record 归属过滤用)。根进程 = sessionId(自己是 root);
3730
+ * 子进程 = env PI_SUBAGENT_ROOT_SESSION_ID 贯穿的真 ROOT(initSession 读取)。
3731
+ * 与 sessionId 正交:sessionId 是本进程 pi session(事件路由等),sessionRootId 是所属根
3732
+ * (collectRecords filter 用,与 createRecordForMode 的 rootSessionId 盖章同源——子进程
3733
+ * 因此看到整棵 ROOT 树)。设计见 recursive-subagent-visibility.md 决策 3。 */
3734
+ private sessionRootId;
3735
+ /** 进程级执行上下文基线(不依赖 ALS 贯穿——pi RPC mode 的 stdin JSONL 是事件回调式
3736
+ * (attachJsonlLineReader stream.on("data")),每个命令是独立异步链,initSession 里
3737
+ * execCtxAls.enterWith 的 store 不会贯穿到后续 tool 调用事件(实测:递归第二层
3738
+ * parentRecordId/depth 丢失而 rootSessionId 正确——rootSessionId 是实例字段所以不受影响)。
3739
+ * 基线 = 本进程自己的身份(initSession 从 env 读取,与 sessionRootId 同机制):
3740
+ * 读 ALS store 失败时兜底,保证「本进程派发的 subagent 都是本进程记录的孩子」
3741
+ * 这一跨进程树形关系成立。
3742
+ * initSession 设置:有 env PI_SUBAGENT_SELF_RECORD_ID → {recordId: env 值, depth: env DEPTH};
3743
+ * 无 env(根进程)→ null(顶层)。 */
3744
+ private execCtxBaseline;
3745
+ /** fork 深度基线(同 ALS 断裂问题:forkDepthAls.getStore() 兜底用)。根进程=0。 */
3746
+ private forkDepthBaseline;
3747
+ /** [MF-3] 所属根进程 cwd(sessions/records 落盘目录编码键)。
3748
+ * 根进程=自身 cwd(构造时 init.cwd);子进程=env PI_SUBAGENT_ROOT_CWD 贯穿的真 ROOT cwd。
3749
+ * worktree 模式下子进程 this.cwd 是 checkout 路径,若按它编码目录,深层 record 落到
3750
+ * enc(worktree) 段、ROOT 扫描不到 → 全树可见性深度 ≥ 2 断裂(与 sessionRootId 同构)。 */
3751
+ private rootCwd;
3752
+ /** UI streaming sink(ctx.ui.setWidget)。workflow 域经 getStreamSink() 取用。 */
3753
+ private streamSink;
3754
+ /** [竞态修复] 主 agent isIdle 查询(ctx.isIdle)。notifier flush gate 用。
3755
+ * initSession 注入,piAdapter 透传给 NotifierHost。 */
3756
+ private isIdleFn;
3757
+ getStreamSink(): StreamSink | null;
3758
+ private _disposed;
3759
+ private _seq;
3760
+ /** background 完成通知器(滑动窗口合并 + 去重)。session_start revive,shutdown dispose。 */
3761
+ private readonly notifier;
3762
+ /** [MF#4][MF#2] fork 深度按 async 调用链传递(AsyncLocalStorage),替代共享可变计数器。
3763
+ * 主 session=0;fork 进入子 session 期间推进为子深度,供嵌套 fork 经 ALS 读到自身深度作为
3764
+ * parentForkDepth。并发 background fork 各自独立调用链,不再互相压低深度值。
3765
+ * [MF#2] 旧实现用单实例字段跨执行链共享 → 并发下 A 还原深度后 B 读到被压低值 → 护栏失效。 */
3766
+ private readonly forkDepthAls;
3767
+ /** subagent 执行上下文按 async 调用链传递(当前正在跑的 record 身份 + 递归深度)。
3768
+ * B run() 期间包此 ALS,B 内创建 C 时 createRecordForMode 读到 B 的 recordId/depth,
3769
+ * 据此设 C.parentRecordId=B.id、C.depth=B.depth+1。主 session 链上无 store → 顶层。
3770
+ * 与 forkDepthAls 独立:后者只数 fork 链(fork=true 才递增),本 ALS 数所有 subagent 嵌套。 */
3771
+ private readonly execCtxAls;
3772
+ /** [review MF1] record 级在途 resume 守卫。resumeRound 全部守卫通过后 add,
3773
+ * runAndFinalize 结束(finally,覆盖轮次完成 / MF-6 失败回退 / abort / 终态化所有分支)时
3774
+ * delete(幂等:execute() 新建 record 不在集合,no-op)。窗口 = resume 发起(含 pool.acquire
3775
+ * 排队)→ 本轮 runAndFinalize 收尾。窗口内同 record 再次到达 resumeRound(冷路径重入 /
3776
+ * EPIPE 兜底)直接 throw——防两个 pi 子进程以 --session 同一 JSONL 双写 + 前一个脱离
3777
+ * kill 记账成孤儿(deliverMessage 冷路径的 acquireActivateLock 只覆盖 resumeRound 同步段,
3778
+ * 锁释放在子进程注册(session-runner spawnedChildren.set)之前,锁空洞由此守卫兜住;
3779
+ * EPIPE 兜底不持锁,同样被覆盖)。child 注册完成后 deliverMessage 走热路径,不经此守卫。 */
3780
+ private readonly resumesInFlight;
3781
+ private readonly manifestStore;
3782
+ /**
3783
+ * [T1/PS-9] subagent sessionDir(getSubagentSessionDir 推导,与 store 同源同一 rootCwd)。
3784
+ * 传给 doFinalizeRecord 的 FinalizeDeps.sessionDir——record.sessionFile 缺失时 finalize
3785
+ * 用它做磁盘 identity 反查(marker/alive 清理的依据)。
3786
+ */
3787
+ private readonly sessionsDir;
3788
+ constructor(init: SubagentServiceInit);
3789
+ /** 覆盖 UI 请求 handler(W3: index.ts session_start 时按 mode 注入 handler 后调)。
3790
+ * 委托 uiObservability 重置缺失告警去重——新 handler 就位后允许重新 warn。 */
3791
+ setUiRequestHandler(handler: UiRequestHandler | undefined): void;
3792
+ /** session-runner handleUiRequest 在 handler 缺失时调用(FR-9 可观测性)。
3793
+ * 委托 uiObservability:按 session 去重,同一 session 的多次 UI 请求只 warn 一次。
3794
+ * W2: console.warn 兜底。W3 接入 pi.appendEntry("subagent:ui-request-missing-handler", ...)。 */
3795
+ notifyMissingHandler(sessionId: string): void;
3796
+ /** session_start 注入 pi + revive(modelRegistry/entries 归 ModelConfigService.initModel)。 */
3797
+ initSession(init: SubagentServiceSessionInit): void;
3798
+ /**
3799
+ * [SPAWN fork depth 跨进程传递] fork 链深度基线:子进程被父 spawn 时,父通过 env
3800
+ * PI_SUBAGENT_FORK_DEPTH 传入当前 fork 链深度。子进程 session_start 时读取作为
3801
+ * forkDepthAls 基线,使后续嵌套 spawn fork 能从正确深度递增。未设置(顶层主
3802
+ * session)→ 基线 0。enterWith 贯穿整个 session 生命周期。
3803
+ */
3804
+ private initForkDepthBaseline;
3805
+ /**
3806
+ * [递归可见性] exec 上下文基线:子进程读 env PI_SUBAGENT_SELF_RECORD_ID / DEPTH
3807
+ * 建立身份基线后,createRecordForMode 读 execCtxAls 自动正确(孙挂到子名下)。
3808
+ * enterWith 贯穿整个 session 生命周期(与 forkDepthAls 同构,决策 4)。
3809
+ */
3810
+ private initExecContextBaseline;
3811
+ /**
3812
+ * 孤儿终态恢复(residual-fixes):session_start 主动触发一次——父扩展死后再无人写
3813
+ * 终态 entry 的 record 在此判定落盘(否则侧栏永久 running)。幂等不 throw,失败不
3814
+ * 阻断 session_start。
3815
+ *
3816
+ * [T5① / PS-8] 只有根进程做扫描者:恢复机制假设「单扫描者」,但子进程 sessionRootId
3817
+ * 经 env 与父同值(过滤域 = 整树共享的 sessions/records 目录),env 贯穿让每个子进程
3818
+ * 都成了扫描者——递归编排中任一子进程启动时,恰有兄弟记录 marker 缺失或超软超时
3819
+ *(hours-long wave 必然命中)→ 活记录被无关进程盖 .finalized sidecar,closed entry
3820
+ * 写进别的进程的 session 文件(跨进程互写,无任何锁)。子进程身份判据 = env
3821
+ * PI_SUBAGENT_SELF_RECORD_ID(父 spawn 时注入的「子进程自己的 record id」,仅子进程
3822
+ * 非空)——与 execCtxBaseline 同源。根进程恢复语义不变。
3823
+ */
3824
+ private recoverOrphansIfRootProcess;
3825
+ /** 孤儿终态恢复委托(RecordStore.recoverOrphanRecords 的唯一公开入口,维持 store
3826
+ * private 封装——与 recoverManifestTmpFiles 同模式)。判定语义见 store 侧注释。
3827
+ * 随后跑 entry-born 孤儿恢复(无子文件锚的 register-only record,spawn 窗口期死亡,
3828
+ * E2E 实测缺口)——主 session 文件经 getMainSessionFile 注入(构造期可空)。 */
3829
+ recoverOrphanRecords(): void;
3830
+ /** 启动恢复:扫描 manifest tmp 残留(崩溃打断的 writeManifest 留下的 *.json.tmp.<pid>),
3831
+ * 3 分支判定(manifest已存在删tmp / tmp合法promote / tmp非法删)。幂等,不 throw。
3832
+ * ADR-035 启动恢复接线——session_start 每次都调(与 maybeCleanupExpiredSessionFiles 一致)。
3833
+ * manifestStore 保持 private 封装,本方法是唯一公开入口。 */
3834
+ recoverManifestTmpFiles(): Promise<{
3835
+ deleted: number;
3836
+ recovered: number;
3837
+ }>;
3838
+ /** SP-4: 关闭所有活跃 record。
3839
+ *
3840
+ * 遍历 store 中所有 running record,逐个 CAS 转终态 + completeRecord + archive。
3841
+ * 对有 worktreeHandle 的 record 触发 worktreeManager.cleanup(T3: worktree 绑定清理)。
3842
+ *
3843
+ * [T2⑥ / PS-1] 补齐三回收面(对照 dispose() 的既有形态,消除同文件双标):
3844
+ * controller.abort + kill(收敛到 killChildWithEscalation)+ disarmIdleTimer +
3845
+ * disarmSettledWatchdog。旧实现只关 record 不中止执行——「record 已关」≠「执行已
3846
+ * 处置」:在途子进程继续跑且无任何用户可及的取消通道(cancel 只查内存 running,
3847
+ * archive 后恒 false),若挂死唯一上界是默认关闭的 spawn watchdog → 泄漏至宿主退出。
3848
+ * abort/kill/timer 三面对已终态/已死 record 均幂等 no-op,dispose() 先行的
3849
+ * abortRunningControllers + killAllSpawnedChildren 不受影响(parent-shutdown 路径
3850
+ * 双保险)。
3851
+ *
3852
+ * [v4 A-6] 旧实现的 recentlyCascaded 收集(供已删除的 before_agent_start 注入告知)
3853
+ * 与 drainCascaded 已一并移除——被关 record 的告知改由 list 的 closedReason 表达。
3854
+ *
3855
+ * @param reason 关闭原因(parent-fork / parent-new / parent-shutdown)
3856
+ * @returns 被关闭的 record 数量
3857
+ */
3858
+ disposeAllRecords(reason: ClosedReason): number;
3859
+ /** SP-4: /fork 新 session 时清理旧 record。
3860
+ * 调用 disposeAllRecords("parent-fork")。由 index.ts 的 session_before_fork handler 触发。 */
3861
+ onParentFork(): number;
3862
+ /** SP-4: /new 创建全新 session 时清理旧 record。
3863
+ * 调用 disposeAllRecords("parent-new")。由 index.ts 的 session_before_switch
3864
+ * (reason==="new")handler 触发。 */
3865
+ onParentNew(): number;
3866
+ /** SP-4: idle record GC(30 天 TTL,实现抽至 idle-gc.ts)。stop 函数(dispose 调)。 */
3867
+ private stopIdleGc;
3868
+ /** 启动 idle record GC 定时器(session_start 调用,幂等)。 */
3869
+ startGcTimer(): void;
3870
+ /** 停止 idle record GC 定时器(dispose 调用)。 */
3871
+ private stopGcTimer;
3872
+ /** session 结束清理(清定时器,丢弃 pending 通知)。幂等。
3873
+ *
3874
+ * [M-7] dispose 顺序假设:pending:unregister emit 依赖 pending-notifications 扩展的
3875
+ * listener 仍然存活。若 pending-notifications 先于本扩展执行 session_shutdown(后注册
3876
+ * 先执行的语义下会如此),listener 已注销,unregister 事件被静默丢弃。这是可接受的
3877
+ * 退化——进程退出后两侧状态本就不保证一致,下次 session_start 的 crash recovery 会修正。 */
3878
+ dispose(): void;
3879
+ /**
3880
+ * [T4④ / PS-5] shutdown flush 被门拦时把未投递 pending 复写落盘(供重启 replay)。
3881
+ *
3882
+ * 触发条件:flushPendingNotifications 后 ledger 仍有 pending(isIdle 门拦 / sendDelivery
3883
+ * 受理失败的残留)且主 agent 非 idle——即本次 shutdown 注定投不出去。落盘动作 =
3884
+ * pi.appendEntry 重写 NOTIFY_LEDGER_CUSTOM_TYPE entry(与 ledger.record 同通道同 schema,
3885
+ * notifyId 幂等:恢复扫描按后写覆盖 + ack/abandoned 差集去重,重复账面不产生重复投递)。
3886
+ * 主 agent idle 时 flush 已投出,无需复写(零开销)。
3887
+ */
3888
+ private persistUndeliveredNotificationsForReplay;
3889
+ /** background 完成回注(record → BgNotifyRecord 映射 + notifier.notify)。
3890
+ * 正在执行(running + 活进程 + 非 timer-armed)静默跳过——notify 只对 closed(终态)、
3891
+ * isIdle(chatMode 轮次完成)或 isResumable(SP-5 one-shot 成功完成 / MF-6 失败轮回退)有意义。
3892
+ * SP-1: closed 统一终态(done/failed/crashed 合并),closedReason 携带 L2 原因。 */
3893
+ private notifyComplete;
3894
+ /** [C-1] chatMode close 终态通知(设计 D2:正文空/本轮增量 + sessionFile 指针行)。
3895
+ *
3896
+ * 与 notifyComplete 的差异只在 dedup 身份与轮次统计:终态通知必须与最后一轮的轮次通知
3897
+ * 区分(轮次通知 key=`id:round`),否则同 key 被 60s dedup 吞——close 后父 agent 永远
3898
+ * 收不到带指针行的终态通知(审查 C-1)。故 round 置 undefined(key 回退为裸 id),
3899
+ * 轮数改经 totalRounds 进文案 "completed after N rounds."(C-2)。
3900
+ *
3901
+ * 仅 chatMode close 语义调用(closeChatIdle / closeAfterRoundSettled 终态化成功后)。
3902
+ * one-shot 显式拒绝(G4:one-shot close 路径现状无终态通知,字节不变);cancel 走
3903
+ * cancelBackground 自己的 notifyComplete,不经本方法。幂等性:两条 close 路径均由
3904
+ * closeSubagent 的 status 分流守卫(closed 后幂等 no-op)/ CAS 抢锁保证只执行一次,
3905
+ * 本方法自身不重复发送;迟到的 kickOffBackground.then 通知与轮次通知同 key=`id:round`,
3906
+ * 60s 窗内仍被吞,不构成第三条。 */
3907
+ /** @param emptyBody true = 终态通知正文置空串(D2 路径②)。W16 P-1 修复后
3908
+ * closeChatIdle 的 doneResult.text 改用 record.result 保真(close 终态
3909
+ * subagent-record entry 的 result 不抹空轮终真实值),「正文空」不再由合成空
3910
+ * text 的副作用承载,改为显式参数——持久化 result 与通知正文两个关注点解耦。 */
3911
+ private notifyClosed;
3912
+ /** notifier 的 NotifierHost 适配器(绑定到 pi.sendMessage + store 查询)。 */
3913
+ private piAdapter;
3914
+ /** record → BgNotifyRecord(notifier.notify 入参映射,内部不外露)。
3915
+ * v4 B-1:守卫放行 closed(终态,含 cancelled)、isIdle(对话模式轮次完成,notify 主 agent G1)
3916
+ * 或 isResumable(running + 无活进程——SP-5 one-shot 成功完成 / MF-6 失败轮回退)。
3917
+ * 正在执行(running + 活进程 + 非 timer-armed)返回 undefined(调用方 notifyComplete 跳过)。
3918
+ * SP-1: closed 统一终态,closedReason 由 BgNotifyRecord 携带。 */
3919
+ private toNotifyRecord;
3920
+ /**
3921
+ * 预解析 model(renderCall 标题行用,同步)。代理 modelService.resolveModel。
3922
+ * 仅解析 override/agentConfig 路径;ctxModel 缺失时拋错,调用方 catch 降级。
3923
+ */
3924
+ resolveModel(agent: string, override?: {
3925
+ model?: string;
3926
+ thinkingLevel?: string;
3927
+ }, ctxModel?: ModelInfo, agentConfig?: AgentConfig): ResolvedModel;
3928
+ /**
3929
+ * 统一执行入口。mode 固定 background(sync 已删除)。
3930
+ * 内部完成:模型解析 → 执行 → 收尾。
3931
+ *
3932
+ * @param opts.ctxModel 主 agent 当前模型(模型解析第三层兼底)。undefined 时仅依赖 override/agentConfig。
3933
+ */
3934
+ execute(opts: ExecuteOptions): Promise<ExecutionHandle>;
3935
+ /**
3936
+ * 按 id 查内存 running record 的只读快照(G3-002 修复)。
3937
+ * 不从 session.jsonl 重建(cancel/list 单点查询只关心内存 running record)。
3938
+ * 供 tool 层 cancelHandler 翻译 throw 用(id 不存在 / mode / 终态三种错误)。
3939
+ * 不存在返回 undefined。
3940
+ */
3941
+ findRecord(id: string): RecordSnapshot | undefined;
3942
+ /** 取消 background record(tryTransition CAS 抢锁防重复副作用)。 */
3943
+ cancel(id: string): boolean;
3944
+ /**
3945
+ * [v8.5 A1/B] 全态查找:任意状态(running/closed)× 任意归属(含异 root session)的
3946
+ * record 快照。供 message 拒绝文案分流(A1)与 fork-from 源解析(B)共用。
3947
+ *
3948
+ * 与 getRecordForAction 的差异:不做归属/直接父校验、不重建可变 record 入内存,
3949
+ * 只读快照(light 形态可能缺详情重数据,身份/sidecar 状态字段齐全)。查询顺序与
3950
+ * getRecordForAction 冷路径同款(idToFile 索引直查 → collectRecords 全扫兑底),
3951
+ * 不限 status——终态(sidecar closed)记录也能查到。
3952
+ *
3953
+ * 返回 undefined:id 在内存与磁盘均不存在。
3954
+ */
3955
+ lookupRecordAnyState(id: string): SubagentRecord | undefined;
3956
+ /**
3957
+ * idle 投递:resume spawn 开启新一轮对话(设计决策 6 idle 分支)。
3958
+ *
3959
+ * record 必须 idle(轮次完成、进程已回收、record 留内存)。手动把 status 设回 "running"
3960
+ * (M2-A 边界:idle→running 是恢复非终态,绕过 tryTransition——tryTransition 要求当前态
3961
+ * running 才 CAS,idle record 直接进 runAndFinalize 会被 tryTransition 拒绝转态)。
3962
+ *
3963
+ * resume 参数从 record identity 读(防多轮对话模型漂移,探针 P-10):sessionFile、
3964
+ * model、thinkingLevel 均为 record 身份字段(创建时确定、不可变)。maxTurns/schema 等
3965
+ * 执行约束第一版不恢复(设计 §5 拆分 1 待验证检查点),agentConfig 用 undefined
3966
+ * (pi --session 续写保留上下文,agent 行为由 session 内 messages 决定;M2-B3 messageHandler 可完善)。
3967
+ *
3968
+ * detached 编排(参照 kickOffBackground):不 await,runAndFinalize 在 background 跑。
3969
+ * chatMode + done 时 runAndFinalize 的 M2-A 分流自动把 record 重新置 idle。并发槽在
3970
+ * runAndFinalize 内重新 acquire(轮次间 idle 已 release);pool.acquire 是排队模型,
3971
+ * 池满时排队等待槽位而非 throw(与 execute 一致)。
3972
+ *
3973
+ * @param record 目标 record(必须 idle)
3974
+ * @param text 新一轮消息正文
3975
+ * @throws Error record 非 idle / 无 sessionFile / 无 controller
3976
+ */
3977
+ resumeRound(record: ExecutionRecord, text: string): void;
3978
+ /**
3979
+ * [V2 决策 3] chatMode 统一投递:按**进程死活**分流,不按 record.status。
3980
+ *
3981
+ * V2 进程长驻——chatMode record 首轮 agent_settled 后进轻量 idle(Step 4a:进程保活、
3982
+ * idle timer armed),续聊时进程仍在内存,不该重开 session。故续聊投递不按 status
3983
+ *(running/idle 都可能是热路径),而是判进程死活:
3984
+ *
3985
+ * 热路径(进程活):prompt + streamingBehavior——pi 权威裁决 busy/idle(F3/F4)。
3986
+ * busy(isStreaming)时 followUp 入队/steer 抢占;idle 时 streamingBehavior 被忽略、
3987
+ * 直接开新 turn。不用 steer/followUp 命令、不依赖 clearQueue(F8),结构上消除残留。
3988
+ * 冷路径(进程死):复用 resumeRound 重开 session + prompt(仅崩溃/timeout kill/跨重启命中)。
3989
+ *
3990
+ * disarm idle timer:新 turn 开始必须 disarm(V2 决策 4),防 turn 期间 idle timer 误杀活进程。
3991
+ *
3992
+ * status 处理:判活分流后**各自**设 running——热路径手动设 running(新 turn 开始);
3993
+ * 冷路径由 resumeRound 校验 idle 并自行设 running + spawn(故不在此预设 running,否则
3994
+ * resumeRound 的 idle 检查会 throw)。resume spawn 后 session-runner 回填 record.pid,
3995
+ * 热路径拿到 child 时也顺便刷新 pid(resume 重开进程后 pid 已变)。
3996
+ *
3997
+ * [review 修复] 曾对比的 deliverToRunning(非 chatMode busy 投递 + pendingMessages
3998
+ * 消费确认制)已删除——SP-5 upgrade 后无生产调用方(V2 决策 3 已删消费确认制)。
3999
+ *
4000
+ * @param record 目标 record(chatMode,running 或 idle)
4001
+ * @param text 消息正文
4002
+ * @param interrupt true=steer(抢占)/ false=followUp(排队),仅热路径 prompt streamingBehavior 用
4003
+ */
4004
+ deliverMessage(record: ExecutionRecord, text: string, interrupt: boolean): Promise<void>;
4005
+ /**
4006
+ * [T2③] 热路径轮 settled watchdog 到期处置(对齐 u-t2a 首轮形态:kill + 该轮失败
4007
+ * 终态化 + 失败通知,error 含 'settled watchdog' 标记与恢复指引)。
4008
+ *
4009
+ * 与首轮的差异:runSpawn 已返回(无收尾链路承接 settledWatchdogFired 标记),失败
4010
+ * 终态化在本回调内完成。chatMode 按 MF-6 语义回退 running-resumable(与首轮 watchdog
4011
+ * 经 runAndFinalize 失败分支的最终形态一致——对话可冷路径复活);非 chatMode 终态
4012
+ * 销毁。CAS(tryTransition closed+gc)防与 cancel/dispose 双收尾,抢锁失败即跳过。
4013
+ *
4014
+ * 回调在 timer 触发的同步上下文执行:同步段只做 kill + CAS(不抛),异步收尾
4015
+ * fire-and-forget 且 catch 归 bestEffort——错误逃出回调 = uncaughtException 崩宿主。
4016
+ */
4017
+ private onHotPathSettledWatchdogTimeout;
4018
+ /**
4019
+ * [T2⑧ / PS-3] 非 EPIPE 热路径失败后的 idle timer 再武装(防泄漏底线)。
4020
+ *
4021
+ * record.idleTimeoutMs 已在 spawn 入口经 assertIdleTimeoutMsSafe 校验(T4②),此处
4022
+ * armIdleTimer 理论不 throw;降级链仍保底:非法 → 挂 DEFAULT_IDLE_TIMEOUT_MS + warn
4023
+ * (兜底可见,对齐 session-runner agent_settled 侧的 T4② 降级形态),双重失败退回
4024
+ * 「不挂」但留 error 痕。
4025
+ */
4026
+ private rearmIdleTimerAfterHotPathFailure;
4027
+ /**
4028
+ * 按 id 查 record 并做归属校验(message/close action 的统一入口)。
4029
+ *
4030
+ * 设计决策 3(归属守卫):校验 record.rootSessionId 必须等于当前 session 的根 id
4031
+ *(this.sessionRootId)。不匹配 / 不存在统一抛「not found or not owned」——不区分
4032
+ * 两种失败,防信息泄露(无法通过错误消息探测其他 session 的 subagent id)。
4033
+ *
4034
+ * 同进程内 running + idle record 都在内存(getMutable);终态 record 已 archive。
4035
+ * 跨重启(SP-2)内存空时,从磁盘 collectRecords 重建 idle record 并 register 进内存。
4036
+ * reconstructAll 已将跨重启 record(无 sidecar marker + pid 死)标记为 running(v4 B-1 跨重启可续聊语义,record-store buildRecord 分支 4),
4037
+ * collectRecords 返回的 SubagentRecord 可直接转为可变 ExecutionRecord 供续操作。
4038
+ *
4039
+ * @param id subagent record id
4040
+ * @param opts.allowReconnect [v8.5 D] message 专属:冷查额外接受「可重连」的 closed 记录
4041
+ * (死因∈ RECONNECTABLE_FINAL_REASONS,A 档真实死因 sidecar 是唯一准入门),经四重守卫后
4042
+ * resurrectClosed 回边为 running 并续写原 session 文件。仅 message 开启;close/cancel 维持单向终态语义。
4043
+ * @returns 可变 ExecutionRecord(message/close handler 直接操作)
4044
+ * @throws Error record 不存在 / 非本 session 所有(含恢复指引)
4045
+ * @throws ResurrectDeniedError 命中可重连集但被 worktree/异进程活实例守卫拦截(自带完整行动语言)
4046
+ */
4047
+ getRecordForAction(id: string, opts?: {
4048
+ allowReconnect?: boolean;
4049
+ }): ExecutionRecord;
4050
+ /** SP-2 冷路径(getRecordForAction 内存未命中分支的提取):从磁盘重建可变 ExecutionRecord
4051
+ * 并 register 进内存。[perf] 先走 idToFile 索引直查(单文件 stat 校验),未命中(进程重启后
4052
+ * 尚未扫描、索引未热)才全目录 collectRecords 兜底建索引——跨重启后每条 message 从
4053
+ * 「readdir + N×4 stat 全扫」降为单文件校验。
4054
+ * @returns 重建的 record;磁盘也无则 undefined
4055
+ * @throws ResurrectDeniedError 可重连候选被 worktree/异进程活实例守卫拦截 */
4056
+ /** 冷查候选定位(coldLookupForAction 步骤 1):idToFile 索引直查 running 命中,
4057
+ * 未命中再全目录 collectRecords 兜底(running,或 allowReconnect 且可重连 closed)。
4058
+ *
4059
+ * [T5③ / PS-7b] running 候选异进程活实例守卫:冷查 running 候选(跨重启 / 内存重建)
4060
+ * 此前不经任何探针直接 resurrect + resume spawn——若其 .alive marker 仍指向活着的
4061
+ * 异进程实例(父进程重启后旧子进程尚存的窗口),resume 会 spawn 第二个 pi 子进程
4062
+ * 写同一 session JSONL(本代码最忌惮的双写者形态,v4 A-5/P7 事故模式)。closed 候选
4063
+ * 的同款守卫已在 assertReconnectAllowed(v8.5 D);本守卫闭合 running 候选的防御
4064
+ * 不对称。marker 的 pid 是子进程 pi 的 pid(非父进程),本进程持有的 running record
4065
+ * 恒在内存(archive 才移出),可达本冷查分支的 running 候选必然来自磁盘重建——
4066
+ * 探针命中即拒绝(ResurrectDeniedError,与 closed 候选守卫同异常类型,错误含 pid
4067
+ * 与恢复指引)。 */
4068
+ private findColdLookupCandidate;
4069
+ /** 可重连守卫(coldLookupForAction 步骤 2,[v8.5 D]):先于任何状态突变与注册。
4070
+ * worktree 绑定丢失 / 异进程活实例以 ResurrectDeniedError 抛出(endedMessageGuard
4071
+ * 必须原样透传,不得改写为 fork-from 指引误导 agent 走已被判死的通道);拒绝时
4072
+ * 内存不得残留该记录(findRecord 契约)。 */
4073
+ private assertReconnectAllowed;
4074
+ /** 磁盘候选重建为可变 record 并 register + 上报(coldLookupForAction 步骤 4)。 */
4075
+ private resurrectColdRecord;
4076
+ private coldLookupForAction;
4077
+ /** [v8.5 D] 冷查候选过滤:closed 且死因落在可重连集。判定源 = closedReason(buildRecord
4078
+ * 归一化后的对外字段:A 档真实死因直通、旧空 sidecar 兑底 disconnected——SubagentRecord
4079
+ * 不暴露 raw finalizedReason);cancelled/user-close/gc 等主动关闭与自然完成死因天然不在集合内。
4080
+ * 防线在集合本身而非调用点。 */
4081
+ private isReconnectableClosed;
4082
+ /**
4083
+ * close action 的统一行为分流(running 子态 × force)。
4084
+ *
4085
+ * running + force:true → cancelBackground(显式 SIGTERM + closed+cancelled 终态)
4086
+ * running + force:false + 无在跑轮 → closeChatIdle(立即终态化 done + 回收保活进程 + disarm timer)
4087
+ * (isIdle timer armed 或 isResumable 无活进程)
4088
+ * running + force:false + 有活进程在跑轮 → 置 closeAfterRound=true(轮完成时终态化:
4089
+ * chatMode 消费点在 onRoundSettled,非 chatMode 在 runAndFinalize CAS 分支)
4090
+ * 其他终态 → 幂等 no-op(已结束)
4091
+ *
4092
+ * 与设计决策 5 一致:close = 正式终态(走 finalize),force 只影响 running 时机。
4093
+ *
4094
+ * @param record 目标 record(getRecordForAction 已校验归属)
4095
+ * @param force true=立即终止(running 时 SIGTERM)/ false=优雅关闭(running 时等轮完)
4096
+ */
4097
+ closeSubagent(record: ExecutionRecord, force: boolean): Promise<void>;
4098
+ /**
4099
+ * 无在跑轮 record 的手动终态化为 done(close action 的 isIdle/isResumable 分支)。
4100
+ *
4101
+ * 无在途 AgentResult(轮次完成时 record 未冻结,turns[] 保留运行时状态),
4102
+ * 构造合成 done result(对齐 cancelBackground 的 cancelledResult 模式)。
4103
+ * 走 doFinalizeRecord 的完整终态化路径(completeRecord + archive + finalized + worktree
4104
+ * cleanup + alive marker + manifest)。
4105
+ *
4106
+ * [M5] 覆盖两路:Path B(无活进程,同旧行为)与 Path A(idle timer armed、进程保活等待
4107
+ * 续聊)。Path A 必须先显式回收进程 + disarm timer——否则 record 已终态化但保活进程
4108
+ * 继续驻留(终态后无人再杀它:closeSubagent 不再来、idle timer 已 disarm、runSpawn
4109
+ * promise 早已 resolve),直到宿主进程退出。
4110
+ *
4111
+ * 不走 tryTransition(v4 B-1 此态 status=running,但由 doFinalizeRecord 内部的
4112
+ * completeRecord 直接覆盖 status,与 cancelBackground 对 record 的处理同构)。
4113
+ */
4114
+ private closeChatIdle;
4115
+ /**
4116
+ * [M5] closeAfterRound 消费:chatMode 轮次完成时终态化 record(closed + user-close)。
4117
+ *
4118
+ * 由 onRoundSettled(agent_settled 回调)调用——chatMode 轮次完成的统一汇聚点(热路径轮
4119
+ * 不经 runAndFinalize CAS 分支,旧消费点对 chatMode 不可达)。合成 result 沿用 record.result
4120
+ *(= 本轮增量,设计 D2 路径①):本轮增量已由调用方前置的轮次通知送达,终态通知正文因此
4121
+ * 是同一段增量 + 轮次统计 + sessionFile 指针行(notifyClosed),不重发全历史。
4122
+ *
4123
+ * 时序:同步前缀(disarm + kill + CAS)在 session-runner 的 resolveRun(0) 之前执行完——
4124
+ * 冷路径轮的 runAndFinalize 续体因 timer 已 disarm 跳过 early return,但其 tryTransition
4125
+ * CAS 对已 closed 的 record 失败 → 跳过二次 finalize(无双收尾);热路径轮无 runAndFinalize
4126
+ * 续体,本方法是唯一收尾。冷路径续体 .then 的 notifyComplete 与轮次通知同 key=`id:round`,
4127
+ * 60s dedup 吞(不与下方终态通知叠加成第三条——后者 key 是裸 id)。
4128
+ */
4129
+ private closeAfterRoundSettled;
4130
+ /**
4131
+ * workflow 编排层专用:sync-await 接口,内部走 background 管道但返回 Promise<AgentResult>。
4132
+ *
4133
+ * 与 execute() 的区别(D-A1):
4134
+ * 1. 返回 workflow AgentResult(content 字段),非 ExecutionHandle
4135
+ * 2. 不调 kickOffBackground → 不注入 followUp 完成通知(BC-11,结果直接返回 workflow)
4136
+ * 3. T2 删 sync 时 executeAndAwait 不受牵连(独立方法)
4137
+ *
4138
+ * 共享:runSpawn + ConcurrencyPool + record + pending emit(D-A4)。
4139
+ */
4140
+ executeAndAwait(opts: ExecuteOptions, signal?: AbortSignal, onEvent?: (event: AgentEvent) => void, stream?: SubagentStream): Promise<AgentResult>;
4141
+ /** 订阅 store 变更(widget/list requestRender)。返回取消订阅。 */
4142
+ onChange(listener: () => void): () => void;
4143
+ /** 列出 running record 快照(widget 计数用)。 */
4144
+ listRunning(): RecordSnapshot[];
4145
+ /** 合并内存(running) + 磁盘(session.jsonl 重建) record(/subagents list + tool list 消费)。
4146
+ * 按 rootSessionId 过滤:根进程=本 session(sessionRootId===sessionId);
4147
+ * 子进程=env 贯穿的真 ROOT(sessionRootId≠sessionId)→ 看到整棵 ROOT 树(决策 3)。
4148
+ * [perf] 磁盘源为 light(头部 identity + 状态,无 eventLog/result/turns 等重数据)
4149
+ * ——列表/补全/hasRunning 够用;详情场景调 getFullRecord(id) 懒加载补齐。 */
4150
+ collectRecords(limit: number, statusFilter?: StatusFilter): SubagentRecord[];
4151
+ /** [perf] 单 record 详情懒加载(全量:eventLog/displayItems/result/turns/tokens)。
4152
+ * 内存 running record 直接投影;磁盘 record 全量重建(per-file 缓存,stat 戳校验)。
4153
+ * 返回 undefined:id 不存在于内存与磁盘。 */
4154
+ getFullRecord(id: string): SubagentRecord | undefined;
4155
+ /** 步骤 1:身份解析。agentConfig → resolveModel(三层:override → agentConfig → 主 agent model)。 */
4156
+ private resolveIdentity;
4157
+ /** 步骤 2:按 mode 生成 id + controller,创建 record 并注册。
4158
+ * [L-1] ExecutionMode 类型固定 "background"(sync 已删除),id/controller 分支简化。 */
4159
+ private createRecordForMode;
4160
+ /** [MF#R4] worktree 前置失败的 early-return handle。
4161
+ * record 已被 finalizeFailed 收尾为 failed、detached promise 从未启动。 */
4162
+ private buildEarlyFailedHandle;
4163
+ /**
4164
+ * 路由到非 pi 引擎的执行入口:routeEngine(注册表校验 + probe/守卫)已由 execute
4165
+ * 完成——这里只剩 unsupported 预检 → record 创建+盖章 → detached 引擎 run。
4166
+ * 全部同步拒绝发生在 record 创建前(不产生孤儿 record)。
4167
+ */
4168
+ private executeViaEngine;
4169
+ /**
4170
+ * 非 pi 引擎的 unsupported 参数预检(D11 处置「调用前拒绝」的判据 = capabilities)。
4171
+ * conversation / fork / worktree 三参数对首期接入的引擎(zcode)均不可用:
4172
+ * conversation 依赖同进程 idle 复用、fork 依赖父 pi session 上下文继承、worktree 依赖
4173
+ * 文件隔离(capabilities.sandbox='none')。同步 throw,文案含 capabilities 依据与恢复指引。
4174
+ */
4175
+ private assertEngineParamSupport;
4176
+ /**
4177
+ * 非 pi 引擎的 detached 执行编排(与 kickOffBackground 同构的 background 语义):
4178
+ * pool 并发槽(maxConcurrent 对非 pi 引擎同样生效)→ journal 接线(D6 第②级:
4179
+ * taskId=record.id,初始池 key 占位 'shared',onPoolResolved retarget 到引擎实际
4180
+ * 池 key——路径与 paths.ts 同源推导)→ engine.run(signal 接 record controller,
4181
+ * kill-chain 两级生效)→ engineHandle 回填(终态迁移落 entry 前)→ 终态迁移 →
4182
+ * bg notify(chat 域宿主职责,与 pi 完成通知同语义)。
4183
+ */
4184
+ private kickOffEngineRun;
4185
+ /**
4186
+ * kickOffEngineRun 的 acquire 后主体:journal 接线(D6 第②级:taskId=record.id,
4187
+ * 初始池 key 占位 'shared',onPoolResolved retarget 到引擎实际池 key)→ engine.run
4188
+ * (signal 接 record controller,kill-chain 两级生效)→ engineHandle 回填(终态迁移
4189
+ * 落 entry 前)→ 终态迁移。bg notify 归编排侧(与 kickOffBackground 收尾通知归编排对称)。
4190
+ */
4191
+ private runEngineTask;
4192
+ /**
4193
+ * 分层并发配额:depth 越深可用配额越少(下限 1)。fork 深度护栏在池维度的投影,
4194
+ * 公式约定以 concurrency-pool.ts 注释为登记处、此处为唯一代码锚点。
4195
+ */
4196
+ private effectiveMaxConcurrentFor;
4197
+ /**
4198
+ * engine.run resolve 的终态迁移:outcome.error → failed(success=false + error 文案);
4199
+ * 否则 done(result=content)。CAS 抢锁(tryTransition)防与 cancelBackground 双收尾。
4200
+ */
4201
+ private finalizeEngineOutcome;
4202
+ /** 共享的"干活 + 收尾"——sync 直接 await,background 在 detached 里调。 */
4203
+ private runAndFinalize;
4204
+ /** background 的步骤 4-6:包进 detached promise(不 await),execute 立即返回。 */
4205
+ private kickOffBackground;
4206
+ /**
4207
+ * 取消 background record。CAS 抢锁(tryTransition)——抢到则 notify + 写 tombstone;
4208
+ * 没抢到(detached 已 finalize,record 已终态)返回 false,不触碰任何收尾副作用。
4209
+ *
4210
+ * stop 手段(abort/kill/disarm)无条件先执行:对已终态 record 幂等无害,且保证
4211
+ * cancel 语义 = 进程必死;收尾副作用(completeRecord/tombstone/archive/notify)只归
4212
+ * CAS 赢家。[A2-1] 此前 CAS 被误删,cancel 可在 doFinalizeRecord Step 0 await 窗口
4213
+ * 命中已终态 record——覆写终态 + tombstone/finalized 双标 + notify 双发 + 谎报 true。
4214
+ */
4215
+ private cancelBackground;
4216
+ /**
4217
+ * D-017 时序收尾:委托 doFinalizeRecord(提取到 finalize-record.ts,降低本文件行数)。
4218
+ * [Critical #1] cleanup 全部在 manifest 写之前,manifest best-effort 不阻断(详见 finalize-record.ts)。 */
4219
+ private finalizeRecord;
4220
+ /**
4221
+ * 对话模式轮次完成收尾:委托 doFinalizeRoundToIdle(record 进 idle,保留内存 + worktree)。
4222
+ * 与 finalizeRecord 对称的委托方法,deps 同源注入。chatMode + done/failed/cancelled 时由 runAndFinalize 调用
4223
+ *(MF-6:chatMode 失败/取消也回退 idle 而非终态销毁)。 */
4224
+ private finalizeRoundToIdle;
4225
+ /** run() 创建期异常的收尾(H1 修复):createAndConfigureSession 失败会抛,本方法合成 failed
4226
+ * AgentResult → CAS 抢锁 → finalizeRecord(与正常路径同形)。返回合成 result 供 runAndFinalize
4227
+ * 继续返回(不 re-throw,swallow 策略)。 */
4228
+ private finalizeFailed;
4229
+ /** S1: 排队中被 abort 走 cancelled 终态(对齐已运行被 abort 的 cancelBackground)。 */
4230
+ private finalizeAborted;
4231
+ /**
4232
+ * 校验 Service 就绪(pi 已注入 + 未 dispose)。
4233
+ *
4234
+ * dispose 后调用是异常路径:session_shutdown 已清资源,正常情况下紧接着
4235
+ * session_start 会 initSession 复活。若走到这里说明 session_start 没跟上
4236
+ * (RPC 边界 / reload 异常等),service 卡在 disposed 状态。
4237
+ *
4238
+ * 旧实现只抛 "hub disposed"——无信息,调用方和 AI 都看不懂,导致反复盲试。
4239
+ * 现在给出原因 + 恢复指引(重启会话或 /new)。真实错误文本会经 renderResult
4240
+ * 兜底透传到 AI(见 tool-render.ts extractResultError)。
4241
+ */
4242
+ private assertReady;
4243
+ /**
4244
+ * [T4② / PS-4] idleTimeoutMs 合法域入口校验(>2^31-1 / 非有限值 fail-fast)。
4245
+ *
4246
+ * 旧链路:非法值穿透到 agent_settled 回调里的 armIdleTimer → assertSafeTimerDelay
4247
+ * throw 被异步 catch 降级——配置错误被吞成静默语义变更(每轮完成通知被 isIdle 放行门
4248
+ * 吞 + 进程无回收 timer)。对齐 shared/timer-delay「不静默 clamp」既有裁决:配置错误
4249
+ * 显式暴露,错误消息含合法范围(0/负数 = 显式禁用是合法语义,不在本校验域;env 非法值
4250
+ * 由 lifecycle-manager 的 warn 回落承接)。execute/executeAndAwait 两入口共用。
4251
+ */
4252
+ private assertIdleTimeoutMsSafe;
4253
+ /** 构造 SessionRunnerContext(spawn 模式:无需 SDK 实例)。 */
4254
+ private buildSessionRunnerContext;
4255
+ }
4256
+ /**
4257
+ * [U10① D6] 第三宿主最小构造入口:仅凭参数注入构造 SubagentService(无全局查找)。
4258
+ *
4259
+ * 构造依赖(modelService / getMainSessionFile / uiRequestHandler)全部经 init
4260
+ * 参数注入;本工厂是 `new SubagentService(init)` 的薄包装,不读也不写
4261
+ * getSubagentService/setSubagentService 的全局槽位——session_start 单例流程
4262
+ * 行为零改动,宿主自持实例时用本工厂。构造内部行为与直接 new 逐字等价。
4263
+ *
4264
+ * @experimental execution 运行时面(设计 docs/design/subagent-core-sink-design.md §3.3 D6):
4265
+ * 一个 minor 周期内允许签名微调,稳定后转常规 semver 承诺。
4266
+ */
4267
+ declare function createSubagentService(init: SubagentServiceInit): SubagentService;
4268
+
4269
+ /** 自描述 record entry 的 customType。写点字面量与本常量的等值由
4270
+ * __tests__/record-store.test.ts 断言钉住(消费方引用本常量,勿用裸字符串)。
4271
+ *
4272
+ * @experimental execution 运行时面(U10① D6):一个 minor 周期内允许签名微调。 */
4273
+ declare const SUBAGENT_RECORD_CUSTOM_TYPE = "subagent-record";
4274
+ /**
4275
+ * `subagent-record` entry 的 data schema(v1)。
4276
+ *
4277
+ * = 完整 SubagentRecord 快照(GUI 侧列表/详情需要的全部持久化字段)+ 版本号。
4278
+ * 显式排除三个非持久化字段(与 SubagentRecord 的差集):
4279
+ * - currentActivity:running 时的瞬时流态,重开 session 无重建价值;
4280
+ * - externalInstance:跨重启探活态(pid/startedAt),由 .alive sidecar 重建;
4281
+ * - worktreeHandle:不可 JSON 序列化的运行时句柄(布尔投影 worktree 保留)。
4282
+ *
4283
+ * undefined 字段经 JSON.stringify 自然缺省(与 SubagentRecord 重建侧语义一致)。
4284
+ *
4285
+ * @experimental execution 运行时面(U10① D6):一个 minor 周期内允许签名微调。
4286
+ */
4287
+ interface SubagentRecordEntryData {
4288
+ /** schema 版本(W16 起 v1)。消费方按 v 判别解析,不认识的版本跳过而非猜测。 */
4289
+ v: 1;
4290
+ id: string;
4291
+ agent: string;
4292
+ /** 任务提示词(详情面板置顶展示)。 */
4293
+ task: string;
4294
+ /** 短标签(≤35 字符)。 */
4295
+ slug: string;
4296
+ status: ExecutionStatus;
4297
+ /** L2 关闭原因(仅 status="closed" 时有意义)。 */
4298
+ closedReason?: ClosedReason;
4299
+ mode: ExecutionMode;
4300
+ startedAt: number;
4301
+ /** 根 Pi session ID(session 隔离过滤用)。 */
4302
+ rootSessionId: string | undefined;
4303
+ /** 直接父 subagent record ID(层级树构建用)。顶层为 undefined。 */
4304
+ parentRecordId: string | undefined;
4305
+ /** subagent 递归深度。顶层 = 0。 */
4306
+ depth: number;
4307
+ endedAt: number | undefined;
4308
+ /** turn 计数。 */
4309
+ turns: number;
4310
+ totalTokens: number;
4311
+ model: string;
4312
+ thinkingLevel: string | undefined;
4313
+ /** 详情事件日志(/subagents 详情面板)。 */
4314
+ eventLog: AgentEventLogEntry[];
4315
+ /** 从 turns[] 派生的展示项。 */
4316
+ displayItems: DisplayItem[];
4317
+ result?: string;
4318
+ error?: string;
4319
+ sessionFile?: string;
4320
+ /** [MF#3] worktree 模式改动 patch 文件路径。 */
4321
+ patchFile?: string;
4322
+ /** 创建时是否启用 worktree 隔离。 */
4323
+ worktree?: boolean;
4324
+ /** 对话轮次计数(仅 chatMode 有意义;round+1 由轮终迁移写点携带)。 */
4325
+ round?: number;
4326
+ /**
4327
+ * 对话模式标志(residual-fixes):chat 与否——GUI 侧 done/waiting 细分判据
4328
+ * (one-shot 轮终 chatMode=false + result 有值 → 完成态)。register 起写入显式值
4329
+ * (one-shot 为显式 false);v1 前存量 entry 缺省,消费端按保守方向处理。
4330
+ */
4331
+ chatMode?: boolean;
4332
+ /** 执行态信号(residual-fixes):true = 无活进程驱动的 running(轮终/孤儿兜底)。 */
4333
+ resumable?: boolean;
4334
+ /**
4335
+ * 实际执行引擎 id(P4 路由留痕,D9①)。缺省(存量 entry)= pi 投影,消费方零迁移。
4336
+ */
4337
+ engine?: string;
4338
+ /** 引擎 fallback 留痕(probe 失败路由回默认引擎)。GUI 警告条数据源。 */
4339
+ engineFallback?: {
4340
+ from: string;
4341
+ reason: string;
4342
+ };
4343
+ /**
4344
+ * 引擎自描述定位符(U1:read 降级链①②级数据源)。引擎无关——sessionRef 整体
4345
+ * 透传不枚举内部键(zcode = { sessionId, dbPath });缺省 = pi(存量 entry 零迁移)。
4346
+ */
4347
+ engineHandle?: {
4348
+ sessionRef: Record<string, string>;
4349
+ journalPath?: string;
4350
+ poolKey: string;
4351
+ };
4352
+ }
4353
+ /** SubagentRecord → 自描述 entry data(快照投影,不 mutate 源)。
4354
+ *
4355
+ * @experimental execution 运行时面(U10① D6):一个 minor 周期内允许签名微调。 */
4356
+ declare function toSubagentRecordEntry(record: SubagentRecord): SubagentRecordEntryData;
4357
+
4358
+ /**
4359
+ * agent .md 的宽容解析结果(执行消费面全字段投影)。
4360
+ *
4361
+ * 双轨语义(D3 定稿):本结构是**执行可用性**面(可用即跑),严格路由面仍是
4362
+ * AgentMeta(注入清单可见性:缺 name/description 不进清单,由 meta !== null 表达)。
4363
+ * 两轨分离是两宿主既有设计,本类型只是把执行侧第三份手写实现(zsw mini parser)
4364
+ * 收敛到 core 单点。
4365
+ */
4366
+ interface AgentProfile {
4367
+ /** 宽容 name:frontmatter name,缺省文件 stem(宽松语义不拒)。 */
4368
+ name: string;
4369
+ /** 宽容 description:frontmatter description,缺省空串(宽松语义不拒)。 */
4370
+ description: string;
4371
+ /** frontmatter 后正文(trim)——即 systemPrompt。 */
4372
+ body: string;
4373
+ /** 路由提示(严格层投影;IF1 未通过时经 legacy fallback 也不取——该资产不进清单)。 */
4374
+ when?: string;
4375
+ examples?: RoutingExample[];
4376
+ model?: string;
4377
+ tools?: string[];
4378
+ /** 执行引擎 id(D9 per-agent)。 */
4379
+ engine?: string;
4380
+ thinkingLevel?: string;
4381
+ defaultBackground?: boolean;
4382
+ /** turn 预算上限(D3 可选执行字段;消费优先级:显式参数 > 本字段 > 缺省)。 */
4383
+ maxTurns?: number;
4384
+ /** tool denylist(D3 可选执行字段,与 tools allowlist 正交)。 */
4385
+ disallowedTools?: string[];
4386
+ /** agent 声明依赖的 skill 名清单(D3 可选执行字段)。 */
4387
+ skills?: string[];
4388
+ /**
4389
+ * IF1 严格层 meta:null = 无 frontmatter / 未闭合 / 严格校验未通过。
4390
+ * 注入清单可见性判定归它(discoverAgents 只放行 meta 非 null 的条目——
4391
+ * 与 pi 现装配循环口径等值)。
4392
+ */
4393
+ meta: AgentMeta | null;
4394
+ /**
4395
+ * 宽容解析降级说明(legacy fallback 生效等)。宽容语义不抛——资产异常时
4396
+ * 返回尽力解析结果 + warnings,调用方决定呈现(错误规格表:`{ name: stem, body,
4397
+ * warnings[] }` 宽容降级)。
4398
+ */
4399
+ warnings: string[];
4400
+ }
4401
+ /**
4402
+ * agent .md 宽容解析(D3/U2 定稿):无 frontmatter 不拒、name 缺省 stem、
4403
+ * description 缺省空串、返回 body 与执行字段全量,**永不抛**。
4404
+ *
4405
+ * 实现基础(D3 字段形态覆盖矩阵):
4406
+ * - 主路径 = parseResourceMeta(eemeli/yaml 全量解析):原生支持单行 key:value、
4407
+ * 行内数组、block-scalar、多行 `- item` 列表;
4408
+ * - meta=null 时(无 frontmatter / 未闭合 / yaml 整体解析失败 / IF1 严格校验未通过)
4409
+ * 经 extractYamlField legacy fallback(仅单行 key:value 形态)兜底取执行字段,
4410
+ * 保证「可用即跑」不因资产格式瑕疵丢失配置(先例:parseAgentWithMeta 的 MF-3
4411
+ * fallback)。fallback 触达时写入 warnings(warn 可见,调用方决定呈现)。
4412
+ *
4413
+ * 与 parseAgentWithMeta 的关系:本函数是新增导出面(执行消费面),既有
4414
+ * parseAgentWithMeta/loadByPath 语义零改动(含 engine 未注册 throw 的解析期校验)。
4415
+ * 本函数宽容语义不抛,故不做 engine 注册校验(执行校验归执行路径)。
4416
+ */
4417
+ declare function parseAgentProfile(text: string, filePath: string): AgentProfile;
4418
+ declare class AgentRegistry {
4419
+ /** 文件级 mtime 缓存(key=绝对路径,跨 loadByPath 保留)。 */
4420
+ private readonly fileCache;
4421
+ /**
4422
+ * 按绝对路径加载 agent(agentRef 统一解析入口——S2 路径统一)。
4423
+ *
4424
+ * - ~/ 前缀展开;相对路径/非 .md 引用返回 undefined(引用唯一形态 = 绝对路径)
4425
+ * - 文件不可读/不存在 → 驱逐缓存 + 返回 undefined(调用方给错误指引)
4426
+ * - mtime 未变复用 config 缓存;cache-miss 时 W4 lint 一次
4427
+ *
4428
+ * require 语义:require:true 时上述两类失败改为 throw(错误文案含
4429
+ * <available_subagents> 恢复指引),供「用户显式点名 agent」的调用点使用——
4430
+ * 显式 ref 失败是配置错误,必须显式报错而非静默降级(三通道对称审查)。
4431
+ */
4432
+ loadByPath(ref: string, require: true): AgentConfig;
4433
+ loadByPath(ref: string, require?: boolean): AgentConfig | undefined;
4434
+ }
4435
+
4436
+ /** list 默认 limit。 */
4437
+ declare const DEFAULT_LIST_LIMIT = 20;
4438
+ /** list limit 上限。 */
4439
+ declare const MAX_LIST_LIMIT = 100;
4440
+ /** background 启动提示文案(完成通知经自动注入消息投递,agent 不应轮询)。 */
4441
+ declare const BG_MESSAGE = "detached, will notify on completion (auto-injected message, do not poll)";
4442
+ /** 通知投递契约回显恒值(契约声明;值语义由 execution/notify-ledger.ts 兑现)。 */
4443
+ declare const NOTIFY_CONTRACT: "ledger+at-least-once";
4444
+ /**
4445
+ * fork-from 开场引导语框架(prompt 未指定时注入):先从继承的历史重建状态
4446
+ * 再继续,防猜。
4447
+ */
4448
+ declare const FORK_FROM_DEFAULT_PROMPT: string;
4449
+ /**
4450
+ * 有显式接续指令时的包裹框架:指令在前、上下文重建要求在后——指令首见即达,
4451
+ * 不湮没在元说明里(弱模型友好)。
4452
+ */
4453
+ declare function wrapForkFromPrompt(prompt: string): string;
4454
+ /** start 入参(拍平后从 tool params 顶层来,task + slug 必填)。
4455
+ * StartHandlerInput 是 SubagentExecuteParams 的子集(13 字段全 optional);
4456
+ * 调用方传整个 params(含 action/listParam/cancelParam),多余字段被忽略。 */
4457
+ interface StartHandlerInput {
4458
+ task?: string;
4459
+ /** 短标签(≤35 字符,kebab-case),必填。 */
4460
+ slug?: string;
4461
+ agent?: string;
4462
+ model?: string;
4463
+ thinkingLevel?: string;
4464
+ skillPath?: string;
4465
+ appendSystemPrompt?: string[];
4466
+ schema?: Record<string, unknown>;
4467
+ maxTurns?: number;
4468
+ graceTurns?: number;
4469
+ /** fork 模式:继承主 session 上下文。 */
4470
+ fork?: boolean;
4471
+ /** worktree 模式:文件系统隔离运行。 */
4472
+ worktree?: boolean;
4473
+ /** 覆盖子 agent 工作目录(默认 mainCwd)。 */
4474
+ cwd?: string;
4475
+ /** 可持续对话模式(true = chatMode,轮次完成进 idle 等续聊)。 */
4476
+ conversation?: boolean;
4477
+ /**
4478
+ * 空闲超时毫秒数(仅 conversation 模式有意义,覆盖默认 5min)。
4479
+ * 显式传 0/负数 = 禁用 idle GC(不挂 timer);不传走 env/默认优先级。
4480
+ */
4481
+ idleTimeoutMs?: number;
4482
+ /** 执行引擎(三层路由第一层:本参数 > agent frontmatter engine > config defaultEngine)。 */
4483
+ engine?: string;
4484
+ }
4485
+ /** start 领域对象(宿主 adapter 包成 bg 工具结果)。 */
4486
+ type StartHandlerResult = {
4487
+ kind: "bg";
4488
+ subagentId: string;
4489
+ sessionFile: string | undefined;
4490
+ /** 短标签,来自 record(handle.details.slug)。用于 result 行展示。 */
4491
+ slug: string;
4492
+ /**
4493
+ * registry 全等回显:handle.details.model = record.model = `${provider}/${id}`,
4494
+ * 源头是 resolveModel 裁决放行的条目——通过校验 = 子进程必然按此名执行。
4495
+ */
4496
+ model: string;
4497
+ response: BgResponse;
4498
+ };
4499
+ interface ListHandlerInput {
4500
+ includeFinished?: boolean;
4501
+ limit?: number;
4502
+ }
4503
+ /** list 领域对象(宿主 adapter 包成 list 工具结果,最外层 subagentId/sessionFile 为 null)。 */
4504
+ interface ListHandlerResult {
4505
+ response: ListResponse;
4506
+ }
4507
+ interface CancelHandlerInput {
4508
+ subagentId?: string;
4509
+ }
4510
+ /** cancel 领域对象(宿主 adapter 包成 cancel 工具结果)。 */
4511
+ interface CancelHandlerResult {
4512
+ subagentId: string;
4513
+ response: CancelResponse;
4514
+ }
4515
+ interface MessageHandlerInput {
4516
+ subagentId?: string;
4517
+ text?: string;
4518
+ interrupt?: boolean;
4519
+ }
4520
+ /** message 领域对象(宿主 adapter 包成 message 工具结果)。
4521
+ * slug 来自 record(GUI message 通道的留痕 details 需要),
4522
+ * 避免调用方二次 getRecordForAction 查询。 */
4523
+ type MessageHandlerResult = {
4524
+ kind: "message";
4525
+ subagentId: string;
4526
+ slug: string;
4527
+ response: MessageResponse;
4528
+ };
4529
+ interface CloseHandlerInput {
4530
+ subagentId?: string;
4531
+ force?: boolean;
4532
+ }
4533
+ /** close 领域对象(宿主 adapter 包成 close 工具结果)。 */
4534
+ type CloseHandlerResult = {
4535
+ kind: "close";
4536
+ subagentId: string;
4537
+ response: CloseResponse;
4538
+ };
4539
+ interface ForkFromHandlerInput {
4540
+ sourceSubagentId?: string;
4541
+ prompt?: string;
4542
+ }
4543
+ /** fork-from 领域对象(宿主 adapter 包成 fork-from 工具结果)。 */
4544
+ type ForkFromHandlerResult = {
4545
+ kind: "fork-from";
4546
+ /** 新 subagent 的 record id(后续续聊用 action:'message')。 */
4547
+ subagentId: string;
4548
+ /** 作为 --fork 继承源的旧记录 session 文件。 */
4549
+ sourceSessionFile: string;
4550
+ response: ForkFromResponse;
4551
+ };
4552
+ /**
4553
+ * message 拒绝时的可行动文案分流。
4554
+ *
4555
+ * 背景:getRecordForAction 冷查只认 status==='running'(可续聊重建),任何终态/
4556
+ * 异归属记录都落到同一个「not found or not owned」错误,把两类完全不同的场景混为一谈:
4557
+ * - user-close/cancelled:用户主动告别,记录真没了 → 引导 start 新的
4558
+ * - parent-shutdown/gc/orphan 等:父会话重启/进程退出导致的断联,对话 jsonl 完好,
4559
+ * resume/fork 基建现成 → 引导 fork-from 从旧记录接续
4560
+ *
4561
+ * 分流规则(消费方是 LLM,保持正交简单):
4562
+ * - 找不到记录 → 原样透传 getRecordForAction 错误(id 打错最常见,原文案最准)
4563
+ * - closed + user-close/cancelled →「已主动关闭,无法续聊」文案
4564
+ * - 其余(closed 其他 reason / running 但异归属)→「断联可接续」文案
4565
+ */
4566
+ declare function endedMessageGuard(service: SubagentService, id: string, original: unknown): Error;
4567
+ /**
4568
+ * 内部 ExecutionStatus → 对外 state 映射(设计决策 10 细则 3)。
4569
+ * 两态收敛后的真实映射只有两条:
4570
+ * running → active / closed → ended(closed 统一终态,含 cancelled)
4571
+ * ExternalState 仍声明 waiting/error 四态联合(对外契约不变),但当前状态机不产生
4572
+ * 这两个值——它们是历史多态映射(idle→waiting / failed+crashed→error)的遗留声明。
4573
+ * 未来内部加态必须扩展此处,漏加会在 default 分支编译报错(而非静默返回 undefined
4574
+ * 让 state 字段以无主值进入 listResponse JSON)。
4575
+ */
4576
+ declare function mapExternalState(status: ExecutionStatus): ExternalState;
4577
+ /** SubagentRecord → SubagentListItem(state 四态主字段 + status 调试字段,duration 实时计算)。
4578
+ * parent 从 record.parentRecordId 派生(配合直接父守卫),resumable 从 isResumable 派生
4579
+ * (「可续聊」对外表达);outcome 一等终态语义(projectOutcome 唯一出口),closedReason
4580
+ * 退出对外 JSON(保留为 record 内部诊断字段),对外成败判读收口到 outcome。
4581
+ * agent 是 GUI/TUI list 共用的显示名——取 basename 短名(displayAgentName),
4582
+ * 完整路径保留在 record.agent(数据层)。 */
4583
+ declare function recordToListItem(r: SubagentRecord): SubagentListItem;
4584
+ declare function startHandler(service: SubagentService, input: StartHandlerInput | undefined, signal: AbortSignal | undefined, ctxModel?: ModelInfo): Promise<StartHandlerResult>;
4585
+ /**
4586
+ * list 数据源(诚实声明):
4587
+ * collectRecords(limit, statusFilter) 合并内存(running) + 磁盘(重建)。磁盘源天然
4588
+ * 跨 session 可见——/new /resume /fork 后前 session 的终态 record 仍在 sessions
4589
+ * 目录里(直到 GC)。内存源仅当前 session 的 running record。
4590
+ */
4591
+ declare function listHandler(service: SubagentService, input: ListHandlerInput | undefined): ListHandlerResult;
4592
+ declare function cancelHandler(service: SubagentService, input: CancelHandlerInput | undefined): Promise<CancelHandlerResult>;
4593
+ /**
4594
+ * message action handler:向对话模式 subagent 续聊/插入消息。
4595
+ *
4596
+ * 状态 × interrupt 自动映射(agent 只表达意图):
4597
+ * running → deliverMessage 热路径(进程活:prompt + streamingBehavior,interrupt=true
4598
+ * 抢占 / false 排队)
4599
+ * 进程死 → deliverMessage 冷路径(resumeRound 重开 session + prompt,interrupt 自动
4600
+ * 退化,agent 无感)
4601
+ * 终态 → throw ended(正常路径不命中——终态 record 已 archive,getRecordForAction 先 throw not found)
4602
+ *
4603
+ * 归属守卫:getRecordForAction 内部校验 rootSessionId。
4604
+ *
4605
+ * @throws Error subagentId/text 缺失 / 不存在或非本 session 所有 / 已结束
4606
+ */
4607
+ declare function messageHandler(service: SubagentService, input: MessageHandlerInput | undefined): Promise<MessageHandlerResult>;
4608
+ /**
4609
+ * close action handler:结束 subagent(对话模式为主,one-shot 同样支持)。
4610
+ *
4611
+ * force 语义(设计决策 5/10):
4612
+ * force:false(默认)= 优雅关闭——
4613
+ * 无在跑轮(等待续聊 timer armed / 无活进程)→ 立即终态化(closed + user-close,回收保活进程)
4614
+ * 有活进程在跑轮 → 置 closeAfterRound,轮完成时终态化——返回 {closed:true} 即承诺轮结束后资源已释放
4615
+ * force:true = 立即终止——running 立即 SIGTERM(cancelBackground 显式 kill)+ closed+cancelled
4616
+ *
4617
+ * 行为分流委托 service.closeSubagent(归属守卫由 getRecordForAction 把关)。
4618
+ * 已终态 record 由 getRecordForAction throw not found(「已结束的不能再操作」语义)。
4619
+ */
4620
+ declare function closeHandler(service: SubagentService, input: CloseHandlerInput | undefined): Promise<CloseHandlerResult>;
4621
+ /**
4622
+ * fork-from action handler:从旧 subagent 的会话历史 spawn 新 id 接续。
4623
+ *
4624
+ * 用于 subagent 因会话重启/进程退出而断联后的恢复:新进程以 --fork 指向旧 session
4625
+ * 文件(copy-on-write 建分支会话),继承全部对话历史;源文件只读不续写。
4626
+ * 旧记录本身不动——closed 单向状态机不变量、tryTransition 语义均不触碰。
4627
+ *
4628
+ * 守卫链(拒绝原因与行动语言对齐,见 assertAndLookupForkFromSource):
4629
+ * 1. 本进程内存 running → 还活着,应走 message(防双写同一子 session 文件)
4630
+ * 2. 不存在 → 引导 list 确认
4631
+ * 3. 异进程活跃 → 别处正跑,不可从此接续(同 id 双写风险;等其结束或在其所属会话内操作)
4632
+ * 4. cancelled/user-close → 用户主动告别,真没了(不提供接续通道)
4633
+ * 5. worktree 记录 → checkout 不可复用,fork 子进程 cwd 会回落主仓破坏隔离
4634
+ * 6. 无子 session 文件 → 无历史可继承(entry-only 孤儿:spawn 窗口期中断)
4635
+ *
4636
+ * @throws Error 各守卫命中 / service.execute 失败(引擎不支持等)
4637
+ */
4638
+ declare function forkFromHandler(service: SubagentService, input: ForkFromHandlerInput | undefined): Promise<ForkFromHandlerResult>;
4639
+
4640
+ /**
4641
+ * 快照格式版本(D4 裁决①:字符串相等比较,无大小序)。
4642
+ *
4643
+ * 版本历史(沿用 pi jsonl-run-store 口径):
4644
+ * - wf-run-v1:status 三态(含 paused)、meta 含 pausedAt(pi 旧格式,读路径拒绝)。
4645
+ * - wf-run-v2(当前):status 两态(running/done)、meta 无 pausedAt。
4646
+ *
4647
+ * 升级格式时 bump 此常量——旧版本快照经 fromRunSnapshot 返回 undefined,由
4648
+ * 宿主 store 层决定跳过可见性(FileRunStore warn / pi 静默)。
4649
+ */
4650
+ declare const SNAPSHOT_VERSION: "wf-run-v2";
4651
+ /** Budget 实例的可序列化投影(构造 opts 同形,重水合直接 new Budget(...))。 */
4652
+ interface BudgetSnapshot {
4653
+ maxTokens?: number;
4654
+ maxCost?: number;
4655
+ maxTimeMs?: number;
4656
+ usedTokens: number;
4657
+ usedCost: number;
4658
+ totalCallCount: number;
4659
+ }
4660
+ /**
4661
+ * AgentCall 实例的可序列化投影。traceNode 整体落盘(节点引用不可序列化,
4662
+ * 落盘值拷贝;重水合后 D-10「引用共享」由 fromRunSnapshot 的 trace 回链尽力
4663
+ * 恢复——Trace.fromArray 注释先例)。
4664
+ */
4665
+ interface CallSnapshot {
4666
+ id: number;
4667
+ opts: AgentCallOpts;
4668
+ status: "pending" | "running" | "done";
4669
+ attempts: number;
4670
+ result?: AgentResult;
4671
+ sessionId?: string;
4672
+ sessionFile?: string;
4673
+ traceNode: ExecutionTraceNode;
4674
+ }
4675
+ /**
4676
+ * WorkflowRun 的持久化快照形态(JSONL 单行,全量而非增量)。
4677
+ *
4678
+ * 形态与 pi 壳 jsonl-run-store.ts 的 RunSnapshot 同构(v 字段含内)——两宿主
4679
+ * 存量互读的前提,任何字段增删必须同步两处并评估存量行。
4680
+ */
4681
+ interface RunSnapshot {
4682
+ v: typeof SNAPSHOT_VERSION;
4683
+ runId: string;
4684
+ spec: RunSpec;
4685
+ state: {
4686
+ status: RunStatus;
4687
+ reason?: DoneReason;
4688
+ budget: BudgetSnapshot;
4689
+ calls: CallSnapshot[];
4690
+ trace: ExecutionTraceNode[];
4691
+ errorLogs: WorkerLogEntry[];
4692
+ error?: string;
4693
+ scriptResult?: unknown;
4694
+ };
4695
+ meta: WorkflowRunMeta;
4696
+ }
4697
+ /**
4698
+ * WorkflowRun → 单行快照。
4699
+ *
4700
+ * - 补 v 字段(D4 裁决②:写入恒带当前版本)。
4701
+ * - strip live(防御内聚):calls[].traceNode 与 trace 数组节点的 `live` 运行期
4702
+ * 对象剥除——ExecutionRecord 含可变 turns[]/controller,不可序列化且跨进程
4703
+ * 必死(重跑时由 dispatchAgentCall 重建);strip 产出新对象,不 mutate 内存
4704
+ * 中的 run(save 后 run 可继续跑)。
4705
+ * - spec.budgetRef 剔除:父 Budget 共享引用是进程内优化(嵌套 workflow 预算
4706
+ * 共享),非持久化数据;Budget 实例若混入 spec 落盘将退化为普通对象投影
4707
+ * (重水合后类型不符的脏字段)——重水合后 budget 从 state.budget 独立重建,
4708
+ * 嵌套 run 的预算共享不跨进程存活。
4709
+ * - runtime 不落盘(worker/controller/timer 不可序列化且跨进程必死——重水合
4710
+ * 语义见 WorkflowRun.reconstruct 注释)。
4711
+ */
4712
+ declare function toRunSnapshot(run: WorkflowRun): RunSnapshot;
4713
+ /**
4714
+ * 快照 → WorkflowRun 重水合。
4715
+ *
4716
+ * 版本 guard(D4 裁决③):v 不等于当前版本即返回 undefined(含缺版本——
4717
+ * 「缺 v 宽容」是 FileRunStore 层预处理职责,不内聚进本函数;pi 侧对 v1 存量
4718
+ * 行的静默跳过语义依赖此拒绝行为)。
4719
+ *
4720
+ * 形状校验失败同样返回 undefined(调用方按损坏行处理,本函数不抛——唯一例外:
4721
+ * done 快照缺 reason 触发 WorkflowRun I2 不变式抛错,属真 bug 不可吞)。
4722
+ */
4723
+ declare function fromRunSnapshot(snap: unknown): WorkflowRun | undefined;
4724
+
4725
+ /**
4726
+ * 原子写原语(sink 设计 U6a / B6):tmp+rename 的单一实现。
4727
+ *
4728
+ * 背景:core 内部散布 6-7 份 tmp+rename 写点(manifest-store / sessions-index /
4729
+ * worktree-registry / engine-discovery / zcode preparer / zcode appserver-home),
4730
+ * tmp 命名各异(`.tmp.<pid>` / `.tmp_<pid>_<rand>` / `.tmp-<pid>-<ts>`)、失败路径
4731
+ * 清理纪律不齐(worktree-registry 失败时漏清残留 tmp)。本模块给出统一原语供全
4732
+ * 部写点收敛(调用点迁移归 u-wire 单元,本单元只建原语)。
4733
+ *
4734
+ * **统一 tmp 命名约定**:`<最终路径>.tmp.<pid>.<seq>-<rand>`。
4735
+ * - `.tmp.` 标记向后兼容 manifest-store 既有扫描(`x.json` 的 tmp 名为
4736
+ * `x.json.tmp.…`,命中其 `.json.tmp.` 模式);
4737
+ * - pid 防两进程共用同一 tmp,seq+rand 防同进程内并发写同目标共用同一 tmp
4738
+ * (对齐 sessions-index tmp 后缀的双重防撞设计)。
4739
+ *
4740
+ * **失败清理语义**:写入或 rename 失败 → 尽力 unlink 自身 tmp(失败仅 debug
4741
+ * 记录)→ 原错误原样上抛(不掩盖、不包装)。rename 成功后 tmp 已不存在,无需
4742
+ * 清理。跨进程/跨历史的陈旧残留 tmp 不由写入路径处理——统一走
4743
+ * listStaleTmpFiles / cleanupStaleTmpFiles 扫描入口(崩溃残留恢复语义的单点,
4744
+ * 见 sink 设计 §4 S6)。
4745
+ *
4746
+ * **两种耐久档位**(对齐现存两族写点的生产模式):
4747
+ * - sync(writeAtomicFileSync):writeFileSync + renameSync,无 fsync——对齐
4748
+ * worktree-registry / engine-discovery / zcode preparer / appserver-home 四处
4749
+ * 同步写点现状(prep/注册表类,进程崩溃窗口可容忍);
4750
+ * - async(writeAtomicFile):fsync 文件 → rename → 尽力 fsync 目录——对齐
4751
+ * manifest-store / sessions-index 的生产耐久模式(掉电也不丢已确认写入)。
4752
+ */
4753
+ /**
4754
+ * 目标文件的原子写 tmp 路径(统一约定:`<最终路径>.tmp.<pid>.<seq>-<rand>`)。
4755
+ *
4756
+ * 独立导出供调用方预告 tmp 名(如崩溃恢复扫描按约定反查)与测试锚定命名形态;
4757
+ * 两个 write 原语内部各自调用(每次写独立 tmp,并发写同目标互不串写)。
4758
+ */
4759
+ declare function atomicTmpPathFor(filePath: string): string;
4760
+ /** 约定 tmp 路径的解析视图。 */
4761
+ interface AtomicTmpRef {
4762
+ /** tmp 文件自身完整路径。 */
4763
+ tmpPath: string;
4764
+ /** 该 tmp 写入的目标路径(约定前缀还原)。 */
4765
+ targetPath: string;
4766
+ /** 创建该 tmp 的进程 pid(扫描方可据此实现存活过滤等策略)。 */
4767
+ pid: number;
4768
+ }
4769
+ /**
4770
+ * 解析约定 tmp 路径为目标视图;非约定形态返回 null。
4771
+ *
4772
+ * 注意贪婪匹配方向:目标名自身含 `.tmp.` 时(如 `foo.tmp.json.tmp.1-x`)
4773
+ * 前缀段正确还原为 `foo.tmp.json`。反之,恰以 `.tmp.<数字>.<uniq>` 结尾的
4774
+ * 用户文件会被误认——清理入口只应在受管目录(runtime 数据目录)内使用。
4775
+ */
4776
+ declare function parseAtomicTmpPath(tmpPath: string): AtomicTmpRef | null;
4777
+ interface AtomicWriteOptions {
4778
+ /** 字符串内容的编码(默认 "utf8";Uint8Array 内容忽略此项)。 */
4779
+ encoding?: BufferEncoding;
4780
+ /** 目标父目录缺失时递归创建(默认 true,对齐四处同步写点的 mkdir 纪律)。 */
4781
+ ensureDir?: boolean;
4782
+ }
4783
+ interface AtomicWriteFileOptions extends AtomicWriteOptions {
4784
+ /**
4785
+ * rename 成功后尽力 fsync 目标目录(默认 true)。POSIX 不要求;失败不否定
4786
+ * 已成功的 rename(对齐 manifest-store/sessions-index 的 best-effort 目录
4787
+ * fsync)。设 false 跳过(省两次目录句柄开销,弱耐久场景)。
4788
+ */
4789
+ fsyncDir?: boolean;
4790
+ }
4791
+ /**
4792
+ * 同步原子写(writeFileSync + renameSync,无 fsync)。
4793
+ *
4794
+ * 读者要么看到旧版完整内容、要么看到新版完整内容,绝无半成品(rename 原子性)。
4795
+ * 失败语义见模块头。适用 prep/注册表类高频小文件;需掉电耐久用 writeAtomicFile。
4796
+ */
4797
+ declare function writeAtomicFileSync(filePath: string, content: string | Uint8Array, options?: AtomicWriteOptions): void;
4798
+ /**
4799
+ * 异步原子写(fsync 文件 → rename → 尽力 fsync 目录)。
4800
+ *
4801
+ * manifest-store.writeManifest / sessions-index.saveIndex 生产耐久模式的统一
4802
+ * 实现。失败语义见模块头。真异步(fs.promises,不阻塞 event loop)。
4803
+ */
4804
+ declare function writeAtomicFile(filePath: string, content: string | Uint8Array, options?: AtomicWriteFileOptions): Promise<void>;
4805
+ /**
4806
+ * 扫描目录内全部约定形态的 tmp 文件(不做删除)。
4807
+ *
4808
+ * 供两类消费方:
4809
+ * - cleanupStaleTmpFiles 的内部步骤;
4810
+ * - 需要按域校验内容再决定「删 or 提升为正式文件」的宿主恢复逻辑
4811
+ * (manifest-store.recoverTmpFiles 模式:tmp 合法且目标缺失 → rename 提升)。
4812
+ *
4813
+ * 目录不存在 → 返回空数组(恢复扫描对未初始化布局宽容)。非约定形态文件
4814
+ * 一律不认(用户数据零误伤边界见 parseAtomicTmpPath 注释)。
4815
+ */
4816
+ declare function listStaleTmpFiles(dir: string): AtomicTmpRef[];
4817
+ interface CleanupStaleTmpOptions {
4818
+ /**
4819
+ * 只清理 mtime 早于 now - maxAgeMs 的残留(进程内在途写入的兜底保护窗口)。
4820
+ * 缺省 = 全清(启动期恢复场景,目录归本进程管)。需按 pid 存活过滤的调用方
4821
+ * 改用 listStaleTmpFiles 自行实现策略(ref.pid 可判活)。
4822
+ */
4823
+ maxAgeMs?: number;
4824
+ /** maxAgeMs 的基准时刻(缺省 Date.now();测试注入确定性时钟)。 */
4825
+ now?: number;
4826
+ }
4827
+ interface CleanupStaleTmpResult {
4828
+ /** 已删除(含扫描与删除之间已消失的——幂等终态)。 */
4829
+ removed: string[];
4830
+ /** 因 maxAgeMs 窗口内被保留的(疑似他方在途写入)。 */
4831
+ kept: string[];
4832
+ /** unlink 失败的(尽力语义,错误已 debug 记录)。 */
4833
+ failed: string[];
4834
+ }
4835
+ /**
4836
+ * 清理目录内约定形态的 tmp 残留(单条失败不阻断其余条目,逐条结果回传)。
4837
+ *
4838
+ * 恢复语义(对齐 manifest-store.recoverTmpFiles 的删除分支泛化):本函数只做
4839
+ * 「删除」级恢复;「校验后提升为正式文件」需域知识(manifest 记录合法性),
4840
+ * 由调用方基于 listStaleTmpFiles + parseAtomicTmpPath().targetPath 自行实现。
4841
+ */
4842
+ declare function cleanupStaleTmpFiles(dir: string, options?: CleanupStaleTmpOptions): CleanupStaleTmpResult;
4843
+
4844
+ /**
4845
+ * bounded JSON pretty 序列化原语(sink 设计 U6a / B7)。
4846
+ *
4847
+ * 来源:自 pi-sw(extensions/universal/subagent-workflow)interface/helpers.ts
4848
+ * 的同名私有函数**逐字平移**(函数体逐字符一致,仅补 export)——输出与该实现
4849
+ * 字节级一致,由 `__tests__/bounded-serialize.test.ts` 的等价锚定 + 硬编码字节
4850
+ * 快照断言守护。pi-sw 本地实现删除与消费切换归 u-sw-misc 单元(设计 U11)。
4851
+ */
4852
+ /**
4853
+ * bounded JSON pretty 序列化:只生成会被保留的前缀(≤8000 输出与
4854
+ * JSON.stringify(value, null, JSON_INDENT) 逐字节一致;>8000 输出与
4855
+ * 全量序列化后 .slice(0, budget) + "\n... (truncated)" 逐字节一致——
4856
+ * 等价测试锚定,见 __tests__/bounded-serialize.test.ts)。
4857
+ *
4858
+ * 现状成本:全量 stringify 产生数 MB 中间串再丢弃 99%;本函数在输出字符流
4859
+ * 越过 budget 后停止一切生成。保真策略:原语(string/number/boolean/null/bigint)
4860
+ * 逐值复用 JSON.stringify(转义/Unicode/数字格式原生一致,零重实现),仅结构
4861
+ * 拼装(2 空格缩进/逗号/括号)自实现。
4862
+ *
4863
+ * 特殊值语义(TC5 a-d,与原生 JSON.stringify 全值域对齐):
4864
+ * (a) 含 toJSON 的对象 → 该子树整体走一次 JSON.stringify(subtree)(原生会先调
4865
+ * toJSON,如 Date 输出带引号序列化串;bounded 拼装展开会与原生不同)。注意
4866
+ * 边界:toJSON 返回对象时原生 pretty 会按缩进展开,本实现按原语串接其
4867
+ * compact 形态——设计 TC5 锚定面为 Date 型(返回字符串)toJSON。
4868
+ * (b) 任何 stringify 抛出(BigInt → TypeError)→ 整体回退 String(value)(对齐
4869
+ * 旧实现整体 try/catch 的整串回退;禁止逐节点回退——输出形态与整串回退不同)。
4870
+ * (c) 对象属性值为 undefined/function/symbol → 拼装时跳过(原生省略)。
4871
+ * (d) 数组元素为 undefined/function/symbol → 序列化为 "null"(原生行为)。
4872
+ *
4873
+ * 截断边界(输出字符流层级,与对完整串 slice 逐字节等价):逐段 append 时本段
4874
+ * 会使总长越过 budget → 只 append 本段前 (budget - 已累积) 字符(恰好 budget,
4875
+ * 可切在转义序列/括号中间,不补任何结构闭合);越过(exceeded)才追加
4876
+ * "\n... (truncated)" 标记——恰好 ===budget 不加(> 判定)。
4877
+ *
4878
+ * 祖先 Set 循环引用守卫:命中 → 整体回退 String(value)(同 (b) 整串回退语义)。
4879
+ */
4880
+ declare function boundedPrettySerialize(value: unknown, budget: number): string;
4881
+
4882
+ /**
4883
+ * 发现并列出全部可用 agent(U2/A6 装配函数)。
4884
+ *
4885
+ * 流程:discoverResources(按优先级低→高,last-writer-wins)→ 逐文件
4886
+ * parseAgentProfile → 仅 IF1 严格层通过(profile.meta !== null)的条目进清单 →
4887
+ * 按 frontmatter name 去重(高优先级靠后覆盖,Map 后写胜)→ name 码点序输出
4888
+ * (KV-cache 契约:顺序与 readdir 枚举序解耦,重建结果逐字节一致)。
4889
+ *
4890
+ * 抛错面(如实声明):单文件读失败仅记日志跳过;目录不存在返回空列表;但底层
4891
+ * discoverResources 的不可恢复扫描错误会原样向上传播(readdir 遇权限拒绝、
4892
+ * EMFILE 竞态等——resource-discovery 自述 "Throws on unrecoverable scan errors",
4893
+ * Promise.all 首个 reject 即整体拒绝),调用方需自行兜底。
4894
+ *
4895
+ * @param workspaceRoot 项目根(findWorkspaceRoot 推导结果)
4896
+ * @param hostRoots 宿主注入发现根(pi 壳 = getAgentDir 三根;无则传 []——
4897
+ * user-agents/project-agents 硬编码槽仍生效,与 ScanConfig 语义一致)。
4898
+ * 注意:hostRoots 之外还有四个硬编码根恒进入扫描,无法经参数关闭——
4899
+ * `~/.agents/agents`、`<workspaceRoot>/.pi/agents`、
4900
+ * `<workspaceRoot>/.agents/agents` 与 `XYZ_EXTENSION_PATHS`
4901
+ * 环境变量展开的扩展源码路径(resource-discovery buildScanTargets
4902
+ * 固定槽位,注入 hostRoots 只是增列而非替换扫描面)
4903
+ */
4904
+ declare function discoverAgents(workspaceRoot: string, hostRoots: DiscoveryRoot[]): Promise<AgentEntry[]>;
4905
+
4906
+ /**
4907
+ * WorkflowRun 摘要投影(设计 D8/B5 —— U7)。
4908
+ *
4909
+ * 「宿主各写一遍」域的收口:pi tool-workflow.toRunSummary 与 zsw
4910
+ * orchestration-host 投影的字段集已实锤分叉(workflow vs name),本模块以 core
4911
+ * WorkflowRun 为准提供单一投影,宿主可在此基础上扩展自己的投影字段
4912
+ * (如 pi 版的 stateFile 需要 RunStore,归宿主扩展——core 不依赖具体 store 实例)。
4913
+ *
4914
+ * 层归属:Engine(纯投影,零 IO、零依赖)。字段名对齐 pi 版(name = scriptName)。
4915
+ */
4916
+
4917
+ /**
4918
+ * WorkflowRun 的可序列化摘要(status action / 列表渲染用)。
4919
+ *
4920
+ * 字段与 pi tool-workflow.toRunSummary 一致(去除依赖 RunStore 的 stateFile——
4921
+ * 宿主可扩展投影自行追加)。slug 旧持久化 run 可能缺失(undefined 保真透传)。
4922
+ */
4923
+ interface WorkflowRunSummary {
4924
+ runId: string;
4925
+ /** 脚本身份名(spec.scriptName)。 */
4926
+ name: string;
4927
+ /** run 级简短标签(可选,旧持久化 run 缺失)。 */
4928
+ slug?: string;
4929
+ status: RunStatus;
4930
+ reason?: DoneReason;
4931
+ /** ISO 时间戳,run 创建/启动时刻。 */
4932
+ startedAt: string;
4933
+ /** ISO 时间戳,transition("done") 时设置;running run 为 undefined。 */
4934
+ completedAt?: string;
4935
+ /** 失败/中止原因(state.error)。 */
4936
+ error?: string;
4937
+ }
4938
+ /**
4939
+ * WorkflowRun → 摘要投影。纯函数,不读 store、不发事件。
4940
+ *
4941
+ * @param run 聚合根(running 或 done 均可投影)
4942
+ */
4943
+ declare function runSummary(run: WorkflowRun): WorkflowRunSummary;
4944
+ /**
4945
+ * 判断是否存在指定名字、仍在 running 的 workflow script。
4946
+ *
4947
+ * pi 版遍历全部 session 的 runs(两层循环);core 版收口为单 runs Map——
4948
+ * per-session 隔离由调用方(宿主逐 session 调用或传入聚合 Map)负责。
4949
+ *
4950
+ * @param runs run 注册表(runId → WorkflowRun)
4951
+ * @param name script 名(按 spec.scriptName 精确匹配)
4952
+ */
4953
+ declare function isScriptRunning(runs: Map<string, WorkflowRun>, name: string): boolean;
4954
+
4955
+ /** 已知参数键集:exact = properties 精确键;patterns = patternProperties 原样转正则。 */
4956
+ interface ArgKeySet {
4957
+ readonly exact: ReadonlySet<string>;
4958
+ readonly patterns: readonly RegExp[];
4959
+ }
4960
+ /** 键集构建选项(宿主差异注入点)。 */
4961
+ interface ArgMetaOptions {
4962
+ /**
4963
+ * 宿主调用信封顶层保留键(如 pi workflow tool 的 action/name/slug/args/model/...)。
4964
+ * workflow 参数名与保留键撞名时,顶层同名键是信封参数而非平铺(m6 评审 M-3):
4965
+ * exact 构建排除保留键;能命中任一保留键的 pattern 整条跳过(^run.*$ 类会误伤
4966
+ * runId/name 等合法调用,m6 exec-review S1)。缺省空集(core 中性形态)。
4967
+ */
4968
+ readonly reservedKeys?: ReadonlySet<string>;
4969
+ }
4970
+ /**
4971
+ * 从 workflow 参数 schema(@pi-meta parameters)动态构建平铺检测的已知键集。
4972
+ *
4973
+ * - exact:properties keys(精确匹配,排除 reservedKeys)
4974
+ * - patterns:patternProperties 原样转正则数组(schema pattern 已是正则源码,
4975
+ * 直接 new RegExp;自动兼容 \d{2} 等变体)
4976
+ * - meta 缺失/非对象 → 空键集(legacy const-meta 类无参数契约,检测跳过)
4977
+ */
4978
+ declare function argKeysFromMeta(meta: Record<string, unknown> | undefined | null, options?: ArgMetaOptions): ArgKeySet;
4979
+ /**
4980
+ * 检测弱模型把 args 子字段平铺到 params 顶层(P0 静默失败防护)。
4981
+ * 返回被平铺的键名列表(空 = 未平铺)。匹配谓词:exact 命中 || pattern 命中
4982
+ * (pattern 自带数字后缀语义——loose startsWith 会误报 batchl/target1);args 内
4983
+ * 已存在的键不算平铺(顶层 + args 共存不算平铺)。
4984
+ *
4985
+ * 参数取 unknown 以解耦宿主 params 类型限制、便于测试构造任意对象。
4986
+ */
4987
+ declare function findFlattenedArgKeys(params: unknown, meta: Record<string, unknown> | undefined | null, options?: ArgMetaOptions): string[];
4988
+ /** 组装层警告(结构化承载,宿主决定展示/日志/fatal 升级)。 */
4989
+ type ArgMetaWarning = {
4990
+ /** 无参数契约(meta 未声明或解析为空)——平铺检测跳过,args 不校验(m6 M-2 显式信号)。 */
4991
+ code: "no_parameter_contract";
4992
+ message: string;
4993
+ } | {
4994
+ /** args 子字段被平铺到顶层——修正动作留宿主(pi 现行为是带 Correct 正例 throw)。 */
4995
+ code: "flattened_args";
4996
+ message: string;
4997
+ keys: readonly string[];
4998
+ };
4999
+ /** normalizeArgsByMeta 产物。 */
5000
+ interface NormalizedArgs {
5001
+ /**
5002
+ * 归一后的 args:params.args ?? {}(params 非对象时为 {})。args 字段为非对象
5003
+ * 标量时原样透传——类型校验责任在 args-validator(schema chokepoint),本函数
5004
+ * 不发明约束(与 m3 exec-review M2 裁决一致)。
5005
+ */
5006
+ readonly args: unknown;
5007
+ readonly warnings: readonly ArgMetaWarning[];
5008
+ }
5009
+ /**
5010
+ * 组装函数:按 meta 归一 params 为 { args, warnings }(pi actionRun 参数处理段的
5011
+ * 纯函数化——argKeysFromMeta + 空契约信号 + 平铺检测 + args 归一四步单点收口)。
5012
+ *
5013
+ * 警告语义与 pi 现行为对位:
5014
+ * - no_parameter_contract ↔ pi logger.warn「未声明参数契约——平铺检测跳过」(M-2)
5015
+ * - flattened_args ↔ pi throw「Detected ... they belong inside 'args'」(Correct
5016
+ * 正例含宿主 tool 键 action/name——平台事实留宿主拼接,core 文案保持中立)
5017
+ */
5018
+ declare function normalizeArgsByMeta(params: unknown, meta: Record<string, unknown> | undefined | null, options?: ArgMetaOptions): NormalizedArgs;
1168
5019
 
1169
5020
  /**
1170
5021
  * @zhushanwen/subagent-core — 公共 API barrel(D5 定稿)
@@ -1173,12 +5024,14 @@ declare function abortRun(runId: string, deps: LifecycleDeps, reason?: string, d
1173
5024
  * (./engines/zcode/reader、./engines/zcode/constants、./engine/paths、./relay-env)
1174
5025
  * + ./workflows/* 资产子入口。exports 面即 semver 契约(D5):收窄不放宽——
1175
5026
  * 新增导出走 minor,本文件刻意不使用 `export *`,逐名列出以使 diff 可审。
1176
- * 内部实现细节(registry / error-recovery / execution 编排件等)不经 barrel 导出,
1177
- * 仓内壳侧深路径消费(`./*` -> src 通配)不受本文件约束。
5027
+ * 内部实现细节(error-recovery / execute-agent-call / worker-script-builder
5028
+ * engine 编排件)不经 barrel 导出;host-surface 扩面(zsw 回接 U0,2026-08-30)
5029
+ * 后 port 的 Infra 实现与宿主组装件已列入公共面,仓内壳侧深路径消费
5030
+ * (`./*` -> src 通配)不受本文件约束。
1178
5031
  *
1179
5032
  * 设计权威源:docs/design/subagent-core-package-extraction.md §3.3 D5;
1180
5033
  * 宿主接入示例见包 README(§3.4 core_host_not_configured 恢复指引的落点)。
1181
5034
  */
1182
- declare const CORE_PACKAGE_VERSION = "0.2.0";
5035
+ declare const CORE_PACKAGE_VERSION = "0.4.0";
1183
5036
 
1184
- export { AgentEvent, AgentOutcome, AgentTaskSpec, CORE_PACKAGE_VERSION, type CoreLogger, DEFAULT_DATA_ROOT, type DiscoveryRoot, EngineCapabilities, EngineHandle, type EnginePort, type EngineRouteOptions, type EngineRouteResult, type EngineRouting, type EngineRoutingInput, type EngineRoutingSource, type EngineRunResult, type HostServices, InteractAction, InteractResult, type LifecycleDeps, type LogLevel, type ModelInfo, type NotifyDomainPorts, ProbeReport, type RunContext, type RunSpec, SessionView, SubagentStream, abortRun, configureCore, configureNotifyDomain, getLogger, routeEngine, runWorkflow };
5037
+ export { AGENT_REF_EXT, type AgentEntry, AgentEvent, AgentOutcome, type AgentProfile, AgentRegistry, type AgentRunner, AgentTaskSpec, type ArgKeySet, type ArgMetaOptions, type ArgMetaWarning, type AtomicTmpRef, type AtomicWriteFileOptions, type AtomicWriteOptions, BG_MESSAGE, BgResponse, CORE_PACKAGE_VERSION, type CachedWorkflowMeta, type CancelHandlerInput, type CancelHandlerResult, CancelResponse, type ChangeListener, type CleanupStaleTmpOptions, type CleanupStaleTmpResult, type CleanupWorktreeOptions, type CloseHandlerInput, type CloseHandlerResult, CloseResponse, type CollectWorktreePatchOptions, type ConcurrencyPool, type CoreLogger, type CreateConcurrencyPoolOptions, DEFAULT_DATA_ROOT, DEFAULT_LIST_LIMIT, DEFAULT_PROVIDER_ID, DEFAULT_WORKFLOW_SAVED_DIR, DEFAULT_WORKFLOW_TMP_DIR, type DiscoveredResource, type DiscoveryRoot, DoneReason, EngineCapabilities, EngineHandle, EngineHandleData, type EnginePort, type EngineRouteOptions, type EngineRouteResult, type EngineRouting, type EngineRoutingInput, type EngineRoutingSource, type EngineRunResult, ExecutionRecord, ExecutionStatus, ExternalState, FORK_FROM_DEFAULT_PROMPT, FileRunStore, type ForkFromHandlerInput, type ForkFromHandlerResult, ForkFromResponse, type GenerateWorkflowScriptOptions, type GenerateWorkflowScriptResult, GitRunError, type HostServices, InteractAction, InteractResult, type InvalidAgentRefMessageOptions, type LauncherDeps, type LifecycleDeps, type LintFinding, type LintResult, type ListFormatOptions, type ListHandlerInput, type ListHandlerResult, ListResponse, type ListWorktreePorcelainOptions, type LogLevel, MAX_LIST_LIMIT, MAX_RETAINED_DONE_RUNS, type MessageHandlerInput, type MessageHandlerResult, MessageResponse, ModelConfigService, type ModelConfigServiceInit, type ModelEntry, ModelInfo, type ModelListFormatOptions, type ModelReasoningInfo, NOTIFY_CONTRACT, type NormalizeWorkflowRefOptions, type NormalizedArgs, type NormalizedWorkflowRef, type NotifyDomainPorts, type PatchBaselineAnchor, ProbeReport, type QueuePolicy, RecordStore, type RecordStorePi, type RecoverCrashedRunsHooks, type RecoverCrashedRunsResult, type ResourceKind$1 as ResourceKind, type ResourceSource, type RunContext, type RunSnapshot, type RunSpec, RunStatus, type RunStore, SAFE_ID_RE, SLUG_MAX_LENGTH, SNAPSHOT_VERSION, SUBAGENT_RECORD_CUSTOM_TYPE, type ScanConfig, SessionView, type StartHandlerInput, type StartHandlerResult, type StatusFilter, SubagentListItem, SubagentRecord, type SubagentRecordEntryData, SubagentService, type SubagentServiceInit, SubagentStream, WORKFLOW_REF_EXT, WORKFLOW_REF_RESERVED_NAMES, type WorkerHandlers, type WorkerHost, WorkerHostImpl, type WorkflowDirOptions, type WorkflowEntry, type WorkflowMeta, type WorkflowRefInvalidReason, WorkflowRun, type WorkflowRunMeta, type WorkflowRunResult, type WorkflowRunSummary, type WorkflowScanConfig, WorkflowScript, WorkflowScriptRegistryImpl, type WorkflowSource, type WorktreePatchResult, type ZcodeEngineDeps, abortRun, argKeysFromMeta, assertSafeId, atomicTmpPathFor, boundedPrettySerialize, cancelHandler, cleanupStaleTmpFiles, cleanupWorktree, closeHandler, collectWorktreePatch, configureCore, configureNotifyDomain, createConcurrencyPool, createSubagentService, createZcodeEngine, deleteWorkflow, discoverAgents, discoverResources, discoverWorkflows, displayAgentName, endedMessageGuard, escapeXml, evictDoneRunsBeyondCap, executeNestedWorkflow, findFlattenedArgKeys, findWorkspaceRoot, forkFromHandler, formatAgentList, formatModelList, formatWorkflowList, fromRunSnapshot, generateWorkflowScript, getCachedFileContent, getCachedParsed, getLogger, getModelConfigService, getWorkflow, getWorkflowByPath, gitRun, hasApiKey, invalidAgentRefMessage, invalidateCache, isProcessAlive, isSafeId, isScriptRunning, isTreeDirty, killAllSpawnedChildren, lintScript, listHandler, listStaleTmpFiles, listWorktreePorcelain, loadWorkflowScriptByPath, loadWorkflows, mapExternalState, maxTurnsToWatchdogMs, messageHandler, normalizeArgsByMeta, normalizeRef, normalizeWorkflowRef, parseAgentProfile, parseAtomicTmpPath, parseResourceMeta, recordToListItem, recoverCrashedRuns, registerZcodeEngine, renderXmlSection, routeEngine, runAndWait, runSummary, runWorkflow, saveWorkflow, scheduleTimeBudget, sortByCodepoint, splitZcodeModelRef, startHandler, summarizeDescription, terminateRunningRuns, toRunSnapshot, toSubagentRecordEntry, wrapForkFromPrompt, writeAtomicFile, writeAtomicFileSync };