From cf2572ea99cc644034184d8e8c613b5b459b6c47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=99=B4=E5=A4=A9?= Date: Mon, 15 Jun 2026 14:00:07 +0800 Subject: [PATCH] =?UTF-8?q?fix(hermes):=20=E5=85=BC=E5=AE=B9=E6=9C=80?= =?UTF-8?q?=E6=96=B0=E7=89=88=20Web=20Search=20=E7=AD=96=E7=95=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 17 +++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- scripts/dev-api.js | 18 +++++++++--------- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/src/commands/hermes.rs | 7 +++---- src-tauri/tauri.conf.json | 2 +- src/engines/hermes/pages/config.js | 2 +- src/locales/modules/engine.js | 1 - tests/hermes-web-config.test.js | 12 ++++++++---- 11 files changed, 44 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e83415f..0b58d7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,23 @@ 格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/), 版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。 +## [0.18.4] - 2026-06-15 + +### 兼容性 (Compatibility) + +- **跟进 Hermes Agent 最新版 Web Search 策略** — Hermes upstream 已回滚无 Key 的 `parallel-free` 后端;面板不再提供或写入 `parallel-free`,Parallel 搜索统一使用需要 `PARALLEL_API_KEY` 的 `parallel` +- **补充 Hermes 最新模型候选** — Z.AI / GLM、Kimi / Moonshot、Nous Portal 与聚合服务商候选模型补充 `glm-5.2`、`kimi-k2.7-code`,减少用户手动输入新版模型名的成本 + +### 测试与验证 (Testing) + +- 已通过 `node --test tests/hermes-web-config.test.js tests/hermes-config-page-ui.test.js` +- 已通过 `cd src-tauri && cargo test hermes_web_config --lib` +- 已通过 `npm run build` +- 已通过 `node -e "import('./scripts/dev-api.js').then(()=>console.log('dev-api import ok'))"` +- 已通过 `cd src-tauri && cargo fmt --check` +- 已通过 `cd src-tauri && cargo check` +- 已通过 `git diff --check` + ## [0.18.3] - 2026-06-11 ### 修复 (Fixes) diff --git a/package-lock.json b/package-lock.json index 649c0f1..31d24c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "clawpanel", - "version": "0.18.3", + "version": "0.18.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "clawpanel", - "version": "0.18.3", + "version": "0.18.4", "license": "AGPL-3.0", "dependencies": { "@tauri-apps/api": "^2.5.0", diff --git a/package.json b/package.json index c87d0cd..171966f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "clawpanel", - "version": "0.18.3", + "version": "0.18.4", "private": true, "description": "ClawPanel - OpenClaw 可视化管理面板,基于 Tauri v2 的跨平台桌面应用", "type": "module", diff --git a/scripts/dev-api.js b/scripts/dev-api.js index ea6b10a..d1dc334 100644 --- a/scripts/dev-api.js +++ b/scripts/dev-api.js @@ -40,20 +40,20 @@ const HERMES_PROVIDER_REGISTRY = [ hermesProvider('nvidia', 'NVIDIA NIM', 'api_key', 'https://integrate.api.nvidia.com/v1', 'NVIDIA_BASE_URL', ['NVIDIA_API_KEY'], 'openai_chat', 'openai', []), hermesProvider('ollama-cloud', 'Ollama Cloud', 'api_key', 'https://ollama.com/v1', 'OLLAMA_BASE_URL', ['OLLAMA_API_KEY'], 'openai_chat', 'openai', []), hermesProvider('copilot', 'GitHub Copilot (PAT)', 'api_key', 'https://api.githubcopilot.com', '', ['COPILOT_GITHUB_TOKEN', 'GH_TOKEN', 'GITHUB_TOKEN'], 'openai_chat', 'none', ['gpt-4o', 'gpt-4.1', 'claude-3.5-sonnet', 'claude-3.7-sonnet', 'claude-sonnet-4-5', 'o1', 'o1-mini', 'gemini-2.5-pro']), - hermesProvider('zai', 'Z.AI / GLM', 'api_key', 'https://api.z.ai/api/paas/v4', 'GLM_BASE_URL', ['GLM_API_KEY', 'ZAI_API_KEY', 'Z_AI_API_KEY'], 'openai_chat', 'openai', ['glm-5.1', 'glm-5', 'glm-5v-turbo', 'glm-5-turbo', 'glm-4.7', 'glm-4.5', 'glm-4.5-flash']), - hermesProvider('kimi-coding', 'Kimi / Moonshot', 'api_key', 'https://api.moonshot.ai/v1', 'KIMI_BASE_URL', ['KIMI_API_KEY'], 'openai_chat', 'openai', ['kimi-for-coding', 'kimi-k2.6', 'kimi-k2.5', 'kimi-k2-thinking', 'kimi-k2-turbo-preview', 'kimi-k2-0905-preview']), - hermesProvider('kimi-coding-cn', 'Kimi / Moonshot (China)', 'api_key', 'https://api.moonshot.cn/v1', '', ['KIMI_CN_API_KEY'], 'openai_chat', 'openai', ['kimi-for-coding', 'kimi-k2.6', 'kimi-k2.5', 'kimi-k2-thinking', 'kimi-k2-turbo-preview']), - hermesProvider('alibaba', 'Alibaba Cloud (DashScope)', 'api_key', 'https://dashscope-intl.aliyuncs.com/compatible-mode/v1', 'DASHSCOPE_BASE_URL', ['DASHSCOPE_API_KEY'], 'openai_chat', 'openai', ['qwen3.5-plus', 'qwen3-coder-plus', 'qwen3-coder-next', 'glm-5', 'glm-4.7', 'kimi-k2.5', 'MiniMax-M2.5']), + hermesProvider('zai', 'Z.AI / GLM', 'api_key', 'https://api.z.ai/api/paas/v4', 'GLM_BASE_URL', ['GLM_API_KEY', 'ZAI_API_KEY', 'Z_AI_API_KEY'], 'openai_chat', 'openai', ['glm-5.2', 'glm-5.1', 'glm-5', 'glm-5v-turbo', 'glm-5-turbo', 'glm-4.7', 'glm-4.5', 'glm-4.5-flash']), + hermesProvider('kimi-coding', 'Kimi / Moonshot', 'api_key', 'https://api.moonshot.ai/v1', 'KIMI_BASE_URL', ['KIMI_API_KEY'], 'openai_chat', 'openai', ['kimi-k2.7-code', 'kimi-for-coding', 'kimi-k2.6', 'kimi-k2.5', 'kimi-k2-thinking', 'kimi-k2-turbo-preview', 'kimi-k2-0905-preview']), + hermesProvider('kimi-coding-cn', 'Kimi / Moonshot (China)', 'api_key', 'https://api.moonshot.cn/v1', '', ['KIMI_CN_API_KEY'], 'openai_chat', 'openai', ['kimi-k2.7-code', 'kimi-for-coding', 'kimi-k2.6', 'kimi-k2.5', 'kimi-k2-thinking', 'kimi-k2-turbo-preview']), + hermesProvider('alibaba', 'Alibaba Cloud (DashScope)', 'api_key', 'https://dashscope-intl.aliyuncs.com/compatible-mode/v1', 'DASHSCOPE_BASE_URL', ['DASHSCOPE_API_KEY'], 'openai_chat', 'openai', ['qwen3.5-plus', 'qwen3-coder-plus', 'qwen3-coder-next', 'glm-5.2', 'glm-5', 'glm-4.7', 'kimi-k2.7-code', 'kimi-k2.5', 'MiniMax-M2.5']), hermesProvider('alibaba-coding-plan', 'Alibaba Cloud (Coding Plan)', 'api_key', 'https://coding-intl.dashscope.aliyuncs.com/v1', 'ALIBABA_CODING_PLAN_BASE_URL', ['ALIBABA_CODING_PLAN_API_KEY', 'DASHSCOPE_API_KEY'], 'openai_chat', 'openai', ['qwen3-coder-plus', 'qwen3-coder-next', 'qwen3.5-plus', 'qwen3.5-coder']), hermesProvider('minimax-cn', 'MiniMax (China)', 'api_key', 'https://api.minimaxi.com/v1', 'MINIMAX_CN_BASE_URL', ['MINIMAX_CN_API_KEY'], 'anthropic_messages', 'anthropic', ['MiniMax-M3', 'MiniMax-M2.7', 'MiniMax-M2.7-highspeed']), hermesProvider('xiaomi', 'Xiaomi MiMo', 'api_key', 'https://api.xiaomimimo.com/v1', 'XIAOMI_BASE_URL', ['XIAOMI_API_KEY'], 'openai_chat', 'openai', ['mimo-v2-pro', 'mimo-v2-omni', 'mimo-v2-flash']), hermesProvider('bedrock', 'AWS Bedrock', 'aws_sdk', 'https://bedrock-runtime.us-east-1.amazonaws.com', 'BEDROCK_BASE_URL', [], 'anthropic_messages', 'none', []), hermesProvider('openrouter', 'OpenRouter', 'api_key', 'https://openrouter.ai/api/v1', 'OPENAI_BASE_URL', ['OPENROUTER_API_KEY'], 'openai_chat', 'openai', [], true), hermesProvider('ai-gateway', 'Vercel AI Gateway', 'api_key', 'https://ai-gateway.vercel.sh/v1', 'AI_GATEWAY_BASE_URL', ['AI_GATEWAY_API_KEY'], 'openai_chat', 'openai', ['anthropic/claude-opus-4.6', 'anthropic/claude-sonnet-4.6', 'anthropic/claude-sonnet-4.5', 'anthropic/claude-haiku-4.5', 'openai/gpt-5', 'openai/gpt-4.1', 'openai/gpt-4.1-mini', 'google/gemini-3-pro-preview', 'google/gemini-3-flash', 'google/gemini-2.5-pro', 'google/gemini-2.5-flash', 'deepseek/deepseek-v3.2'], true), - hermesProvider('opencode-zen', 'OpenCode Zen', 'api_key', 'https://opencode.ai/zen/v1', 'OPENCODE_ZEN_BASE_URL', ['OPENCODE_ZEN_API_KEY'], 'openai_chat', 'openai', ['gpt-5.4-pro', 'gpt-5.4', 'gpt-5.3-codex', 'claude-opus-4-6', 'claude-sonnet-4-6', 'claude-haiku-4-5', 'gemini-3.1-pro', 'gemini-3-pro', 'minimax-m2.7', 'glm-5', 'kimi-k2.5', 'qwen3-coder'], true), - hermesProvider('opencode-go', 'OpenCode Go', 'api_key', 'https://opencode.ai/zen/go/v1', 'OPENCODE_GO_BASE_URL', ['OPENCODE_GO_API_KEY'], 'openai_chat', 'openai', ['glm-5.1', 'glm-5', 'kimi-k2.5', 'mimo-v2-pro', 'mimo-v2-omni', 'minimax-m2.7', 'minimax-m2.5'], true), + hermesProvider('opencode-zen', 'OpenCode Zen', 'api_key', 'https://opencode.ai/zen/v1', 'OPENCODE_ZEN_BASE_URL', ['OPENCODE_ZEN_API_KEY'], 'openai_chat', 'openai', ['gpt-5.4-pro', 'gpt-5.4', 'gpt-5.3-codex', 'claude-opus-4-6', 'claude-sonnet-4-6', 'claude-haiku-4-5', 'gemini-3.1-pro', 'gemini-3-pro', 'minimax-m2.7', 'glm-5.2', 'glm-5', 'kimi-k2.7-code', 'kimi-k2.5', 'qwen3-coder'], true), + hermesProvider('opencode-go', 'OpenCode Go', 'api_key', 'https://opencode.ai/zen/go/v1', 'OPENCODE_GO_BASE_URL', ['OPENCODE_GO_API_KEY'], 'openai_chat', 'openai', ['glm-5.2', 'glm-5.1', 'glm-5', 'kimi-k2.7-code', 'kimi-k2.5', 'mimo-v2-pro', 'mimo-v2-omni', 'minimax-m2.7', 'minimax-m2.5'], true), hermesProvider('kilocode', 'Kilo Code', 'api_key', 'https://api.kilo.ai/api/gateway', 'KILOCODE_BASE_URL', ['KILOCODE_API_KEY'], 'openai_chat', 'openai', ['anthropic/claude-opus-4.6', 'anthropic/claude-sonnet-4.6', 'openai/gpt-5.4', 'google/gemini-3-pro-preview', 'google/gemini-3-flash-preview'], true), - hermesProvider('nous', 'Nous Portal', 'oauth_device_code', 'https://inference-api.nousresearch.com/v1', '', [], 'openai_chat', 'none', ['moonshotai/kimi-k2.6', 'anthropic/claude-opus-4.7', 'anthropic/claude-sonnet-4.6', 'openai/gpt-5.4', 'google/gemini-3-pro-preview', 'qwen/qwen3.5-plus-02-15', 'minimax/minimax-m2.7', 'z-ai/glm-5.1', 'x-ai/grok-4.20-beta'], true, 'hermes auth login nous'), + hermesProvider('nous', 'Nous Portal', 'oauth_device_code', 'https://inference-api.nousresearch.com/v1', '', [], 'openai_chat', 'none', ['moonshotai/kimi-k2.6', 'moonshotai/kimi-k2.7-code', 'anthropic/claude-opus-4.7', 'anthropic/claude-sonnet-4.6', 'openai/gpt-5.4', 'google/gemini-3-pro-preview', 'qwen/qwen3.5-plus-02-15', 'minimax/minimax-m2.7', 'z-ai/glm-5.1', 'x-ai/grok-4.20-beta'], true, 'hermes auth login nous'), hermesProvider('openai-codex', 'OpenAI Codex', 'oauth_external', 'https://chatgpt.com/backend-api/codex', '', [], 'codex_responses', 'none', ['gpt-5.5', 'gpt-5.4-mini', 'gpt-5.4', 'gpt-5.3-codex', 'gpt-5.2-codex', 'gpt-5.1-codex-max', 'gpt-5.1-codex-mini'], false, 'hermes auth login openai-codex'), hermesProvider('qwen-oauth', 'Qwen OAuth', 'oauth_external', 'https://dashscope-intl.aliyuncs.com/compatible-mode/v1', '', [], 'openai_chat', 'none', ['qwen3.5-plus', 'qwen3-coder-plus', 'qwen3-coder-next'], false, 'hermes auth login qwen-oauth'), hermesProvider('google-gemini-cli', 'Google Gemini (OAuth)', 'oauth_external', 'https://generativelanguage.googleapis.com/v1beta/openai', '', [], 'openai_chat', 'none', ['gemini-2.5-pro', 'gemini-2.5-flash'], false, 'hermes auth login google-gemini-cli'), @@ -3868,7 +3868,7 @@ const HERMES_TERMINAL_MODAL_MODES = new Set(['auto', 'managed', 'direct']) const HERMES_TERMINAL_VERCEL_RUNTIMES = new Set(['node24', 'node22', 'python3.13']) const HERMES_BROWSER_ENGINES = new Set(['auto', 'lightpanda', 'chrome']) const HERMES_BROWSER_DIALOG_POLICIES = new Set(['must_respond', 'auto_dismiss', 'auto_accept']) -const HERMES_WEB_BACKENDS = new Set(['tavily', 'firecrawl', 'parallel-free', 'parallel', 'exa', 'searxng', 'brave', 'brave_free', 'ddgs', 'xai', 'native']) +const HERMES_WEB_BACKENDS = new Set(['tavily', 'firecrawl', 'parallel', 'exa', 'searxng', 'brave', 'brave_free', 'ddgs', 'xai', 'native']) const HERMES_LSP_WAIT_MODES = new Set(['document', 'full']) const HERMES_LSP_INSTALL_STRATEGIES = new Set(['auto', 'manual', 'off']) const HERMES_MODEL_CATALOG_DEFAULT_URL = 'https://hermes-agent.nousresearch.com/docs/api/model-catalog.json' @@ -4038,7 +4038,7 @@ function normalizeHermesWebBackend(value, key, strict = false) { const backend = String(value ?? '').trim().toLowerCase() if (!backend) return '' if (HERMES_WEB_BACKENDS.has(backend)) return backend - if (strict) throw new Error(`${key} 必须为空或 tavily、firecrawl、parallel-free、parallel、exa、searxng、brave、brave_free、ddgs、xai、native`) + if (strict) throw new Error(`${key} 必须为空或 tavily、firecrawl、parallel、exa、searxng、brave、brave_free、ddgs、xai、native`) return '' } diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 0021115..92f76c3 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -366,7 +366,7 @@ dependencies = [ [[package]] name = "clawpanel" -version = "0.18.3" +version = "0.18.4" dependencies = [ "base64 0.22.1", "chrono", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index fbeb040..aefca6d 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clawpanel" -version = "0.18.3" +version = "0.18.4" edition = "2021" description = "ClawPanel - OpenClaw 可视化管理面板" authors = ["qingchencloud"] diff --git a/src-tauri/src/commands/hermes.rs b/src-tauri/src/commands/hermes.rs index b59c0a9..b9f085e 100644 --- a/src-tauri/src/commands/hermes.rs +++ b/src-tauri/src/commands/hermes.rs @@ -8045,7 +8045,6 @@ fn normalize_hermes_web_backend( backend.as_str(), "tavily" | "firecrawl" - | "parallel-free" | "parallel" | "exa" | "searxng" @@ -8058,7 +8057,7 @@ fn normalize_hermes_web_backend( return Ok(backend); } if strict { - Err(format!("{key} 必须为空或 tavily、firecrawl、parallel-free、parallel、exa、searxng、brave、brave_free、ddgs、xai、native")) + Err(format!("{key} 必须为空或 tavily、firecrawl、parallel、exa、searxng、brave、brave_free、ddgs、xai、native")) } else { Ok(String::new()) } @@ -19454,7 +19453,7 @@ streaming: merge_hermes_web_config( &mut config, &json!({ - "webBackend": "parallel-free", + "webBackend": "parallel", "webSearchBackend": "exa", "webExtractBackend": "native", }), @@ -19463,7 +19462,7 @@ streaming: assert_eq!(config["model"]["provider"].as_str(), Some("anthropic")); assert_eq!(config["streaming"]["enabled"].as_bool(), Some(true)); - assert_eq!(config["web"]["backend"].as_str(), Some("parallel-free")); + assert_eq!(config["web"]["backend"].as_str(), Some("parallel")); assert_eq!(config["web"]["search_backend"].as_str(), Some("exa")); assert_eq!(config["web"]["extract_backend"].as_str(), Some("native")); assert_eq!(config["web"]["custom_flag"].as_str(), Some("keep-web")); diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 5bed7ae..9cda4cb 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/tauri-apps/tauri/dev/crates/tauri-config-schema/schema.json", "productName": "ClawPanel", - "version": "0.18.3", + "version": "0.18.4", "identifier": "ai.openclaw.clawpanel", "build": { "frontendDist": "../dist", diff --git a/src/engines/hermes/pages/config.js b/src/engines/hermes/pages/config.js index 15f3e00..4809b39 100644 --- a/src/engines/hermes/pages/config.js +++ b/src/engines/hermes/pages/config.js @@ -409,7 +409,7 @@ const TERMINAL_MODAL_MODES = ['auto', 'managed', 'direct'] const TERMINAL_VERCEL_RUNTIMES = ['node24', 'node22', 'python3.13'] const BROWSER_ENGINES = ['auto', 'lightpanda', 'chrome'] const BROWSER_DIALOG_POLICIES = ['must_respond', 'auto_dismiss', 'auto_accept'] -const WEB_BACKENDS = ['', 'tavily', 'firecrawl', 'parallel-free', 'parallel', 'exa', 'searxng', 'brave', 'brave_free', 'ddgs', 'xai', 'native'] +const WEB_BACKENDS = ['', 'tavily', 'firecrawl', 'parallel', 'exa', 'searxng', 'brave', 'brave_free', 'ddgs', 'xai', 'native'] const LSP_WAIT_MODES = ['document', 'full'] const LSP_INSTALL_STRATEGIES = ['auto', 'manual', 'off'] const STT_PROVIDERS = ['auto', 'local', 'groq', 'openai', 'mistral'] diff --git a/src/locales/modules/engine.js b/src/locales/modules/engine.js index 55cd870..59b1f3c 100644 --- a/src/locales/modules/engine.js +++ b/src/locales/modules/engine.js @@ -738,7 +738,6 @@ export default { hermesWebConfigBackend_auto: _('自动选择', 'Auto select', '自動選擇'), hermesWebConfigBackend_tavily: _('Tavily', 'Tavily', 'Tavily'), hermesWebConfigBackend_firecrawl: _('Firecrawl', 'Firecrawl', 'Firecrawl'), - 'hermesWebConfigBackend_parallel-free': _('Parallel Free(免 Key)', 'Parallel Free (no key)', 'Parallel Free(免 Key)'), hermesWebConfigBackend_parallel: _('Parallel', 'Parallel', 'Parallel'), hermesWebConfigBackend_exa: _('Exa', 'Exa', 'Exa'), hermesWebConfigBackend_searxng: _('SearXNG', 'SearXNG', 'SearXNG'), diff --git a/tests/hermes-web-config.test.js b/tests/hermes-web-config.test.js index 2eeb692..9fc4f1a 100644 --- a/tests/hermes-web-config.test.js +++ b/tests/hermes-web-config.test.js @@ -20,13 +20,13 @@ test('Hermes Web 工具配置读取会回显 YAML 字段', () => { const values = buildHermesWebConfigValues({ web: { backend: 'tavily', - search_backend: 'parallel-free', + search_backend: 'parallel', extract_backend: 'firecrawl', }, }) assert.equal(values.webBackend, 'tavily') - assert.equal(values.webSearchBackend, 'parallel-free') + assert.equal(values.webSearchBackend, 'parallel') assert.equal(values.webExtractBackend, 'firecrawl') }) @@ -41,14 +41,14 @@ test('Hermes Web 工具配置保存会保留未知字段并写入上游结构', }, streaming: { enabled: true }, }, { - webBackend: 'parallel-free', + webBackend: 'parallel', webSearchBackend: 'exa', webExtractBackend: 'native', }) assert.deepEqual(next.model, { provider: 'anthropic' }) assert.deepEqual(next.streaming, { enabled: true }) - assert.equal(next.web.backend, 'parallel-free') + assert.equal(next.web.backend, 'parallel') assert.equal(next.web.search_backend, 'exa') assert.equal(next.web.extract_backend, 'native') assert.equal(next.web.custom_flag, 'keep-web') @@ -87,4 +87,8 @@ test('Hermes Web 工具配置保存会拒绝非法后端', () => { () => mergeHermesWebConfig({}, { webExtractBackend: 'unsafe' }), /web\.extract_backend/, ) + assert.throws( + () => mergeHermesWebConfig({}, { webBackend: 'parallel-free' }), + /web\.backend/, + ) })