From 081ad4af252cb8f6943ca6699bccca7f565bec09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=99=B4=E5=A4=A9?= Date: Thu, 14 May 2026 01:24:59 +0800 Subject: [PATCH] fix(hermes): prefer /v1/runs over /v1/responses to reuse session_id (#275) - /v1/responses ignores body.session_id and generates a fresh server-side session for each request, causing the Hermes sessions list to grow by one entry per ClawPanel message. - /v1/runs honors body.session_id directly, so reversing the call priority (try /v1/runs first; fall back to /v1/responses on HTTP 404 for older Hermes Agent builds) lets sessions group naturally without breaking backward compatibility. --- scripts/dev-api.js | 12 +++++++++--- src-tauri/src/commands/hermes.rs | 19 +++++++++++++------ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/scripts/dev-api.js b/scripts/dev-api.js index 9a4cb91..cf95596 100644 --- a/scripts/dev-api.js +++ b/scripts/dev-api.js @@ -8617,9 +8617,8 @@ async function _handleHermesAgentRunStream(req, res, args = {}) { if (args.conversationHistory) payload.conversation_history = args.conversationHistory if (args.instructions) payload.instructions = args.instructions - const handledByResponses = await _tryHermesResponsesStream(gwUrl, apiKey, payload, args, controller, res) - if (handledByResponses) return - + // 优先 /v1/runs:支持 body.session_id 复用,避免 Hermes session 暴增(#275)。 + // /v1/responses 上游强制每次生成新 UUID 作 session id,只作为老版本兼容的 fallback。 const startedResp = await globalThis.fetch(`${gwUrl}/v1/runs`, { method: 'POST', headers, @@ -8627,6 +8626,13 @@ async function _handleHermesAgentRunStream(req, res, args = {}) { signal: controller.signal, }) if (!startedResp.ok) { + // 404 → 老版本 Hermes Agent 没 /v1/runs,降级 /v1/responses + if (startedResp.status === 404) { + try { await startedResp.body?.cancel() } catch {} + const handledByResponses = await _tryHermesResponsesStream(gwUrl, apiKey, payload, args, controller, res) + if (handledByResponses) return + throw new Error('HTTP 404: /v1/runs 不存在,且 /v1/responses fallback 失败') + } const text = await startedResp.text() throw new Error(`HTTP ${startedResp.status}: ${text}`) } diff --git a/src-tauri/src/commands/hermes.rs b/src-tauri/src/commands/hermes.rs index 55974ca..cef0128 100644 --- a/src-tauri/src/commands/hermes.rs +++ b/src-tauri/src/commands/hermes.rs @@ -3394,12 +3394,9 @@ pub async fn hermes_agent_run( payload["instructions"] = Value::String(inst.clone()); } - if let Some(response_run_id) = - try_hermes_responses_run(&app, &gw_url, &api_key, &payload, session_id.as_deref()).await? - { - return Ok(response_run_id); - } - + // 优先 /v1/runs:该端点显式支持 body.session_id,按 client 传的 session id 复用 session, + // 避免 Hermes 服务端 `sessions list` 中每条消息生成一个新 session(issue #275)。 + // /v1/responses 会忽略 body.session_id 并对每次请求新建 session_id,所以不作为主路径。 let client = hermes_gateway_http_client(std::time::Duration::from_secs(10)) .map_err(|e| format!("HTTP 客户端创建失败: {e}"))?; @@ -3425,6 +3422,16 @@ pub async fn hermes_agent_run( }; if !resp.status().is_success() { let status = resp.status().as_u16(); + // 404 → 老版本 Hermes Agent 没有 /v1/runs,降级到 /v1/responses 兼容 + // (代价:session 会暴增,但至少能用;建议用户升级 Hermes Agent) + if status == 404 { + if let Some(response_run_id) = + try_hermes_responses_run(&app, &gw_url, &api_key, &payload, session_id.as_deref()) + .await? + { + return Ok(response_run_id); + } + } let text = resp.text().await.unwrap_or_default(); return Err(format!("HTTP {status}: {text}")); }