后端修复: Ollama adapter 移除死代码/修复超时硬编码/iteration硬编码/pullModel无超时/流异常断开补发DONE; SSE解析器支持非字符串arguments; Sandbox fail-closed安全加固; IPC移除未使用变量 新增功能: TaskOrchestrator子任务编排器; DelegateTaskTool委派工具; Agent Loop并行工具执行; 上下文自动压缩; 记忆检索注入System Prompt 前端修复: useAgentStream text_delta thought累积bug; 工具调用状态正确流转; 修复重复stateChange事件; TraceStep.thought正确填充
128 lines
5.4 KiB
TypeScript
128 lines
5.4 KiB
TypeScript
/**
|
|
* MetonaAI Desktop — Preload 脚本
|
|
*
|
|
* 通过 contextBridge 安全地将 IPC 通道暴露给渲染进程。
|
|
* 渲染进程无 Node.js 访问权限,只能通过此桥接层通信。
|
|
*/
|
|
|
|
import { contextBridge, ipcRenderer } from 'electron';
|
|
import { electronAPI } from '@electron-toolkit/preload';
|
|
|
|
/**
|
|
* Metona API — 暴露给渲染进程的安全接口
|
|
*/
|
|
const metonaAPI = {
|
|
// ===== Agent 交互 =====
|
|
agent: {
|
|
/** 发送用户消息 */
|
|
sendMessage: (message: unknown, sessionId: string) => ipcRenderer.invoke('agent:sendMessage', message, sessionId),
|
|
/** 监听流式事件 */
|
|
onStreamEvent: (callback: (event: unknown) => void) => {
|
|
const listener = (_event: Electron.IpcRendererEvent, data: unknown) => callback(data);
|
|
ipcRenderer.on('agent:streamEvent', listener);
|
|
return () => ipcRenderer.removeListener('agent:streamEvent', listener);
|
|
},
|
|
/** 监听状态变化 */
|
|
onStateChange: (callback: (state: unknown) => void) => {
|
|
const listener = (_event: Electron.IpcRendererEvent, data: unknown) => callback(data);
|
|
ipcRenderer.on('agent:stateChange', listener);
|
|
return () => ipcRenderer.removeListener('agent:stateChange', listener);
|
|
},
|
|
/** 中断会话 */
|
|
abortSession: (sessionId: string) => ipcRenderer.invoke('agent:abortSession', sessionId),
|
|
/** 监听 Provider 切换通知 */
|
|
onProviderSwitched: (callback: (data: unknown) => void) => {
|
|
const listener = (_event: Electron.IpcRendererEvent, data: unknown) => callback(data);
|
|
ipcRenderer.on('agent:providerSwitched', listener);
|
|
return () => ipcRenderer.removeListener('agent:providerSwitched', listener);
|
|
},
|
|
},
|
|
|
|
// ===== 会话管理 =====
|
|
sessions: {
|
|
list: () => ipcRenderer.invoke('sessions:list'),
|
|
create: (title?: string) => ipcRenderer.invoke('sessions:create', title),
|
|
rename: (sessionId: string, title: string) => ipcRenderer.invoke('sessions:rename', sessionId, title),
|
|
delete: (sessionId: string) => ipcRenderer.invoke('sessions:delete', sessionId),
|
|
getMessages: (sessionId: string) => ipcRenderer.invoke('sessions:getMessages', sessionId),
|
|
pin: (sessionId: string, pinned: boolean) => ipcRenderer.invoke('sessions:pin', sessionId, pinned),
|
|
archive: (sessionId: string, archived: boolean) => ipcRenderer.invoke('sessions:archive', sessionId, archived),
|
|
deleteMessage: (messageId: string) => ipcRenderer.invoke('sessions:deleteMessage', messageId),
|
|
clearMessages: (sessionId: string) => ipcRenderer.invoke('sessions:clearMessages', sessionId),
|
|
saveTrace: (sessionId: string, data: unknown) => ipcRenderer.invoke('sessions:saveTrace', sessionId, data),
|
|
getTrace: (sessionId: string) => ipcRenderer.invoke('sessions:getTrace', sessionId),
|
|
},
|
|
|
|
// ===== MCP 管理 =====
|
|
mcp: {
|
|
listServers: () => ipcRenderer.invoke('mcp:listServers'),
|
|
addServer: (config: unknown) => ipcRenderer.invoke('mcp:addServer', config),
|
|
removeServer: (name: string) => ipcRenderer.invoke('mcp:removeServer', name),
|
|
toggleServer: (name: string, enabled: boolean) => ipcRenderer.invoke('mcp:toggleServer', name, enabled),
|
|
},
|
|
|
|
// ===== 记忆系统 =====
|
|
memory: {
|
|
search: (query: string, options?: unknown) => ipcRenderer.invoke('db:searchMemories', query, options),
|
|
},
|
|
|
|
// ===== 配置 =====
|
|
config: {
|
|
get: (key: string) => ipcRenderer.invoke('config:get', key),
|
|
set: (key: string, value: unknown) => ipcRenderer.invoke('config:set', key, value),
|
|
},
|
|
|
|
// ===== 应用工具 =====
|
|
app: {
|
|
getVersion: () => ipcRenderer.invoke('app:getVersion'),
|
|
getAppDataPath: () => ipcRenderer.invoke('app:getAppDataPath'),
|
|
openExternal: (url: string) => ipcRenderer.invoke('app:openExternal', url),
|
|
showItemInFolder: (path: string) => ipcRenderer.invoke('app:showItemInFolder', path),
|
|
selectFolder: (defaultPath?: string) => ipcRenderer.invoke('app:selectFolder', defaultPath),
|
|
},
|
|
|
|
// ===== 工具管理 =====
|
|
tools: {
|
|
list: () => ipcRenderer.invoke('tools:list'),
|
|
toggle: (toolName: string, enabled: boolean) => ipcRenderer.invoke('tools:toggle', toolName, enabled),
|
|
},
|
|
|
|
// ===== 数据管理 =====
|
|
data: {
|
|
exportData: (sessionId?: string) => ipcRenderer.invoke('data:export', sessionId),
|
|
clearSessions: () => ipcRenderer.invoke('data:clearSessions'),
|
|
clearMemories: () => ipcRenderer.invoke('data:clearMemories'),
|
|
clearAuditLogs: () => ipcRenderer.invoke('data:clearAuditLogs'),
|
|
},
|
|
|
|
// ===== SearXNG =====
|
|
searxng: {
|
|
testConnection: (url: string, authKey: string, authType: string) =>
|
|
ipcRenderer.invoke('searxng:testConnection', url, authKey, authType),
|
|
},
|
|
|
|
// ===== Toast 通知桥接 =====
|
|
toast: {
|
|
onShow: (callback: (data: { type: string; message: string; options?: unknown }) => void) => {
|
|
const listener = (_event: Electron.IpcRendererEvent, data: { type: string; message: string; options?: unknown }) =>
|
|
callback(data);
|
|
ipcRenderer.on('toast:show', listener);
|
|
return () => ipcRenderer.removeListener('toast:show', listener);
|
|
},
|
|
},
|
|
};
|
|
|
|
// ===== 安全暴露到渲染进程 =====
|
|
if (process.contextIsolated) {
|
|
try {
|
|
contextBridge.exposeInMainWorld('electron', electronAPI);
|
|
contextBridge.exposeInMainWorld('metona', metonaAPI);
|
|
} catch (error) {
|
|
console.error('Failed to expose APIs:', error);
|
|
}
|
|
} else {
|
|
// 降级方案(不推荐)
|
|
(globalThis as unknown as Record<string, unknown>).electron = electronAPI;
|
|
(globalThis as unknown as Record<string, unknown>).metona = metonaAPI;
|
|
}
|