@x-otto/runtime 0.0.1-alpha.1 → 0.0.1-alpha.11

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.
package/dist/index.d.ts CHANGED
@@ -1,15 +1,220 @@
1
- import { CostSummary, Message, Model, ModelResolution, ProviderRegistry, ThinkingLevel } from "@x-otto/ai";
1
+ import { CostSummary, Message, Model, ModelResolution, ProviderRegistry, StreamFunction, ThinkingLevel } from "@x-otto/ai";
2
2
  import { HookRegistry, HookSpec, SystemPromptTransformInput, ToolExecuteAfterInput } from "@x-otto/hooks";
3
3
  import { EventBus, Events, Logger, TypedEventEmitter } from "@x-otto/shared";
4
4
  import { AppendLog, PanelStateDraftEntry, PanelStateEditedFile, PanelStatePersistence, PanelStateSnapshot, PanelStateSubagentEntry, PanelStateTodoItem, PanelStateTurnSummary, PasteStatePersistence, PasteStateSnapshot } from "@x-otto/persistence";
5
- import { InMemorySession, LeaseInspection, Session, SessionEntry, SessionPersistence, SessionSnapshot } from "@x-otto/session";
5
+ import { EVENT_DOMAIN, EventDomain, SYSTEM_PROMPT_SECTIONS, SystemPromptSectionSpec, applySectionBudget, getEventDomain } from "@x-otto/interchange";
6
+ import * as _$_x_otto_session0 from "@x-otto/session";
7
+ import { InMemorySession, LeaseInspection, Session, SessionEntry, SessionPanelState, SessionPersistence, SessionSnapshot } from "@x-otto/session";
6
8
  import { OttoProcessInfo } from "@x-otto/env";
7
- import { Agent, AgentEvent, AgentMessage, AgentSessionEvent, AgentSessionEventMap, AgentSessionEventType, AgentSessionSubscriber, AgentTool, ApprovalRisk, AskUserQuestion, ClockPort, DescribeImagesPort, GrillAnswer, GrillQuestion, GrillRequest, MemoryPort, MemoryPort as MemoryPort$1, StreamFunction, StreamRetryConfig, ToolHookExecutor, ToolResult, TraceEvent, TraceRecorder, applyMemoryTransform } from "@x-otto/agent";
9
+ import { Agent, AgentEvent, AgentMessage, AgentSessionEvent, AgentSessionEventMap, AgentSessionEventType, AgentSessionSubscriber, AgentTool, ApprovalRisk, AskUserQuestion, ClockPort, DescribeImagesPort, GrillAnswer, GrillQuestion, GrillRequest, IdPort, MemoryPort, MemoryPort as MemoryPort$1, RandomPort, StreamFunction as StreamFunction$1, StreamRetryConfig, ToolHookExecutor, ToolResult, TraceEvent, TraceRecorder, applyMemoryTransform } from "@x-otto/agent";
8
10
  import * as _$_x_otto_session_contract0 from "@x-otto/session-contract";
9
11
  import { ResidencyBudgetConfig, TurnRecord, TurnRecord as TurnRecord$1 } from "@x-otto/session-contract";
10
12
  import { ChildProcess, SpawnOptions } from "node:child_process";
11
13
  import { Devtools } from "@x-otto/devtools";
12
14
 
15
+ //#region src/plane/host-registry.d.ts
16
+ type RegistrationPlane = 'host' | 'preset';
17
+ /** 注册条目公共字段(平面无关)。 */
18
+ interface BasePlaneEntry {
19
+ /** 条目唯一 id(格内唯一;插件注册约定 `plugin:<pluginId>` / `waterfall:<timing>`)。 */
20
+ id: string;
21
+ /** 贡献类型标签('plugin' 身份声明 / 'waterfall' 贡献 / 未来轴)。 */
22
+ kind: string;
23
+ /** 贡献载荷(注册者语义自定)。 */
24
+ value: unknown;
25
+ /** 来源插件 id(非插件来源的注册可省略)。 */
26
+ pluginId?: string;
27
+ /**
28
+ * R6':来源插件 manifest 版本(信任绑定版本——插件更新后需重新评估白名单,
29
+ * trust 不跨版本自动存续)。非插件来源注册可省略。
30
+ */
31
+ pluginVersion?: string;
32
+ }
33
+ /**
34
+ * host 平面条目草稿(进程级单例)。`plane: 'host'` + `scope: 'process'` 为编译期
35
+ * 字面量——只能传给 HostRegistry.register(),传进任何 PRESET 格注册 API 即编译错误。
36
+ */
37
+ type HostPlaneDraft = BasePlaneEntry & {
38
+ readonly plane: 'host';
39
+ readonly scope: 'process';
40
+ };
41
+ /** PRESET workspace 格条目草稿——`scope: 'workspace'` 只能传给 workspace 格的注册 API。 */
42
+ type WorkspacePresetDraft = BasePlaneEntry & {
43
+ readonly plane: 'preset';
44
+ readonly scope: 'workspace';
45
+ };
46
+ /** PRESET 会话格条目草稿——`scope: 'session'` 只能传给会话格的注册 API。 */
47
+ type SessionPresetDraft = BasePlaneEntry & {
48
+ readonly plane: 'preset';
49
+ readonly scope: 'session';
50
+ };
51
+ /** 已盖章的 host 平面条目(无附加键)。 */
52
+ interface HostPlaneEntry extends HostPlaneDraft {}
53
+ /** 已盖章的 PRESET workspace 条目——workspaceKey 由格在注册时盖章(调用方不可自报)。 */
54
+ interface WorkspacePresetEntry extends WorkspacePresetDraft {
55
+ /** 所属 workspace key(`ws_<uuid>`)。由格盖章,非调用方自报。 */
56
+ workspaceKey: string;
57
+ }
58
+ /** 已盖章的 PRESET 会话条目——sessionId 由格在注册时盖章(调用方不可自报)。 */
59
+ interface SessionPresetEntry extends SessionPresetDraft {
60
+ /** 所属会话 id。由格盖章,非调用方自报。 */
61
+ sessionId: string;
62
+ }
63
+ type PlaneEntry = HostPlaneEntry | WorkspacePresetEntry | SessionPresetEntry;
64
+ /** 发射端上下文(host 单点发射携带)。缺省字段 = 不匹配对应平面。 */
65
+ interface EmitContext {
66
+ /** 发射时已知的会话 id——会话格条目仅当匹配才可达。 */
67
+ sessionId?: string;
68
+ /** 发射时已知的 workspace key——workspace 格条目仅当匹配才可达。 */
69
+ workspaceKey?: string;
70
+ }
71
+ /** R6' 版本漂移条目(插件更新后残留的旧版本注册,需重新评估)。 */
72
+ interface StalePlaneEntry {
73
+ entry: PlaneEntry;
74
+ /** 注册时快照的版本。 */
75
+ registeredVersion: string | undefined;
76
+ /** 插件当前 manifest 版本(可能缺省——manifest 无版本字段)。 */
77
+ currentVersion: string | undefined;
78
+ }
79
+ /** PRESET workspace 格(注册 + 列表 + 清理)。 */
80
+ declare class WorkspacePresetGrid {
81
+ /** workspace key(`ws_<uuid>`)——构造时由 HostRegistry.workspace() 传入。 */
82
+ readonly key: string;
83
+ private readonly entries;
84
+ /** 已 dispose(格关闭后拒绝新注册,fail-closed)。 */
85
+ private disposed;
86
+ constructor(/** workspace key(`ws_<uuid>`)——构造时由 HostRegistry.workspace() 传入。 */
87
+
88
+ key: string);
89
+ /**
90
+ * 注册一条 workspace 级 PRESET 贡献。同 id 重复注册 = 整体替换(R6':新评估覆盖
91
+ * 旧评估,不继承——插件更新后经 unregister→register 全量替换)。
92
+ */
93
+ register(draft: WorkspacePresetDraft): WorkspacePresetEntry;
94
+ unregister(id: string): boolean;
95
+ /** 撤销某插件的全部条目(pluginId 匹配)。返回撤销条数。 */
96
+ unregisterByPlugin(pluginId: string): number;
97
+ list(): readonly WorkspacePresetEntry[];
98
+ /** workspace 关闭 → 清空本格。幂等。 */
99
+ dispose(): void;
100
+ }
101
+ /** PRESET 会话格(注册 + 列表 + 清理)。 */
102
+ declare class SessionPresetGrid {
103
+ readonly sessionId: string;
104
+ private readonly entries;
105
+ private disposed;
106
+ constructor(sessionId: string);
107
+ register(draft: SessionPresetDraft): SessionPresetEntry;
108
+ unregister(id: string): boolean;
109
+ unregisterByPlugin(pluginId: string): number;
110
+ list(): readonly SessionPresetEntry[];
111
+ /** 会话 dispose → 清空本格。幂等。 */
112
+ dispose(): void;
113
+ }
114
+ /** PRESET workspace 作用域句柄——`workspace.preset.register()`(RFC-327 D2 API 形状)。 */
115
+ declare class WorkspacePresetScope {
116
+ readonly workspaceKey: string;
117
+ /**
118
+ * PRESET workspace 格。注册 API 形状 = `workspace.preset.register(draft)`。
119
+ * 语义同 WorkspacePresetGrid(注册/撤销/列表/清理),随 workspace 生命周期。
120
+ */
121
+ readonly preset: WorkspacePresetGrid;
122
+ constructor(workspaceKey: string);
123
+ /** workspace 关闭 → 清 workspace 格(R2':HOST 桶不受影响)。幂等。 */
124
+ dispose(): void;
125
+ }
126
+ /** PRESET 会话作用域句柄——`session.preset.register()`(RFC-327 D2 API 形状)。 */
127
+ declare class SessionPresetScope {
128
+ readonly sessionId: string;
129
+ /**
130
+ * PRESET 会话格。注册 API 形状 = `session.preset.register(draft)`。
131
+ * 语义同 SessionPresetGrid(注册/撤销/列表/清理),随会话生命周期。
132
+ */
133
+ readonly preset: SessionPresetGrid;
134
+ constructor(sessionId: string);
135
+ /** 会话 dispose → 清会话格(R2':HOST 桶与 workspace 格不受影响)。幂等。 */
136
+ dispose(): void;
137
+ }
138
+ /**
139
+ * HostRegistry —— 进程级单例注册表(RFC-327 修订 D2,T-327-M2-01/02)。
140
+ *
141
+ * 三桶:host 平面(进程级单例)+ PRESET workspace 格(按 workspaceKey)+ PRESET
142
+ * 会话格(按 sessionId)。生命周期=进程;dispose() 清空全部。
143
+ *
144
+ * 编译期跨平面拒绝(R2'):register 只收 HostPlaneDraft(plane:'host'),
145
+ * workspace/session 格的注册 API 只收各自字面量草稿——跨平面注册在类型层即失败。
146
+ */
147
+ declare class HostRegistry {
148
+ private readonly hostEntries;
149
+ private readonly workspaceScopes;
150
+ private readonly sessionScopes;
151
+ private disposed;
152
+ private ensureNotDisposed;
153
+ /** host 平面注册(进程级单例)。`host.register()`(RFC-327 D2 API 形状)。 */
154
+ register(draft: HostPlaneDraft): HostPlaneEntry;
155
+ unregister(id: string): boolean;
156
+ /** 撤销某插件在 host 平面的全部条目。返回撤销条数。 */
157
+ unregisterByPlugin(pluginId: string): number;
158
+ /** host 平面条目(进程级单例,不随任何会话/workspace 清理)。 */
159
+ list(): readonly HostPlaneEntry[];
160
+ /**
161
+ * PRESET workspace 格句柄(ensure 语义——workspace 挂载/首次注册时建格,幂等)。
162
+ * 注册 API 形状 = `workspace.preset.register(draft)`。
163
+ */
164
+ workspace(workspaceKey: string): WorkspacePresetScope;
165
+ /**
166
+ * PRESET 会话格句柄(ensure 语义——会话创建时派生会话格,幂等)。
167
+ * 注册 API 形状 = `session.preset.register(draft)`。
168
+ */
169
+ session(sessionId: string): SessionPresetScope;
170
+ /** workspace 关闭 → 清 workspace 格(不重建;不存在即 no-op)。 */
171
+ disposeWorkspaceGrid(workspaceKey: string): void;
172
+ /**
173
+ * RFC-327 修订 D2(M2,应用点 B 建议修订):把 `fromKey` 格的条目迁移到 `toKey` 格。
174
+ *
175
+ * 场景:pendingWorkspaceInit(交互态首次打开待信任确认)路径下插件 reload 先于
176
+ * workspaceRef 就位,PMM 用 DEFAULT_WORKSPACE_KEY('default')兜底注册;真实 key
177
+ * 就位后(completeWorkspaceDependentStartup)必须把 default 格条目迁到真实格,
178
+ * 否则两格并存、发射端按真实 key 路由查不到插件条目。
179
+ *
180
+ * 语义:条目按 id 迁移(整体搬,不重新盖章之外不改变语义);fromKey 格迁移后清空
181
+ * 且 dispose(fail-closed,防后续误注册到已废弃格)。fromKey 不存在 / toKey===fromKey
182
+ * 均为 no-op。只迁移 workspace 级条目(host/会话格不涉及)。
183
+ */
184
+ migrateWorkspaceGrid(fromKey: string, toKey: string): void;
185
+ /** 会话 dispose → 清会话格(不重建;不存在即 no-op)。 */
186
+ disposeSessionGrid(sessionId: string): void;
187
+ /** 撤销某插件在全部平面(host + 全部 workspace 格 + 全部会话格)的条目。返回撤销条数。 */
188
+ unregisterByPluginEverywhere(pluginId: string): number;
189
+ /**
190
+ * R6' 版本漂移检出:返回 pluginId 名下所有注册版本 ≠ 当前版本的条目。
191
+ * 插件更新后,正常 reload 走 unregister→register 全量替换;本方法兜底检出
192
+ * 「旧版本条目残留」(脏插件/撤销失败等异常路径)——调用方应据此重新评估
193
+ * 白名单(trust 不跨版本自动存续)。`currentVersion` 为 undefined(manifest
194
+ * 无版本)时,无版本条目视为一致、带版本条目视为漂移(需重新评估)。
195
+ */
196
+ revalidatePlugin(pluginId: string, currentVersion: string | undefined): StalePlaneEntry[];
197
+ /**
198
+ * 发射端路由(host 单点发射携带上下文)——按上下文过滤 PRESET 注册:
199
+ * host 平面条目恒达(进程级单例);
200
+ * workspace 格条目仅当 ctx.workspaceKey 匹配;
201
+ * 会话格条目仅当 ctx.sessionId 匹配(s1 事件不达 s2 的 PRESET)。
202
+ */
203
+ resolveForEmit(ctx: EmitContext): readonly PlaneEntry[];
204
+ /** 全部平面的全部条目(诊断/审计用)。 */
205
+ listAll(): readonly PlaneEntry[];
206
+ /** 进程生命周期结束 → 清空全部平面(host + workspace 格 + 会话格)。幂等。 */
207
+ dispose(): void;
208
+ }
209
+ //#endregion
210
+ //#region src/session/semantic-reviewer.d.ts
211
+ type SemanticReviewerVerdict = 'allow' | 'deny' | 'abstain';
212
+ type SemanticReviewer = (toolName: string, args: Record<string, unknown>, description: string, risk?: ApprovalRisk) => Promise<SemanticReviewerVerdict>;
213
+ /** 升级审批哨兵(与 T2 的 escalation reason 同源;此处不 import agent 常量避免反向依赖,测试锁定同值)。 */
214
+ declare const ESCALATION_MARKER = "[sandbox-escalation]";
215
+ /** 仅「升级审批」请求可进语义裁决(普通 ask 仍走用户审批——范围克制,RFC §2 D3)。 */
216
+ declare function isEscalationApproval(description: string): boolean;
217
+ //#endregion
13
218
  //#region src/context-source.d.ts
14
219
  interface ContextSource {
15
220
  /** 本源的唯一标识。重复注册→覆盖前一次同 id 源。 */
@@ -133,6 +338,11 @@ interface MemorySubsystem extends MemoryPort$1, ContextInjectionPort, MemoryDelt
133
338
  * 经过宿主完整解析后的 session 配置。
134
339
  * 所有业务字段均已确定,无需再做 merge。
135
340
  * Manager 层只接受此类型来创建 session,不持有业务默认值。
341
+ *
342
+ * RFC-394 D2/R1:这是配置链的**唯一真源**。AgentSessionOptions 从本类型派生
343
+ * (`SessionConfigSubset`),AgentConfig 从 Options 映射,PersistedSessionConfig
344
+ * 从字段元数据(`PERSISTABLE_CONFIG_FIELDS`)生成——新增字段只改此处,编译器
345
+ * 自动传播,消灭手抄映射清单(此前 4 次漏传事故的根因)。
136
346
  */
137
347
  interface ResolvedSessionConfig {
138
348
  model: Model;
@@ -219,7 +429,27 @@ interface ResolvedSessionConfig {
219
429
  */
220
430
  describeImages?: DescribeImagesPort;
221
431
  }
222
- interface AgentSessionOptions {
432
+ /**
433
+ * RFC-394 D2/R1:从 ResolvedSessionConfig 派生到 AgentSessionOptions 的会话配置子集。
434
+ *
435
+ * 这 27 个字段是 ResolvedSessionConfig 中**同时出现在 AgentSessionOptions 里**的字段——
436
+ * 此前逐字段手抄(buildAgentSession:461+),现用 Pick 派生,增删只改 ResolvedSessionConfig
437
+ * 一处,编译器自动传播到 Options。AgentSessionOptions = SessionConfigSubset & InfrastructurePorts。
438
+ *
439
+ * 不在此子集的字段(permissionAlwaysAllow / availableModels)是会话级权限/模型集合,
440
+ * 不传给 AgentSession 构造(由 SessionPool 直接读写 configs Map)。
441
+ */
442
+ type SessionConfigSubset = Pick<ResolvedSessionConfig, 'model' | 'titleModel' | 'modelResolution' | 'agentName' | 'systemPrompt' | 'systemTail' | 'tools' | 'thinkingLevel' | 'maxToolTurns' | 'maxToolTurnExtensions' | 'promptOutputTokenBudget' | 'promptWallClockBudgetMs' | 'stallDetection' | 'sessionCostBudgetUSD' | 'todoContinuation' | 'promptRefresh' | 'workspaceDir' | 'workspaceId' | 'transient' | 'depth' | 'interactive' | 'listable' | 'retryAccelerate' | 'streamRetry' | 'networkDisconnectRetry' | 'lifecycleAsyncTimeoutMs' | 'describeImages'>;
443
+ /**
444
+ * RFC-394 D2/R1:AgentSessionOptions = SessionConfigSubset(从 ResolvedSessionConfig 派生)
445
+ * + InfrastructurePorts(运行时注入的基础设施端口)。
446
+ *
447
+ * 此前 AgentSessionOptions 是 42 字段的独立 interface,27 个字段与 ResolvedSessionConfig
448
+ * 逐字段手抄重复——现用 Pick 派生消灭手抄。新增 ResolvedSessionConfig 字段时,
449
+ * SessionConfigSubset 自动跟随(如需传入 AgentSession),编译器报错提示遗漏。
450
+ */
451
+ interface AgentSessionOptions extends SessionConfigSubset {
452
+ /** 会话 id(仅 createSession 路径可选注入)。 */
223
453
  id?: string;
224
454
  /**
225
455
  * RFC-324 D1/R5:turn-gated 压缩的超时兜底(毫秒)惰性读数。
@@ -229,86 +459,29 @@ interface AgentSessionOptions {
229
459
  * 未注入时回退 `SESSION_COMPACTION_TIMEOUT_MS`(其本身已含 env 覆盖)。
230
460
  */
231
461
  getCompactionTimeoutMs?: () => number | undefined;
232
- model: Model;
233
- /** 模型决策溯源(为何选中此 model)——透传给观测层供 inspector 显示。 */
234
- modelResolution?: ModelResolution;
235
- agentName?: string;
236
- systemPrompt?: string;
237
- /** 会话恒定的 volatile system 尾段种子(append 子代理角色块等);透传至 PromptParams.systemTail。 */
238
- systemTail?: string[];
239
- tools?: AgentTool[];
240
- thinkingLevel?: ThinkingLevel;
241
- maxToolTurns?: number;
242
- maxToolTurnExtensions?: number;
243
- /** RFC-104 D3:per-prompt output-token 预算(AgentSessionOptions 透传面)。 */
244
- promptOutputTokenBudget?: number;
245
- /** RFC-104 D3:per-prompt 墙钟预算(ms)。 */
246
- promptWallClockBudgetMs?: number;
247
462
  /** RFC-104 D2 缺省:顶层默认开、子 agent 默认关;OTTO_BUDGET_STEER_WRAPUP=1/0 覆盖。 */
248
463
  budgetSteerWrapUp?: boolean;
249
- /**
250
- * RFC-203/RFC-204 D4:早期停滞检测配置透传(第三层,此前 AgentConfig/agent.ts 两层
251
- * 断链已在 RFC-204 M2 修复)。缺省启用(走 ENGINE_DEFAULTS);`false` 显式关闭。
252
- */
253
- stallDetection?: {
254
- windowTurns?: number;
255
- repeatThreshold?: number;
256
- } | false;
257
- /**
258
- * RFC-204 D1/D2:会话累计成本软预算(美元)。超过阈值时一次性 emit
259
- * `session.cost-budget-exceeded`(不阻断)。缺省 = 不启用预算检测。
260
- */
261
- sessionCostBudgetUSD?: number;
262
- /**
263
- * RFC-094:todo 停机闭环硬闸——prompt 结束后若 todoList 仍有未完成项,自动续跑(cap 轮内)。
264
- * 缺省从 OTTO_TODO_CONTINUE / OTTO_TODO_CONTINUE_MAX 解析(默认开、cap=3);depth>0 恒不启闸。
265
- */
266
- todoContinuation?: {
267
- enabled: boolean;
268
- max: number;
269
- };
270
- workspaceDir?: string;
271
- /** workspace 逻辑标识(`workspaceRef.id`),按 workspace 过滤会话列表时使用。 */
272
- workspaceId?: string;
273
- transient?: boolean;
274
464
  hookRegistry: HookRegistry;
275
- stream: StreamFunction;
276
- /**
277
- * RFC-078 M134:标题生成用的便宜模型(app 层经 RFC-061 `category:'search'` + usability gate 解析)。
278
- * 缺省 → 不启用 LLM 标题生成(仅保留 derived 启发式占位)。禁用对话模型/claude-OAuth(R3)。
279
- */
280
- titleModel?: Model;
465
+ stream: StreamFunction$1;
281
466
  logger: Logger;
282
467
  devtools?: Devtools;
283
- promptRefresh?: PromptRefresh;
284
468
  memory?: MemoryPort;
285
469
  recorder?: TraceRecorder;
286
470
  /** RFC-145 D3:nondet batch 冲洗回调(透传给 Agent → work-loop)。缺省 = 不聚合。 */
287
471
  nondetFlush?: () => void;
288
472
  clock?: ClockPort;
289
- depth?: number;
290
- interactive?: boolean;
291
473
  /**
292
- * 是否在 `/resume` 列表中可见(缺省 = 可见)。`false` 用于 headless 一次性内部会话
293
- * (`otto --print/--json`)——照常落盘(sessionId/trace 有外部契约消费方),但不进列表。
294
- * 详见 `PersistedSessionConfig.listable`。
474
+ * RFC-371 M-A:父任务锚点(任务树语义)。orchestrator 委派子会话时注入
475
+ * `RunSessionOptions.parentTaskId`,透传 Agent → ToolExecutor → 工具上下文 →
476
+ * task_delegate/agent_call → orchestrator per-parent fan-out 闸。undefined = root 层。
295
477
  */
296
- listable?: boolean;
297
- retryAccelerate?: {
298
- skip: boolean;
299
- };
300
- /** RFC-230:流式重试策略投影,见 `ResolvedSessionConfig.streamRetry` 同名字段注释。 */
301
- streamRetry?: StreamRetryConfig;
302
- /** RFC-230:网络断连重试策略投影,见 `ResolvedSessionConfig.networkDisconnectRetry` 同名字段注释。 */
303
- networkDisconnectRetry?: StreamRetryConfig;
304
- /** RFC-366:lifecycleAsync 挂死兜底超时(毫秒),见 `ResolvedSessionConfig.lifecycleAsyncTimeoutMs` 同名字段注释。 */
305
- lifecycleAsyncTimeoutMs?: number;
478
+ parentTaskId?: string;
306
479
  /**
307
- * RFC-181 D3/M3:图像委托描述端口(宿主 VisionDelegateService 实现,coding 层在会话
308
- * 装配时注入)。缺省 = 主模型不支持视觉时,image block 降级为占位文本(见
309
- * @x-otto/agent vision-gate.ts 完整设计说明)。
480
+ * RFC-380 T4:LLM 语义审批层(opt-in)。仅升级审批(sandbox-denied escalate ask)
481
+ * 且配置时生效;allow/deny 自动裁决(宿主侧需消费 approval.required
482
+ * semanticVerdict 记命令 bypass),abstain/未配置 → 既有用户弹窗流程。
310
483
  */
311
- describeImages?: DescribeImagesPort;
484
+ semanticReviewer?: SemanticReviewer;
312
485
  }
313
486
  interface AgentSessionInfo {
314
487
  id: string;
@@ -372,7 +545,7 @@ declare function resolveTodoContinuationConfig(env: {
372
545
  max: number;
373
546
  };
374
547
  declare class AgentSession extends EventBus<AgentSessionEventMap> {
375
- readonly session: Session<AgentMessage>;
548
+ readonly session: Session<AgentMessage> & SessionPanelState;
376
549
  readonly workspaceDir: string;
377
550
  readonly workspaceId?: string;
378
551
  private agent;
@@ -403,15 +576,52 @@ declare class AgentSession extends EventBus<AgentSessionEventMap> {
403
576
  }> | undefined;
404
577
  /** RFC-324 D1/R5:turn-gate 超时兜底的惰性读数(见 AgentSessionOptions 同名字段)。 */
405
578
  private readonly getCompactionTimeoutMs?;
406
- model: Model;
579
+ private _model;
407
580
  /** 模型决策溯源(哪层选中 + 拒了谁)——供 task-runner 透传观测层。 */
408
- modelResolution?: ModelResolution;
409
- tools: AgentTool[];
410
- systemPrompt: string;
581
+ private _modelResolution?;
582
+ private _tools;
583
+ private _systemPrompt;
411
584
  /** 会话恒定的 volatile system 尾段种子(append 子代理角色块等);透传至每轮 PromptParams.systemTail。 */
412
- systemTail?: string[];
585
+ private _systemTail?;
586
+ private _maxToolTurns;
587
+ private _maxToolTurnExtensions;
588
+ /** RFC-104 D3:per-prompt 预算(fork 拷贝面;depth>0 构造时已被剥除)。 */
589
+ private _promptOutputTokenBudget?;
590
+ private _promptWallClockBudgetMs?;
591
+ private _thinkingLevel;
592
+ private _promptRefresh?;
593
+ /** 公开只读——模型。写操作走 applyModel()。 */
594
+ get model(): Model;
595
+ get modelResolution(): ModelResolution | undefined;
596
+ get tools(): AgentTool[];
597
+ get systemPrompt(): string;
598
+ get systemTail(): string[] | undefined;
599
+ get maxToolTurns(): number;
600
+ get maxToolTurnExtensions(): number;
601
+ get promptOutputTokenBudget(): number | undefined;
602
+ get promptWallClockBudgetMs(): number | undefined;
603
+ get thinkingLevel(): ThinkingLevel;
604
+ get promptRefresh(): PromptRefresh | undefined;
605
+ /** RFC-394 D4/R4:显式 apply 方法——外部写运行时状态的唯一入口。 */
606
+ applyModel(model: Model, resolution?: ModelResolution): void;
607
+ applyThinkingLevel(level: ThinkingLevel): void;
608
+ applyTools(tools: AgentTool[]): void;
609
+ applySystemPrompt(prompt: string): void;
610
+ applyBudgets(opts: {
611
+ maxToolTurns?: number;
612
+ maxToolTurnExtensions?: number;
613
+ promptOutputTokenBudget?: number;
614
+ promptWallClockBudgetMs?: number;
615
+ }): void;
413
616
  agentName: string;
414
617
  depth: number;
618
+ /**
619
+ * RFC-371 M-A:父任务锚点(透传面)。orchestrator 委派子会话时注入
620
+ * `runOptions.parentTaskId`,Agent 构造经 `AgentConfig.parentTaskId` 透传给
621
+ * ToolExecutor——task_delegate/agent_call 的 per-parent fan-out 闸依赖此字段。
622
+ * undefined = root 层(顶层用户会话),不受 fan-out 闸约束。
623
+ */
624
+ readonly parentTaskId?: string;
415
625
  /**
416
626
  * orchestrator ephemeral 委派子会话标记 —— 契约上不落盘(见
417
627
  * `PersistenceSync.isTransient`)、不进 `/resume` 列表。构造期固定,运行中不变。
@@ -425,13 +635,6 @@ declare class AgentSession extends EventBus<AgentSessionEventMap> {
425
635
  * 置 false —— 照常落盘但不进列表。详见 `PersistedSessionConfig.listable`。
426
636
  */
427
637
  readonly listable: boolean;
428
- maxToolTurns: number;
429
- maxToolTurnExtensions: number;
430
- /** RFC-104 D3:per-prompt 预算(fork 拷贝面;depth>0 构造时已被剥除)。 */
431
- promptOutputTokenBudget?: number;
432
- promptWallClockBudgetMs?: number;
433
- thinkingLevel: ThinkingLevel;
434
- promptRefresh?: PromptRefresh;
435
638
  private logger;
436
639
  private hookRegistry;
437
640
  private devtools?;
@@ -441,7 +644,9 @@ declare class AgentSession extends EventBus<AgentSessionEventMap> {
441
644
  /** RFC-145 D3:nondet batch 冲洗回调。 */
442
645
  private readonly nondetFlush?;
443
646
  private readonly clock?;
444
- /** RFC-337 D2:steer 消息 id 单调计数器(会话内唯一,供 ESC 撤回按序移除)。 */
647
+ /** RFC-337 D2:steer 消息 id 单调计数器(会话内唯一,供 ESC 撤回按序移除)。
648
+ * RFC-372 S7:改为 `{ value: number }` 对象——steer 逻辑提取到 agent-session-steer.ts
649
+ * 后需经引用传递(number 是值类型,函数内 ++ 不会回传)。 */
445
650
  private steerSeq;
446
651
  private readonly persistence;
447
652
  /**
@@ -491,6 +696,8 @@ declare class AgentSession extends EventBus<AgentSessionEventMap> {
491
696
  * task-runner 在跑 task_delegate/fork 子会话时据此把运行态接入 globalAgentObservability。
492
697
  */
493
698
  get coreAgent(): Agent;
699
+ /** RFC-372 S7:steer 集群的依赖面(供 agent-session-steer.ts 消费)。 */
700
+ private get steerDeps();
494
701
  get isExecuting(): boolean;
495
702
  /**
496
703
  * prompt 全生命周期忙判定。isExecuting 只覆盖 streaming/tool_executing,
@@ -509,7 +716,7 @@ declare class AgentSession extends EventBus<AgentSessionEventMap> {
509
716
  systemPromptLength: number;
510
717
  tools: string[];
511
718
  };
512
- constructor(session: Session<AgentMessage>, options: AgentSessionOptions);
719
+ constructor(session: Session<AgentMessage> & SessionPanelState, options: AgentSessionOptions);
513
720
  /**
514
721
  * 由 SessionPool / 宿主类在 session.created hook 执行完毕后调用。
515
722
  * 此时 EventBridge 已完成订阅,session.start 事件可被正确接收。
@@ -559,8 +766,13 @@ declare class AgentSession extends EventBus<AgentSessionEventMap> {
559
766
  * hadToolActivity:本轮是否有真实工具调用产出(assistant tool_call 消息),供闸门
560
767
  * 无进展判定区分"零动作卡死"(放行)与"有产出但没同步 todo 状态"(定向续跑一次)。
561
768
  * images(RFC-111 D4b):仅首轮(外层 `prompt()` 调用)传入,todo 续跑等内部触发轮
562
- * 不重复携带图片——避免同一张图片在续跑轮里被重复注入上下文。 */
769
+ * 不重复携带图片——避免同一张图片在续跑轮里被重复注入上下文。
770
+ *
771
+ * RFC-372 M3:实现提取到 agent-session-prompt-round.ts(runPromptRound 函数),
772
+ * 此处经 promptRoundHost getter 桥接全部依赖。纯结构重构,行为零变化。 */
563
773
  private runPromptRound;
774
+ /** RFC-372 M3:runPromptRound 的依赖面(供 agent-session-prompt-round.ts 消费)。 */
775
+ private get promptRoundHost();
564
776
  /**
565
777
  * RFC-337 D2:注入一条 steer 消息并返回其稳定 `id`(供 cli 侧持有以便 ESC 撤回)。
566
778
  *
@@ -571,11 +783,6 @@ declare class AgentSession extends EventBus<AgentSessionEventMap> {
571
783
  steer(text: string): {
572
784
  id: string;
573
785
  };
574
- /**
575
- * RFC-337 D2:按 id 移除一条尚未被模型消费的 steer 消息,返回是否命中。
576
- * 未命中(已被 `drainSteeringQueue` 交付进 LLM 上下文)时返回 `false`——调用方据此退化
577
- * 为「中止整回合」(见 RFC-337 §D3)。转调 `Agent.removeSteer`,只操作内存态队列。
578
- */
579
786
  removeSteer(id: string): boolean;
580
787
  followUp(text: string): void;
581
788
  abort(): void;
@@ -626,6 +833,25 @@ declare class AgentSession extends EventBus<AgentSessionEventMap> {
626
833
  renameSession(title: string): void;
627
834
  /** H5-20:本会话累计用量 + 成本汇总(供 /cost 命令 / TUI 状态栏消费)。 */
628
835
  getCostSummary(): CostSummary;
836
+ /**
837
+ * RFC-390 D2:最近一轮的 uncached input token(RFC-411 修正口径标注)。
838
+ * **口径事实(RFC-411)**:`usage.inputTokens` 是 provider 分桶中的 uncached 部分,
839
+ * **不含 cacheRead/cacheWrite 前缀**(三分桶铁证 packages/ai/src/cache-hit-rate.ts:75
840
+ * totalInput = cacheRead + cacheWrite + inputTokens)——它不是"当前上下文占用"。
841
+ * RFC-390 D2 原注释"total 口径含 cache 前缀"是错误标签,已废止(见 RFC-411 执行摘要)。
842
+ * 供 memory-manager 的 `getRealInputTokens` 惰性端口消费——空响应轮 `record()` 跳过时
843
+ * 值停留在上一轮。undefined = 本会话尚无 usage(首次请求前)。
844
+ * (RealInputPolicy 因该口径低估从"补漏"退化为近乎恒不触发,坐标换算修正 defer 至
845
+ * memory 包独立 RFC——TokenPressurePolicy 真实估算仍是主力防线。)
846
+ */
847
+ getLastInputTokens(): number | undefined;
848
+ /**
849
+ * RFC-434 D2:上一轮请求的 **total 口径** input(`cacheRead + cacheWrite + inputTokens`)
850
+ * ——"上下文实际有多大"的 provider 权威读数。供 memory-manager 的 `getRealInputTokens`
851
+ * 端口消费,取代原先错用的 `getLastInputTokens`(uncached-only,见 RFC-411 口径定性)。
852
+ * `undefined` = 本会话尚无 usage。
853
+ */
854
+ getLastTotalInputTokens(): number | undefined;
629
855
  /**
630
856
  * RFC-019 M1:最近一回合的缓存命中率(R1)。`null` = 供应商无缓存 API 或本回合无 usage 数据
631
857
  * ——消费方应据此隐藏该字段,而非展示误导性的 0。供 `/cache-stats`、summary 行消费。
@@ -885,6 +1111,9 @@ declare class PanelStateStore {
885
1111
  /**
886
1112
  * 批量加载——直接返回 `PanelStateSnapshot`,供 persistence-sync 恢复/迁移路径使用。
887
1113
  * 比分别调用 4 次 get 方法更高效(一次 load 而非四次)。
1114
+ *
1115
+ * RFC-393 M4:版本门——读出的 `ver` 不匹配 `PANEL_STATE_VER` 时废弃旧 checkpoint
1116
+ * (返回 null,触发全量重读/迁移兜底)。缺省(旧数据无 ver)= 0,自动废弃。
888
1117
  */
889
1118
  loadAll(sessionId: string): Promise<PanelStateSnapshot | null>;
890
1119
  /**
@@ -935,6 +1164,42 @@ declare class ResidencyGovernor {
935
1164
  }
936
1165
  //#endregion
937
1166
  //#region src/session/session-manager.d.ts
1167
+ /**
1168
+ * RFC-394 D3/R2:后端能力 seam——收敛此前散装旋钮为结构化能力接口。
1169
+ *
1170
+ * 此前 SessionPoolOptions 有 5 个后端能力旋钮(isSessionWriteLocked /
1171
+ * inspectSessionWriteLease / forceReleaseSessionWriteLease / listColdSessionsOverride /
1172
+ * panelStateStore),每个都是"未注入即降级"的独立可选字段。现在收敛为 2 个
1173
+ * 能力接口对象,由持久化后端自声明(复用 supportsArchive 式能力探测惯例)。
1174
+ *
1175
+ * residencyGovernor / getResidencyConfig / planeRegistry 不是后端能力旋钮
1176
+ * (它们是跨模块基础设施注入——内存治理信号源/双平面注册表),保留为显式注入。
1177
+ */
1178
+ /**
1179
+ * 写租约管理能力——合并此前 3 个散装旋钮(isSessionWriteLocked /
1180
+ * inspectSessionWriteLease / forceReleaseSessionWriteLease)。
1181
+ *
1182
+ * 由本地 SQLite 后端注入(SessionWriteLeaseManager 的薄封装);
1183
+ * 远端/内存后端无租约机制,不注入(行为与迁移前一致)。
1184
+ */
1185
+ interface WriteLeaseManager {
1186
+ /** 某会话当前是否被**另一存活进程**持有写租约(无副作用,不 acquire)。 */
1187
+ isLockedByOther(sessionId: string): boolean;
1188
+ /** 写租约只读诊断(持有者 pid/hostname/存活性/mtime 距今)。 */
1189
+ inspect(sessionId: string): _$_x_otto_session0.LeaseInspection;
1190
+ /** 强制释放写租约(跳过 token 实核,仅供用户主动 /session unlock --force)。 */
1191
+ forceRelease(sessionId: string): void;
1192
+ }
1193
+ /**
1194
+ * 冷会话枚举能力——收敛此前 listColdSessionsOverride 散装旋钮。
1195
+ *
1196
+ * 由本地 SQLite 后端注入(SqliteSessionRepository 单次 SQL 查询);
1197
+ * 远端后端不注入,缺省走 listPaginated+load 兜底路径。
1198
+ */
1199
+ interface ColdSessionLister {
1200
+ /** 枚举持久化后端已知但未必已加载进内存的冷会话(本地高效 SQL / 远端兜底分页)。 */
1201
+ listColdSessions(workspaceKey: string): Promise<AgentSessionInfo[]>;
1202
+ }
938
1203
  /**
939
1204
  * Manager 只持有基础设施配置(持久化、网络、日志)。
940
1205
  * 业务配置(model、systemPrompt、tools 等)由宿主(coding/第二宿主)解析后
@@ -975,6 +1240,15 @@ interface SessionPoolOptions {
975
1240
  * 确定性时钟端口。缺省=真实系统时钟(行为不变);replay 由 trace 回灌。
976
1241
  */
977
1242
  clock?: ClockPort;
1243
+ /**
1244
+ * 确定性 id 生成端口(M-2)。缺省=systemId(crypto.randomUUID);replay/测试注入固定序列。
1245
+ * 透传给 InMemorySession 与 fork 产物。
1246
+ */
1247
+ idGen?: IdPort;
1248
+ /**
1249
+ * 确定性随机端口(M-2)。缺省=systemRandom(Math.random);replay 由 trace 回灌。
1250
+ */
1251
+ randomGen?: RandomPort;
978
1252
  /**
979
1253
  * 仅用于 restore / restoreAll 场景。
980
1254
  * 正常 createSession 路径必须传入完整 ResolvedSessionConfig,不使用此字段。
@@ -995,37 +1269,27 @@ interface SessionPoolOptions {
995
1269
  /** RFC-323 M4:驻留预算运行时配置 getter(settings 注入,透传给 PersistenceSync)。 */
996
1270
  getResidencyConfig?: () => _$_x_otto_session_contract0.ResidencyBudgetConfig | undefined;
997
1271
  /**
998
- * RFC-146:本地高效冷会话枚举策略(可选注入)。仅本地 SQLite 后端在
999
- * `session-pool-factory.ts` 构造期注入(内部持有 `SqliteSessionRepository`,
1000
- * 单次 SQL 查询即拿到完整 `AgentSessionInfo[]`,远快于"逐个 HTTP load()");
1001
- * 远端后端(`otto serve` / `remote-persistence-server`)不注入,缺省走
1002
- * `listAllPersistedSessions` 的 `persistence.listPaginated()+load()` 兜底路径。
1003
- * `SessionPool` 只判断"策略是否已注入",不感知底层是什么仓储实现——
1004
- * 消灭对具体仓储类型的依赖(见 RFC-146 §6b「sessionRepoRef 迁移策略」)。
1272
+ * RFC-327 修订 D2(M2):双平面注册边界——进程级 HostRegistry(可选注入)。
1273
+ * 注入后会话在进池(create/fork/restore 三个装池点)时派生 PRESET 会话格,
1274
+ * 出池(remove/idle 驱逐/LRU 驱逐/池 dispose)时清理会话格。未注入 = 不派生
1275
+ * (宿主不接双平面即零影响)。
1005
1276
  */
1006
- listColdSessionsOverride?: (workspaceKey: string) => Promise<AgentSessionInfo[]>;
1277
+ planeRegistry?: HostRegistry;
1007
1278
  /**
1008
- * 只读探测(可选注入):某会话当前是否被**另一存活进程**持有写租约(RFC-159 D3
1009
- * SessionWriteLeaseManager.peekLockedByOther 的薄封装,无副作用)。仅本地 SQLite 后端
1010
- * 注入(`session-pool-factory.ts`);未注入时 `listSessions`/`listAllPersistedSessions`
1011
- * 恒不标注只读(远端/内存后端无租约机制,行为与迁移前一致)。用于 `/resume` 列表
1012
- * 提前展示"切进去会是只读",而非等到真正 save 被拒才事后感知。
1279
+ * RFC-394 D3/R2:后端能力 seam——收敛此前 5 个散装旋钮(listColdSessionsOverride /
1280
+ * isSessionWriteLocked / inspectSessionWriteLease / forceReleaseSessionWriteLease /
1281
+ * panelStateStore 的后端注入面)为 2 个能力接口。由持久化后端自声明注入;
1282
+ * 未注入时各方法走降级路径(行为与迁移前一致)。新后端能力走"能力接口 + 自声明"
1283
+ * 模式,禁止在 SessionPoolOptions 新增散装旋钮(R2)。
1013
1284
  */
1014
- isSessionWriteLocked?: (sessionId: string) => boolean;
1285
+ writeLeaseManager?: WriteLeaseManager;
1286
+ coldSessionLister?: ColdSessionLister;
1015
1287
  /**
1016
- * RFC-171 D4:写租约只读诊断(可选注入)——`SessionWriteLeaseManager.inspect` 的薄封装。
1017
- * 仅本地 SQLite 后端注入。供 `/session unlock` 只读诊断模式消费(持有者 pid/hostname/
1018
- * 存活性/mtime 距今);未注入时恒返回 `{ locked: false }`(远端/内存后端无租约机制)。
1288
+ * RFC-380 T4:LLM 语义审批层(opt-in)——app 级共享,透传给每个 AgentSession。
1289
+ * 仅升级审批(description [sandbox-escalation])触发;allow/deny 自动裁决,
1290
+ * abstain/未注入 既有用户弹窗流程(行为不变)。fail-closed 语义见 semantic-reviewer.ts。
1019
1291
  */
1020
- inspectSessionWriteLease?: (sessionId: string) => LeaseInspection;
1021
- /**
1022
- * RFC-171 D4:强制释放写租约(可选注入)——`SessionWriteLeaseManager.forceRelease` 的
1023
- * 薄封装。跳过 token 实核,仅供用户主动触发的 `/session unlock --force` 使用。
1024
- */
1025
- forceReleaseSessionWriteLease?: (sessionId: string) => void;
1026
- }
1027
- interface DiskSessionPoolOptions extends SessionPoolOptions {
1028
- sessionDir: string;
1292
+ semanticReviewer?: SemanticReviewer;
1029
1293
  }
1030
1294
  interface RemoteSessionPoolOptions extends SessionPoolOptions {
1031
1295
  sessionUrl: string;
@@ -1048,7 +1312,7 @@ interface SessionPorts {
1048
1312
  recorder?: TraceRecorder;
1049
1313
  /** RFC-145 D3:nondet batch 冲洗回调(RecordingPorts.flushNondetBatch)。缺省 = 不聚合。 */
1050
1314
  nondetFlush?: () => void;
1051
- stream?: StreamFunction;
1315
+ stream?: StreamFunction$1;
1052
1316
  tools?: AgentTool[];
1053
1317
  disableMemory?: boolean;
1054
1318
  }
@@ -1057,12 +1321,32 @@ declare class SessionPool {
1057
1321
  private readonly panelStateStoreRef;
1058
1322
  private readonly sessions;
1059
1323
  private readonly configs;
1324
+ /**
1325
+ * 驻留时钟(in-memory 池的"这个会话是何时被装进池子的"),**与 `lastActiveAt` 分离**。
1326
+ *
1327
+ * 背景(/resume 时间戳全变"刚刚"的根因):`restore`/`restoreAll` 此前用
1328
+ * `{ ...snapshot.metadata, lastActiveAt: Date.now() }` 装载快照,把"恢复"当成了"活动"。
1329
+ * 该值随后被 `save()` 的 `touchSession(..., snapshot.metadata.lastActiveAt, ...)` 原样
1330
+ * 写回 DB —— 每次启动 `restoreAll` 恢复的会话(池容量 50)都被盖成当前时间且**永久落盘**,
1331
+ * 于是 `/resume` 列表里所有历史会话都显示成"N 秒前",真实活动时间被不可逆地抹掉,
1332
+ * 同时按 `lastActiveAt` 降序的排序也彻底失去意义(全部并列在同一时刻)。
1333
+ *
1334
+ * 但那个 `Date.now()` 并非笔误:它保护刚恢复的会话不被 `collectIdle` 立刻自食
1335
+ * (快照的 `lastActiveAt` 可能已是 99 小时前,一装载就超 idleTimeout,见
1336
+ * m17-session-persistence-e2e "M18-PR-03:restore 刷新 lastActiveAt=now(恢复不自食)")。
1337
+ *
1338
+ * 所以正确的切分是:**驱逐判据 = max(进池时刻, 真实活动时间),展示与排序只看
1339
+ * `lastActiveAt`(真实活动时间、落盘)**。本时钟只记"何时进的池"(create/fork/restore
1340
+ * 三个装池点登记,出池时清除),活动侧的续期由 `lastActiveAt` 天然承担——两者如何
1341
+ * 合成见 `residencySince`。本时钟是进程内易失状态,永不落盘,故不会污染 /resume。
1342
+ */
1343
+ private readonly residencyClock;
1060
1344
  private readonly persistence;
1061
1345
  private readonly maxSessions;
1062
1346
  private readonly idleTimeoutMs;
1063
1347
  private readonly threshold;
1064
1348
  private activeSessionId?;
1065
- readonly stream: StreamFunction;
1349
+ readonly stream: StreamFunction$1;
1066
1350
  private readonly hookRegistry;
1067
1351
  private readonly logger;
1068
1352
  private readonly devtools?;
@@ -1070,16 +1354,18 @@ declare class SessionPool {
1070
1354
  private readonly traceStore?;
1071
1355
  private readonly checkpointStore?;
1072
1356
  private readonly clock?;
1357
+ private readonly idGen;
1358
+ private readonly randomGen;
1073
1359
  private readonly residencyGovernor?;
1074
1360
  private readonly getResidencyConfig?;
1361
+ /** RFC-327 修订 D2(M2):双平面注册边界——会话格派生/清理的宿主注册表(可选)。 */
1362
+ private readonly planeRegistry?;
1075
1363
  private readonly configProvider?;
1076
- /** RFC-146:本地高效冷会话枚举策略(可选,见 SessionPoolOptions.listColdSessionsOverride)。 */
1077
- private readonly listColdSessionsOverride?;
1078
- /** 只读探测(可选,见 SessionPoolOptions.isSessionWriteLocked)。 */
1079
- private readonly isSessionWriteLocked?;
1080
- /** RFC-171 D4:写租约诊断/强制释放(可选,见 SessionPoolOptions 对应字段)。 */
1081
- private readonly inspectSessionWriteLeaseFn?;
1082
- private readonly forceReleaseSessionWriteLeaseFn?;
1364
+ /** RFC-394 D3:后端能力 seam(收敛此前 5 个散装旋钮)。 */
1365
+ private readonly writeLeaseManager?;
1366
+ private readonly coldSessionLister?;
1367
+ /** RFC-380 T4:LLM 语义审批层(opt-in)——透传给每个 buildAgentSession。 */
1368
+ private readonly semanticReviewer?;
1083
1369
  /**
1084
1370
  * 持久化是否为「自动行为」。disk/remote 后端 = true(prompt 终态增量落盘 +
1085
1371
  * 宿主 start/stop 自动 restoreAll/saveAll);in-memory 缺省 = false(行为与历史一致,
@@ -1112,6 +1398,31 @@ declare class SessionPool {
1112
1398
  /** 会话级「账号可用模型」读写(同 permissionAlwaysAllow 持久化语义)。 */
1113
1399
  getSessionAvailableModels(id: string): string[];
1114
1400
  setSessionAvailableModels(id: string, models: readonly string[]): void;
1401
+ /**
1402
+ * 登记"会话进池时刻"(create/fork/restore 三个装池点)。纯进程内状态,永不落盘,
1403
+ * 不会污染 `/resume` 展示的 `lastActiveAt`。活动侧续期见 `residencySince`。
1404
+ *
1405
+ * RFC-327 修订 D2(M2):同一装池点是会话格的派生点——派生 PRESET 会话格
1406
+ * (`session.preset.register()` 的目标)。会话级贡献随会话 dispose 清理
1407
+ * (见 clearSessionPlaneGrid 在四个出池点的调用)。
1408
+ */
1409
+ private admitToResidency;
1410
+ /** RFC-327 修订 D2(M2):会话出池 → 清理 PRESET 会话格(不重建;未派生即 no-op)。 */
1411
+ private clearSessionPlaneGrid;
1412
+ /**
1413
+ * 驱逐判据用的"最近驻留活动时刻" = **进池时刻与真实活动时间中较新的那个**。
1414
+ *
1415
+ * 两个来源各自只能守住一半,必须取 max:
1416
+ * - 只读 `lastActiveAt`:刚恢复的会话带着快照里可能 99 小时前的时间戳,一进池就超
1417
+ * idleTimeout 被自食(旧 M18-PR-03 要防的正是这个)。
1418
+ * - 只读驻留时钟:它仅在装池时登记、不随对话续期,判据会退化成"进池后过了多久"——
1419
+ * 用户连用两小时的会话反而因"进池早"被当成 idle 驱逐,且 `collectIdle` 并不豁免
1420
+ * `activeSessionId`,连当前正在用的会话都可能被踢出池。
1421
+ *
1422
+ * 取较新值后:恢复瞬间由进池时刻托底,此后每次真实活动经 `lastActiveAt` 自然续期,
1423
+ * 而真正长期无活动的会话两个值都旧,仍会被正常驱逐(不会变成"永不驱逐")。
1424
+ */
1425
+ private residencySince;
1115
1426
  /**
1116
1427
  * 保持既有 private 外观(createSession/fork/restore 调用点不变)。
1117
1428
  */
@@ -1202,15 +1513,15 @@ declare class SessionPool {
1202
1513
  * 不阻塞会话删除本身)。
1203
1514
  */
1204
1515
  private clearSessionStores;
1205
- /**
1206
- * fork 直接复用 source session 自身的已解析配置,
1207
- * 消息通过复制 snapshot entries 完整迁移。
1208
- */
1209
1516
  /**
1210
1517
  * RFC-160 D3/D4:出口 hydrate(fork 用)。无剥离态直通;有剥离态且持久层具备能力则回填;
1211
1518
  * 能力缺席 → 断言炸(接线遗漏立即暴露,绝不把剥离态固化为新会话正文)。
1212
1519
  */
1213
1520
  private ensureEntriesHydrated;
1521
+ /**
1522
+ * fork 直接复用 source session 自身的已解析配置,
1523
+ * 消息通过复制 snapshot entries 完整迁移。
1524
+ */
1214
1525
  forkSession(sourceId: string, newId?: string): Promise<AgentSession | undefined>;
1215
1526
  save(id: string): Promise<void>;
1216
1527
  saveAll(): Promise<void>;
@@ -1252,16 +1563,12 @@ declare class SessionPool {
1252
1563
  private canAccommodate;
1253
1564
  dispose(): void;
1254
1565
  }
1255
- declare function createDiskSessionPool(options: DiskSessionPoolOptions): SessionPool;
1256
1566
  declare function createRemoteSessionPool(options: RemoteSessionPoolOptions): SessionPool;
1257
1567
  declare function createInMemorySessionPool(options: SessionPoolOptions): SessionPool;
1258
1568
  //#endregion
1259
1569
  //#region src/create-agent-runtime.d.ts
1260
1570
  type SessionStorageConfig = {
1261
1571
  kind: 'in-memory';
1262
- } | {
1263
- kind: 'disk';
1264
- sessionDir: string;
1265
1572
  } | {
1266
1573
  kind: 'remote';
1267
1574
  sessionUrl: string;
@@ -1286,22 +1593,12 @@ type SessionStorageConfig = {
1286
1593
  autoPersist?: boolean; /** 后端专属资源释放(如 SqliteSessionRepository.close());dispose 时在 sessions.dispose 后调用。 */
1287
1594
  onDispose?: () => void | Promise<void>;
1288
1595
  /**
1289
- * RFC-146:本地高效冷会话枚举策略透传(见 SessionPoolOptions.listColdSessionsOverride)。
1290
- * 关系化 SQLite 分支(session-pool-factory.ts)经此注入,走 SqliteSessionRepository 单次
1291
- * SQL 查询而非通用 listPaginated+load 慢路径。
1292
- */
1293
- listColdSessionsOverride?: (workspaceKey: string) => Promise<AgentSessionInfo[]>;
1294
- /**
1295
- * 只读探测策略透传(见 SessionPoolOptions.isSessionWriteLocked)。关系化 SQLite 分支
1296
- * 经此注入 SessionWriteLeaseManager.peekLockedByOther 的薄封装。
1297
- */
1298
- isSessionWriteLocked?: (sessionId: string) => boolean;
1299
- /**
1300
- * RFC-171 D4:写租约诊断/强制释放策略透传(见 SessionPoolOptions 对应字段)。
1301
- * 关系化 SQLite 分支经此注入 SessionWriteLeaseManager.inspect/forceRelease 的薄封装。
1596
+ * RFC-394 D3/R2:后端能力 seam——收敛此前 4 个散装透传字段
1597
+ * (listColdSessionsOverride / isSessionWriteLocked / inspectSessionWriteLease /
1598
+ * forceReleaseSessionWriteLease)为 2 个能力接口。由后端自声明注入。
1302
1599
  */
1303
- inspectSessionWriteLease?: (sessionId: string) => LeaseInspection;
1304
- forceReleaseSessionWriteLease?: (sessionId: string) => void;
1600
+ writeLeaseManager?: WriteLeaseManager;
1601
+ coldSessionLister?: ColdSessionLister;
1305
1602
  /**
1306
1603
  * RFC-108 D3 面板态(todoList/editedFiles/subagents/drafts)独立持久化的落盘目录。
1307
1604
  * 2026-07-21 修复:`kind:'custom'`(coding 默认的关系化 SQLite 后端)此前不构造
@@ -1350,6 +1647,18 @@ interface RuntimeOptions {
1350
1647
  residencyGovernor?: ResidencyGovernor;
1351
1648
  /** RFC-323 M4:驻留预算运行时配置 getter(settings 注入透传)。 */
1352
1649
  getResidencyConfig?: () => _$_x_otto_session_contract0.ResidencyBudgetConfig | undefined;
1650
+ /**
1651
+ * RFC-327 修订 D2(M2):双平面注册边界——进程级 HostRegistry(可选注入)。
1652
+ * 注入后会话池在会话创建/fork/restore 时派生 PRESET 会话格,会话移除/驱逐/池
1653
+ * dispose 时清理会话格(对齐「会话创建派生会话格、会话 dispose 清会话格」生命周期)。
1654
+ * 未注入 = 会话格不派生(行为不变,宿主不接双平面即零影响)。
1655
+ */
1656
+ planeRegistry?: HostRegistry;
1657
+ /**
1658
+ * RFC-380 T4:LLM 语义审批层(opt-in)。注入后仅升级审批(sandbox-denied escalate ask)
1659
+ * 触发;allow/deny 自动裁决,abstain/未注入 → 既有用户弹窗流程(行为不变)。
1660
+ */
1661
+ semanticReviewer?: SemanticReviewer;
1353
1662
  }
1354
1663
  interface AgentRuntime {
1355
1664
  /** 统一派生入口:子 runtime / 宿主 / swarm / task-runner 皆经此(§4-11)。 */
@@ -1360,7 +1669,7 @@ interface AgentRuntime {
1360
1669
  readonly sessions: SessionPool;
1361
1670
  readonly hookRegistry: HookRegistry;
1362
1671
  /** 引擎 stream(从 providerRegistry 派生,§4-11:子 runtime 用之而非各自 createStreamFunction)。 */
1363
- readonly stream: StreamFunction;
1672
+ readonly stream: StreamFunction$1;
1364
1673
  /** ContextSource 有序注册表(D33):通用源已内置注册,coding 策略源由宿主追加。 */
1365
1674
  readonly contextSources: ContextSourceRegistry;
1366
1675
  readonly traceStore: AppendLog<TraceEvent>;
@@ -1373,7 +1682,7 @@ interface AgentRuntime {
1373
1682
  * createAgentRuntime 内置调用之;宿主的离线装配路径(不经 createAgentRuntime)亦复用,
1374
1683
  * 确保「App 经 rt.contextSources 构建注入 hooks」对所有后端一致——消灭 App 端的二次造表。
1375
1684
  */
1376
- declare function createDefaultContextSourceRegistry(workspaceDir: string, memory?: MemorySubsystem): ContextSourceRegistry;
1685
+ declare function createDefaultContextSourceRegistry(_workspaceDir: string, _memory?: MemorySubsystem): ContextSourceRegistry;
1377
1686
  declare function createAgentRuntime(options?: RuntimeOptions): AgentRuntime;
1378
1687
  //#endregion
1379
1688
  //#region src/engine-stores.d.ts
@@ -1593,11 +1902,6 @@ declare class MemoryGovernor {
1593
1902
  constructor(options?: MemoryGovernorOptions);
1594
1903
  /** 当前分级(供外部只读查询,如 TUI 状态展示)。 */
1595
1904
  get currentLevel(): MemoryPressureLevel;
1596
- /**
1597
- * 晚绑定 onWarning/onCritical/onCriticalDuringGrace(App 构造时活跃 session 尚不存在,
1598
- * M2/M3 在 session 就绪后调用注入)。对齐 `App.setScheduleService` 的晚绑定模式,
1599
- * 非构造期可选依赖。
1600
- */
1601
1905
  /**
1602
1906
  * 合并式更新(每个字段独立覆盖,未传的字段保留原值)——RFC-115 追补:`app.ts`
1603
1907
  * 启动期注入 `onSample`(trace 落盘)后,`interactive.ts` 会话就绪时再调用一次
@@ -1873,6 +2177,11 @@ interface ProcessSpawnConfig {
1873
2177
  */
1874
2178
  env?: Record<string, string>;
1875
2179
  cwd?: string;
2180
+ /** RFC-394 M5:从 ProcessRegistry RegisterInput 收敛——输出落临时文件路径(bash 工具填)。 */
2181
+ logPath?: string;
2182
+ /** RFC-394 M5:从 ProcessRegistry RegisterInput 收敛——best-effort 嗅探端口/URL。 */
2183
+ port?: number;
2184
+ url?: string;
1876
2185
  }
1877
2186
  interface KillOptions {
1878
2187
  graceMs?: number;
@@ -1896,6 +2205,22 @@ interface ManagedProcess {
1896
2205
  readonly pgid: number | undefined;
1897
2206
  readonly status: ProcessStatus;
1898
2207
  readonly config: ProcessSpawnConfig;
2208
+ /** RFC-394 M5:便捷 getter(从 config 投影,消费方不用深挖 config.xxx)。 */
2209
+ readonly command: string;
2210
+ readonly cwd: string | undefined;
2211
+ readonly logPath: string | undefined;
2212
+ readonly port: number | undefined;
2213
+ readonly url: string | undefined;
2214
+ /** 进程启动时刻(spawn/register 时戳,注入时钟)。 */
2215
+ readonly startedAt: number;
2216
+ /** 退出码(未退出或被信号杀死为 undefined)。 */
2217
+ readonly exitCode: number | undefined;
2218
+ /**
2219
+ * RFC-223:运行时输出识别到的服务信息(url/port/protocol)。
2220
+ * 由 `ProcessRuntime.updateDetectedService` 按 D2 去重规则写入,供状态栏
2221
+ * 「服务」指示器与 ServicesDialog 消费。未识别到时为 undefined。
2222
+ */
2223
+ readonly detectedService: DetectedService | undefined;
1899
2224
  /** 原始字节流 stdin/stdout/stderr(规则 5/H2:非行缓冲,帧解析由消费者负责) */
1900
2225
  readonly stdin: NodeJS.WritableStream | null;
1901
2226
  readonly stdout: NodeJS.ReadableStream | null;
@@ -1938,6 +2263,18 @@ declare class ProcessRuntime {
1938
2263
  get(id: string): ManagedProcess | undefined;
1939
2264
  list(filter?: ProcessFilter): ManagedProcess[];
1940
2265
  listForSession(sessionId: string): ManagedProcess[];
2266
+ /** RFC-394 M5:从 ProcessRegistry 收敛——按 sessionId+id 精确查找。 */
2267
+ getForSession(sessionId: string, id: string): ManagedProcess | undefined;
2268
+ /**
2269
+ * RFC-394 M5:从 ProcessRegistry 收敛——registry 级 kill(id)。
2270
+ * ProcessRuntime 的 kill 是实例方法(proc.kill()),此方法提供 registry 级入口。
2271
+ */
2272
+ kill(id: string, opts?: KillOptions): Promise<void>;
2273
+ /**
2274
+ * RFC-394 M5:从 ProcessRegistry 收敛——更新已检测到的服务信息。
2275
+ * 用于 background bash 运行时输出识别到端口/URL 的场景(RFC-223)。
2276
+ */
2277
+ updateDetectedService(id: string, service: DetectedService): void;
1941
2278
  listForOwner(owner: ProcessOwner): ManagedProcess[];
1942
2279
  touch(id: string): void;
1943
2280
  remove(id: string): void;
@@ -2021,6 +2358,12 @@ interface ApplyDiffResult {
2021
2358
  * apply 可能出乎意料地冲突/无冲突",不用来拒绝 apply。
2022
2359
  */
2023
2360
  declare function hasRepoDrifted(repoRoot: string, base: string): boolean;
2361
+ /**
2362
+ * 纯预检:diff 能否干净 apply 到主仓工作区(`git apply --check`,无任何副作用)。
2363
+ * 供 governance/apply-policy 的 `conflict` 信号使用——在真正 `applyDiff` 之前判定"是否可干净
2364
+ * 落地"。空 diff 视为干净。true=无冲突可落地;false=有冲突/无法判定(交给 applyDiff 兜底)。
2365
+ */
2366
+ declare function checkApply(repoRoot: string, diff: string): boolean;
2024
2367
  /**
2025
2368
  * 把作业 diff apply 到主仓工作区。**check-first,不强改**:先 `git apply --check` 验证能否干净
2026
2369
  * 应用——能才真 apply;不能(主仓漂移/冲突)则返回 applied=false + conflictPaths,**绝不**往主树
@@ -2042,6 +2385,15 @@ declare function revertDiff(repoRoot: string, diff: string): ApplyDiffResult;
2042
2385
  * 真实失败无声吞掉,运维完全无感知)。`onError` 缺省不传时行为与修复前一致(纯静默)。
2043
2386
  */
2044
2387
  declare function removeWorktree(wt: Worktree, onError?: (op: 'worktree remove' | 'branch delete' | 'worktree prune', err: unknown) => void): void;
2388
+ /**
2389
+ * RFC-435 R8:扫描磁盘上存在的全部 job worktree id(跨进程 rehydrate 源)。
2390
+ *
2391
+ * 用与 pruneOrphanWorktrees 相同的筛选逻辑(tmpdir `otto-wt-*` 前缀 + `otto/job-*` branch),
2392
+ * 但**不删除任何东西**——只返回 jobId 集合。调用方(如 installAgentJobReaper)把本进程
2393
+ * 活跃集与此集合并后传给 pruneOrphanWorktrees,使"他进程(daemon/另一会话)创建的 job
2394
+ * worktree"不会被误判为孤儿销毁(R8:剪枝必须基于 rehydrate 后活跃集合)。
2395
+ */
2396
+ declare function listWorktreeJobIds(repoRoot: string): string[];
2045
2397
  /**
2046
2398
  * 扫描并清理孤儿 worktree(SIGKILL/OOM 未走 exit reaper 留下的残留)。
2047
2399
  * 用 `git -C repoRoot worktree list --porcelain` 列出该仓的全部已注册 worktree,
@@ -2473,6 +2825,11 @@ declare function attachAgentObserver(agent: Agent, meta: AgentObservationMeta, r
2473
2825
  declare function createEnvironmentContextSource(workspaceDir: string): ContextSource;
2474
2826
  //#endregion
2475
2827
  //#region src/context-sources/memory-index.d.ts
2828
+ /**
2829
+ * RFC-390 D4:标准组合根不再注册此 source(memory 内容由 coding 侧 lesson-injection hook
2830
+ * 合并进 <agent_knowledge> 信封)。保留供 V 形态/无 coding 组合使用——priority 复用
2831
+ * agentKnowledge 的 35(同位替代)。
2832
+ */
2476
2833
  declare function createMemoryIndexContextSource(_workspaceDir: string, memory?: ContextBuildInput['memory']): ContextSource;
2477
2834
  //#endregion
2478
2835
  //#region src/context-sources/todo-reminder.d.ts
@@ -2541,83 +2898,6 @@ interface DocSyncPort {
2541
2898
  declare function formatDocSyncReminder(toolName: string): string;
2542
2899
  declare function createDocSyncReminderHooks(port: DocSyncPort, priority?: 64): HookSpec[];
2543
2900
  //#endregion
2544
- //#region src/context-sources/section-order.d.ts
2545
- /**
2546
- * RFC-020 Phase 3(M54)P3-04:system-prompt 注入 section 的**唯一有序声明**。
2547
- *
2548
- * 治病灶——此前各 section 的 priority 数字(20/25/35/38/40/60)散在 coding hooks 与 runtime
2549
- * context-sources 两包里,"system prompt 各段什么顺序"得捞齐多处数字才能拼出。现收敛为此处一张表:
2550
- * - 数组/字段顺序即 priority 升序 = 实际 `system.prompt.transform` hook 的执行/拼接顺序;
2551
- * - `lane` 标明该段落 **stable 前缀**(systemPrompt,进 prompt cache)还是 **volatile 尾段**
2552
- * (systemTail,落在 cache 断点之后,逐轮可变不击穿缓存);
2553
- * - runtime context-sources(environment/memory-index/memory-delta)与 coding hooks
2554
- * (tool-guidance/lesson/skill-catalog)均从此处取 priority —— 单一真相源,改序只此一处。
2555
- *
2556
- * 放在 @x-otto/runtime(coding 依赖 runtime、反向不依赖):coding 侧 hook 经 `@x-otto/runtime` import。
2557
- * 注:本表是**排序权威**(方案 H:section 仍由各自 hook 自注册,priority 数字降级为对本表的引用);
2558
- * 不强行把 `SYSTEM_PROMPT_SECTIONS` 各 section 物理并入单一 registry(那超出外科尺度、非目标)——
2559
- * section 数量以该常量的字段数为准,不在本段注释里重复写死具体数字(history: 建表时 7 个,
2560
- * 后追加 docSyncReminder 成 8 个,写死数字会重犯本次发现的漂移)。
2561
- */
2562
- type SystemPromptLane = 'stable' | 'volatile';
2563
- interface SystemPromptSectionSpec {
2564
- /** 对应 hook / ContextSource 的 name/id(与注册名一致,供 drift guard 比对)。 */
2565
- readonly name: string;
2566
- readonly priority: number;
2567
- readonly lane: SystemPromptLane;
2568
- }
2569
- declare const SYSTEM_PROMPT_SECTIONS: {
2570
- readonly toolGuidance: {
2571
- readonly name: "tool-guidance-injection";
2572
- readonly priority: 20;
2573
- readonly lane: "stable";
2574
- };
2575
- /**
2576
- * RFC-318 D7:三分流判别(缺工具 / 流程重复 / 其余)。紧跟 toolGuidance——它是对
2577
- * "现有工具够不够用"的元判断,语义上属工具指引的延伸;内容恒定故走 stable lane。
2578
- */
2579
- readonly skillLoopGuidance: {
2580
- readonly name: "skill-loop-guidance-injection";
2581
- readonly priority: 21;
2582
- readonly lane: "stable";
2583
- };
2584
- readonly environment: {
2585
- readonly name: "runtime:environment";
2586
- readonly priority: 25;
2587
- readonly lane: "stable";
2588
- };
2589
- readonly memoryIndex: {
2590
- readonly name: "runtime:memory-index";
2591
- readonly priority: 35;
2592
- readonly lane: "stable";
2593
- };
2594
- readonly skillCatalog: {
2595
- readonly name: "skill-catalog-injection";
2596
- readonly priority: 38;
2597
- readonly lane: "stable";
2598
- };
2599
- readonly lesson: {
2600
- readonly name: "lesson-injection";
2601
- readonly priority: 40;
2602
- readonly lane: "stable";
2603
- };
2604
- readonly memoryDelta: {
2605
- readonly name: "memory-delta-injection";
2606
- readonly priority: 60;
2607
- readonly lane: "volatile";
2608
- };
2609
- readonly todoReminder: {
2610
- readonly name: "todo-reminder-injection";
2611
- readonly priority: 62;
2612
- readonly lane: "volatile";
2613
- };
2614
- readonly docSyncReminder: {
2615
- readonly name: "doc-sync-reminder-injection";
2616
- readonly priority: 64;
2617
- readonly lane: "volatile";
2618
- };
2619
- };
2620
- //#endregion
2621
2901
  //#region src/session-stable.d.ts
2622
2902
  interface SessionStableInjectionOptions {
2623
2903
  name: string;
@@ -2627,6 +2907,47 @@ interface SessionStableInjectionOptions {
2627
2907
  }
2628
2908
  declare function createSessionStableInjectionHooks(options: SessionStableInjectionOptions): HookSpec[];
2629
2909
  //#endregion
2910
+ //#region src/define-prompt-section.d.ts
2911
+ interface PromptSectionDefinition {
2912
+ /**
2913
+ * section 名称——必须在 SYSTEM_PROMPT_SECTIONS 表中已登记且 lane 为 'stable'。
2914
+ * priority / budgetBytes 从表查取,调用方不可覆盖。
2915
+ */
2916
+ readonly name: string;
2917
+ /**
2918
+ * 计算注入内容。返回空/undefined 表示本会话无注入(首轮计算后会话内冻结,不再重算)。
2919
+ * 产物自动过 applySectionBudget(调用方不需手动套预算)。
2920
+ */
2921
+ compute: (input: SystemPromptTransformInput) => Promise<string | undefined> | string | undefined;
2922
+ }
2923
+ /**
2924
+ * 定义一个 stable prompt section 注入 hook。
2925
+ *
2926
+ * 自动从 SYSTEM_PROMPT_SECTIONS 表查 priority / budgetBytes,走 session 冻结的 stable
2927
+ * lane,产物统一过 applySectionBudget。
2928
+ *
2929
+ * @example
2930
+ * ```ts
2931
+ * const hooks = definePromptSection({
2932
+ * name: SYSTEM_PROMPT_SECTIONS.skillLoopGuidance.name,
2933
+ * compute: async () => isEnabled() ? await loadGuidance() : undefined,
2934
+ * })
2935
+ * ```
2936
+ */
2937
+ declare function definePromptSection(definition: PromptSectionDefinition): HookSpec[];
2938
+ //#endregion
2939
+ //#region src/session/tool-result-store-global.d.ts
2940
+ /**
2941
+ * tool-result-store-global.ts — RFC-093:ToolResultStore 全局单例
2942
+ *
2943
+ * AgentSession 构造 spill 闭包时引用。懒初始化(首次 spill 时基于 cwd 解析 .otto 目录)——
2944
+ * 避免模块加载期做 fs 探测。
2945
+ */
2946
+ declare const globalToolResultStore: {
2947
+ spill(sessionId: string, toolCallId: string, text: string): Promise<string | undefined>;
2948
+ cleanupSession(sessionId: string): Promise<void>;
2949
+ };
2950
+ //#endregion
2630
2951
  //#region src/session/derive-title.d.ts
2631
2952
  /**
2632
2953
  * 从会话消息列表中派生标题(RFC-030 P1)。
@@ -2650,6 +2971,31 @@ declare function deriveSessionTitle(messages: readonly AgentMessage[]): string |
2650
2971
  */
2651
2972
  declare function resolveSessionTitle(persistedTitle: string | undefined, messages: readonly AgentMessage[], id: string): string;
2652
2973
  //#endregion
2974
+ //#region src/session/llm-escalation-reviewer.d.ts
2975
+ interface LlmEscalationReviewerDeps {
2976
+ /** 会话注入的流函数(与对话同一条,按 model.api 路由 provider)。 */
2977
+ stream: StreamFunction;
2978
+ /** 便宜判定模型(由 app 层解析,同 generateTitle 的 category:'search' 口径)。 */
2979
+ model: Model;
2980
+ /** 上限超时(ms),默认 10s;超时 → abstain。 */
2981
+ timeoutMs?: number;
2982
+ }
2983
+ /**
2984
+ * 解析 `{"verdict":"allow|deny|abstain"}`。
2985
+ *
2986
+ * **fail-closed 解析**:只有明确抽到 `allow`/`deny` 才返回之,其余(畸形 JSON、截断、
2987
+ * 散文、未知枚举值、空)一律 'abstain'。特别地,绝不因为文本里**出现** "allow" 字样
2988
+ * 就放行——必须是结构化 verdict 字段。
2989
+ */
2990
+ declare function parseVerdict(raw: string): SemanticReviewerVerdict;
2991
+ /**
2992
+ * 构造 LLM 语义审批器。返回的函数签名即 `SemanticReviewer`。
2993
+ *
2994
+ * 注意:本函数**不做**「是否为升级审批」的判断——那由 gates 的 `isEscalationApproval`
2995
+ * 门负责(单一真源)。
2996
+ */
2997
+ declare function createLlmEscalationReviewer(deps: LlmEscalationReviewerDeps): SemanticReviewer;
2998
+ //#endregion
2653
2999
  //#region src/session/hooks.d.ts
2654
3000
  interface ToolHookOptions {
2655
3001
  hookRegistry: HookRegistry;
@@ -2658,6 +3004,21 @@ interface ToolHookOptions {
2658
3004
  }
2659
3005
  declare function createToolHookExecutor(options: ToolHookOptions): ToolHookExecutor;
2660
3006
  //#endregion
3007
+ //#region src/session/session-compaction-lock.d.ts
3008
+ /**
3009
+ * RFC-381 M1/M2:跨进程 durable 压缩锁端口(与 @x-otto/memory 的 CompactionLockPort
3010
+ * 结构同形——runtime 不依赖 memory 包,结构子类型即满足;memory 侧注入处见
3011
+ * MemoryManagerConfig.compactionLock)。
3012
+ */
3013
+ interface SessionCompactionLockPort {
3014
+ acquire(sessionId: string, token: string): Promise<boolean>;
3015
+ release(sessionId: string, token: string): Promise<void>;
3016
+ }
3017
+ declare const DEFAULT_COMPACTION_LOCK_ORPHAN_TIMEOUT_MS = 60000;
3018
+ declare function createSessionCompactionLock(session: Session<unknown>, options?: {
3019
+ orphanTimeoutMs?: number;
3020
+ }): SessionCompactionLockPort;
3021
+ //#endregion
2661
3022
  //#region src/session/persistence-sync.d.ts
2662
3023
  interface PersistenceSyncDeps {
2663
3024
  persistence: SessionPersistence<AgentMessage>;
@@ -2708,6 +3069,15 @@ interface PersistenceSyncDeps {
2708
3069
  * 这是后端能力事实,不是用户配置——未来远端支持 append-tail 后改声明即可自动升级。
2709
3070
  */
2710
3071
  turnFlush?: 'incremental' | 'snapshot';
3072
+ /**
3073
+ * M-2:确定性时钟端口(可选)。restore/restoreAll 装载快照时注入 InMemorySession——
3074
+ * restore 本身不产生活动(快照 metadata 权威),但为确定性重放/测试保持一致构造。
3075
+ */
3076
+ clock?: ClockPort;
3077
+ /**
3078
+ * M-2:确定性 id 生成端口(可选)。restore 路径不 append entry,故当前仅作一致性注入。
3079
+ */
3080
+ idGen?: IdPort;
2711
3081
  }
2712
3082
  declare class PersistenceSync {
2713
3083
  private readonly deps;
@@ -2784,13 +3154,6 @@ declare class PersistenceSync {
2784
3154
  */
2785
3155
  track(agentSession: AgentSession, transient: boolean): void;
2786
3156
  flushPending(id: string): Promise<void>;
2787
- /**
2788
- * P2 缓解(独立 review 补充,2026-07-12):会话可能经 idle 驱逐(`collectIdle`)或满池 LRU
2789
- * 驱逐(`evictLeastRecent`)消失,这两条路径不经过 `removeSession`/`flushPending`(前者是
2790
- * 同步批量收集不适合 await 落盘完成,后者已自行 fire-and-forget 触发 `saveSnapshot`)——
2791
- * 提供一个轻量同步纯清理方法,供这两条驱逐路径调用,与 `flushPending` 共享同一清理意图
2792
- * 但不附带"等待落盘完成"的语义(驱逐场景不需要,也不应阻塞驱逐循环)。
2793
- */
2794
3157
  /**
2795
3158
  * RFC-323 D4:计算用于预算分配的活跃会话数(排除 transient)。
2796
3159
  *
@@ -2808,6 +3171,13 @@ declare class PersistenceSync {
2808
3171
  * 一致地排除 transient(不订阅持久化、不参与预算分摊)。
2809
3172
  */
2810
3173
  private sumEstimatedContentBytes;
3174
+ /**
3175
+ * P2 缓解(独立 review 补充,2026-07-12):会话可能经 idle 驱逐(`collectIdle`)或满池 LRU
3176
+ * 驱逐(`evictLeastRecent`)消失,这两条路径不经过 `removeSession`/`flushPending`(前者是
3177
+ * 同步批量收集不适合 await 落盘完成,后者已自行 fire-and-forget 触发 `saveSnapshot`)——
3178
+ * 提供一个轻量同步纯清理方法,供这两条驱逐路径调用,与 `flushPending` 共享同一清理意图
3179
+ * 但不附带"等待落盘完成"的语义(驱逐场景不需要,也不应阻塞驱逐循环)。
3180
+ */
2811
3181
  forgetSession(id: string): void;
2812
3182
  /**
2813
3183
  * RFC-352 D2:驱逐的最终 snapshot 仍在队列中时不能提前清 residency 状态。
@@ -2830,26 +3200,6 @@ declare class PersistenceSync {
2830
3200
  */
2831
3201
  private isTransient;
2832
3202
  save(id: string): Promise<void>;
2833
- /**
2834
- * RFC-336 D1:会话行预建——会话创建即落 `sessions` 一行(无 entries),不等首条消息。
2835
- *
2836
- * **要解决的问题**(实测复现,2026-08-10):`repo.createSession()` 的唯一调用点是
2837
- * `RelationalSessionPersistence.save()`,而 save 由 `track()` 订阅的 `turn.end`/
2838
- * `prompt.end` 触发——两者都发生在**首个模型回合完成之后**。因此"用户已回车、模型还没
2839
- * 回任何东西"这段窗口(≈ 一次完整模型往返,数秒~数十秒)内进程若被强杀(SIGKILL/OOM/
2840
- * 断电),DB 里连 `sessions` 行都不存在 → 整个会话蒸发,`/resume` 完全看不到,
2841
- * 且 `detectCrashGap` 因 `load()` 返回 null 而根本不执行(RFC-305 D3 的盲区)。
2842
- *
2843
- * **为什么复用 doSaveEntries 而非直调 repo**(D1-b,RFC-159 D3):写租约检查在
2844
- * `RelationalSessionPersistence.save()` 入口,是所有会话写路径的单一汇聚点。直调
2845
- * `repo.createSession()` 会开一条无租约的写路径先例。走这里则天然经 `enqueue` 串行链
2846
- * → `doSaveEntries` → `persistence.save()`,租约与写序语义全部继承,零新增写路径。
2847
- * 空 entries 快照在 save 内部即 `createSession()` + 零次 `appendEntry()`,正是所需语义。
2848
- *
2849
- * **幂等**:`createSession` 是 `ON CONFLICT DO NOTHING`;重复调用安全。
2850
- * **fire-and-forget**(规则 3):失败只 warn,绝不阻塞会话创建——预建是可用性增强,
2851
- * 不是创建流程的正确性前提(失败时退化为改动前行为:首个 turn 后才落行)。
2852
- */
2853
3203
  /**
2854
3204
  * RFC-336 D2:输入边界落盘——prep 成功、模型往返开始**之前**把新增 entries 落库。
2855
3205
  *
@@ -2870,6 +3220,26 @@ declare class PersistenceSync {
2870
3220
  * prompt 终态落盘共用同一条 per-session 链,天然串行、写租约语义一致。
2871
3221
  */
2872
3222
  requestInputBoundaryFlush(agentSession: AgentSession, transient: boolean): void;
3223
+ /**
3224
+ * RFC-336 D1:会话行预建——会话创建即落 `sessions` 一行(无 entries),不等首条消息。
3225
+ *
3226
+ * **要解决的问题**(实测复现,2026-08-10):`repo.createSession()` 的唯一调用点是
3227
+ * `RelationalSessionPersistence.save()`,而 save 由 `track()` 订阅的 `turn.end`/
3228
+ * `prompt.end` 触发——两者都发生在**首个模型回合完成之后**。因此"用户已回车、模型还没
3229
+ * 回任何东西"这段窗口(≈ 一次完整模型往返,数秒~数十秒)内进程若被强杀(SIGKILL/OOM/
3230
+ * 断电),DB 里连 `sessions` 行都不存在 → 整个会话蒸发,`/resume` 完全看不到,
3231
+ * 且 `detectCrashGap` 因 `load()` 返回 null 而根本不执行(RFC-305 D3 的盲区)。
3232
+ *
3233
+ * **为什么复用 doSaveEntries 而非直调 repo**(D1-b,RFC-159 D3):写租约检查在
3234
+ * `RelationalSessionPersistence.save()` 入口,是所有会话写路径的单一汇聚点。直调
3235
+ * `repo.createSession()` 会开一条无租约的写路径先例。走这里则天然经 `enqueue` 串行链
3236
+ * → `doSaveEntries` → `persistence.save()`,租约与写序语义全部继承,零新增写路径。
3237
+ * 空 entries 快照在 save 内部即 `createSession()` + 零次 `appendEntry()`,正是所需语义。
3238
+ *
3239
+ * **幂等**:`createSession` 是 `ON CONFLICT DO NOTHING`;重复调用安全。
3240
+ * **fire-and-forget**(规则 3):失败只 warn,绝不阻塞会话创建——预建是可用性增强,
3241
+ * 不是创建流程的正确性前提(失败时退化为改动前行为:首个 turn 后才落行)。
3242
+ */
2873
3243
  precreateSessionRow(agentSession: AgentSession, transient: boolean): void;
2874
3244
  /**
2875
3245
  * 驱逐路径专用:用调用方**同步捕获**的快照入链落盘。
@@ -2942,21 +3312,6 @@ declare class PersistenceSync {
2942
3312
  * 若未提供 configProvider 则返回 null。
2943
3313
  */
2944
3314
  restore(id: string): Promise<AgentSession | null>;
2945
- /**
2946
- * RFC-305 D3:restore 时 trace↔DB 对账——检出"上次进程异常终止时已发生但未落库"的
2947
- * turn 缺口并告警。对账基准 = trace append-log(逐事件实时落盘,独立于 save 链):
2948
- * DB 最后一条已持久化 entry 时间戳之后,trace 仍有 `turn.end` lifecycle 事件 →
2949
- * 这些 turn 的消息只进过内存、从未进 DB,已随崩溃永久丢失。
2950
- *
2951
- * 用时间戳而非 turn 号对账:trace 的 turn 字段 per-prompt 从 0 重置,跨 prompt 不可比;
2952
- * ts 与 entry.timestamp 同源(Date.now 系时钟),单机单调可比。
2953
- *
2954
- * 容忍度:正常退出路径(prompt.end 立即落盘 + turn 级入链)下 trace turn.end 与 DB
2955
- * 落库几乎同时,容忍 CRASH_GAP_TOLERANCE_MS 内的尾差(在途写 + 时钟粒度),超出才告警。
2956
- *
2957
- * fail-soft:traceStore 缺失/读取异常/无 turn.end 事件 → 静默跳过(debug 日志),
2958
- * 绝不阻塞 restore、绝不产生假告警。
2959
- */
2960
3315
  /**
2961
3316
  * RFC-305 D3:对账结果查询口——restore/restoreAll 内部把本次恢复检出的崩溃缺口记入
2962
3317
  * per-session 缓存,调用方(CLI resume 路径)在订阅建立后读取并 toast。缓存随
@@ -2999,7 +3354,7 @@ interface EngineSnapshot {
2999
3354
  }
3000
3355
  /** 引擎产出的治理决策(所有字段确定性)。 */
3001
3356
  interface ResidencyDecision {
3002
- /** 单会话当前有效字节预算(已/处分配层 × 压力档)。 */
3357
+ /** 单会话当前有效字节预算(分配量经压力档缩放后的最终值)。 */
3003
3358
  maxBytesPerSession: number;
3004
3359
  /** 当前全局驻留压力比例(0.0 ~ 1.0)。 */
3005
3360
  globalPressure: number;
@@ -3007,15 +3362,15 @@ interface ResidencyDecision {
3007
3362
  isGlobalPressure: boolean;
3008
3363
  }
3009
3364
  /**
3010
- * 计算“每个会话当前 应该 byte budget — 给定 snapshot,治理一个值。
3365
+ * 计算给定 snapshot 下每个会话的有效字节预算。
3011
3366
  *
3012
- * 特性:
3013
- * - 负反馈:currentTotalResidentBytes 越大 pressure 越高 → 每个会话预算*越低*。
3014
- * - 下界 MIN_PER_SESSION:防止分配结果不可用作 (如果 N 极大, SESSION_MAX=50×16=800MB < totalBudget).
3015
- * - 上界 MAX_PER_SESSION:模型折叠输入的最大(imageasher, window by compression)约为 214MB/会话。
3016
- * - 确定性:相同 snapshot → 相同 decision(R4,皱纹是稳定 的。
3367
+ * 特性:
3368
+ * - 负反馈:currentTotalResidentBytes 越大 pressure 越高 → 每个会话预算越低。
3369
+ * - 下界 MIN_PER_SESSION:防会话数极大时分配结果不可用。
3370
+ * - 上界 MAX_PER_SESSION:单会话驻留上限。
3371
+ * - 确定性:相同 snapshot → 相同 decision(R4)。
3017
3372
  */
3018
3373
  declare function decide(snapshot: EngineSnapshot): ResidencyDecision;
3019
3374
  //#endregion
3020
- export { type AgentJobListener, AgentJobRegistry, type AgentJobRegistryOptions, type AgentJobStatus, type AgentObsStatus, type AgentObservabilityOptions, AgentObservabilityRegistry, type AgentObservation, type AgentObservationMeta, type AgentObservationSnapshot, type AgentRuntime, AgentSession, type AgentSessionEvent, type AgentSessionEventMap, type AgentSessionEventType, type AgentSessionInfo, type AgentSessionJob, type AgentSessionOptions, type AgentSessionSubscriber, type ApplyDiffResult, type ApplyPolicy, type ApplySignals, type ArchiveRecallPort, type AskUserQuestion, type AutonomyLevel, type BackgroundProcess, type BackgroundProcessStatus, CAPABILITY_SURFACE_PATTERNS, type Checkpoint, type CheckpointData, type ContextBuildInput, type ContextInjectionPort, type ContextSource, ContextSourceRegistry, DEFAULT_MAX_AUTO_APPLY_LINES, type DiskSessionPoolOptions, type DocSyncPort, type EngineStoreLocation, type EngineStoreOverrides, type ExitResult, FORBIDDEN_ZONE_PATTERNS, type FileEntrySnapshot, type FileSnapshot, FleetMonitor, type FleetMonitorOptions, type FleetSample, type GrillAnswer, type GrillQuestion, type GrillRequest, type KillOptions, LEVEL_DIVISOR, type ManagedProcess, type MemoryDeltaPort, MemoryGovernor, type MemoryGovernorOptions, type MemoryPort, type MemoryPressureLevel, type MemorySample, type MemorySubsystem, PanelStateStore, PersistenceSync, type PersistenceSyncDeps, type ProcessCategory, type ProcessFilter, type ProcessLifecycle, type ProcessListener, type ProcessOwner, ProcessRegistry, type ProcessRegistryOptions, ProcessRuntime, type ProcessRuntimeOptions, type ProcessSpawnConfig, type ProcessStatus, type PromptOptions, type PromptRefresh, RUNTIME_DEFAULTS, type RegisterAgentJobInput, type RegisterInput, type RemoteSessionPoolOptions, type ReplayLevel, type ResidencyDiagnostics, ResidencyGovernor, type ResidencyPressureLevel, type ResolvedSessionConfig, type RestoreMode, type RuntimeOptions, SYSTEM_PROMPT_SECTIONS, type SandboxRunner, type SandboxToolCall, type SandboxToolResult, SandboxUnavailableError, SessionPool, type SessionPoolOptions, type SessionPorts, type SessionStableInjectionOptions, type SessionStorageConfig, type SystemPromptSectionSpec, TimeTravelController, type TimeTravelDeps, type TodoReminderPort, type TracebackView, type TurnRecord, type Verdict, type Worktree, applyDiff, applyMemoryTransform, attachAgentObserver, buildEngineStores, captureFiles, createAgentRuntime, createDefaultContextSourceRegistry, createDiskSessionPool, createDocSyncReminderHooks, createEnvironmentContextSource, createInMemorySessionPool, createMemoryDeltaHooks, createMemoryIndexContextSource, createRemoteSessionPool, createSessionStableInjectionHooks, createTodoReminderHooks, createToolHookExecutor, createWorktree, decideApply, decide as decideResidency, defaultMaxToolTurnExtensions, deriveSessionTitle, diffTouchedPaths, effectiveResidencyBudget, formatDocSyncReminder, globalAgentJobRegistry, globalAgentObservability, globalProcessRegistry, globalProcessRuntime, hasRepoDrifted, installAgentJobReaper, installExitReaper, installProcessReaper, messagesFromSnapshot, pruneOrphanWorktrees, reconstructMessages, removeWorktree, resolveSessionTitle, resolveTodoContinuationConfig, restoreFiles, revertDiff, touchesCapabilitySurface, touchesForbiddenZone, worktreeDiff };
3375
+ export { type AgentJobListener, AgentJobRegistry, type AgentJobRegistryOptions, type AgentJobStatus, type AgentObsStatus, type AgentObservabilityOptions, AgentObservabilityRegistry, type AgentObservation, type AgentObservationMeta, type AgentObservationSnapshot, type AgentRuntime, AgentSession, type AgentSessionEvent, type AgentSessionEventMap, type AgentSessionEventType, type AgentSessionInfo, type AgentSessionJob, type AgentSessionOptions, type AgentSessionSubscriber, type ApplyDiffResult, type ApplyPolicy, type ApplySignals, type ArchiveRecallPort, type AskUserQuestion, type AutonomyLevel, type BackgroundProcess, type BackgroundProcessStatus, type BasePlaneEntry, CAPABILITY_SURFACE_PATTERNS, type Checkpoint, type CheckpointData, type ColdSessionLister, type ContextBuildInput, type ContextInjectionPort, type ContextSource, ContextSourceRegistry, DEFAULT_COMPACTION_LOCK_ORPHAN_TIMEOUT_MS, DEFAULT_MAX_AUTO_APPLY_LINES, type DocSyncPort, ESCALATION_MARKER, EVENT_DOMAIN, type EmitContext, type EngineStoreLocation, type EngineStoreOverrides, type EventDomain, type ExitResult, FORBIDDEN_ZONE_PATTERNS, type FileEntrySnapshot, type FileSnapshot, FleetMonitor, type FleetMonitorOptions, type FleetSample, type GrillAnswer, type GrillQuestion, type GrillRequest, type HostPlaneDraft, type HostPlaneEntry, HostRegistry, type KillOptions, LEVEL_DIVISOR, type LlmEscalationReviewerDeps, type ManagedProcess, type MemoryDeltaPort, MemoryGovernor, type MemoryGovernorOptions, type MemoryPort, type MemoryPressureLevel, type MemorySample, type MemorySubsystem, PanelStateStore, PersistenceSync, type PersistenceSyncDeps, type PlaneEntry, type ProcessCategory, type ProcessFilter, type ProcessLifecycle, type ProcessListener, type ProcessOwner, ProcessRegistry, type ProcessRegistryOptions, ProcessRuntime, type ProcessRuntimeOptions, type ProcessSpawnConfig, type ProcessStatus, type PromptOptions, type PromptRefresh, type PromptSectionDefinition, RUNTIME_DEFAULTS, type RegisterAgentJobInput, type RegisterInput, type RegistrationPlane, type RemoteSessionPoolOptions, type ReplayLevel, type ResidencyDiagnostics, ResidencyGovernor, type ResidencyPressureLevel, type ResolvedSessionConfig, type RestoreMode, type RuntimeOptions, SYSTEM_PROMPT_SECTIONS, type SandboxRunner, type SandboxToolCall, type SandboxToolResult, SandboxUnavailableError, type SemanticReviewer, type SemanticReviewerVerdict, type SessionCompactionLockPort, SessionPool, type SessionPoolOptions, type SessionPorts, type SessionPresetDraft, type SessionPresetEntry, type SessionStableInjectionOptions, type SessionStorageConfig, type StalePlaneEntry, type SystemPromptSectionSpec, TimeTravelController, type TimeTravelDeps, type TodoReminderPort, type TracebackView, type TurnRecord, type Verdict, type WorkspacePresetDraft, type WorkspacePresetEntry, type Worktree, type WriteLeaseManager, applyDiff, applyMemoryTransform, applySectionBudget, attachAgentObserver, buildEngineStores, captureFiles, checkApply, createAgentRuntime, createDefaultContextSourceRegistry, createDocSyncReminderHooks, createEnvironmentContextSource, createInMemorySessionPool, createLlmEscalationReviewer, createMemoryDeltaHooks, createMemoryIndexContextSource, createRemoteSessionPool, createSessionCompactionLock, createSessionStableInjectionHooks, createTodoReminderHooks, createToolHookExecutor, createWorktree, decideApply, decide as decideResidency, defaultMaxToolTurnExtensions, definePromptSection, deriveSessionTitle, diffTouchedPaths, effectiveResidencyBudget, formatDocSyncReminder, getEventDomain, globalAgentJobRegistry, globalAgentObservability, globalProcessRegistry, globalProcessRuntime, globalToolResultStore, hasRepoDrifted, installAgentJobReaper, installExitReaper, installProcessReaper, isEscalationApproval, listWorktreeJobIds, messagesFromSnapshot, parseVerdict, pruneOrphanWorktrees, reconstructMessages, removeWorktree, resolveSessionTitle, resolveTodoContinuationConfig, restoreFiles, revertDiff, touchesCapabilitySurface, touchesForbiddenZone, worktreeDiff };
3021
3376
  //# sourceMappingURL=index.d.ts.map