新增功能: 1. 技能自动生成系统 (Skill Loop) - 新增 skills 表:id/name/description/trigger_keywords/tool_chain/success_count/fail_count - skill-manager.ts: 从成功工具调用链自动提取可复用技能 - 关键词匹配 + 成功率加权的技能检索 - 匹配到的技能自动注入 system prompt(含工具链步骤和成功率) - Agent Loop 结束后自动提取,开始前自动匹配注入 - DB/IPC/Preload 全链路支持 2. 用户画像模型 - 自动从对话中推断技术栈(14 种技术关键词检测) - 画像存储在 settings.user_profile - 自动注入 system prompt(技术栈/角色/风格) - 随使用不断积累完善 3. 人格模板切换(6 种预设) - 🤖 通用助手(默认,温度跟随设置) - 💻 编程助手(温度 0.3,注重代码质量) - ✍️ 写作助手(温度 0.8,注重文采流畅) - 📊 数据分析(温度 0.2,注重精确逻辑) - 🌐 翻译助手(温度 0.4,注重信达雅) - 🔍 代码审查(温度 0.3,注重安全性能) - 设置面板 3×2 网格按钮,点击切换 + 自动调整温度 修改文件:9 个已有 + 1 个新建(skill-manager.ts)
108 lines
6.1 KiB
TypeScript
108 lines
6.1 KiB
TypeScript
/**
|
|
* Metona Ollama Desktop - Preload 脚本
|
|
*/
|
|
|
|
import { contextBridge, ipcRenderer } from 'electron';
|
|
|
|
contextBridge.exposeInMainWorld('metonaDesktop', {
|
|
isDesktop: true,
|
|
info: () => ipcRenderer.invoke('app:info'),
|
|
dialog: {
|
|
openFile: (filters?: unknown) => ipcRenderer.invoke('dialog:openFile', { filters }),
|
|
saveFile: (options?: unknown) => ipcRenderer.invoke('dialog:saveFile', options)
|
|
},
|
|
fs: {
|
|
readFile: (filePath: string) => ipcRenderer.invoke('fs:readFile', filePath),
|
|
readFileBase64: (filePath: string) => ipcRenderer.invoke('fs:readFileBase64', filePath),
|
|
writeFile: (filePath: string, content: string, encoding?: string) => ipcRenderer.invoke('fs:writeFile', filePath, content, encoding)
|
|
},
|
|
tool: {
|
|
execute: (toolName: string, args: Record<string, unknown>) => ipcRenderer.invoke('tool:execute', toolName, args),
|
|
getConfig: () => ipcRenderer.invoke('tool:getConfig'),
|
|
setAllowedDirs: (dirs: string[]) => ipcRenderer.invoke('tool:setAllowedDirs', dirs)
|
|
},
|
|
notify: (title: string, body: string) => ipcRenderer.invoke('notify', title, body),
|
|
window: {
|
|
minimize: () => ipcRenderer.invoke('window:minimize'),
|
|
maximize: () => ipcRenderer.invoke('window:maximize'),
|
|
close: () => ipcRenderer.invoke('window:close')
|
|
},
|
|
openExternal: (url: string) => ipcRenderer.invoke('shell:openExternal', url),
|
|
onMenuAction: (callback: (action: string) => void) => {
|
|
ipcRenderer.on('menu-action', (_: unknown, action: string) => callback(action));
|
|
},
|
|
onTrayAction: (callback: (action: string) => void) => {
|
|
ipcRenderer.on('tray-action', (_: unknown, action: string) => callback(action));
|
|
},
|
|
onAppQuit: (callback: () => void) => {
|
|
ipcRenderer.on('app-quit', () => callback());
|
|
},
|
|
removeAllListeners: (channel: string) => {
|
|
ipcRenderer.removeAllListeners(channel);
|
|
},
|
|
onMainLog: (callback: (data: { level: string; message: string; detail?: string }) => void) => {
|
|
ipcRenderer.on('main:log', (_: unknown, data: { level: string; message: string; detail?: string }) => callback(data));
|
|
},
|
|
db: {
|
|
saveSession: (session: unknown) => ipcRenderer.invoke('db:saveSession', session),
|
|
getSession: (id: string) => ipcRenderer.invoke('db:getSession', id),
|
|
getAllSessions: () => ipcRenderer.invoke('db:getAllSessions'),
|
|
deleteSession: (id: string) => ipcRenderer.invoke('db:deleteSession', id),
|
|
clearAllSessions: () => ipcRenderer.invoke('db:clearAllSessions'),
|
|
saveMessage: (msg: unknown) => ipcRenderer.invoke('db:saveMessage', msg),
|
|
getMessages: (sessionId: string) => ipcRenderer.invoke('db:getMessages', sessionId),
|
|
saveMemory: (entry: unknown) => ipcRenderer.invoke('db:saveMemory', entry),
|
|
getMemory: (id: string) => ipcRenderer.invoke('db:getMemory', id),
|
|
getAllMemories: () => ipcRenderer.invoke('db:getAllMemories'),
|
|
getMemoriesByType: (type: string) => ipcRenderer.invoke('db:getMemoriesByType', type),
|
|
deleteMemory: (id: string) => ipcRenderer.invoke('db:deleteMemory', id),
|
|
clearAllMemories: () => ipcRenderer.invoke('db:clearAllMemories'),
|
|
searchMemories: (query: string, limit?: number) => ipcRenderer.invoke('db:searchMemories', query, limit),
|
|
saveSetting: (key: string, value: unknown) => ipcRenderer.invoke('db:saveSetting', key, value),
|
|
getSetting: (key: string, defaultValue?: unknown) => ipcRenderer.invoke('db:getSetting', key, defaultValue),
|
|
saveToolCall: (tc: unknown) => ipcRenderer.invoke('db:saveToolCall', tc),
|
|
getToolCalls: (sessionId: string) => ipcRenderer.invoke('db:getToolCalls', sessionId),
|
|
saveTrace: (trace: unknown) => ipcRenderer.invoke('db:saveTrace', trace),
|
|
getTraces: (sessionId: string) => ipcRenderer.invoke('db:getTraces', sessionId),
|
|
exportSessions: () => ipcRenderer.invoke('db:exportSessions'),
|
|
importSessions: (data: unknown) => ipcRenderer.invoke('db:importSessions', data),
|
|
// v4.2 Skills
|
|
saveSkill: (skill: unknown) => ipcRenderer.invoke('db:saveSkill', skill),
|
|
getAllSkills: () => ipcRenderer.invoke('db:getAllSkills'),
|
|
deleteSkill: (id: string) => ipcRenderer.invoke('db:deleteSkill', id),
|
|
clearAllSkills: () => ipcRenderer.invoke('db:clearAllSkills'),
|
|
searchSkills: (query: string, limit?: number) => ipcRenderer.invoke('db:searchSkills', query, limit),
|
|
incrementSkillUsage: (id: string, success: boolean, durationMs: number) => ipcRenderer.invoke('db:incrementSkillUsage', id, success, durationMs),
|
|
},
|
|
workspace: {
|
|
getDir: () => ipcRenderer.invoke('workspace:getDir'),
|
|
setDir: (dir: string) => ipcRenderer.invoke('workspace:setDir', dir),
|
|
listDir: (dirPath?: string) => ipcRenderer.invoke('workspace:listDir', dirPath),
|
|
readFile: (filePath: string) => ipcRenderer.invoke('workspace:readFile', filePath),
|
|
exec: (params: { id: string; command: string; cwd?: string }) => {
|
|
ipcRenderer.send('workspace:exec', params);
|
|
},
|
|
kill: (id: string) => {
|
|
ipcRenderer.send('workspace:kill', id);
|
|
},
|
|
onOutput: (callback: (data: { id: string; type: 'stdout' | 'stderr'; data: string }) => void) => {
|
|
ipcRenderer.on('workspace:output', (_: unknown, data: { id: string; type: 'stdout' | 'stderr'; data: string }) => callback(data));
|
|
},
|
|
onExit: (callback: (data: { id: string; code: number | null }) => void) => {
|
|
ipcRenderer.on('workspace:exit', (_: unknown, data: { id: string; code: number | null }) => callback(data));
|
|
},
|
|
/** 无超时工具命令执行,用于 AI Tool Calling */
|
|
execTool: (command: string, cwd?: string) => ipcRenderer.invoke('tool:execute', 'run_command', { command, cwd }),
|
|
/** AI 命令实时输出推送到工作空间终端 */
|
|
onCmdOutput: (callback: (data: { command: string; type: 'stdout' | 'stderr'; data: string }) => void) => {
|
|
ipcRenderer.on('cmd:output', (_: unknown, data: { command: string; type: 'stdout' | 'stderr'; data: string }) => callback(data));
|
|
},
|
|
/** AI 命令执行完毕 */
|
|
onCmdDone: (callback: (data: { command: string; exitCode: number | null }) => void) => {
|
|
ipcRenderer.on('cmd:done', (_: unknown, data: { command: string; exitCode: number | null }) => callback(data));
|
|
},
|
|
/** 终止当前 AI 命令 */
|
|
cmdKill: () => ipcRenderer.invoke('cmd:kill')
|
|
}
|
|
});
|