一个终端 AI Agent 如何演变为原生桌面应用——从第一行代码到今天,追溯那些塑造它的关键人物和决策。
Hermes Agent 仓库的第一个提交。纯 Python + CLI — 没有 GUI,没有桌面端。一个基于终端的 AI Agent,具备工具调用、会话管理和消息网关(Telegram、Discord、飞书等平台支持)。
hermes dashboard 命令诞生——一个基于 React + Vite 的 Web UI,通过 WebSocket 连接到 Python 后端。这是桌面客户端的前身。
apps/desktop/ 目录的第一个提交。一个基于 Electron 的结构化聊天应用,采用三栏布局(侧栏 + 对话 + 右侧面板)。主进程代码 558 行。没有安装器,没有更新系统——只是一个空壳。
桌面端正式合并进入 Hermes 主分支,与 CLI 和 Gateway 平起平坐。这次合并的规模远超其他所有提交——带来了跨平台安装器、自动更新、首次引导、远程后端、Windows NSIS 安装器(自动通过 winget 安装依赖)以及 electron-builder 集成。当时的版本号:0.0.2。
加入 HERMES_DESKTOP_REMOTE_URL 和 HERMES_DESKTOP_REMOTE_TOKEN 环境变量——在 WSL2 里运行 Hermes 后端,在原生 Windows 上渲染 Electron GUI。这是驱动远程连接架构设计的使用场景。
分阶段的引导流程:检测 Python/Git → 克隆仓库 → 创建 venv → 安装依赖。Windows 上 NSIS 安装器自动检测缺失依赖并通过 winget 安装。macOS/Linux 上通过 shell 脚本实现相同的阶段。
tui_gateway/ Python 包诞生——一套专为桌面端和 TUI 模式设计的 WebSocket 服务器。桌面端不再与 Web dashboard 共用后端,拥有了自己的消息协议、项目树扫描、git 探测和事件推送。
桌面端现在可以在 macOS 上自我更新:hermes update 拉取最新代码 → npm run pack 重建 .app → 交换脚本将新包替换运行中的副本 → 重启应用。应用内更新链路完整闭环。
桌面端获得了真正的嵌入式终端——@xterm/xterm + WebGL 渲染加速,后端由 node-pty 原生绑定驱动。用户可以直接在桌面窗口里执行 shell 命令。
每个聊天可以弹出到独立的 OS 窗口——非常适合在查看子 Agent 执行日志的同时继续主对话。背景节流被显式禁用,确保未聚焦窗口中的流式输出持续渲染。
一个透明的、始终置顶的宠物窗口,跟随 Hermes 吉祥物。宠物在自己独立的 Electron 表面运行(win=overlay),不连接网关——只是一个挂载宠物渲染的 React 组件。
完整的 i18n 框架,简体中文(zh-Hans)作为首个非英语语言。I18nProvider 包裹整个 React 组件树。
一系列修复阻止后台进程在 Windows 上弹出 cmd 窗口。终端弹窗问题困扰了桌面端数周——最终通过精确的 windowsHide 选项和进程创建标志解决。
Electron-builder 创建的 NSIS 安装器、afterPack 钩子为可执行程序打上 Hermes 标识、AppUserModelID 确保通知正常工作。Windows 桌面体验追上 macOS。
设置界面的完全重构。技能中心、工具管理和 MCP 服务器配置全部汇入统一的能力(Capabilities)标签页——主从布局、实时状态探测、React Query 支持的数据获取。
桌面端的组件库被提取为 @nous-research/ui,在桌面端、Web dashboard 和 TUI 应用之间共享。这正式化了过去在项目间复制粘贴的设计系统。
引导覆盖层获得完整的 OAuth 流程——通过 PKCE/设备码流登录 Nous Portal、Anthropic 或 OpenAI Codex。API 密钥输入被降级为备用链接。应用的第一帧画面现在是从欢迎卡片开始,而不是空白聊天界面。
两个 macOS 特有的问题:打包的 Electron 应用从 file:// 加载,被 WebSocket 来源校验拒绝(修复:在 loopback 绑定上信任非 Web 来源)。macOS Tahoe (macOS 26) 移动了交通灯按钮,破坏了窗口布局——通过运行时读取原生覆盖层宽度解决。
模型选择器现在显示每个模型的 $/Mtok 价格、免费/付费徽章,并为免费 Nous 账户禁用付费模型——与 CLI 的 hermes model 体验一致。
自动化桌面发布流水线被移除,改为按平台手动构建 + 上传。安装包手动发布到 GitHub Releases;网站通过环境变量引用它们。项目进入稳态维护节奏。
Hermes 桌面端是一个 Electron 应用,搭载 React 19、Tailwind CSS v4、嵌入式 xterm.js 终端、宠物覆盖层、多窗口支持、国际化、自动更新、OAuth 引导 和完整的 Agent 工具集。它运行与 CLI 相同的 Python 后端,共享配置、会话、技能和记忆——全部放在一个 ~/.hermes/ 目录下。
从 apps/desktop/ 的 Git 提交历史中提取 · 按提交次数排名
这些数字背后,是三个月的反复试错和无数个深夜
electron/main.cjs 从最初的 558 行,膨胀到今天的 7,700 行。6月底,Brooklyn 花了整整一天,把这个"上帝文件"拆成 42 个独立的模块——文件系统 IPC、终端 IPC、自动更新、卸载逻辑、主题管理……每个模块对应一个独立的 .cjs 文件。
Hermes 的终端工具会在后台 spawn 子进程。在 Windows 上,每个子进程都会弹出一个 cmd 窗口——用户的主对话窗口瞬间被几十个黑窗口淹没。这个问题横跨三周、涉及 4 位开发者、多个 PR 被 revert,最终才找到正确的 windowsHide 参数组合。
性能优化被回滚过、架构改动被回滚过、甚至连 composer 的按键响应优化都被回滚过。5月21日一天内连续 4 次 revert,包括一个"revert 了 revert"的来回。不是所有的改进都能一次做对。
流式输出的最后一行文字,完成时占位光标被移除,导致整段文字回缩——用户看到的是每次 AI 回复"跳了一下"。这个 bug 涉及 streaming caret 的 inline-block 宽度、action bar 在 streaming 过程中的挂载/卸载,修复方案是在光标上加了一个负 margin。
macOS 26 (Tahoe) 的系统更新把交通灯按钮(红绿灯)移了位置。桌面端的标题栏布局依赖这些按钮的坐标,系统一更新就全乱了。修复方案:在运行时动态读取原生覆盖层宽度,而不是硬编码。
Windows 上桌面端自更新后,更新脚本会触发后端重启,后端的重启又触发桌面端认为"更新未完成"再次运行更新——无限循环。每个版本都要重新确认"这次更新不会再把自己搞死"。
整个 6 月,桌面端有 871 次提交——平均每天 29 次。对比 5 月的 171 次,增长 5 倍。从嵌入终端、多窗口、宠物、国际化到 Windows 安装器,绝大多数核心功能都在这个月密集落地。同时伴随的是来回修复和多次返工。
多窗口模式下,非活跃窗口的流式输出会暂停。Electron 默认在窗口失焦时停止 requestAnimationFrame,而 Hermes 的流式渲染依赖它。修复需要给每个窗口显式设置 backgroundThrottling: false。但会话窗口的 webPreferences 和主窗口的定义是手写复制的,修复了主窗口忘了修子窗口。
Hermes 是完全开源的,欢迎中国开发者参与
数据来源:github.com/NousResearch/hermes-agent · MIT 协议
本页面由 Hermes Agent 自身编写——读取自己的 Git 历史来讲述自己的故事。