@huyooo/ai-chat-bridge-electron 0.2.13 → 0.2.14

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.
@@ -175,6 +175,8 @@ interface FileInfo {
175
175
  interface ElectronBridgeOptions extends Omit<AgentConfig, 'tools'> {
176
176
  /** IPC channel 前缀 */
177
177
  channelPrefix?: string;
178
+ /** Tavily API Key(用于统一 Web Search;可选) */
179
+ tavilyApiKey?: string;
178
180
  /**
179
181
  * 统一数据目录(必填)
180
182
  * 所有数据都存储在此目录下:
@@ -559,6 +559,21 @@ async function createElectronBridge(options) {
559
559
  ipcMain2.handle(`${channelPrefix}:models`, () => {
560
560
  return MODELS;
561
561
  });
562
+ ipcMain2.handle(`${channelPrefix}:getAllTools`, async () => {
563
+ console.log("[Main] getAllTools \u8C03\u7528\uFF0C\u5F53\u524D\u5DE5\u5177\u6570\u91CF:", agent["tools"].size);
564
+ console.log("[Main] toolConfig \u662F\u5426\u5B58\u5728:", !!agent["toolConfig"]);
565
+ console.log("[Main] toolConfig \u5185\u5BB9:", agent["toolConfig"] ? `${agent["toolConfig"].length} \u4E2A\u5DE5\u5177\u914D\u7F6E` : "undefined");
566
+ if (agent["tools"].size === 0 && agent["toolConfig"]) {
567
+ console.log("[Main] \u5DE5\u5177\u672A\u521D\u59CB\u5316\uFF0C\u5F00\u59CB asyncInit...");
568
+ await agent["asyncInit"]();
569
+ console.log("[Main] asyncInit \u5B8C\u6210\uFF0C\u5DE5\u5177\u6570\u91CF:", agent["tools"].size);
570
+ } else if (!agent["toolConfig"]) {
571
+ console.warn("[Main] \u26A0\uFE0F toolConfig \u4E0D\u5B58\u5728\uFF0C\u5DE5\u5177\u53EF\u80FD\u672A\u6CE8\u5165\uFF01");
572
+ }
573
+ const tools = agent.getAllTools();
574
+ console.log("[Main] getAllTools \u8FD4\u56DE:", tools.length, "\u4E2A\u5DE5\u5177", tools.map((t) => t.name));
575
+ return tools;
576
+ });
562
577
  ipcMain2.handle(`${channelPrefix}:send`, async (event, params) => {
563
578
  const webContents = event.sender;
564
579
  const { message, images, options: options2 = {}, sessionId } = params;
@@ -671,6 +686,7 @@ async function createElectronBridge(options) {
671
686
  sessionId: params.sessionId,
672
687
  role: params.role,
673
688
  content: params.content,
689
+ images: params.images || [],
674
690
  model: params.model || null,
675
691
  mode: params.mode || null,
676
692
  webSearchEnabled: params.webSearchEnabled ?? null,
@@ -691,6 +707,10 @@ async function createElectronBridge(options) {
691
707
  await storage.deleteMessagesAfter(sessionId, new Date(timestamp), getContext());
692
708
  return { success: true };
693
709
  });
710
+ ipcMain2.handle(`${channelPrefix}:messages:deleteAfterMessageId`, async (_event, sessionId, messageId) => {
711
+ await storage.deleteMessagesAfterMessageId(sessionId, messageId, getContext());
712
+ return { success: true };
713
+ });
694
714
  ipcMain2.handle(`${channelPrefix}:operations:list`, async (_event, sessionId) => {
695
715
  return storage.getOperations(sessionId, getContext());
696
716
  });
@@ -21,12 +21,14 @@ interface SendMessageParams {
21
21
  interface ModelOption {
22
22
  modelId: string;
23
23
  displayName: string;
24
- /** 分组名称(由后端决定,前端只负责渲染,必填) */
25
- group: string;
26
- /** 是否来自 OpenRouter(保留用于兼容,后续可能移除) */
27
- isOpenRouter?: boolean;
28
- /** 提供商名称(保留用于兼容,后续可能移除) */
29
- provider?: string;
24
+ supportsThinking: boolean;
25
+ supportsVision: boolean;
26
+ tooltip?: {
27
+ features?: string[];
28
+ /** 开销信息(数组,分行显示) */
29
+ cost?: string[];
30
+ description?: string;
31
+ };
30
32
  }
31
33
  /** 会话记录 */
32
34
  interface SessionRecord {
@@ -49,6 +51,8 @@ interface MessageRecord {
49
51
  sessionId: string;
50
52
  role: 'user' | 'assistant';
51
53
  content: string;
54
+ /** 用户消息附带的图片(data URL 或 base64) */
55
+ images: string[];
52
56
  /** 生成此消息时使用的模型 */
53
57
  model?: string | null;
54
58
  /** 生成此消息时使用的模式 (ask/agent) */
@@ -60,7 +64,10 @@ interface MessageRecord {
60
64
  /** 执行步骤列表 JSON */
61
65
  steps?: string | null;
62
66
  operationIds?: string | null;
63
- timestamp: Date;
67
+ /** 消息序号(会话内递增,用于分叉删除,必须) */
68
+ sequence: number;
69
+ /** 时间戳(毫秒,Unix 时间戳,不受 JSON 序列化影响) */
70
+ timestamp: number;
64
71
  }
65
72
  /** 操作记录 */
66
73
  interface OperationRecord {
@@ -124,6 +131,11 @@ interface AsrResultData {
124
131
  interface AiChatBridge {
125
132
  /** 获取可用模型 */
126
133
  getModels(): Promise<ModelOption[]>;
134
+ /** 获取所有工具列表(用于设置面板) */
135
+ getAllTools(): Promise<Array<{
136
+ name: string;
137
+ description: string;
138
+ }>>;
127
139
  /** 发送消息 */
128
140
  send(params: SendMessageParams): Promise<void>;
129
141
  /** 取消当前请求 */
@@ -169,6 +181,8 @@ interface AiChatBridge {
169
181
  sessionId: string;
170
182
  role: 'user' | 'assistant';
171
183
  content: string;
184
+ /** 用户消息附带的图片(data URL 或 base64) */
185
+ images?: string[];
172
186
  /** 生成此消息时使用的模型 */
173
187
  model?: string;
174
188
  /** 生成此消息时使用的模式 (ask/agent) */
@@ -193,6 +207,10 @@ interface AiChatBridge {
193
207
  deleteMessagesAfter(sessionId: string, timestamp: number): Promise<{
194
208
  success: boolean;
195
209
  }>;
210
+ /** 删除指定消息之后的所有消息(用于分叉) */
211
+ deleteMessagesAfterMessageId(sessionId: string, messageId: string): Promise<{
212
+ success: boolean;
213
+ }>;
196
214
  /** 获取操作日志 */
197
215
  getOperations(sessionId: string): Promise<OperationRecord[]>;
198
216
  /** 获取回收站 */
@@ -5,6 +5,7 @@ function exposeElectronBridge(options = {}) {
5
5
  const bridge = {
6
6
  // ============ Chat API ============
7
7
  getModels: () => ipcRenderer.invoke(`${channelPrefix}:models`),
8
+ getAllTools: () => ipcRenderer.invoke(`${channelPrefix}:getAllTools`),
8
9
  send: (params) => ipcRenderer.invoke(`${channelPrefix}:send`, params),
9
10
  cancel: () => ipcRenderer.invoke(`${channelPrefix}:cancel`),
10
11
  // 无状态架构:历史通过 ChatOptions.history 传入
@@ -40,6 +41,7 @@ function exposeElectronBridge(options = {}) {
40
41
  saveMessage: (params) => ipcRenderer.invoke(`${channelPrefix}:messages:save`, params),
41
42
  updateMessage: (params) => ipcRenderer.invoke(`${channelPrefix}:messages:update`, params),
42
43
  deleteMessagesAfter: (sessionId, timestamp) => ipcRenderer.invoke(`${channelPrefix}:messages:deleteAfter`, sessionId, timestamp),
44
+ deleteMessagesAfterMessageId: (sessionId, messageId) => ipcRenderer.invoke(`${channelPrefix}:messages:deleteAfterMessageId`, sessionId, messageId),
43
45
  // ============ Operations API ============
44
46
  getOperations: (sessionId) => ipcRenderer.invoke(`${channelPrefix}:operations:list`, sessionId),
45
47
  // ============ Trash API ============
@@ -269,7 +269,7 @@ interface AiChatBridge {
269
269
  * 在渲染进程中使用,创建 ChatAdapter
270
270
  */
271
271
 
272
- type ChatEventType = 'thinking_start' | 'thinking_delta' | 'thinking_end' | 'search_start' | 'search_result' | 'search_end' | 'tool_approval_request' | 'tool_call_start' | 'tool_call_result' | 'text_delta' | 'image' | 'video' | 'stream_start' | 'done' | 'error' | 'abort' | 'step_start' | 'step_end';
272
+ type ChatEventType = 'thinking_start' | 'thinking_delta' | 'thinking_end' | 'search_start' | 'search_result' | 'search_end' | 'tool_approval_request' | 'tool_call_start' | 'tool_call_output' | 'tool_call_result' | 'text_delta' | 'image' | 'video' | 'stream_start' | 'done' | 'error' | 'abort' | 'step_start' | 'step_end';
273
273
  interface ChatEvent {
274
274
  type: ChatEventType;
275
275
  data: unknown;
@@ -21,12 +21,14 @@ interface SendMessageParams {
21
21
  interface ModelOption$1 {
22
22
  modelId: string;
23
23
  displayName: string;
24
- /** 分组名称(由后端决定,前端只负责渲染,必填) */
25
- group: string;
26
- /** 是否来自 OpenRouter(保留用于兼容,后续可能移除) */
27
- isOpenRouter?: boolean;
28
- /** 提供商名称(保留用于兼容,后续可能移除) */
29
- provider?: string;
24
+ supportsThinking: boolean;
25
+ supportsVision: boolean;
26
+ tooltip?: {
27
+ features?: string[];
28
+ /** 开销信息(数组,分行显示) */
29
+ cost?: string[];
30
+ description?: string;
31
+ };
30
32
  }
31
33
  /** 会话记录 */
32
34
  interface SessionRecord$1 {
@@ -49,6 +51,8 @@ interface MessageRecord$1 {
49
51
  sessionId: string;
50
52
  role: 'user' | 'assistant';
51
53
  content: string;
54
+ /** 用户消息附带的图片(data URL 或 base64) */
55
+ images: string[];
52
56
  /** 生成此消息时使用的模型 */
53
57
  model?: string | null;
54
58
  /** 生成此消息时使用的模式 (ask/agent) */
@@ -60,7 +64,10 @@ interface MessageRecord$1 {
60
64
  /** 执行步骤列表 JSON */
61
65
  steps?: string | null;
62
66
  operationIds?: string | null;
63
- timestamp: Date;
67
+ /** 消息序号(会话内递增,用于分叉删除,必须) */
68
+ sequence: number;
69
+ /** 时间戳(毫秒,Unix 时间戳,不受 JSON 序列化影响) */
70
+ timestamp: number;
64
71
  }
65
72
  /** 操作记录 */
66
73
  interface OperationRecord$1 {
@@ -124,6 +131,11 @@ interface AsrResultData$1 {
124
131
  interface AiChatBridge {
125
132
  /** 获取可用模型 */
126
133
  getModels(): Promise<ModelOption$1[]>;
134
+ /** 获取所有工具列表(用于设置面板) */
135
+ getAllTools(): Promise<Array<{
136
+ name: string;
137
+ description: string;
138
+ }>>;
127
139
  /** 发送消息 */
128
140
  send(params: SendMessageParams): Promise<void>;
129
141
  /** 取消当前请求 */
@@ -169,6 +181,8 @@ interface AiChatBridge {
169
181
  sessionId: string;
170
182
  role: 'user' | 'assistant';
171
183
  content: string;
184
+ /** 用户消息附带的图片(data URL 或 base64) */
185
+ images?: string[];
172
186
  /** 生成此消息时使用的模型 */
173
187
  model?: string;
174
188
  /** 生成此消息时使用的模式 (ask/agent) */
@@ -193,6 +207,10 @@ interface AiChatBridge {
193
207
  deleteMessagesAfter(sessionId: string, timestamp: number): Promise<{
194
208
  success: boolean;
195
209
  }>;
210
+ /** 删除指定消息之后的所有消息(用于分叉) */
211
+ deleteMessagesAfterMessageId(sessionId: string, messageId: string): Promise<{
212
+ success: boolean;
213
+ }>;
196
214
  /** 获取操作日志 */
197
215
  getOperations(sessionId: string): Promise<OperationRecord$1[]>;
198
216
  /** 获取回收站 */
@@ -340,7 +358,7 @@ interface AiChatBridge {
340
358
  * 在渲染进程中使用,创建 ChatAdapter
341
359
  */
342
360
 
343
- type ChatEventType = 'thinking_start' | 'thinking_delta' | 'thinking_end' | 'search_start' | 'search_result' | 'search_end' | 'tool_approval_request' | 'tool_call_start' | 'tool_call_result' | 'text_delta' | 'image' | 'video' | 'stream_start' | 'done' | 'error' | 'abort' | 'step_start' | 'step_end';
361
+ type ChatEventType = 'thinking_start' | 'thinking_delta' | 'thinking_end' | 'search_start' | 'search_result' | 'search_end' | 'tool_approval_request' | 'tool_call_start' | 'tool_call_output' | 'tool_call_result' | 'text_delta' | 'image' | 'video' | 'stream_start' | 'done' | 'error' | 'abort' | 'step_start' | 'step_end';
344
362
  interface ChatEvent {
345
363
  type: ChatEventType;
346
364
  data: unknown;
@@ -369,6 +387,8 @@ interface ChatOptions {
369
387
  enableWebSearch?: boolean;
370
388
  /** 深度思考开关(每个 provider 内部使用最优参数) */
371
389
  thinkingMode?: ThinkingMode;
390
+ /** 启用的工具名称列表(undefined 表示全部启用) */
391
+ enabledTools?: string[];
372
392
  /** 自动运行配置 */
373
393
  autoRunConfig?: AutoRunConfig;
374
394
  /** 对话历史(无状态架构,历史从前端传入) */
@@ -382,12 +402,17 @@ interface ModelOption {
382
402
  modelId: string;
383
403
  /** 显示名称 */
384
404
  displayName: string;
385
- /** 分组名称(由后端决定,前端只负责渲染,必填) */
386
- group: string;
387
- /** 是否来自 OpenRouter(保留用于兼容,后续可能移除) */
388
- isOpenRouter?: boolean;
389
- /** 提供商名称(保留用于兼容,后续可能移除) */
390
- provider?: string;
405
+ /** 是否支持深度思考 */
406
+ supportsThinking: boolean;
407
+ /** 是否支持图片理解 */
408
+ supportsVision: boolean;
409
+ /** 模型项 hover 提示(由后端定义,前端只渲染) */
410
+ tooltip?: {
411
+ features?: string[];
412
+ /** 开销信息(数组,分行显示) */
413
+ cost?: string[];
414
+ description?: string;
415
+ };
391
416
  }
392
417
  interface SessionRecord {
393
418
  id: string;
@@ -408,6 +433,8 @@ interface MessageRecord {
408
433
  sessionId: string;
409
434
  role: 'user' | 'assistant';
410
435
  content: string;
436
+ /** 用户消息附带的图片(data URL 或 base64) */
437
+ images: string[];
411
438
  /** 生成此消息时使用的模型 */
412
439
  model?: string | null;
413
440
  /** 生成此消息时使用的模式 (ask/agent) */
@@ -419,7 +446,10 @@ interface MessageRecord {
419
446
  /** 执行步骤列表 JSON */
420
447
  steps?: string | null;
421
448
  operationIds?: string | null;
422
- timestamp: Date;
449
+ /** 消息序号(会话内递增,用于分叉删除,必须) */
450
+ sequence: number;
451
+ /** 时间戳(毫秒,Unix 时间戳,不受 JSON 序列化影响) */
452
+ timestamp: number;
423
453
  }
424
454
  interface OperationRecord {
425
455
  id: string;
@@ -478,6 +508,11 @@ interface AsrResultData {
478
508
  interface ChatAdapter {
479
509
  /** 获取可用模型 */
480
510
  getModels(): Promise<ModelOption[]>;
511
+ /** 获取所有工具列表(用于设置面板) */
512
+ getAllTools?(): Promise<Array<{
513
+ name: string;
514
+ description: string;
515
+ }>>;
481
516
  /** 发送消息,返回异步迭代器 */
482
517
  sendMessage(message: string, options?: ChatOptions, images?: string[], sessionId?: string): AsyncIterable<ChatEvent>;
483
518
  /** 取消当前请求 */
@@ -517,6 +552,8 @@ interface ChatAdapter {
517
552
  sessionId: string;
518
553
  role: 'user' | 'assistant';
519
554
  content: string;
555
+ /** 用户消息附带的图片(data URL 或 base64) */
556
+ images?: string[];
520
557
  /** 生成此消息时使用的模型 */
521
558
  model?: string;
522
559
  /** 生成此消息时使用的模式 (ask/agent) */
@@ -537,6 +574,8 @@ interface ChatAdapter {
537
574
  }): Promise<void>;
538
575
  /** 删除指定时间之后的消息(用于分叉) */
539
576
  deleteMessagesAfter(sessionId: string, timestamp: number): Promise<void>;
577
+ /** 删除指定消息之后的所有消息(用于分叉) */
578
+ deleteMessagesAfterMessageId(sessionId: string, messageId: string): Promise<void>;
540
579
  /** 获取操作日志 */
541
580
  getOperations(sessionId: string): Promise<OperationRecord[]>;
542
581
  /** 获取回收站 */
@@ -14,6 +14,13 @@ function createElectronAdapter(options = {}) {
14
14
  const bridge = getBridge();
15
15
  return bridge.getModels();
16
16
  },
17
+ async getAllTools() {
18
+ const bridge = getBridge();
19
+ if (typeof bridge.getAllTools === "function") {
20
+ return bridge.getAllTools();
21
+ }
22
+ return [];
23
+ },
17
24
  async *sendMessage(message, options2, images, sessionId) {
18
25
  const bridge = getBridge();
19
26
  const queue = [];
@@ -84,6 +91,9 @@ function createElectronAdapter(options = {}) {
84
91
  async deleteMessagesAfter(sessionId, timestamp) {
85
92
  await getBridge().deleteMessagesAfter(sessionId, timestamp);
86
93
  },
94
+ async deleteMessagesAfterMessageId(sessionId, messageId) {
95
+ await getBridge().deleteMessagesAfterMessageId(sessionId, messageId);
96
+ },
87
97
  // ============ Operations API ============
88
98
  async getOperations(sessionId) {
89
99
  return getBridge().getOperations(sessionId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huyooo/ai-chat-bridge-electron",
3
- "version": "0.2.13",
3
+ "version": "0.2.14",
4
4
  "description": "AI Chat Electron Bridge - IPC integration for Electron apps",
5
5
  "type": "module",
6
6
  "main": "./dist/main/index.js",
@@ -34,9 +34,9 @@
34
34
  "clean": "rm -rf dist"
35
35
  },
36
36
  "dependencies": {
37
- "@huyooo/ai-chat-core": "^0.2.13",
38
- "@huyooo/ai-chat-storage": "^0.2.13",
39
- "@huyooo/ai-search": "^0.2.13",
37
+ "@huyooo/ai-chat-core": "^0.2.14",
38
+ "@huyooo/ai-chat-storage": "^0.2.14",
39
+ "@huyooo/ai-search": "^0.2.14",
40
40
  "uuid": "^11.1.0",
41
41
  "ws": "^8.18.3"
42
42
  },
package/src/main/index.ts CHANGED
@@ -46,6 +46,8 @@ import {
46
46
  export interface ElectronBridgeOptions extends Omit<AgentConfig, 'tools'> {
47
47
  /** IPC channel 前缀 */
48
48
  channelPrefix?: string;
49
+ /** Tavily API Key(用于统一 Web Search;可选) */
50
+ tavilyApiKey?: string;
49
51
  /**
50
52
  * 统一数据目录(必填)
51
53
  * 所有数据都存储在此目录下:
@@ -88,6 +90,8 @@ interface MessageParams {
88
90
  sessionId: string;
89
91
  role: 'user' | 'assistant';
90
92
  content: string;
93
+ /** 用户消息附带的图片(data URL 或 base64) */
94
+ images?: string[];
91
95
  /** 生成此消息时使用的模型 */
92
96
  model?: string;
93
97
  /** 生成此消息时使用的模式 (ask/agent) */
@@ -225,6 +229,28 @@ export async function createElectronBridge(options: ElectronBridgeOptions) {
225
229
  return MODELS;
226
230
  });
227
231
 
232
+ // 获取所有工具列表(用于设置面板)
233
+ ipcMain.handle(`${channelPrefix}:getAllTools`, async () => {
234
+ // 确保工具已初始化(通过调用 chat 方法触发 asyncInit)
235
+ // 如果工具还未初始化,先触发一次初始化
236
+ console.log('[Main] getAllTools 调用,当前工具数量:', agent['tools'].size);
237
+ console.log('[Main] toolConfig 是否存在:', !!agent['toolConfig']);
238
+ console.log('[Main] toolConfig 内容:', agent['toolConfig'] ? `${agent['toolConfig'].length} 个工具配置` : 'undefined');
239
+
240
+ // 如果工具未初始化,尝试初始化
241
+ if (agent['tools'].size === 0 && agent['toolConfig']) {
242
+ console.log('[Main] 工具未初始化,开始 asyncInit...');
243
+ await agent['asyncInit']();
244
+ console.log('[Main] asyncInit 完成,工具数量:', agent['tools'].size);
245
+ } else if (!agent['toolConfig']) {
246
+ console.warn('[Main] ⚠️ toolConfig 不存在,工具可能未注入!');
247
+ }
248
+
249
+ const tools = (agent as any).getAllTools();
250
+ console.log('[Main] getAllTools 返回:', tools.length, '个工具', tools.map((t: any) => t.name));
251
+ return tools;
252
+ });
253
+
228
254
  // 发送消息
229
255
  ipcMain.handle(`${channelPrefix}:send`, async (event, params: SendMessageParams) => {
230
256
  const webContents = event.sender;
@@ -387,6 +413,7 @@ export async function createElectronBridge(options: ElectronBridgeOptions) {
387
413
  sessionId: params.sessionId,
388
414
  role: params.role,
389
415
  content: params.content,
416
+ images: params.images || [],
390
417
  model: params.model || null,
391
418
  mode: params.mode || null,
392
419
  webSearchEnabled: params.webSearchEnabled ?? null,
@@ -412,6 +439,12 @@ export async function createElectronBridge(options: ElectronBridgeOptions) {
412
439
  return { success: true };
413
440
  });
414
441
 
442
+ // 删除指定消息之后的所有消息(用于分叉)
443
+ ipcMain.handle(`${channelPrefix}:messages:deleteAfterMessageId`, async (_event, sessionId: string, messageId: string) => {
444
+ await storage.deleteMessagesAfterMessageId(sessionId, messageId, getContext());
445
+ return { success: true };
446
+ });
447
+
415
448
  // 获取操作日志
416
449
  ipcMain.handle(`${channelPrefix}:operations:list`, async (_event, sessionId: string) => {
417
450
  return storage.getOperations(sessionId, getContext());
@@ -25,12 +25,14 @@ interface SendMessageParams {
25
25
  interface ModelOption {
26
26
  modelId: string;
27
27
  displayName: string;
28
- /** 分组名称(由后端决定,前端只负责渲染,必填) */
29
- group: string;
30
- /** 是否来自 OpenRouter(保留用于兼容,后续可能移除) */
31
- isOpenRouter?: boolean;
32
- /** 提供商名称(保留用于兼容,后续可能移除) */
33
- provider?: string;
28
+ supportsThinking: boolean;
29
+ supportsVision: boolean;
30
+ tooltip?: {
31
+ features?: string[];
32
+ /** 开销信息(数组,分行显示) */
33
+ cost?: string[];
34
+ description?: string;
35
+ };
34
36
  }
35
37
 
36
38
  /** 会话记录 */
@@ -55,6 +57,8 @@ interface MessageRecord {
55
57
  sessionId: string;
56
58
  role: 'user' | 'assistant';
57
59
  content: string;
60
+ /** 用户消息附带的图片(data URL 或 base64) */
61
+ images: string[];
58
62
  /** 生成此消息时使用的模型 */
59
63
  model?: string | null;
60
64
  /** 生成此消息时使用的模式 (ask/agent) */
@@ -66,7 +70,10 @@ interface MessageRecord {
66
70
  /** 执行步骤列表 JSON */
67
71
  steps?: string | null;
68
72
  operationIds?: string | null;
69
- timestamp: Date;
73
+ /** 消息序号(会话内递增,用于分叉删除,必须) */
74
+ sequence: number;
75
+ /** 时间戳(毫秒,Unix 时间戳,不受 JSON 序列化影响) */
76
+ timestamp: number;
70
77
  }
71
78
 
72
79
  /** 操作记录 */
@@ -137,6 +144,8 @@ export interface AiChatBridge {
137
144
  // ============ Chat API ============
138
145
  /** 获取可用模型 */
139
146
  getModels(): Promise<ModelOption[]>;
147
+ /** 获取所有工具列表(用于设置面板) */
148
+ getAllTools(): Promise<Array<{ name: string; description: string }>>;
140
149
  /** 发送消息 */
141
150
  send(params: SendMessageParams): Promise<void>;
142
151
  /** 取消当前请求 */
@@ -171,6 +180,8 @@ export interface AiChatBridge {
171
180
  sessionId: string;
172
181
  role: 'user' | 'assistant';
173
182
  content: string;
183
+ /** 用户消息附带的图片(data URL 或 base64) */
184
+ images?: string[];
174
185
  /** 生成此消息时使用的模型 */
175
186
  model?: string;
176
187
  /** 生成此消息时使用的模式 (ask/agent) */
@@ -191,6 +202,8 @@ export interface AiChatBridge {
191
202
  }): Promise<{ success: boolean }>;
192
203
  /** 删除指定时间之后的消息(用于分叉) */
193
204
  deleteMessagesAfter(sessionId: string, timestamp: number): Promise<{ success: boolean }>;
205
+ /** 删除指定消息之后的所有消息(用于分叉) */
206
+ deleteMessagesAfterMessageId(sessionId: string, messageId: string): Promise<{ success: boolean }>;
194
207
 
195
208
  // ============ Operations API ============
196
209
  /** 获取操作日志 */
@@ -323,6 +336,9 @@ export function exposeElectronBridge(options: ExposeOptions = {}) {
323
336
  getModels: () =>
324
337
  ipcRenderer.invoke(`${channelPrefix}:models`),
325
338
 
339
+ getAllTools: () =>
340
+ ipcRenderer.invoke(`${channelPrefix}:getAllTools`),
341
+
326
342
  send: (params: SendMessageParams) =>
327
343
  ipcRenderer.invoke(`${channelPrefix}:send`, params),
328
344
 
@@ -390,6 +406,8 @@ export function exposeElectronBridge(options: ExposeOptions = {}) {
390
406
 
391
407
  deleteMessagesAfter: (sessionId: string, timestamp: number) =>
392
408
  ipcRenderer.invoke(`${channelPrefix}:messages:deleteAfter`, sessionId, timestamp),
409
+ deleteMessagesAfterMessageId: (sessionId: string, messageId: string) =>
410
+ ipcRenderer.invoke(`${channelPrefix}:messages:deleteAfterMessageId`, sessionId, messageId),
393
411
 
394
412
  // ============ Operations API ============
395
413
  getOperations: (sessionId: string) =>
@@ -16,6 +16,7 @@ export type ChatEventType =
16
16
  | 'search_end'
17
17
  | 'tool_approval_request'
18
18
  | 'tool_call_start'
19
+ | 'tool_call_output'
19
20
  | 'tool_call_result'
20
21
  | 'text_delta'
21
22
  | 'image'
@@ -69,6 +70,8 @@ export interface ChatOptions {
69
70
  enableWebSearch?: boolean;
70
71
  /** 深度思考开关(每个 provider 内部使用最优参数) */
71
72
  thinkingMode?: ThinkingMode;
73
+ /** 启用的工具名称列表(undefined 表示全部启用) */
74
+ enabledTools?: string[];
72
75
  /** 自动运行配置 */
73
76
  autoRunConfig?: AutoRunConfig;
74
77
  /** 对话历史(无状态架构,历史从前端传入) */
@@ -83,12 +86,17 @@ export interface ModelOption {
83
86
  modelId: string;
84
87
  /** 显示名称 */
85
88
  displayName: string;
86
- /** 分组名称(由后端决定,前端只负责渲染,必填) */
87
- group: string;
88
- /** 是否来自 OpenRouter(保留用于兼容,后续可能移除) */
89
- isOpenRouter?: boolean;
90
- /** 提供商名称(保留用于兼容,后续可能移除) */
91
- provider?: string;
89
+ /** 是否支持深度思考 */
90
+ supportsThinking: boolean;
91
+ /** 是否支持图片理解 */
92
+ supportsVision: boolean;
93
+ /** 模型项 hover 提示(由后端定义,前端只渲染) */
94
+ tooltip?: {
95
+ features?: string[];
96
+ /** 开销信息(数组,分行显示) */
97
+ cost?: string[];
98
+ description?: string;
99
+ };
92
100
  }
93
101
 
94
102
  // 会话记录
@@ -113,6 +121,8 @@ export interface MessageRecord {
113
121
  sessionId: string;
114
122
  role: 'user' | 'assistant';
115
123
  content: string;
124
+ /** 用户消息附带的图片(data URL 或 base64) */
125
+ images: string[];
116
126
  /** 生成此消息时使用的模型 */
117
127
  model?: string | null;
118
128
  /** 生成此消息时使用的模式 (ask/agent) */
@@ -124,7 +134,10 @@ export interface MessageRecord {
124
134
  /** 执行步骤列表 JSON */
125
135
  steps?: string | null;
126
136
  operationIds?: string | null;
127
- timestamp: Date;
137
+ /** 消息序号(会话内递增,用于分叉删除,必须) */
138
+ sequence: number;
139
+ /** 时间戳(毫秒,Unix 时间戳,不受 JSON 序列化影响) */
140
+ timestamp: number;
128
141
  }
129
142
 
130
143
  // 操作记录
@@ -196,6 +209,8 @@ export interface ChatAdapter {
196
209
  // ============ Chat API ============
197
210
  /** 获取可用模型 */
198
211
  getModels(): Promise<ModelOption[]>;
212
+ /** 获取所有工具列表(用于设置面板) */
213
+ getAllTools?(): Promise<Array<{ name: string; description: string }>>;
199
214
  /** 发送消息,返回异步迭代器 */
200
215
  sendMessage(message: string, options?: ChatOptions, images?: string[], sessionId?: string): AsyncIterable<ChatEvent>;
201
216
  /** 取消当前请求 */
@@ -226,6 +241,8 @@ export interface ChatAdapter {
226
241
  sessionId: string;
227
242
  role: 'user' | 'assistant';
228
243
  content: string;
244
+ /** 用户消息附带的图片(data URL 或 base64) */
245
+ images?: string[];
229
246
  /** 生成此消息时使用的模型 */
230
247
  model?: string;
231
248
  /** 生成此消息时使用的模式 (ask/agent) */
@@ -246,6 +263,8 @@ export interface ChatAdapter {
246
263
  }): Promise<void>;
247
264
  /** 删除指定时间之后的消息(用于分叉) */
248
265
  deleteMessagesAfter(sessionId: string, timestamp: number): Promise<void>;
266
+ /** 删除指定消息之后的所有消息(用于分叉) */
267
+ deleteMessagesAfterMessageId(sessionId: string, messageId: string): Promise<void>;
249
268
 
250
269
  // ============ Operations API ============
251
270
  /** 获取操作日志 */
@@ -353,6 +372,15 @@ export function createElectronAdapter(options: CreateAdapterOptions = {}): ChatA
353
372
  return bridge.getModels();
354
373
  },
355
374
 
375
+ async getAllTools(): Promise<Array<{ name: string; description: string }>> {
376
+ const bridge = getBridge();
377
+ // preload 已保证存在;这里保留一层容错,避免宿主未升级时直接崩
378
+ if (typeof (bridge as unknown as { getAllTools?: unknown }).getAllTools === 'function') {
379
+ return bridge.getAllTools();
380
+ }
381
+ return [];
382
+ },
383
+
356
384
  async *sendMessage(
357
385
  message: string,
358
386
  options?: ChatOptions,
@@ -451,6 +479,10 @@ export function createElectronAdapter(options: CreateAdapterOptions = {}): ChatA
451
479
  await getBridge().deleteMessagesAfter(sessionId, timestamp);
452
480
  },
453
481
 
482
+ async deleteMessagesAfterMessageId(sessionId: string, messageId: string): Promise<void> {
483
+ await getBridge().deleteMessagesAfterMessageId(sessionId, messageId);
484
+ },
485
+
454
486
  // ============ Operations API ============
455
487
  async getOperations(sessionId: string): Promise<OperationRecord[]> {
456
488
  return getBridge().getOperations(sessionId);