dsh-remote-bridge 2.2.3 → 2.2.4
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/docs/api/asyncapi.yaml +1 -1
- package/docs/api/openapi.yaml +1 -1
- package/lib/index.d.ts +967 -941
- package/lib/index.js +30 -16
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
|
@@ -105,1107 +105,1133 @@ declare class WorkspaceDatabase {
|
|
|
105
105
|
private migrate;
|
|
106
106
|
}
|
|
107
107
|
//#endregion
|
|
108
|
-
//#region src/host/
|
|
109
|
-
interface
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
code: string;
|
|
117
|
-
message: string;
|
|
118
|
-
details?: unknown;
|
|
119
|
-
};
|
|
120
|
-
};
|
|
108
|
+
//#region src/host/settings-adapter.d.ts
|
|
109
|
+
interface SettingsNamespaceView {
|
|
110
|
+
ns: string;
|
|
111
|
+
schema?: unknown;
|
|
112
|
+
value: unknown;
|
|
113
|
+
base?: unknown;
|
|
114
|
+
user?: unknown;
|
|
115
|
+
revision: number;
|
|
121
116
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
type: 'text';
|
|
134
|
-
text: string;
|
|
135
|
-
} | {
|
|
136
|
-
type: 'image';
|
|
137
|
-
mediaType: string;
|
|
138
|
-
data: string;
|
|
139
|
-
name?: string;
|
|
140
|
-
} | {
|
|
141
|
-
type: 'file';
|
|
142
|
-
receiptId: string;
|
|
143
|
-
};
|
|
144
|
-
interface DshApiProxy {
|
|
145
|
-
respond(message: {
|
|
146
|
-
type: 'client-response';
|
|
147
|
-
rpcId: string;
|
|
148
|
-
result: {
|
|
149
|
-
ok: true;
|
|
150
|
-
value: unknown;
|
|
151
|
-
};
|
|
152
|
-
}): Promise<{
|
|
153
|
-
accepted: true;
|
|
154
|
-
} | {
|
|
155
|
-
accepted: false;
|
|
156
|
-
reason: 'not-pending' | 'bad-response';
|
|
157
|
-
}>;
|
|
158
|
-
sessions: {
|
|
159
|
-
list(request: {
|
|
160
|
-
rpcId: string;
|
|
161
|
-
payload: {
|
|
162
|
-
cursor?: string;
|
|
163
|
-
};
|
|
164
|
-
}): Promise<RpcResponse<{
|
|
165
|
-
items: DshSessionSummary[];
|
|
166
|
-
}>>;
|
|
167
|
-
create(request: {
|
|
168
|
-
rpcId: string;
|
|
169
|
-
payload: {
|
|
170
|
-
cwd?: string;
|
|
171
|
-
workspaceId?: string;
|
|
172
|
-
sessionId?: string;
|
|
173
|
-
agentPreset?: string;
|
|
174
|
-
};
|
|
175
|
-
}): Promise<RpcResponse<{
|
|
176
|
-
sessionId: string;
|
|
177
|
-
agentPreset?: string;
|
|
178
|
-
}>>;
|
|
179
|
-
history(request: {
|
|
180
|
-
rpcId: string;
|
|
181
|
-
payload: {
|
|
182
|
-
sessionId: string;
|
|
183
|
-
beforeSeq?: number;
|
|
184
|
-
maxMessages?: number;
|
|
185
|
-
};
|
|
186
|
-
}): Promise<RpcResponse<{
|
|
187
|
-
events: unknown[];
|
|
188
|
-
hasMore: boolean;
|
|
189
|
-
projections?: unknown;
|
|
190
|
-
}>>;
|
|
191
|
-
prompt(request: {
|
|
192
|
-
rpcId: string;
|
|
193
|
-
payload: {
|
|
194
|
-
sessionId: string;
|
|
195
|
-
mode: 'queue' | 'steer';
|
|
196
|
-
content: PromptContentPart[];
|
|
197
|
-
clientTimeZone?: string;
|
|
198
|
-
};
|
|
199
|
-
}): Promise<RpcResponse<{
|
|
200
|
-
accepted: true;
|
|
201
|
-
command?: unknown;
|
|
202
|
-
}>>;
|
|
203
|
-
/**
|
|
204
|
-
* 上传一个文件,换取 prompt 可引用的凭证(`receiptId`)。
|
|
205
|
-
* `data` 是文件字节的**标准 base64**。
|
|
206
|
-
*/
|
|
207
|
-
uploadAttachment(request: {
|
|
208
|
-
rpcId: string;
|
|
209
|
-
payload: {
|
|
210
|
-
sessionId: string;
|
|
211
|
-
data: string;
|
|
212
|
-
name?: string;
|
|
213
|
-
};
|
|
214
|
-
}): Promise<RpcResponse<{
|
|
215
|
-
receiptId: string;
|
|
216
|
-
name: string | null;
|
|
217
|
-
}>>;
|
|
218
|
-
/**
|
|
219
|
-
* 按 id 取回附件字节。
|
|
220
|
-
*
|
|
221
|
-
* `attachmentId` 形如 `sha256:<hex>`(**含冒号**,客户端必须 URL 编码)。
|
|
222
|
-
* 返回 `Uint8Array` 而不是 base64:图片动辄上百 KB,多一次编解码纯属浪费。
|
|
223
|
-
*
|
|
224
|
-
* **必须带 `sessionId`**:内核要靠它读会话事件、取出这张图的真实 ref,
|
|
225
|
-
* 再据此做完整性校验(详见 chat-adapter 的 readAttachment 注释)。
|
|
226
|
-
*/
|
|
227
|
-
readAttachment(request: {
|
|
228
|
-
rpcId: string;
|
|
229
|
-
payload: {
|
|
230
|
-
sessionId: string;
|
|
231
|
-
attachmentId: string;
|
|
232
|
-
};
|
|
233
|
-
}): Promise<RpcResponse<{
|
|
234
|
-
bytes: Uint8Array;
|
|
235
|
-
mediaType: string;
|
|
236
|
-
}>>;
|
|
237
|
-
/**
|
|
238
|
-
* 取回一张**交接图片**的字节(纯文本模型发图时,图片被落成工作区文件)。
|
|
239
|
-
*
|
|
240
|
-
* 与 [readAttachment] 的区别:那一个取内核附件存储里的图(走
|
|
241
|
-
* `sessionController.attachment`),这一个取插件自己写进 `.dsh-images/`
|
|
242
|
-
* 的交接件。手机端用它把图片**显示**给人看;消息里的路径文本照旧留给模型。
|
|
243
|
-
*/
|
|
244
|
-
readHandoffImage(request: {
|
|
245
|
-
rpcId: string;
|
|
246
|
-
payload: {
|
|
247
|
-
sessionId: string;
|
|
248
|
-
name: string;
|
|
249
|
-
};
|
|
250
|
-
}): Promise<RpcResponse<{
|
|
251
|
-
bytes: Uint8Array;
|
|
252
|
-
mediaType: string;
|
|
253
|
-
}>>;
|
|
254
|
-
cancel(request: {
|
|
117
|
+
interface ConfigurableProvider {
|
|
118
|
+
provider: string;
|
|
119
|
+
displayName: string;
|
|
120
|
+
settingsNs: string;
|
|
121
|
+
settingsPath: string[];
|
|
122
|
+
active: boolean;
|
|
123
|
+
declared?: boolean;
|
|
124
|
+
}
|
|
125
|
+
interface SettingsApiProxy extends DshApiProxy {
|
|
126
|
+
settings: {
|
|
127
|
+
describe(request: {
|
|
255
128
|
rpcId: string;
|
|
256
|
-
payload:
|
|
257
|
-
sessionId: string;
|
|
258
|
-
};
|
|
129
|
+
payload: Record<string, never>;
|
|
259
130
|
}): Promise<RpcResponse<{
|
|
260
|
-
|
|
131
|
+
writable: boolean;
|
|
132
|
+
hasDocument: boolean;
|
|
133
|
+
namespaces: SettingsNamespaceView[];
|
|
261
134
|
}>>;
|
|
262
|
-
|
|
135
|
+
mutate(request: {
|
|
263
136
|
rpcId: string;
|
|
264
137
|
payload: {
|
|
265
|
-
|
|
266
|
-
|
|
138
|
+
ns: string;
|
|
139
|
+
ops: Array<{
|
|
140
|
+
op: 'set';
|
|
141
|
+
path: string[];
|
|
142
|
+
value: unknown;
|
|
143
|
+
} | {
|
|
144
|
+
op: 'unset';
|
|
145
|
+
path: string[];
|
|
146
|
+
}>;
|
|
147
|
+
expectedRevision?: number;
|
|
267
148
|
};
|
|
268
|
-
}): Promise<RpcResponse<
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
fork(request: {
|
|
149
|
+
}): Promise<RpcResponse<SettingsNamespaceView>>;
|
|
150
|
+
};
|
|
151
|
+
credentials: {
|
|
152
|
+
describe(request: {
|
|
273
153
|
rpcId: string;
|
|
274
154
|
payload: {
|
|
275
|
-
|
|
276
|
-
atSeq?: number;
|
|
155
|
+
refs: string[];
|
|
277
156
|
};
|
|
278
157
|
}): Promise<RpcResponse<{
|
|
279
|
-
|
|
158
|
+
credentials: Record<string, {
|
|
159
|
+
configured: boolean;
|
|
160
|
+
source?: string;
|
|
161
|
+
writable: boolean;
|
|
162
|
+
}>;
|
|
280
163
|
}>>;
|
|
281
|
-
|
|
164
|
+
set(request: {
|
|
282
165
|
rpcId: string;
|
|
283
166
|
payload: {
|
|
284
|
-
|
|
167
|
+
ref: string;
|
|
168
|
+
value: string;
|
|
285
169
|
};
|
|
286
|
-
}): Promise<RpcResponse<
|
|
287
|
-
|
|
170
|
+
}): Promise<RpcResponse<Record<string, never>>>;
|
|
171
|
+
unset(request: {
|
|
288
172
|
rpcId: string;
|
|
289
173
|
payload: {
|
|
290
|
-
|
|
291
|
-
provider: string;
|
|
292
|
-
model: string;
|
|
293
|
-
reasoningEffort?: string;
|
|
174
|
+
ref: string;
|
|
294
175
|
};
|
|
295
|
-
}): Promise<RpcResponse<
|
|
296
|
-
selected: ModelSelectionView;
|
|
297
|
-
}>>;
|
|
176
|
+
}): Promise<RpcResponse<Record<string, never>>>;
|
|
298
177
|
};
|
|
299
|
-
|
|
300
|
-
|
|
178
|
+
llm: {
|
|
179
|
+
providers(request: {
|
|
301
180
|
rpcId: string;
|
|
302
181
|
payload: Record<string, never>;
|
|
303
182
|
}): Promise<RpcResponse<{
|
|
304
|
-
|
|
305
|
-
archivedSessionIds: string[];
|
|
183
|
+
providers: ConfigurableProvider[];
|
|
306
184
|
}>>;
|
|
307
|
-
|
|
185
|
+
models(request: {
|
|
308
186
|
rpcId: string;
|
|
309
|
-
payload:
|
|
310
|
-
path: string;
|
|
311
|
-
};
|
|
187
|
+
payload: Record<string, never>;
|
|
312
188
|
}): Promise<RpcResponse<{
|
|
313
|
-
|
|
314
|
-
|
|
189
|
+
groups: ModelProviderGroupView[];
|
|
190
|
+
failures: {
|
|
191
|
+
provider: string;
|
|
192
|
+
message: string;
|
|
193
|
+
}[];
|
|
315
194
|
}>>;
|
|
316
|
-
|
|
195
|
+
discoverModels(request: {
|
|
317
196
|
rpcId: string;
|
|
318
197
|
payload: {
|
|
319
|
-
|
|
320
|
-
|
|
198
|
+
settingsNs: string;
|
|
199
|
+
provider?: string;
|
|
200
|
+
baseURL?: string;
|
|
201
|
+
api?: string;
|
|
202
|
+
apiKey?: string;
|
|
321
203
|
};
|
|
322
204
|
}): Promise<RpcResponse<{
|
|
323
|
-
|
|
205
|
+
models: ProviderModelView[];
|
|
324
206
|
}>>;
|
|
325
|
-
delete(request: {
|
|
326
|
-
rpcId: string;
|
|
327
|
-
payload: {
|
|
328
|
-
workspaceId: string;
|
|
329
|
-
};
|
|
330
|
-
}): Promise<RpcResponse<{
|
|
331
|
-
deleted: true;
|
|
332
|
-
}>>;
|
|
333
|
-
archiveSession(request: {
|
|
334
|
-
rpcId: string;
|
|
335
|
-
payload: {
|
|
336
|
-
sessionId: string;
|
|
337
|
-
};
|
|
338
|
-
}): Promise<RpcResponse<{
|
|
339
|
-
archivedSessionIds: string[];
|
|
340
|
-
}>>;
|
|
341
|
-
};
|
|
342
|
-
agentPresets: {
|
|
343
|
-
list(request: {
|
|
344
|
-
rpcId: string;
|
|
345
|
-
payload: Record<string, never>;
|
|
346
|
-
}): Promise<RpcResponse<{
|
|
347
|
-
presets: DshAgentPresetSummary[];
|
|
348
|
-
authorable: boolean;
|
|
349
|
-
hasDocument: boolean;
|
|
350
|
-
}>>;
|
|
351
|
-
select(request: {
|
|
352
|
-
rpcId: string;
|
|
353
|
-
payload: {
|
|
354
|
-
sessionId: string;
|
|
355
|
-
agentPreset: string;
|
|
356
|
-
};
|
|
357
|
-
}): Promise<RpcResponse<{
|
|
358
|
-
agentPreset: string;
|
|
359
|
-
}>>;
|
|
360
|
-
};
|
|
361
|
-
/**
|
|
362
|
-
* 用户问题(内核 `ctx.userQuestions`,即 ask_user_question 工具)。
|
|
363
|
-
*
|
|
364
|
-
* 为什么单开一个命名空间而不是塞进 `sessions`:它由**另一个内核服务**提供
|
|
365
|
-
* (`@deepseek-ai/dsh-user-questions`),在一条没装它的 profile 上是**可选能力** ——
|
|
366
|
-
* 分开之后,「这个能力不在」和「这个会话有问题」在类型上就分得开。
|
|
367
|
-
*/
|
|
368
|
-
userQuestions: {
|
|
369
|
-
/**
|
|
370
|
-
* 列出还能回答的问题(内核 `continued(agent)`)。
|
|
371
|
-
* 没有 live agent、或没有该服务时返回空列表 —— 冷会话是常态,不该报错。
|
|
372
|
-
*/
|
|
373
|
-
list(request: {
|
|
374
|
-
rpcId: string;
|
|
375
|
-
payload: {
|
|
376
|
-
sessionId: string;
|
|
377
|
-
};
|
|
378
|
-
}): Promise<RpcResponse<{
|
|
379
|
-
items: UserQuestionView[];
|
|
380
|
-
}>>;
|
|
381
|
-
/** 提交答案;失败原因见 [UserQuestionAnswerOutcome]。 */
|
|
382
|
-
answer(request: {
|
|
383
|
-
rpcId: string;
|
|
384
|
-
payload: {
|
|
385
|
-
sessionId: string;
|
|
386
|
-
callId: string;
|
|
387
|
-
answer: UserQuestionAnswer;
|
|
388
|
-
};
|
|
389
|
-
}): Promise<RpcResponse<UserQuestionAnswerOutcome>>;
|
|
390
|
-
};
|
|
391
|
-
events: {
|
|
392
|
-
mux(request: {
|
|
393
|
-
rpcId: string;
|
|
394
|
-
payload: Record<string, never>;
|
|
395
|
-
}, signal: AbortSignal): AsyncIterable<{
|
|
396
|
-
rpcId: string;
|
|
397
|
-
payload: Record<string, unknown>;
|
|
398
|
-
}>;
|
|
399
|
-
host(request: {
|
|
400
|
-
rpcId: string;
|
|
401
|
-
payload: Record<string, never>;
|
|
402
|
-
}, signal: AbortSignal): AsyncIterable<{
|
|
403
|
-
rpcId: string;
|
|
404
|
-
payload: Record<string, unknown>;
|
|
405
|
-
}>;
|
|
406
207
|
};
|
|
407
208
|
}
|
|
408
|
-
interface
|
|
409
|
-
provider: string;
|
|
410
|
-
model: string;
|
|
411
|
-
reasoningEffort?: string;
|
|
412
|
-
}
|
|
413
|
-
interface ModelReasoningView {
|
|
414
|
-
efforts: {
|
|
415
|
-
id: string;
|
|
416
|
-
name: string;
|
|
417
|
-
description?: string;
|
|
418
|
-
}[];
|
|
419
|
-
defaultEffort?: string;
|
|
420
|
-
}
|
|
421
|
-
interface ModelProviderGroupView {
|
|
209
|
+
interface ProviderModelView {
|
|
422
210
|
id: string;
|
|
423
|
-
name
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
name: string;
|
|
427
|
-
description?: string;
|
|
428
|
-
contextWindow?: number;
|
|
429
|
-
maxTokens?: number;
|
|
430
|
-
reasoning?: ModelReasoningView;
|
|
431
|
-
}[];
|
|
432
|
-
}
|
|
433
|
-
interface SessionModelsView {
|
|
434
|
-
current: ModelSelectionView;
|
|
435
|
-
routable: boolean;
|
|
436
|
-
groups: ModelProviderGroupView[];
|
|
437
|
-
failures: {
|
|
438
|
-
provider: string;
|
|
439
|
-
message: string;
|
|
440
|
-
}[];
|
|
441
|
-
}
|
|
442
|
-
interface CommandDescriptorView {
|
|
443
|
-
name: string;
|
|
444
|
-
description: string;
|
|
445
|
-
input?: {
|
|
446
|
-
hint: string;
|
|
447
|
-
};
|
|
448
|
-
}
|
|
449
|
-
interface CommandExecutionView {
|
|
450
|
-
commandId: string;
|
|
451
|
-
result: {
|
|
452
|
-
kind: 'success' | 'error';
|
|
453
|
-
text?: string;
|
|
454
|
-
sourceEventSeq?: number;
|
|
455
|
-
};
|
|
456
|
-
}
|
|
457
|
-
interface DshCommandRuntime {
|
|
458
|
-
list(agent: unknown): readonly CommandDescriptorView[];
|
|
459
|
-
execute(agent: unknown, line: string, signal: AbortSignal): Promise<CommandExecutionView | undefined>;
|
|
460
|
-
}
|
|
461
|
-
interface DshAgentRegistry {
|
|
462
|
-
get(sessionId: string): unknown | undefined;
|
|
211
|
+
name?: string;
|
|
212
|
+
contextWindow?: number;
|
|
213
|
+
maxTokens?: number;
|
|
463
214
|
}
|
|
464
|
-
interface
|
|
465
|
-
|
|
466
|
-
|
|
215
|
+
interface ProviderConfigView {
|
|
216
|
+
baseURL?: string;
|
|
217
|
+
api?: string;
|
|
218
|
+
displayName?: string;
|
|
219
|
+
thinking?: string;
|
|
220
|
+
reasoningEffort?: string;
|
|
221
|
+
models: ProviderModelView[];
|
|
222
|
+
modelsInherited?: boolean;
|
|
467
223
|
}
|
|
468
|
-
interface
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
224
|
+
interface ProviderView {
|
|
225
|
+
id: string;
|
|
226
|
+
displayName: string;
|
|
227
|
+
active: boolean;
|
|
228
|
+
declared?: boolean;
|
|
229
|
+
configurable: boolean;
|
|
230
|
+
configured: boolean;
|
|
231
|
+
removable: boolean;
|
|
232
|
+
credential: {
|
|
233
|
+
ref: string;
|
|
234
|
+
configured: boolean;
|
|
235
|
+
source?: string;
|
|
236
|
+
writable: boolean;
|
|
480
237
|
};
|
|
238
|
+
config: ProviderConfigView;
|
|
481
239
|
}
|
|
482
|
-
interface
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
240
|
+
interface ProviderPatch {
|
|
241
|
+
displayName?: string | null;
|
|
242
|
+
baseURL?: string | null;
|
|
243
|
+
api?: string | null;
|
|
244
|
+
apiKey?: string | null;
|
|
245
|
+
thinking?: string | null;
|
|
246
|
+
reasoningEffort?: string | null;
|
|
247
|
+
models?: ProviderModelView[];
|
|
248
|
+
expectedRevision?: number;
|
|
489
249
|
}
|
|
490
|
-
interface
|
|
250
|
+
interface CustomProviderCreate {
|
|
491
251
|
id: string;
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
252
|
+
displayName?: string;
|
|
253
|
+
baseURL: string;
|
|
254
|
+
api: string;
|
|
255
|
+
apiKey?: string;
|
|
256
|
+
models: ProviderModelView[];
|
|
257
|
+
expectedRevision?: number;
|
|
497
258
|
}
|
|
498
|
-
interface
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
updatedAt: number;
|
|
503
|
-
running: boolean;
|
|
504
|
-
blank: boolean;
|
|
505
|
-
title?: string;
|
|
506
|
-
workspaceId?: string;
|
|
507
|
-
workspaceTitle?: string;
|
|
508
|
-
agentPreset?: string;
|
|
509
|
-
parentSessionId?: string;
|
|
510
|
-
origin?: 'subagent';
|
|
511
|
-
pendingInteraction?: 'approval';
|
|
259
|
+
interface CustomProviderCapability {
|
|
260
|
+
available: boolean;
|
|
261
|
+
protocols: string[];
|
|
262
|
+
revision?: number;
|
|
512
263
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
264
|
+
declare class DshSettingsAdapter {
|
|
265
|
+
private readonly api;
|
|
266
|
+
constructor(api: SettingsApiProxy);
|
|
267
|
+
listProviders(): Promise<{
|
|
268
|
+
writable: boolean;
|
|
269
|
+
revisionByNamespace: Record<string, number>;
|
|
270
|
+
customProvider: CustomProviderCapability;
|
|
271
|
+
providers: ProviderView[];
|
|
272
|
+
}>;
|
|
273
|
+
catalog(): Promise<{
|
|
274
|
+
groups: ModelProviderGroupView[];
|
|
275
|
+
failures: {
|
|
276
|
+
provider: string;
|
|
277
|
+
message: string;
|
|
278
|
+
}[];
|
|
279
|
+
}>;
|
|
280
|
+
updateProvider(providerId: string, patch: ProviderPatch): Promise<void>;
|
|
281
|
+
createProvider(input: CustomProviderCreate): Promise<void>;
|
|
282
|
+
removeProvider(providerId: string, expectedRevision?: number): Promise<void>;
|
|
283
|
+
discover(providerId: string, draft: {
|
|
284
|
+
baseURL?: string;
|
|
285
|
+
api?: string;
|
|
286
|
+
apiKey?: string;
|
|
287
|
+
}): Promise<ProviderModelView[]>;
|
|
288
|
+
private providerContext;
|
|
521
289
|
}
|
|
290
|
+
//#endregion
|
|
291
|
+
//#region src/host/dsh-0.2-bridge.d.ts
|
|
522
292
|
/**
|
|
523
|
-
*
|
|
293
|
+
* Prompt content a 0.2.x Host accepts — mirrors its `PromptContentPart` union.
|
|
524
294
|
*
|
|
525
|
-
*
|
|
526
|
-
*
|
|
527
|
-
* 不该等插件跟着发版。所以这里只钉住三个**契约字段**,其余原样透传。
|
|
295
|
+
* 权威定义在 `chat-adapter.ts`(协议层),这里只是复用它,避免两处各写一遍
|
|
296
|
+
* 然后悄悄漂移。
|
|
528
297
|
*/
|
|
529
|
-
|
|
530
|
-
callId: string;
|
|
531
|
-
/** 内核 `UserQuestionState`:'open' 或 'continued';本端点只返回 continued。 */
|
|
532
|
-
state: string;
|
|
533
|
-
/** 问题项**原样**透传(id / question / detail? / header? / options? / multiSelect? / intent?)。 */
|
|
534
|
-
questions: unknown[];
|
|
535
|
-
/** 内核以后加的字段照样透传,不在这里裁剪。 */
|
|
536
|
-
[key: string]: unknown;
|
|
537
|
-
}
|
|
538
|
-
/** 一条问题的答案(内核 `AskUserQuestionAnswerItem`)。 */
|
|
539
|
-
interface UserQuestionAnswerItem {
|
|
540
|
-
/** 被回答的问题 id,必须与请求里的问题 id 对上。 */
|
|
541
|
-
id: string;
|
|
542
|
-
/** 选中的选项 label;多选时可能多项。 */
|
|
543
|
-
selected: string[];
|
|
544
|
-
/** 可选的「其他」自由文本。 */
|
|
545
|
-
custom?: string;
|
|
546
|
-
}
|
|
547
|
-
/** 一次提交的完整答案批次(内核 `AskUserQuestionAnswer`):一次调用里的每个问题各占一项。 */
|
|
548
|
-
interface UserQuestionAnswer {
|
|
549
|
-
answers: UserQuestionAnswerItem[];
|
|
550
|
-
}
|
|
298
|
+
type PromptContentPartLike = PromptContentPart;
|
|
551
299
|
/**
|
|
552
|
-
*
|
|
300
|
+
* `ctx.fileUploads`:内核的文件上传服务。
|
|
553
301
|
*
|
|
554
|
-
*
|
|
555
|
-
*
|
|
556
|
-
* 409 / 400 / 503。做成**值**之后由 adapter 决定 HTTP 状态,
|
|
557
|
-
* 和 approvals 的 `respond()` 返回 `{ accepted: false, reason }` 是同一个套路。
|
|
302
|
+
* 由内核的 `@deepseek-ai/dsh-client-file-upload` 以 cordis 服务的形式暴露,
|
|
303
|
+
* 因此插件**不需要**把字节转发到内核的 HTTP 路由(那条路由只绑 loopback,手机也到不了)。
|
|
558
304
|
*/
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
/**
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
}
|
|
573
|
-
accepted: false;
|
|
574
|
-
reason: UserQuestionAnswerFailure;
|
|
575
|
-
};
|
|
576
|
-
interface ChatWorkspaceView {
|
|
577
|
-
id: string;
|
|
578
|
-
title: string;
|
|
579
|
-
rootId: string;
|
|
580
|
-
path: string;
|
|
581
|
-
createdAt: string;
|
|
582
|
-
updatedAt: string;
|
|
305
|
+
interface FileUploadsLike {
|
|
306
|
+
upload(agent: unknown, request: {
|
|
307
|
+
data: string;
|
|
308
|
+
name?: string;
|
|
309
|
+
}, signal: AbortSignal): Promise<{
|
|
310
|
+
receiptId: string;
|
|
311
|
+
}>;
|
|
312
|
+
/**
|
|
313
|
+
* 把凭证绑定到某次 prompt。返回值不 commit 的话凭证会被回收 ——
|
|
314
|
+
* 所以调用方必须在 prompt **成功之后** commit()。
|
|
315
|
+
*/
|
|
316
|
+
bindPrompt(agent: unknown, receiptIds: readonly string[], requestId: string): {
|
|
317
|
+
commit(): void;
|
|
318
|
+
};
|
|
583
319
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
320
|
+
/**
|
|
321
|
+
* `ctx.attachments`:内核的附件存储。
|
|
322
|
+
*
|
|
323
|
+
* 为什么会话里的图片必须经由它:消息**发送时**图片是 base64 内联的,但**存进历史后**
|
|
324
|
+
* 会变成 `{type:'image', attachment:{attachmentId:'sha256:…'}}` 这样的引用 ——
|
|
325
|
+
* 字节在附件存储里,不在事件里。客户端只拿到 id,所以必须有一步「按 id 取字节」,
|
|
326
|
+
* 否则历史里的图片一律显示不出来。
|
|
327
|
+
*/
|
|
328
|
+
interface AttachmentStoreLike {
|
|
329
|
+
readImage(
|
|
330
|
+
/**
|
|
331
|
+
* 附件引用。
|
|
332
|
+
*
|
|
333
|
+
* `attachmentId` 是唯一的**定位**字段(内核用 `ID_PATTERN.exec()` 取出 sha256
|
|
334
|
+
* 再拼 `objects/<前两位>/<sha>`);而 `mediaType` / `bytes` / `width` / `height`
|
|
335
|
+
* 参与读后**完整性校验** —— 缺任何一项都会抛 `ATTACHMENT_CORRUPT`,
|
|
336
|
+
* 哪怕字节已经正确读出来了。
|
|
337
|
+
*/
|
|
338
|
+
ref: {
|
|
339
|
+
attachmentId: string;
|
|
340
|
+
mediaType?: string;
|
|
341
|
+
bytes?: number;
|
|
342
|
+
width?: number;
|
|
343
|
+
height?: number;
|
|
344
|
+
}, signal?: AbortSignal): Promise<{
|
|
345
|
+
ref: {
|
|
346
|
+
mediaType: string;
|
|
347
|
+
};
|
|
348
|
+
data: Uint8Array;
|
|
349
|
+
}>;
|
|
591
350
|
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
private readonly sessionRootLookups;
|
|
598
|
-
private readonly pendingApprovals;
|
|
599
|
-
constructor(api: DshApiProxy, database: WorkspaceDatabase, commandServices?: DshCommandServices | undefined);
|
|
600
|
-
listSessions(principal: Principal): Promise<ChatSessionView[]>;
|
|
601
|
-
listWorkspaces(principal: Principal): Promise<ChatWorkspaceView[]>;
|
|
602
|
-
createWorkspace(principal: Principal, rootId: string, relativePath: string): Promise<ChatWorkspaceView>;
|
|
603
|
-
renameWorkspace(principal: Principal, workspaceId: string, title: string): Promise<ChatWorkspaceView>;
|
|
604
|
-
deleteWorkspace(principal: Principal, workspaceId: string): Promise<void>;
|
|
605
|
-
listAgentPresets(): Promise<AgentPresetView[]>;
|
|
606
|
-
selectAgentPreset(principal: Principal, sessionId: string, agentPreset: string): Promise<string>;
|
|
607
|
-
createSessionInWorkspace(principal: Principal, workspaceId: string, options: {
|
|
608
|
-
sessionId?: string;
|
|
609
|
-
agentPreset?: string;
|
|
610
|
-
}): Promise<{
|
|
611
|
-
id: string;
|
|
612
|
-
agentPreset?: string;
|
|
351
|
+
interface SessionControllerLike {
|
|
352
|
+
list(request: {
|
|
353
|
+
cursor?: string;
|
|
354
|
+
}, signal: AbortSignal): Promise<{
|
|
355
|
+
items: readonly unknown[];
|
|
613
356
|
}>;
|
|
614
|
-
|
|
357
|
+
create(request: {
|
|
358
|
+
workspaceId?: string;
|
|
359
|
+
cwd?: string;
|
|
615
360
|
sessionId?: string;
|
|
616
361
|
agentPreset?: string;
|
|
617
362
|
}): Promise<{
|
|
618
|
-
|
|
363
|
+
sessionId: string;
|
|
619
364
|
agentPreset?: string;
|
|
620
365
|
}>;
|
|
621
|
-
|
|
622
|
-
|
|
366
|
+
/**
|
|
367
|
+
* Live follow stream. Its first frame is always a `snapshot` carrying the
|
|
368
|
+
* opening window (`cursor`, `records`, `hasMore`), and that is the only
|
|
369
|
+
* reliable way to read history: `page()` demands a `throughSeq` "obtained from
|
|
370
|
+
* the corresponding follow opening frame", so guessing one (e.g. `-1`) returns
|
|
371
|
+
* a well-formed but *empty* page — a silent failure, not an error.
|
|
372
|
+
*/
|
|
373
|
+
follow(request: {
|
|
374
|
+
address: {
|
|
375
|
+
kind: 'session';
|
|
376
|
+
sessionId: string;
|
|
377
|
+
};
|
|
623
378
|
maxMessages?: number;
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
379
|
+
/**
|
|
380
|
+
* Assistant presentation frames are opt-in: without `assistantStream: true`
|
|
381
|
+
* the host emits durable events only, so a live stream would carry no
|
|
382
|
+
* incremental text at all (the transcript then appears only after commit).
|
|
383
|
+
*/
|
|
384
|
+
assistantStream?: true;
|
|
385
|
+
}, signal: AbortSignal): AsyncIterable<{
|
|
386
|
+
type?: string;
|
|
387
|
+
cursor?: number;
|
|
388
|
+
records?: readonly unknown[];
|
|
389
|
+
hasMore?: boolean;
|
|
628
390
|
}>;
|
|
629
|
-
prompt(
|
|
391
|
+
prompt(request: {
|
|
392
|
+
requestId: string;
|
|
393
|
+
sessionId: string;
|
|
394
|
+
mode: 'queue' | 'steer';
|
|
395
|
+
content: readonly PromptContentPartLike[];
|
|
396
|
+
clientTimeZone?: string;
|
|
397
|
+
}, signal: AbortSignal): Promise<{
|
|
630
398
|
accepted: true;
|
|
631
|
-
command?: unknown;
|
|
632
399
|
}>;
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
400
|
+
cancel(request: {
|
|
401
|
+
sessionId: string;
|
|
402
|
+
}): {
|
|
403
|
+
accepted: true;
|
|
404
|
+
};
|
|
405
|
+
rename(request: {
|
|
406
|
+
sessionId: string;
|
|
407
|
+
title: string;
|
|
408
|
+
}): Promise<{
|
|
409
|
+
title: string;
|
|
410
|
+
seq: number;
|
|
411
|
+
}>;
|
|
412
|
+
fork(request: {
|
|
413
|
+
sessionId: string;
|
|
414
|
+
atSeq?: number;
|
|
415
|
+
}): Promise<{
|
|
416
|
+
sessionId: string;
|
|
642
417
|
}>;
|
|
643
418
|
/**
|
|
644
|
-
*
|
|
419
|
+
* 取一张**被这个会话引用过**的图片字节(内核主机端方法,名字就叫 `attachment`)。
|
|
645
420
|
*
|
|
646
|
-
*
|
|
647
|
-
* `ctx.attachments.readImage
|
|
648
|
-
|
|
649
|
-
* 自己拼 ref 那条路根本走不通;
|
|
650
|
-
* 2. 内核那个方法内部一次做完三件事:校验该会话确实引用了这张图、
|
|
651
|
-
* 读出字节、用**会话事件里那条真实 ref** 做完整性校验
|
|
652
|
-
* (`mediaType` / `bytes` / `width` / `height` 四项逐项比对)。
|
|
653
|
-
* 绕开它就得把这三件事全复刻 —— 先前正是漏了第三件,于是每张图
|
|
654
|
-
* `ATTACHMENT_CORRUPT`:「字节读到了,却被校验拒之门外」。
|
|
421
|
+
* 这是取历史图片的**正路**,它内部一次做完三件事:校验会话确实引用了这张图、
|
|
422
|
+
* 调 `ctx.attachments.readImage`、用**会话事件里那条真实 ref**做完整性校验
|
|
423
|
+
*(`mediaType` / `bytes` / `width` / `height` 四项逐项比对)。
|
|
655
424
|
*
|
|
656
|
-
*
|
|
657
|
-
*
|
|
425
|
+
* 为什么必须走它、不能自己调 `ctx.attachments`:实测本机这条 profile 里
|
|
426
|
+
* `ctx.attachments` 是 **MISSING**(诊断日志
|
|
427
|
+
* `ctx.attachments MISSING(所以改走内核 sessionController.readAttachment)`),
|
|
428
|
+
* 自己拼 ref 那条路根本走不通;而且绕开内核就得把它内部三件事全复刻一遍,
|
|
429
|
+
* 早先正是只传了 id、缺四项元数据,导致 `ATTACHMENT_CORRUPT`。
|
|
430
|
+
*
|
|
431
|
+
* 注意方法名:**主机端叫 `attachment`**,客户端契约里才叫 `readAttachment`
|
|
432
|
+
*(`dsh-api-session-controller/lib/types/client/contract/session.d.ts`)。
|
|
433
|
+
* `data` 是 **base64 字符串**(主机端实现里 `Buffer.from(stored.data).toString("base64")`),
|
|
434
|
+
* 不是 Uint8Array —— 调用方要自己解码。
|
|
658
435
|
*/
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
436
|
+
attachment(request: {
|
|
437
|
+
sessionId: string;
|
|
438
|
+
attachmentId: string;
|
|
439
|
+
}): Promise<{
|
|
440
|
+
attachment: {
|
|
441
|
+
mediaType: string;
|
|
442
|
+
};
|
|
443
|
+
data: string;
|
|
444
|
+
}>;
|
|
445
|
+
selectModel(request: {
|
|
446
|
+
sessionId: string;
|
|
447
|
+
provider: string;
|
|
448
|
+
model: string;
|
|
449
|
+
reasoningEffort?: string;
|
|
450
|
+
}): Promise<{
|
|
451
|
+
selected: unknown;
|
|
662
452
|
}>;
|
|
663
453
|
/**
|
|
664
|
-
*
|
|
665
|
-
*
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
454
|
+
* Host model catalog. 0.2.x reports `default`, the routable route list, and
|
|
455
|
+
* per-route failures; the plugin's `SessionModelsView` instead wants a boolean
|
|
456
|
+
* `routable` plus a per-session `current`, so the bridge folds them (see the
|
|
457
|
+
* `models` method) rather than passing this through.
|
|
458
|
+
*/
|
|
459
|
+
modelCatalog(): Promise<{
|
|
460
|
+
default?: unknown;
|
|
461
|
+
routableProviders?: readonly string[];
|
|
462
|
+
groups?: readonly unknown[];
|
|
463
|
+
failures?: readonly {
|
|
464
|
+
id?: unknown;
|
|
465
|
+
name?: unknown;
|
|
466
|
+
message?: unknown;
|
|
467
|
+
}[];
|
|
670
468
|
}>;
|
|
671
|
-
|
|
672
|
-
|
|
469
|
+
/**
|
|
470
|
+
* Projection baseline for one session. Unlike the catalog this is per-session,
|
|
471
|
+
* which is what makes it the source of the session's *current* model selection
|
|
472
|
+
* (`values.modelSelection`).
|
|
473
|
+
*/
|
|
474
|
+
projections(request: {
|
|
475
|
+
sessionId: string;
|
|
476
|
+
}, signal: AbortSignal): Promise<{
|
|
477
|
+
asOfSeq?: number;
|
|
478
|
+
values?: Record<string, unknown>;
|
|
479
|
+
} | null>;
|
|
480
|
+
resolveAgent(sessionId: string): Promise<unknown>;
|
|
481
|
+
}
|
|
482
|
+
interface WorkspaceControllerLike {
|
|
483
|
+
create(request: {
|
|
484
|
+
path: string;
|
|
485
|
+
}): Promise<{
|
|
486
|
+
workspace: unknown;
|
|
487
|
+
created: boolean;
|
|
488
|
+
}>;
|
|
489
|
+
rename(request: {
|
|
490
|
+
workspaceId: string;
|
|
673
491
|
title: string;
|
|
674
|
-
|
|
492
|
+
}): Promise<unknown>;
|
|
493
|
+
delete(request: {
|
|
494
|
+
workspaceId: string;
|
|
495
|
+
}): Promise<{
|
|
496
|
+
deleted: boolean;
|
|
675
497
|
}>;
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
498
|
+
archiveSession(request: {
|
|
499
|
+
sessionId: string;
|
|
500
|
+
}): Promise<{
|
|
501
|
+
archivedSessionIds: readonly string[];
|
|
502
|
+
}>;
|
|
503
|
+
}
|
|
504
|
+
interface WorkspaceRegistryLike {
|
|
505
|
+
list(): readonly {
|
|
506
|
+
id: string;
|
|
507
|
+
path: string;
|
|
508
|
+
title: string;
|
|
509
|
+
createdAt: string;
|
|
510
|
+
updatedAt: string;
|
|
511
|
+
sessionIds?: readonly string[];
|
|
512
|
+
}[];
|
|
513
|
+
}
|
|
514
|
+
interface SettingsControllerLike {
|
|
515
|
+
describe(): {
|
|
516
|
+
writable: boolean;
|
|
517
|
+
hasDocument: boolean;
|
|
518
|
+
namespaces: readonly unknown[];
|
|
519
|
+
};
|
|
520
|
+
mutate(ns: string, ops: readonly unknown[], expectedRevision: number | undefined): Promise<unknown>;
|
|
521
|
+
}
|
|
522
|
+
interface CredentialsControllerLike {
|
|
523
|
+
describe(refs: readonly string[]): Promise<Record<string, unknown>>;
|
|
524
|
+
set(ref: string, value: string): Promise<void>;
|
|
525
|
+
unset(ref: string): Promise<void>;
|
|
526
|
+
}
|
|
527
|
+
interface AgentPresetsLike {
|
|
528
|
+
list(): Promise<readonly unknown[]>;
|
|
529
|
+
remoteExportList(): Promise<unknown>;
|
|
530
|
+
resolveAgent?: never;
|
|
531
|
+
select(agent: unknown, agentPreset: string): Promise<string>;
|
|
532
|
+
}
|
|
533
|
+
interface LlmLike {
|
|
534
|
+
/** Live routes, as `{ id, name }` rows. */
|
|
535
|
+
listProviders(): readonly {
|
|
536
|
+
id: string;
|
|
537
|
+
}[];
|
|
680
538
|
/**
|
|
681
|
-
*
|
|
539
|
+
* Every route an adapter plugin could activate through configuration, live or
|
|
540
|
+
* dormant, carrying the settings namespace and path that configures it.
|
|
541
|
+
* Configuration surfaces merge this with `listProviders()` for liveness.
|
|
542
|
+
*/
|
|
543
|
+
listConfigurableProviders(): readonly {
|
|
544
|
+
provider: string;
|
|
545
|
+
displayName: string;
|
|
546
|
+
settingsNs: string;
|
|
547
|
+
settingsPath: readonly string[];
|
|
548
|
+
declared?: boolean;
|
|
549
|
+
error?: string;
|
|
550
|
+
}[];
|
|
551
|
+
discoverModels(settingsNs: string, request: unknown, signal?: AbortSignal): Promise<readonly unknown[]>;
|
|
552
|
+
/**
|
|
553
|
+
* 一个路由 + 模型 id 的**真实元数据**。
|
|
682
554
|
*
|
|
683
|
-
*
|
|
684
|
-
*
|
|
555
|
+
* 为什么必须用它、而不是自己猜模型能不能看图:内核拒绝「给纯文本模型发图片」时
|
|
556
|
+
* 用的就是**这个方法**(`SessionController` 的 prompt 准入里
|
|
557
|
+
* `this.ctx.llm.resolveModelInfo(...)` 再查 `inputModalities`)。入口要与内核
|
|
558
|
+
* 得出同一个结论,只能查同一份元数据 —— 名字、前缀、家族表都是启发式,会漂。
|
|
685
559
|
*/
|
|
686
|
-
|
|
560
|
+
resolveModelInfo(provider: string, model: string, signal?: AbortSignal): Promise<{
|
|
561
|
+
inputModalities?: readonly string[];
|
|
562
|
+
} | undefined>;
|
|
563
|
+
}
|
|
564
|
+
/** Host services the bridge consumes; assembled by `src/index.ts` from `ctx`. */
|
|
565
|
+
interface Dsh02HostServices {
|
|
566
|
+
sessionController: SessionControllerLike;
|
|
567
|
+
workspaceController: WorkspaceControllerLike;
|
|
568
|
+
workspaceRegistry: WorkspaceRegistryLike;
|
|
569
|
+
settingsController: SettingsControllerLike;
|
|
570
|
+
credentialsController: CredentialsControllerLike;
|
|
571
|
+
agentPresets: AgentPresetsLike;
|
|
572
|
+
llm: LlmLike;
|
|
687
573
|
/**
|
|
688
|
-
*
|
|
574
|
+
* 内核附件存储(`ctx.attachments`);会话历史里的图片要经它取字节。
|
|
689
575
|
*
|
|
690
|
-
*
|
|
691
|
-
*
|
|
692
|
-
*
|
|
693
|
-
*
|
|
576
|
+
* 必须是**已经取到的服务对象**,不能是 `ctx` 本身:cordis 的注入属性是有生命周期的
|
|
577
|
+
* 惰性访问器,只在 `apply` 执行期间可读。把它存进 `host` 里、等到 HTTP 请求回调
|
|
578
|
+
* (`readAttachment`)里再读,就会撞上
|
|
579
|
+
* `cannot get property "attachments" without inject` —— 请求链路本身没问题,
|
|
580
|
+
* 挂在服务端、而且只在真的去取图时才炸,所以很难在本地测出来。
|
|
694
581
|
*/
|
|
695
|
-
|
|
696
|
-
models(principal: Principal, sessionId: string): Promise<SessionModelsView>;
|
|
697
|
-
selectModel(principal: Principal, sessionId: string, selection: ModelSelectionView): Promise<ModelSelectionView>;
|
|
698
|
-
listCommands(principal: Principal, sessionId: string): Promise<CommandDescriptorView[]>;
|
|
699
|
-
executeCommand(principal: Principal, sessionId: string, line: string): Promise<CommandExecutionView>;
|
|
700
|
-
isSessionAuthorized(principal: Principal, sessionId: string): Promise<boolean>;
|
|
701
|
-
startEvents(signal: AbortSignal, emit: (event: WorkspaceEvent) => void): void;
|
|
702
|
-
private captureApproval;
|
|
703
|
-
private hasPendingApproval;
|
|
582
|
+
attachments: AttachmentStoreLike;
|
|
704
583
|
/**
|
|
705
|
-
*
|
|
584
|
+
* Kernel file-upload service (`ctx.fileUploads`).
|
|
706
585
|
*
|
|
707
|
-
*
|
|
708
|
-
*
|
|
709
|
-
* property of the session rather than a failure of this call: the caller
|
|
710
|
-
* decides whether it means "no commands" (listing) or an error (execution).
|
|
586
|
+
* 为什么用它而不是内核那条 `/api/session/uploadFileBinary` HTTP 路由:那条路由
|
|
587
|
+
* 只绑 loopback(手机根本到不了),而这个 cordis 服务就在同一个进程里,插件可直接调。
|
|
711
588
|
*/
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
589
|
+
fileUploads: FileUploadsLike;
|
|
590
|
+
/**
|
|
591
|
+
* Cordis event bus of the host context. Used for the two facts the old
|
|
592
|
+
* `apiProxy` event streams carried that no controller returns: session
|
|
593
|
+
* registry changes and the approval answerer waterfall.
|
|
594
|
+
*/
|
|
595
|
+
on(event: string, listener: (...args: any[]) => unknown): () => void;
|
|
719
596
|
}
|
|
597
|
+
/**
|
|
598
|
+
* 需要交接时,把每个 `image` 部件换成**两行文本**;否则原样返回。
|
|
599
|
+
*
|
|
600
|
+
* 同一行路径要同时伺候两类读者:
|
|
601
|
+
* - **给人看**(第一行):图片序号、原始文件名、工作目录里的短路径;
|
|
602
|
+
* - **给机器看**(第二行 `[Image: source: <绝对路径>]`):这是读图技能明确
|
|
603
|
+
* 识别的占位形状之一(带图片扩展名的绝对路径同样触发),任何按路径读图的
|
|
604
|
+
* 工具都能接手,不出现任何具体插件名。
|
|
605
|
+
*
|
|
606
|
+
* 任何一步抛错都退回原内容:这个功能的失败**不该**把「发消息」本身弄坏。
|
|
607
|
+
*/
|
|
608
|
+
/**
|
|
609
|
+
* 交接文案用哪种语言。
|
|
610
|
+
*
|
|
611
|
+
* 判据是**发消息那个客户端的界面语言**,由 HTTP `Accept-Language` 头带进来
|
|
612
|
+
* (router → chat-adapter → 这里)。拿不到就退回中文 —— 那是这个功能既有的默认口径,
|
|
613
|
+
* 判断不出来的时候不该突然换语言。
|
|
614
|
+
*/
|
|
615
|
+
type HandoffLocale = 'zh' | 'en';
|
|
720
616
|
//#endregion
|
|
721
|
-
//#region src/host/
|
|
722
|
-
interface
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
settingsPath: string[];
|
|
735
|
-
active: boolean;
|
|
736
|
-
declared?: boolean;
|
|
617
|
+
//#region src/host/chat-adapter.d.ts
|
|
618
|
+
interface RpcResponse<T> {
|
|
619
|
+
result: {
|
|
620
|
+
ok: true;
|
|
621
|
+
value: T;
|
|
622
|
+
} | {
|
|
623
|
+
ok: false;
|
|
624
|
+
error: {
|
|
625
|
+
code: string;
|
|
626
|
+
message: string;
|
|
627
|
+
details?: unknown;
|
|
628
|
+
};
|
|
629
|
+
};
|
|
737
630
|
}
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
631
|
+
/**
|
|
632
|
+
* 一条 prompt 的内容片段 —— 与 DSH 0.2.x 的 `PromptContentPart` 一一对应。
|
|
633
|
+
*
|
|
634
|
+
* 三种形态的代价差别很大,值得写清楚:
|
|
635
|
+
* - `text` 直接发送;
|
|
636
|
+
* - `image` 字节**内联**在 payload 里(base64),一次调用完成,但请求体膨胀约 33%,
|
|
637
|
+
* 所以客户端必须先限制原图大小,别等 413;
|
|
638
|
+
* - `file` 的 `receiptId` 必须来自**先前的**上传调用,并在同一次 prompt 里绑定,
|
|
639
|
+
* 否则凭证会被回收。
|
|
640
|
+
*/
|
|
641
|
+
type PromptContentPart = {
|
|
642
|
+
type: 'text';
|
|
643
|
+
text: string;
|
|
644
|
+
} | {
|
|
645
|
+
type: 'image';
|
|
646
|
+
mediaType: string;
|
|
647
|
+
data: string;
|
|
648
|
+
name?: string;
|
|
649
|
+
} | {
|
|
650
|
+
type: 'file';
|
|
651
|
+
receiptId: string;
|
|
652
|
+
};
|
|
653
|
+
interface DshApiProxy {
|
|
654
|
+
respond(message: {
|
|
655
|
+
type: 'client-response';
|
|
656
|
+
rpcId: string;
|
|
657
|
+
result: {
|
|
658
|
+
ok: true;
|
|
659
|
+
value: unknown;
|
|
660
|
+
};
|
|
661
|
+
}): Promise<{
|
|
662
|
+
accepted: true;
|
|
663
|
+
} | {
|
|
664
|
+
accepted: false;
|
|
665
|
+
reason: 'not-pending' | 'bad-response';
|
|
666
|
+
}>;
|
|
667
|
+
sessions: {
|
|
668
|
+
list(request: {
|
|
669
|
+
rpcId: string;
|
|
670
|
+
payload: {
|
|
671
|
+
cursor?: string;
|
|
672
|
+
};
|
|
673
|
+
}): Promise<RpcResponse<{
|
|
674
|
+
items: DshSessionSummary[];
|
|
675
|
+
}>>;
|
|
676
|
+
create(request: {
|
|
677
|
+
rpcId: string;
|
|
678
|
+
payload: {
|
|
679
|
+
cwd?: string;
|
|
680
|
+
workspaceId?: string;
|
|
681
|
+
sessionId?: string;
|
|
682
|
+
agentPreset?: string;
|
|
683
|
+
};
|
|
684
|
+
}): Promise<RpcResponse<{
|
|
685
|
+
sessionId: string;
|
|
686
|
+
agentPreset?: string;
|
|
687
|
+
}>>;
|
|
688
|
+
history(request: {
|
|
689
|
+
rpcId: string;
|
|
690
|
+
payload: {
|
|
691
|
+
sessionId: string;
|
|
692
|
+
beforeSeq?: number;
|
|
693
|
+
maxMessages?: number;
|
|
694
|
+
};
|
|
695
|
+
}): Promise<RpcResponse<{
|
|
696
|
+
events: unknown[];
|
|
697
|
+
hasMore: boolean;
|
|
698
|
+
projections?: unknown;
|
|
699
|
+
}>>;
|
|
700
|
+
/**
|
|
701
|
+
* 提交一条 prompt。
|
|
702
|
+
*
|
|
703
|
+
* `locale` 不是内核字段,是**我们**加的:告诉图片交接「发消息的客户端界面是什么语言」,
|
|
704
|
+
* 好让交接说明说用户看得懂的话。它会一直传到 `dsh-0.2-bridge` 并在调内核前被摘掉。
|
|
705
|
+
*/
|
|
706
|
+
prompt(request: {
|
|
707
|
+
rpcId: string;
|
|
708
|
+
payload: {
|
|
709
|
+
sessionId: string;
|
|
710
|
+
mode: 'queue' | 'steer';
|
|
711
|
+
content: PromptContentPart[];
|
|
712
|
+
clientTimeZone?: string;
|
|
713
|
+
locale?: HandoffLocale;
|
|
714
|
+
};
|
|
715
|
+
}): Promise<RpcResponse<{
|
|
716
|
+
accepted: true;
|
|
717
|
+
command?: unknown;
|
|
718
|
+
}>>;
|
|
719
|
+
/**
|
|
720
|
+
* 上传一个文件,换取 prompt 可引用的凭证(`receiptId`)。
|
|
721
|
+
* `data` 是文件字节的**标准 base64**。
|
|
722
|
+
*/
|
|
723
|
+
uploadAttachment(request: {
|
|
724
|
+
rpcId: string;
|
|
725
|
+
payload: {
|
|
726
|
+
sessionId: string;
|
|
727
|
+
data: string;
|
|
728
|
+
name?: string;
|
|
729
|
+
};
|
|
730
|
+
}): Promise<RpcResponse<{
|
|
731
|
+
receiptId: string;
|
|
732
|
+
name: string | null;
|
|
733
|
+
}>>;
|
|
734
|
+
/**
|
|
735
|
+
* 按 id 取回附件字节。
|
|
736
|
+
*
|
|
737
|
+
* `attachmentId` 形如 `sha256:<hex>`(**含冒号**,客户端必须 URL 编码)。
|
|
738
|
+
* 返回 `Uint8Array` 而不是 base64:图片动辄上百 KB,多一次编解码纯属浪费。
|
|
739
|
+
*
|
|
740
|
+
* **必须带 `sessionId`**:内核要靠它读会话事件、取出这张图的真实 ref,
|
|
741
|
+
* 再据此做完整性校验(详见 chat-adapter 的 readAttachment 注释)。
|
|
742
|
+
*/
|
|
743
|
+
readAttachment(request: {
|
|
744
|
+
rpcId: string;
|
|
745
|
+
payload: {
|
|
746
|
+
sessionId: string;
|
|
747
|
+
attachmentId: string;
|
|
748
|
+
};
|
|
749
|
+
}): Promise<RpcResponse<{
|
|
750
|
+
bytes: Uint8Array;
|
|
751
|
+
mediaType: string;
|
|
752
|
+
}>>;
|
|
753
|
+
/**
|
|
754
|
+
* 取回一张**交接图片**的字节(纯文本模型发图时,图片被落成工作区文件)。
|
|
755
|
+
*
|
|
756
|
+
* 与 [readAttachment] 的区别:那一个取内核附件存储里的图(走
|
|
757
|
+
* `sessionController.attachment`),这一个取插件自己写进 `.dsh-images/`
|
|
758
|
+
* 的交接件。手机端用它把图片**显示**给人看;消息里的路径文本照旧留给模型。
|
|
759
|
+
*/
|
|
760
|
+
readHandoffImage(request: {
|
|
761
|
+
rpcId: string;
|
|
762
|
+
payload: {
|
|
763
|
+
sessionId: string;
|
|
764
|
+
name: string;
|
|
765
|
+
};
|
|
766
|
+
}): Promise<RpcResponse<{
|
|
767
|
+
bytes: Uint8Array;
|
|
768
|
+
mediaType: string;
|
|
769
|
+
}>>;
|
|
770
|
+
cancel(request: {
|
|
771
|
+
rpcId: string;
|
|
772
|
+
payload: {
|
|
773
|
+
sessionId: string;
|
|
774
|
+
};
|
|
775
|
+
}): Promise<RpcResponse<{
|
|
776
|
+
accepted: true;
|
|
777
|
+
}>>;
|
|
778
|
+
rename(request: {
|
|
779
|
+
rpcId: string;
|
|
780
|
+
payload: {
|
|
781
|
+
sessionId: string;
|
|
782
|
+
title: string;
|
|
783
|
+
};
|
|
784
|
+
}): Promise<RpcResponse<{
|
|
785
|
+
title: string;
|
|
786
|
+
seq: number;
|
|
787
|
+
}>>;
|
|
788
|
+
fork(request: {
|
|
741
789
|
rpcId: string;
|
|
742
|
-
payload:
|
|
790
|
+
payload: {
|
|
791
|
+
sessionId: string;
|
|
792
|
+
atSeq?: number;
|
|
793
|
+
};
|
|
743
794
|
}): Promise<RpcResponse<{
|
|
744
|
-
|
|
745
|
-
hasDocument: boolean;
|
|
746
|
-
namespaces: SettingsNamespaceView[];
|
|
795
|
+
sessionId: string;
|
|
747
796
|
}>>;
|
|
748
|
-
|
|
797
|
+
models(request: {
|
|
749
798
|
rpcId: string;
|
|
750
799
|
payload: {
|
|
751
|
-
|
|
752
|
-
ops: Array<{
|
|
753
|
-
op: 'set';
|
|
754
|
-
path: string[];
|
|
755
|
-
value: unknown;
|
|
756
|
-
} | {
|
|
757
|
-
op: 'unset';
|
|
758
|
-
path: string[];
|
|
759
|
-
}>;
|
|
760
|
-
expectedRevision?: number;
|
|
800
|
+
sessionId: string;
|
|
761
801
|
};
|
|
762
|
-
}): Promise<RpcResponse<
|
|
802
|
+
}): Promise<RpcResponse<SessionModelsView>>;
|
|
803
|
+
selectModel(request: {
|
|
804
|
+
rpcId: string;
|
|
805
|
+
payload: {
|
|
806
|
+
sessionId: string;
|
|
807
|
+
provider: string;
|
|
808
|
+
model: string;
|
|
809
|
+
reasoningEffort?: string;
|
|
810
|
+
};
|
|
811
|
+
}): Promise<RpcResponse<{
|
|
812
|
+
selected: ModelSelectionView;
|
|
813
|
+
}>>;
|
|
763
814
|
};
|
|
764
|
-
|
|
765
|
-
|
|
815
|
+
workspace: {
|
|
816
|
+
list(request: {
|
|
817
|
+
rpcId: string;
|
|
818
|
+
payload: Record<string, never>;
|
|
819
|
+
}): Promise<RpcResponse<{
|
|
820
|
+
items: DshWorkspaceSummary[];
|
|
821
|
+
archivedSessionIds: string[];
|
|
822
|
+
}>>;
|
|
823
|
+
create(request: {
|
|
766
824
|
rpcId: string;
|
|
767
825
|
payload: {
|
|
768
|
-
|
|
826
|
+
path: string;
|
|
769
827
|
};
|
|
770
828
|
}): Promise<RpcResponse<{
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
source?: string;
|
|
774
|
-
writable: boolean;
|
|
775
|
-
}>;
|
|
829
|
+
workspace: DshWorkspaceSummary;
|
|
830
|
+
created: boolean;
|
|
776
831
|
}>>;
|
|
777
|
-
|
|
832
|
+
rename(request: {
|
|
778
833
|
rpcId: string;
|
|
779
834
|
payload: {
|
|
780
|
-
|
|
781
|
-
|
|
835
|
+
workspaceId: string;
|
|
836
|
+
title: string;
|
|
782
837
|
};
|
|
783
|
-
}): Promise<RpcResponse<
|
|
784
|
-
|
|
838
|
+
}): Promise<RpcResponse<{
|
|
839
|
+
workspace: DshWorkspaceSummary;
|
|
840
|
+
}>>;
|
|
841
|
+
delete(request: {
|
|
785
842
|
rpcId: string;
|
|
786
843
|
payload: {
|
|
787
|
-
|
|
844
|
+
workspaceId: string;
|
|
788
845
|
};
|
|
789
|
-
}): Promise<RpcResponse<
|
|
846
|
+
}): Promise<RpcResponse<{
|
|
847
|
+
deleted: true;
|
|
848
|
+
}>>;
|
|
849
|
+
archiveSession(request: {
|
|
850
|
+
rpcId: string;
|
|
851
|
+
payload: {
|
|
852
|
+
sessionId: string;
|
|
853
|
+
};
|
|
854
|
+
}): Promise<RpcResponse<{
|
|
855
|
+
archivedSessionIds: string[];
|
|
856
|
+
}>>;
|
|
790
857
|
};
|
|
791
|
-
|
|
792
|
-
|
|
858
|
+
agentPresets: {
|
|
859
|
+
list(request: {
|
|
793
860
|
rpcId: string;
|
|
794
861
|
payload: Record<string, never>;
|
|
795
862
|
}): Promise<RpcResponse<{
|
|
796
|
-
|
|
863
|
+
presets: DshAgentPresetSummary[];
|
|
864
|
+
authorable: boolean;
|
|
865
|
+
hasDocument: boolean;
|
|
797
866
|
}>>;
|
|
798
|
-
|
|
867
|
+
select(request: {
|
|
799
868
|
rpcId: string;
|
|
800
|
-
payload:
|
|
869
|
+
payload: {
|
|
870
|
+
sessionId: string;
|
|
871
|
+
agentPreset: string;
|
|
872
|
+
};
|
|
801
873
|
}): Promise<RpcResponse<{
|
|
802
|
-
|
|
803
|
-
failures: {
|
|
804
|
-
provider: string;
|
|
805
|
-
message: string;
|
|
806
|
-
}[];
|
|
874
|
+
agentPreset: string;
|
|
807
875
|
}>>;
|
|
808
|
-
|
|
876
|
+
};
|
|
877
|
+
/**
|
|
878
|
+
* 用户问题(内核 `ctx.userQuestions`,即 ask_user_question 工具)。
|
|
879
|
+
*
|
|
880
|
+
* 为什么单开一个命名空间而不是塞进 `sessions`:它由**另一个内核服务**提供
|
|
881
|
+
* (`@deepseek-ai/dsh-user-questions`),在一条没装它的 profile 上是**可选能力** ——
|
|
882
|
+
* 分开之后,「这个能力不在」和「这个会话有问题」在类型上就分得开。
|
|
883
|
+
*/
|
|
884
|
+
userQuestions: {
|
|
885
|
+
/**
|
|
886
|
+
* 列出还能回答的问题(内核 `continued(agent)`)。
|
|
887
|
+
* 没有 live agent、或没有该服务时返回空列表 —— 冷会话是常态,不该报错。
|
|
888
|
+
*/
|
|
889
|
+
list(request: {
|
|
809
890
|
rpcId: string;
|
|
810
891
|
payload: {
|
|
811
|
-
|
|
812
|
-
provider?: string;
|
|
813
|
-
baseURL?: string;
|
|
814
|
-
api?: string;
|
|
815
|
-
apiKey?: string;
|
|
892
|
+
sessionId: string;
|
|
816
893
|
};
|
|
817
894
|
}): Promise<RpcResponse<{
|
|
818
|
-
|
|
895
|
+
items: UserQuestionView[];
|
|
819
896
|
}>>;
|
|
897
|
+
/** 提交答案;失败原因见 [UserQuestionAnswerOutcome]。 */
|
|
898
|
+
answer(request: {
|
|
899
|
+
rpcId: string;
|
|
900
|
+
payload: {
|
|
901
|
+
sessionId: string;
|
|
902
|
+
callId: string;
|
|
903
|
+
answer: UserQuestionAnswer;
|
|
904
|
+
};
|
|
905
|
+
}): Promise<RpcResponse<UserQuestionAnswerOutcome>>;
|
|
906
|
+
};
|
|
907
|
+
events: {
|
|
908
|
+
mux(request: {
|
|
909
|
+
rpcId: string;
|
|
910
|
+
payload: Record<string, never>;
|
|
911
|
+
}, signal: AbortSignal): AsyncIterable<{
|
|
912
|
+
rpcId: string;
|
|
913
|
+
payload: Record<string, unknown>;
|
|
914
|
+
}>;
|
|
915
|
+
host(request: {
|
|
916
|
+
rpcId: string;
|
|
917
|
+
payload: Record<string, never>;
|
|
918
|
+
}, signal: AbortSignal): AsyncIterable<{
|
|
919
|
+
rpcId: string;
|
|
920
|
+
payload: Record<string, unknown>;
|
|
921
|
+
}>;
|
|
820
922
|
};
|
|
821
923
|
}
|
|
822
|
-
interface
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
contextWindow?: number;
|
|
826
|
-
maxTokens?: number;
|
|
827
|
-
}
|
|
828
|
-
interface ProviderConfigView {
|
|
829
|
-
baseURL?: string;
|
|
830
|
-
api?: string;
|
|
831
|
-
displayName?: string;
|
|
832
|
-
thinking?: string;
|
|
924
|
+
interface ModelSelectionView {
|
|
925
|
+
provider: string;
|
|
926
|
+
model: string;
|
|
833
927
|
reasoningEffort?: string;
|
|
834
|
-
models: ProviderModelView[];
|
|
835
|
-
modelsInherited?: boolean;
|
|
836
928
|
}
|
|
837
|
-
interface
|
|
929
|
+
interface ModelReasoningView {
|
|
930
|
+
efforts: {
|
|
931
|
+
id: string;
|
|
932
|
+
name: string;
|
|
933
|
+
description?: string;
|
|
934
|
+
}[];
|
|
935
|
+
defaultEffort?: string;
|
|
936
|
+
}
|
|
937
|
+
interface ModelProviderGroupView {
|
|
838
938
|
id: string;
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
939
|
+
name: string;
|
|
940
|
+
models: {
|
|
941
|
+
id: string;
|
|
942
|
+
name: string;
|
|
943
|
+
description?: string;
|
|
944
|
+
contextWindow?: number;
|
|
945
|
+
maxTokens?: number;
|
|
946
|
+
reasoning?: ModelReasoningView;
|
|
947
|
+
}[];
|
|
948
|
+
}
|
|
949
|
+
interface SessionModelsView {
|
|
950
|
+
current: ModelSelectionView;
|
|
951
|
+
routable: boolean;
|
|
952
|
+
groups: ModelProviderGroupView[];
|
|
953
|
+
failures: {
|
|
954
|
+
provider: string;
|
|
955
|
+
message: string;
|
|
956
|
+
}[];
|
|
957
|
+
}
|
|
958
|
+
interface CommandDescriptorView {
|
|
959
|
+
name: string;
|
|
960
|
+
description: string;
|
|
961
|
+
input?: {
|
|
962
|
+
hint: string;
|
|
850
963
|
};
|
|
851
|
-
config: ProviderConfigView;
|
|
852
964
|
}
|
|
853
|
-
interface
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
965
|
+
interface CommandExecutionView {
|
|
966
|
+
commandId: string;
|
|
967
|
+
result: {
|
|
968
|
+
kind: 'success' | 'error';
|
|
969
|
+
text?: string;
|
|
970
|
+
sourceEventSeq?: number;
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
interface DshCommandRuntime {
|
|
974
|
+
list(agent: unknown): readonly CommandDescriptorView[];
|
|
975
|
+
execute(agent: unknown, line: string, signal: AbortSignal): Promise<CommandExecutionView | undefined>;
|
|
976
|
+
}
|
|
977
|
+
interface DshAgentRegistry {
|
|
978
|
+
get(sessionId: string): unknown | undefined;
|
|
979
|
+
}
|
|
980
|
+
interface DshCommandServices {
|
|
981
|
+
commands: DshCommandRuntime;
|
|
982
|
+
agents: DshAgentRegistry;
|
|
983
|
+
}
|
|
984
|
+
interface DshSessionSummary {
|
|
985
|
+
sessionId: string;
|
|
986
|
+
updatedAt: number;
|
|
987
|
+
running: boolean;
|
|
988
|
+
blank: boolean;
|
|
989
|
+
cwd?: string;
|
|
990
|
+
agentPreset?: string;
|
|
991
|
+
parentSessionId?: string;
|
|
992
|
+
origin?: 'subagent';
|
|
993
|
+
projections?: {
|
|
994
|
+
asOfSeq: number;
|
|
995
|
+
values: Record<string, unknown>;
|
|
996
|
+
};
|
|
862
997
|
}
|
|
863
|
-
interface
|
|
998
|
+
interface DshWorkspaceSummary {
|
|
999
|
+
workspaceId: string;
|
|
1000
|
+
path: string;
|
|
1001
|
+
title: string;
|
|
1002
|
+
sessionIds: string[];
|
|
1003
|
+
createdAt: string;
|
|
1004
|
+
updatedAt: string;
|
|
1005
|
+
}
|
|
1006
|
+
interface DshAgentPresetSummary {
|
|
864
1007
|
id: string;
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
expectedRevision?: number;
|
|
1008
|
+
trust: 'system' | 'user';
|
|
1009
|
+
isDefault: boolean;
|
|
1010
|
+
name?: string;
|
|
1011
|
+
description?: string;
|
|
1012
|
+
broken?: string;
|
|
871
1013
|
}
|
|
872
|
-
interface
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
1014
|
+
interface ChatSessionView {
|
|
1015
|
+
id: string;
|
|
1016
|
+
rootId: string;
|
|
1017
|
+
cwd: string;
|
|
1018
|
+
updatedAt: number;
|
|
1019
|
+
running: boolean;
|
|
1020
|
+
blank: boolean;
|
|
1021
|
+
title?: string;
|
|
1022
|
+
workspaceId?: string;
|
|
1023
|
+
workspaceTitle?: string;
|
|
1024
|
+
agentPreset?: string;
|
|
1025
|
+
parentSessionId?: string;
|
|
1026
|
+
origin?: 'subagent';
|
|
1027
|
+
pendingInteraction?: 'approval';
|
|
876
1028
|
}
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
}>;
|
|
886
|
-
catalog(): Promise<{
|
|
887
|
-
groups: ModelProviderGroupView[];
|
|
888
|
-
failures: {
|
|
889
|
-
provider: string;
|
|
890
|
-
message: string;
|
|
891
|
-
}[];
|
|
892
|
-
}>;
|
|
893
|
-
updateProvider(providerId: string, patch: ProviderPatch): Promise<void>;
|
|
894
|
-
createProvider(input: CustomProviderCreate): Promise<void>;
|
|
895
|
-
removeProvider(providerId: string, expectedRevision?: number): Promise<void>;
|
|
896
|
-
discover(providerId: string, draft: {
|
|
897
|
-
baseURL?: string;
|
|
898
|
-
api?: string;
|
|
899
|
-
apiKey?: string;
|
|
900
|
-
}): Promise<ProviderModelView[]>;
|
|
901
|
-
private providerContext;
|
|
1029
|
+
interface PendingApprovalView {
|
|
1030
|
+
id: string;
|
|
1031
|
+
sessionId: string;
|
|
1032
|
+
toolName: string;
|
|
1033
|
+
reason?: string;
|
|
1034
|
+
detail?: string;
|
|
1035
|
+
risk: 'standard' | 'full-access';
|
|
1036
|
+
requestedAt: number;
|
|
902
1037
|
}
|
|
903
|
-
//#endregion
|
|
904
|
-
//#region src/host/dsh-0.2-bridge.d.ts
|
|
905
1038
|
/**
|
|
906
|
-
*
|
|
907
|
-
*
|
|
908
|
-
* 权威定义在 `chat-adapter.ts`(协议层),这里只是复用它,避免两处各写一遍
|
|
909
|
-
* 然后悄悄漂移。
|
|
910
|
-
*/
|
|
911
|
-
type PromptContentPartLike = PromptContentPart;
|
|
912
|
-
/**
|
|
913
|
-
* `ctx.fileUploads`:内核的文件上传服务。
|
|
1039
|
+
* 一条「还能回答」的用户问题 —— 内核 `userQuestions.continued()` 的一行。
|
|
914
1040
|
*
|
|
915
|
-
*
|
|
916
|
-
*
|
|
1041
|
+
* 为什么类型是宽松的、而不是照抄内核的 `AskUserQuestionItem`:内核往 item 或
|
|
1042
|
+
* question 上加过字段(`intent` 就是一例),手机端该自己决定要不要用,
|
|
1043
|
+
* 不该等插件跟着发版。所以这里只钉住三个**契约字段**,其余原样透传。
|
|
917
1044
|
*/
|
|
918
|
-
interface
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
1045
|
+
interface UserQuestionView {
|
|
1046
|
+
callId: string;
|
|
1047
|
+
/** 内核 `UserQuestionState`:'open' 或 'continued';本端点只返回 continued。 */
|
|
1048
|
+
state: string;
|
|
1049
|
+
/** 问题项**原样**透传(id / question / detail? / header? / options? / multiSelect? / intent?)。 */
|
|
1050
|
+
questions: unknown[];
|
|
1051
|
+
/** 内核以后加的字段照样透传,不在这里裁剪。 */
|
|
1052
|
+
[key: string]: unknown;
|
|
1053
|
+
}
|
|
1054
|
+
/** 一条问题的答案(内核 `AskUserQuestionAnswerItem`)。 */
|
|
1055
|
+
interface UserQuestionAnswerItem {
|
|
1056
|
+
/** 被回答的问题 id,必须与请求里的问题 id 对上。 */
|
|
1057
|
+
id: string;
|
|
1058
|
+
/** 选中的选项 label;多选时可能多项。 */
|
|
1059
|
+
selected: string[];
|
|
1060
|
+
/** 可选的「其他」自由文本。 */
|
|
1061
|
+
custom?: string;
|
|
1062
|
+
}
|
|
1063
|
+
/** 一次提交的完整答案批次(内核 `AskUserQuestionAnswer`):一次调用里的每个问题各占一项。 */
|
|
1064
|
+
interface UserQuestionAnswer {
|
|
1065
|
+
answers: UserQuestionAnswerItem[];
|
|
932
1066
|
}
|
|
933
1067
|
/**
|
|
934
|
-
*
|
|
1068
|
+
* 「把答案送进内核」的结果。
|
|
935
1069
|
*
|
|
936
|
-
*
|
|
937
|
-
*
|
|
938
|
-
*
|
|
939
|
-
*
|
|
1070
|
+
* 刻意**不走 RpcResponse 的 error 通道**:`unwrapDsh` 只认 `not-found` /
|
|
1071
|
+
* `busy` / `locked` 三种 code 去映射 HTTP 状态,其余一律 400 —— 而这里需要区分
|
|
1072
|
+
* 409 / 400 / 503。做成**值**之后由 adapter 决定 HTTP 状态,
|
|
1073
|
+
* 和 approvals 的 `respond()` 返回 `{ accepted: false, reason }` 是同一个套路。
|
|
940
1074
|
*/
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
1075
|
+
type UserQuestionAnswerFailure =
|
|
1076
|
+
/** 内核里没有这个 callId 的悬空问题(`answer()` 返回了 false)。 */
|
|
1077
|
+
'not-pending' |
|
|
1078
|
+
/** 已经有一条回复在排队(内核 `REPLY_QUEUED`)。重复提交不是服务器故障。 */
|
|
1079
|
+
'reply-queued' |
|
|
1080
|
+
/** 答案批次没有「一问一答」地对齐(内核 `BAD_ANSWER`)。 */
|
|
1081
|
+
'answer-invalid' |
|
|
1082
|
+
/** 这个 agent 现在不接受人工回答(非 live root / 被别的 agent 拥有),或会话没有 live agent。 */
|
|
1083
|
+
'not-answerable' |
|
|
1084
|
+
/** 这套 DSH 构建里压根没有 `ctx.userQuestions` 服务。 */
|
|
1085
|
+
'unavailable';
|
|
1086
|
+
type UserQuestionAnswerOutcome = {
|
|
1087
|
+
accepted: true;
|
|
1088
|
+
} | {
|
|
1089
|
+
accepted: false;
|
|
1090
|
+
reason: UserQuestionAnswerFailure;
|
|
1091
|
+
};
|
|
1092
|
+
interface ChatWorkspaceView {
|
|
1093
|
+
id: string;
|
|
1094
|
+
title: string;
|
|
1095
|
+
rootId: string;
|
|
1096
|
+
path: string;
|
|
1097
|
+
createdAt: string;
|
|
1098
|
+
updatedAt: string;
|
|
963
1099
|
}
|
|
964
|
-
interface
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
* Assistant presentation frames are opt-in: without `assistantStream: true`
|
|
994
|
-
* the host emits durable events only, so a live stream would carry no
|
|
995
|
-
* incremental text at all (the transcript then appears only after commit).
|
|
996
|
-
*/
|
|
997
|
-
assistantStream?: true;
|
|
998
|
-
}, signal: AbortSignal): AsyncIterable<{
|
|
999
|
-
type?: string;
|
|
1000
|
-
cursor?: number;
|
|
1001
|
-
records?: readonly unknown[];
|
|
1002
|
-
hasMore?: boolean;
|
|
1003
|
-
}>;
|
|
1004
|
-
prompt(request: {
|
|
1005
|
-
requestId: string;
|
|
1006
|
-
sessionId: string;
|
|
1007
|
-
mode: 'queue' | 'steer';
|
|
1008
|
-
content: readonly PromptContentPartLike[];
|
|
1009
|
-
clientTimeZone?: string;
|
|
1010
|
-
}, signal: AbortSignal): Promise<{
|
|
1011
|
-
accepted: true;
|
|
1100
|
+
interface AgentPresetView {
|
|
1101
|
+
id: string;
|
|
1102
|
+
name: string;
|
|
1103
|
+
description?: string;
|
|
1104
|
+
trust: 'system' | 'user';
|
|
1105
|
+
isDefault: boolean;
|
|
1106
|
+
available: boolean;
|
|
1107
|
+
}
|
|
1108
|
+
declare class DshChatAdapter {
|
|
1109
|
+
private readonly api;
|
|
1110
|
+
private readonly database;
|
|
1111
|
+
private readonly commandServices?;
|
|
1112
|
+
private readonly sessionRootIds;
|
|
1113
|
+
private readonly sessionRootLookups;
|
|
1114
|
+
private readonly pendingApprovals;
|
|
1115
|
+
constructor(api: DshApiProxy, database: WorkspaceDatabase, commandServices?: DshCommandServices | undefined);
|
|
1116
|
+
listSessions(principal: Principal): Promise<ChatSessionView[]>;
|
|
1117
|
+
listWorkspaces(principal: Principal): Promise<ChatWorkspaceView[]>;
|
|
1118
|
+
createWorkspace(principal: Principal, rootId: string, relativePath: string): Promise<ChatWorkspaceView>;
|
|
1119
|
+
renameWorkspace(principal: Principal, workspaceId: string, title: string): Promise<ChatWorkspaceView>;
|
|
1120
|
+
deleteWorkspace(principal: Principal, workspaceId: string): Promise<void>;
|
|
1121
|
+
listAgentPresets(): Promise<AgentPresetView[]>;
|
|
1122
|
+
selectAgentPreset(principal: Principal, sessionId: string, agentPreset: string): Promise<string>;
|
|
1123
|
+
createSessionInWorkspace(principal: Principal, workspaceId: string, options: {
|
|
1124
|
+
sessionId?: string;
|
|
1125
|
+
agentPreset?: string;
|
|
1126
|
+
}): Promise<{
|
|
1127
|
+
id: string;
|
|
1128
|
+
agentPreset?: string;
|
|
1012
1129
|
}>;
|
|
1013
|
-
|
|
1014
|
-
sessionId
|
|
1015
|
-
|
|
1016
|
-
accepted: true;
|
|
1017
|
-
};
|
|
1018
|
-
rename(request: {
|
|
1019
|
-
sessionId: string;
|
|
1020
|
-
title: string;
|
|
1130
|
+
createSession(principal: Principal, rootId: string, relativePath: string, options: {
|
|
1131
|
+
sessionId?: string;
|
|
1132
|
+
agentPreset?: string;
|
|
1021
1133
|
}): Promise<{
|
|
1022
|
-
|
|
1023
|
-
|
|
1134
|
+
id: string;
|
|
1135
|
+
agentPreset?: string;
|
|
1024
1136
|
}>;
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1137
|
+
history(principal: Principal, sessionId: string, options: {
|
|
1138
|
+
beforeSeq?: number;
|
|
1139
|
+
maxMessages?: number;
|
|
1028
1140
|
}): Promise<{
|
|
1029
|
-
|
|
1141
|
+
events: unknown[];
|
|
1142
|
+
hasMore: boolean;
|
|
1143
|
+
projections?: unknown;
|
|
1144
|
+
}>;
|
|
1145
|
+
prompt(principal: Principal, sessionId: string, parts: PromptContentPart[], mode: 'queue' | 'steer', clientTimeZone?: string, locale?: HandoffLocale): Promise<{
|
|
1146
|
+
accepted: true;
|
|
1147
|
+
command?: unknown;
|
|
1030
1148
|
}>;
|
|
1031
1149
|
/**
|
|
1032
|
-
*
|
|
1033
|
-
*
|
|
1034
|
-
* 这是取历史图片的**正路**,它内部一次做完三件事:校验会话确实引用了这张图、
|
|
1035
|
-
* 调 `ctx.attachments.readImage`、用**会话事件里那条真实 ref**做完整性校验
|
|
1036
|
-
*(`mediaType` / `bytes` / `width` / `height` 四项逐项比对)。
|
|
1037
|
-
*
|
|
1038
|
-
* 为什么必须走它、不能自己调 `ctx.attachments`:实测本机这条 profile 里
|
|
1039
|
-
* `ctx.attachments` 是 **MISSING**(诊断日志
|
|
1040
|
-
* `ctx.attachments MISSING(所以改走内核 sessionController.readAttachment)`),
|
|
1041
|
-
* 自己拼 ref 那条路根本走不通;而且绕开内核就得把它内部三件事全复刻一遍,
|
|
1042
|
-
* 早先正是只传了 id、缺四项元数据,导致 `ATTACHMENT_CORRUPT`。
|
|
1150
|
+
* 上传一个附件,返回 prompt 可以引用的凭证。
|
|
1043
1151
|
*
|
|
1044
|
-
*
|
|
1045
|
-
|
|
1046
|
-
* `data` 是 **base64 字符串**(主机端实现里 `Buffer.from(stored.data).toString("base64")`),
|
|
1047
|
-
* 不是 Uint8Array —— 调用方要自己解码。
|
|
1152
|
+
* 为什么需要这一步而不是让客户端直接把字节塞进消息:内核对**文件**类型要求的是
|
|
1153
|
+
* `receiptId`(由上传换取),只有**图片**才允许内联 base64。
|
|
1048
1154
|
*/
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
}): Promise<{
|
|
1053
|
-
attachment: {
|
|
1054
|
-
mediaType: string;
|
|
1055
|
-
};
|
|
1056
|
-
data: string;
|
|
1057
|
-
}>;
|
|
1058
|
-
selectModel(request: {
|
|
1059
|
-
sessionId: string;
|
|
1060
|
-
provider: string;
|
|
1061
|
-
model: string;
|
|
1062
|
-
reasoningEffort?: string;
|
|
1063
|
-
}): Promise<{
|
|
1064
|
-
selected: unknown;
|
|
1155
|
+
uploadAttachment(principal: Principal, sessionId: string, data: string, name?: string): Promise<{
|
|
1156
|
+
receiptId: string;
|
|
1157
|
+
name: string | null;
|
|
1065
1158
|
}>;
|
|
1066
1159
|
/**
|
|
1067
|
-
*
|
|
1068
|
-
*
|
|
1069
|
-
*
|
|
1070
|
-
* `
|
|
1160
|
+
* 取回会话历史里某个图片附件的字节。
|
|
1161
|
+
*
|
|
1162
|
+
* **为什么会走内核的 `sessionController.attachment()`,而不自己调
|
|
1163
|
+
* `ctx.attachments.readImage()`**:
|
|
1164
|
+
* 1. 实测本机 profile 里 `ctx.attachments` 是 **MISSING**(拿不到注入属性)——
|
|
1165
|
+
* 自己拼 ref 那条路根本走不通;
|
|
1166
|
+
* 2. 内核那个方法内部一次做完三件事:校验该会话确实引用了这张图、
|
|
1167
|
+
* 读出字节、用**会话事件里那条真实 ref** 做完整性校验
|
|
1168
|
+
* (`mediaType` / `bytes` / `width` / `height` 四项逐项比对)。
|
|
1169
|
+
* 绕开它就得把这三件事全复刻 —— 先前正是漏了第三件,于是每张图
|
|
1170
|
+
* `ATTACHMENT_CORRUPT`:「字节读到了,却被校验拒之门外」。
|
|
1171
|
+
*
|
|
1172
|
+
* 因此 **sessionId 是必需参数**(内核要拿它读会话事件、找到那张图的真实 ref);
|
|
1173
|
+
* 早先设计的「客户端把四项元数据带上来」是多余的 —— 内核手里本来就有。
|
|
1071
1174
|
*/
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
groups?: readonly unknown[];
|
|
1076
|
-
failures?: readonly {
|
|
1077
|
-
id?: unknown;
|
|
1078
|
-
name?: unknown;
|
|
1079
|
-
message?: unknown;
|
|
1080
|
-
}[];
|
|
1175
|
+
readAttachment(principal: Principal, sessionId: string, attachmentId: string): Promise<{
|
|
1176
|
+
bytes: Uint8Array;
|
|
1177
|
+
mediaType: string;
|
|
1081
1178
|
}>;
|
|
1082
1179
|
/**
|
|
1083
|
-
*
|
|
1084
|
-
*
|
|
1085
|
-
* (`values.modelSelection`).
|
|
1180
|
+
* 取回一张**交接图片**(纯文本模型发图时落在工作区 `.dsh-images/` 的那份)。
|
|
1181
|
+
* 与 [readAttachment] 同样要求会话归属;文件名在桥里还有一道白名单校验。
|
|
1086
1182
|
*/
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
asOfSeq?: number;
|
|
1091
|
-
values?: Record<string, unknown>;
|
|
1092
|
-
} | null>;
|
|
1093
|
-
resolveAgent(sessionId: string): Promise<unknown>;
|
|
1094
|
-
}
|
|
1095
|
-
interface WorkspaceControllerLike {
|
|
1096
|
-
create(request: {
|
|
1097
|
-
path: string;
|
|
1098
|
-
}): Promise<{
|
|
1099
|
-
workspace: unknown;
|
|
1100
|
-
created: boolean;
|
|
1183
|
+
readHandoffImage(principal: Principal, sessionId: string, name: string): Promise<{
|
|
1184
|
+
bytes: Uint8Array;
|
|
1185
|
+
mediaType: string;
|
|
1101
1186
|
}>;
|
|
1102
|
-
|
|
1103
|
-
|
|
1187
|
+
cancel(principal: Principal, sessionId: string): Promise<void>;
|
|
1188
|
+
renameSession(principal: Principal, sessionId: string, title: string): Promise<{
|
|
1104
1189
|
title: string;
|
|
1105
|
-
|
|
1106
|
-
delete(request: {
|
|
1107
|
-
workspaceId: string;
|
|
1108
|
-
}): Promise<{
|
|
1109
|
-
deleted: boolean;
|
|
1110
|
-
}>;
|
|
1111
|
-
archiveSession(request: {
|
|
1112
|
-
sessionId: string;
|
|
1113
|
-
}): Promise<{
|
|
1114
|
-
archivedSessionIds: readonly string[];
|
|
1190
|
+
seq: number;
|
|
1115
1191
|
}>;
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
path: string;
|
|
1121
|
-
title: string;
|
|
1122
|
-
createdAt: string;
|
|
1123
|
-
updatedAt: string;
|
|
1124
|
-
sessionIds?: readonly string[];
|
|
1125
|
-
}[];
|
|
1126
|
-
}
|
|
1127
|
-
interface SettingsControllerLike {
|
|
1128
|
-
describe(): {
|
|
1129
|
-
writable: boolean;
|
|
1130
|
-
hasDocument: boolean;
|
|
1131
|
-
namespaces: readonly unknown[];
|
|
1132
|
-
};
|
|
1133
|
-
mutate(ns: string, ops: readonly unknown[], expectedRevision: number | undefined): Promise<unknown>;
|
|
1134
|
-
}
|
|
1135
|
-
interface CredentialsControllerLike {
|
|
1136
|
-
describe(refs: readonly string[]): Promise<Record<string, unknown>>;
|
|
1137
|
-
set(ref: string, value: string): Promise<void>;
|
|
1138
|
-
unset(ref: string): Promise<void>;
|
|
1139
|
-
}
|
|
1140
|
-
interface AgentPresetsLike {
|
|
1141
|
-
list(): Promise<readonly unknown[]>;
|
|
1142
|
-
remoteExportList(): Promise<unknown>;
|
|
1143
|
-
resolveAgent?: never;
|
|
1144
|
-
select(agent: unknown, agentPreset: string): Promise<string>;
|
|
1145
|
-
}
|
|
1146
|
-
interface LlmLike {
|
|
1147
|
-
/** Live routes, as `{ id, name }` rows. */
|
|
1148
|
-
listProviders(): readonly {
|
|
1149
|
-
id: string;
|
|
1150
|
-
}[];
|
|
1151
|
-
/**
|
|
1152
|
-
* Every route an adapter plugin could activate through configuration, live or
|
|
1153
|
-
* dormant, carrying the settings namespace and path that configures it.
|
|
1154
|
-
* Configuration surfaces merge this with `listProviders()` for liveness.
|
|
1155
|
-
*/
|
|
1156
|
-
listConfigurableProviders(): readonly {
|
|
1157
|
-
provider: string;
|
|
1158
|
-
displayName: string;
|
|
1159
|
-
settingsNs: string;
|
|
1160
|
-
settingsPath: readonly string[];
|
|
1161
|
-
declared?: boolean;
|
|
1162
|
-
error?: string;
|
|
1163
|
-
}[];
|
|
1164
|
-
discoverModels(settingsNs: string, request: unknown, signal?: AbortSignal): Promise<readonly unknown[]>;
|
|
1192
|
+
forkSession(principal: Principal, sessionId: string, atSeq?: number): Promise<string>;
|
|
1193
|
+
archiveSession(principal: Principal, sessionId: string): Promise<string[]>;
|
|
1194
|
+
listPendingApprovals(principal: Principal, sessionId: string): Promise<PendingApprovalView[]>;
|
|
1195
|
+
decideApproval(principal: Principal, sessionId: string, approvalId: string, outcome: 'allowed-once' | 'rejected'): Promise<PendingApprovalView>;
|
|
1165
1196
|
/**
|
|
1166
|
-
*
|
|
1197
|
+
* 列出「agent 正在等、且还能回答」的用户问题。
|
|
1167
1198
|
*
|
|
1168
|
-
*
|
|
1169
|
-
*
|
|
1170
|
-
* `this.ctx.llm.resolveModelInfo(...)` 再查 `inputModalities`)。入口要与内核
|
|
1171
|
-
* 得出同一个结论,只能查同一份元数据 —— 名字、前缀、家族表都是启发式,会漂。
|
|
1199
|
+
* 口径与 [listPendingApprovals] 一致:先 requireSession(会话对本设备不可见 → 404),
|
|
1200
|
+
* 再问内核;字段一个都不裁 —— 客户端照着渲染,内核加字段不用改插件。
|
|
1172
1201
|
*/
|
|
1173
|
-
|
|
1174
|
-
inputModalities?: readonly string[];
|
|
1175
|
-
} | undefined>;
|
|
1176
|
-
}
|
|
1177
|
-
/** Host services the bridge consumes; assembled by `src/index.ts` from `ctx`. */
|
|
1178
|
-
interface Dsh02HostServices {
|
|
1179
|
-
sessionController: SessionControllerLike;
|
|
1180
|
-
workspaceController: WorkspaceControllerLike;
|
|
1181
|
-
workspaceRegistry: WorkspaceRegistryLike;
|
|
1182
|
-
settingsController: SettingsControllerLike;
|
|
1183
|
-
credentialsController: CredentialsControllerLike;
|
|
1184
|
-
agentPresets: AgentPresetsLike;
|
|
1185
|
-
llm: LlmLike;
|
|
1202
|
+
listUserQuestions(principal: Principal, sessionId: string): Promise<UserQuestionView[]>;
|
|
1186
1203
|
/**
|
|
1187
|
-
*
|
|
1204
|
+
* 把一次答案提交给内核。
|
|
1188
1205
|
*
|
|
1189
|
-
*
|
|
1190
|
-
*
|
|
1191
|
-
*
|
|
1192
|
-
*
|
|
1193
|
-
* 挂在服务端、而且只在真的去取图时才炸,所以很难在本地测出来。
|
|
1206
|
+
* 失败原因全部由 `api.userQuestions.answer` 以**值**给出(见 [UserQuestionAnswerOutcome]),
|
|
1207
|
+
* 这里翻成 HTTP 语义:503 能力缺失 / 409 状态冲突 / 400 批次不合法。
|
|
1208
|
+
* 为什么不在 bridge 里直接抛:`unwrapDsh` 只把 `not-found` / `busy` / `locked`
|
|
1209
|
+
* 映成 404/409,其余一律 400 —— 表达不了这几种区别。
|
|
1194
1210
|
*/
|
|
1195
|
-
|
|
1211
|
+
answerUserQuestion(principal: Principal, sessionId: string, callId: string, answer: UserQuestionAnswer): Promise<void>;
|
|
1212
|
+
models(principal: Principal, sessionId: string): Promise<SessionModelsView>;
|
|
1213
|
+
selectModel(principal: Principal, sessionId: string, selection: ModelSelectionView): Promise<ModelSelectionView>;
|
|
1214
|
+
listCommands(principal: Principal, sessionId: string): Promise<CommandDescriptorView[]>;
|
|
1215
|
+
executeCommand(principal: Principal, sessionId: string, line: string): Promise<CommandExecutionView>;
|
|
1216
|
+
isSessionAuthorized(principal: Principal, sessionId: string): Promise<boolean>;
|
|
1217
|
+
startEvents(signal: AbortSignal, emit: (event: WorkspaceEvent) => void): void;
|
|
1218
|
+
private captureApproval;
|
|
1219
|
+
private hasPendingApproval;
|
|
1196
1220
|
/**
|
|
1197
|
-
*
|
|
1221
|
+
* Resolve the Agent backing a session for command work.
|
|
1198
1222
|
*
|
|
1199
|
-
*
|
|
1200
|
-
*
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
/**
|
|
1204
|
-
* Cordis event bus of the host context. Used for the two facts the old
|
|
1205
|
-
* `apiProxy` event streams carried that no controller returns: session
|
|
1206
|
-
* registry changes and the approval answerer waterfall.
|
|
1223
|
+
* Returns `undefined` when the session exists but cannot be activated. 0.2.x
|
|
1224
|
+
* rejects activation for a Session owned by a subagent, so that outcome is a
|
|
1225
|
+
* property of the session rather than a failure of this call: the caller
|
|
1226
|
+
* decides whether it means "no commands" (listing) or an error (execution).
|
|
1207
1227
|
*/
|
|
1208
|
-
|
|
1228
|
+
private commandAgent;
|
|
1229
|
+
private requireSession;
|
|
1230
|
+
private requireWorkspace;
|
|
1231
|
+
private visibleWorkspaces;
|
|
1232
|
+
private requireAuthorizedRoot;
|
|
1233
|
+
private authorizedRoots;
|
|
1234
|
+
private sessionRootId;
|
|
1209
1235
|
}
|
|
1210
1236
|
//#endregion
|
|
1211
1237
|
//#region src/host/rate-limit.d.ts
|