From 2d3ea852558dcf92ae29749f02e9694cb79a8251 Mon Sep 17 00:00:00 2001 From: thzxx Date: Tue, 23 Jun 2026 22:46:00 +0800 Subject: [PATCH] =?UTF-8?q?v0.12.9:=20Completion=20Gate=20=E6=9E=B6?= =?UTF-8?q?=E6=9E=84=E4=BF=AE=E5=A4=8D=20+=20=E5=B7=A5=E4=BD=9C=E7=A9=BA?= =?UTF-8?q?=E9=97=B4=E6=96=87=E4=BB=B6=E5=A4=B9=E9=80=89=E6=8B=A9=20+=20?= =?UTF-8?q?=E6=90=9C=E7=B4=A2/Plan=20=E5=85=A8=E9=93=BE=E8=B7=AF=E5=8A=A0?= =?UTF-8?q?=E5=9B=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix: - Completion Gate read_file 误报消除 — 缩窄匹配为仅明确工具声明 + 写后读豁免 - Gate 阻断重试消息重写 — 待办中与 Gate 拦截匹配时明确要求调工具而非模糊修正 - FileWriteDedupHook (pre_tool) + PlanAutoTrackHook (post_tool) — Harness 插件化 - 工作空间浏览按钮修复 — openFile→openDirectory,原生文件夹选择对话框 - pre_tool Hook 执行结果可阻断 — HookResult.passed=false 时拦截工具执行 - Plan 步骤上限 6 + 动词过滤 + 阶段标题过滤 — 适配不同模型格式 - Plan Mode 任务描述固化到系统提示词 — 压缩后不丢失任务目标 - 全部完成信号去重 — system 消息不重复注入 - handleReflecting 空响应 pop() 精确定位最后 assistant 消息 Refactor: - agent-engine.ts 移除全部硬编码模块变量和函数 — 迁移到 Hook 系统 - extractPlanSteps 增加 ACTION_VERBS 动词过滤 - Plan rejection 不再重置 loopCount --- README.md | 6 +- package-lock.json | 4 +- package.json | 2 +- src/main/ipc.ts | 8 ++ src/main/menu.ts | 2 +- src/main/preload.ts | 3 +- src/renderer/components/settings-modal.ts | 11 +-- src/renderer/index.html | 2 +- src/renderer/services/agent-engine.ts | 113 +++++++++++----------- src/renderer/services/completion-gate.ts | 10 +- src/renderer/services/hooks.ts | 108 +++++++++++++++++++++ src/renderer/types.d.ts | 1 + 12 files changed, 194 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index 5b57c7a..f69c18a 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@

- version + version electron typescript license @@ -256,7 +256,7 @@ npm start ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ npm run dist ``` -产出:`release/Metona Ollama Setup v0.12.5.exe` +产出:`release/Metona Ollama Setup v0.12.9.exe` ## 🛠️ 常用命令 @@ -507,7 +507,7 @@ npm start ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ npm run dist ``` -Output: `release/Metona Ollama Setup v0.12.5.exe` +Output: `release/Metona Ollama Setup v0.12.9.exe` ## 🛠️ Common Commands diff --git a/package-lock.json b/package-lock.json index 68094e7..a263547 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "metona-ollama-desktop", - "version": "0.12.5", + "version": "0.12.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "metona-ollama-desktop", - "version": "0.12.5", + "version": "0.12.9", "license": "MIT", "dependencies": { "ffmpeg-static": "^5.2.0", diff --git a/package.json b/package.json index b7ece6d..d13d3fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "metona-ollama-desktop", - "version": "0.12.5", + "version": "0.12.9", "description": "Metona Ollama - TypeScript + Electron \u684c\u9762 AI \u804a\u5929\u5ba2\u6237\u7aef", "main": "dist/main/main.js", "author": "thzxx", diff --git a/src/main/ipc.ts b/src/main/ipc.ts index 68cb9cd..dfabb34 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -130,6 +130,14 @@ export async function setupIPC(): Promise { return result.filePath; }); + ipcMain.handle('dialog:openDirectory', async () => { + const result = await dialog.showOpenDialog(mainWindow!, { + properties: ['openDirectory'] + }); + if (result.canceled) return null; + return result.filePaths; + }); + ipcMain.handle('fs:readFile', async (_, filePath: string) => { try { const check = checkPathAllowed(path.resolve(filePath), 'read'); diff --git a/src/main/menu.ts b/src/main/menu.ts index 39a6c8c..b707874 100644 --- a/src/main/menu.ts +++ b/src/main/menu.ts @@ -101,7 +101,7 @@ export function createMenu(): void { dialog.showMessageBox(mainWindow!, { type: 'info', title: '关于 Metona Ollama', - message: 'Metona Ollama Desktop v0.12.5', + message: 'Metona Ollama Desktop v0.12.9', detail: 'TypeScript + Electron Ollama AI 聊天客户端\n\nhttps://gitee.com/thzxx/metona-ollama', icon: getIconPath() }); diff --git a/src/main/preload.ts b/src/main/preload.ts index 35bb05b..0211920 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -19,7 +19,8 @@ contextBridge.exposeInMainWorld('metonaDesktop', { }, dialog: { openFile: (options?: unknown) => ipcRenderer.invoke('dialog:openFile', options), - saveFile: (options?: unknown) => ipcRenderer.invoke('dialog:saveFile', options) + saveFile: (options?: unknown) => ipcRenderer.invoke('dialog:saveFile', options), + openDirectory: () => ipcRenderer.invoke('dialog:openDirectory') }, fs: { readFile: (filePath: string) => ipcRenderer.invoke('fs:readFile', filePath), diff --git a/src/renderer/components/settings-modal.ts b/src/renderer/components/settings-modal.ts index 007e18c..ea241e8 100644 --- a/src/renderer/components/settings-modal.ts +++ b/src/renderer/components/settings-modal.ts @@ -267,12 +267,11 @@ export function initSettingsModal(): void { btnBrowseWorkspace.addEventListener('click', async () => { const bridge = window.metonaDesktop; if (!bridge?.isDesktop) return; - const paths = await bridge.dialog.openFile({ - filters: [{ name: '文件夹', extensions: ['*'] }] - }); - // Electron openFile 对话框不能直接选文件夹,这里用 saveFile 代替 - // 或者让用户手动输入路径 - showToast('请在输入框中直接修改路径', 'info'); + const paths = await bridge.dialog.openDirectory(); + if (paths && paths.length > 0) { + inputWorkspaceDir.value = paths[0]; + inputWorkspaceDir.dispatchEvent(new Event('blur')); + } }); // 双击输入框解锁编辑 inputWorkspaceDir.addEventListener('dblclick', () => { diff --git a/src/renderer/index.html b/src/renderer/index.html index 6002790..99e035f 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -28,7 +28,7 @@

Metona Ollama - v0.12.5 + v0.12.9