From e4f83bd09acf084980dec5f19eb88fbabe437e9f Mon Sep 17 00:00:00 2001 From: thzxx <1440196015@qq.com> Date: Sun, 2 Aug 2026 19:09:29 +0800 Subject: [PATCH] =?UTF-8?q?v0.16.19:=20=E4=BF=AE=E5=A4=8D=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E9=9D=A2=E6=9D=BF=E6=B8=85=E7=A9=BA=E3=80=81URL?= =?UTF-8?q?=E8=AF=AF=E4=BC=A0=E6=96=87=E4=BB=B6=E5=B7=A5=E5=85=B7=E3=80=81?= =?UTF-8?q?=E7=BD=91=E7=BB=9C=E5=B7=A5=E5=85=B7=E8=B6=85=E6=97=B6=E3=80=81?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E6=88=AA=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 216 ++++++++++++------------- package-lock.json | 4 +- package.json | 2 +- src/main/menu.ts | 2 +- src/main/tool-handlers-fs.ts | 14 +- src/main/tool-handlers-shared.ts | 7 + src/renderer/components/chat-area.ts | 19 +++ src/renderer/components/input-area.ts | 8 +- src/renderer/index.html | 2 +- src/renderer/main.ts | 3 + src/renderer/services/agent-engine.ts | 31 +++- src/renderer/services/log-service.ts | 81 ++++------ src/renderer/services/tool-registry.ts | 102 ++++++++---- 13 files changed, 295 insertions(+), 196 deletions(-) diff --git a/README.md b/README.md index ecbb10b..0770f74 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@

Metona Ollama Desktop

- 🤖 本地 AI 桌面客户?· Local AI Desktop Client + 🤖 本地 AI 桌面客户�?· Local AI Desktop Client

@@ -14,7 +14,7 @@

- version + version electron typescript license @@ -22,65 +22,65 @@

- 所?AI 推理在本地完成,数据不离开本机?/em>
- All AI inference runs locally ?data never leaves your machine. + 所�?AI 推理在本地完成,数据不离开本机�?/em>
+ All AI inference runs locally �?data never leaves your machine.

--- # 🇨🇳 中文 -## ?特? +## �?特�? | | 功能 | 说明 | |:---:|:---|:---| -| 🤖 | **ReAct Agent Loop** | 始终开启的唯一对话模式? 状态机(INIT→THINKING→PARSING→EXECUTING→OBSERVING→REFLECTING→COMPRESSING→TERMINATED),最?85 轮(可配置),智能重试(永久错误立即返回、瞬态错误指数退避),工具去重,智能路径依赖串行化,看门狗超时保?| -| 🛡?| **提示词加?* | 参考数据标记(<<>>? 工具结果仅为数据非指令的安全规则注入 | -| 📋 | **Plan Mode** | 开关切换,AI 首先生成执行计划(Markdown 渲染确认弹窗),批准后按步骤追踪执行,plan_track 工具标记完成状态,**支持断点续传**(中止后可恢复未完成计划?| -| 🔧 | **32 个内置工?* | 文件系统?3个) · 命令执行 · 联网搜索 · 浏览器控制(9个) · Git · 记忆 · 会话 · 子代?· 系统工具 · Plan Mode 追踪 | -| 🧠 | **智能记忆系统** | 三类记忆(fact / preference / rule),存储于工作空?MEMORY.md 文件,受路径保护?memory 工具可访问,写入前安全扫描,容量 500 条,对话结束自动提取,严格格式校?| -| 📋 | **自定义文?* | SOUL.md(人格,不可压缩? AGENT.md(行为准则,内置 fallback? USER.md(用户画像,仅工作空间读取,不存在则不注入) | +| 🤖 | **ReAct Agent Loop** | 始终开启的唯一对话模式�? 状态机(INIT→THINKING→PARSING→EXECUTING→OBSERVING→REFLECTING→COMPRESSING→TERMINATED),最�?85 轮(可配置),智能重试(永久错误立即返回、瞬态错误指数退避),工具去重,智能路径依赖串行化,看门狗超时保�?| +| 🛡�?| **提示词加�?* | 参考数据标记(<<>>�? 工具结果仅为数据非指令的安全规则注入 | +| 📋 | **Plan Mode** | 开关切换,AI 首先生成执行计划(Markdown 渲染确认弹窗),批准后按步骤追踪执行,plan_track 工具标记完成状态,**支持断点续传**(中止后可恢复未完成计划�?| +| 🔧 | **32 个内置工�?* | 文件系统�?3个) · 命令执行 · 联网搜索 · 浏览器控制(9个) · Git · 记忆 · 会话 · 子代�?· 系统工具 · Plan Mode 追踪 | +| 🧠 | **智能记忆系统** | 三类记忆(fact / preference / rule),存储于工作空�?MEMORY.md 文件,受路径保护�?memory 工具可访问,写入前安全扫描,容量 500 条,对话结束自动提取,严格格式校�?| +| 📋 | **自定义文�?* | SOUL.md(人格,不可压缩�? AGENT.md(行为准则,内置 fallback�? USER.md(用户画像,仅工作空间读取,不存在则不注入) | | 🌐 | **MCP 协议扩展** | JSON-RPC 2.0 over stdio,动态工具发现,Shadowing 防护 | -| 🔍 | **联网搜索(双模式?* | SearXNG 元搜索引?JSON API?0+引擎聚合? 四引?HTML 解析(Bing+百度+搜狗+360),双模式可切换;web_fetch 支持反爬+UA切换+浏览器回退 | -| 🌏 | **浏览器控?* | 打开网页 · 截图 · 执行 JS · 提取内容 · 点击 · 输入 · 滚动 · 关闭 | -| 🖥?| **工作空间面板** | 终端(增量流式输出)+ 文件浏览器,命令安全检?| -| 🔢 | **上下文长度手动控?* | 设置面板下拉选择?28K / 256K / 512K / 1M),默认 128K,模型栏显示当前配置值,下拉框中显示每个模型自身的上下文长度 | -| 🗜?| **智能上下文管?* | 滑动窗口 + Token 自动校准 + 消息重要性评?+ LLM 结构?JSON 压缩,智能触发(120 条增量压?+ 300 条硬上限?| -| ⏱️ | **智能超时保护** | Agent Loop 看门狗(可配,默?30min? 流式总超时(可配,默?300s? 工具 HTTP/MCP 超时可配 | -| 🪝 | **Hook 系统** | 4 阶段生命周期钩子(pre_tool / post_tool / post_iteration / pre_completion),内置安全检查、文件去重、自动计划追踪、变更审计、结果校?| -| 👥 | **子代理委?* | spawn_task 工具,独立上下文 + 超时保护 | -| 📈 | **Token 仪表?* | 全局 + 会话统计,消耗趋势柱状图? 秒刷新,输入/输出分色 | -| 📋 | **系统提示词卡?* | 每条 AI 回复顶部折叠卡片,点击查看实际发送给模型的完整上下文 | -| 🎨 | **暖色?UI** | 奶白 `#FAF7F2` + 珊瑚?`#E8734A`,长时间使用不疲劳,流式渲染优化 | +| 🔍 | **联网搜索(双模式�?* | SearXNG 元搜索引�?JSON API�?0+引擎聚合�? 四引�?HTML 解析(Bing+百度+搜狗+360),双模式可切换;web_fetch 支持反爬+UA切换+浏览器回退 | +| 🌏 | **浏览器控�?* | 打开网页 · 截图 · 执行 JS · 提取内容 · 点击 · 输入 · 滚动 · 关闭 | +| 🖥�?| **工作空间面板** | 终端(增量流式输出)+ 文件浏览器,命令安全检�?| +| 🔢 | **上下文长度手动控�?* | 设置面板下拉选择�?28K / 256K / 512K / 1M),默认 128K,模型栏显示当前配置值,下拉框中显示每个模型自身的上下文长度 | +| 🗜�?| **智能上下文管�?* | 滑动窗口 + Token 自动校准 + 消息重要性评�?+ LLM 结构�?JSON 压缩,智能触发(120 条增量压�?+ 300 条硬上限�?| +| ⏱️ | **智能超时保护** | Agent Loop 看门狗(可配,默�?30min�? 流式总超时(可配,默�?300s�? 工具 HTTP/MCP 超时可配 | +| 🪝 | **Hook 系统** | 4 阶段生命周期钩子(pre_tool / post_tool / post_iteration / pre_completion),内置安全检查、文件去重、自动计划追踪、变更审计、结果校�?| +| 👥 | **子代理委�?* | spawn_task 工具,独立上下文 + 超时保护 | +| 📈 | **Token 仪表�?* | 全局 + 会话统计,消耗趋势柱状图�? 秒刷新,输入/输出分色 | +| 📋 | **系统提示词卡�?* | 每条 AI 回复顶部折叠卡片,点击查看实际发送给模型的完整上下文 | +| 🎨 | **暖色�?UI** | 奶白 `#FAF7F2` + 珊瑚�?`#E8734A`,长时间使用不疲劳,流式渲染优化 | | 🔔 | **系统托盘** | 原生托盘集成 | | 🔐 | **AES-256-GCM** | 数据加密支持 | -| 🧪 | **可观测?* | 执行轨迹缓冲批量写入 SQLite + Agent Metrics JSON/Prometheus 双格式导?| +| 🧪 | **可观测�?* | 执行轨迹缓冲批量写入 SQLite + Agent Metrics JSON/Prometheus 双格式导�?| ## 🔧 工具清单
-📁 文件系统?3 个) +📁 文件系统�?3 个) | 工具 | 功能 | |------|------| -| `read_file` | 读取文件(文?binary?000行默认) | +| `read_file` | 读取文件(文�?binary�?000行默认) | | `write_file` | 写入/追加文件(支持base64二进制) | | `list_directory` | 列出目录内容 | -| `search_files` | 搜索文件(正?通配符) | +| `search_files` | 搜索文件(正�?通配符) | | `create_directory` | 创建目录 | | `delete_file` | 删除文件/目录(返回大小) | -| `move_file` | 移动/重命名文?| +| `move_file` | 移动/重命名文�?| | `copy_file` | 复制文件 | | `edit_file` | 编辑文件(支持正则替换) | | `tree` | 目录树结构(默认5层深度) | | `download_file` | 下载文件 | -| `read_multiple_files` | 批量读取?0文件/10KB?| +| `read_multiple_files` | 批量读取�?0文件/10KB�?| | `compress` | 压缩文件/目录 |
-?命令执行? 个) +�?命令执行�? 个) | 工具 | 功能 | |------|------| @@ -89,11 +89,11 @@
-🔍 联网搜索? 个) +🔍 联网搜索�? 个) | 工具 | 功能 | |------|------| -| `web_search` | SearXNG JSON API / 四引擎并行,默认 30 条结?| +| `web_search` | SearXNG JSON API / 四引擎并行,默认 30 条结�?| | `web_fetch` | 网页内容抓取,自动重试(指数退避),移动端UA切换,SPA页面自动升级到浏览器渲染 |
@@ -111,27 +111,27 @@ | `browser_type` | 输入文本 | | `browser_scroll` | 页面滚动 | | `browser_wait` | 等待元素/延时 | -| `browser_close` | 关闭浏览?| +| `browser_close` | 关闭浏览�?|
-📦 Git? 个,17 个子命令?/strong> +📦 Git�? 个,17 个子命令�?/strong> -| 子命?| 功能 | +| 子命�?| 功能 | |--------|------| | `init` `clone` `add` `commit` `push` `pull` | 基础操作 | -| `diff` `log` `status` `branch` `checkout` | 查看与切?| +| `diff` `log` `status` `branch` `checkout` | 查看与切�?| | `merge` `stash` `reset` `tag` `remote` | 高级操作 |
-🧠 记忆管理? 个) +🧠 记忆管理�? 个) | 工具 | 功能 | |------|------| -| `memory` | 统一记忆管理? ?action:search 搜索 / add 添加 / replace 替换 / remove 删除 / read_all 读取全部)。工作空?MEMORY.md 受路径保护,仅此工具可访?| +| `memory` | 统一记忆管理�? �?action:search 搜索 / add 添加 / replace 替换 / remove 删除 / read_all 读取全部)。工作空�?MEMORY.md 受路径保护,仅此工具可访�?|
@@ -142,21 +142,21 @@ |------|------| | `session_list` | 列出历史会话 | | `session_read` | 读取会话内容 | -| `spawn_task` | 委派子代理(独立上下?+ 超时保护?| +| `spawn_task` | 委派子代理(独立上下�?+ 超时保护�?|
-🕐 系统工具? 个) +🕐 系统工具�? 个) | 工具 | 功能 | |------|------| -| `calculator` | 安全数学计算? - * / ** % (),递归下降解析器) | +| `calculator` | 安全数学计算�? - * / ** % (),递归下降解析器) |
-📋 Plan Mode? 个,?Plan 模式激活) +📋 Plan Mode�? 个,�?Plan 模式激活) | 工具 | 功能 | |------|------| @@ -164,51 +164,51 @@
-## 🏗?架构 +## 🏗�?架构 ``` -用户消息 ?扫描工作空间 SOUL.md(不可压缩层)→ AGENT.md ?USER.md(工作空间,不存在则跳过? - ? - 记忆检?(MEMORY.md 关键词搜? ?上下文注? - ? - Agent Engine (8 状态机 ReAct Loop, ?5 ? 智能重试, 路径依赖串行? 看门?分级超时) - ? - 提示词加固(参考数据标?+ 工具结果安全规则? - ? +用户消息 �?扫描工作空间 SOUL.md(不可压缩层)→ AGENT.md �?USER.md(工作空间,不存在则跳过�? + �? + 记忆检�?(MEMORY.md 关键词搜�? �?上下文注�? + �? + Agent Engine (8 状态机 ReAct Loop, �?5 �? 智能重试, 路径依赖串行�? 看门�?分级超时) + �? + 提示词加固(参考数据标�?+ 工具结果安全规则�? + �? Ollama API (流式响应,num_ctx 用户可配 128K/256K/512K/1M) - ? - Tool Registry (32 内置 + MCP 动?+ Plan Mode plan_track) - ? - Hook 系统 (pre/post tool/iteration/completion) ?观察结果 ?反??循环 / 最终回? + �? + Tool Registry (32 内置 + MCP 动�?+ Plan Mode plan_track) + �? + Hook 系统 (pre/post tool/iteration/completion) �?观察结果 �?反�?�?循环 / 最终回�? ``` -### 🧩 UI 组件(原?DOM? +### 🧩 UI 组件(原�?DOM�? ``` main.ts (入口) - ├── header.ts # 顶部导航? - ├── model-bar.ts # 模型选择? + ├── header.ts # 顶部导航�? + ├── model-bar.ts # 模型选择�? ├── chat-area.ts # 聊天消息区域 - ├── input-area.ts # 输入?+ 文件上传 + Plan Mode 开? - ├── workspace-panel.ts # 终端 + 文件浏览?+ 工具卡片 + ├── input-area.ts # 输入�?+ 文件上传 + Plan Mode 开�? + ├── workspace-panel.ts # 终端 + 文件浏览�?+ 工具卡片 ├── settings-modal.ts # 设置面板(含看门狗超时配置) ├── history-modal.ts # 会话历史 ├── memory-modal.ts # 记忆管理 ├── tools-modal.ts # 工具列表 ├── token-dashboard.ts # Token 消耗仪表盘 ├── tool-confirm-modal.ts # 工具执行确认 - ├── prompt-modal.ts # 系统提示词查?+ Plan 确认弹窗 + ├── prompt-modal.ts # 系统提示词查�?+ Plan 确认弹窗 ├── toast.ts # Toast 通知 └── lightbox.ts # 图片灯箱 ``` -### 🗄?数据? +### 🗄�?数据�? -SQLite (sql.js WASM)? 张表,WAL 模式? +SQLite (sql.js WASM)�? 张表,WAL 模式�? -| ?| 用?| +| �?| 用�?| |---|---| -| `sessions` | 会话(parent_id 父子关系?| +| `sessions` | 会话(parent_id 父子关系�?| | `messages` | 消息(外键级联删除) | | `tool_calls` | 工具调用记录 | | `settings` | 设置(JSON 序列化) | @@ -218,17 +218,17 @@ SQLite (sql.js WASM) | 层级 | 措施 | |------|------| -| 📁 文件系统 | `checkPathAllowed()` ?路径黑名单(33 个系?敏感目录,含 Linux + Windows?| -| ?命令执行 | `checkCommandAllowed()` ?命令黑名单(30 条危险命令,?POSIX + Windows? 三种模式 | -| 🖥?前端渲染 | HTML 净化器(白名单标签 + URI 协议检查) | -| 🔒 Electron | `contextIsolation: true` + IPC 白名?+ IPC fs 路径验证 | +| 📁 文件系统 | `checkPathAllowed()` �?路径黑名单(33 个系�?敏感目录,含 Linux + Windows�?| +| �?命令执行 | `checkCommandAllowed()` �?命令黑名单(30 条危险命令,�?POSIX + Windows�? 三种模式 | +| 🖥�?前端渲染 | HTML 净化器(白名单标签 + URI 协议检查) | +| 🔒 Electron | `contextIsolation: true` + IPC 白名�?+ IPC fs 路径验证 | | 🔐 数据加密 | AES-256-GCM | -| 🧠 记忆安全 | 写入?Prompt Injection / 敏感信息检测,多层级去重过?| -| 🌐 MCP 安全 | Shadowing 防护 + 双下划线分隔防歧?| -| 🌐 网络安全 | `web_fetch` 流式体积限制?0MB? ?content-length 时防 OOM | -| ?进程安全 | Windows `taskkill` 强制终止 + 工作空间路径大小写不敏感校验 | +| 🧠 记忆安全 | 写入�?Prompt Injection / 敏感信息检测,多层级去重过�?| +| 🌐 MCP 安全 | Shadowing 防护 + 双下划线分隔防歧�?| +| 🌐 网络安全 | `web_fetch` 流式体积限制�?0MB�? �?content-length 时防 OOM | +| �?进程安全 | Windows `taskkill` 强制终止 + 工作空间路径大小写不敏感校验 | -## 🚀 快速开? +## 🚀 快速开�? ```bash git clone https://gitee.com/thzxx/metona-ollama-desktop.git @@ -238,32 +238,32 @@ npm install npm start ``` -## 📦 构建 Windows 安装? +## 📦 构建 Windows 安装�? ```bash # 环境:Ubuntu 24.04 + Node.js v22 + Wine 9.0+ ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ npm run dist ``` -产出:`release/Metona Ollama Setup v0.16.18.exe` +产出:`release/Metona Ollama Setup v0.16.19.exe` -## 🛠?常用命令 +## 🛠�?常用命令 ```bash -npm start # 构建并运? +npm start # 构建并运�? npm run dev:renderer # Vite watch(渲染进程) -npm run dev:main # tsc watch(主进程? +npm run dev:main # tsc watch(主进程�? npm run build # 完整构建 -npm run dist # 构建 Windows 安装? +npm run dist # 构建 Windows 安装�? ``` ## 💻 系统要求 -- 🖥?Windows 10/11?4 位) -- 🤖 Ollama 本地运行中(默认 `http://localhost:11434`? +- 🖥�?Windows 10/11�?4 位) +- 🤖 Ollama 本地运行中(默认 `http://localhost:11434`�? - 💾 建议 8GB+ 内存 -## 📄 许可? +## 📄 许可�? [MIT](LICENSE) @@ -271,27 +271,27 @@ npm run dist # 构建 Windows 安装 # 🇬🇧 English -## ?Features +## �?Features | | Feature | Description | |:---:|:---|:---| | 🤖 | **ReAct Agent Loop** | Always-on, only chat mode. 8-state machine, up to 85 iterations (configurable), smart retry (permanent errors return immediately, transient errors use exponential backoff), tool dedup, path-aware dependency serialization, watchdog + tiered timeout protection | -| 🛡?| **Prompt Hardening** | Reference data markers (<<>>) + tool results are data-not-instructions safety rules | +| 🛡�?| **Prompt Hardening** | Reference data markers (<<>>) + tool results are data-not-instructions safety rules | | 📋 | **Plan Mode** | Toggle switch. AI first generates an execution plan (Markdown-rendered confirmation dialog), then tracks step-by-step execution, **supports resume** (interrupted plans restore on next session) | | 🔧 | **32 Built-in Tools** | File system · Command · Web search · Browser · Git · Memory · Sessions · Sub-agent · System · Plan Mode tracking | -| 🧠 | **Smart Memory System** | Three types (fact / preference / rule), stored in workspace MEMORY.md file, path-protected ?only the memory tool can access it, pre-write security scan, 500 capacity, auto-extract on conversation end, strict format validation | +| 🧠 | **Smart Memory System** | Three types (fact / preference / rule), stored in workspace MEMORY.md file, path-protected �?only the memory tool can access it, pre-write security scan, 500 capacity, auto-extract on conversation end, strict format validation | | 📋 | **Custom Files** | SOUL.md (persona, never compressed) + AGENT.md (behavior rules, built-in fallback) + USER.md (user profile, workspace only, skipped if absent) | | 🌐 | **MCP Protocol Extension** | JSON-RPC 2.0 over stdio, dynamic tool discovery, Shadowing protection | | 🔍 | **Web Search (dual-mode)** | SearXNG meta-search JSON API (70+ engines) / quad-engine HTML parsing (Bing+Baidu+Sogou+360), switchable; web_fetch with auto-retry+mobile UA+SPA browser fallback | | 🌏 | **Browser Control** | Open pages · Screenshot · JS execution · Content extraction · Click · Type · Scroll · Close | -| 🖥?| **Workspace Panel** | Terminal (incremental streaming) + file browser, command security checks | +| 🖥�?| **Workspace Panel** | Terminal (incremental streaming) + file browser, command security checks | | 🔢 | **Manual Context Length** | Settings dropdown (128K / 256K / 512K / 1M), default 128K. Model bar shows configured value, dropdown shows per-model context length | -| 🗜?| **Smart Context Manager** | Sliding window + Token auto-calibration + message importance scoring + LLM structured JSON compression, smart triggers (120 msg incremental + 300 msg hard limit) | +| 🗜�?| **Smart Context Manager** | Sliding window + Token auto-calibration + message importance scoring + LLM structured JSON compression, smart triggers (120 msg incremental + 300 msg hard limit) | | ⏱️ | **Smart Timeout Protection** | Agent Loop watchdog (configurable, default 30min) + stream total timeout (configurable, default 300s) + configurable HTTP/MCP timeouts | | 🪝 | **Hook System** | 4-phase lifecycle hooks (pre_tool / post_tool / post_iteration / pre_completion), built-in security check, file dedup, auto plan tracking, change audit, result validation | | 👥 | **Sub-agent Delegation** | spawn_task tool, isolated context + timeout protection | | 📈 | **Token Dashboard** | Global + session stats, consumption trend bar chart, 2s refresh, input/output color-coded | -| 📋 | **System Prompt Card** | Collapsible card atop each AI reply ?click to inspect the full context sent to the model | +| 📋 | **System Prompt Card** | Collapsible card atop each AI reply �?click to inspect the full context sent to the model | | 🎨 | **Warm-tone UI** | Cream `#FAF7F2` + coral `#E8734A`, fatigue-free for extended use, streaming render optimized | | 🔔 | **System Tray** | Native tray integration | | 🔐 | **AES-256-GCM** | Data encryption support | @@ -321,7 +321,7 @@ npm run dist # 构建 Windows 安装
-?Command Execution (1) +�?Command Execution (1) | Tool | Function | |------|------| @@ -404,22 +404,22 @@ npm run dist # 构建 Windows 安装
-## 🏗?Architecture +## 🏗�?Architecture ``` -User message ?workspace SOUL.md (never compressed) ?AGENT.md ?USER.md (workspace, skipped if absent) - ? - Memory Retrieval (MEMORY.md keyword search) ?Context Injection - ? - Agent Engine (8-state ReAct Loop, ?5 iter, smart retry, path-aware serialization, watchdog+tiered timeout) - ? +User message �?workspace SOUL.md (never compressed) �?AGENT.md �?USER.md (workspace, skipped if absent) + �? + Memory Retrieval (MEMORY.md keyword search) �?Context Injection + �? + Agent Engine (8-state ReAct Loop, �?5 iter, smart retry, path-aware serialization, watchdog+tiered timeout) + �? Prompt Hardening (reference data markers + tool result safety rules) - ? + �? Ollama API (Streaming Response, num_ctx user-configurable 128K/256K/512K/1M) - ? + �? Tool Registry (32 Built-in + MCP Dynamic + Plan Mode plan_track) - ? - Hook System (pre/post tool/iteration/completion) ?Observation ?Reflection ?Loop / Final Answer + �? + Hook System (pre/post tool/iteration/completion) �?Observation �?Reflection �?Loop / Final Answer ``` ### 🧩 UI Components (Native DOM) @@ -442,7 +442,7 @@ main.ts (Entry) └── lightbox.ts # Image lightbox ``` -### 🗄?Database +### 🗄�?Database SQLite (sql.js WASM), 5 tables, WAL mode: @@ -458,15 +458,15 @@ SQLite (sql.js WASM), 5 tables, WAL mode: | Layer | Measure | |------|------| -| 📁 File System | `checkPathAllowed()` ?path blacklist (33 system/sensitive dirs, Linux + Windows) | -| ?Command Execution | `checkCommandAllowed()` ?command blacklist (30 dangerous commands, POSIX + Windows) + three modes | -| 🖥?Frontend Rendering | HTML sanitizer (whitelist tags + URI protocol check) | +| 📁 File System | `checkPathAllowed()` �?path blacklist (33 system/sensitive dirs, Linux + Windows) | +| �?Command Execution | `checkCommandAllowed()` �?command blacklist (30 dangerous commands, POSIX + Windows) + three modes | +| 🖥�?Frontend Rendering | HTML sanitizer (whitelist tags + URI protocol check) | | 🔒 Electron | `contextIsolation: true` + IPC whitelist + IPC fs path validation | | 🔐 Data Encryption | AES-256-GCM | | 🧠 Memory Security | Pre-write prompt injection / sensitive info detection, multi-layer dedup filtering | | 🌐 MCP Security | Shadowing protection + double-underscore delimiter disambiguation | | 🌐 Network Security | `web_fetch` streaming size limit (10MB) + OOM prevention without content-length | -| ?Process Security | Windows `taskkill` forced termination + case-insensitive workspace path validation | +| �?Process Security | Windows `taskkill` forced termination + case-insensitive workspace path validation | ## 🚀 Quick Start @@ -485,9 +485,9 @@ npm start ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ npm run dist ``` -Output: `release/Metona Ollama Setup v0.16.18.exe` +Output: `release/Metona Ollama Setup v0.16.19.exe` -## 🛠?Common Commands +## 🛠�?Common Commands ```bash npm start # Build and run @@ -499,7 +499,7 @@ npm run dist # Build Windows installer ## 💻 System Requirements -- 🖥?Windows 10/11 (64-bit) +- 🖥�?Windows 10/11 (64-bit) - 🤖 Ollama running locally (default `http://localhost:11434`) - 💾 8GB+ RAM recommended diff --git a/package-lock.json b/package-lock.json index 70ecebd..b5ee04a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "metona-ollama-desktop", - "version": "0.16.18", + "version": "0.16.19", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "metona-ollama-desktop", - "version": "0.16.18", + "version": "0.16.19", "license": "MIT", "dependencies": { "ffmpeg-static": "^5.2.0", diff --git a/package.json b/package.json index 3fc0fad..e8bbf07 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "metona-ollama-desktop", - "version": "0.16.18", + "version": "0.16.19", "description": "Metona Ollama - TypeScript + Electron 桌面 AI 聊天客户端", "main": "dist/main/main.js", "author": "thzxx", diff --git a/src/main/menu.ts b/src/main/menu.ts index c0395ee..ab594cb 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.16.18', + message: 'Metona Ollama Desktop v0.16.19', detail: 'TypeScript + Electron Ollama AI 聊天客户端\n\nhttps://gitee.com/thzxx/metona-ollama', icon: getIconPath() }); diff --git a/src/main/tool-handlers-fs.ts b/src/main/tool-handlers-fs.ts index 0c58f4b..c1aff1c 100644 --- a/src/main/tool-handlers-fs.ts +++ b/src/main/tool-handlers-fs.ts @@ -6,11 +6,15 @@ import * as fs from 'fs/promises'; import * as path from 'path'; import * as os from 'os'; import { checkPathAllowed } from './tool-security.js'; -import { sendLog, resolvePath, type ToolResult } from './tool-handlers-shared.js'; +import { sendLog, resolvePath, isUrl, type ToolResult } from './tool-handlers-shared.js'; import { getWorkspaceDir } from './workspace.js'; export async function handleReadFile(params: { path: string; encoding?: string; start_line?: number; end_line?: number; mode?: string; offset_bytes?: number; limit_bytes?: number }): Promise { try { + // URL 检测:read_file 仅支持本地文件,URL 请使用 web_fetch + if (isUrl(params.path)) { + return { success: false, error: `read_file 仅支持本地文件路径,不支持 URL。请改用 web_fetch 工具读取网页内容。收到的 URL: ${params.path}` }; + } const filePath = resolvePath(params.path); const allowed = checkPathAllowed(filePath, 'read'); if (!allowed.ok) return { success: false, error: allowed.reason }; @@ -637,6 +641,14 @@ export async function handleTree(params: { path: string; max_depth?: number; inc export async function handleReadMultipleFiles(params: { paths: string[]; max_chars_per_file?: number }): Promise { try { + // URL 检测:read_multiple_files 仅支持本地文件,URL 请使用 web_fetch + const urlPaths = (params.paths || []).filter(p => isUrl(p)); + if (urlPaths.length > 0) { + return { + success: false, + error: `read_multiple_files 仅支持本地文件路径,不支持 URL。以下路径是 URL,请改用 web_fetch 工具逐个抓取:\n${urlPaths.join('\n')}` + }; + } const maxChars = params.max_chars_per_file || 0; // 0 = 不截断 // 并行读取所有文件(最多50个) diff --git a/src/main/tool-handlers-shared.ts b/src/main/tool-handlers-shared.ts index 73f44e8..ef234c1 100644 --- a/src/main/tool-handlers-shared.ts +++ b/src/main/tool-handlers-shared.ts @@ -11,6 +11,13 @@ export function sendLog(level: 'info' | 'success' | 'warn' | 'error' | 'debug', mainWindow?.webContents.send('main:log', { level, message, detail }); } +/** 检测字符串是否为 URL(http:// 或 https://) */ +export function isUrl(str: string): boolean { + if (typeof str !== 'string' || str.length < 8) return false; + const lower = str.toLowerCase().trim(); + return lower.startsWith('http://') || lower.startsWith('https://'); +} + /** 解析路径:相对路径基于工作空间目录 */ export function resolvePath(inputPath: string): string { if (path.isAbsolute(inputPath)) return path.resolve(inputPath); diff --git a/src/renderer/components/chat-area.ts b/src/renderer/components/chat-area.ts index e45ec49..448bb83 100644 --- a/src/renderer/components/chat-area.ts +++ b/src/renderer/components/chat-area.ts @@ -583,6 +583,25 @@ export function appendAssistantPlaceholder(): void { scrollToBottom(); } +/** + * 确保 loading 占位符存在 — 如果当前没有 loading 占位符,创建一个。 + * 用于中间轮次 THINKING 开始前,确保用户看到"正在思考..."状态。 + */ +export function ensureLoadingPlaceholder(): void { + if (currentPlaceholder && currentPlaceholder.classList.contains('loading') && currentPlaceholder.isConnected) { + return; + } + // 当前 placeholder 不存在或不是 loading 状态 → 清理旧 placeholder 并创建新的 + if (currentPlaceholder && !currentPlaceholder.classList.contains('loading') && currentPlaceholder.isConnected) { + // 上一轮的 placeholder 已经被 updateLastAssistantMessage 转为正常消息但未通过 renderMessages 固化 + // 不需要移除,直接追加新的 loading + } + if (currentPlaceholder && !currentPlaceholder.isConnected) { + currentPlaceholder = null; + } + appendAssistantPlaceholder(); +} + // R34: 加载提示旋转动画 const LOADING_HINTS = [ '正在思考...', diff --git a/src/renderer/components/input-area.ts b/src/renderer/components/input-area.ts index 8100ce6..534df6b 100644 --- a/src/renderer/components/input-area.ts +++ b/src/renderer/components/input-area.ts @@ -9,7 +9,7 @@ import { getSelectedModel, isThinkEnabled, isVisionAvailable, isToolCallingSuppo import { renderMessages, clearMessagesDOM, appendAssistantPlaceholder, appendSystemMessage, updateLastAssistantMessage, clearMessages, safeMarkdown, enableAutoScroll, resetAutoScroll, - resetCurrentPlaceholder, removeCurrentPlaceholder + resetCurrentPlaceholder, removeCurrentPlaceholder, ensureLoadingPlaceholder } from './chat-area.js'; import { showToast } from './toast.js'; import { addToolCard, startToolCard, updateToolCard, clearToolCardsExternal, clearTerminalExternal, getWorkspaceDirPath, hasActiveCards, showWorkingHint, clearWorkingHint } from './workspace-panel.js'; @@ -590,6 +590,9 @@ async function handleRetry(): Promise { retryContent = ''; retryIterations++; }, + onThinkingStart: () => { + ensureLoadingPlaceholder(); + }, onThinking: (thinking) => { retryThinkContent = thinking; updateLastAssistantMessage(retryContent, thinking, null, getSelectedModel()); }, onContent: (content) => { retryContent = content; updateLastAssistantMessage(content, retryThinkContent || null, null, getSelectedModel()); }, onToolCallStart: (call) => { @@ -1289,6 +1292,9 @@ async function sendMessageWithAgentLoop(text: string, currentSession: ChatSessio assistantContent = ''; thinkContent = ''; }, + onThinkingStart: () => { + ensureLoadingPlaceholder(); + }, onThinking: (thinking) => { thinkContent = thinking; updateLastAssistantMessage(assistantContent, thinkContent || null, null, getSelectedModel()); diff --git a/src/renderer/index.html b/src/renderer/index.html index 309b23b..bb73c31 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -28,7 +28,7 @@
Metona Ollama - v0.16.18 + v0.16.19