diff --git a/README.md b/README.md
index 0991fce..275b4e8 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@
-
+
@@ -231,7 +231,7 @@ npm start
ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ npm run dist
```
-产出:`release/Metona Ollama Setup v0.10.1.exe`
+产出:`release/Metona Ollama Setup v0.10.2.exe`
## 🛠️ 常用命令
@@ -454,7 +454,7 @@ npm start
ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ npm run dist
```
-Output: `release/Metona Ollama Setup v0.10.1.exe`
+Output: `release/Metona Ollama Setup v0.10.2.exe`
## 🛠️ Common Commands
diff --git a/docs/BUILD.md b/docs/BUILD.md
index c938fed..e9ae18c 100644
--- a/docs/BUILD.md
+++ b/docs/BUILD.md
@@ -91,7 +91,7 @@ npm run dist # NSIS 安装包
| 文件 | 说明 |
|------|------|
-| `release/Metona Ollama Setup v0.10.1.exe` | NSIS 安装包(可选目录、创建快捷方式) |
+| `release/Metona Ollama Setup v0.10.2.exe` | NSIS 安装包(可选目录、创建快捷方式) |
> 仅提供 NSIS 安装包。
@@ -103,7 +103,7 @@ npm run dist # NSIS 安装包
TOKEN="your_access_token"
curl -X POST "https://gitee.com/api/v5/repos/thzxx/metona-ollama-desktop/releases?access_token=$TOKEN" \
-H "Content-Type: application/json" \
- -d '{"tag_name":"v0.10.1","name":"v0.10.1","body":"Release notes","target_commitish":"master"}'
+ -d '{"tag_name":"v0.10.2","name":"v0.10.2","body":"Release notes","target_commitish":"master"}'
```
### 上传附件
@@ -112,7 +112,7 @@ curl -X POST "https://gitee.com/api/v5/repos/thzxx/metona-ollama-desktop/release
TOKEN="your_access_token"
RELEASE_ID=""
curl -X POST "https://gitee.com/api/v5/repos/thzxx/metona-ollama-desktop/releases/$RELEASE_ID/attach_files?access_token=$TOKEN" \
- -H "Content-Type: multipart/form-data" -F "file=@release/Metona Ollama Setup v0.10.1.exe"
+ -H "Content-Type: multipart/form-data" -F "file=@release/Metona Ollama Setup v0.10.2.exe"
```
## 故障排查
diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md
index 1e12e78..a705a81 100644
--- a/docs/DEVELOPMENT.md
+++ b/docs/DEVELOPMENT.md
@@ -1,6 +1,6 @@
# Metona Ollama Desktop — 开发规范
-> 版本: v0.10.1 | 更新: 2026-06-05 | 维护: 项目团队
+> 版本: v0.10.2 | 更新: 2026-06-05 | 维护: 项目团队
---
diff --git a/package-lock.json b/package-lock.json
index dd5edbd..0c2d44c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "metona-ollama-desktop",
- "version": "0.10.1",
+ "version": "0.10.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "metona-ollama-desktop",
- "version": "0.10.1",
+ "version": "0.10.2",
"license": "MIT",
"dependencies": {
"sql.js": "^1.11.0"
diff --git a/package.json b/package.json
index 2dd85b5..e82ff10 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "metona-ollama-desktop",
- "version": "0.10.1",
+ "version": "0.10.2",
"description": "Metona Ollama - TypeScript + Electron 桌面 AI 聊天客户端",
"main": "dist/main/main.js",
"author": "thzxx",
@@ -48,7 +48,7 @@
"requestedExecutionLevel": "asInvoker"
},
"nsis": {
- "artifactName": "Metona Ollama Setup v0.10.1.${ext}",
+ "artifactName": "Metona Ollama Setup v0.10.2.${ext}",
"oneClick": false,
"allowToChangeInstallationDirectory": true,
"createDesktopShortcut": true,
diff --git a/release-notes.md b/release-notes.md
index 294e738..08aa363 100644
--- a/release-notes.md
+++ b/release-notes.md
@@ -4,7 +4,7 @@
## 项目简介
-Metona Ollama Desktop 是一个基于 **Ollama** 的本地 AI 桌面客户端,采用 TypeScript + Electron 构建。所有 AI 推理均在本地完成,数据不离开本机。支持 ReAct Agent Loop、38 个内置工具、智能记忆系统、MCP 协议扩展、三引擎联网搜索、浏览器控制等能力。
+Metona Ollama Desktop 是一个基于 **Ollama** 的本地 AI 桌面客户端,采用 TypeScript + Electron 构建。所有 AI 推理均在本地完成,数据不离开本机。支持 ReAct Agent Loop、38 个内置工具、智能记忆系统、MCP 协议扩展、四引擎联网搜索、浏览器控制等能力。
---
@@ -24,7 +24,7 @@ Metona Ollama Desktop 是一个基于 **Ollama** 的本地 AI 桌面客户端,
|------|------|------|
| 文件系统 | 17 | read_file, write_file, list_directory, search_files, create_directory, delete_file, move_file, copy_file, append_file, edit_file, get_file_info, tree, download_file, diff_files, replace_in_files, read_multiple_files, compress |
| 命令执行 | 1 | run_command(实时流式输出,工作空间终端集成) |
-| 联网搜索 | 2 | web_search(Bing + 百度 + Google 三引擎), web_fetch |
+| 联网搜索 | 2 | web_search(Bing + 百度 + DuckDuckGo + Google 四引擎), web_fetch |
| 浏览器控制 | 8 | browser_open, browser_screenshot, browser_evaluate, browser_extract, browser_click, browser_type, browser_scroll, browser_close |
| Git | 1 | git(init/clone/add/commit/push/pull/diff/log/status/branch/checkout/merge/stash/reset/tag/remote) |
| 记忆管理 | 4 | memory_search, memory_add, memory_replace, memory_remove |
@@ -43,10 +43,10 @@ Metona Ollama Desktop 是一个基于 **Ollama** 的本地 AI 桌面客户端,
- JSON-RPC 2.0 over stdio 标准协议
- 动态工具发现与执行
- Shadowing 防护(防止 MCP 工具覆盖内置工具名)
-- 30 秒请求超时保护
+- 60 秒请求超时保护
-### 🔍 三引擎联网搜索
-- Bing + 百度 + Google 三引擎聚合
+### 🔍 四引擎联网搜索
+- Bing + 百度 + DuckDuckGo + Google 四引擎聚合
- web_search 返回标题、URL、摘要(默认 15 条)
- web_fetch 支持网页内容抓取(默认不截断)
- 工具链规则:搜索后自动引导抓取详情
diff --git a/src/main/ipc.ts b/src/main/ipc.ts
index 3214c21..879972e 100644
--- a/src/main/ipc.ts
+++ b/src/main/ipc.ts
@@ -49,9 +49,10 @@ import {
handleCompress
} from './tool-handlers.js';
import { browserOpen, browserScreenshot, browserEvaluate, browserExtract, browserClick, browserType, browserScroll, browserClose } from './browser.js';
-import { startServer, stopServer, stopAllServers, callTool, getAllTools, getServerStatuses, refreshTools } from './mcp-manager.js';
+import { startServer, stopServer, stopAllServers, callTool, getAllTools, getServerStatuses, refreshTools, setMCPTimeout } from './mcp-manager.js';
import { getAllowedDirs, getBlockedDirs, setAllowedDirs } from './tool-security.js';
import { startProcess, killProcess, getWorkspaceDir, setWorkspaceDir, listWorkspaceDir } from './workspace.js';
+import { setHTTPTimeout } from './tool-handlers-system.js';
/** 工具结果摘要(用于日志面板) */
function summarizeResult(toolName: string, result: Record): string {
@@ -222,6 +223,16 @@ export async function setupIPC(): Promise {
setAllowedDirs(dirs);
});
+ ipcMain.handle('tool:setTimeouts', (_, timeouts: { http?: number; mcp?: number }) => {
+ if (typeof timeouts.http === 'number' && timeouts.http > 0) {
+ setHTTPTimeout(timeouts.http);
+ }
+ if (typeof timeouts.mcp === 'number' && timeouts.mcp > 0) {
+ setMCPTimeout(timeouts.mcp);
+ }
+ return { success: true };
+ });
+
// ── Workspace IPC ──
// 获取工作空间目录
diff --git a/src/main/mcp-manager.ts b/src/main/mcp-manager.ts
index f350dc0..9a5ca42 100644
--- a/src/main/mcp-manager.ts
+++ b/src/main/mcp-manager.ts
@@ -52,7 +52,13 @@ interface MCPServerInstance {
// ─── 状态 ───
const servers = new Map();
-const MCP_TIMEOUT = 30000; // 30 秒超时
+/** MCP 请求超时(毫秒),默认 60s,可通过 IPC 设置 */
+let MCP_TIMEOUT = 60_000;
+
+/** 设置 MCP 请求超时(毫秒) */
+export function setMCPTimeout(ms: number): void {
+ MCP_TIMEOUT = Math.max(10_000, Math.min(300_000, ms)); // 10s ~ 300s
+}
// ─── JSON-RPC 通信 ───
diff --git a/src/main/menu.ts b/src/main/menu.ts
index aa9b795..0c0a0f2 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.10.1',
+ message: 'Metona Ollama Desktop v0.10.2',
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 7da1ce3..85209c2 100644
--- a/src/main/preload.ts
+++ b/src/main/preload.ts
@@ -19,7 +19,8 @@ contextBridge.exposeInMainWorld('metonaDesktop', {
tool: {
execute: (toolName: string, args: Record) => ipcRenderer.invoke('tool:execute', toolName, args),
getConfig: () => ipcRenderer.invoke('tool:getConfig'),
- setAllowedDirs: (dirs: string[]) => ipcRenderer.invoke('tool:setAllowedDirs', dirs)
+ setAllowedDirs: (dirs: string[]) => ipcRenderer.invoke('tool:setAllowedDirs', dirs),
+ setTimeouts: (timeouts: { http?: number; mcp?: number }) => ipcRenderer.invoke('tool:setTimeouts', timeouts)
},
notify: (title: string, body: string) => ipcRenderer.invoke('notify', title, body),
window: {
diff --git a/src/main/tool-handlers-system.ts b/src/main/tool-handlers-system.ts
index 8d07138..3e9a43d 100644
--- a/src/main/tool-handlers-system.ts
+++ b/src/main/tool-handlers-system.ts
@@ -118,8 +118,13 @@ export function killToolProcess(): boolean {
-/** HTTP 请求超时(毫秒) */
-const HTTP_TIMEOUT = 15_000;
+/** HTTP 请求超时(毫秒),默认 30s,可通过 IPC 设置 */
+let HTTP_TIMEOUT = 30_000;
+
+/** 设置 HTTP 请求超时(毫秒) */
+export function setHTTPTimeout(ms: number): void {
+ HTTP_TIMEOUT = Math.max(5000, Math.min(300_000, ms)); // 5s ~ 300s
+}
// ── LRU 搜索缓存 ──────────────────────────────────
interface CacheEntry { results: Array<{ title: string; url: string; snippet: string; engine: string; reachable?: boolean }>; time: number; }
diff --git a/src/renderer/components/settings-modal.ts b/src/renderer/components/settings-modal.ts
index 4f09f1c..c0eb4d8 100644
--- a/src/renderer/components/settings-modal.ts
+++ b/src/renderer/components/settings-modal.ts
@@ -64,6 +64,49 @@ export function initSettingsModal(): void {
}, 500);
document.querySelector('#inputMaxTurns')!.addEventListener('input', saveMaxTurns);
+ // ── v5.2 超时设置 ──
+ const inputHttpTimeout = document.querySelector('#inputHttpTimeout') as HTMLInputElement;
+ const inputStreamTimeout = document.querySelector('#inputStreamTimeout') as HTMLInputElement;
+ const inputMCPTimeout = document.querySelector('#inputMCPTimeout') as HTMLInputElement;
+
+ const applyTimeouts = async () => {
+ const db = state.get(KEYS.DB);
+ const bridge = window.metonaDesktop;
+
+ // HTTP 超时(秒→毫秒)
+ const httpVal = inputHttpTimeout.value.trim();
+ const httpSec = httpVal ? parseInt(httpVal) : 0;
+ if (db) await db.saveSetting('httpTimeout', httpSec > 0 ? httpSec : 0);
+ if (bridge?.tool?.setTimeouts) {
+ await bridge.tool.setTimeouts({ http: httpSec > 0 ? httpSec * 1000 : 30_000 });
+ }
+ logSetting('HTTP 超时', httpSec > 0 ? `${httpSec}s` : '默认(30s)');
+
+ // 流式超时(秒→毫秒)
+ const streamVal = inputStreamTimeout.value.trim();
+ const streamSec = streamVal ? parseInt(streamVal) : 0;
+ state.set('streamTimeout', streamSec > 0 ? streamSec * 1000 : 300_000);
+ if (db) await db.saveSetting('streamTimeout', streamSec > 0 ? streamSec : 0);
+ logSetting('流式超时', streamSec > 0 ? `${streamSec}s` : '默认(300s)');
+
+ // MCP 超时(秒→毫秒)
+ const mcpVal = inputMCPTimeout.value.trim();
+ const mcpSec = mcpVal ? parseInt(mcpVal) : 0;
+ if (db) await db.saveSetting('mcpTimeout', mcpSec > 0 ? mcpSec : 0);
+ if (bridge?.tool?.setTimeouts) {
+ await bridge.tool.setTimeouts({ mcp: mcpSec > 0 ? mcpSec * 1000 : 60_000 });
+ }
+ logSetting('MCP 超时', mcpSec > 0 ? `${mcpSec}s` : '默认(60s)');
+ };
+
+ const saveHttpTimeout = debounce(applyTimeouts, 500);
+ const saveStreamTimeout = debounce(applyTimeouts, 500);
+ const saveMCPTimeout = debounce(applyTimeouts, 500);
+
+ inputHttpTimeout.addEventListener('input', saveHttpTimeout);
+ inputStreamTimeout.addEventListener('input', saveStreamTimeout);
+ inputMCPTimeout.addEventListener('input', saveMCPTimeout);
+
// ── v5.0 MCP 服务器管理 ──
async function renderMCPServerList(): Promise {
const { getMCPServers, getMCPServerStatuses, toggleMCPServer, removeMCPServer } = await import('../services/mcp-client.js');
@@ -259,12 +302,31 @@ export function openSettingsModal(): void {
populateMemoryEmbedModels();
populateSubAgentModels();
updateMemoryVectorStatus();
+ loadTimeoutSettings();
}
export function closeSettingsModal(): void {
settingsModalEl.style.display = 'none';
}
+/** 加载已保存的超时设置到输入框 */
+async function loadTimeoutSettings(): Promise {
+ const db = state.get(KEYS.DB);
+ if (!db) return;
+
+ const httpTimeout = await db.getSetting('httpTimeout', 0);
+ const streamTimeout = await db.getSetting('streamTimeout', 0);
+ const mcpTimeout = await db.getSetting('mcpTimeout', 0);
+
+ const inputHttp = document.querySelector('#inputHttpTimeout') as HTMLInputElement;
+ const inputStream = document.querySelector('#inputStreamTimeout') as HTMLInputElement;
+ const inputMCP = document.querySelector('#inputMCPTimeout') as HTMLInputElement;
+
+ if (inputHttp) inputHttp.value = httpTimeout > 0 ? String(httpTimeout) : '';
+ if (inputStream) inputStream.value = streamTimeout > 0 ? String(streamTimeout) : '';
+ if (inputMCP) inputMCP.value = mcpTimeout > 0 ? String(mcpTimeout) : '';
+}
+
async function exportAllSessions(): Promise {
const db = state.get(KEYS.DB);
if (!db) return;
diff --git a/src/renderer/index.html b/src/renderer/index.html
index 2049f90..20cd891 100644
--- a/src/renderer/index.html
+++ b/src/renderer/index.html
@@ -28,7 +28,7 @@
Agent Loop 最大工具调用轮数。默认 85 轮,复杂任务可能需要更多轮次。
+
+
+
调整各通信层超时时长。留空则使用默认值(HTTP:30s / 流式:300s / MCP:60s)。设为 0 可禁用超时。
+
+
+
+
+
web_search / web_fetch 等 HTTP 请求的超时
+
+
+
+
+
Ollama 单轮流式响应的最大等待时间
+
+
+
+
+
MCP Server JSON-RPC 请求的超时(需重启 MCP 服务器生效)
+
+
+
@@ -396,7 +417,7 @@
💬 聊天功能
- 流式回复 — 实时打字效果,随时点 ■ 停止
- Think 推理 — 下方 Think 按钮切换,让模型展示深度思考过程(需模型支持,如 Qwen3)
- 上下文长度自动检测 — 切换模型时自动从模型元数据获取实际支持的上下文长度(如 Qwen3.6 27B = 262,144 tokens),无需手动配置
- 多模态 — 上传图片,模型需支持 Vision 能力。图片自动压缩至合适分辨率,节省上下文
- 文件分析 — 支持 50+ 种文本/代码格式,单文件 ≤500KB,自动剥离注释并按上下文预算智能截断
- AI 回复顶部 — 每条 AI 回复上方展示 📋 系统提示词折叠卡片,可点击查看实际发送给模型的完整上下文
🔧 Tool Calling(始终开启)
- 所有消息均通过 Agent Loop 自主调用本地工具,像一个本地 Agent,无普通聊天模式
- 38 个工具,分为 8 类:
- 文件系统(17 个):read_file / write_file / list_directory / search_files / create_directory / delete_file / move_file / copy_file / append_file / edit_file / get_file_info / tree / download_file / diff_files / replace_in_files / read_multiple_files / compress
- 命令执行(1 个):run_command(实时流式输出,支持自动/需确认/禁用三种模式,可配置超时)
- 联网搜索(2 个):web_search(4引擎并行:Bing+百度+DuckDuckGo+Google,结果缓存5分钟,URL可达性预检)/ web_fetch(自动重试+移动端UA切换+SPA页面自动升级到浏览器渲染)
- Git(1 个):git(16 个子操作,push/pull/clone 内置60-120s超时保护)
- 浏览器控制(8 个):browser_open / browser_screenshot / browser_evaluate / browser_extract / browser_click / browser_type / browser_scroll / browser_close
- 记忆 & 会话 & Skill(8 个):memory_search / memory_add / memory_replace / memory_remove / session_list / session_read / skill_list / skill_view
- 子代理(1 个):spawn_task
- read_multiple_files 支持并行读取,diff_files 优先调用系统 diff/git diff 命令
- search_files 支持正则表达式搜索(use_regex=true)
- list_directory 支持 offset 分页,大目录可分页读取
- 仅
run_command(命令执行)支持三模式切换:自动/需确认/禁用 - 危险命令(
rm -rf、mkfs、反弹 shell 等)和系统路径(/etc、~/.ssh 等)被自动拦截 - 工具调用以可视化卡片展示状态(pending → running → success/error),在工作空间🔧工具页签中显示
- 默认最大 85 轮工具调用循环,上下文使用率>80%时自动缩减到3轮
- 独立工具自动并行执行(16个只读工具加入并行白名单),有依赖关系的工具串行执行
- ⚠️ 需要模型支持 Tool Calling(推荐 Qwen3、Llama 3.1+、Mistral)
🧠 Agent 记忆系统
- 设置中开启后,AI 从对话中自动提取关键信息(事实/偏好/规则),跨会话持续积累
- 增量提取:长对话中每 20 轮自动触发轻量级记忆提取,不等到对话结束
- 对话结束时自动触发完整记忆提取
- 新对话时自动检索相关记忆注入上下文,让 AI "记住"你
- 点击顶部 🧠 按钮打开记忆面板:搜索、筛选、编辑、删除
- 记忆容量上限 500 条,超限时自动清理低价值条目(规则类型受保护)
- 向量语义搜索:在设置中选择嵌入模型后,支持语义级别记忆检索(更精准)
- 未选择嵌入模型时,使用关键词匹配检索记忆
- AI 可通过 memory 工具主动管理记忆
- 记忆写入前自动安全扫描(prompt injection / 敏感信息 / 不可见字符检测)
- 记忆存储在本地 SQLite,不会上传到任何服务器
-
🤖 Agent Loop v0.10.1 增强
- 智能上下文压缩:滑动窗口 + LLM结构化JSON摘要,超过50%上下文窗口自动触发
- 流式调用超时保护:可配置超时(默认180s),Ollama 卡死不再永久阻塞
- Final Answer 智能检测:多模式匹配(Final Answer/最终答案/最终回答/总结),避免漏检或误触
- Token 感知迭代预算:上下文使用率>80%时自动缩减剩余轮次到3轮
- 工具缓存 TTL:搜索5分钟/网页10分钟/文件30分钟,过期自动刷新
- 自动子任务拆解:检测"同时/分别/以及"等并行关键词,自动 spawn 子代理并行处理
- 旧工具结果自动截断:超过10轮的工具结果自动截断到500字符,控制上下文膨胀
+
🤖 Agent Loop v0.10.2 增强
- 智能上下文压缩:滑动窗口 + LLM结构化JSON摘要,超过50%上下文窗口自动触发
- 流式调用超时保护:可配置超时(默认300s),Ollama 卡死不再永久阻塞
- HTTP/MCP 超时可配:设置面板可分别调整 HTTP(默认30s)和 MCP(默认60s)超时
- Final Answer 智能检测:多模式匹配(Final Answer/最终答案/最终回答/总结),避免漏检或误触
- Token 感知迭代预算:上下文使用率>80%时自动缩减剩余轮次到3轮
- 工具缓存 TTL:搜索5分钟/网页10分钟/文件30分钟,过期自动刷新
- 自动子任务拆解:检测"同时/分别/以及"等并行关键词,自动 spawn 子代理并行处理
- 旧工具结果自动截断:超过10轮的工具结果自动截断到500字符,控制上下文膨胀
🔌 MCP(Model Context Protocol)
- 支持连接外部 MCP Server,动态扩展工具能力
- 设置面板可添加/启用/禁用/删除 MCP 服务器
- MCP 工具以
mcp_{server}_{tool} 前缀注册,与内置工具统一调度 - 启动时自动连接已启用的 MCP 服务器
📋 自定义文件(SOUL.md / AGENT.md / USER.md)
- 在工作空间目录创建以下文件即可自定义 AI 行为,修改后下一轮对话立即生效
- SOUL.md — AI 身份、性格、行为准则(永远不可被压缩,注入为最高优先级系统提示词)
- AGENT.md — 工具调用规则、链式调用模式、核心约束(内置精简默认版,可通过工作空间覆盖)
- USER.md — 用户画像:技术栈、偏好、习惯等个人信息,AI 在对话中自动参考
- 可在 AI 回复顶部的 📋 系统提示词卡片中查看实际注入的完整上下文
- 删除工作空间中的文件即可恢复为内置默认版本
🎯 技能自动生成
- AI 完成包含 2+ 工具调用的复杂任务后,自动从执行轨迹中提取可复用技能
- 语义匹配:配置嵌入模型后,根据用户消息的语义自动匹配最相关技能
- 参数自优化:同一技能链多次执行后,自动合并高频参数提示,越用越精准
- 技能衰减:30 天未使用的技能自动降权,90 天几乎忽略
- 链合并:相同前缀(前 2 步相同)的多个技能自动合并为抽象技能
- AI 可通过
skill_list(渐进式摘要)和 skill_view(完整工具链)查看技能 - 技能存储在本地 SQLite,不会上传到任何服务器
diff --git a/src/renderer/main.ts b/src/renderer/main.ts
index bb7ff2a..bb37e10 100644
--- a/src/renderer/main.ts
+++ b/src/renderer/main.ts
@@ -362,6 +362,30 @@ async function init(): Promise
{
state.set('maxTurns', maxTurns);
(document.querySelector('#inputMaxTurns') as HTMLInputElement).value = String(maxTurns);
+ // ── v5.2 超时设置 ──
+ const httpTimeout = await db.getSetting('httpTimeout', 0);
+ const streamTimeout = await db.getSetting('streamTimeout', 0);
+ const mcpTimeout = await db.getSetting('mcpTimeout', 0);
+
+ // 应用流式超时到 state(agent-engine 会读取)
+ state.set('streamTimeout', streamTimeout > 0 ? streamTimeout * 1000 : 300_000);
+
+ // 应用 HTTP 和 MCP 超时到主进程
+ const bridge = window.metonaDesktop;
+ if (bridge?.tool?.setTimeouts) {
+ await bridge.tool.setTimeouts({
+ http: httpTimeout > 0 ? httpTimeout * 1000 : 30_000,
+ mcp: mcpTimeout > 0 ? mcpTimeout * 1000 : 60_000,
+ });
+ }
+
+ // 恢复输入框显示值
+ if (document.querySelector('#inputHttpTimeout')) {
+ (document.querySelector('#inputHttpTimeout') as HTMLInputElement).value = httpTimeout > 0 ? String(httpTimeout) : '';
+ (document.querySelector('#inputStreamTimeout') as HTMLInputElement).value = streamTimeout > 0 ? String(streamTimeout) : '';
+ (document.querySelector('#inputMCPTimeout') as HTMLInputElement).value = mcpTimeout > 0 ? String(mcpTimeout) : '';
+ }
+
// ── Tool Calling 设置 ──
const toolCallingEnabled = await db.getSetting('toolCallingEnabled', true);
const runCommandMode = await db.getSetting('runCommandMode', 'confirm') as 'auto' | 'confirm' | 'disabled';
diff --git a/src/renderer/public/SOUL.md b/src/renderer/public/SOUL.md
index 11fa2c4..b3c9e8d 100644
--- a/src/renderer/public/SOUL.md
+++ b/src/renderer/public/SOUL.md
@@ -6,7 +6,7 @@
- **名称**: Metona
- **角色**: Metona Ollama 桌面 AI 助手
-- **核心能力**: ReAct Agent Loop v0.10.1 + 38 个增强工具 + 记忆系统 + 技能自动生成 + 智能上下文压缩
+- **核心能力**: ReAct Agent Loop v0.10.2 + 38 个增强工具 + 记忆系统 + 技能自动生成 + 智能上下文压缩
## 性格与语气
diff --git a/src/renderer/services/agent-engine.ts b/src/renderer/services/agent-engine.ts
index 1de08ad..01aa053 100644
--- a/src/renderer/services/agent-engine.ts
+++ b/src/renderer/services/agent-engine.ts
@@ -646,7 +646,7 @@ export async function runAgentLoop(
state.set(KEYS.ABORT_CONTROLLER, abortController);
// 流式调用超时保护(可配置,0 = 禁用超时)
- const STREAM_TIMEOUT_MS = state.get('streamTimeout', 180_000); // 默认 180s
+ const STREAM_TIMEOUT_MS = state.get('streamTimeout', 300_000); // 默认 300s(可在设置中配置,0=禁用)
let streamTimer: ReturnType | null = null;
if (STREAM_TIMEOUT_MS > 0) {
streamTimer = setTimeout(() => {
diff --git a/src/renderer/services/sub-agent.ts b/src/renderer/services/sub-agent.ts
index 2a2aea0..bfe1a0f 100644
--- a/src/renderer/services/sub-agent.ts
+++ b/src/renderer/services/sub-agent.ts
@@ -1,5 +1,5 @@
/**
- * SubAgent - 子代理委派系统 (v0.10.1 增强版)
+ * SubAgent - 子代理委派系统 (v0.10.2 增强版)
* 子代理拥有受限工具集(只读),可独立完成调研/搜索/分析类任务
* 主 Agent 通过 spawn_task 工具并行委派多个子代理
*/