@zhushanwen/subagent-core 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/dist/{chunk-OL5BK4VN.js → chunk-LZJOEDGQ.js} +455 -203
  2. package/dist/engine-discovery-scan-Da4Dkzsi.d.cts +818 -0
  3. package/dist/engine-discovery-scan-Da4Dkzsi.d.ts +818 -0
  4. package/dist/execution/engine/engine-discovery-scan.cjs +216 -155
  5. package/dist/execution/engine/engine-discovery-scan.d.cts +1 -2
  6. package/dist/execution/engine/engine-discovery-scan.d.ts +1 -2
  7. package/dist/execution/engine/engine-discovery-scan.js +1 -1
  8. package/dist/execution/relay-env.js +5 -4
  9. package/dist/index.cjs +7686 -5894
  10. package/dist/index.d.cts +1876 -1360
  11. package/dist/index.d.ts +1876 -1360
  12. package/dist/index.js +7048 -5493
  13. package/dist.bundle/index.cjs +7747 -5957
  14. package/package.json +4 -4
  15. package/src/__tests__/manifest-store.test.ts +16 -15
  16. package/src/__tests__/robustness-medium-batch1.test.ts +10 -14
  17. package/src/__tests__/robustness-medium-batch2.test.ts +7 -2
  18. package/src/execution/__tests__/alive-store.test.ts +117 -1
  19. package/src/execution/__tests__/batch-finalized.test.ts +206 -0
  20. package/src/execution/__tests__/chat-engine-routing.test.ts +117 -1
  21. package/src/execution/__tests__/{cold-resurrect.test.ts → cold-lookup.test.ts} +133 -33
  22. package/src/execution/__tests__/collect-coordinator-service.test.ts +7 -5
  23. package/src/execution/__tests__/conversation-continuation.test.ts +1452 -0
  24. package/src/execution/__tests__/delivery-methods.test.ts +90 -176
  25. package/src/execution/__tests__/dialog-queue.test.ts +70 -3
  26. package/src/execution/__tests__/dispose-manifest-recovery.test.ts +475 -0
  27. package/src/execution/__tests__/execute-and-await-worktree.test.ts +3 -3
  28. package/src/execution/__tests__/execute-nesting.test.ts +8 -5
  29. package/src/execution/__tests__/execution-record.test.ts +8 -148
  30. package/src/execution/__tests__/finalize-record.test.ts +295 -129
  31. package/src/execution/__tests__/get-record-for-action-restart.test.ts +21 -29
  32. package/src/execution/__tests__/helpers/fake-engine-port.ts +13 -68
  33. package/src/execution/__tests__/helpers/legacy-sidecar.ts +33 -0
  34. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +9 -5
  35. package/src/execution/__tests__/idle-gc.test.ts +60 -0
  36. package/src/execution/__tests__/lifecycle-manager.test.ts +3 -254
  37. package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +43 -22
  38. package/src/execution/__tests__/model-config-service.test.ts +111 -0
  39. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +10 -6
  40. package/src/execution/__tests__/notify-ledger.test.ts +42 -1
  41. package/src/execution/__tests__/rebuild-indexes.test.ts +329 -0
  42. package/src/execution/__tests__/record-binding.test.ts +613 -0
  43. package/src/execution/__tests__/record-origin.test.ts +318 -0
  44. package/src/execution/__tests__/record-store-intent-api.test.ts +600 -0
  45. package/src/execution/__tests__/record-store-orphan-revive.test.ts +71 -3
  46. package/src/execution/__tests__/record-store.test.ts +72 -54
  47. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +8 -5
  48. package/src/execution/__tests__/round-supervisor-workflow-origin.test.ts +205 -0
  49. package/src/execution/__tests__/run-orchestration-write-lease.test.ts +293 -0
  50. package/src/execution/__tests__/run-orchestration.test.ts +199 -0
  51. package/src/execution/__tests__/session-file-gc.test.ts +35 -4
  52. package/src/execution/__tests__/session-reconstructor.test.ts +78 -0
  53. package/src/execution/__tests__/state-marker.test.ts +288 -0
  54. package/src/execution/__tests__/stream-sink-retirement.test.ts +8 -5
  55. package/src/execution/__tests__/subagent-actions-core.test.ts +67 -16
  56. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +0 -1
  57. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +27 -11
  58. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +17 -11
  59. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +130 -53
  60. package/src/execution/__tests__/subagent-service.test.ts +17 -215
  61. package/src/execution/__tests__/subprocess-agent-runner-no-progress-killall.test.ts +138 -0
  62. package/src/execution/__tests__/subprocess-agent-runner.test.ts +105 -660
  63. package/src/execution/__tests__/sync-collect-recovery.test.ts +23 -16
  64. package/src/execution/__tests__/terminal-write-latency.test.ts +79 -0
  65. package/src/execution/__tests__/workflow-agent-dispatch.test.ts +782 -0
  66. package/src/execution/alive-store.ts +37 -28
  67. package/src/execution/{cold-resurrect.ts → cold-lookup.ts} +48 -45
  68. package/src/execution/config.ts +1 -1
  69. package/src/execution/conversation-continuation.ts +541 -0
  70. package/src/execution/dialog-queue.ts +60 -12
  71. package/src/execution/engine/__tests__/common/capability-gate.test.ts +1 -1
  72. package/src/execution/engine/__tests__/common/journal-wiring.test.ts +16 -1
  73. package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/fake-engine-protocol.mjs +17 -51
  74. package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/smoke-run.fixture.json +0 -5
  75. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +1 -1
  76. package/src/execution/engine/__tests__/conformance/protocol-blackbox.test.ts +5 -9
  77. package/src/execution/engine/__tests__/engine-discovery-scan.test.ts +0 -3
  78. package/src/execution/engine/__tests__/engine-discovery.test.ts +0 -3
  79. package/src/execution/engine/__tests__/model-prompt.test.ts +0 -3
  80. package/src/execution/engine/__tests__/registry.test.ts +0 -1
  81. package/src/execution/engine/__tests__/routing.test.ts +0 -1
  82. package/src/execution/engine/__tests__/run-failure-worktree-cleanup.test.ts +0 -3
  83. package/src/execution/engine/client/__tests__/__fixtures__/fake-engine.mjs +0 -3
  84. package/src/execution/engine/client/__tests__/engine-client-reap.test.ts +212 -0
  85. package/src/execution/engine/client/__tests__/mirror.test.ts +73 -1
  86. package/src/execution/engine/client/__tests__/protocol-closure.test.ts +2 -9
  87. package/src/execution/engine/client/__tests__/remote-engine.test.ts +46 -12
  88. package/src/execution/engine/client/engine-client.ts +81 -25
  89. package/src/execution/engine/client/mirror.ts +9 -0
  90. package/src/execution/engine/client/remote-engine.ts +52 -34
  91. package/src/execution/engine/client/reverse-router.ts +6 -27
  92. package/src/execution/engine/common/__tests__/run-signals.test.ts +79 -0
  93. package/src/execution/engine/common/capability-gate.ts +27 -0
  94. package/src/execution/engine/common/data-dir.ts +3 -2
  95. package/src/execution/engine/common/errors.ts +1 -1
  96. package/src/execution/engine/common/journal-wiring.ts +18 -9
  97. package/src/execution/engine/common/run-signals.ts +83 -0
  98. package/src/execution/engine/d8-compat.ts +1 -1
  99. package/src/execution/engine/host/__tests__/host-bridge.test.ts +3 -18
  100. package/src/execution/engine/host/host-bridge.ts +19 -41
  101. package/src/execution/engine/host/pi-host-binding.ts +5 -5
  102. package/src/execution/engine/port.ts +34 -54
  103. package/src/execution/engine/types.ts +11 -14
  104. package/src/execution/execution-record.ts +15 -47
  105. package/src/execution/finalize-record.ts +94 -197
  106. package/src/execution/idle-gc.ts +9 -3
  107. package/src/execution/lifecycle-manager.ts +25 -361
  108. package/src/execution/lifecycle-predicates.ts +22 -19
  109. package/src/execution/manifest-store.ts +40 -42
  110. package/src/execution/model-config-service.ts +1 -1
  111. package/src/execution/notifier.ts +39 -1
  112. package/src/execution/notify-host.ts +13 -4
  113. package/src/execution/record-entry.ts +20 -2
  114. package/src/execution/record-store.ts +925 -160
  115. package/src/execution/relay-env.ts +8 -3
  116. package/src/execution/round-supervisor/domain.ts +17 -5
  117. package/src/execution/round-supervisor/service-binding.test.ts +89 -53
  118. package/src/execution/round-supervisor/service-binding.ts +71 -21
  119. package/src/execution/round-supervisor/supervisor.ts +23 -9
  120. package/src/execution/service/record-access.ts +547 -0
  121. package/src/execution/service/record-lifecycle.ts +542 -0
  122. package/src/execution/service/run-orchestration.ts +1647 -0
  123. package/src/execution/service/service-bootstrap.ts +111 -0
  124. package/src/execution/service/service-constants.ts +27 -0
  125. package/src/execution/service/session-baselines.ts +401 -0
  126. package/src/execution/service/sync-collect-domain.ts +442 -0
  127. package/src/execution/service/workflow-dispatch.ts +567 -0
  128. package/src/execution/session-file-gc.ts +11 -8
  129. package/src/execution/session-reconstructor.ts +44 -1
  130. package/src/execution/sessions-index.ts +20 -3
  131. package/src/execution/settled-watchdog.ts +86 -43
  132. package/src/execution/state-marker.ts +516 -0
  133. package/src/execution/stream-sink.ts +1 -1
  134. package/src/execution/subagent-actions-core.ts +53 -31
  135. package/src/execution/subagent-service.ts +643 -2980
  136. package/src/execution/subprocess-agent-runner.ts +46 -323
  137. package/src/execution/types.ts +60 -43
  138. package/src/execution/ui-request-handler-factory.ts +5 -0
  139. package/src/execution/worktree-manager.ts +1 -1
  140. package/src/execution/worktree-registry.ts +1 -1
  141. package/src/index.ts +6 -5
  142. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +2 -3
  143. package/src/orchestration/__tests__/agent-call-stream.test.ts +24 -49
  144. package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +4 -4
  145. package/src/orchestration/__tests__/file-run-store.test.ts +5 -7
  146. package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +2 -3
  147. package/src/orchestration/__tests__/run-snapshot.test.ts +27 -43
  148. package/src/orchestration/__tests__/worker-message-pump-workflow-dispatch.test.ts +274 -0
  149. package/src/orchestration/execute-agent-call.ts +1 -1
  150. package/src/orchestration/file-run-store.ts +1 -1
  151. package/src/orchestration/models/ports.ts +20 -13
  152. package/src/orchestration/models/types.ts +11 -13
  153. package/src/orchestration/run-snapshot.ts +18 -22
  154. package/src/orchestration/worker-message-pump.ts +33 -57
  155. package/src/shared/atomic-write.ts +10 -8
  156. package/dist/chunk-T2SYBW3I.js +0 -24
  157. package/dist/engine-discovery-scan-BXLA8y-z.d.cts +0 -1624
  158. package/dist/engine-discovery-scan-BXLA8y-z.d.ts +0 -1624
  159. package/src/execution/__tests__/chat-round-first-round-watchdog.test.ts +0 -255
  160. package/src/execution/__tests__/finalized-marker.test.ts +0 -104
  161. package/src/execution/__tests__/lifecycle-manager-lock.test.ts +0 -211
  162. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +0 -488
  163. package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +0 -75
  164. package/src/execution/__tests__/tombstone-store.test.ts +0 -73
  165. package/src/execution/engine/__tests__/conformance/chat-round-protocol.test.ts +0 -230
  166. package/src/execution/finalized-marker.ts +0 -70
  167. package/src/execution/tombstone-store.ts +0 -72
package/dist/index.d.cts CHANGED
@@ -1,14 +1,803 @@
1
- import { E as EnginePort, D as DiscoverEnginesOptions, a as ExecutionRecord, C as ClosedReason, b as ExecutionOutcome, A as AgentEventLogEntry, c as ExecutionStatus, P as ProjectedOutcome, R as RecordSnapshot, S as SubagentRecord, d as ExecuteOptions, e as SubagentStream, f as AgentResult, g as AgentResult$1, h as SubagentsGlobalConfig, M as ModelRegistryLike, i as ModelInfo, j as AgentConfig, k as ResolvedModel, l as StreamSink, m as ExecutionHandle, n as ExecutionMode, o as PatchResult, p as AgentCallOpts, q as ExecutionTraceNode, T as TracePatch, r as RunStatus, s as DoneReason, W as WorkerLogEntry, t as DisplayItem, u as CancelResponse, v as CloseResponse, F as ForkFromResponse, L as ListResponse, w as MessageResponse, B as BgResponse, x as ExternalState, y as SubagentListItem, z as DiscoveryRoot } from './engine-discovery-scan-BXLA8y-z.cjs';
2
- export { G as CLOSED_REASONS, H as CoreLogger, I as DEFAULT_AGENT_NAME, J as DEFAULT_DATA_ROOT, K as DEFAULT_ENGINE_ID, N as DirtyWorktreeError, O as EngineHandle, Q as EngineRunResult, U as ForkDepthExceededError, V as HostServices, X as LogLevel, Y as ResurrectDeniedError, Z as RunContext, _ as SLUG_MAX_LENGTH, $ as SubagentToolResult, a0 as configureCore, a1 as getHostServices, a2 as getLogger, a3 as normalizeEngineId } from './engine-discovery-scan-BXLA8y-z.cjs';
3
- import { ProbeReport, SessionView, ToolCall, UiRequest, UiRequestHandler, UiResponse, AgentEvent, UiChannelRegistry, WorktreeHandle, AgentOutcomeUsage } from '@zhushanwen/subagent-engine-sdk';
4
- export { AgentEvent, AgentOutcome, ChannelHandler, EngineCapabilities, EngineHandleData, InteractAction, InteractResult, ProbeReport, ReplayedTurn, SessionView, ToolCallEntry, UiChannelRegistry } from '@zhushanwen/subagent-engine-sdk';
1
+ import { M as ModelInfo, E as EnginePort, D as DiscoverEnginesOptions, a as ModelRegistryLike, A as AgentConfig, R as ResolvedModel, S as SubagentStream, b as AgentResult$1, c as StreamSink, d as AgentCallOpts, e as ExecutionTraceNode, T as TracePatch, f as RunStatus, g as DoneReason, W as WorkerLogEntry, h as DiscoveryRoot } from './engine-discovery-scan-Da4Dkzsi.cjs';
2
+ export { C as CoreLogger, i as DEFAULT_DATA_ROOT, j as DEFAULT_ENGINE_ID, k as EngineHandle, l as EngineRunResult, H as HostServices, L as LogLevel, m as RunContext, n as SLUG_MAX_LENGTH, o as configureCore, p as getHostServices, q as getLogger, r as normalizeEngineId } from './engine-discovery-scan-Da4Dkzsi.cjs';
3
+ import { Turn, AgentFailureKind, ToolCall, AgentUsageTotal, WorktreeHandle, ProbeReport, SessionView, AgentEvent, UiRequest, UiRequestHandler, UiResponse, UiChannelRegistry, AgentOutcomeUsage } from '@zhushanwen/subagent-engine-sdk';
4
+ export { AgentEvent, AgentOutcome, ChannelHandler, EngineCapabilities, EngineHandleData, ProbeReport, ReplayedTurn, SessionView, ToolCallEntry, UiChannelRegistry } from '@zhushanwen/subagent-engine-sdk';
5
+ import { GuiRenderResult } from '@xyz-agent/extension-protocol';
5
6
  import { Worker } from 'node:worker_threads';
6
7
  import 'node:child_process';
7
- import '@xyz-agent/extension-protocol';
8
8
 
9
9
  /** thinking level 支持顺序(低→高)。spawn 侧 `:level` 后缀仅接受本白名单值。 */
10
10
  declare const THINKING_ORDER: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
11
11
 
12
+ /**
13
+ * 未显式指定 agent 时的兜底名。
14
+ *
15
+ * 必须是真实存在、可被 agentRegistry 发现的 agent(用户 agentDir 内置的通用 agent)。
16
+ * Service 层(resolveIdentity)与 TUI 层(extractAgentName)共用此常量,保证
17
+ * 「调用时显示的名」与「实际加载的 agent.md」一致。
18
+ *
19
+ * [HISTORICAL] 旧实现两处各硬编码:service 用 "default"(虚构名),format 用
20
+ * "worker"(真实但不是兜底语义,worker agent 已在 2026-08 agent 重构中删除)。
21
+ * 导致不传 agent 时,block 标题显示 worker,但实际执行兜底逻辑不一致。统一为
22
+ * general-purpose 后名实相符。
23
+ */
24
+ declare const DEFAULT_AGENT_NAME = "general-purpose";
25
+ /**
26
+ * 唯一执行状态。所有路径共用。v4 B-1 两态收敛:旧 idle 折入 running、
27
+ * 旧 cancelled 折入 closed(closedReason='cancelled' 区分)。
28
+ *
29
+ * running = 活跃态。含两种子态(由派生谓词区分,见 lifecycle-predicates.ts):
30
+ * - 对话模式等待续聊(旧 idle):进程已回收,待冷路径 resume
31
+ * (isResumable=running && 无活进程句柄)。旧「进程保活待热路径」形态随
32
+ * H1 U6 长驻退役消亡,isIdle(hasIdleTimer)生产恒 false。
33
+ * - 正在执行(有活进程句柄)。
34
+ *
35
+ * closed = 统一终态(done/failed/crashed/cancelled 合并)。具体关闭原因由
36
+ * {@link ClosedReason} 子枚举表达(如 user-close / gc / cancelled / parent-shutdown)。
37
+ * ExecutionRecord.closedReason 携带 L2 原因,投影层按需派生对外语义(error / ended)。
38
+ */
39
+ type ExecutionStatus = "running" | "closed";
40
+ /**
41
+ * record 来源身份(H2 W1,设计 subagent-workflow-record-unification §3.3 D1 建议新增):
42
+ * "tool" — 主 agent 经 subagent 工具手动派发(现状全部 record);
43
+ * "workflow" — workflow 脚本内 agent() 调用派发(生产写入方 W2 executeWorkflowAgent 接线)。
44
+ * 缺省语义 = "tool":存量 record / 未传字段的 entry 反序列化产物一律视为手动派发,
45
+ * 四个投影消费面(subagents tool list / renderer 侧栏计数 / renderer 后台工作指示 /
46
+ * TUI /subagents)对缺省 record 的可见性与历史行为完全一致(零迁移)。
47
+ */
48
+ type RecordOrigin = "tool" | "workflow";
49
+ /**
50
+ * closed 终态的 L2 关闭原因子枚举。
51
+ *
52
+ * 与 ExecutionStatus="closed" 配合使用,表达「为什么关闭」:
53
+ * parent-shutdown — 父进程 session_shutdown 时回收子进程
54
+ * parent-fork — 父进程 fork 新 session 时清理旧子进程
55
+ * parent-new — 父进程创建新 subagent 时清理旧子进程
56
+ * user-close — 用户手动 close action(含对话模式 close)
57
+ * cancelled — 用户取消(close(force:true) / cancelBackground)
58
+ * gc — 通用完成/失败(一次执行自然结束、超时、错误等无专属 reason 的终态)
59
+ * disconnected — .finalized sidecar 存在但无 reason 内容(磁盘重建兜底):
60
+ * 正常结束但死因不可考——旧格式 sidecar(v8.5 前写入的是空文件)、
61
+ * 或外部工具手工创建。替代旧的误导性 "gc" 兜底(自然完成 vs 断联
62
+ * 不分),message/fork-from 据此给出可行动指引。
63
+ */
64
+ type ClosedReason = 'parent-shutdown' | 'parent-fork' | 'parent-new' | 'user-close' | 'cancelled' | 'gc' | 'disconnected';
65
+ /**
66
+ * [v8.5 D] 透明重生守卫拒绝专用错误:messageHandler 的 endedMessageGuard 必须原样
67
+ * 透传本类错误(自带完整行动语言),不得按 A1 分流规则改写——否则 worktree/异进程
68
+ * 占用文案会被「fork-from 指引」覆盖,误导 agent 走已被判死的通道。
69
+ */
70
+ declare class ResurrectDeniedError extends Error {
71
+ }
72
+ /** ClosedReason 全枚举值(运行时守卫用——防御性解析外部输入时校验成员资格)。 */
73
+ declare const CLOSED_REASONS: readonly ClosedReason[];
74
+ /**
75
+ * 终态三态对外语义(U3 C-outcome 一等披露)。
76
+ *
77
+ * 由 completeRecord 唯一写入点按 deriveOutcome 一次计算(判定顺序:cancelled 优先
78
+ * → error 非空 → completed),消费方(project/list/notify 文案/渲染器)只读本字段,
79
+ * 不再各自手写成败推导 switch(三处同构 switch 已随 U3 收敛删除)。
80
+ *
81
+ * [D6 显式取舍] parent-shutdown/parent-fork/parent-new 合成关闭(subagent-service
82
+ * disposeAllRecords 合成 result 恒写 error:"closed due to ...")落 "failed"——语义为
83
+ * 「父进程关闭时子 agent 未完成即失败」,选定行为而非疏漏,勿当 bug 改回 cancelled。
84
+ */
85
+ type ExecutionOutcome = "completed" | "failed" | "cancelled";
86
+ /**
87
+ * 对外投影的 outcome 联合:含历史 record(outcome 字段诞生前的存量数据)兼容态。
88
+ * 投影层(projectOutcome 唯一出口)对无 outcome 字段的 closed record 按
89
+ * deriveOutcome(closedReason, error) 兜底派生;"closed-legacy" 预留给连派生输入都
90
+ * 不足以判读的存量形态,消费方必须处理该成员(不得因未知值崩溃)。
91
+ */
92
+ type ProjectedOutcome = ExecutionOutcome | "closed-legacy";
93
+ /**
94
+ * 对外两态(设计决策 10 细则 3):内部 ExecutionStatus(v4 B-1 两态)收敛为 agent
95
+ * 可理解的状态语义。真实映射只有两条:
96
+ * running → active / closed → ended(closed 统一终态,含 cancelled)。
97
+ * mapExternalState 不消费 ClosedReason——closed 恒映射 ended。
98
+ *
99
+ * 原始 ExecutionStatus 进 list item 的 status 字段供调试;state 是对外主字段。
100
+ * 映射实现见 subagent-actions-core.ts mapExternalState——未来内部加态必须扩展该处,
101
+ * 漏加会在 default 分支编译报错,不影响对外契约。
102
+ */
103
+ type ExternalState = "active" | "ended";
104
+ /** 执行模式。background = 调用方立即拿 handle 返回,子 agent 在 detached promise 里跑。 */
105
+ type ExecutionMode = "background";
106
+
107
+ /**
108
+ * eventLog 条目(getEventLog 派生产出的元素)。所有字段 readonly。
109
+ *
110
+ * text_output / thinking 类型已移除——它们是 100 字切片的碎片副产物,
111
+ * 现在完整内容收口在 record.turns[] 里,eventLog 只承载离散语义事件
112
+ * (tool 调用 / turn 边界 / error)。
113
+ */
114
+ interface AgentEventLogEntry {
115
+ readonly type: "tool_start" | "tool_end" | "turn_end" | "error";
116
+ readonly label: string;
117
+ /** 事件发生的墙钟时间戳(Date.now(),ms)。由 getEventLog 从 turns[] 派生时记录。 */
118
+ readonly ts: number;
119
+ readonly status?: "running" | "done" | "failed";
120
+ }
121
+ /**
122
+ * [STEP3] displayItem:从 turns[] 派生的展示项(对齐 nicobailon getDisplayItems)。
123
+ *
124
+ * 与 eventLog 的区别:eventLog 承载离散语义事件(tool_start/tool_end/turn_end),
125
+ * displayItem 承载「可渲染单元」(toolCall 含完整 name+args 供 formatToolCall 格式化;
126
+ * text 含 assistant 正文)。renderResult compact 分支改用 displayItems 后,
127
+ * 行格式与 nicobailon 完全一致(→ formatToolCall)。
128
+ */
129
+ interface DisplayItem {
130
+ readonly type: "toolCall" | "text";
131
+ /** toolCall:tool 名称(bash/read/edit...);text:无。 */
132
+ readonly name?: string;
133
+ /** toolCall:tool 原始 args(供 formatToolCall 提取路径/命令);text:无。 */
134
+ readonly args?: Record<string, unknown>;
135
+ /** toolCall:执行状态(running 时无✓/✗标记);text:正文文本。 */
136
+ readonly status?: "running" | "done" | "failed";
137
+ /** text:assistant 正文(compact 时取首行/截断)。 */
138
+ readonly text?: string;
139
+ }
140
+ /** 一次 session 执行的完整结果。collectResult 产出,写入 Record.outcome。 */
141
+ interface AgentResult {
142
+ text: string;
143
+ turns: number;
144
+ durationMs: number;
145
+ success: boolean;
146
+ error?: string;
147
+ /**
148
+ * [D5-③] 失败分诊结构化标签(类型 SSOT 在 orchestration/models/types.ts 的
149
+ * AgentFailureKind——消费语义「unknown=可重试」与其文档同源)。collectResult 对
150
+ * 最终 error 分类后写入;缺省 = unknown(可重试)。type-only 引用零运行时依赖。
151
+ */
152
+ failureKind?: AgentFailureKind;
153
+ sessionId: string;
154
+ toolCalls: ToolCall[];
155
+ usage?: AgentUsageTotal;
156
+ /** /resume /fork 可恢复的 session 文件名(不含目录)。 */
157
+ sessionFile?: string;
158
+ /** schema 模式下,structured-output tool 的 result.details(已通过 schema 校验)。 */
159
+ parsedOutput?: unknown;
160
+ }
161
+
162
+ /** git diff patch 结果。 */
163
+ interface PatchResult {
164
+ readonly patchFile: string;
165
+ readonly failed: boolean;
166
+ /** patch 是否实际写入 patchFile。true=diff 非空且写盘成功;false=空 diff 或写失败。
167
+ * 调用方据此回填 record.patchFile,避免悬空路径(`git apply` 不存在的文件)。 */
168
+ readonly written: boolean;
169
+ }
170
+ /** fork depth 超限错误。 */
171
+ declare class ForkDepthExceededError extends Error {
172
+ constructor(message: string);
173
+ }
174
+ /** worktree 有未提交变更错误。 */
175
+ declare class DirtyWorktreeError extends Error {
176
+ constructor(message: string);
177
+ }
178
+ /**
179
+ * 所有执行路径的唯一状态源。
180
+ *
181
+ * 收口设计:一次执行的完整内容(text/thinking/toolCalls/usage)按 turn 收口在
182
+ * `turns: Turn[]` 里。eventLog / currentActivity / result 文本均从 turns[] 派生
183
+ * (getEventLog / getCurrentActivity / getFullText),不再独立存储切片或缓冲。
184
+ *
185
+ * 生命周期:createRecord() 创建 → updateFromEvent() 实时更新(累积进 turns)→
186
+ * completeRecord() 冻结 → archive 立即移出内存(读时从 session.jsonl 重建)。
187
+ *
188
+ * TUI 永远拿 RecordSnapshot(.slice() 快照),不直接持此可变对象。
189
+ */
190
+ interface ExecutionRecord {
191
+ /** 唯一 ID(sync: "run-N",bg: "bg-N-xxx")。 */
192
+ readonly id: string;
193
+ readonly agent: string;
194
+ readonly model: string;
195
+ readonly thinkingLevel: string | undefined;
196
+ readonly mode: ExecutionMode;
197
+ readonly task: string;
198
+ /**
199
+ * 人类可读的短标签(≤35 字符),简述本次 subagent「在做什么」。
200
+ * 区别于 agent(类型名)/ task(完整 prompt)。旧持久化 record 反序列化时缺失兜底空串。
201
+ */
202
+ readonly slug: string;
203
+ readonly startedAt: number;
204
+ /** 根 Pi session ID(session 隔离过滤用)。递归链上所有层 record 同值。 */
205
+ readonly rootSessionId: string | undefined;
206
+ /** 直接父 subagent record ID(层级树构建用)。顶层 record 为 undefined。 */
207
+ readonly parentRecordId: string | undefined;
208
+ /** subagent 递归深度。顶层(主 session 直接创建)=0,每层嵌套 +1。 */
209
+ readonly depth: number;
210
+ /**
211
+ * 来源身份(H2 W1,D1)。缺省(undefined)语义 = "tool"(存量 record 零迁移);
212
+ * "workflow" = workflow 脚本 agent() 派发(生产写入方 W2 接线)。过滤在投影/查询
213
+ * 消费面(list 默认滤 workflow origin),store 治理面(孤儿恢复/revive)全量可见。
214
+ * 持久化经 subagent-record entry。
215
+ */
216
+ readonly origin?: RecordOrigin;
217
+ /**
218
+ * origin="workflow" 时所属 workflow run 的 id(W2 写入);tool 来源恒 undefined。
219
+ * W2 run 视图进度 / W3 下钻按本 id 查询本 run 的 record 集(内存 ∪ 磁盘重建口径,
220
+ * collectRecordsByParentRunId)。持久化经 subagent-record entry。
221
+ */
222
+ readonly parentRunId?: string;
223
+ /**
224
+ * 对话模式标志(可持续对话 subagent)。true = 轮次完成进 idle 态(保留 record +
225
+ * worktree)等待续聊,而非一次性终态化。
226
+ * undefined/false = 一次性模式(默认,行为完全不变)。
227
+ * 向后兼容:旧 record / 旧 session 文件无此字段,按一次性模式处理。
228
+ */
229
+ readonly chatMode?: boolean;
230
+ /**
231
+ * 执行态信号(residual-fixes 设计):true = 该 record 无活进程驱动(轮终 idle /
232
+ * 重建孤儿兜底),处于「可续聊/等续聊」态——不是后台真在跑。轮终迁移
233
+ * (doFinalizeRoundToIdle)置 true,冷路径续轮(进程启动)清除;GUI 侧
234
+ * streaming/waiting 细分与 hasRunning 判据消费。缺省 falsy = 有进程或旧数据。
235
+ */
236
+ resumable?: boolean;
237
+ /**
238
+ * 空闲超时毫秒数(仅 chatMode 有意义)。覆盖默认 5min idle timeout。
239
+ * 优先级:参数 > env XYZ_SUBAGENT_IDLE_TIMEOUT_MS > 默认 300000ms。
240
+ * 向后兼容:旧 record 无此字段,按默认值处理。
241
+ */
242
+ readonly idleTimeoutMs?: number;
243
+ /**
244
+ * 实际执行引擎 id(P4 路由留痕,D9①)。创建时确定不可变;缺省(存量 record)
245
+ * = pi 投影(消费方零迁移)。持久化经 subagent-record entry。
246
+ */
247
+ readonly engine?: string;
248
+ /**
249
+ * 引擎 fallback 留痕(D9①:probe 失败路由回默认引擎)。GUI 警告条数据源;
250
+ * 缺省 = 无 fallback。持久化经 subagent-record entry。
251
+ */
252
+ readonly engineFallback?: {
253
+ from: string;
254
+ reason: string;
255
+ };
256
+ /**
257
+ * 引擎自描述定位符(U2:非 pi run resolve 后回填、终态迁移落 entry 前——run 前
258
+ * 缺省不可用)。sessionRef 整体透传(失败终态 sessionId 缺失时仍回填已有部分,
259
+ * 读侧①级降②级的防御形态);journalPath 为 retarget 后实际落盘路径。pi 分支不
260
+ * 回填(sessionFile 即定位符)。持久化经 subagent-record entry。
261
+ */
262
+ engineHandle?: {
263
+ sessionRef: Record<string, string>;
264
+ journalPath?: string;
265
+ poolKey: string;
266
+ };
267
+ /**
268
+ * 同步收集模式标记(subagent-sync-collect 设计 §3.1.3,U1 foundation 契约)。
269
+ * 创建时确定不可变;undefined = async(缺省语义,旧记录零迁移)。持久化经
270
+ * subagent-record entry(record-entry.ts 序列化白名单,entry 唯一写点)。
271
+ * 消费方:U2 collectCoordinator 路由(sync→批缓冲)、U5 E1 重建投影。
272
+ * U2 接线点:service.createRecordForMode 从 ExecuteOptions.collect 读入 identity。
273
+ */
274
+ readonly collectMode?: "sync";
275
+ status: ExecutionStatus;
276
+ /** L2 关闭原因子枚举(仅 status="closed" 时有意义)。表达「为什么关闭」。
277
+ * 由 tryTransition(record, "closed", reason) 写入;投影层按需派生对外语义。
278
+ * 向后兼容:旧 record 无此字段,按 gc 处理(通用完成/失败)。 */
279
+ closedReason?: ClosedReason;
280
+ /**
281
+ * 终态三态对外语义(U3 C-outcome)。completeRecord 唯一写入点按 deriveOutcome
282
+ * 一次计算,消费方只读本字段不再自行推导。向后兼容:旧 record / 磁盘重建
283
+ * record 无此字段,投影层按 projectOutcome 兜底(closed-legacy 语义)。
284
+ */
285
+ outcome?: ExecutionOutcome;
286
+ /**
287
+ * 离开批的终局标记(subagent-sync-collect 设计 §3.1.3,U1 foundation 契约)。
288
+ * 两出口统一落标:① 批闭合 flush 写账成功后;② E9 dispose 逐条转 async 写账后
289
+ * (均 appendEntry 持久化,U3/U5 写点)。undefined = 未离开批 / 旧记录零迁移。
290
+ * 消费方:U5 E1 重建扫描只收「collectMode=sync 且无本标记」的成员(防双重通知)。
291
+ */
292
+ batchFinalized?: boolean;
293
+ /** 完整执行内容,按 turn 组织。createRecord 初始化为 [空 turn]。 */
294
+ turns: Turn[];
295
+ /** turn 计数(= turns.filter(closed).length,冗余存储供投影直接读)。 */
296
+ turnCount: number;
297
+ totalTokens: number;
298
+ /** 运行期最近一次 error 事件的消息(getEventLog 派生 error 条目用)。 */
299
+ lastError: string | undefined;
300
+ /**
301
+ * 对话轮次计数(仅 chatMode 有意义)。首轮运行时 = 0;每完成一轮(finalizeRoundToIdle
302
+ * 进 idle)+1。undefined 时视为 0。非 chatMode 不自增。
303
+ */
304
+ round?: number;
305
+ /**
306
+ * record 进入 idle 态的时间戳(ms)。finalizeRoundToIdle 设值;GC 定时器据此计算
307
+ * 剩余 TTL。undefined = 非 idle 态(running/closed/cancelled)或旧 record 缺失字段。
308
+ */
309
+ idleSince?: number;
310
+ /**
311
+ * close 优雅关闭标志(M2-B3)。chatMode record 运行中调 `close {force:false}` 时置 true;
312
+ * runAndFinalize 的 done 分流检查此标志——true 则终态化为 done(而非进 idle),并清标志。
313
+ * undefined/false = 正常 idle 分流(对话模式轮次完成进 idle 等续聊)。
314
+ * 仅 chatMode + running 时有意义;force:true(立即终止)不走此标志。
315
+ */
316
+ closeAfterRound?: boolean;
317
+ endedAt: number | undefined;
318
+ result: string | undefined;
319
+ error: string | undefined;
320
+ /** 完整 AgentResult(含 usage/toolCalls,完成时填)。 */
321
+ agentResult: AgentResult | undefined;
322
+ /** session jsonl 文件名。session 创建成功后由 session-runner.run() 回填(窗口期内 undefined)。 */
323
+ sessionFile?: string;
324
+ /**
325
+ * [V2 决策 3] 子进程 pid(spawn 后回填到内存 record,并随 record 持久化落盘)。
326
+ *
327
+ * 诊断字段:排障时对照 record 文件与进程表核实 spawn 事实。原职责 4 孤儿扫描
328
+ * (按持久化 pid 扫收上次崩溃遗留孤儿)自落地起未接线,已随 L2 死代码清扫删除。
329
+ * undefined = 尚未 spawn / 已退出。向后兼容:旧 record 无此字段,按无 pid 处理。
330
+ */
331
+ pid?: number;
332
+ /** [MF#3] worktree 模式下子 agent 改动的 patch 文件路径(worktree 外,供调用方应用)。 */
333
+ patchFile?: string;
334
+ /** worktree 隔离时的 handle(仅 worktree:true 时存在;fork alone 无此字段)。 */
335
+ worktreeHandle?: WorktreeHandle;
336
+ /**
337
+ * [review round2] 该 record 创建时启用了 worktree 隔离(跨重启磁盘重建时从 session
338
+ * entry 的 worktree 标志恢复)。handle 本体不可序列化——跨重启后 worktreeHandle 恒
339
+ * undefined,续聊(冷路径 resume)须拒绝(防 cwd 静默回落主 repo 破坏隔离)。仅内存
340
+ * record 使用,与持久化无关;execute() 新建 record 不设(有真 handle 时无意义)。
341
+ */
342
+ hadWorktree?: boolean;
343
+ controller: AbortController | undefined;
344
+ }
345
+ /**
346
+ * Tool 返回的 details(内层扁平结构)。
347
+ * 由 project(record) 唯一产出——sync/bg 两路径字段一致。
348
+ * 含 mode + sessionFile(供外层 SubagentToolResult 分组 + spinner 判断)。
349
+ *
350
+ * 分层(spec FR-3):此为**内层**,不感知 action/外层分组。
351
+ * 外层 SubagentToolResult 由 adapter 包裹产出(加 action/subagentId/sessionFile + 分组)。
352
+ */
353
+ interface SubagentToolDetails {
354
+ status: ExecutionStatus;
355
+ /**
356
+ * 终态三态对外语义(U3 C-outcome,projectOutcome 唯一出口)。running → undefined;
357
+ * 历史数据无 outcome 字段时兜底派生(见 ProjectedOutcome)。
358
+ */
359
+ outcome?: ProjectedOutcome;
360
+ mode: ExecutionMode;
361
+ agent: string;
362
+ model: string;
363
+ thinkingLevel: string | undefined;
364
+ /** 短标签(≤35 字符),来自 record.slug。旧 record 反序列化时为空串。 */
365
+ slug: string;
366
+ turns: number;
367
+ totalTokens: number;
368
+ elapsedSeconds: number;
369
+ eventLog: AgentEventLogEntry[];
370
+ /** [STEP3] 从 turns[] 派生的展示项(对齐 nicobailon getDisplayItems)。 */
371
+ displayItems: DisplayItem[];
372
+ result?: string;
373
+ error?: string;
374
+ /** running 时的当前活动行(tool/thinking/text 优先级)。 */
375
+ currentActivity?: {
376
+ type: "tool" | "text" | "thinking";
377
+ label: string;
378
+ };
379
+ /** schema 模式下,structured-output tool 的 result.details(对齐 workflow agent-pool)。 */
380
+ parsedOutput?: unknown;
381
+ /** session jsonl 文件名(不含目录)。窗口期内可能 undefined(session 尚未创建成功)。 */
382
+ sessionFile?: string;
383
+ /** [MF#3] worktree 模式下子 agent 改动的 patch 文件路径(worktree 外,供调用方应用)。 */
384
+ patchFile?: string;
385
+ }
386
+ /** Hub.execute 的入参(sync/bg 共用)。mode 由 Hub 内部判定,不暴露给调用方。 */
387
+ interface ExecuteOptions {
388
+ task: string;
389
+ /**
390
+ * 短标签(≤35 字符),简述本次执行用途,展示在 TUI。必填。
391
+ * workflow 内 agent() 调用时从 AgentCallOpts.description 透传而来。
392
+ */
393
+ slug: string;
394
+ agent?: string;
395
+ model?: string;
396
+ thinkingLevel?: string;
397
+ skillPath?: string;
398
+ appendSystemPrompt?: string[];
399
+ schema?: Record<string, unknown>;
400
+ /** D-A6 bridge: workflow schemaEnv 经 ExecuteOptions 透传到 runSpawn childEnv。 */
401
+ schemaEnv?: string;
402
+ /**
403
+ * Turn 上限 limiter。显式 0/负 = 显式不限:压过 SPAWN_WATCHDOG_ENV 兑底不挂
404
+ * watchdog(SP-6 参数 > env,U5);undefined 未传才由 env 兑底。
405
+ */
406
+ maxTurns?: number;
407
+ graceTurns?: number;
408
+ /** sync 模式来自 Pi tool 框架;background 模式 hub 忽略,自建 controller。 */
409
+ signal?: AbortSignal;
410
+ /** 主 agent 当前模型(模型解析第三层兼底)。execute 调用方从 ctx.model 传入。 */
411
+ ctxModel?: ModelInfo;
412
+ /** background 完成回调(sync 不调)。 */
413
+ onComplete?: (record: RecordSnapshot) => void;
414
+ /** 是否继承父会话上下文(fork 模式,只继承上下文)。 */
415
+ fork?: boolean;
416
+ /**
417
+ * [v8.5 B] fork-from 显式指定继承源 session 文件(非主 session)。
418
+ * 与 fork:true 的区别:fork:true 用主 session 作 --fork 源;本字段用任意已有
419
+ * session 文件(断联 subagent 接续场景)作源。优先级高于 fork;传了本字段时
420
+ * fork 取值不影响 spawn 参数。仅 pi 引擎支持(同 fork)。仅 background tool
421
+ * 层 fork-from action 使用;workflow / executeAndAwait 不消费。
422
+ */
423
+ forkFromSessionFile?: string;
424
+ /** 文件系统隔离:true=创建新 git worktree,WorktreeHandle=复用外部已创建的;undefined=不隔离(parent cwd)。 */
425
+ worktree?: boolean | WorktreeHandle;
426
+ /** 覆盖执行 cwd(默认 mainCwd)。 */
427
+ cwd?: string;
428
+ /**
429
+ * 可持续对话模式(决策 8:独立 chatMode 标志,不扩展 ExecutionMode)。
430
+ * true = record 标记 chatMode,轮次完成进 idle 态(保留 record + worktree,等待 message 续聊);
431
+ * undefined/false = 一次性模式(默认,行为完全不变)。service.execute 透传到 createRecordForMode。
432
+ */
433
+ conversation?: boolean;
434
+ /**
435
+ * 同步收集模式(subagent-sync-collect 设计 §3.1.3,U1 foundation 契约)。
436
+ * undefined = config collectSync.default(缺省 "async",新 session 生效)。
437
+ * schema 层枚举限 "async"|"sync";运行时宽收 string 与 engine 字段同风格
438
+ * (非法值 ≠ "sync" 按 async 处理)。
439
+ * "sync" + conversation:true 组合在 startHandler E4 校验即拒
440
+ * (immediate throw,不产生半启动 record)。
441
+ * U2 接线点:service.createRecordForMode 读入 createRecord identity.collectMode
442
+ * (U1 打通类型与 startHandler 透传,record 落点归 U2)。
443
+ */
444
+ collect?: string;
445
+ /**
446
+ * 空闲超时毫秒数(仅 conversation 模式有意义)。覆盖默认 5min idle timeout。
447
+ * 优先级:参数 > env XYZ_SUBAGENT_IDLE_TIMEOUT_MS > 默认 300000ms。
448
+ * 显式传 0/负数 = 禁用 idle GC(不挂 timer;旧实现 0 会落成 setTimeout(0) 立即 kill)。
449
+ */
450
+ idleTimeoutMs?: number;
451
+ /**
452
+ * 实际执行引擎 id(P4 路由留痕):pi 引擎由 PiEngine.run 在还原 opts 时写入;
453
+ * 缺省(历史调用方不设)= pi 投影。createRecordForMode 读入 record identity。
454
+ */
455
+ engine?: string;
456
+ /**
457
+ * 引擎 fallback 留痕(D9①:probe 失败路由回默认引擎时由路由层写入)。
458
+ * from = 请求引擎 id,reason 恒 'engine_probe_failed'(GUI 警告条数据源)。
459
+ */
460
+ engineFallback?: {
461
+ from: string;
462
+ reason: string;
463
+ };
464
+ }
465
+ /**
466
+ * execute 返回值。
467
+ * background: { mode:"background", subagentId, sessionFile, details } —— 立即返回。
468
+ * subagentId 供后续 cancel/list 用;sessionFile 窗口期可能 undefined。
469
+ */
470
+ type ExecutionHandle = {
471
+ mode: "background";
472
+ subagentId: string;
473
+ sessionFile: string | undefined;
474
+ details: SubagentToolDetails;
475
+ };
476
+ /** list 的 item 结构。 */
477
+ interface SubagentListItem {
478
+ subagentId: string;
479
+ agent: string;
480
+ /** 短标签(≤35 字符),来自 record.slug。旧 record 反序列化时为空串。 */
481
+ slug: string;
482
+ /** 对外四态(决策 10 细则 3,主字段)。由 mapExternalState(status) 派生。 */
483
+ state: ExternalState;
484
+ /** 原始内部状态(调试用,供 details 展示)。 */
485
+ status: ExecutionStatus;
486
+ mode: ExecutionMode;
487
+ /** 运行秒数(running 态实时计算,终态 endedAt-startedAt)。 */
488
+ duration: number;
489
+ model: string;
490
+ totalTokens: number;
491
+ /** session jsonl 文件名(窗口期内可能 undefined)。 */
492
+ sessionFile?: string;
493
+ /** 直接父 subagent record ID(顶层 record 为 undefined)。[v4 A-6] 从
494
+ * record.parentRecordId 派生,配合 A-5 直接父守卫(message/close 仅作用于直接子)。 */
495
+ parent?: string;
496
+ /** 可冷路径 resume(running 且无活进程句柄)。[v4 A-6] B-1「可续聊」对外表达,
497
+ * agent 据 list 判断哪些 running subagent 实际可续聊(vs 正在忙)。 */
498
+ resumable?: boolean;
499
+ /**
500
+ * 终态三态对外语义(U3 C-outcome 一等披露,projectOutcome 唯一出口):
501
+ * completed / failed / cancelled,历史 record 无 outcome 字段时兜底派生,
502
+ * 不可判读的存量形态为 "closed-legacy"。GUI pane / agent 据此判读成败,
503
+ * 无需翻 error 字段原文(S5)。
504
+ */
505
+ outcome?: ProjectedOutcome;
506
+ /**
507
+ * 来源身份(H2 W1):undefined(存量 list 形态 / record 无 origin)= "tool" 语义。
508
+ * includeWorkflow 打开后 list 条目与手动派发 record 靠本字段区分(排查 workflow
509
+ * run's subagents 场景的辨识数据)。
510
+ */
511
+ origin?: RecordOrigin;
512
+ /** origin="workflow" 时所属 workflow run id;tool 来源恒缺省(同 record 侧)。 */
513
+ parentRunId?: string;
514
+ }
515
+ /** background 启动的内层响应(挂在 SubagentToolResult.bgResponse)。 */
516
+ interface BgResponse {
517
+ status: "running";
518
+ mode: "background";
519
+ /** 启动提示文案("detached, will notify on completion")。 */
520
+ message: string;
521
+ /**
522
+ * 终态三态语义(U3 C-outcome 对外 JSON 契约完备位)。start 时点 record 尚未终态,
523
+ * 恒 undefined(JSON.stringify 落键省略);终态成败语义经 list items[].outcome
524
+ * 披露。旧字段 status/mode/message 原样保留(向后兼容)。
525
+ */
526
+ outcome?: ProjectedOutcome;
527
+ /**
528
+ * 通知投递契约回显位(U1 预置,U2 通知账本的契约声明)。恒值
529
+ * "ledger+at-least-once":主 agent 在当前 run 结束或有限延迟内收到完成通知,
530
+ * 送达保证为 at-least-once + notifyId 幂等可识别。字段与填充由 U1 负责,
531
+ * 值语义由 U2(execution/notify-ledger.ts)兑现。
532
+ */
533
+ notifyContract: "ledger+at-least-once";
534
+ /**
535
+ * 同步收集登记回显段(subagent-sync-collect 设计 §3.1.1 交互样例,U1 foundation)。
536
+ * 仅 resolved 模式为 sync 时附带(async 响应字节零变化,G3):mode = 生效模式;
537
+ * pendingSyncCount = 当前未闭合批的 sync 成员总数(含本条;跨轮派发续累不重置,
538
+ * 与 D2 隐式批一致)。
539
+ */
540
+ collect?: {
541
+ mode: "sync";
542
+ pendingSyncCount: number;
543
+ };
544
+ }
545
+ /** list 的内层响应(挂在 SubagentToolResult.listResponse)。 */
546
+ interface ListResponse {
547
+ /** items 中 status==="running" 的计数(受 limit 截断如实反映,非全局总数)。 */
548
+ running: number;
549
+ items: SubagentListItem[];
550
+ }
551
+ /** cancel 的内层响应(挂在 SubagentToolResult.cancelResponse)。 */
552
+ interface CancelResponse {
553
+ cancelled: true;
554
+ }
555
+ /**
556
+ * message 的内层响应(挂在 SubagentToolResult.messageResponse,决策 10 瘦身)。
557
+ *
558
+ * [R1 删除记录] 旧 PendingMessage(在途消息缓存条目,消费确认制,设计决策 6 状态×
559
+ * interrupt 映射)已随 deliverToRunning 一并删除——SP-5 upgrade 后无生产调用方,
560
+ * 配套三段消费链(push / message_start shift / redeliverPending 补投)全部不可达。
561
+ * 详见 subagent-service.ts 的删除记录注释。
562
+ */
563
+ interface MessageResponse {
564
+ delivered: true;
565
+ }
566
+ /** close 的内层响应(挂在 SubagentToolResult.closeResponse,决策 10 瘦身)。 */
567
+ interface CloseResponse {
568
+ closed: true;
569
+ }
570
+ /**
571
+ * Tool 外层出参(renderResult + LLM content JSON 同源)。
572
+ * adapter 唯一产出:领域对象(bg/list/cancel/message/close 五选一)+ action/subagentId/sessionFile。
573
+ *
574
+ * - background 启动 → bgResponse(subagentId 有值;sessionFile 窗口期可能 undefined)
575
+ * - list → listResponse(最外层 subagentId/sessionFile 为 null,sessionFile 在各 item 内)
576
+ * - cancel → cancelResponse(subagentId 有值;sessionFile 无意义,可为 null)
577
+ * - message → messageResponse(subagentId 有值;sessionFile 无意义,可为 null)
578
+ * - close → closeResponse(subagentId 有值;sessionFile 无意义,可为 null)
579
+ */
580
+ type SubagentToolResult = {
581
+ action: "start";
582
+ subagentId: string;
583
+ sessionFile: string | null;
584
+ slug: string; /** registry 全等回显(U1):放行即与 registry 条目全等,"provider/id" 形态。 */
585
+ model: string;
586
+ bgResponse: BgResponse;
587
+ __gui__?: GuiRenderResult;
588
+ } | {
589
+ action: "list";
590
+ subagentId: null;
591
+ sessionFile: null;
592
+ listResponse: ListResponse;
593
+ __gui__?: GuiRenderResult;
594
+ } | {
595
+ action: "cancel";
596
+ subagentId: string;
597
+ sessionFile: null;
598
+ cancelResponse: CancelResponse;
599
+ __gui__?: GuiRenderResult;
600
+ } | {
601
+ action: "message";
602
+ subagentId: string;
603
+ sessionFile: null;
604
+ messageResponse: MessageResponse;
605
+ __gui__?: GuiRenderResult;
606
+ } | {
607
+ action: "close";
608
+ subagentId: string;
609
+ sessionFile: null;
610
+ closeResponse: CloseResponse;
611
+ __gui__?: GuiRenderResult;
612
+ } | {
613
+ action: "fork-from";
614
+ subagentId: string;
615
+ sessionFile: string | null;
616
+ forkFromResponse: ForkFromResponse;
617
+ __gui__?: GuiRenderResult;
618
+ };
619
+ /** fork-from 的内层响应:新 subagent id + 作为继承源的旧记录 session 文件。
620
+ * [v8.5 B] 断联恢复通道——新 subagent 以 --fork 方式继承旧会话历史,源文件只读
621
+ * 不续写(pi fork 建 branched session,copy-on-write)。 */
622
+ interface ForkFromResponse {
623
+ /** 新 subagent record id(接续对话用 action:'message')。 */
624
+ newSubagentId: string;
625
+ /** 作为继承源的旧 subagent session jsonl 绝对路径。 */
626
+ sourceSessionFile: string;
627
+ }
628
+ /** /subagents list 左列展示单元。来自内存(running) 或 session.jsonl 重建(终态)。 */
629
+ interface SubagentRecord {
630
+ id: string;
631
+ agent: string;
632
+ /** 任务提示词(详情面板置顶展示)。磁盘/内存源均有。 */
633
+ task: string;
634
+ /** 短标签(≤35 字符)。磁盘重建源旧文件可能缺失→兜底空串。 */
635
+ slug: string;
636
+ status: ExecutionStatus;
637
+ /** L2 关闭原因子枚举(仅 status="closed" 时有意义)。SP-1 新增。 */
638
+ closedReason?: ClosedReason;
639
+ /** 终态三态对外语义(U3 C-outcome)。磁盘重建源一等直读;无字段的存量兜底走 projectOutcome。 */
640
+ outcome?: ExecutionOutcome;
641
+ mode: ExecutionMode;
642
+ startedAt: number;
643
+ /** 根 Pi session ID(session 隔离过滤用)。递归链上所有层 record 同值。 */
644
+ rootSessionId: string | undefined;
645
+ /** 直接父 subagent record ID(层级树构建用)。顶层 record 为 undefined。 */
646
+ parentRecordId: string | undefined;
647
+ /** subagent 递归深度。顶层 =0,每层嵌套 +1。 */
648
+ depth: number;
649
+ /**
650
+ * 来源身份(H2 W1,D1,与 ExecutionRecord.origin 同源投影/entry 重建)。
651
+ * 缺省(undefined / 存量磁盘重建源)语义 = "tool";消费面按 `=== "workflow"`
652
+ * 负向判定,list 查询缺省过滤(includeWorkflow 缺省 false)。
653
+ */
654
+ origin?: RecordOrigin;
655
+ /**
656
+ * origin="workflow" 时所属 workflow run id(与 ExecutionRecord.parentRunId 同源)。
657
+ * W2/W3 run 视图下钻按 collectRecordsByParentRunId 查询;tool 来源恒 undefined。
658
+ */
659
+ parentRunId?: string;
660
+ endedAt: number | undefined;
661
+ turns: number;
662
+ totalTokens: number;
663
+ model: string;
664
+ thinkingLevel: string | undefined;
665
+ eventLog: AgentEventLogEntry[];
666
+ /** [STEP3] 从 turns[] 派生的展示项(对齐 nicobailon getDisplayItems)。 */
667
+ displayItems: DisplayItem[];
668
+ /** running 时的当前活动行(仅内存源;磁盘重建无此数据)。streaming 可观测性用。 */
669
+ currentActivity?: {
670
+ type: "tool" | "text" | "thinking";
671
+ label: string;
672
+ };
673
+ result?: string;
674
+ error?: string;
675
+ sessionFile?: string;
676
+ /** [MF#3] worktree 模式下子 agent 改动的 patch 文件路径(worktree 外,供调用方应用)。 */
677
+ patchFile?: string;
678
+ /**
679
+ * [review round2] 创建时启用 worktree 隔离(磁盘重建源从 session entry 恢复;内存源由
680
+ * recordToSubagent 从 worktreeHandle 投影)。getRecordForAction 跨重启重建时据此拒绝续聊。
681
+ */
682
+ worktree?: boolean;
683
+ /**
684
+ * 对话轮次计数(仅 chatMode idle record 有意义)。round 仅在内存维护(doFinalizeRoundToIdle
685
+ * 递增),跨重启不恢复(round 无磁盘持久化);非对话模式 / 非 idle record 为 undefined。内存源由 recordToSubagent 从
686
+ * ExecutionRecord.round 投影。
687
+ */
688
+ round?: number;
689
+ /**
690
+ * 对话模式标志(与 ExecutionRecord.chatMode 同义;投影给 GUI 侧 streaming/waiting/done
691
+ * 细分判据——one-shot 轮终(chatMode 非 true + result 有值)显示完成态,chat 轮终显示
692
+ * 等续聊。内存源由 recordToSubagent 投影,磁盘源经 subagent-record entry 重建)。
693
+ */
694
+ chatMode?: boolean;
695
+ /**
696
+ * 执行态信号(与 ExecutionRecord.resumable 同义):true = 无活进程驱动的 running
697
+ * (轮终 idle / 重建孤儿兜底),GUI 侧据此排除「真在跑」判定。
698
+ */
699
+ resumable?: boolean;
700
+ /** fork 模式下的 worktree handle。 */
701
+ worktreeHandle?: WorktreeHandle;
702
+ /**
703
+ * 实际执行引擎 id(P4 路由留痕)。缺省 = pi 投影(存量 record 零迁移);
704
+ * GUI 警告条/引擎标记的数据源之一。
705
+ */
706
+ engine?: string;
707
+ /** 引擎 fallback 留痕(D9①:probe 失败路由回默认引擎)。GUI 警告条数据源。 */
708
+ engineFallback?: {
709
+ from: string;
710
+ reason: string;
711
+ };
712
+ /**
713
+ * 引擎自描述定位符(U1:EngineHandleData 的持久化消费面子集,引擎无关——
714
+ * sessionRef 整体透传不枚举内部键)。read 降级链①②级的数据源(runtime
715
+ * subagent-engine-history);缺省 = pi(走 JSONL 直读链)。
716
+ */
717
+ engineHandle?: {
718
+ sessionRef: Record<string, string>;
719
+ journalPath?: string;
720
+ poolKey: string;
721
+ };
722
+ /**
723
+ * 同步收集模式标记(与 ExecutionRecord.collectMode 同源投影/重建,U1 foundation)。
724
+ * 缺省(存量 record)= async 投影,消费方零迁移。
725
+ */
726
+ collectMode?: "sync";
727
+ /**
728
+ * 离开批终局标记(与 ExecutionRecord.batchFinalized 同源投影/重建,U1 foundation)。
729
+ * 缺省 = 未离开批;U5 E1 重建扫描据此排除已离场成员。
730
+ */
731
+ batchFinalized?: boolean;
732
+ }
733
+ /**
734
+ * 同步收集(sync collect)配置节类型(subagent-sync-collect 设计 §3.1.3,U1 foundation)。
735
+ * 权威默认值在 config.ts DEFAULT_COLLECT_SYNC;坏值 sanitize 回默认不炸启动(E5,
736
+ * 与 maxConcurrent 同判)。类型定义于 types.ts(避免 config → types 反向依赖成环),
737
+ * config.ts re-export。
738
+ */
739
+ interface CollectSyncConfig {
740
+ /** start 未显式传 collect 时的缺省模式。新 session 生效(与 engine 配置时机一致)。 */
741
+ default: "async" | "sync";
742
+ /** 批通知单条目结果正文预算(字符):超出截断并接 session_read 指针行。flush 时热读。 */
743
+ perItemChars: number;
744
+ /** 批通知结果正文总量预算(字符):Σ 超限时统一收紧 effectivePerItem(U4 算法)。flush 时热读。 */
745
+ totalChars: number;
746
+ }
747
+ /**
748
+ * 全局配置(~/.pi/agent/subagents/config.json)。
749
+ *
750
+ * 模型解析已退化为「主 agent model 优先,仅 override 时查 registry」——
751
+ * 不再有 category/fallback/yolo 字段。config.json 只保留 maxConcurrent
752
+ * (pool 大小)。旧 config.json 中的 categories/fallback 等字段读取时忽略。
753
+ */
754
+ interface SubagentsGlobalConfig {
755
+ version: number;
756
+ maxConcurrent: number;
757
+ /**
758
+ * 全局默认执行引擎(D9 三层优先级的最底层:调用参数 > agent frontmatter > 本值)。
759
+ * 缺省 'pi'(P4 路由层 DEFAULT_ENGINE_ID)。加载期只做类型校验,注册表校验归路由层。
760
+ */
761
+ defaultEngine?: string;
762
+ /** 引擎路由策略(D9①):strict=true 时一切 probe 失败直接报错(不 fallback)。 */
763
+ engineRouting?: {
764
+ strict: boolean;
765
+ };
766
+ /**
767
+ * 同步收集配置节(subagent-sync-collect 设计 §3.1.3,U1 foundation)。
768
+ * 整节缺省 = DEFAULT_COLLECT_SYNC(config.ts);逐字段 sanitize 回默认(E5)。
769
+ */
770
+ collectSync?: CollectSyncConfig;
771
+ }
772
+ /**
773
+ * Record 的只读视图。store.snapshot() 返回。
774
+ * TUI 拿到此类型,保证不会回写 Core 状态。
775
+ *
776
+ * 不含 eventLog——snapshot 的消费点(cancel 判 mode/status、hasRunning 判 mode、
777
+ * toNotifyRecord 取 result/error)均不读 eventLog。需要 eventLog 的场景用 project()
778
+ * 投影的 SubagentToolDetails。需要完整内容用 record.turns[](Core 内部)。
779
+ */
780
+ interface RecordSnapshot {
781
+ readonly id: string;
782
+ readonly agent: string;
783
+ readonly model: string;
784
+ readonly thinkingLevel: string | undefined;
785
+ readonly mode: ExecutionMode;
786
+ readonly task: string;
787
+ /** 短标签(≤35 字符)。来自 record.slug。 */
788
+ readonly slug: string;
789
+ readonly status: ExecutionStatus;
790
+ /** 对话模式标志(与 ExecutionRecord.chatMode 同源)。cancel 别名判定用。 */
791
+ readonly chatMode?: boolean;
792
+ readonly turns: number;
793
+ readonly totalTokens: number;
794
+ readonly startedAt: number;
795
+ readonly endedAt: number | undefined;
796
+ readonly result: string | undefined;
797
+ readonly error: string | undefined;
798
+ readonly sessionFile: string | undefined;
799
+ }
800
+
12
801
  type ExtensionMode = "tui" | "rpc" | "json" | "print";
13
802
 
14
803
  /** 投递意图(与 session-delivery 的 DeliveryIntent 字面量一致)。 */
@@ -497,215 +1286,396 @@ declare function projectLiveProgress(record: ExecutionRecord): {
497
1286
  lastError: string | undefined;
498
1287
  };
499
1288
 
500
- /** 入队项的 child 引用形状(只取 pid 用于 rejectChildDialogs 匹配)。 */
501
- interface DialogChildRef {
502
- pid: number;
1289
+ interface ManifestRecord {
1290
+ id: string;
1291
+ rootSessionId: string;
1292
+ /** 直接父 subagent record ID(层级树构建用)。顶层 record 缺失(undefined)。M3a 补字段。 */
1293
+ parentRecordId?: string;
1294
+ agentName: string;
1295
+ /**
1296
+ * 终态枚举:finalizeRecord 写 running/closed/cancelled 三态。
1297
+ * SP-1 重构:旧 completed/failed 合并为 closed(L1 统一终态)。
1298
+ * cancelled 保持独立(用户取消语义)。crashed 不进 manifest——
1299
+ * crashed 是重启重建时靠 sidecar 四分支推断的派生态(见 record-store.ts reconstructAll)。
1300
+ * 历史 "error"/"completed"/"failed" 值由读侧 mapManifestStatus 向后兼容映射。
1301
+ */
1302
+ status: "running" | "closed" | "cancelled";
1303
+ /**
1304
+ * [U4c / D5 词汇双写过渡] 内部权威状态词汇(ExecutionStatus 二态)。
1305
+ * 与上方旧 status 三态投影**永久双写**——无版本磁盘 schema 不做破坏性变更;
1306
+ * session-reader(独立 npm 包独立进程)直读旧 status 做 identity 富字段投影
1307
+ * 与孤儿判定,删字段 = 外部消费方富字段降级。旧 status 只降权威地位不删字段。
1308
+ * 宿主内消费方不读本字段(终态判定走 `.state` 权威,D1);本字段是词汇收口
1309
+ * (全景① ExecutionStatus+ClosedReason)在 manifest 写面的过渡锚。
1310
+ */
1311
+ executionStatus?: ExecutionStatus;
1312
+ /**
1313
+ * [M2 Gate B] closed 终态的 L2 关闭原因(status="closed" 时有意义)。旧 manifest 无
1314
+ * 此字段(undefined = 死因不可考,读侧守卫归一 undefined)。缺失时 manifest 源重建
1315
+ * 的快照丢 closedReason,endedMessageGuard 把 user-close/cancelled 误分流进
1316
+ * 「reconnectable/fork-from」分支——本字段是 manifest 源快照三分流的唯一依据
1317
+ * (磁盘 sidecar 源由 .state reason 承载,不经本字段)。
1318
+ */
1319
+ closedReason?: ClosedReason;
1320
+ createdAt: number;
1321
+ completedAt?: number;
1322
+ sessionFile?: string;
1323
+ /** FR-7 补字段:manifest 写入时从 ExecutionRecord 抓取,供 manifestToSubagent 投影真实值。 */
1324
+ task?: string;
1325
+ slug?: string;
1326
+ model?: string;
503
1327
  }
504
- /** enqueue 的可选项。child 用于 rejectChildDialogs 关联(child close 时批量 reject)。 */
505
- interface EnqueueOptions {
506
- child?: DialogChildRef;
1328
+ declare class ManifestStore {
1329
+ private readonly dir;
1330
+ /** [perf] per-file 缓存:file → { stamp, record }。record=null 表示「已解析但非法」(缓存
1331
+ * 负结果避免反复 parse 损坏文件)。stat 戳变化(writeManifest tmp→rename 后 mtime/size 变)
1332
+ * 自动失效;删除的文件在下次扫描时修剪。 */
1333
+ private readonly cache;
1334
+ constructor(dir: string);
1335
+ /**
1336
+ * 原子写:tmp → fsync → rename → fsync dir(shared/atomic-write 统一原语,
1337
+ * U6b 迁移——原逐行实现与 writeAtomicFile 逐环等值)。真异步(fs.promises,
1338
+ * 不阻塞 event loop)。
1339
+ *
1340
+ * 失败时原语尽力清理残留 tmp(debug 记录,不掩盖原错误)并原样上抛——
1341
+ * 调用方(RecordStore 写面:writeManifestPersisted 缺省异步分支 / 批写 barrier /
1342
+ * rebuildIndexes 重建降级(debug 留痕)/ rematerializeManifest(warn 语义))
1343
+ * 决定降级策略——各面降级策略分化见各调用点。
1344
+ */
1345
+ writeManifest(record: ManifestRecord): Promise<void>;
1346
+ /**
1347
+ * 按 id 读 manifest。文件不存在/JSON 损坏/schema 不合法均返回 null。
1348
+ * 调用方需处理 null。
1349
+ */
1350
+ readManifest(id: string): Promise<ManifestRecord | null>;
1351
+ /**
1352
+ * 同步读取所有 manifest 记录(best-effort,损坏/非法文件跳过)。
1353
+ * 供 RecordStore.collectRecords 投影 orphan 记录使用——替代对私有 dir 的反射访问。
1354
+ * 仅返回通过 isValidManifest 校验的记录。
1355
+ *
1356
+ * [perf] per-file 缓存 + stat 戳校验:collectRecords 每次渲染都调本方法,旧实现每次
1357
+ * 全量 readFileSync + JSON.parse 千级 manifest(实测 ~300ms/次)。命中缓存的文件零读取。
1358
+ */
1359
+ listAllSync(): readonly ManifestRecord[];
1360
+ /**
1361
+ * 启动时清扫 tmp 残留([U4c / D6] tmp 恢复退役后的语义——[H4/U5 收口] 更名
1362
+ * recoverTmpFiles → sweepTmpFiles,名实对齐「静默删除」)。
1363
+ *
1364
+ * 旧语义(ADR-035 三分支:manifest 已存在删 tmp / tmp 合法且 manifest 缺失
1365
+ * promote / tmp 非法删)已随缓存降级退役——manifest 现为可丢可重建缓存
1366
+ * (权威 = `.state`,重建 = RecordStore.rebuildIndexes,D5),promote 半写 tmp
1367
+ * 只会把陈旧快照复活成「看似权威」的索引,语义失效;统一**静默删除**全部
1368
+ * tmp(含 0 字节/半写形态——D8 停机窗残留由本清扫顺带清理)。
1369
+ *
1370
+ * [T5④ / PS-13] per-file 容错保留:单个 tmp 删除失败(ENOENT——并发回收/外部
1371
+ * 清理抢先、EACCES 等)只 warn + 跳过该文件,不再中断整轮。promote 退役后
1372
+ * 无恢复形态,返回值简化为删除计数。
1373
+ *
1374
+ * @returns 删除的 tmp 文件数。
1375
+ */
1376
+ sweepTmpFiles(): Promise<number>;
1377
+ }
1378
+
1379
+ /** 三态读取结果(判别联合,调用方按 status 分派)。 */
1380
+ type GlobalConfigReadResult =
1381
+ /** 读到明确值:JSON 可解析,字段已经 sanitize。 */
1382
+ {
1383
+ status: "ok";
1384
+ config: SubagentsGlobalConfig;
1385
+ }
1386
+ /** 明确缺省:文件不存在(ENOENT)。这是用户意图(删配置切回缺省 pi),不是故障。 */
1387
+ | {
1388
+ status: "absent";
1389
+ config: SubagentsGlobalConfig;
507
1390
  }
1391
+ /** 读失败:坏 JSON / 权限等。携带原始错误消息供诊断;调用方保持 lastEngine 不动。 */
1392
+ | {
1393
+ status: "failed";
1394
+ reason: string;
1395
+ };
508
1396
  /**
509
- * L2 跨子进程全局 dialog 串行队列(进程单例)。
510
- *
511
- * 用法(createUiRequestHandlerForMode 返回的总 handler 内):
512
- * ```ts
513
- * const dialogQueue = new DialogGlobalQueue();
514
- * return async (req: UiRequest) => {
515
- * // 调用方负责判断:dialog 入队,fire-and-forget 直接调 realHandler
516
- * if (isDialogMethod(req.method)) return dialogQueue.enqueue(req, realHandler);
517
- * return realHandler(req);
518
- * };
519
- * ```
520
- *
521
- * 语义保证:
522
- * - FIFO 串行:前一个 handler settle 后才处理下一个
523
- * - SR-4:rejectChildDialogs(child) 把该 child 的 pending 全部 resolve 为 {cancelled:true}
524
- * - handler 抛错兜底:catch → {cancelled:true} → 继续下一个(队列不卡死)
525
- * - 超时上界(LC-3/T2⑦):每个 dialog 项必有上界——req.timeout 显式传值优先,
526
- * 未传挂 DEFAULT_DIALOG_TIMEOUT_MS(30min,裁决值);到点 settle {cancelled:true}
527
- * 并 warn(恢复指引见 dialogTimeoutLogMessage),L2 processing 释放、队列继续推进。
528
- * 「等用户无限久」改为默认有界是有意的行为变更。
529
- * - 调用方约定只对 dialog 类调 enqueue;fire-and-forget 由调用方直接调 handler 不入队
530
- *(enqueue 内仍防御性兼容 fire-and-forget,但不保证行为)
1397
+ * 三态读取全局配置(设计 D5)。
531
1398
  *
532
- * 线程模型:纯 Promise + 微任务驱动,无锁。Node 单线程 event loop 保证队列状态一致。
1399
+ * 为什么不用 loadGlobalConfig:后者 ENOENT 与坏 JSON 同归 DEFAULT_CONFIG(静默
1400
+ * 回落),检测器若消费该形态会把「用户删配置切回缺省」(合法变更,须生效)误判成
1401
+ * 「读失败」(须保持 lastEngine),也会把坏 JSON(torn write)误判成合法缺省——
1402
+ * 两个方向都会产生错误的引擎切换信号。
1403
+ */
1404
+ declare function readGlobalConfig(agentDir: string): GlobalConfigReadResult;
1405
+
1406
+ /** Service 构造参数(进程级,跨 session 不变)。 */
1407
+ interface ModelConfigServiceInit {
1408
+ agentDir: string;
1409
+ /** 项目根目录(ctx.cwd,用于推导 workspaceRoot 扫描 project 级资源)。 */
1410
+ cwd: string;
1411
+ }
1412
+ /** session_start 注入参数(session 级,每次重建)。 */
1413
+ interface ModelServiceSessionInit {
1414
+ /** 模型注册表(鉴权 + 发现)。null 立即抛错(fail-fast)。 */
1415
+ modelRegistry: ModelRegistryLike | null;
1416
+ /** 当前 session ID。 */
1417
+ sessionId: string;
1418
+ /**
1419
+ * 主 agent 当前 model(session_start 时注入,model_select 时刷新)。
1420
+ *
1421
+ * renderCall 阶段的 ToolRenderContext 不含 model 字段(SDK 限制),无法直接拿到
1422
+ * 主 agent model。缓存后 renderCall 的 resolveModel 能命中第三层(ctxModel),
1423
+ * 让标题行恢复显示 model——即使未显式传 model 也能展示默认 model。
1424
+ *
1425
+ * [HISTORICAL] 99f20da1e 引入三层 fallback 后,renderCall 因拿不到 ctxModel
1426
+ * 而 resolveModel 拗错→降级不显示 model。此缓存修复该降级。
1427
+ */
1428
+ ctxModel?: ModelInfo;
1429
+ }
1430
+ /**
1431
+ * 配置 + 模型解析 Service。进程级单例。
533
1432
  *
534
- * 单 session 假设(M-2,与 index.ts lastSessionId 同源):本队列是进程级单例(实例挂在
535
- * globalThis[Symbol.for("@zhushanwen/pi-subagents.dialogQueue")],见 getOrCreateDialogQueue)。
536
- * rejectAll() 清空所有 pending dialog——无 per-session 隔离。Pi 当前架构保证单进程
537
- * 单 session 串行(同进程不会并发多个 session),故 session_shutdown 调 rejectAll() 只会清掉
538
- * 当前 session 的 pending。若未来 Pi 支持同进程多 session 并发,session A 退出会误清 session B
539
- * pending dialog——届时需改为 per-session 隔离(入队项 QueueItem sessionId,rejectAll
540
- * 改 rejectAllForSession(sessionId),session_shutdown 只清当前 session)。
1433
+ * ┌──────────────────────────────────────────────────────┐
1434
+ * │ globalConfig(~/.pi/.../config.json,仅 maxConcurrent)│
1435
+ * │ agentRegistry(agent .md 发现 + frontmatter) │
1436
+ * │ modelRegistry(SDK 注入的可用模型) │
1437
+ * │ │
1438
+ * │ resolveModel: override agentConfig agent model
1439
+ * └──────────────────────────────────────────────────────┘
541
1440
  */
542
- declare class DialogGlobalQueue {
543
- /** 等待处理的队列(FIFO)。正在处理的项从 queue shift 出后由 current 持有。 */
544
- private queue;
545
- /** 正在处理的项(handler 已调、未 settle)。用于 rejectChildDialogs 取消占位中的 dialog。 */
546
- private current;
547
- private processing;
1441
+ declare class ModelConfigService {
1442
+ private globalConfig;
1443
+ private readonly agentRegistry;
1444
+ private readonly agentRegistryDir;
1445
+ private modelRegistry;
1446
+ private _sessionId;
1447
+ /** 主 agent 当前 model 缓存(session_start 注入,model_select 刷新)。 */
1448
+ private _ctxModel;
1449
+ constructor(init: ModelConfigServiceInit);
1450
+ /**
1451
+ * session_start 注入。封装 3 步固定时序:
1452
+ * 1. reloadGlobalConfig(复用时拿最新 config)
1453
+ * 2. injectModelRegistry(fail-fast:null 抛错)
1454
+ * 3. setSessionId
1455
+ */
1456
+ initModel(init: ModelServiceSessionInit): void;
548
1457
  /**
549
- * 入队一个 UI 请求,返回 Promise<UiResponse>。
1458
+ * 将一次三态读取结果提交到路由缓存(纯赋值幂等)。
550
1459
  *
551
- * 调用方约定:只对 dialog 类(isDialogMethod===true)调 enqueue。fire-and-forget
552
- * 调用方(ui-request-handler-factory.ts)在 enqueue 前判 isDialogMethod 后直接调 handler,
553
- * 不经过本队列。enqueue 内仍防御性兼容 fire-and-forget(万一调用方未判):直接调 handler 返回,
554
- * 不入队串行,但调用方不应依赖此防御行为。
1460
+ * ok/absent 覆盖缓存、failed 保持缓存不动(坏 JSON 不能把好缓存打回缺省);
1461
+ * 返回入参便于链式消费。用途 = 构造性同源:session_start 初始化与 per-turn 引擎
1462
+ * 检测各只读一次文件,同一读取结果既刷新路由缓存又充当检测基准,消灭两次独立
1463
+ * 读取之间的分叉窗口(两次读值不一致时检测走 unchanged,状态段/路由永停旧值)。
1464
+ */
1465
+ applyGlobalConfig(read: GlobalConfigReadResult): GlobalConfigReadResult;
1466
+ /**
1467
+ * 三态重读全局配置并提交缓存(幂等可重入),返回本次读取结果供调用方感知。
555
1468
  *
556
- * dialog 项处理(TC-E4 case 1):进队列 FIFO 串行,等前一个 settle 后才调 handler
557
- *(争输入焦点,防并发弹窗)。
1469
+ * initModel 提取(设计 D2):引擎感知检测器 per-turn poll 发现 config 变更时
1470
+ * 调用本方法,使「system prompt 现值、路由缓存、变更通知」同 turn 对齐——只改注入
1471
+ * 不刷新路由缓存,会出现 prompt 说引擎 B、实际派发跑引擎 A(权威信息源说谎)。
1472
+ * 幂等性:只做「读文件 → 按三态提交缓存」单向赋值,无时序状态,重复调用收敛到
1473
+ * 同一结果。三态语义(failed 保持缓存、静默回落 DEFAULT 是旧缺陷——读失败曾把
1474
+ * 好缓存打回缺省且调用方无法感知):ok/absent 覆盖、failed 保持并携带原因。
1475
+ */
1476
+ reloadGlobalConfig(): GlobalConfigReadResult;
1477
+ /**
1478
+ * 刷新主 agent model 缓存。model_select 事件时调用。
1479
+ * renderCall 的 resolveModel 读此缓存以显示标题行 model。
1480
+ */
1481
+ setCtxModel(model: ModelInfo | undefined): void;
1482
+ /**
1483
+ * 解析 agent 的模型(三层:override → agentConfig → 主 agent model)。
558
1484
  *
559
- * handler 抛错兜底:catch {cancelled:true}(dialog 路径,队列不卡死)。
560
- * SR-4:opts.child 用于 rejectChildDialogs 关联(dialog 项会被批量 reject,含 current)。
1485
+ * @param agentRef agent 引用(.md 绝对路径;查 agentConfig 的 model override)
1486
+ * @param override 调用方显式 override(最高优先级)
1487
+ * @param ctxModel 主 agent 当前模型(兜底,直接透传)
1488
+ */
1489
+ resolveModel(agentRef: string, override?: {
1490
+ model?: string;
1491
+ thinkingLevel?: string;
1492
+ }, ctxModel?: ModelInfo,
1493
+ /** 已解析的 agent 配置(调用方已加载时复用,避免同一 agentRef 二次 loadByPath)。 */
1494
+ agentConfig?: AgentConfig): ResolvedModel;
1495
+ /** 查询 agent 配置(SubagentService 内部判定 defaultBackground 用)。
1496
+ * undefined = 合法缺省语义(未点名 / 默认 general-purpose 形态)。 */
1497
+ getAgentConfig(agentRef?: string): AgentConfig | undefined;
1498
+ /**
1499
+ * 查询 agent 配置——显式 ref 失败即 throw(SubagentService.resolveIdentity 用)。
561
1500
  *
562
- * @param req UI 请求(约定只传 dialog 类;fire-and-forget 防御性兼容)
563
- * @param handler 真正执行请求的 handler(TUI/GUI 模式分流后的 realHandler)
564
- * @param opts 可选 child 引用(用于 rejectChildDialogs 关联)
565
- * @returns handler 的响应;dialog 抛错时回 {cancelled:true};child close 时回 {cancelled:true}
1501
+ * getAgentConfig 的语义分界(「用户显式点名」vs「默认 general-purpose」):
1502
+ * 用户显式点名的 agentRef(工具 agent 参数 / workflow agent({agent}) opts)解析
1503
+ * 失败 = 配置错误,必须显式报错——错误文案含 <available_subagents> 恢复指引
1504
+ * (对齐 workflow name not found 反馈风格),不允许静默降级为无配置
1505
+ * general-purpose 形态(systemPrompt/工具白名单全丢且零反馈)。默认形态
1506
+ * (不传 agent)走 getAgentConfig:undefined = 合法缺省,走 override → ctxModel 兑底。
566
1507
  */
567
- enqueue(req: UiRequest, handler: UiRequestHandler, opts?: EnqueueOptions): Promise<UiResponse>;
1508
+ getRequiredAgentConfig(agentRef: string): AgentConfig;
1509
+ /** 全局配置深拷贝(调用方拿到副本,改不影响 Service 内部)。 */
1510
+ getGlobalConfig(): SubagentsGlobalConfig;
1511
+ /** 内部:session id 缓存(initModel 注入;当前无消费者,保留供未来 session 作用域需求)。 */
1512
+ get sessionId(): string | undefined;
1513
+ /** agent 配置目录(SubagentService 构造 store/SessionRunnerContext 时读)。 */
1514
+ getAgentDir(): string;
1515
+ /** modelRegistry(SubagentService 构造 factoryCtx 时读)。已注入保证非 null。 */
1516
+ getModelRegistry(): ModelRegistryLike;
1517
+ /** 校验 modelRegistry 已注入。 */
1518
+ private assertReady;
1519
+ }
1520
+ /** 获取进程单例。session_start 前为 null。 */
1521
+ declare function getModelConfigService(): ModelConfigService | null;
1522
+ /** 设置进程单例(session_start 首次创建时)。 */
1523
+ declare function setModelConfigService(service: ModelConfigService): void;
1524
+
1525
+ declare class WorktreeManager {
1526
+ private readonly registry;
1527
+ private readonly agentDir;
1528
+ private readonly writeQueues;
1529
+ private readonly ambiguousSkipCycles;
1530
+ constructor(agentDir: string);
568
1531
  /**
569
- * settle 一个 item(幂等)。handler 完成 / rejectChildDialogs / rejectAll / 超时 timer
570
- * 都通过本方法,settled 标志保证只 settle 一次(防竞争)。
1532
+ * 为子 agent 创建隔离 worktree。
571
1533
  *
572
- * #19 单一推进点:本方法 settle Promise + 清状态后,**唯一**调 processNext 推进队列。
573
- * processNext 尾部不再调 processNext(旧代码双重推进,虽靠 processing 标志幂等,但语义混乱)。
574
- * 为什么推进必须在 settleItem 而非 processNext 尾部:rejectChildDialogs 取消一个永不 settle
575
- * 的 current(handler 等用户输入卡死)时,processNext 的 `await item.handler` 永不 resume,
576
- * 尾部不会执行;只有 settleItem 里的 processNext 才能打破死锁,推进下一个。
1534
+ * @param mainCwd 主仓库根目录
1535
+ * @param recordId 执行记录 ID(必须匹配 `^[\w-]+$`)
1536
+ * @returns 冻结的 WorktreeHandle
1537
+ */
1538
+ create(mainCwd: string, recordId: string): Promise<WorktreeHandle>;
1539
+ /**
1540
+ * 注册子进程 pid(runSpawn spawn() 返回后调)。
1541
+ * create 时 pid 未知写 0 占位,子进程 spawn 返回后(child.pid 同步可得)由此补全。
1542
+ * reaper 据 pid 死活判孤儿,pid=0 条目用 SPAWN_GRACE 宽限。
1543
+ * sessionFile 可选补全:传入时填入 registry entry(reaper 据 pid 死活判孤儿,不读本字段;保留供诊断)。
577
1544
  *
578
- * [A2-2] 超时 timer 清理也统一收口在这里:任何路径抢先 settle(reject/rejectAll/超时
579
- * 自身)都必须撤下 armed timer——handler 永挂时 processNext 内联的 clearTimeout 不可达,
580
- * 不在这里清则 timer 到期触发虚假「dialog timed out」warn 且句柄滞留至超时点。
1545
+ * [D5a] async 化:pid 补全走跨进程锁内 RMW(互斥窗口消除 updatePid 与并发 add/remove
1546
+ * 的交错)。永不 reject(锁降级 + best-effort save 均内部兜底),调用方可安全
1547
+ * fire-and-forget(session-runner stdout data 回调上下文)。
581
1548
  */
582
- private settleItem;
1549
+ registerPid(branch: string, pid: number, sessionFile?: string): Promise<void>;
583
1550
  /**
584
- * SR-4:把指定 child 的所有 pending dialog resolve {cancelled:true}。
1551
+ * 清理 worktree:git worktree remove --force + git branch -D + 注册表移除。
1552
+ * 三步各自独立 try/catch——任一步失败不阻断其余(如 remove 失败仍尝试 branch -D + 注册表移除),
1553
+ * 避免单步失败导致后续资源泄漏。
585
1554
  *
586
- * 触发场景:子进程 close(用户取消 / crash / 超时 kill)时,其 pending dialog 的 handler
587
- * 可能永不 settle(等用户输入),导致 Promise 永挂 + 内存泄漏。本方法批量清理。
1555
+ * @param handle 要清理的 worktree handle(含 mainCwd,不靠路径反推)
1556
+ */
1557
+ cleanup(handle: WorktreeHandle): Promise<void>;
1558
+ /**
1559
+ * 收集 worktree 的改动为 patch。
588
1560
  *
589
- * 处理范围(TC-E4 case 2):
590
- * - 正在处理中(current)的该 child 项:settle {cancelled:true},解阻塞队列推进下一个
591
- * (关键:handler 可能永不 settle,必须由这里打破死锁)
592
- * - 队列中等待处理的该 child 项:settle {cancelled:true} 并移除
1561
+ * [MF#3] patchFile 由调用方指定(写在 worktree 之外,避免被 cleanup 删除)。
1562
+ * [MF#2] 先 git add -A 暂存全部改动(含未跟踪新文件),再 git diff --cached baseCommit
1563
+ * 对比暂存区与 base commit。旧实现 `git diff HEAD baseCommit` 是树 vs 树对比:
1564
+ * worktree HEAD 初始即 baseCommit,子 agent 不提交时 HEAD 仍 == baseCommit → diff 恒空 → 改动丢失。
593
1565
  *
594
- * 不影响其他 child pending dialog(TC-E4 case 2 子测试 2)。
1566
+ * @param handle worktree handle
1567
+ * @param patchFile patch 输出路径(须在 worktree 之外)
1568
+ * @returns patch 结果(patchFile 路径 + failed/written 标记)。
1569
+ * written=true 仅当 diff 非空且写盘成功;空 diff 或写失败均 written=false,
1570
+ * 调用方据此回填 record.patchFile,避免悬空路径(`git apply` 不存在的文件)。
595
1571
  */
596
- rejectChildDialogs(child: DialogChildRef): void;
1572
+ collectPatch(handle: WorktreeHandle, patchFile: string): Promise<PatchResult>;
597
1573
  /**
598
- * 处理队列下一项(FIFO)。
1574
+ * 扫描并清理 pi-sub-* 孤儿 worktree + 物理面对账(D5b)。
599
1575
  *
600
- * processing 标志保证串行:handler 运行期间 processing=true,新的 processNext 调用直接返回;
601
- * handler settle 后由 settleItem processing=false 并推进下一项(#19 单一推进点)。
1576
+ * 阶段一(既有):遍历全局注册表(<agentDir>/subagents/worktrees.json),
1577
+ * pid 死活判孤儿。不依赖当前 cwd 是否 git repo——注册表里记了 repo 路径,
1578
+ * 直接 git -C <repo> 跨 repo 清理。
602
1579
  *
603
- * handler 抛错兜底(TC-E4 case 3):catch → settle {cancelled:true} → 继续。
604
- * 不能让一个失败卡死队列(processing 永远 true)。
1580
+ * 判据(唯一不删条件 = 进程还活着):
1581
+ * pid > 0 且 isProcessAlive(pid) → 跳过(活进程,绝不删)
1582
+ * pid > 0 且进程已死 → 孤儿(正常退出未 cleanup / 崩溃残留)
1583
+ * pid == 0 且超 SPAWN_GRACE_MS → 孤儿(create 后崩溃,pid 永未补全)
1584
+ * pid == 0 且未超宽限 → 跳过(可能正在 spawn)
605
1585
  *
606
- * LC-3/T2⑦ 超时上界:`await item.handler` 原本无上界——host UI promise 挂死或用户
607
- * 永不回答时 processing true(全局 dialog 死锁)。现在每项挂队列级 timer:
608
- * req.timeout(请求方显式传值)优先,未传/非法挂 DEFAULT_DIALOG_TIMEOUT_MS。
609
- * 到点 settle {cancelled:true}(完整错误消息落父进程日志,含恢复指引与等待时长),
610
- * settleItem 的 settled 标志保证与 handler 完成 / rejectChildDialogs 三方竞态下
611
- * 恰 settle 一次。timer 回调闭包捕获 item(非读 this.current):迟到触发时
612
- * settled 标志已置位,直接 noop,不误伤后继项。
1586
+ * 阶段二(D5b):物理面(tmpdir checkout + 分支)与注册表双向 diff 收敛——
1587
+ * 兑现 worktree-registry.ts 头注释声称的「tmpdir + 分支对账兜底」。全流程
1588
+ * 幂等、失败仅日志(对账失败不阻断 session_start)。
613
1589
  */
614
- private processNext;
1590
+ scan(): Promise<void>;
615
1591
  /**
616
- * #10:把所有 pending dialog(queue + current)全部 settle 为 {cancelled:true},
617
- * 并清空 queue/current/processing 状态。session_shutdown 调用,保证不留永挂 Promise。
618
- *
619
- * 约定签名:rejectAll(): void(无参,返 void)。Group C 的 index.ts session_shutdown 依赖此契约。
620
- *
621
- * 幂等:依赖 settleItem 的 settled 标志——重复调用只会对已 settled 项 noop。
622
- * 顺序敏感(#19 推进点在 settleItem):必须先清空 queue 数组再 settle current,
623
- * 否则 settleItem(current) 同步触发的 processNext 会从旧 queue 抢占下一项作为新 current,
624
- * 避开本方法的 cancel 语义。清空后 processNext 看到空队列直接返回,新 current 不会被抢占。
1592
+ * D5b 双向 diff 对账:物理面(tmpdir checkout 目录 + git branch --list)与
1593
+ * 注册表互查,收敛三类漂移(锁消灭交错主因后,本对账兜底条目丢失/文件损坏的长尾)。
1594
+ * 方向一/方向二的完整判据见 {@link removePhantomRegistryEntries} /
1595
+ * {@link reconcileUnregisteredWorktrees}。
1596
+ */
1597
+ private reconcileWithPhysical;
1598
+ /** 对账方向一(注册有 物理无):条目的分支与 checkout 目录都已不存在 条目指向
1599
+ * 幻影资源 移除条目(纯清账,不删任何仍存在的资源,幂等安全)。 */
1600
+ private removePhantomRegistryEntries;
1601
+ /** 对账方向二(物理有 → 注册无):按 enc 段(encodeCwd(mainCwd))聚合,活信号 =
1602
+ * <agentDir>/subagents/<enc>/sessions/*.alive 中存活的 pid(session-runner
1603
+ * first header 时写入,崩溃残留不删):
1604
+ * - 无活 pid:残留判死,checkout mtime 超 SPAWN_GRACE_MS 才清(防误清另一
1605
+ * 进程 worktree add 完成到 registry.add 落盘之间的 create 窗口);
1606
+ * - 恰好 1 个活 pid 且恰好 1 个残留:补写回注册表(自愈——最常见的双 session
1607
+ * 并发覆盖丢条目场景,补写后回归标准 pid 判据路径);
1608
+ * - 多活 pid 或多残留无法建立 branch↔pid 对应:跳过 + warn——宁延迟勿误删;
1609
+ * 活体自身 cleanup 路径正常(registry.remove 幂等),死体等活 pid 全灭后
1610
+ * 下一周期收敛。
625
1611
  *
626
- * session 假设(M-2):见类注释。本方法清空所有 pending 不分 session——依赖 Pi 单进程
627
- * session 串行保证。session_shutdown handler(index.ts)调用本方法时,进程内只会有当前
628
- * session 的 pending dialog。多 session 并发场景的迁移策略(rejectAllForSession)见类注释。
1612
+ * 返回本轮实际进入「歧义跳过」的 checkout 路径集(PS-12 老化计数收敛依据,
1613
+ * reconcileWithPhysical 尾部)。 */
1614
+ private reconcileUnregisteredWorktrees;
1615
+ /** 单 enc 段的残留处置三分支:无活 pid 判死清理 / 唯一对应自愈补写 / 多对应保守跳过。 */
1616
+ private reconcileEncSegment;
1617
+ /** 无活 pid 段:残留判死清理——checkout mtime 超 SPAWN_GRACE_MS 才清(防误清另一
1618
+ * 进程 worktree add 完成到 registry.add 落盘之间的 create 窗口)。 */
1619
+ private cleanupDeadSegment;
1620
+ /**
1621
+ * 物理面发现:扫描 <tmpdir>/pi-subagents/<enc>/<pi-sub-*> checkout 目录。
1622
+ * repo 从 checkout/.git 指针文件推导(`gitdir: <repo>/.git/worktrees/<branch>`,
1623
+ * 普通 repo 与 bare+worktree(.bare/worktrees/...)统一取 worktrees 段上两级);
1624
+ * 推导失败(残缺 checkout)repo=undefined,由调用方按无主残留处置。
629
1625
  */
630
- rejectAll(): void;
631
- /** 清空队列状态(仅在 rejectAll 之后调用)。pending Promise 必须先由 rejectAll settle。
632
- * 不 settle Promise 的纯状态重置——单独调用会导致 Promise 永挂(footgun),故设为 private。
633
- * 外部调用方应使用 rejectAll()(它 settle 所有 pending + 重置状态,是原子操作)。 */
634
- private resetState;
635
- /** 当前队列长度(测试/诊断用)。含等待处理项(不含 current)。 */
636
- get size(): number;
637
- }
638
-
639
- interface ManifestRecord {
640
- id: string;
641
- rootSessionId: string;
642
- /** 直接父 subagent record ID(层级树构建用)。顶层 record 缺失(undefined)。M3a 补字段。 */
643
- parentRecordId?: string;
644
- agentName: string;
1626
+ private discoverPhysicalWorktrees;
645
1627
  /**
646
- * 终态枚举:finalizeRecord running/closed/cancelled 三态。
647
- * SP-1 重构:旧 completed/failed 合并为 closedL1 统一终态)。
648
- * cancelled 保持独立(用户取消语义)。crashed 不进 manifest——
649
- * crashed 是重启重建时靠 sidecar 四分支推断的派生态(见 record-store.ts reconstructAll)。
650
- * 历史 "error"/"completed"/"failed" 值由读侧 mapManifestStatus 向后兼容映射。
1628
+ * per repo 查物理分支全集:`git -C <repo> branch --list 'pi-sub-*' --format=%(refname:short)`。
1629
+ * 读类命令不加写锁;单 repo 失败 map 不含该 repoget 返回 undefined),
1630
+ * 调用方据此保守跳过该 repo 的条目判定(防把「查询失败」误判成「分支不存在」)。
651
1631
  */
652
- status: "running" | "closed" | "cancelled";
653
- createdAt: number;
654
- completedAt?: number;
655
- sessionFile?: string;
656
- /** FR-7 补字段:manifest 写入时从 ExecutionRecord 抓取,供 manifestToSubagent 投影真实值。 */
657
- task?: string;
658
- slug?: string;
659
- model?: string;
660
- }
661
- declare class ManifestStore {
662
- private readonly dir;
663
- /** [perf] per-file 缓存:file → { stamp, record }。record=null 表示「已解析但非法」(缓存
664
- * 负结果避免反复 parse 损坏文件)。stat 戳变化(writeManifest tmp→rename 后 mtime/size 变)
665
- * 自动失效;删除的文件在下次扫描时修剪。 */
666
- private readonly cache;
667
- constructor(dir: string);
1632
+ private listPhysicalBranches;
668
1633
  /**
669
- * 原子写:tmp fsync rename → fsync dir(shared/atomic-write 统一原语,
670
- * U6b 迁移——原逐行实现与 writeAtomicFile 逐环等值)。真异步(fs.promises,
671
- * 不阻塞 event loop)。
672
- *
673
- * 失败时原语尽力清理残留 tmp(debug 记录,不掩盖原错误)并原样上抛——
674
- * 调用方(finalizeRecord)决定降级策略。
1634
+ * 收集 enc 段的活 pid:<agentDir>/subagents/<enc>/sessions/*.alive
1635
+ * readAliveMarker 解析成功且 isProcessAlive 的 pid(去重)。
1636
+ * 崩溃残留的 .alive(pid 已死)天然过滤掉——这正是「死活判据」的物理面来源。
675
1637
  */
676
- writeManifest(record: ManifestRecord): Promise<void>;
1638
+ private collectAlivePids;
677
1639
  /**
678
- * id manifest。文件不存在/JSON 损坏/schema 不合法均返回 null。
679
- * 调用方需处理 null。
1640
+ * 清理物理残留(D5b 方向二的死体处置):worktree remove prune branch -D
1641
+ * 目录 rm 兜底,四步各自 best-effort(幂等,失败仅日志)。
1642
+ * prune 必要性:checkout 目录已不存在的 worktree,remove 会失败且 branch -D
1643
+ * 被「used by worktree」拒绝——prune 清掉缺失目录的元数据后分支才可删。
680
1644
  */
681
- readManifest(id: string): Promise<ManifestRecord | null>;
1645
+ private cleanupPhysical;
682
1646
  /**
683
- * 同步读取所有 manifest 记录(best-effort,损坏/非法文件跳过)。
684
- * 供 RecordStore.collectRecords 投影 orphan 记录使用——替代对私有 dir 的反射访问。
685
- * 仅返回通过 isValidManifest 校验的记录。
686
- *
687
- * [perf] per-file 缓存 + stat 戳校验:collectRecords 每次渲染都调本方法,旧实现每次
688
- * 全量 readFileSync + JSON.parse 千级 manifest(实测 ~300ms/次)。命中缓存的文件零读取。
1647
+ * 判孤儿:pid 死活为主判据。pid=0 走 SPAWN_GRACE 宽限(create→spawn 窗口)。
689
1648
  */
690
- listAllSync(): readonly ManifestRecord[];
1649
+ private isOrphan;
1650
+ /** 清理单个孤儿条目:worktree remove + branch -D + 注册表移除,三步各自 best-effort。 */
1651
+ private cleanupOrphan;
691
1652
  /**
692
- * 启动时恢复 tmp 文件。
693
- * 3 分支逻辑:
694
- * 1. manifest 已存在 tmp(陈旧)
695
- * 2. tmp 合法 + manifest 缺失 → rename tmp 为 manifest
696
- * 3. tmp 非法 + manifest 缺失 → 删 tmp
1653
+ * git 命令异步执行器。与 gitRun 同一超时/错误包装约定(message 格式逐字一致),
1654
+ * 差异仅在错误属性形态(GitRunError 挂 exitCode/stderr/timedOut)。
1655
+ * 写类命令经 per-repo mutex 串行(不依赖 git 锁实现细节 + 并发限流 + 行为确定性)。
697
1656
  *
698
- * [T5④ / PS-13] per-file 容错:单个 tmp 文件操作失败(ENOENT——并发回收/外部清理
699
- * 抢先、EACCES 等)只 warn + 跳过该文件,不再中断整轮——旧实现单文件 ENOENT 即抛,
700
- * 剩余 tmp 本轮不再处理,自愈但不可见(残留顺延下次启动)。跳过数经 warn 汇总留痕,
701
- * 调用方返回值形态不变(跳过者不计数)。
1657
+ * stdout 保真返回(不 trim):collectPatch diff 输出原样落盘为 patch 文件,
1658
+ * 裁掉尾换行会产出 `git apply` 拒绝的 corrupt patch(2026-08-16 门 4 实测)。
1659
+ * 需要干净文本的消费点(baseCommit / 脏树 status 拼接)自行 trim。
702
1660
  */
703
- recoverTmpFiles(): Promise<{
704
- deleted: number;
705
- recovered: number;
706
- }>;
1661
+ private gitRunAsync;
707
1662
  }
708
1663
 
1664
+ /**
1665
+ * [H1 U2 / D7] 轮终处置的 outcome 入参(判别联合)。
1666
+ *
1667
+ * 命名与协议 AgentOutcome 同名不同物(设计 D7 v6:kind 判别联合消歧)——
1668
+ * kind 判别轮终分流:成功轮正文来源 = Continuation 成功分支的 content(协议
1669
+ * AgentOutcome.content 映射产物);失败轮 reason = 失败原因原文。
1670
+ */
1671
+ type RoundSettlementOutcome = {
1672
+ kind: "success";
1673
+ content: string;
1674
+ } | {
1675
+ kind: "failed";
1676
+ reason: string;
1677
+ };
1678
+
709
1679
  /** store 变更监听器(返回取消订阅函数)。 */
710
1680
  type ChangeListener = () => void;
711
1681
  /** status 过滤模式(collectRecords 的核心能力参数)。 */
@@ -760,11 +1730,31 @@ declare class RecordStore {
760
1730
  /** [perf L-1] loadIndex 高版本标志的进程级持久态:true 时本进程所有后续扫描均不
761
1731
  * 落盘(防 v1/v2 last-writer-wins 覆盖振荡),直至下次 loadIndex 重新评估。 */
762
1732
  private indexHigherVersion;
1733
+ /** [U4c / G1] manifest 惰性重建的每 id 尝试守卫(mergedRecords 高频路径防磁盘写
1734
+ * 放大:每 id 每进程至多一次;dispose/revive 随其余缓存态一并重置)。 */
1735
+ private manifestRebuildTried;
1736
+ /**
1737
+ * [D8 v7] manifest 同步写目录(与 manifestStore 同一 records 目录,由构造方提供——
1738
+ * manifestStore.dir 私有,本类不经反射访问)。提供时终态原语(markFinalized /
1739
+ * markCancelled / markBatchFinalized)走 writeAtomicFileSync 同步落盘——停机窗
1740
+ * fire-and-forget 竞态构造性消灭(disposeAllRecords 同步链全链同步段内完成);
1741
+ * 已接线(subagent-service.ts 构造点 recordsDir);缺省分支仅纯内存测试形态。
1742
+ */
1743
+ private readonly manifestDir;
1744
+ /**
1745
+ * [§3.1 markRoundIdle 簿记⑧] pending-notifications 轮终注销(发射点②)的注入面。
1746
+ * store 不直接依赖 pending 注册表(「零副作用编排」边界——文件布局收口 ≠ 通知
1747
+ * 注册表依赖);由 SubagentService 构造点注入 emitPendingUnregister 闭包(U5 收口
1748
+ * 接线),缺省 no-op(纯内存测试形态不发射)。
1749
+ */
1750
+ private pendingUnregister;
763
1751
  constructor(sessionsDir: string, manifestStore?: ManifestStore | undefined,
764
1752
  /** Pi 入口(注入 appendEntry 用于上报损坏 manifest)。
765
1753
  * SubagentService 构造时 this.pi 尚未注入(session_start 之前),传 undefined 兜底;
766
1754
  * 后续通过 setPi() 注入(见下)。允许 null = 兼容 PiLike 字段类型。 */
767
- pi?: RecordStorePi);
1755
+ pi?: RecordStorePi,
1756
+ /** [D8 v7] manifest 同步写目录(见 manifestDir 字段注释)。 */
1757
+ manifestDir?: string);
768
1758
  /** session_start 后由 SubagentService.initSession 调,注入真实 Pi handle。
769
1759
  * 设计为独立方法而非要求构造时必传——RecordStore 在 SubagentService 构造时即建
770
1760
  * (与 sessionsDir/manifestStore 一同初始化),但 this.pi 此时尚未注入。
@@ -777,7 +1767,7 @@ declare class RecordStore {
777
1767
  /**
778
1768
  * 归档:record 已被 completeRecord 设置了终态 status。
779
1769
  * 立即从内存移除(终态 record 下次读时从 session.jsonl 重建)。
780
- * cancelled record 由调用方先写 tombstone(cancel 路径),此处只负责移除。
1770
+ * cancelled record 由调用方先写终态 sidecar(cancel 路径),此处只负责移除。
781
1771
  *
782
1772
  * W16 [D4]:终态冻结字段(result/endedAt/closedReason)在 completeRecord 已就绪,
783
1773
  * 此处 append 的快照即完整终态记录(所有终态路径的必经锚点)。
@@ -792,12 +1782,210 @@ declare class RecordStore {
792
1782
  * pi 未注入(session_start 前)时可选链静默降级,不阻断主流程。
793
1783
  */
794
1784
  reportRecordTransition(record: ExecutionRecord): void;
1785
+ /**
1786
+ * 注入 pending-notifications 轮终注销闭包(markRoundIdle 簿记⑧发射点②,见字段
1787
+ * pendingUnregister 注释)。装配点 = SubagentService 构造器(U5 收口接线)。
1788
+ */
1789
+ setPendingUnregister(fn: ((id: string, status: string) => void) | undefined): void;
1790
+ /**
1791
+ * 意图原语:事件追加(过程记录)。turns/eventLog/totalTokens 经 execution-record
1792
+ * 事件归约累积(字段⑧),随后 entry 变迁上报(best-effort——过程面可丢,重建由
1793
+ * 子 session 文件承接)。调用方按事件粒度决定调用频率(高频 delta 逐事件上报会
1794
+ * 放大 entry 写面,编排粒度属调用方职责)。
1795
+ *
1796
+ * @returns false = id 不在内存(未注册/已归档)——事件丢弃并 debug 留痕。
1797
+ */
1798
+ appendEvent(id: string, event: AgentEvent): boolean;
1799
+ /**
1800
+ * 意图原语:轮始重置(字段①②⑤)。status=running + result/resumable 清除——
1801
+ * §5.4 isStreaming 公式要求 result undefined 才显示 streaming,不清则续轮流仍显示
1802
+ * waiting。归口写点:热路径轮始与冷启动 resume 续轮(subagent-service,U3 迁移)。
1803
+ *
1804
+ * @returns false = id 不在内存(debug 留痕,无副作用)。
1805
+ */
1806
+ markRoundStarted(id: string): boolean;
1807
+ /**
1808
+ * 意图原语:轮末收口——**保持 running-resumable**(名称沿用,非置 idle:status 写
1809
+ * idle 会断 SP-5 升级链与 hasRunning 判据)。簿记全集(①-⑨,= doFinalizeRoundToIdle
1810
+ * 现状簿记 + D3a 修订):
1811
+ * ① status 保持 running;② result 按 outcome 写入(成功=content / 失败=前值??
1812
+ * 失败摘要 + lastError);③ round+1;④ closedReason 清除([S10]);⑤ resumable=true
1813
+ * (GUI waiting 判据);⑥ idleSince 刷新(idle-GC 判据);⑦ **`.alive` 保留**
1814
+ * (D3a 跨轮延续——写权声明至 release 两出口[终态原语/idle-GC 归档],轮终
1815
+ * record 仍 resumable 随时续聊 spawn 写同一 sessionFile,删则轮后跨进程防御
1816
+ * 空窗);⑧ pending 注销发射点②(进程已死,从活跃后代差集移除——经
1817
+ * setPendingUnregister 注入,未注入时跳过);⑨ reportRecordTransition(entry
1818
+ * 携带新 round 与本轮 result)。
1819
+ * worktree/通知等副作用编排留调用方。
1820
+ *
1821
+ * @param outcome 轮终结果(kind 判别:success=content / failed=reason)
1822
+ * @returns false = id 不在内存(debug 留痕,无副作用)。
1823
+ * @throws Error record 终态簿记已冻结(endedAt 已设——复活终态的调用即 bug,
1824
+ * fail-fast,对齐 doFinalizeRoundToIdle A3 断言)。
1825
+ */
1826
+ markRoundIdle(id: string, outcome: RoundSettlementOutcome): boolean;
1827
+ /**
1828
+ * 意图原语:正常终态(含 disposeAllRecords 编排性关闭,reason=parent-*,D8 矩阵)。
1829
+ * 只吸收**持久化面**——collectPatch / worktree cleanup / pending 注销① / onFinalized
1830
+ * 钩子留调用方编排(§3.1 副作用边界)。内存终态冻结(completeRecord/tryTransition)
1831
+ * 亦留调用方——状态机操作非文件布局。
1832
+ *
1833
+ * 内部写序(D8 v7):`.state` writeSync **先**(终态权威优先落)→ entry/archive →
1834
+ * manifest writeSync 后 → `.alive` 删除(release 出口①)。
1835
+ *
1836
+ * 失败语义(§3.4):`.state` 重试耗尽仍未落 → 返回 false,**零持久化副作用**(不
1837
+ * archive / 不写 manifest / 不 release 写权声明)——record 留 running 形态(磁盘无
1838
+ * 终态位,下次 boot 孤儿恢复终态化承接);错误已在 state-marker 层 error 级响亮暴露。
1839
+ *
1840
+ * @returns true = 持久化面完成;false = `.state` 未落(record 不应被视作已终态化)。
1841
+ */
1842
+ markFinalized(record: ExecutionRecord, closedReason?: ClosedReason): boolean;
1843
+ /**
1844
+ * 意图原语:取消终态(tombstone)。写序与失败语义同 markFinalized(D8 v7);区别
1845
+ * 仅 `.state` 载荷 = {status:"cancelled", endedAt}(重建判定分支消费精确结束时间)。
1846
+ * 归口写点:cancelBackground 终态写面(record-lifecycle,U2a 迁移)。
1847
+ */
1848
+ markCancelled(record: ExecutionRecord): boolean;
1849
+ /**
1850
+ * 意图原语:sync 批终态(统一写点)。内部写序显式复刻 barrier:manifest 落盘
1851
+ * **完成**先于批通知写账(batchFinalized 落标 entry)——「通知可达 ⇒ 索引就位」
1852
+ * 构造性保证(session-reader 指针行反查依赖;缓存降级下 barrier 不可删,D4②/D5)。
1853
+ * manifestDir 提供时为同步写(写完即返回);缺省降级 allSettled 异步屏障(原
1854
+ * writeSyncBatchManifestBarrier,已随 U3 归口删除)。
1855
+ */
1856
+ markBatchFinalized(records: readonly SubagentRecord[]): Promise<void>;
1857
+ /**
1858
+ * 意图原语:引擎死亡收养(字段⑤⑩——error/result/resumable 三写,record 保持
1859
+ * resumable 交监督器接管,禁 completed 谎报 / closed 直接终局)。归口写点:
1860
+ * adoptResumableAfterEngineDeath(run-orchestration——已随 U2b 修复轮迁移);
1861
+ * 监督器 adoptOnProcessDeath 编排留调用方。
1862
+ *
1863
+ * @returns false = id 不在内存(debug 留痕,无副作用)。
1864
+ */
1865
+ adoptEngineDeath(id: string, opts: {
1866
+ error: string;
1867
+ }): boolean;
1868
+ /**
1869
+ * 意图原语:磁盘终态位翻回活态(透明重生回边整体收编,D3c 规格)。
1870
+ *
1871
+ * acquire-first 顺序(单 try 域原子收敛):写 `.alive` 写权声明(acquire)→ 删
1872
+ * `.state` → 删 `.finalized`/`.cancelled` legacy(读侧兼容回退认领旧名,残留未删
1873
+ * 则重建仍读出终态),随后 resurrectClosed 内存翻回 + register——
1874
+ * 任一步失败**响亮抛错**(禁止 best-effort 吞错续跑:acquire 失败 = 双写风险敞口;
1875
+ * acquire-first 顺序保证失败时磁盘保持 closed 可读形态——极端形态下经 legacy
1876
+ * 文件名回退仍读出终态,见 catch 文案)。reportTransition(entry 上报)留编排层
1877
+ * (纯投递副作用,失败不破坏状态一致性)。
1878
+ *
1879
+ * 两种接管形态统一(wasClosed 判别):closed 候选 = 三件套全量;running 候选接管
1880
+ * (跨重启磁盘重建)= 跳过删终态位(无 `.state` 可删)**仍 acquire marker**
1881
+ * (「接管即声明」——现状此路径不写 marker 的双写窗随归口消灭)。
1882
+ * 中间形态推演(G3 单点论证)见设计 D3c (i)(ii)(iii):三形态无卡死态、无双写窗。
1883
+ *
1884
+ * @param record 调用方重建的可变 record(createRecord 产物)
1885
+ * @param wasClosed 磁盘候选是否为 closed 形态(cold-lookup 的 found.status 判定)
1886
+ * @throws Error acquire 或终态位删除失败(含 sessionFile 缺失——无锚点无法声明写权)
1887
+ */
1888
+ markResurrected(record: ExecutionRecord, wasClosed: boolean): void;
1889
+ /**
1890
+ * 意图原语:idle-GC 归档(30 天 TTL 内存回收,**非终态化**——record 磁盘仍 running
1891
+ * 可接管;归档 ≠ 放弃可重连性,故不写 `.state`「gc」——那会把「可接管」变「不可
1892
+ * 重连硬拒」,D3a 被否分支)。
1893
+ *
1894
+ * 写序(D3a/轮 5):store.archive **先**、`.alive` release **后**——archive 抛错则
1895
+ * 原语整体失败、marker 必未删(持有与声明一致);release 失败 best-effort 留痕
1896
+ * (removeAliveMarker 内部 warn——GC 为旁路维护路径不阻断 interval,泄漏窗 = 至
1897
+ * 宿主退出,已接受)。归档 record 后续被接管时统一 acquireWriteLease 重新声明。
1898
+ *
1899
+ * [U4c / G2] 归档点补写 manifest(投影 running——磁盘确仍 running):record 离开
1900
+ * 内存后,外部 session-reader 的 identity 富字段主路径只剩 manifest(子文件
1901
+ * identity entry 随 30 天 GC 衰减),归档时不落盘则该 record 在 manifest 面长期
1902
+ * 缺席。写失败走 writeTerminalManifest 同款响亮上报(终态写面共用通道)。
1903
+ */
1904
+ markIdleArchived(record: ExecutionRecord): void;
1905
+ /**
1906
+ * [A6 / D3a 时机①] store 内部 acquire 动作——writeAliveMarker 的唯一包装(G1 口径
1907
+ * = store 内部写面;非新意图原语)。spawn 侧 sessionFile 锚点确立(run 应答回填 /
1908
+ * 冷启动 resume 续轮)时由调用方挂钩(U2b),宿主开始往 session 文件写即声明写权。
1909
+ *
1910
+ * @throws Error 写失败原样上抛(acquire 失败 = 双写风险敞口,调用方响亮处理——
1911
+ * 禁止 best-effort 吞错续跑,D3c 失败语义)。
1912
+ */
1913
+ acquireWriteLease(sessionFile: string, recordId: string): void;
1914
+ /** 终态 manifest 投影(markFinalized/markCancelled 共用;对齐 writeManifestBestEffort
1915
+ * 现状投影——status 统一 closed,closedReason 随投影携带)。
1916
+ * [U4c / G2 词汇双写] executionStatus(ExecutionStatus 二态)随终态写面双写——
1917
+ * 旧 status 三态是 session-reader 直读的投影字段(永久保留),新字段是内部权威
1918
+ * 词汇的写面过渡锚(D5 词汇全景①③并存)。 */
1919
+ private static terminalManifestRecord;
1920
+ /** 批成员 manifest 投影(markBatchFinalized 用;原 writeBatchMemberManifest,
1921
+ * 已随 U3 归口删除——status 如实投影[成功成员此刻 running+resumable],后续
1922
+ * upgrade 终态时原子覆盖)。[U4c / G2] executionStatus/closedReason 双写同 terminal 投影。 */
1923
+ private static batchManifestRecord;
1924
+ /**
1925
+ * [U4c / G1+G2] 状态派生 manifest 投影(rebuildIndexes / 反查 miss 惰性通道 /
1926
+ * markIdleArchived 归档点共用)。数据源 = SubagentRecord 投影(identity entry/
1927
+ * binding + `.state` sidecar 矩阵,D1「.state 权威 + entry 尽力」)——词汇双写
1928
+ * 同终态写面。旧 status 三态从 closedReason 派生 cancelled(对齐 markCancelled
1929
+ * 的 `.state` cancelled 分支重建语义:buildRecord 分支 1 closedReason="cancelled")。
1930
+ */
1931
+ private static derivedManifestRecord;
1932
+ /**
1933
+ * [D8 v7] manifest 落盘统一通道:manifestDir 提供时 writeAtomicFileSync 同步写
1934
+ * (停机竞态构造性消灭;与 ManifestStore.writeManifest 字节形态一致——2 空格缩进
1935
+ * JSON,读侧/外部 session-reader 不感知差异);缺省 fire-and-forget 异步写(仅纯
1936
+ * 内存测试形态)。写失败响亮(error 日志 + 用户可见 entry,对齐 writeManifestBestEffort)。
1937
+ */
1938
+ private writeManifestPersisted;
1939
+ /**
1940
+ * [D8 v7] 终态 manifest 落盘(markFinalized/markCancelled 共用,投影 status 恒
1941
+ * closed)。同步性与失败语义见 writeManifestPersisted。
1942
+ */
1943
+ private writeTerminalManifest;
1944
+ /**
1945
+ * [U4c / G1] 全量重建可丢缓存(boot 通道:boot revive 完成后由宿主调用)。
1946
+ *
1947
+ * manifest 与 sessions-index 均为可丢缓存(D5):权威 = `.state`,重建源 =
1948
+ * `.state` + entry 尽力。本方法:
1949
+ * 1. 触发全量扫描(reconstructAll)——sessions-index 的重建隐式完成于既有机制
1950
+ * (loadIndex 空/损坏 → 探测 → dirty → saveIndex,「损坏静默回退全扫」同款
1951
+ * 先例),不另设第二条索引写路径;
1952
+ * 2. 对扫描重建出的每个 record,manifest 缺失时补写(幂等补缺,不覆写幸存
1953
+ * manifest——幸存者可能比重建源新鲜,覆写 = 用尽力数据降级权威快照)。
1954
+ *
1955
+ * 失败降级(D5 三要素):单 record 重建源不可用(子 session 已 GC/损坏 → 不在
1956
+ * 扫描集)或 manifest 写失败 → debug 留痕跳过该条,整轮不抛(S5「重建照常无错
1957
+ * 误静默吞」)——缓存补缺失败不构成宿主错误。
1958
+ *
1959
+ * @returns 补写的 manifest 数(诊断/日志用)。
1960
+ */
1961
+ rebuildIndexes(): number;
1962
+ /**
1963
+ * [U4c / G1] 单 record 的 manifest 惰性补建(rebuildIndexes 全量轮 + mergedRecords
1964
+ * 惰性通道共用)。manifest 已存在 → 跳过;无 manifest 落点(manifestDir/manifestStore
1965
+ * 均缺省,纯内存测试形态)→ 跳过;每 id 每进程至多尝试一次(manifestRebuildTried
1966
+ * 守卫——boot 全量轮与惰性通道共享,防重复磁盘写)。写失败 debug 留痕不抛
1967
+ * (缓存面,§3.4「缓存损坏」行——无需动作)。
1968
+ *
1969
+ * @returns true = 本次实际补写。
1970
+ */
1971
+ private rebuildManifestIfMissing;
1972
+ /** manifest 写失败的双通道上报(error 日志给开发者 + entry 给用户,对齐
1973
+ * writeManifestBestEffort 现状)。 */
1974
+ private static reportManifestWriteFailure;
1975
+ /**
1976
+ * [H4 收口 / G1] entry 重物化腿的 manifest 投影补写(record-access 可重连终态
1977
+ * 重物化通道的唯一入口——store 外零 manifest 直写)。manifest 是可丢缓存(D5),
1978
+ * 本方法只做缺员补写:失败 warn 留痕不响亮(缓存补缺失败不构成宿主错误,对齐
1979
+ * U4c rebuildIndexes 的降级语义——区别于终态面 writeManifestPersisted 的响亮);
1980
+ * manifestDir 接线时为同步写(停机窗防护与终态面同源)。
1981
+ */
1982
+ rematerializeManifest(manifest: ManifestRecord): void;
795
1983
  /** 按 id 查找。返回可变 record(仅 runtime 内部用)。 */
796
1984
  getMutable(id: string): ExecutionRecord | undefined;
797
1985
  /**
798
1986
  * abort 所有 running record 的 controller(background 子进程 SIGTERM)。
799
1987
  *
800
- * 仅在 SubagentService.dispose(进程退出路径)调用。不做 CAS/tombstone——dispose
1988
+ * 仅在 SubagentService.dispose(进程退出路径)调用。不做 CAS/终态标记——dispose
801
1989
  * 是终局,状态机收尾无意义;目的是让 background 子进程的 AbortSignal 触发 →
802
1990
  * runSpawn 的 signal listener → child.kill("SIGTERM"),防止主进程退出后子进程成孤儿。
803
1991
  *
@@ -818,7 +2006,7 @@ declare class RecordStore {
818
2006
  * ╔══════════════════════════════════════════════════════════════════╗
819
2007
  * ║ 1. 磁盘源:扫 sessionsDir 的 .jsonl,逐个 scanFile([perf] 头部 ║
820
2008
  * ║ identity 轻量重建 + stat 戳缓存命中零读取)。cancelled ║
821
- * ║ tombstone override status。详情字段(eventLog/result/turns) ║
2009
+ * ║ 终态 sidecar override status。详情字段(eventLog/result/turns) ║
822
2010
  * ║ 缺省,由 getFullRecord(id) 懒加载 ║
823
2011
  * ║ 2. 内存源覆盖(同 id 内存优先——running record 更新鲜) ║
824
2012
  * ║ 3. session 过滤:只留 rootSessionId === rootSessionFilter 的 ║
@@ -836,8 +2024,30 @@ declare class RecordStore {
836
2024
  *
837
2025
  * session 隔离:同一 cwd 下多个 Pi session 共享 sessionsDir,靠 rootSessionId
838
2026
  * 区分。内存与磁盘源都按 rootSessionFilter 过滤后再 merge/sort/slice。
2027
+ *
2028
+ * [H2 W1] includeWorkflow(设计 subagent-workflow-record-unification §3.3 D1):
2029
+ * 缺省 false = 过滤 origin==="workflow" 的 record(subagents tool list / TUI
2030
+ * /subagents 等消费面默认不展示 workflow 派发的 record);true = 全量(排查通道)。
2031
+ * 过滤只在查询消费面——治理路径(recoverOrphanRecords / recoverEntryOnlyOrphans /
2032
+ * revive)与本参数无关,永远全量可见(D1 ⑥ 负向规格)。
2033
+ */
2034
+ collectRecords(limit: number, statusFilter?: StatusFilter, rootSessionFilter?: string, includeWorkflow?: boolean): SubagentRecord[];
2035
+ /**
2036
+ * [H2 W1] 按 workflow run id 列 record(parentRunId 查询入口,W2 run 视图进度 /
2037
+ * W3 下钻消费)。查询域 = collectRecords 现状口径(内存 ∪ 磁盘重建 ∪ manifest 补充,
2038
+ * 设计 D2「查询域显式」)。不过滤 origin——按 parentRunId 显式查询即下钻/治理语义
2039
+ * (tool 来源 record parentRunId 恒 undefined,不会被误中)。
2040
+ *
2041
+ * 返回排序与 slice 口径同 collectRecords(STATUS_PRIORITY + startedAt desc + limit)。
839
2042
  */
840
- collectRecords(limit: number, statusFilter?: StatusFilter, rootSessionFilter?: string): SubagentRecord[];
2043
+ collectRecordsByParentRunId(parentRunId: string, limit: number, rootSessionFilter?: string): SubagentRecord[];
2044
+ /**
2045
+ * collectRecords / collectRecordsByParentRunId 共用的三源合并(磁盘重建 ∪ manifest
2046
+ * 补充 ∪ 内存覆盖)。返回 byId Map(内存优先——running record 是活态比磁盘重建新)。
2047
+ * [D1 ⑤] 磁盘重建投影本身不做任何 origin 过滤——record 全量重建,过滤只在上层
2048
+ * 查询消费面按参数生效。
2049
+ */
2050
+ private mergedRecords;
841
2051
  /**
842
2052
  * 重建 SubagentRecord 的自描述 entry 落盘入口(签名适配:reportRecordTransition 收
843
2053
  * ExecutionRecord,重建孤儿的数据源是 SubagentRecord——直接经 toSubagentRecordEntry
@@ -845,14 +2055,15 @@ declare class RecordStore {
845
2055
  */
846
2056
  reportSubagentRecord(record: SubagentRecord): void;
847
2057
  /**
848
- * 孤儿终态恢复:对重建矩阵分支 4 兜底(running 且无 externalInstance)的 record
2058
+ * 孤儿终态恢复:对重建矩阵兜底分支(running 且无异宿主在持声明;编号沿用设计
2059
+ * D3b 文档的「分支 4」表述,分支 3 已随 externalInstance 投影移除)的 record
849
2060
  * 判定真实终态并落 entry,消除「父扩展死后再无人写终态 → 侧栏永久 running」。
850
2061
  *
851
2062
  * 判定(residual-fixes §5.2 三判据 + chat 分流):
852
2063
  * - chatMode = true → 不终态化(跨重启可续聊是产品语义,v4 B-1),落 resumable
853
2064
  * entry 供侧栏 waiting 细分;
854
2065
  * - 子 JSONL 末行完整 JSON.parse → closed(closedReason=gc,与分支 2 重建映射一致;
855
- * done/failed 细分由 error 字段经 deriveClosedDisplay 派生)+ 写 .finalized sidecar
2066
+ * done/failed 细分由 error 字段经 deriveClosedDisplay 派生)+ 写 .state sidecar
856
2067
  * (防重锚——下次重建走分支 2 不再进判定);
857
2068
  * - 末行截断 → closed + error(保守,错误方向安全)+ sidecar;
858
2069
  * - 文件不可读(IO 错误,可能暂时)→ 不判终态,落 resumable entry(防御性路径,
@@ -902,7 +2113,9 @@ declare class RecordStore {
902
2113
  */
903
2114
  private isEntryOrphanCandidate;
904
2115
  /** entry-born 孤儿按无文件判据收敛落 entry:chatMode → resumable(分流语义一致);
905
- * 否则 closed+gc+error(子文件由子进程创建,无文件 = 子进程从未开跑,error 方向安全)。 */
2116
+ * 否则 closed+gc+error(entry-born zcode 引擎 record 的常态形态——无子 session 文件
2117
+ * 非异常,D8 第五行 entry-born 作用域注记;one-shot 直断 closed/gc,续聊对无 transcript
2118
+ * 形态结构性不成立,承接通道仅 start fresh)。 */
906
2119
  private finalizeEntryOnlyOrphan;
907
2120
  /**
908
2121
  * [E1/U5] sync 批崩溃恢复扫描:主 session 文件「每 id 末条 subagent-record entry」
@@ -927,7 +2140,7 @@ declare class RecordStore {
927
2140
  * /resume /fork /new 后复活(dispose 的逆操作)。
928
2141
  *
929
2142
  * [PS-10/T6④] 同步复位 orphanJudged 防重缓存:resumable 形态(IO-error 保守分支 /
930
- * chatMode 分流)没有 .finalized sidecar 锚,重判资格完全由本缓存承载——dispose 时
2143
+ * chatMode 分流)没有 .state sidecar 锚,重判资格完全由本缓存承载——dispose 时
931
2144
  * 有 clear(session 结束),但 revive 此前不复位,导致「同进程内曾经的 IO 失败记录
932
2145
  * 永久停留 resumable」,与本文件 recoverOrphanRecords 注释承诺的「IO 恢复后重开可重判」
933
2146
  * 不符。/new 复活正是「重开」语义:IO 已恢复的记录下次 recoverOrphanRecords 重新判定
@@ -935,13 +2148,17 @@ declare class RecordStore {
935
2148
  */
936
2149
  revive(): void;
937
2150
  /**
938
- * 四分支 sidecar 矩阵重建([perf] light 版)。
2151
+ * 三分支状态矩阵重建(终态 marker / 兜底;[perf] light 版)。
939
2152
  *
940
2153
  * 优先级:
941
- * 1. .cancelled → closed(closedReason=cancelled)
942
- * 2. .finalized → closed(closedReason=sidecar 内容 reason;空/旧格式 → disconnected)
943
- * 3. .alive + pid 存活 + 未超软超时 → running, externalInstance=true
944
- * 4. 兜底(无 marker、pid 死、超时)→ running(v4 B-1 可续聊语义)
2154
+ * 1. 终态 sidecar status=cancelled → closed(closedReason=cancelled)
2155
+ * 2. 终态 sidecar status=finalized → closed(closedReason=内容 reason;空/旧格式 → disconnected)
2156
+ * (旧名 .finalized/.cancelled readStateMarker 归一,判定分支不区分来源)
2157
+ * 3. 兜底(其余一切,含 .alive 在持形态)→ running(v4 B-1 可续聊语义)
2158
+ *
2159
+ * [U4a / D3b (a)] 原分支 3(.alive + pid 活 → running + externalInstance 投影)已
2160
+ * 移除:探活缓存的读面角色由 (a′)(a″) 的 findForeignLiveInstance 现查探针替代,
2161
+ * status 判定与 .alive 解耦(终态判定由 `.state` 权威分支承接)。
945
2162
  *
946
2163
  * [perf]:逐文件 scanFile(stat 戳校验 + 头部 identity 轻量重建)。命中缓存的
947
2164
  * 文件零文件读取;变化的文件只重建自身,其余 N-1 个复用缓存。
@@ -951,18 +2168,31 @@ declare class RecordStore {
951
2168
  */
952
2169
  private reconstructAll;
953
2170
  /**
954
- * 扫描单文件:stat 戳(jsonl + 3 sidecar)校验,全同 复用缓存(零文件读取,
955
- * 含负缓存直接返回 null);否则重建 light。
2171
+ * 扫描单文件:stat 戳(jsonl + 终态 sidecar + record 绑定)校验,全同
2172
+ * 复用缓存(零文件读取,含负缓存直接返回 null);否则重建 light。
956
2173
  * identity 定位两级:头部 64KB(首轮会话)→ 全文 fallback(续聊场景 identity
957
- * append 在尾部);两级都找不到 → 写负缓存(防每轮全文重读)。
958
- * 返回 null:文件消失/读失败/无 identity跳过。
2174
+ * append 在尾部);两级都找不到 → [UF-1] record 绑定 sidecar 回退(宿主侧身份
2175
+ * 载荷重建 light)→ 仍无写负缓存(防每轮全文重读)。
2176
+ * 返回 null:文件消失/读失败/无 identity 且无绑定 → 跳过。
959
2177
  */
960
2178
  private scanFile;
2179
+ /**
2180
+ * [UF-1 / H2 A3] 绑定承载域补投影(仅 header === undefined 的绑定重建路径;
2181
+ * identity entry 命中时绑定不参与)。自 scanFile 原样搬移:赋值条件与顺序等价。
2182
+ */
2183
+ private static projectBindingDerivedFields;
2184
+ /**
2185
+ * [UF-1] record 绑定载荷 → light 身份基底(IdentityHeaderRecon 同形投影)。
2186
+ * 绑定缺失/损坏返回 undefined(调用方落负缓存,不把损坏残留误判成身份)。
2187
+ * forkDepth/model_change/thinking_level_change 等头部途经信息绑定期不存在:
2188
+ * forkDepth 恒 undefined、model/thinkingLevel 取绑定快照值。
2189
+ */
2190
+ private static identityFromBinding;
961
2191
  /**
962
2192
  * [perf L-1] 磁盘索引查询(首扫惰性装载,miss/空索引时 get 恒 undefined = 无索引)。
963
- * 条目戳匹配 jsonl 当前 stat → 零内容读取构造缓存条目。sidecar payload(tombstone/
964
- * alive)是活态数据,沿用探测分支的每轮重读语义;finalized reason 静态数据仅在
965
- * sidecar 存在时读一次(文件小,成本可忽略)。
2193
+ * 条目戳匹配 jsonl 当前 stat → 零内容读取构造缓存条目。sidecar payload(终态 marker)
2194
+ * 是活态数据,沿用探测分支的每轮重读语义;终态 reason 静态数据仅在 sidecar 存在
2195
+ * 时读一次(文件小,成本可忽略)。
966
2196
  *
967
2197
  * 返回 undefined = 索引未命中/戳不匹配(调用方落到原三级探测);null = 负条目命中
968
2198
  * (「确认无 identity」跨实例持久,零探测跳过,与内存负缓存同款形态)。
@@ -991,7 +2221,8 @@ declare class RecordStore {
991
2221
  * [perf] byId 索引直查 light record(单文件 stat 校验,不触发 getFullRecord 的
992
2222
  * 全量重建)。idToFile 未热(进程重启后尚未扫描过)时返回 undefined,调用方
993
2223
  * 自行兜底全目录扫描——用于把「跨重启后每条 message 一次 collectRecords 全扫」
994
- * 降为 O(1) 索引命中。
2224
+ * 降为 O(1) 索引命中。(反查 miss 的索引自愈不在此层——miss 契约被 cold-lookup
2225
+ * 链与 record-binding 测试钉死;manifest 惰性重建挂 mergedRecords,见其注释。)
995
2226
  */
996
2227
  findLightById(id: string): SubagentRecord | undefined;
997
2228
  /**
@@ -1004,13 +2235,9 @@ declare class RecordStore {
1004
2235
  * assistant message 等)→ 返回 light(无详情可补,缓存哨兵防重复全文重读)。
1005
2236
  */
1006
2237
  getFullRecord(id: string): SubagentRecord | undefined;
1007
- /** alive 探活刷新(scanFile 缓存命中与 reconstructAll 快路径共用):
1008
- * 分支 3 的 running + alive 条目每扫重查 pid(结果不落盘,进程死亡无 IO),
1009
- * 保留旧实现「每次 collectRecords 重新 isProcessAlive」的语义。 */
1010
- private static refreshAlive;
1011
2238
  /** identity 基底 + sidecar 状态矩阵 → 缓存条目(索引命中与探测重建两分支的公共装配点)。 */
1012
2239
  private static buildFileCacheEntry;
1013
- /** identity 基底(头部 light 或全量 recon)+ 四分支 sidecar 状态矩阵 SubagentRecord。 */
2240
+ /** identity 基底(头部 light 或全量 recon)+ sidecar 状态矩阵(终态 marker / 兜底两态)→ SubagentRecord。 */
1014
2241
  private static buildRecord;
1015
2242
  /** 排序比较器:status priority(running<failed<cancelled<done)+ startedAt desc。 */
1016
2243
  private static compareRecords;
@@ -1018,216 +2245,182 @@ declare class RecordStore {
1018
2245
  private readManifestsSync;
1019
2246
  /** FR-8: ManifestRecord → SubagentRecord(manifest 源投影)。
1020
2247
  * task/slug/model 从 manifest 真实值投影(配合 writeManifest 补字段),缺失兜底空串。
1021
- * status 越界(mapManifestStatus 返回 null)时返回 null,由 collectRecords 跳过。 */
2248
+ * status 越界(mapManifestStatus 返回 null)时返回 null,由 collectRecords 跳过。
2249
+ * [M2 Gate B] closedReason 投影(枚举守卫,同 mapManifestStatus 的越界容错口径):
2250
+ * 旧 manifest 无此字段 / 损坏值 → undefined。缺失曾让 manifest 源快照在
2251
+ * endedMessageGuard 丢失三分流依据(user-close/cancelled 误入 reconnectable 分支)。 */
1022
2252
  private static manifestToSubagent;
1023
2253
  /** ExecutionRecord → SubagentRecord(内存源投影)。 */
1024
2254
  private static recordToSubagent;
1025
2255
  }
1026
2256
 
1027
- /**
1028
- * chat 域轮次交接包的 core 契约基座(设计 §3.8 D2「ChatRoundTicket → HostBridge」)。
1029
- * pi 专有扩展(identity / SessionRunnerContext / SpawnResumeOpts resume)在
1030
- * pi-engine.ChatRoundTicket extends 本接口(W7 随包迁移;过渡期 record/opts/signal/
1031
- * priority/stream 五字段的宿主编排语义在此单一权威)。
1032
- */
1033
- interface HostChatRoundTicket {
1034
- record: ExecutionRecord;
1035
- opts: ExecuteOptions;
1036
- signal: AbortSignal | undefined;
1037
- priority: number;
1038
- stream?: SubagentStream;
1039
- }
1040
2257
  /**
1041
2258
  * HostBridge 的服务实现面(SubagentService 的结构子集,鸭子类型——与原 pi-engine 的
1042
- * PiEngineService 同一形态的协议化泛化:chat 域轮次面泛型参数化,pi 绑定 =
1043
- * HostBridgeServiceFace<ChatRoundTicket>,见 pi-host-binding.ts re-export)。
1044
- * 为什么用结构接口而非 import SubagentService 类型:防 Service 内部演进连锁影响
1045
- * 契约面 + 测试可注入 fake(PiEngineService 的既有先例,语义照搬)。
1046
- */
1047
- interface HostBridgeServiceFace<TTicket extends HostChatRoundTicket = HostChatRoundTicket> {
1048
- executeAndAwait(opts: ExecuteOptions, signal?: AbortSignal, onEvent?: (event: AgentEvent) => void, stream?: SubagentStream): Promise<AgentResult>;
2259
+ * PiEngineService 同一形态的协议化泛化)。为什么用结构接口而非 import SubagentService
2260
+ * 类型:防 Service 内部演进连锁影响契约面 + 测试可注入 fake(PiEngineService 的既有
2261
+ * 先例,语义照搬)。
2262
+ * [H1 U6] chat 域轮次交接可选面(HostChatRoundTicket / takeChatRound / runChatRound /
2263
+ * resumeChatRound)已随 chat 域退役删除——续聊 = Continuation → 新 run + resume,
2264
+ * 引擎经协议 converse,不再回调宿主编排面。
2265
+ */
2266
+ interface HostBridgeServiceFace {
2267
+ executeAndAwait(opts: ExecuteOptions, signal?: AbortSignal, onEvent?: (event: AgentEvent) => void, stream?: SubagentStream): Promise<AgentResult$1>;
1049
2268
  getRecordForAction(id: string): ExecutionRecord;
1050
2269
  closeSubagent(record: ExecutionRecord, force: boolean): Promise<void>;
1051
2270
  cancel(id: string): boolean;
1052
2271
  collectRecords(limit: number, statusFilter?: StatusFilter): SubagentRecord[];
1053
- /** chat 域轮次交接(run 的 chat 分支入口):按 taskId 取走预备包(一次性消费)。 */
1054
- takeChatRound?(taskId: string): TTicket | undefined;
1055
- /** 执行预备的 chat 轮次(编排归 Service:pool 槽 + runSpawn + 终态迁移)。 */
1056
- runChatRound?(ticket: TTicket): Promise<AgentResult$1>;
1057
- /** 冷路径续轮(interact message 分支的编排回调:守卫 + record 迁移 + kick-off)。 */
1058
- resumeChatRound?(record: ExecutionRecord, text: string): void;
1059
2272
  /** record 状态迁移上报(热路径投递后让 runtime 派生缓存失效 / GUI 回流)。 */
1060
2273
  reportRecordTransition?(record: ExecutionRecord): void;
1061
2274
  }
1062
2275
 
1063
- /** 三态读取结果(判别联合,调用方按 status 分派)。 */
1064
- type GlobalConfigReadResult =
1065
- /** 读到明确值:JSON 可解析,字段已经 sanitize。 */
1066
- {
1067
- status: "ok";
1068
- config: SubagentsGlobalConfig;
1069
- }
1070
- /** 明确缺省:文件不存在(ENOENT)。这是用户意图(删配置切回缺省 pi),不是故障。 */
1071
- | {
1072
- status: "absent";
1073
- config: SubagentsGlobalConfig;
1074
- }
1075
- /** 读失败:坏 JSON / 权限等。携带原始错误消息供诊断;调用方保持 lastEngine 不动。 */
1076
- | {
1077
- status: "failed";
1078
- reason: string;
1079
- };
1080
2276
  /**
1081
- * 三态读取全局配置(设计 D5)。
2277
+ * [T4① / PS-2] 轮次完成回注的 notify 门(按 closedReason 白名单放行)。
1082
2278
  *
1083
- * 为什么不用 loadGlobalConfig:后者 ENOENT 与坏 JSON 同归 DEFAULT_CONFIG(静默
1084
- * 回落),检测器若消费该形态会把「用户删配置切回缺省」(合法变更,须生效)误判成
1085
- * 「读失败」(须保持 lastEngine),也会把坏 JSON(torn write)误判成合法缺省——
1086
- * 两个方向都会产生错误的引擎切换信号。
2279
+ * cancelled(cancelBackground 自己 notify)与 parent-new/parent-fork(编排性关闭,
2280
+ * 告知由 list 的 closedReason 表达)不放行;其余(undefined = 本路径抢到 CAS 尚未
2281
+ * 终态化的迟到回调、user-close/gc 等真实终态)照旧回注。导出供测试与调用点复用。
2282
+ * [H1 U2] 消费面扩为三处:Continuation 成功分支(route 前)、失败分支(独立载荷
2283
+ * 发出前,B#19 投递门照迁移)、泛化派发主干尾部(one-shot 回注,现状不变)。
1087
2284
  */
1088
- declare function readGlobalConfig(agentDir: string): GlobalConfigReadResult;
2285
+ declare function notifyGateAllowsDelivery(closedReason: ClosedReason | undefined): boolean;
1089
2286
 
1090
- /** Service 构造参数(进程级,跨 session 不变)。 */
1091
- interface ModelConfigServiceInit {
1092
- agentDir: string;
1093
- /** 项目根目录(ctx.cwd,用于推导 workspaceRoot 扫描 project 级资源)。 */
1094
- cwd: string;
2287
+ /** 入队项的 child 引用形状(只取 pid 用于 rejectChildDialogs 匹配)。 */
2288
+ interface DialogChildRef {
2289
+ pid: number;
1095
2290
  }
1096
- /** session_start 注入参数(session 级,每次重建)。 */
1097
- interface ModelServiceSessionInit {
1098
- /** 模型注册表(鉴权 + 发现)。null 立即抛错(fail-fast)。 */
1099
- modelRegistry: ModelRegistryLike | null;
1100
- /** 当前 session ID。 */
1101
- sessionId: string;
1102
- /**
1103
- * 主 agent 当前 model(session_start 时注入,model_select 时刷新)。
1104
- *
1105
- * renderCall 阶段的 ToolRenderContext 不含 model 字段(SDK 限制),无法直接拿到
1106
- * 主 agent model。缓存后 renderCall 的 resolveModel 能命中第三层(ctxModel),
1107
- * 让标题行恢复显示 model——即使未显式传 model 也能展示默认 model。
1108
- *
1109
- * [HISTORICAL] 99f20da1e 引入三层 fallback 后,renderCall 因拿不到 ctxModel
1110
- * 而 resolveModel 拗错→降级不显示 model。此缓存修复该降级。
1111
- */
1112
- ctxModel?: ModelInfo;
2291
+ /** enqueue 的可选项。child 用于 rejectChildDialogs 关联(child close 时批量 reject)。 */
2292
+ interface EnqueueOptions {
2293
+ child?: DialogChildRef;
1113
2294
  }
1114
2295
  /**
1115
- * 配置 + 模型解析 Service。进程级单例。
2296
+ * L2 跨子进程全局 dialog 串行队列(进程单例)。
1116
2297
  *
1117
- * ┌──────────────────────────────────────────────────────┐
1118
- * │ globalConfig(~/.pi/.../config.json,仅 maxConcurrent)│
1119
- * │ agentRegistry(agent .md 发现 + frontmatter) │
1120
- * │ modelRegistry(SDK 注入的可用模型) │
1121
- * │ │
1122
- * │ resolveModel: override agentConfig → 主 agent model │
1123
- * └──────────────────────────────────────────────────────┘
2298
+ * 用法(createUiRequestHandlerForMode 返回的总 handler 内):
2299
+ * ```ts
2300
+ * const dialogQueue = new DialogGlobalQueue();
2301
+ * return async (req: UiRequest) => {
2302
+ * // 调用方负责判断:dialog 入队,fire-and-forget 直接调 realHandler
2303
+ * if (isDialogMethod(req.method)) return dialogQueue.enqueue(req, realHandler);
2304
+ * return realHandler(req);
2305
+ * };
2306
+ * ```
2307
+ *
2308
+ * 语义保证:
2309
+ * - FIFO 串行:前一个 handler settle 后才处理下一个
2310
+ * - SR-4:rejectChildDialogs(child) 把该 child 的 pending 全部 resolve 为 {cancelled:true}
2311
+ * - handler 抛错兜底:catch → {cancelled:true} → 继续下一个(队列不卡死)
2312
+ * - 超时上界(LC-3/T2⑦):每个 dialog 项必有上界——req.timeout 显式传值优先,
2313
+ * 未传挂 DEFAULT_DIALOG_TIMEOUT_MS(30min,裁决值);到点 settle {cancelled:true}
2314
+ * 并 warn(恢复指引见 dialogTimeoutLogMessage),L2 processing 释放、队列继续推进。
2315
+ * 「等用户无限久」改为默认有界是有意的行为变更。
2316
+ * - 调用方约定只对 dialog 类调 enqueue;fire-and-forget 由调用方直接调 handler 不入队
2317
+ *(enqueue 内仍防御性兼容 fire-and-forget,但不保证行为)
2318
+ *
2319
+ * 线程模型:纯 Promise + 微任务驱动,无锁。Node 单线程 event loop 保证队列状态一致。
2320
+ *
2321
+ * 单 session 假设(M-2,与 index.ts lastSessionId 同源):本队列是进程级单例(实例挂在
2322
+ * globalThis[Symbol.for("@zhushanwen/pi-subagents.dialogQueue")],见 getOrCreateDialogQueue)。
2323
+ * rejectAll() 清空所有 pending dialog——无 per-session 隔离。Pi 当前架构保证单进程
2324
+ * 单 session 串行(同进程不会并发多个 session),故 session_shutdown 调 rejectAll() 只会清掉
2325
+ * 当前 session 的 pending。若未来 Pi 支持同进程多 session 并发,session A 退出会误清 session B
2326
+ * 的 pending dialog——届时需改为 per-session 隔离(入队项 QueueItem 带 sessionId,rejectAll
2327
+ * 改 rejectAllForSession(sessionId),session_shutdown 只清当前 session)。
1124
2328
  */
1125
- declare class ModelConfigService {
1126
- private globalConfig;
1127
- private readonly agentRegistry;
1128
- private readonly agentRegistryDir;
1129
- private modelRegistry;
1130
- private _sessionId;
1131
- /** 主 agent 当前 model 缓存(session_start 注入,model_select 刷新)。 */
1132
- private _ctxModel;
1133
- constructor(init: ModelConfigServiceInit);
1134
- /**
1135
- * session_start 注入。封装 3 步固定时序:
1136
- * 1. reloadGlobalConfig(复用时拿最新 config)
1137
- * 2. injectModelRegistry(fail-fast:null 抛错)
1138
- * 3. setSessionId
1139
- */
1140
- initModel(init: ModelServiceSessionInit): void;
2329
+ declare class DialogGlobalQueue {
2330
+ /** 等待处理的队列(FIFO)。正在处理的项从 queue shift 出后由 current 持有。 */
2331
+ private queue;
2332
+ /** 正在处理的项(handler 已调、未 settle)。用于 rejectChildDialogs 取消占位中的 dialog。 */
2333
+ private current;
2334
+ private processing;
1141
2335
  /**
1142
- * 将一次三态读取结果提交到路由缓存(纯赋值幂等)。
2336
+ * 入队一个 UI 请求,返回 Promise<UiResponse>。
1143
2337
  *
1144
- * ok/absent 覆盖缓存、failed 保持缓存不动(坏 JSON 不能把好缓存打回缺省);
1145
- * 返回入参便于链式消费。用途 = 构造性同源:session_start 初始化与 per-turn 引擎
1146
- * 检测各只读一次文件,同一读取结果既刷新路由缓存又充当检测基准,消灭两次独立
1147
- * 读取之间的分叉窗口(两次读值不一致时检测走 unchanged,状态段/路由永停旧值)。
2338
+ * 调用方约定:只对 dialog 类(isDialogMethod===true)调 enqueue。fire-and-forget
2339
+ * 调用方(ui-request-handler-factory.ts)在 enqueue 前判 isDialogMethod 后直接调 handler,
2340
+ * 不经过本队列。enqueue 内仍防御性兼容 fire-and-forget(万一调用方未判):直接调 handler 返回,
2341
+ * 不入队串行,但调用方不应依赖此防御行为。
2342
+ *
2343
+ * dialog 项处理(TC-E4 case 1):进队列 FIFO 串行,等前一个 settle 后才调 handler
2344
+ *(争输入焦点,防并发弹窗)。
2345
+ *
2346
+ * handler 抛错兜底:catch → 回 {cancelled:true}(dialog 路径,队列不卡死)。
2347
+ * SR-4:opts.child 用于 rejectChildDialogs 关联(dialog 项会被批量 reject,含 current)。
2348
+ *
2349
+ * @param req UI 请求(约定只传 dialog 类;fire-and-forget 防御性兼容)
2350
+ * @param handler 真正执行请求的 handler(TUI/GUI 模式分流后的 realHandler)
2351
+ * @param opts 可选 child 引用(用于 rejectChildDialogs 关联)
2352
+ * @returns handler 的响应;dialog 抛错时回 {cancelled:true};child close 时回 {cancelled:true}
1148
2353
  */
1149
- applyGlobalConfig(read: GlobalConfigReadResult): GlobalConfigReadResult;
2354
+ enqueue(req: UiRequest, handler: UiRequestHandler, opts?: EnqueueOptions): Promise<UiResponse>;
1150
2355
  /**
1151
- * 三态重读全局配置并提交缓存(幂等可重入),返回本次读取结果供调用方感知。
2356
+ * settle 一个 item(幂等)。handler 完成 / rejectChildDialogs / rejectAll / 超时 timer
2357
+ * 都通过本方法,settled 标志保证只 settle 一次(防竞争)。
1152
2358
  *
1153
- * initModel 提取(设计 D2):引擎感知检测器 per-turn poll 发现 config 变更时
1154
- * 调用本方法,使「system prompt 现值、路由缓存、变更通知」同 turn 对齐——只改注入
1155
- * 不刷新路由缓存,会出现 prompt 说引擎 B、实际派发跑引擎 A(权威信息源说谎)。
1156
- * 幂等性:只做「读文件 按三态提交缓存」单向赋值,无时序状态,重复调用收敛到
1157
- * 同一结果。三态语义(failed 保持缓存、静默回落 DEFAULT 是旧缺陷——读失败曾把
1158
- * 好缓存打回缺省且调用方无法感知):ok/absent 覆盖、failed 保持并携带原因。
2359
+ * #19 单一推进点:本方法 settle Promise + 清状态后,**唯一**调 processNext 推进队列。
2360
+ * processNext 尾部不再调 processNext(旧代码双重推进,虽靠 processing 标志幂等,但语义混乱)。
2361
+ * 为什么推进必须在 settleItem 而非 processNext 尾部:rejectChildDialogs 取消一个永不 settle
2362
+ * current(handler 等用户输入卡死)时,processNext 的 `await item.handler` 永不 resume,
2363
+ * 尾部不会执行;只有 settleItem 里的 processNext 才能打破死锁,推进下一个。
2364
+ *
2365
+ * [A2-2] 超时 timer 清理也统一收口在这里:任何路径抢先 settle(reject/rejectAll/超时
2366
+ * 自身)都必须撤下 armed timer——handler 永挂时 processNext 内联的 clearTimeout 不可达,
2367
+ * 不在这里清则 timer 到期触发虚假「dialog timed out」warn 且句柄滞留至超时点。
1159
2368
  */
1160
- reloadGlobalConfig(): GlobalConfigReadResult;
2369
+ private settleItem;
1161
2370
  /**
1162
- * 刷新主 agent model 缓存。model_select 事件时调用。
1163
- * renderCall 的 resolveModel 读此缓存以显示标题行 model。
2371
+ * SR-4:把指定 child 的所有 pending dialog resolve 为 {cancelled:true}。
2372
+ *
2373
+ * 触发场景:子进程 close(用户取消 / crash / 超时 kill)时,其 pending dialog 的 handler
2374
+ * 可能永不 settle(等用户输入),导致 Promise 永挂 + 内存泄漏。本方法批量清理。
2375
+ *
2376
+ * 处理范围(TC-E4 case 2):
2377
+ * - 正在处理中(current)的该 child 项:settle {cancelled:true},解阻塞队列推进下一个
2378
+ * (关键:handler 可能永不 settle,必须由这里打破死锁)
2379
+ * - 队列中等待处理的该 child 项:settle {cancelled:true} 并移除
2380
+ *
2381
+ * 不影响其他 child 的 pending dialog(TC-E4 case 2 子测试 2)。
1164
2382
  */
1165
- setCtxModel(model: ModelInfo | undefined): void;
2383
+ rejectChildDialogs(child: DialogChildRef): void;
1166
2384
  /**
1167
- * 解析 agent 的模型(三层:override → agentConfig → 主 agent model)。
2385
+ * 处理队列下一项(FIFO)。
1168
2386
  *
1169
- * @param agentRef agent 引用(.md 绝对路径;查 agentConfig 的 model override)
1170
- * @param override 调用方显式 override(最高优先级)
1171
- * @param ctxModel 主 agent 当前模型(兜底,直接透传)
2387
+ * processing 标志保证串行:handler 运行期间 processing=true,新的 processNext 调用直接返回;
2388
+ * handler settle 后由 settleItem 清 processing=false 并推进下一项(#19 单一推进点)。
2389
+ *
2390
+ * handler 抛错兜底(TC-E4 case 3):catch → settle {cancelled:true} → 继续。
2391
+ * 不能让一个失败卡死队列(processing 永远 true)。
2392
+ *
2393
+ * LC-3/T2⑦ 超时上界:`await item.handler` 原本无上界——host UI promise 挂死或用户
2394
+ * 永不回答时 processing 恒 true(全局 dialog 死锁)。现在每项挂队列级 timer:
2395
+ * req.timeout(请求方显式传值)优先,未传/非法挂 DEFAULT_DIALOG_TIMEOUT_MS。
2396
+ * 到点 settle {cancelled:true}(完整错误消息落父进程日志,含恢复指引与等待时长),
2397
+ * settleItem 的 settled 标志保证与 handler 完成 / rejectChildDialogs 三方竞态下
2398
+ * 恰 settle 一次。timer 回调闭包捕获 item(非读 this.current):迟到触发时
2399
+ * settled 标志已置位,直接 noop,不误伤后继项。
1172
2400
  */
1173
- resolveModel(agentRef: string, override?: {
1174
- model?: string;
1175
- thinkingLevel?: string;
1176
- }, ctxModel?: ModelInfo,
1177
- /** 已解析的 agent 配置(调用方已加载时复用,避免同一 agentRef 二次 loadByPath)。 */
1178
- agentConfig?: AgentConfig): ResolvedModel;
1179
- /** 查询 agent 配置(SubagentService 内部判定 defaultBackground 用)。
1180
- * undefined = 合法缺省语义(未点名 / 默认 general-purpose 形态)。 */
1181
- getAgentConfig(agentRef?: string): AgentConfig | undefined;
2401
+ private processNext;
1182
2402
  /**
1183
- * 查询 agent 配置——显式 ref 失败即 throw(SubagentService.resolveIdentity 用)。
2403
+ * #10:把所有 pending dialog(queue + current)全部 settle 为 {cancelled:true},
2404
+ * 并清空 queue/current/processing 状态。session_shutdown 调用,保证不留永挂 Promise。
1184
2405
  *
1185
- * getAgentConfig 的语义分界(「用户显式点名」vs「默认 general-purpose」):
1186
- * 用户显式点名的 agentRef(工具 agent 参数 / workflow agent({agent}) opts)解析
1187
- * 失败 = 配置错误,必须显式报错——错误文案含 <available_subagents> 恢复指引
1188
- * (对齐 workflow name not found 反馈风格),不允许静默降级为无配置
1189
- * general-purpose 形态(systemPrompt/工具白名单全丢且零反馈)。默认形态
1190
- * (不传 agent)走 getAgentConfig:undefined = 合法缺省,走 override → ctxModel 兑底。
2406
+ * 约定签名:rejectAll(): void(无参,返 void)。Group C 的 index.ts session_shutdown 依赖此契约。
2407
+ *
2408
+ * 幂等:依赖 settleItem settled 标志——重复调用只会对已 settled 项 noop。
2409
+ * 顺序敏感(#19 推进点在 settleItem):必须先清空 queue 数组再 settle current,
2410
+ * 否则 settleItem(current) 同步触发的 processNext 会从旧 queue 抢占下一项作为新 current,
2411
+ * 避开本方法的 cancel 语义。清空后 processNext 看到空队列直接返回,新 current 不会被抢占。
2412
+ *
2413
+ * 单 session 假设(M-2):见类注释。本方法清空所有 pending 不分 session——依赖 Pi 单进程
2414
+ * 单 session 串行保证。session_shutdown handler(index.ts)调用本方法时,进程内只会有当前
2415
+ * session 的 pending dialog。多 session 并发场景的迁移策略(rejectAllForSession)见类注释。
1191
2416
  */
1192
- getRequiredAgentConfig(agentRef: string): AgentConfig;
1193
- /** 全局配置深拷贝(调用方拿到副本,改不影响 Service 内部)。 */
1194
- getGlobalConfig(): SubagentsGlobalConfig;
1195
- /** 内部:session id 缓存(initModel 注入;当前无消费者,保留供未来 session 作用域需求)。 */
1196
- get sessionId(): string | undefined;
1197
- /** agent 配置目录(SubagentService 构造 store/SessionRunnerContext 时读)。 */
1198
- getAgentDir(): string;
1199
- /** modelRegistry(SubagentService 构造 factoryCtx 时读)。已注入保证非 null。 */
1200
- getModelRegistry(): ModelRegistryLike;
1201
- /** 校验 modelRegistry 已注入。 */
1202
- private assertReady;
1203
- }
1204
- /** 获取进程单例。session_start 前为 null。 */
1205
- declare function getModelConfigService(): ModelConfigService | null;
1206
- /** 设置进程单例(session_start 首次创建时)。 */
1207
- declare function setModelConfigService(service: ModelConfigService): void;
1208
-
1209
- /** Pi ExtensionAPI 的最小接口(duck-typed)——原定义于 subagent-service.ts,随通知簇
1210
- * (piAdapter / emitPending* 的依赖)搬移至此并导出(Service 的 session 注入参数仍引用)。
1211
- * subagent-service 直接调 pi.sendMessage 发 background 完成通知(BgNotifier 滑动窗口合并),
1212
- * 不委托 pending-notifications EventBus 中继——后者只管 registry 不参与通知发送。 */
1213
- interface PiLike {
1214
- appendEntry(customType: string, data?: unknown): void;
1215
- events: {
1216
- emit(channel: string, data: unknown): void;
1217
- };
1218
- sendMessage(message: {
1219
- customType: string;
1220
- content: string;
1221
- display: boolean;
1222
- details?: unknown;
1223
- }, options?: {
1224
- triggerTurn?: boolean;
1225
- deliverAs?: "steer" | "followUp" | "nextTurn";
1226
- }): void;
1227
- /** 订阅 pi 事件(D8:notifier 的 settled 边沿订阅用 'agent_settled')。
1228
- * pi 0.84.1 的 on 返回 void 且无 off——退订语义由调用侧 disposed 标志包装兑现。
1229
- * 可选:旧测试 mock pi 可能未实现 on,缺省时 notifier 退化为内核退避路径。 */
1230
- on?(event: "agent_settled", handler: () => void): void;
2417
+ rejectAll(): void;
2418
+ /** 清空队列状态(仅在 rejectAll 之后调用)。pending Promise 必须先由 rejectAll settle。
2419
+ * 不 settle Promise 的纯状态重置——单独调用会导致 Promise 永挂(footgun),故设为 private。
2420
+ * 外部调用方应使用 rejectAll()(它 settle 所有 pending + 重置状态,是原子操作)。 */
2421
+ private resetState;
2422
+ /** 当前队列长度(测试/诊断用)。含等待处理项(不含 current)。 */
2423
+ get size(): number;
1231
2424
  }
1232
2425
 
1233
2426
  /** [D4 查询面聚合] 读模型轴(record 快照读取 + store 订阅)——Service 上的
@@ -1239,8 +2432,13 @@ interface SubagentQueries {
1239
2432
  /** [v8.5 A1/B] 全态查找:任意状态 × 任意归属的 record 快照(message 拒绝文案分流
1240
2433
  * 与 fork-from 源解析共用)。id 在内存与磁盘均不存在返回 undefined。 */
1241
2434
  lookupRecordAnyState(id: string): SubagentRecord | undefined;
1242
- /** 合并内存 + 磁盘 record(/subagents list + tool list 消费,按 rootSessionId 过滤)。 */
1243
- collectRecords(limit: number, statusFilter?: StatusFilter): SubagentRecord[];
2435
+ /** 合并内存 + 磁盘 record(/subagents list + tool list 消费,按 rootSessionId 过滤)。
2436
+ * [H2 W1] includeWorkflow 缺省 false = 过滤 origin==="workflow"(D1 投影过滤①③④);
2437
+ * true = 排查通道全量。 */
2438
+ collectRecords(limit: number, statusFilter?: StatusFilter, includeWorkflow?: boolean): SubagentRecord[];
2439
+ /** [H2 W1] 按 workflow run id 列 record(内存 ∪ 磁盘重建 ∪ manifest 口径,不过滤
2440
+ * origin——W2 run 视图进度 / W3 下钻消费)。 */
2441
+ collectRecordsByParentRunId(parentRunId: string, limit: number): SubagentRecord[];
1244
2442
  /** [perf] 单 record 详情懒加载(全量:eventLog/displayItems/result/turns/tokens)。 */
1245
2443
  getFullRecord(id: string): SubagentRecord | undefined;
1246
2444
  /** 订阅 store 变更(widget/list requestRender)。返回取消订阅。 */
@@ -1256,10 +2454,10 @@ interface SubagentChatActions {
1256
2454
  }): ExecutionRecord;
1257
2455
  /** close action 的统一行为分流(running 子态 × force)。 */
1258
2456
  closeSubagent(record: ExecutionRecord, force: boolean): Promise<void>;
1259
- /** chatMode 统一投递入口(message action,经引擎交互面执行)。 */
1260
- deliverChatMessage(record: ExecutionRecord, text: string, interrupt: boolean): Promise<void>;
2457
+ /** chatMode 统一投递入口(message action → ConversationContinuation.onMessage,
2458
+ * [H1 U6] interrupt 参数随 D2 打断统一语义退役)。 */
2459
+ deliverChatMessage(record: ExecutionRecord, text: string): Promise<void>;
1261
2460
  }
1262
-
1263
2461
  /**
1264
2462
  * Service 构造参数(进程级)。
1265
2463
  *
@@ -1276,7 +2474,51 @@ interface SubagentServiceInit {
1276
2474
  * 签名见 dialog-queue.ts UiRequestHandler:接收 UiRequest,返回 UiResponse。 */
1277
2475
  uiRequestHandler?: UiRequestHandler;
1278
2476
  }
1279
- /** session_start 注入参数(session 级)。 */
2477
+ /** 获取进程单例。session_start 前为 null。 */
2478
+ declare function getSubagentService(): SubagentService | null;
2479
+ /** 设置进程单例(session_start 首次创建时)。 */
2480
+ declare function setSubagentService(service: SubagentService): void;
2481
+ /**
2482
+ * [U10① D6] 第三宿主最小构造入口:仅凭参数注入构造 SubagentService(无全局查找)。
2483
+ *
2484
+ * 构造依赖(modelService / getMainSessionFile / uiRequestHandler)全部经 init
2485
+ * 参数注入;本工厂是 `new SubagentService(init)` 的薄包装,不读也不写
2486
+ * getSubagentService/setSubagentService 的全局槽位——session_start 单例流程
2487
+ * 行为零改动,宿主自持实例时用本工厂。构造内部行为与直接 new 逐字等价。
2488
+ *
2489
+ * @experimental execution 运行时面(设计 docs/design/subagent-core-sink-design.md §3.3 D6):
2490
+ * 一个 minor 周期内允许签名微调,稳定后转常规 semver 承诺。
2491
+ */
2492
+ declare function createSubagentService(init: SubagentServiceInit): SubagentService;
2493
+
2494
+ /** Pi ExtensionAPI 的最小接口(duck-typed)——原定义于 subagent-service.ts,随通知簇
2495
+ * (piAdapter / emitPending* 的依赖)搬移至此并导出(Service 的 session 注入参数仍引用)。
2496
+ * subagent-service 直接调 pi.sendMessage 发 background 完成通知(BgNotifier 滑动窗口合并),
2497
+ * 不委托 pending-notifications EventBus 中继——后者只管 registry 不参与通知发送。 */
2498
+ interface PiLike {
2499
+ appendEntry(customType: string, data?: unknown): void;
2500
+ events: {
2501
+ emit(channel: string, data: unknown): void;
2502
+ };
2503
+ sendMessage(message: {
2504
+ customType: string;
2505
+ content: string;
2506
+ display: boolean;
2507
+ details?: unknown;
2508
+ }, options?: {
2509
+ triggerTurn?: boolean;
2510
+ deliverAs?: "steer" | "followUp" | "nextTurn";
2511
+ }): void;
2512
+ /** 订阅 pi 事件(D8:notifier 的 settled 边沿订阅用 'agent_settled')。
2513
+ * pi 0.84.4 的 on 返回 void 且无 off——退订语义由调用侧 disposed 标志包装兑现。
2514
+ * 可选:旧测试 mock pi 可能未实现 on,缺省时 notifier 退化为内核退避路径。 */
2515
+ on?(event: "agent_settled", handler: () => void): void;
2516
+ }
2517
+
2518
+ /** session_start 注入参数(session 级)。
2519
+ * [R1] 接口本体自壳文件迁入(唯一消费者 = SessionBaselines.initSession);壳经类型
2520
+ * 别名 re-export 保持对外符号面(SubagentServiceSessionInit 仍从 subagent-service.ts
2521
+ * 导出,消费方零改动)。 */
1280
2522
  interface SubagentServiceSessionInit {
1281
2523
  pi: PiLike;
1282
2524
  sessionId: string;
@@ -1297,22 +2539,15 @@ interface SubagentServiceSessionInit {
1297
2539
  * createUiRequestHandlerForMode 返回 undefined 时壳侧传 null);值 = 注入并重置
1298
2540
  * 缺失告警去重。 */
1299
2541
  uiRequestHandler?: UiRequestHandler | null;
1300
- /** L2 跨子进程全局 dialog 串行队列(进程单例)。透传给 session-runner,
1301
- * child close 时调 rejectChildDialogs 清理 pending(SR-4 防全局死锁)。 */
2542
+ /** L2 跨子进程全局 dialog 串行队列(进程单例)。子进程退出时经引擎镜像层
2543
+ * (SpawnedChildrenMirror notifyChildProcessExited)取消该 pid 的挂起请求(SR-4)。 */
1302
2544
  dialogQueue?: DialogGlobalQueue;
1303
2545
  /** [竞态修复] 主 agent 是否空闲查询(ctx.isIdle),透传给 notifier 的 flush isIdle gate。
1304
2546
  * 避免 background 完成通知在 agent_end→finishRun 窗口里走错 sendMessage 分支丢失。
1305
2547
  * 可选:未注入时 notifier flush 不 gate(原行为)。 */
1306
2548
  isIdle?: () => boolean;
1307
2549
  }
1308
- /**
1309
- * [T4① / PS-2] 轮次完成回注的 notify 门(按 closedReason 白名单放行)。
1310
- *
1311
- * cancelled(cancelBackground 自己 notify)与 parent-new/parent-fork(编排性关闭,
1312
- * 告知由 list 的 closedReason 表达)不放行;其余(undefined = 本路径抢到 CAS 尚未
1313
- * 终态化的迟到回调、user-close/gc 等真实终态)照旧回注。导出供测试与未来调用点复用。
1314
- */
1315
- declare function notifyGateAllowsDelivery(closedReason: ClosedReason | undefined): boolean;
2550
+
1316
2551
  /**
1317
2552
  * 执行编排 Service。进程级单例。
1318
2553
  *
@@ -1337,44 +2572,6 @@ declare class SubagentService {
1337
2572
  private readonly cwd;
1338
2573
  private readonly worktreeManager;
1339
2574
  private readonly getMainSessionFile;
1340
- /** UI 请求 handler(进程级,可被 setUiRequestHandler / initSession 覆盖)。 */
1341
- private uiRequestHandler;
1342
- /** L2 dialog 串行队列(进程级)。SR-4:child close 时 session-runner 调 rejectChildDialogs 清理。 */
1343
- private dialogQueue;
1344
- /** UI 请求可观测性(sessionMode + handler 缺失告警去重,提取自本类降低行数)。 */
1345
- private readonly uiObservability;
1346
- private pi;
1347
- /** 当前 Pi session ID(本进程 pi session,事件路由等用;record 过滤不用它)。initSession 时注入。 */
1348
- private sessionId;
1349
- /** 主 session 文件(initSession 按值直传——jiti 多实例下闭包缓存不可靠,见 SessionInit 注释)。 */
1350
- private mainSessionFile;
1351
- /** 所属根 session ID(record 归属过滤用)。根进程 = sessionId(自己是 root);
1352
- * 子进程 = env PI_SUBAGENT_ROOT_SESSION_ID 贯穿的真 ROOT(initSession 读取)。
1353
- * 与 sessionId 正交:sessionId 是本进程 pi session(事件路由等),sessionRootId 是所属根
1354
- * (collectRecords filter 用,与 createRecordForMode 的 rootSessionId 盖章同源——子进程
1355
- * 因此看到整棵 ROOT 树)。设计见 recursive-subagent-visibility.md 决策 3。 */
1356
- private sessionRootId;
1357
- /**
1358
- * [D3-⑤ 嵌套防护合一] 进程内执行嵌套上下文(原 execCtxAls 私有字段下沉公共层
1359
- * common/nesting-guard.ts ExecutionNestingContext——机制注释含 ALS 断裂基线兜底)。
1360
- * 实例 per-Service:基线随宿主进程身份而异(initSession 从 env 建立)。
1361
- */
1362
- private readonly execNesting;
1363
- /** fork 深度基线(同 ALS 断裂问题:forkDepthAls.getStore() 兜底用)。根进程=0。 */
1364
- private forkDepthBaseline;
1365
- /** [MF-3] 所属根进程 cwd(sessions/records 落盘目录编码键)。
1366
- * 根进程=自身 cwd(构造时 init.cwd);子进程=env PI_SUBAGENT_ROOT_CWD 贯穿的真 ROOT cwd。
1367
- * worktree 模式下子进程 this.cwd 是 checkout 路径,若按它编码目录,深层 record 落到
1368
- * enc(worktree) 段、ROOT 扫描不到 → 全树可见性深度 ≥ 2 断裂(与 sessionRootId 同构)。 */
1369
- private rootCwd;
1370
- /** UI streaming sink(ctx.ui.setWidget)。workflow 域经 getStreamSink() 取用。 */
1371
- private streamSink;
1372
- /** [竞态修复] 主 agent isIdle 查询(ctx.isIdle)。notifier flush gate 用。
1373
- * initSession 注入,piAdapter 透传给 NotifierHost。 */
1374
- private isIdleFn;
1375
- getStreamSink(): StreamSink | null;
1376
- private _disposed;
1377
- private _seq;
1378
2575
  /** [D4-①] 通知簇 host 面(notifyComplete/notifyClosed/pending 注册注销 + notifier
1379
2576
  * 实例封装,原私有通知簇四方法与模块函数的搬移落点——notify-host.ts)。
1380
2577
  * deps 惰性求值(pi/session 级状态运行时可变),行为与原 constructor 内
@@ -1382,61 +2579,6 @@ declare class SubagentService {
1382
2579
  * [sync-collect 合并] toNotifyRecord/notify/notifyBatch 随批路由需要由 host 导出
1383
2580
  * (collectCoordinator 闭包与 E9/E1 直发路径消费,见 notify-host.ts 接口注释)。 */
1384
2581
  private readonly notifyHost;
1385
- /** collectCoordinator(subagent-sync-collect U2):sync 批缓冲 + 闭合检测 + flush 分流。 */
1386
- private readonly collectCoordinator;
1387
- /** [E9] dispose 时已转 async 写账的成员 id(revive 后 flushBatch 防御过滤用)。
1388
- * 背景:dispose 后同进程 revive(/resume /fork /new)时协调器内部缓冲仍持有已转换
1389
- * 成员快照(协调器无 drain API,U5 领地不含 collect-coordinator.ts)——若后续新
1390
- * sync 成员触发闭合,陈旧快照会随批重投(新成员集新 hash,账本跨键不拦)→ 双重
1391
- * 通知。flushBatch 闭包按本集过滤,陈旧成员零重投。id 唯一 per spawn,无误伤面。 */
1392
- private readonly e9ConvertedIds;
1393
- /** [v2 D4] E1 等待分支的 settled 有界重扫状态(null = 未注册)。disposed 后保持
1394
- * 非 null——同 session 内不再重复注册(补发完成/达限后 settled 边沿已无事可做,
1395
- * 单注册即单重扫);initSession(revive)置 null 允许新 session 重新注册。
1396
- * pi.on 无 off(见 armSettledRescan):旧 handler 闭包捕获旧 state,未 disposed 时
1397
- * 遇 settled 边沿仍会执行,扫描 this.mainSessionFile 当前值(handler 不绑定注册时
1398
- * 的文件域);dispose 的惰化处置见 dispose()。 */
1399
- private settledRescanState;
1400
- /** [MF#4][MF#2] fork 深度按 async 调用链传递(AsyncLocalStorage),替代共享可变计数器。
1401
- * 主 session=0;fork 进入子 session 期间推进为子深度,供嵌套 fork 经 ALS 读到自身深度作为
1402
- * parentForkDepth。并发 background fork 各自独立调用链,不再互相压低深度值。
1403
- * [MF#2] 旧实现用单实例字段跨执行链共享 → 并发下 A 还原深度后 B 读到被压低值 → 护栏失效。 */
1404
- private readonly forkDepthAls;
1405
- /** [review MF1] record 级在途 resume 守卫。冷路径续轮(resumeChatRound)全部守卫通过后
1406
- * add,runAndFinalize 结束(finally,覆盖轮次完成 / MF-6 失败回退 / abort / 终态化所有
1407
- * 分支)时 delete(幂等:execute() 新建 record 不在集合,no-op)。窗口 = resume 发起
1408
- * (含 pool.acquire 排队)→ 本轮 runAndFinalize 收尾。窗口内同 record 再次到达续轮
1409
- * (冷路径重入 / EPIPE 兜底)直接 throw——防两个 pi 子进程以 --session 同一 JSONL 双写 +
1410
- * 前一个脱离 kill 记账成孤儿(冷路径的 acquireActivateLock 只覆盖续轮同步段,锁释放
1411
- * 在子进程注册(session-runner spawnedChildren.set)之前,锁空洞由此守卫兜住;EPIPE
1412
- * 兜底不持锁,同样被覆盖)。child 注册完成后投递走热路径,不经此守卫。 */
1413
- private readonly resumesInFlight;
1414
- /**
1415
- * [W3 chat 域收口] chat 轮次反向通道路由表(recordId 键):首轮 run chat 注册、
1416
- * interact 续聊轮复用——host/streamDelta(recordId 分支)与 host/roundLifecycle
1417
- * (recordId 键)的分发目标(EngineClient.recordRoutes 经
1418
- * EnginePort.registerChatRoundRoute 注册)。record 终态化路径注销([F-5 修复]
1419
- * 汇聚点 = doFinalizeRecord 的 onFinalized 钩子 + cancelBackground /
1420
- * disposeAllRecords 两处直连终态化路径显式注销)。
1421
- */
1422
- private readonly chatRoundRoutes;
1423
- /** [D4-③] 冷路径复活依赖(原四件 private 方法的搬移落点——cold-resurrect.ts;
1424
- * deps 闭包惰性求值:sessionRootId / execNesting 基线运行时可变)。 */
1425
- private readonly coldResurrectDeps;
1426
- /**
1427
- * [W4] 轮次活性监督器(D2「等待有主」权威层):死亡事件纳管(run failed / 引擎
1428
- * exited → adoptOnProcessDeath)+ boot 分区重认领(initSession)+ 三态判定
1429
- * (record 级视图)+ 该放弃(终态化 failed + 注销 + 终止通知)。装配绑定面
1430
- * (deps/giveUp 编排/sweep 挂点)在 round-supervisor/service-binding.ts——变化轴
1431
- * 独立(通知文案 / 终态化编排 / store 读侧判据只动该文件),本字段只持实例。
1432
- */
1433
- private readonly roundSupervisor;
1434
- /** [D4 查询面聚合] 读模型消费面(壳 interface/ 视图与 tool 查询经此访问;
1435
- * 纯委托——方法本体保留 private 实现不重写,行为逐字节等价)。 */
1436
- readonly queries: SubagentQueries;
1437
- /** [D4 对话 action 面聚合] chat 域 message/close 消费面(壳 subagent-actions 经此访问;
1438
- * 纯委托同上。PiEngineService 适配器不经此——引擎边界走 piEngineServiceAdapter)。 */
1439
- readonly chatActions: SubagentChatActions;
1440
2582
  private readonly manifestStore;
1441
2583
  /**
1442
2584
  * [D6 #7a] records 目录(与 manifestStore 同源同一推导)——屏障失败 warn 带 manifest
@@ -1450,587 +2592,174 @@ declare class SubagentService {
1450
2592
  */
1451
2593
  private readonly sessionsDir;
1452
2594
  constructor(init: SubagentServiceInit);
1453
- /** session_start 注入 pi + revive(modelRegistry/entries ModelConfigService.initModel)。 */
2595
+ /** [D4 查询面聚合] 读模型消费面(壳 interface/ 视图与 tool 查询经此访问;
2596
+ * 纯委托——方法本体保留 private 实现不重写,行为逐字节等价)。 */
2597
+ readonly queries: SubagentQueries;
2598
+ /** [D4 对话 action 面聚合] chat 域 message/close 消费面(壳 subagent-actions 经此访问;
2599
+ * 纯委托同上。PiEngineService 适配器不经此——引擎边界走 piEngineServiceAdapter)。 */
2600
+ readonly chatActions: SubagentChatActions;
2601
+ /** [R1] 域 #2 聚合实例:session 级基线状态(pi / session 身份 / fork ALS / 嵌套 ALS /
2602
+ * UI handler 面)的唯一宿主与唯一写者(r0-inventory 清单① #13-#25 共 13 字段)。
2603
+ * deps 全晚绑定闭包(装配见构造器),壳经下方 getter/方法透传,对外签名零变化。 */
2604
+ private readonly baselines;
2605
+ private get pi();
2606
+ private get sessionId();
2607
+ private get mainSessionFile();
2608
+ private get sessionRootId();
2609
+ private get streamSink();
2610
+ private get isIdleFn();
2611
+ private get execNesting();
2612
+ private get rootCwd();
2613
+ private get uiObservability();
2614
+ /** session_start 注入 pi + revive(modelRegistry/entries 归 ModelConfigService.initModel)。
2615
+ * 本体已迁 SessionBaselines.initSession(fork/exec 嵌套基线建立、复活与跨域编排时序
2616
+ * 逐行等价随迁);壳纯转发,对外签名不变。 */
1454
2617
  initSession(init: SubagentServiceSessionInit): void;
1455
2618
  /**
1456
- * [SPAWN fork depth 跨进程传递] fork 链深度基线:子进程被父 spawn 时,父通过 env
1457
- * PI_SUBAGENT_FORK_DEPTH 传入当前 fork 链深度。子进程 session_start 时读取作为
1458
- * forkDepthAls 基线,使后续嵌套 spawn fork 能从正确深度递增。未设置(顶层主
1459
- * session)→ 基线 0。enterWith 贯穿整个 session 生命周期。
1460
- */
1461
- private initForkDepthBaseline;
1462
- /**
1463
- * [递归可见性] exec 上下文基线:子进程读 env PI_SUBAGENT_SELF_RECORD_ID / DEPTH
1464
- * 建立身份基线后,createRecordForMode 读嵌套上下文自动正确(孙挂到子名下)。
1465
- * enterWith 贯穿整个 session 生命周期(与 forkDepthAls 同构,决策 4)。
1466
- */
1467
- private initExecContextBaseline;
1468
- /**
1469
- * 孤儿终态恢复(residual-fixes):session_start 主动触发一次——父扩展死后再无人写
1470
- * 终态 entry 的 record 在此判定落盘(否则侧栏永久 running)。幂等不 throw,失败不
1471
- * 阻断 session_start。
1472
- *
1473
- * [T5① / PS-8] 只有根进程做扫描者:恢复机制假设「单扫描者」,但子进程 sessionRootId
1474
- * 经 env 与父同值(过滤域 = 整树共享的 sessions/records 目录),env 贯穿让每个子进程
1475
- * 都成了扫描者——递归编排中任一子进程启动时,恰有兄弟记录 marker 缺失或超软超时
1476
- *(hours-long wave 必然命中)→ 活记录被无关进程盖 .finalized sidecar,closed entry
1477
- * 写进别的进程的 session 文件(跨进程互写,无任何锁)。子进程身份判据 = env
1478
- * PI_SUBAGENT_SELF_RECORD_ID(父 spawn 时注入的「子进程自己的 record id」,仅子进程
1479
- * 非空)——与 execNesting 基线同源。根进程恢复语义不变。
2619
+ * [F6] 当前根 session id 的只读访问——引擎接线方(SAR 等)构造 RunContext 注入
2620
+ * `ctx.sessionRootId`(pi 引擎 relay 归属键 SESSION_ID 权威源)。initSession 后有值
2621
+ * (根进程 = session id;嵌套 = env 贯穿的真 ROOT)。[D3+] 壳终态保留面。
1480
2622
  */
2623
+ getSessionRootId(): string | null;
2624
+ /** UI streaming sink 只读访问(workflow 域消费)。[D3+] 壳终态保留面。 */
2625
+ getStreamSink(): StreamSink | null;
2626
+ /** [D4 下沉] 就绪断言本体已迁聚合(late-bound getter 现读 pi/disposed,错误文案不变);
2627
+ * 壳 9 个调用点经此转发。 */
2628
+ private assertReady;
2629
+ /** [R2] 域 #5 聚合实例:sync 批自闭合语义(collectCoordinator 装配 + E9 dispose 转账 +
2630
+ * E1 崩溃恢复 + settled 有界重扫 + collectSync 配置读取)的唯一宿主与唯一写者
2631
+ * (r0-inventory 清单① #26-#28)。deps 全晚绑定闭包(装配见构造器;检查点①
2632
+ * flushBatch 显式依赖注入随聚合落地),壳经下方 getter/方法透传,对外签名零变化。 */
2633
+ private readonly syncCollect;
2634
+ private get collectCoordinator();
2635
+ /** [E1] sync 批崩溃恢复(index.ts session_start 恢复编排处调用)。本体已迁
2636
+ * SyncCollectDomain(扫描/补发/落标/settled 重扫时序逐行等价随迁);壳纯转发,
2637
+ * 对外签名不变。 */
2638
+ recoverSyncCollectBatch(): Promise<void>;
2639
+ /** collectSync.default 当前生效值(startHandler 缺省 collect 解析用;本体与配置读取
2640
+ * 链已迁聚合)。[D3+] 壳终态保留面(subagent-actions-core 消费)。 */
2641
+ getCollectSyncDefault(): "async" | "sync";
2642
+ /** [R3] 读建面聚合实例(域 #3 孤儿恢复 / #8 查询面 / #10 action 网关 / #13 身份解析与
2643
+ * record 创建的唯一宿主)。deps 全晚绑定闭包(装配见构造器),壳经下方同名方法
2644
+ * 透传,对外签名零变化。[D-R3-1] 与 RecordLifecycle 拆分的计划变更见聚合文件头。 */
2645
+ private readonly recordAccess;
2646
+ /** [R3] 终态迁移写面聚合实例(域 #4 回收面 / #11 close 三路 / #17 cancel / #18
2647
+ * finalize 簇的唯一宿主——D5「store 与终态迁移入口的唯一宿主」,H4 落点)。
2648
+ * deps 全晚绑定闭包(装配见构造器;C-5 跨域汇聚经回调、collectCoordinator 经 #5
2649
+ * 显式投影现读),壳经下方同名方法透传,对外签名零变化。 */
2650
+ private readonly recordLifecycle;
2651
+ /** [R3] 孤儿终态恢复编排入口(baselines deps.recoverOrphans 回调消费点——initSession
2652
+ * 复活后编排,时序逐行等价随迁读建面);壳纯转发,签名不变。 */
1481
2653
  private recoverOrphansIfRootProcess;
1482
- /** 孤儿终态恢复委托(RecordStore.recoverOrphanRecords 的唯一调用入口,维持 store
1483
- * private 封装——与 recoverManifestTmpFiles 同模式;[D4] public 面收窄:唯一调用方
1484
- * initSession,转 private)。判定语义见 store 侧注释。
1485
- * mainSessionFile 随调用透传(v2 D3 覆写 merge 数据源:主文件末条 entry 的批域
1486
- * 标记与轮终 result/model;initSession 先赋值后恢复,时序就绪)。
1487
- * 随后跑 entry-born 孤儿恢复(无子文件锚的 register-only record,spawn 窗口期死亡,
1488
- * E2E 实测缺口)——主 session 文件经 getMainSessionFile 注入(构造期可空)。 */
1489
- private recoverOrphanRecords;
1490
- /** 启动恢复:扫描 manifest tmp 残留(崩溃打断的 writeManifest 留下的 *.json.tmp.<pid>),
1491
- * 3 分支判定(manifest已存在删tmp / tmp合法promote / tmp非法删)。幂等,不 throw。
1492
- * ADR-035 启动恢复接线——session_start 每次都调(与 maybeCleanupExpiredSessionFiles 一致)。
1493
- * manifestStore 保持 private 封装,本方法是唯一公开入口。 */
2654
+ /** 启动恢复:manifest tmp 残留清扫(ADR-035;index.ts session_start 每次都调)。
2655
+ * [U4c / D6] tmp 恢复已退役为**静默删除**——manifest 现为可丢可重建缓存(权威
2656
+ * = `.state`,重建 = rebuildIndexes),promote 半写 tmp 的恢复语义失效;本入口
2657
+ * 只清残留(含 0 字节/半写形态)。本体已迁 RecordAccess(行为随 ManifestStore
2658
+ * 退役语义);壳纯转发,对外签名不变。 */
1494
2659
  recoverManifestTmpFiles(): Promise<{
1495
2660
  deleted: number;
1496
2661
  recovered: number;
1497
2662
  }>;
1498
- /** SP-4: 关闭所有活跃 record。
1499
- *
1500
- * 遍历 store 中所有 running record,逐个 CAS 转终态 + completeRecord + archive。
1501
- * 对有 worktreeHandle record 触发 worktreeManager.cleanup(T3: worktree 绑定清理)。
1502
- *
1503
- * [T2⑥ / PS-1] 补齐三回收面(对照 dispose() 的既有形态,消除同文件双标):
1504
- * controller.abort + kill(收敛到 killChildWithEscalation)+ disarmIdleTimer +
1505
- * disarmSettledWatchdog。旧实现只关 record 不中止执行——「record 已关」≠「执行已
1506
- * 处置」:在途子进程继续跑且无任何用户可及的取消通道(cancel 只查内存 running,
1507
- * archive 后恒 false),若挂死唯一上界是默认关闭的 spawn watchdog → 泄漏至宿主退出。
1508
- * abort/kill/timer 三面对已终态/已死 record 均幂等 no-op,dispose() 先行的
1509
- * abortRunningControllers + killAllSpawnedChildren 不受影响(parent-shutdown 路径
1510
- * 双保险)。
1511
- *
1512
- * [v4 A-6] 旧实现的 recentlyCascaded 收集(供已删除的 before_agent_start 注入告知)
1513
- * 与 drainCascaded 已一并移除——被关 record 的告知改由 list 的 closedReason 表达。
1514
- *
1515
- * @param reason 关闭原因(parent-fork / parent-new / parent-shutdown)
1516
- * @returns 被关闭的 record 数量
2663
+ /**
2664
+ * [U4c / G1] 缓存降级重建通道(boot 全量腿):manifest 与 sessions-index 均为
2665
+ * 可丢缓存,本入口在 boot revive 完成后(initSession 的孤儿恢复/重物化已收敛
2666
+ * 磁盘态)全量重建——幂等补缺(幸存 manifest 不覆写),失败静默降级不抛。
2667
+ * 语义细节见 RecordStore.rebuildIndexes(D5 三要素)。壳纯转发。
1517
2668
  */
2669
+ rebuildIndexes(): number;
2670
+ /** SP-4: 关闭所有活跃 record(dispose 编排 parent-shutdown 路径 + D3 对外面)。
2671
+ * 本体已迁 RecordLifecycle(三回收面 + manifest 补写 + 锚点提升时序逐行等价随迁);
2672
+ * 壳纯转发,对外签名不变。 */
1518
2673
  disposeAllRecords(reason: ClosedReason): number;
1519
- /** SP-4: /fork 新 session 时清理旧 record
1520
- * 调用 disposeAllRecords("parent-fork")。由 index.ts 的 session_before_fork handler 触发。 */
2674
+ /** SP-4: /fork 新 session 时清理旧 record(index.ts session_before_fork handler 触发)。
2675
+ * 本体已迁 RecordLifecycle;壳纯转发,对外签名不变。 */
1521
2676
  onParentFork(): number;
1522
- /** SP-4: /new 创建全新 session 时清理旧 record
1523
- * 调用 disposeAllRecords("parent-new")。由 index.ts 的 session_before_switch
1524
- * (reason==="new")handler 触发。 */
2677
+ /** SP-4: /new 创建全新 session 时清理旧 record(index.ts session_before_switch 触发)。
2678
+ * 本体已迁 RecordLifecycle;壳纯转发,对外签名不变。 */
1525
2679
  onParentNew(): number;
1526
- /** SP-4: idle record GC(30 TTL,实现抽至 idle-gc.ts)。stop 函数(dispose 调)。 */
1527
- private stopIdleGc;
1528
- /** 启动 idle record GC 定时器(session_start 调用,幂等)。
1529
- * [W4] WorkflowRun store(FileRunStore)同批纳入:running startedAt 30
1530
- * 锚窗的 run 终态化归档(只终态化不补注销,见 idle-gc.ts 头注)。宿主未
1531
- * configureCore loadAll 抛错由 idle-gc 内部吞掉(单轮跳过)。
1532
- * [F-1 修复] stateDir pi 壳 JsonlRunStore 落盘布局同源
1533
- * (resolvePiWorkflowStateDir → <sessionDir>/workflow-state/)——缺省 dataRoot 根
1534
- * 与 pi 生产落盘不相交,WorkflowRun GC 曾恒空转(W4 引入的装配错位)。 */
1535
- startGcTimer(): void;
1536
- /** 停止 idle record GC 定时器(dispose 调用)。 */
1537
- private stopGcTimer;
1538
- /** session 结束清理(清定时器,丢弃 pending 通知)。幂等。
1539
- *
1540
- * [M-7] dispose 顺序假设:pending:unregister emit 依赖 pending-notifications 扩展的
1541
- * listener 仍然存活。若 pending-notifications 先于本扩展执行 session_shutdown(后注册
1542
- * 先执行的语义下会如此),listener 已注销,unregister 事件被静默丢弃。这是可接受的
1543
- * 退化——进程退出后两侧状态本就不保证一致,下次 session_start 的 crash recovery 会修正。 */
1544
- /** [U4 deviation #8 接线] collectSync 预算热读(flush 时读值,与 getCollectSyncDefault
1545
- * 同款访问链 modelService.getGlobalConfig().collectSync)。节缺失/读失败 → undefined
1546
- * → notifyBatch 落 buildBatchLlmContent 设计默认值(4000/24000,E5 不炸启动)。
1547
- * sanitizeCollectSync 保证节存在时两字段必有合法正整数。 */
1548
- private getCollectSyncBudget;
1549
- /** [E9 专用] batchFinalized 落标 + manifest fire-and-forget 写(设计 §3.1.5 E9)。
1550
- * 批通知路径(flush/E1)已改走「manifest 屏障 → 写账 → 纯落标」序列(「通知可达
1551
- * ⇒ 索引就位」的构造性保证,见 flushBatch 闭包 / runSyncCollectRecoveryScan),
1552
- * 不再经本 helper;仅 E9 转换的成员保持原形态——其走 async 单条通知(全文注入、
1553
- * 无指针行消费),manifest 无时序要求,落标后 fire-and-forget 补写(list 后手动
1554
- * 反查的顺带索引)。
1555
- * 路径 = getFullRecord 冷路径重建 → appendBatchFinalizedEntry 纯落标 → fire
1556
- * manifest。getFullRecord 不可达(子 session 文件缺失/已 GC)→ 跳过该成员
1557
- * (详见 flushBatch 闭包注释)。 */
1558
- private markMembersBatchFinalized;
1559
- /** batchFinalized 落标唯一出口(appendEntry 公共末步,纯落标):显式覆写
1560
- * collectMode/batchFinalized → reportSubagentRecord。覆写动机:recordToSubagent
1561
- * 投影已含两字段(U5 修复),但 getFullRecord 冷路径含 sidecar/manifest 重建分支
1562
- * (非 entry 源),显式赋值防非 entry 源重建时丢标记。
1563
- *
1564
- * [v2 D1 断链 1] 落标即「离开批 = 通知已/即将送达 = 指针行即将被消费」——成功
1565
- * 成员走 SP-5 改道 doFinalizeRoundToIdle(不写 manifest),批路径不补写则
1566
- * records/<sa-id>.json 永不产生、session-reader 反查 0 命中。manifest 写点已从
1567
- * 本出口的 fire-and-forget 前移至各调用方:批通知路径(flush/E1)在写账前屏障
1568
- * await 全部落盘(「通知可达 ⇒ 索引就位」的构造性保证);E9 保持落标后
1569
- * fire-and-forget(async 单条通知无指针行消费,无时序要求)。
1570
- * rec 两来源(flush 的 getFullRecord 内存全量 / E1 的 rebuildEntryRecord 重建
1571
- * 快照)必需字段恒齐备(id/agentName←agent/rootSessionId/createdAt←startedAt),
1572
- * task/slug/parentRecordId 等可选 undefined 自然缺省。 */
1573
- private appendBatchFinalizedEntry;
1574
- /** 批成员 manifest(sa- id → sessionFile 反查索引)写的唯一投影点(D2 字段投影 +
1575
- * status 如实投影:成功成员此刻 record 实态 running+resumable → "running",后续
1576
- * message upgrade 走完整 finalize 时 Step 4 原子覆盖为 "closed")。
1577
- * 返回原始 promise 不吞错——失败语义由调用方定:批通知路径经
1578
- * writeSyncBatchManifestBarrier 的 allSettled(debug 不阻断写账);E9 经
1579
- * fire-and-forget catch(debug 不阻断落标)。 */
1580
- private writeBatchMemberManifest;
1581
- /** [时序屏障] 批通知路径(flush/E1)专用:成员 manifest 并行写 + await 全部完成
1582
- * (allSettled)后才允许写账投递——「通知可达 ⇒ 索引就位」的构造性保证。写失败
1583
- * 不阻断(best-effort 语义与 doFinalizeRecord Step 4 一致:反查索引缺失只影响指针行
1584
- * 反查,session-reader 错误文案已指引绝对路径兜底,不构成写账失败);warn 留痕
1585
- * (D6 #7a / SC-1:屏障失败意味着该成员指针行反查索引缺失,debug 级在排障时不可见)。 */
1586
- private writeSyncBatchManifestBarrier;
1587
- /** [E9] dispose 时批未闭合:缓冲中已终态未通知成员逐条转 async 语义写账(放弃攒批)
1588
- * + 落 batchFinalized 标记(E1 重建扫描据此排除,防双重通知),交由既有 shutdown
1589
- * flush / resume 重放兑底;仍在跑的成员走现有退出路径(disposeAllRecords 关闭,
1590
- * 与 async 一致)。写账用 notifier.notify 现有通路(ledger.record + attemptDeliver)。
1591
- * 源序:写账先于落标——写账后崩溃 → E1 重建收该成员,但 async notifyId 与批 hash
1592
- * 跨键不拦的重发属设计披露的 E9 残余窗(at-least-once 良性,PS-17 同族,v1 接受)。 */
1593
- private convertPendingSyncBufferToAsync;
1594
- /**
1595
- * [E1] sync 批崩溃恢复钩子(设计 §3.1.5 E1,index.ts session_start 恢复编排处调用,
1596
- * 须晚于 initSession——孤儿终态恢复先行收敛 running 成员,「全员终态」判定才可达):
1597
- *
1598
- * - 扫描主 session 文件每 id 末条 subagent-record entry(store.scanLastRecordEntries,
1599
- * collectLastRecordEntries 同构 + 投影扩展含 collectMode/batchFinalized + 终态五
1600
- * 字段;禁走 collectRecords light 路径——主 session 落标 entry 对它不可见);
1601
- * - 只收 collectMode=sync 且无 batchFinalized 的成员(排除已通过批 flush 或 E9
1602
- * 转换离场的,防双重通知),按 rootSessionId 过滤当前根;
1603
- * - 全员终态且账本无同成员集批记录 → manifest 屏障(await 落盘,「通知可达 ⇒
1604
- * 索引就位」构造性保证,与 flushBatch 同款)→ notifyBatch 补发(内容 = 末条
1605
- * entry 终态快照;账本 record 同 hash 幂等拒绝 = 已投递/已在账,两种结局都算
1606
- * 「已处理」);
1607
- * - 仍有 running → 本次不动,注册 settled 有界重扫(D4,见 armSettledRescan)——
1608
- * 成员延迟终态(主 agent 冷路径 resume → 正常流落 entry)由 settled 边沿驱动
1609
- * 重扫收敛,不再依赖「下次 session_start」作唯一再驱动(v2 §2.4 断链 4);
1610
- * running 口径与协调器同构
1611
- * (resumable 豁免,v2 D3——覆写不可达的防御分支残余不被误判「仍在跑」);
1612
- * - 补发尝试后统一补 batchFinalized 标记(账本拒绝也算已投递;直接用末条重建快照
1613
- * 落标不经 getFullRecord——子文件缺失/已 GC 时标记仍可落盘,窗口自愈不依赖二次
1614
- * 重启;补标自身崩溃重入幂等收敛,末条 entry last-writer-wins)。
1615
- *
1616
- * async 化(时序屏障修复):返回 Promise 但**内部自捕获不外抛**——宿主 index.ts
1617
- * session_start 以同步 try/catch 调用(其 catch 兑现不到 promise 内的异常),
1618
- * 自捕获维持同款 warn 容错语义,浮动调用零适配、不产生 unhandled rejection。
1619
- *
1620
- * [U8 拆批修复] 与协调器合批排程无交集:E1 只在 session_start 编排处运行(此前
1621
- * dispose 已取消挂起排程),补发直走 notifier.notifyBatch 不经协调器;异常时序
1622
- * 相撞由账本 sync-batch:<hash> 幂等拒绝兜底。
1623
- */
1624
- recoverSyncCollectBatch(): Promise<void>;
1625
- /** [E1/D4] 单次「扫描→判定→可达则补发+落标」,E1 首扫与 settled 重扫共用同一实现
1626
- * (防两处复制粘贴分岔)。三态返回:idle(无 sync 候选——已全部落标/E9 转换/异根,
1627
- * 无事可等)/ waiting(仍有 running 成员,本次不动)/ dispatched(全员终态,已补发
1628
- * +统一落标)。async:补发前有 manifest 屏障 await(见函数头 E1 注释)。 */
1629
- /** 返回 outcome + waitingIds(达限 warn 需滞留成员 id,D6 #7b——仅 waiting 态非空)。 */
1630
- private runSyncCollectRecoveryScan;
1631
- /** [v2 D4] 注册 agent_settled 有界重扫(幂等:settledRescanState 非 null 不叠加注册
1632
- * ——E1 现仅 session_start 单调用点,守卫是防第二入口引入时的注册叠加断言面)。
1633
- * 每次 settled 边沿重跑同一 E1 扫描(runSyncCollectRecoveryScan):dispatched
1634
- * (补发+落标完成,scan 的 warn 已留痕)或 idle(候选已被其他通路落标)→ disposed;
1635
- * 累计 SETTLED_RESCAN_LIMIT 次仍在等 → disposed + warn 留痕(D6 #7b / SC-2:达限
1636
- * 放弃重扫意味着滞留成员的批通知要等下次 session_start 才收敛,含滞留 id 的 warn
1637
- * 是唯一线索,debug 级排障不可见;后续事件零处理,下次 session_start 再收敛)。
1638
- * pi.on 无 off(0.84.4 实装)——disposed 标志包装兑现退订(scheduler extension
1639
- * index.ts subscribeSettled 同款先例)。P-settled 定谳(0.84.4 dist 实装证据):
1640
- * pi.on 为 per-extension 列表分发——loader.js `on()` 把 handler push 进
1641
- * extension.handlers.get(event) 数组(非覆盖),runner.js `emit()` 对全部
1642
- * extension 的全部 handler 逐一 await;故本注册与 ledger host 经
1643
- * piAdapter.onAgentSettled 注册的 settled 分发互不干扰,无需降级并入 host 链。 */
1644
- private armSettledRescan;
1645
- dispose(): void;
1646
- /**
1647
- * [T4④ / PS-5] shutdown flush 被门拦时把未投递 pending 复写落盘(供重启 replay)。
1648
- *
1649
- * 触发条件:flushPendingNotifications 后 ledger 仍有 pending(isIdle 门拦 / sendDelivery
1650
- * 受理失败的残留)且主 agent 非 idle——即本次 shutdown 注定投不出去。落盘动作 =
1651
- * pi.appendEntry 重写 NOTIFY_LEDGER_CUSTOM_TYPE entry(与 ledger.record 同通道同 schema,
1652
- * notifyId 幂等:恢复扫描按后写覆盖 + ack/abandoned 差集去重,重复账面不产生重复投递)。
1653
- * 主 agent idle 时 flush 已投出,无需复写(零开销)。
1654
- */
1655
- private persistUndeliveredNotificationsForReplay;
1656
- /**
1657
- * 预解析 model(renderCall 标题行用,同步)。代理 modelService.resolveModel。
1658
- * 仅解析 override/agentConfig 路径;ctxModel 缺失时拋错,调用方 catch 降级。
1659
- */
1660
- resolveModel(agent: string, override?: {
1661
- model?: string;
1662
- thinkingLevel?: string;
1663
- }, ctxModel?: ModelInfo, agentConfig?: AgentConfig): ResolvedModel;
1664
- /**
1665
- * 统一执行入口。mode 固定 background(sync 已删除)。
1666
- * 内部完成:模型解析 → 执行 → 收尾。
1667
- *
1668
- * @param opts.ctxModel 主 agent 当前模型(模型解析第三层兼底)。undefined 时仅依赖 override/agentConfig。
1669
- */
1670
- execute(opts: ExecuteOptions): Promise<ExecutionHandle>;
1671
- /**
1672
- * 按 id 查内存 running record 的只读快照(G3-002 修复)。
1673
- * 不从 session.jsonl 重建(cancel/list 单点查询只关心内存 running record)。
1674
- * 供 tool 层 cancelHandler 翻译 throw 用(id 不存在 / mode / 终态三种错误)。
1675
- * 不存在返回 undefined。
1676
- */
1677
- private findRecord;
1678
- /** 取消 background record(tryTransition CAS 抢锁防重复副作用)。 */
1679
- cancel(id: string): boolean;
1680
- /**
1681
- * [v8.5 A1/B] 全态查找:任意状态(running/closed)× 任意归属(含异 root session)的
1682
- * record 快照。供 message 拒绝文案分流(A1)与 fork-from 源解析(B)共用。
1683
- *
1684
- * 与 getRecordForAction 的差异:不做归属/直接父校验、不重建可变 record 入内存,
1685
- * 只读快照(light 形态可能缺详情重数据,身份/sidecar 状态字段齐全)。查询顺序与
1686
- * getRecordForAction 冷路径同款(idToFile 索引直查 → collectRecords 全扫兑底),
1687
- * 不限 status——终态(sidecar closed)记录也能查到。
1688
- *
1689
- * 返回 undefined:id 在内存与磁盘均不存在。
1690
- */
1691
- private lookupRecordAnyState;
1692
- /**
1693
- * [V2 决策 3 → W3 协议形态] chatMode 统一投递入口(message action 的 Service 面)
1694
- * ——经协议 interact(message action)发往 pi-subagent-cli 引擎进程。协议知识
1695
- * (stdin prompt 命令 + streamingBehavior 映射 + EPIPE 兜底)在引擎进程内
1696
- * (chat-session.deliverMessage);编排层按结构化结果分流:
1697
- *
1698
- * 受理(引擎侧进程活):prompt + streamingBehavior——pi 权威裁决 busy/idle。
1699
- * 冷路径(engine_session_not_resumable = 进程死/无活进程):冷路径续轮
1700
- * (resumeColdRound → run chat + resume 锚点接续,仅崩溃/timeout kill/跨重启命中)。
1701
- * 其他拒绝(EPIPE 兜底耗尽 / engine_interact_failed):业务拒绝原样 throw
1702
- * (文案自带行动语言——与 inproc 形态逐字节一致)。
1703
- *
1704
- * @param record 目标 record(chatMode,running 或 idle)
1705
- * @param text 消息正文
1706
- * @param interrupt true=steer(抢占)/ false=followUp(排队)
1707
- */
1708
- private deliverChatMessage;
1709
- /**
1710
- * 冷路径续轮(PiEngine.deliverPrompt 的编排回调,D2 下沉后的归属):resume spawn
1711
- * 开启新一轮对话(设计决策 6 idle 分支)。仅进程死(idle timer reap / 崩溃 / 跨重启
1712
- * / EPIPE 兜底)时经引擎到达。
1713
- *
1714
- * record 必须 idle-resumable(轮次完成、进程已回收、record 留内存)。手动把 status
1715
- * 设回 "running"(M2-A 边界:idle→running 是恢复非终态,绕过 tryTransition——
1716
- * tryTransition 要求当前态 running 才 CAS)。
1717
- *
1718
- * resume 参数从 record identity 读(防多轮对话模型漂移,探针 P-10):sessionFile、
1719
- * model、thinkingLevel 均为 record 身份字段(创建时确定、不可变)。maxTurns/schema 等
1720
- * 执行约束第一版不恢复(设计 §5 拆分 1 待验证检查点),agentConfig 用 undefined
1721
- *(pi --session 续写保留上下文,agent 行为由 session 内 messages 决定;M2-B3 messageHandler 可完善)。
1722
- *
1723
- * detached 编排(kickOffChatRound,经 EnginePort 交接):不 await,轮次在 background 跑。
1724
- * chatMode + done 时轮次收尾的 M2-A 分流自动把 record 回退 idle-resumable。并发槽在
1725
- * 轮次执行内重新 acquire(轮次间 idle 已 release);pool.acquire 是排队模型,
1726
- * 池满时排队等待槽位而非 throw(与 execute 一致)。
1727
- *
1728
- * @param record 目标 record(必须 idle-resumable)
1729
- * @param text 新一轮消息正文
1730
- * @throws Error record 非 running / 无 sessionFile / 无 controller / worktree 绑定丢失 / 续轮在途
1731
- */
1732
- private resumeColdRound;
1733
- /**
1734
- * [T2③] 热路径轮 settled watchdog 到期处置(对齐 u-t2a 首轮形态:kill + 该轮失败
1735
- * 终态化 + 失败通知,error 含 'settled watchdog' 标记与恢复指引)。
1736
- *
1737
- * [D9 两段式] 两段(mid-round 无进展 / settled 收尾段上界)共用本处置,fire 信息
1738
- *(段 + 窗长)由原语注入,失败文案按段分叉窗长语义。
1739
- *
1740
- * 与首轮的差异:runSpawn 已返回(无收尾链路承接 settledWatchdogFired 标记),失败
1741
- * 终态化在本回调内完成。chatMode 按 MF-6 语义回退 running-resumable(与首轮 watchdog
1742
- * 经 runAndFinalize 失败分支的最终形态一致——对话可冷路径复活);非 chatMode 终态
1743
- * 销毁。CAS(tryTransition closed+gc)防与 cancel/dispose 双收尾,抢锁失败即跳过。
1744
- *
1745
- * 回调在 timer 触发的同步上下文执行:同步段只做 kill + CAS(不抛),异步收尾
1746
- * fire-and-forget 且 catch 归 bestEffort——错误逃出回调 = uncaughtException 崩宿主。
1747
- */
1748
- private onHotPathSettledWatchdogTimeout;
2680
+ /** 启动 idle record GC 定时器(session_start 调用,幂等)。本体已迁 RecordLifecycle
2681
+ * (stopIdleGc 句柄为聚合唯一写者字段);壳纯转发,对外签名不变。 */
2682
+ startGcTimer(): void;
2683
+ /** 停止 idle record GC 定时器(dispose 编排消费点)。壳纯转发。 */
2684
+ private stopGcTimer;
2685
+ /** id 查内存 running record 的只读快照(G3-002 修复)。本体已迁 RecordAccess
2686
+ * (assertReady 就绪门经 deps 现读);壳纯转发,签名不变。 */
2687
+ private findRecord;
2688
+ /** [v8.5 A1/B] 全态查找(message 拒绝文案分流 + fork-from 源解析共用)。本体已迁
2689
+ * RecordAccess;壳纯转发。 */
2690
+ private lookupRecordAnyState;
2691
+ /** 订阅 store 变更(widget/list requestRender)。本体已迁 RecordAccess;壳纯转发。 */
2692
+ private onChange;
2693
+ /** 合并内存 + 磁盘 record(/subagents list + tool list 消费,按 rootSessionId 过滤)。
2694
+ * 本体已迁 RecordAccess;壳纯转发(默认参数在聚合侧保持同值)。 */
2695
+ private collectRecords;
2696
+ /** [perf] record 详情懒加载。本体已迁 RecordAccess;壳纯转发。 */
2697
+ private getFullRecord;
1749
2698
  /**
1750
- * 按 id 查 record 并做归属校验(message/close action 的统一入口)。
1751
- *
1752
- * 设计决策 3(归属守卫):校验 record.rootSessionId 必须等于当前 session 的根 id
1753
- *(this.sessionRootId)。不匹配 / 不存在统一抛「not found or not owned」——不区分
1754
- * 两种失败,防信息泄露(无法通过错误消息探测其他 session 的 subagent id)。
1755
- *
1756
- * 同进程内 running + idle record 都在内存(getMutable);终态 record 已 archive。
1757
- * 跨重启(SP-2)内存空时,从磁盘 collectRecords 重建 idle record 并 register 进内存。
1758
- * reconstructAll 已将跨重启 record(无 sidecar marker + pid 死)标记为 running(v4 B-1 跨重启可续聊语义,record-store buildRecord 分支 4),
1759
- * collectRecords 返回的 SubagentRecord 可直接转为可变 ExecutionRecord 供续操作。
1760
- *
1761
- * @param id subagent record id
1762
- * @param opts.allowReconnect [v8.5 D] message 专属:冷查额外接受「可重连」的 closed 记录
1763
- * (死因∈ RECONNECTABLE_FINAL_REASONS,A 档真实死因 sidecar 是唯一准入门),经四重守卫后
1764
- * resurrectClosed 回边为 running 并续写原 session 文件。仅 message 开启;close/cancel 维持单向终态语义。
1765
- * @returns 可变 ExecutionRecord(message/close handler 直接操作)
1766
- * @throws Error record 不存在 / 非本 session 所有(含恢复指引)
1767
- * @throws ResurrectDeniedError 命中可重连集但被 worktree/异进程活实例守卫拦截(自带完整行动语言)
2699
+ * 按 id 查 record 并做归属校验(message/close action 的统一入口;含 SP-2 冷查复活
2700
+ * 与 v4 A-5 直接父校验)。本体已迁 RecordAccess;壳纯转发,签名不变。
1768
2701
  */
1769
2702
  private getRecordForAction;
1770
2703
  /**
1771
- * close action 的统一行为分流(running 子态 × force)。
1772
- *
1773
- * running + force:true → cancelBackground(显式 SIGTERM + closed+cancelled 终态)
1774
- * running + force:false + 无在跑轮 → closeChatIdle(立即终态化 done + 回收保活进程 + disarm timer)
1775
- * (isIdle timer armed 或 isResumable 无活进程)
1776
- * running + force:false + 有活进程在跑轮 → 置 closeAfterRound=true(轮完成时终态化:
1777
- * chatMode 消费点在 onRoundSettled,非 chatMode 在 runAndFinalize CAS 分支)
1778
- * 其他终态 → 幂等 no-op(已结束)
1779
- *
1780
- * 与设计决策 5 一致:close = 正式终态(走 finalize),force 只影响 running 时机。
1781
- *
1782
- * @param record 目标 record(getRecordForAction 已校验归属)
1783
- * @param force true=立即终止(running 时 SIGTERM)/ false=优雅关闭(running 时等轮完)
2704
+ * close action 的统一行为分流(running 子态 × force:chatMode abort+清队+立即终态 /
2705
+ * one-shot closeAfterRound 挂起 / force 走 cancelBackground)。本体已迁 RecordLifecycle;
2706
+ * 壳纯转发,签名不变。
1784
2707
  */
1785
2708
  private closeSubagent;
1786
- /**
1787
- * 无在跑轮 record 的手动终态化为 done(close action 的 isIdle/isResumable 分支)。
1788
- *
1789
- * 无在途 AgentResult(轮次完成时 record 未冻结,turns[] 保留运行时状态),
1790
- * 构造合成 done result(对齐 cancelBackground cancelledResult 模式)。
1791
- * doFinalizeRecord 的完整终态化路径(completeRecord + archive + finalized + worktree
1792
- * cleanup + alive marker + manifest)。
1793
- *
1794
- * [M5] 覆盖两路:Path B(无活进程,同旧行为)与 Path A(idle timer armed、进程保活等待
1795
- * 续聊)。Path A 必须先显式回收进程 + disarm timer——否则 record 已终态化但保活进程
1796
- * 继续驻留(终态后无人再杀它:closeSubagent 不再来、idle timer 已 disarm、runSpawn
1797
- * promise 早已 resolve),直到宿主进程退出。
1798
- *
1799
- * 不走 tryTransition(v4 B-1 此态 status=running,但由 doFinalizeRecord 内部的
1800
- * completeRecord 直接覆盖 status,与 cancelBackground 对 record 的处理同构)。
2709
+ /** 取消 background record(tryTransition CAS 抢锁防重复副作用)。本体已迁
2710
+ * RecordLifecycle;壳纯转发,对外签名不变(D3 壳终态保留面)。 */
2711
+ cancel(id: string): boolean;
2712
+ /** [R4] #6-#15 核心编排聚合实例:run 域执行编排(execute/executeAndAwait 入口、
2713
+ * 引擎编排 + adopt 分诊、settleOneShotOutcome 终态收口、Continuation 协作面 +
2714
+ * continuations 队列(C-4/C-5 宿主)、pool/worktree 资源)。deps 全晚绑定闭包
2715
+ * (装配见构造器),壳经下方同名方法透传,对外签名零变化。 */
2716
+ private readonly runOrchestration;
2717
+ /** [R4 / D-R4-1] workflow 族聚合实例(executeWorkflowAgent 派发链 + 类外 helper)。
2718
+ * runOrchestration 组间零互调零 import(跨文件协作经壳 deps 闭包)。 */
2719
+ private readonly workflowDispatch;
2720
+ /**
2721
+ * 预解析 model(renderCall 标题行用,同步)。本体已迁 RunOrchestration;壳纯转发,
2722
+ * 对外签名不变(subagent-actions-core 消费)。
1801
2723
  */
1802
- private closeChatIdle;
2724
+ resolveModel(agent: string, override?: {
2725
+ model?: string;
2726
+ thinkingLevel?: string;
2727
+ }, ctxModel?: ModelInfo, agentConfig?: AgentConfig): ResolvedModel;
1803
2728
  /**
1804
- * [M5] closeAfterRound 消费:chatMode 轮次完成时终态化 recordclosed + user-close)。
1805
- *
1806
- * onRoundSettled(agent_settled 回调)调用——chatMode 轮次完成的统一汇聚点(热路径轮
1807
- * 不经 runAndFinalize CAS 分支,旧消费点对 chatMode 不可达)。合成 result 沿用 record.result
1808
- *(= 本轮增量,设计 D2 路径①):本轮增量已由调用方前置的轮次通知送达,终态通知正文因此
1809
- * 是同一段增量 + 轮次统计 + sessionFile 指针行(notifyClosed),不重发全历史。
1810
- *
1811
- * 时序:同步前缀(disarm + kill + CAS)在 session-runner 的 resolveRun(0) 之前执行完——
1812
- * 冷路径轮的 runAndFinalize 续体因 timer 已 disarm 跳过 early return,但其 tryTransition
1813
- * CAS 对已 closed 的 record 失败 → 跳过二次 finalize(无双收尾);热路径轮无 runAndFinalize
1814
- * 续体,本方法是唯一收尾。冷路径续体 .then 的 notifyComplete 与轮次通知同 key=`id:round`,
1815
- * 60s dedup 吞(不与下方终态通知叠加成第三条——后者 key 是裸 id)。
1816
- */
1817
- private closeAfterRoundSettled;
2729
+ * 统一执行入口。mode 固定 backgroundsync 已删除)。本体已迁 RunOrchestration
2730
+ * (路由 → identity → record 创建 → worktree → 引擎 run 编排逐行等价随迁);
2731
+ * 壳纯转发,对外签名不变(subagent-tool / subagent-actions 消费)。 */
2732
+ execute(opts: ExecuteOptions): Promise<ExecutionHandle>;
1818
2733
  /**
1819
- * workflow 编排层专用:sync-await 接口,内部走 background 管道但返回 Promise<AgentResult>。
1820
- *
1821
- * execute() 的区别(D-A1):
1822
- * 1. 返回 workflow AgentResult(content 字段),非 ExecutionHandle
1823
- * 2. 不经 chat 轮次 kick-off(detached 回注)→ 不注入 followUp 完成通知(BC-11,结果直接返回 workflow)
1824
- * 3. T2 删 sync 时 executeAndAwait 不受牵连(独立方法)
1825
- *
1826
- * 共享:runSpawn + ConcurrencyPool + record + pending emit(D-A4)。
1827
- */
1828
- executeAndAwait(opts: ExecuteOptions, signal?: AbortSignal, onEvent?: (event: AgentEvent) => void, stream?: SubagentStream): Promise<AgentResult>;
1829
- /** 订阅 store 变更(widget/list requestRender)。返回取消订阅。 */
1830
- private onChange;
1831
- /** 合并内存(running) + 磁盘(session.jsonl 重建) record(/subagents list + tool list 消费)。
1832
- * 按 rootSessionId 过滤:根进程=本 session(sessionRootId===sessionId);
1833
- * 子进程=env 贯穿的真 ROOT(sessionRootId≠sessionId)→ 看到整棵 ROOT 树(决策 3)。
1834
- * [perf] 磁盘源为 light(头部 identity + 状态,无 eventLog/result/turns 等重数据)
1835
- * ——列表/补全/hasRunning 够用;详情场景调 getFullRecord(id) 懒加载补齐。 */
1836
- private collectRecords;
2734
+ * workflow 编排层专用 sync-await 接口(D-A1)。本体已迁 RunOrchestration;壳纯转发
2735
+ * (SAR / piEngineServiceAdapter 消费)。 */
2736
+ executeAndAwait(opts: ExecuteOptions, signal?: AbortSignal, onEvent?: (event: AgentEvent) => void, stream?: SubagentStream): Promise<AgentResult$1>;
1837
2737
  /**
1838
- * collectSync.default 当前生效值(subagent-sync-collect U2,偏差#3 接线:
1839
- * startHandler 缺省 collect 解析用)。
1840
- * config 未配/读失败 DEFAULT_COLLECT_SYNC.default 兜底(E5 不炸启动)。
1841
- * 新 session 生效语义与 engine 配置一致(globalConfig 由 ModelConfigService
1842
- * reloadGlobalConfig 刷新)。
1843
- */
1844
- /** collectSync 节单读取点(S9,code-simplify):「读节」一处,「投影成 default 或
1845
- * budget」各自 accessor 负责(getCollectSyncDefault / getCollectSyncBudget)。 */
1846
- private collectSyncSection;
1847
- getCollectSyncDefault(): "async" | "sync";
1848
- /** [perf] 单 record 详情懒加载(全量:eventLog/displayItems/result/turns/tokens)。
1849
- * 内存 running record 直接投影;磁盘 record 全量重建(per-file 缓存,stat 戳校验)。
1850
- * 返回 undefined:id 不存在于内存与磁盘。 */
1851
- private getFullRecord;
1852
- /** 步骤 1:身份解析。agentConfig → resolveModel(三层:override → agentConfig → 主 agent model)。
1853
- *
1854
- * [u-h2] pi 未命中跨引擎候选(D2-4):resolveModel 抛 notFoundError(pi registry
1855
- * 全等裁决未命中)时反查其他已注册引擎清单,唯一命中则追加「该 id 属于引擎 X」
1856
- * 候选段(场景 3);其余裁决失败(孪生歧义/auth)与命中路径原样返回(零回归)。
1857
- * execute() 与 executeAndAwait() 两个派发路径共享本方法,故 chat 与 workflow 域的
1858
- * pi 校验同享场景 3 文案。
1859
- */
1860
- private resolveIdentity;
2738
+ * [H2 W2] workflow 域统一派发入口。本体已迁 WorkflowDispatch([D-R4-1] workflow
2739
+ * 独立聚合);壳纯转发,对外签名不变(SAR / pump 消费)。 */
2740
+ executeWorkflowAgent(opts: AgentCallOpts, parentRunId: string, signal?: AbortSignal, onEvent?: (event: AgentEvent) => void, stream?: SubagentStream): Promise<AgentResult$1>;
1861
2741
  /**
1862
- * [u-h2 D2-1③] pi 引擎的 identity 解析:跳过 pi registry 三层解析(ctxModel 主
1863
- * agent model 不透传——主 agent 的 pi id 对目标引擎大概率无效,缺省语义归引擎)。
1864
- *
1865
- * 逐层语义(设计 D2-1 归趋表):
1866
- * - model = engineModel(调用参数 opts.model > agentConfig.model frontmatter,
1867
- * agent 作者声明不忽略——配错在 validateModel 同步报错,不落引擎缺省静默续跑);
1868
- * - 无显式 model → 校验/留痕走引擎缺省(validateModel(undefined) 的 canonicalRef);
1869
- * - thinkingLevel 直接透传(引擎中立参数,不涉 registry)。
1870
- *
1871
- * 引擎未实现 validateModel 时 modelRef 原样透传(其 prepare 期校验兜底,现状语义)。
1872
- */
1873
- private resolveIdentityForEngine;
1874
- /** 步骤 2:按 mode 生成 id + controller,创建 record 并注册。
1875
- * [L-1] ExecutionMode 类型固定 "background"(sync 已删除),id/controller 分支简化。 */
1876
- private createRecordForMode;
1877
- /** [MF#R4] worktree 前置失败的 early-return handle。
1878
- * record 已被 finalizeFailed 收尾为 failed、detached promise 从未启动。 */
1879
- private buildEarlyFailedHandle;
1880
- /**
1881
- * chat 域统一执行入口(D2 单轨:全引擎——含 pi——经此进入 EnginePort)。路由
1882
- *(routeEngineForHost:三层 + pi 同步短路 + probe/守卫)已由 execute 完成——这里
1883
- * 只剩 unsupported 预检 → identity(pi 链解析 / 非 pi 按目标引擎校验,
1884
- * [u-h2 D2-1/D2-2])→ record 创建+盖章 → worktree → detached 引擎 run。
1885
- * 全部同步拒绝发生在 record 创建前(不产生孤儿 record)。
1886
- */
1887
- private executeViaEngine;
1888
- /**
1889
- * 非 pi 引擎的 detached 执行编排(与 pi 轮次 kick-off 同构的 background 语义):
1890
- * pool 并发槽(maxConcurrent 对非 pi 引擎同样生效)→ journal 接线(D6 第②级:
1891
- * taskId=record.id,初始池 key 占位 'shared',onPoolResolved retarget 到引擎实际
1892
- * 池 key——路径与 paths.ts 同源推导)→ engine.run(signal 接 record controller,
1893
- * kill-chain 两级生效)→ engineHandle 回填(终态迁移落 entry 前)→ 终态迁移 →
1894
- * bg notify(chat 域宿主职责,与 pi 完成通知同语义)。
1895
- */
1896
- private kickOffEngineRun;
1897
- /**
1898
- * kickOffEngineRun 的 acquire 后主体:journal 接线(D6 第②级:taskId=record.id,
1899
- * 初始池 key 占位 'shared',onPoolResolved retarget 到引擎实际池 key)→ engine.run
1900
- * (signal 接 record controller,kill-chain 两级生效)→ engineHandle 回填(终态迁移
1901
- * 落 entry 前)→ 终态迁移。bg notify 归编排侧(与 pi 轮次收尾通知归编排对称)。
1902
- */
1903
- private runEngineTask;
1904
- /**
1905
- * [W4 表 3 行 1] 引擎/子进程死亡、宿主存活的 record 处置:run 终态如实记 failed
1906
- * 证据(record.error),record **保持 resumable**(session 文件在盘,逻辑任务可续
1907
- * ——冷路径 resume 可直接续写),交监督器接管(合并单条通知 + 三态判定)。禁止
1908
- * 两个事故方向:completed 谎报(G3)与 closed 直接终局(resume 可能性丢失,等待
1909
- * 无主——2026-09-08 事故环 2/3 的 core 侧形态)。
1910
- *
1911
- * 判据状态源钉死 record 级:写点只动 record 字段(resumable/result/error),
1912
- * 不清镜像不查引擎——引擎进程被动重建(ensureConnected 退避重填镜像)不翻转
1913
- * 本处置(纳管模型:死亡事件纳管、重建不解管)。
1914
- */
1915
- private adoptResumableAfterEngineDeath;
2742
+ * [D5 双写点 gate 判据] SP-5 升级(one-shot chatMode)的 conversation 位检查。
2743
+ * 本体已迁 RunOrchestration;壳纯转发(subagent-actions-core 消费)。 */
2744
+ canUpgradeToConversation(record: Pick<ExecutionRecord, "engine">): boolean;
2745
+ /**
2746
+ * [V2 决策 3 H1 U2 改写 / U6 定形] chatMode 统一投递入口。本体已迁
2747
+ * RunOrchestration;壳纯转发(chatActions 聚合面消费)。 */
2748
+ private deliverChatMessage;
1916
2749
  /**
1917
- * 分层并发配额:depth 越深可用配额越少(下限 1)。fork 深度护栏在池维度的投影,
1918
- * 公式约定以 concurrency-pool.ts 注释为登记处、此处为唯一代码锚点。
2750
+ * D-017 时序收尾:委托 doFinalizeRecord(finalize-record.ts 独立模块)。本体已迁
2751
+ * RecordLifecycle(FinalizeDeps 装配经 deps 现读 + onFinalized 钩子经 C-5 回调);
2752
+ * 壳纯转发,签名不变。
1919
2753
  */
1920
- private effectiveMaxConcurrentFor;
2754
+ private finalizeRecord;
1921
2755
  /**
1922
- * engine.run resolve 的终态迁移:outcome.error → failed(success=false + error 文案);
1923
- * 否则 done(result=content)。CAS 抢锁(tryTransition)防与 cancelBackground 双收尾。
1924
- *
1925
- * [W4 3 行 1] 进程死亡分诊:outcome.error 存在且 **exitCode === null**(引擎侧
1926
- * 进程被信号终止/崩溃的合成 outcome 形态——RemoteEngine 运行中失败合成分支恒
1927
- * exitCode:null,引擎如实上报的 turn 失败带数值 exitCode;engine-client 注释同源
1928
- * 「exitCode null = 被信号杀死,杀链判据」)且宿主存活且非 conversation 形态 →
1929
- * record 保持 resumable 交监督器接管(如实 failed 证据 + 合并通知 + 三态判定),
1930
- * 不再 closed 终局。返回 true = 走了接管分支(调用方跳过 bg 完成回注)。
1931
- */
1932
- private finalizeEngineOutcome;
1933
- /**
1934
- * workflow 域"干活 + 收尾"(sync await;[W3] 执行叶 = 协议 engine.run——原 inproc
1935
- * runSpawn 链随 inproc pi 引擎目录 删除消亡,journal 接线 / record 终态迁移语义保持)。
1936
- * 编排分段保持「装配(池槽/worktree)→ 执行 → 回收(finally)→ 错误收口 → 终态收口」。
1937
- */
1938
- private runAndFinalize;
1939
- /** engine.run taskSpec 装配单一来源(runAndFinalize 与 kickOffChatRound 共用):
1940
- * executeOptions 协议映射 + model = record 留痕词形(resolved 解析产物,
1941
- * joinEngineModelRef 规范形)覆盖——原 identity.resolved 经 runSpawn --model
1942
- * 兜底的协议等价承载。 */
1943
- private taskSpecWithModel;
1944
- /**
1945
- * one-shot(非 chatMode)终态收口(原 settleOneShotOutcome 分支):成功轮消费
1946
- * closeAfterRound 挂起标志;失败/取消一次性销毁。CAS 抢锁失败(cancel/dispose 抢先
1947
- * 终态化)静默跳过。runAndFinalize(workflow 域)与 kickOffChatRound 非 chatMode
1948
- * 分支共用。
1949
- */
1950
- private settleOneShotOutcome;
1951
- /** AgentOutcome → execution AgentResult 单一映射源(workflow run 域映射;
1952
- * finalizeEngineOutcome 终态 result 复用此处构造——exitCode null = 被信号杀死的
1953
- * 合成终态,error 如实透传)。 */
1954
- private outcomeToAgentResult;
1955
- /** [U04 提取·装配] 池槽获取:pooled(background)record 排队 acquire。成功返回 undefined
1956
- * 继续执行;失败返回终态 result 供调用方 early-return(该路径在 try/finally 之前,
1957
- * 不触发轮次资源回收——与原控制流逐字节一致)。 */
1958
- private acquirePoolOrFinalize;
1959
- /** [U04 提取·装配] 解析 worktree 参数:boolean → WorktreeHandle | undefined(true/undefined 由 run 内部处理)。 */
1960
- private resolveWorktreeHandle;
1961
- /** [U04 提取·回收] 轮次资源回收(finally 语义,幂等):池槽归还(仅 pooled 且 acquire
1962
- * 成功)、streaming widget 清除。 */
1963
- private releaseRoundResources;
1964
- /** [U04 提取·错误收口] MF-6(决策 6 spec §3.1):chatMode(含 resume)spawn/创建失败
1965
- * 不销毁对话——回退 idle(可恢复),让 agent 可重试 message 或 close。与一次性模式
1966
- * (finalizeFailed 终态销毁)区分。返回合成 failed result(swallow,不 re-throw)。 */
1967
- private finalizeChatSpawnFailure;
1968
- /** [U04 提取·终态收口] closeAfterRound 挂起标志消费(原三处分支的公共收尾序列):
1969
- * 清标志 + 终态化 closed。reason:成功轮恒 user-close;失败/取消轮用派生值。 */
1970
- private consumeCloseAfterRound;
1971
- /**
1972
- * pi chat 域轮次的 detached 编排([W3 协议形态]):轮次经协议 run(会话形态
1973
- * chat{recordId, resume?})发往 pi-subagent-cli 引擎进程——
1974
- * - 应答时点 = 首轮 agent_settled(W2 契约:idle 帧先于应答帧;outcome = 本轮
1975
- * 内容非会话终态)→ 应答到达即本轮 settle(round+1 / 增量通知 / closeAfterRound
1976
- * 消费,见 settleChatRoundFromResponse);
1977
- * - 流式 delta / 轮次生命周期经 host/streamDelta + host/roundLifecycle 反向通道
1978
- * 回流:首轮 runId 键(RunContext.stream / onRoundLifecycle)、续聊轮 recordId
1979
- * 键(chatRoundRoutes 注册的 recordId 路由);
1980
- * - recordId 路由在本方法注册(首轮 + 冷续轮替换式重注册),record 终态化路径注销。
1981
- * chat 域不接 event journal(pi 子代理 session JSONL 即原生数据源;与迁移前产物
1982
- * 形态一致——journal 接线仅 workflow 域 SAR 与非 pi 引擎 chat 路径)。
1983
- */
1984
- private kickOffChatRound;
1985
- /**
1986
- * [W3] chat 轮次生命周期帧消费(host/roundLifecycle,runId 键首轮 / recordId 键续聊轮
1987
- * 经本方法收敛)。相位 → 宿主编排语义(W4 协议事件面三入口):
1988
- * - settled:轮收敛——settled-watchdog 中段让位收尾段(noteRoundSettledFromProtocol);
1989
- * - idle:轮收口进 idle 稳态——两段守护一并清(disarmRoundFromProtocol)+ 冷续锚点
1990
- * 回填(sessionFile/engineHandle,idle 帧先于 run 应答帧)+ **idle 定时器挂载**
1991
- * (引擎不实现 idle 定时器——core arm,W2 交接契约);
1992
- * - failed:轮异常终止(engine_round_aborted / engine_round_crashed /
1993
- * engine_round_epipe_exhausted)——record 如实标 failed(禁 completed 谎报),
1994
- * chatMode 按 MF-6 回退可恢复(onChatRoundFailed)。
1995
- */
1996
- private handleChatRoundPhase;
1997
- /**
1998
- * [W3] idle 相位 = idle 定时器锚点(原 session-runner handleAgentSettledForChatMode
1999
- * 的 armIdleTimer 段):挂载降级链(配置值 → DEFAULT 兜底 + warn)语义保持;超时
2000
- * 处置 = 引擎侧进程回收(协议 interact close force)——idle timer SIGTERM 的协议形态。
2001
- */
2002
- private armChatIdleTimer;
2003
- /** [W3] idle 帧锚点回填(冷续锚点 = 引擎侧会话滚动/compaction 后的最新定位)。 */
2004
- private backfillChatAnchor;
2005
- /**
2006
- * [W3] chat 轮 failed 相位分诊:错误如实落 record(失败相位错误码——engine_round_
2007
- * aborted(宿主 cancel/close)/ engine_round_crashed(子进程外部死亡)/
2008
- * engine_round_epipe_exhausted(EPIPE 兜底耗尽)),禁 completed 谎报。chatMode 按
2009
- * MF-6 不销毁对话——回退可恢复(finalizeRoundToIdle + 通知),冷续 run 接续;
2010
- * record 已终态(cancel/close 抢先)= 迟到帧,幂等跳过。
2011
- */
2012
- private onChatRoundFailed;
2013
- /**
2014
- * [W3] 首轮 run 应答到达(= 首轮 agent_settled,W2 契约)的本轮 settle:round+1 /
2015
- * 轮次增量通知 / base 推进 / reportRecordTransition / closeAfterRound 消费——语义
2016
- * 权威自持于此(原 round-settlement.ts createRoundSettler 已删,本闭包为其唯一后继),
2017
- * 唯一偏差 = 轮次文本源:协议形态下 live turns 留在引擎进程内,core 以应答
2018
- * outcome.content(W2「outcome = 本轮内容」)为增量权威,record.turns 派生不可用。
2019
- * idle 定时器挂载不在此处——idle 相位帧先于应答帧到达,armChatIdleTimer 已锚定。
2020
- */
2021
- private settleChatRoundFromResponse;
2022
- /** [W3] chat 域引擎终止意图(协议面):cancel = interact cancel(引擎侧 SIGTERM →
2023
- * settle 等待 → 杀链升级);close = interact close force(立即杀链收割)。fire-and-forget
2024
- * ——失败 best-effort 留证(宿主组级收割兜底 = EngineClient killAll,dispose 路径)。 */
2025
- private terminateChatSession;
2026
- /** chat record 的协议 interact handle(engineHandle 缺省时按 recordId 合成——
2027
- * 引擎侧 interact 定位键 = sessionRef.recordId;sessionFile 有值时一并携带)。 */
2028
- private chatHandleFor;
2029
- /** [W3] pi 引擎 port 解析(chat 域路由与终止面共用):registry cli 形态 port,
2030
- * 未注册 = 不可用 stub(engine_not_found,见 pi-host-binding)。 */
2031
- private resolveChatEnginePort;
2032
- /** [W3] record 终态化路径的 chat 反向通道路由注销(幂等)。 */
2033
- private unregisterChatRoundRoute;
2756
+ * [W4] 轮次活性监督器(D2「等待有主」权威层):死亡事件纳管(run failed / 引擎
2757
+ * exited adoptOnProcessDeath)+ boot 分区重认领(initSession)+ 三态判定
2758
+ * (record 级视图)+ 该放弃(终态化 failed + 注销 + 终止通知)。装配绑定面
2759
+ * (deps/giveUp 编排/sweep 挂点)在 round-supervisor/service-binding.ts——变化轴
2760
+ * 独立(通知文案 / 终态化编排 / store 读侧判据只动该文件),本字段只持实例。
2761
+ */
2762
+ private readonly roundSupervisor;
2034
2763
  /** 引擎服务面适配器(HostBridgeServiceFace 结构视图):闭包持有本实例的编排面。
2035
2764
  * [W3] chat 域轮次交接可选面(takeChatRound/runChatRound/resumeChatRound)随
2036
2765
  * inproc PiEngine 删除移除——引擎经协议 converse,不再回调宿主编排面。 */
@@ -2039,76 +2768,25 @@ declare class SubagentService {
2039
2768
  * 传入的 getService 兼容位消费——W3 后该面无引擎侧消费方,保留使 SAR 调用点
2040
2769
  * 零改动,W8 收口时随签名一并清理)。getter 形态:face 视图(惰性构造)。 */
2041
2770
  get asEngineService(): HostBridgeServiceFace;
2042
- /**
2043
- * 取消 background record。CAS 抢锁(tryTransition)——抢到则 notify + 写 tombstone;
2044
- * 没抢到(detached 已 finalize,record 已终态)返回 false,不触碰任何收尾副作用。
2045
- *
2046
- * stop 手段(abort/kill/disarm)无条件先执行:对已终态 record 幂等无害,且保证
2047
- * cancel 语义 = 进程必死;收尾副作用(completeRecord/tombstone/archive/notify)只归
2048
- * CAS 赢家。[A2-1] 此前 CAS 被误删,cancel 可在 doFinalizeRecord Step 0 await 窗口
2049
- * 命中已终态 record——覆写终态 + tombstone/finalized 双标 + notify 双发 + 谎报 true。
2050
- */
2051
- private cancelBackground;
2052
- /**
2053
- * D-017 时序收尾:委托 doFinalizeRecord(提取到 finalize-record.ts,降低本文件行数)。
2054
- * [Critical #1] cleanup 全部在 manifest 写之前,manifest best-effort 不阻断(详见 finalize-record.ts)。 */
2055
- private finalizeRecord;
2056
- /**
2057
- * 对话模式轮次完成收尾:委托 doFinalizeRoundToIdle(record 进 idle,保留内存 + worktree)。
2058
- * 与 finalizeRecord 对称的委托方法,deps 同源注入。chatMode + done/failed/cancelled 时由 runAndFinalize 调用
2059
- *(MF-6:chatMode 失败/取消也回退 idle 而非终态销毁)。 */
2060
- private finalizeRoundToIdle;
2061
- /** run() 创建期异常的收尾(H1 修复):createAndConfigureSession 失败会抛,本方法合成 failed
2062
- * AgentResult → CAS 抢锁 → finalizeRecord(与正常路径同形)。返回合成 result 供 runAndFinalize
2063
- * 继续返回(不 re-throw,swallow 策略)。
2064
- * [W3 契约变更⑤(run 期失败清理前置副作用)] kickOffEngineRun 前已建的 worktree
2065
- * (executeViaEngine 创建点,record.worktreeHandle 已绑定)经本方法 → finalizeRecord
2066
- * → doFinalizeRecord Step 3b cleanupWorktreeIfBound 清理(manifest 多声明 run 期
2067
- * 失败 / engine.run prepare 期 reject 共用本收尾链);CAS 没抢锁(cancel 抢先终态)
2068
- * 时由 cancelBackground 的 worktree cleanup 覆盖。唯一前置副作用 = worktree(并发
2069
- * 池槽 acquire/release 在 kickOffEngineRun finally 内自回收,journal 是宿主数据不清理)。 */
2070
- private finalizeFailed;
2071
- /** S1: 排队中被 abort 走 cancelled 终态(对齐已运行被 abort 的 cancelBackground)。 */
2072
- private finalizeAborted;
2073
- /**
2074
- * 校验 Service 就绪(pi 已注入 + 未 dispose)。
2075
- *
2076
- * dispose 后调用是异常路径:session_shutdown 已清资源,正常情况下紧接着
2077
- * session_start 会 initSession 复活。若走到这里说明 session_start 没跟上
2078
- * (RPC 边界 / reload 异常等),service 卡在 disposed 状态。
2771
+ private _disposed;
2772
+ /** session 结束清理(清定时器,丢弃 pending 通知)。幂等。
2079
2773
  *
2080
- * 旧实现只抛 "hub disposed"——无信息,调用方和 AI 都看不懂,导致反复盲试。
2081
- * 现在给出原因 + 恢复指引(重启会话或 /new)。真实错误文本会经 renderResult
2082
- * 兜底透传到 AI(见 tool-render.ts extractResultError)。
2083
- */
2084
- private assertReady;
2774
+ * [M-7] dispose 顺序假设:pending:unregister emit 依赖 pending-notifications 扩展的
2775
+ * listener 仍然存活。若 pending-notifications 先于本扩展执行 session_shutdown(后注册
2776
+ * 先执行的语义下会如此),listener 已注销,unregister 事件被静默丢弃。这是可接受的
2777
+ * 退化——进程退出后两侧状态本就不保证一致,下次 session_start 的 crash recovery 会修正。 */
2778
+ dispose(): void;
2085
2779
  /**
2086
- * [T4 / PS-4] idleTimeoutMs 合法域入口校验(>2^31-1 / 非有限值 fail-fast)。
2780
+ * [T4 / PS-5] shutdown flush 被门拦时把未投递 pending 复写落盘(供重启 replay)。
2087
2781
  *
2088
- * 旧链路:非法值穿透到 agent_settled 回调里的 armIdleTimer assertSafeTimerDelay
2089
- * throw 被异步 catch 降级——配置错误被吞成静默语义变更(每轮完成通知被 isIdle 放行门
2090
- * + 进程无回收 timer)。对齐 shared/timer-delay「不静默 clamp」既有裁决:配置错误
2091
- * 显式暴露,错误消息含合法范围(0/负数 = 显式禁用是合法语义,不在本校验域;env 非法值
2092
- * lifecycle-manager warn 回落承接)。execute/executeAndAwait 两入口共用。
2782
+ * 触发条件:flushPendingNotifications ledger 仍有 pending(isIdle 门拦 / sendDelivery
2783
+ * 受理失败的残留)且主 agent idle——即本次 shutdown 注定投不出去。落盘动作 =
2784
+ * pi.appendEntry 重写 NOTIFY_LEDGER_CUSTOM_TYPE entry(与 ledger.record 同通道同 schema,
2785
+ * notifyId 幂等:恢复扫描按后写覆盖 + ack/abandoned 差集去重,重复账面不产生重复投递)。
2786
+ * agent idle flush 已投出,无需复写(零开销)。
2093
2787
  */
2094
- private assertIdleTimeoutMsSafe;
2788
+ private persistUndeliveredNotificationsForReplay;
2095
2789
  }
2096
- /** 获取进程单例。session_start 前为 null。 */
2097
- declare function getSubagentService(): SubagentService | null;
2098
- /** 设置进程单例(session_start 首次创建时)。 */
2099
- declare function setSubagentService(service: SubagentService): void;
2100
- /**
2101
- * [U10① D6] 第三宿主最小构造入口:仅凭参数注入构造 SubagentService(无全局查找)。
2102
- *
2103
- * 构造依赖(modelService / getMainSessionFile / uiRequestHandler)全部经 init
2104
- * 参数注入;本工厂是 `new SubagentService(init)` 的薄包装,不读也不写
2105
- * getSubagentService/setSubagentService 的全局槽位——session_start 单例流程
2106
- * 行为零改动,宿主自持实例时用本工厂。构造内部行为与直接 new 逐字等价。
2107
- *
2108
- * @experimental execution 运行时面(设计 docs/design/subagent-core-sink-design.md §3.3 D6):
2109
- * 一个 minor 周期内允许签名微调,稳定后转常规 semver 承诺。
2110
- */
2111
- declare function createSubagentService(init: SubagentServiceInit): SubagentService;
2112
2790
 
2113
2791
  /**
2114
2792
  * U4 投递计数分桶(设计 §5 U4:分桶口径与 §2.2 三条丢失路径一一对应,回归时定位到
@@ -2239,6 +2917,14 @@ interface SubagentIdentityData {
2239
2917
  worktree?: boolean;
2240
2918
  /** @deprecated 兼容旧文件:旧 identity entry 写的是 parentSessionId,读取时 fallback 到 rootSessionId。 */
2241
2919
  parentSessionId?: string;
2920
+ /**
2921
+ * 来源身份(H2 S3 修复)。旧文件缺失 → undefined(= "tool" 语义,缺省零迁移)。
2922
+ * standalone 形态(identity entry 链)的 workflow record 经本字段在重建面保留
2923
+ * origin 过滤语义;读取侧经 normalizeReconOrigin 守卫归一(非法值 → undefined)。
2924
+ */
2925
+ origin?: RecordOrigin;
2926
+ /** origin="workflow" 时所属 workflow run id。旧文件缺失 → undefined。 */
2927
+ parentRunId?: string;
2242
2928
  }
2243
2929
  /** custom entry 的 customType 标识(session-runner 写 / reconstructor 读,约定常量)。 */
2244
2930
  declare const IDENTITY_CUSTOM_TYPE = "subagent-identity";
@@ -2275,11 +2961,12 @@ declare function getOrCreateChannelRegistry(): UiChannelRegistry;
2275
2961
  /**
2276
2962
  * 可冷路径 resume(running 且无活进程句柄)。
2277
2963
  *
2278
- * idle 中进程已回收的子态(Path B / 跨重启重建)。deliverMessage 冷路径、GC、
2279
- * close action 的「无活进程立即终态化」分支据此判定。
2964
+ * 等待续聊态的现行判据([H1 U6] 每轮 = 新进程、轮末即回收,完成的轮次天然无活
2965
+ * 进程句柄;跨重启重建同理)。deliverMessage 冷路径、GC、close action
2966
+ * 「无活进程立即终态化」分支据此判定。
2280
2967
  *
2281
- * 与 {@link isIdle} 的关系:isResumable=true 蕴含 isIdle 可能为 false(Path B 进程
2282
- * 已退出,timer 未 armed);isIdle=true(Path A)则 isResumable=false(进程保活)。
2968
+ * 与 {@link isIdle} 的关系:isIdle 生产恒 false(见其 JSDoc),本谓词是
2969
+ * 「等待续聊」唯一运行时生效的派生判据。
2283
2970
  *
2284
2971
  * [v4 A-6] 签名泛化为 Pick<"id"|"status">:ExecutionRecord(活态)与 SubagentRecord
2285
2972
  * (list 快照)均结构兼容——recordToListItem 据此为 list 输出派生 resumable 字段。
@@ -2336,145 +3023,6 @@ interface HostUIContext {
2336
3023
  * @returns UiRequestHandler(tui/gui);headless 返回 undefined */
2337
3024
  declare function createUiRequestHandlerForMode(ctx: HostUIContext, registry: UiChannelRegistry, dialogQueue: DialogGlobalQueue): UiRequestHandler | undefined;
2338
3025
 
2339
- declare class WorktreeManager {
2340
- private readonly registry;
2341
- private readonly agentDir;
2342
- private readonly writeQueues;
2343
- private readonly ambiguousSkipCycles;
2344
- constructor(agentDir: string);
2345
- /**
2346
- * 为子 agent 创建隔离 worktree。
2347
- *
2348
- * @param mainCwd 主仓库根目录
2349
- * @param recordId 执行记录 ID(必须匹配 `^[\w-]+$`)
2350
- * @returns 冻结的 WorktreeHandle
2351
- */
2352
- create(mainCwd: string, recordId: string): Promise<WorktreeHandle>;
2353
- /**
2354
- * 注册子进程 pid(runSpawn spawn() 返回后调)。
2355
- * create 时 pid 未知写 0 占位,子进程 spawn 返回后(child.pid 同步可得)由此补全。
2356
- * reaper 据 pid 死活判孤儿,pid=0 条目用 SPAWN_GRACE 宽限。
2357
- * sessionFile 可选补全:传入时填入 registry entry(reaper 据 pid 死活判孤儿,不读本字段;保留供诊断)。
2358
- *
2359
- * [D5a] async 化:pid 补全走跨进程锁内 RMW(互斥窗口消除 updatePid 与并发 add/remove
2360
- * 的交错)。永不 reject(锁降级 + best-effort save 均内部兜底),调用方可安全
2361
- * fire-and-forget(session-runner 的 stdout data 回调上下文)。
2362
- */
2363
- registerPid(branch: string, pid: number, sessionFile?: string): Promise<void>;
2364
- /**
2365
- * 清理 worktree:git worktree remove --force + git branch -D + 注册表移除。
2366
- * 三步各自独立 try/catch——任一步失败不阻断其余(如 remove 失败仍尝试 branch -D + 注册表移除),
2367
- * 避免单步失败导致后续资源泄漏。
2368
- *
2369
- * @param handle 要清理的 worktree handle(含 mainCwd,不靠路径反推)
2370
- */
2371
- cleanup(handle: WorktreeHandle): Promise<void>;
2372
- /**
2373
- * 收集 worktree 的改动为 patch。
2374
- *
2375
- * [MF#3] patchFile 由调用方指定(写在 worktree 之外,避免被 cleanup 删除)。
2376
- * [MF#2] 先 git add -A 暂存全部改动(含未跟踪新文件),再 git diff --cached baseCommit
2377
- * 对比暂存区与 base commit。旧实现 `git diff HEAD baseCommit` 是树 vs 树对比:
2378
- * worktree HEAD 初始即 baseCommit,子 agent 不提交时 HEAD 仍 == baseCommit → diff 恒空 → 改动丢失。
2379
- *
2380
- * @param handle worktree handle
2381
- * @param patchFile patch 输出路径(须在 worktree 之外)
2382
- * @returns patch 结果(patchFile 路径 + failed/written 标记)。
2383
- * written=true 仅当 diff 非空且写盘成功;空 diff 或写失败均 written=false,
2384
- * 调用方据此回填 record.patchFile,避免悬空路径(`git apply` 不存在的文件)。
2385
- */
2386
- collectPatch(handle: WorktreeHandle, patchFile: string): Promise<PatchResult>;
2387
- /**
2388
- * 扫描并清理 pi-sub-* 孤儿 worktree + 物理面对账(D5b)。
2389
- *
2390
- * 阶段一(既有):遍历全局注册表(<agentDir>/subagents/worktrees.json),
2391
- * 按 pid 死活判孤儿。不依赖当前 cwd 是否 git repo——注册表里记了 repo 路径,
2392
- * 直接 git -C <repo> 跨 repo 清理。
2393
- *
2394
- * 判据(唯一不删条件 = 进程还活着):
2395
- * pid > 0 且 isProcessAlive(pid) → 跳过(活进程,绝不删)
2396
- * pid > 0 且进程已死 → 孤儿(正常退出未 cleanup / 崩溃残留)
2397
- * pid == 0 且超 SPAWN_GRACE_MS → 孤儿(create 后崩溃,pid 永未补全)
2398
- * pid == 0 且未超宽限 → 跳过(可能正在 spawn)
2399
- *
2400
- * 阶段二(D5b):物理面(tmpdir checkout + 分支)与注册表双向 diff 收敛——
2401
- * 兑现 worktree-registry.ts 头注释声称的「tmpdir + 分支对账兜底」。全流程
2402
- * 幂等、失败仅日志(对账失败不阻断 session_start)。
2403
- */
2404
- scan(): Promise<void>;
2405
- /**
2406
- * D5b 双向 diff 对账:物理面(tmpdir checkout 目录 + git branch --list)与
2407
- * 注册表互查,收敛三类漂移(锁消灭交错主因后,本对账兜底条目丢失/文件损坏的长尾)。
2408
- * 方向一/方向二的完整判据见 {@link removePhantomRegistryEntries} /
2409
- * {@link reconcileUnregisteredWorktrees}。
2410
- */
2411
- private reconcileWithPhysical;
2412
- /** 对账方向一(注册有 → 物理无):条目的分支与 checkout 目录都已不存在 → 条目指向
2413
- * 幻影资源 → 移除条目(纯清账,不删任何仍存在的资源,幂等安全)。 */
2414
- private removePhantomRegistryEntries;
2415
- /** 对账方向二(物理有 → 注册无):按 enc 段(encodeCwd(mainCwd))聚合,活信号 =
2416
- * <agentDir>/subagents/<enc>/sessions/*.alive 中存活的 pid(session-runner
2417
- * first header 时写入,崩溃残留不删):
2418
- * - 无活 pid:残留判死,checkout mtime 超 SPAWN_GRACE_MS 才清(防误清另一
2419
- * 进程 worktree add 完成到 registry.add 落盘之间的 create 窗口);
2420
- * - 恰好 1 个活 pid 且恰好 1 个残留:补写回注册表(自愈——最常见的双 session
2421
- * 并发覆盖丢条目场景,补写后回归标准 pid 判据路径);
2422
- * - 多活 pid 或多残留无法建立 branch↔pid 对应:跳过 + warn——宁延迟勿误删;
2423
- * 活体自身 cleanup 路径正常(registry.remove 幂等),死体等活 pid 全灭后
2424
- * 下一周期收敛。
2425
- *
2426
- * 返回本轮实际进入「歧义跳过」的 checkout 路径集(PS-12 老化计数收敛依据,
2427
- * 见 reconcileWithPhysical 尾部)。 */
2428
- private reconcileUnregisteredWorktrees;
2429
- /** 单 enc 段的残留处置三分支:无活 pid 判死清理 / 唯一对应自愈补写 / 多对应保守跳过。 */
2430
- private reconcileEncSegment;
2431
- /** 无活 pid 段:残留判死清理——checkout mtime 超 SPAWN_GRACE_MS 才清(防误清另一
2432
- * 进程 worktree add 完成到 registry.add 落盘之间的 create 窗口)。 */
2433
- private cleanupDeadSegment;
2434
- /**
2435
- * 物理面发现:扫描 <tmpdir>/pi-subagents/<enc>/<pi-sub-*> checkout 目录。
2436
- * repo 从 checkout/.git 指针文件推导(`gitdir: <repo>/.git/worktrees/<branch>`,
2437
- * 普通 repo 与 bare+worktree(.bare/worktrees/...)统一取 worktrees 段上两级);
2438
- * 推导失败(残缺 checkout)repo=undefined,由调用方按无主残留处置。
2439
- */
2440
- private discoverPhysicalWorktrees;
2441
- /**
2442
- * per repo 查物理分支全集:`git -C <repo> branch --list 'pi-sub-*' --format=%(refname:short)`。
2443
- * 读类命令不加写锁;单 repo 失败 → map 不含该 repo(get 返回 undefined),
2444
- * 调用方据此保守跳过该 repo 的条目判定(防把「查询失败」误判成「分支不存在」)。
2445
- */
2446
- private listPhysicalBranches;
2447
- /**
2448
- * 收集 enc 段的活 pid:<agentDir>/subagents/<enc>/sessions/*.alive 中
2449
- * readAliveMarker 解析成功且 isProcessAlive 的 pid(去重)。
2450
- * 崩溃残留的 .alive(pid 已死)天然过滤掉——这正是「死活判据」的物理面来源。
2451
- */
2452
- private collectAlivePids;
2453
- /**
2454
- * 清理物理残留(D5b 方向二的死体处置):worktree remove → prune → branch -D
2455
- * → 目录 rm 兜底,四步各自 best-effort(幂等,失败仅日志)。
2456
- * prune 必要性:checkout 目录已不存在的 worktree,remove 会失败且 branch -D
2457
- * 被「used by worktree」拒绝——prune 清掉缺失目录的元数据后分支才可删。
2458
- */
2459
- private cleanupPhysical;
2460
- /**
2461
- * 判孤儿:pid 死活为主判据。pid=0 走 SPAWN_GRACE 宽限(create→spawn 窗口)。
2462
- */
2463
- private isOrphan;
2464
- /** 清理单个孤儿条目:worktree remove + branch -D + 注册表移除,三步各自 best-effort。 */
2465
- private cleanupOrphan;
2466
- /**
2467
- * git 命令异步执行器。与 gitRun 同一超时/错误包装约定(message 格式逐字一致),
2468
- * 差异仅在错误属性形态(GitRunError 挂 exitCode/stderr/timedOut)。
2469
- * 写类命令经 per-repo mutex 串行(不依赖 git 锁实现细节 + 并发限流 + 行为确定性)。
2470
- *
2471
- * stdout 保真返回(不 trim):collectPatch 把 diff 输出原样落盘为 patch 文件,
2472
- * 裁掉尾换行会产出 `git apply` 拒绝的 corrupt patch(2026-08-16 门 4 实测)。
2473
- * 需要干净文本的消费点(baseCommit / 脏树 status 拼接)自行 trim。
2474
- */
2475
- private gitRunAsync;
2476
- }
2477
-
2478
3026
  /**
2479
3027
  * Workflow Extension — Worker Handle
2480
3028
  *
@@ -2797,7 +3345,7 @@ declare class AgentCall {
2797
3345
  readonly opts: AgentCallOpts;
2798
3346
  status: AgentCallStatus;
2799
3347
  attempts: number;
2800
- result?: AgentResult;
3348
+ result?: AgentResult$1;
2801
3349
  /** Pi subprocess session ID(uuidv7,G-017 归此)。 */
2802
3350
  sessionId?: string;
2803
3351
  /** Session JSONL 绝对路径(finalizeCall 后从 result.sessionFile 填入,对齐 sessionId 模式)。 */
@@ -2814,7 +3362,7 @@ declare class AgentCall {
2814
3362
  * 标记完成(成功或失败均调用——result.error 区分)。
2815
3363
  * @throws 若当前非 running(pending 不能直接跳 done,必须先 markRunning)
2816
3364
  */
2817
- markDone(result: AgentResult): void;
3365
+ markDone(result: AgentResult$1): void;
2818
3366
  /** 记录 pi subprocess session ID(dispatch 成功后)。 */
2819
3367
  setSessionId(sessionId: string): void;
2820
3368
  /** 记录 session JSONL 绝对路径(finalizeCall 后,对齐 setSessionId 模式)。 */
@@ -3099,17 +3647,17 @@ declare class WorkflowRun {
3099
3647
  /**
3100
3648
  * Agent 子进程执行 port。Infra 实现:SubprocessAgentRunner。
3101
3649
  *
3102
- * run 执行单次 agent 调用(委托 SubagentService.executeAndAwait),返回结构化结果。
3103
- * signal 用于 abort 传播。
3650
+ * run 执行单次 agent 调用(委托 SubagentService.executeWorkflowAgent,[H2 W4] 纯转调),
3651
+ * 返回结构化结果。signal 用于 abort 传播。
3104
3652
  *
3105
- * onEvent(可选):强类型 AgentEvent 回调,供调用方实时更新 live record 供 TUI 展示进度。
3106
- * 不传则不回调(向后兼容;现有调用点不传不受影响)。
3653
+ * onEvent(可选):强类型 AgentEvent 回调,透传 service 派发路径(journal 转发/守护
3654
+ * 刷新源),生产 pump 侧恒 undefined。
3107
3655
  *
3108
3656
  * D-005: onEvent 签名从 raw Record<string,unknown> 升级为 AgentEvent——委托后不再有
3109
3657
  * raw JSONL 中间层(executeAndAwait 直接出 AgentEvent,session-runner handleSdkEvent 出口)。
3110
3658
  */
3111
3659
  interface AgentRunner {
3112
- run(opts: AgentCallOpts, signal: AbortSignal, onEvent?: (event: AgentEvent) => void, stream?: SubagentStream): Promise<AgentResult>;
3660
+ run(opts: AgentCallOpts, signal: AbortSignal, onEvent?: (event: AgentEvent) => void, stream?: SubagentStream): Promise<AgentResult$1>;
3113
3661
  }
3114
3662
  /**
3115
3663
  * WorkflowRun 持久化 port。Infra 实现:JsonlRunStore。
@@ -3209,103 +3757,52 @@ interface LifecycleDeps {
3209
3757
  */
3210
3758
  onWorkflowCall?: (name: string, args: Record<string, unknown>, parentRun: WorkflowRun) => Promise<unknown>;
3211
3759
  /**
3212
- * UI streaming sink(ctx.ui.setWidget),workflow agent call 创建 SubagentStream 用。
3213
- *
3214
- * Interface makeDeps 注入(从 SubagentService.getStreamSink() 取)。
3215
- * dispatchAgentCall 用它创建 SubagentStream(widgetKey=subagent-stream-<runId>-<stepIndex>),
3216
- * 使 workflow agent call 的 text_delta 走与 background subagent 相同的 streaming 链路。
3217
- * 可选——无 UI 模式(TUI/RPC setWidget)时为 undefined,dispatchAgentCall 不创建 stream。
3218
- */
3219
- streamSink?: StreamSink;
3760
+ * [H2 W3] workflow agent() 统一派发入口(SubagentService.executeWorkflowAgent
3761
+ * deps 注入形态,设计 §3.5 终态数据流)。窄函数类型——不引 execution 层具体类,
3762
+ * 保持本 ports 文件零 infra/execution 依赖。由组合根(extension index.ts makeDeps)
3763
+ * 注入:闭包捕获 getSubagentService() 单例,parentRunId 由 pump 侧补 run.runId
3764
+ *
3765
+ * 可选——未注入时(旧测试 deps)dispatchAgentCall 回退 deps.runner([H2 W4]
3766
+ * SAR 已掏空为纯转调 executeWorkflowAgent,两分支执行体归一非双轨,差异仅 parentRunId
3767
+ * 来源:注入 = 真实 run.runId,回退 = SAR_UNATTACHED_PARENT_RUN_ID 占位;生产装配
3768
+ * 两字段同时注入,dispatch 恒优先)。
3769
+ */
3770
+ workflowAgentDispatch?: (opts: AgentCallOpts, parentRunId: string, signal?: AbortSignal) => Promise<AgentResult$1>;
3220
3771
  }
3221
3772
 
3222
- /**
3223
- * SAR 构造参数。
3224
- *
3225
- * per-session(makeDeps 时创建,随 session 销毁):
3226
- * - subagentService: 进程单例(getSubagentService()),委托目标
3227
- * - ctxModel: 当前 session 主 agent 模型(D-008 model 填底,opts.model 空时用此)
3228
- *
3229
- * resolveAgentOpts 在 orchestration 层完成(M2 修正后单参数,只处理 schema SO 指令 + skill),
3230
- * 结果填进 AgentCallOpts.skillPath/schemaEnv/appendSystemPrompt,SAR 收已解析的 opts。
3231
- * 不含 agent/session 目录/临时文件集合依赖(agent ref 交 resolveIdentity,无临时文件)。
3232
- */
3773
+ /** SAR 构造参数(签名冻结——session-lifecycle.ts 装配点零改动)。 */
3233
3774
  interface SubprocessAgentRunnerDeps {
3234
3775
  subagentService: SubagentService;
3776
+ /** 兼容保留:run() 不消费(model 解析归 service resolveIdentity)。 */
3235
3777
  ctxModel?: ModelInfo;
3236
3778
  }
3237
3779
  /**
3238
- * AgentRunner port 实现——委托 SubagentService.executeAndAwait
3780
+ * AgentRunner port 实现——纯转调 SubagentService.executeWorkflowAgent
3239
3781
  *
3240
- * 层归属:execution(MF-3 从 orchestration 迁入)。implements orchestration 层 port。
3782
+ * 层归属:executionimplements orchestration 层 port。
3241
3783
  *
3242
- * 行为契约(BC-1/BC-2/BC-9/BC-10):
3784
+ * 契约:
3243
3785
  * - opts 形状不变(AgentCallOpts,含 resolveAgentOpts 填的 skillPath/schemaEnv)
3244
- * - result 形状不变(workflow AgentResult: content/parsedOutput/usage/error/toolCalls)
3245
- * - 不 reject——失败信息入 result.error(与 executeAgentCall 契约一致)
3246
- * - timeoutMs 合并 signal(D-A9);onEvent 桥接 AgentEvent→workflow liveRecord(D-A8)
3247
- *
3248
- * [P1 引擎接线] 执行经 EnginePort。pi 引擎绑定本 SAR 的服务引用(per-session DI——
3249
- * 单测注入 mock 时全局单例不可见;生产环境两者是同一进程单例对象),行为零变化;
3250
- * 非 pi 引擎(P4 路由可达:frontmatter/调用参数/全局默认指定)经 registry.getEngine
3251
- * 动态获取——「引擎身份」的归属边界在注册表,SAR 不感知具体引擎实现。
3252
- *
3253
- * [P4 配置路由] 每次运行经 engine/routing.ts 的 routeEngineForHost(D3-② 路由单点:
3254
- * 唯一实现承载三层优先级(调用参数 opts.engine > agent frontmatter engine > 全局默认
3255
- * 'pi')+ pi 同步短路 + registry 注入 + probe/fallback 三守卫,D9;本类与
3256
- * SubagentService.execute 是其仅有的两调用点)。路由失败(engine_not_found /
3257
- * engine_probe_failed / model_not_available)与预检命中(engine_capability_unsupported,
3258
- * D3-④)不 reject,入 result.error——与 executeAgentCall 契约一致。
3786
+ * - result 形状不变(AgentResult——executeWorkflowAgent 返回类型即 orchestration
3787
+ * AgentResult,零映射)
3788
+ * - 路由失败/预检命中/嵌套超限经 service 同步抛错(§3.4 同步抛错回脚本 + D3
3789
+ * 路由先于 record 创建的构造性推论:不产生孤儿 record)——传播形态与
3790
+ * workflowAgentDispatch 注入路径一致(dispatch 闭包同样不吞错),pump
3791
+ * dispatchCall catch 兜底回发。
3259
3792
  */
3260
3793
  declare class SubprocessAgentRunner implements AgentRunner {
3261
3794
  private readonly subagentService;
3262
3795
  private ctxModel;
3263
- /** pi 引擎(per-session DI 绑定,缺省执行路径——见类注释)。 */
3264
- private readonly piEngine;
3265
3796
  constructor(deps: SubprocessAgentRunnerDeps);
3266
3797
  /**
3267
- * 刷新主 agent model 缓存。model_select 事件时由 index.ts 调用。
3268
- * H1 修复:旧实现 ctxModel 是 readonly,session_start 后固化,
3269
- * model_select 只刷新 ModelConfigService._ctxModel 不更新 SAR →
3270
- * workflow 路径用过期模型。
3271
- */
3272
- updateCtxModel(model: ModelInfo | undefined): void;
3273
- /**
3274
- * 执行单次 agent 调用:路由(P4)→ 预检(D3-④)→ EnginePort.run → PiEngine 委托
3275
- * SubagentService.executeAndAwait。
3798
+ * 刷新主 agent model 缓存(model_select 事件时由 extension index.ts 调用)。
3276
3799
  *
3277
- * 接线链路:
3278
- * routeEngineForHost(D3-② 路由单点:三层 + pi 同步短路 + probe + 守卫)→
3279
- * assertTaskShapeSupported(D3-④ 预检 capabilities 化)→ mergeTimeoutSignal →
3280
- * engine.run(opts 直传——D6 合流:AgentCallOpts 即 EnginePort 任务形状,零映射;
3281
- * PiEngine 内一次直出 ExecuteOptions + engine 留痕) → executeAndAwait →
3282
- * AgentOutcome → AgentResult
3283
- *
3284
- * 错误处理:不 reject。
3285
- * - 路由失败(未注册 id / probe 失败 + 守卫或 strict)→ AgentResult.error(错误码前缀)
3286
- * - 预检命中(capabilities 不支持的任务形状参数)→ AgentResult.error
3287
- * (engine_capability_unsupported——[D3-④] workflow 域 zcode+worktree 由漏拦变
3288
- * 拦截,本单元唯一有意行为变化;拒绝在 journal/run 之前,无进程无 journal 产物)
3289
- * - executeAndAwait 内部失败 → 返回 AgentResult(success:false) → 已映射 error 字段
3290
- * - executeAndAwait throw(嵌套超限 BC-12)→ catch → AgentResult.error
3291
- * - spawn 级失败已由 runSpawn 内部收口为 failed AgentResult(不逃逸)
3292
- */
3293
- run(opts: AgentCallOpts, signal: AbortSignal, onEvent?: (event: AgentEvent) => void, stream?: SubagentStream): Promise<AgentResult>;
3294
- /**
3295
- * 引擎获取(routeEngineForHost 的 probe 注入体):pi 走 per-session DI 绑定(mock
3296
- * 语义 + 生产同单例,P1 行为零变化);非 pi 经 registry.getEngine 动态获取(P4:
3297
- * 引擎身份归属注册表,未注册 id 抛 EngineNotFoundError——路由期已前置校验,这里
3298
- * 是防御性兜底)。[D3-②] get/has/list 的路由期注入已由 routeEngineForHost 内部
3299
- * 承载(本地 pi 恒可用口径),本方法只剩 probe 一个消费点(pi 请求同步短路恒免探)。
3300
- */
3301
- private engineFor;
3302
- /**
3303
- * 三层路由输入装配(D9):调用参数(opts.engine,workflow step 级透传)> agent
3304
- * frontmatter(ModelConfigService.getAgentConfig——loadByPath mtime 缓存,幂等)>
3305
- * 全局默认(config.json defaultEngine)。单例未就绪(session_start 前/测试 mock)
3306
- * 时各层缺省——落内置 'pi'。
3800
+ * [H2 W4] run() 不再消费 ctxModel(model 解析归 service resolveIdentity),
3801
+ * 本方法保留仅为装配链兼容(index.ts 调用不炸);字段更新不再影响执行路径。
3307
3802
  */
3308
- private buildRoutingInput;
3803
+ updateCtxModel(model: ModelInfo | undefined): void;
3804
+ /** 纯转调 service 统一编排入口(形参原样透传;parentRunId 用直调占位)。 */
3805
+ run(opts: AgentCallOpts, signal: AbortSignal, onEvent?: (event: AgentEvent) => void, stream?: SubagentStream): Promise<AgentResult$1>;
3309
3806
  }
3310
3807
 
3311
3808
  /** 索引文件名(落 sessionsDir 兄弟位置——写索引不改 sessionsDir 目录 mtime,不击穿 L0 快路径)。 */
@@ -3320,10 +3817,11 @@ declare const SUBAGENT_RECORD_CUSTOM_TYPE = "subagent-record";
3320
3817
  * `subagent-record` entry 的 data schema(v1)。
3321
3818
  *
3322
3819
  * = 完整 SubagentRecord 快照(GUI 侧列表/详情需要的全部持久化字段)+ 版本号。
3323
- * 显式排除三个非持久化字段(与 SubagentRecord 的差集):
3820
+ * 显式排除两个非持久化字段(与 SubagentRecord 的差集):
3324
3821
  * - currentActivity:running 时的瞬时流态,重开 session 无重建价值;
3325
- * - externalInstance:跨重启探活态(pid/startedAt),由 .alive sidecar 重建;
3326
3822
  * - worktreeHandle:不可 JSON 序列化的运行时句柄(布尔投影 worktree 保留)。
3823
+ *([U4a / D3b (a)] externalInstance 投影已随字段链删除——探活态由 .alive sidecar
3824
+ * 现查探针承担,不再进 record 快照。)
3327
3825
  *
3328
3826
  * undefined 字段经 JSON.stringify 自然缺省(与 SubagentRecord 重建侧语义一致)。
3329
3827
  *
@@ -3406,6 +3904,18 @@ interface SubagentRecordEntryData {
3406
3904
  * 未离开批 / 旧 entry 零迁移。消费方:U5 E1 重建扫描只收无标记成员(防双重通知)。
3407
3905
  */
3408
3906
  batchFinalized?: boolean;
3907
+ /**
3908
+ * 来源身份(H2 W1,设计 subagent-workflow-record-unification §3.3 D1)。
3909
+ * undefined(存量 entry)= "tool" 语义,消费方零迁移。重启后 origin 过滤面
3910
+ * (subagents list / renderer / TUI)生效的唯一持久化载体——漏本字段则重启后
3911
+ * workflow record 逃过全部投影过滤。
3912
+ */
3913
+ origin?: RecordOrigin;
3914
+ /**
3915
+ * origin="workflow" 时所属 workflow run id(W2 写入);tool 来源恒缺省。
3916
+ * W2/W3 run 视图按 collectRecordsByParentRunId 从本字段回查本 run 的 record 集。
3917
+ */
3918
+ parentRunId?: string;
3409
3919
  }
3410
3920
  /** SubagentRecord → 自描述 entry data(快照投影,不 mutate 源)。
3411
3921
  *
@@ -3640,6 +4150,12 @@ type StartHandlerResult = {
3640
4150
  };
3641
4151
  interface ListHandlerInput {
3642
4152
  includeFinished?: boolean;
4153
+ /**
4154
+ * [H2 W1,设计 subagent-workflow-record-unification §3.3 D1①] 同时列出 workflow
4155
+ * 脚本 agent() 派发的 record(origin="workflow")。缺省 false——list 默认只展示
4156
+ * 手动 tool 派发的 subagent;排查 workflow 子代理时显式传 true。
4157
+ */
4158
+ includeWorkflow?: boolean;
3643
4159
  limit?: number;
3644
4160
  }
3645
4161
  /** list 领域对象(宿主 adapter 包成 list 工具结果,最外层 subagentId/sessionFile 为 null)。 */
@@ -3710,13 +4226,12 @@ declare function endedMessageGuard(service: SubagentService, id: string, origina
3710
4226
  * 内部 ExecutionStatus → 对外 state 映射(设计决策 10 细则 3)。
3711
4227
  * 两态收敛后的真实映射只有两条:
3712
4228
  * running → active / closed → ended(closed 统一终态,含 cancelled)
3713
- * ExternalState 仍声明 waiting/error 四态联合(对外契约不变),但当前状态机不产生
3714
- * 这两个值——它们是历史多态映射(idle→waiting / failed+crashed→error)的遗留声明。
4229
+ * ExternalState 即两态联合(历史 waiting/error 死值成员已随 L2 清扫删除)。
3715
4230
  * 未来内部加态必须扩展此处,漏加会在 default 分支编译报错(而非静默返回 undefined
3716
4231
  * 让 state 字段以无主值进入 listResponse JSON)。
3717
4232
  */
3718
4233
  declare function mapExternalState(status: ExecutionStatus): ExternalState;
3719
- /** SubagentRecord → SubagentListItem(state 四态主字段 + status 调试字段,duration 实时计算)。
4234
+ /** SubagentRecord → SubagentListItem(state 两态主字段 + status 调试字段,duration 实时计算)。
3720
4235
  * parent 从 record.parentRecordId 派生(配合直接父守卫),resumable 从 isResumable 派生
3721
4236
  * (「可续聊」对外表达);outcome 一等终态语义(projectOutcome 唯一出口),closedReason
3722
4237
  * 退出对外 JSON(保留为 record 内部诊断字段),对外成败判读收口到 outcome。
@@ -3726,21 +4241,21 @@ declare function recordToListItem(r: SubagentRecord): SubagentListItem;
3726
4241
  declare function startHandler(service: SubagentService, input: StartHandlerInput | undefined, signal: AbortSignal | undefined, ctxModel?: ModelInfo): Promise<StartHandlerResult>;
3727
4242
  /**
3728
4243
  * list 数据源(诚实声明):
3729
- * collectRecords(limit, statusFilter) 合并内存(running) + 磁盘(重建)。磁盘源天然
3730
- * session 可见——/new /resume /fork 后前 session 的终态 record 仍在 sessions
3731
- * 目录里(直到 GC)。内存源仅当前 session 的 running record。
4244
+ * collectRecords(limit, statusFilter, includeWorkflow) 合并内存(running) + 磁盘(重建)
4245
+ * 磁盘源天然跨 session 可见——/new /resume /fork 后前 session 的终态 record 仍在
4246
+ * sessions 目录里(直到 GC)。内存源仅当前 session 的 running record。
4247
+ * [H2 W1] origin==="workflow" 的 record 默认过滤(D1①),includeWorkflow:true 放行。
3732
4248
  */
3733
4249
  declare function listHandler(service: SubagentService, input: ListHandlerInput | undefined): ListHandlerResult;
3734
4250
  declare function cancelHandler(service: SubagentService, input: CancelHandlerInput | undefined): Promise<CancelHandlerResult>;
3735
4251
  /**
3736
4252
  * message action handler:向对话模式 subagent 续聊/插入消息。
3737
4253
  *
3738
- * 状态 × interrupt 自动映射(agent 只表达意图):
3739
- * runningdeliverChatMessage 热路径(进程活:prompt + streamingBehavior,interrupt=true
3740
- * 抢占 / false 排队)
3741
- * 进程死 → deliverChatMessage 冷路径(resumeRound 重开 session + prompt,interrupt 自动
3742
- * 退化,agent 无感)
3743
- * 终态 → throw ended(正常路径不命中——终态 record 已 archive,getRecordForAction 先 throw not found)
4254
+ * [H1 U6] 状态分流面收敛:running Continuation 派发新轮(新 run + resume 锚点);
4255
+ * 在途轮存在 D2 打断(abort + 入队);终态 → throw ended(正常路径不命中——终态
4256
+ * record archive,getRecordForAction 先 throw not found)。interrupt 输入字段保留
4257
+ *([A4] 工具 schema 兼容面——extensions subagent-tool-schema 仍声明该字段)但不参与
4258
+ * 分派(D2 统一打断语义)。
3744
4259
  *
3745
4260
  * 归属守卫:getRecordForAction 内部校验 rootSessionId。
3746
4261
  *
@@ -4592,7 +5107,7 @@ declare function executeNestedWorkflow(name: string, args: Record<string, unknow
4592
5107
  * pending:unregister → onRunDone」终态序列的唯一定义点(D5-② 单点化,收敛原
4593
5108
  * 8 处逐字复制;OR-8 收口步骤与 OR-4/B-4 双围栏内化于本函数)
4594
5109
  *
4595
- * 4 个导出函数(domain-models.md §失败处理矩阵):
5110
+ * 4 个 handle* 路由函数(domain-models.md §失败处理矩阵)+ 终态化/重建/防御 helper 若干:
4596
5111
  * - handleWorkerMessage(run, raw, deps, handlers) — 路由 agent_call/return/error/log
4597
5112
  * - handleWorkerError(run, err, deps, handlers) — worker uncaught error
4598
5113
  * - handleWorkerExit(run, code, handle, deps, handlers) — worker exit
@@ -4679,7 +5194,7 @@ declare function resetRebuildFailureInjectionForTest(): void;
4679
5194
  * result 以 IN_FLIGHT_CALL_CANCELLED_MSG 承载取消原因;
4680
5195
  * - trace 节点置 failed + 固定取消文案 + completedAt(「failed + Cancelled 文案」
4681
5196
  * 即取消的既有表达形态,不新增状态枚举);
4682
- * - node.live 清除(终态 run TUI 轮询,防 ExecutionRecord 滞留)。
5197
+ * - node 无运行期附属对象([H2 W3] trace.live 退役后节点只剩终态摘要字段)。
4683
5198
  *
4684
5199
  * 调用点约定:每个 transition("done") 成功后、store.save 之前——先收口再落盘,
4685
5200
  * 内存态与持久化快照在同一时点收敛(「run-snapshot 落盘前」的实现形态)。
@@ -5218,7 +5733,7 @@ interface CallSnapshot {
5218
5733
  opts: AgentCallOpts;
5219
5734
  status: "pending" | "running" | "done";
5220
5735
  attempts: number;
5221
- result?: AgentResult;
5736
+ result?: AgentResult$1;
5222
5737
  sessionId?: string;
5223
5738
  sessionFile?: string;
5224
5739
  traceNode: ExecutionTraceNode;
@@ -5249,10 +5764,9 @@ interface RunSnapshot {
5249
5764
  * WorkflowRun → 单行快照。
5250
5765
  *
5251
5766
  * - 补 v 字段(D4 裁决②:写入恒带当前版本)。
5252
- * - strip live(防御内聚):calls[].traceNode trace 数组节点的 `live` 运行期
5253
- * 对象剥除——ExecutionRecord 含可变 turns[]/controller,不可序列化且跨进程
5254
- * 必死(重跑时由 dispatchAgentCall 重建);strip 产出新对象,不 mutate 内存
5255
- * 中的 run(save 后 run 可继续跑)。
5767
+ * - trace 节点全量序列化([H2 W3] live strip 分支随 ExecutionTraceNode.live 字段
5768
+ * 删除而退役——节点不再携带运行期对象,无需防御性剥离;旧快照经 fromRunSnapshot
5769
+ * 重水合时多余键自然丢弃,存量行零迁移)。
5256
5770
  * - spec.budgetRef 剔除:父 Budget 共享引用是进程内优化(嵌套 workflow 预算
5257
5771
  * 共享),非持久化数据;Budget 实例若混入 spec 落盘将退化为普通对象投影
5258
5772
  * (重水合后类型不符的脏字段)——重水合后 budget 从 state.budget 独立重建,
@@ -5637,9 +6151,10 @@ declare function renderXmlSection(section: {
5637
6151
  * worktree-registry / engine-discovery / zcode preparer / zcode appserver-home),
5638
6152
  * tmp 命名各异(`.tmp.<pid>` / `.tmp_<pid>_<rand>` / `.tmp-<pid>-<ts>`)、失败路径
5639
6153
  * 清理纪律不齐(worktree-registry 失败时漏清残留 tmp)。本模块给出统一原语供全
5640
- * 部写点收敛(调用点迁移归 u-wire 单元,本单元只建原语)。现存消费方 4 处:
5641
- * manifest-store / sessions-index / worktree-registry / engine-discovery——zcode
5642
- * preparer 与 zcode appserver-home 两处写点已随 2026-09 共享宿主 HOME 重构删除。
6154
+ * 部写点收敛(调用点迁移归 u-wire 单元,本单元只建原语)。现存消费方 5 处:
6155
+ * manifest-store / sessions-index / worktree-registry / engine-discovery / record-store
6156
+ * (manifest writeSync 面)——zcode preparer 与 zcode appserver-home 两处写点已随
6157
+ * 2026-09 共享宿主 HOME 重构删除。
5643
6158
  *
5644
6159
  * **统一 tmp 命名约定**:`<最终路径>.tmp.<pid>.<seq>-<rand>`。
5645
6160
  * - `.tmp.` 标记向后兼容 manifest-store 既有扫描(`x.json` 的 tmp 名为
@@ -5655,8 +6170,8 @@ declare function renderXmlSection(section: {
5655
6170
  *
5656
6171
  * **两种耐久档位**(对齐现存两族写点的生产模式):
5657
6172
  * - sync(writeAtomicFileSync):writeFileSync + renameSync,无 fsync——对齐
5658
- * worktree-registry / engine-discovery 两处同步写点现状(注册表/发现缓存类,
5659
- * 进程崩溃窗口可容忍);
6173
+ * worktree-registry / engine-discovery / record-store(manifest writeSync 面)
6174
+ * 同步写点现状(注册表/发现缓存/索引类,进程崩溃窗口可容忍);
5660
6175
  * - async(writeAtomicFile):fsync 文件 → rename → 尽力 fsync 目录——对齐
5661
6176
  * manifest-store / sessions-index 的生产耐久模式(掉电也不丢已确认写入)。
5662
6177
  */
@@ -5717,8 +6232,9 @@ declare function writeAtomicFile(filePath: string, content: string | Uint8Array,
5717
6232
  *
5718
6233
  * 供两类消费方:
5719
6234
  * - cleanupStaleTmpFiles 的内部步骤;
5720
- * - 需要按域校验内容再决定「删 or 提升为正式文件」的宿主恢复逻辑
5721
- * (manifest-store.recoverTmpFiles 模式:tmp 合法且目标缺失 → rename 提升)。
6235
+ * - 需要按域校验内容再决定「删 or 提升为正式文件」的宿主恢复逻辑(基于
6236
+ * listStaleTmpFiles + parseAtomicTmpPath().targetPath 组合实现;曾有的
6237
+ * manifest promote 消费方已随 U4c 缓存降级退役为 sweepTmpFiles 纯删除)。
5722
6238
  *
5723
6239
  * 目录不存在 → 返回空数组(恢复扫描对未初始化布局宽容)。非约定形态文件
5724
6240
  * 一律不认(用户数据零误伤边界见 parseAtomicTmpPath 注释)。
@@ -5745,7 +6261,7 @@ interface CleanupStaleTmpResult {
5745
6261
  /**
5746
6262
  * 清理目录内约定形态的 tmp 残留(单条失败不阻断其余条目,逐条结果回传)。
5747
6263
  *
5748
- * 恢复语义(对齐 manifest-store.recoverTmpFiles 的删除分支泛化):本函数只做
6264
+ * 恢复语义(删除级恢复;曾对齐的 manifest-store promote 分支已随 U4c 退役):本函数只做
5749
6265
  * 「删除」级恢复;「校验后提升为正式文件」需域知识(manifest 记录合法性),
5750
6266
  * 由调用方基于 listStaleTmpFiles + parseAtomicTmpPath().targetPath 自行实现。
5751
6267
  */
@@ -5811,6 +6327,6 @@ declare function boundedPrettySerialize(value: unknown, budget: number): string;
5811
6327
  * docs/design/subagent-core-sink-design.md(sink 下沉收口扩面,2026-08-31);
5812
6328
  * 宿主接入示例见包 README(§3.4 core_host_not_configured 恢复指引的落点)。
5813
6329
  */
5814
- declare const CORE_PACKAGE_VERSION = "0.8.0";
6330
+ declare const CORE_PACKAGE_VERSION = "0.9.0";
5815
6331
 
5816
- export { AGENT_REF_EXT, AgentCall, AgentCallOpts, type AgentEntry, AgentEventLogEntry, type AgentProfile, AgentRegistry, AgentResult, type AgentRunner, type ArgKeySet, type ArgMetaOptions, type ArgMetaWarning, type AtomicTmpRef, type AtomicWriteFileOptions, type AtomicWriteOptions, BG_MESSAGE, BgResponse, Budget, CORE_PACKAGE_VERSION, type CachedWorkflowMeta, type CancelHandlerInput, type CancelHandlerResult, CancelResponse, type ChangeListener, type CleanupStaleTmpOptions, type CleanupStaleTmpResult, type CleanupWorktreeOptions, type CloseHandlerInput, type CloseHandlerResult, CloseResponse, ClosedReason, type CollectWorktreePatchOptions, type ConcurrencyPool, type CreateConcurrencyPoolOptions, type D8CompatZcodeEngineDeps, DEFAULT_LIST_LIMIT, DEFAULT_PROVIDER_ID, DEFAULT_SAVE_MIN_INTERVAL_MS, DEFAULT_STATE_MAX_RUNS, DEFAULT_WORKFLOW_SAVED_DIR, DEFAULT_WORKFLOW_TMP_DIR, DialogGlobalQueue, type DiscoveredResource, DiscoveryRoot, DisplayItem, DoneReason, EnginePort, type EngineRouteOptions, type EngineRouteResult, type EngineRouting, type EngineRoutingInput, type EngineRoutingSource, ExecutionMode, ExecutionOutcome, ExecutionRecord, ExecutionStatus, ExecutionTraceNode, ExternalState, FORK_FROM_DEFAULT_PROMPT, FileRunStore, type FinalizeRunOptions, type ForkFromHandlerInput, type ForkFromHandlerResult, ForkFromResponse, type GenerateWorkflowScriptOptions, type GenerateWorkflowScriptResult, GitRunError, type GlobalConfigReadResult, IDENTITY_CUSTOM_TYPE, INDEX_FILENAME, type InFlightListener, type InFlightSnapshot, type InvalidAgentRefMessageOptions, type LauncherDeps, type LifecycleDeps, type LintFinding, type LintResult, type ListFormatOptions, type ListHandlerInput, type ListHandlerResult, ListResponse, type ListWorktreePorcelainOptions, MAX_LIST_LIMIT, MAX_RETAINED_DONE_RUNS, MAX_TIMER_DELAY_MS, 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 NotifyLedgerHost, type PatchBaselineAnchor, type PruneStateDeps, type QueuePolicy, RecordStore, type RecordStorePi, type RecoverCrashedRunsHooks, type RecoverCrashedRunsResult, type ResourceKind, type ResourceSource, type RunSnapshot, type RunSpec, type RunState, RunStatus, type RunStore, SAFE_ID_RE, SNAPSHOT_VERSION, SUBAGENT_RECORD_CUSTOM_TYPE, type ScanConfig, type StartHandlerInput, type StartHandlerResult, type StatusFilter, type SubagentIdentityData, SubagentListItem, SubagentRecord, type SubagentRecordEntryData, SubagentService, type SubagentServiceInit, SubagentStream, SubprocessAgentRunner, THINKING_ORDER, Trace, WORKFLOW_REF_EXT, WORKFLOW_REF_RESERVED_NAMES, type WorkerHandlers, type WorkerHost, WorkerHostImpl, WorkerLogEntry, type WorkflowDirOptions, type WorkflowEntry, type WorkflowMeta, type WorkflowRefInvalidReason, WorkflowRun, type WorkflowRunMeta, type WorkflowRunResult, type WorkflowRunSummary, type WorkflowScanConfig, WorkflowScript, type WorkflowScriptRegistry, WorkflowScriptRegistryImpl, type WorkflowSource, WorktreeManager, type WorktreePatchResult, ZCODE_FALLBACK_DEFAULT_MODEL, type ZcodeEngineDeps, abortRun, argKeysFromMeta, assertSafeId, atomicTmpPathFor, bestEffort, bindNotifyLedgerHost, boundedPrettySerialize, buildEngineModelsPromptAppend, buildSubagentEngineSection, cancelHandler, cleanupStaleTmpFiles, cleanupWorktree, clearSkillPathCache, closeHandler, closeOutInFlightCalls, collectWorktreePatch, computeElapsedSeconds, configureNotifyDomain, countAllToolCalls, createConcurrencyPool, createSubagentService, createUiRequestHandlerForMode, createZcodeEngine, deleteWorkflow, deriveOutcome, discoverAgents, discoverResources, discoverWorkflows, displayAgentName, endedMessageGuard, escapeXml, evictDoneRunsBeyondCap, executeNestedWorkflow, finalizeRun, findFlattenedArgKeys, findWorkspaceRoot, forkFromHandler, formatAgentList, formatModelList, formatWorkflowList, fromRunSnapshot, generateWorkflowScript, getAllToolCalls, getBoundNotifyLedger, getCachedFileContent, getCachedParsed, getInFlightSnapshot, getModelConfigService, getOrCreateChannelRegistry, getSubagentService, getWorkflow, getWorkflowByPath, gitRun, hasApiKey, invalidAgentRefMessage, invalidateCache, isProcessAlive, isResumable, isSafeId, isScriptRunning, isTreeDirty, killAllSpawnedChildren, killRecordChildWithEscalation, lintScript, listHandler, listStaleTmpFiles, listWorktreePorcelain, loadWorkflowScriptByPath, loadWorkflows, makeSerializeFailedResult, mapExternalState, maxTurnsToWatchdogMs, maybeCleanupExpiredSessionFiles, messageHandler, normalizeArgsByMeta, normalizeRef, normalizeWorkflowRef, notifyGateAllowsDelivery, parseAgentProfile, parseAtomicTmpPath, parseEngineHandle, parseResourceMeta, parseResourceMetaDetailed, postBudgetUpdate, projectLiveProgress, projectOutcome, pruneStateFilesBeyondCap, readGlobalConfig, readSubagentHistoryMessages, recordToListItem, recoverCrashedRuns, registerNativeSessionReader, registerZcodeEngine, renderXmlSection, resetRebuildFailureInjectionForTest, routeEngine, runAndWait, runSummary, runWorkflow, saveWorkflow, scheduleTimeBudget, setEngineDiscoveryRescanOptions, setInFlightListener, setModelConfigService, setSubagentService, sortByCodepoint, splitZcodeModelRef, startHandler, summarizeDescription, syncEnginesFile, terminateRunningRuns, toRunSnapshot, toSubagentRecordEntry, wrapForkFromPrompt, writeAtomicFile, writeAtomicFileSync };
6332
+ export { AGENT_REF_EXT, AgentCall, AgentCallOpts, type AgentEntry, type AgentEventLogEntry, type AgentProfile, AgentRegistry, AgentResult$1 as AgentResult, type AgentRunner, type ArgKeySet, type ArgMetaOptions, type ArgMetaWarning, type AtomicTmpRef, type AtomicWriteFileOptions, type AtomicWriteOptions, BG_MESSAGE, type BgResponse, Budget, CLOSED_REASONS, CORE_PACKAGE_VERSION, type CachedWorkflowMeta, type CancelHandlerInput, type CancelHandlerResult, type CancelResponse, type ChangeListener, type CleanupStaleTmpOptions, type CleanupStaleTmpResult, type CleanupWorktreeOptions, type CloseHandlerInput, type CloseHandlerResult, type CloseResponse, type ClosedReason, type CollectWorktreePatchOptions, type ConcurrencyPool, type CreateConcurrencyPoolOptions, type D8CompatZcodeEngineDeps, DEFAULT_AGENT_NAME, DEFAULT_LIST_LIMIT, DEFAULT_PROVIDER_ID, DEFAULT_SAVE_MIN_INTERVAL_MS, DEFAULT_STATE_MAX_RUNS, DEFAULT_WORKFLOW_SAVED_DIR, DEFAULT_WORKFLOW_TMP_DIR, DialogGlobalQueue, DirtyWorktreeError, type DiscoveredResource, DiscoveryRoot, type DisplayItem, DoneReason, EnginePort, type EngineRouteOptions, type EngineRouteResult, type EngineRouting, type EngineRoutingInput, type EngineRoutingSource, type ExecutionMode, type ExecutionOutcome, type ExecutionRecord, type ExecutionStatus, ExecutionTraceNode, type ExternalState, FORK_FROM_DEFAULT_PROMPT, FileRunStore, type FinalizeRunOptions, ForkDepthExceededError, type ForkFromHandlerInput, type ForkFromHandlerResult, type ForkFromResponse, type GenerateWorkflowScriptOptions, type GenerateWorkflowScriptResult, GitRunError, type GlobalConfigReadResult, IDENTITY_CUSTOM_TYPE, INDEX_FILENAME, type InFlightListener, type InFlightSnapshot, type InvalidAgentRefMessageOptions, type LauncherDeps, type LifecycleDeps, type LintFinding, type LintResult, type ListFormatOptions, type ListHandlerInput, type ListHandlerResult, type ListResponse, type ListWorktreePorcelainOptions, MAX_LIST_LIMIT, MAX_RETAINED_DONE_RUNS, MAX_TIMER_DELAY_MS, type MessageHandlerInput, type MessageHandlerResult, type MessageResponse, ModelConfigService, type ModelConfigServiceInit, type ModelEntry, ModelInfo, type ModelListFormatOptions, type ModelReasoningInfo, NOTIFY_CONTRACT, type NormalizeWorkflowRefOptions, type NormalizedArgs, type NormalizedWorkflowRef, type NotifyDomainPorts, type NotifyLedgerHost, type PatchBaselineAnchor, type PruneStateDeps, type QueuePolicy, RecordStore, type RecordStorePi, type RecoverCrashedRunsHooks, type RecoverCrashedRunsResult, type ResourceKind, type ResourceSource, ResurrectDeniedError, type RunSnapshot, type RunSpec, type RunState, RunStatus, type RunStore, SAFE_ID_RE, SNAPSHOT_VERSION, SUBAGENT_RECORD_CUSTOM_TYPE, type ScanConfig, type StartHandlerInput, type StartHandlerResult, type StatusFilter, type SubagentIdentityData, type SubagentListItem, type SubagentRecord, type SubagentRecordEntryData, SubagentService, type SubagentServiceInit, SubagentStream, type SubagentToolResult, SubprocessAgentRunner, THINKING_ORDER, Trace, WORKFLOW_REF_EXT, WORKFLOW_REF_RESERVED_NAMES, type WorkerHandlers, type WorkerHost, WorkerHostImpl, WorkerLogEntry, type WorkflowDirOptions, type WorkflowEntry, type WorkflowMeta, type WorkflowRefInvalidReason, WorkflowRun, type WorkflowRunMeta, type WorkflowRunResult, type WorkflowRunSummary, type WorkflowScanConfig, WorkflowScript, type WorkflowScriptRegistry, WorkflowScriptRegistryImpl, type WorkflowSource, WorktreeManager, type WorktreePatchResult, ZCODE_FALLBACK_DEFAULT_MODEL, type ZcodeEngineDeps, abortRun, argKeysFromMeta, assertSafeId, atomicTmpPathFor, bestEffort, bindNotifyLedgerHost, boundedPrettySerialize, buildEngineModelsPromptAppend, buildSubagentEngineSection, cancelHandler, cleanupStaleTmpFiles, cleanupWorktree, clearSkillPathCache, closeHandler, closeOutInFlightCalls, collectWorktreePatch, computeElapsedSeconds, configureNotifyDomain, countAllToolCalls, createConcurrencyPool, createSubagentService, createUiRequestHandlerForMode, createZcodeEngine, deleteWorkflow, deriveOutcome, discoverAgents, discoverResources, discoverWorkflows, displayAgentName, endedMessageGuard, escapeXml, evictDoneRunsBeyondCap, executeNestedWorkflow, finalizeRun, findFlattenedArgKeys, findWorkspaceRoot, forkFromHandler, formatAgentList, formatModelList, formatWorkflowList, fromRunSnapshot, generateWorkflowScript, getAllToolCalls, getBoundNotifyLedger, getCachedFileContent, getCachedParsed, getInFlightSnapshot, getModelConfigService, getOrCreateChannelRegistry, getSubagentService, getWorkflow, getWorkflowByPath, gitRun, hasApiKey, invalidAgentRefMessage, invalidateCache, isProcessAlive, isResumable, isSafeId, isScriptRunning, isTreeDirty, killAllSpawnedChildren, killRecordChildWithEscalation, lintScript, listHandler, listStaleTmpFiles, listWorktreePorcelain, loadWorkflowScriptByPath, loadWorkflows, makeSerializeFailedResult, mapExternalState, maxTurnsToWatchdogMs, maybeCleanupExpiredSessionFiles, messageHandler, normalizeArgsByMeta, normalizeRef, normalizeWorkflowRef, notifyGateAllowsDelivery, parseAgentProfile, parseAtomicTmpPath, parseEngineHandle, parseResourceMeta, parseResourceMetaDetailed, postBudgetUpdate, projectLiveProgress, projectOutcome, pruneStateFilesBeyondCap, readGlobalConfig, readSubagentHistoryMessages, recordToListItem, recoverCrashedRuns, registerNativeSessionReader, registerZcodeEngine, renderXmlSection, resetRebuildFailureInjectionForTest, routeEngine, runAndWait, runSummary, runWorkflow, saveWorkflow, scheduleTimeBudget, setEngineDiscoveryRescanOptions, setInFlightListener, setModelConfigService, setSubagentService, sortByCodepoint, splitZcodeModelRef, startHandler, summarizeDescription, syncEnginesFile, terminateRunningRuns, toRunSnapshot, toSubagentRecordEntry, wrapForkFromPrompt, writeAtomicFile, writeAtomicFileSync };