virlen-remote 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/LICENSE +21 -0
  3. package/README.md +192 -0
  4. package/dist/index.d.ts +44 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +34 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/protocol/answer.d.ts +48 -0
  9. package/dist/protocol/answer.d.ts.map +1 -0
  10. package/dist/protocol/answer.js +65 -0
  11. package/dist/protocol/answer.js.map +1 -0
  12. package/dist/protocol/api.d.ts +576 -0
  13. package/dist/protocol/api.d.ts.map +1 -0
  14. package/dist/protocol/api.js +38 -0
  15. package/dist/protocol/api.js.map +1 -0
  16. package/dist/protocol/endpoint.d.ts +76 -0
  17. package/dist/protocol/endpoint.d.ts.map +1 -0
  18. package/dist/protocol/endpoint.js +307 -0
  19. package/dist/protocol/endpoint.js.map +1 -0
  20. package/dist/protocol/errors.d.ts +32 -0
  21. package/dist/protocol/errors.d.ts.map +1 -0
  22. package/dist/protocol/errors.js +56 -0
  23. package/dist/protocol/errors.js.map +1 -0
  24. package/dist/protocol/frame.d.ts +51 -0
  25. package/dist/protocol/frame.d.ts.map +1 -0
  26. package/dist/protocol/frame.js +154 -0
  27. package/dist/protocol/frame.js.map +1 -0
  28. package/dist/protocol/hello.d.ts +73 -0
  29. package/dist/protocol/hello.d.ts.map +1 -0
  30. package/dist/protocol/hello.js +37 -0
  31. package/dist/protocol/hello.js.map +1 -0
  32. package/dist/protocol/host.d.ts +116 -0
  33. package/dist/protocol/host.d.ts.map +1 -0
  34. package/dist/protocol/host.js +52 -0
  35. package/dist/protocol/host.js.map +1 -0
  36. package/dist/protocol/identity.d.ts +107 -0
  37. package/dist/protocol/identity.d.ts.map +1 -0
  38. package/dist/protocol/identity.js +143 -0
  39. package/dist/protocol/identity.js.map +1 -0
  40. package/dist/protocol/ids.d.ts +2 -0
  41. package/dist/protocol/ids.d.ts.map +1 -0
  42. package/dist/protocol/ids.js +20 -0
  43. package/dist/protocol/ids.js.map +1 -0
  44. package/dist/protocol/pairing.d.ts +43 -0
  45. package/dist/protocol/pairing.d.ts.map +1 -0
  46. package/dist/protocol/pairing.js +78 -0
  47. package/dist/protocol/pairing.js.map +1 -0
  48. package/dist/testing/index.d.ts +6 -0
  49. package/dist/testing/index.d.ts.map +1 -0
  50. package/dist/testing/index.js +5 -0
  51. package/dist/testing/index.js.map +1 -0
  52. package/dist/testing/mock-host.d.ts +66 -0
  53. package/dist/testing/mock-host.d.ts.map +1 -0
  54. package/dist/testing/mock-host.js +573 -0
  55. package/dist/testing/mock-host.js.map +1 -0
  56. package/dist/transport/broadcast.d.ts +26 -0
  57. package/dist/transport/broadcast.d.ts.map +1 -0
  58. package/dist/transport/broadcast.js +71 -0
  59. package/dist/transport/broadcast.js.map +1 -0
  60. package/dist/transport/ice.d.ts +130 -0
  61. package/dist/transport/ice.d.ts.map +1 -0
  62. package/dist/transport/ice.js +338 -0
  63. package/dist/transport/ice.js.map +1 -0
  64. package/dist/transport/memory.d.ts +52 -0
  65. package/dist/transport/memory.d.ts.map +1 -0
  66. package/dist/transport/memory.js +135 -0
  67. package/dist/transport/memory.js.map +1 -0
  68. package/dist/transport/rtc.d.ts +76 -0
  69. package/dist/transport/rtc.d.ts.map +1 -0
  70. package/dist/transport/rtc.js +310 -0
  71. package/dist/transport/rtc.js.map +1 -0
  72. package/dist/transport/signaling.d.ts +101 -0
  73. package/dist/transport/signaling.d.ts.map +1 -0
  74. package/dist/transport/signaling.js +235 -0
  75. package/dist/transport/signaling.js.map +1 -0
  76. package/dist/transport/types.d.ts +41 -0
  77. package/dist/transport/types.d.ts.map +1 -0
  78. package/dist/transport/types.js +11 -0
  79. package/dist/transport/types.js.map +1 -0
  80. package/package.json +69 -0
  81. package/src/index.ts +155 -0
  82. package/src/protocol/answer.ts +89 -0
  83. package/src/protocol/api.ts +517 -0
  84. package/src/protocol/endpoint.ts +405 -0
  85. package/src/protocol/errors.ts +90 -0
  86. package/src/protocol/frame.ts +196 -0
  87. package/src/protocol/hello.ts +112 -0
  88. package/src/protocol/host.ts +150 -0
  89. package/src/protocol/identity.ts +184 -0
  90. package/src/protocol/ids.ts +20 -0
  91. package/src/protocol/pairing.ts +95 -0
  92. package/src/testing/index.ts +5 -0
  93. package/src/testing/mock-host.ts +691 -0
  94. package/src/transport/broadcast.ts +81 -0
  95. package/src/transport/ice.ts +402 -0
  96. package/src/transport/memory.ts +164 -0
  97. package/src/transport/rtc.ts +344 -0
  98. package/src/transport/signaling.ts +317 -0
  99. package/src/transport/types.ts +50 -0
@@ -0,0 +1,576 @@
1
+ /**
2
+ * 类型化 API 定义 + 两端共用的推导辅助(见 docs/phone-control-bridge.md §2.2/§2.3)。
3
+ *
4
+ * **命名法:按「谁实现」命名,而不是「谁调用」。**
5
+ * - `host.*` = 电脑实现、手机调用
6
+ * - `mobile.*` = 手机实现、电脑调用
7
+ * 这样同一份定义能被两端共用:手机侧推导出类型安全客户端,电脑侧推导出必须实现的 handler 签名。
8
+ * **改一处,两端同时编译报错。**
9
+ *
10
+ * ⚠️ 本文件当前承载「协议表」;DTO 为白名单投影(§7-⑥:绝不 `{...session}`),
11
+ * 具体从电脑侧 store 投影到 DTO 的适配在 M2 的 `dto.ts` 落地。
12
+ */
13
+ import type { CallContext, Endpoint } from './endpoint.js';
14
+ export interface SessionSummaryDTO {
15
+ id: string;
16
+ title: string;
17
+ updatedAt: number;
18
+ /** 该会话是否正在跑 run(手机据此显示「工作中」)。 */
19
+ working?: boolean;
20
+ /** 是否置顶(M4:手机可置顶/取消置顶,需要知道当前态——目标态语义,不能靠切换)。 */
21
+ pinned?: boolean;
22
+ /** 归属 Agent id(抽屉「按 Agent 分组」的依据)。 */
23
+ agentId?: string;
24
+ /** 归属 Agent 显示名(电脑侧解析,手机不查表)。 */
25
+ agentName?: string;
26
+ /**
27
+ * 工作目录(**归一化绝对路径**,`/` 分隔)。
28
+ *
29
+ * ⚠️ 这是 §22 对 §7-⑥ 的**有意放宽**:手机端要按工作目录分组、并在**新建会话时**选目录,
30
+ * 必须能看到它。越权防线不在「手机看不到路径」,而在**电脑侧的候选集校验**
31
+ * (`host.session.create` 只接受 `host.workspace.list` 给过的目录,见 §22.3)。
32
+ * 仍然不外发 `systemPrompt` / `params` / `allowedTools`(那些才是真正的泄露面)。
33
+ */
34
+ workspace?: string;
35
+ providerConfigId?: string;
36
+ providerName?: string;
37
+ modelId?: string;
38
+ }
39
+ export interface MessageDTO {
40
+ id: string;
41
+ role: 'user' | 'assistant' | 'tool' | 'system';
42
+ /** 纯文本投影;图片等富内容一期剥离为占位符(§7-⑦)。空串 = 无正文(手机端不渲染空气泡)。 */
43
+ text: string;
44
+ createdAt: number;
45
+ /**
46
+ * 工具名(仅 `role:'tool'`,如 `read_file`)。
47
+ *
48
+ * 来源:电脑侧用 `toolCallId` 反查发起该调用的 assistant 消息的 `toolCalls[].name`
49
+ * (与桌面 `message-list/helpers.ts::resolveJumpAnchorId` 同一套匹配规则)。
50
+ * 手机端**不猜**:拿不到就只显示「工具」。
51
+ */
52
+ toolName?: string;
53
+ }
54
+ export interface RuntimeDTO {
55
+ working: boolean;
56
+ paused?: boolean;
57
+ error?: string;
58
+ /** 是否正在压缩上下文(压缩是长任务,手机据此显示进度)。 */
59
+ compacting?: boolean;
60
+ /**
61
+ * 正在生成的工具调用进度(引擎在**累积工具参数**期间推送)。
62
+ *
63
+ * 为什么需要:provider 在累积参数 JSON 期间不发任何事件(见 `docs/phone-control-bridge.md`
64
+ * §27),长参数(如 `write_file` 写一篇文章)意味着数秒到数十秒内上层看不到任何变化 ——
65
+ * 这个字段让界面能显示「正在写入 write_file · 1.2k 字符…」,而不是看起来卡死。
66
+ *
67
+ * 只带工具名与已累积字符数,**不含参数内容**(带宽 / 隐私 / 界面也不需要)。
68
+ */
69
+ toolProgress?: {
70
+ name: string;
71
+ chars: number;
72
+ } | null;
73
+ }
74
+ /**
75
+ * 授权分级(M4,见 docs/phone-control-bridge.md §16.2)。
76
+ *
77
+ * **分级只决定「手机批准前是否需二次确认」,不决定「谁能批」**(宽松档下手机可批全部)。
78
+ * 判定在电脑侧(`virlen-app/src/bridge/approval-policy.ts`),手机只消费结果。
79
+ */
80
+ export type ApprovalTier = 'low' | 'high';
81
+ /**
82
+ * 一次待应答交互的完整投影(手机端据此渲染卡片)。
83
+ *
84
+ * ⚠️ **电脑侧的 `interactionId` 是唯一权威标识**:手机应答时必须原样回传,
85
+ * 电脑侧按 id 精确路由(见 `events/toolInteractEvent.ts` 文件头)。
86
+ */
87
+ export interface InteractionDTO {
88
+ interactionId: string;
89
+ /**
90
+ * 归属会话。
91
+ * **空字符串 = 会话未知** —— 终端内确认(`presentation:'terminal'`)的 `PendingConfirmInfo`
92
+ * 不带会话信息,手机端以「全局卡片」呈现(§16.4)。
93
+ */
94
+ sessionId: string;
95
+ toolCallId?: string;
96
+ kind: 'choice' | 'authorization';
97
+ createdAt: number;
98
+ /** 风险分级:`high` 时手机必须先展开原文 + 勾选确认,并在应答时带 `confirmed:true`。 */
99
+ tier: ApprovalTier;
100
+ question?: string;
101
+ options?: string[];
102
+ multi?: boolean;
103
+ /** 权限唯一 key(跨 TS / Rust 稳定契约),如 `terminal.normal.execute` */
104
+ permName?: string;
105
+ title?: string;
106
+ subTitle?: string;
107
+ /** 正文:命令文本 / 脚本正文 */
108
+ desc?: string;
109
+ /** 实际执行命令(当 `desc` 不是命令本身时提供,如脚本正文) */
110
+ command?: string;
111
+ hint?: string;
112
+ /** 风险等级(`safe` / `install` / `dangerous`,与 `classify.rs` 对齐) */
113
+ risk?: string;
114
+ /** AI 申请不使用沙盒 */
115
+ sandboxBypass?: boolean;
116
+ /**
117
+ * 呈现方式:
118
+ * - `modal`:桌面弹窗授权(默认)
119
+ * - `terminal`:**终端内确认** —— 手机只能「原样放行」或「拒绝」,**不能编辑命令**(§16.4)
120
+ */
121
+ presentation?: 'modal' | 'terminal';
122
+ }
123
+ /**
124
+ * 一次交互的终态。
125
+ *
126
+ * 与电脑侧 `toolInteractEvent` 的 `InteractionOutcome`(`allow|reject|shelve`)**语义对齐但命名不同**:
127
+ * 线上统一用 `deny`(对手机而言「拒绝」比「reject」更一致),另加 `expired`(电脑侧已不再挂起)。
128
+ */
129
+ export type InteractionOutcome = 'allow' | 'deny' | 'shelve' | 'expired';
130
+ /**
131
+ * 流式正文的发送方式(见 `host.event.message.stream`)。
132
+ *
133
+ * - `full`:每帧发**整段正文** —— 实现简单,但一条 n 字的消息会传 O(n²) 字节;
134
+ * - `delta`:一帧只发**新增后缀**(配 `offset` 对齐)—— O(n)。
135
+ *
136
+ * **由客户端在 `hello` 里声明偏好**(`HelloParams.streamMode`),服务端不猜:
137
+ * 没声明就按 `full` 发,否则旧客户端会把一帧增量当成全文渲染(正文错位)。
138
+ */
139
+ export type StreamMode = 'full' | 'delta';
140
+ export interface SendParams {
141
+ sessionId: string;
142
+ text: string;
143
+ }
144
+ export interface MsgPageParams {
145
+ sessionId: string;
146
+ /**
147
+ * 更早一页的游标(M5)—— 原样回传上一次应答的 `MsgPageDTO.cursor`。
148
+ *
149
+ * ⚠️ 不传 = 取**尾部窗口**;传了 = 取该游标之前的一页。
150
+ * 不要把 `undefined` 与 `null` 混淆:电脑侧判据是 `!= null`。
151
+ */
152
+ fromRowid?: number;
153
+ /**
154
+ * 每页条数。
155
+ *
156
+ * ⚠️ **电脑侧实现目前忽略本字段**:分页窗口由电脑侧的加载窗口
157
+ * (`MESSAGE_PAGE_SIZE` = 60)决定 —— 展示窗口必须 ≡ 已加载窗口,否则游标会指向
158
+ * 窗口**之外**的消息,续页时漏掉中间消息(见 §20.2-A)。预留给二期(届时需同步调整游标语义)。
159
+ */
160
+ limit?: number;
161
+ }
162
+ export interface MsgPageDTO {
163
+ messages: MessageDTO[];
164
+ hasMore: boolean;
165
+ /**
166
+ * 下一页(更早)游标(M5)。
167
+ *
168
+ * **不透明**:手机端原样回传给 `MsgPageParams.fromRowid` 即可,**不要解析其数值语义**
169
+ * (电脑侧实现是 rowid,但那是实现细节)。
170
+ * `hasMore=false` 时为 `null`。
171
+ */
172
+ cursor?: number | null;
173
+ }
174
+ export interface AnswerParams {
175
+ interactionId: string;
176
+ action: AnswerAction;
177
+ /**
178
+ * `action='choose'` 时的选择结果:`{ selected?: string[]; customReply?: string }`。
179
+ * 电脑侧会按与桌面 UI **完全相同的形态**构造给引擎的 `ToolResult`(含 `uiData`)。
180
+ */
181
+ value?: unknown;
182
+ /**
183
+ * 高风险(`tier='high'`)的**批准**必须为 `true`。
184
+ * ⚠️ 电脑侧**独立校验**:手机 UI 漏改不算数(缺则 `E_CONFIRM_REQUIRED`)—— §16.2。
185
+ */
186
+ confirmed?: boolean;
187
+ }
188
+ /** 手机一次应答的四种动作。 */
189
+ export type AnswerAction = 'allow' | 'deny' | 'shelve' | 'choose';
190
+ /**
191
+ * 「100%」对应的上下文窗口默认值(电脑侧设置里可改;CLI 读同一份设置)。
192
+ *
193
+ * 放在协议层是因为**两端要用同一个数**:电脑侧算 `ContextInfoDTO.windowTokens`,
194
+ * 手机侧算占用比例,mock 宿主也用它。各端各写一份必然会分叉。
195
+ */
196
+ export declare const DEFAULT_CONTEXT_WINDOW_TOKENS = 200000;
197
+ /**
198
+ * 压缩上下文的最小占用比例。
199
+ *
200
+ * 低于它:桌面 token 环只提示「无需压缩」,电脑侧 `host.session.compress` 直接拒
201
+ * (`E_BAD_REQUEST`),手机端也不显示压缩按钮 —— **同一条判据,不许两端各写一份**
202
+ * (§18.5 的教训:口径写两份的那次是「给 AI 的文案」)。
203
+ */
204
+ export declare const COMPRESS_MIN_RATIO = 0.4;
205
+ /**
206
+ * 一个**已启用**的模型服务及其可选模型(`host.model.list`)。
207
+ *
208
+ * 白名单投影:只给 `id` / `name` / `models` —— **不含** `apiKey` / `baseUrl` / `params`
209
+ * (那些是电脑侧的机密与本地配置,§7-⑥)。
210
+ */
211
+ export interface ModelProviderDTO {
212
+ id: string;
213
+ name: string;
214
+ models: string[];
215
+ }
216
+ /**
217
+ * 新建会话可选的工作目录(`host.workspace.list`)。
218
+ *
219
+ * ⚠️ **候选集由电脑侧给出,手机不得自造**(「不能让手机端创建没有过的目录」)。
220
+ * 电脑侧从既有数据里收集:各会话的 `workspace` + 各 Agent 的 `defaultWorkspace` + 全局默认工作目录。
221
+ * `host.session.create` 会**独立校验**回传的 `workspace` 是否属于该集合(§22.3)。
222
+ */
223
+ export interface WorkspaceOptionDTO {
224
+ /** 归一化绝对路径(`/` 分隔);也是 `host.session.create` 回传的值。 */
225
+ path: string;
226
+ /** 末级目录名(电脑侧算好,避免两端各写一份路径解析)。 */
227
+ name: string;
228
+ /** 使用该目录的既有会话数(展示用)。 */
229
+ sessionCount: number;
230
+ }
231
+ /**
232
+ * 上下文占用(`host.session.context` / `host.event.session.context.changed`)。
233
+ *
234
+ * 口径与桌面 token 环**完全一致**(`domain/usage/context-occupancy.ts`,单一真源):
235
+ * 取最后一条带 `uiData.contextTokens`(压缩产物)或 `usage.totalTokens` 的消息。
236
+ */
237
+ export interface ContextInfoDTO {
238
+ /** 当前上下文占用(token);无法判定(消息未加载 / 无用量)时为 `null`。 */
239
+ tokens: number | null;
240
+ /** 「100%」对应的上下文窗口大小(来自电脑侧设置,CLI 读同一份)。 */
241
+ windowTokens: number;
242
+ }
243
+ export interface SetModelParams {
244
+ sessionId: string;
245
+ providerConfigId: string;
246
+ modelId: string;
247
+ }
248
+ export interface ContextParams {
249
+ sessionId: string;
250
+ }
251
+ export interface CompressParams {
252
+ sessionId: string;
253
+ /**
254
+ * 必须为 `true`。
255
+ *
256
+ * 压缩会**用摘要替换整段历史**(不可逆,与删除会话同档)——§16.3-3 要求手机端二次确认,
257
+ * 电脑侧独立校验,缺则 `E_CONFIRM_REQUIRED`。
258
+ */
259
+ confirm: true;
260
+ }
261
+ /** 交互已被处理 / 手机应答被拒的原因(UI 据此提示,不弹错误)。 */
262
+ export type AnswerRejectReason = 'not-found' | 'already-settled' | 'confirm-required' | 'invalid-value' | 'unsupported-by-host';
263
+ export interface AnswerResult {
264
+ accepted: boolean;
265
+ reason?: AnswerRejectReason;
266
+ }
267
+ export interface CreateSessionParams {
268
+ /** 不传则用电脑端的默认命名 */
269
+ title?: string;
270
+ /**
271
+ * 工作目录(**必须来自 `host.workspace.list`**;电脑侧独立校验,越权即 `E_BAD_REQUEST`)。
272
+ *
273
+ * 不传 = 沿用 Agent 的 `defaultWorkspace`(电脑侧行为)。
274
+ */
275
+ workspace?: string;
276
+ /** 模型服务与模型 id;不传 = 沿用默认 Agent 的默认模型。 */
277
+ providerConfigId?: string;
278
+ modelId?: string;
279
+ }
280
+ export interface RenameSessionParams {
281
+ sessionId: string;
282
+ title: string;
283
+ }
284
+ export interface PinSessionParams {
285
+ sessionId: string;
286
+ pinned: boolean;
287
+ }
288
+ export interface DeleteSessionParams {
289
+ sessionId: string;
290
+ /**
291
+ * 必须为 `true`。会话删除不可逆(消息级联删除)—— §16.3-3 要求手机端二次确认,
292
+ * 电脑侧独立校验,缺则 `E_CONFIRM_REQUIRED`。
293
+ */
294
+ confirm: true;
295
+ }
296
+ /** 电脑实现、手机调用。 */
297
+ export interface HostApi {
298
+ 'host.hello': {
299
+ params: import('./hello.js').HelloParams;
300
+ result: import('./hello.js').HelloResult;
301
+ };
302
+ 'host.session.list': {
303
+ params: Record<string, never>;
304
+ result: {
305
+ sessions: SessionSummaryDTO[];
306
+ };
307
+ };
308
+ 'host.session.messages': {
309
+ params: MsgPageParams;
310
+ result: MsgPageDTO;
311
+ };
312
+ /** delta 模式对齐用(§3.6):跳号时拉全文。 */
313
+ 'host.session.message.get': {
314
+ params: {
315
+ sessionId: string;
316
+ messageId: string;
317
+ };
318
+ result: {
319
+ message: MessageDTO;
320
+ };
321
+ };
322
+ 'host.session.send': {
323
+ params: SendParams;
324
+ result: {
325
+ messageId: string;
326
+ };
327
+ };
328
+ 'host.session.cancel': {
329
+ params: {
330
+ sessionId: string;
331
+ };
332
+ result: {
333
+ ok: true;
334
+ };
335
+ };
336
+ /**
337
+ * 从暂停的 run 快照恢复执行(M5,与手机端「暂存」配对)。
338
+ *
339
+ * 电脑侧落点为唯一恢复入口 `resumePausedRun`;会话未处于暂停态时电脑侧报 `E_BAD_REQUEST`。
340
+ */
341
+ 'host.session.resume': {
342
+ params: {
343
+ sessionId: string;
344
+ };
345
+ result: {
346
+ ok: true;
347
+ };
348
+ };
349
+ 'host.session.subscribe': {
350
+ params: {
351
+ sessionId: string;
352
+ fromRowid?: number;
353
+ };
354
+ result: {
355
+ ok: true;
356
+ };
357
+ };
358
+ 'host.session.create': {
359
+ params: CreateSessionParams;
360
+ result: {
361
+ sessionId: string;
362
+ };
363
+ };
364
+ 'host.session.rename': {
365
+ params: RenameSessionParams;
366
+ result: {
367
+ ok: true;
368
+ };
369
+ };
370
+ 'host.session.pin': {
371
+ params: PinSessionParams;
372
+ result: {
373
+ ok: true;
374
+ };
375
+ };
376
+ /** ⚠️ 不可逆:必须带 `confirm: true`(缺则 `E_CONFIRM_REQUIRED`)。 */
377
+ 'host.session.delete': {
378
+ params: DeleteSessionParams;
379
+ result: {
380
+ ok: true;
381
+ };
382
+ };
383
+ 'host.interaction.answer': {
384
+ params: AnswerParams;
385
+ result: AnswerResult;
386
+ };
387
+ /**
388
+ * 当前待应答交互(**拉取式**,M4)。
389
+ *
390
+ * 为什么必需:`host.event.interaction.requested` 是**一次性事件** ——
391
+ * 手机在交互发起之后才连上/重连时,事件已错过,它只看到「会话卡在 working」而不知为何。
392
+ * 手机在每次链路就绪后拉一次本方法,即可补齐(权威源是电脑侧注册表)。
393
+ */
394
+ 'host.interaction.list': {
395
+ params: Record<string, never>;
396
+ result: {
397
+ interactions: InteractionDTO[];
398
+ };
399
+ };
400
+ /** 已启用的模型服务与模型(手机端模型选择器)。 */
401
+ 'host.model.list': {
402
+ params: Record<string, never>;
403
+ result: {
404
+ providers: ModelProviderDTO[];
405
+ };
406
+ };
407
+ /**
408
+ * 切换会话使用的模型(**对已有会话**;桌面 model-switcher 的等价操作)。
409
+ *
410
+ * 与桌面同构:不刷新 `updatedAt`(会话时间只由用户发消息刷新),下一轮生效。
411
+ */
412
+ 'host.session.setModel': {
413
+ params: SetModelParams;
414
+ result: {
415
+ ok: true;
416
+ };
417
+ };
418
+ /**
419
+ * 新建会话可选的工作目录候选集。
420
+ *
421
+ * ⚠️ 只用于**新建会话**;已有会话的工作目录不可改(用户 2026-09-28 拍板,§22.3)。
422
+ */
423
+ 'host.workspace.list': {
424
+ params: Record<string, never>;
425
+ result: {
426
+ workspaces: WorkspaceOptionDTO[];
427
+ };
428
+ };
429
+ /** 上下文占用快照(拉取式,配合 `host.event.session.context.changed` 增量)。 */
430
+ 'host.session.context': {
431
+ params: ContextParams;
432
+ result: ContextInfoDTO;
433
+ };
434
+ /**
435
+ * 压缩上下文(用摘要替换历史)。
436
+ *
437
+ * fire-and-forget:RPC 只回投递确认 —— 进度走 `runtime.compacting`,结果走
438
+ * `host.event.session.messages.reset`(手机重拉窗口)+ `message.added`(摘要消息)。
439
+ * ⚠️ 必须带 `confirm: true`;上下文充裕(低于 40%)时电脑侧直接拒(与桌面 token 环同判据)。
440
+ */
441
+ 'host.session.compress': {
442
+ params: CompressParams;
443
+ result: {
444
+ ok: true;
445
+ };
446
+ };
447
+ }
448
+ /** 手机实现、电脑调用。 */
449
+ export interface MobileApi {
450
+ 'mobile.ping': {
451
+ params: Record<string, never>;
452
+ result: {
453
+ ts: number;
454
+ };
455
+ };
456
+ }
457
+ export interface HostEvents {
458
+ 'host.event.session.list.changed': {
459
+ sessions: SessionSummaryDTO[];
460
+ };
461
+ /** 已定稿消息的变化 —— 完整对象,天然幂等。 */
462
+ 'host.event.message.added': {
463
+ sessionId: string;
464
+ message: MessageDTO;
465
+ };
466
+ 'host.event.message.updated': {
467
+ sessionId: string;
468
+ message: MessageDTO;
469
+ };
470
+ /**
471
+ * 「正在生成中的那一条消息」的流式同步 —— 与 message.* 分离的独立通道。
472
+ *
473
+ * **一期发 `mode='full'`(每帧整段),二期(2026-09-30,§32)起支持 `mode='delta'`。**
474
+ * 带宽差一个量级:一条 n 字的消息,整帧发是 O(n²) 字节(每帧都把已有全文再传一遍),
475
+ * 增量发是 O(n)。真机上「长回复越到后面越卡」的观感就来自前者。
476
+ */
477
+ 'host.event.message.stream': {
478
+ sessionId: string;
479
+ messageId: string;
480
+ /** 同一 `messageId` 内递增,从 1 开始;换消息重新从 1 算。 */
481
+ seq: number;
482
+ /**
483
+ * `full` = `text` 是整段正文(客户端直接替换);
484
+ * `delta` = `text` 只是新增后缀(客户端按 `offset` 追加)。
485
+ *
486
+ * 什么时候是 `full`(三个时机,客户端不能假定「只有首帧是 full」):
487
+ * 1. 该消息的**首帧**(客户端没有任何基准);
488
+ * 2. 正文**被改写**(新正文不是旧正文的前缀,如定稿回填 / 修复);
489
+ * 3. `final=true` 的收尾帧(定稿全文)。
490
+ */
491
+ mode: StreamMode;
492
+ text: string;
493
+ /**
494
+ * 仅 `mode='delta'`:本段 `text` 在整段正文中的**起始偏移**(= 发送前已发出的长度)。
495
+ *
496
+ * 为什么必须有它:客户端手上有多少正文**只有它自己知道** —— 中途订阅、
497
+ * `messages.reset` 后重拉、断线重连都会让它落后于服务端的发送基准。有了偏移,客户端能:
498
+ * - 把**完全重复**的段丢掉(`offset + text.length ≤ 本地长度`);
499
+ * - 把**部分重叠**的段按尾巴补上(本地长度 − `offset` 之后的那些字符);
500
+ * - 在**真缺一段**时(`offset > 本地长度`)发现缺口,调 `host.session.message.get` 拉全文对齐。
501
+ * 没有它就只能靠 seq 连续性猜,而猜错是**静默的正文错位**(比丢帧难查得多)。
502
+ */
503
+ offset?: number;
504
+ /** 收尾帧:`text` 为定稿全文(客户端渲染最终版本以随后的 `message.added` 为准)。 */
505
+ final: boolean;
506
+ };
507
+ 'host.event.session.runtime.changed': {
508
+ sessionId: string;
509
+ runtime: RuntimeDTO;
510
+ };
511
+ 'host.event.interaction.requested': {
512
+ interaction: InteractionDTO;
513
+ };
514
+ 'host.event.interaction.resolved': {
515
+ interactionId: string;
516
+ by: 'host' | 'mobile';
517
+ /** 终态;`expired` = 电脑侧已不再挂起该交互(如会话被取消) */
518
+ outcome?: InteractionOutcome;
519
+ };
520
+ /**
521
+ * 上下文占用变化(§22)。
522
+ *
523
+ * 与 `interaction.requested` 同样是「增量 + 快照」组合里的增量:快照是
524
+ * `host.session.context`。只推**已订阅**会话(与消息推送同一订阅集合)。
525
+ */
526
+ 'host.event.session.context.changed': {
527
+ sessionId: string;
528
+ context: ContextInfoDTO;
529
+ };
530
+ /**
531
+ * 该会话的消息**被整体替换或删除**(压缩 / 删消息),本地增删 diff 已无法表达。
532
+ *
533
+ * 语义:手机**丢弃**该会话缓存的消息窗口并重拉(`host.session.messages`),
534
+ * 而不是逐条对账 —— 压缩会把整段历史换成一条 summary,逐条 diff 只会得到一份错位的历史。
535
+ * 为什么必需:此前删除的消息只是从电脑侧快照里消失(不发事件),手机端会一直显示幽灵消息。
536
+ */
537
+ 'host.event.session.messages.reset': {
538
+ sessionId: string;
539
+ };
540
+ 'host.event.connection.changed': {
541
+ path: 'direct' | 'relay';
542
+ degraded: boolean;
543
+ };
544
+ }
545
+ export interface MobileEvents {
546
+ 'mobile.event.visibility': {
547
+ state: 'foreground' | 'background';
548
+ at: number;
549
+ };
550
+ 'mobile.event.viewing': {
551
+ sessionId: string | null;
552
+ };
553
+ }
554
+ export type ParamsOf<A, K extends keyof A> = A[K] extends {
555
+ params: infer P;
556
+ } ? P : never;
557
+ export type ResultOf<A, K extends keyof A> = A[K] extends {
558
+ result: infer R;
559
+ } ? R : never;
560
+ /** 由方法表推导的类型安全客户端(手机侧用它调 `host.*`)。 */
561
+ export interface TypedCaller<A> {
562
+ call<K extends keyof A & string>(method: K, params: ParamsOf<A, K>, timeoutMs?: number): Promise<ResultOf<A, K>>;
563
+ }
564
+ /** 由方法表推导的 handler 实现类型(电脑侧用它实现 `host.*`,key 必须齐全)。 */
565
+ export type HandlerImpl<A> = {
566
+ [K in keyof A & string]: (params: ParamsOf<A, K>, ctx: CallContext) => ResultOf<A, K> | Promise<ResultOf<A, K>>;
567
+ };
568
+ /** 由事件表推导的订阅辅助。 */
569
+ export interface TypedSubscriber<E> {
570
+ subscribe<K extends keyof E & string>(topic: K, listener: (payload: E[K]) => void): () => void;
571
+ }
572
+ export declare function createCaller<A>(endpoint: Endpoint): TypedCaller<A>;
573
+ export declare function createSubscriber<E>(endpoint: Endpoint): TypedSubscriber<E>;
574
+ /** 把一份 handler 实现注册到 endpoint(key 必须齐全,返回值类型必须匹配)。 */
575
+ export declare function registerHandlers<A>(endpoint: Endpoint, impl: HandlerImpl<A>): void;
576
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/protocol/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ // ── §22:模型 / 工作目录 / 上下文(手机端可看可切的部分)──
2
+ /**
3
+ * 「100%」对应的上下文窗口默认值(电脑侧设置里可改;CLI 读同一份设置)。
4
+ *
5
+ * 放在协议层是因为**两端要用同一个数**:电脑侧算 `ContextInfoDTO.windowTokens`,
6
+ * 手机侧算占用比例,mock 宿主也用它。各端各写一份必然会分叉。
7
+ */
8
+ export const DEFAULT_CONTEXT_WINDOW_TOKENS = 200000;
9
+ /**
10
+ * 压缩上下文的最小占用比例。
11
+ *
12
+ * 低于它:桌面 token 环只提示「无需压缩」,电脑侧 `host.session.compress` 直接拒
13
+ * (`E_BAD_REQUEST`),手机端也不显示压缩按钮 —— **同一条判据,不许两端各写一份**
14
+ * (§18.5 的教训:口径写两份的那次是「给 AI 的文案」)。
15
+ */
16
+ export const COMPRESS_MIN_RATIO = 0.4;
17
+ export function createCaller(endpoint) {
18
+ return {
19
+ call(method, params, timeoutMs) {
20
+ return endpoint.call(method, params, timeoutMs === undefined ? undefined : { timeoutMs });
21
+ },
22
+ };
23
+ }
24
+ export function createSubscriber(endpoint) {
25
+ return {
26
+ subscribe(topic, listener) {
27
+ return endpoint.subscribe(topic, listener);
28
+ },
29
+ };
30
+ }
31
+ /** 把一份 handler 实现注册到 endpoint(key 必须齐全,返回值类型必须匹配)。 */
32
+ export function registerHandlers(endpoint, impl) {
33
+ for (const method of Object.keys(impl)) {
34
+ const fn = impl[method];
35
+ endpoint.handle(method, (params, ctx) => Promise.resolve(fn(params, ctx)));
36
+ }
37
+ }
38
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/protocol/api.ts"],"names":[],"mappings":"AA+MA,uCAAuC;AAEvC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,MAAO,CAAA;AAEpD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,CAAA;AA8QrC,MAAM,UAAU,YAAY,CAAI,QAAkB;IAChD,OAAO;QACL,IAAI,CAA6B,MAAS,EAAE,MAAsB,EAAE,SAAkB;YACpF,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAA4B,CAAA;QACtH,CAAC;KACF,CAAA;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAI,QAAkB;IACpD,OAAO;QACL,SAAS,CAA6B,KAAQ,EAAE,QAAiC;YAC/E,OAAO,QAAQ,CAAC,SAAS,CAAC,KAAK,EAAE,QAAsC,CAAC,CAAA;QAC1E,CAAC;KACF,CAAA;AACH,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,gBAAgB,CAAI,QAAkB,EAAE,IAAoB;IAC1E,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACvC,MAAM,EAAE,GAAG,IAAI,CAAC,MAA0B,CAAmD,CAAA;QAC7F,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAA;IAC5E,CAAC;AACH,CAAC"}