Skip to content

Latest commit

 

History

History
694 lines (538 loc) · 50.5 KB

File metadata and controls

694 lines (538 loc) · 50.5 KB

Reasonix 接入 Hermes Studio —— 项目进度(交接版)

最后更新:2026-08-06 22:30 项目目录:/opt/Project/reasonix-x 交接状态:批量重复 bug 已修复(方案 A)+ 工具调用过程无显示 bug 已修复(tool_dispatch/tool_result,21:08 已生效)+ 对话不显示思维链 bug 已修复(reasoning 分支发 response.reasoning.delta,21:22 已生效)+ 编程工具页 scoped 启动 500 已修复(providerName 块外引用,22:27 已生效,CLI 实测跑通)+ reasonix 启动新增 --permission-mode bypassPermissions(20:58 已生效),前三项待群聊实测确认 + 群聊头像问题仍待解决

⚠️ 当前状态(务必先读)

功能接入已基本完成并验证:

  • ✅ 编程工具页:Reasonix 卡片出现(老板确认)
  • ✅ 编程工具页:内置终端启动正常——「全局默认设置」与「使用提供商和模型」均可启动(scoped 500 已修复 22:27,见下)
  • ✅ 群聊房间:可添加 Reasonix agent,不再报 Invalid agent(老板确认)
  • ✅ 工作流:可添加 Reasonix 节点并运行(老板确认)
  • ✅ 群聊里 Reasonix 头像显示正常(根因=浏览器缓存旧版前端,修复=入口加 ?v=2 cache busting,见下文「已修复」)
  • ✅ reasonix 升级到 v1.21.0(stream-json 新增 stream_attempt 事件,已显式忽略,见下文「升级记录」)
  • 已解决:Reasonix-x 群聊房间进不去/浏览器卡死(根因=177k tokens 历史消息 + 群聊列表 virtualized:!1 一次性渲染全部。修复=前端补丁开虚拟滚动 virtualized:!0,见下文「群聊卡死排查」)

进度总览

阶段 状态 说明
1. 可行性验证 ✅ 完成 reasonix 能跑任务/流式输出/多轮续聊(--continue
2. 方案设计 ✅ 完成 OpenAI 兼容 API,与 codex 一致(老板拍板)
3. 补丁实现 ✅ 完成 后端 43 处 reasonix、前端 3 文件(保持原名)
4. 端到端验证 ✅ 完成 面板/群聊/工作流/头像均通(头像=cache busting 修复)
5. 补丁机制收尾 ✅ 完成 apply-patch.sh + patches/(rules.json + apply_patch.py)已实现并验证

已完成(全部改动)

后端 dist/server/index.js(43 处 reasonix)

  1. 白名单 a_I():加 ||I==="reasonix"
  2. 注册数组 ErI:加 {id:"reasonix",name:"Reasonix",provider:"DeepSeek",command:"reasonix",packageName:"reasonix"}
  3. launch 配置 EH():reasonix 分支——写项目级 reasonix.toml[[providers]] 段:base_url/model/api_key_env=HERMES_REASONIX_KEY)+ apiKey 写 ~/.reasonix/.env
  4. 启动分发 send():加 agentId==="reasonix"startReasonixExecTurn
  5. 运行方法startReasonixExecTurn / handleReasonixExecLine / appendReasonixText / completeReasonixExecTurn(spawn reasonix run --dir <ws> --model <provider> --output-format stream-json --permission-mode bypassPermissions <task>,续聊 --continue⚠️ 20:58 新增固定追加 --permission-mode bypassPermissions,免确认运行)
  6. 群聊白名单 A3:加 "reasonix"(否则报 400 Invalid agent)
  7. 群聊映射 this.agent==="reasonix"?"reasonix":从兜底 codex 拎出
  8. 工作流
    • j9():加 reasonix 分支(agent:"reasonix",codingAgentId:"reasonix"
    • workflow 校验:G!=="reasonix" 放行
    • iRI 能力校验:reasonix 只要求 apiMode 在 pie 白名单(W=a==="reasonix"?pie.has(b):...),不要求 provider 在 profile groups(关键修复,否则 409 "target capability is unavailable")
    • Poe() 映射加 reasonix
  9. 其他硬编码(reasonix 掉到 claude/codex 分支的):startCodingAgentMemoryExportDhe()KQIemitTerminalStatus 显示名、die()pQ()、workflow agent 会话判断、事件上报 agent 短名(3 处三元链)

前端(3 个文件 + 入口,最初改名 -rx 后缀,⚠️ 已恢复原名,见踩坑记录 10)

文件 改动
CodingAgentsView-CjdONoI4.js Ke 卡片 + B 配置段 + _ 状态 + I/w/F/O 状态对象 + 图标映射
GroupChatView-BoO-c_wr.js 下拉加 reasonix + 映射 + ba 头像映射加 reasonix:"/coding-agents/reasonix.png"
WorkflowView-BRPAvC7B.js 下拉加 reasonix + gi() 映射加 reasonix
index-BhdrPxdZ.js 保持原名(不再改名绕缓存)
index.html 引用指向 index-BhdrPxdZ.js

资源与配置

  • 头像:coding-agents/reasonix.png(老板提供的 77x79 PNG,4062 字节)
  • systemd unit:加 Environment=PATH=/root/.nvm/versions/node/v23.11.1/bin:...
  • /root/.reasonix/.env:含 DEEPSEEK_API_KEY + HERMES_REASONIX_KEY(真实 key)

🔍 群聊 reasonix 流式渲染查证(2026-08-06,结论记录)

问题:群聊 @reasonix 回复显示不流畅,不像流式渲染;@codex 相对流畅。

已逐环查明的完整链路(代码层全部支持流式)

  1. reasonix 上游 (esengine/DeepSeek-Reasonix.git, main-v2):agent.go 逐 chunk emit event.Text(每 chunk 一个),实测 stream-json 每 40ms 一个 delta、逐行带换行输出 → 上游原生 token 级流式

    ⚠️ 修正(2026-08-06 方案 A 修复后实测):本行"token 级流式"为早期查证,与下文 156 行矛盾。方案 A 定位确认 reasonix stream-json 对同一内容发 text/message/result 3 种事件、text 事件给整段文本(非逐 token),故 156 行「整段 delta 已是可给的最细粒度」才是最终定论。本行保留作历史脉络。

  2. Hermes 单聊startReasonixExecTurnhandleReasonixExecLine 逐行解析 → appendReasonixText 逐 delta 发 output_text.delta
  3. Hermes 群聊replyToMentionWithChatRunchatRunService.runAndWaitOQI(普通agent) / KQI(coding agent 专用)JHye.startstartReasonixExecTurn(class I @6410311)。reasonix 输出逐行流式转发。
  4. 前端GroupChatView 监听 message_stream_deltacontent+=delta 实时渲染(依赖 message_stream_start 置 isStreaming=true)。

关键结论

  • codex 与 reasonix 走完全相同的 class IstartCodexExecTurn/startReasonixExecTurn),逐行处理,代码层无流式差异
  • 群聊 coding_agent_id:reasonixKQIJHye.start 专用路径,reasonix 是主执行者,理论上流式
  • 实测局限:通过 agent-bridge IPC 发的普通 chat(让 Hermes agent 把 reasonix 当工具调)得到聚合返回(284 delta 同刻),但该实测路径 ≠ 群聊专用路径。codex 同测试也聚合(分4批),说明"工具调用路径聚合"是 Hermes 通用行为,非 reasonix 特有。

老板确认具体表现:逐字出现但经常停顿(不是整段一次性跳出)。

结论(已定位,2026-08-06):流式链路是通的(文字确实逐 token 出),停顿点是 reasonix 工具调用边界。reasonix 的 text 事件逐 chunk 输出,但进入工具调用(ToolDispatch)时暂停输出 text,直到工具执行完才继续。任务里 reasonix 频繁调工具(写代码/跑命令),所以呈现"逐字→停顿(工具执行)→逐字→停顿"的节奏。codex 工具调用节奏不同(更少/更快),视觉上相对流畅。

这是 reasonix 上游的设计行为(工具调用期间本就不该输出 text),不是 Hermes 的 bug。若需改善视觉,可在 Hermes 侧给工具调用停顿加"加载中"提示(待老板拍板是否做)。

验证方法(未跑,待用):用 reasonix 跑含工具调用的任务,实时抓 text/tool 事件时间戳,确认 text 在 tool 事件期间暂停。

查证代码位置速查

  • reasonix 上游:internal/agent/agent.go(ChunkText→event.Text)、internal/cli/run_output.go(stream-json 逐事件)
  • Hermes 群聊编码引擎:class I @dist/server/index.js 6410311(含 startReasonixExecTurn/startCodexExecTurn)
  • 群聊分发:KQI @8415033(coding_agent 专用)、JH @7670265(引擎启动)
  • bridge 聚合:agent-bridge/python/bridge_pool.py _run_chat stream_callback(普通 chat 路径)

✅ 已修复:群聊 Reasonix 头像没变(2026-08-06,根因=浏览器缓存)

现象:群聊里 Reasonix 头像显示旧图,不是提供的 reasonix.png

服务端/前端逻辑 100% 正确(已逐环验证)

  • 后端 bBthis.agent=e.agent||"hermes" → 存 "reasonix"
  • 前端 ba 映射含 reasonix:"/coding-agents/reasonix.png"
  • 前端 Ia(f)=ea(f?.avatar)||Si(f?.agent)Si 查 ba → reasonix.png ✅
  • 服务端 GroupChatView 含 reasonix.png ✅
  • index.html → index-BhdrPxdZ.js → GroupChatView ✅
  • reasonix.png HTTP 200 ✅
  • 排除 Service Worker(notification-sw.js 不缓存资源)

根因(确定):浏览器缓存了旧版未改名的前端文件。前端资产用一年 immutable 缓存,改名文件走新名,但入口 index-BhdrPxdZ.js 未改名 → 浏览器命中旧缓存,加载旧版 GroupChatView(无 reasonix 头像映射)。

修复(cache busting):给入口 JS 引用加版本查询串,强制浏览器加载全部新资产:

<script ... src="/assets/js/index-BhdrPxdZ.js?v=2" ...>

dist/client/index.html 追加 ?v=2,强制所有 index-BhdrPxdZ.js 依赖的 chunk 重新拉取。已确认 index.html 当前为 ?v=2,老板确认头像显示正常。

踩坑教训:新代理接入后头像不更新,先验证全链路逻辑正确,再怀疑/处理缓存(memory 明确记录此反模式)。改名文件要同步改入口引用,否则浏览器缓存旧版。

✅ 已修复:群聊思考链路内容批量重复(2026-08-06)

现象

群聊里 reasonix 回复时,同一段内容会重复出现 3 遍,且思考链路没有逐字流式渲染(一块一块蹦出来)。

根因

reasonix 的 --output-format stream-json 对同一内容会发 3 种事件(实测逐行 NDJSON):

{"kind":"text","text":"Hi!"}        // 第1遍:增量文本(最接近流式)
{"kind":"message","text":"Hi!"}     // 第2遍:完整消息(与 text 同内容)
{"type":"result","result":"Hi!"}    // 第3遍:最终结果(与上同内容)

handleReasonixExecLinetext / message / result 三种事件都当作独立 delta 追加(appendReasonixText -> 发 response.output_text.delta),前端按 delta 累加 → 同一文本被发 3 次 → 群聊重复 3 遍。

对照 codex 为何没问题:codex 走 item.started / item.completed / response_item 结构化事件 + appendCodexFinalText(只发一次最终文本),有状态机去重;reasonix 抄了简化版,没区分"增量"和"终态"。

修复方案(方案 A:从源头只选一个文本来源)

修改文件/root/.nvm/versions/node/v23.11.1/lib/node_modules/hermes-web-ui/dist/server/index.js (已备份为 index.js.reasonix-rep.baknode --check 语法验证通过,进程已重启生效)

handleReasonixExecLine 改动逻辑:

事件 改前 改后
text appendReasonixText(第1次) ✅ 保留:appendReasonixText(增量)
message appendReasonixText(第2次,重复) ⛔ 改为 return,忽略(内容已被 text 覆盖)
result appendReasonixText(第3次,重复)+ 完成 ⛔ 改为:仅当 !e.printTextStarted(text 从未来过)时兜底补发 n.result,再完成 turn;若 text 已发过则不再补文本

关键状态appendReasonixText 第一次调用会把 e.printTextStartedtrue。所以:

  • 正常情况:text 来过 → printTextStarted=trueresult 跳过补文本,只完成回合 → 不重复
  • 兜底情况:text 从没来 → printTextStarted=falseresult 补发一次完整文本 → 不丢内容

补丁后 handleReasonixExecLine 代码体(实测):

handleReasonixExecLine(e,l){let t=l.trim();if(!t)return;let n;try{n=JSON.parse(t)}catch{Y.debug({...},"[coding-agent-run] ignored non-json Reasonix line");return}
let a=String(n.kind||n.type||"").trim();
if(a==="text"){let c=String(n.text||"");if(c)this.appendReasonixText(e,c);return}
if(a==="message")return;                                                    // ← 去重:忽略 message
if(a==="usage"){e.codexPendingUsage=n.usage;return}
if(a==="result"){
  if(e.printCompleted)return;
  let c=String(n.result||"");
  if(c&&!e.printTextStarted)this.appendReasonixText(e,c);                   // ← 仅 text 从未发过时兜底
  this.completeReasonixExecTurn(e,e.codexPendingUsage)
}
if(a==="turn.failed"||a==="error")this.failCodexExecTurn(e,...)}

⚠️ 未解决:流式逐字渲染

本次修复消除了重复,但未解决"逐字流式"。原因:reasonix 的 stream-json 协议本身只给 message 级整段文本{"kind":"text","text":"整段"}),没有 token 级增量事件。要做到真正逐字流式,需 reasonix 上游支持 token 级 delta(如 text_delta)或后端改走 SSE/原始 token 流。在当前协议下,整段 delta 已是它能给的最细粒度。

生效情况

  • 补丁写入:19:21:52
  • 服务进程重启:19:23:23(重启后已加载补丁代码)
  • 老板确认重启成功 ✅,待群里实测验证重复是否消除

✅ 已修复:reasonix 工具调用过程无显示(2026-08-06 21:08)

现象

群聊 @reasonix 期间,reasonix 调用工具(写代码/跑命令)时,前端看不到工具执行过程(没有工具调用卡片/状态),只有最终大段文字跳出。对照 codex 能显示工具调用卡片。

根因

reasonix 的 --output-format stream-json 在工具调用时会发出独立的 tool_dispatch(开始)和 tool_result(结束)事件:

{"kind":"tool_dispatch","tool":{"id":"...","call_id":"...","name":"bash","args":"...","partial":false}}
{"kind":"tool_result","tool":{"id":"...","call_id":"...","name":"bash","output":"..."}}

(已从 reasonix 上游二进制确认:case 'tool_dispatch': if(e.tool)renderToolDispatch(e.tool)case 'tool_result': ...tool 对象含 id/call_id/name/args/output/partial 字段)

handleReasonixExecLine 只处理 text/message/usage/result/turn.failed遇到 tool_dispatch/tool_result 直接忽略(fall through 到结尾 return),导致工具调用过程在前端完全不显示。

修复

修改文件/root/.nvm/versions/node/v23.11.1/lib/node_modules/hermes-web-ui/dist/server/index.js(21:08 修改,8521943 字节,语法 node --check 通过,服务 21:08:26 重启已加载)

handleReasonixExecLine 新增两个分支(复用 Hermes 已有 codex 工具渲染机制 e.codexToolBlocks Map + handleClaudePrintResponseEvent 发标准 output_item 事件):

事件 处理
tool_dispatch 跳过 partial(增量预览帧);解析 id/call_id/name/args,存入 codexToolBlocks,发 response.output_item.addeditem.type:"function_call")→ 前端显示工具调用卡片
tool_result 解析 id/call_id/output,发 response.output_item.doneitem.type:"function_call_output")→ 前端回填工具执行结果

同时把原 text/message 合并分支拆开:text 只取 n.textmessage 单独 return(保持方案 A 去重逻辑,忽略 message)。

生效情况

  • 文件修改:21:08:03
  • 服务进程重启:21:08:26(已加载)
  • 前端 GroupChatView 本就能渲染 tool_callstoolStatus:"running" 等,Hermes 通用机制),reasonix 复用同一通道,无需改前端 ✅

✅ 已修复:对话不显示思维链(2026-08-06 21:22)

现象

群聊/对话里 @reasonix 回复时,reasonix 的思维链(thinking/reasoning)过程不显示,只看到最终答案。对照 codex 能展开显示思维链折叠块。

根因

reasonix 的 --output-format stream-json 在模型输出思维链时会发独立的 reasoning 事件(已从上游二进制确认:case 'reasoning': appendReasoning(e.reasoning||e.text||'')reasoning 事件含 e.reasoning/e.text 字段)。原 handleReasonixExecLine 只处理 text/message/usage/result/tool_dispatch/tool_result/turn.failed遇到 reasoning 事件直接忽略,导致思维链在前端完全不显示。

修复

修改文件/root/.nvm/versions/node/v23.11.1/lib/node_modules/hermes-web-ui/dist/server/index.js(21:22 修改,8522034 字节,语法 node --check 通过,服务 21:22:39 重启已加载)

handleReasonixExecLine 新增一个分支(复用 Hermes 已有 codex 思维链机制):

事件 处理
reasoning n.text,调 this.appendCodexReasoning(e,rc) → 发 response.reasoning.delta 流式事件 → 前端思维链折叠块逐字显示

appendCodexReasoning 定义(codex 侧原有):

appendCodexReasoning(e,l){l&&this.handleClaudePrintResponseEvent(e,{type:"response.reasoning.delta",data:{type:"response.reasoning.delta",item_id:e.printMessageId,output_index:0,delta:l}})}

reasonix 复用同一方法,与 codex 思维链行为完全一致。

生效情况

  • 文件修改:21:22:22
  • 服务进程重启:21:22:39(已加载)
  • 前端 GroupChatView 本就有思维链渲染(reasoning/reasoning_content 字段 + reasoning_delta 流式事件,Hermes 通用机制),reasonix 复用同一通道,无需改前端 ✅

✅ 已修复:编程工具页「使用提供商和模型」启动 reasonix 报 API Error 500(2026-08-06 22:27)

现象

web 控制台编程工具页启动 reasonix 内置终端:

  • 选「使用提供商和模型」→ 提示 API Error 500: Error: providerName is not defined
  • 选「全局默认设置」→ 成功进入 reasonix CLI 界面

根因

EH()(launch 配置函数)reasonix 分支块内定义 let providerName="hermes-"+n 并构造 h=["--model",providerName,"--output-format","stream-json",...];但 22:05 某次改动把函数尾部 QH 调用从 args:h 改成了块外三元表达式:

args:l.id==="reasonix"?["--model",providerName,...]:h

在 reasonix 分支块外引用块内 let 变量ReferenceError: providerName is not defined → HTTP 500。

触发条件:

  • scoped(使用提供商和模型):走到 if(l.id==="reasonix") 分支求值三元 → 抛错
  • global(全局默认设置)if(t==="global") 提前 return,不经过 QH 三元 → 正常

该手误自接入 reasonix 时已存在(19:21 备份中 providerName 即未定义),之前群聊均走 global 路径未暴露;22:05 改动首次把它引入必经路径。

修复

QH 调用改回 args:h(块内 h 已含完整 --model 参数):

let N=QH({workspaceDir:Z,env:u,command:l.command,args:h}),V=await Sle({rootDir:m,workspaceDir:Z,env:u,command:l.command,args:h})
  • 文件修改:22:26:39(8522184 字节,node --check 通过)
  • 服务重启:22:27:07(已加载修复代码)
  • providerName 出现 5→4 处(块外引用归零)

验证(本 shell 在沙箱外,实测跑通)

  1. .env 实为 174 字节普通文件,含 DEEPSEEK_API_KEY/FEISHU_BOT_APP_SECRET/HERMES_REASONIX_KEY 三键——印证核对记录二的「沙箱外可读真实 .env」结论
  2. 实测 reasonix CLI 接受 scoped 路径的 --model 'hermes-custom:火山'(含冒号中文 provider 名):
    • 在 reasonix-x workspace(有 reasonix.toml 定义该 provider)完整跑通turn_started → text → message → usage → result success,模型 ark-code-latest,正常回复 ✅
    • 在无 reasonix.toml 的目录报 unknown model "hermes-custom:火山" (configured: deepseek)——属预期(workspace 级 provider 需 workspace 配置,非 bug)
  3. 结论:scoped 路径 500 已消除,内置终端「使用提供商和模型」应可正常启动

✅ 代码核对记录(2026-08-06 21:10)

核对方式:读取 docs/DEVELOPMENT.mddocs/PROGRESS.md,对照生产安装的 dist/server/index.js、前端 3 文件、coding-agents/reasonix.pngreasonix.toml、systemd unit。

核对结论:文档与代码基本一致,发现 1 处未记录的新改动 + 1 处环境认知澄清

项目 核对结果
后端 reasonix 出现处 生产 index.js43 处 reasonix,与文档一致 ✅
handleReasonixExecLine 方案 A 去重 与文档描述的补丁代码体完全一致(text 保留 / message 忽略 / result 兜底补发 + completeReasonixExecTurn)✅
appendReasonixText / completeReasonixExecTurn 与文档一致(complete 复用 completeClaudePrintTurn)✅
前端 GroupChatView reasonix:"/coding-agents/reasonix.png" 头像映射 + reasonix 选项,共 2 处 ✅
前端 CodingAgentsView 含 reasonix 卡片($e 图标映射 / Ke 数组 / I/w/F/O 状态 / 配置段)✅
前端 WorkflowView 含 reasonix 选项 ✅
coding-agents/reasonix.png 存在,4062 字节(与文档 77x79 PNG 一致)✅
index.html 引用 index-BhdrPxdZ.js(保持原名)✅
systemd unit Environment=PATH 含 nvm bin,与文档一致 ✅
workspace reasonix.toml default_model="hermes-custom:火山" + [[providers]](base_url ark / model / api_key_env=HERMES_REASONIX_KEY),与文档一致 ✅

⚠️ 新改动(文档遗漏):startReasonixExecTurn 固定追加 --permission-mode bypassPermissions

  • 生产 index.js(20:58 修改,服务 20:59 重启已加载)reasonix 启动命令为:
    reasonix run [--dir <ws>] ... --output-format stream-json [--effort r] --permission-mode bypassPermissions <task>
    
  • 缘由:reasonix --permission-mode 默认 ask,群聊/工作流场景会卡权限确认;bypassPermissions 免确认运行(reasonix 合法取值之一,reasonix --help 确认)。
  • 已同步更新 DEVELOPMENT.md 5.5 与本文档第 5 点。

✅ 代码核对记录二(2026-08-06 21:15,工具调用显示修复)

核对方式:对照 19:21 备份 index.js.reasonix-rep.bak 与生产 index.js(21:08),定位 handleReasonixExecLine 方法体差异(旧 678 字节 → 新 1611 字节,+933)。

核对结论:新增 tool_dispatch / tool_result 两个事件处理分支,修复 reasonix 工具调用过程无显示

项目 核对结果
新增 tool_dispatch 跳过 partial,解析 id/call_id/name/argscodexToolBlocks + 发 response.output_item.addedfunction_call) ✅
新增 tool_result 解析 id/call_id/output → 发 response.output_item.donefunction_call_output) ✅
reasonix 上游事件真实性 二进制确认有 tool_dispatch/tool_resultcase 'tool_dispatch': renderToolDispatch),tool 对象含 id/call_id/name/args/output/partial,与后端解析字段匹配 ✅
前端渲染 GroupChatView 内置 tool_calls 渲染(toolStatus:"running" 等),reasonix 复用 output_item 通道,无需改前端 ✅
语法与生效 node --check 通过;index.js 21:08:03 修改、服务 21:08:26 重启已加载 ✅

⚠️ 潜在待确认点:reasonix 上游还有 tool_progress 事件(工具执行中进度),后端 handleReasonixExecLine 未处理该事件(落入 return 忽略)。因 tool_dispatch 已带 partial 帧逻辑,tool_progress 是否为必要功能待实测确认(非阻塞)。

环境认知澄清(非问题):/root/.reasonix/.env 显示为字符设备(1:3=/dev/null)

  • 原因:Reasonix 的 bwrap 沙箱把 /dev/null 只读绑定/root/.reasonix/.env--ro-bind /dev/null /root/.reasonix/.env),防止沙箱内子进程读取含密钥的 .env。这是正常安全隔离,非环境损坏。
  • 沙箱外(reasonix 主进程)能访问真实 .env;从工具 shell 内看到的 .env/dev/null不要把它当 bug 去删/改

✅ 代码核对记录三(2026-08-06 21:30,思维链显示修复)

核对方式:对比 19:21 备份 index.js.reasonix-rep.bak 与生产 index.js(21:22),定位 handleReasonixExecLine 新增的 reasoning 分支。

核对结论:新增 reasoning 事件处理分支,修复 reasonix 对话不显示思维链

项目 核对结果
新增 reasoning 分支 `if(a==="reasoning"){let rc=String(n.text
appendCodexReasoning 复用 codex 思维链通道,发 response.reasoning.delta 流式事件(item_id:e.printMessageId),与 codex 完全一致 ✅
reasonix 上游事件真实性 二进制确认 stream-json 产出 `case 'reasoning': appendReasoning(e.reasoning
前端渲染 GroupChatView 处理 reasoning/reasoning_content 字段 + reasoning_delta 流式事件(chat-F3DnDu6u.js 含 reasoning.delta),reasonix 复用同一通道,无需改前端 ✅
语法与生效 node --check 通过;index.js 21:22:22 修改、服务 21:22:39 重启已加载 ✅
既有改动未回退 生产仍 43 处 reasonix;tool_dispatch/tool_result/bypassPermissions 均仍在 ✅

⚠️ 实测限制:reasonix 实际运行受沙箱 .env 绑定限制(主进程有 key,shell 内读不到),无法在本 shell 实跑抓取 reasoning 事件;但上游二进制 case 'reasoning' 分支 + 当前会话本身即 reasonix 运行产物,足以佐证链路。

待办(交接给后续)

🔴 当前阻塞(最高优先)

  • 解决 Reasonix-x 群聊房间进不去/浏览器卡死(根因=177k tokens + 群聊 virtualized:!1 一次性渲染全部。修复=前端补丁 patches/apply_frontend_patch.py 开虚拟滚动 virtualized:!0,2026-08-07 已应用并验证)

功能验证(大多已完成,仅剩实测确认)

  • 群聊"批量重复"已消除(方案A已打+重启)
  • 群聊"工具调用过程"已显示(tool_dispatch/tool_result 已打+重启 21:08)
  • 群聊/对话"思维链"已显示(reasoning 分支已打+重启 21:22)
  • 编程工具页「使用提供商和模型」启动 reasonix 不再 500(已修复 22:27)
  • 群聊头像已修复(cache busting ?v=2)
  • apply-patch.sh 补丁脚本已实现并验证(21 条规则+2插入,verify 全 ✅)
  • reasonix 升级到 v1.21.0 + stream_attempt 显式忽略

其他

  • 确认 reasonix 上游 tool_progress 事件是否需后端处理(当前未处理,非阻塞)
  • 更新 /opt/workspaces/local-docs 维护文档
  • 升级保护:npm 升级会覆盖 dist 所有前端改动,补丁脚本必须覆盖(当前 apply-patch.sh 只覆盖后端 index.js,前端改动需另行处理)

关键决策记录

  • 接入方式:方案A(真实 Reasonix 卡片 + 群聊 + 工作流)
  • API 方式(老板拍板):像 codex 一样面板选 provider/model/apiKey,走 OpenAI 兼容 API
  • 补丁维护:脚本化补丁(apply-patch.sh 一键重打),非源码 fork
  • 续聊--continue

踩坑记录(务必阅读,避免重蹈)

  1. minified 单行文件 + 正则转义b.split(/\r?\n/) 在 Python 字符串转义错写成 /\n?\n/Invalid regular expression改完立即 node --check
  2. 重复插入:同一方法插两次 → Unexpected identifier。插入前先确认存在性。
  3. 括号闭合:方法后多加 } 闭合类 → Unexpected token 'async'
  4. 缓存机制(重要)assets/js/*.jsmax-age=31536000, immutable(一年缓存,内容 hash 文件名)。直接改 dist 内容不改文件名 = 浏览器永远用旧版⚠️ 曾尝试改名 -rx 后缀绕缓存,酿成白屏,最终方案:保持文件名不变(见踩坑 10)。
  5. HOME 差异(重要):Web UI 进程 HOME=/root,reasonix 读 /root/.reasonix/.env;Hermes CLI 会话 HOME=/root/.hermes/profiles/kaifazhe/home测试 reasonix 必须 HOME=/root,否则报 missing env 误判。
  6. 群聊 400 Invalid agentA3 白名单漏了 reasonix。
  7. 工作流 409iRI 能力校验要求 provider 在 profile groups,reasonix 需宽放(只查 apiMode)。
  8. 改动编译产物文件名 → 全站白屏(高危,本次已踩):Vite 模块引用是双向的——主入口 __vite__mapDeps 指向依赖,依赖 chunk 也用 import("./index-BhdrPxdZ.js") 相对路径指回主入口。只改主入口文件名 + index.html 引用、不同步改其他 249 个 chunk 里的相对 import,浏览器请求旧文件名 → 服务端 SPA-fallback 返回 200+text/html → strict MIME 拒绝 → Failed to load module script: MIME text/html白屏。教训:不要改编译产物文件名。验证白屏根因用无头 Chrome(--headless=new --enable-logging=stderr --dump-dom)抓 console 的 Failed to load module 与 Network 请求序列(INIT=script 能看出谁发起的旧引用)。

风险备忘

  • 升级即失效:npm 升级覆盖 dist → 所有改动全消失。必须先跑补丁脚本再重启。
  • 费用:reasonix 走 DeepSeek(同 codex 账号),模型档位注意(默认 low)
  • 数据未持久化:群聊 agent 在内存(gc_room_agents 表 0 行),重启后可能丢失,需确认业务是否接受

🔄 回滚记录(2026-08-06 深夜)

背景

未接入 git 前,回滚只能靠手工 .bak 备份。本轮建立 git 仓库并整理回滚基线。

回滚基线(dist/server/index.js)

备份 时间 reasonix 数 providerName JrI reasonix 段 内容
index.js.bak 16:25 0 0 原始未接入
index.js.bak2 20:58 43 4 主接入
index.js.bak3 21:42 43 4 主接入+去重+工具+思维链
index.js.bak4 22:01 47 4 = 当前完整生产
index.js.fixed 同步 47 4 生产修复版(同步入库)

关键澄清(重要)

bak3 与 bak4 的唯一差异 = 只有 JrI 里的 reasonix 配置文件段(150 字节),经 diff 逐字节确认(仅 1507c1507 一处)。

  • scoped 500 修复(providerName 块外引用)在 bak3 里就已存在(4 处),从未丢失。
  • 早期"回滚到 bak3 会丢 scoped 500 修复"的判断有误——那是用 tr ';' '\n' 分行 diff 被整大块误导。实际 bak3/bak4 只差配置段。

已执行操作

  1. 建 git 仓库 + 首次提交(docs/配置/4个bak基线)
  2. 按"方案A"回滚到 bak3(cp index.js.bak3 → 生产)
  3. 发现点配置文件报 500:根因 = 回滚丢 JrI 里 reasonix 配置段 → JrI["reasonix"] undefined → HW/d3 的 .find() TypeError
  4. 只补 JrI reasonix 配置段(保留回滚态,不复活其他),生产 md5 恢复 b1a5dbcb = bak4 = 完整修复态
  5. 提交 git checkpoint(bak5/bak6)+ 同步 index.js.fixed 入库

当前状态

  • 生产 index.js = f506dc2a(含 reasonix 全部接入 + 配置段 + scoped 终端启动修复),服务 active 23:29:03 重启生效
  • 点配置文件 500 已修复
  • scoped 内置终端启动 reasonix CLI 已修复(见下节)
  • git 历史:8d9f15c(初始化) → bf9742b(回滚) → 68bd69a(docs+fixed)

✅ 已修复:scoped 内置终端无法启动 reasonix CLI(2026-08-06 23:29)

现象

编程工具页选「使用提供商和模型」(scoped)启动 reasonix 内置终端,只能进入 shell,无法启动 reasonix CLI:

cd '.../workspace/default/custom_火山' && '.../reasonix/launch.sh'
root@debian:~/.hermes-web-ui/.../custom_火山#

(直接退回 shell 提示符,reasonix 未启动)

根因(100% 确认)

EH() 里 reasonix 分支生成的 h=["--model",providerName,"--output-format","stream-json",...],被用于 launch.sh:

exec reasonix --model 'hermes-custom:火山' --output-format stream-json

reasonix help 明确 --output-format 只在 run/-p 子命令下有效,裸交互模式(reasonix [--model])不接受该 flag → 启动即退出(退出码 2)→ 退回 shell。

全局默认配置能启动:全局分支 args:y=[](无 flag),launch.sh = reasonix(裸交互),所以正常。

修复(方案X:h 干净化 + run 命令补 flag)

  1. EH() reasonix 分支:h 构造去掉 --output-format stream-jsonh=["--model",providerName,...r?["--effort",r]:[]]
    • launch.sh 变为 exec reasonix --model X(干净,能进交互 CLI)
  2. startReasonixExecTurnrun 命令拼接补回 --output-format stream-json
    ["run",...cmd,...args,"--permission-mode","bypassPermissions","--output-format","stream-json",l]
    • 群聊/对话 reasonix run 不受影响(flag 在 run 子命令下合法)

实证

  • node-pty(内置终端环境)跑修复后命令 reasonix --model 'hermes-custom:火山' --permission-mode bypassPermissions成功进入 reasonix CLI◆ reasonix · ark-code-latest ... ❯ 提示符)
  • node --check 通过;生产 md5 b1a5dbcb → f506dc2a
  • 服务重启 23:29:03 生效,老板 web 控制台确认修复成功

影响面

  • 内置终端(scoped 启动 reasonix CLI):✅ 修复
  • 群聊/对话(reasonix run):✅ 不受影响(补回 flag)
  • 全局默认配置:✅ 不受影响

踩坑教训

  1. 业务表面现象("只能进 shell")≠ 根因在终端/pty。真正根因在 launch.sh 的命令参数——--output-format stream-json 对 reasonix 交互 CLI 是无效 flag。
  2. scoped 和 global 的启动命令差异是排查关键:全局 args=[] 能启动,scoped 带 flag 不能 → 差异点即根因。
  3. reasonix 交互 CLI 需要真实 TTY(bubbletea TUI),但 Hermes 用 node-pty 提供,能支持;首次会弹 telemetry 确认(允许发送匿名统计 [Y/n]),全局已确认过则正常。

✅ reasonix 升级到 v1.21.0 + stream_attempt 兼容(2026-08-07)

升级

  • 版本:reasonix@1.19.1 → 1.21.0(npm 全局,npm install -g reasonix@1.21.0
  • 走正式发布渠道,未用自更新

关键发现:stream-json 输出格式变化

新版 1.21.0 的 --output-format stream-json 新增 stream_attempt 事件

旧版: turn_started → text → message → usage → result
新版: turn_started → stream_attempt(begin) → text → message → stream_attempt(commit) → usage → result

兼容处理

Hermes handleReasonixExecLinekind 分发,原对未知 kind(含 stream_attempt)静默忽略。为"防患未然",显式声明忽略 stream_attempt

if(a==="stream_attempt"){Y.debug({runId:e.id,event:"reasonix-stream-attempt",attempt:n.streamAttempt},"[coding-agent-run] ignoring reasonix stream_attempt");return}
  • 由"被动跳过"改为"主动声明忽略",留调试日志 reasonix-stream-attempt
  • 不影响 text/message/result 正常处理(它们仍在处理分支)
  • 生产 md5 f506dc2a → 22a54955,重启 10:37:28 生效
  • 备份:应用前 index.js.bak8(=旧生产 f506dc2a),快照 index.js.test2.js(=当前生产)

验证

  • reasonix run "只回复OK" --output-format stream-json 跑通,result success
  • Hermes 补丁 verify 14 项全 ✅
  • 服务 active,无需重启(reasonix 是外部二进制,Hermes 每次 spawn 调用新版本)

踩坑教训

  • reasonix 升级会改变 stream-json 输出格式(新增事件)。升级后必须重新验证 stream-json 输出,确认 Hermes 的 handleReasonixExecLine 能兼容新事件。
  • 未知 kind 事件被静默忽略不会报错,但可能掩盖未来协议变化。关键事件若被忽略会导致功能异常而不自知 → 建议显式声明忽略并留日志。

✅ 已解决:Reasonix-x 群聊房间进不去(浏览器卡死)(2026-08-07)

现象

老板进「Reasonix-x」群聊房间(该房间用 reasonix 审核项目代码,发了很多对话+工具消息,统计 token 约 177k),一进就浏览器卡死/页面无响应

已排查确认(证据)

  1. 不是工具事件风暴:服务端日志显示 reasonix 群聊 run 正常启动/退出(reasonix exec exited code:1),无 delta 洪峰。reasonix tool_dispatchif(m.partial)return 保护,不会每个 delta 发 tool.started(与 codex 不同)。
  2. 不是 reasonix 升级导致:stream_attempt 已在后端显式忽略,不影响前端。
  3. 不是历史消息落盘gc_messages/gc_rooms 数据库全空(0 条)——群聊数据在 Web UI 进程内存里,未持久化。
  4. 根因:前端一次性渲染全部历史消息,无虚拟滚动
S(Ed, { ..., messages:i.value, virtualized:!1, "estimated-item-height":170, ...})

virtualized:!1 = 不虚拟滚动,一次性渲染所有消息 DOM。177k tokens 的审核对话+工具卡片全部渲染 → 主线程阻塞 → 浏览器无响应。 5. 每次消息变化全量重算Be(()=>i.value.map(...[G.id,G.content?.length,G.reasoning?.length,G.toolStatus...])) 每次都对全部消息重新计算长度,消息量大时同样卡死。

修复方案(已实施,2026-08-07)

表 C:前端补丁开虚拟滚动(实际采用)。

实施:升级 hermes-web-ui 0.6.37→0.6.39(重打 reasonix 补丁,8 个锚点因 minified 重构改名全部适配)+ 新增前端补丁 patches/apply_frontend_patch.py,把群聊列表 virtualized:!1virtualized:!0

// GroupChatView-C-9d0P7n.js
M(Ud, { class:"group-message-list", messages:s.value, virtualized:!0, ... })

为什么有效(代码证据)VirtualMessageList 组件渲染是三元分支:

  • virtualized:true → 走 DynamicScroller(vue-virtual-scroller),只渲染视口内+overscan 的 item,用 spacer 撑高,DOM 数量恒定
  • virtualized:falsemn(e.messages) 遍历渲染全部消息 DOM → 177k 消息全量渲染 → 主线程阻塞

验证:前端补丁 verify ✅(virtualized:!0 生效),服务端补丁 verify ✅(14 项全绿),服务 active,reasonix v1.21.0 installed:true。

注意

  • 单聊 MessageList 仍 virtualized:!1(上游 0.6.39 全局行为,未擅改)
  • 前端补丁在 npm 升级后会因文件 hash 变化失效,需重新 apply_frontend_patch.py apply
  • 浏览器需强刷(原 GroupChatView 被缓存)

注意:升级 0.6.39 后所有 reasonix 补丁丢失,需 apply-patch.sh 重打,且新版本工具事件逻辑大改,补丁可能需更新。

✅ GitHub 在线仓库已配置(2026-08-07)

  • 仓库:ddonggit/reasonix-x(public,MIT)
  • 描述/主题已配置;README + LICENSE 已写入
  • 凭证:git credential store(~/.git-credentials,权限600),token 已存本机,后续 push 无需再提供
  • remote:https://github.com/ddonggit/reasonix-x.git(干净 URL,无内嵌 token)
  • git 身份:咚咚 <ddonggit@ddonggit.noreply.github.com>
  • 全部提交已推送,git push 即可同步

✅ 已修复:群聊服务崩溃 502 Bad Gateway(事务嵌套)(2026-08-07)

现象

老板在 Reasonix-x 群聊房间跑 reasonix 任务,工具调用卡死后无法 @人、无法发言,刷新页面报 502 Bad Gateway。系统自动重启后短暂恢复,但反复崩溃(当日 16:53/16:56/16:57/16:59/17:02/17:24 共 6 次)。

根因(100% 确认,崩溃栈稳定可复现)

hermes-web-ui v0.6.39 服务端群聊消息保存路径存在单次调用内事务嵌套缺陷

saveMessageAndRefreshRoom  →  外层 BEGIN IMMEDIATE(未提交)
  └─ pruneMessages(roomId)  →  withImmediateTransaction  →  内层 BEGIN IMMEDIATE
     └─ SQLite 抛 "cannot start a transaction within a transaction"
        └─ 未捕获异常 → process.exit(1) → nginx upstream 8648 拒连 → 502
  • pruneMessages(消息超 500 条时触发清理)内部调用 withImmediateTransaction 开新事务,而外层 saveMessageAndRefreshRoom 已在 BEGIN IMMEDIATE 事务中且未提交 → 嵌套 BEGIN。
  • 这是单次调用内的嵌套(非多连接并发),所以稳定可复现,不是偶发环境问题。
  • 崩溃栈(6 次完全一致):
Error: cannot start a transaction within a transaction
  at PB.withImmediateTransaction
  at PB.pruneMessages
  at PB.saveMessageAndRefreshRoom
  at Sy.handleMessage   ← socket.io 群聊消息处理

修复(SAVEPOINT 方案)

withImmediateTransaction 改用 SAVEPOINT:进入时检测当前连接是否已在事务中,若是则发 SAVEPOINT sp_n(SQLite 原生支持在 BEGIN IMMEDIATE 事务内嵌套 SAVEPOINT),不再发 BEGIN IMMEDIATE,消除嵌套 BEGIN。

  • patches/rules.json:新增 SAVEPOINT 事务修复 replace 规则(幂等)
  • patches/apply_patch.pyverify 增加 SAVEPOINT 检查项;apply 改为逐规则幂等应用

验证

  • 数据库副本压测:精确模拟 saveMessageAndRefreshRoom + pruneMessages 完整事务链,含 prune 触发(>500 条)+ 异常恢复,全部通过,0 崩溃
  • 服务 17:36:46 重启加载修复版后稳定运行,0 崩溃
  • apply_patch.py verify 全 ✅(含新增 SAVEPOINT 检查项)

持久化

  • commit 348b9fa(rules.json + apply_patch.py)
  • 补丁规则已写入 rules.json,npm 升级后 apply-patch.sh 会自动重打,不会再丢

教训

  • 此前的修复只改了运行文件,没存进补丁体系(定时炸弹:升级即丢)。本次已补齐 rules.json 规则 + verify 检查 + git 提交,三重保障。
  • SQLite 单连接同步执行下,崩溃未必来自并发,单次调用内的事务嵌套同样致命且更隐蔽。

✅ 已修复:reasonix 群聊头像显示为 hermes(0.6.39 升级后复发)(2026-08-07)

现象

群聊房间 reasonix(开发者)显示的不是定制头像,而是 hermes 默认头像。

根因

hermes-web-ui 0.6.39 升级覆盖了前端 GroupChatView bundle,导致两个问题:

  1. 前端头像映射表 Aa 只含 hermes/ekko/codex/claude 四个条目,缺少 reasonix → reasonix 回落显示 hermes.png
  2. reasonix.png 图片文件未复制到 dist/client/coding-agents/ 目录 → 网页读取不到

此 bug 在 0.6.36 时代(8月6日)修过一次(当时仅 cache busting,见 DEVELOPMENT.md 5.7),但 0.6.39 升级把修复覆盖了,且当时只改运行文件没进补丁体系,所以升级即丢。

修复

  • patches/reasonix.png:reasonix 群聊头像图片资源(随补丁入库,4062 字节)
  • patches/apply_frontend_patch.py:新增「reasonix 群聊头像映射」补丁规则,将 reasonix:"/coding-agents/reasonix.png" 注入 Aa 映射表;apply 时自动复制 reasonix.pngdist/client/coding-agents/verify/check 覆盖头像映射 + 图片存在性检查
  • 运行时:已应用补丁 + cache-bust(?v=avatar)绕过 immutable 缓存

验证

  • reasonix.png HTTP 200,4062 字节(真实图片,非 SPA fallback)
  • 前端 Aa 映射表已含 reasonix 条目
  • index.htmlno-cache,主入口 + GroupChatView 引用加 ?v=avatar,确保浏览器重新下载
  • 老板清除浏览器数据 + 刷新后确认头像恢复正常

持久化

  • commit ba2a0ae(apply_frontend_patch.py + reasonix.png)
  • 补丁规则已写入前端补丁脚本,npm 升级后 apply_frontend_patch.py apply 会自动重打

教训

  • 0.6.36 时代的头像修复只改运行文件没进补丁体系,0.6.39 升级即丢。本次已补齐前端补丁规则 + 图片入库 + git 提交。
  • 前端 immutable 缓存(max-age=31536000)下,改内容不改文件名 = 浏览器永远用旧版。cache-bust 查询串是必要手段;用户侧需清除浏览器数据或强刷。

✅ 已调优:群聊 agent 单轮工作量上限 maxSteps 90->150(2026-08-07)

背景

群聊里每个 agent(含审核家)回复时受 runtime.maxSteps 限制,单轮工具调用+思考步数到顶即被强制截断回复。此前值为 90,导致多步骤任务(如读文档结构+写文档)一轮做不完就被迫先开口,表现为"做一半被中断"。

改动

  • 配置文件:/root/.hermes-web-ui/.ekko/config/config.jsonruntime.maxSteps 由 90 调为 150
  • 原文件已备份为 config.json.bak-maxSteps,可随时回滚
  • 该配置在 agent 启动时读取,需重启 hermes-web-ui 服务才对新会话生效;当前正在跑的对话沿用旧值直到重启

生效

  • 19:05:17 由助理执行 systemctl restart hermes-web-ui 重启,进程 PID 41747 拉起
  • 重启后验证:服务 active,maxSteps=150 已生效;新对话/任务自动用新配置

相关配置项(同文件 runtime 段)

  • maxModelRetries = 3(模型出错重试)
  • maxConsecutiveToolFailures = 6(连续工具失败上限)
  • tools.codeExec.maxToolCalls = 50(代码执行工具次数)
  • delegation.subtaskMaxSteps = 30(子任务步数)
  • model.requestTimeoutMs = 300000(单次请求超时 5 分钟)

权衡

  • 调大 maxSteps 让单轮能做更多事、减少中断,但每轮更久、更费 token
  • 如后续觉得太慢/太费,可调回 90 或折中值

注意

  • 此配置不在 reasonix-x 补丁体系内(属 hermes-web-ui 运行时配置,非 dist 补丁),npm 升级不会覆盖该文件,无需写进 apply-patch
  • 若 hermes-web-ui 重装/换机,需手动重新设置该值

2026-08-07 编程工具页 reasonix 恢复(0.6.39 升级后丢失,commit 9b8c633)

现象

  • 升级 hermes-web-ui 后,web 控制台「编程工具」页只剩 Claude Code、Codex 两张卡片,Reasonix 卡片消失
  • 编程工具页无法看到/操作 reasonix

根因

  • 编程工具页是独立的客户端 JS 文件 CodingAgentsView-*.js,与群聊页(GroupChatView)分开
  • 该文件里 agent 列表 Ke写死的,只有 claude-code 和 codex,没有 reasonix
  • 0.6.39 升级覆盖了该文件,之前(如有)的 reasonix 条目丢失
  • 结构性漏补apply_frontend_patch.py 此前只覆盖 GroupChatView(群聊虚拟滚动 + 头像映射),从未覆盖 CodingAgentsView;apply-patch.sh 一键脚本也只调用后端 apply_patch.py,不调用前端补丁脚本 -> 升级后前端补丁根本没被打上

修复内容

  1. CodingAgentsView-*.js 补 8 处 reasonix 配置:
    • $e 图标映射加 Reasonix:"/coding-agents/reasonix.png"
    • Ke agent 列表加 {id:"reasonix",tool:"Reasonix",provider:"DeepSeek"}
    • B 配置对象加 reasonix 条目
    • 安装状态 I、删除状态 O、错误状态 w、加载状态 F 等均加 reasonix 条目
  2. 缓存绕过(cache-bust):CodingAgentsView 被浏览器标记 immutable(缓存一年),改文件不重新下载。把 index.html 主入口版本从 ?v=avatar 升到 ?v=coding,主入口里 CodingAgentsView 的 import 加 ?v=coding(2 处)-> 浏览器重新下载新版主入口 + 新版 CodingAgentsView
  3. 持久化到补丁体系:
    • apply_frontend_patch.py 新增 8 条 CodingAgentsView 规则 + cache-bust 应用逻辑 + verify 检查
    • apply-patch.sh 一键脚本改为同时调用后端 + 前端补丁(修复结构性漏补)
  4. verify 自检 27 项全部通过

验证

  • 运行文件 8 处替换全部成功,node --check 语法通过
  • HTTP 拉取的 CodingAgentsView 内容已包含 reasonix(Ke 数组、$e 图标、B 配置等多处)
  • index.html ?v=coding + Cache-Control: no-cache 生效
  • 纯前端改动,无需重启服务;用户需强制刷新浏览器(Ctrl/Cmd+Shift+R)

教训(结构性)

  • 一键脚本 apply-patch.sh 之前只打后端补丁、不打前端补丁,这是导致本次漏补的结构性原因。升级后即便跑了一键脚本,前端补丁也没生效。现已修复:一键脚本改为后端 + 前端一起打
  • 前端补丁脚本 apply_frontend_patch.py 的覆盖范围必须与后端对齐:后端 apply_patch.py 的 rules.json 有大量 reasonix 规则全打在 server/index.js,但前端有多个独立文件(GroupChatView、CodingAgentsView 等),每个文件都要单独维护补丁规则,否则升级后只有部分页面有 reasonix

2026-08-08 新建群聊房间下拉 / 工作流节点下拉无 reasonix(0.6.39 升级后丢失,结构性漏补)

现象

升级后:

  • 新建群聊房间「agent 类型」下拉只有 Hermes / Claude Code / Codex / Ekko Agent,无 Reasonix
  • 工作流新建 agent 节点「agent」下拉只有 Hermes / Claude Code / Codex,无 Reasonix
  • 编程工具页(已修)与后端 reasonix 逻辑均正常

根因

  • 与 5.15 编程工具页同根因:前端多个独立 chunk 文件,agent 列表写死,升级后被覆盖,且 apply_frontend_patch.py 从未覆盖这两个文件
  • 群聊下拉在 GroupChatView-*.jsNe 数组;工作流下拉在 WorkflowView-*.js_t 数组,都无 reasonix

修复内容

  1. GroupChatView-*.js 补 2 处:Ne 下拉加 {label:"Reasonix",value:"reasonix"}Ve provider 过滤映射加 me.value==="reasonix"?"reasonix" 分支
  2. WorkflowView-*.js 补 2 处:_t 下拉加 {label:"Reasonix",value:"reasonix"}gi() skill key 映射加 t==="reasonix"?"reasonix" 分支
  3. cache-bust:CACHE_VERSION coding -> gcwfapply_cachebust() 扩展循环处理 CodingAgentsView / GroupChatView / WorkflowView 三 chunk(WorkflowView 此前无版本参数)
  4. 持久化:apply_frontend_patch.py 新增 4 条规则 + cache-bust 覆盖三 chunk + verify 同步检查

验证

  • 4 条锚点在真实 dist 文件唯一匹配(dry-run 通过);check 正确识别为「未补丁」;语法通过
  • 已应用到运行文件:通过 nsenter -t <hermes-web-ui PID> -m 进入服务进程的可写 mount namespace(当前 shell 根 / 只读,但服务进程 namespace 是 rw),执行 python3 patches/apply_frontend_patch.py apply 成功落地 4 条规则 + cache-bust。verify 全部通过
  • 🔧 修复 apply_cachebust 脚本 bug:循环内写盘后未更新 js 内存副本,导致后处理的 chunk(WorkflowView)覆盖先处理的(CodingAgentsView/GroupChatView)的 cache-bust。已补 js = new_js(commit e68d84d)
  • 浏览器需强制刷新(Ctrl+Shift+R / Cmd+Shift+R)加载 ?v=gcwf 新资源

教训(结构性)

  • 前端每个写死 agent 列表的独立 chunk(GroupChatView、CodingAgentsView、WorkflowView…)都必须单独维护补丁规则
  • 升级后应跑 bash apply-patch.sh check 逐一核对所有前端文件的 reasonix 条目,而不只是后端