@x-otto/runtime 0.0.1-alpha.0 → 0.0.1-alpha.10

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,210 @@
1
- import { CostSummary, Message, Model, ModelResolution, ProviderRegistry, StreamEvent, ThinkingLevel } from "@x-otto/ai";
1
+ import { CostSummary, Message, Model, ModelResolution, ProviderRegistry, 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
5
  import { InMemorySession, LeaseInspection, Session, SessionEntry, SessionPersistence, SessionSnapshot } from "@x-otto/session";
6
6
  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";
7
+ import { Agent, AgentEvent, AgentMessage, AgentSessionEvent, AgentSessionEventMap, AgentSessionEventType, AgentSessionSubscriber, AgentTool, ApprovalRisk, AskUserQuestion, ClockPort, DescribeImagesPort, GrillAnswer, GrillQuestion, GrillRequest, IdPort, MemoryPort, MemoryPort as MemoryPort$1, RandomPort, StreamFunction, StreamRetryConfig, ToolHookExecutor, ToolResult, TraceEvent, TraceRecorder, applyMemoryTransform } from "@x-otto/agent";
8
8
  import * as _$_x_otto_session_contract0 from "@x-otto/session-contract";
9
9
  import { ResidencyBudgetConfig, TurnRecord, TurnRecord as TurnRecord$1 } from "@x-otto/session-contract";
10
10
  import { ChildProcess, SpawnOptions } from "node:child_process";
11
11
  import { Devtools } from "@x-otto/devtools";
12
12
 
13
+ //#region src/plane/host-registry.d.ts
14
+ type RegistrationPlane = 'host' | 'preset';
15
+ /** 注册条目公共字段(平面无关)。 */
16
+ interface BasePlaneEntry {
17
+ /** 条目唯一 id(格内唯一;插件注册约定 `plugin:<pluginId>` / `waterfall:<timing>`)。 */
18
+ id: string;
19
+ /** 贡献类型标签('plugin' 身份声明 / 'waterfall' 贡献 / 未来轴)。 */
20
+ kind: string;
21
+ /** 贡献载荷(注册者语义自定)。 */
22
+ value: unknown;
23
+ /** 来源插件 id(非插件来源的注册可省略)。 */
24
+ pluginId?: string;
25
+ /**
26
+ * R6':来源插件 manifest 版本(信任绑定版本——插件更新后需重新评估白名单,
27
+ * trust 不跨版本自动存续)。非插件来源注册可省略。
28
+ */
29
+ pluginVersion?: string;
30
+ }
31
+ /**
32
+ * host 平面条目草稿(进程级单例)。`plane: 'host'` + `scope: 'process'` 为编译期
33
+ * 字面量——只能传给 HostRegistry.register(),传进任何 PRESET 格注册 API 即编译错误。
34
+ */
35
+ type HostPlaneDraft = BasePlaneEntry & {
36
+ readonly plane: 'host';
37
+ readonly scope: 'process';
38
+ };
39
+ /** PRESET workspace 格条目草稿——`scope: 'workspace'` 只能传给 workspace 格的注册 API。 */
40
+ type WorkspacePresetDraft = BasePlaneEntry & {
41
+ readonly plane: 'preset';
42
+ readonly scope: 'workspace';
43
+ };
44
+ /** PRESET 会话格条目草稿——`scope: 'session'` 只能传给会话格的注册 API。 */
45
+ type SessionPresetDraft = BasePlaneEntry & {
46
+ readonly plane: 'preset';
47
+ readonly scope: 'session';
48
+ };
49
+ /** 已盖章的 host 平面条目(无附加键)。 */
50
+ interface HostPlaneEntry extends HostPlaneDraft {}
51
+ /** 已盖章的 PRESET workspace 条目——workspaceKey 由格在注册时盖章(调用方不可自报)。 */
52
+ interface WorkspacePresetEntry extends WorkspacePresetDraft {
53
+ /** 所属 workspace key(`ws_<uuid>`)。由格盖章,非调用方自报。 */
54
+ workspaceKey: string;
55
+ }
56
+ /** 已盖章的 PRESET 会话条目——sessionId 由格在注册时盖章(调用方不可自报)。 */
57
+ interface SessionPresetEntry extends SessionPresetDraft {
58
+ /** 所属会话 id。由格盖章,非调用方自报。 */
59
+ sessionId: string;
60
+ }
61
+ type PlaneEntry = HostPlaneEntry | WorkspacePresetEntry | SessionPresetEntry;
62
+ /** 发射端上下文(host 单点发射携带)。缺省字段 = 不匹配对应平面。 */
63
+ interface EmitContext {
64
+ /** 发射时已知的会话 id——会话格条目仅当匹配才可达。 */
65
+ sessionId?: string;
66
+ /** 发射时已知的 workspace key——workspace 格条目仅当匹配才可达。 */
67
+ workspaceKey?: string;
68
+ }
69
+ /** R6' 版本漂移条目(插件更新后残留的旧版本注册,需重新评估)。 */
70
+ interface StalePlaneEntry {
71
+ entry: PlaneEntry;
72
+ /** 注册时快照的版本。 */
73
+ registeredVersion: string | undefined;
74
+ /** 插件当前 manifest 版本(可能缺省——manifest 无版本字段)。 */
75
+ currentVersion: string | undefined;
76
+ }
77
+ /** PRESET workspace 格(注册 + 列表 + 清理)。 */
78
+ declare class WorkspacePresetGrid {
79
+ /** workspace key(`ws_<uuid>`)——构造时由 HostRegistry.workspace() 传入。 */
80
+ readonly key: string;
81
+ private readonly entries;
82
+ /** 已 dispose(格关闭后拒绝新注册,fail-closed)。 */
83
+ private disposed;
84
+ constructor(/** workspace key(`ws_<uuid>`)——构造时由 HostRegistry.workspace() 传入。 */
85
+
86
+ key: string);
87
+ /**
88
+ * 注册一条 workspace 级 PRESET 贡献。同 id 重复注册 = 整体替换(R6':新评估覆盖
89
+ * 旧评估,不继承——插件更新后经 unregister→register 全量替换)。
90
+ */
91
+ register(draft: WorkspacePresetDraft): WorkspacePresetEntry;
92
+ unregister(id: string): boolean;
93
+ /** 撤销某插件的全部条目(pluginId 匹配)。返回撤销条数。 */
94
+ unregisterByPlugin(pluginId: string): number;
95
+ list(): readonly WorkspacePresetEntry[];
96
+ /** workspace 关闭 → 清空本格。幂等。 */
97
+ dispose(): void;
98
+ }
99
+ /** PRESET 会话格(注册 + 列表 + 清理)。 */
100
+ declare class SessionPresetGrid {
101
+ readonly sessionId: string;
102
+ private readonly entries;
103
+ private disposed;
104
+ constructor(sessionId: string);
105
+ register(draft: SessionPresetDraft): SessionPresetEntry;
106
+ unregister(id: string): boolean;
107
+ unregisterByPlugin(pluginId: string): number;
108
+ list(): readonly SessionPresetEntry[];
109
+ /** 会话 dispose → 清空本格。幂等。 */
110
+ dispose(): void;
111
+ }
112
+ /** PRESET workspace 作用域句柄——`workspace.preset.register()`(RFC-327 D2 API 形状)。 */
113
+ declare class WorkspacePresetScope {
114
+ readonly workspaceKey: string;
115
+ /**
116
+ * PRESET workspace 格。注册 API 形状 = `workspace.preset.register(draft)`。
117
+ * 语义同 WorkspacePresetGrid(注册/撤销/列表/清理),随 workspace 生命周期。
118
+ */
119
+ readonly preset: WorkspacePresetGrid;
120
+ constructor(workspaceKey: string);
121
+ /** workspace 关闭 → 清 workspace 格(R2':HOST 桶不受影响)。幂等。 */
122
+ dispose(): void;
123
+ }
124
+ /** PRESET 会话作用域句柄——`session.preset.register()`(RFC-327 D2 API 形状)。 */
125
+ declare class SessionPresetScope {
126
+ readonly sessionId: string;
127
+ /**
128
+ * PRESET 会话格。注册 API 形状 = `session.preset.register(draft)`。
129
+ * 语义同 SessionPresetGrid(注册/撤销/列表/清理),随会话生命周期。
130
+ */
131
+ readonly preset: SessionPresetGrid;
132
+ constructor(sessionId: string);
133
+ /** 会话 dispose → 清会话格(R2':HOST 桶与 workspace 格不受影响)。幂等。 */
134
+ dispose(): void;
135
+ }
136
+ /**
137
+ * HostRegistry —— 进程级单例注册表(RFC-327 修订 D2,T-327-M2-01/02)。
138
+ *
139
+ * 三桶:host 平面(进程级单例)+ PRESET workspace 格(按 workspaceKey)+ PRESET
140
+ * 会话格(按 sessionId)。生命周期=进程;dispose() 清空全部。
141
+ *
142
+ * 编译期跨平面拒绝(R2'):register 只收 HostPlaneDraft(plane:'host'),
143
+ * workspace/session 格的注册 API 只收各自字面量草稿——跨平面注册在类型层即失败。
144
+ */
145
+ declare class HostRegistry {
146
+ private readonly hostEntries;
147
+ private readonly workspaceScopes;
148
+ private readonly sessionScopes;
149
+ private disposed;
150
+ private ensureNotDisposed;
151
+ /** host 平面注册(进程级单例)。`host.register()`(RFC-327 D2 API 形状)。 */
152
+ register(draft: HostPlaneDraft): HostPlaneEntry;
153
+ unregister(id: string): boolean;
154
+ /** 撤销某插件在 host 平面的全部条目。返回撤销条数。 */
155
+ unregisterByPlugin(pluginId: string): number;
156
+ /** host 平面条目(进程级单例,不随任何会话/workspace 清理)。 */
157
+ list(): readonly HostPlaneEntry[];
158
+ /**
159
+ * PRESET workspace 格句柄(ensure 语义——workspace 挂载/首次注册时建格,幂等)。
160
+ * 注册 API 形状 = `workspace.preset.register(draft)`。
161
+ */
162
+ workspace(workspaceKey: string): WorkspacePresetScope;
163
+ /**
164
+ * PRESET 会话格句柄(ensure 语义——会话创建时派生会话格,幂等)。
165
+ * 注册 API 形状 = `session.preset.register(draft)`。
166
+ */
167
+ session(sessionId: string): SessionPresetScope;
168
+ /** workspace 关闭 → 清 workspace 格(不重建;不存在即 no-op)。 */
169
+ disposeWorkspaceGrid(workspaceKey: string): void;
170
+ /**
171
+ * RFC-327 修订 D2(M2,应用点 B 建议修订):把 `fromKey` 格的条目迁移到 `toKey` 格。
172
+ *
173
+ * 场景:pendingWorkspaceInit(交互态首次打开待信任确认)路径下插件 reload 先于
174
+ * workspaceRef 就位,PMM 用 DEFAULT_WORKSPACE_KEY('default')兜底注册;真实 key
175
+ * 就位后(completeWorkspaceDependentStartup)必须把 default 格条目迁到真实格,
176
+ * 否则两格并存、发射端按真实 key 路由查不到插件条目。
177
+ *
178
+ * 语义:条目按 id 迁移(整体搬,不重新盖章之外不改变语义);fromKey 格迁移后清空
179
+ * 且 dispose(fail-closed,防后续误注册到已废弃格)。fromKey 不存在 / toKey===fromKey
180
+ * 均为 no-op。只迁移 workspace 级条目(host/会话格不涉及)。
181
+ */
182
+ migrateWorkspaceGrid(fromKey: string, toKey: string): void;
183
+ /** 会话 dispose → 清会话格(不重建;不存在即 no-op)。 */
184
+ disposeSessionGrid(sessionId: string): void;
185
+ /** 撤销某插件在全部平面(host + 全部 workspace 格 + 全部会话格)的条目。返回撤销条数。 */
186
+ unregisterByPluginEverywhere(pluginId: string): number;
187
+ /**
188
+ * R6' 版本漂移检出:返回 pluginId 名下所有注册版本 ≠ 当前版本的条目。
189
+ * 插件更新后,正常 reload 走 unregister→register 全量替换;本方法兜底检出
190
+ * 「旧版本条目残留」(脏插件/撤销失败等异常路径)——调用方应据此重新评估
191
+ * 白名单(trust 不跨版本自动存续)。`currentVersion` 为 undefined(manifest
192
+ * 无版本)时,无版本条目视为一致、带版本条目视为漂移(需重新评估)。
193
+ */
194
+ revalidatePlugin(pluginId: string, currentVersion: string | undefined): StalePlaneEntry[];
195
+ /**
196
+ * 发射端路由(host 单点发射携带上下文)——按上下文过滤 PRESET 注册:
197
+ * host 平面条目恒达(进程级单例);
198
+ * workspace 格条目仅当 ctx.workspaceKey 匹配;
199
+ * 会话格条目仅当 ctx.sessionId 匹配(s1 事件不达 s2 的 PRESET)。
200
+ */
201
+ resolveForEmit(ctx: EmitContext): readonly PlaneEntry[];
202
+ /** 全部平面的全部条目(诊断/审计用)。 */
203
+ listAll(): readonly PlaneEntry[];
204
+ /** 进程生命周期结束 → 清空全部平面(host + workspace 格 + 会话格)。幂等。 */
205
+ dispose(): void;
206
+ }
207
+ //#endregion
13
208
  //#region src/context-source.d.ts
14
209
  interface ContextSource {
15
210
  /** 本源的唯一标识。重复注册→覆盖前一次同 id 源。 */
@@ -57,6 +252,10 @@ declare class ContextSourceRegistry {
57
252
  sealAndBuildHooks(workspaceDir?: string, memory?: ContextBuildInput['memory']): HookSpec[];
58
253
  }
59
254
  //#endregion
255
+ //#region src/session/semantic-reviewer.d.ts
256
+ type SemanticReviewerVerdict = 'allow' | 'deny' | 'abstain';
257
+ type SemanticReviewer = (toolName: string, args: Record<string, unknown>, description: string, risk?: ApprovalRisk) => Promise<SemanticReviewerVerdict>;
258
+ //#endregion
60
259
  //#region src/context-sources/memory-delta.d.ts
61
260
  /**
62
261
  * memory-delta(#3 volatile memory delta)
@@ -184,6 +383,12 @@ interface ResolvedSessionConfig {
184
383
  transient?: boolean;
185
384
  depth?: number;
186
385
  interactive?: boolean;
386
+ /**
387
+ * 是否在 `/resume` 列表中可见(缺省 = 可见)。`false` 用于 headless 一次性内部会话
388
+ * (`otto --print/--json`)——照常落盘(sessionId/trace 有外部契约消费方),但不进列表。
389
+ * 详见 `PersistedSessionConfig.listable`。
390
+ */
391
+ listable?: boolean;
187
392
  retryAccelerate?: {
188
393
  skip: boolean;
189
394
  };
@@ -191,6 +396,11 @@ interface ResolvedSessionConfig {
191
396
  streamRetry?: StreamRetryConfig;
192
397
  /** RFC-230:网络断连重试策略投影(从宿主 `ResilienceConfig.networkDisconnect` 而来)。缺省 = agent 包内部默认值。 */
193
398
  networkDisconnectRetry?: StreamRetryConfig;
399
+ /**
400
+ * RFC-366:lifecycleAsync 工具等待的挂死兜底超时(毫秒)。缺省 undefined = 不启用。
401
+ * headless/bench 经 `OTTO_LIFECYCLE_ASYNC_TIMEOUT_MS` 环境变量启用。
402
+ */
403
+ lifecycleAsyncTimeoutMs?: number;
194
404
  /**
195
405
  * 会话级「始终允许」工具名(弹窗运行时授权)。随快照持久化,--continue 回灌 PermissionRegistry
196
406
  * 会话级集合——一次性授权跟随会话、不泄漏到项目其它会话。宿主(App)据此桥接 registry↔持久化。
@@ -276,7 +486,25 @@ interface AgentSessionOptions {
276
486
  nondetFlush?: () => void;
277
487
  clock?: ClockPort;
278
488
  depth?: number;
489
+ /**
490
+ * RFC-371 M-A:父任务锚点(任务树语义)。orchestrator 委派子会话时注入
491
+ * `RunSessionOptions.parentTaskId`,透传 Agent → ToolExecutor → 工具上下文 →
492
+ * task_delegate/agent_call → orchestrator per-parent fan-out 闸。undefined = root 层。
493
+ */
494
+ parentTaskId?: string;
279
495
  interactive?: boolean;
496
+ /**
497
+ * RFC-380 T4:LLM 语义审批层(opt-in)。仅升级审批(sandbox-denied escalate ask)
498
+ * 且配置时生效;allow/deny 自动裁决(宿主侧需消费 approval.required 的
499
+ * semanticVerdict 记命令 bypass),abstain/未配置 → 既有用户弹窗流程。
500
+ */
501
+ semanticReviewer?: SemanticReviewer;
502
+ /**
503
+ * 是否在 `/resume` 列表中可见(缺省 = 可见)。`false` 用于 headless 一次性内部会话
504
+ * (`otto --print/--json`)——照常落盘(sessionId/trace 有外部契约消费方),但不进列表。
505
+ * 详见 `PersistedSessionConfig.listable`。
506
+ */
507
+ listable?: boolean;
280
508
  retryAccelerate?: {
281
509
  skip: boolean;
282
510
  };
@@ -284,6 +512,8 @@ interface AgentSessionOptions {
284
512
  streamRetry?: StreamRetryConfig;
285
513
  /** RFC-230:网络断连重试策略投影,见 `ResolvedSessionConfig.networkDisconnectRetry` 同名字段注释。 */
286
514
  networkDisconnectRetry?: StreamRetryConfig;
515
+ /** RFC-366:lifecycleAsync 挂死兜底超时(毫秒),见 `ResolvedSessionConfig.lifecycleAsyncTimeoutMs` 同名字段注释。 */
516
+ lifecycleAsyncTimeoutMs?: number;
287
517
  /**
288
518
  * RFC-181 D3/M3:图像委托描述端口(宿主 VisionDelegateService 实现,coding 层在会话
289
519
  * 装配时注入)。缺省 = 主模型不支持视觉时,image block 降级为占位文本(见
@@ -329,6 +559,12 @@ interface PromptOptions {
329
559
  mediaType: string;
330
560
  }>;
331
561
  streamingBehavior?: 'steer' | 'followUp';
562
+ /**
563
+ * RFC-363 D2:内部标志——续跑上一回合(不重复 append user message)。
564
+ * **不对外**:外部调用方应使用 `AgentSession.retryLastRound()`,它带前置校验。
565
+ * 此处保留是因为 `executePrompt` 是 `prompt()`/`retryLastRound()` 共用的执行体。
566
+ */
567
+ resumeLastRound?: boolean;
332
568
  }
333
569
  type PromptRefresh = () => Promise<string | undefined>;
334
570
  //#endregion
@@ -387,6 +623,26 @@ declare class AgentSession extends EventBus<AgentSessionEventMap> {
387
623
  systemTail?: string[];
388
624
  agentName: string;
389
625
  depth: number;
626
+ /**
627
+ * RFC-371 M-A:父任务锚点(透传面)。orchestrator 委派子会话时注入
628
+ * `runOptions.parentTaskId`,Agent 构造经 `AgentConfig.parentTaskId` 透传给
629
+ * ToolExecutor——task_delegate/agent_call 的 per-parent fan-out 闸依赖此字段。
630
+ * undefined = root 层(顶层用户会话),不受 fan-out 闸约束。
631
+ */
632
+ readonly parentTaskId?: string;
633
+ /**
634
+ * orchestrator ephemeral 委派子会话标记 —— 契约上不落盘(见
635
+ * `PersistenceSync.isTransient`)、不进 `/resume` 列表。构造期固定,运行中不变。
636
+ * 此前该事实只存在于 SessionPool 私有的 `configs` Map 里,池外消费方(如
637
+ * `listAllSessions` 的 /resume 过滤)拿不到,只能靠标题猜;提为公开只读字段后
638
+ * 与既有的 `depth` 一样可直接判定。
639
+ */
640
+ readonly transient: boolean;
641
+ /**
642
+ * 是否在 `/resume` 列表可见(缺省 true)。headless 一次性会话(`otto --print/--json`)
643
+ * 置 false —— 照常落盘但不进列表。详见 `PersistedSessionConfig.listable`。
644
+ */
645
+ readonly listable: boolean;
390
646
  maxToolTurns: number;
391
647
  maxToolTurnExtensions: number;
392
648
  /** RFC-104 D3:per-prompt 预算(fork 拷贝面;depth>0 构造时已被剥除)。 */
@@ -403,7 +659,9 @@ declare class AgentSession extends EventBus<AgentSessionEventMap> {
403
659
  /** RFC-145 D3:nondet batch 冲洗回调。 */
404
660
  private readonly nondetFlush?;
405
661
  private readonly clock?;
406
- /** RFC-337 D2:steer 消息 id 单调计数器(会话内唯一,供 ESC 撤回按序移除)。 */
662
+ /** RFC-337 D2:steer 消息 id 单调计数器(会话内唯一,供 ESC 撤回按序移除)。
663
+ * RFC-372 S7:改为 `{ value: number }` 对象——steer 逻辑提取到 agent-session-steer.ts
664
+ * 后需经引用传递(number 是值类型,函数内 ++ 不会回传)。 */
407
665
  private steerSeq;
408
666
  private readonly persistence;
409
667
  /**
@@ -418,18 +676,10 @@ declare class AgentSession extends EventBus<AgentSessionEventMap> {
418
676
  private readonly gates;
419
677
  private readonly interactive;
420
678
  private readonly agentUnsubs;
421
- private readonly costTracker;
422
679
  /** RFC-204 D1/D2:会话级成本软预算阈值(美元);undefined = 不启用检测。 */
423
680
  private readonly sessionCostBudgetUSD?;
424
- /** RFC-204 D2:本会话是否已越过成本预算并提醒过(一次性,session 生命周期内不重复)。 */
425
- private sessionCrossedCostBudget;
426
- private lastTokenUsage;
427
- /**
428
- * RFC-019 M1:本回合缓存命中率(R1 = cacheRead / totalInput)。
429
- * `null` = 供应商无缓存 API 或本回合无 usage 数据 —— 消费方(TUI summary 行)据此隐藏该字段,
430
- * 不展示可能永远是 0 的误导性数字(见 docs/rfc/RFC-019-cache-hit-rate-metrics.md)。
431
- */
432
- private lastCacheHitRatio;
681
+ /** 用量/成本统计,RFC-353 S7 提取(agent-session-usage-tracker.ts)。 */
682
+ private readonly usageTracker;
433
683
  /** RFC-094:todo 停机闭环硬闸(post-RFC-092 review F7 拆出为独立 concern)。 */
434
684
  private readonly todoGateRunner;
435
685
  /**
@@ -461,6 +711,8 @@ declare class AgentSession extends EventBus<AgentSessionEventMap> {
461
711
  * task-runner 在跑 task_delegate/fork 子会话时据此把运行态接入 globalAgentObservability。
462
712
  */
463
713
  get coreAgent(): Agent;
714
+ /** RFC-372 S7:steer 集群的依赖面(供 agent-session-steer.ts 消费)。 */
715
+ private get steerDeps();
464
716
  get isExecuting(): boolean;
465
717
  /**
466
718
  * prompt 全生命周期忙判定。isExecuting 只覆盖 streaming/tool_executing,
@@ -485,24 +737,6 @@ declare class AgentSession extends EventBus<AgentSessionEventMap> {
485
737
  * 此时 EventBridge 已完成订阅,session.start 事件可被正确接收。
486
738
  */
487
739
  notifyCreated(): void;
488
- onStreamEvent: ({
489
- event
490
- }: {
491
- event: StreamEvent;
492
- }) => Promise<void>;
493
- private onTurnStart;
494
- private onTurnEnd;
495
- private onToolCallStart;
496
- private onToolCallEnd;
497
- /**
498
- * RFC-094 D2:todoList 会话持久化单源基线——write_todos 成功后把权威快照
499
- * (result.details.todos,set/update 均为合并后完整列表)写进 session metadata。
500
- * 修活非 CLI 前端(service/ACP)的 todo-reminder:其数据源 = metadata().config.todoList,
501
- * 此前全仓只有 CLI 投影层回写,service 模式下 reminder 永不注入。
502
- * 保序:publish 之前同步写 baseline;CLI 订阅回调随后覆写 turn-enriched 版(携回合号)。
503
- * 既有条目的 turn 按 id 保留,runtime 不知道回合投影、不清 CLI 写入的 turn。
504
- */
505
- private syncTodoListFromToolResult;
506
740
  /** 本次 prompt 是否触发了预算收尾 steering(供 runPrintMode 在优雅路径发提示)。 */
507
741
  budgetSteeredThisPrompt: boolean;
508
742
  /**
@@ -524,14 +758,36 @@ declare class AgentSession extends EventBus<AgentSessionEventMap> {
524
758
  /** 取消置顶。无条件清空分组(不保留旧分组,见 RFC-158 §4)。 */
525
759
  unpin(): void;
526
760
  prompt(text: string, options?: PromptOptions): Promise<void>;
761
+ /**
762
+ * RFC-363 D2:**重试上一回合**——只重发模型请求,不把 prompt 二次压进历史。
763
+ *
764
+ * 使用场景:引擎内层退避(work-loop 的 429/5xx/断连重试)耗尽后错误冒泡到 CLI,
765
+ * 用户在 ErrorRetryCard 点「重试」或自动倒计时到期。此时那条 user message 已经
766
+ * 在历史里(prep 成功过,且 RFC-336 已把它落库),走正常 `prompt()` 会 append 第二条。
767
+ *
768
+ * 前置条件:历史尾部存在可续跑的 user message。不满足时抛
769
+ * `RETRY_NO_RESUMABLE_ROUND`,由调用方决定如何回退(通常是改走正常 `prompt()`)——
770
+ * **决策权留在 CLI**:只有它知道用户当前想发什么文本,引擎不擅自替用户重造消息。
771
+ *
772
+ * 典型的不满足场景(均为正确行为,不是异常):
773
+ * - prep 阶段失败已 removeLast 回滚 → 历史里没有它,本就该走正常 prompt;
774
+ * - 失败与重试之间发生 compaction → 消息被折叠进摘要,不再是可直接重发的待处理消息;
775
+ * - fork/resume 后历史来自 DB 快照。
776
+ */
777
+ retryLastRound(): Promise<void>;
527
778
  /** prompt() 的执行体(RFC-194 D1 拆出——prompt() 需在 isBusy 守卫后拿到本次执行的 Promise)。 */
528
779
  private executePrompt;
529
780
  /** 跑一轮完整 prompt 管线(buildContext → workLoop → persist),回填会话级参数。
530
781
  * hadToolActivity:本轮是否有真实工具调用产出(assistant tool_call 消息),供闸门
531
782
  * 无进展判定区分"零动作卡死"(放行)与"有产出但没同步 todo 状态"(定向续跑一次)。
532
783
  * images(RFC-111 D4b):仅首轮(外层 `prompt()` 调用)传入,todo 续跑等内部触发轮
533
- * 不重复携带图片——避免同一张图片在续跑轮里被重复注入上下文。 */
784
+ * 不重复携带图片——避免同一张图片在续跑轮里被重复注入上下文。
785
+ *
786
+ * RFC-372 M3:实现提取到 agent-session-prompt-round.ts(runPromptRound 函数),
787
+ * 此处经 promptRoundHost getter 桥接全部依赖。纯结构重构,行为零变化。 */
534
788
  private runPromptRound;
789
+ /** RFC-372 M3:runPromptRound 的依赖面(供 agent-session-prompt-round.ts 消费)。 */
790
+ private get promptRoundHost();
535
791
  /**
536
792
  * RFC-337 D2:注入一条 steer 消息并返回其稳定 `id`(供 cli 侧持有以便 ESC 撤回)。
537
793
  *
@@ -542,11 +798,6 @@ declare class AgentSession extends EventBus<AgentSessionEventMap> {
542
798
  steer(text: string): {
543
799
  id: string;
544
800
  };
545
- /**
546
- * RFC-337 D2:按 id 移除一条尚未被模型消费的 steer 消息,返回是否命中。
547
- * 未命中(已被 `drainSteeringQueue` 交付进 LLM 上下文)时返回 `false`——调用方据此退化
548
- * 为「中止整回合」(见 RFC-337 §D3)。转调 `Agent.removeSteer`,只操作内存态队列。
549
- */
550
801
  removeSteer(id: string): boolean;
551
802
  followUp(text: string): void;
552
803
  abort(): void;
@@ -595,7 +846,6 @@ declare class AgentSession extends EventBus<AgentSessionEventMap> {
595
846
  clearTurnPanels(): void;
596
847
  /** 重命名会话(custom 等级,永不被 ai/derived 覆盖)。 */
597
848
  renameSession(title: string): void;
598
- private promptUsage;
599
849
  /** H5-20:本会话累计用量 + 成本汇总(供 /cost 命令 / TUI 状态栏消费)。 */
600
850
  getCostSummary(): CostSummary;
601
851
  /**
@@ -875,9 +1125,15 @@ declare class PanelStateStore {
875
1125
  * residency-governor.ts — RFC-178 触发器 C:内存压力驱动的会话驻留收紧。
876
1126
  *
877
1127
  * 把 MemoryGovernor 的 warning/critical 分级信号翻译成**驻留维度**的动态收紧系数:
878
- * - healthy → 系数 1(cap 用全值:条数 2000 / 字节 256MB)
879
- * - warning → 系数 1/2(条数 1000 / 字节 128MB)
880
- * - critical → 系数 1/4(条数 500 / 字节 64MB)
1128
+ * - healthy → 系数 1(cap 用全值)
1129
+ * - warning → 系数 1/2
1130
+ * - critical → 系数 1/4
1131
+ *
1132
+ * 系数作用于调用方传入的 base(`effectiveMaxMessages`/`effectiveMaxBytes` 的入参),
1133
+ * 本类不持有具体数值——base 由 `residency-action-coordinator` 注入,条数来自
1134
+ * `SESSION_MAX_HISTORY_MESSAGES`(RFC-321 D7 起为 50000 结构兜底闸),字节来自
1135
+ * 分配器的 `maxBytesPerSession`。此处刻意不写死具体数字:早期注释举例的"条数 2000"
1136
+ * 在 D7 上调后长期未同步,反而误导读者(2026-08-12 复审移除)。
881
1137
  *
882
1138
  * 设计约束(RFC-178 D1/D4):
883
1139
  * - **离散阶梯**而非连续值——内存采样受 GC 时机影响非确定,阶梯化把非确定性收敛到
@@ -941,6 +1197,15 @@ interface SessionPoolOptions {
941
1197
  * 确定性时钟端口。缺省=真实系统时钟(行为不变);replay 由 trace 回灌。
942
1198
  */
943
1199
  clock?: ClockPort;
1200
+ /**
1201
+ * 确定性 id 生成端口(M-2)。缺省=systemId(crypto.randomUUID);replay/测试注入固定序列。
1202
+ * 透传给 InMemorySession 与 fork 产物。
1203
+ */
1204
+ idGen?: IdPort;
1205
+ /**
1206
+ * 确定性随机端口(M-2)。缺省=systemRandom(Math.random);replay 由 trace 回灌。
1207
+ */
1208
+ randomGen?: RandomPort;
944
1209
  /**
945
1210
  * 仅用于 restore / restoreAll 场景。
946
1211
  * 正常 createSession 路径必须传入完整 ResolvedSessionConfig,不使用此字段。
@@ -960,6 +1225,13 @@ interface SessionPoolOptions {
960
1225
  residencyGovernor?: ResidencyGovernor;
961
1226
  /** RFC-323 M4:驻留预算运行时配置 getter(settings 注入,透传给 PersistenceSync)。 */
962
1227
  getResidencyConfig?: () => _$_x_otto_session_contract0.ResidencyBudgetConfig | undefined;
1228
+ /**
1229
+ * RFC-327 修订 D2(M2):双平面注册边界——进程级 HostRegistry(可选注入)。
1230
+ * 注入后会话在进池(create/fork/restore 三个装池点)时派生 PRESET 会话格,
1231
+ * 出池(remove/idle 驱逐/LRU 驱逐/池 dispose)时清理会话格。未注入 = 不派生
1232
+ * (宿主不接双平面即零影响)。
1233
+ */
1234
+ planeRegistry?: HostRegistry;
963
1235
  /**
964
1236
  * RFC-146:本地高效冷会话枚举策略(可选注入)。仅本地 SQLite 后端在
965
1237
  * `session-pool-factory.ts` 构造期注入(内部持有 `SqliteSessionRepository`,
@@ -1023,6 +1295,26 @@ declare class SessionPool {
1023
1295
  private readonly panelStateStoreRef;
1024
1296
  private readonly sessions;
1025
1297
  private readonly configs;
1298
+ /**
1299
+ * 驻留时钟(in-memory 池的"这个会话是何时被装进池子的"),**与 `lastActiveAt` 分离**。
1300
+ *
1301
+ * 背景(/resume 时间戳全变"刚刚"的根因):`restore`/`restoreAll` 此前用
1302
+ * `{ ...snapshot.metadata, lastActiveAt: Date.now() }` 装载快照,把"恢复"当成了"活动"。
1303
+ * 该值随后被 `save()` 的 `touchSession(..., snapshot.metadata.lastActiveAt, ...)` 原样
1304
+ * 写回 DB —— 每次启动 `restoreAll` 恢复的会话(池容量 50)都被盖成当前时间且**永久落盘**,
1305
+ * 于是 `/resume` 列表里所有历史会话都显示成"N 秒前",真实活动时间被不可逆地抹掉,
1306
+ * 同时按 `lastActiveAt` 降序的排序也彻底失去意义(全部并列在同一时刻)。
1307
+ *
1308
+ * 但那个 `Date.now()` 并非笔误:它保护刚恢复的会话不被 `collectIdle` 立刻自食
1309
+ * (快照的 `lastActiveAt` 可能已是 99 小时前,一装载就超 idleTimeout,见
1310
+ * m17-session-persistence-e2e "M18-PR-03:restore 刷新 lastActiveAt=now(恢复不自食)")。
1311
+ *
1312
+ * 所以正确的切分是:**驱逐判据 = max(进池时刻, 真实活动时间),展示与排序只看
1313
+ * `lastActiveAt`(真实活动时间、落盘)**。本时钟只记"何时进的池"(create/fork/restore
1314
+ * 三个装池点登记,出池时清除),活动侧的续期由 `lastActiveAt` 天然承担——两者如何
1315
+ * 合成见 `residencySince`。本时钟是进程内易失状态,永不落盘,故不会污染 /resume。
1316
+ */
1317
+ private readonly residencyClock;
1026
1318
  private readonly persistence;
1027
1319
  private readonly maxSessions;
1028
1320
  private readonly idleTimeoutMs;
@@ -1036,8 +1328,12 @@ declare class SessionPool {
1036
1328
  private readonly traceStore?;
1037
1329
  private readonly checkpointStore?;
1038
1330
  private readonly clock?;
1331
+ private readonly idGen;
1332
+ private readonly randomGen;
1039
1333
  private readonly residencyGovernor?;
1040
1334
  private readonly getResidencyConfig?;
1335
+ /** RFC-327 修订 D2(M2):双平面注册边界——会话格派生/清理的宿主注册表(可选)。 */
1336
+ private readonly planeRegistry?;
1041
1337
  private readonly configProvider?;
1042
1338
  /** RFC-146:本地高效冷会话枚举策略(可选,见 SessionPoolOptions.listColdSessionsOverride)。 */
1043
1339
  private readonly listColdSessionsOverride?;
@@ -1078,6 +1374,31 @@ declare class SessionPool {
1078
1374
  /** 会话级「账号可用模型」读写(同 permissionAlwaysAllow 持久化语义)。 */
1079
1375
  getSessionAvailableModels(id: string): string[];
1080
1376
  setSessionAvailableModels(id: string, models: readonly string[]): void;
1377
+ /**
1378
+ * 登记"会话进池时刻"(create/fork/restore 三个装池点)。纯进程内状态,永不落盘,
1379
+ * 不会污染 `/resume` 展示的 `lastActiveAt`。活动侧续期见 `residencySince`。
1380
+ *
1381
+ * RFC-327 修订 D2(M2):同一装池点是会话格的派生点——派生 PRESET 会话格
1382
+ * (`session.preset.register()` 的目标)。会话级贡献随会话 dispose 清理
1383
+ * (见 clearSessionPlaneGrid 在四个出池点的调用)。
1384
+ */
1385
+ private admitToResidency;
1386
+ /** RFC-327 修订 D2(M2):会话出池 → 清理 PRESET 会话格(不重建;未派生即 no-op)。 */
1387
+ private clearSessionPlaneGrid;
1388
+ /**
1389
+ * 驱逐判据用的"最近驻留活动时刻" = **进池时刻与真实活动时间中较新的那个**。
1390
+ *
1391
+ * 两个来源各自只能守住一半,必须取 max:
1392
+ * - 只读 `lastActiveAt`:刚恢复的会话带着快照里可能 99 小时前的时间戳,一进池就超
1393
+ * idleTimeout 被自食(旧 M18-PR-03 要防的正是这个)。
1394
+ * - 只读驻留时钟:它仅在装池时登记、不随对话续期,判据会退化成"进池后过了多久"——
1395
+ * 用户连用两小时的会话反而因"进池早"被当成 idle 驱逐,且 `collectIdle` 并不豁免
1396
+ * `activeSessionId`,连当前正在用的会话都可能被踢出池。
1397
+ *
1398
+ * 取较新值后:恢复瞬间由进池时刻托底,此后每次真实活动经 `lastActiveAt` 自然续期,
1399
+ * 而真正长期无活动的会话两个值都旧,仍会被正常驱逐(不会变成"永不驱逐")。
1400
+ */
1401
+ private residencySince;
1081
1402
  /**
1082
1403
  * 保持既有 private 外观(createSession/fork/restore 调用点不变)。
1083
1404
  */
@@ -1168,15 +1489,15 @@ declare class SessionPool {
1168
1489
  * 不阻塞会话删除本身)。
1169
1490
  */
1170
1491
  private clearSessionStores;
1171
- /**
1172
- * fork 直接复用 source session 自身的已解析配置,
1173
- * 消息通过复制 snapshot entries 完整迁移。
1174
- */
1175
1492
  /**
1176
1493
  * RFC-160 D3/D4:出口 hydrate(fork 用)。无剥离态直通;有剥离态且持久层具备能力则回填;
1177
1494
  * 能力缺席 → 断言炸(接线遗漏立即暴露,绝不把剥离态固化为新会话正文)。
1178
1495
  */
1179
1496
  private ensureEntriesHydrated;
1497
+ /**
1498
+ * fork 直接复用 source session 自身的已解析配置,
1499
+ * 消息通过复制 snapshot entries 完整迁移。
1500
+ */
1180
1501
  forkSession(sourceId: string, newId?: string): Promise<AgentSession | undefined>;
1181
1502
  save(id: string): Promise<void>;
1182
1503
  saveAll(): Promise<void>;
@@ -1285,7 +1606,7 @@ type SessionStorageConfig = {
1285
1606
  pasteStatePersistence?: PasteStatePersistence;
1286
1607
  };
1287
1608
  interface RuntimeOptions {
1288
- /** LLM provider 注册表。缺省=createDefaultProviderRegistry() */
1609
+ /** LLM provider 注册表。缺省=createProviderRegistry()(空注册表,provider 由宿主插件装载)。 */
1289
1610
  providerRegistry?: ProviderRegistry;
1290
1611
  /** hook 注册表(tool.execute / system.prompt.transform 等的容器)。缺省=default preset。 */
1291
1612
  hookRegistry?: HookRegistry;
@@ -1316,6 +1637,13 @@ interface RuntimeOptions {
1316
1637
  residencyGovernor?: ResidencyGovernor;
1317
1638
  /** RFC-323 M4:驻留预算运行时配置 getter(settings 注入透传)。 */
1318
1639
  getResidencyConfig?: () => _$_x_otto_session_contract0.ResidencyBudgetConfig | undefined;
1640
+ /**
1641
+ * RFC-327 修订 D2(M2):双平面注册边界——进程级 HostRegistry(可选注入)。
1642
+ * 注入后会话池在会话创建/fork/restore 时派生 PRESET 会话格,会话移除/驱逐/池
1643
+ * dispose 时清理会话格(对齐「会话创建派生会话格、会话 dispose 清会话格」生命周期)。
1644
+ * 未注入 = 会话格不派生(行为不变,宿主不接双平面即零影响)。
1645
+ */
1646
+ planeRegistry?: HostRegistry;
1319
1647
  }
1320
1648
  interface AgentRuntime {
1321
1649
  /** 统一派生入口:子 runtime / 宿主 / swarm / task-runner 皆经此(§4-11)。 */
@@ -1559,11 +1887,6 @@ declare class MemoryGovernor {
1559
1887
  constructor(options?: MemoryGovernorOptions);
1560
1888
  /** 当前分级(供外部只读查询,如 TUI 状态展示)。 */
1561
1889
  get currentLevel(): MemoryPressureLevel;
1562
- /**
1563
- * 晚绑定 onWarning/onCritical/onCriticalDuringGrace(App 构造时活跃 session 尚不存在,
1564
- * M2/M3 在 session 就绪后调用注入)。对齐 `App.setScheduleService` 的晚绑定模式,
1565
- * 非构造期可选依赖。
1566
- */
1567
1890
  /**
1568
1891
  * 合并式更新(每个字段独立覆盖,未传的字段保留原值)——RFC-115 追补:`app.ts`
1569
1892
  * 启动期注入 `onSample`(trace 落盘)后,`interactive.ts` 会话就绪时再调用一次
@@ -1987,6 +2310,12 @@ interface ApplyDiffResult {
1987
2310
  * apply 可能出乎意料地冲突/无冲突",不用来拒绝 apply。
1988
2311
  */
1989
2312
  declare function hasRepoDrifted(repoRoot: string, base: string): boolean;
2313
+ /**
2314
+ * 纯预检:diff 能否干净 apply 到主仓工作区(`git apply --check`,无任何副作用)。
2315
+ * 供 governance/apply-policy 的 `conflict` 信号使用——在真正 `applyDiff` 之前判定"是否可干净
2316
+ * 落地"。空 diff 视为干净。true=无冲突可落地;false=有冲突/无法判定(交给 applyDiff 兜底)。
2317
+ */
2318
+ declare function checkApply(repoRoot: string, diff: string): boolean;
1990
2319
  /**
1991
2320
  * 把作业 diff apply 到主仓工作区。**check-first,不强改**:先 `git apply --check` 验证能否干净
1992
2321
  * 应用——能才真 apply;不能(主仓漂移/冲突)则返回 applied=false + conflictPaths,**绝不**往主树
@@ -2521,9 +2850,9 @@ declare function createDocSyncReminderHooks(port: DocSyncPort, priority?: 64): H
2521
2850
  *
2522
2851
  * 放在 @x-otto/runtime(coding 依赖 runtime、反向不依赖):coding 侧 hook 经 `@x-otto/runtime` import。
2523
2852
  * 注:本表是**排序权威**(方案 H:section 仍由各自 hook 自注册,priority 数字降级为对本表的引用);
2524
- * 不强行把 `SYSTEM_PROMPT_SECTIONS` 各 section 物理并入单一 registry(那超出外科尺度、非目标)——
2853
+ * 不强行把 `SYSTEM_PROMPT_SECTIONS` 各 section 物理并入单一 registry(那超出外科尺度、非目标)。
2525
2854
  * section 数量以该常量的字段数为准,不在本段注释里重复写死具体数字(history: 建表时 7 个,
2526
- * 后追加 docSyncReminder 成 8 个,写死数字会重犯本次发现的漂移)。
2855
+ * 后追加 docSyncReminder 成 8 个,又补 skillLoopGuidance 成 9 个——写死数字会重犯本次发现的漂移)。
2527
2856
  */
2528
2857
  type SystemPromptLane = 'stable' | 'volatile';
2529
2858
  interface SystemPromptSectionSpec {
@@ -2531,12 +2860,20 @@ interface SystemPromptSectionSpec {
2531
2860
  readonly name: string;
2532
2861
  readonly priority: number;
2533
2862
  readonly lane: SystemPromptLane;
2863
+ /**
2864
+ * RFC-374 M3:本 section 的注入字节硬顶(UTF-8)。注入点经
2865
+ * `applySectionBudget` 统一裁剪——预算只此一处声明,各注入点不持局部常量。
2866
+ * 取值原则:明显高于常态内容(防御失控注入而非改变正常行为),
2867
+ * 需要收紧时只改本表。
2868
+ */
2869
+ readonly budgetBytes: number;
2534
2870
  }
2535
2871
  declare const SYSTEM_PROMPT_SECTIONS: {
2536
2872
  readonly toolGuidance: {
2537
2873
  readonly name: "tool-guidance-injection";
2538
2874
  readonly priority: 20;
2539
2875
  readonly lane: "stable";
2876
+ readonly budgetBytes: 20000;
2540
2877
  };
2541
2878
  /**
2542
2879
  * RFC-318 D7:三分流判别(缺工具 / 流程重复 / 其余)。紧跟 toolGuidance——它是对
@@ -2546,43 +2883,83 @@ declare const SYSTEM_PROMPT_SECTIONS: {
2546
2883
  readonly name: "skill-loop-guidance-injection";
2547
2884
  readonly priority: 21;
2548
2885
  readonly lane: "stable";
2886
+ readonly budgetBytes: 2000;
2549
2887
  };
2550
2888
  readonly environment: {
2551
2889
  readonly name: "runtime:environment";
2552
2890
  readonly priority: 25;
2553
2891
  readonly lane: "stable";
2892
+ readonly budgetBytes: 8000;
2554
2893
  };
2555
2894
  readonly memoryIndex: {
2556
2895
  readonly name: "runtime:memory-index";
2557
2896
  readonly priority: 35;
2558
2897
  readonly lane: "stable";
2898
+ readonly budgetBytes: 24000;
2559
2899
  };
2560
2900
  readonly skillCatalog: {
2561
2901
  readonly name: "skill-catalog-injection";
2562
2902
  readonly priority: 38;
2563
2903
  readonly lane: "stable";
2904
+ readonly budgetBytes: 12000;
2564
2905
  };
2906
+ /** RFC-374 M3 初始 8KB 低于 RFC-382 T5/T6 重建后的常态内容(8.5KB 实测,2026-08-14)——
2907
+ * 字节顶须 > 常态内容(防御失控注入而非改变正常行为),否则每次构建静默截掉 pack 尾部。
2908
+ * 10KB 维持 ~1.2x 防御余量;token 侧仍受 RFC-284 8k soft/12k hard 预算约束。 */
2565
2909
  readonly lesson: {
2566
2910
  readonly name: "lesson-injection";
2567
2911
  readonly priority: 40;
2568
2912
  readonly lane: "stable";
2913
+ readonly budgetBytes: 10000;
2914
+ }; /** RFC-318 R7 治理补全:lesson-relevant-hints 原为表外 hook,现纳入统一预算面(volatile 尾段)。 */
2915
+ readonly lessonRelevantHints: {
2916
+ readonly name: "lesson-relevant-hints";
2917
+ readonly priority: 41;
2918
+ readonly lane: "volatile";
2919
+ readonly budgetBytes: 4000;
2920
+ };
2921
+ /**
2922
+ * RFC-383 M2(§9 修订 6):高频 skill 内化指令核心——引擎内部 hook
2923
+ * `internalize-skill-injection` 的 volatile 尾段预算面。**必须注册**:不注册则
2924
+ * applySectionBudget 原样放行、4KB 硬顶失效(决策器提炼不受控)。priority 42 排在
2925
+ * lessonRelevantHints(41) 之后、memoryDelta(60) 之前。
2926
+ */
2927
+ readonly internalize: {
2928
+ readonly name: "internalize-skill-injection";
2929
+ readonly priority: 42;
2930
+ readonly lane: "volatile";
2931
+ readonly budgetBytes: 4000;
2569
2932
  };
2570
2933
  readonly memoryDelta: {
2571
2934
  readonly name: "memory-delta-injection";
2572
2935
  readonly priority: 60;
2573
2936
  readonly lane: "volatile";
2937
+ readonly budgetBytes: 8000;
2574
2938
  };
2575
2939
  readonly todoReminder: {
2576
2940
  readonly name: "todo-reminder-injection";
2577
2941
  readonly priority: 62;
2578
2942
  readonly lane: "volatile";
2943
+ readonly budgetBytes: 4000;
2579
2944
  };
2580
2945
  readonly docSyncReminder: {
2581
2946
  readonly name: "doc-sync-reminder-injection";
2582
2947
  readonly priority: 64;
2583
2948
  readonly lane: "volatile";
2949
+ readonly budgetBytes: 4000;
2584
2950
  };
2585
2951
  };
2952
+ /**
2953
+ * RFC-374 M3:按 section name 应用注入预算。内容超预算时裁剪(boundFragment,
2954
+ * 纯文本截断后缀,RFC-142 红线兼容);name 不在表中时原样返回(插件自定义
2955
+ * context source 不受本表预算约束——它不进表就不进预算面,是显式而非默认)。
2956
+ *
2957
+ * 截断诊断(08-14 lesson 字节顶事故跟进):超预算裁剪是静默丢信息——内容里的
2958
+ * 截断后缀模型能看见但宿主日志看不见,预算配置漂移(如 pack 内容长大超过
2959
+ * budgetBytes)只表现为「注入内容莫名缺尾」。此处 warn(每进程每 section 一次)
2960
+ * 把截断升级为可诊断事件,触发即提示检查 budgetBytes 与内容常态体积是否匹配。
2961
+ */
2962
+ declare function applySectionBudget(name: string, content: string): string;
2586
2963
  //#endregion
2587
2964
  //#region src/session-stable.d.ts
2588
2965
  interface SessionStableInjectionOptions {
@@ -2593,6 +2970,18 @@ interface SessionStableInjectionOptions {
2593
2970
  }
2594
2971
  declare function createSessionStableInjectionHooks(options: SessionStableInjectionOptions): HookSpec[];
2595
2972
  //#endregion
2973
+ //#region src/session/tool-result-store-global.d.ts
2974
+ /**
2975
+ * tool-result-store-global.ts — RFC-093:ToolResultStore 全局单例
2976
+ *
2977
+ * AgentSession 构造 spill 闭包时引用。懒初始化(首次 spill 时基于 cwd 解析 .otto 目录)——
2978
+ * 避免模块加载期做 fs 探测。
2979
+ */
2980
+ declare const globalToolResultStore: {
2981
+ spill(sessionId: string, toolCallId: string, text: string): Promise<string | undefined>;
2982
+ cleanupSession(sessionId: string): Promise<void>;
2983
+ };
2984
+ //#endregion
2596
2985
  //#region src/session/derive-title.d.ts
2597
2986
  /**
2598
2987
  * 从会话消息列表中派生标题(RFC-030 P1)。
@@ -2624,6 +3013,21 @@ interface ToolHookOptions {
2624
3013
  }
2625
3014
  declare function createToolHookExecutor(options: ToolHookOptions): ToolHookExecutor;
2626
3015
  //#endregion
3016
+ //#region src/session/session-compaction-lock.d.ts
3017
+ /**
3018
+ * RFC-381 M1/M2:跨进程 durable 压缩锁端口(与 @x-otto/memory 的 CompactionLockPort
3019
+ * 结构同形——runtime 不依赖 memory 包,结构子类型即满足;memory 侧注入处见
3020
+ * MemoryManagerConfig.compactionLock)。
3021
+ */
3022
+ interface SessionCompactionLockPort {
3023
+ acquire(sessionId: string, token: string): Promise<boolean>;
3024
+ release(sessionId: string, token: string): Promise<void>;
3025
+ }
3026
+ declare const DEFAULT_COMPACTION_LOCK_ORPHAN_TIMEOUT_MS = 60000;
3027
+ declare function createSessionCompactionLock(session: Session<unknown>, options?: {
3028
+ orphanTimeoutMs?: number;
3029
+ }): SessionCompactionLockPort;
3030
+ //#endregion
2627
3031
  //#region src/session/persistence-sync.d.ts
2628
3032
  interface PersistenceSyncDeps {
2629
3033
  persistence: SessionPersistence<AgentMessage>;
@@ -2674,6 +3078,15 @@ interface PersistenceSyncDeps {
2674
3078
  * 这是后端能力事实,不是用户配置——未来远端支持 append-tail 后改声明即可自动升级。
2675
3079
  */
2676
3080
  turnFlush?: 'incremental' | 'snapshot';
3081
+ /**
3082
+ * M-2:确定性时钟端口(可选)。restore/restoreAll 装载快照时注入 InMemorySession——
3083
+ * restore 本身不产生活动(快照 metadata 权威),但为确定性重放/测试保持一致构造。
3084
+ */
3085
+ clock?: ClockPort;
3086
+ /**
3087
+ * M-2:确定性 id 生成端口(可选)。restore 路径不 append entry,故当前仅作一致性注入。
3088
+ */
3089
+ idGen?: IdPort;
2677
3090
  }
2678
3091
  declare class PersistenceSync {
2679
3092
  private readonly deps;
@@ -2750,13 +3163,6 @@ declare class PersistenceSync {
2750
3163
  */
2751
3164
  track(agentSession: AgentSession, transient: boolean): void;
2752
3165
  flushPending(id: string): Promise<void>;
2753
- /**
2754
- * P2 缓解(独立 review 补充,2026-07-12):会话可能经 idle 驱逐(`collectIdle`)或满池 LRU
2755
- * 驱逐(`evictLeastRecent`)消失,这两条路径不经过 `removeSession`/`flushPending`(前者是
2756
- * 同步批量收集不适合 await 落盘完成,后者已自行 fire-and-forget 触发 `saveSnapshot`)——
2757
- * 提供一个轻量同步纯清理方法,供这两条驱逐路径调用,与 `flushPending` 共享同一清理意图
2758
- * 但不附带"等待落盘完成"的语义(驱逐场景不需要,也不应阻塞驱逐循环)。
2759
- */
2760
3166
  /**
2761
3167
  * RFC-323 D4:计算用于预算分配的活跃会话数(排除 transient)。
2762
3168
  *
@@ -2774,6 +3180,13 @@ declare class PersistenceSync {
2774
3180
  * 一致地排除 transient(不订阅持久化、不参与预算分摊)。
2775
3181
  */
2776
3182
  private sumEstimatedContentBytes;
3183
+ /**
3184
+ * P2 缓解(独立 review 补充,2026-07-12):会话可能经 idle 驱逐(`collectIdle`)或满池 LRU
3185
+ * 驱逐(`evictLeastRecent`)消失,这两条路径不经过 `removeSession`/`flushPending`(前者是
3186
+ * 同步批量收集不适合 await 落盘完成,后者已自行 fire-and-forget 触发 `saveSnapshot`)——
3187
+ * 提供一个轻量同步纯清理方法,供这两条驱逐路径调用,与 `flushPending` 共享同一清理意图
3188
+ * 但不附带"等待落盘完成"的语义(驱逐场景不需要,也不应阻塞驱逐循环)。
3189
+ */
2777
3190
  forgetSession(id: string): void;
2778
3191
  /**
2779
3192
  * RFC-352 D2:驱逐的最终 snapshot 仍在队列中时不能提前清 residency 状态。
@@ -2781,27 +3194,21 @@ declare class PersistenceSync {
2781
3194
  * 不得反向清掉它。
2782
3195
  */
2783
3196
  forgetEvictedSession(id: string): void;
2784
- save(id: string): Promise<void>;
2785
3197
  /**
2786
- * RFC-336 D1:会话行预建——会话创建即落 `sessions` 一行(无 entries),不等首条消息。
2787
- *
2788
- * **要解决的问题**(实测复现,2026-08-10):`repo.createSession()` 的唯一调用点是
2789
- * `RelationalSessionPersistence.save()`,而 save 由 `track()` 订阅的 `turn.end`/
2790
- * `prompt.end` 触发——两者都发生在**首个模型回合完成之后**。因此"用户已回车、模型还没
2791
- * 回任何东西"这段窗口(≈ 一次完整模型往返,数秒~数十秒)内进程若被强杀(SIGKILL/OOM/
2792
- * 断电),DB 里连 `sessions` 行都不存在 → 整个会话蒸发,`/resume` 完全看不到,
2793
- * 且 `detectCrashGap` 因 `load()` 返回 null 而根本不执行(RFC-305 D3 的盲区)。
3198
+ * transient 会话(orchestrator ephemeral 子会话)的落盘禁令判定 —— **单一真源**。
2794
3199
  *
2795
- * **为什么复用 doSaveEntries 而非直调 repo**(D1-b,RFC-159 D3):写租约检查在
2796
- * `RelationalSessionPersistence.save()` 入口,是所有会话写路径的单一汇聚点。直调
2797
- * `repo.createSession()` 会开一条无租约的写路径先例。走这里则天然经 `enqueue` 串行链
2798
- * → `doSaveEntries` → `persistence.save()`,租约与写序语义全部继承,零新增写路径。
2799
- * 空 entries 快照在 save 内部即 `createSession()` + 零次 `appendEntry()`,正是所需语义。
3200
+ * 契约(`task-runner.ts`):`sessionMode:'ephemeral'`(缺省)的委派子会话建成
3201
+ * `transient:true`,任务结束即 `removeSession`;只有显式 `sessionMode:'sticky'`
3202
+ * 才是"特别授权"的一等持久子会话(不设 transient,正常落盘)。
2800
3203
  *
2801
- * **幂等**:`createSession` 是 `ON CONFLICT DO NOTHING`;重复调用安全。
2802
- * **fire-and-forget**(规则 3):失败只 warn,绝不阻塞会话创建——预建是可用性增强,
2803
- * 不是创建流程的正确性前提(失败时退化为改动前行为:首个 turn 后才落行)。
3204
+ * 此前该禁令**只在订阅侧**(`track`/`precreateSessionRow`/`requestInputBoundaryFlush`)
3205
+ * 把关,而 `saveAll()`(停机批量落盘)与两条驱逐路径的 `saveSnapshot()` 绕过订阅面
3206
+ * 直接写盘 —— transient 子会话 entries 通常很多(非空守卫拦不住),于是被整批固化进
3207
+ * DB 并污染 `/resume`(实测本机 22 行)。护栏下沉到两个写入汇聚点(`doSaveEntries`
3208
+ * 与 `saveSnapshot`)后,任何现存或未来的调用方都不可能再绕过。
2804
3209
  */
3210
+ private isTransient;
3211
+ save(id: string): Promise<void>;
2805
3212
  /**
2806
3213
  * RFC-336 D2:输入边界落盘——prep 成功、模型往返开始**之前**把新增 entries 落库。
2807
3214
  *
@@ -2822,6 +3229,26 @@ declare class PersistenceSync {
2822
3229
  * prompt 终态落盘共用同一条 per-session 链,天然串行、写租约语义一致。
2823
3230
  */
2824
3231
  requestInputBoundaryFlush(agentSession: AgentSession, transient: boolean): void;
3232
+ /**
3233
+ * RFC-336 D1:会话行预建——会话创建即落 `sessions` 一行(无 entries),不等首条消息。
3234
+ *
3235
+ * **要解决的问题**(实测复现,2026-08-10):`repo.createSession()` 的唯一调用点是
3236
+ * `RelationalSessionPersistence.save()`,而 save 由 `track()` 订阅的 `turn.end`/
3237
+ * `prompt.end` 触发——两者都发生在**首个模型回合完成之后**。因此"用户已回车、模型还没
3238
+ * 回任何东西"这段窗口(≈ 一次完整模型往返,数秒~数十秒)内进程若被强杀(SIGKILL/OOM/
3239
+ * 断电),DB 里连 `sessions` 行都不存在 → 整个会话蒸发,`/resume` 完全看不到,
3240
+ * 且 `detectCrashGap` 因 `load()` 返回 null 而根本不执行(RFC-305 D3 的盲区)。
3241
+ *
3242
+ * **为什么复用 doSaveEntries 而非直调 repo**(D1-b,RFC-159 D3):写租约检查在
3243
+ * `RelationalSessionPersistence.save()` 入口,是所有会话写路径的单一汇聚点。直调
3244
+ * `repo.createSession()` 会开一条无租约的写路径先例。走这里则天然经 `enqueue` 串行链
3245
+ * → `doSaveEntries` → `persistence.save()`,租约与写序语义全部继承,零新增写路径。
3246
+ * 空 entries 快照在 save 内部即 `createSession()` + 零次 `appendEntry()`,正是所需语义。
3247
+ *
3248
+ * **幂等**:`createSession` 是 `ON CONFLICT DO NOTHING`;重复调用安全。
3249
+ * **fire-and-forget**(规则 3):失败只 warn,绝不阻塞会话创建——预建是可用性增强,
3250
+ * 不是创建流程的正确性前提(失败时退化为改动前行为:首个 turn 后才落行)。
3251
+ */
2825
3252
  precreateSessionRow(agentSession: AgentSession, transient: boolean): void;
2826
3253
  /**
2827
3254
  * 驱逐路径专用:用调用方**同步捕获**的快照入链落盘。
@@ -2894,21 +3321,6 @@ declare class PersistenceSync {
2894
3321
  * 若未提供 configProvider 则返回 null。
2895
3322
  */
2896
3323
  restore(id: string): Promise<AgentSession | null>;
2897
- /**
2898
- * RFC-305 D3:restore 时 trace↔DB 对账——检出"上次进程异常终止时已发生但未落库"的
2899
- * turn 缺口并告警。对账基准 = trace append-log(逐事件实时落盘,独立于 save 链):
2900
- * DB 最后一条已持久化 entry 时间戳之后,trace 仍有 `turn.end` lifecycle 事件 →
2901
- * 这些 turn 的消息只进过内存、从未进 DB,已随崩溃永久丢失。
2902
- *
2903
- * 用时间戳而非 turn 号对账:trace 的 turn 字段 per-prompt 从 0 重置,跨 prompt 不可比;
2904
- * ts 与 entry.timestamp 同源(Date.now 系时钟),单机单调可比。
2905
- *
2906
- * 容忍度:正常退出路径(prompt.end 立即落盘 + turn 级入链)下 trace turn.end 与 DB
2907
- * 落库几乎同时,容忍 CRASH_GAP_TOLERANCE_MS 内的尾差(在途写 + 时钟粒度),超出才告警。
2908
- *
2909
- * fail-soft:traceStore 缺失/读取异常/无 turn.end 事件 → 静默跳过(debug 日志),
2910
- * 绝不阻塞 restore、绝不产生假告警。
2911
- */
2912
3324
  /**
2913
3325
  * RFC-305 D3:对账结果查询口——restore/restoreAll 内部把本次恢复检出的崩溃缺口记入
2914
3326
  * per-session 缓存,调用方(CLI resume 路径)在订阅建立后读取并 toast。缓存随
@@ -2951,7 +3363,7 @@ interface EngineSnapshot {
2951
3363
  }
2952
3364
  /** 引擎产出的治理决策(所有字段确定性)。 */
2953
3365
  interface ResidencyDecision {
2954
- /** 单会话当前有效字节预算(已/处分配层 × 压力档)。 */
3366
+ /** 单会话当前有效字节预算(分配量经压力档缩放后的最终值)。 */
2955
3367
  maxBytesPerSession: number;
2956
3368
  /** 当前全局驻留压力比例(0.0 ~ 1.0)。 */
2957
3369
  globalPressure: number;
@@ -2959,15 +3371,15 @@ interface ResidencyDecision {
2959
3371
  isGlobalPressure: boolean;
2960
3372
  }
2961
3373
  /**
2962
- * 计算“每个会话当前 应该 byte budget — 给定 snapshot,治理一个值。
3374
+ * 计算给定 snapshot 下每个会话的有效字节预算。
2963
3375
  *
2964
- * 特性:
2965
- * - 负反馈:currentTotalResidentBytes 越大 pressure 越高 → 每个会话预算*越低*。
2966
- * - 下界 MIN_PER_SESSION:防止分配结果不可用作 (如果 N 极大, SESSION_MAX=50×16=800MB < totalBudget).
2967
- * - 上界 MAX_PER_SESSION:模型折叠输入的最大(imageasher, window by compression)约为 214MB/会话。
2968
- * - 确定性:相同 snapshot → 相同 decision(R4,皱纹是稳定 的。
3376
+ * 特性:
3377
+ * - 负反馈:currentTotalResidentBytes 越大 pressure 越高 → 每个会话预算越低。
3378
+ * - 下界 MIN_PER_SESSION:防会话数极大时分配结果不可用。
3379
+ * - 上界 MAX_PER_SESSION:单会话驻留上限。
3380
+ * - 确定性:相同 snapshot → 相同 decision(R4)。
2969
3381
  */
2970
3382
  declare function decide(snapshot: EngineSnapshot): ResidencyDecision;
2971
3383
  //#endregion
2972
- 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 };
3384
+ 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 ContextBuildInput, type ContextInjectionPort, type ContextSource, ContextSourceRegistry, DEFAULT_COMPACTION_LOCK_ORPHAN_TIMEOUT_MS, DEFAULT_MAX_AUTO_APPLY_LINES, type DiskSessionPoolOptions, type DocSyncPort, type EmitContext, 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 HostPlaneDraft, type HostPlaneEntry, HostRegistry, type KillOptions, LEVEL_DIVISOR, 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, 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 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, applyDiff, applyMemoryTransform, applySectionBudget, attachAgentObserver, buildEngineStores, captureFiles, checkApply, createAgentRuntime, createDefaultContextSourceRegistry, createDiskSessionPool, createDocSyncReminderHooks, createEnvironmentContextSource, createInMemorySessionPool, createMemoryDeltaHooks, createMemoryIndexContextSource, createRemoteSessionPool, createSessionCompactionLock, createSessionStableInjectionHooks, createTodoReminderHooks, createToolHookExecutor, createWorktree, decideApply, decide as decideResidency, defaultMaxToolTurnExtensions, deriveSessionTitle, diffTouchedPaths, effectiveResidencyBudget, formatDocSyncReminder, globalAgentJobRegistry, globalAgentObservability, globalProcessRegistry, globalProcessRuntime, globalToolResultStore, hasRepoDrifted, installAgentJobReaper, installExitReaper, installProcessReaper, messagesFromSnapshot, pruneOrphanWorktrees, reconstructMessages, removeWorktree, resolveSessionTitle, resolveTodoContinuationConfig, restoreFiles, revertDiff, touchesCapabilitySurface, touchesForbiddenZone, worktreeDiff };
2973
3385
  //# sourceMappingURL=index.d.ts.map